airtable-ts 1.3.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,12 @@ All of these problems are solved with airtable-ts.
15
15
 
16
16
  In development, you'll define the expected types for different fields: enabling you to leverage type hints in your code editor. After deployment, if people make breaking changes to your base schema you'll get clear runtime errors that pinpoint the problem (rather than your app silently failing, or worse: doing something dangerous!) We also fix unintuitive API behavior, like not being able to tell whether a checkbox field has been deleted or the values are just unticked.
17
17
 
18
- ## Usage
18
+ ## Related libraries
19
+
20
+ - [airtable-ts-codegen](https://github.com/domdomegg/airtable-ts-codegen): Autogenerate TypeScript definitions for your Airtable base, with perfect compatibility with airtable-ts
21
+ - [airtable-ts-formula](https://github.com/domdomegg/airtable-ts-formula): Type-safe, securely-escaped and rename-robust formulae for Airtable (e.g. for `filterByFormula`)
22
+
23
+ ## Example
19
24
 
20
25
  Install it with `npm install airtable-ts`. Then, use it like:
21
26
 
@@ -24,6 +29,7 @@ import { AirtableTs, Table } from 'airtable-ts';
24
29
 
25
30
  const db = new AirtableTs({
26
31
  // Create your own at https://airtable.com/create/tokens
32
+ // Recommended scopes: schema.bases:read, data.records:read, data.records:write
27
33
  apiKey: 'pat1234.abcdef',
28
34
  });
29
35
 
@@ -68,6 +74,171 @@ async function prefixTitleOfFirstClassOfFirstStudent(prefix: string) {
68
74
  const rawSdk: Airtable = db.airtable;
69
75
  ```
70
76
 
77
+ ## AirtableTs Class Reference
78
+
79
+ The `AirtableTs` class provides several methods to interact with your Airtable base:
80
+
81
+ ### Constructor
82
+
83
+ ```ts
84
+ new AirtableTs(options: AirtableTsOptions)
85
+ ```
86
+
87
+ Creates a new instance of the AirtableTs client.
88
+
89
+ **Parameters:**
90
+ - `options`: Configuration options
91
+ - `apiKey`: Your Airtable API key (required)
92
+ - Create one at https://airtable.com/create/tokens
93
+ - Recommended scopes: schema.bases:read, data.records:read, data.records:write
94
+ - `baseSchemaCacheDurationMs`: Duration in milliseconds to cache base schema (default: 120,000ms = 2 minutes)
95
+ - Other options from Airtable.js are supported, including: `apiVersion`, `customHeaders`, `endpointUrl`, `noRetryIfRateLimited`, `requestTimeout`
96
+
97
+ **Example:**
98
+ ```ts
99
+ const db = new AirtableTs({
100
+ apiKey: 'pat1234.abcdef',
101
+ baseSchemaCacheDurationMs: 300000, // 5 minutes
102
+ });
103
+ ```
104
+
105
+ ### get
106
+
107
+ ```ts
108
+ async get<T extends Item>(table: Table<T>, id: string): Promise<T>
109
+ ```
110
+
111
+ Retrieves a single record from a table by its ID.
112
+
113
+ **Parameters:**
114
+ - `table`: Table definition object
115
+ - `id`: The ID of the record to retrieve
116
+
117
+ **Example:**
118
+ ```ts
119
+ const student = await db.get(studentTable, 'rec1234');
120
+ console.log(student.firstName); // Access fields with type safety
121
+ ```
122
+
123
+ ### scan
124
+
125
+ ```ts
126
+ async scan<T extends Item>(table: Table<T>, params?: ScanParams): Promise<T[]>
127
+ ```
128
+
129
+ Retrieves all records from a table, with optional filtering parameters.
130
+
131
+ **Parameters:**
132
+ - `table`: Table definition object
133
+ - `params` (optional): Parameters for filtering, sorting, and limiting results
134
+ - `filterByFormula`: An Airtable formula to filter records
135
+ - Tip: use [airtable-ts-formula](https://github.com/domdomegg/airtable-ts-formula) for type-safe, securely-escaped and rename-robust formulae!
136
+ - `sort`: Array of sort objects (e.g., `[{field: 'firstName', direction: 'asc'}]`)
137
+ - `maxRecords`: Maximum number of records to return
138
+ - `view`: Name of a view to use for record selection
139
+ - `timeZone`: Timezone for interpreting date values
140
+ - `userLocale`: Locale for formatting date values
141
+
142
+ **Example:**
143
+ ```ts
144
+ // Get all records
145
+ const allStudents = await db.scan(studentTable);
146
+
147
+ // Get records with filtering and sorting
148
+ const topStudents = await db.scan(studentTable, {
149
+ filterByFormula: '{grade} >= 90',
150
+ sort: [{field: 'grade', direction: 'desc'}],
151
+ maxRecords: 10
152
+ });
153
+ ```
154
+
155
+ ### insert
156
+
157
+ ```ts
158
+ async insert<T extends Item>(table: Table<T>, data: Partial<Omit<T, 'id'>>): Promise<T>
159
+ ```
160
+
161
+ Creates a new record in a table. Returns the new record.
162
+
163
+ **Parameters:**
164
+ - `table`: Table definition object
165
+ - `data`: The data for the new record (without an ID, as Airtable will generate one)
166
+
167
+ **Example:**
168
+ ```ts
169
+ const newStudent = await db.insert(studentTable, {
170
+ firstName: 'Jane',
171
+ classes: ['rec5678', 'rec9012']
172
+ });
173
+ console.log(newStudent.id); // The new record ID generated by Airtable
174
+ ```
175
+
176
+ ### update
177
+
178
+ ```ts
179
+ async update<T extends Item>(table: Table<T>, data: Partial<T> & { id: string }): Promise<T>
180
+ ```
181
+
182
+ Updates an existing record in a table. Returns the updated record.
183
+
184
+ **Parameters:**
185
+ - `table`: Table definition object
186
+ - `data`: The data to update, must include the record ID
187
+
188
+ **Example:**
189
+ ```ts
190
+ const updatedStudent = await db.update(studentTable, {
191
+ id: 'rec1234',
192
+ firstName: 'John',
193
+ // Only include fields you want to update
194
+ });
195
+ ```
196
+
197
+ ### remove
198
+
199
+ ```ts
200
+ async remove<T extends Item>(table: Table<T>, id: string): Promise<{ id: string }>
201
+ ```
202
+
203
+ Deletes a record from a table.
204
+
205
+ **Parameters:**
206
+ - `table`: Table definition object
207
+ - `id`: The ID of the record to delete
208
+
209
+ **Example:**
210
+ ```ts
211
+ await db.remove(studentTable, 'rec1234');
212
+ ```
213
+
214
+ ### table
215
+
216
+ ```ts
217
+ async table<T extends Item>(table: Table<T>): Promise<AirtableTsTable<T>>
218
+ ```
219
+
220
+ Retrieves the AirtableTsTable object for the given table definition. This is the Airtable.js table, enriched with a `fields` key that includes details of the Airtable schema for this table.
221
+
222
+ This is useful for advanced use cases where you need direct access to the Airtable table object.
223
+
224
+ **Parameters:**
225
+ - `table`: Table definition object
226
+
227
+ **Example:**
228
+ ```ts
229
+ const airtableTsTable = await db.table(studentTable);
230
+ // Now you can use the raw Airtable table object with field information
231
+ console.log(airtableTsTable.fields); // Access the table's field definitions
232
+ ```
233
+
234
+ ### airtable
235
+
236
+ The underlying Airtable.js SDK is exposed in the `airtable` property.
237
+
238
+ ```ts
239
+ const rawSdk: Airtable = db.airtable;
240
+ ```
241
+
71
242
  ## Contributing
72
243
 
73
244
  Pull requests are welcomed on GitHub! To get started:
@@ -0,0 +1,18 @@
1
+ import Airtable from 'airtable';
2
+ import { Item, Table } from './mapping/typeUtils';
3
+ import { AirtableTsTable, AirtableTsOptions, ScanParams } from './types';
4
+ export declare class AirtableTs {
5
+ airtable: Airtable;
6
+ private options;
7
+ constructor(options: AirtableTsOptions);
8
+ get<T extends Item>(table: Table<T>, id: string): Promise<T>;
9
+ scan<T extends Item>(table: Table<T>, params?: ScanParams): Promise<T[]>;
10
+ insert<T extends Item>(table: Table<T>, data: Partial<Omit<T, 'id'>>): Promise<T>;
11
+ update<T extends Item>(table: Table<T>, data: Partial<T> & {
12
+ id: string;
13
+ }): Promise<T>;
14
+ remove<T extends Item>(table: Table<T>, id: string): Promise<{
15
+ id: string;
16
+ }>;
17
+ table<T extends Item>(table: Table<T>): Promise<AirtableTsTable<T>>;
18
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AirtableTs = void 0;
7
+ const airtable_1 = __importDefault(require("airtable"));
8
+ const getAirtableTsTable_1 = require("./getAirtableTsTable");
9
+ const assertMatchesSchema_1 = require("./assertMatchesSchema");
10
+ const recordMapper_1 = require("./mapping/recordMapper");
11
+ const getFields_1 = require("./getFields");
12
+ const wrapToCatchAirtableErrors_1 = require("./wrapToCatchAirtableErrors");
13
+ const AirtableTsError_1 = require("./AirtableTsError");
14
+ class AirtableTs {
15
+ airtable;
16
+ options;
17
+ constructor(options) {
18
+ this.airtable = new airtable_1.default(options);
19
+ this.options = {
20
+ ...airtable_1.default.default_config(),
21
+ ...options,
22
+ baseSchemaCacheDurationMs: options.baseSchemaCacheDurationMs ?? 120000,
23
+ };
24
+ }
25
+ async get(table, id) {
26
+ if (!id) {
27
+ throw new AirtableTsError_1.AirtableTsError({
28
+ message: `The record ID must be supplied when getting a record. This was thrown when trying to get a '${table.name}' (${table.tableId}) record.`,
29
+ type: AirtableTsError_1.ErrorType.INVALID_PARAMETER,
30
+ suggestion: 'Provide a valid record ID when calling the get method.',
31
+ });
32
+ }
33
+ const airtableTsTable = await (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
34
+ const record = await airtableTsTable.find(id);
35
+ if (!record) {
36
+ throw new AirtableTsError_1.AirtableTsError({
37
+ message: `No record with ID '${id}' exists in table '${table.name}'.`,
38
+ type: AirtableTsError_1.ErrorType.RESOURCE_NOT_FOUND,
39
+ suggestion: 'Verify that the record ID is correct and that the record exists in the table.',
40
+ });
41
+ }
42
+ return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
43
+ }
44
+ async scan(table, params) {
45
+ const airtableTsTable = await (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
46
+ const records = await airtableTsTable.select({
47
+ fields: (0, getFields_1.getFields)(table),
48
+ ...params,
49
+ }).all();
50
+ return records.map((record) => (0, recordMapper_1.mapRecordFromAirtable)(table, record));
51
+ }
52
+ async insert(table, data) {
53
+ (0, assertMatchesSchema_1.assertMatchesSchema)(table, { ...data, id: 'placeholder' });
54
+ const airtableTsTable = await (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
55
+ const record = await airtableTsTable.create((0, recordMapper_1.mapRecordToAirtable)(table, data, airtableTsTable));
56
+ return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
57
+ }
58
+ async update(table, data) {
59
+ (0, assertMatchesSchema_1.assertMatchesSchema)(table, { ...data });
60
+ const { id, ...withoutId } = data;
61
+ const airtableTsTable = await (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
62
+ const record = await airtableTsTable.update(data.id, (0, recordMapper_1.mapRecordToAirtable)(table, withoutId, airtableTsTable));
63
+ return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
64
+ }
65
+ async remove(table, id) {
66
+ if (!id) {
67
+ throw new AirtableTsError_1.AirtableTsError({
68
+ message: `The record ID must be supplied when removing a record. This was thrown when trying to get a '${table.name}' (${table.tableId}) record.`,
69
+ type: AirtableTsError_1.ErrorType.INVALID_PARAMETER,
70
+ suggestion: 'Provide a valid record ID when calling the remove method.',
71
+ });
72
+ }
73
+ const airtableTsTable = await (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
74
+ const record = await airtableTsTable.destroy(id);
75
+ return { id: record.id };
76
+ }
77
+ async table(table) {
78
+ return (0, getAirtableTsTable_1.getAirtableTsTable)(this.airtable, table, this.options);
79
+ }
80
+ }
81
+ exports.AirtableTs = AirtableTs;
82
+ // Wrap all methods of AirtableTs with error handling
83
+ // See https://github.com/Airtable/airtable.js/issues/294
84
+ (0, wrapToCatchAirtableErrors_1.wrapToCatchAirtableErrors)(AirtableTs);
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Error types for categorizing different kinds of errors
3
+ */
4
+ export declare enum ErrorType {
5
+ SCHEMA_VALIDATION = "SCHEMA_VALIDATION",
6
+ RESOURCE_NOT_FOUND = "RESOURCE_NOT_FOUND",
7
+ INVALID_PARAMETER = "INVALID_PARAMETER",
8
+ API_ERROR = "API_ERROR"
9
+ }
10
+ /**
11
+ * Base error class for all airtable-ts errors
12
+ */
13
+ export declare class AirtableTsError extends Error {
14
+ /** Error type for categorization */
15
+ type: ErrorType;
16
+ constructor(options: {
17
+ message: string;
18
+ type: ErrorType;
19
+ suggestion?: string;
20
+ });
21
+ }
22
+ export declare const prependError: (error: unknown, prefix: string) => unknown;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prependError = exports.AirtableTsError = exports.ErrorType = void 0;
4
+ /**
5
+ * Error types for categorizing different kinds of errors
6
+ */
7
+ var ErrorType;
8
+ (function (ErrorType) {
9
+ ErrorType["SCHEMA_VALIDATION"] = "SCHEMA_VALIDATION";
10
+ ErrorType["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND";
11
+ ErrorType["INVALID_PARAMETER"] = "INVALID_PARAMETER";
12
+ ErrorType["API_ERROR"] = "API_ERROR";
13
+ })(ErrorType || (exports.ErrorType = ErrorType = {}));
14
+ /**
15
+ * Base error class for all airtable-ts errors
16
+ */
17
+ class AirtableTsError extends Error {
18
+ /** Error type for categorization */
19
+ type;
20
+ constructor(options) {
21
+ const { message, suggestion, type } = options;
22
+ super(suggestion ? `${message} Suggestion: ${suggestion}` : message);
23
+ this.type = type;
24
+ this.name = 'AirtableTsError';
25
+ }
26
+ }
27
+ exports.AirtableTsError = AirtableTsError;
28
+ const prependError = (error, prefix) => {
29
+ if (error instanceof AirtableTsError) {
30
+ // eslint-disable-next-line no-param-reassign
31
+ error.message = `${prefix}: ${error.message}`;
32
+ // eslint-disable-next-line no-param-reassign
33
+ error.stack = `Error: ${prefix}: ${error.stack?.startsWith('Error: ') ? error.stack.slice('Error: '.length) : error.stack}`;
34
+ }
35
+ return error;
36
+ };
37
+ exports.prependError = prependError;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertMatchesSchema = void 0;
4
4
  const typeUtils_1 = require("./mapping/typeUtils");
5
+ const AirtableTsError_1 = require("./AirtableTsError");
5
6
  /**
6
7
  * In theory, this should never catch stuff because our type mapping logic should
7
8
  * verify the types are compatible.
@@ -14,7 +15,10 @@ const typeUtils_1 = require("./mapping/typeUtils");
14
15
  */
15
16
  function assertMatchesSchema(table, data, mode = 'partial') {
16
17
  if (typeof data !== 'object' || data === null) {
17
- throw new Error(`[airtable-ts] Item for ${table.name} is not an object`);
18
+ throw new AirtableTsError_1.AirtableTsError({
19
+ message: `Data passed in to airtable-ts should be an object but received ${data === null ? 'null' : typeof data}.`,
20
+ type: AirtableTsError_1.ErrorType.SCHEMA_VALIDATION,
21
+ });
18
22
  }
19
23
  Object.entries(table.schema).forEach(([fieldName, type]) => {
20
24
  const value = data[fieldName];
@@ -22,10 +26,16 @@ function assertMatchesSchema(table, data, mode = 'partial') {
22
26
  if (mode === 'partial') {
23
27
  return;
24
28
  }
25
- throw new Error(`[airtable-ts] Item for ${table.name} table is missing field '${fieldName}' (expected ${type})`);
29
+ throw new AirtableTsError_1.AirtableTsError({
30
+ message: `Data passed in to airtable-ts is missing required field '${fieldName}' in table '${table.name}' (expected type: ${type}).`,
31
+ type: AirtableTsError_1.ErrorType.SCHEMA_VALIDATION,
32
+ });
26
33
  }
27
34
  if (!(0, typeUtils_1.matchesType)(value, type)) {
28
- throw new Error(`[airtable-ts] Item for ${table.name} table has invalid value for field '${fieldName}' (actual type ${typeof value}, but expected ${type})`);
35
+ throw new AirtableTsError_1.AirtableTsError({
36
+ message: `Invalid value passed in to airtable-ts for field '${fieldName}' in table '${table.name}' (received type: ${typeof value}, expected type: ${type}).`,
37
+ type: AirtableTsError_1.ErrorType.SCHEMA_VALIDATION,
38
+ });
29
39
  }
30
40
  });
31
41
  }
@@ -0,0 +1,4 @@
1
+ import Airtable from 'airtable';
2
+ import { Item, Table } from './mapping/typeUtils';
3
+ import { AirtableTsTable, CompleteAirtableTsOptions } from './types';
4
+ export declare const getAirtableTsTable: <T extends Item>(airtable: Airtable, table: Table<T>, options: CompleteAirtableTsOptions) => Promise<AirtableTsTable<T>>;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.getAirtableTsTable = void 0;
27
+ const axios_1 = __importStar(require("axios"));
28
+ const AirtableTsError_1 = require("./AirtableTsError");
29
+ const getAirtableTsTable = async (airtable, table, options) => {
30
+ const airtableTable = airtable.base(table.baseId).table(table.tableId);
31
+ // We need the schema so we know which type mapper to use.
32
+ // Even if we were inferring a type mapper from the schema, we'd still have to
33
+ // do this as otherwise we can't distinguish a column with all null values from
34
+ // a column that is missing entirely. We'd like to do that as for safety, we'd
35
+ // rather throw an error if the column is missing entirely; this suggests a
36
+ // misconfiguration. But an all-null column is okay. The particular case that
37
+ // this is likely for is checkbox columns.
38
+ const baseSchema = await getAirtableBaseSchema(table.baseId, options);
39
+ const tableDefinition = baseSchema.find((t) => t.id === table.tableId);
40
+ if (!tableDefinition) {
41
+ throw new AirtableTsError_1.AirtableTsError({
42
+ message: `Table '${table.name}' (${table.tableId}) does not exist in base ${table.baseId}.`,
43
+ type: AirtableTsError_1.ErrorType.RESOURCE_NOT_FOUND,
44
+ suggestion: 'Verify that the base ID and table ID are correct.',
45
+ });
46
+ }
47
+ return Object.assign(airtableTable, {
48
+ fields: tableDefinition.fields,
49
+ tsDefinition: table,
50
+ });
51
+ };
52
+ exports.getAirtableTsTable = getAirtableTsTable;
53
+ const baseSchemaCache = new Map();
54
+ /**
55
+ * Get the schemas from the cache or Airtable API for the tables in the given base.
56
+ * @see https://airtable.com/developers/web/api/get-base-schema
57
+ * @param baseId The base id to get the schemas for
58
+ */
59
+ const getAirtableBaseSchema = async (baseId, options) => {
60
+ const fromCache = baseSchemaCache.get(baseId);
61
+ if (fromCache && Date.now() - fromCache.at < options.baseSchemaCacheDurationMs) {
62
+ return fromCache.data;
63
+ }
64
+ if (!options.apiKey) {
65
+ throw new AirtableTsError_1.AirtableTsError({
66
+ message: 'API key is required but was not provided.',
67
+ type: AirtableTsError_1.ErrorType.INVALID_PARAMETER,
68
+ suggestion: 'Provide a valid Airtable API key when initializing AirtableTs.',
69
+ });
70
+ }
71
+ const res = await (0, axios_1.default)({
72
+ baseURL: options.endpointUrl ?? 'https://api.airtable.com',
73
+ url: `/v0/meta/bases/${baseId}/tables`,
74
+ ...(options.requestTimeout ? { timeout: options.requestTimeout } : {}),
75
+ headers: {
76
+ // eslint-disable-next-line no-underscore-dangle
77
+ Authorization: `Bearer ${options.apiKey}`,
78
+ ...options.customHeaders,
79
+ },
80
+ }).catch((err) => {
81
+ const normalizedErrorMessage = err instanceof axios_1.AxiosError
82
+ ? `${err.message}. Status: ${err.status}. Data: ${JSON.stringify(err.response?.data)}`
83
+ : err;
84
+ throw new AirtableTsError_1.AirtableTsError({
85
+ message: `Failed to get base schema: ${normalizedErrorMessage}`,
86
+ type: AirtableTsError_1.ErrorType.API_ERROR,
87
+ suggestion: 'Ensure the API token is correct, and has `schema.bases:read` permission to the target base.',
88
+ });
89
+ });
90
+ const baseSchema = res.data.tables;
91
+ if (baseSchemaCache.size > 100) {
92
+ baseSchemaCache.clear();
93
+ // If you're seeing this warning, then we probably either need to:
94
+ // - Update the maximum limit before clearing the cache, provided we have memory headroom; or
95
+ // - Use a last recently used cache or similar
96
+ console.warn('[airtable-ts] baseSchemaCache cleared to avoid a memory leak: this code is not currently optimized for accessing over 100 different bases from a single instance');
97
+ }
98
+ baseSchemaCache.set(baseId, { at: Date.now(), data: baseSchema });
99
+ return baseSchema;
100
+ };
package/dist/index.d.ts CHANGED
@@ -1,21 +1,5 @@
1
- import Airtable from 'airtable';
2
- import type { QueryParams } from 'airtable/lib/query_params';
3
- import { Item, Table } from './mapping/typeUtils';
4
- import { AirtableTsOptions } from './types';
5
- export declare class AirtableTs {
6
- airtable: Airtable;
7
- private options;
8
- constructor(options: AirtableTsOptions);
9
- get<T extends Item>(table: Table<T>, id: string): Promise<T>;
10
- scan<T extends Item>(table: Table<T>, params?: ScanParams): Promise<T[]>;
11
- insert<T extends Item>(table: Table<T>, data: Partial<Omit<T, 'id'>>): Promise<T>;
12
- update<T extends Item>(table: Table<T>, data: Partial<T> & {
13
- id: string;
14
- }): Promise<T>;
15
- remove<T extends Item>(table: Table<T>, id: string): Promise<{
16
- id: string;
17
- }>;
18
- }
19
- export type ScanParams = Omit<QueryParams<unknown>, 'fields' | 'cellFormat' | 'method' | 'returnFieldsByFieldId' | 'pageSize' | 'offset'>;
20
- export type { AirtableTsOptions } from './types';
1
+ export { AirtableTs } from './AirtableTs';
2
+ export { AirtableTsError } from './AirtableTsError';
3
+ export type { AirtableTsOptions, ScanParams, AirtableTsTable } from './types';
21
4
  export type { Item, Table } from './mapping/typeUtils';
5
+ export type { WrappedAirtableError } from './wrapToCatchAirtableErrors';
package/dist/index.js CHANGED
@@ -1,64 +1,7 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AirtableTs = void 0;
7
- const airtable_1 = __importDefault(require("airtable"));
8
- const getAirtableTable_1 = require("./getAirtableTable");
9
- const assertMatchesSchema_1 = require("./assertMatchesSchema");
10
- const recordMapper_1 = require("./mapping/recordMapper");
11
- const getFields_1 = require("./getFields");
12
- class AirtableTs {
13
- airtable;
14
- options;
15
- constructor(options) {
16
- this.airtable = new airtable_1.default(options);
17
- this.options = {
18
- ...airtable_1.default.default_config(),
19
- ...options,
20
- baseSchemaCacheDurationMs: options.baseSchemaCacheDurationMs ?? 120000,
21
- };
22
- }
23
- async get(table, id) {
24
- if (!id) {
25
- throw new Error(`[airtable-ts] Tried to get record in ${table.name} with no id`);
26
- }
27
- const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
28
- const record = await airtableTable.find(id);
29
- if (!record) {
30
- throw new Error(`[airtable-ts] Failed to find record in ${table.name} with key ${id}`);
31
- }
32
- return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
33
- }
34
- async scan(table, params) {
35
- const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
36
- const records = await airtableTable.select({
37
- fields: (0, getFields_1.getFields)(table),
38
- ...params,
39
- }).all();
40
- return records.map((record) => (0, recordMapper_1.mapRecordFromAirtable)(table, record));
41
- }
42
- async insert(table, data) {
43
- (0, assertMatchesSchema_1.assertMatchesSchema)(table, { ...data, id: 'placeholder' });
44
- const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
45
- const record = await airtableTable.create((0, recordMapper_1.mapRecordToAirtable)(table, data, airtableTable));
46
- return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
47
- }
48
- async update(table, data) {
49
- (0, assertMatchesSchema_1.assertMatchesSchema)(table, { ...data });
50
- const { id, ...withoutId } = data;
51
- const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
52
- const record = await airtableTable.update(data.id, (0, recordMapper_1.mapRecordToAirtable)(table, withoutId, airtableTable));
53
- return (0, recordMapper_1.mapRecordFromAirtable)(table, record);
54
- }
55
- async remove(table, id) {
56
- if (!id) {
57
- throw new Error(`[airtable-ts] Tried to remove record in ${table.name} with no id`);
58
- }
59
- const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
60
- const record = await airtableTable.destroy(id);
61
- return { id: record.id };
62
- }
63
- }
64
- exports.AirtableTs = AirtableTs;
3
+ exports.AirtableTsError = exports.AirtableTs = void 0;
4
+ var AirtableTs_1 = require("./AirtableTs");
5
+ Object.defineProperty(exports, "AirtableTs", { enumerable: true, get: function () { return AirtableTs_1.AirtableTs; } });
6
+ var AirtableTsError_1 = require("./AirtableTsError");
7
+ Object.defineProperty(exports, "AirtableTsError", { enumerable: true, get: function () { return AirtableTsError_1.AirtableTsError; } });