@oddington/sdk 0.1.11 → 0.1.13
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 +173 -29
- package/dist/auth-provider.js +3909 -0
- package/dist/browser.js +1295 -642
- package/dist/index.js +62 -24399
- package/dist/types/packages/sdk/src/auth-types.d.ts +40 -0
- package/dist/types/packages/sdk/src/public.d.ts +14 -1
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +56 -0
- package/dist/types/shared/app-routine-contract.d.ts +43 -0
- package/dist/types/shared/files-contract.d.ts +8 -0
- package/dist/types/shared/inbox-contract.d.ts +1 -0
- package/package.json +2 -1
package/dist/browser.js
CHANGED
|
@@ -131,8 +131,8 @@ function validateBlock(value) {
|
|
|
131
131
|
});
|
|
132
132
|
return (!records.length || new Set(records.map((r) => r.ref)).size !== records.length) && fail("Empty or duplicate records"), { kind: "records", label, records, ...target, ...expanded };
|
|
133
133
|
}
|
|
134
|
-
let
|
|
135
|
-
return (!
|
|
134
|
+
let files3 = array(b.files, "files").map(validateInboxFile);
|
|
135
|
+
return (!files3.length || new Set(files3.map((file2) => file2.id)).size !== files3.length) && fail("Empty or duplicate files"), { kind: "files", label, files: files3, ...expanded };
|
|
136
136
|
}
|
|
137
137
|
function richTextBlock(value) {
|
|
138
138
|
return validateBlock({ ...value, kind: "rich-text" });
|
|
@@ -161,8 +161,8 @@ function validateAuthoredSource(value) {
|
|
|
161
161
|
keys(option, ["value", "label", "description", "note"], "choice");
|
|
162
162
|
let note;
|
|
163
163
|
if (option.note !== void 0) {
|
|
164
|
-
let
|
|
165
|
-
keys(
|
|
164
|
+
let declaration2 = object(option.note, "choice note");
|
|
165
|
+
keys(declaration2, ["placeholder"], "choice note"), note = { placeholder: text(declaration2.placeholder, "note placeholder") };
|
|
166
166
|
}
|
|
167
167
|
return {
|
|
168
168
|
value: id(option.value, "choice value"),
|
|
@@ -208,8 +208,8 @@ function validateRecordSource(value) {
|
|
|
208
208
|
let suggestions = array(source.suggestions ?? [], "suggestions").map((value2) => {
|
|
209
209
|
let entry = object(value2, "suggestion");
|
|
210
210
|
keys(entry, ["record", "reason"], "suggestion");
|
|
211
|
-
let
|
|
212
|
-
return
|
|
211
|
+
let record3 = validateRecordReference(entry.record);
|
|
212
|
+
return record3.repository !== repository && fail("Suggestion belongs to another repository"), { record: record3, ...entry.reason === void 0 ? {} : { reason: text(entry.reason, "suggestion reason") } };
|
|
213
213
|
});
|
|
214
214
|
return (suggestions.length > 8 || new Set(suggestions.map((entry) => entry.record.ref)).size !== suggestions.length) && fail("Record selection permits at most eight unique suggestions"), {
|
|
215
215
|
type: "records",
|
|
@@ -239,7 +239,11 @@ function handlerReference(value) {
|
|
|
239
239
|
}
|
|
240
240
|
function defineHandler(value) {
|
|
241
241
|
let source = object(value, "handler");
|
|
242
|
-
|
|
242
|
+
keys(source, ["id", "results", "run"], "handler");
|
|
243
|
+
let stable = (value2) => typeof value2 == "string" && /^[a-zA-Z0-9][a-zA-Z0-9_./-]{0,127}$/.test(value2);
|
|
244
|
+
stable(source.id) || fail("Handler ID must be stable");
|
|
245
|
+
let results = source.results === void 0 ? void 0 : distinctIds(source.results, "handler results", !1);
|
|
246
|
+
return results && (results.length > 64 || results.some((result2) => !stable(result2))) && fail("Handler results must be stable IDs"), typeof source.run != "function" && fail("Handler requires a run function"), Object.freeze({ id: value.id, ...results === void 0 ? {} : { results: Object.freeze(results.sort()) }, run: value.run });
|
|
243
247
|
}
|
|
244
248
|
function respond(handler, input) {
|
|
245
249
|
return defineHandler(handler), handlerReference({ id: handler.id, input });
|
|
@@ -266,7 +270,7 @@ function presentation(source) {
|
|
|
266
270
|
new Set(views.map((view) => view.id)).size !== views.length && fail("Duplicate view ID");
|
|
267
271
|
let body2 = source.body === void 0 ? [] : array(source.body, "body").map(validateBlock);
|
|
268
272
|
for (let block of body2) {
|
|
269
|
-
let targets = block.kind === "record" ? [block.view] : block.kind === "records" ? [block.view, ...block.records.map((
|
|
273
|
+
let targets = block.kind === "record" ? [block.view] : block.kind === "records" ? [block.view, ...block.records.map((record3) => record3.view)] : [];
|
|
270
274
|
for (let target of targets) target !== void 0 && !views.some((view) => view.id === target) && fail(`Unknown Block View target: ${target}`);
|
|
271
275
|
}
|
|
272
276
|
return { title: text(source.title, "title"), description: text(source.description, "description"), views, body: body2 };
|
|
@@ -375,331 +379,323 @@ function validateRecordRead(value) {
|
|
|
375
379
|
};
|
|
376
380
|
}
|
|
377
381
|
|
|
378
|
-
var
|
|
379
|
-
constructor(
|
|
380
|
-
super(
|
|
381
|
-
this.
|
|
382
|
-
this.
|
|
382
|
+
var FileOperationError = class extends Error {
|
|
383
|
+
constructor(code, status, retryable = !1) {
|
|
384
|
+
super(code);
|
|
385
|
+
this.code = code;
|
|
386
|
+
this.status = status;
|
|
387
|
+
this.retryable = retryable;
|
|
383
388
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
389
|
+
code;
|
|
390
|
+
status;
|
|
391
|
+
retryable;
|
|
392
|
+
}, FILE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, FILE_ORIGINAL_MAX_BYTES = 5 * 1024 ** 3;
|
|
393
|
+
function parseFileDescriptor(value) {
|
|
394
|
+
let invalid5 = () => {
|
|
395
|
+
throw new FileOperationError("unavailable", 503, !0);
|
|
396
|
+
};
|
|
397
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return invalid5();
|
|
398
|
+
let f = value;
|
|
399
|
+
if (Object.keys(f).sort().join(",") !== "details,enrichments,id,mediaType,name,path,sha256,size" || typeof f.id != "string" || !FILE_ID_PATTERN.test(f.id) || !safeFileName(f.name) || !safeFilePath(f.path) || !Number.isSafeInteger(f.size) || f.size < 0 || f.size > FILE_ORIGINAL_MAX_BYTES || typeof f.sha256 != "string" || !/^[0-9a-f]{64}$/.test(f.sha256) || !f.enrichments || typeof f.enrichments != "object" || Array.isArray(f.enrichments) || !f.details || typeof f.details != "object" || Array.isArray(f.details)) return invalid5();
|
|
400
|
+
let d = f.details;
|
|
401
|
+
if (d.kind === "other") {
|
|
402
|
+
if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid5();
|
|
403
|
+
} else if (d.kind === "csv") {
|
|
404
|
+
if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0) return invalid5();
|
|
405
|
+
} else if (d.kind === "pdf") {
|
|
406
|
+
if (f.mediaType !== "application/pdf" || Object.keys(d).some((key) => !["kind", "pages"].includes(key)) || "pages" in d && (!Number.isSafeInteger(d.pages) || d.pages < 1)) return invalid5();
|
|
407
|
+
} else return invalid5();
|
|
408
|
+
for (let [name, descriptor2] of Object.entries(f.enrichments)) {
|
|
409
|
+
if (!safeEnrichmentName(name)) return invalid5();
|
|
410
|
+
parseEnrichmentDescriptor(descriptor2);
|
|
400
411
|
}
|
|
401
|
-
return
|
|
402
|
-
}
|
|
403
|
-
function validateRecordUpdate(value) {
|
|
404
|
-
let input = validateRecordWrite(value);
|
|
405
|
-
if (input.records.some((record2) => !("expectedVersion" in record2)))
|
|
406
|
-
throw new InboxContractError("Record creation is unavailable for this operation");
|
|
407
|
-
return input;
|
|
412
|
+
return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid5() : f;
|
|
408
413
|
}
|
|
409
|
-
function
|
|
410
|
-
let
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
return structuredClone(input);
|
|
414
|
+
function parseEnrichmentDescriptor(value) {
|
|
415
|
+
let invalid5 = () => {
|
|
416
|
+
throw new FileOperationError("unavailable", 503, !0);
|
|
417
|
+
};
|
|
418
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return invalid5();
|
|
419
|
+
let d = value;
|
|
420
|
+
return Object.keys(d).sort().join(",") !== "mediaType,sha256,size" || !["application/octet-stream", "text/plain", "text/markdown", "application/json"].includes(d.mediaType) || !Number.isSafeInteger(d.size) || d.size < 0 || d.size > 16 * 1024 * 1024 || typeof d.sha256 != "string" || !/^[0-9a-f]{64}$/.test(d.sha256) ? invalid5() : d;
|
|
417
421
|
}
|
|
418
|
-
function
|
|
419
|
-
|
|
420
|
-
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record2) => record2?.ref)).size !== input.records.length || new TextEncoder().encode(JSON.stringify(input)).byteLength > RECORD_WRITE_INPUT_LIMIT)
|
|
421
|
-
throw new InboxContractError("Invalid record change batch");
|
|
422
|
-
let writes = [], deletes = [];
|
|
423
|
-
for (let record2 of input.records) {
|
|
424
|
-
if (!record2 || typeof record2 != "object" || Array.isArray(record2))
|
|
425
|
-
throw new InboxContractError("Invalid record change");
|
|
426
|
-
"set" in record2 || "fields" in record2 ? writes.push(record2) : deletes.push(record2);
|
|
427
|
-
}
|
|
428
|
-
return writes.length && validateRecordWrite({ repository: input.repository, records: writes }), deletes.length && validateRecordDelete({ repository: input.repository, records: deletes }), structuredClone(input);
|
|
422
|
+
function safeEnrichmentName(value) {
|
|
423
|
+
return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
|
|
429
424
|
}
|
|
430
|
-
function
|
|
431
|
-
let
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
425
|
+
function parseFileEnrichMetadata(value, maxBytes) {
|
|
426
|
+
let invalid5 = () => {
|
|
427
|
+
throw new FileOperationError("invalid_input", 400);
|
|
428
|
+
};
|
|
429
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return invalid5();
|
|
430
|
+
let d = value;
|
|
431
|
+
if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key)) || !safeEnrichmentName(d.name) || !Number.isSafeInteger(d.size) || d.size < 0 || d.mediaType !== void 0 && (typeof d.mediaType != "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType)) || d.replace !== void 0 && (typeof d.replace != "string" || !/^[0-9a-f]{64}$/.test(d.replace))) return invalid5();
|
|
432
|
+
if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
433
|
+
return d;
|
|
435
434
|
}
|
|
436
|
-
function
|
|
437
|
-
return value === value.trim() && value
|
|
435
|
+
function safeFileName(value) {
|
|
436
|
+
return typeof value == "string" && value.length > 0 && value.length <= 255 && value === value.trim() && value !== "." && value !== ".." && !/[\u0000-\u001f\u007f-\u009f/\\:*?"<>|]/u.test(value) && !/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/.test(value) && !/[. ]$/.test(value) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value);
|
|
438
437
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
|
|
438
|
+
function safeFilePath(value) {
|
|
439
|
+
return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
|
|
442
440
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
441
|
+
function parseFileCreateMetadata(value, maxBytes) {
|
|
442
|
+
let invalid5 = () => {
|
|
443
|
+
throw new FileOperationError("invalid_input", 400);
|
|
444
|
+
};
|
|
445
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return invalid5();
|
|
446
|
+
let input = value;
|
|
447
|
+
if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "inputMediaType", "size", "requestKey"].includes(key))) return invalid5();
|
|
448
|
+
let path = input.path === void 0 ? "Files" : input.path;
|
|
449
|
+
if (!safeFileName(input.name) || !safeFilePath(path) || path.split("/")[0].toLowerCase() === ".os" || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || input.inputMediaType !== void 0 && input.inputMediaType !== "application/pdf" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid5();
|
|
450
|
+
if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
451
|
+
if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid5();
|
|
452
|
+
let csv;
|
|
453
|
+
if (input.csv !== void 0) {
|
|
454
|
+
if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid5();
|
|
455
|
+
let hints = input.csv;
|
|
456
|
+
if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid5();
|
|
457
|
+
let encoding = hints.encoding === void 0 ? "utf-8" : hints.encoding, delimiter = hints.delimiter === void 0 ? "," : hints.delimiter, headerRow = hints.headerRow === void 0 ? 1 : hints.headerRow;
|
|
458
|
+
if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid5();
|
|
459
|
+
csv = { encoding, delimiter, headerRow };
|
|
454
460
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
461
|
+
return {
|
|
462
|
+
name: input.name,
|
|
463
|
+
path,
|
|
464
|
+
...input.storage ? { storage: input.storage } : {},
|
|
465
|
+
...input.inputMediaType === "application/pdf" ? { inputMediaType: input.inputMediaType } : {},
|
|
466
|
+
...csv ? { csv } : {},
|
|
467
|
+
size: input.size,
|
|
468
|
+
requestKey: input.requestKey
|
|
469
|
+
};
|
|
458
470
|
}
|
|
459
471
|
|
|
460
|
-
var
|
|
461
|
-
constructor(
|
|
462
|
-
super(
|
|
463
|
-
this.
|
|
464
|
-
this.receipts = receipts;
|
|
465
|
-
this.name = "AirtableOperationError";
|
|
472
|
+
var AppRoutineContractError = class extends Error {
|
|
473
|
+
constructor(code) {
|
|
474
|
+
super(code);
|
|
475
|
+
this.code = code;
|
|
466
476
|
}
|
|
467
|
-
|
|
468
|
-
|
|
477
|
+
code;
|
|
478
|
+
}, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/, owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
|
|
479
|
+
throw new AppRoutineContractError("invalid_contract");
|
|
480
|
+
}, invalidValue = () => {
|
|
481
|
+
throw new AppRoutineContractError("invalid_value");
|
|
469
482
|
};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
|
|
483
|
+
function record(value) {
|
|
484
|
+
return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
485
|
+
}
|
|
486
|
+
function fields2(value, required, optional = []) {
|
|
487
|
+
return required.every((key) => owns(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
488
|
+
}
|
|
489
|
+
function denseArray(value) {
|
|
490
|
+
return Array.isArray(value) && Object.keys(value).length === value.length && Array.from({ length: value.length }, (_, index) => owns(value, String(index))).every(Boolean);
|
|
491
|
+
}
|
|
492
|
+
function parseAppRoutineContract(value) {
|
|
493
|
+
let properties = 0;
|
|
494
|
+
function schema2(value2, depth = 1) {
|
|
495
|
+
if (depth > 8 || !record(value2)) return invalidContract();
|
|
496
|
+
switch (value2.type) {
|
|
497
|
+
case "object": {
|
|
498
|
+
if (!fields2(value2, ["type", "properties", "additionalProperties"], ["required"]) || value2.additionalProperties !== !1 || !record(value2.properties)) return invalidContract();
|
|
499
|
+
let entries = Object.entries(value2.properties);
|
|
500
|
+
if (properties += entries.length, properties > 64 || entries.some(([key]) => !propertyName.test(key))) return invalidContract();
|
|
501
|
+
let required;
|
|
502
|
+
if (owns(value2, "required")) {
|
|
503
|
+
if (!denseArray(value2.required) || value2.required.some((key) => typeof key != "string" || !owns(value2.properties, key)) || new Set(value2.required).size !== value2.required.length) return invalidContract();
|
|
504
|
+
required = [...value2.required];
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
type: "object",
|
|
508
|
+
properties: Object.fromEntries(entries.map(([key, child]) => [key, schema2(child, depth + 1)])),
|
|
509
|
+
...required === void 0 ? {} : { required },
|
|
510
|
+
additionalProperties: !1
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
case "array":
|
|
514
|
+
return !fields2(value2, ["type", "items", "maxItems"]) || !Number.isSafeInteger(value2.maxItems) || value2.maxItems < 1 || value2.maxItems > 64 ? invalidContract() : { type: "array", items: schema2(value2.items, depth + 1), maxItems: value2.maxItems };
|
|
515
|
+
case "boolean":
|
|
516
|
+
case "file":
|
|
517
|
+
return fields2(value2, ["type"]) ? { type: value2.type } : invalidContract();
|
|
518
|
+
case "string":
|
|
519
|
+
return !fields2(value2, ["type"], ["maxLength"]) || owns(value2, "maxLength") && (!Number.isSafeInteger(value2.maxLength) || value2.maxLength < 0 || value2.maxLength > 4096) ? invalidContract() : { type: "string", ...owns(value2, "maxLength") ? { maxLength: value2.maxLength } : {} };
|
|
520
|
+
case "integer":
|
|
521
|
+
case "number": {
|
|
522
|
+
let validNumber = value2.type === "integer" ? Number.isSafeInteger : Number.isFinite;
|
|
523
|
+
return !fields2(value2, ["type"], ["minimum", "maximum"]) || owns(value2, "minimum") && !validNumber(value2.minimum) || owns(value2, "maximum") && !validNumber(value2.maximum) || owns(value2, "minimum") && owns(value2, "maximum") && value2.minimum > value2.maximum ? invalidContract() : {
|
|
524
|
+
type: value2.type,
|
|
525
|
+
...owns(value2, "minimum") ? { minimum: value2.minimum } : {},
|
|
526
|
+
...owns(value2, "maximum") ? { maximum: value2.maximum } : {}
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
default:
|
|
530
|
+
return invalidContract();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) || typeof value.routine != "string" || !routineName.test(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
|
|
534
|
+
let input = schema2(value.input), result2 = owns(value, "result") ? schema2(value.result) : void 0, ids = /* @__PURE__ */ new Set(), steps = value.steps.map((step) => !record(step) || !fields2(step, ["id"], ["summary"]) || typeof step.id != "string" || !stepId.test(step.id) || ids.has(step.id) ? invalidContract() : (ids.add(step.id), { id: step.id, ...owns(step, "summary") ? { summary: schema2(step.summary) } : {} }));
|
|
535
|
+
return { routine: value.routine, input, steps, ...result2 === void 0 ? {} : { result: result2 } };
|
|
480
536
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
537
|
+
function jsonData(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
538
|
+
if (!(value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value))) {
|
|
539
|
+
if (!record(value) && !denseArray(value) || ancestors.has(value)) return invalidValue();
|
|
540
|
+
ancestors.add(value);
|
|
541
|
+
for (let child of Object.values(value)) jsonData(child, ancestors);
|
|
542
|
+
ancestors.delete(value);
|
|
543
|
+
}
|
|
483
544
|
}
|
|
484
|
-
function
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
545
|
+
function validateRoutineSchemaValue(schema2, value) {
|
|
546
|
+
switch (schema2.type) {
|
|
547
|
+
case "object":
|
|
548
|
+
if (!record(value) || (schema2.required ?? []).some((key) => !owns(value, key)) || Object.keys(value).some((key) => !owns(schema2.properties, key))) return invalidValue();
|
|
549
|
+
for (let [key, child] of Object.entries(value)) validateRoutineSchemaValue(schema2.properties[key], child);
|
|
550
|
+
return;
|
|
551
|
+
case "array":
|
|
552
|
+
if (!denseArray(value) || value.length > schema2.maxItems) return invalidValue();
|
|
553
|
+
for (let child of value) validateRoutineSchemaValue(schema2.items, child);
|
|
554
|
+
return;
|
|
555
|
+
case "boolean":
|
|
556
|
+
return typeof value != "boolean" ? invalidValue() : void 0;
|
|
557
|
+
case "string":
|
|
558
|
+
return typeof value != "string" || Array.from(value).length > (schema2.maxLength ?? 4096) ? invalidValue() : void 0;
|
|
559
|
+
case "integer":
|
|
560
|
+
case "number":
|
|
561
|
+
return typeof value != "number" || !(schema2.type === "integer" ? Number.isSafeInteger(value) : Number.isFinite(value)) || schema2.minimum !== void 0 && value < schema2.minimum || schema2.maximum !== void 0 && value > schema2.maximum ? invalidValue() : void 0;
|
|
562
|
+
case "file":
|
|
563
|
+
try {
|
|
564
|
+
jsonData(value), parseFileDescriptor(value);
|
|
565
|
+
} catch {
|
|
566
|
+
return invalidValue();
|
|
567
|
+
}
|
|
491
568
|
}
|
|
492
569
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
570
|
+
|
|
571
|
+
var LIMIT = 256 * 1024, invalid = () => {
|
|
572
|
+
throw new AppRoutineContractError("invalid_value");
|
|
573
|
+
}, invalidContract2 = () => {
|
|
574
|
+
throw new AppRoutineContractError("invalid_contract");
|
|
575
|
+
}, empty = { type: "object", properties: {}, required: [], additionalProperties: !1 };
|
|
576
|
+
function freeze(value) {
|
|
577
|
+
if (value !== null && typeof value == "object") {
|
|
578
|
+
for (let child of Object.values(value)) freeze(child);
|
|
579
|
+
Object.freeze(value);
|
|
580
|
+
}
|
|
581
|
+
return value;
|
|
496
582
|
}
|
|
497
|
-
function
|
|
498
|
-
if (
|
|
499
|
-
if (!
|
|
500
|
-
let
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
)
|
|
583
|
+
function json(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
584
|
+
if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return;
|
|
585
|
+
if (!value || typeof value != "object" || ancestors.has(value)) return invalid();
|
|
586
|
+
let array2 = Array.isArray(value);
|
|
587
|
+
if (!array2 && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return invalid();
|
|
588
|
+
let keys4 = Reflect.ownKeys(value);
|
|
589
|
+
if (array2 && (keys4.length !== value.length + 1 || !Array.from({ length: value.length }, (_, index) => Object.hasOwn(value, index)).every(Boolean))) return invalid();
|
|
590
|
+
ancestors.add(value);
|
|
591
|
+
for (let key of keys4) {
|
|
592
|
+
if (array2 && key === "length") continue;
|
|
593
|
+
if (typeof key != "string") return invalid();
|
|
594
|
+
let descriptor2 = Object.getOwnPropertyDescriptor(value, key);
|
|
595
|
+
if (!descriptor2.enumerable || !Object.hasOwn(descriptor2, "value")) return invalid();
|
|
596
|
+
json(descriptor2.value, ancestors);
|
|
597
|
+
}
|
|
598
|
+
ancestors.delete(value);
|
|
505
599
|
}
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
tables: { list: async () => await call("tables.list", {}) },
|
|
512
|
-
records: {
|
|
513
|
-
list: async (input) => await call("records.list", input),
|
|
514
|
-
get: async (input) => await call("records.get", input),
|
|
515
|
-
create: async (input) => await call("records.create", input),
|
|
516
|
-
update: async (input) => await call("records.update", input),
|
|
517
|
-
delete: async (input) => await call("records.delete", input)
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
} };
|
|
600
|
+
function capture(schema2, value) {
|
|
601
|
+
json(value), validateRoutineSchemaValue(schema2, value);
|
|
602
|
+
let encoded = JSON.stringify(value);
|
|
603
|
+
return new TextEncoder().encode(encoded).byteLength > LIMIT ? invalid() : freeze(JSON.parse(encoded));
|
|
521
604
|
}
|
|
522
|
-
|
|
523
|
-
let { name, baseId, accessToken } = authority;
|
|
524
|
-
(!/^[a-z0-9][a-z0-9-]{0,62}$/.test(name) || !/^app[A-Za-z0-9]+$/.test(baseId) || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(accessToken)) && fail2("Airtable connection unavailable");
|
|
525
|
-
let input;
|
|
605
|
+
function declaration(value) {
|
|
526
606
|
try {
|
|
527
|
-
|
|
607
|
+
json(value);
|
|
528
608
|
} catch {
|
|
529
|
-
return
|
|
609
|
+
return invalidContract2();
|
|
530
610
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
611
|
+
return freeze(parseAppRoutineContract({ routine: "definition", input: value, steps: [{ id: "inspect" }] }).input);
|
|
612
|
+
}
|
|
613
|
+
var schema = {
|
|
614
|
+
object(properties) {
|
|
615
|
+
return declaration({ type: "object", properties, required: Object.keys(properties), additionalProperties: !1 });
|
|
616
|
+
},
|
|
617
|
+
array(items, options) {
|
|
618
|
+
return declaration({ type: "array", items, maxItems: options.maxItems });
|
|
619
|
+
},
|
|
620
|
+
string(options = {}) {
|
|
621
|
+
return declaration({ type: "string", ...options });
|
|
622
|
+
},
|
|
623
|
+
integer(options = {}) {
|
|
624
|
+
return declaration({ type: "integer", ...options });
|
|
625
|
+
},
|
|
626
|
+
number(options = {}) {
|
|
627
|
+
return declaration({ type: "number", ...options });
|
|
628
|
+
},
|
|
629
|
+
boolean() {
|
|
630
|
+
return declaration({ type: "boolean" });
|
|
631
|
+
}
|
|
632
|
+
}, file = () => declaration({ type: "file" }), definitions = /* @__PURE__ */ new WeakMap(), defineRoutine = (definition) => {
|
|
633
|
+
if (!definition || typeof definition != "object" || Object.getPrototypeOf(definition) !== Object.prototype || Reflect.ownKeys(definition).some((key) => typeof key != "string" || !["input", "steps", "result", "run"].includes(key) || !Object.getOwnPropertyDescriptor(definition, key).enumerable || !Object.hasOwn(Object.getOwnPropertyDescriptor(definition, key), "value")) || typeof definition.run != "function") return invalidContract2();
|
|
634
|
+
try {
|
|
635
|
+
for (let key of ["input", "steps", "result"])
|
|
636
|
+
definition[key] !== void 0 && json(definition[key]);
|
|
637
|
+
} catch {
|
|
638
|
+
return invalidContract2();
|
|
639
|
+
}
|
|
640
|
+
if (definition.steps !== void 0 && (!definition.steps || typeof definition.steps != "object" || Array.isArray(definition.steps))) return invalidContract2();
|
|
641
|
+
let steps = definition.steps === void 0 ? [] : Object.entries(definition.steps).map(([id2, summary]) => ({ id: id2, ...summary === null ? {} : { summary } })), parsed = parseAppRoutineContract({
|
|
642
|
+
routine: "definition",
|
|
643
|
+
input: definition.input === void 0 ? empty : definition.input,
|
|
644
|
+
steps: steps.length ? steps : [{ id: "inspect" }],
|
|
645
|
+
...definition.result === void 0 ? {} : { result: definition.result }
|
|
646
|
+
}), contract = freeze({
|
|
647
|
+
input: parsed.input,
|
|
648
|
+
steps: steps.length ? parsed.steps : [],
|
|
649
|
+
...parsed.result === void 0 ? {} : { result: parsed.result }
|
|
650
|
+
}), captured = Object.freeze({
|
|
651
|
+
...definition,
|
|
652
|
+
...definition.input === void 0 ? {} : { input: contract.input },
|
|
653
|
+
...definition.steps === void 0 ? {} : { steps: freeze(Object.fromEntries(contract.steps.map((step) => [step.id, step.summary ?? null]))) },
|
|
654
|
+
...definition.result === void 0 ? {} : { result: contract.result },
|
|
655
|
+
// Only this authored definition is callable; no generic runner is exported.
|
|
656
|
+
run: (input, os2) => executeRoutineDefinition(captured, input, os2)
|
|
538
657
|
});
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
let value
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
658
|
+
return definitions.set(captured, { contract, run: definition.run }), captured;
|
|
659
|
+
};
|
|
660
|
+
function inspectRoutineDefinition(definition) {
|
|
661
|
+
if (!definition || typeof definition != "object") return invalidContract2();
|
|
662
|
+
let registered = definitions.get(definition);
|
|
663
|
+
return registered ? registered.contract : invalidContract2();
|
|
664
|
+
}
|
|
665
|
+
function captureRoutineInput(definition, input) {
|
|
666
|
+
return capture(inspectRoutineDefinition(definition).input, input);
|
|
667
|
+
}
|
|
668
|
+
async function executeRoutineDefinition(definition, input, runtime) {
|
|
669
|
+
let captured = captureRoutineInput(definition, input), { contract, run } = definitions.get(definition), active2 = !0, broken = !1, reports = Promise.resolve(), os2 = Object.freeze({
|
|
670
|
+
...runtime,
|
|
671
|
+
run: freeze({ ...runtime.run }),
|
|
672
|
+
step(id2, ...summary) {
|
|
673
|
+
if (!active2 || broken || summary.length > 1)
|
|
674
|
+
return broken = !0, Promise.reject(new AppRoutineContractError("invalid_value"));
|
|
675
|
+
let value;
|
|
676
|
+
try {
|
|
677
|
+
let step = contract.steps.find((step2) => step2.id === id2);
|
|
678
|
+
if (!step) return invalid();
|
|
679
|
+
if (step.summary === void 0) {
|
|
680
|
+
if (summary.length) return invalid();
|
|
681
|
+
} else value = capture(step.summary, summary[0]);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
return broken = !0, Promise.reject(error);
|
|
684
|
+
}
|
|
685
|
+
let report = reports.then(() => broken ? invalid() : runtime.step(id2, value));
|
|
686
|
+
return reports = report.catch(() => {
|
|
687
|
+
broken = !0;
|
|
688
|
+
}), report;
|
|
561
689
|
}
|
|
690
|
+
});
|
|
691
|
+
try {
|
|
692
|
+
let result2 = await run(captured, os2);
|
|
693
|
+
return active2 = !1, await reports, broken ? invalid() : contract.result !== void 0 ? capture(contract.result, result2) : result2 !== void 0 ? invalid() : void 0;
|
|
694
|
+
} finally {
|
|
695
|
+
active2 = !1;
|
|
562
696
|
}
|
|
563
|
-
if (operation2 === "tables.list") {
|
|
564
|
-
keys2(input, []);
|
|
565
|
-
let tables = [], seen = /* @__PURE__ */ new Set(), offset;
|
|
566
|
-
do {
|
|
567
|
-
let query2 = new URLSearchParams(offset ? { offset } : {}), page = await request(`meta/bases/${baseId}/tables?${query2}`);
|
|
568
|
-
Array.isArray(page.tables) || fail2("Malformed Airtable tables");
|
|
569
|
-
for (let value of page.tables) {
|
|
570
|
-
let t = object2(value);
|
|
571
|
-
(!tableId(t.id) || !text2(t.name) || !text2(t.primaryFieldId) || !Array.isArray(t.fields) || tables.some((existing) => existing.id === t.id)) && fail2("Malformed Airtable table");
|
|
572
|
-
let metadata = t.fields.map((value2) => {
|
|
573
|
-
let f = object2(value2);
|
|
574
|
-
return (typeof f.id != "string" || !/^fld[A-Za-z0-9]+$/.test(f.id) || !text2(f.name) || !text2(f.type)) && fail2("Malformed Airtable field"), { id: f.id, name: f.name, type: f.type, ...f.options === void 0 ? {} : { options: serializable(f.options) } };
|
|
575
|
-
});
|
|
576
|
-
(!metadata.some((f) => f.id === t.primaryFieldId) || new Set(metadata.map((f) => f.id)).size !== metadata.length) && fail2("Malformed Airtable fields"), tables.push({ id: t.id, name: t.name, primaryFieldId: t.primaryFieldId, fields: metadata });
|
|
577
|
-
}
|
|
578
|
-
offset = nextOffset(page.offset, seen);
|
|
579
|
-
} while (offset);
|
|
580
|
-
return tables;
|
|
581
|
-
}
|
|
582
|
-
(!text2(input.table) || input.table === "." || input.table === "..") && fail2("Invalid Airtable table"), mutation && !tableId(input.table) && fail2("Airtable writes require a stable table ID");
|
|
583
|
-
let path = `${baseId}/${encodeURIComponent(input.table)}`;
|
|
584
|
-
if (operation2 === "records.get") {
|
|
585
|
-
keys2(input, ["table", "id"]), recordId(input.id) || fail2("Invalid Airtable record ID");
|
|
586
|
-
let result3 = record(await request(`${path}/${input.id}`));
|
|
587
|
-
return result3.id !== input.id && fail2("Malformed Airtable record"), result3;
|
|
588
|
-
}
|
|
589
|
-
if (operation2 === "records.list") {
|
|
590
|
-
keys2(input, ["table", "fields"], ["table"]), input.fields !== void 0 && (!Array.isArray(input.fields) || !input.fields.length || input.fields.some((f) => !text2(f)) || new Set(input.fields).size !== input.fields.length) && fail2("Invalid Airtable field selection");
|
|
591
|
-
let records2 = [], ids2 = /* @__PURE__ */ new Set(), seen = /* @__PURE__ */ new Set(), offset;
|
|
592
|
-
do {
|
|
593
|
-
let query2 = new URLSearchParams();
|
|
594
|
-
for (let field of input.fields ?? []) query2.append("fields[]", field);
|
|
595
|
-
offset && query2.set("offset", offset);
|
|
596
|
-
let page = await request(`${path}?${query2}`);
|
|
597
|
-
Array.isArray(page.records) || fail2("Malformed Airtable records");
|
|
598
|
-
for (let value of page.records) {
|
|
599
|
-
let r = record(value);
|
|
600
|
-
ids2.has(r.id) && fail2("Malformed Airtable pagination"), ids2.add(r.id), records2.push(r);
|
|
601
|
-
}
|
|
602
|
-
offset = nextOffset(page.offset, seen);
|
|
603
|
-
} while (offset);
|
|
604
|
-
return records2;
|
|
605
|
-
}
|
|
606
|
-
mutation || fail2("Unsupported Airtable operation"), keys2(input, ["table", "records"]), (!Array.isArray(input.records) || !input.records.length || input.records.length > 10) && fail2("Invalid Airtable batch");
|
|
607
|
-
let kind = operation2.slice(8), ids = /* @__PURE__ */ new Set(), records = input.records.map((value) => {
|
|
608
|
-
let r = object2(value);
|
|
609
|
-
if (keys2(r, kind === "create" ? ["fields"] : kind === "update" ? ["id", "expected", "set"] : ["id", "expected"]), kind === "create") return { fields: fields2(r.fields, !0) };
|
|
610
|
-
(!recordId(r.id) || ids.has(r.id)) && fail2("Invalid Airtable batch record ID"), ids.add(r.id);
|
|
611
|
-
let set = kind === "update" ? fields2(r.set, !0) : void 0;
|
|
612
|
-
return set && Object.values(set).some((value2) => value2 === null) && fail2("Airtable guard null cannot be an update value"), { id: r.id, expected: fields2(r.expected), ...set ? { set } : {} };
|
|
613
|
-
});
|
|
614
|
-
for (let r of records) {
|
|
615
|
-
if (!("id" in r)) continue;
|
|
616
|
-
let current2 = record(await request(`${path}/${r.id}`));
|
|
617
|
-
current2.id !== r.id && fail2("Malformed Airtable record");
|
|
618
|
-
for (let [key, value] of Object.entries(r.expected))
|
|
619
|
-
(value === null ? Object.hasOwn(current2.fields, key) : !Object.hasOwn(current2.fields, key) || !equal(current2.fields[key], value)) && fail2("Airtable reviewed values are stale");
|
|
620
|
-
}
|
|
621
|
-
await options.beforeMutation?.();
|
|
622
|
-
let query = new URLSearchParams();
|
|
623
|
-
if (kind === "delete") for (let id2 of ids) query.append("records[]", id2);
|
|
624
|
-
let result2 = await request(`${path}${kind === "delete" ? `?${query}` : ""}`, {
|
|
625
|
-
method: kind === "create" ? "POST" : kind === "update" ? "PATCH" : "DELETE",
|
|
626
|
-
...kind === "delete" ? {} : { body: JSON.stringify({ records: records.map((r) => "id" in r ? { id: r.id, fields: r.set } : r) }) }
|
|
627
|
-
}, !0), acknowledged = [], returned = [];
|
|
628
|
-
try {
|
|
629
|
-
if (!Array.isArray(result2.records)) throw new Error();
|
|
630
|
-
for (let value of result2.records) {
|
|
631
|
-
let r = object2(value);
|
|
632
|
-
if (!recordId(r.id) || acknowledged.includes(r.id) || kind !== "create" && !ids.has(r.id) || kind === "delete" && r.deleted !== !0) throw new Error();
|
|
633
|
-
acknowledged.push(r.id), kind !== "delete" && returned.push(record(r));
|
|
634
|
-
}
|
|
635
|
-
if (acknowledged.length !== records.length) throw new Error();
|
|
636
|
-
} catch {
|
|
637
|
-
throw new AirtableOperationError(
|
|
638
|
-
"Incomplete Airtable mutation acknowledgment",
|
|
639
|
-
acknowledged.length ? "partial" : "uncertain",
|
|
640
|
-
acknowledged.length ? [receipt(acknowledged)] : []
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
return { receipt: receipt(acknowledged), ...kind === "delete" ? {} : { records: returned } };
|
|
644
|
-
}
|
|
645
|
-
function nextOffset(value, seen) {
|
|
646
|
-
if (value !== void 0)
|
|
647
|
-
return (!text2(value) || seen.has(value)) && fail2("Malformed Airtable pagination"), seen.add(value), value;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
var MANAGED_CONNECTION_CAPABILITY_ENV = "ODDINGTON_MANAGED_CONNECTION_CAPABILITY";
|
|
651
|
-
function managedConnections(capability) {
|
|
652
|
-
let endpoint = new URL(capability.endpoint);
|
|
653
|
-
if (endpoint.protocol !== "http:" || endpoint.hostname !== "127.0.0.1" || !endpoint.port || endpoint.pathname !== "/" || endpoint.search || endpoint.hash || endpoint.username || endpoint.password || !/^[0-9a-f-]{36}$/.test(capability.token)) throw new AirtableOperationError("Airtable capability unavailable");
|
|
654
|
-
return createConnections(async (name, operation2, input) => {
|
|
655
|
-
let safeInput;
|
|
656
|
-
try {
|
|
657
|
-
safeInput = serializable(input);
|
|
658
|
-
} catch {
|
|
659
|
-
safeInput = null;
|
|
660
|
-
}
|
|
661
|
-
try {
|
|
662
|
-
let response = await fetch(new URL("airtable", endpoint), {
|
|
663
|
-
method: "POST",
|
|
664
|
-
redirect: "error",
|
|
665
|
-
headers: { authorization: `Bearer ${capability.token}`, "content-type": "application/json" },
|
|
666
|
-
body: JSON.stringify({ name, operation: operation2, input: safeInput }),
|
|
667
|
-
signal: AbortSignal.timeout(125e3)
|
|
668
|
-
}), value = await response.json();
|
|
669
|
-
if (!response.ok) throw new AirtableOperationError(
|
|
670
|
-
"Airtable operation failed; the execution retains its evidence",
|
|
671
|
-
value.effect === "partial" || value.effect === "uncertain" ? value.effect : "unapplied",
|
|
672
|
-
value.receipts ?? []
|
|
673
|
-
);
|
|
674
|
-
return value;
|
|
675
|
-
} catch (error) {
|
|
676
|
-
throw error instanceof AirtableOperationError ? error : new AirtableOperationError(
|
|
677
|
-
"Airtable parent acknowledgment unavailable",
|
|
678
|
-
["records.create", "records.update", "records.delete"].includes(operation2) ? "uncertain" : "unapplied"
|
|
679
|
-
);
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
697
|
}
|
|
683
698
|
|
|
684
|
-
var connections = /* @__PURE__ */ createConnections(async (name, operation2, input) => {
|
|
685
|
-
try {
|
|
686
|
-
let managed = process.env[MANAGED_CONNECTION_CAPABILITY_ENV];
|
|
687
|
-
if (managed) {
|
|
688
|
-
let client = managedConnections(JSON.parse(managed)).airtable(name);
|
|
689
|
-
return operation2 === "tables.list" ? await client.tables.list() : await client.records[operation2.slice(8)](input);
|
|
690
|
-
}
|
|
691
|
-
let raw = JSON.parse(process.env[ROUTINE_CONNECTION_CREDENTIALS_ENV] ?? "[]");
|
|
692
|
-
if (!Array.isArray(raw)) throw new Error();
|
|
693
|
-
let leases = raw.map((value) => parseConnectionLease(value));
|
|
694
|
-
if (leases.some((lease2) => lease2.provider !== "airtable") || new Set(leases.map((lease2) => lease2.name)).size !== leases.length) throw new Error();
|
|
695
|
-
let lease = leases.find((lease2) => lease2.name === name);
|
|
696
|
-
if (!lease || lease.provider !== "airtable") throw new Error();
|
|
697
|
-
return await executeAirtableOperation(lease, operation2, input);
|
|
698
|
-
} catch (error) {
|
|
699
|
-
throw error instanceof AirtableOperationError ? error : new AirtableOperationError("Airtable connection unavailable");
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
|
|
703
699
|
var DataOperationError = class extends Error {
|
|
704
700
|
constructor(code, retryable = !1, effect, component) {
|
|
705
701
|
super(`Data operation failed: ${code}`);
|
|
@@ -715,26 +711,26 @@ var DataOperationError = class extends Error {
|
|
|
715
711
|
component;
|
|
716
712
|
};
|
|
717
713
|
|
|
718
|
-
var
|
|
714
|
+
var invalid2 = () => {
|
|
719
715
|
throw new DataOperationError("invalid_query");
|
|
720
716
|
}, historyText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
|
|
721
717
|
function historyVersion(value) {
|
|
722
718
|
return historyText(value) && value.length > 0 && new TextEncoder().encode(value).length <= 1024 && !/[\u0000-\u001f\u007f-\u009f]/.test(value) && !/^[0-9a-f]{40}$/i.test(value);
|
|
723
719
|
}
|
|
724
720
|
function historyProperties(value) {
|
|
725
|
-
if (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return
|
|
721
|
+
if (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return invalid2();
|
|
726
722
|
let descriptors = Object.getOwnPropertyDescriptors(value);
|
|
727
|
-
return Reflect.ownKeys(value).some((key) => typeof key != "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")) ?
|
|
723
|
+
return Reflect.ownKeys(value).some((key) => typeof key != "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")) ? invalid2() : descriptors;
|
|
728
724
|
}
|
|
729
725
|
var historyCollection = (value) => typeof value == "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value), historyCursor = (value) => typeof value == "string" && value.length <= 128 * 1024 && /^history-cursor-v1\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]+$/.test(value);
|
|
730
726
|
function historyArray(value) {
|
|
731
|
-
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) return
|
|
727
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) return invalid2();
|
|
732
728
|
let descriptors = Object.getOwnPropertyDescriptors(value);
|
|
733
|
-
if (Reflect.ownKeys(value).length !== value.length + 1) return
|
|
729
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) return invalid2();
|
|
734
730
|
let result2 = [];
|
|
735
731
|
for (let i = 0; i < value.length; i++) {
|
|
736
732
|
let descriptor2 = descriptors[i];
|
|
737
|
-
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value")) return
|
|
733
|
+
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value")) return invalid2();
|
|
738
734
|
result2.push(descriptor2.value);
|
|
739
735
|
}
|
|
740
736
|
return result2;
|
|
@@ -742,19 +738,19 @@ function historyArray(value) {
|
|
|
742
738
|
function captureOptions(value, changes) {
|
|
743
739
|
if (value === void 0) return {};
|
|
744
740
|
let descriptors = historyProperties(value), result2 = {}, allowed = changes ? ["collections", "refs", "from", "through", "limit", "after"] : ["limit", "before"];
|
|
745
|
-
if (Object.keys(descriptors).some((key) => !allowed.includes(key))) return
|
|
741
|
+
if (Object.keys(descriptors).some((key) => !allowed.includes(key))) return invalid2();
|
|
746
742
|
for (let key of allowed) {
|
|
747
743
|
if (!Object.hasOwn(descriptors, key)) continue;
|
|
748
744
|
let scalar = descriptors[key].value;
|
|
749
745
|
if (key === "collections" || key === "refs") {
|
|
750
746
|
let values = historyArray(scalar);
|
|
751
|
-
if (!values.length || values.length > (key === "collections" ? 16 : 100) || !values.every(key === "collections" ? historyCollection : validRecordRef) || new Set(values).size !== values.length) return
|
|
747
|
+
if (!values.length || values.length > (key === "collections" ? 16 : 100) || !values.every(key === "collections" ? historyCollection : validRecordRef) || new Set(values).size !== values.length) return invalid2();
|
|
752
748
|
result2[key] = values;
|
|
753
749
|
} else if (key === "limit") {
|
|
754
|
-
if (!Number.isInteger(scalar) || scalar < 1 || scalar > (changes ? 500 : 100)) return
|
|
750
|
+
if (!Number.isInteger(scalar) || scalar < 1 || scalar > (changes ? 500 : 100)) return invalid2();
|
|
755
751
|
result2.limit = scalar;
|
|
756
752
|
} else {
|
|
757
|
-
if (!(key === "after" ? historyCursor(scalar) : historyVersion(scalar))) return
|
|
753
|
+
if (!(key === "after" ? historyCursor(scalar) : historyVersion(scalar))) return invalid2();
|
|
758
754
|
result2[key] = scalar;
|
|
759
755
|
}
|
|
760
756
|
}
|
|
@@ -762,39 +758,39 @@ function captureOptions(value, changes) {
|
|
|
762
758
|
}
|
|
763
759
|
var captureHistoryListOptions = (value) => captureOptions(value, !1), captureHistoryChangesOptions = (value) => captureOptions(value, !0);
|
|
764
760
|
function captureHistoryInstant(value) {
|
|
765
|
-
if (!historyText(value) || value.length > 1024) return
|
|
761
|
+
if (!historyText(value) || value.length > 1024) return invalid2();
|
|
766
762
|
let match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(value);
|
|
767
|
-
if (!match) return
|
|
763
|
+
if (!match) return invalid2();
|
|
768
764
|
let [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number), days = new Date(Date.UTC(year, month, 0));
|
|
769
|
-
return days.setUTCFullYear(year), year < 1 || month < 1 || month > 12 || day < 1 || day > days.getUTCDate() || hour > 23 || minute > 59 || second > 59 || Number(match[9] ?? 0) > 23 || Number(match[10] ?? 0) > 59 ?
|
|
765
|
+
return days.setUTCFullYear(year), year < 1 || month < 1 || month > 12 || day < 1 || day > days.getUTCDate() || hour > 23 || minute > 59 || second > 59 || Number(match[9] ?? 0) > 23 || Number(match[10] ?? 0) > 59 ? invalid2() : value;
|
|
770
766
|
}
|
|
771
767
|
|
|
772
|
-
var
|
|
768
|
+
var invalid3 = () => {
|
|
773
769
|
throw new DataOperationError("invalid_query");
|
|
774
770
|
};
|
|
775
771
|
function captureRecordsSearchInput(query, options) {
|
|
776
|
-
if (!historyText(query)) return
|
|
772
|
+
if (!historyText(query)) return invalid3();
|
|
777
773
|
let length = [...query.trim()].length;
|
|
778
|
-
if (length < 1 || length > 256) return
|
|
774
|
+
if (length < 1 || length > 256) return invalid3();
|
|
779
775
|
if (options === void 0) return { query };
|
|
780
776
|
let descriptors = historyProperties(options);
|
|
781
|
-
if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key))) return
|
|
777
|
+
if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key))) return invalid3();
|
|
782
778
|
let captured = {};
|
|
783
779
|
if (Object.hasOwn(descriptors, "collections")) {
|
|
784
780
|
let supplied = descriptors.collections.value;
|
|
785
|
-
if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return
|
|
781
|
+
if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return invalid3();
|
|
786
782
|
let collections = [];
|
|
787
783
|
for (let index = 0; index < supplied.length; index++) {
|
|
788
784
|
let descriptor2 = Object.getOwnPropertyDescriptor(supplied, String(index));
|
|
789
|
-
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !historyText(descriptor2.value)) return
|
|
785
|
+
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !historyText(descriptor2.value)) return invalid3();
|
|
790
786
|
collections.push(descriptor2.value);
|
|
791
787
|
}
|
|
792
|
-
if (new Set(collections).size !== collections.length) return
|
|
788
|
+
if (new Set(collections).size !== collections.length) return invalid3();
|
|
793
789
|
captured.collections = collections;
|
|
794
790
|
}
|
|
795
791
|
if (Object.hasOwn(descriptors, "limit")) {
|
|
796
792
|
let supplied = descriptors.limit.value;
|
|
797
|
-
if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return
|
|
793
|
+
if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid3();
|
|
798
794
|
captured.limit = supplied;
|
|
799
795
|
}
|
|
800
796
|
return { query, options: captured };
|
|
@@ -820,13 +816,13 @@ function captureValue(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
|
|
|
820
816
|
ancestors.delete(value);
|
|
821
817
|
}
|
|
822
818
|
}
|
|
823
|
-
function captureDataDelete(
|
|
824
|
-
let identity = mapping(
|
|
819
|
+
function captureDataDelete(record3) {
|
|
820
|
+
let identity = mapping(record3);
|
|
825
821
|
return !validRecordRef(identity.$ref) || typeof identity.$version != "string" || !identity.$version.length || identity.$version.length > 512 ? invalidChange() : { ref: identity.$ref, expectedVersion: identity.$version };
|
|
826
822
|
}
|
|
827
|
-
function captureDataUpdate(
|
|
828
|
-
let identity = captureDataDelete(
|
|
829
|
-
return !Object.keys(
|
|
823
|
+
function captureDataUpdate(record3, set) {
|
|
824
|
+
let identity = captureDataDelete(record3), fields4 = mapping(set);
|
|
825
|
+
return !Object.keys(fields4).length || Object.keys(fields4).some((key) => key.startsWith("$") || ["id", "type"].includes(key)) ? invalidChange() : { ...identity, set: captureValue(fields4) };
|
|
830
826
|
}
|
|
831
827
|
function captureIdentities(value, update, allowEmpty = !0) {
|
|
832
828
|
if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4) return invalidChange();
|
|
@@ -845,14 +841,14 @@ function captureDataCreate(value) {
|
|
|
845
841
|
let row = mapping(value);
|
|
846
842
|
if (!validRecordRef(row.$ref) || Object.keys(row).some((key) => key.startsWith("$") && !["$ref", "$filing"].includes(key)))
|
|
847
843
|
return invalidChange();
|
|
848
|
-
let { $ref: ref, $filing, ...
|
|
844
|
+
let { $ref: ref, $filing, ...fields4 } = row, filing;
|
|
849
845
|
if ($filing !== void 0) {
|
|
850
846
|
let components = mapping($filing);
|
|
851
847
|
if (Object.entries(components).some(([name, value2]) => !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name) || ["constructor", "prototype"].includes(name) || typeof value2 != "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value2)))
|
|
852
848
|
return invalidChange();
|
|
853
849
|
filing = { ...components };
|
|
854
850
|
}
|
|
855
|
-
return { ref, fields: captureValue(
|
|
851
|
+
return { ref, fields: captureValue(fields4), ...filing ? { filing } : {} };
|
|
856
852
|
}
|
|
857
853
|
function captureDataChanges(updatesValue, createsValue = [], deletesValue = []) {
|
|
858
854
|
let updates = captureDataUpdates(updatesValue, !0);
|
|
@@ -860,8 +856,8 @@ function captureDataChanges(updatesValue, createsValue = [], deletesValue = [])
|
|
|
860
856
|
let creates = createsValue.map((item) => {
|
|
861
857
|
let row = mapping(item);
|
|
862
858
|
if (Object.keys(row).some((key) => !["ref", "fields", "filing"].includes(key)) || !Object.hasOwn(row, "ref") || !Object.hasOwn(row, "fields")) return invalidChange();
|
|
863
|
-
let
|
|
864
|
-
return Object.keys(
|
|
859
|
+
let fields4 = mapping(row.fields);
|
|
860
|
+
return Object.keys(fields4).some((key) => key.startsWith("$")) ? invalidChange() : captureDataCreate({ ...fields4, $ref: row.ref, ...Object.hasOwn(row, "filing") ? { $filing: row.filing } : {} });
|
|
865
861
|
}), deletes = captureIdentities(deletesValue, !1), refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
|
|
866
862
|
return !refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length ? invalidChange() : { updates, creates, deletes };
|
|
867
863
|
}
|
|
@@ -887,96 +883,9 @@ function dataTransportError(status, value) {
|
|
|
887
883
|
);
|
|
888
884
|
}
|
|
889
885
|
|
|
890
|
-
var
|
|
891
|
-
constructor(code, status, retryable = !1) {
|
|
892
|
-
super(code);
|
|
893
|
-
this.code = code;
|
|
894
|
-
this.status = status;
|
|
895
|
-
this.retryable = retryable;
|
|
896
|
-
}
|
|
897
|
-
code;
|
|
898
|
-
status;
|
|
899
|
-
retryable;
|
|
900
|
-
}, FILE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, FILE_ORIGINAL_MAX_BYTES = 5 * 1024 ** 3;
|
|
901
|
-
function parseFileDescriptor(value) {
|
|
902
|
-
let invalid4 = () => {
|
|
903
|
-
throw new FileOperationError("unavailable", 503, !0);
|
|
904
|
-
};
|
|
905
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
906
|
-
let f = value;
|
|
907
|
-
if (Object.keys(f).sort().join(",") !== "details,enrichments,id,mediaType,name,path,sha256,size" || typeof f.id != "string" || !FILE_ID_PATTERN.test(f.id) || !safeFileName(f.name) || !safeFilePath(f.path) || !Number.isSafeInteger(f.size) || f.size < 0 || f.size > FILE_ORIGINAL_MAX_BYTES || typeof f.sha256 != "string" || !/^[0-9a-f]{64}$/.test(f.sha256) || !f.enrichments || typeof f.enrichments != "object" || Array.isArray(f.enrichments) || !f.details || typeof f.details != "object" || Array.isArray(f.details)) return invalid4();
|
|
908
|
-
let d = f.details;
|
|
909
|
-
if (d.kind === "other") {
|
|
910
|
-
if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid4();
|
|
911
|
-
} else if (d.kind === "csv") {
|
|
912
|
-
if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0) return invalid4();
|
|
913
|
-
} else return invalid4();
|
|
914
|
-
for (let [name, descriptor2] of Object.entries(f.enrichments)) {
|
|
915
|
-
if (!safeEnrichmentName(name)) return invalid4();
|
|
916
|
-
parseEnrichmentDescriptor(descriptor2);
|
|
917
|
-
}
|
|
918
|
-
return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid4() : f;
|
|
919
|
-
}
|
|
920
|
-
function parseEnrichmentDescriptor(value) {
|
|
921
|
-
let invalid4 = () => {
|
|
922
|
-
throw new FileOperationError("unavailable", 503, !0);
|
|
923
|
-
};
|
|
924
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
925
|
-
let d = value;
|
|
926
|
-
return Object.keys(d).sort().join(",") !== "mediaType,sha256,size" || !["application/octet-stream", "text/plain", "text/markdown", "application/json"].includes(d.mediaType) || !Number.isSafeInteger(d.size) || d.size < 0 || d.size > 16 * 1024 * 1024 || typeof d.sha256 != "string" || !/^[0-9a-f]{64}$/.test(d.sha256) ? invalid4() : d;
|
|
927
|
-
}
|
|
928
|
-
function safeEnrichmentName(value) {
|
|
929
|
-
return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
|
|
930
|
-
}
|
|
931
|
-
function parseFileEnrichMetadata(value, maxBytes) {
|
|
932
|
-
let invalid4 = () => {
|
|
933
|
-
throw new FileOperationError("invalid_input", 400);
|
|
934
|
-
};
|
|
935
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
936
|
-
let d = value;
|
|
937
|
-
if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key)) || !safeEnrichmentName(d.name) || !Number.isSafeInteger(d.size) || d.size < 0 || d.mediaType !== void 0 && (typeof d.mediaType != "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType)) || d.replace !== void 0 && (typeof d.replace != "string" || !/^[0-9a-f]{64}$/.test(d.replace))) return invalid4();
|
|
938
|
-
if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
939
|
-
return d;
|
|
940
|
-
}
|
|
941
|
-
function safeFileName(value) {
|
|
942
|
-
return typeof value == "string" && value.length > 0 && value.length <= 255 && value === value.trim() && value !== "." && value !== ".." && !/[\u0000-\u001f\u007f-\u009f/\\:*?"<>|]/u.test(value) && !/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/.test(value) && !/[. ]$/.test(value) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value);
|
|
943
|
-
}
|
|
944
|
-
function safeFilePath(value) {
|
|
945
|
-
return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
|
|
946
|
-
}
|
|
947
|
-
function parseFileCreateMetadata(value, maxBytes) {
|
|
948
|
-
let invalid4 = () => {
|
|
949
|
-
throw new FileOperationError("invalid_input", 400);
|
|
950
|
-
};
|
|
951
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
952
|
-
let input = value;
|
|
953
|
-
if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid4();
|
|
954
|
-
let path = input.path === void 0 ? "Files" : input.path;
|
|
955
|
-
if (!safeFileName(input.name) || !safeFilePath(path) || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid4();
|
|
956
|
-
if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
957
|
-
if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid4();
|
|
958
|
-
let csv;
|
|
959
|
-
if (input.csv !== void 0) {
|
|
960
|
-
if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid4();
|
|
961
|
-
let hints = input.csv;
|
|
962
|
-
if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid4();
|
|
963
|
-
let encoding = hints.encoding === void 0 ? "utf-8" : hints.encoding, delimiter = hints.delimiter === void 0 ? "," : hints.delimiter, headerRow = hints.headerRow === void 0 ? 1 : hints.headerRow;
|
|
964
|
-
if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid4();
|
|
965
|
-
csv = { encoding, delimiter, headerRow };
|
|
966
|
-
}
|
|
967
|
-
return {
|
|
968
|
-
name: input.name,
|
|
969
|
-
path,
|
|
970
|
-
...input.storage ? { storage: input.storage } : {},
|
|
971
|
-
...csv ? { csv } : {},
|
|
972
|
-
size: input.size,
|
|
973
|
-
requestKey: input.requestKey
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
var DEFAULT_ORIGIN = "https://auth.oddington.ai";
|
|
886
|
+
var AUTH_ORIGIN = "https://auth.oddington.ai";
|
|
978
887
|
function oddingtonOrigin(environment = process.env) {
|
|
979
|
-
let value = environment.ODDINGTON_OS_ORIGIN ??
|
|
888
|
+
let value = environment.ODDINGTON_OS_ORIGIN ?? AUTH_ORIGIN, url = new URL(value);
|
|
980
889
|
if (url.protocol !== "https:" && url.hostname !== "127.0.0.1")
|
|
981
890
|
throw new Error("ODDINGTON_OS_ORIGIN must use HTTPS");
|
|
982
891
|
return url.origin;
|
|
@@ -992,9 +901,7 @@ async function resolveAppContext() {
|
|
|
992
901
|
let host = memberContext;
|
|
993
902
|
if (!host) throw new Error("App context unavailable.");
|
|
994
903
|
let context2 = await host();
|
|
995
|
-
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now())
|
|
996
|
-
throw new Error("App context unavailable.");
|
|
997
|
-
if (context2.kind !== "local" && (typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)))
|
|
904
|
+
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now() || context2.kind !== "local" && (typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)))
|
|
998
905
|
throw new Error("App context unavailable.");
|
|
999
906
|
return context2;
|
|
1000
907
|
}
|
|
@@ -1092,19 +999,19 @@ var data = {
|
|
|
1092
999
|
}), input;
|
|
1093
1000
|
};
|
|
1094
1001
|
return {
|
|
1095
|
-
create(
|
|
1002
|
+
create(record3) {
|
|
1096
1003
|
assertMutable();
|
|
1097
|
-
let next = [...creates, captureDataCreate(
|
|
1004
|
+
let next = [...creates, captureDataCreate(record3)];
|
|
1098
1005
|
snapshot(updates, next, deletes), creates = next;
|
|
1099
1006
|
},
|
|
1100
|
-
update(
|
|
1007
|
+
update(record3, set) {
|
|
1101
1008
|
assertMutable();
|
|
1102
|
-
let next = [...updates, captureDataUpdate(
|
|
1009
|
+
let next = [...updates, captureDataUpdate(record3, set)];
|
|
1103
1010
|
snapshot(next, creates, deletes), updates = next;
|
|
1104
1011
|
},
|
|
1105
|
-
delete(
|
|
1012
|
+
delete(record3) {
|
|
1106
1013
|
assertMutable();
|
|
1107
|
-
let next = [...deletes, captureDataDelete(
|
|
1014
|
+
let next = [...deletes, captureDataDelete(record3)];
|
|
1108
1015
|
snapshot(updates, creates, next), deletes = next;
|
|
1109
1016
|
},
|
|
1110
1017
|
async validate() {
|
|
@@ -1138,7 +1045,7 @@ var data = {
|
|
|
1138
1045
|
}
|
|
1139
1046
|
};
|
|
1140
1047
|
|
|
1141
|
-
var MAX_BYTES = 16 * 1024 * 1024, PART_BYTES = 8 * 1024 * 1024, METADATA_BYTES = 4096, RESULT_BYTES = 16 * 1024, CONTROL_RESULT_BYTES = 128 * 1024, TIMEOUT_MS = 3e4, MAX_ACTIVE = 4, ATTEMPTS = 2, active = 0,
|
|
1048
|
+
var MAX_BYTES = 16 * 1024 * 1024, PART_BYTES = 8 * 1024 * 1024, METADATA_BYTES = 4096, RESULT_BYTES = 16 * 1024, CONTROL_RESULT_BYTES = 128 * 1024, TIMEOUT_MS = 3e4, MAX_ACTIVE = 4, ATTEMPTS = 2, active = 0, invalid4 = () => new FileOperationError("invalid_input", 400), unavailable = () => new FileOperationError("unavailable", 503, !0), failures = {
|
|
1142
1049
|
400: ["invalid_input", "unsupported_context"],
|
|
1143
1050
|
401: ["unauthenticated"],
|
|
1144
1051
|
403: ["access_denied"],
|
|
@@ -1155,7 +1062,7 @@ async function boundedBytes(stream, limit) {
|
|
|
1155
1062
|
for (; ; ) {
|
|
1156
1063
|
let { done, value } = await reader.read();
|
|
1157
1064
|
if (done) break;
|
|
1158
|
-
if (!(value instanceof Uint8Array)) throw
|
|
1065
|
+
if (!(value instanceof Uint8Array)) throw invalid4();
|
|
1159
1066
|
if (size += value.byteLength, size > limit) throw new FileOperationError("request_too_large", 413);
|
|
1160
1067
|
bytes.set(value, size - value.byteLength);
|
|
1161
1068
|
}
|
|
@@ -1229,52 +1136,51 @@ async function hostedContent(url, init2, parent, upstream = fetch) {
|
|
|
1229
1136
|
}
|
|
1230
1137
|
}
|
|
1231
1138
|
async function send(suffix, init2, descriptor2, initial, parent) {
|
|
1232
|
-
let original = initial, checkCurrent = () => {
|
|
1139
|
+
let original = initial, checkCurrent = (context2 = original) => {
|
|
1233
1140
|
if (parent?.aborted) throw unavailable();
|
|
1234
|
-
if (!original.isCurrent()) throw new FileOperationError("unsupported_context", 400);
|
|
1141
|
+
if (!original.isCurrent() || context2.url !== original.url) throw new FileOperationError("unsupported_context", 400);
|
|
1235
1142
|
};
|
|
1236
1143
|
for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
|
|
1237
1144
|
checkCurrent();
|
|
1238
1145
|
let context2 = await resolveFilesContext();
|
|
1239
|
-
|
|
1146
|
+
checkCurrent(context2);
|
|
1240
1147
|
try {
|
|
1241
|
-
|
|
1242
|
-
let
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
return descriptor2 ? fetch(url, request) : hostedContent(url, request, parent);
|
|
1251
|
-
})();
|
|
1252
|
-
if (!response.ok) {
|
|
1253
|
-
let value2 = await result(response), error = value2 && typeof value2 == "object" && "error" in value2 ? value2.error : void 0, received = error && typeof error == "object" && "code" in error ? error.code : void 0, code = failures[response.status]?.find((code2) => code2 === received);
|
|
1254
|
-
throw code ? new FileOperationError(code, response.status, code === "capacity" || code === "unavailable") : unavailable();
|
|
1255
|
-
}
|
|
1256
|
-
if (response.status !== 200)
|
|
1257
|
-
throw response.body?.cancel().catch(() => {
|
|
1258
|
-
}), unavailable();
|
|
1259
|
-
try {
|
|
1260
|
-
checkCurrent();
|
|
1261
|
-
} catch (error) {
|
|
1262
|
-
throw response.body?.cancel().catch(() => {
|
|
1263
|
-
}), error;
|
|
1264
|
-
}
|
|
1265
|
-
if (!descriptor2) return response;
|
|
1266
|
-
let value = await result(response, descriptor2 === "control" ? CONTROL_RESULT_BYTES : RESULT_BYTES);
|
|
1267
|
-
if (descriptor2 === "control")
|
|
1268
|
-
return checkCurrent(), value;
|
|
1269
|
-
let parsed;
|
|
1270
|
-
if (descriptor2 === "enrichment") {
|
|
1271
|
-
if (!value || typeof value != "object" || Array.isArray(value)) throw unavailable();
|
|
1272
|
-
let { name, ...metadata } = value;
|
|
1273
|
-
if (!safeEnrichmentName(name)) throw unavailable();
|
|
1274
|
-
parsed = { name, ...parseEnrichmentDescriptor(metadata) };
|
|
1275
|
-
} else parsed = parseFileDescriptor(value);
|
|
1276
|
-
return checkCurrent(), parsed;
|
|
1148
|
+
let response = await (async () => {
|
|
1149
|
+
let url = `${context2.url}${suffix}`, request = {
|
|
1150
|
+
...init2,
|
|
1151
|
+
signal: parent,
|
|
1152
|
+
headers: { ...context2.headers, ...init2.headers },
|
|
1153
|
+
redirect: "error",
|
|
1154
|
+
cache: "no-store"
|
|
1155
|
+
};
|
|
1156
|
+
return descriptor2 ? fetch(url, request) : hostedContent(url, request, parent);
|
|
1277
1157
|
})();
|
|
1158
|
+
if (!response.ok) {
|
|
1159
|
+
let value2 = await result(response), error = value2 && typeof value2 == "object" && "error" in value2 ? value2.error : void 0, received = error && typeof error == "object" && "code" in error ? error.code : void 0, code = failures[response.status]?.find((code2) => code2 === received);
|
|
1160
|
+
throw code ? new FileOperationError(code, response.status, code === "capacity" || code === "unavailable") : unavailable();
|
|
1161
|
+
}
|
|
1162
|
+
if (response.status !== (descriptor2 === 204 ? 204 : 200) || descriptor2 === 204 && response.body !== null)
|
|
1163
|
+
throw response.body?.cancel().catch(() => {
|
|
1164
|
+
}), unavailable();
|
|
1165
|
+
try {
|
|
1166
|
+
checkCurrent();
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
throw response.body?.cancel().catch(() => {
|
|
1169
|
+
}), error;
|
|
1170
|
+
}
|
|
1171
|
+
if (descriptor2 === 204) return;
|
|
1172
|
+
if (!descriptor2) return response;
|
|
1173
|
+
let value = await result(response, descriptor2 === "control" ? CONTROL_RESULT_BYTES : RESULT_BYTES);
|
|
1174
|
+
if (descriptor2 === "control")
|
|
1175
|
+
return checkCurrent(), value;
|
|
1176
|
+
let parsed;
|
|
1177
|
+
if (descriptor2 === "enrichment") {
|
|
1178
|
+
if (!value || typeof value != "object" || Array.isArray(value)) throw unavailable();
|
|
1179
|
+
let { name, ...metadata } = value;
|
|
1180
|
+
if (!safeEnrichmentName(name)) throw unavailable();
|
|
1181
|
+
parsed = { name, ...parseEnrichmentDescriptor(metadata) };
|
|
1182
|
+
} else parsed = parseFileDescriptor(value);
|
|
1183
|
+
return checkCurrent(), parsed;
|
|
1278
1184
|
} catch (error) {
|
|
1279
1185
|
let failure = error instanceof FileOperationError ? error : unavailable();
|
|
1280
1186
|
if (init2.body instanceof ReadableStream || !failure.retryable || attempt + 1 === ATTEMPTS) throw failure;
|
|
@@ -1283,7 +1189,7 @@ async function send(suffix, init2, descriptor2, initial, parent) {
|
|
|
1283
1189
|
throw unavailable();
|
|
1284
1190
|
}
|
|
1285
1191
|
function idPath(id2) {
|
|
1286
|
-
if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw
|
|
1192
|
+
if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid4();
|
|
1287
1193
|
return `/${id2}`;
|
|
1288
1194
|
}
|
|
1289
1195
|
async function multipartCreate(input, metadata, exchange, release = () => {
|
|
@@ -1326,18 +1232,18 @@ async function multipartCreate(input, metadata, exchange, release = () => {
|
|
|
1326
1232
|
if (!chunk || offset === chunk.byteLength) {
|
|
1327
1233
|
chunk = void 0, offset = 0;
|
|
1328
1234
|
let value2 = await next();
|
|
1329
|
-
if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw
|
|
1235
|
+
if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid4();
|
|
1330
1236
|
}
|
|
1331
1237
|
let take = Math.min(length - filled, chunk.byteLength - offset);
|
|
1332
1238
|
part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
|
|
1333
1239
|
}
|
|
1334
1240
|
}
|
|
1335
|
-
if (part.byteLength !== length) throw
|
|
1241
|
+
if (part.byteLength !== length) throw invalid4();
|
|
1336
1242
|
if (reader && sent + length === metadata.size)
|
|
1337
1243
|
for (lastProgress = Date.now(); ; ) {
|
|
1338
1244
|
let value2 = await next();
|
|
1339
1245
|
if (value2.done) break;
|
|
1340
|
-
if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw
|
|
1246
|
+
if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid4();
|
|
1341
1247
|
}
|
|
1342
1248
|
let value = await request((signal) => exchange(`${route}/part`, {
|
|
1343
1249
|
method: "POST",
|
|
@@ -1351,7 +1257,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
|
|
|
1351
1257
|
}
|
|
1352
1258
|
if (upload) for (let name of Object.keys(upload.file.enrichments)) {
|
|
1353
1259
|
let bytes = await request(() => upload.enrichment(name));
|
|
1354
|
-
if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw
|
|
1260
|
+
if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid4();
|
|
1355
1261
|
await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
|
|
1356
1262
|
method: "POST",
|
|
1357
1263
|
headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
|
|
@@ -1373,166 +1279,504 @@ async function multipartCreate(input, metadata, exchange, release = () => {
|
|
|
1373
1279
|
chunk = void 0, reader?.releaseLock(), pending.size ? Promise.allSettled([...pending]).then(release) : release();
|
|
1374
1280
|
}
|
|
1375
1281
|
}
|
|
1376
|
-
async function operation(work, initial, parent) {
|
|
1377
|
-
let context2 = initial ?? await resolveFilesContext();
|
|
1378
|
-
if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
|
|
1379
|
-
|
|
1380
|
-
try {
|
|
1381
|
-
return await work(context2,
|
|
1382
|
-
} finally {
|
|
1383
|
-
active--;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
})
|
|
1282
|
+
async function operation(work, initial, parent) {
|
|
1283
|
+
let context2 = initial ?? await resolveFilesContext();
|
|
1284
|
+
if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
|
|
1285
|
+
return active++, timed(async (signal) => {
|
|
1286
|
+
try {
|
|
1287
|
+
return await work(context2, signal);
|
|
1288
|
+
} finally {
|
|
1289
|
+
active--;
|
|
1290
|
+
}
|
|
1291
|
+
}, parent).catch((error) => {
|
|
1292
|
+
throw error instanceof FileOperationError ? error : unavailable();
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
function contentPath(id2, name) {
|
|
1296
|
+
let path = idPath(id2);
|
|
1297
|
+
if (name === void 0) return `${path}/content`;
|
|
1298
|
+
if (!safeEnrichmentName(name)) throw invalid4();
|
|
1299
|
+
return `${path}/enrichments/${encodeURIComponent(name)}/content`;
|
|
1300
|
+
}
|
|
1301
|
+
async function read(id2, names) {
|
|
1302
|
+
let context2 = await resolveFilesContext();
|
|
1303
|
+
if (idPath(id2), names === void 0 || typeof names == "string") {
|
|
1304
|
+
let path = contentPath(id2, names);
|
|
1305
|
+
return operation((context3, signal) => send(path, { method: "GET" }, !1, context3, signal), context2);
|
|
1306
|
+
}
|
|
1307
|
+
if (!Array.isArray(names)) throw invalid4();
|
|
1308
|
+
let distinct = [], seen = /* @__PURE__ */ new Set();
|
|
1309
|
+
for (let name of names) {
|
|
1310
|
+
if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid4();
|
|
1311
|
+
if (!seen.has(name)) {
|
|
1312
|
+
if (seen.size === 16) throw invalid4();
|
|
1313
|
+
seen.add(name), distinct.push(name);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
let paths = distinct.map((name) => contentPath(id2, name));
|
|
1317
|
+
return timed(async (parent) => {
|
|
1318
|
+
let responses = {};
|
|
1319
|
+
try {
|
|
1320
|
+
for (let i = 0; i < paths.length; i++) {
|
|
1321
|
+
let response = await operation((context3, signal) => send(paths[i], { method: "GET" }, !1, context3, signal), context2, parent);
|
|
1322
|
+
Object.defineProperty(responses, distinct[i], { value: response, enumerable: !0 });
|
|
1323
|
+
}
|
|
1324
|
+
if (!context2.isCurrent()) throw new FileOperationError("unsupported_context", 400);
|
|
1325
|
+
return responses;
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
for (let response of Object.values(responses)) response.body?.cancel().catch(() => {
|
|
1328
|
+
});
|
|
1329
|
+
throw error;
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
var files = {
|
|
1334
|
+
delete(id2) {
|
|
1335
|
+
return operation((context2, signal) => send(idPath(id2), { method: "DELETE" }, 204, context2, signal));
|
|
1336
|
+
},
|
|
1337
|
+
async upload(id2) {
|
|
1338
|
+
return operation(async (context2, signal) => {
|
|
1339
|
+
let file2 = await send(`${idPath(id2)}/upload`, { method: "POST" }, !0, context2, signal);
|
|
1340
|
+
if (file2.id !== id2) throw unavailable();
|
|
1341
|
+
return file2;
|
|
1342
|
+
});
|
|
1343
|
+
},
|
|
1344
|
+
async create(input, options) {
|
|
1345
|
+
let context2 = await resolveFilesContext();
|
|
1346
|
+
if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "size", "path", "name", "storage", "csv"].includes(key)) || options !== void 0 && (!options || typeof options != "object" || Array.isArray(options) || Object.keys(options).some((key) => key !== "requestKey")))
|
|
1347
|
+
throw invalid4();
|
|
1348
|
+
let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
|
|
1349
|
+
if (!blob && !array2 && !stream) throw invalid4();
|
|
1350
|
+
let intrinsic = blob ? input.file.size : array2 ? input.file.byteLength : void 0;
|
|
1351
|
+
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid4();
|
|
1352
|
+
let name = input.name;
|
|
1353
|
+
name === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
|
|
1354
|
+
let metadata = parseFileCreateMetadata({
|
|
1355
|
+
name,
|
|
1356
|
+
size: intrinsic ?? input.size,
|
|
1357
|
+
path: input.path,
|
|
1358
|
+
storage: input.storage,
|
|
1359
|
+
csv: input.csv,
|
|
1360
|
+
...blob && input.file.type === "application/pdf" ? { inputMediaType: "application/pdf" } : {},
|
|
1361
|
+
requestKey: options?.requestKey === void 0 ? crypto.randomUUID() : options.requestKey
|
|
1362
|
+
}, FILE_ORIGINAL_MAX_BYTES);
|
|
1363
|
+
if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
|
|
1364
|
+
let header = encodeURIComponent(JSON.stringify(metadata));
|
|
1365
|
+
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid4();
|
|
1366
|
+
if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
|
|
1367
|
+
return active++, metadata.size > MAX_BYTES ? multipartCreate(input, metadata, (suffix, init2, kind, signal) => kind === !0 ? send(suffix, init2, !0, context2, signal) : send(suffix, init2, "control", context2, signal)) : timed(async (signal) => {
|
|
1368
|
+
try {
|
|
1369
|
+
let bytes = array2 ? input.file.slice() : await boundedBytes(
|
|
1370
|
+
blob ? input.file.stream() : input.file,
|
|
1371
|
+
metadata.size
|
|
1372
|
+
);
|
|
1373
|
+
if (bytes.byteLength !== metadata.size) throw invalid4();
|
|
1374
|
+
if (signal.aborted) throw unavailable();
|
|
1375
|
+
return await send("", {
|
|
1376
|
+
method: "POST",
|
|
1377
|
+
headers: { "content-type": "application/octet-stream", "x-oddington-file": header },
|
|
1378
|
+
body: bytes.buffer
|
|
1379
|
+
}, !0, context2, signal);
|
|
1380
|
+
} finally {
|
|
1381
|
+
active--;
|
|
1382
|
+
}
|
|
1383
|
+
}).catch((error) => {
|
|
1384
|
+
throw error instanceof FileOperationError ? error : unavailable();
|
|
1385
|
+
});
|
|
1386
|
+
},
|
|
1387
|
+
async enrich(input) {
|
|
1388
|
+
let context2 = await resolveFilesContext();
|
|
1389
|
+
if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "name", "content", "mediaType", "replace"].includes(key)) || typeof input.content != "string" && !(input.content instanceof Uint8Array)) throw invalid4();
|
|
1390
|
+
let path = `${idPath(input.file)}/enrichments`;
|
|
1391
|
+
if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
|
|
1392
|
+
throw new FileOperationError("request_too_large", 413);
|
|
1393
|
+
return operation(async (context3, signal) => {
|
|
1394
|
+
let bytes;
|
|
1395
|
+
if (typeof input.content == "string") {
|
|
1396
|
+
let buffer = new Uint8Array(Math.min(MAX_BYTES, input.content.length * 3)), encoded = new TextEncoder().encodeInto(input.content, buffer);
|
|
1397
|
+
if (encoded.read !== input.content.length) throw new FileOperationError("request_too_large", 413);
|
|
1398
|
+
bytes = buffer.slice(0, encoded.written);
|
|
1399
|
+
} else bytes = input.content.slice();
|
|
1400
|
+
let metadata = parseFileEnrichMetadata({
|
|
1401
|
+
name: input.name,
|
|
1402
|
+
size: bytes.byteLength,
|
|
1403
|
+
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
1404
|
+
...input.replace === void 0 ? {} : { replace: input.replace }
|
|
1405
|
+
}, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
|
|
1406
|
+
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid4();
|
|
1407
|
+
let result2 = await send(path, { method: "POST", headers: {
|
|
1408
|
+
"content-type": "application/octet-stream",
|
|
1409
|
+
"x-oddington-enrichment": header
|
|
1410
|
+
}, body: bytes.buffer }, "enrichment", context3, signal);
|
|
1411
|
+
if (result2.name !== metadata.name) throw unavailable();
|
|
1412
|
+
return result2;
|
|
1413
|
+
}, context2);
|
|
1414
|
+
},
|
|
1415
|
+
async get(id2) {
|
|
1416
|
+
return operation((context2, signal) => send(idPath(id2), { method: "GET" }, !0, context2, signal));
|
|
1417
|
+
},
|
|
1418
|
+
read
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
function validGoogleScopes(value) {
|
|
1422
|
+
return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
var ROUTINE_CONNECTION_CREDENTIALS_ENV = "ODDINGTON_ROUTINE_CONNECTION_CREDENTIALS";
|
|
1426
|
+
var connectionUuid = (v) => typeof v == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(v);
|
|
1427
|
+
function parseConnectionLease(raw, now = Date.now()) {
|
|
1428
|
+
let v = raw;
|
|
1429
|
+
if (!v || typeof v != "object" || Array.isArray(v) || !connectionUuid(v.connectionId) || !connectionUuid(v.runId) || typeof v.name != "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(v.name) || typeof v.accessToken != "string" || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(v.accessToken))
|
|
1430
|
+
throw new Error("Invalid connection credential");
|
|
1431
|
+
if (v.provider === "gmail") {
|
|
1432
|
+
if (Object.keys(v).sort().join(",") !== "accessToken,accountEmail,connectionId,expiresAt,name,provider,runId,scopes" || typeof v.accountEmail != "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.accountEmail) || !validGoogleScopes(v.scopes) || typeof v.expiresAt != "string" || !Number.isFinite(Date.parse(v.expiresAt)) || Date.parse(v.expiresAt) <= now + 6e4 || Date.parse(v.expiresAt) > now + 36e5)
|
|
1433
|
+
throw new Error("Invalid connection credential");
|
|
1434
|
+
return v;
|
|
1435
|
+
}
|
|
1436
|
+
if (v.provider === "airtable" && Object.keys(v).sort().join(",") === "accessToken,baseId,connectionId,name,provider,runExpiresAt,runId" && typeof v.baseId == "string" && /^app[A-Za-z0-9]+$/.test(v.baseId) && typeof v.runExpiresAt == "string" && Number.isFinite(Date.parse(v.runExpiresAt)) && Date.parse(v.runExpiresAt) > now + 6e4 && Date.parse(v.runExpiresAt) <= now + 15 * 6e4)
|
|
1437
|
+
return v;
|
|
1438
|
+
throw new Error("Invalid connection credential");
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
var AirtableOperationError = class extends Error {
|
|
1442
|
+
constructor(message, effect = "unapplied", receipts = []) {
|
|
1443
|
+
super(message);
|
|
1444
|
+
this.effect = effect;
|
|
1445
|
+
this.receipts = receipts;
|
|
1446
|
+
this.name = "AirtableOperationError";
|
|
1447
|
+
}
|
|
1448
|
+
effect;
|
|
1449
|
+
receipts;
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
var recordId = (v) => typeof v == "string" && /^rec[A-Za-z0-9]+$/.test(v), tableId = (v) => typeof v == "string" && /^tbl[A-Za-z0-9]+$/.test(v);
|
|
1453
|
+
function fail2(message) {
|
|
1454
|
+
throw new AirtableOperationError(message);
|
|
1455
|
+
}
|
|
1456
|
+
function object2(v) {
|
|
1457
|
+
return (!v || typeof v != "object" || Array.isArray(v) || ![null, Object.prototype].includes(Object.getPrototypeOf(v))) && fail2("Invalid Airtable data"), v;
|
|
1458
|
+
}
|
|
1459
|
+
function keys2(v, allowed, required = allowed) {
|
|
1460
|
+
(Object.keys(v).some((k) => !allowed.includes(k)) || required.some((k) => !Object.hasOwn(v, k))) && fail2("Invalid Airtable input");
|
|
1461
|
+
}
|
|
1462
|
+
function text2(v) {
|
|
1463
|
+
return typeof v == "string" && v.length > 0 && v.length <= 1024;
|
|
1464
|
+
}
|
|
1465
|
+
function fields3(v, nonempty = !1) {
|
|
1466
|
+
let result2 = object2(v);
|
|
1467
|
+
(nonempty && !Object.keys(result2).length || Object.keys(result2).some((k) => !text2(k))) && fail2("Invalid Airtable fields");
|
|
1468
|
+
try {
|
|
1469
|
+
return serializable(result2);
|
|
1470
|
+
} catch {
|
|
1471
|
+
return fail2("Invalid Airtable fields");
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
function record2(v) {
|
|
1475
|
+
let r = object2(v);
|
|
1476
|
+
return (!recordId(r.id) || r.createdTime !== void 0 && (typeof r.createdTime != "string" || !Number.isFinite(Date.parse(r.createdTime)))) && fail2("Malformed Airtable record"), { id: r.id, ...r.createdTime === void 0 ? {} : { createdTime: r.createdTime }, fields: fields3(r.fields) };
|
|
1477
|
+
}
|
|
1478
|
+
function equal(a, b) {
|
|
1479
|
+
if (a === b) return !0;
|
|
1480
|
+
if (!a || !b || typeof a != "object" || typeof b != "object" || Array.isArray(a) !== Array.isArray(b)) return !1;
|
|
1481
|
+
let left = Object.keys(a), right = Object.keys(b);
|
|
1482
|
+
return left.length === right.length && left.every((k) => Object.hasOwn(b, k) && equal(
|
|
1483
|
+
a[k],
|
|
1484
|
+
b[k]
|
|
1485
|
+
));
|
|
1486
|
+
}
|
|
1487
|
+
function createConnections(transport) {
|
|
1488
|
+
return { airtable(name) {
|
|
1489
|
+
(typeof name != "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(name)) && fail2("Invalid Airtable connection name");
|
|
1490
|
+
let call = (operation2, input) => transport(name, operation2, input);
|
|
1491
|
+
return {
|
|
1492
|
+
tables: { list: async () => await call("tables.list", {}) },
|
|
1493
|
+
records: {
|
|
1494
|
+
list: async (input) => await call("records.list", input),
|
|
1495
|
+
get: async (input) => await call("records.get", input),
|
|
1496
|
+
create: async (input) => await call("records.create", input),
|
|
1497
|
+
update: async (input) => await call("records.update", input),
|
|
1498
|
+
delete: async (input) => await call("records.delete", input)
|
|
1499
|
+
}
|
|
1500
|
+
};
|
|
1501
|
+
} };
|
|
1502
|
+
}
|
|
1503
|
+
async function executeAirtableOperation(authority, operation2, raw, options = {}) {
|
|
1504
|
+
let { name, baseId, accessToken } = authority;
|
|
1505
|
+
(!/^[a-z0-9][a-z0-9-]{0,62}$/.test(name) || !/^app[A-Za-z0-9]+$/.test(baseId) || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(accessToken)) && fail2("Airtable connection unavailable");
|
|
1506
|
+
let input;
|
|
1507
|
+
try {
|
|
1508
|
+
input = object2(serializable(raw));
|
|
1509
|
+
} catch {
|
|
1510
|
+
return fail2("Invalid Airtable input");
|
|
1511
|
+
}
|
|
1512
|
+
let mutation = ["records.create", "records.update", "records.delete"].includes(operation2), receipt = (ids2) => ({
|
|
1513
|
+
provider: "airtable",
|
|
1514
|
+
connection: `airtable/${name}`,
|
|
1515
|
+
baseId,
|
|
1516
|
+
table: input.table,
|
|
1517
|
+
operation: operation2.slice(8),
|
|
1518
|
+
records: ids2.map((id2) => ({ id: id2 }))
|
|
1519
|
+
});
|
|
1520
|
+
async function request(path2, init2 = {}, write = !1) {
|
|
1521
|
+
let response;
|
|
1522
|
+
try {
|
|
1523
|
+
response = await (options.fetch ?? fetch)(`https://api.airtable.com/v0/${path2}`, {
|
|
1524
|
+
...init2,
|
|
1525
|
+
headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
|
|
1526
|
+
redirect: "manual",
|
|
1527
|
+
signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3)
|
|
1528
|
+
});
|
|
1529
|
+
} catch {
|
|
1530
|
+
throw new AirtableOperationError("Airtable request acknowledgment unavailable", write ? "uncertain" : "unapplied");
|
|
1531
|
+
}
|
|
1532
|
+
if (!response.ok)
|
|
1533
|
+
throw await response.body?.cancel().catch(() => {
|
|
1534
|
+
}), new AirtableOperationError("Airtable request rejected", write && (response.status >= 500 || response.status === 408 || response.status < 400) ? "uncertain" : "unapplied");
|
|
1535
|
+
try {
|
|
1536
|
+
if (!response.headers.get("content-type")?.toLowerCase().includes("application/json")) throw new Error();
|
|
1537
|
+
let value = object2(await response.json()), safe = serializable(value);
|
|
1538
|
+
if (Object.hasOwn(value, "error") || JSON.stringify(safe).includes(accessToken)) throw new Error();
|
|
1539
|
+
return value;
|
|
1540
|
+
} catch {
|
|
1541
|
+
throw new AirtableOperationError("Malformed Airtable acknowledgment", write ? "uncertain" : "unapplied");
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (operation2 === "tables.list") {
|
|
1545
|
+
keys2(input, []);
|
|
1546
|
+
let tables = [], seen = /* @__PURE__ */ new Set(), offset;
|
|
1547
|
+
do {
|
|
1548
|
+
let query2 = new URLSearchParams(offset ? { offset } : {}), page = await request(`meta/bases/${baseId}/tables?${query2}`);
|
|
1549
|
+
Array.isArray(page.tables) || fail2("Malformed Airtable tables");
|
|
1550
|
+
for (let value of page.tables) {
|
|
1551
|
+
let t = object2(value);
|
|
1552
|
+
(!tableId(t.id) || !text2(t.name) || !text2(t.primaryFieldId) || !Array.isArray(t.fields) || tables.some((existing) => existing.id === t.id)) && fail2("Malformed Airtable table");
|
|
1553
|
+
let metadata = t.fields.map((value2) => {
|
|
1554
|
+
let f = object2(value2);
|
|
1555
|
+
return (typeof f.id != "string" || !/^fld[A-Za-z0-9]+$/.test(f.id) || !text2(f.name) || !text2(f.type)) && fail2("Malformed Airtable field"), { id: f.id, name: f.name, type: f.type, ...f.options === void 0 ? {} : { options: serializable(f.options) } };
|
|
1556
|
+
});
|
|
1557
|
+
(!metadata.some((f) => f.id === t.primaryFieldId) || new Set(metadata.map((f) => f.id)).size !== metadata.length) && fail2("Malformed Airtable fields"), tables.push({ id: t.id, name: t.name, primaryFieldId: t.primaryFieldId, fields: metadata });
|
|
1558
|
+
}
|
|
1559
|
+
offset = nextOffset(page.offset, seen);
|
|
1560
|
+
} while (offset);
|
|
1561
|
+
return tables;
|
|
1562
|
+
}
|
|
1563
|
+
(!text2(input.table) || input.table === "." || input.table === "..") && fail2("Invalid Airtable table"), mutation && !tableId(input.table) && fail2("Airtable writes require a stable table ID");
|
|
1564
|
+
let path = `${baseId}/${encodeURIComponent(input.table)}`;
|
|
1565
|
+
if (operation2 === "records.get") {
|
|
1566
|
+
keys2(input, ["table", "id"]), recordId(input.id) || fail2("Invalid Airtable record ID");
|
|
1567
|
+
let result3 = record2(await request(`${path}/${input.id}`));
|
|
1568
|
+
return result3.id !== input.id && fail2("Malformed Airtable record"), result3;
|
|
1569
|
+
}
|
|
1570
|
+
if (operation2 === "records.list") {
|
|
1571
|
+
keys2(input, ["table", "fields"], ["table"]), input.fields !== void 0 && (!Array.isArray(input.fields) || !input.fields.length || input.fields.some((f) => !text2(f)) || new Set(input.fields).size !== input.fields.length) && fail2("Invalid Airtable field selection");
|
|
1572
|
+
let records2 = [], ids2 = /* @__PURE__ */ new Set(), seen = /* @__PURE__ */ new Set(), offset;
|
|
1573
|
+
do {
|
|
1574
|
+
let query2 = new URLSearchParams();
|
|
1575
|
+
for (let field of input.fields ?? []) query2.append("fields[]", field);
|
|
1576
|
+
offset && query2.set("offset", offset);
|
|
1577
|
+
let page = await request(`${path}?${query2}`);
|
|
1578
|
+
Array.isArray(page.records) || fail2("Malformed Airtable records");
|
|
1579
|
+
for (let value of page.records) {
|
|
1580
|
+
let r = record2(value);
|
|
1581
|
+
ids2.has(r.id) && fail2("Malformed Airtable pagination"), ids2.add(r.id), records2.push(r);
|
|
1582
|
+
}
|
|
1583
|
+
offset = nextOffset(page.offset, seen);
|
|
1584
|
+
} while (offset);
|
|
1585
|
+
return records2;
|
|
1586
|
+
}
|
|
1587
|
+
mutation || fail2("Unsupported Airtable operation"), keys2(input, ["table", "records"]), (!Array.isArray(input.records) || !input.records.length || input.records.length > 10) && fail2("Invalid Airtable batch");
|
|
1588
|
+
let kind = operation2.slice(8), ids = /* @__PURE__ */ new Set(), records = input.records.map((value) => {
|
|
1589
|
+
let r = object2(value);
|
|
1590
|
+
if (keys2(r, kind === "create" ? ["fields"] : kind === "update" ? ["id", "expected", "set"] : ["id", "expected"]), kind === "create") return { fields: fields3(r.fields, !0) };
|
|
1591
|
+
(!recordId(r.id) || ids.has(r.id)) && fail2("Invalid Airtable batch record ID"), ids.add(r.id);
|
|
1592
|
+
let set = kind === "update" ? fields3(r.set, !0) : void 0;
|
|
1593
|
+
return set && Object.values(set).some((value2) => value2 === null) && fail2("Airtable guard null cannot be an update value"), { id: r.id, expected: fields3(r.expected), ...set ? { set } : {} };
|
|
1594
|
+
});
|
|
1595
|
+
for (let r of records) {
|
|
1596
|
+
if (!("id" in r)) continue;
|
|
1597
|
+
let current2 = record2(await request(`${path}/${r.id}`));
|
|
1598
|
+
current2.id !== r.id && fail2("Malformed Airtable record");
|
|
1599
|
+
for (let [key, value] of Object.entries(r.expected))
|
|
1600
|
+
(value === null ? Object.hasOwn(current2.fields, key) : !Object.hasOwn(current2.fields, key) || !equal(current2.fields[key], value)) && fail2("Airtable reviewed values are stale");
|
|
1601
|
+
}
|
|
1602
|
+
await options.beforeMutation?.();
|
|
1603
|
+
let query = new URLSearchParams();
|
|
1604
|
+
if (kind === "delete") for (let id2 of ids) query.append("records[]", id2);
|
|
1605
|
+
let result2 = await request(`${path}${kind === "delete" ? `?${query}` : ""}`, {
|
|
1606
|
+
method: kind === "create" ? "POST" : kind === "update" ? "PATCH" : "DELETE",
|
|
1607
|
+
...kind === "delete" ? {} : { body: JSON.stringify({ records: records.map((r) => "id" in r ? { id: r.id, fields: r.set } : r) }) }
|
|
1608
|
+
}, !0), acknowledged = [], returned = [];
|
|
1609
|
+
try {
|
|
1610
|
+
if (!Array.isArray(result2.records)) throw new Error();
|
|
1611
|
+
for (let value of result2.records) {
|
|
1612
|
+
let r = object2(value);
|
|
1613
|
+
if (!recordId(r.id) || acknowledged.includes(r.id) || kind !== "create" && !ids.has(r.id) || kind === "delete" && r.deleted !== !0) throw new Error();
|
|
1614
|
+
acknowledged.push(r.id), kind !== "delete" && returned.push(record2(r));
|
|
1615
|
+
}
|
|
1616
|
+
if (acknowledged.length !== records.length) throw new Error();
|
|
1617
|
+
} catch {
|
|
1618
|
+
throw new AirtableOperationError(
|
|
1619
|
+
"Incomplete Airtable mutation acknowledgment",
|
|
1620
|
+
acknowledged.length ? "partial" : "uncertain",
|
|
1621
|
+
acknowledged.length ? [receipt(acknowledged)] : []
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
return { receipt: receipt(acknowledged), ...kind === "delete" ? {} : { records: returned } };
|
|
1625
|
+
}
|
|
1626
|
+
function nextOffset(value, seen) {
|
|
1627
|
+
if (value !== void 0)
|
|
1628
|
+
return (!text2(value) || seen.has(value)) && fail2("Malformed Airtable pagination"), seen.add(value), value;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
var MANAGED_CONNECTION_CAPABILITY_ENV = "ODDINGTON_MANAGED_CONNECTION_CAPABILITY";
|
|
1632
|
+
function managedConnections(capability) {
|
|
1633
|
+
let endpoint = new URL(capability.endpoint);
|
|
1634
|
+
if (endpoint.protocol !== "http:" || endpoint.hostname !== "127.0.0.1" || !endpoint.port || endpoint.pathname !== "/" || endpoint.search || endpoint.hash || endpoint.username || endpoint.password || !/^[0-9a-f-]{36}$/.test(capability.token)) throw new AirtableOperationError("Airtable capability unavailable");
|
|
1635
|
+
return createConnections(async (name, operation2, input) => {
|
|
1636
|
+
let safeInput;
|
|
1637
|
+
try {
|
|
1638
|
+
safeInput = serializable(input);
|
|
1639
|
+
} catch {
|
|
1640
|
+
safeInput = null;
|
|
1641
|
+
}
|
|
1642
|
+
try {
|
|
1643
|
+
let response = await fetch(new URL("airtable", endpoint), {
|
|
1644
|
+
method: "POST",
|
|
1645
|
+
redirect: "error",
|
|
1646
|
+
headers: { authorization: `Bearer ${capability.token}`, "content-type": "application/json" },
|
|
1647
|
+
body: JSON.stringify({ name, operation: operation2, input: safeInput }),
|
|
1648
|
+
signal: AbortSignal.timeout(125e3)
|
|
1649
|
+
}), value = await response.json();
|
|
1650
|
+
if (!response.ok) throw new AirtableOperationError(
|
|
1651
|
+
"Airtable operation failed; the execution retains its evidence",
|
|
1652
|
+
value.effect === "partial" || value.effect === "uncertain" ? value.effect : "unapplied",
|
|
1653
|
+
value.receipts ?? []
|
|
1654
|
+
);
|
|
1655
|
+
return value;
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
throw error instanceof AirtableOperationError ? error : new AirtableOperationError(
|
|
1658
|
+
"Airtable parent acknowledgment unavailable",
|
|
1659
|
+
["records.create", "records.update", "records.delete"].includes(operation2) ? "uncertain" : "unapplied"
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
var observer;
|
|
1666
|
+
async function reportLocalRoutineEffect(value) {
|
|
1667
|
+
await observer?.(value);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
var connections = /* @__PURE__ */ createConnections(async (name, operation2, input) => {
|
|
1671
|
+
try {
|
|
1672
|
+
let managed = process.env[MANAGED_CONNECTION_CAPABILITY_ENV];
|
|
1673
|
+
if (managed) {
|
|
1674
|
+
let client = managedConnections(JSON.parse(managed)).airtable(name);
|
|
1675
|
+
return operation2 === "tables.list" ? await client.tables.list() : await client.records[operation2.slice(8)](input);
|
|
1676
|
+
}
|
|
1677
|
+
let raw = JSON.parse(process.env[ROUTINE_CONNECTION_CREDENTIALS_ENV] ?? "[]");
|
|
1678
|
+
if (!Array.isArray(raw)) throw new Error();
|
|
1679
|
+
let leases = raw.map((value) => parseConnectionLease(value));
|
|
1680
|
+
if (leases.some((lease2) => lease2.provider !== "airtable") || new Set(leases.map((lease2) => lease2.name)).size !== leases.length) throw new Error();
|
|
1681
|
+
let lease = leases.find((lease2) => lease2.name === name);
|
|
1682
|
+
if (!lease || lease.provider !== "airtable") throw new Error();
|
|
1683
|
+
let writing = ["records.create", "records.update", "records.delete"].includes(operation2), captured = serializable(input), result2;
|
|
1684
|
+
try {
|
|
1685
|
+
result2 = await executeAirtableOperation(lease, operation2, captured);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
throw writing && error instanceof AirtableOperationError && await reportLocalRoutineEffect({
|
|
1688
|
+
type: "effect",
|
|
1689
|
+
connection: `airtable/${name}`,
|
|
1690
|
+
operation: operation2,
|
|
1691
|
+
input: captured,
|
|
1692
|
+
effect: error.effect,
|
|
1693
|
+
receipts: error.receipts
|
|
1694
|
+
}), error;
|
|
1695
|
+
}
|
|
1696
|
+
return writing && await reportLocalRoutineEffect({
|
|
1697
|
+
type: "effect",
|
|
1698
|
+
connection: `airtable/${name}`,
|
|
1699
|
+
operation: operation2,
|
|
1700
|
+
input: captured,
|
|
1701
|
+
effect: "confirmed",
|
|
1702
|
+
receipts: [result2.receipt]
|
|
1703
|
+
}), result2;
|
|
1704
|
+
} catch (error) {
|
|
1705
|
+
throw error instanceof AirtableOperationError ? error : new AirtableOperationError("Airtable connection unavailable");
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
|
|
1710
|
+
|
|
1711
|
+
var RECORD_WRITE_INPUT_LIMIT = 8 * 1024 * 1024, RoutineRecordWriteError = class extends Error {
|
|
1712
|
+
constructor(message, effect) {
|
|
1713
|
+
super(message);
|
|
1714
|
+
this.effect = effect;
|
|
1715
|
+
this.name = "RoutineRecordWriteError";
|
|
1716
|
+
}
|
|
1717
|
+
effect;
|
|
1718
|
+
};
|
|
1719
|
+
function validateRecordWrite(value) {
|
|
1720
|
+
let input = serializable(value);
|
|
1721
|
+
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((edit) => edit?.ref)).size !== input.records.length)
|
|
1722
|
+
throw new InboxContractError("Invalid record update batch");
|
|
1723
|
+
for (let edit of input.records) {
|
|
1724
|
+
if (!edit || Array.isArray(edit) || typeof edit != "object" || !validRecordRef(edit.ref))
|
|
1725
|
+
throw new InboxContractError("Invalid record edit");
|
|
1726
|
+
if ("expectedVersion" in edit) {
|
|
1727
|
+
if (Object.keys(edit).sort().join(",") !== "expectedVersion,ref,set" || typeof edit.expectedVersion != "string" || !/^record-v1:[0-9a-f]{64}$/.test(edit.expectedVersion) || !edit.set || Array.isArray(edit.set) || typeof edit.set != "object" || !Object.keys(edit.set).length)
|
|
1728
|
+
throw new InboxContractError("Invalid record edit");
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
if (Object.keys(edit).sort().join(",") !== "fields,path,ref" || typeof edit.path != "string" || !validCreatePath(edit.path) || !edit.fields || Array.isArray(edit.fields) || typeof edit.fields != "object" || !Object.keys(edit.fields).length)
|
|
1732
|
+
throw new InboxContractError("Invalid record creation");
|
|
1733
|
+
}
|
|
1734
|
+
return structuredClone(input);
|
|
1735
|
+
}
|
|
1736
|
+
function validateRecordUpdate(value) {
|
|
1737
|
+
let input = validateRecordWrite(value);
|
|
1738
|
+
if (input.records.some((record3) => !("expectedVersion" in record3)))
|
|
1739
|
+
throw new InboxContractError("Record creation is unavailable for this operation");
|
|
1740
|
+
return input;
|
|
1394
1741
|
}
|
|
1395
|
-
function
|
|
1396
|
-
let
|
|
1397
|
-
if (
|
|
1398
|
-
|
|
1399
|
-
|
|
1742
|
+
function validateRecordDelete(value) {
|
|
1743
|
+
let input = serializable(value);
|
|
1744
|
+
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record3) => record3?.ref)).size !== input.records.length)
|
|
1745
|
+
throw new InboxContractError("Invalid record deletion batch");
|
|
1746
|
+
for (let record3 of input.records)
|
|
1747
|
+
if (!record3 || Array.isArray(record3) || typeof record3 != "object" || Object.keys(record3).sort().join(",") !== "expectedVersion,ref" || !validRecordRef(record3.ref) || typeof record3.expectedVersion != "string" || !/^record-v1:[0-9a-f]{64}$/.test(record3.expectedVersion))
|
|
1748
|
+
throw new InboxContractError("Invalid record deletion");
|
|
1749
|
+
return structuredClone(input);
|
|
1400
1750
|
}
|
|
1401
|
-
|
|
1402
|
-
let
|
|
1403
|
-
if (
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid3();
|
|
1411
|
-
if (!seen.has(name)) {
|
|
1412
|
-
if (seen.size === 16) throw invalid3();
|
|
1413
|
-
seen.add(name), distinct.push(name);
|
|
1414
|
-
}
|
|
1751
|
+
function validateRecordChange(value) {
|
|
1752
|
+
let input = serializable(value);
|
|
1753
|
+
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record3) => record3?.ref)).size !== input.records.length || new TextEncoder().encode(JSON.stringify(input)).byteLength > RECORD_WRITE_INPUT_LIMIT)
|
|
1754
|
+
throw new InboxContractError("Invalid record change batch");
|
|
1755
|
+
let writes = [], deletes = [];
|
|
1756
|
+
for (let record3 of input.records) {
|
|
1757
|
+
if (!record3 || typeof record3 != "object" || Array.isArray(record3))
|
|
1758
|
+
throw new InboxContractError("Invalid record change");
|
|
1759
|
+
"set" in record3 || "fields" in record3 ? writes.push(record3) : deletes.push(record3);
|
|
1415
1760
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1761
|
+
return writes.length && validateRecordWrite({ repository: input.repository, records: writes }), deletes.length && validateRecordDelete({ repository: input.repository, records: deletes }), structuredClone(input);
|
|
1762
|
+
}
|
|
1763
|
+
function validateRoutineRecordWrite(value) {
|
|
1764
|
+
let body2 = serializable(value);
|
|
1765
|
+
if (!body2 || typeof body2 != "object" || Array.isArray(body2) || Object.keys(body2).sort().join(",") !== "input,requestKey" || typeof body2.requestKey != "string" || !body2.requestKey.trim() || body2.requestKey !== body2.requestKey.trim() || body2.requestKey.length > 512)
|
|
1766
|
+
throw new InboxContractError("A stable routine request key is required");
|
|
1767
|
+
return { input: validateRecordChange(body2.input), requestKey: body2.requestKey };
|
|
1768
|
+
}
|
|
1769
|
+
function validCreatePath(value) {
|
|
1770
|
+
return value === value.trim() && value.endsWith(".md") && !value.startsWith("/") && value.split("/").every((segment) => !!segment && ![".", ".."].includes(segment) && /^[a-zA-Z0-9_.-]+$/.test(segment));
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
var sources = /* @__PURE__ */ new WeakMap();
|
|
1774
|
+
function registerBrowserAuth(handle2, source) {
|
|
1775
|
+
sources.set(handle2, source);
|
|
1776
|
+
}
|
|
1777
|
+
function browserAuthSource(handle2) {
|
|
1778
|
+
return sources.get(handle2);
|
|
1432
1779
|
}
|
|
1433
|
-
var files = {
|
|
1434
|
-
async upload(id2) {
|
|
1435
|
-
return operation(async (context2, signal) => {
|
|
1436
|
-
let file = await send(`${idPath(id2)}/upload`, { method: "POST" }, !0, context2, signal);
|
|
1437
|
-
if (file.id !== id2) throw unavailable();
|
|
1438
|
-
return file;
|
|
1439
|
-
});
|
|
1440
|
-
},
|
|
1441
|
-
async create(input, options) {
|
|
1442
|
-
let context2 = await resolveFilesContext();
|
|
1443
|
-
if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "size", "path", "name", "storage", "csv"].includes(key)) || options !== void 0 && (!options || typeof options != "object" || Array.isArray(options) || Object.keys(options).some((key) => key !== "requestKey")))
|
|
1444
|
-
throw invalid3();
|
|
1445
|
-
let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
|
|
1446
|
-
if (!blob && !array2 && !stream) throw invalid3();
|
|
1447
|
-
let intrinsic = blob ? input.file.size : array2 ? input.file.byteLength : void 0;
|
|
1448
|
-
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid3();
|
|
1449
|
-
let name = input.name;
|
|
1450
|
-
name === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
|
|
1451
|
-
let metadata = parseFileCreateMetadata({
|
|
1452
|
-
name,
|
|
1453
|
-
size: intrinsic ?? input.size,
|
|
1454
|
-
path: input.path,
|
|
1455
|
-
storage: input.storage,
|
|
1456
|
-
csv: input.csv,
|
|
1457
|
-
requestKey: options?.requestKey === void 0 ? crypto.randomUUID() : options.requestKey
|
|
1458
|
-
}, FILE_ORIGINAL_MAX_BYTES);
|
|
1459
|
-
if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
|
|
1460
|
-
let header = encodeURIComponent(JSON.stringify(metadata));
|
|
1461
|
-
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
|
|
1462
|
-
if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
|
|
1463
|
-
if (active++, context2.localFiles && metadata.size > MAX_BYTES) {
|
|
1464
|
-
let controller = new AbortController(), file = input.file, offset = 0, body2 = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
|
|
1465
|
-
pull(controller2) {
|
|
1466
|
-
if (offset === file.byteLength) {
|
|
1467
|
-
controller2.close();
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
let end = Math.min(offset + 256 * 1024, file.byteLength);
|
|
1471
|
-
controller2.enqueue(file.subarray(offset, end)), offset = end;
|
|
1472
|
-
}
|
|
1473
|
-
}, { highWaterMark: 0 });
|
|
1474
|
-
try {
|
|
1475
|
-
return await send("", { method: "POST", headers: {
|
|
1476
|
-
"content-type": "application/octet-stream",
|
|
1477
|
-
"x-oddington-file": header
|
|
1478
|
-
}, body: body2 }, !0, context2, controller.signal);
|
|
1479
|
-
} finally {
|
|
1480
|
-
active--;
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
return metadata.size > MAX_BYTES ? multipartCreate(input, metadata, (suffix, init2, kind, signal) => kind === !0 ? send(suffix, init2, !0, context2, signal) : send(suffix, init2, "control", context2, signal)) : timed(async (signal) => {
|
|
1484
|
-
try {
|
|
1485
|
-
let bytes = array2 ? input.file.slice() : await boundedBytes(
|
|
1486
|
-
blob ? input.file.stream() : input.file,
|
|
1487
|
-
metadata.size
|
|
1488
|
-
);
|
|
1489
|
-
if (bytes.byteLength !== metadata.size) throw invalid3();
|
|
1490
|
-
if (signal.aborted) throw unavailable();
|
|
1491
|
-
return await send("", {
|
|
1492
|
-
method: "POST",
|
|
1493
|
-
headers: { "content-type": "application/octet-stream", "x-oddington-file": header },
|
|
1494
|
-
body: bytes.buffer
|
|
1495
|
-
}, !0, context2, signal);
|
|
1496
|
-
} finally {
|
|
1497
|
-
active--;
|
|
1498
|
-
}
|
|
1499
|
-
}).catch((error) => {
|
|
1500
|
-
throw error instanceof FileOperationError ? error : unavailable();
|
|
1501
|
-
});
|
|
1502
|
-
},
|
|
1503
|
-
async enrich(input) {
|
|
1504
|
-
let context2 = await resolveFilesContext();
|
|
1505
|
-
if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "name", "content", "mediaType", "replace"].includes(key)) || typeof input.content != "string" && !(input.content instanceof Uint8Array)) throw invalid3();
|
|
1506
|
-
let path = `${idPath(input.file)}/enrichments`;
|
|
1507
|
-
if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
|
|
1508
|
-
throw new FileOperationError("request_too_large", 413);
|
|
1509
|
-
return operation(async (context3, signal) => {
|
|
1510
|
-
let bytes;
|
|
1511
|
-
if (typeof input.content == "string") {
|
|
1512
|
-
let buffer = new Uint8Array(Math.min(MAX_BYTES, input.content.length * 3)), encoded = new TextEncoder().encodeInto(input.content, buffer);
|
|
1513
|
-
if (encoded.read !== input.content.length) throw new FileOperationError("request_too_large", 413);
|
|
1514
|
-
bytes = buffer.slice(0, encoded.written);
|
|
1515
|
-
} else bytes = input.content.slice();
|
|
1516
|
-
let metadata = parseFileEnrichMetadata({
|
|
1517
|
-
name: input.name,
|
|
1518
|
-
size: bytes.byteLength,
|
|
1519
|
-
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
1520
|
-
...input.replace === void 0 ? {} : { replace: input.replace }
|
|
1521
|
-
}, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
|
|
1522
|
-
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
|
|
1523
|
-
let result2 = await send(path, { method: "POST", headers: {
|
|
1524
|
-
"content-type": "application/octet-stream",
|
|
1525
|
-
"x-oddington-enrichment": header
|
|
1526
|
-
}, body: bytes.buffer }, "enrichment", context3, signal);
|
|
1527
|
-
if (result2.name !== metadata.name) throw unavailable();
|
|
1528
|
-
return result2;
|
|
1529
|
-
}, context2);
|
|
1530
|
-
},
|
|
1531
|
-
async get(id2) {
|
|
1532
|
-
return operation((context2, signal) => send(idPath(id2), { method: "GET" }, !0, context2, signal));
|
|
1533
|
-
},
|
|
1534
|
-
read
|
|
1535
|
-
};
|
|
1536
1780
|
|
|
1537
1781
|
var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap(), AppResponseReadError = class extends Error {
|
|
1538
1782
|
}, AppContextDenied = class extends Error {
|
|
@@ -1851,8 +2095,15 @@ function validBinding(value) {
|
|
|
1851
2095
|
}
|
|
1852
2096
|
|
|
1853
2097
|
var installedDispose;
|
|
1854
|
-
function init({ auth }) {
|
|
2098
|
+
function init({ auth: input }) {
|
|
1855
2099
|
installedDispose?.();
|
|
2100
|
+
let registered = browserAuthSource(input), auth2 = registered ?? input;
|
|
2101
|
+
if (!registered && input && "getState" in input || typeof auth2?.getSession != "function" || typeof auth2?.onAuthStateChange != "function") {
|
|
2102
|
+
installedDispose = installMemberContext(async () => {
|
|
2103
|
+
throw new Error("Hosted Data context unavailable.");
|
|
2104
|
+
});
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
1856
2107
|
let endpoint, workspace, localOrigin;
|
|
1857
2108
|
try {
|
|
1858
2109
|
let url = new URL(globalThis.location.origin);
|
|
@@ -1861,13 +2112,13 @@ function init({ auth }) {
|
|
|
1861
2112
|
url.protocol === "https:" && !url.port && !url.username && !url.password && !url.search && !url.hash && url.pathname === "/" && /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(slug) && slug !== "auth" && (endpoint = url.origin, workspace = slug);
|
|
1862
2113
|
} catch {
|
|
1863
2114
|
}
|
|
1864
|
-
let active2 = !0, revision = 0, person, local = localOrigin ? appContext(localOrigin, () => active2) : void 0, { data: { subscription } } =
|
|
2115
|
+
let active2 = !0, revision = 0, person, token, local = localOrigin ? appContext(localOrigin, () => active2) : void 0, { data: { subscription } } = auth2.onAuthStateChange((event2, session) => {
|
|
1865
2116
|
let next = session?.user.id ?? null;
|
|
1866
|
-
(event2 === "SIGNED_OUT" || person !== void 0 && next !== person) && (revision++, local?.invalidate()), person = next;
|
|
2117
|
+
(event2 === "SIGNED_OUT" || person !== void 0 && next !== person || registered && person !== void 0 && event2 === "SIGNED_IN" && token !== session?.access_token) && (revision++, local?.invalidate()), person = next, token = session?.access_token;
|
|
1867
2118
|
}), uninstall = installMemberContext(async () => {
|
|
1868
2119
|
if (local) return local.resolve();
|
|
1869
2120
|
if (!active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
|
|
1870
|
-
let started = revision, { data: { session }, error } = await
|
|
2121
|
+
let started = revision, { data: { session }, error } = await auth2.getSession();
|
|
1871
2122
|
if (!active2 || globalThis.location.origin !== endpoint || started !== revision || error || !session || person !== void 0 && person !== session.user.id)
|
|
1872
2123
|
throw new Error("Hosted Data context unavailable.");
|
|
1873
2124
|
let expiresAt = (session.expires_at ?? 0) * 1e3;
|
|
@@ -1887,6 +2138,403 @@ function init({ auth }) {
|
|
|
1887
2138
|
}
|
|
1888
2139
|
var os = { init };
|
|
1889
2140
|
|
|
2141
|
+
var CLI_CALLBACK = "http://127.0.0.1:53682/oauth/callback";
|
|
2142
|
+
function authorizationConfiguration(value) {
|
|
2143
|
+
let setup = value;
|
|
2144
|
+
if (!setup || typeof setup.clientId != "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(setup.clientId) || setup.redirectUri !== CLI_CALLBACK || !Array.isArray(setup.scopes) || setup.scopes.length !== 1 || setup.scopes[0] !== "email") throw new Error("Unavailable");
|
|
2145
|
+
return { clientId: setup.clientId, redirectUri: CLI_CALLBACK, scopes: ["email"] };
|
|
2146
|
+
}
|
|
2147
|
+
function authorizationDetails(value, id2, setup, userId, account) {
|
|
2148
|
+
let details = value;
|
|
2149
|
+
return !details || details.authorization_id !== id2 || details.redirect_uri !== setup.redirectUri || details.client?.id !== setup.clientId || typeof details.client.name != "string" || !details.client.name.trim() || details.client.name.length > 128 || /[\x00-\x1f\x7f]/.test(details.client.name) || details.user?.id !== userId || details.user.email !== account.email || typeof details.scope != "string" || details.scope.trim() !== "email" ? Object.freeze({ status: "invalid" }) : Object.freeze({
|
|
2150
|
+
status: "consent",
|
|
2151
|
+
clientName: details.client.name.trim(),
|
|
2152
|
+
account,
|
|
2153
|
+
scopes: Object.freeze([...setup.scopes]),
|
|
2154
|
+
callbackAddress: new URL(setup.redirectUri).host
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
function authorizationReturn(value, decision) {
|
|
2158
|
+
let returnUrl = value?.redirect_url;
|
|
2159
|
+
if (typeof returnUrl != "string" || returnUrl.length > 8192 || !returnUrl.startsWith(`${CLI_CALLBACK}?`) || /[\x00-\x20\x7f]/.test(returnUrl)) return Object.freeze({ status: "invalid" });
|
|
2160
|
+
try {
|
|
2161
|
+
let url = new URL(returnUrl);
|
|
2162
|
+
if (url.origin + url.pathname !== CLI_CALLBACK || url.username || url.password || url.hash) return Object.freeze({ status: "invalid" });
|
|
2163
|
+
let parameters = url.searchParams, allowed = ["code", "state", "error", "error_description", "error_uri"];
|
|
2164
|
+
for (let key of parameters.keys())
|
|
2165
|
+
if (!allowed.includes(key) || parameters.getAll(key).length !== 1) return Object.freeze({ status: "invalid" });
|
|
2166
|
+
let code = parameters.get("code"), error = parameters.get("error");
|
|
2167
|
+
return !parameters.get("state") || !!code == !!error || decision === "deny" && !error || decision === "approve" && !code || parameters.has("code") && !code || parameters.has("error") && !error || code && (parameters.has("error_description") || parameters.has("error_uri")) ? Object.freeze({ status: "invalid" }) : Object.freeze({ status: "return", returnUrl });
|
|
2168
|
+
} catch {
|
|
2169
|
+
return Object.freeze({ status: "invalid" });
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
function authorizationFailure(error) {
|
|
2173
|
+
let status = error?.status;
|
|
2174
|
+
return Object.freeze({ status: status === 404 || status === 410 || status === 400 ? "expired" : status === 401 ? "signed_out" : status === 403 || status === 422 ? "invalid" : "unavailable" });
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
function createBrowserAuth(runtime) {
|
|
2178
|
+
let coordinators = /* @__PURE__ */ new Map();
|
|
2179
|
+
function publish(c, state) {
|
|
2180
|
+
c.state = Object.freeze(state);
|
|
2181
|
+
for (let listener of c.listeners)
|
|
2182
|
+
try {
|
|
2183
|
+
listener(c.state);
|
|
2184
|
+
} catch {
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
function notify(c, event2) {
|
|
2188
|
+
for (let listener of c.sessions)
|
|
2189
|
+
try {
|
|
2190
|
+
listener(event2, event2 === "SIGNED_OUT" ? null : c.session);
|
|
2191
|
+
} catch {
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function expire(c) {
|
|
2195
|
+
!c.active || !c.session && c.state.status === "expired" || (c.revision++, c.timer && clearTimeout(c.timer), c.session = null, publish(c, { status: "expired" }), notify(c, "SIGNED_OUT"));
|
|
2196
|
+
}
|
|
2197
|
+
function accept(c, event2, session) {
|
|
2198
|
+
if (!c.active || event2 === "INITIAL_SESSION" && c.state.status !== "initializing" || event2 === "TOKEN_REFRESHED" && !c.session && c.state.status !== "initializing") return;
|
|
2199
|
+
(c.session?.user?.id !== session?.user?.id || event2 === "SIGNED_OUT" || event2 === "SIGNED_IN" && c.session && session?.access_token !== c.session.access_token) && c.revision++, c.timer && clearTimeout(c.timer);
|
|
2200
|
+
let pending = c.state.pending;
|
|
2201
|
+
if (!session) {
|
|
2202
|
+
c.session = null, publish(c, { status: c.state.status === "expired" ? "expired" : "signed_out", ...pending ? { pending } : {} }), notify(c, "SIGNED_OUT");
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
if (!validSession(session)) {
|
|
2206
|
+
c.session = session, expire(c);
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
c.session = session;
|
|
2210
|
+
let email = typeof session.user.email == "string" ? session.user.email.trim() : "", metadata = session.user.user_metadata, displayName = ["name", "full_name", "first_name"].map((key) => metadata?.[key]).find((value) => typeof value == "string" && value.trim());
|
|
2211
|
+
publish(c, { status: "signed_in", account: Object.freeze({ email, displayName: displayName?.trim() || email }), ...pending ? { pending } : {} }), notify(c, event2);
|
|
2212
|
+
let checkExpiry = () => {
|
|
2213
|
+
if (!c.active || !c.session) return;
|
|
2214
|
+
let remaining = c.session.expires_at * 1e3 - Date.now();
|
|
2215
|
+
remaining <= 0 ? expire(c) : c.timer = setTimeout(checkExpiry, Math.min(remaining, 2147483647));
|
|
2216
|
+
};
|
|
2217
|
+
checkExpiry();
|
|
2218
|
+
}
|
|
2219
|
+
function validSession(session) {
|
|
2220
|
+
return typeof session.access_token == "string" && /^[^\s]{1,8192}$/.test(session.access_token) && typeof session.user?.id == "string" && !!session.user.id && Number.isFinite(session.expires_at) && session.expires_at * 1e3 > Date.now();
|
|
2221
|
+
}
|
|
2222
|
+
function createCoordinator(origin) {
|
|
2223
|
+
let c = {
|
|
2224
|
+
state: Object.freeze({ status: "initializing" }),
|
|
2225
|
+
session: null,
|
|
2226
|
+
revision: 0,
|
|
2227
|
+
active: !0,
|
|
2228
|
+
refs: 0,
|
|
2229
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
2230
|
+
sessions: /* @__PURE__ */ new Set(),
|
|
2231
|
+
ready: Promise.resolve()
|
|
2232
|
+
};
|
|
2233
|
+
return c.ready = (async () => {
|
|
2234
|
+
let revision = c.revision;
|
|
2235
|
+
try {
|
|
2236
|
+
if (!allowedOrigin(origin)) throw runtime.failure("unavailable");
|
|
2237
|
+
let response = await fetch(`${origin}/api/auth`, { method: "GET", credentials: "same-origin", redirect: "error", cache: "no-store" });
|
|
2238
|
+
if (!response.ok) throw runtime.failure("unavailable");
|
|
2239
|
+
let configuration = validateConfiguration(await response.json());
|
|
2240
|
+
if (configuration.authorization && origin !== "https://auth.oddington.ai" && origin !== "http://127.0.0.1:5176") throw runtime.failure("unavailable");
|
|
2241
|
+
if (c.authorization = configuration.authorization, !c.active || globalThis.location?.origin !== origin) return;
|
|
2242
|
+
if (c.provider = await runtime.provider(configuration, {
|
|
2243
|
+
revision: () => c.revision,
|
|
2244
|
+
current: (revision2) => current2(c, origin, revision2),
|
|
2245
|
+
canPersistSession: () => c.active && (c.state.status === "initializing" || !!c.session || c.state.pending === "verify_code"),
|
|
2246
|
+
canAuthorize: (accessToken) => current2(c, origin, c.revision) && !!c.session && c.session.access_token === accessToken && c.state.pending !== "sign_out"
|
|
2247
|
+
}), !c.active || globalThis.location?.origin !== origin) {
|
|
2248
|
+
c.provider.dispose();
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
let subscription = c.provider.onAuthStateChange((event2, session) => {
|
|
2252
|
+
c.state.status === "initializing" && c.state.pending === "sign_out" || accept(c, event2, session);
|
|
2253
|
+
}).data.subscription;
|
|
2254
|
+
c.unsubscribe = () => subscription.unsubscribe();
|
|
2255
|
+
let started = c.revision, { data: data3, error } = await c.provider.getSession();
|
|
2256
|
+
if (!current2(c, origin, started)) return;
|
|
2257
|
+
error ? terminalSessionFailure(error) ? expire(c) : (!c.session || !validSession(c.session)) && publish(c, { status: "unavailable", error: "unavailable" }) : c.state.pending !== "sign_out" && accept(c, "INITIAL_SESSION", data3.session);
|
|
2258
|
+
} catch {
|
|
2259
|
+
current2(c, origin, revision) && publish(c, { status: "unavailable", error: "unavailable" });
|
|
2260
|
+
}
|
|
2261
|
+
})(), c;
|
|
2262
|
+
}
|
|
2263
|
+
function current2(c, origin, revision) {
|
|
2264
|
+
return c.session && !validSession(c.session) && expire(c), c.active && c.revision === revision && globalThis.location?.origin === origin;
|
|
2265
|
+
}
|
|
2266
|
+
return { create() {
|
|
2267
|
+
let origin = globalThis.location?.origin ?? "", c = coordinators.get(origin);
|
|
2268
|
+
c || (c = createCoordinator(origin), coordinators.set(origin, c));
|
|
2269
|
+
let coordinator = c;
|
|
2270
|
+
coordinator.refs++;
|
|
2271
|
+
let active2 = !0, ownedAction, authorizationId, authorizationGeneration = 0, inspected, observers = /* @__PURE__ */ new Set(), sessionObservers = /* @__PURE__ */ new Set(), lastState = coordinator.state, observation = (state) => {
|
|
2272
|
+
lastState = state;
|
|
2273
|
+
for (let listener of observers)
|
|
2274
|
+
try {
|
|
2275
|
+
listener(state);
|
|
2276
|
+
} catch {
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
coordinator.listeners.add(observation);
|
|
2280
|
+
let assertActive = () => {
|
|
2281
|
+
if (!active2) throw runtime.failure("disposed");
|
|
2282
|
+
};
|
|
2283
|
+
async function action(kind, work) {
|
|
2284
|
+
if (assertActive(), coordinator.action && kind !== "sign_out" || kind === "sign_out" && coordinator.state.pending === "sign_out") throw runtime.failure("busy");
|
|
2285
|
+
kind === "sign_out" && (coordinator.revision++, notify(coordinator, "SIGNED_OUT"));
|
|
2286
|
+
let marker = {};
|
|
2287
|
+
coordinator.action = marker, ownedAction = marker;
|
|
2288
|
+
let revision = coordinator.revision;
|
|
2289
|
+
publish(coordinator, { ...coordinator.state, error: void 0, pending: kind });
|
|
2290
|
+
try {
|
|
2291
|
+
if (await coordinator.ready, assertActive(), !coordinator.provider || !coordinator.active || globalThis.location?.origin !== origin) throw runtime.failure("unavailable");
|
|
2292
|
+
if (!current2(coordinator, origin, revision) || coordinator.action !== marker || (await work(coordinator.provider), assertActive(), coordinator.action !== marker) || !current2(coordinator, origin, revision) && kind !== "verify_code" && kind !== "sign_out") throw runtime.failure("stale");
|
|
2293
|
+
} catch (error) {
|
|
2294
|
+
let reason = active2 ? current2(coordinator, origin, revision) ? safeReason(error) ?? "unavailable" : "stale" : "disposed";
|
|
2295
|
+
throw active2 && current2(coordinator, origin, revision) && publish(coordinator, { ...coordinator.state, error: reason }), runtime.failure(reason);
|
|
2296
|
+
} finally {
|
|
2297
|
+
coordinator.action === marker && (coordinator.action = void 0, coordinator.active && (publish(coordinator, { ...coordinator.state, pending: void 0 }), kind === "sign_out" && coordinator.session && notify(coordinator, "TOKEN_REFRESHED"))), ownedAction === marker && (ownedAction = void 0);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
async function authorize(kind, id2) {
|
|
2301
|
+
if (!active2) return Object.freeze({ status: "disposed" });
|
|
2302
|
+
if (id2 !== authorizationId && (authorizationId = id2, authorizationGeneration++, inspected = void 0), !id2) return Object.freeze({ status: "missing" });
|
|
2303
|
+
if (typeof id2 != "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(id2)) return Object.freeze({ status: "invalid" });
|
|
2304
|
+
let generation = authorizationGeneration;
|
|
2305
|
+
if (await coordinator.ready, !active2) return Object.freeze({ status: "disposed" });
|
|
2306
|
+
if (generation !== authorizationGeneration) return Object.freeze({ status: "stale" });
|
|
2307
|
+
let revision = coordinator.revision;
|
|
2308
|
+
if (!current2(coordinator, origin, revision)) return Object.freeze({ status: "stale" });
|
|
2309
|
+
if (!coordinator.session || coordinator.state.pending === "sign_out") return Object.freeze({ status: coordinator.state.status === "expired" ? "expired" : coordinator.state.status === "unavailable" ? "unavailable" : "signed_out" });
|
|
2310
|
+
if (!coordinator.authorization || !coordinator.provider) return Object.freeze({ status: "unavailable" });
|
|
2311
|
+
if (coordinator.action) return Object.freeze({ status: "busy" });
|
|
2312
|
+
if (kind !== "get" && (!inspected || inspected.id !== id2 || inspected.revision !== revision)) return Object.freeze({ status: "stale" });
|
|
2313
|
+
if (kind !== "get" && inspected?.result.status !== "consent") return inspected.result;
|
|
2314
|
+
let marker = {};
|
|
2315
|
+
coordinator.action = marker, ownedAction = marker;
|
|
2316
|
+
let requestCurrent = () => active2 && generation === authorizationGeneration && current2(coordinator, origin, revision) && coordinator.action === marker && !!coordinator.session && coordinator.state.pending !== "sign_out", stale = () => Object.freeze({ status: active2 ? "stale" : "disposed" });
|
|
2317
|
+
try {
|
|
2318
|
+
let provider2 = coordinator.provider, response = await provider2.authorization.get(id2, requestCurrent);
|
|
2319
|
+
if (!requestCurrent()) return stale();
|
|
2320
|
+
if (response.error) {
|
|
2321
|
+
inspected = void 0;
|
|
2322
|
+
let failure = authorizationFailure(response.error);
|
|
2323
|
+
return failure.status === "signed_out" && expire(coordinator), failure;
|
|
2324
|
+
}
|
|
2325
|
+
let account = coordinator.state.account, result2 = response.data && typeof response.data == "object" && "redirect_url" in response.data ? authorizationReturn(response.data) : authorizationDetails(response.data, id2, coordinator.authorization, coordinator.session.user.id, account);
|
|
2326
|
+
if (inspected = { id: id2, revision, result: result2 }, kind === "get" || result2.status !== "consent") return result2;
|
|
2327
|
+
if (!requestCurrent()) return stale();
|
|
2328
|
+
let decision = await provider2.authorization[kind](id2, requestCurrent);
|
|
2329
|
+
if (!requestCurrent()) return stale();
|
|
2330
|
+
let returned = decision.error ? authorizationFailure(decision.error) : authorizationReturn(decision.data, kind);
|
|
2331
|
+
return returned.status === "signed_out" && expire(coordinator), inspected = { id: id2, revision, result: returned }, returned;
|
|
2332
|
+
} catch (error) {
|
|
2333
|
+
return !requestCurrent() || (inspected = void 0, safeReason(error) === "stale") ? stale() : Object.freeze({ status: "unavailable" });
|
|
2334
|
+
} finally {
|
|
2335
|
+
coordinator.action === marker && (coordinator.action = void 0), ownedAction === marker && (ownedAction = void 0);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
let handle2 = Object.freeze({
|
|
2339
|
+
authorization: Object.freeze({
|
|
2340
|
+
get: (id2) => authorize("get", id2),
|
|
2341
|
+
approve: (id2) => authorize("approve", id2),
|
|
2342
|
+
deny: (id2) => authorize("deny", id2)
|
|
2343
|
+
}),
|
|
2344
|
+
getState() {
|
|
2345
|
+
return active2 && coordinator.session && !validSession(coordinator.session) && expire(coordinator), lastState;
|
|
2346
|
+
},
|
|
2347
|
+
subscribe(listener) {
|
|
2348
|
+
return assertActive(), observers.add(listener), () => {
|
|
2349
|
+
observers.delete(listener);
|
|
2350
|
+
};
|
|
2351
|
+
},
|
|
2352
|
+
async sendCode(email) {
|
|
2353
|
+
if (assertActive(), !validEmail(email)) throw runtime.failure("invalid_input");
|
|
2354
|
+
await action("send_code", async (provider2) => {
|
|
2355
|
+
let { error } = await provider2.signInWithOtp({ email: email.trim(), options: { shouldCreateUser: !1 } });
|
|
2356
|
+
if (error && !accountRefusal(error)) throw runtime.failure("unavailable");
|
|
2357
|
+
});
|
|
2358
|
+
},
|
|
2359
|
+
async verifyCode(email, code) {
|
|
2360
|
+
if (assertActive(), !validEmail(email) || typeof code != "string" || !/^\d{6}$/.test(code)) throw runtime.failure("invalid_input");
|
|
2361
|
+
await action("verify_code", async (provider2) => {
|
|
2362
|
+
let revision = coordinator.revision, { data: data3, error } = await provider2.verifyOtp({ email: email.trim(), token: code, type: "email" });
|
|
2363
|
+
if (error) throw runtime.failure(accountRefusal(error) ? "invalid_code" : "unavailable");
|
|
2364
|
+
if (!data3.session || !validSession(data3.session)) throw runtime.failure("invalid_code");
|
|
2365
|
+
if (!current2(coordinator, origin, revision) && (coordinator.session?.access_token !== data3.session.access_token || coordinator.revision !== revision + 1 || coordinator.state.pending !== "verify_code")) throw runtime.failure("stale");
|
|
2366
|
+
accept(coordinator, "SIGNED_IN", data3.session);
|
|
2367
|
+
});
|
|
2368
|
+
},
|
|
2369
|
+
async signOut() {
|
|
2370
|
+
assertActive(), await action("sign_out", async (provider2) => {
|
|
2371
|
+
let revision = coordinator.revision, { error } = await provider2.signOut({ scope: "local" });
|
|
2372
|
+
if (error) throw runtime.failure("unavailable");
|
|
2373
|
+
if (!current2(coordinator, origin, revision) && coordinator.session) throw runtime.failure("stale");
|
|
2374
|
+
accept(coordinator, "SIGNED_OUT", null);
|
|
2375
|
+
});
|
|
2376
|
+
},
|
|
2377
|
+
dispose() {
|
|
2378
|
+
if (active2) {
|
|
2379
|
+
active2 = !1, authorizationGeneration++, inspected = void 0, ownedAction && coordinator.action === ownedAction && coordinator.revision++, observers.clear(), coordinator.listeners.delete(observation);
|
|
2380
|
+
for (let listener of sessionObservers)
|
|
2381
|
+
coordinator.sessions.delete(listener), listener("SIGNED_OUT", null);
|
|
2382
|
+
sessionObservers.clear(), --coordinator.refs === 0 && (coordinator.active = !1, coordinator.revision++, coordinator.timer && clearTimeout(coordinator.timer), coordinator.unsubscribe?.(), coordinator.provider?.dispose(), coordinator.provider = void 0, coordinator.session = null, coordinator.listeners.clear(), coordinator.sessions.clear(), coordinators.delete(origin));
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
return registerBrowserAuth(handle2, {
|
|
2387
|
+
async getSession() {
|
|
2388
|
+
assertActive(), await coordinator.ready, assertActive();
|
|
2389
|
+
let revision = coordinator.revision;
|
|
2390
|
+
if (!current2(coordinator, origin, revision) || !coordinator.provider || !coordinator.session || coordinator.state.pending === "sign_out") return { data: { session: null }, error: null };
|
|
2391
|
+
let result2;
|
|
2392
|
+
try {
|
|
2393
|
+
result2 = await coordinator.provider.getSession();
|
|
2394
|
+
} catch {
|
|
2395
|
+
result2 = { data: { session: null }, error: runtime.failure("unavailable") };
|
|
2396
|
+
}
|
|
2397
|
+
return !active2 || !current2(coordinator, origin, revision) ? { data: { session: null }, error: runtime.failure("stale") } : result2.error ? !terminalSessionFailure(result2.error) && coordinator.session && validSession(coordinator.session) ? { data: { session: coordinator.session }, error: null } : (expire(coordinator), { data: { session: null }, error: runtime.failure("unavailable") }) : (accept(coordinator, "TOKEN_REFRESHED", result2.data.session), { data: { session: coordinator.session }, error: null });
|
|
2398
|
+
},
|
|
2399
|
+
onAuthStateChange(callback) {
|
|
2400
|
+
if (!active2)
|
|
2401
|
+
return callback("SIGNED_OUT", null), { data: { subscription: { unsubscribe() {
|
|
2402
|
+
} } } };
|
|
2403
|
+
let listener = (event2, session) => callback(event2, session);
|
|
2404
|
+
return coordinator.sessions.add(listener), sessionObservers.add(listener), callback("INITIAL_SESSION", coordinator.session), { data: { subscription: { unsubscribe() {
|
|
2405
|
+
coordinator.sessions.delete(listener), sessionObservers.delete(listener);
|
|
2406
|
+
} } } };
|
|
2407
|
+
}
|
|
2408
|
+
}), handle2;
|
|
2409
|
+
} };
|
|
2410
|
+
}
|
|
2411
|
+
function safeReason(error) {
|
|
2412
|
+
let reason = error?.reason;
|
|
2413
|
+
return ["invalid_input", "invalid_code", "unavailable", "busy", "stale", "disposed"].includes(reason) ? reason : void 0;
|
|
2414
|
+
}
|
|
2415
|
+
function accountRefusal(error) {
|
|
2416
|
+
return [400, 401, 403, 404, 422].includes(error?.status ?? 0);
|
|
2417
|
+
}
|
|
2418
|
+
function terminalSessionFailure(error) {
|
|
2419
|
+
return [400, 401, 403, 404, 422].includes(error?.status ?? 0);
|
|
2420
|
+
}
|
|
2421
|
+
function validEmail(email) {
|
|
2422
|
+
return typeof email == "string" && email.length <= 320 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
|
|
2423
|
+
}
|
|
2424
|
+
function allowedOrigin(origin) {
|
|
2425
|
+
if (origin === "http://127.0.0.1:5176" || origin === "https://auth.oddington.ai") return !0;
|
|
2426
|
+
try {
|
|
2427
|
+
let url = new URL(origin), slug = url.hostname.endsWith(".oddington.ai") ? url.hostname.slice(0, -13) : "";
|
|
2428
|
+
return slug.length <= 63 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug) && !["auth", "api", "os", "www"].includes(slug) && origin === `https://${slug}.oddington.ai`;
|
|
2429
|
+
} catch {
|
|
2430
|
+
return !1;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
function validateConfiguration(value) {
|
|
2434
|
+
let configuration = value;
|
|
2435
|
+
if (!configuration || typeof configuration.providerUrl != "string" || typeof configuration.publishableKey != "string") throw new Error("Unavailable");
|
|
2436
|
+
let url = new URL(configuration.providerUrl);
|
|
2437
|
+
if (url.origin !== configuration.providerUrl || url.protocol !== "https:" || url.username || url.password || url.port || !configuration.publishableKey) throw new Error("Unavailable");
|
|
2438
|
+
return {
|
|
2439
|
+
providerUrl: url.origin,
|
|
2440
|
+
publishableKey: configuration.publishableKey,
|
|
2441
|
+
...configuration.authorization === void 0 ? {} : { authorization: authorizationConfiguration(configuration.authorization) }
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
var BrowserAuthError = class extends Error {
|
|
2446
|
+
constructor(reason) {
|
|
2447
|
+
super(`Browser authentication failed: ${reason}`);
|
|
2448
|
+
this.reason = reason;
|
|
2449
|
+
this.name = "BrowserAuthError";
|
|
2450
|
+
}
|
|
2451
|
+
reason;
|
|
2452
|
+
};
|
|
2453
|
+
async function provider(configuration, guard) {
|
|
2454
|
+
let { AuthClient } = await import("./auth-provider.js");
|
|
2455
|
+
if (!guard.current(guard.revision())) throw new BrowserAuthError("stale");
|
|
2456
|
+
let provenance = /* @__PURE__ */ new Map(), removalRevision, authorizationRequest, authorizationQueue = Promise.resolve(), storage = globalThis.localStorage, client = new AuthClient({
|
|
2457
|
+
url: `${configuration.providerUrl}/auth/v1`,
|
|
2458
|
+
headers: { apikey: configuration.publishableKey, Authorization: `Bearer ${configuration.publishableKey}` },
|
|
2459
|
+
storageKey: `sb-${new URL(configuration.providerUrl).hostname.split(".")[0]}-auth-token`,
|
|
2460
|
+
persistSession: !0,
|
|
2461
|
+
autoRefreshToken: !0,
|
|
2462
|
+
detectSessionInUrl: !1,
|
|
2463
|
+
storage: {
|
|
2464
|
+
getItem: (key) => storage.getItem(key),
|
|
2465
|
+
removeItem(key) {
|
|
2466
|
+
if (!guard.current(removalRevision ?? guard.revision())) throw new BrowserAuthError("stale");
|
|
2467
|
+
storage.removeItem(key);
|
|
2468
|
+
},
|
|
2469
|
+
setItem(key, value) {
|
|
2470
|
+
let token = JSON.parse(value).access_token, revision = token ? provenance.get(token) : void 0;
|
|
2471
|
+
if (!guard.current(revision ?? guard.revision()) || token && !guard.canPersistSession()) throw new BrowserAuthError("stale");
|
|
2472
|
+
storage.setItem(key, value);
|
|
2473
|
+
}
|
|
2474
|
+
},
|
|
2475
|
+
fetch: Object.assign(async (input, init2) => {
|
|
2476
|
+
let revision = guard.revision(), request = String(input).startsWith(`${configuration.providerUrl}/auth/v1/oauth/authorizations/`) ? authorizationRequest : void 0, rejectStale = () => {
|
|
2477
|
+
throw request && (request.rejected = !0), new BrowserAuthError("stale");
|
|
2478
|
+
};
|
|
2479
|
+
request && (!request.current() || !guard.canAuthorize(new Headers(init2?.headers).get("authorization")?.replace(/^Bearer /, "") ?? "")) && rejectStale();
|
|
2480
|
+
let response = await fetch(input, { ...init2, redirect: "error" }), body2 = await response.arrayBuffer();
|
|
2481
|
+
(!guard.current(revision) || request && !request.current()) && rejectStale();
|
|
2482
|
+
let result2 = new Response(
|
|
2483
|
+
[204, 205, 304].includes(response.status) ? null : body2,
|
|
2484
|
+
{ status: response.status, statusText: response.statusText, headers: response.headers }
|
|
2485
|
+
), json2 = result2.json.bind(result2);
|
|
2486
|
+
return result2.json = async () => {
|
|
2487
|
+
let value = await json2();
|
|
2488
|
+
return (!guard.current(revision) || request && !request.current()) && rejectStale(), value && typeof value.access_token == "string" && provenance.set(value.access_token, revision), value;
|
|
2489
|
+
}, result2;
|
|
2490
|
+
}, { preconnect: fetch.preconnect })
|
|
2491
|
+
});
|
|
2492
|
+
async function authorization(kind, id2, current2) {
|
|
2493
|
+
let previous = authorizationQueue, release;
|
|
2494
|
+
authorizationQueue = new Promise((resolve) => {
|
|
2495
|
+
release = resolve;
|
|
2496
|
+
}), await previous;
|
|
2497
|
+
let request = { current: current2, rejected: !1 };
|
|
2498
|
+
authorizationRequest = request;
|
|
2499
|
+
try {
|
|
2500
|
+
if (!current2()) throw new BrowserAuthError("stale");
|
|
2501
|
+
let result2 = await (kind === "get" ? client.oauth.getAuthorizationDetails(id2) : kind === "approve" ? client.oauth.approveAuthorization(id2, { skipBrowserRedirect: !0 }) : client.oauth.denyAuthorization(id2, { skipBrowserRedirect: !0 }));
|
|
2502
|
+
if (request.rejected || !current2()) throw new BrowserAuthError("stale");
|
|
2503
|
+
return result2;
|
|
2504
|
+
} catch (error) {
|
|
2505
|
+
throw request.rejected || !current2() ? new BrowserAuthError("stale") : error;
|
|
2506
|
+
} finally {
|
|
2507
|
+
authorizationRequest = void 0, release();
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return {
|
|
2511
|
+
authorization: {
|
|
2512
|
+
get: (id2, current2) => authorization("get", id2, current2),
|
|
2513
|
+
approve: (id2, current2) => authorization("approve", id2, current2),
|
|
2514
|
+
deny: (id2, current2) => authorization("deny", id2, current2)
|
|
2515
|
+
},
|
|
2516
|
+
getSession: () => client.getSession(),
|
|
2517
|
+
onAuthStateChange: (callback) => client.onAuthStateChange((event2, session) => {
|
|
2518
|
+
let revision = session ? provenance.get(session.access_token) : void 0;
|
|
2519
|
+
revision !== void 0 && !guard.current(revision) || (callback(event2, session), session && provenance.delete(session.access_token));
|
|
2520
|
+
}),
|
|
2521
|
+
signInWithOtp: (input) => client.signInWithOtp(input),
|
|
2522
|
+
verifyOtp: (input) => client.verifyOtp(input),
|
|
2523
|
+
async signOut(input) {
|
|
2524
|
+
removalRevision = guard.revision();
|
|
2525
|
+
try {
|
|
2526
|
+
return await client.signOut(input);
|
|
2527
|
+
} finally {
|
|
2528
|
+
removalRevision = void 0;
|
|
2529
|
+
}
|
|
2530
|
+
},
|
|
2531
|
+
dispose() {
|
|
2532
|
+
provenance.clear(), client.dispose();
|
|
2533
|
+
}
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
var auth = /* @__PURE__ */ createBrowserAuth({ provider, failure: (reason) => new BrowserAuthError(reason) });
|
|
2537
|
+
|
|
1890
2538
|
var identifier = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, isReason = (value) => value === "authority_unavailable" || value === "execution_unavailable" || value === "invalid_input", AppRoutineError = class extends Error {
|
|
1891
2539
|
constructor(reason) {
|
|
1892
2540
|
super(reason);
|
|
@@ -2070,7 +2718,7 @@ var app = Object.freeze({ routines: new Proxy(/* @__PURE__ */ Object.create(null
|
|
|
2070
2718
|
deleteProperty: () => !1
|
|
2071
2719
|
}) });
|
|
2072
2720
|
|
|
2073
|
-
var
|
|
2721
|
+
var LIMIT2 = 16 * 1024, context;
|
|
2074
2722
|
var appRun = {
|
|
2075
2723
|
current() {
|
|
2076
2724
|
return context;
|
|
@@ -2091,7 +2739,7 @@ function validateCsvHints(value) {
|
|
|
2091
2739
|
return v;
|
|
2092
2740
|
}
|
|
2093
2741
|
|
|
2094
|
-
function
|
|
2742
|
+
function routinePublicationClient2(options = {}) {
|
|
2095
2743
|
let environment = {}, local = void 0, workspace = options.workspace ?? environment.ODDINGTON_ROUTINE_WORKSPACE, capability = options.capability ?? environment.ODDINGTON_ROUTINE_RUN;
|
|
2096
2744
|
if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== void 0 && !/^[0-9a-f]{64}$/.test(capability))
|
|
2097
2745
|
throw new Error("A workspace and valid optional routine capability are required");
|
|
@@ -2159,6 +2807,7 @@ function routinePublicationClient(options = {}) {
|
|
|
2159
2807
|
export {
|
|
2160
2808
|
AirtableOperationError,
|
|
2161
2809
|
AppRoutineError,
|
|
2810
|
+
BrowserAuthError,
|
|
2162
2811
|
DataOperationError,
|
|
2163
2812
|
FileOperationError,
|
|
2164
2813
|
InboxContractError,
|
|
@@ -2166,10 +2815,13 @@ export {
|
|
|
2166
2815
|
app,
|
|
2167
2816
|
appRun,
|
|
2168
2817
|
asksBlock,
|
|
2818
|
+
auth,
|
|
2169
2819
|
connections,
|
|
2170
2820
|
continueAsk,
|
|
2171
2821
|
data,
|
|
2172
2822
|
defineHandler,
|
|
2823
|
+
defineRoutine,
|
|
2824
|
+
file,
|
|
2173
2825
|
fileBlock,
|
|
2174
2826
|
files,
|
|
2175
2827
|
os,
|
|
@@ -2179,6 +2831,7 @@ export {
|
|
|
2179
2831
|
resolveAsk,
|
|
2180
2832
|
respond,
|
|
2181
2833
|
richTextBlock,
|
|
2182
|
-
routinePublicationClient,
|
|
2834
|
+
routinePublicationClient2 as routinePublicationClient,
|
|
2835
|
+
schema,
|
|
2183
2836
|
selection
|
|
2184
2837
|
};
|