From 05f010156c54d3fc316179a8b98f03aa17636cc4 Mon Sep 17 00:00:00 2001 From: Eric Carraway Date: Wed, 8 May 2024 10:11:19 -0400 Subject: [PATCH] add initial `dist/` to version control --- .gitignore | 2 - dist/index.d.ts | 65 +++++++++++++++++++++++++++++ dist/index.js | 102 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- tsconfig.dist.json | 10 ----- tsconfig.json | 2 + 6 files changed, 170 insertions(+), 13 deletions(-) delete mode 100644 .gitignore create mode 100644 dist/index.d.ts create mode 100644 dist/index.js delete mode 100644 tsconfig.dist.json diff --git a/.gitignore b/.gitignore deleted file mode 100644 index de4d1f0..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..ce42011 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,65 @@ +export type AirtableClientOpts = { + /** A string of at least 10 characters. */ + apiKey: string; + /** A string of at least 10 characters beginning with "app". */ + baseId: string; + /** + * Optionally set the base URL (without a trailing slash). + * Defaults to "https://api.airtable.com/v0". + */ + baseUrl?: string; +}; +export type AirtableResponse = { + data: unknown; + ok: boolean; + status: number; + statusText: string; +}; +export type FieldsObj = Record; +export type CreateRecordOpts = { + fields: FieldsObj; + tableIdOrName: string; +}; +export type GetRecordOpts = { + /** A string of at least 10 characters beginning with "rec". */ + recordId: string; + tableIdOrName: string; +}; +export type UpdateRecordOpts = { + fields: FieldsObj; + /** + * A PATCH request (the default) will only update the fields you specify, + * leaving the rest as they were. + * + * A PUT request will perform a destructive update and clear all unspecified cell values. + */ + method?: `PATCH` | `PUT`; + /** A string of at least 10 characters beginning with "rec". */ + recordId: string; + tableIdOrName: string; +}; +/** + * @see https://github.com/ensembleblock/airtable + */ +export declare class AirtableClient { + private baseId; + private baseUrl; + private headers; + constructor({ apiKey, baseId, baseUrl }: AirtableClientOpts); + /** + * Create a record. + * @see https://airtable.com/developers/web/api/create-records + */ + createRecord({ fields, tableIdOrName, }: CreateRecordOpts): Promise; + /** + * Retrieve a single record using an Airtable `recordId`. + * Any "empty" fields (e.g. "", [], or false) in the record will not be returned. + * @see https://airtable.com/developers/web/api/get-record + */ + getRecord({ recordId, tableIdOrName, }: GetRecordOpts): Promise; + /** + * Updates a single record. + * @see https://airtable.com/developers/web/api/update-record + */ + updateRecord({ fields, method, recordId, tableIdOrName, }: UpdateRecordOpts): Promise; +} diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..5904094 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,102 @@ +/** + * @see https://github.com/ensembleblock/airtable + */ +export class AirtableClient { + baseId = null; + baseUrl = `https://api.airtable.com/v0`; + headers = {}; + constructor({ apiKey, baseId, baseUrl }) { + if (typeof apiKey !== `string` || apiKey.length < 10) { + throw new TypeError(`AirtableClient expected 'apiKey' to be string of at least 10 characters`); + } + if (typeof baseId !== `string` || + baseId.length < 10 || + !baseId.startsWith(`app`)) { + throw new TypeError(`AirtableClient expected 'baseId' to be string of at least 10 characters starting with 'app'`); + } + this.headers = { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': `application/json`, + }; + this.baseId = baseId; + if (baseUrl) { + if (typeof baseUrl !== `string` || baseUrl.endsWith(`/`)) { + throw new TypeError(`AirtableClient expected 'baseUrl' to be a string without a trailing slash`); + } + this.baseUrl = baseUrl; + } + } + /** + * Create a record. + * @see https://airtable.com/developers/web/api/create-records + */ + async createRecord({ fields, tableIdOrName, }) { + if (!fields || typeof fields !== `object` || Array.isArray(fields)) { + throw new TypeError(`Airtable createRecord expected 'fields' to be a plain object`); + } + if (!tableIdOrName || typeof tableIdOrName !== `string`) { + throw new TypeError(`Airtable createRecord expected 'tableIdOrName' to be a non-empty string`); + } + const createRecordUrl = `${this.baseUrl}/${this.baseId}/${tableIdOrName}`; + const body = JSON.stringify({ fields }); + const res = await fetch(createRecordUrl, { + body, + headers: this.headers, + method: `POST`, + }); + const data = await res.json(); + return { data, ok: res.ok, status: res.status, statusText: res.statusText }; + } + /** + * Retrieve a single record using an Airtable `recordId`. + * Any "empty" fields (e.g. "", [], or false) in the record will not be returned. + * @see https://airtable.com/developers/web/api/get-record + */ + async getRecord({ recordId, tableIdOrName, }) { + if (typeof recordId !== `string` || + recordId.length < 10 || + !recordId.startsWith(`rec`)) { + throw new TypeError(`Airtable getRecord expected 'recordId' to be string of at least 10 characters starting with 'rec'`); + } + if (!tableIdOrName || typeof tableIdOrName !== `string`) { + throw new TypeError(`Airtable getRecord expected 'tableIdOrName' to be a non-empty string`); + } + const getRecordUrl = `${this.baseUrl}/${this.baseId}/${tableIdOrName}/${recordId}`; + const res = await fetch(getRecordUrl, { + headers: this.headers, + method: `GET`, + }); + const data = await res.json(); + return { data, ok: res.ok, status: res.status, statusText: res.statusText }; + } + /** + * Updates a single record. + * @see https://airtable.com/developers/web/api/update-record + */ + async updateRecord({ fields, method = `PATCH`, recordId, tableIdOrName, }) { + if (!fields || typeof fields !== `object` || Array.isArray(fields)) { + throw new TypeError(`Airtable updateRecord expected 'fields' to be a plain object`); + } + if (typeof method !== `string` || + ![`PATCH`, `PUT`].includes(method.toUpperCase())) { + throw new TypeError(`Airtable updateRecord expected 'method' to be 'PATCH' or 'PUT'`); + } + if (typeof recordId !== `string` || + recordId.length < 10 || + !recordId.startsWith(`rec`)) { + throw new TypeError(`Airtable updateRecord expected 'recordId' to be string of at least 10 characters starting with 'rec'`); + } + if (!tableIdOrName || typeof tableIdOrName !== `string`) { + throw new TypeError(`Airtable updateRecord expected 'tableIdOrName' to be a non-empty string`); + } + const updateRecordUrl = `${this.baseUrl}/${this.baseId}/${tableIdOrName}/${recordId}`; + const body = JSON.stringify({ fields }); + const res = await fetch(updateRecordUrl, { + body, + headers: this.headers, + method: method.toUpperCase(), + }); + const data = await res.json(); + return { data, ok: res.ok, status: res.status, statusText: res.statusText }; + } +} diff --git a/package.json b/package.json index 38c4bea..30f6f1c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "access": "public" }, "scripts": { - "build": "tsc --project tsconfig.dist.json", + "build": "tsc", "clean": "rimraf dist node_modules", "clean:build": "rimraf dist", "dev": "tsc --watch", diff --git a/tsconfig.dist.json b/tsconfig.dist.json deleted file mode 100644 index 0382932..0000000 --- a/tsconfig.dist.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "sourceMap": true, - "inlineSources": true, - "rootDir": "./src", - "outDir": "./dist" - }, - "include": ["src"] -} diff --git a/tsconfig.json b/tsconfig.json index aaa163e..bd4ec5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,9 @@ "outDir": "dist", "pretty": true, "resolveJsonModule": false, + "rootDir": "./src", "skipLibCheck": true, + "sourceMap": false, "strict": true, "stripInternal": true, "target": "ES2022",