@yuuko1410/feishu-bitable 0.0.2 → 0.0.3
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 +464 -0
- package/lib/client.d.ts +8 -1
- package/lib/index.cjs +302 -126
- package/lib/index.d.ts +1 -1
- package/lib/index.js +302 -126
- package/lib/types.d.ts +6 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -96,7 +96,8 @@ function normalizeRecordFields(fields) {
|
|
|
96
96
|
}
|
|
97
97
|
function splitUpdateRecord(record) {
|
|
98
98
|
const { record_id: recordId, ...fields } = record;
|
|
99
|
-
|
|
99
|
+
const normalizedFields = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
100
|
+
return { recordId, fields: normalizedFields };
|
|
100
101
|
}
|
|
101
102
|
async function toBuffer(file) {
|
|
102
103
|
if (typeof file === "string") {
|
|
@@ -149,18 +150,44 @@ function isFileLike(value) {
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
// src/client.ts
|
|
153
|
+
var DEFAULT_LOGGER = {
|
|
154
|
+
info(message, meta) {
|
|
155
|
+
if (meta) {
|
|
156
|
+
console.info(`[feishu-bitable] ${message}`, meta);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
console.info(`[feishu-bitable] ${message}`);
|
|
160
|
+
},
|
|
161
|
+
warn(message, meta) {
|
|
162
|
+
if (meta) {
|
|
163
|
+
console.warn(`[feishu-bitable] ${message}`, meta);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
console.warn(`[feishu-bitable] ${message}`);
|
|
167
|
+
},
|
|
168
|
+
error(message, meta) {
|
|
169
|
+
if (meta) {
|
|
170
|
+
console.error(`[feishu-bitable] ${message}`, meta);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.error(`[feishu-bitable] ${message}`);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
152
177
|
class Bitable {
|
|
153
178
|
client;
|
|
154
179
|
defaultAppToken;
|
|
155
180
|
maxRetries;
|
|
156
181
|
retryDelayMs;
|
|
157
182
|
defaultConcurrency;
|
|
183
|
+
logger;
|
|
158
184
|
constructor(optionsOrToken, appId, appSecret) {
|
|
159
185
|
const options = this.resolveConstructorOptions(optionsOrToken, appId, appSecret);
|
|
160
186
|
this.defaultAppToken = options.defaultAppToken;
|
|
161
187
|
this.maxRetries = Math.max(1, options.maxRetries ?? 5);
|
|
162
188
|
this.retryDelayMs = Math.max(100, options.retryDelayMs ?? 1000);
|
|
163
189
|
this.defaultConcurrency = Math.max(1, options.defaultConcurrency ?? 1);
|
|
190
|
+
this.logger = options.logger === null ? null : options.logger ?? DEFAULT_LOGGER;
|
|
164
191
|
this.client = options.sdkClient ?? new lark.Client({
|
|
165
192
|
appId: options.appId,
|
|
166
193
|
appSecret: options.appSecret,
|
|
@@ -176,159 +203,251 @@ class Bitable {
|
|
|
176
203
|
});
|
|
177
204
|
}
|
|
178
205
|
async fetchAllRecords(tableId, options = {}, appToken) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
206
|
+
return this.runLogged("fetchAllRecords", {
|
|
207
|
+
tableId,
|
|
208
|
+
pageSize: options.pageSize ?? FEISHU_BATCH_LIMIT,
|
|
209
|
+
hasViewId: Boolean(options.viewId),
|
|
210
|
+
fieldCount: options.fieldNames?.length ?? 0
|
|
211
|
+
}, async () => {
|
|
212
|
+
const token = this.resolveAppToken(appToken);
|
|
213
|
+
const pageSize = Math.max(1, Math.min(options.pageSize ?? FEISHU_BATCH_LIMIT, FEISHU_BATCH_LIMIT));
|
|
214
|
+
const iterator = await this.client.bitable.v1.appTableRecord.searchWithIterator({
|
|
215
|
+
path: {
|
|
216
|
+
app_token: token,
|
|
217
|
+
table_id: tableId
|
|
218
|
+
},
|
|
219
|
+
params: {
|
|
220
|
+
page_size: pageSize,
|
|
221
|
+
user_id_type: "open_id"
|
|
222
|
+
},
|
|
223
|
+
data: {
|
|
224
|
+
view_id: options.viewId,
|
|
225
|
+
field_names: options.fieldNames,
|
|
226
|
+
filter: options.filter,
|
|
227
|
+
sort: options.sort,
|
|
228
|
+
automatic_fields: options.automaticFields
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
const allRecords = [];
|
|
232
|
+
for await (const page of iterator) {
|
|
233
|
+
const items = page?.items ?? [];
|
|
234
|
+
allRecords.push(...items.map((item) => normalizeRecord(item.record_id, item.fields, options.normalizeFields !== false)));
|
|
196
235
|
}
|
|
236
|
+
this.logInfo("fetchAllRecords completed", {
|
|
237
|
+
tableId,
|
|
238
|
+
recordCount: allRecords.length
|
|
239
|
+
});
|
|
240
|
+
return allRecords;
|
|
197
241
|
});
|
|
198
|
-
const allRecords = [];
|
|
199
|
-
for await (const page of iterator) {
|
|
200
|
-
const items = page?.items ?? [];
|
|
201
|
-
allRecords.push(...items.map((item) => normalizeRecord(item.record_id, item.fields, options.normalizeFields !== false)));
|
|
202
|
-
}
|
|
203
|
-
return allRecords;
|
|
204
242
|
}
|
|
205
243
|
async insertList(tableId, records, options = {}) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
records: chunk.map((fields) => ({ fields }))
|
|
244
|
+
return this.runLogged("insertList", {
|
|
245
|
+
tableId,
|
|
246
|
+
recordCount: records.length,
|
|
247
|
+
chunkSize: options.chunkSize ?? FEISHU_BATCH_LIMIT,
|
|
248
|
+
concurrency: options.concurrency ?? this.defaultConcurrency
|
|
249
|
+
}, async () => {
|
|
250
|
+
if (records.length === 0) {
|
|
251
|
+
return [];
|
|
215
252
|
}
|
|
216
|
-
|
|
253
|
+
const token = this.resolveAppToken(options.appToken);
|
|
254
|
+
const chunks = chunkArray(records, options.chunkSize ?? FEISHU_BATCH_LIMIT);
|
|
255
|
+
const responses = await runWithConcurrency(chunks, options.concurrency ?? this.defaultConcurrency, async (chunk) => this.withRetry("insert records", async () => assertFeishuResponse(await this.client.bitable.v1.appTableRecord.batchCreate({
|
|
256
|
+
path: { app_token: token, table_id: tableId },
|
|
257
|
+
data: {
|
|
258
|
+
records: chunk.map((fields) => ({ fields }))
|
|
259
|
+
}
|
|
260
|
+
}), "insert records")));
|
|
261
|
+
this.logInfo("insertList completed", {
|
|
262
|
+
tableId,
|
|
263
|
+
chunkCount: chunks.length
|
|
264
|
+
});
|
|
265
|
+
return responses;
|
|
266
|
+
});
|
|
217
267
|
}
|
|
218
268
|
async batchUpdateRecords(payload) {
|
|
219
|
-
return this.
|
|
269
|
+
return this.runLogged("batchUpdateRecords", {
|
|
270
|
+
tableId: payload.path.table_id,
|
|
271
|
+
recordCount: payload.data.records.length
|
|
272
|
+
}, async () => this.executeBatchUpdateRecords(payload));
|
|
220
273
|
}
|
|
221
274
|
async updateRecords(tableId, records, options = {}) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
275
|
+
return this.runLogged("updateRecords", {
|
|
276
|
+
tableId,
|
|
277
|
+
inputRecordCount: records.length,
|
|
278
|
+
chunkSize: options.chunkSize ?? FEISHU_BATCH_LIMIT,
|
|
279
|
+
concurrency: options.concurrency ?? this.defaultConcurrency
|
|
280
|
+
}, async () => {
|
|
281
|
+
if (records.length === 0) {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
const token = this.resolveAppToken(options.appToken);
|
|
285
|
+
const chunks = chunkArray(records, options.chunkSize ?? FEISHU_BATCH_LIMIT);
|
|
286
|
+
const responses = await runWithConcurrency(chunks, options.concurrency ?? this.defaultConcurrency, async (chunk, index) => {
|
|
287
|
+
const batchRecords = chunk.map((record) => {
|
|
288
|
+
const { recordId, fields } = splitUpdateRecord(record);
|
|
289
|
+
if (Object.keys(fields).length === 0) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
record_id: recordId,
|
|
294
|
+
fields
|
|
295
|
+
};
|
|
296
|
+
}).filter((record) => Boolean(record));
|
|
297
|
+
if (batchRecords.length === 0) {
|
|
298
|
+
this.logInfo("updateRecords skipped empty chunk", {
|
|
299
|
+
tableId,
|
|
300
|
+
chunkIndex: index,
|
|
301
|
+
inputChunkSize: chunk.length
|
|
302
|
+
});
|
|
303
|
+
return {
|
|
304
|
+
code: 0,
|
|
305
|
+
msg: "ok",
|
|
306
|
+
data: {
|
|
307
|
+
records: []
|
|
308
|
+
}
|
|
309
|
+
};
|
|
241
310
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
311
|
+
const payload = {
|
|
312
|
+
path: {
|
|
313
|
+
app_token: token,
|
|
314
|
+
table_id: tableId
|
|
315
|
+
},
|
|
316
|
+
data: {
|
|
317
|
+
records: batchRecords
|
|
318
|
+
}
|
|
247
319
|
};
|
|
248
|
-
|
|
249
|
-
|
|
320
|
+
if (options.userIdType || options.ignoreConsistencyCheck !== undefined) {
|
|
321
|
+
payload.params = {
|
|
322
|
+
user_id_type: options.userIdType,
|
|
323
|
+
ignore_consistency_check: options.ignoreConsistencyCheck
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return this.executeBatchUpdateRecords(payload);
|
|
327
|
+
});
|
|
328
|
+
this.logInfo("updateRecords completed", {
|
|
329
|
+
tableId,
|
|
330
|
+
chunkCount: chunks.length
|
|
331
|
+
});
|
|
332
|
+
return responses;
|
|
250
333
|
});
|
|
251
334
|
}
|
|
252
335
|
async deleteList(tableId, recordIds, options = {}) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
records: chunk
|
|
336
|
+
return this.runLogged("deleteList", {
|
|
337
|
+
tableId,
|
|
338
|
+
recordCount: recordIds.length,
|
|
339
|
+
chunkSize: options.chunkSize ?? FEISHU_BATCH_LIMIT,
|
|
340
|
+
concurrency: options.concurrency ?? this.defaultConcurrency
|
|
341
|
+
}, async () => {
|
|
342
|
+
if (recordIds.length === 0) {
|
|
343
|
+
return [];
|
|
262
344
|
}
|
|
263
|
-
|
|
345
|
+
const token = this.resolveAppToken(options.appToken);
|
|
346
|
+
const chunks = chunkArray(recordIds, options.chunkSize ?? FEISHU_BATCH_LIMIT);
|
|
347
|
+
const responses = await runWithConcurrency(chunks, options.concurrency ?? this.defaultConcurrency, async (chunk) => this.withRetry("delete records", async () => assertFeishuResponse(await this.client.bitable.v1.appTableRecord.batchDelete({
|
|
348
|
+
path: { app_token: token, table_id: tableId },
|
|
349
|
+
data: {
|
|
350
|
+
records: chunk
|
|
351
|
+
}
|
|
352
|
+
}), "delete records")));
|
|
353
|
+
this.logInfo("deleteList completed", {
|
|
354
|
+
tableId,
|
|
355
|
+
chunkCount: chunks.length
|
|
356
|
+
});
|
|
357
|
+
return responses;
|
|
358
|
+
});
|
|
264
359
|
}
|
|
265
360
|
async uploadFile(options) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
361
|
+
return this.runLogged("uploadFile", {
|
|
362
|
+
parentType: options.parentType,
|
|
363
|
+
parentNode: options.parentNode,
|
|
364
|
+
hasExtra: Boolean(options.extra)
|
|
365
|
+
}, async () => {
|
|
366
|
+
const buffer = await toBuffer(options.file);
|
|
367
|
+
const fileName = options.fileName ?? inferFileName(options.file);
|
|
368
|
+
this.logInfo("uploadFile resolved input", {
|
|
369
|
+
fileName,
|
|
370
|
+
size: buffer.byteLength,
|
|
371
|
+
mode: buffer.byteLength <= FEISHU_SIMPLE_UPLOAD_LIMIT ? "simple" : "multipart"
|
|
372
|
+
});
|
|
373
|
+
if (buffer.byteLength <= FEISHU_SIMPLE_UPLOAD_LIMIT) {
|
|
374
|
+
return await this.withRetry("upload file", async () => this.client.drive.v1.media.uploadAll({
|
|
375
|
+
data: {
|
|
376
|
+
file_name: fileName,
|
|
377
|
+
parent_type: options.parentType,
|
|
378
|
+
parent_node: options.parentNode,
|
|
379
|
+
size: buffer.byteLength,
|
|
380
|
+
extra: options.extra,
|
|
381
|
+
file: buffer
|
|
382
|
+
}
|
|
383
|
+
})) ?? {};
|
|
384
|
+
}
|
|
385
|
+
const prepare = assertFeishuResponse(await this.withRetry("prepare multipart upload", async () => this.client.drive.v1.media.uploadPrepare({
|
|
270
386
|
data: {
|
|
271
387
|
file_name: fileName,
|
|
272
388
|
parent_type: options.parentType,
|
|
273
389
|
parent_node: options.parentNode,
|
|
274
|
-
size: buffer.byteLength
|
|
275
|
-
extra: options.extra,
|
|
276
|
-
file: buffer
|
|
390
|
+
size: buffer.byteLength
|
|
277
391
|
}
|
|
278
|
-
}))
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
data
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
392
|
+
})), "prepare multipart upload");
|
|
393
|
+
const uploadId = prepare.data?.upload_id;
|
|
394
|
+
const blockSize = prepare.data?.block_size;
|
|
395
|
+
const blockNum = prepare.data?.block_num;
|
|
396
|
+
if (!uploadId || !blockSize || !blockNum) {
|
|
397
|
+
throw new FeishuBitableError("prepare multipart upload failed: missing upload metadata", {
|
|
398
|
+
details: prepare
|
|
399
|
+
});
|
|
286
400
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
await this.withRetry(
|
|
401
|
+
for (let index = 0;index < blockNum; index++) {
|
|
402
|
+
const start = index * blockSize;
|
|
403
|
+
const end = Math.min(start + blockSize, buffer.byteLength);
|
|
404
|
+
const chunk = buffer.subarray(start, end);
|
|
405
|
+
await this.withRetry(`upload file chunk ${index + 1}/${blockNum}`, async () => this.client.drive.v1.media.uploadPart({
|
|
406
|
+
data: {
|
|
407
|
+
upload_id: uploadId,
|
|
408
|
+
seq: index,
|
|
409
|
+
size: chunk.byteLength,
|
|
410
|
+
file: chunk
|
|
411
|
+
}
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
const finish = assertFeishuResponse(await this.withRetry("finish multipart upload", async () => this.client.drive.v1.media.uploadFinish({
|
|
301
415
|
data: {
|
|
302
416
|
upload_id: uploadId,
|
|
303
|
-
|
|
304
|
-
size: chunk.byteLength,
|
|
305
|
-
file: chunk
|
|
417
|
+
block_num: blockNum
|
|
306
418
|
}
|
|
307
|
-
}));
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
block_num: blockNum
|
|
313
|
-
}
|
|
314
|
-
})), "finish multipart upload");
|
|
315
|
-
return {
|
|
316
|
-
file_token: finish.data?.file_token
|
|
317
|
-
};
|
|
419
|
+
})), "finish multipart upload");
|
|
420
|
+
return {
|
|
421
|
+
file_token: finish.data?.file_token
|
|
422
|
+
};
|
|
423
|
+
});
|
|
318
424
|
}
|
|
319
425
|
async downloadFile(fileToken, extra) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
426
|
+
return this.runLogged("downloadFile", {
|
|
427
|
+
fileToken,
|
|
428
|
+
hasExtra: Boolean(extra)
|
|
429
|
+
}, async () => {
|
|
430
|
+
const response = await this.withRetry("download file", async () => this.client.drive.v1.media.download({
|
|
431
|
+
path: {
|
|
432
|
+
file_token: fileToken
|
|
433
|
+
},
|
|
434
|
+
params: {
|
|
435
|
+
extra
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
const buffer = await readableToBuffer(response.getReadableStream());
|
|
439
|
+
this.logInfo("downloadFile completed", {
|
|
440
|
+
fileToken,
|
|
441
|
+
size: buffer.byteLength
|
|
442
|
+
});
|
|
443
|
+
return buffer;
|
|
444
|
+
});
|
|
329
445
|
}
|
|
330
446
|
async downLoadFile(fileToken, extra) {
|
|
331
|
-
return this.
|
|
447
|
+
return this.runLogged("downLoadFile", {
|
|
448
|
+
fileToken,
|
|
449
|
+
hasExtra: Boolean(extra)
|
|
450
|
+
}, async () => this.downloadFile(fileToken, extra));
|
|
332
451
|
}
|
|
333
452
|
resolveConstructorOptions(optionsOrToken, appIdArg, appSecretArg) {
|
|
334
453
|
const objectMode = typeof optionsOrToken === "object" && optionsOrToken !== null && !Array.isArray(optionsOrToken);
|
|
@@ -356,6 +475,9 @@ class Bitable {
|
|
|
356
475
|
}
|
|
357
476
|
return token;
|
|
358
477
|
}
|
|
478
|
+
async executeBatchUpdateRecords(payload) {
|
|
479
|
+
return this.withRetry("batch update records", async () => assertFeishuResponse(await this.client.bitable.v1.appTableRecord.batchUpdate(payload), "batch update records"));
|
|
480
|
+
}
|
|
359
481
|
async withRetry(label, task) {
|
|
360
482
|
let lastError;
|
|
361
483
|
for (let attempt = 1;attempt <= this.maxRetries; attempt++) {
|
|
@@ -363,16 +485,70 @@ class Bitable {
|
|
|
363
485
|
return await task();
|
|
364
486
|
} catch (error) {
|
|
365
487
|
lastError = error;
|
|
488
|
+
const meta = {
|
|
489
|
+
label,
|
|
490
|
+
attempt,
|
|
491
|
+
maxRetries: this.maxRetries,
|
|
492
|
+
retryDelayMs: this.retryDelayMs,
|
|
493
|
+
...this.getErrorMeta(error)
|
|
494
|
+
};
|
|
366
495
|
if (attempt === this.maxRetries) {
|
|
496
|
+
this.logError("request exhausted retries", meta);
|
|
367
497
|
break;
|
|
368
498
|
}
|
|
499
|
+
this.logWarn("request attempt failed, retrying", meta);
|
|
369
500
|
await sleep(this.retryDelayMs * attempt);
|
|
370
501
|
}
|
|
371
502
|
}
|
|
372
|
-
|
|
373
|
-
|
|
503
|
+
const causeMessage = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
504
|
+
throw new FeishuBitableError(`${label} failed after ${this.maxRetries} attempts${causeMessage}`, {
|
|
505
|
+
cause: lastError,
|
|
506
|
+
code: lastError instanceof FeishuBitableError ? lastError.code : undefined,
|
|
507
|
+
details: lastError instanceof FeishuBitableError ? lastError.details : undefined
|
|
374
508
|
});
|
|
375
509
|
}
|
|
510
|
+
async runLogged(action, meta, task) {
|
|
511
|
+
this.logInfo(`${action} started`, meta);
|
|
512
|
+
try {
|
|
513
|
+
const result = await task();
|
|
514
|
+
this.logInfo(`${action} succeeded`, meta);
|
|
515
|
+
return result;
|
|
516
|
+
} catch (error) {
|
|
517
|
+
this.logError(`${action} failed`, {
|
|
518
|
+
...meta,
|
|
519
|
+
...this.getErrorMeta(error)
|
|
520
|
+
});
|
|
521
|
+
throw error;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
logInfo(message, meta) {
|
|
525
|
+
this.logger?.info(message, meta);
|
|
526
|
+
}
|
|
527
|
+
logWarn(message, meta) {
|
|
528
|
+
this.logger?.warn?.(message, meta);
|
|
529
|
+
}
|
|
530
|
+
logError(message, meta) {
|
|
531
|
+
this.logger?.error?.(message, meta);
|
|
532
|
+
}
|
|
533
|
+
getErrorMeta(error) {
|
|
534
|
+
if (error instanceof FeishuBitableError) {
|
|
535
|
+
return {
|
|
536
|
+
errorName: error.name,
|
|
537
|
+
errorMessage: error.message,
|
|
538
|
+
errorCode: error.code,
|
|
539
|
+
errorDetails: error.details
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (error instanceof Error) {
|
|
543
|
+
return {
|
|
544
|
+
errorName: error.name,
|
|
545
|
+
errorMessage: error.message
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
errorMessage: String(error)
|
|
550
|
+
};
|
|
551
|
+
}
|
|
376
552
|
}
|
|
377
553
|
|
|
378
554
|
// src/index.ts
|
package/lib/types.d.ts
CHANGED
|
@@ -62,6 +62,11 @@ export type BitableFilterGroup = {
|
|
|
62
62
|
};
|
|
63
63
|
export type MediaParentType = "doc_image" | "docx_image" | "sheet_image" | "doc_file" | "docx_file" | "sheet_file" | "vc_virtual_background" | "bitable_image" | "bitable_file" | "moments" | "ccm_import_open" | "calendar" | "base_global" | "lark_ai_media_analysis";
|
|
64
64
|
export type UploadableFile = string | Buffer | Uint8Array | ArrayBuffer | Blob | BunFileLike;
|
|
65
|
+
export type BitableLogger = {
|
|
66
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
67
|
+
warn?: (message: string, meta?: Record<string, unknown>) => void;
|
|
68
|
+
error?: (message: string, meta?: Record<string, unknown>) => void;
|
|
69
|
+
};
|
|
65
70
|
export type BunFileLike = {
|
|
66
71
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
67
72
|
name?: string;
|
|
@@ -76,6 +81,7 @@ export interface BitableConstructorOptions {
|
|
|
76
81
|
retryDelayMs?: number;
|
|
77
82
|
defaultConcurrency?: number;
|
|
78
83
|
sdkClient?: lark.Client;
|
|
84
|
+
logger?: BitableLogger | null;
|
|
79
85
|
}
|
|
80
86
|
export interface FetchAllRecordsOptions {
|
|
81
87
|
viewId?: string;
|