airtable-ts 1.3.0 → 1.3.2
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/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/mapping/fieldMappers.d.ts +1 -1
- package/dist/mapping/fieldMappers.js +165 -558
- package/dist/mapping/recordMapper.js +28 -18
- package/dist/mapping/typeUtils.d.ts +7 -1
- package/dist/mapping/typeUtils.js +3 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ export declare class AirtableTs {
|
|
|
12
12
|
update<T extends Item>(table: Table<T>, data: Partial<T> & {
|
|
13
13
|
id: string;
|
|
14
14
|
}): Promise<T>;
|
|
15
|
-
remove<T extends Item>(table: Table<T>, id: string): Promise<
|
|
15
|
+
remove<T extends Item>(table: Table<T>, id: string): Promise<{
|
|
16
|
+
id: string;
|
|
17
|
+
}>;
|
|
16
18
|
}
|
|
17
19
|
export type ScanParams = Omit<QueryParams<unknown>, 'fields' | 'cellFormat' | 'method' | 'returnFieldsByFieldId' | 'pageSize' | 'offset'>;
|
|
18
20
|
export type { AirtableTsOptions } from './types';
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ class AirtableTs {
|
|
|
58
58
|
}
|
|
59
59
|
const airtableTable = await (0, getAirtableTable_1.getAirtableTable)(this.airtable, table, this.options);
|
|
60
60
|
const record = await airtableTable.destroy(id);
|
|
61
|
-
return
|
|
61
|
+
return { id: record.id };
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
exports.AirtableTs = AirtableTs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AirtableTypeString, FromAirtableTypeString, FromTsTypeString, TsTypeString } from './typeUtils';
|
|
2
2
|
type Mapper = {
|
|
3
3
|
[T in TsTypeString]?: {
|
|
4
|
-
[A in AirtableTypeString]?: {
|
|
4
|
+
[A in AirtableTypeString | 'unknown']?: {
|
|
5
5
|
toAirtable: (value: FromTsTypeString<T>) => FromAirtableTypeString<A>;
|
|
6
6
|
fromAirtable: (value: FromAirtableTypeString<A> | null | undefined) => FromTsTypeString<T>;
|
|
7
7
|
};
|
|
@@ -1,161 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fieldMappers = void 0;
|
|
4
|
-
const
|
|
5
|
-
if (value === null || value === undefined) {
|
|
6
|
-
throw new Error('[airtable-ts] Missing required value');
|
|
7
|
-
}
|
|
8
|
-
return value;
|
|
9
|
-
};
|
|
4
|
+
const typeUtils_1 = require("./typeUtils");
|
|
10
5
|
const fallbackMapperPair = (toFallback, fromFallback) => ({
|
|
11
6
|
toAirtable: (value) => value ?? toFallback,
|
|
12
7
|
fromAirtable: (value) => value ?? fromFallback,
|
|
13
8
|
});
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return [value];
|
|
34
|
-
},
|
|
35
|
-
fromAirtable: (value) => {
|
|
36
|
-
if (!value) {
|
|
37
|
-
throw new Error('[airtable-ts] Failed to coerce multipleSelects type field to a single string, as it was blank');
|
|
38
|
-
}
|
|
39
|
-
if (value.length !== 1) {
|
|
40
|
-
throw new Error(`[airtable-ts] Can't coerce multipleSelects to a single string, as there were ${value?.length} entries`);
|
|
41
|
-
}
|
|
42
|
-
return value[0];
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
multipleRecordLinks: {
|
|
46
|
-
toAirtable: (value) => {
|
|
47
|
-
return [value];
|
|
48
|
-
},
|
|
49
|
-
fromAirtable: (value) => {
|
|
50
|
-
if (!value) {
|
|
51
|
-
throw new Error('[airtable-ts] Failed to coerce multipleRecordLinks type field to a single string, as it was blank');
|
|
52
|
-
}
|
|
53
|
-
if (value.length !== 1) {
|
|
54
|
-
throw new Error(`[airtable-ts] Can't coerce multipleRecordLinks to a single string, as there were ${value?.length} entries`);
|
|
55
|
-
}
|
|
56
|
-
return value[0];
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
date: {
|
|
60
|
-
toAirtable: (value) => {
|
|
61
|
-
const date = new Date(value);
|
|
62
|
-
if (Number.isNaN(date.getTime())) {
|
|
63
|
-
throw new Error('[airtable-ts] Invalid date string');
|
|
64
|
-
}
|
|
65
|
-
return date.toJSON().slice(0, 10);
|
|
66
|
-
},
|
|
67
|
-
fromAirtable: (value) => {
|
|
68
|
-
const date = new Date(value ?? '');
|
|
69
|
-
if (Number.isNaN(date.getTime())) {
|
|
70
|
-
throw new Error('[airtable-ts] Invalid date string');
|
|
71
|
-
}
|
|
72
|
-
return date.toJSON();
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
dateTime: {
|
|
76
|
-
toAirtable: (value) => {
|
|
77
|
-
const date = new Date(value);
|
|
78
|
-
if (Number.isNaN(date.getTime())) {
|
|
79
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
80
|
-
}
|
|
81
|
-
return date.toJSON();
|
|
82
|
-
},
|
|
83
|
-
fromAirtable: (value) => {
|
|
84
|
-
const date = new Date(value ?? '');
|
|
85
|
-
if (Number.isNaN(date.getTime())) {
|
|
86
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
87
|
-
}
|
|
88
|
-
return date.toJSON();
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
createdTime: {
|
|
92
|
-
toAirtable: (value) => {
|
|
93
|
-
const date = new Date(value);
|
|
94
|
-
if (Number.isNaN(date.getTime())) {
|
|
95
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
96
|
-
}
|
|
97
|
-
return date.toJSON();
|
|
98
|
-
},
|
|
99
|
-
fromAirtable: (value) => {
|
|
100
|
-
const date = new Date(value ?? '');
|
|
101
|
-
if (Number.isNaN(date.getTime())) {
|
|
102
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
103
|
-
}
|
|
104
|
-
return date.toJSON();
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
lastModifiedTime: {
|
|
108
|
-
toAirtable: (value) => {
|
|
109
|
-
const date = new Date(value);
|
|
110
|
-
if (Number.isNaN(date.getTime())) {
|
|
111
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
112
|
-
}
|
|
113
|
-
return date.toJSON();
|
|
114
|
-
},
|
|
115
|
-
fromAirtable: (value) => {
|
|
116
|
-
const date = new Date(value ?? '');
|
|
117
|
-
if (Number.isNaN(date.getTime())) {
|
|
118
|
-
throw new Error('[airtable-ts] Invalid dateTime string');
|
|
119
|
-
}
|
|
120
|
-
return date.toJSON();
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
multipleLookupValues: {
|
|
124
|
-
toAirtable: () => { throw new Error('[airtable-ts] lookup type field is readonly'); },
|
|
125
|
-
fromAirtable: (value) => {
|
|
126
|
-
if (!value) {
|
|
127
|
-
throw new Error('[airtable-ts] Failed to coerce lookup type field to a single string, as it was blank');
|
|
128
|
-
}
|
|
129
|
-
if (value.length !== 1) {
|
|
130
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single string, as there were ${value?.length} entries`);
|
|
131
|
-
}
|
|
132
|
-
if (typeof value[0] !== 'string') {
|
|
133
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single string, as it was of type ${typeof value[0]}`);
|
|
134
|
-
}
|
|
135
|
-
return value[0];
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
rollup: {
|
|
139
|
-
toAirtable: () => { throw new Error('[airtable-ts] rollup type field is readonly'); },
|
|
140
|
-
fromAirtable: (value) => {
|
|
141
|
-
if (typeof value === 'string')
|
|
142
|
-
return value;
|
|
143
|
-
if (value === undefined || value === null)
|
|
144
|
-
return '';
|
|
145
|
-
throw new Error(`[airtable-ts] Can't coerce rollup to a string, as it was of type ${typeof value}`);
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
formula: {
|
|
149
|
-
toAirtable: () => { throw new Error('[airtable-ts] formula type field is readonly'); },
|
|
150
|
-
fromAirtable: (value) => {
|
|
151
|
-
if (typeof value === 'string')
|
|
152
|
-
return value;
|
|
153
|
-
if (value === undefined || value === null)
|
|
154
|
-
return '';
|
|
155
|
-
throw new Error(`[airtable-ts] Can't coerce formula to a string, as it was of type ${typeof value}`);
|
|
156
|
-
},
|
|
157
|
-
},
|
|
9
|
+
const dateTimeMapperPair = {
|
|
10
|
+
// Number assumed to be unix time in seconds
|
|
11
|
+
toAirtable: (value) => {
|
|
12
|
+
if (value === null)
|
|
13
|
+
return null;
|
|
14
|
+
const date = new Date(typeof value === 'number' ? value * 1000 : value);
|
|
15
|
+
if (Number.isNaN(date.getTime())) {
|
|
16
|
+
throw new Error('[airtable-ts] Invalid dateTime');
|
|
17
|
+
}
|
|
18
|
+
return date.toJSON();
|
|
19
|
+
},
|
|
20
|
+
fromAirtable: (value) => {
|
|
21
|
+
if (value === null || value === undefined)
|
|
22
|
+
return null;
|
|
23
|
+
const date = new Date(value);
|
|
24
|
+
if (Number.isNaN(date.getTime())) {
|
|
25
|
+
throw new Error('[airtable-ts] Invalid dateTime');
|
|
26
|
+
}
|
|
27
|
+
return date.toJSON();
|
|
158
28
|
},
|
|
29
|
+
};
|
|
30
|
+
const readonly = (airtableType) => () => { throw new Error(`[airtable-ts] ${airtableType} type field is readonly`); };
|
|
31
|
+
const coerce = (airtableType, tsType) => (value) => {
|
|
32
|
+
const parsedType = (0, typeUtils_1.parseType)(tsType);
|
|
33
|
+
if (!parsedType.array && typeof value === parsedType.single) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
if (parsedType.array && Array.isArray(value) && value.every((v) => typeof v === parsedType.single)) {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
if (parsedType.nullable && (value === undefined || value === null || (Array.isArray(value) && value.length === 0))) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (parsedType.array && typeof value === parsedType.single) {
|
|
43
|
+
return [value];
|
|
44
|
+
}
|
|
45
|
+
if (!parsedType.array && Array.isArray(value) && value.length === 1 && typeof value[0] === parsedType.single) {
|
|
46
|
+
return value[0];
|
|
47
|
+
}
|
|
48
|
+
if (!parsedType.array && Array.isArray(value) && value.length !== 1) {
|
|
49
|
+
throw new Error(`[airtable-ts] Can't coerce ${airtableType} to a ${tsType}, as there were ${value.length} array entries`);
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`[airtable-ts] Can't coerce ${airtableType} to a ${tsType}, as it was of type ${typeof value}`);
|
|
52
|
+
};
|
|
53
|
+
const stringOrNull = {
|
|
159
54
|
'string | null': {
|
|
160
55
|
url: fallbackMapperPair(null, null),
|
|
161
56
|
email: fallbackMapperPair(null, null),
|
|
@@ -164,302 +59,57 @@ exports.fieldMappers = {
|
|
|
164
59
|
multilineText: fallbackMapperPair(null, null),
|
|
165
60
|
richText: fallbackMapperPair(null, null),
|
|
166
61
|
singleSelect: fallbackMapperPair(null, null),
|
|
167
|
-
externalSyncSource: {
|
|
168
|
-
toAirtable: () => { throw new Error('[airtable-ts] externalSyncSource type field is readonly'); },
|
|
169
|
-
fromAirtable: (value) => value ?? null,
|
|
170
|
-
},
|
|
171
62
|
multipleSelects: {
|
|
172
|
-
toAirtable: (value) =>
|
|
173
|
-
|
|
174
|
-
},
|
|
175
|
-
fromAirtable: (value) => {
|
|
176
|
-
if (!value || value.length === 0) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
if (value.length !== 1) {
|
|
180
|
-
throw new Error(`[airtable-ts] Can't coerce multipleSelects to a single string, as there were ${value?.length} entries`);
|
|
181
|
-
}
|
|
182
|
-
return value[0];
|
|
183
|
-
},
|
|
63
|
+
toAirtable: (value) => (value ? [value] : []),
|
|
64
|
+
fromAirtable: coerce('multipleSelects', 'string | null'),
|
|
184
65
|
},
|
|
185
66
|
multipleRecordLinks: {
|
|
186
|
-
toAirtable: (value) =>
|
|
187
|
-
|
|
188
|
-
},
|
|
189
|
-
fromAirtable: (value) => {
|
|
190
|
-
if (!value || value.length === 0) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
if (value.length !== 1) {
|
|
194
|
-
throw new Error(`[airtable-ts] Can't coerce multipleRecordLinks to a single string, as there were ${value?.length} entries`);
|
|
195
|
-
}
|
|
196
|
-
return value[0];
|
|
197
|
-
},
|
|
67
|
+
toAirtable: (value) => (value ? [value] : []),
|
|
68
|
+
fromAirtable: coerce('multipleRecordLinks', 'string | null'),
|
|
198
69
|
},
|
|
199
70
|
date: {
|
|
200
|
-
toAirtable: (value) =>
|
|
201
|
-
|
|
202
|
-
return null;
|
|
203
|
-
const date = new Date(value);
|
|
204
|
-
if (Number.isNaN(date.getTime())) {
|
|
205
|
-
throw new Error('[airtable-ts] Invalid date');
|
|
206
|
-
}
|
|
207
|
-
return date.toJSON().slice(0, 10);
|
|
208
|
-
},
|
|
209
|
-
fromAirtable: (value) => {
|
|
210
|
-
if (value === null || value === undefined)
|
|
211
|
-
return null;
|
|
212
|
-
const date = new Date(value);
|
|
213
|
-
if (Number.isNaN(date.getTime())) {
|
|
214
|
-
throw new Error('[airtable-ts] Invalid date');
|
|
215
|
-
}
|
|
216
|
-
return date.toJSON();
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
dateTime: {
|
|
220
|
-
toAirtable: (value) => {
|
|
221
|
-
if (value === null)
|
|
222
|
-
return null;
|
|
223
|
-
const date = new Date(value);
|
|
224
|
-
if (Number.isNaN(date.getTime())) {
|
|
225
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
226
|
-
}
|
|
227
|
-
return date.toJSON();
|
|
228
|
-
},
|
|
229
|
-
fromAirtable: (value) => {
|
|
230
|
-
if (value === null || value === undefined)
|
|
231
|
-
return null;
|
|
232
|
-
const date = new Date(value);
|
|
233
|
-
if (Number.isNaN(date.getTime())) {
|
|
234
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
235
|
-
}
|
|
236
|
-
return date.toJSON();
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
createdTime: {
|
|
240
|
-
toAirtable: (value) => {
|
|
241
|
-
if (value === null)
|
|
242
|
-
return null;
|
|
243
|
-
const date = new Date(value);
|
|
244
|
-
if (Number.isNaN(date.getTime())) {
|
|
245
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
246
|
-
}
|
|
247
|
-
return date.toJSON();
|
|
248
|
-
},
|
|
249
|
-
fromAirtable: (value) => {
|
|
250
|
-
if (value === null || value === undefined)
|
|
251
|
-
return null;
|
|
252
|
-
const date = new Date(value);
|
|
253
|
-
if (Number.isNaN(date.getTime())) {
|
|
254
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
255
|
-
}
|
|
256
|
-
return date.toJSON();
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
lastModifiedTime: {
|
|
260
|
-
toAirtable: (value) => {
|
|
261
|
-
if (value === null)
|
|
262
|
-
return null;
|
|
263
|
-
const date = new Date(value);
|
|
264
|
-
if (Number.isNaN(date.getTime())) {
|
|
265
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
266
|
-
}
|
|
267
|
-
return date.toJSON();
|
|
268
|
-
},
|
|
269
|
-
fromAirtable: (value) => {
|
|
270
|
-
if (value === null || value === undefined)
|
|
271
|
-
return null;
|
|
272
|
-
const date = new Date(value);
|
|
273
|
-
if (Number.isNaN(date.getTime())) {
|
|
274
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
275
|
-
}
|
|
276
|
-
return date.toJSON();
|
|
277
|
-
},
|
|
71
|
+
toAirtable: (value) => dateTimeMapperPair.toAirtable(value)?.slice(0, 10) ?? null,
|
|
72
|
+
fromAirtable: dateTimeMapperPair.fromAirtable,
|
|
278
73
|
},
|
|
74
|
+
dateTime: dateTimeMapperPair,
|
|
75
|
+
createdTime: dateTimeMapperPair,
|
|
76
|
+
lastModifiedTime: dateTimeMapperPair,
|
|
279
77
|
multipleLookupValues: {
|
|
280
|
-
toAirtable: (
|
|
281
|
-
fromAirtable: (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single string, as there were ${value?.length} entries`);
|
|
287
|
-
}
|
|
288
|
-
if (typeof value[0] !== 'string') {
|
|
289
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single string, as it was of type ${typeof value[0]}`);
|
|
290
|
-
}
|
|
291
|
-
return value[0];
|
|
292
|
-
},
|
|
78
|
+
toAirtable: readonly('multipleLookupValues'),
|
|
79
|
+
fromAirtable: coerce('multipleLookupValues', 'string | null'),
|
|
80
|
+
},
|
|
81
|
+
externalSyncSource: {
|
|
82
|
+
toAirtable: readonly('externalSyncSource'),
|
|
83
|
+
fromAirtable: coerce('externalSyncSource', 'string | null'),
|
|
293
84
|
},
|
|
294
85
|
rollup: {
|
|
295
|
-
toAirtable: (
|
|
296
|
-
fromAirtable: (
|
|
297
|
-
if (typeof value === 'string')
|
|
298
|
-
return value;
|
|
299
|
-
if (value === undefined || value === null)
|
|
300
|
-
return null;
|
|
301
|
-
throw new Error(`[airtable-ts] Can't coerce rollup to a string, as it was of type ${typeof value}`);
|
|
302
|
-
},
|
|
86
|
+
toAirtable: readonly('rollup'),
|
|
87
|
+
fromAirtable: coerce('rollup', 'string | null'),
|
|
303
88
|
},
|
|
304
89
|
formula: {
|
|
305
|
-
toAirtable: (
|
|
306
|
-
fromAirtable: (
|
|
307
|
-
if (typeof value === 'string')
|
|
308
|
-
return value;
|
|
309
|
-
if (value === undefined || value === null)
|
|
310
|
-
return null;
|
|
311
|
-
throw new Error(`[airtable-ts] Can't coerce formula to a string, as it was of type ${typeof value}`);
|
|
312
|
-
},
|
|
90
|
+
toAirtable: readonly('formula'),
|
|
91
|
+
fromAirtable: coerce('formula', 'string | null'),
|
|
313
92
|
},
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
multipleLookupValues: {
|
|
318
|
-
toAirtable: () => { throw new Error('[airtable-ts] lookup type field is readonly'); },
|
|
319
|
-
fromAirtable: (value) => {
|
|
320
|
-
if (!value) {
|
|
321
|
-
throw new Error('[airtable-ts] Failed to coerce lookup type field to a single boolean, as it was blank');
|
|
322
|
-
}
|
|
323
|
-
if (value.length !== 1) {
|
|
324
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single boolean, as there were ${value?.length} entries`);
|
|
325
|
-
}
|
|
326
|
-
if (typeof value[0] !== 'boolean') {
|
|
327
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single boolean, as it was of type ${typeof value[0]}`);
|
|
328
|
-
}
|
|
329
|
-
return value[0];
|
|
330
|
-
},
|
|
93
|
+
unknown: {
|
|
94
|
+
toAirtable: (value) => value,
|
|
95
|
+
fromAirtable: coerce('unknown', 'string | null'),
|
|
331
96
|
},
|
|
332
97
|
},
|
|
98
|
+
};
|
|
99
|
+
const booleanOrNull = {
|
|
333
100
|
'boolean | null': {
|
|
334
101
|
checkbox: fallbackMapperPair(null, null),
|
|
335
102
|
multipleLookupValues: {
|
|
336
|
-
toAirtable: (
|
|
337
|
-
fromAirtable: (
|
|
338
|
-
if (!value || value.length === 0) {
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
if (value.length !== 1) {
|
|
342
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single boolean, as there were ${value?.length} entries`);
|
|
343
|
-
}
|
|
344
|
-
if (typeof value[0] !== 'boolean') {
|
|
345
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single boolean, as it was of type ${typeof value[0]}`);
|
|
346
|
-
}
|
|
347
|
-
return value[0];
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
number: {
|
|
352
|
-
number: requiredMapperPair,
|
|
353
|
-
rating: requiredMapperPair,
|
|
354
|
-
duration: requiredMapperPair,
|
|
355
|
-
currency: requiredMapperPair,
|
|
356
|
-
percent: requiredMapperPair,
|
|
357
|
-
count: {
|
|
358
|
-
toAirtable: () => { throw new Error('[airtable-ts] count type field is readonly'); },
|
|
359
|
-
fromAirtable: (value) => required(value),
|
|
360
|
-
},
|
|
361
|
-
autoNumber: {
|
|
362
|
-
toAirtable: () => { throw new Error('[airtable-ts] autoNumber type field is readonly'); },
|
|
363
|
-
fromAirtable: (value) => required(value),
|
|
103
|
+
toAirtable: readonly('multipleLookupValues'),
|
|
104
|
+
fromAirtable: coerce('multipleLookupValues', 'boolean | null'),
|
|
364
105
|
},
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const date = new Date(value * 1000);
|
|
369
|
-
if (Number.isNaN(date.getTime())) {
|
|
370
|
-
throw new Error('[airtable-ts] Invalid date');
|
|
371
|
-
}
|
|
372
|
-
return date.toJSON().slice(0, 10);
|
|
373
|
-
},
|
|
374
|
-
fromAirtable: (value) => {
|
|
375
|
-
const date = new Date(value ?? '');
|
|
376
|
-
if (Number.isNaN(date.getTime())) {
|
|
377
|
-
throw new Error('[airtable-ts] Invalid date');
|
|
378
|
-
}
|
|
379
|
-
return Math.floor(date.getTime() / 1000);
|
|
380
|
-
},
|
|
381
|
-
},
|
|
382
|
-
// Number assumed to be unix time in seconds
|
|
383
|
-
dateTime: {
|
|
384
|
-
toAirtable: (value) => {
|
|
385
|
-
const date = new Date(value * 1000);
|
|
386
|
-
if (Number.isNaN(date.getTime())) {
|
|
387
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
388
|
-
}
|
|
389
|
-
return date.toJSON();
|
|
390
|
-
},
|
|
391
|
-
fromAirtable: (value) => {
|
|
392
|
-
const date = new Date(value ?? '');
|
|
393
|
-
if (Number.isNaN(date.getTime())) {
|
|
394
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
395
|
-
}
|
|
396
|
-
return Math.floor(date.getTime() / 1000);
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
createdTime: {
|
|
400
|
-
toAirtable: (value) => {
|
|
401
|
-
const date = new Date(value * 1000);
|
|
402
|
-
if (Number.isNaN(date.getTime())) {
|
|
403
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
404
|
-
}
|
|
405
|
-
return date.toJSON();
|
|
406
|
-
},
|
|
407
|
-
fromAirtable: (value) => {
|
|
408
|
-
const date = new Date(value ?? '');
|
|
409
|
-
if (Number.isNaN(date.getTime())) {
|
|
410
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
411
|
-
}
|
|
412
|
-
return Math.floor(date.getTime() / 1000);
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
lastModifiedTime: {
|
|
416
|
-
toAirtable: (value) => {
|
|
417
|
-
const date = new Date(value * 1000);
|
|
418
|
-
if (Number.isNaN(date.getTime())) {
|
|
419
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
420
|
-
}
|
|
421
|
-
return date.toJSON();
|
|
422
|
-
},
|
|
423
|
-
fromAirtable: (value) => {
|
|
424
|
-
const date = new Date(value ?? '');
|
|
425
|
-
if (Number.isNaN(date.getTime())) {
|
|
426
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
427
|
-
}
|
|
428
|
-
return Math.floor(date.getTime() / 1000);
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
multipleLookupValues: {
|
|
432
|
-
toAirtable: () => { throw new Error('[airtable-ts] lookup type field is readonly'); },
|
|
433
|
-
fromAirtable: (value) => {
|
|
434
|
-
if (!value) {
|
|
435
|
-
throw new Error('[airtable-ts] Failed to coerce lookup type field to a single number, as it was blank');
|
|
436
|
-
}
|
|
437
|
-
if (value.length !== 1) {
|
|
438
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single number, as there were ${value?.length} entries`);
|
|
439
|
-
}
|
|
440
|
-
if (typeof value[0] !== 'number') {
|
|
441
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single number, as it was of type ${typeof value[0]}`);
|
|
442
|
-
}
|
|
443
|
-
return value[0];
|
|
444
|
-
},
|
|
445
|
-
},
|
|
446
|
-
rollup: {
|
|
447
|
-
toAirtable: () => { throw new Error('[airtable-ts] rollup type field is readonly'); },
|
|
448
|
-
fromAirtable: (value) => {
|
|
449
|
-
if (typeof value === 'number')
|
|
450
|
-
return value;
|
|
451
|
-
throw new Error(`[airtable-ts] Can't coerce rollup to a number, as it was of type ${typeof value}`);
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
formula: {
|
|
455
|
-
toAirtable: () => { throw new Error('[airtable-ts] formula type field is readonly'); },
|
|
456
|
-
fromAirtable: (value) => {
|
|
457
|
-
if (typeof value === 'number')
|
|
458
|
-
return value;
|
|
459
|
-
throw new Error(`[airtable-ts] Can't coerce formula to a number, as it was of type ${typeof value}`);
|
|
460
|
-
},
|
|
106
|
+
unknown: {
|
|
107
|
+
toAirtable: (value) => value,
|
|
108
|
+
fromAirtable: coerce('unknown', 'boolean | null'),
|
|
461
109
|
},
|
|
462
110
|
},
|
|
111
|
+
};
|
|
112
|
+
const numberOrNull = {
|
|
463
113
|
'number | null': {
|
|
464
114
|
number: fallbackMapperPair(null, null),
|
|
465
115
|
rating: fallbackMapperPair(null, null),
|
|
@@ -468,190 +118,147 @@ exports.fieldMappers = {
|
|
|
468
118
|
percent: fallbackMapperPair(null, null),
|
|
469
119
|
count: {
|
|
470
120
|
fromAirtable: (value) => value ?? null,
|
|
471
|
-
toAirtable: (
|
|
121
|
+
toAirtable: readonly('count'),
|
|
472
122
|
},
|
|
473
123
|
autoNumber: {
|
|
474
124
|
fromAirtable: (value) => value ?? null,
|
|
475
|
-
toAirtable: (
|
|
125
|
+
toAirtable: readonly('autoNumber'),
|
|
476
126
|
},
|
|
477
|
-
// Number assumed to be unix time in seconds
|
|
478
127
|
date: {
|
|
479
|
-
toAirtable: (value) =>
|
|
480
|
-
if (value === null)
|
|
481
|
-
return null;
|
|
482
|
-
const date = new Date(value * 1000);
|
|
483
|
-
if (Number.isNaN(date.getTime())) {
|
|
484
|
-
throw new Error('[airtable-ts] Invalid date');
|
|
485
|
-
}
|
|
486
|
-
return date.toJSON().slice(0, 10);
|
|
487
|
-
},
|
|
128
|
+
toAirtable: (value) => dateTimeMapperPair.toAirtable(value)?.slice(0, 10) ?? null,
|
|
488
129
|
fromAirtable: (value) => {
|
|
489
|
-
|
|
130
|
+
const nullableValue = dateTimeMapperPair.fromAirtable(value);
|
|
131
|
+
if (nullableValue === null)
|
|
490
132
|
return null;
|
|
491
|
-
const date = new Date(
|
|
133
|
+
const date = new Date(nullableValue);
|
|
492
134
|
if (Number.isNaN(date.getTime())) {
|
|
493
135
|
throw new Error('[airtable-ts] Invalid date');
|
|
494
136
|
}
|
|
495
137
|
return Math.floor(date.getTime() / 1000);
|
|
496
138
|
},
|
|
497
139
|
},
|
|
498
|
-
// Number assumed to be unix time in seconds
|
|
499
140
|
dateTime: {
|
|
500
|
-
toAirtable:
|
|
501
|
-
if (value === null)
|
|
502
|
-
return null;
|
|
503
|
-
const date = new Date(value * 1000);
|
|
504
|
-
if (Number.isNaN(date.getTime())) {
|
|
505
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
506
|
-
}
|
|
507
|
-
return date.toJSON();
|
|
508
|
-
},
|
|
141
|
+
toAirtable: dateTimeMapperPair.toAirtable,
|
|
509
142
|
fromAirtable: (value) => {
|
|
510
|
-
|
|
143
|
+
const nullableValue = dateTimeMapperPair.fromAirtable(value);
|
|
144
|
+
if (nullableValue === null)
|
|
511
145
|
return null;
|
|
512
|
-
const date = new Date(
|
|
146
|
+
const date = new Date(nullableValue);
|
|
513
147
|
if (Number.isNaN(date.getTime())) {
|
|
514
|
-
throw new Error('[airtable-ts] Invalid
|
|
148
|
+
throw new Error('[airtable-ts] Invalid date');
|
|
515
149
|
}
|
|
516
150
|
return Math.floor(date.getTime() / 1000);
|
|
517
151
|
},
|
|
518
152
|
},
|
|
519
153
|
createdTime: {
|
|
520
|
-
toAirtable:
|
|
521
|
-
if (value === null)
|
|
522
|
-
return null;
|
|
523
|
-
const date = new Date(value * 1000);
|
|
524
|
-
if (Number.isNaN(date.getTime())) {
|
|
525
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
526
|
-
}
|
|
527
|
-
return date.toJSON();
|
|
528
|
-
},
|
|
154
|
+
toAirtable: dateTimeMapperPair.toAirtable,
|
|
529
155
|
fromAirtable: (value) => {
|
|
530
|
-
|
|
156
|
+
const nullableValue = dateTimeMapperPair.fromAirtable(value);
|
|
157
|
+
if (nullableValue === null)
|
|
531
158
|
return null;
|
|
532
|
-
const date = new Date(
|
|
159
|
+
const date = new Date(nullableValue);
|
|
533
160
|
if (Number.isNaN(date.getTime())) {
|
|
534
|
-
throw new Error('[airtable-ts] Invalid
|
|
161
|
+
throw new Error('[airtable-ts] Invalid date');
|
|
535
162
|
}
|
|
536
163
|
return Math.floor(date.getTime() / 1000);
|
|
537
164
|
},
|
|
538
165
|
},
|
|
539
166
|
lastModifiedTime: {
|
|
540
|
-
toAirtable:
|
|
541
|
-
if (value === null)
|
|
542
|
-
return null;
|
|
543
|
-
const date = new Date(value * 1000);
|
|
544
|
-
if (Number.isNaN(date.getTime())) {
|
|
545
|
-
throw new Error('[airtable-ts] Invalid dateTime');
|
|
546
|
-
}
|
|
547
|
-
return date.toJSON();
|
|
548
|
-
},
|
|
167
|
+
toAirtable: dateTimeMapperPair.toAirtable,
|
|
549
168
|
fromAirtable: (value) => {
|
|
550
|
-
|
|
169
|
+
const nullableValue = dateTimeMapperPair.fromAirtable(value);
|
|
170
|
+
if (nullableValue === null)
|
|
551
171
|
return null;
|
|
552
|
-
const date = new Date(
|
|
172
|
+
const date = new Date(nullableValue);
|
|
553
173
|
if (Number.isNaN(date.getTime())) {
|
|
554
|
-
throw new Error('[airtable-ts] Invalid
|
|
174
|
+
throw new Error('[airtable-ts] Invalid date');
|
|
555
175
|
}
|
|
556
176
|
return Math.floor(date.getTime() / 1000);
|
|
557
177
|
},
|
|
558
178
|
},
|
|
559
179
|
multipleLookupValues: {
|
|
560
|
-
toAirtable: (
|
|
561
|
-
fromAirtable: (
|
|
562
|
-
if (!value || value.length === 0) {
|
|
563
|
-
return null;
|
|
564
|
-
}
|
|
565
|
-
if (value.length !== 1) {
|
|
566
|
-
throw new Error(`[airtable-ts] Can't coerce lookup to a single number, as there were ${value?.length} entries`);
|
|
567
|
-
}
|
|
568
|
-
if (typeof value[0] !== 'number') {
|
|
569
|
-
throw new Error(`[airtable-ts] Can't coerce singular lookup to a single number, as it was of type ${typeof value[0]}`);
|
|
570
|
-
}
|
|
571
|
-
return value[0];
|
|
572
|
-
},
|
|
180
|
+
toAirtable: readonly('multipleLookupValues'),
|
|
181
|
+
fromAirtable: coerce('multipleLookupValues', 'number | null'),
|
|
573
182
|
},
|
|
574
183
|
rollup: {
|
|
575
|
-
toAirtable: (
|
|
576
|
-
fromAirtable: (
|
|
577
|
-
if (typeof value === 'number')
|
|
578
|
-
return value;
|
|
579
|
-
if (value === null || value === undefined)
|
|
580
|
-
return null;
|
|
581
|
-
throw new Error(`[airtable-ts] Can't coerce rollup to a number, as it was of type ${typeof value}`);
|
|
582
|
-
},
|
|
184
|
+
toAirtable: readonly('rollup'),
|
|
185
|
+
fromAirtable: coerce('rollup', 'number | null'),
|
|
583
186
|
},
|
|
584
187
|
formula: {
|
|
585
|
-
toAirtable: (
|
|
586
|
-
fromAirtable: (
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
throw new Error(`[airtable-ts] Can't coerce formula to a number, as it was of type ${typeof value}`);
|
|
592
|
-
},
|
|
188
|
+
toAirtable: readonly('formula'),
|
|
189
|
+
fromAirtable: coerce('formula', 'number | null'),
|
|
190
|
+
},
|
|
191
|
+
unknown: {
|
|
192
|
+
toAirtable: (value) => value,
|
|
193
|
+
fromAirtable: coerce('unknown', 'number | null'),
|
|
593
194
|
},
|
|
594
195
|
},
|
|
595
|
-
|
|
196
|
+
};
|
|
197
|
+
const stringArrayOrNull = {
|
|
198
|
+
'string[] | null': {
|
|
596
199
|
multipleSelects: fallbackMapperPair([], []),
|
|
597
200
|
multipleRecordLinks: fallbackMapperPair([], []),
|
|
598
201
|
multipleLookupValues: {
|
|
599
202
|
toAirtable: () => { throw new Error('[airtable-ts] lookup type field is readonly'); },
|
|
600
|
-
fromAirtable: (
|
|
601
|
-
if (!Array.isArray(value)) {
|
|
602
|
-
throw new Error('[airtable-ts] Failed to coerce lookup type field to a string array, as it was not an array');
|
|
603
|
-
}
|
|
604
|
-
if (value.some((v) => typeof v !== 'string')) {
|
|
605
|
-
throw new Error('[airtable-ts] Can\'t coerce lookup to a string array, as it had non string type');
|
|
606
|
-
}
|
|
607
|
-
return value;
|
|
608
|
-
},
|
|
203
|
+
fromAirtable: coerce('multipleLookupValues', 'string[] | null'),
|
|
609
204
|
},
|
|
610
205
|
formula: {
|
|
611
206
|
toAirtable: () => { throw new Error('[airtable-ts] formula type field is readonly'); },
|
|
612
|
-
fromAirtable: (
|
|
613
|
-
if (!Array.isArray(value)) {
|
|
614
|
-
throw new Error('[airtable-ts] Failed to coerce formula type field to a string array, as it was not an array');
|
|
615
|
-
}
|
|
616
|
-
if (value.some((v) => typeof v !== 'string')) {
|
|
617
|
-
throw new Error('[airtable-ts] Can\'t coerce formula to a string array, as it had non string type');
|
|
618
|
-
}
|
|
619
|
-
return value;
|
|
620
|
-
},
|
|
207
|
+
fromAirtable: coerce('multipleLookupValues', 'string[] | null'),
|
|
621
208
|
},
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
multipleRecordLinks: fallbackMapperPair(null, null),
|
|
626
|
-
multipleLookupValues: {
|
|
627
|
-
toAirtable: () => { throw new Error('[airtable-ts] lookup type field is readonly'); },
|
|
628
|
-
fromAirtable: (value) => {
|
|
629
|
-
if (!value && !Array.isArray(value)) {
|
|
630
|
-
return null;
|
|
631
|
-
}
|
|
632
|
-
if (!Array.isArray(value)) {
|
|
633
|
-
throw new Error('[airtable-ts] Failed to coerce lookup type field to a string array, as it was not an array');
|
|
634
|
-
}
|
|
635
|
-
if (value.some((v) => typeof v !== 'string')) {
|
|
636
|
-
throw new Error('[airtable-ts] Can\'t coerce lookup to a string array, as it had non string type');
|
|
637
|
-
}
|
|
638
|
-
return value;
|
|
639
|
-
},
|
|
640
|
-
},
|
|
641
|
-
formula: {
|
|
642
|
-
toAirtable: () => { throw new Error('[airtable-ts] formula type field is readonly'); },
|
|
643
|
-
fromAirtable: (value) => {
|
|
644
|
-
if (!value && !Array.isArray(value)) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
if (!Array.isArray(value)) {
|
|
648
|
-
throw new Error('[airtable-ts] Failed to coerce formula type field to a string array, as it was not an array');
|
|
649
|
-
}
|
|
650
|
-
if (value.some((v) => typeof v !== 'string')) {
|
|
651
|
-
throw new Error('[airtable-ts] Can\'t coerce formula to a string array, as it had non string type');
|
|
652
|
-
}
|
|
653
|
-
return value;
|
|
654
|
-
},
|
|
209
|
+
unknown: {
|
|
210
|
+
toAirtable: (value) => value,
|
|
211
|
+
fromAirtable: coerce('unknown', 'string[] | null'),
|
|
655
212
|
},
|
|
656
213
|
},
|
|
657
214
|
};
|
|
215
|
+
exports.fieldMappers = {
|
|
216
|
+
...stringOrNull,
|
|
217
|
+
string: {
|
|
218
|
+
...Object.fromEntries(Object.entries(stringOrNull['string | null']).map(([airtableType, nullablePair]) => {
|
|
219
|
+
return [airtableType, {
|
|
220
|
+
toAirtable: nullablePair.toAirtable,
|
|
221
|
+
fromAirtable: (value) => {
|
|
222
|
+
const nullableValue = nullablePair.fromAirtable(value);
|
|
223
|
+
if (nullableValue === null && ['multipleRecordLinks', 'dateTime', 'createdTime', 'lastModifiedTime'].includes(airtableType)) {
|
|
224
|
+
throw new Error(`[airtable-ts] Expected non-null or non-empty value to map to string for field type ${airtableType}`);
|
|
225
|
+
}
|
|
226
|
+
return nullableValue ?? '';
|
|
227
|
+
},
|
|
228
|
+
}];
|
|
229
|
+
})),
|
|
230
|
+
},
|
|
231
|
+
...booleanOrNull,
|
|
232
|
+
boolean: {
|
|
233
|
+
...Object.fromEntries(Object.entries(booleanOrNull['boolean | null']).map(([airtableType, nullablePair]) => {
|
|
234
|
+
return [airtableType, {
|
|
235
|
+
toAirtable: nullablePair.toAirtable,
|
|
236
|
+
fromAirtable: (value) => nullablePair.fromAirtable(value) ?? false,
|
|
237
|
+
}];
|
|
238
|
+
})),
|
|
239
|
+
},
|
|
240
|
+
...numberOrNull,
|
|
241
|
+
number: {
|
|
242
|
+
...Object.fromEntries(Object.entries(numberOrNull['number | null']).map(([airtableType, nullablePair]) => {
|
|
243
|
+
return [airtableType, {
|
|
244
|
+
toAirtable: nullablePair.toAirtable,
|
|
245
|
+
fromAirtable: (value) => {
|
|
246
|
+
const nullableValue = nullablePair.fromAirtable(value);
|
|
247
|
+
if (nullableValue === null) {
|
|
248
|
+
throw new Error(`[airtable-ts] Expected non-null or non-empty value to map to number for field type ${airtableType}`);
|
|
249
|
+
}
|
|
250
|
+
return nullableValue;
|
|
251
|
+
},
|
|
252
|
+
}];
|
|
253
|
+
})),
|
|
254
|
+
},
|
|
255
|
+
...stringArrayOrNull,
|
|
256
|
+
'string[]': {
|
|
257
|
+
...Object.fromEntries(Object.entries(stringArrayOrNull['string[] | null']).map(([airtableType, nullablePair]) => {
|
|
258
|
+
return [airtableType, {
|
|
259
|
+
toAirtable: nullablePair.toAirtable,
|
|
260
|
+
fromAirtable: (value) => nullablePair.fromAirtable(value) ?? [],
|
|
261
|
+
}];
|
|
262
|
+
})),
|
|
263
|
+
},
|
|
264
|
+
};
|
|
@@ -4,6 +4,20 @@ exports.visibleForTesting = exports.mapRecordToAirtable = exports.mapRecordFromA
|
|
|
4
4
|
const fieldMappers_1 = require("./fieldMappers");
|
|
5
5
|
const nameMapper_1 = require("./nameMapper");
|
|
6
6
|
const typeUtils_1 = require("./typeUtils");
|
|
7
|
+
const getMapper = (tsType, airtableType) => {
|
|
8
|
+
const tsMapper = fieldMappers_1.fieldMappers[tsType];
|
|
9
|
+
if (!tsMapper) {
|
|
10
|
+
throw new Error(`[airtable-ts] No mappers for ts type ${tsType}`);
|
|
11
|
+
}
|
|
12
|
+
if (tsMapper[airtableType]) {
|
|
13
|
+
return tsMapper[airtableType];
|
|
14
|
+
}
|
|
15
|
+
if (tsMapper.unknown) {
|
|
16
|
+
console.warn(`[airtable-ts] Unknown airtable type ${airtableType}. This is not fully supported and exact mapping behaviour may change in a future release.`);
|
|
17
|
+
return tsMapper.unknown;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`[airtable-ts] Expected to be able to map to ts type ${tsType}, but got airtable type ${airtableType} which can't.`);
|
|
20
|
+
};
|
|
7
21
|
/**
|
|
8
22
|
* This function coerces an Airtable record to a TypeScript object, given an
|
|
9
23
|
* object type definition. It will do this using the field mappers on each
|
|
@@ -28,18 +42,10 @@ const mapRecordTypeAirtableToTs = (tsTypes, record) => {
|
|
|
28
42
|
throw new Error(`[airtable-ts] Failed to get Airtable field ${fieldNameOrId}`);
|
|
29
43
|
}
|
|
30
44
|
const value = record.fields[fieldDefinition.name];
|
|
31
|
-
const tsMapper = fieldMappers_1.fieldMappers[tsType];
|
|
32
|
-
if (!tsMapper) {
|
|
33
|
-
throw new Error(`[airtable-ts] No mappers for ts type ${tsType}`);
|
|
34
|
-
}
|
|
35
|
-
const specificMapper = tsMapper[fieldDefinition.type]?.fromAirtable;
|
|
36
|
-
if (!specificMapper) {
|
|
37
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
38
|
-
throw new Error(`[airtable-ts] Expected field ${record._table.name}.${fieldNameOrId} to be able to map to ts type ${tsType}, but got airtable type ${fieldDefinition.type} which can't.`);
|
|
39
|
-
}
|
|
40
45
|
try {
|
|
46
|
+
const { fromAirtable } = getMapper(tsType, fieldDefinition.type);
|
|
41
47
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
item[fieldNameOrId] =
|
|
48
|
+
item[fieldNameOrId] = fromAirtable(value);
|
|
43
49
|
}
|
|
44
50
|
catch (error) {
|
|
45
51
|
if (error instanceof Error) {
|
|
@@ -88,16 +94,20 @@ const mapRecordTypeTsToAirtable = (tsTypes, tsRecord, airtableTable) => {
|
|
|
88
94
|
if (!fieldDefinition) {
|
|
89
95
|
throw new Error(`[airtable-ts] Failed to get Airtable field ${fieldNameOrId}`);
|
|
90
96
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
try {
|
|
98
|
+
const { toAirtable } = getMapper(tsType, fieldDefinition.type);
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
item[fieldNameOrId] = toAirtable(value);
|
|
94
101
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error instanceof Error) {
|
|
104
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
105
|
+
error.message = `Failed to map field ${airtableTable.name}.${fieldNameOrId}: ${error.message}`;
|
|
106
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
107
|
+
error.stack = `Error: Failed to map field ${airtableTable.name}.${fieldNameOrId}: ${error.stack?.startsWith('Error: ') ? error.stack.slice('Error: '.length) : error.stack}`;
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
98
110
|
}
|
|
99
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
-
item[fieldNameOrId] = specificMapper(value);
|
|
101
111
|
});
|
|
102
112
|
return Object.assign(item, { id: tsRecord.id });
|
|
103
113
|
};
|
|
@@ -3,7 +3,13 @@ type NonNullToString<T> = T extends string ? 'string' : T extends number ? 'numb
|
|
|
3
3
|
export type ToTsTypeString<T> = null extends T ? `${NonNullToString<T>} | null` : NonNullToString<T>;
|
|
4
4
|
export type FromTsTypeString<T> = T extends 'string' ? string : T extends 'string | null' ? string | null : T extends 'number' ? number : T extends 'number | null' ? number | null : T extends 'boolean' ? boolean : T extends 'boolean | null' ? boolean | null : T extends 'string[]' ? string[] : T extends 'string[] | null' ? string[] | null : T extends 'number[]' ? number[] : T extends 'number[] | null' ? number[] | null : T extends 'boolean[]' ? boolean[] : T extends 'boolean[] | null' ? boolean[] | null : never;
|
|
5
5
|
export type AirtableTypeString = 'aiText' | 'autoNumber' | 'barcode' | 'button' | 'checkbox' | 'count' | 'createdBy' | 'createdTime' | 'currency' | 'date' | 'dateTime' | 'duration' | 'email' | 'externalSyncSource' | 'formula' | 'lastModifiedBy' | 'lastModifiedTime' | 'lookup' | 'multipleLookupValues' | 'multilineText' | 'multipleAttachments' | 'multipleCollaborators' | 'multipleRecordLinks' | 'multipleSelects' | 'number' | 'percent' | 'phoneNumber' | 'rating' | 'richText' | 'rollup' | 'singleCollaborator' | 'singleLineText' | 'singleSelect' | 'url';
|
|
6
|
-
export type FromAirtableTypeString<T extends AirtableTypeString> = null | (T extends 'url' | 'email' | 'phoneNumber' | 'singleLineText' | 'multilineText' | 'richText' | 'singleSelect' | 'externalSyncSource' | 'date' | 'dateTime' | 'createdTime' | 'lastModifiedTime' ? string : T extends 'multipleRecordLinks' | 'multipleSelects' ? string[] : T extends 'number' | 'rating' | 'duration' | 'currency' | 'percent' | 'count' | 'autoNumber' ? number : T extends 'checkbox' ? boolean : T extends 'lookup' | 'multipleLookupValues' | 'rollup' | 'formula' ? FromAirtableTypeString<any>[] : never);
|
|
6
|
+
export type FromAirtableTypeString<T extends AirtableTypeString | 'unknown'> = null | (T extends 'url' | 'email' | 'phoneNumber' | 'singleLineText' | 'multilineText' | 'richText' | 'singleSelect' | 'externalSyncSource' | 'date' | 'dateTime' | 'createdTime' | 'lastModifiedTime' ? string : T extends 'multipleRecordLinks' | 'multipleSelects' ? string[] : T extends 'number' | 'rating' | 'duration' | 'currency' | 'percent' | 'count' | 'autoNumber' ? number : T extends 'checkbox' ? boolean : T extends 'lookup' | 'multipleLookupValues' | 'rollup' | 'formula' ? FromAirtableTypeString<any>[] : T extends 'unknown' ? unknown : never);
|
|
7
|
+
interface TypeDef {
|
|
8
|
+
single: 'string' | 'number' | 'boolean';
|
|
9
|
+
array: boolean;
|
|
10
|
+
nullable: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const parseType: (t: TsTypeString) => TypeDef;
|
|
7
13
|
/**
|
|
8
14
|
* Verifies whether the given value is assignable to the given type
|
|
9
15
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.airtableFieldNameTsTypes = exports.matchesType = void 0;
|
|
3
|
+
exports.airtableFieldNameTsTypes = exports.matchesType = exports.parseType = void 0;
|
|
4
4
|
const parseType = (t) => {
|
|
5
5
|
if (t.endsWith('[] | null')) {
|
|
6
6
|
return {
|
|
@@ -29,6 +29,7 @@ const parseType = (t) => {
|
|
|
29
29
|
nullable: false,
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
|
+
exports.parseType = parseType;
|
|
32
33
|
/**
|
|
33
34
|
* Verifies whether the given value is assignable to the given type
|
|
34
35
|
*
|
|
@@ -42,7 +43,7 @@ const parseType = (t) => {
|
|
|
42
43
|
* @example true
|
|
43
44
|
*/
|
|
44
45
|
const matchesType = (value, tsType) => {
|
|
45
|
-
const expectedType = parseType(tsType);
|
|
46
|
+
const expectedType = (0, exports.parseType)(tsType);
|
|
46
47
|
if (expectedType.nullable && value === null) {
|
|
47
48
|
return true;
|
|
48
49
|
}
|