@oddington/sdk 0.1.10 → 0.1.12
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 +164 -29
- package/dist/auth-provider.js +3909 -0
- package/dist/browser.js +1718 -1064
- package/dist/index.js +62 -24400
- package/dist/types/packages/sdk/src/auth-types.d.ts +40 -0
- package/dist/types/packages/sdk/src/public.d.ts +15 -2
- 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/data-contract.d.ts +22 -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,1163 +379,1404 @@ 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
|
-
|
|
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);
|
|
411
|
+
}
|
|
412
|
+
return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid5() : f;
|
|
413
|
+
}
|
|
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;
|
|
421
|
+
}
|
|
422
|
+
function safeEnrichmentName(value) {
|
|
423
|
+
return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
|
|
424
|
+
}
|
|
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;
|
|
434
|
+
}
|
|
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);
|
|
437
|
+
}
|
|
438
|
+
function safeFilePath(value) {
|
|
439
|
+
return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
|
|
440
|
+
}
|
|
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 };
|
|
460
|
+
}
|
|
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
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
var AppRoutineContractError = class extends Error {
|
|
473
|
+
constructor(code) {
|
|
474
|
+
super(code);
|
|
475
|
+
this.code = code;
|
|
476
|
+
}
|
|
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");
|
|
385
482
|
};
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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();
|
|
397
531
|
}
|
|
398
|
-
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)
|
|
399
|
-
throw new InboxContractError("Invalid record creation");
|
|
400
532
|
}
|
|
401
|
-
|
|
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 } };
|
|
402
536
|
}
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
+
}
|
|
408
544
|
}
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
+
}
|
|
568
|
+
}
|
|
417
569
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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);
|
|
427
580
|
}
|
|
428
|
-
return
|
|
581
|
+
return value;
|
|
429
582
|
}
|
|
430
|
-
function
|
|
431
|
-
|
|
432
|
-
if (!
|
|
433
|
-
|
|
434
|
-
|
|
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);
|
|
435
599
|
}
|
|
436
|
-
function
|
|
437
|
-
|
|
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));
|
|
438
604
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
605
|
+
function declaration(value) {
|
|
606
|
+
try {
|
|
607
|
+
json(value);
|
|
608
|
+
} catch {
|
|
609
|
+
return invalidContract2();
|
|
610
|
+
}
|
|
611
|
+
return freeze(parseAppRoutineContract({ routine: "definition", input: value, steps: [{ id: "inspect" }] }).input);
|
|
442
612
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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)
|
|
657
|
+
});
|
|
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;
|
|
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;
|
|
454
696
|
}
|
|
455
|
-
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)
|
|
456
|
-
return v;
|
|
457
|
-
throw new Error("Invalid connection credential");
|
|
458
697
|
}
|
|
459
698
|
|
|
460
|
-
var
|
|
461
|
-
constructor(
|
|
462
|
-
super(
|
|
699
|
+
var DataOperationError = class extends Error {
|
|
700
|
+
constructor(code, retryable = !1, effect, component) {
|
|
701
|
+
super(`Data operation failed: ${code}`);
|
|
702
|
+
this.code = code;
|
|
703
|
+
this.retryable = retryable;
|
|
463
704
|
this.effect = effect;
|
|
464
|
-
this.
|
|
465
|
-
this.name = "
|
|
705
|
+
this.component = component;
|
|
706
|
+
this.name = "DataOperationError";
|
|
466
707
|
}
|
|
708
|
+
code;
|
|
709
|
+
retryable;
|
|
467
710
|
effect;
|
|
468
|
-
|
|
711
|
+
component;
|
|
469
712
|
};
|
|
470
713
|
|
|
471
|
-
var
|
|
472
|
-
|
|
473
|
-
|
|
714
|
+
var invalid2 = () => {
|
|
715
|
+
throw new DataOperationError("invalid_query");
|
|
716
|
+
}, historyText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
|
|
717
|
+
function historyVersion(value) {
|
|
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);
|
|
474
719
|
}
|
|
475
|
-
function
|
|
476
|
-
|
|
720
|
+
function historyProperties(value) {
|
|
721
|
+
if (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return invalid2();
|
|
722
|
+
let descriptors = Object.getOwnPropertyDescriptors(value);
|
|
723
|
+
return Reflect.ownKeys(value).some((key) => typeof key != "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")) ? invalid2() : descriptors;
|
|
477
724
|
}
|
|
478
|
-
|
|
479
|
-
|
|
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);
|
|
726
|
+
function historyArray(value) {
|
|
727
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) return invalid2();
|
|
728
|
+
let descriptors = Object.getOwnPropertyDescriptors(value);
|
|
729
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) return invalid2();
|
|
730
|
+
let result2 = [];
|
|
731
|
+
for (let i = 0; i < value.length; i++) {
|
|
732
|
+
let descriptor2 = descriptors[i];
|
|
733
|
+
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value")) return invalid2();
|
|
734
|
+
result2.push(descriptor2.value);
|
|
735
|
+
}
|
|
736
|
+
return result2;
|
|
480
737
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
738
|
+
function captureOptions(value, changes) {
|
|
739
|
+
if (value === void 0) return {};
|
|
740
|
+
let descriptors = historyProperties(value), result2 = {}, allowed = changes ? ["collections", "refs", "from", "through", "limit", "after"] : ["limit", "before"];
|
|
741
|
+
if (Object.keys(descriptors).some((key) => !allowed.includes(key))) return invalid2();
|
|
742
|
+
for (let key of allowed) {
|
|
743
|
+
if (!Object.hasOwn(descriptors, key)) continue;
|
|
744
|
+
let scalar = descriptors[key].value;
|
|
745
|
+
if (key === "collections" || key === "refs") {
|
|
746
|
+
let values = historyArray(scalar);
|
|
747
|
+
if (!values.length || values.length > (key === "collections" ? 16 : 100) || !values.every(key === "collections" ? historyCollection : validRecordRef) || new Set(values).size !== values.length) return invalid2();
|
|
748
|
+
result2[key] = values;
|
|
749
|
+
} else if (key === "limit") {
|
|
750
|
+
if (!Number.isInteger(scalar) || scalar < 1 || scalar > (changes ? 500 : 100)) return invalid2();
|
|
751
|
+
result2.limit = scalar;
|
|
752
|
+
} else {
|
|
753
|
+
if (!(key === "after" ? historyCursor(scalar) : historyVersion(scalar))) return invalid2();
|
|
754
|
+
result2[key] = scalar;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return result2;
|
|
483
758
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
(
|
|
759
|
+
var captureHistoryListOptions = (value) => captureOptions(value, !1), captureHistoryChangesOptions = (value) => captureOptions(value, !0);
|
|
760
|
+
function captureHistoryInstant(value) {
|
|
761
|
+
if (!historyText(value) || value.length > 1024) return invalid2();
|
|
762
|
+
let match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(value);
|
|
763
|
+
if (!match) return invalid2();
|
|
764
|
+
let [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number), days = new Date(Date.UTC(year, month, 0));
|
|
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;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
var invalid3 = () => {
|
|
769
|
+
throw new DataOperationError("invalid_query");
|
|
770
|
+
};
|
|
771
|
+
function captureRecordsSearchInput(query, options) {
|
|
772
|
+
if (!historyText(query)) return invalid3();
|
|
773
|
+
let length = [...query.trim()].length;
|
|
774
|
+
if (length < 1 || length > 256) return invalid3();
|
|
775
|
+
if (options === void 0) return { query };
|
|
776
|
+
let descriptors = historyProperties(options);
|
|
777
|
+
if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key))) return invalid3();
|
|
778
|
+
let captured = {};
|
|
779
|
+
if (Object.hasOwn(descriptors, "collections")) {
|
|
780
|
+
let supplied = descriptors.collections.value;
|
|
781
|
+
if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return invalid3();
|
|
782
|
+
let collections = [];
|
|
783
|
+
for (let index = 0; index < supplied.length; index++) {
|
|
784
|
+
let descriptor2 = Object.getOwnPropertyDescriptor(supplied, String(index));
|
|
785
|
+
if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !historyText(descriptor2.value)) return invalid3();
|
|
786
|
+
collections.push(descriptor2.value);
|
|
787
|
+
}
|
|
788
|
+
if (new Set(collections).size !== collections.length) return invalid3();
|
|
789
|
+
captured.collections = collections;
|
|
790
|
+
}
|
|
791
|
+
if (Object.hasOwn(descriptors, "limit")) {
|
|
792
|
+
let supplied = descriptors.limit.value;
|
|
793
|
+
if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid3();
|
|
794
|
+
captured.limit = supplied;
|
|
795
|
+
}
|
|
796
|
+
return { query, options: captured };
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
var DATA_REQUEST_LIMIT = 256 * 1024, invalidChange = () => {
|
|
800
|
+
throw new DataOperationError("invalid_change", !1, "unapplied");
|
|
801
|
+
};
|
|
802
|
+
function mapping(value) {
|
|
487
803
|
try {
|
|
488
|
-
return
|
|
804
|
+
return historyProperties(value), value;
|
|
489
805
|
} catch {
|
|
490
|
-
return
|
|
806
|
+
return invalidChange();
|
|
491
807
|
}
|
|
492
808
|
}
|
|
493
|
-
function
|
|
494
|
-
|
|
495
|
-
|
|
809
|
+
function captureValue(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
|
|
810
|
+
if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return value;
|
|
811
|
+
if (!value || typeof value != "object" || ancestors.has(value) || depth > 32) return invalidChange();
|
|
812
|
+
ancestors.add(value);
|
|
813
|
+
try {
|
|
814
|
+
return Array.isArray(value) ? value.length > 1e5 || Object.keys(value).length !== value.length || Object.getOwnPropertySymbols(value).length ? invalidChange() : Array.from(value, (item) => captureValue(item, ancestors, depth + 1)) : Object.fromEntries(Object.entries(mapping(value)).map(([key, item]) => ["__proto__", "constructor", "prototype"].includes(key) ? invalidChange() : [key, captureValue(item, ancestors, depth + 1)]));
|
|
815
|
+
} finally {
|
|
816
|
+
ancestors.delete(value);
|
|
817
|
+
}
|
|
496
818
|
}
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
let left = Object.keys(a), right = Object.keys(b);
|
|
501
|
-
return left.length === right.length && left.every((k) => Object.hasOwn(b, k) && equal(
|
|
502
|
-
a[k],
|
|
503
|
-
b[k]
|
|
504
|
-
));
|
|
819
|
+
function captureDataDelete(record3) {
|
|
820
|
+
let identity = mapping(record3);
|
|
821
|
+
return !validRecordRef(identity.$ref) || typeof identity.$version != "string" || !identity.$version.length || identity.$version.length > 512 ? invalidChange() : { ref: identity.$ref, expectedVersion: identity.$version };
|
|
505
822
|
}
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
let call = (operation2, input) => transport(name, operation2, input);
|
|
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
|
-
} };
|
|
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) };
|
|
521
826
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
return fail2("Invalid Airtable input");
|
|
530
|
-
}
|
|
531
|
-
let mutation = ["records.create", "records.update", "records.delete"].includes(operation2), receipt = (ids2) => ({
|
|
532
|
-
provider: "airtable",
|
|
533
|
-
connection: `airtable/${name}`,
|
|
534
|
-
baseId,
|
|
535
|
-
table: input.table,
|
|
536
|
-
operation: operation2.slice(8),
|
|
537
|
-
records: ids2.map((id2) => ({ id: id2 }))
|
|
827
|
+
function captureIdentities(value, update, allowEmpty = !0) {
|
|
828
|
+
if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4) return invalidChange();
|
|
829
|
+
let captured = value.map((item) => {
|
|
830
|
+
let row = mapping(item);
|
|
831
|
+
if (Object.keys(row).sort().join(",") !== (update ? "expectedVersion,ref,set" : "expectedVersion,ref")) return invalidChange();
|
|
832
|
+
let identity = { $ref: row.ref, $version: row.expectedVersion };
|
|
833
|
+
return update ? captureDataUpdate(identity, row.set) : captureDataDelete(identity);
|
|
538
834
|
});
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
try {
|
|
555
|
-
if (!response.headers.get("content-type")?.toLowerCase().includes("application/json")) throw new Error();
|
|
556
|
-
let value = object2(await response.json()), safe = serializable(value);
|
|
557
|
-
if (Object.hasOwn(value, "error") || JSON.stringify(safe).includes(accessToken)) throw new Error();
|
|
558
|
-
return value;
|
|
559
|
-
} catch {
|
|
560
|
-
throw new AirtableOperationError("Malformed Airtable acknowledgment", write ? "uncertain" : "unapplied");
|
|
561
|
-
}
|
|
562
|
-
}
|
|
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;
|
|
835
|
+
return new Set(captured.map((item) => item.ref)).size !== captured.length ? invalidChange() : captured;
|
|
836
|
+
}
|
|
837
|
+
function captureDataUpdates(value, allowEmpty = !1) {
|
|
838
|
+
return captureIdentities(value, !0, allowEmpty);
|
|
839
|
+
}
|
|
840
|
+
function captureDataCreate(value) {
|
|
841
|
+
let row = mapping(value);
|
|
842
|
+
if (!validRecordRef(row.$ref) || Object.keys(row).some((key) => key.startsWith("$") && !["$ref", "$filing"].includes(key)))
|
|
843
|
+
return invalidChange();
|
|
844
|
+
let { $ref: ref, $filing, ...fields4 } = row, filing;
|
|
845
|
+
if ($filing !== void 0) {
|
|
846
|
+
let components = mapping($filing);
|
|
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)))
|
|
848
|
+
return invalidChange();
|
|
849
|
+
filing = { ...components };
|
|
605
850
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
851
|
+
return { ref, fields: captureValue(fields4), ...filing ? { filing } : {} };
|
|
852
|
+
}
|
|
853
|
+
function captureDataChanges(updatesValue, createsValue = [], deletesValue = []) {
|
|
854
|
+
let updates = captureDataUpdates(updatesValue, !0);
|
|
855
|
+
if (!Array.isArray(createsValue) || createsValue.length > 1e4) return invalidChange();
|
|
856
|
+
let creates = createsValue.map((item) => {
|
|
857
|
+
let row = mapping(item);
|
|
858
|
+
if (Object.keys(row).some((key) => !["ref", "fields", "filing"].includes(key)) || !Object.hasOwn(row, "ref") || !Object.hasOwn(row, "fields")) return invalidChange();
|
|
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 } : {} });
|
|
861
|
+
}), deletes = captureIdentities(deletesValue, !1), refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
|
|
862
|
+
return !refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length ? invalidChange() : { updates, creates, deletes };
|
|
863
|
+
}
|
|
864
|
+
function checkDataRequestSize(input) {
|
|
865
|
+
if (new TextEncoder().encode(JSON.stringify(input)).byteLength > DATA_REQUEST_LIMIT)
|
|
866
|
+
throw new DataOperationError("request_too_large", !1, "unapplied");
|
|
867
|
+
}
|
|
868
|
+
function dataTransportError(status, value) {
|
|
869
|
+
let failures2 = {
|
|
870
|
+
400: ["invalid_query", "invalid_change"],
|
|
871
|
+
401: ["unauthenticated"],
|
|
872
|
+
403: ["access_denied"],
|
|
873
|
+
409: ["conflict", "publication_pending"],
|
|
874
|
+
413: ["request_too_large"],
|
|
875
|
+
429: ["capacity"],
|
|
876
|
+
503: ["unavailable", "publication_pending"]
|
|
877
|
+
}, error = value && typeof value == "object" && "error" in value ? value.error : void 0, row = error && typeof error == "object" ? error : void 0, code = failures2[status]?.find((allowed) => allowed === row?.code) ?? "unavailable", effect = ["unapplied", "uncertain", "applied"].includes(String(row?.effect)) ? row.effect : void 0;
|
|
878
|
+
return new DataOperationError(
|
|
879
|
+
code,
|
|
880
|
+
["capacity", "unavailable", "publication_pending"].includes(code),
|
|
881
|
+
code === "conflict" ? "unapplied" : effect,
|
|
882
|
+
code === "invalid_change" && typeof row?.component == "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(row.component) ? row.component : void 0
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
var AUTH_ORIGIN = "https://auth.oddington.ai";
|
|
887
|
+
function oddingtonOrigin(environment = process.env) {
|
|
888
|
+
let value = environment.ODDINGTON_OS_ORIGIN ?? AUTH_ORIGIN, url = new URL(value);
|
|
889
|
+
if (url.protocol !== "https:" && url.hostname !== "127.0.0.1")
|
|
890
|
+
throw new Error("ODDINGTON_OS_ORIGIN must use HTTPS");
|
|
891
|
+
return url.origin;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
var memberContext;
|
|
895
|
+
function installMemberContext(resolve) {
|
|
896
|
+
return memberContext = resolve, () => {
|
|
897
|
+
memberContext === resolve && (memberContext = void 0);
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
async function resolveAppContext() {
|
|
901
|
+
let host = memberContext;
|
|
902
|
+
if (!host) throw new Error("App context unavailable.");
|
|
903
|
+
let context2 = await host();
|
|
904
|
+
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now() || context2.kind !== "local" && (typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)))
|
|
905
|
+
throw new Error("App context unavailable.");
|
|
906
|
+
return context2;
|
|
907
|
+
}
|
|
908
|
+
async function resolveDataContext() {
|
|
909
|
+
try {
|
|
910
|
+
let host = memberContext;
|
|
911
|
+
if (host !== void 0) {
|
|
912
|
+
let context2 = await host();
|
|
913
|
+
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now()) throw new Error();
|
|
914
|
+
if (context2.kind === "local") return { local: context2.requestData };
|
|
915
|
+
if (!context2 || !Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
|
|
916
|
+
return checked(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` });
|
|
917
|
+
}
|
|
918
|
+
throw new Error();
|
|
919
|
+
} catch {
|
|
920
|
+
throw new DataOperationError("context_unavailable");
|
|
620
921
|
}
|
|
621
|
-
|
|
622
|
-
|
|
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 = [];
|
|
922
|
+
}
|
|
923
|
+
async function resolveFilesContext() {
|
|
628
924
|
try {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
let
|
|
632
|
-
if (
|
|
633
|
-
|
|
925
|
+
let environment = {}, host = memberContext;
|
|
926
|
+
if (host) {
|
|
927
|
+
let context2 = await host();
|
|
928
|
+
if (context2.kind === "local") throw new Error();
|
|
929
|
+
if (!Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
|
|
930
|
+
return {
|
|
931
|
+
...filesContext(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` }),
|
|
932
|
+
isCurrent: context2.isCurrent,
|
|
933
|
+
local: void 0,
|
|
934
|
+
localFiles: void 0
|
|
935
|
+
};
|
|
634
936
|
}
|
|
635
|
-
|
|
937
|
+
throw new Error();
|
|
636
938
|
} catch {
|
|
637
|
-
throw new
|
|
638
|
-
"Incomplete Airtable mutation acknowledgment",
|
|
639
|
-
acknowledged.length ? "partial" : "uncertain",
|
|
640
|
-
acknowledged.length ? [receipt(acknowledged)] : []
|
|
641
|
-
);
|
|
939
|
+
throw new FileOperationError("unsupported_context", 400);
|
|
642
940
|
}
|
|
643
|
-
return { receipt: receipt(acknowledged), ...kind === "delete" ? {} : { records: returned } };
|
|
644
941
|
}
|
|
645
|
-
function
|
|
646
|
-
if (
|
|
647
|
-
|
|
942
|
+
function filesContext(workspace, endpoint, headers) {
|
|
943
|
+
if (!workspace || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(workspace) || workspace.length > 63 || ["auth", "www", "api", "os"].includes(workspace) || endpoint !== `https://${workspace}.oddington.ai`) throw new Error();
|
|
944
|
+
return { url: `${endpoint}/api/files`, headers: { ...headers, "x-oddington-workspace": workspace }, isCurrent: () => !0 };
|
|
945
|
+
}
|
|
946
|
+
function checked(workspace, endpoint, headers) {
|
|
947
|
+
if (!workspace || !/^[a-z0-9][a-z0-9-]{0,127}$/.test(workspace)) throw new Error();
|
|
948
|
+
let origin = new URL(endpoint);
|
|
949
|
+
if (origin.protocol !== "https:" || origin.username || origin.password || origin.search || origin.hash || origin.pathname !== "/")
|
|
950
|
+
throw new Error();
|
|
951
|
+
return { url: `${origin.origin}/api/data`, headers: { ...headers, "x-oddington-workspace": workspace } };
|
|
648
952
|
}
|
|
649
953
|
|
|
650
|
-
var
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
-
);
|
|
954
|
+
var data = {
|
|
955
|
+
repository(repository) {
|
|
956
|
+
async function send2(input) {
|
|
957
|
+
let context2 = await resolveDataContext();
|
|
958
|
+
try {
|
|
959
|
+
let envelope = { repository, ...input };
|
|
960
|
+
if (checkDataRequestSize(envelope), "local" in context2) return await context2.local("data/query", envelope);
|
|
961
|
+
let response = await fetch(context2.url, {
|
|
962
|
+
method: "POST",
|
|
963
|
+
redirect: "error",
|
|
964
|
+
cache: "no-store",
|
|
965
|
+
headers: { "content-type": "application/json", ...context2.headers },
|
|
966
|
+
body: JSON.stringify(envelope)
|
|
967
|
+
}), result2 = await response.json();
|
|
968
|
+
if (!response.ok)
|
|
969
|
+
throw dataTransportError(response.status, result2);
|
|
970
|
+
return result2;
|
|
971
|
+
} catch (error) {
|
|
972
|
+
throw error instanceof DataOperationError ? error : new DataOperationError("unavailable", !0);
|
|
973
|
+
}
|
|
680
974
|
}
|
|
681
|
-
|
|
682
|
-
|
|
975
|
+
return {
|
|
976
|
+
collections: {
|
|
977
|
+
list: () => send2({ operation: "collections.list" }),
|
|
978
|
+
schema: (name) => send2({ operation: "collections.schema", name })
|
|
979
|
+
},
|
|
980
|
+
records: {
|
|
981
|
+
select: (query) => send2({ operation: "records.select", query }),
|
|
982
|
+
search: async (query, options) => send2({ operation: "records.search", ...captureRecordsSearchInput(query, options) })
|
|
983
|
+
},
|
|
984
|
+
history: {
|
|
985
|
+
list: async (options) => send2({ operation: "history.list", options: captureHistoryListOptions(options) }),
|
|
986
|
+
at: async (instant) => send2({ operation: "history.at", instant: captureHistoryInstant(instant) }),
|
|
987
|
+
changes: async (options) => send2({ operation: "history.changes", options: captureHistoryChangesOptions(options) })
|
|
988
|
+
},
|
|
989
|
+
changes() {
|
|
990
|
+
let updates = [], creates = [], deletes = [], publicationId, pending, confirmed, assertMutable = () => {
|
|
991
|
+
if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
|
|
992
|
+
}, snapshot = (nextUpdates = updates, nextCreates = creates, nextDeletes = deletes) => {
|
|
993
|
+
let { updates: updates2, creates: creates2, deletes: deletes2 } = captureDataChanges(nextUpdates, nextCreates, nextDeletes), input = { updates: updates2, ...creates2.length ? { creates: creates2 } : {}, ...deletes2.length ? { deletes: deletes2 } : {} };
|
|
994
|
+
return checkDataRequestSize({
|
|
995
|
+
repository,
|
|
996
|
+
operation: "changes.publish",
|
|
997
|
+
...input,
|
|
998
|
+
publicationId: publicationId ?? "00000000-0000-4000-8000-000000000000"
|
|
999
|
+
}), input;
|
|
1000
|
+
};
|
|
1001
|
+
return {
|
|
1002
|
+
create(record3) {
|
|
1003
|
+
assertMutable();
|
|
1004
|
+
let next = [...creates, captureDataCreate(record3)];
|
|
1005
|
+
snapshot(updates, next, deletes), creates = next;
|
|
1006
|
+
},
|
|
1007
|
+
update(record3, set) {
|
|
1008
|
+
assertMutable();
|
|
1009
|
+
let next = [...updates, captureDataUpdate(record3, set)];
|
|
1010
|
+
snapshot(next, creates, deletes), updates = next;
|
|
1011
|
+
},
|
|
1012
|
+
delete(record3) {
|
|
1013
|
+
assertMutable();
|
|
1014
|
+
let next = [...deletes, captureDataDelete(record3)];
|
|
1015
|
+
snapshot(updates, creates, next), deletes = next;
|
|
1016
|
+
},
|
|
1017
|
+
async validate() {
|
|
1018
|
+
await send2({ operation: "changes.validate", ...snapshot() });
|
|
1019
|
+
},
|
|
1020
|
+
publish() {
|
|
1021
|
+
if (confirmed) return Promise.resolve(structuredClone(confirmed));
|
|
1022
|
+
if (!pending) {
|
|
1023
|
+
let captured;
|
|
1024
|
+
try {
|
|
1025
|
+
captured = snapshot();
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
return Promise.reject(error);
|
|
1028
|
+
}
|
|
1029
|
+
publicationId ??= crypto.randomUUID(), pending = send2({ operation: "changes.publish", ...captured, publicationId }).then((result2) => (confirmed = structuredClone(result2), result2)).catch((error) => {
|
|
1030
|
+
throw error instanceof DataOperationError ? new DataOperationError(
|
|
1031
|
+
error.code,
|
|
1032
|
+
error.retryable,
|
|
1033
|
+
error.effect ?? (["unavailable", "publication_pending"].includes(error.code) ? "uncertain" : "unapplied"),
|
|
1034
|
+
error.component
|
|
1035
|
+
) : new DataOperationError("unavailable", !0, "uncertain");
|
|
1036
|
+
}).finally(() => {
|
|
1037
|
+
pending = void 0;
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
return pending.then((result2) => structuredClone(result2));
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
683
1047
|
|
|
684
|
-
var
|
|
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 = {
|
|
1049
|
+
400: ["invalid_input", "unsupported_context"],
|
|
1050
|
+
401: ["unauthenticated"],
|
|
1051
|
+
403: ["access_denied"],
|
|
1052
|
+
405: ["method_not_allowed"],
|
|
1053
|
+
409: ["conflict", "local_only"],
|
|
1054
|
+
413: ["request_too_large"],
|
|
1055
|
+
429: ["capacity"],
|
|
1056
|
+
503: ["integrity", "unavailable"]
|
|
1057
|
+
};
|
|
1058
|
+
async function boundedBytes(stream, limit) {
|
|
1059
|
+
if (!stream) return new Uint8Array();
|
|
1060
|
+
let reader = stream.getReader(), bytes = new Uint8Array(limit), size = 0;
|
|
685
1061
|
try {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1062
|
+
for (; ; ) {
|
|
1063
|
+
let { done, value } = await reader.read();
|
|
1064
|
+
if (done) break;
|
|
1065
|
+
if (!(value instanceof Uint8Array)) throw invalid4();
|
|
1066
|
+
if (size += value.byteLength, size > limit) throw new FileOperationError("request_too_large", 413);
|
|
1067
|
+
bytes.set(value, size - value.byteLength);
|
|
690
1068
|
}
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
var DataOperationError = class extends Error {
|
|
704
|
-
constructor(code, retryable = !1, effect, component) {
|
|
705
|
-
super(`Data operation failed: ${code}`);
|
|
706
|
-
this.code = code;
|
|
707
|
-
this.retryable = retryable;
|
|
708
|
-
this.effect = effect;
|
|
709
|
-
this.component = component;
|
|
710
|
-
this.name = "DataOperationError";
|
|
1069
|
+
} finally {
|
|
1070
|
+
reader.releaseLock();
|
|
711
1071
|
}
|
|
712
|
-
|
|
713
|
-
retryable;
|
|
714
|
-
effect;
|
|
715
|
-
component;
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
var invalid = () => {
|
|
719
|
-
throw new DataOperationError("invalid_query");
|
|
720
|
-
}, historyText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
|
|
721
|
-
function historyVersion(value) {
|
|
722
|
-
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
|
-
}
|
|
724
|
-
function historyProperties(value) {
|
|
725
|
-
if (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return invalid();
|
|
726
|
-
let descriptors = Object.getOwnPropertyDescriptors(value);
|
|
727
|
-
return Reflect.ownKeys(value).some((key) => typeof key != "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")) ? invalid() : descriptors;
|
|
1072
|
+
return bytes.slice(0, size);
|
|
728
1073
|
}
|
|
729
|
-
function
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1074
|
+
async function timed(work, parent, timeout = TIMEOUT_MS) {
|
|
1075
|
+
let controller = new AbortController(), timer, abort, expiry = new Promise((_, reject) => {
|
|
1076
|
+
abort = () => {
|
|
1077
|
+
controller.abort(), reject(unavailable());
|
|
1078
|
+
}, timer = setTimeout(abort, timeout), parent?.addEventListener("abort", abort, { once: !0 }), parent?.aborted && abort();
|
|
1079
|
+
});
|
|
1080
|
+
try {
|
|
1081
|
+
return await Promise.race([work(controller.signal), expiry]);
|
|
1082
|
+
} finally {
|
|
1083
|
+
clearTimeout(timer), parent?.removeEventListener("abort", abort);
|
|
737
1084
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1085
|
+
}
|
|
1086
|
+
async function result(response, maximum = RESULT_BYTES) {
|
|
1087
|
+
try {
|
|
1088
|
+
let bytes = await boundedBytes(response.body, maximum);
|
|
1089
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(bytes));
|
|
1090
|
+
} catch {
|
|
1091
|
+
throw unavailable();
|
|
742
1092
|
}
|
|
743
|
-
return result2;
|
|
744
1093
|
}
|
|
745
|
-
function
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1094
|
+
async function hostedContent(url, init2, parent, upstream = fetch) {
|
|
1095
|
+
let delivery = new AbortController(), abort = () => delivery.abort(parent.reason);
|
|
1096
|
+
parent.addEventListener("abort", abort, { once: !0 }), parent.aborted && abort();
|
|
1097
|
+
let detach = () => parent.removeEventListener("abort", abort);
|
|
1098
|
+
try {
|
|
1099
|
+
let response = await upstream(url, { ...init2, signal: delivery.signal });
|
|
1100
|
+
if (delivery.signal.aborted)
|
|
1101
|
+
throw response.body?.cancel().catch(() => {
|
|
1102
|
+
}), unavailable();
|
|
1103
|
+
if (!response.body)
|
|
1104
|
+
return detach(), response;
|
|
1105
|
+
let source = response.body, reader, settled = !1, cancel = async (reason) => {
|
|
1106
|
+
if (!settled) {
|
|
1107
|
+
settled = !0, detach(), delivery.abort(reason);
|
|
1108
|
+
try {
|
|
1109
|
+
await (reader ? reader.cancel(reason) : source.cancel(reason));
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
if (error !== delivery.signal.reason && !(error instanceof Error && error.name === "AbortError")) throw error;
|
|
1112
|
+
} finally {
|
|
1113
|
+
reader?.releaseLock();
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}, body2 = new ReadableStream({
|
|
1117
|
+
async pull(controller) {
|
|
1118
|
+
try {
|
|
1119
|
+
reader ??= source.getReader();
|
|
1120
|
+
let { done, value } = await reader.read();
|
|
1121
|
+
if (settled) return;
|
|
1122
|
+
done ? (settled = !0, detach(), reader.releaseLock(), controller.close()) : controller.enqueue(value);
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
if (settled) return;
|
|
1125
|
+
controller.error(error), await cancel(error).catch(() => {
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
cancel
|
|
1130
|
+
}, { highWaterMark: 0 }), wrapped = new Response(body2, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
1131
|
+
for (let key of ["url", "redirected", "type"])
|
|
1132
|
+
Object.defineProperty(wrapped, key, { value: response[key] });
|
|
1133
|
+
return wrapped;
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
throw detach(), delivery.abort(error), error;
|
|
1136
|
+
}
|
|
751
1137
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1138
|
+
async function send(suffix, init2, descriptor2, initial, parent) {
|
|
1139
|
+
let original = initial, checkCurrent = (context2 = original) => {
|
|
1140
|
+
if (parent?.aborted) throw unavailable();
|
|
1141
|
+
if (!original.isCurrent() || context2.url !== original.url) throw new FileOperationError("unsupported_context", 400);
|
|
1142
|
+
};
|
|
1143
|
+
for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
|
|
1144
|
+
checkCurrent();
|
|
1145
|
+
let context2 = await resolveFilesContext();
|
|
1146
|
+
checkCurrent(context2);
|
|
1147
|
+
try {
|
|
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);
|
|
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;
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
let failure = error instanceof FileOperationError ? error : unavailable();
|
|
1186
|
+
if (init2.body instanceof ReadableStream || !failure.retryable || attempt + 1 === ATTEMPTS) throw failure;
|
|
772
1187
|
}
|
|
773
|
-
if (new Set(collections).size !== collections.length) return invalid2();
|
|
774
|
-
captured.collections = collections;
|
|
775
|
-
}
|
|
776
|
-
if (Object.hasOwn(descriptors, "limit")) {
|
|
777
|
-
let supplied = descriptors.limit.value;
|
|
778
|
-
if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid2();
|
|
779
|
-
captured.limit = supplied;
|
|
780
1188
|
}
|
|
781
|
-
|
|
1189
|
+
throw unavailable();
|
|
782
1190
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
};
|
|
787
|
-
function mapping(value) {
|
|
788
|
-
return !value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)) || Reflect.ownKeys(value).some((key) => typeof key != "string" || !Object.getOwnPropertyDescriptor(value, key)?.enumerable || !Object.hasOwn(Object.getOwnPropertyDescriptor(value, key), "value")) ? invalidChange() : value;
|
|
1191
|
+
function idPath(id2) {
|
|
1192
|
+
if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid4();
|
|
1193
|
+
return `/${id2}`;
|
|
789
1194
|
}
|
|
790
|
-
function
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1195
|
+
async function multipartCreate(input, metadata, exchange, release = () => {
|
|
1196
|
+
active--;
|
|
1197
|
+
}, upload) {
|
|
1198
|
+
let route = upload ? `${idPath(upload.file.id)}/upload/multipart` : "/multipart", ticket, reader, chunk, offset = 0, receipts = [], pending = /* @__PURE__ */ new Set(), request = (work, timeout = TIMEOUT_MS) => timed((signal) => {
|
|
1199
|
+
let task = work(signal);
|
|
1200
|
+
return pending.add(task), task.then(() => pending.delete(task), () => pending.delete(task)), task;
|
|
1201
|
+
}, void 0, timeout), control = (body2) => ({
|
|
1202
|
+
method: "POST",
|
|
1203
|
+
headers: { "content-type": "application/json", ...ticket ? { "x-oddington-transfer": ticket } : {} },
|
|
1204
|
+
body: JSON.stringify(body2)
|
|
1205
|
+
});
|
|
794
1206
|
try {
|
|
795
|
-
|
|
1207
|
+
let start = await request((signal) => exchange(`${route}/start`, control(upload?.file ?? metadata), "control", signal));
|
|
1208
|
+
if (!start || typeof start != "object" || Array.isArray(start)) throw unavailable();
|
|
1209
|
+
let session = start;
|
|
1210
|
+
if (Object.keys(session).sort().join(",") !== "partBytes,ticket" || session.partBytes !== PART_BYTES || typeof session.ticket != "string" || !session.ticket || session.ticket.length > 64 * 1024) throw unavailable();
|
|
1211
|
+
ticket = session.ticket, input.file instanceof ReadableStream && (reader = input.file.getReader());
|
|
1212
|
+
let lastProgress = Date.now(), next = async () => {
|
|
1213
|
+
try {
|
|
1214
|
+
if (Date.now() - lastProgress >= TIMEOUT_MS) throw unavailable();
|
|
1215
|
+
let value = await request(() => reader.read(), Math.max(1, TIMEOUT_MS - (Date.now() - lastProgress)));
|
|
1216
|
+
return value.value?.byteLength && (lastProgress = Date.now()), value;
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
throw await reader.cancel(error).catch(() => {
|
|
1219
|
+
}), error;
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
for (let sent = 0, partNumber = 1; sent < metadata.size || metadata.size === 0 && partNumber === 1; partNumber++) {
|
|
1223
|
+
let length = Math.min(PART_BYTES, metadata.size - sent), part;
|
|
1224
|
+
if (input.file instanceof Uint8Array) part = input.file.subarray(sent, sent + length).slice();
|
|
1225
|
+
else if (input.file instanceof Blob) {
|
|
1226
|
+
let slice = input.file.slice(sent, sent + length);
|
|
1227
|
+
part = new Uint8Array(await request(() => slice.arrayBuffer()));
|
|
1228
|
+
} else {
|
|
1229
|
+
part = new Uint8Array(length);
|
|
1230
|
+
let filled = 0;
|
|
1231
|
+
for (; filled < length; ) {
|
|
1232
|
+
if (!chunk || offset === chunk.byteLength) {
|
|
1233
|
+
chunk = void 0, offset = 0;
|
|
1234
|
+
let value2 = await next();
|
|
1235
|
+
if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid4();
|
|
1236
|
+
}
|
|
1237
|
+
let take = Math.min(length - filled, chunk.byteLength - offset);
|
|
1238
|
+
part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
if (part.byteLength !== length) throw invalid4();
|
|
1242
|
+
if (reader && sent + length === metadata.size)
|
|
1243
|
+
for (lastProgress = Date.now(); ; ) {
|
|
1244
|
+
let value2 = await next();
|
|
1245
|
+
if (value2.done) break;
|
|
1246
|
+
if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid4();
|
|
1247
|
+
}
|
|
1248
|
+
let value = await request((signal) => exchange(`${route}/part`, {
|
|
1249
|
+
method: "POST",
|
|
1250
|
+
headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket, "x-oddington-part": String(partNumber) },
|
|
1251
|
+
body: part.buffer
|
|
1252
|
+
}, "control", signal));
|
|
1253
|
+
part = void 0;
|
|
1254
|
+
let receipt = value;
|
|
1255
|
+
if (!receipt || typeof receipt != "object" || Object.keys(receipt).sort().join(",") !== "etag,partNumber" || receipt.partNumber !== partNumber || typeof receipt.etag != "string" || !receipt.etag || receipt.etag.length > 256 || /[\u0000-\u001f\u007f]/.test(receipt.etag)) throw unavailable();
|
|
1256
|
+
receipts.push({ partNumber, etag: receipt.etag }), sent += length, lastProgress = Date.now();
|
|
1257
|
+
}
|
|
1258
|
+
if (upload) for (let name of Object.keys(upload.file.enrichments)) {
|
|
1259
|
+
let bytes = await request(() => upload.enrichment(name));
|
|
1260
|
+
if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid4();
|
|
1261
|
+
await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
|
|
1262
|
+
method: "POST",
|
|
1263
|
+
headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
|
|
1264
|
+
body: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)
|
|
1265
|
+
}, "control", signal));
|
|
1266
|
+
}
|
|
1267
|
+
await upload?.beforeComplete();
|
|
1268
|
+
let completion = control({ parts: receipts });
|
|
1269
|
+
if (new TextEncoder().encode(completion.body).byteLength > 128 * 1024) throw unavailable();
|
|
1270
|
+
return parseFileDescriptor(await request(
|
|
1271
|
+
(signal) => exchange(`${route}/complete`, completion, !0, signal),
|
|
1272
|
+
TIMEOUT_MS + Math.ceil(metadata.size / (1024 * 1024)) * 1e3
|
|
1273
|
+
));
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
throw reader && await reader.cancel(error).catch(() => {
|
|
1276
|
+
}), ticket && await request((signal) => exchange(`${route}/abort`, control({}), "control", signal)).catch(() => {
|
|
1277
|
+
}), error instanceof FileOperationError ? error : unavailable();
|
|
796
1278
|
} finally {
|
|
797
|
-
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
function captureDataDelete(record2) {
|
|
801
|
-
let identity = mapping(record2);
|
|
802
|
-
return !validRecordRef(identity.$ref) || typeof identity.$version != "string" || !identity.$version.length || identity.$version.length > 512 ? invalidChange() : { ref: identity.$ref, expectedVersion: identity.$version };
|
|
803
|
-
}
|
|
804
|
-
function captureDataUpdate(record2, set) {
|
|
805
|
-
let identity = captureDataDelete(record2), fields3 = mapping(set);
|
|
806
|
-
return !Object.keys(fields3).length || Object.keys(fields3).some((key) => key.startsWith("$") || ["id", "type"].includes(key)) ? invalidChange() : { ...identity, set: captureValue(fields3) };
|
|
807
|
-
}
|
|
808
|
-
function captureDataUpdates(value, allowEmpty = !1) {
|
|
809
|
-
if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4) return invalidChange();
|
|
810
|
-
let updates = value.map((item) => {
|
|
811
|
-
let row = mapping(item);
|
|
812
|
-
return Object.keys(row).sort().join(",") !== "expectedVersion,ref,set" ? invalidChange() : captureDataUpdate({ $ref: row.ref, $version: row.expectedVersion }, row.set);
|
|
813
|
-
});
|
|
814
|
-
return new Set(updates.map((update) => update.ref)).size !== updates.length ? invalidChange() : updates;
|
|
815
|
-
}
|
|
816
|
-
function captureDataCreate(value) {
|
|
817
|
-
let row = mapping(value);
|
|
818
|
-
if (!validRecordRef(row.$ref) || Object.keys(row).some((key) => key.startsWith("$") && !["$ref", "$filing"].includes(key)))
|
|
819
|
-
return invalidChange();
|
|
820
|
-
let { $ref: ref, $filing, ...fields3 } = row, filing;
|
|
821
|
-
if ($filing !== void 0) {
|
|
822
|
-
let components = mapping($filing);
|
|
823
|
-
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)))
|
|
824
|
-
return invalidChange();
|
|
825
|
-
filing = { ...components };
|
|
1279
|
+
chunk = void 0, reader?.releaseLock(), pending.size ? Promise.allSettled([...pending]).then(release) : release();
|
|
826
1280
|
}
|
|
827
|
-
return { ref, fields: captureValue(fields3), ...filing ? { filing } : {} };
|
|
828
1281
|
}
|
|
829
|
-
function
|
|
830
|
-
let
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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();
|
|
837
1293
|
});
|
|
838
|
-
if (!Array.isArray(deletesValue) || deletesValue.length > 1e4) return invalidChange();
|
|
839
|
-
let deletes = deletesValue.map((item) => {
|
|
840
|
-
let row = mapping(item);
|
|
841
|
-
return Object.keys(row).sort().join(",") !== "expectedVersion,ref" ? invalidChange() : captureDataDelete({ $ref: row.ref, $version: row.expectedVersion });
|
|
842
|
-
}), refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
|
|
843
|
-
return !refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length ? invalidChange() : { updates, creates, deletes };
|
|
844
|
-
}
|
|
845
|
-
function checkDataRequestSize(input) {
|
|
846
|
-
if (new TextEncoder().encode(JSON.stringify(input)).byteLength > DATA_REQUEST_LIMIT)
|
|
847
|
-
throw new DataOperationError("request_too_large", !1, "unapplied");
|
|
848
1294
|
}
|
|
849
|
-
function
|
|
850
|
-
let
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
409: ["conflict", "publication_pending"],
|
|
855
|
-
413: ["request_too_large"],
|
|
856
|
-
429: ["capacity"],
|
|
857
|
-
503: ["unavailable", "publication_pending"]
|
|
858
|
-
}, error = value && typeof value == "object" && "error" in value ? value.error : void 0, row = error && typeof error == "object" ? error : void 0, code = failures2[status]?.find((allowed) => allowed === row?.code) ?? "unavailable", effect = ["unapplied", "uncertain", "applied"].includes(String(row?.effect)) ? row.effect : void 0;
|
|
859
|
-
return new DataOperationError(
|
|
860
|
-
code,
|
|
861
|
-
["capacity", "unavailable", "publication_pending"].includes(code),
|
|
862
|
-
code === "conflict" ? "unapplied" : effect,
|
|
863
|
-
code === "invalid_change" && typeof row?.component == "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(row.component) ? row.component : void 0
|
|
864
|
-
);
|
|
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`;
|
|
865
1300
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
this.status = status;
|
|
872
|
-
this.retryable = retryable;
|
|
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);
|
|
873
1306
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
883
|
-
let f = value;
|
|
884
|
-
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();
|
|
885
|
-
let d = f.details;
|
|
886
|
-
if (d.kind === "other") {
|
|
887
|
-
if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid4();
|
|
888
|
-
} else if (d.kind === "csv") {
|
|
889
|
-
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();
|
|
890
|
-
} else return invalid4();
|
|
891
|
-
for (let [name, descriptor2] of Object.entries(f.enrichments)) {
|
|
892
|
-
if (!safeEnrichmentName(name)) return invalid4();
|
|
893
|
-
parseEnrichmentDescriptor(descriptor2);
|
|
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
|
+
}
|
|
894
1315
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
};
|
|
912
|
-
if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
|
|
913
|
-
let d = value;
|
|
914
|
-
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();
|
|
915
|
-
if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
916
|
-
return d;
|
|
917
|
-
}
|
|
918
|
-
function safeFileName(value) {
|
|
919
|
-
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);
|
|
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
|
+
});
|
|
920
1332
|
}
|
|
921
|
-
|
|
922
|
-
|
|
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;
|
|
923
1423
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
let csv;
|
|
936
|
-
if (input.csv !== void 0) {
|
|
937
|
-
if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid4();
|
|
938
|
-
let hints = input.csv;
|
|
939
|
-
if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid4();
|
|
940
|
-
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;
|
|
941
|
-
if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid4();
|
|
942
|
-
csv = { encoding, delimiter, headerRow };
|
|
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;
|
|
943
1435
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
...input.storage ? { storage: input.storage } : {},
|
|
948
|
-
...csv ? { csv } : {},
|
|
949
|
-
size: input.size,
|
|
950
|
-
requestKey: input.requestKey
|
|
951
|
-
};
|
|
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");
|
|
952
1439
|
}
|
|
953
1440
|
|
|
954
|
-
var
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}
|
|
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
|
+
};
|
|
961
1451
|
|
|
962
|
-
var
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
memberContext === resolve && (memberContext = void 0);
|
|
966
|
-
};
|
|
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);
|
|
967
1455
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
if (!host) throw new Error("App context unavailable.");
|
|
971
|
-
let context2 = await host();
|
|
972
|
-
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now())
|
|
973
|
-
throw new Error("App context unavailable.");
|
|
974
|
-
if (context2.kind !== "local" && (typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)))
|
|
975
|
-
throw new Error("App context unavailable.");
|
|
976
|
-
return context2;
|
|
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;
|
|
977
1458
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
let host = memberContext;
|
|
981
|
-
if (host !== void 0) {
|
|
982
|
-
let context2 = await host();
|
|
983
|
-
if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now()) throw new Error();
|
|
984
|
-
if (context2.kind === "local") return { local: context2.requestData };
|
|
985
|
-
if (!context2 || !Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
|
|
986
|
-
return checked(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` });
|
|
987
|
-
}
|
|
988
|
-
throw new Error();
|
|
989
|
-
} catch {
|
|
990
|
-
throw new DataOperationError("context_unavailable");
|
|
991
|
-
}
|
|
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");
|
|
992
1461
|
}
|
|
993
|
-
|
|
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");
|
|
994
1468
|
try {
|
|
995
|
-
|
|
996
|
-
if (host) {
|
|
997
|
-
let context2 = await host();
|
|
998
|
-
if (context2.kind === "local") throw new Error();
|
|
999
|
-
if (!Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
|
|
1000
|
-
return {
|
|
1001
|
-
...filesContext(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` }),
|
|
1002
|
-
isCurrent: context2.isCurrent,
|
|
1003
|
-
local: void 0,
|
|
1004
|
-
localFiles: void 0
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
throw new Error();
|
|
1469
|
+
return serializable(result2);
|
|
1008
1470
|
} catch {
|
|
1009
|
-
|
|
1471
|
+
return fail2("Invalid Airtable fields");
|
|
1010
1472
|
}
|
|
1011
1473
|
}
|
|
1012
|
-
function
|
|
1013
|
-
|
|
1014
|
-
return {
|
|
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) };
|
|
1015
1477
|
}
|
|
1016
|
-
function
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
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
|
+
));
|
|
1022
1486
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
let context2 = await resolveDataContext();
|
|
1028
|
-
try {
|
|
1029
|
-
let envelope = { repository, ...input };
|
|
1030
|
-
if (checkDataRequestSize(envelope), "local" in context2) return await context2.local("data/query", envelope);
|
|
1031
|
-
let response = await fetch(context2.url, {
|
|
1032
|
-
method: "POST",
|
|
1033
|
-
redirect: "error",
|
|
1034
|
-
cache: "no-store",
|
|
1035
|
-
headers: { "content-type": "application/json", ...context2.headers },
|
|
1036
|
-
body: JSON.stringify(envelope)
|
|
1037
|
-
}), result2 = await response.json();
|
|
1038
|
-
if (!response.ok)
|
|
1039
|
-
throw dataTransportError(response.status, result2);
|
|
1040
|
-
return result2;
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
throw error instanceof DataOperationError ? error : new DataOperationError("unavailable", !0);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
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);
|
|
1045
1491
|
return {
|
|
1046
|
-
|
|
1047
|
-
list: () => send2({ operation: "collections.list" }),
|
|
1048
|
-
schema: (name) => send2({ operation: "collections.schema", name })
|
|
1049
|
-
},
|
|
1492
|
+
tables: { list: async () => await call("tables.list", {}) },
|
|
1050
1493
|
records: {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
at: async (instant) => send2({ operation: "history.at", instant: captureHistoryInstant(instant) })
|
|
1057
|
-
},
|
|
1058
|
-
changes() {
|
|
1059
|
-
let updates = [], creates = [], deletes = [], publicationId, pending, confirmed, snapshot = () => {
|
|
1060
|
-
let captured = captureDataChanges(updates, creates, deletes), input = {
|
|
1061
|
-
updates: captured.updates,
|
|
1062
|
-
...captured.creates.length ? { creates: captured.creates } : {},
|
|
1063
|
-
...captured.deletes.length ? { deletes: captured.deletes } : {}
|
|
1064
|
-
};
|
|
1065
|
-
return checkDataRequestSize({
|
|
1066
|
-
repository,
|
|
1067
|
-
operation: "changes.publish",
|
|
1068
|
-
...input,
|
|
1069
|
-
publicationId: publicationId ?? "00000000-0000-4000-8000-000000000000"
|
|
1070
|
-
}), input;
|
|
1071
|
-
};
|
|
1072
|
-
return {
|
|
1073
|
-
create(record2) {
|
|
1074
|
-
if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
|
|
1075
|
-
let next = [...creates, captureDataCreate(record2)];
|
|
1076
|
-
captureDataChanges(updates, next, deletes), checkDataRequestSize({
|
|
1077
|
-
repository,
|
|
1078
|
-
operation: "changes.publish",
|
|
1079
|
-
updates,
|
|
1080
|
-
creates: next,
|
|
1081
|
-
...deletes.length ? { deletes } : {},
|
|
1082
|
-
publicationId: "00000000-0000-4000-8000-000000000000"
|
|
1083
|
-
}), creates = next;
|
|
1084
|
-
},
|
|
1085
|
-
update(record2, set) {
|
|
1086
|
-
if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
|
|
1087
|
-
let next = [...updates, captureDataUpdate(record2, set)];
|
|
1088
|
-
captureDataChanges(next, creates, deletes), checkDataRequestSize({
|
|
1089
|
-
repository,
|
|
1090
|
-
operation: "changes.publish",
|
|
1091
|
-
updates: next,
|
|
1092
|
-
...creates.length ? { creates } : {},
|
|
1093
|
-
...deletes.length ? { deletes } : {},
|
|
1094
|
-
publicationId: "00000000-0000-4000-8000-000000000000"
|
|
1095
|
-
}), updates = next;
|
|
1096
|
-
},
|
|
1097
|
-
delete(record2) {
|
|
1098
|
-
if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
|
|
1099
|
-
let next = [...deletes, captureDataDelete(record2)];
|
|
1100
|
-
captureDataChanges(updates, creates, next), checkDataRequestSize({
|
|
1101
|
-
repository,
|
|
1102
|
-
operation: "changes.publish",
|
|
1103
|
-
updates,
|
|
1104
|
-
deletes: next,
|
|
1105
|
-
...creates.length ? { creates } : {},
|
|
1106
|
-
publicationId: "00000000-0000-4000-8000-000000000000"
|
|
1107
|
-
}), deletes = next;
|
|
1108
|
-
},
|
|
1109
|
-
async validate() {
|
|
1110
|
-
await send2({ operation: "changes.validate", ...snapshot() });
|
|
1111
|
-
},
|
|
1112
|
-
publish() {
|
|
1113
|
-
if (confirmed) return Promise.resolve(structuredClone(confirmed));
|
|
1114
|
-
if (!pending) {
|
|
1115
|
-
let captured;
|
|
1116
|
-
try {
|
|
1117
|
-
captured = snapshot();
|
|
1118
|
-
} catch (error) {
|
|
1119
|
-
return Promise.reject(error);
|
|
1120
|
-
}
|
|
1121
|
-
publicationId ??= crypto.randomUUID(), pending = send2({ operation: "changes.publish", ...captured, publicationId }).then((result2) => (confirmed = structuredClone(result2), result2)).catch((error) => {
|
|
1122
|
-
throw error instanceof DataOperationError ? new DataOperationError(
|
|
1123
|
-
error.code,
|
|
1124
|
-
error.retryable,
|
|
1125
|
-
error.effect ?? (["unavailable", "publication_pending"].includes(error.code) ? "uncertain" : "unapplied"),
|
|
1126
|
-
error.component
|
|
1127
|
-
) : new DataOperationError("unavailable", !0, "uncertain");
|
|
1128
|
-
}).finally(() => {
|
|
1129
|
-
pending = void 0;
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
return pending.then((result2) => structuredClone(result2));
|
|
1133
|
-
}
|
|
1134
|
-
};
|
|
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)
|
|
1135
1499
|
}
|
|
1136
1500
|
};
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
403: ["access_denied"],
|
|
1144
|
-
405: ["method_not_allowed"],
|
|
1145
|
-
409: ["conflict", "local_only"],
|
|
1146
|
-
413: ["request_too_large"],
|
|
1147
|
-
429: ["capacity"],
|
|
1148
|
-
503: ["integrity", "unavailable"]
|
|
1149
|
-
};
|
|
1150
|
-
async function boundedBytes(stream, limit) {
|
|
1151
|
-
if (!stream) return new Uint8Array();
|
|
1152
|
-
let reader = stream.getReader(), bytes = new Uint8Array(limit), size = 0;
|
|
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;
|
|
1153
1507
|
try {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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");
|
|
1160
1531
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
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;
|
|
1163
1586
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
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 } : {} };
|
|
1171
1594
|
});
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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");
|
|
1176
1601
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
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 = [];
|
|
1179
1609
|
try {
|
|
1180
|
-
|
|
1181
|
-
|
|
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();
|
|
1182
1617
|
} catch {
|
|
1183
|
-
throw
|
|
1618
|
+
throw new AirtableOperationError(
|
|
1619
|
+
"Incomplete Airtable mutation acknowledgment",
|
|
1620
|
+
acknowledged.length ? "partial" : "uncertain",
|
|
1621
|
+
acknowledged.length ? [receipt(acknowledged)] : []
|
|
1622
|
+
);
|
|
1184
1623
|
}
|
|
1624
|
+
return { receipt: receipt(acknowledged), ...kind === "delete" ? {} : { records: returned } };
|
|
1185
1625
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
let detach = () => parent.removeEventListener("abort", abort);
|
|
1190
|
-
try {
|
|
1191
|
-
let response = await upstream(url, { ...init2, signal: delivery.signal });
|
|
1192
|
-
if (delivery.signal.aborted)
|
|
1193
|
-
throw response.body?.cancel().catch(() => {
|
|
1194
|
-
}), unavailable();
|
|
1195
|
-
if (!response.body)
|
|
1196
|
-
return detach(), response;
|
|
1197
|
-
let source = response.body, reader, settled = !1, cancel = async (reason) => {
|
|
1198
|
-
if (!settled) {
|
|
1199
|
-
settled = !0, detach(), delivery.abort(reason);
|
|
1200
|
-
try {
|
|
1201
|
-
await (reader ? reader.cancel(reason) : source.cancel(reason));
|
|
1202
|
-
} catch (error) {
|
|
1203
|
-
if (error !== delivery.signal.reason && !(error instanceof Error && error.name === "AbortError")) throw error;
|
|
1204
|
-
} finally {
|
|
1205
|
-
reader?.releaseLock();
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
}, body2 = new ReadableStream({
|
|
1209
|
-
async pull(controller) {
|
|
1210
|
-
try {
|
|
1211
|
-
reader ??= source.getReader();
|
|
1212
|
-
let { done, value } = await reader.read();
|
|
1213
|
-
if (settled) return;
|
|
1214
|
-
done ? (settled = !0, detach(), reader.releaseLock(), controller.close()) : controller.enqueue(value);
|
|
1215
|
-
} catch (error) {
|
|
1216
|
-
if (settled) return;
|
|
1217
|
-
controller.error(error), await cancel(error).catch(() => {
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
},
|
|
1221
|
-
cancel
|
|
1222
|
-
}, { highWaterMark: 0 }), wrapped = new Response(body2, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
1223
|
-
for (let key of ["url", "redirected", "type"])
|
|
1224
|
-
Object.defineProperty(wrapped, key, { value: response[key] });
|
|
1225
|
-
return wrapped;
|
|
1226
|
-
} catch (error) {
|
|
1227
|
-
throw detach(), delivery.abort(error), error;
|
|
1228
|
-
}
|
|
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;
|
|
1229
1629
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
};
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
let context2 = await resolveFilesContext();
|
|
1238
|
-
if (checkCurrent(), context2.url !== original.url) throw new FileOperationError("unsupported_context", 400);
|
|
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;
|
|
1239
1637
|
try {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
try {
|
|
1259
|
-
checkCurrent();
|
|
1260
|
-
} catch (error) {
|
|
1261
|
-
throw response.body?.cancel().catch(() => {
|
|
1262
|
-
}), error;
|
|
1263
|
-
}
|
|
1264
|
-
if (!descriptor2) return response;
|
|
1265
|
-
let value = await result(response, descriptor2 === "control" ? CONTROL_RESULT_BYTES : RESULT_BYTES);
|
|
1266
|
-
if (descriptor2 === "control")
|
|
1267
|
-
return checkCurrent(), value;
|
|
1268
|
-
let parsed;
|
|
1269
|
-
if (descriptor2 === "enrichment") {
|
|
1270
|
-
if (!value || typeof value != "object" || Array.isArray(value)) throw unavailable();
|
|
1271
|
-
let { name, ...metadata } = value;
|
|
1272
|
-
if (!safeEnrichmentName(name)) throw unavailable();
|
|
1273
|
-
parsed = { name, ...parseEnrichmentDescriptor(metadata) };
|
|
1274
|
-
} else parsed = parseFileDescriptor(value);
|
|
1275
|
-
return checkCurrent(), parsed;
|
|
1276
|
-
})();
|
|
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;
|
|
1277
1656
|
} catch (error) {
|
|
1278
|
-
|
|
1279
|
-
|
|
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
|
+
);
|
|
1280
1661
|
}
|
|
1281
|
-
}
|
|
1282
|
-
throw unavailable();
|
|
1662
|
+
});
|
|
1283
1663
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1664
|
+
|
|
1665
|
+
var observer;
|
|
1666
|
+
async function reportLocalRoutineEffect(value) {
|
|
1667
|
+
await observer?.(value);
|
|
1287
1668
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}, upload) {
|
|
1291
|
-
let route = upload ? `${idPath(upload.file.id)}/upload/multipart` : "/multipart", ticket, reader, chunk, offset = 0, receipts = [], pending = /* @__PURE__ */ new Set(), request = (work, timeout = TIMEOUT_MS) => timed((signal) => {
|
|
1292
|
-
let task = work(signal);
|
|
1293
|
-
return pending.add(task), task.then(() => pending.delete(task), () => pending.delete(task)), task;
|
|
1294
|
-
}, void 0, timeout), control = (body2) => ({
|
|
1295
|
-
method: "POST",
|
|
1296
|
-
headers: { "content-type": "application/json", ...ticket ? { "x-oddington-transfer": ticket } : {} },
|
|
1297
|
-
body: JSON.stringify(body2)
|
|
1298
|
-
});
|
|
1669
|
+
|
|
1670
|
+
var connections = /* @__PURE__ */ createConnections(async (name, operation2, input) => {
|
|
1299
1671
|
try {
|
|
1300
|
-
let
|
|
1301
|
-
if (
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
ticket = session.ticket, input.file instanceof ReadableStream && (reader = input.file.getReader());
|
|
1305
|
-
let lastProgress = Date.now(), next = async () => {
|
|
1306
|
-
try {
|
|
1307
|
-
if (Date.now() - lastProgress >= TIMEOUT_MS) throw unavailable();
|
|
1308
|
-
let value = await request(() => reader.read(), Math.max(1, TIMEOUT_MS - (Date.now() - lastProgress)));
|
|
1309
|
-
return value.value?.byteLength && (lastProgress = Date.now()), value;
|
|
1310
|
-
} catch (error) {
|
|
1311
|
-
throw await reader.cancel(error).catch(() => {
|
|
1312
|
-
}), error;
|
|
1313
|
-
}
|
|
1314
|
-
};
|
|
1315
|
-
for (let sent = 0, partNumber = 1; sent < metadata.size || metadata.size === 0 && partNumber === 1; partNumber++) {
|
|
1316
|
-
let length = Math.min(PART_BYTES, metadata.size - sent), part;
|
|
1317
|
-
if (input.file instanceof Uint8Array) part = input.file.subarray(sent, sent + length).slice();
|
|
1318
|
-
else if (input.file instanceof Blob) {
|
|
1319
|
-
let slice = input.file.slice(sent, sent + length);
|
|
1320
|
-
part = new Uint8Array(await request(() => slice.arrayBuffer()));
|
|
1321
|
-
} else {
|
|
1322
|
-
part = new Uint8Array(length);
|
|
1323
|
-
let filled = 0;
|
|
1324
|
-
for (; filled < length; ) {
|
|
1325
|
-
if (!chunk || offset === chunk.byteLength) {
|
|
1326
|
-
chunk = void 0, offset = 0;
|
|
1327
|
-
let value2 = await next();
|
|
1328
|
-
if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid3();
|
|
1329
|
-
}
|
|
1330
|
-
let take = Math.min(length - filled, chunk.byteLength - offset);
|
|
1331
|
-
part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
if (part.byteLength !== length) throw invalid3();
|
|
1335
|
-
if (reader && sent + length === metadata.size)
|
|
1336
|
-
for (lastProgress = Date.now(); ; ) {
|
|
1337
|
-
let value2 = await next();
|
|
1338
|
-
if (value2.done) break;
|
|
1339
|
-
if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid3();
|
|
1340
|
-
}
|
|
1341
|
-
let value = await request((signal) => exchange(`${route}/part`, {
|
|
1342
|
-
method: "POST",
|
|
1343
|
-
headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket, "x-oddington-part": String(partNumber) },
|
|
1344
|
-
body: part.buffer
|
|
1345
|
-
}, "control", signal));
|
|
1346
|
-
part = void 0;
|
|
1347
|
-
let receipt = value;
|
|
1348
|
-
if (!receipt || typeof receipt != "object" || Object.keys(receipt).sort().join(",") !== "etag,partNumber" || receipt.partNumber !== partNumber || typeof receipt.etag != "string" || !receipt.etag || receipt.etag.length > 256 || /[\u0000-\u001f\u007f]/.test(receipt.etag)) throw unavailable();
|
|
1349
|
-
receipts.push({ partNumber, etag: receipt.etag }), sent += length, lastProgress = Date.now();
|
|
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);
|
|
1350
1676
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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;
|
|
1359
1695
|
}
|
|
1360
|
-
await
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
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;
|
|
1367
1704
|
} catch (error) {
|
|
1368
|
-
throw
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
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");
|
|
1373
1733
|
}
|
|
1734
|
+
return structuredClone(input);
|
|
1374
1735
|
}
|
|
1375
|
-
|
|
1376
|
-
let
|
|
1377
|
-
if (
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
return await work(context2, parent ?? new AbortController().signal);
|
|
1381
|
-
} finally {
|
|
1382
|
-
active--;
|
|
1383
|
-
}
|
|
1384
|
-
return timed(async (signal) => {
|
|
1385
|
-
try {
|
|
1386
|
-
return await work(context2, signal);
|
|
1387
|
-
} finally {
|
|
1388
|
-
active--;
|
|
1389
|
-
}
|
|
1390
|
-
}, parent).catch((error) => {
|
|
1391
|
-
throw error instanceof FileOperationError ? error : unavailable();
|
|
1392
|
-
});
|
|
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;
|
|
1393
1741
|
}
|
|
1394
|
-
function
|
|
1395
|
-
let
|
|
1396
|
-
if (
|
|
1397
|
-
|
|
1398
|
-
|
|
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);
|
|
1399
1750
|
}
|
|
1400
|
-
|
|
1401
|
-
let
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid3();
|
|
1410
|
-
if (!seen.has(name)) {
|
|
1411
|
-
if (seen.size === 16) throw invalid3();
|
|
1412
|
-
seen.add(name), distinct.push(name);
|
|
1413
|
-
}
|
|
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);
|
|
1414
1760
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
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);
|
|
1431
1779
|
}
|
|
1432
|
-
var files = {
|
|
1433
|
-
async upload(id2) {
|
|
1434
|
-
return operation(async (context2, signal) => {
|
|
1435
|
-
let file = await send(`${idPath(id2)}/upload`, { method: "POST" }, !0, context2, signal);
|
|
1436
|
-
if (file.id !== id2) throw unavailable();
|
|
1437
|
-
return file;
|
|
1438
|
-
});
|
|
1439
|
-
},
|
|
1440
|
-
async create(input, options) {
|
|
1441
|
-
let context2 = await resolveFilesContext();
|
|
1442
|
-
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")))
|
|
1443
|
-
throw invalid3();
|
|
1444
|
-
let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
|
|
1445
|
-
if (!blob && !array2 && !stream) throw invalid3();
|
|
1446
|
-
let intrinsic = blob ? input.file.size : array2 ? input.file.byteLength : void 0;
|
|
1447
|
-
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid3();
|
|
1448
|
-
let name = input.name;
|
|
1449
|
-
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));
|
|
1450
|
-
let metadata = parseFileCreateMetadata({
|
|
1451
|
-
name,
|
|
1452
|
-
size: intrinsic ?? input.size,
|
|
1453
|
-
path: input.path,
|
|
1454
|
-
storage: input.storage,
|
|
1455
|
-
csv: input.csv,
|
|
1456
|
-
requestKey: options?.requestKey === void 0 ? crypto.randomUUID() : options.requestKey
|
|
1457
|
-
}, FILE_ORIGINAL_MAX_BYTES);
|
|
1458
|
-
if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
|
|
1459
|
-
let header = encodeURIComponent(JSON.stringify(metadata));
|
|
1460
|
-
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
|
|
1461
|
-
if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
|
|
1462
|
-
if (active++, context2.localFiles && metadata.size > MAX_BYTES) {
|
|
1463
|
-
let controller = new AbortController(), file = input.file, offset = 0, body2 = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
|
|
1464
|
-
pull(controller2) {
|
|
1465
|
-
if (offset === file.byteLength) {
|
|
1466
|
-
controller2.close();
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
let end = Math.min(offset + 256 * 1024, file.byteLength);
|
|
1470
|
-
controller2.enqueue(file.subarray(offset, end)), offset = end;
|
|
1471
|
-
}
|
|
1472
|
-
}, { highWaterMark: 0 });
|
|
1473
|
-
try {
|
|
1474
|
-
return await send("", { method: "POST", headers: {
|
|
1475
|
-
"content-type": "application/octet-stream",
|
|
1476
|
-
"x-oddington-file": header
|
|
1477
|
-
}, body: body2 }, !0, context2, controller.signal);
|
|
1478
|
-
} finally {
|
|
1479
|
-
active--;
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
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) => {
|
|
1483
|
-
try {
|
|
1484
|
-
let bytes = array2 ? input.file.slice() : await boundedBytes(
|
|
1485
|
-
blob ? input.file.stream() : input.file,
|
|
1486
|
-
metadata.size
|
|
1487
|
-
);
|
|
1488
|
-
if (bytes.byteLength !== metadata.size) throw invalid3();
|
|
1489
|
-
if (signal.aborted) throw unavailable();
|
|
1490
|
-
return await send("", {
|
|
1491
|
-
method: "POST",
|
|
1492
|
-
headers: { "content-type": "application/octet-stream", "x-oddington-file": header },
|
|
1493
|
-
body: bytes.buffer
|
|
1494
|
-
}, !0, context2, signal);
|
|
1495
|
-
} finally {
|
|
1496
|
-
active--;
|
|
1497
|
-
}
|
|
1498
|
-
}).catch((error) => {
|
|
1499
|
-
throw error instanceof FileOperationError ? error : unavailable();
|
|
1500
|
-
});
|
|
1501
|
-
},
|
|
1502
|
-
async enrich(input) {
|
|
1503
|
-
let context2 = await resolveFilesContext();
|
|
1504
|
-
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();
|
|
1505
|
-
let path = `${idPath(input.file)}/enrichments`;
|
|
1506
|
-
if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
|
|
1507
|
-
throw new FileOperationError("request_too_large", 413);
|
|
1508
|
-
return operation(async (context3, signal) => {
|
|
1509
|
-
let bytes;
|
|
1510
|
-
if (typeof input.content == "string") {
|
|
1511
|
-
let buffer = new Uint8Array(Math.min(MAX_BYTES, input.content.length * 3)), encoded = new TextEncoder().encodeInto(input.content, buffer);
|
|
1512
|
-
if (encoded.read !== input.content.length) throw new FileOperationError("request_too_large", 413);
|
|
1513
|
-
bytes = buffer.slice(0, encoded.written);
|
|
1514
|
-
} else bytes = input.content.slice();
|
|
1515
|
-
let metadata = parseFileEnrichMetadata({
|
|
1516
|
-
name: input.name,
|
|
1517
|
-
size: bytes.byteLength,
|
|
1518
|
-
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
1519
|
-
...input.replace === void 0 ? {} : { replace: input.replace }
|
|
1520
|
-
}, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
|
|
1521
|
-
if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
|
|
1522
|
-
let result2 = await send(path, { method: "POST", headers: {
|
|
1523
|
-
"content-type": "application/octet-stream",
|
|
1524
|
-
"x-oddington-enrichment": header
|
|
1525
|
-
}, body: bytes.buffer }, "enrichment", context3, signal);
|
|
1526
|
-
if (result2.name !== metadata.name) throw unavailable();
|
|
1527
|
-
return result2;
|
|
1528
|
-
}, context2);
|
|
1529
|
-
},
|
|
1530
|
-
async get(id2) {
|
|
1531
|
-
return operation((context2, signal) => send(idPath(id2), { method: "GET" }, !0, context2, signal));
|
|
1532
|
-
},
|
|
1533
|
-
read
|
|
1534
|
-
};
|
|
1535
1780
|
|
|
1536
1781
|
var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap(), AppResponseReadError = class extends Error {
|
|
1537
1782
|
}, AppContextDenied = class extends Error {
|
|
@@ -1850,8 +2095,15 @@ function validBinding(value) {
|
|
|
1850
2095
|
}
|
|
1851
2096
|
|
|
1852
2097
|
var installedDispose;
|
|
1853
|
-
function init({ auth }) {
|
|
2098
|
+
function init({ auth: input }) {
|
|
1854
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
|
+
}
|
|
1855
2107
|
let endpoint, workspace, localOrigin;
|
|
1856
2108
|
try {
|
|
1857
2109
|
let url = new URL(globalThis.location.origin);
|
|
@@ -1860,13 +2112,13 @@ function init({ auth }) {
|
|
|
1860
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);
|
|
1861
2113
|
} catch {
|
|
1862
2114
|
}
|
|
1863
|
-
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) => {
|
|
1864
2116
|
let next = session?.user.id ?? null;
|
|
1865
|
-
(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;
|
|
1866
2118
|
}), uninstall = installMemberContext(async () => {
|
|
1867
2119
|
if (local) return local.resolve();
|
|
1868
2120
|
if (!active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
|
|
1869
|
-
let started = revision, { data: { session }, error } = await
|
|
2121
|
+
let started = revision, { data: { session }, error } = await auth2.getSession();
|
|
1870
2122
|
if (!active2 || globalThis.location.origin !== endpoint || started !== revision || error || !session || person !== void 0 && person !== session.user.id)
|
|
1871
2123
|
throw new Error("Hosted Data context unavailable.");
|
|
1872
2124
|
let expiresAt = (session.expires_at ?? 0) * 1e3;
|
|
@@ -1886,6 +2138,403 @@ function init({ auth }) {
|
|
|
1886
2138
|
}
|
|
1887
2139
|
var os = { init };
|
|
1888
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/browser-configuration`, { 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
|
+
|
|
1889
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 {
|
|
1890
2539
|
constructor(reason) {
|
|
1891
2540
|
super(reason);
|
|
@@ -2069,7 +2718,7 @@ var app = Object.freeze({ routines: new Proxy(/* @__PURE__ */ Object.create(null
|
|
|
2069
2718
|
deleteProperty: () => !1
|
|
2070
2719
|
}) });
|
|
2071
2720
|
|
|
2072
|
-
var
|
|
2721
|
+
var LIMIT2 = 16 * 1024, context;
|
|
2073
2722
|
var appRun = {
|
|
2074
2723
|
current() {
|
|
2075
2724
|
return context;
|
|
@@ -2090,7 +2739,7 @@ function validateCsvHints(value) {
|
|
|
2090
2739
|
return v;
|
|
2091
2740
|
}
|
|
2092
2741
|
|
|
2093
|
-
function
|
|
2742
|
+
function routinePublicationClient2(options = {}) {
|
|
2094
2743
|
let environment = {}, local = void 0, workspace = options.workspace ?? environment.ODDINGTON_ROUTINE_WORKSPACE, capability = options.capability ?? environment.ODDINGTON_ROUTINE_RUN;
|
|
2095
2744
|
if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== void 0 && !/^[0-9a-f]{64}$/.test(capability))
|
|
2096
2745
|
throw new Error("A workspace and valid optional routine capability are required");
|
|
@@ -2158,6 +2807,7 @@ function routinePublicationClient(options = {}) {
|
|
|
2158
2807
|
export {
|
|
2159
2808
|
AirtableOperationError,
|
|
2160
2809
|
AppRoutineError,
|
|
2810
|
+
BrowserAuthError,
|
|
2161
2811
|
DataOperationError,
|
|
2162
2812
|
FileOperationError,
|
|
2163
2813
|
InboxContractError,
|
|
@@ -2165,10 +2815,13 @@ export {
|
|
|
2165
2815
|
app,
|
|
2166
2816
|
appRun,
|
|
2167
2817
|
asksBlock,
|
|
2818
|
+
auth,
|
|
2168
2819
|
connections,
|
|
2169
2820
|
continueAsk,
|
|
2170
2821
|
data,
|
|
2171
2822
|
defineHandler,
|
|
2823
|
+
defineRoutine,
|
|
2824
|
+
file,
|
|
2172
2825
|
fileBlock,
|
|
2173
2826
|
files,
|
|
2174
2827
|
os,
|
|
@@ -2178,6 +2831,7 @@ export {
|
|
|
2178
2831
|
resolveAsk,
|
|
2179
2832
|
respond,
|
|
2180
2833
|
richTextBlock,
|
|
2181
|
-
routinePublicationClient,
|
|
2834
|
+
routinePublicationClient2 as routinePublicationClient,
|
|
2835
|
+
schema,
|
|
2182
2836
|
selection
|
|
2183
2837
|
};
|