@oddington/sdk 0.1.6 → 0.1.8

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.
@@ -0,0 +1,1965 @@
1
+ var InboxContractError = class extends Error {
2
+ constructor(message) {
3
+ super(message), this.name = "InboxContractError";
4
+ }
5
+ };
6
+ function fail(message) {
7
+ throw new InboxContractError(message);
8
+ }
9
+ function object(value, label) {
10
+ return (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) && fail(`${label} must be a plain object`), value;
11
+ }
12
+ function keys(value, allowed, label) {
13
+ for (let key of Reflect.ownKeys(value)) {
14
+ (typeof key != "string" || !allowed.includes(key)) && fail(`${label} does not support ${String(key)}`);
15
+ let descriptor2 = Object.getOwnPropertyDescriptor(value, key);
16
+ (!descriptor2.enumerable || !("value" in descriptor2)) && fail(`${label} must contain only JSON data`);
17
+ }
18
+ }
19
+ function text(value, label) {
20
+ return (typeof value != "string" || !value.trim()) && fail(`${label} must be nonempty text`), value;
21
+ }
22
+ function id(value, label) {
23
+ let result2 = text(value, label);
24
+ return result2 !== result2.trim() && fail(`${label} cannot have surrounding whitespace`), result2;
25
+ }
26
+ function array(value, label) {
27
+ (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || Reflect.ownKeys(value).length !== value.length + 1) && fail(`${label} must be a dense array`);
28
+ for (let index = 0; index < value.length; index += 1) {
29
+ let descriptor2 = Object.getOwnPropertyDescriptor(value, String(index));
30
+ (!descriptor2?.enumerable || !("value" in descriptor2)) && fail(`${label} must contain only JSON data`);
31
+ }
32
+ return value;
33
+ }
34
+ function distinctIds(value, label, nonempty = !0) {
35
+ let result2 = array(value, label).map((entry) => id(entry, label));
36
+ return nonempty && !result2.length && fail(`${label} must not be empty`), new Set(result2).size !== result2.length && fail(`${label} contains duplicate IDs`), result2;
37
+ }
38
+ var memberEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
39
+ function personReference(value, label) {
40
+ let result2 = id(value, label);
41
+ return memberEmail.test(result2) ? result2.toLowerCase() : result2;
42
+ }
43
+ function distinctPeople(value, label, nonempty = !0) {
44
+ let result2 = array(value, label).map((entry) => personReference(entry, label));
45
+ return nonempty && !result2.length && fail(`${label} must not be empty`), new Set(result2).size !== result2.length && fail(`${label} contains duplicate IDs`), result2;
46
+ }
47
+ function serializable(value, ancestors = /* @__PURE__ */ new Set()) {
48
+ if (value === null || typeof value == "boolean" || typeof value == "string" || typeof value == "number" && Number.isFinite(value)) return value;
49
+ (!value || typeof value != "object") && fail("Input must be losslessly serializable JSON"), ancestors.has(value) && fail("Input cannot contain cycles"), ancestors.add(value);
50
+ try {
51
+ if (Array.isArray(value))
52
+ return array(value, "JSON array"), value.map((entry) => serializable(entry, ancestors));
53
+ let source = object(value, "JSON input");
54
+ return keys(source, Object.keys(source), "JSON input"), Object.fromEntries(Object.entries(source).map(([key, entry]) => [key, serializable(entry, ancestors)]));
55
+ } finally {
56
+ ancestors.delete(value);
57
+ }
58
+ }
59
+ function natural(value, label, minimum = 0) {
60
+ return (!Number.isSafeInteger(value) || value < minimum) && fail(`${label} must be an integer >= ${minimum}`), value;
61
+ }
62
+ function validateInboxFile(value) {
63
+ let v = object(serializable(value), "Inbox file");
64
+ keys(v, ["id", "name", "mediaType", "size", "sha256", "details"], "Inbox file");
65
+ let fileId = id(v.id, "Inbox file ID"), name = id(v.name, "filename"), mediaType = id(v.mediaType, "media type");
66
+ (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(fileId) || /[\x00-\x1f\x7f/\\]/.test(name) || !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(mediaType) || typeof v.sha256 != "string" || !/^[0-9a-f]{64}$/.test(v.sha256)) && fail("Invalid Inbox file identity or metadata");
67
+ let size = natural(v.size, "file size");
68
+ size > 25 * 1024 * 1024 && fail("File exceeds supported size");
69
+ let d = object(v.details, "file details"), details;
70
+ switch (d.kind) {
71
+ case "csv":
72
+ keys(d, ["kind", "encoding", "delimiter", "headerRow", "dataRows"], "CSV details"), (mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter)) && fail("Invalid CSV details"), details = {
73
+ kind: "csv",
74
+ encoding: d.encoding,
75
+ delimiter: d.delimiter,
76
+ headerRow: natural(d.headerRow, "header row"),
77
+ dataRows: natural(d.dataRows, "data rows")
78
+ };
79
+ break;
80
+ case "pdf":
81
+ keys(d, ["kind", "pages"], "PDF details"), mediaType !== "application/pdf" && fail("Invalid PDF media type"), details = { kind: "pdf", ...d.pages === void 0 ? {} : { pages: natural(d.pages, "PDF pages", 1) } };
82
+ break;
83
+ case "other":
84
+ keys(d, ["kind"], "other file details"), ["text/csv", "application/pdf"].includes(mediaType) && fail("File type and details differ"), details = { kind: "other" };
85
+ break;
86
+ default:
87
+ return fail("Unknown file details kind");
88
+ }
89
+ return { id: fileId, name, mediaType, size, sha256: v.sha256, details };
90
+ }
91
+ function fields(value) {
92
+ let result2 = array(value, "selected fields").map((entry) => {
93
+ let field = object(entry, "field");
94
+ return keys(field, ["label", "value"], "field"), { label: text(field.label, "field label"), value: text(field.value, "field value") };
95
+ });
96
+ return new Set(result2.map((field) => field.label)).size !== result2.length && fail("Duplicate field label"), result2;
97
+ }
98
+ function validateBlock(value) {
99
+ let b = object(serializable(value), "Block");
100
+ if (b.kind === "rich-text")
101
+ return keys(b, ["kind", "markdown"], "Rich text Block"), { kind: "rich-text", markdown: text(b.markdown, "Markdown") };
102
+ if (b.kind === "asks") {
103
+ keys(b, ["kind", "problem", "asks"], "Asks Block");
104
+ let asks = distinctIds(b.asks, "Ask IDs");
105
+ return asks.some((ask) => !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(ask)) && fail("Invalid Ask ID"), { kind: "asks", problem: text(b.problem, "problem"), asks };
106
+ }
107
+ ["record", "records", "files"].includes(b.kind) || fail("Unknown Block kind"), keys(b, ["kind", "label", "initiallyExpanded", ...b.kind === "record" ? ["record", "view"] : b.kind === "records" ? ["records", "view"] : ["files"]], "Block");
108
+ let label = text(b.label, "Block label");
109
+ b.initiallyExpanded !== void 0 && typeof b.initiallyExpanded != "boolean" && fail("Invalid initiallyExpanded");
110
+ let expanded = b.initiallyExpanded === void 0 ? {} : { initiallyExpanded: b.initiallyExpanded }, target = b.view === void 0 ? {} : { view: id(b.view, "View target") };
111
+ if (b.kind === "record") {
112
+ let r = object(b.record, "presented record");
113
+ return keys(r, ["ref", "title", "fields"], "presented record"), {
114
+ kind: "record",
115
+ label,
116
+ ...target,
117
+ ...expanded,
118
+ record: { ref: id(r.ref, "record reference"), title: text(r.title, "record title"), fields: fields(r.fields) }
119
+ };
120
+ }
121
+ if (b.kind === "records") {
122
+ let records = array(b.records, "records").map((entry) => {
123
+ let r = object(entry, "record summary");
124
+ return keys(r, ["ref", "title", "description", "facts", "view"], "record summary"), {
125
+ ref: id(r.ref, "record reference"),
126
+ title: text(r.title, "record title"),
127
+ ...r.description === void 0 ? {} : { description: text(r.description, "record description") },
128
+ ...r.facts === void 0 ? {} : { facts: fields(r.facts) },
129
+ ...r.view === void 0 ? {} : { view: id(r.view, "row View target") }
130
+ };
131
+ });
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
+ }
134
+ let files2 = array(b.files, "files").map(validateInboxFile);
135
+ return (!files2.length || new Set(files2.map((file) => file.id)).size !== files2.length) && fail("Empty or duplicate files"), { kind: "files", label, files: files2, ...expanded };
136
+ }
137
+ function richTextBlock(value) {
138
+ return validateBlock({ ...value, kind: "rich-text" });
139
+ }
140
+ function recordBlock(value) {
141
+ return validateBlock({ ...value, kind: "record" });
142
+ }
143
+ function recordsBlock(value) {
144
+ return validateBlock({ ...value, kind: "records" });
145
+ }
146
+ function fileBlock(value) {
147
+ return validateBlock({ ...value, kind: "files" });
148
+ }
149
+ function asksBlock(value) {
150
+ return validateBlock({ ...value, kind: "asks" });
151
+ }
152
+ function selection(value) {
153
+ let input = object(value, "selection");
154
+ return keys(input, ["source"], "selection"), validateResponseDefinition({ type: "selection", source: input.source });
155
+ }
156
+ function validateAuthoredSource(value) {
157
+ let source = object(value, "response definition");
158
+ keys(source, ["type", "options", "suggestions", "search"], "response definition"), source.type !== "authored" && fail("Expected authored options");
159
+ let options = array(source.options, "options").map((entry) => {
160
+ let option = object(entry, "choice");
161
+ keys(option, ["value", "label", "description", "note"], "choice");
162
+ let note;
163
+ if (option.note !== void 0) {
164
+ let declaration = object(option.note, "choice note");
165
+ keys(declaration, ["placeholder"], "choice note"), note = { placeholder: text(declaration.placeholder, "note placeholder") };
166
+ }
167
+ return {
168
+ value: id(option.value, "choice value"),
169
+ label: text(option.label, "choice label"),
170
+ ...option.description === void 0 ? {} : { description: text(option.description, "choice description") },
171
+ ...note ? { note } : {}
172
+ };
173
+ });
174
+ if (options.length || fail("Choices require at least one option"), new Set(options.map((option) => option.value)).size !== options.length && fail("Duplicate choice value"), Object.hasOwn(source, "search") || Object.hasOwn(source, "suggestions")) {
175
+ let labels = object(source.search, "choice search");
176
+ keys(labels, ["triggerLabel", "placeholder", "emptyLabel"], "choice search");
177
+ let search = {
178
+ triggerLabel: text(labels.triggerLabel, "search trigger label"),
179
+ placeholder: text(labels.placeholder, "search placeholder"),
180
+ emptyLabel: text(labels.emptyLabel, "search empty label")
181
+ }, suggestions = array(source.suggestions, "suggestions").map((value2) => id(value2, "suggestion value"));
182
+ return (suggestions.length > 8 || new Set(suggestions).size !== suggestions.length || suggestions.some((value2) => !options.some((option) => option.value === value2))) && fail("Search permits at most eight unique published suggestions"), { type: "authored", options, suggestions, search };
183
+ }
184
+ return options.length > 9 && fail("Choices require one to nine options"), { type: "authored", options };
185
+ }
186
+ function validateRecordReference(value) {
187
+ let row = object(value, "Record reference");
188
+ keys(row, ["repository", "ref"], "Record reference");
189
+ let repository = id(row.repository, "repository"), ref = id(row.ref, "Record reference");
190
+ return (!/^[a-z0-9][a-z0-9._/-]{0,127}$/.test(repository) || /\.\.|\/\//.test(repository) || ref.length > 512 || !/^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)+$/.test(ref)) && fail("Invalid Record identity"), { repository, ref };
191
+ }
192
+ function validateRecordSource(value) {
193
+ let source = object(value, "Record selection");
194
+ keys(source, ["type", "collection", "refPrefix", "where", "display", "suggestions"], "Record selection"), source.type !== "records" && fail("Expected Record options");
195
+ let collection = object(source.collection, "collection");
196
+ keys(collection, ["repository", "name"], "collection");
197
+ let repository = validateRecordReference({ repository: collection.repository, ref: "records/example" }).repository, name = id(collection.name, "collection name");
198
+ /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(name) || fail("Invalid collection name");
199
+ let refPrefix = source.refPrefix === void 0 ? void 0 : id(source.refPrefix, "Record reference prefix");
200
+ refPrefix !== void 0 && (!refPrefix.endsWith("/") || refPrefix.length > 512 || !/^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)*\/$/.test(refPrefix)) && fail("Invalid Record reference prefix");
201
+ let where = object(source.where ?? {}, "Record filter"), fieldName = (name2) => /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name2) && !["constructor", "prototype"].includes(name2);
202
+ for (let [name2, value2] of Object.entries(where))
203
+ (!fieldName(name2) || !(value2 === null || ["string", "number", "boolean"].includes(typeof value2)) || typeof value2 == "number" && !Number.isFinite(value2)) && fail("Record filters require scalar equality");
204
+ let display = object(source.display, "Record display");
205
+ keys(display, ["labelFields"], "Record display");
206
+ let labelFields = distinctIds(display.labelFields, "label fields");
207
+ (labelFields.length > 2 || labelFields.some((name2) => !fieldName(name2))) && fail("Select one or two scalar label fields");
208
+ let suggestions = array(source.suggestions ?? [], "suggestions").map((value2) => {
209
+ let entry = object(value2, "suggestion");
210
+ keys(entry, ["record", "reason"], "suggestion");
211
+ let record2 = validateRecordReference(entry.record);
212
+ return record2.repository !== repository && fail("Suggestion belongs to another repository"), { record: record2, ...entry.reason === void 0 ? {} : { reason: text(entry.reason, "suggestion reason") } };
213
+ });
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
+ type: "records",
216
+ collection: { repository, name },
217
+ ...refPrefix ? { refPrefix } : {},
218
+ where: { ...where },
219
+ display: { labelFields },
220
+ suggestions
221
+ };
222
+ }
223
+ function validateResponseDefinition(value) {
224
+ let definition = object(value, "response definition");
225
+ keys(definition, ["type", "source"], "response definition"), definition.type !== "selection" && fail("Expected a selection definition");
226
+ let source = object(definition.source, "option source");
227
+ switch (source.type) {
228
+ case "authored":
229
+ return { type: "selection", source: validateAuthoredSource(source) };
230
+ case "records":
231
+ return { type: "selection", source: validateRecordSource(source) };
232
+ default:
233
+ return fail("Unsupported option source");
234
+ }
235
+ }
236
+ function handlerReference(value) {
237
+ let source = object(value, "handler reference");
238
+ return keys(source, ["id", "input"], "handler reference"), { id: id(source.id, "handler ID"), input: serializable(source.input) };
239
+ }
240
+ function defineHandler(value) {
241
+ let source = object(value, "handler");
242
+ return keys(source, ["id", "run"], "handler"), id(source.id, "handler ID"), typeof source.run != "function" && fail("Handler requires a run function"), Object.freeze({ id: value.id, run: value.run });
243
+ }
244
+ function respond(handler, input) {
245
+ return defineHandler(handler), handlerReference({ id: handler.id, input });
246
+ }
247
+ function question(value) {
248
+ let source = object(value, "question");
249
+ keys(source, ["id", "to", "allowedResponders", "prompt", "instructions", "response", "handler"], "question");
250
+ let to = personReference(source.to, "question assignee"), allowedResponders = source.allowedResponders === void 0 ? [to] : distinctPeople(source.allowedResponders, "allowed responders");
251
+ return allowedResponders.includes(to) || fail("Allowed responders must include the assignee"), {
252
+ id: id(source.id, "question ID"),
253
+ to,
254
+ allowedResponders,
255
+ prompt: text(source.prompt, "question prompt"),
256
+ ...source.instructions === void 0 ? {} : { instructions: text(source.instructions, "instructions") },
257
+ response: validateResponseDefinition(source.response),
258
+ ...Object.hasOwn(source, "handler") ? { handler: handlerReference(source.handler) } : {}
259
+ };
260
+ }
261
+ function presentation(source) {
262
+ let views = source.views === void 0 ? [] : array(source.views, "views").map((entry) => {
263
+ let view = object(entry, "view");
264
+ return keys(view, ["id", "view", "input"], "view"), { id: id(view.id, "view ID"), view: id(view.view, "view name"), input: serializable(view.input) };
265
+ });
266
+ new Set(views.map((view) => view.id)).size !== views.length && fail("Duplicate view ID");
267
+ let body2 = source.body === void 0 ? [] : array(source.body, "body").map(validateBlock);
268
+ for (let block of body2) {
269
+ let targets = block.kind === "record" ? [block.view] : block.kind === "records" ? [block.view, ...block.records.map((record2) => record2.view)] : [];
270
+ for (let target of targets) target !== void 0 && !views.some((view) => view.id === target) && fail(`Unknown Block View target: ${target}`);
271
+ }
272
+ return { title: text(source.title, "title"), description: text(source.description, "description"), views, body: body2 };
273
+ }
274
+ function validatePublication(value) {
275
+ let source = object(value, "publication");
276
+ source.kind !== "ask" && source.kind !== "digest" && fail("Publication requires an explicit kind"), keys(source, [
277
+ "kind",
278
+ "title",
279
+ "description",
280
+ "recipients",
281
+ "views",
282
+ "body",
283
+ "notifyImmediately",
284
+ ...source.kind === "ask" ? ["questions"] : []
285
+ ], "publication"), source.notifyImmediately !== void 0 && typeof source.notifyImmediately != "boolean" && fail("notifyImmediately must be a boolean");
286
+ let itemPresentation = { ...presentation(source), ...source.notifyImmediately === void 0 ? {} : { notifyImmediately: source.notifyImmediately } }, linkedAsks = (itemPresentation.body ?? []).flatMap((block) => block.kind === "asks" ? block.asks : []);
287
+ if (new Set(linkedAsks).size !== linkedAsks.length && fail("Ask belongs to more than one problem"), source.kind === "digest") return {
288
+ kind: "digest",
289
+ ...itemPresentation,
290
+ recipients: distinctPeople(source.recipients, "digest recipients")
291
+ };
292
+ itemPresentation.body?.some((block) => block.kind === "asks") && fail("Asks Blocks belong to Digests");
293
+ let questions = array(source.questions === void 0 ? [] : source.questions, "questions").map((value2) => question(value2));
294
+ new Set(questions.map((entry) => entry.id)).size !== questions.length && fail("Duplicate question ID");
295
+ let extra = source.recipients === void 0 ? [] : distinctPeople(source.recipients, "recipients", !1);
296
+ return !questions.length && !extra.length && fail("Ask requires at least one recipient"), {
297
+ kind: "ask",
298
+ ...itemPresentation,
299
+ questions,
300
+ recipients: [.../* @__PURE__ */ new Set([...extra, ...questions.flatMap((entry) => [entry.to, ...entry.allowedResponders])])]
301
+ };
302
+ }
303
+ function continueAsk(value) {
304
+ let source = object(value, "continueAsk");
305
+ return keys(source, [], "continueAsk"), { type: "continue" };
306
+ }
307
+ function resolution(value) {
308
+ let source = object(value, "resolution");
309
+ return keys(source, ["outcome", "evidence"], "resolution"), { outcome: text(source.outcome, "outcome"), ...source.evidence === void 0 ? {} : {
310
+ evidence: array(source.evidence, "evidence").map((entry) => serializable(entry))
311
+ } };
312
+ }
313
+ function resolveAsk(value) {
314
+ return { type: "resolve", ...resolution(value) };
315
+ }
316
+ function validateResolution(value) {
317
+ let source = object(value, "published resolution");
318
+ return keys(source, ["expectedRevision", "outcome", "evidence"], "published resolution"), (!Number.isSafeInteger(source.expectedRevision) || source.expectedRevision < 1) && fail("Expected revision must be a positive integer"), {
319
+ expectedRevision: source.expectedRevision,
320
+ ...resolution({
321
+ outcome: source.outcome,
322
+ ...source.evidence === void 0 ? {} : { evidence: source.evidence }
323
+ })
324
+ };
325
+ }
326
+ function publicationRequestKey(value) {
327
+ let options = object(value, "publication options");
328
+ return keys(options, ["requestKey"], "publication options"), id(options.requestKey, "publication request key");
329
+ }
330
+ function createInboxPublisher(transport) {
331
+ return {
332
+ async publishAsk(value, options) {
333
+ let source = object(value, "ask");
334
+ keys(source, ["title", "description", "recipients", "views", "body", "questions", "notifyImmediately"], "ask");
335
+ let result2 = await transport.publish({
336
+ requestKey: publicationRequestKey(options),
337
+ item: validatePublication({ ...source, kind: "ask" })
338
+ });
339
+ return id(result2.id, "published ask ID"), (!("revision" in result2) || !Number.isSafeInteger(result2.revision) || result2.revision !== 1) && fail("Initial ask publication must return revision 1"), { id: result2.id, revision: result2.revision };
340
+ },
341
+ async publishDigest(value, options) {
342
+ let source = object(value, "digest");
343
+ keys(source, ["title", "description", "recipients", "views", "body", "notifyImmediately"], "digest");
344
+ let result2 = await transport.publish({
345
+ requestKey: publicationRequestKey(options),
346
+ item: validatePublication({ ...source, kind: "digest" })
347
+ });
348
+ return { id: id(result2.id, "published digest ID") };
349
+ },
350
+ async publishResolution(askId, value) {
351
+ await transport.resolve({ askId: id(askId, "ask ID"), resolution: validateResolution(value) });
352
+ }
353
+ };
354
+ }
355
+
356
+ var REPOSITORY = /^[a-z0-9][a-z0-9._/-]{0,127}$/;
357
+ function validRepositoryAlias(value) {
358
+ return typeof value == "string" && REPOSITORY.test(value) && !value.includes("//") && !value.includes("..");
359
+ }
360
+
361
+ var RECORD_READ_INPUT_LIMIT = 1024 * 1024, RECORD_CAPTURE_OUTPUT_LIMIT = 25 * 1024 * 1024;
362
+ function validRecordRef(value) {
363
+ return typeof value == "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)+$/.test(value);
364
+ }
365
+ function validateRecordRead(value) {
366
+ if (!value || typeof value != "object" || Array.isArray(value))
367
+ throw new InboxContractError("Invalid selected-record read");
368
+ let input = value;
369
+ if (Object.keys(input).some((key) => !["repository", "refs", "atCommit"].includes(key)) || !validRepositoryAlias(input.repository) || !Array.isArray(input.refs) || !input.refs.length || input.refs.length > 1e4 || input.refs.some((ref) => !validRecordRef(ref)) || new Set(input.refs).size !== input.refs.length || input.atCommit !== void 0 && (typeof input.atCommit != "string" || !/^[0-9a-f]{40}$/.test(input.atCommit)))
370
+ throw new InboxContractError("Invalid selected-record read");
371
+ return {
372
+ repository: input.repository,
373
+ refs: [...input.refs],
374
+ ...input.atCommit === void 0 ? {} : { atCommit: input.atCommit }
375
+ };
376
+ }
377
+
378
+ var RECORD_WRITE_INPUT_LIMIT = 8 * 1024 * 1024, RoutineRecordWriteError = class extends Error {
379
+ constructor(message, effect) {
380
+ super(message);
381
+ this.effect = effect;
382
+ this.name = "RoutineRecordWriteError";
383
+ }
384
+ effect;
385
+ };
386
+ function validateRecordWrite(value) {
387
+ let input = serializable(value);
388
+ 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)
389
+ throw new InboxContractError("Invalid record update batch");
390
+ for (let edit of input.records) {
391
+ if (!edit || Array.isArray(edit) || typeof edit != "object" || !validRecordRef(edit.ref))
392
+ throw new InboxContractError("Invalid record edit");
393
+ if ("expectedVersion" in edit) {
394
+ 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)
395
+ throw new InboxContractError("Invalid record edit");
396
+ continue;
397
+ }
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
+ }
401
+ return structuredClone(input);
402
+ }
403
+ function validateRecordUpdate(value) {
404
+ let input = validateRecordWrite(value);
405
+ if (input.records.some((record2) => !("expectedVersion" in record2)))
406
+ throw new InboxContractError("Record creation is unavailable for this operation");
407
+ return input;
408
+ }
409
+ function validateRecordDelete(value) {
410
+ let input = serializable(value);
411
+ if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record2) => record2?.ref)).size !== input.records.length)
412
+ throw new InboxContractError("Invalid record deletion batch");
413
+ for (let record2 of input.records)
414
+ if (!record2 || Array.isArray(record2) || typeof record2 != "object" || Object.keys(record2).sort().join(",") !== "expectedVersion,ref" || !validRecordRef(record2.ref) || typeof record2.expectedVersion != "string" || !/^record-v1:[0-9a-f]{64}$/.test(record2.expectedVersion))
415
+ throw new InboxContractError("Invalid record deletion");
416
+ return structuredClone(input);
417
+ }
418
+ function validateRecordChange(value) {
419
+ let input = serializable(value);
420
+ if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record2) => record2?.ref)).size !== input.records.length || new TextEncoder().encode(JSON.stringify(input)).byteLength > RECORD_WRITE_INPUT_LIMIT)
421
+ throw new InboxContractError("Invalid record change batch");
422
+ let writes = [], deletes = [];
423
+ for (let record2 of input.records) {
424
+ if (!record2 || typeof record2 != "object" || Array.isArray(record2))
425
+ throw new InboxContractError("Invalid record change");
426
+ "set" in record2 || "fields" in record2 ? writes.push(record2) : deletes.push(record2);
427
+ }
428
+ return writes.length && validateRecordWrite({ repository: input.repository, records: writes }), deletes.length && validateRecordDelete({ repository: input.repository, records: deletes }), structuredClone(input);
429
+ }
430
+ function validateRoutineRecordWrite(value) {
431
+ let body2 = serializable(value);
432
+ 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)
433
+ throw new InboxContractError("A stable routine request key is required");
434
+ return { input: validateRecordChange(body2.input), requestKey: body2.requestKey };
435
+ }
436
+ function validCreatePath(value) {
437
+ return value === value.trim() && value.endsWith(".md") && !value.startsWith("/") && value.split("/").every((segment) => !!segment && ![".", ".."].includes(segment) && /^[a-zA-Z0-9_.-]+$/.test(segment));
438
+ }
439
+
440
+ function validGoogleScopes(value) {
441
+ return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
442
+ }
443
+
444
+ var ROUTINE_CONNECTION_CREDENTIALS_ENV = "ODDINGTON_ROUTINE_CONNECTION_CREDENTIALS";
445
+ 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);
446
+ function parseConnectionLease(raw, now = Date.now()) {
447
+ let v = raw;
448
+ 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))
449
+ throw new Error("Invalid connection credential");
450
+ if (v.provider === "gmail") {
451
+ 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)
452
+ throw new Error("Invalid connection credential");
453
+ return v;
454
+ }
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
+ }
459
+
460
+ var AirtableOperationError = class extends Error {
461
+ constructor(message, effect = "unapplied", receipts = []) {
462
+ super(message);
463
+ this.effect = effect;
464
+ this.receipts = receipts;
465
+ this.name = "AirtableOperationError";
466
+ }
467
+ effect;
468
+ receipts;
469
+ };
470
+
471
+ 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);
472
+ function fail2(message) {
473
+ throw new AirtableOperationError(message);
474
+ }
475
+ function object2(v) {
476
+ return (!v || typeof v != "object" || Array.isArray(v) || ![null, Object.prototype].includes(Object.getPrototypeOf(v))) && fail2("Invalid Airtable data"), v;
477
+ }
478
+ function keys2(v, allowed, required = allowed) {
479
+ (Object.keys(v).some((k) => !allowed.includes(k)) || required.some((k) => !Object.hasOwn(v, k))) && fail2("Invalid Airtable input");
480
+ }
481
+ function text2(v) {
482
+ return typeof v == "string" && v.length > 0 && v.length <= 1024;
483
+ }
484
+ function fields2(v, nonempty = !1) {
485
+ let result2 = object2(v);
486
+ (nonempty && !Object.keys(result2).length || Object.keys(result2).some((k) => !text2(k))) && fail2("Invalid Airtable fields");
487
+ try {
488
+ return serializable(result2);
489
+ } catch {
490
+ return fail2("Invalid Airtable fields");
491
+ }
492
+ }
493
+ function record(v) {
494
+ let r = object2(v);
495
+ 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: fields2(r.fields) };
496
+ }
497
+ function equal(a, b) {
498
+ if (a === b) return !0;
499
+ if (!a || !b || typeof a != "object" || typeof b != "object" || Array.isArray(a) !== Array.isArray(b)) return !1;
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
+ ));
505
+ }
506
+ function createConnections(transport) {
507
+ return { airtable(name) {
508
+ (typeof name != "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(name)) && fail2("Invalid Airtable connection name");
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
+ } };
521
+ }
522
+ async function executeAirtableOperation(authority, operation2, raw, options = {}) {
523
+ let { name, baseId, accessToken } = authority;
524
+ (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(name) || !/^app[A-Za-z0-9]+$/.test(baseId) || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(accessToken)) && fail2("Airtable connection unavailable");
525
+ let input;
526
+ try {
527
+ input = object2(serializable(raw));
528
+ } catch {
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 }))
538
+ });
539
+ async function request(path2, init2 = {}, write = !1) {
540
+ let response;
541
+ try {
542
+ response = await (options.fetch ?? fetch)(`https://api.airtable.com/v0/${path2}`, {
543
+ ...init2,
544
+ headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
545
+ redirect: "manual",
546
+ signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3)
547
+ });
548
+ } catch {
549
+ throw new AirtableOperationError("Airtable request acknowledgment unavailable", write ? "uncertain" : "unapplied");
550
+ }
551
+ if (!response.ok)
552
+ throw await response.body?.cancel().catch(() => {
553
+ }), new AirtableOperationError("Airtable request rejected", write && (response.status >= 500 || response.status === 408 || response.status < 400) ? "uncertain" : "unapplied");
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;
605
+ }
606
+ mutation || fail2("Unsupported Airtable operation"), keys2(input, ["table", "records"]), (!Array.isArray(input.records) || !input.records.length || input.records.length > 10) && fail2("Invalid Airtable batch");
607
+ let kind = operation2.slice(8), ids = /* @__PURE__ */ new Set(), records = input.records.map((value) => {
608
+ let r = object2(value);
609
+ if (keys2(r, kind === "create" ? ["fields"] : kind === "update" ? ["id", "expected", "set"] : ["id", "expected"]), kind === "create") return { fields: fields2(r.fields, !0) };
610
+ (!recordId(r.id) || ids.has(r.id)) && fail2("Invalid Airtable batch record ID"), ids.add(r.id);
611
+ let set = kind === "update" ? fields2(r.set, !0) : void 0;
612
+ return set && Object.values(set).some((value2) => value2 === null) && fail2("Airtable guard null cannot be an update value"), { id: r.id, expected: fields2(r.expected), ...set ? { set } : {} };
613
+ });
614
+ for (let r of records) {
615
+ if (!("id" in r)) continue;
616
+ let current2 = record(await request(`${path}/${r.id}`));
617
+ current2.id !== r.id && fail2("Malformed Airtable record");
618
+ for (let [key, value] of Object.entries(r.expected))
619
+ (value === null ? Object.hasOwn(current2.fields, key) : !Object.hasOwn(current2.fields, key) || !equal(current2.fields[key], value)) && fail2("Airtable reviewed values are stale");
620
+ }
621
+ await options.beforeMutation?.();
622
+ let query = new URLSearchParams();
623
+ if (kind === "delete") for (let id2 of ids) query.append("records[]", id2);
624
+ let result2 = await request(`${path}${kind === "delete" ? `?${query}` : ""}`, {
625
+ method: kind === "create" ? "POST" : kind === "update" ? "PATCH" : "DELETE",
626
+ ...kind === "delete" ? {} : { body: JSON.stringify({ records: records.map((r) => "id" in r ? { id: r.id, fields: r.set } : r) }) }
627
+ }, !0), acknowledged = [], returned = [];
628
+ try {
629
+ if (!Array.isArray(result2.records)) throw new Error();
630
+ for (let value of result2.records) {
631
+ let r = object2(value);
632
+ if (!recordId(r.id) || acknowledged.includes(r.id) || kind !== "create" && !ids.has(r.id) || kind === "delete" && r.deleted !== !0) throw new Error();
633
+ acknowledged.push(r.id), kind !== "delete" && returned.push(record(r));
634
+ }
635
+ if (acknowledged.length !== records.length) throw new Error();
636
+ } catch {
637
+ throw new AirtableOperationError(
638
+ "Incomplete Airtable mutation acknowledgment",
639
+ acknowledged.length ? "partial" : "uncertain",
640
+ acknowledged.length ? [receipt(acknowledged)] : []
641
+ );
642
+ }
643
+ return { receipt: receipt(acknowledged), ...kind === "delete" ? {} : { records: returned } };
644
+ }
645
+ function nextOffset(value, seen) {
646
+ if (value !== void 0)
647
+ return (!text2(value) || seen.has(value)) && fail2("Malformed Airtable pagination"), seen.add(value), value;
648
+ }
649
+
650
+ var MANAGED_CONNECTION_CAPABILITY_ENV = "ODDINGTON_MANAGED_CONNECTION_CAPABILITY";
651
+ function managedConnections(capability) {
652
+ let endpoint = new URL(capability.endpoint);
653
+ if (endpoint.protocol !== "http:" || endpoint.hostname !== "127.0.0.1" || !endpoint.port || endpoint.pathname !== "/" || endpoint.search || endpoint.hash || endpoint.username || endpoint.password || !/^[0-9a-f-]{36}$/.test(capability.token)) throw new AirtableOperationError("Airtable capability unavailable");
654
+ return createConnections(async (name, operation2, input) => {
655
+ let safeInput;
656
+ try {
657
+ safeInput = serializable(input);
658
+ } catch {
659
+ safeInput = null;
660
+ }
661
+ try {
662
+ let response = await fetch(new URL("airtable", endpoint), {
663
+ method: "POST",
664
+ redirect: "error",
665
+ headers: { authorization: `Bearer ${capability.token}`, "content-type": "application/json" },
666
+ body: JSON.stringify({ name, operation: operation2, input: safeInput }),
667
+ signal: AbortSignal.timeout(125e3)
668
+ }), value = await response.json();
669
+ if (!response.ok) throw new AirtableOperationError(
670
+ "Airtable operation failed; the execution retains its evidence",
671
+ value.effect === "partial" || value.effect === "uncertain" ? value.effect : "unapplied",
672
+ value.receipts ?? []
673
+ );
674
+ return value;
675
+ } catch (error) {
676
+ throw error instanceof AirtableOperationError ? error : new AirtableOperationError(
677
+ "Airtable parent acknowledgment unavailable",
678
+ ["records.create", "records.update", "records.delete"].includes(operation2) ? "uncertain" : "unapplied"
679
+ );
680
+ }
681
+ });
682
+ }
683
+
684
+ var connections = /* @__PURE__ */ createConnections(async (name, operation2, input) => {
685
+ try {
686
+ let managed = process.env[MANAGED_CONNECTION_CAPABILITY_ENV];
687
+ if (managed) {
688
+ let client = managedConnections(JSON.parse(managed)).airtable(name);
689
+ return operation2 === "tables.list" ? await client.tables.list() : await client.records[operation2.slice(8)](input);
690
+ }
691
+ let raw = JSON.parse(process.env[ROUTINE_CONNECTION_CREDENTIALS_ENV] ?? "[]");
692
+ if (!Array.isArray(raw)) throw new Error();
693
+ let leases = raw.map((value) => parseConnectionLease(value));
694
+ if (leases.some((lease2) => lease2.provider !== "airtable") || new Set(leases.map((lease2) => lease2.name)).size !== leases.length) throw new Error();
695
+ let lease = leases.find((lease2) => lease2.name === name);
696
+ if (!lease || lease.provider !== "airtable") throw new Error();
697
+ return await executeAirtableOperation(lease, operation2, input);
698
+ } catch (error) {
699
+ throw error instanceof AirtableOperationError ? error : new AirtableOperationError("Airtable connection unavailable");
700
+ }
701
+ });
702
+
703
+ 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";
711
+ }
712
+ code;
713
+ retryable;
714
+ effect;
715
+ component;
716
+ };
717
+
718
+ var invalid = () => {
719
+ throw new DataOperationError("invalid_query");
720
+ }, scalarText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
721
+ function captureRecordsSearchInput(query, options) {
722
+ if (!scalarText(query)) return invalid();
723
+ let length = [...query.trim()].length;
724
+ if (length < 1 || length > 256) return invalid();
725
+ if (options === void 0) return { query };
726
+ if (!options || typeof options != "object" || Array.isArray(options) || ![Object.prototype, null].includes(Object.getPrototypeOf(options))) return invalid();
727
+ let descriptors = Object.getOwnPropertyDescriptors(options);
728
+ if (Reflect.ownKeys(options).some((key) => typeof key != "string" || !["collections", "limit"].includes(key) || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value"))) return invalid();
729
+ let captured = {};
730
+ if (Object.hasOwn(descriptors, "collections")) {
731
+ let supplied = descriptors.collections.value;
732
+ if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return invalid();
733
+ let collections = [];
734
+ for (let index = 0; index < supplied.length; index++) {
735
+ let descriptor2 = Object.getOwnPropertyDescriptor(supplied, String(index));
736
+ if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !scalarText(descriptor2.value)) return invalid();
737
+ collections.push(descriptor2.value);
738
+ }
739
+ if (new Set(collections).size !== collections.length) return invalid();
740
+ captured.collections = collections;
741
+ }
742
+ if (Object.hasOwn(descriptors, "limit")) {
743
+ let supplied = descriptors.limit.value;
744
+ if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid();
745
+ captured.limit = supplied;
746
+ }
747
+ return { query, options: captured };
748
+ }
749
+
750
+ var DATA_REQUEST_LIMIT = 256 * 1024, invalidChange = () => {
751
+ throw new DataOperationError("invalid_change", !1, "unapplied");
752
+ };
753
+ function mapping(value) {
754
+ 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;
755
+ }
756
+ function captureValue(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
757
+ if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return value;
758
+ if (!value || typeof value != "object" || ancestors.has(value) || depth > 32) return invalidChange();
759
+ ancestors.add(value);
760
+ try {
761
+ 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)]));
762
+ } finally {
763
+ ancestors.delete(value);
764
+ }
765
+ }
766
+ function captureDataDelete(record2) {
767
+ let identity = mapping(record2);
768
+ return !validRecordRef(identity.$ref) || typeof identity.$version != "string" || !identity.$version.length || identity.$version.length > 512 ? invalidChange() : { ref: identity.$ref, expectedVersion: identity.$version };
769
+ }
770
+ function captureDataUpdate(record2, set) {
771
+ let identity = captureDataDelete(record2), fields3 = mapping(set);
772
+ return !Object.keys(fields3).length || Object.keys(fields3).some((key) => key.startsWith("$") || ["id", "type"].includes(key)) ? invalidChange() : { ...identity, set: captureValue(fields3) };
773
+ }
774
+ function captureDataUpdates(value, allowEmpty = !1) {
775
+ if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4) return invalidChange();
776
+ let updates = value.map((item) => {
777
+ let row = mapping(item);
778
+ return Object.keys(row).sort().join(",") !== "expectedVersion,ref,set" ? invalidChange() : captureDataUpdate({ $ref: row.ref, $version: row.expectedVersion }, row.set);
779
+ });
780
+ return new Set(updates.map((update) => update.ref)).size !== updates.length ? invalidChange() : updates;
781
+ }
782
+ function captureDataCreate(value) {
783
+ let row = mapping(value);
784
+ if (!validRecordRef(row.$ref) || Object.keys(row).some((key) => key.startsWith("$") && !["$ref", "$filing"].includes(key)))
785
+ return invalidChange();
786
+ let { $ref: ref, $filing, ...fields3 } = row, filing;
787
+ if ($filing !== void 0) {
788
+ let components = mapping($filing);
789
+ 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)))
790
+ return invalidChange();
791
+ filing = { ...components };
792
+ }
793
+ return { ref, fields: captureValue(fields3), ...filing ? { filing } : {} };
794
+ }
795
+ function captureDataChanges(updatesValue, createsValue = [], deletesValue = []) {
796
+ let updates = captureDataUpdates(updatesValue, !0);
797
+ if (!Array.isArray(createsValue) || createsValue.length > 1e4) return invalidChange();
798
+ let creates = createsValue.map((item) => {
799
+ let row = mapping(item);
800
+ if (Object.keys(row).some((key) => !["ref", "fields", "filing"].includes(key)) || !Object.hasOwn(row, "ref") || !Object.hasOwn(row, "fields")) return invalidChange();
801
+ let fields3 = mapping(row.fields);
802
+ return Object.keys(fields3).some((key) => key.startsWith("$")) ? invalidChange() : captureDataCreate({ ...fields3, $ref: row.ref, ...Object.hasOwn(row, "filing") ? { $filing: row.filing } : {} });
803
+ });
804
+ if (!Array.isArray(deletesValue) || deletesValue.length > 1e4) return invalidChange();
805
+ let deletes = deletesValue.map((item) => {
806
+ let row = mapping(item);
807
+ return Object.keys(row).sort().join(",") !== "expectedVersion,ref" ? invalidChange() : captureDataDelete({ $ref: row.ref, $version: row.expectedVersion });
808
+ }), refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
809
+ return !refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length ? invalidChange() : { updates, creates, deletes };
810
+ }
811
+ function checkDataRequestSize(input) {
812
+ if (new TextEncoder().encode(JSON.stringify(input)).byteLength > DATA_REQUEST_LIMIT)
813
+ throw new DataOperationError("request_too_large", !1, "unapplied");
814
+ }
815
+ function dataTransportError(status, value) {
816
+ let failures2 = {
817
+ 400: ["invalid_query", "invalid_change"],
818
+ 401: ["unauthenticated"],
819
+ 403: ["access_denied"],
820
+ 409: ["conflict", "publication_pending"],
821
+ 413: ["request_too_large"],
822
+ 429: ["capacity"],
823
+ 503: ["unavailable", "publication_pending"]
824
+ }, 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;
825
+ return new DataOperationError(
826
+ code,
827
+ ["capacity", "unavailable", "publication_pending"].includes(code),
828
+ code === "conflict" ? "unapplied" : effect,
829
+ code === "invalid_change" && typeof row?.component == "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(row.component) ? row.component : void 0
830
+ );
831
+ }
832
+
833
+ var FileOperationError = class extends Error {
834
+ constructor(code, status, retryable = !1) {
835
+ super(code);
836
+ this.code = code;
837
+ this.status = status;
838
+ this.retryable = retryable;
839
+ }
840
+ code;
841
+ status;
842
+ retryable;
843
+ }, 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;
844
+ function parseFileDescriptor(value) {
845
+ let invalid3 = () => {
846
+ throw new FileOperationError("unavailable", 503, !0);
847
+ };
848
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
849
+ let f = value;
850
+ 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 invalid3();
851
+ let d = f.details;
852
+ if (d.kind === "other") {
853
+ if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid3();
854
+ } else if (d.kind === "csv") {
855
+ 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 invalid3();
856
+ } else return invalid3();
857
+ for (let [name, descriptor2] of Object.entries(f.enrichments)) {
858
+ if (!safeEnrichmentName(name)) return invalid3();
859
+ parseEnrichmentDescriptor(descriptor2);
860
+ }
861
+ return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid3() : f;
862
+ }
863
+ function parseEnrichmentDescriptor(value) {
864
+ let invalid3 = () => {
865
+ throw new FileOperationError("unavailable", 503, !0);
866
+ };
867
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
868
+ let d = value;
869
+ 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) ? invalid3() : d;
870
+ }
871
+ function safeEnrichmentName(value) {
872
+ return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
873
+ }
874
+ function parseFileEnrichMetadata(value, maxBytes) {
875
+ let invalid3 = () => {
876
+ throw new FileOperationError("invalid_input", 400);
877
+ };
878
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
879
+ let d = value;
880
+ 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 invalid3();
881
+ if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
882
+ return d;
883
+ }
884
+ function safeFileName(value) {
885
+ 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);
886
+ }
887
+ function safeFilePath(value) {
888
+ return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
889
+ }
890
+ function parseFileCreateMetadata(value, maxBytes) {
891
+ let invalid3 = () => {
892
+ throw new FileOperationError("invalid_input", 400);
893
+ };
894
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
895
+ let input = value;
896
+ if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid3();
897
+ let path = input.path === void 0 ? "Files" : input.path;
898
+ if (!safeFileName(input.name) || !safeFilePath(path) || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid3();
899
+ if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
900
+ 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 invalid3();
901
+ let csv;
902
+ if (input.csv !== void 0) {
903
+ if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid3();
904
+ let hints = input.csv;
905
+ if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid3();
906
+ 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;
907
+ if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid3();
908
+ csv = { encoding, delimiter, headerRow };
909
+ }
910
+ return {
911
+ name: input.name,
912
+ path,
913
+ ...input.storage ? { storage: input.storage } : {},
914
+ ...csv ? { csv } : {},
915
+ size: input.size,
916
+ requestKey: input.requestKey
917
+ };
918
+ }
919
+
920
+ var DEFAULT_ORIGIN = "https://auth.oddington.ai";
921
+ function oddingtonOrigin(environment = process.env) {
922
+ let value = environment.ODDINGTON_OS_ORIGIN ?? DEFAULT_ORIGIN, url = new URL(value);
923
+ if (url.protocol !== "https:" && url.hostname !== "127.0.0.1")
924
+ throw new Error("ODDINGTON_OS_ORIGIN must use HTTPS");
925
+ return url.origin;
926
+ }
927
+
928
+ var memberContext;
929
+ function installMemberContext(resolve) {
930
+ return memberContext = resolve, () => {
931
+ memberContext === resolve && (memberContext = void 0);
932
+ };
933
+ }
934
+ async function resolveAppContext() {
935
+ let host = memberContext;
936
+ if (!host) throw new Error("App context unavailable.");
937
+ let context2 = await host();
938
+ if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now())
939
+ throw new Error("App context unavailable.");
940
+ if (context2.kind !== "local" && (typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)))
941
+ throw new Error("App context unavailable.");
942
+ return context2;
943
+ }
944
+ async function resolveDataContext() {
945
+ try {
946
+ let host = memberContext;
947
+ if (host !== void 0) {
948
+ let context2 = await host();
949
+ if (host !== memberContext || !context2.isCurrent() || context2.expiresAt <= Date.now()) throw new Error();
950
+ if (context2.kind === "local") return { local: context2.requestData };
951
+ if (!context2 || !Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
952
+ return checked(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` });
953
+ }
954
+ throw new Error();
955
+ } catch {
956
+ throw new DataOperationError("context_unavailable");
957
+ }
958
+ }
959
+ async function resolveFilesContext() {
960
+ try {
961
+ let environment = {}, host = memberContext;
962
+ if (host) {
963
+ let context2 = await host();
964
+ if (context2.kind === "local") throw new Error();
965
+ if (!Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now() || typeof context2.accessToken != "string" || !/^[^\s]{1,8192}$/.test(context2.accessToken)) throw new Error();
966
+ return {
967
+ ...filesContext(context2.workspace, context2.endpoint, { authorization: `Bearer ${context2.accessToken}` }),
968
+ isCurrent: context2.isCurrent,
969
+ local: void 0,
970
+ localFiles: void 0
971
+ };
972
+ }
973
+ throw new Error();
974
+ } catch {
975
+ throw new FileOperationError("unsupported_context", 400);
976
+ }
977
+ }
978
+ function filesContext(workspace, endpoint, headers) {
979
+ 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();
980
+ return { url: `${endpoint}/api/files`, headers: { ...headers, "x-oddington-workspace": workspace }, isCurrent: () => !0 };
981
+ }
982
+ function checked(workspace, endpoint, headers) {
983
+ if (!workspace || !/^[a-z0-9][a-z0-9-]{0,127}$/.test(workspace)) throw new Error();
984
+ let origin = new URL(endpoint);
985
+ if (origin.protocol !== "https:" || origin.username || origin.password || origin.search || origin.hash || origin.pathname !== "/")
986
+ throw new Error();
987
+ return { url: `${origin.origin}/api/data`, headers: { ...headers, "x-oddington-workspace": workspace } };
988
+ }
989
+
990
+ var data = {
991
+ repository(repository) {
992
+ async function send2(input) {
993
+ let context2 = await resolveDataContext();
994
+ try {
995
+ if (checkDataRequestSize({ repository, ...input }), "local" in context2) return await context2.local("data/query", { repository, ...input });
996
+ let response = await fetch(context2.url, {
997
+ method: "POST",
998
+ redirect: "error",
999
+ cache: "no-store",
1000
+ headers: { "content-type": "application/json", ...context2.headers },
1001
+ body: JSON.stringify({ repository, ...input })
1002
+ }), result2 = await response.json();
1003
+ if (!response.ok)
1004
+ throw dataTransportError(response.status, result2);
1005
+ return result2;
1006
+ } catch (error) {
1007
+ throw error instanceof DataOperationError ? error : new DataOperationError("unavailable", !0);
1008
+ }
1009
+ }
1010
+ return {
1011
+ collections: {
1012
+ list: () => send2({ operation: "collections.list" }),
1013
+ schema: (name) => send2({ operation: "collections.schema", name })
1014
+ },
1015
+ records: {
1016
+ select: (query) => send2({ operation: "records.select", query }),
1017
+ search: (query, options) => {
1018
+ try {
1019
+ return send2({ operation: "records.search", ...captureRecordsSearchInput(query, options) });
1020
+ } catch (error) {
1021
+ return Promise.reject(error);
1022
+ }
1023
+ }
1024
+ },
1025
+ changes() {
1026
+ let updates = [], creates = [], deletes = [], publicationId, pending, confirmed, snapshot = () => {
1027
+ let captured = captureDataChanges(updates, creates, deletes), input = {
1028
+ updates: captured.updates,
1029
+ ...captured.creates.length ? { creates: captured.creates } : {},
1030
+ ...captured.deletes.length ? { deletes: captured.deletes } : {}
1031
+ };
1032
+ return checkDataRequestSize({
1033
+ repository,
1034
+ operation: "changes.publish",
1035
+ ...input,
1036
+ publicationId: publicationId ?? "00000000-0000-4000-8000-000000000000"
1037
+ }), input;
1038
+ };
1039
+ return {
1040
+ create(record2) {
1041
+ if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1042
+ let next = [...creates, captureDataCreate(record2)];
1043
+ captureDataChanges(updates, next, deletes), checkDataRequestSize({
1044
+ repository,
1045
+ operation: "changes.publish",
1046
+ updates,
1047
+ creates: next,
1048
+ ...deletes.length ? { deletes } : {},
1049
+ publicationId: "00000000-0000-4000-8000-000000000000"
1050
+ }), creates = next;
1051
+ },
1052
+ update(record2, set) {
1053
+ if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1054
+ let next = [...updates, captureDataUpdate(record2, set)];
1055
+ captureDataChanges(next, creates, deletes), checkDataRequestSize({
1056
+ repository,
1057
+ operation: "changes.publish",
1058
+ updates: next,
1059
+ ...creates.length ? { creates } : {},
1060
+ ...deletes.length ? { deletes } : {},
1061
+ publicationId: "00000000-0000-4000-8000-000000000000"
1062
+ }), updates = next;
1063
+ },
1064
+ delete(record2) {
1065
+ if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1066
+ let next = [...deletes, captureDataDelete(record2)];
1067
+ captureDataChanges(updates, creates, next), checkDataRequestSize({
1068
+ repository,
1069
+ operation: "changes.publish",
1070
+ updates,
1071
+ deletes: next,
1072
+ ...creates.length ? { creates } : {},
1073
+ publicationId: "00000000-0000-4000-8000-000000000000"
1074
+ }), deletes = next;
1075
+ },
1076
+ async validate() {
1077
+ await send2({ operation: "changes.validate", ...snapshot() });
1078
+ },
1079
+ publish() {
1080
+ if (confirmed) return Promise.resolve(structuredClone(confirmed));
1081
+ if (!pending) {
1082
+ let captured;
1083
+ try {
1084
+ captured = snapshot();
1085
+ } catch (error) {
1086
+ return Promise.reject(error);
1087
+ }
1088
+ publicationId ??= crypto.randomUUID(), pending = send2({ operation: "changes.publish", ...captured, publicationId }).then((result2) => (confirmed = structuredClone(result2), result2)).catch((error) => {
1089
+ throw error instanceof DataOperationError ? new DataOperationError(
1090
+ error.code,
1091
+ error.retryable,
1092
+ error.effect ?? (["unavailable", "publication_pending"].includes(error.code) ? "uncertain" : "unapplied"),
1093
+ error.component
1094
+ ) : new DataOperationError("unavailable", !0, "uncertain");
1095
+ }).finally(() => {
1096
+ pending = void 0;
1097
+ });
1098
+ }
1099
+ return pending.then((result2) => structuredClone(result2));
1100
+ }
1101
+ };
1102
+ }
1103
+ };
1104
+ }
1105
+ };
1106
+
1107
+ 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, invalid2 = () => new FileOperationError("invalid_input", 400), unavailable = () => new FileOperationError("unavailable", 503, !0), failures = {
1108
+ 400: ["invalid_input", "unsupported_context"],
1109
+ 401: ["unauthenticated"],
1110
+ 403: ["access_denied"],
1111
+ 405: ["method_not_allowed"],
1112
+ 409: ["conflict", "local_only"],
1113
+ 413: ["request_too_large"],
1114
+ 429: ["capacity"],
1115
+ 503: ["integrity", "unavailable"]
1116
+ };
1117
+ async function boundedBytes(stream, limit) {
1118
+ if (!stream) return new Uint8Array();
1119
+ let reader = stream.getReader(), bytes = new Uint8Array(limit), size = 0;
1120
+ try {
1121
+ for (; ; ) {
1122
+ let { done, value } = await reader.read();
1123
+ if (done) break;
1124
+ if (!(value instanceof Uint8Array)) throw invalid2();
1125
+ if (size += value.byteLength, size > limit) throw new FileOperationError("request_too_large", 413);
1126
+ bytes.set(value, size - value.byteLength);
1127
+ }
1128
+ } finally {
1129
+ reader.releaseLock();
1130
+ }
1131
+ return bytes.slice(0, size);
1132
+ }
1133
+ async function timed(work, parent, timeout = TIMEOUT_MS) {
1134
+ let controller = new AbortController(), timer, abort, expiry = new Promise((_, reject) => {
1135
+ abort = () => {
1136
+ controller.abort(), reject(unavailable());
1137
+ }, timer = setTimeout(abort, timeout), parent?.addEventListener("abort", abort, { once: !0 }), parent?.aborted && abort();
1138
+ });
1139
+ try {
1140
+ return await Promise.race([work(controller.signal), expiry]);
1141
+ } finally {
1142
+ clearTimeout(timer), parent?.removeEventListener("abort", abort);
1143
+ }
1144
+ }
1145
+ async function result(response, maximum = RESULT_BYTES) {
1146
+ try {
1147
+ let bytes = await boundedBytes(response.body, maximum);
1148
+ return JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(bytes));
1149
+ } catch {
1150
+ throw unavailable();
1151
+ }
1152
+ }
1153
+ async function hostedContent(url, init2, parent, upstream = fetch) {
1154
+ let delivery = new AbortController(), abort = () => delivery.abort(parent.reason);
1155
+ parent.addEventListener("abort", abort, { once: !0 }), parent.aborted && abort();
1156
+ let detach = () => parent.removeEventListener("abort", abort);
1157
+ try {
1158
+ let response = await upstream(url, { ...init2, signal: delivery.signal });
1159
+ if (delivery.signal.aborted)
1160
+ throw response.body?.cancel().catch(() => {
1161
+ }), unavailable();
1162
+ if (!response.body)
1163
+ return detach(), response;
1164
+ let source = response.body, reader, settled = !1, cancel = async (reason) => {
1165
+ if (!settled) {
1166
+ settled = !0, detach(), delivery.abort(reason);
1167
+ try {
1168
+ await (reader ? reader.cancel(reason) : source.cancel(reason));
1169
+ } catch (error) {
1170
+ if (error !== delivery.signal.reason && !(error instanceof Error && error.name === "AbortError")) throw error;
1171
+ } finally {
1172
+ reader?.releaseLock();
1173
+ }
1174
+ }
1175
+ }, body2 = new ReadableStream({
1176
+ async pull(controller) {
1177
+ try {
1178
+ reader ??= source.getReader();
1179
+ let { done, value } = await reader.read();
1180
+ if (settled) return;
1181
+ done ? (settled = !0, detach(), reader.releaseLock(), controller.close()) : controller.enqueue(value);
1182
+ } catch (error) {
1183
+ if (settled) return;
1184
+ controller.error(error), await cancel(error).catch(() => {
1185
+ });
1186
+ }
1187
+ },
1188
+ cancel
1189
+ }, { highWaterMark: 0 }), wrapped = new Response(body2, { status: response.status, statusText: response.statusText, headers: response.headers });
1190
+ for (let key of ["url", "redirected", "type"])
1191
+ Object.defineProperty(wrapped, key, { value: response[key] });
1192
+ return wrapped;
1193
+ } catch (error) {
1194
+ throw detach(), delivery.abort(error), error;
1195
+ }
1196
+ }
1197
+ async function send(suffix, init2, descriptor2, initial, parent) {
1198
+ let original = initial, checkCurrent = () => {
1199
+ if (parent?.aborted) throw unavailable();
1200
+ if (!original.isCurrent()) throw new FileOperationError("unsupported_context", 400);
1201
+ };
1202
+ for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
1203
+ checkCurrent();
1204
+ let context2 = await resolveFilesContext();
1205
+ if (checkCurrent(), context2.url !== original.url) throw new FileOperationError("unsupported_context", 400);
1206
+ try {
1207
+ return await (async () => {
1208
+ let response = context2.localFiles ? await context2.localFiles(suffix, init2, parent) : await (async () => {
1209
+ let url = `${context2.url}${suffix}`, request = {
1210
+ ...init2,
1211
+ signal: parent,
1212
+ headers: { ...context2.headers, ...init2.headers },
1213
+ redirect: "error",
1214
+ cache: "no-store"
1215
+ };
1216
+ return descriptor2 ? fetch(url, request) : hostedContent(url, request, parent);
1217
+ })();
1218
+ if (!response.ok) {
1219
+ 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);
1220
+ throw code ? new FileOperationError(code, response.status, code === "capacity" || code === "unavailable") : unavailable();
1221
+ }
1222
+ if (response.status !== 200)
1223
+ throw response.body?.cancel().catch(() => {
1224
+ }), unavailable();
1225
+ try {
1226
+ checkCurrent();
1227
+ } catch (error) {
1228
+ throw response.body?.cancel().catch(() => {
1229
+ }), error;
1230
+ }
1231
+ if (!descriptor2) return response;
1232
+ let value = await result(response, descriptor2 === "control" ? CONTROL_RESULT_BYTES : RESULT_BYTES);
1233
+ if (descriptor2 === "control")
1234
+ return checkCurrent(), value;
1235
+ let parsed;
1236
+ if (descriptor2 === "enrichment") {
1237
+ if (!value || typeof value != "object" || Array.isArray(value)) throw unavailable();
1238
+ let { name, ...metadata } = value;
1239
+ if (!safeEnrichmentName(name)) throw unavailable();
1240
+ parsed = { name, ...parseEnrichmentDescriptor(metadata) };
1241
+ } else parsed = parseFileDescriptor(value);
1242
+ return checkCurrent(), parsed;
1243
+ })();
1244
+ } catch (error) {
1245
+ let failure = error instanceof FileOperationError ? error : unavailable();
1246
+ if (init2.body instanceof ReadableStream || !failure.retryable || attempt + 1 === ATTEMPTS) throw failure;
1247
+ }
1248
+ }
1249
+ throw unavailable();
1250
+ }
1251
+ function idPath(id2) {
1252
+ if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid2();
1253
+ return `/${id2}`;
1254
+ }
1255
+ async function multipartCreate(input, metadata, exchange, release = () => {
1256
+ active--;
1257
+ }, upload) {
1258
+ 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) => {
1259
+ let task = work(signal);
1260
+ return pending.add(task), task.then(() => pending.delete(task), () => pending.delete(task)), task;
1261
+ }, void 0, timeout), control = (body2) => ({
1262
+ method: "POST",
1263
+ headers: { "content-type": "application/json", ...ticket ? { "x-oddington-transfer": ticket } : {} },
1264
+ body: JSON.stringify(body2)
1265
+ });
1266
+ try {
1267
+ let start = await request((signal) => exchange(`${route}/start`, control(upload?.file ?? metadata), "control", signal));
1268
+ if (!start || typeof start != "object" || Array.isArray(start)) throw unavailable();
1269
+ let session = start;
1270
+ 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();
1271
+ ticket = session.ticket, input.file instanceof ReadableStream && (reader = input.file.getReader());
1272
+ let lastProgress = Date.now(), next = async () => {
1273
+ try {
1274
+ if (Date.now() - lastProgress >= TIMEOUT_MS) throw unavailable();
1275
+ let value = await request(() => reader.read(), Math.max(1, TIMEOUT_MS - (Date.now() - lastProgress)));
1276
+ return value.value?.byteLength && (lastProgress = Date.now()), value;
1277
+ } catch (error) {
1278
+ throw await reader.cancel(error).catch(() => {
1279
+ }), error;
1280
+ }
1281
+ };
1282
+ for (let sent = 0, partNumber = 1; sent < metadata.size || metadata.size === 0 && partNumber === 1; partNumber++) {
1283
+ let length = Math.min(PART_BYTES, metadata.size - sent), part;
1284
+ if (input.file instanceof Uint8Array) part = input.file.subarray(sent, sent + length).slice();
1285
+ else if (input.file instanceof Blob) {
1286
+ let slice = input.file.slice(sent, sent + length);
1287
+ part = new Uint8Array(await request(() => slice.arrayBuffer()));
1288
+ } else {
1289
+ part = new Uint8Array(length);
1290
+ let filled = 0;
1291
+ for (; filled < length; ) {
1292
+ if (!chunk || offset === chunk.byteLength) {
1293
+ chunk = void 0, offset = 0;
1294
+ let value2 = await next();
1295
+ if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid2();
1296
+ }
1297
+ let take = Math.min(length - filled, chunk.byteLength - offset);
1298
+ part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
1299
+ }
1300
+ }
1301
+ if (part.byteLength !== length) throw invalid2();
1302
+ if (reader && sent + length === metadata.size)
1303
+ for (lastProgress = Date.now(); ; ) {
1304
+ let value2 = await next();
1305
+ if (value2.done) break;
1306
+ if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid2();
1307
+ }
1308
+ let value = await request((signal) => exchange(`${route}/part`, {
1309
+ method: "POST",
1310
+ headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket, "x-oddington-part": String(partNumber) },
1311
+ body: part.buffer
1312
+ }, "control", signal));
1313
+ part = void 0;
1314
+ let receipt = value;
1315
+ 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();
1316
+ receipts.push({ partNumber, etag: receipt.etag }), sent += length, lastProgress = Date.now();
1317
+ }
1318
+ if (upload) for (let name of Object.keys(upload.file.enrichments)) {
1319
+ let bytes = await request(() => upload.enrichment(name));
1320
+ if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid2();
1321
+ await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
1322
+ method: "POST",
1323
+ headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
1324
+ body: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)
1325
+ }, "control", signal));
1326
+ }
1327
+ await upload?.beforeComplete();
1328
+ let completion = control({ parts: receipts });
1329
+ if (new TextEncoder().encode(completion.body).byteLength > 128 * 1024) throw unavailable();
1330
+ return parseFileDescriptor(await request(
1331
+ (signal) => exchange(`${route}/complete`, completion, !0, signal),
1332
+ TIMEOUT_MS + Math.ceil(metadata.size / (1024 * 1024)) * 1e3
1333
+ ));
1334
+ } catch (error) {
1335
+ throw reader && await reader.cancel(error).catch(() => {
1336
+ }), ticket && await request((signal) => exchange(`${route}/abort`, control({}), "control", signal)).catch(() => {
1337
+ }), error instanceof FileOperationError ? error : unavailable();
1338
+ } finally {
1339
+ chunk = void 0, reader?.releaseLock(), pending.size ? Promise.allSettled([...pending]).then(release) : release();
1340
+ }
1341
+ }
1342
+ async function operation(work, initial, parent) {
1343
+ let context2 = initial ?? await resolveFilesContext();
1344
+ if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
1345
+ if (active++, context2.localFiles)
1346
+ try {
1347
+ return await work(context2, parent ?? new AbortController().signal);
1348
+ } finally {
1349
+ active--;
1350
+ }
1351
+ return timed(async (signal) => {
1352
+ try {
1353
+ return await work(context2, signal);
1354
+ } finally {
1355
+ active--;
1356
+ }
1357
+ }, parent).catch((error) => {
1358
+ throw error instanceof FileOperationError ? error : unavailable();
1359
+ });
1360
+ }
1361
+ function contentPath(id2, name) {
1362
+ let path = idPath(id2);
1363
+ if (name === void 0) return `${path}/content`;
1364
+ if (!safeEnrichmentName(name)) throw invalid2();
1365
+ return `${path}/enrichments/${encodeURIComponent(name)}/content`;
1366
+ }
1367
+ async function read(id2, names) {
1368
+ let context2 = await resolveFilesContext();
1369
+ if (idPath(id2), names === void 0 || typeof names == "string") {
1370
+ let path = contentPath(id2, names);
1371
+ return operation((context3, signal) => send(path, { method: "GET" }, !1, context3, signal), context2);
1372
+ }
1373
+ if (!Array.isArray(names)) throw invalid2();
1374
+ let distinct = [], seen = /* @__PURE__ */ new Set();
1375
+ for (let name of names) {
1376
+ if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid2();
1377
+ if (!seen.has(name)) {
1378
+ if (seen.size === 16) throw invalid2();
1379
+ seen.add(name), distinct.push(name);
1380
+ }
1381
+ }
1382
+ let paths = distinct.map((name) => contentPath(id2, name));
1383
+ return timed(async (parent) => {
1384
+ let responses = {};
1385
+ try {
1386
+ for (let i = 0; i < paths.length; i++) {
1387
+ let response = await operation((context3, signal) => send(paths[i], { method: "GET" }, !1, context3, signal), context2, parent);
1388
+ Object.defineProperty(responses, distinct[i], { value: response, enumerable: !0 });
1389
+ }
1390
+ if (!context2.isCurrent()) throw new FileOperationError("unsupported_context", 400);
1391
+ return responses;
1392
+ } catch (error) {
1393
+ for (let response of Object.values(responses)) response.body?.cancel().catch(() => {
1394
+ });
1395
+ throw error;
1396
+ }
1397
+ });
1398
+ }
1399
+ var files = {
1400
+ async upload(id2) {
1401
+ return operation(async (context2, signal) => {
1402
+ let file = await send(`${idPath(id2)}/upload`, { method: "POST" }, !0, context2, signal);
1403
+ if (file.id !== id2) throw unavailable();
1404
+ return file;
1405
+ });
1406
+ },
1407
+ async create(input, options) {
1408
+ let context2 = await resolveFilesContext();
1409
+ 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")))
1410
+ throw invalid2();
1411
+ let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
1412
+ if (!blob && !array2 && !stream) throw invalid2();
1413
+ let intrinsic = blob ? input.file.size : array2 ? input.file.byteLength : void 0;
1414
+ if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid2();
1415
+ let name = input.name;
1416
+ 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));
1417
+ let metadata = parseFileCreateMetadata({
1418
+ name,
1419
+ size: intrinsic ?? input.size,
1420
+ path: input.path,
1421
+ storage: input.storage,
1422
+ csv: input.csv,
1423
+ requestKey: options?.requestKey === void 0 ? crypto.randomUUID() : options.requestKey
1424
+ }, FILE_ORIGINAL_MAX_BYTES);
1425
+ if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
1426
+ let header = encodeURIComponent(JSON.stringify(metadata));
1427
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1428
+ if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
1429
+ if (active++, context2.localFiles && metadata.size > MAX_BYTES) {
1430
+ let controller = new AbortController(), file = input.file, offset = 0, body2 = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
1431
+ pull(controller2) {
1432
+ if (offset === file.byteLength) {
1433
+ controller2.close();
1434
+ return;
1435
+ }
1436
+ let end = Math.min(offset + 256 * 1024, file.byteLength);
1437
+ controller2.enqueue(file.subarray(offset, end)), offset = end;
1438
+ }
1439
+ }, { highWaterMark: 0 });
1440
+ try {
1441
+ return await send("", { method: "POST", headers: {
1442
+ "content-type": "application/octet-stream",
1443
+ "x-oddington-file": header
1444
+ }, body: body2 }, !0, context2, controller.signal);
1445
+ } finally {
1446
+ active--;
1447
+ }
1448
+ }
1449
+ 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) => {
1450
+ try {
1451
+ let bytes = array2 ? input.file.slice() : await boundedBytes(
1452
+ blob ? input.file.stream() : input.file,
1453
+ metadata.size
1454
+ );
1455
+ if (bytes.byteLength !== metadata.size) throw invalid2();
1456
+ if (signal.aborted) throw unavailable();
1457
+ return await send("", {
1458
+ method: "POST",
1459
+ headers: { "content-type": "application/octet-stream", "x-oddington-file": header },
1460
+ body: bytes.buffer
1461
+ }, !0, context2, signal);
1462
+ } finally {
1463
+ active--;
1464
+ }
1465
+ }).catch((error) => {
1466
+ throw error instanceof FileOperationError ? error : unavailable();
1467
+ });
1468
+ },
1469
+ async enrich(input) {
1470
+ let context2 = await resolveFilesContext();
1471
+ 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 invalid2();
1472
+ let path = `${idPath(input.file)}/enrichments`;
1473
+ if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
1474
+ throw new FileOperationError("request_too_large", 413);
1475
+ return operation(async (context3, signal) => {
1476
+ let bytes;
1477
+ if (typeof input.content == "string") {
1478
+ let buffer = new Uint8Array(Math.min(MAX_BYTES, input.content.length * 3)), encoded = new TextEncoder().encodeInto(input.content, buffer);
1479
+ if (encoded.read !== input.content.length) throw new FileOperationError("request_too_large", 413);
1480
+ bytes = buffer.slice(0, encoded.written);
1481
+ } else bytes = input.content.slice();
1482
+ let metadata = parseFileEnrichMetadata({
1483
+ name: input.name,
1484
+ size: bytes.byteLength,
1485
+ ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
1486
+ ...input.replace === void 0 ? {} : { replace: input.replace }
1487
+ }, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
1488
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1489
+ let result2 = await send(path, { method: "POST", headers: {
1490
+ "content-type": "application/octet-stream",
1491
+ "x-oddington-enrichment": header
1492
+ }, body: bytes.buffer }, "enrichment", context3, signal);
1493
+ if (result2.name !== metadata.name) throw unavailable();
1494
+ return result2;
1495
+ }, context2);
1496
+ },
1497
+ async get(id2) {
1498
+ return operation((context2, signal) => send(idPath(id2), { method: "GET" }, !0, context2, signal));
1499
+ },
1500
+ read
1501
+ };
1502
+
1503
+ var sessionKey = "_os.app.session", AppResponseReadError = class extends Error {
1504
+ };
1505
+ async function appResponse(response, maximum, signal) {
1506
+ if (signal.aborted || !response.body || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json") throw new Error();
1507
+ let reader = response.body.getReader(), abort = () => {
1508
+ reader.cancel().catch(() => {
1509
+ });
1510
+ };
1511
+ signal.addEventListener("abort", abort, { once: !0 });
1512
+ let bytes = 0, text3 = "", decoder = new TextDecoder("utf-8", { fatal: !0 });
1513
+ try {
1514
+ for (; ; ) {
1515
+ let chunk;
1516
+ try {
1517
+ chunk = await reader.read();
1518
+ } catch {
1519
+ throw new AppResponseReadError();
1520
+ }
1521
+ if (signal.aborted) throw new Error();
1522
+ if (chunk.done) return JSON.parse(text3 + decoder.decode());
1523
+ if (bytes += chunk.value.byteLength, bytes > maximum)
1524
+ throw abort(), new Error();
1525
+ text3 += decoder.decode(chunk.value, { stream: !0 });
1526
+ }
1527
+ } finally {
1528
+ signal.removeEventListener("abort", abort), reader.releaseLock();
1529
+ }
1530
+ }
1531
+ function isAppOrigin(value) {
1532
+ try {
1533
+ let url = new URL(value);
1534
+ return url.protocol === "http:" && url.hostname === "127.0.0.1" && !!url.port && url.port !== "0" && url.origin === value;
1535
+ } catch {
1536
+ return !1;
1537
+ }
1538
+ }
1539
+ function appContext(origin, active2) {
1540
+ let binding, credential, denied = !1, controller = new AbortController(), expiry, invalidate = () => {
1541
+ denied = !0, controller.abort(), clearTimeout(expiry), clearInterval(monitor);
1542
+ try {
1543
+ active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential) && globalThis.localStorage.removeItem(sessionKey);
1544
+ } catch {
1545
+ }
1546
+ }, current2 = () => {
1547
+ try {
1548
+ return !denied && active2() && globalThis.location.origin === origin && globalThis.localStorage.getItem(sessionKey) === credential && (!binding || binding.expiresAt > Date.now());
1549
+ } catch {
1550
+ return !1;
1551
+ }
1552
+ }, monitor = setInterval(() => {
1553
+ credential && !current2() && invalidate();
1554
+ }, 100), dispose = () => {
1555
+ controller.abort(), clearTimeout(expiry), clearInterval(monitor);
1556
+ };
1557
+ async function requestData(operation2, input) {
1558
+ try {
1559
+ if (operation2 !== "data/query" || !current2()) throw new Error();
1560
+ let url = `${origin}/_os/app/data`, response = await fetch(url, {
1561
+ method: "POST",
1562
+ credentials: "omit",
1563
+ redirect: "error",
1564
+ cache: "no-store",
1565
+ referrerPolicy: "no-referrer",
1566
+ signal: controller.signal,
1567
+ headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
1568
+ body: JSON.stringify(input)
1569
+ });
1570
+ if (!current2() || response.redirected || response.url && response.url !== url) throw new Error();
1571
+ if (response.status === 401 || response.status === 403)
1572
+ throw invalidate(), new DataOperationError("access_denied");
1573
+ let result2 = await appResponse(response, Number.MAX_SAFE_INTEGER, controller.signal);
1574
+ if (!current2()) throw new Error();
1575
+ if (!response.ok) throw dataTransportError(response.status, result2);
1576
+ return result2;
1577
+ } catch (error) {
1578
+ throw error instanceof DataOperationError ? error : !current2() || controller.signal.aborted ? new DataOperationError("context_unavailable") : new DataOperationError("unavailable", !0);
1579
+ }
1580
+ }
1581
+ async function requestRoutine(key, suffix, init2 = {}) {
1582
+ if (!current2())
1583
+ throw invalidate(), new Error("Local app context unavailable.");
1584
+ let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, response = await fetch(url, {
1585
+ ...init2,
1586
+ credentials: "omit",
1587
+ redirect: "error",
1588
+ cache: "no-store",
1589
+ referrerPolicy: "no-referrer",
1590
+ signal: controller.signal,
1591
+ headers: { ...init2.body === void 0 ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
1592
+ });
1593
+ if (!current2() || response.redirected || response.url && response.url !== url)
1594
+ throw invalidate(), new Error("Local app context unavailable.");
1595
+ return (response.status === 401 || response.status === 403) && invalidate(), response;
1596
+ }
1597
+ return { resolve: async () => {
1598
+ try {
1599
+ if (denied || !active2() || globalThis.location.origin !== origin) throw new Error();
1600
+ let stored = globalThis.localStorage.getItem(sessionKey);
1601
+ if (!stored || !/^[A-Za-z0-9_-]{43}$/.test(stored) || credential && stored !== credential) throw new Error();
1602
+ if (credential = stored, !current2()) throw new Error();
1603
+ let response = await fetch(`${origin}/_os/app/context`, {
1604
+ credentials: "omit",
1605
+ redirect: "error",
1606
+ referrerPolicy: "no-referrer",
1607
+ headers: { authorization: `Bearer ${credential}` },
1608
+ signal: controller.signal
1609
+ });
1610
+ if (!response.ok || response.redirected || response.url && response.url !== `${origin}/_os/app/context` || !response.headers.get("content-type")?.startsWith("application/json")) throw new Error();
1611
+ let value = await appResponse(response, 4096, controller.signal);
1612
+ if (!validBinding(value) || !current2() || value.expiresAt <= Date.now() || value.expiresAt > Date.now() + 6e5 || binding && (value.appId !== binding.appId || value.memberId !== binding.memberId || value.workspace !== binding.workspace || value.expiresAt > binding.expiresAt)) throw new Error();
1613
+ return binding = { ...value }, clearTimeout(expiry), expiry = setTimeout(invalidate, binding.expiresAt - Date.now()), {
1614
+ kind: "local",
1615
+ ...binding,
1616
+ isCurrent: current2,
1617
+ signal: controller.signal,
1618
+ invalidate,
1619
+ requestRoutine,
1620
+ requestData,
1621
+ requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
1622
+ };
1623
+ } catch {
1624
+ throw invalidate(), new Error("Local app context unavailable.");
1625
+ }
1626
+ }, invalidate, dispose };
1627
+ }
1628
+ function validBinding(value) {
1629
+ if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,workspace") return !1;
1630
+ let binding = value;
1631
+ return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
1632
+ }
1633
+
1634
+ var installedDispose;
1635
+ function init({ auth }) {
1636
+ installedDispose?.();
1637
+ let endpoint, workspace, localOrigin;
1638
+ try {
1639
+ let url = new URL(globalThis.location.origin);
1640
+ isAppOrigin(globalThis.location.origin) && (localOrigin = url.origin);
1641
+ let slug = url.hostname.endsWith(".oddington.ai") ? url.hostname.slice(0, -13) : "";
1642
+ 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);
1643
+ } catch {
1644
+ }
1645
+ let active2 = !0, revision = 0, person, local = localOrigin ? appContext(localOrigin, () => active2) : void 0, { data: { subscription } } = auth.onAuthStateChange((event2, session) => {
1646
+ let next = session?.user.id ?? null;
1647
+ (event2 === "SIGNED_OUT" || person !== void 0 && next !== person) && (revision++, local?.invalidate()), person = next;
1648
+ }), uninstall = installMemberContext(async () => {
1649
+ if (local) return local.resolve();
1650
+ if (!active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
1651
+ let started = revision, { data: { session }, error } = await auth.getSession();
1652
+ if (!active2 || globalThis.location.origin !== endpoint || started !== revision || error || !session || person !== void 0 && person !== session.user.id)
1653
+ throw new Error("Hosted Data context unavailable.");
1654
+ let expiresAt = (session.expires_at ?? 0) * 1e3;
1655
+ if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || !session.user.id)
1656
+ throw new Error("Hosted Data context unavailable.");
1657
+ return person = session.user.id, {
1658
+ workspace,
1659
+ endpoint,
1660
+ accessToken: session.access_token,
1661
+ expiresAt,
1662
+ isCurrent: () => active2 && globalThis.location.origin === endpoint && revision === started && person === session.user.id
1663
+ };
1664
+ }), dispose = () => {
1665
+ active2 && (active2 = !1, local?.dispose(), revision++, subscription.unsubscribe(), uninstall(), installedDispose === dispose && (installedDispose = void 0));
1666
+ };
1667
+ installedDispose = dispose;
1668
+ }
1669
+ var os = { init };
1670
+
1671
+ 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 {
1672
+ constructor(reason) {
1673
+ super(reason);
1674
+ this.reason = reason;
1675
+ this.name = "AppRoutineError";
1676
+ }
1677
+ reason;
1678
+ };
1679
+ function fail3(reason) {
1680
+ throw new AppRoutineError(reason);
1681
+ }
1682
+ function object3(value) {
1683
+ return !!value && typeof value == "object" && !Array.isArray(value);
1684
+ }
1685
+ function keys3(value, expected) {
1686
+ return Object.keys(value).sort().join(",") === expected;
1687
+ }
1688
+ function current(context2) {
1689
+ (!context2.isCurrent() || context2.signal.aborted) && fail3("authority_unavailable");
1690
+ }
1691
+ async function body(context2, response, maximum) {
1692
+ try {
1693
+ let value = await appResponse(response, maximum, context2.signal);
1694
+ return current(context2), response.ok || (object3(value) && keys3(value, "error") && isReason(value.error) && (value.error === "authority_unavailable" && context2.invalidate(), fail3(value.error)), fail3("authority_unavailable")), value;
1695
+ } catch (error) {
1696
+ if (current(context2), error instanceof AppResponseReadError || error instanceof AppRoutineError) throw error;
1697
+ return fail3("execution_unavailable");
1698
+ }
1699
+ }
1700
+ function descriptor(value, key) {
1701
+ return (!object3(value) || !keys3(value, "after,key,routine,runId") || value.key !== key || typeof value.runId != "string" || !uuid.test(value.runId) || value.after !== 0 || typeof value.routine != "string" || !identifier.test(value.routine)) && fail3("execution_unavailable"), value;
1702
+ }
1703
+ function safeValue(value, depth = 0) {
1704
+ return depth > 32 ? !1 : value === null || typeof value == "string" || typeof value == "boolean" ? !0 : typeof value == "number" ? Number.isFinite(value) : Array.isArray(value) ? value.every((item) => safeValue(item, depth + 1)) : object3(value) && Object.entries(value).every(([key, item]) => !["__proto__", "constructor", "prototype"].includes(key) && safeValue(item, depth + 1));
1705
+ }
1706
+ function event(value, run, steps) {
1707
+ (!object3(value) || value.runId !== run.runId || value.key !== run.key || value.routine !== run.routine || !Number.isSafeInteger(value.sequence) || value.sequence < 1 || new TextEncoder().encode(JSON.stringify(value)).byteLength > 16 * 1024) && fail3("execution_unavailable");
1708
+ let common = "key,routine,runId,sequence,";
1709
+ if (value.type === "started" && keys3(value, common + "type") && value.sequence === 1) return { type: "started" };
1710
+ if (value.type === "step" && typeof value.id == "string" && identifier.test(value.id) && (!steps || steps.includes(value.id)) && keys3(value, "id," + common + (Object.hasOwn(value, "summary") ? "summary,type" : "type")) && (!Object.hasOwn(value, "summary") || safeValue(value.summary)))
1711
+ return {
1712
+ type: "step",
1713
+ id: value.id,
1714
+ ...Object.hasOwn(value, "summary") ? { summary: value.summary } : {}
1715
+ };
1716
+ if (value.type === "finished") {
1717
+ if (value.state === "succeeded" && keys3(value, "key," + (Object.hasOwn(value, "result") ? "result," : "") + "routine,runId,sequence,state,type") && (!Object.hasOwn(value, "result") || safeValue(value.result)))
1718
+ return {
1719
+ type: "finished",
1720
+ state: "succeeded",
1721
+ ...Object.hasOwn(value, "result") ? { result: value.result } : {}
1722
+ };
1723
+ if (value.state === "failed" && value.error === "execution_failed" && keys3(value, "error," + common + "state,type"))
1724
+ return { type: "finished", state: "failed", error: "execution_failed" };
1725
+ if (value.state === "cancelled" && keys3(value, common + "state,type")) return { type: "finished", state: "cancelled" };
1726
+ }
1727
+ return fail3("execution_unavailable");
1728
+ }
1729
+ async function pause(context2, delay) {
1730
+ current(context2), await new Promise((resolve, reject) => {
1731
+ let abort = () => {
1732
+ clearTimeout(timer), reject(new AppRoutineError("authority_unavailable"));
1733
+ }, timer = setTimeout(() => {
1734
+ context2.signal.removeEventListener("abort", abort), resolve();
1735
+ }, delay);
1736
+ context2.signal.addEventListener("abort", abort, { once: !0 });
1737
+ }), current(context2);
1738
+ }
1739
+ function handle(context2, run, steps) {
1740
+ return Object.freeze({
1741
+ async *events(...args) {
1742
+ args.length && fail3("invalid_input");
1743
+ let cursor = 0, terminal = !1, delay = 100, retainedBytes = 0, seen = [];
1744
+ for (; !terminal; ) {
1745
+ current(context2);
1746
+ let response;
1747
+ try {
1748
+ response = await context2.requestRoutine(run.key, `runs/${run.runId}/events?after=${cursor}`);
1749
+ } catch {
1750
+ current(context2), await pause(context2, delay), delay = Math.min(delay * 2, 2e3);
1751
+ continue;
1752
+ }
1753
+ if (response.status >= 500) {
1754
+ await response.body?.cancel(), await pause(context2, delay), delay = Math.min(delay * 2, 2e3);
1755
+ continue;
1756
+ }
1757
+ let value;
1758
+ try {
1759
+ value = await body(context2, response, 80 * 1024);
1760
+ } catch (error) {
1761
+ if (!(error instanceof AppResponseReadError)) throw error;
1762
+ await pause(context2, delay), delay = Math.min(delay * 2, 2e3);
1763
+ continue;
1764
+ }
1765
+ (!object3(value) || !keys3(value, "after,events,terminal") || !Array.isArray(value.events) || value.events.length > 256 || typeof value.terminal != "boolean" || !Number.isSafeInteger(value.after)) && fail3("execution_unavailable");
1766
+ let delivery = [];
1767
+ for (let raw of value.events) {
1768
+ let publicEvent = event(raw, run, steps), encoded = JSON.stringify(raw), sequence = raw.sequence;
1769
+ if (sequence <= cursor) {
1770
+ seen[sequence - 1] !== encoded && fail3("execution_unavailable");
1771
+ continue;
1772
+ }
1773
+ (terminal || sequence !== cursor + 1 || sequence > 256 || (cursor === 0 ? publicEvent.type !== "started" : publicEvent.type === "started")) && fail3("execution_unavailable"), retainedBytes += new TextEncoder().encode(encoded).byteLength, retainedBytes > 1024 * 1024 && fail3("execution_unavailable"), seen.push(encoded), cursor = sequence, terminal = publicEvent.type === "finished", delivery.push(publicEvent);
1774
+ }
1775
+ (value.after !== cursor || value.terminal !== terminal) && fail3("execution_unavailable");
1776
+ for (let item of delivery)
1777
+ current(context2), yield item;
1778
+ delay = 100, terminal || await pause(context2, delivery.length ? 100 : 250);
1779
+ }
1780
+ },
1781
+ async cancel(...args) {
1782
+ args.length && fail3("invalid_input");
1783
+ try {
1784
+ let value = await body(context2, await context2.requestRoutine(run.key, `runs/${run.runId}/cancel`, { method: "POST" }), 1024);
1785
+ (!object3(value) || !keys3(value, "cancelled") || value.cancelled !== !0) && fail3("execution_unavailable");
1786
+ } catch (error) {
1787
+ if (current(context2), error instanceof AppRoutineError) throw error;
1788
+ fail3("execution_unavailable");
1789
+ }
1790
+ }
1791
+ });
1792
+ }
1793
+ async function appRoutineOperation(key, input, recover) {
1794
+ let context2;
1795
+ try {
1796
+ identifier.test(key) || fail3("authority_unavailable");
1797
+ let resolved = await resolveAppContext();
1798
+ if (resolved.kind !== "local" && fail3("execution_unavailable"), context2 = resolved, recover) {
1799
+ let value2 = await body(context2, await context2.requestRoutine(key, "runs"), 16384);
1800
+ (!object3(value2) || !keys3(value2, "runs") || !Array.isArray(value2.runs) || value2.runs.length > 20) && fail3("execution_unavailable");
1801
+ let runs = value2.runs.map((run) => descriptor(run, key));
1802
+ return (new Set(runs.map((run) => run.runId)).size !== runs.length || new Set(runs.map((run) => run.routine)).size > 1) && fail3("execution_unavailable"), runs.map((run) => handle(resolved, run));
1803
+ }
1804
+ let available = await body(context2, await context2.requestAvailability(key), 16 * 1024);
1805
+ object3(available) && keys3(available, "available,reason") && available.available === !1 && isReason(available.reason) && fail3(available.reason), (!object3(available) || !keys3(available, "available,steps") || available.available !== !0 || !Array.isArray(available.steps) || available.steps.length < 1 || available.steps.length > 64 || !available.steps.every((step) => typeof step == "string" && identifier.test(step)) || new Set(available.steps).size !== available.steps.length) && fail3("execution_unavailable");
1806
+ let steps = [...available.steps], encoded;
1807
+ try {
1808
+ encoded = JSON.stringify(input), (encoded === void 0 || new TextEncoder().encode(encoded).byteLength > 16 * 1024) && fail3("invalid_input");
1809
+ } catch {
1810
+ fail3("invalid_input");
1811
+ }
1812
+ let value = await body(context2, await context2.requestRoutine(key, "runs", {
1813
+ method: "POST",
1814
+ body: `{"input":${encoded}}`
1815
+ }), 16 * 1024);
1816
+ return handle(context2, descriptor(value, key), steps);
1817
+ } catch (error) {
1818
+ if (context2 && current(context2), error instanceof AppRoutineError) throw error;
1819
+ return fail3(context2 ? "execution_unavailable" : "authority_unavailable");
1820
+ }
1821
+ }
1822
+
1823
+ var authorityUnavailable = () => ({ available: !1, reason: "authority_unavailable" }), MAX_RESPONSE_BYTES = 16 * 1024;
1824
+ async function availability(key) {
1825
+ try {
1826
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/.test(key)) return authorityUnavailable();
1827
+ let context2 = await resolveAppContext();
1828
+ if (context2.kind !== "local") return { available: !1, reason: "execution_unavailable" };
1829
+ let response = await context2.requestAvailability(key);
1830
+ if (!response.ok || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json")
1831
+ return authorityUnavailable();
1832
+ let value = await appResponse(response, MAX_RESPONSE_BYTES, context2.signal);
1833
+ if (!context2.isCurrent() || !value || typeof value != "object" || Array.isArray(value)) return authorityUnavailable();
1834
+ let result2 = value;
1835
+ return result2.available === !1 && Object.keys(result2).sort().join(",") === "available,reason" && (result2.reason === "authority_unavailable" || result2.reason === "execution_unavailable") ? { available: !1, reason: result2.reason } : result2.available !== !0 || Object.keys(result2).sort().join(",") !== "available,steps" || !Array.isArray(result2.steps) || result2.steps.length < 1 || result2.steps.length > 64 || !result2.steps.every((step) => typeof step == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/.test(step)) || new Set(result2.steps).size !== result2.steps.length ? authorityUnavailable() : { available: !0, steps: [...result2.steps] };
1836
+ } catch {
1837
+ return authorityUnavailable();
1838
+ }
1839
+ }
1840
+ var app = Object.freeze({ routines: new Proxy(/* @__PURE__ */ Object.create(null), {
1841
+ get(_target, key) {
1842
+ if (typeof key == "string")
1843
+ return Object.freeze({
1844
+ availability: (...args) => args.length ? Promise.resolve(authorityUnavailable()) : availability(key),
1845
+ run: (...args) => args.length === 1 ? appRoutineOperation(key, args[0], !1) : Promise.reject(new AppRoutineError("invalid_input")),
1846
+ recover: (...args) => args.length === 0 ? appRoutineOperation(key, void 0, !0) : Promise.reject(new AppRoutineError("invalid_input"))
1847
+ });
1848
+ },
1849
+ set: () => !1,
1850
+ defineProperty: () => !1,
1851
+ deleteProperty: () => !1
1852
+ }) });
1853
+
1854
+ var LIMIT = 16 * 1024, context;
1855
+ var appRun = {
1856
+ current() {
1857
+ return context;
1858
+ }
1859
+ };
1860
+
1861
+ function validateFileAddOptions(value) {
1862
+ let v = serializable(value);
1863
+ if (!v || typeof v != "object" || Array.isArray(v) || Object.keys(v).some((k) => !["path", "name", "csv"].includes(k)) || typeof v.path != "string" || !v.path || v.path !== v.path.trim() || /[\\\x00-\x1f]/.test(v.path) || v.path.split("/").some((part) => !part || part === "." || part === "..") || /^[A-Za-z]:/.test(v.path))
1864
+ throw new InboxContractError("File path must be relative to the configured working root");
1865
+ if (v.name !== void 0 && (typeof v.name != "string" || !v.name || v.name !== v.name.trim() || v.name.length > 255 || /[\x00-\x1f\x7f/\\]/.test(v.name))) throw new InboxContractError("File name must be a safe filename");
1866
+ return v.csv !== void 0 && validateCsvHints(v.csv), v;
1867
+ }
1868
+ function validateCsvHints(value) {
1869
+ let v = serializable(value);
1870
+ if (!v || typeof v != "object" || Array.isArray(v) || Object.keys(v).some((k) => !["encoding", "delimiter", "headerRow"].includes(k)) || v.encoding !== void 0 && !["utf-8", "windows-1252"].includes(v.encoding) || v.delimiter !== void 0 && ![",", ";", " "].includes(v.delimiter) || v.headerRow !== void 0 && (!Number.isSafeInteger(v.headerRow) || v.headerRow < 0))
1871
+ throw new InboxContractError("Invalid CSV parsing hints");
1872
+ return v;
1873
+ }
1874
+
1875
+ function routinePublicationClient(options = {}) {
1876
+ let environment = {}, local = void 0, workspace = options.workspace ?? environment.ODDINGTON_ROUTINE_WORKSPACE, capability = options.capability ?? environment.ODDINGTON_ROUTINE_RUN;
1877
+ if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== void 0 && !/^[0-9a-f]{64}$/.test(capability))
1878
+ throw new Error("A workspace and valid optional routine capability are required");
1879
+ let workingRoot = options.workingRoot ?? environment.ODDINGTON_INBOX_WORKING_ROOT ?? void 0, headers = capability ? { "x-oddington-run": capability } : {}, request = options.request ?? (capability ? async (path, init2) => {
1880
+ let origin = oddingtonOrigin(), target = new URL(path, `${origin}/`);
1881
+ if (target.origin !== origin || !target.pathname.startsWith(`/api/cli/workspaces/${workspace}/inbox/`))
1882
+ throw new Error("Invalid routine endpoint");
1883
+ return fetch(target, { ...init2, redirect: "error" });
1884
+ } : async () => {
1885
+ throw new Error("Member publication requires a host-supplied request");
1886
+ });
1887
+ async function send2(operation2, body2) {
1888
+ if (local) return await local(operation2, body2);
1889
+ let response = await request(`/api/cli/workspaces/${workspace}/inbox/${operation2}`, {
1890
+ method: "POST",
1891
+ headers: { "content-type": "application/json", ...headers },
1892
+ body: JSON.stringify(body2)
1893
+ }), result2 = await response.json();
1894
+ if (!response.ok && operation2 === "records/update")
1895
+ throw new RoutineRecordWriteError(result2.error ?? "Could not confirm routine preparation", result2.effect ?? "uncertain");
1896
+ if (!response.ok) throw new Error(result2.error ?? "Could not confirm routine publication");
1897
+ return result2;
1898
+ }
1899
+ let transport = {
1900
+ async publish(body2) {
1901
+ return await send2("publish", body2);
1902
+ },
1903
+ async resolve(body2) {
1904
+ await send2("resolve", body2);
1905
+ }
1906
+ };
1907
+ async function readRecords(input) {
1908
+ return await send2("records/read", validateRecordRead(input));
1909
+ }
1910
+ return {
1911
+ ...createInboxPublisher(transport),
1912
+ readRecords,
1913
+ repository: {
1914
+ readRecords,
1915
+ async writeRecords(input, options2) {
1916
+ if (!capability) throw new Error("A trusted routine launch is required");
1917
+ return await send2("records/update", validateRoutineRecordWrite({ input, requestKey: options2?.requestKey }));
1918
+ },
1919
+ async updateRecords(input, options2) {
1920
+ if (!capability) throw new Error("A trusted routine launch is required");
1921
+ return await send2("records/update", validateRoutineRecordWrite({ input: validateRecordUpdate(input), requestKey: options2?.requestKey }));
1922
+ },
1923
+ async deleteRecords(input, options2) {
1924
+ if (!capability) throw new Error("A trusted routine launch is required");
1925
+ return await send2("records/update", validateRoutineRecordWrite({ input: validateRecordDelete(input), requestKey: options2?.requestKey }));
1926
+ }
1927
+ },
1928
+ files: {
1929
+ async add(value) {
1930
+ let input = validateFileAddOptions(value);
1931
+ if (local) return validateInboxFile(await local("files/add", input));
1932
+ throw workingRoot ? new Error("Inbox file reads require a Node working root") : new Error("Inbox file reads require a working root");
1933
+ }
1934
+ },
1935
+ async check(body2) {
1936
+ await send2("check", { requestKey: body2.requestKey, item: validatePublication(body2.item) });
1937
+ }
1938
+ };
1939
+ }
1940
+ export {
1941
+ AirtableOperationError,
1942
+ AppRoutineError,
1943
+ DataOperationError,
1944
+ FileOperationError,
1945
+ InboxContractError,
1946
+ RoutineRecordWriteError,
1947
+ app,
1948
+ appRun,
1949
+ asksBlock,
1950
+ connections,
1951
+ continueAsk,
1952
+ data,
1953
+ defineHandler,
1954
+ fileBlock,
1955
+ files,
1956
+ os,
1957
+ question,
1958
+ recordBlock,
1959
+ recordsBlock,
1960
+ resolveAsk,
1961
+ respond,
1962
+ richTextBlock,
1963
+ routinePublicationClient,
1964
+ selection
1965
+ };