@oddington/sdk 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -717,31 +717,65 @@ var DataOperationError = class extends Error {
717
717
 
718
718
  var invalid = () => {
719
719
  throw new DataOperationError("invalid_query");
720
- }, scalarText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
720
+ }, historyText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
721
+ function historyVersion(value) {
722
+ return historyText(value) && value.length > 0 && new TextEncoder().encode(value).length <= 1024 && !/[\u0000-\u001f\u007f-\u009f]/.test(value) && !/^[0-9a-f]{40}$/i.test(value);
723
+ }
724
+ function historyProperties(value) {
725
+ if (!value || typeof value != "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return invalid();
726
+ let descriptors = Object.getOwnPropertyDescriptors(value);
727
+ return Reflect.ownKeys(value).some((key) => typeof key != "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")) ? invalid() : descriptors;
728
+ }
729
+ function captureHistoryListOptions(value) {
730
+ if (value === void 0) return {};
731
+ let descriptors = historyProperties(value), result2 = {};
732
+ if (Object.keys(descriptors).some((key) => !["limit", "before"].includes(key))) return invalid();
733
+ if (Object.hasOwn(descriptors, "limit")) {
734
+ let limit = descriptors.limit.value;
735
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) return invalid();
736
+ result2.limit = limit;
737
+ }
738
+ if (Object.hasOwn(descriptors, "before")) {
739
+ let before = descriptors.before.value;
740
+ if (!historyVersion(before)) return invalid();
741
+ result2.before = before;
742
+ }
743
+ return result2;
744
+ }
745
+ function captureHistoryInstant(value) {
746
+ if (!historyText(value) || value.length > 1024) return invalid();
747
+ let match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(value);
748
+ if (!match) return invalid();
749
+ let [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number), days = new Date(Date.UTC(year, month, 0));
750
+ return days.setUTCFullYear(year), year < 1 || month < 1 || month > 12 || day < 1 || day > days.getUTCDate() || hour > 23 || minute > 59 || second > 59 || Number(match[9] ?? 0) > 23 || Number(match[10] ?? 0) > 59 ? invalid() : value;
751
+ }
752
+
753
+ var invalid2 = () => {
754
+ throw new DataOperationError("invalid_query");
755
+ };
721
756
  function captureRecordsSearchInput(query, options) {
722
- if (!scalarText(query)) return invalid();
757
+ if (!historyText(query)) return invalid2();
723
758
  let length = [...query.trim()].length;
724
- if (length < 1 || length > 256) return invalid();
759
+ if (length < 1 || length > 256) return invalid2();
725
760
  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();
761
+ let descriptors = historyProperties(options);
762
+ if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key))) return invalid2();
729
763
  let captured = {};
730
764
  if (Object.hasOwn(descriptors, "collections")) {
731
765
  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();
766
+ if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return invalid2();
733
767
  let collections = [];
734
768
  for (let index = 0; index < supplied.length; index++) {
735
769
  let descriptor2 = Object.getOwnPropertyDescriptor(supplied, String(index));
736
- if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !scalarText(descriptor2.value)) return invalid();
770
+ if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !historyText(descriptor2.value)) return invalid2();
737
771
  collections.push(descriptor2.value);
738
772
  }
739
- if (new Set(collections).size !== collections.length) return invalid();
773
+ if (new Set(collections).size !== collections.length) return invalid2();
740
774
  captured.collections = collections;
741
775
  }
742
776
  if (Object.hasOwn(descriptors, "limit")) {
743
777
  let supplied = descriptors.limit.value;
744
- if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid();
778
+ if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid2();
745
779
  captured.limit = supplied;
746
780
  }
747
781
  return { query, options: captured };
@@ -842,42 +876,42 @@ var FileOperationError = class extends Error {
842
876
  retryable;
843
877
  }, 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
878
  function parseFileDescriptor(value) {
845
- let invalid3 = () => {
879
+ let invalid4 = () => {
846
880
  throw new FileOperationError("unavailable", 503, !0);
847
881
  };
848
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
882
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
849
883
  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();
884
+ if (Object.keys(f).sort().join(",") !== "details,enrichments,id,mediaType,name,path,sha256,size" || typeof f.id != "string" || !FILE_ID_PATTERN.test(f.id) || !safeFileName(f.name) || !safeFilePath(f.path) || !Number.isSafeInteger(f.size) || f.size < 0 || f.size > FILE_ORIGINAL_MAX_BYTES || typeof f.sha256 != "string" || !/^[0-9a-f]{64}$/.test(f.sha256) || !f.enrichments || typeof f.enrichments != "object" || Array.isArray(f.enrichments) || !f.details || typeof f.details != "object" || Array.isArray(f.details)) return invalid4();
851
885
  let d = f.details;
852
886
  if (d.kind === "other") {
853
- if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid3();
887
+ if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid4();
854
888
  } 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();
889
+ if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0) return invalid4();
890
+ } else return invalid4();
857
891
  for (let [name, descriptor2] of Object.entries(f.enrichments)) {
858
- if (!safeEnrichmentName(name)) return invalid3();
892
+ if (!safeEnrichmentName(name)) return invalid4();
859
893
  parseEnrichmentDescriptor(descriptor2);
860
894
  }
861
- return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid3() : f;
895
+ return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid4() : f;
862
896
  }
863
897
  function parseEnrichmentDescriptor(value) {
864
- let invalid3 = () => {
898
+ let invalid4 = () => {
865
899
  throw new FileOperationError("unavailable", 503, !0);
866
900
  };
867
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
901
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
868
902
  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;
903
+ return Object.keys(d).sort().join(",") !== "mediaType,sha256,size" || !["application/octet-stream", "text/plain", "text/markdown", "application/json"].includes(d.mediaType) || !Number.isSafeInteger(d.size) || d.size < 0 || d.size > 16 * 1024 * 1024 || typeof d.sha256 != "string" || !/^[0-9a-f]{64}$/.test(d.sha256) ? invalid4() : d;
870
904
  }
871
905
  function safeEnrichmentName(value) {
872
906
  return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
873
907
  }
874
908
  function parseFileEnrichMetadata(value, maxBytes) {
875
- let invalid3 = () => {
909
+ let invalid4 = () => {
876
910
  throw new FileOperationError("invalid_input", 400);
877
911
  };
878
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
912
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
879
913
  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();
914
+ if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key)) || !safeEnrichmentName(d.name) || !Number.isSafeInteger(d.size) || d.size < 0 || d.mediaType !== void 0 && (typeof d.mediaType != "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType)) || d.replace !== void 0 && (typeof d.replace != "string" || !/^[0-9a-f]{64}$/.test(d.replace))) return invalid4();
881
915
  if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
882
916
  return d;
883
917
  }
@@ -888,23 +922,23 @@ function safeFilePath(value) {
888
922
  return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
889
923
  }
890
924
  function parseFileCreateMetadata(value, maxBytes) {
891
- let invalid3 = () => {
925
+ let invalid4 = () => {
892
926
  throw new FileOperationError("invalid_input", 400);
893
927
  };
894
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
928
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
895
929
  let input = value;
896
- if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid3();
930
+ if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid4();
897
931
  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();
932
+ if (!safeFileName(input.name) || !safeFilePath(path) || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid4();
899
933
  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();
934
+ if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid4();
901
935
  let csv;
902
936
  if (input.csv !== void 0) {
903
- if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid3();
937
+ if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid4();
904
938
  let hints = input.csv;
905
- if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid3();
939
+ if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid4();
906
940
  let encoding = hints.encoding === void 0 ? "utf-8" : hints.encoding, delimiter = hints.delimiter === void 0 ? "," : hints.delimiter, headerRow = hints.headerRow === void 0 ? 1 : hints.headerRow;
907
- if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid3();
941
+ if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid4();
908
942
  csv = { encoding, delimiter, headerRow };
909
943
  }
910
944
  return {
@@ -992,13 +1026,14 @@ var data = {
992
1026
  async function send2(input) {
993
1027
  let context2 = await resolveDataContext();
994
1028
  try {
995
- if (checkDataRequestSize({ repository, ...input }), "local" in context2) return await context2.local("data/query", { repository, ...input });
1029
+ let envelope = { repository, ...input };
1030
+ if (checkDataRequestSize(envelope), "local" in context2) return await context2.local("data/query", envelope);
996
1031
  let response = await fetch(context2.url, {
997
1032
  method: "POST",
998
1033
  redirect: "error",
999
1034
  cache: "no-store",
1000
1035
  headers: { "content-type": "application/json", ...context2.headers },
1001
- body: JSON.stringify({ repository, ...input })
1036
+ body: JSON.stringify(envelope)
1002
1037
  }), result2 = await response.json();
1003
1038
  if (!response.ok)
1004
1039
  throw dataTransportError(response.status, result2);
@@ -1014,13 +1049,11 @@ var data = {
1014
1049
  },
1015
1050
  records: {
1016
1051
  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
- }
1052
+ search: async (query, options) => send2({ operation: "records.search", ...captureRecordsSearchInput(query, options) })
1053
+ },
1054
+ history: {
1055
+ list: async (options) => send2({ operation: "history.list", options: captureHistoryListOptions(options) }),
1056
+ at: async (instant) => send2({ operation: "history.at", instant: captureHistoryInstant(instant) })
1024
1057
  },
1025
1058
  changes() {
1026
1059
  let updates = [], creates = [], deletes = [], publicationId, pending, confirmed, snapshot = () => {
@@ -1104,7 +1137,7 @@ var data = {
1104
1137
  }
1105
1138
  };
1106
1139
 
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 = {
1140
+ 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, invalid3 = () => new FileOperationError("invalid_input", 400), unavailable = () => new FileOperationError("unavailable", 503, !0), failures = {
1108
1141
  400: ["invalid_input", "unsupported_context"],
1109
1142
  401: ["unauthenticated"],
1110
1143
  403: ["access_denied"],
@@ -1121,7 +1154,7 @@ async function boundedBytes(stream, limit) {
1121
1154
  for (; ; ) {
1122
1155
  let { done, value } = await reader.read();
1123
1156
  if (done) break;
1124
- if (!(value instanceof Uint8Array)) throw invalid2();
1157
+ if (!(value instanceof Uint8Array)) throw invalid3();
1125
1158
  if (size += value.byteLength, size > limit) throw new FileOperationError("request_too_large", 413);
1126
1159
  bytes.set(value, size - value.byteLength);
1127
1160
  }
@@ -1249,7 +1282,7 @@ async function send(suffix, init2, descriptor2, initial, parent) {
1249
1282
  throw unavailable();
1250
1283
  }
1251
1284
  function idPath(id2) {
1252
- if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid2();
1285
+ if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid3();
1253
1286
  return `/${id2}`;
1254
1287
  }
1255
1288
  async function multipartCreate(input, metadata, exchange, release = () => {
@@ -1292,18 +1325,18 @@ async function multipartCreate(input, metadata, exchange, release = () => {
1292
1325
  if (!chunk || offset === chunk.byteLength) {
1293
1326
  chunk = void 0, offset = 0;
1294
1327
  let value2 = await next();
1295
- if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid2();
1328
+ if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid3();
1296
1329
  }
1297
1330
  let take = Math.min(length - filled, chunk.byteLength - offset);
1298
1331
  part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
1299
1332
  }
1300
1333
  }
1301
- if (part.byteLength !== length) throw invalid2();
1334
+ if (part.byteLength !== length) throw invalid3();
1302
1335
  if (reader && sent + length === metadata.size)
1303
1336
  for (lastProgress = Date.now(); ; ) {
1304
1337
  let value2 = await next();
1305
1338
  if (value2.done) break;
1306
- if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid2();
1339
+ if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid3();
1307
1340
  }
1308
1341
  let value = await request((signal) => exchange(`${route}/part`, {
1309
1342
  method: "POST",
@@ -1317,7 +1350,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
1317
1350
  }
1318
1351
  if (upload) for (let name of Object.keys(upload.file.enrichments)) {
1319
1352
  let bytes = await request(() => upload.enrichment(name));
1320
- if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid2();
1353
+ if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid3();
1321
1354
  await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
1322
1355
  method: "POST",
1323
1356
  headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
@@ -1361,7 +1394,7 @@ async function operation(work, initial, parent) {
1361
1394
  function contentPath(id2, name) {
1362
1395
  let path = idPath(id2);
1363
1396
  if (name === void 0) return `${path}/content`;
1364
- if (!safeEnrichmentName(name)) throw invalid2();
1397
+ if (!safeEnrichmentName(name)) throw invalid3();
1365
1398
  return `${path}/enrichments/${encodeURIComponent(name)}/content`;
1366
1399
  }
1367
1400
  async function read(id2, names) {
@@ -1370,12 +1403,12 @@ async function read(id2, names) {
1370
1403
  let path = contentPath(id2, names);
1371
1404
  return operation((context3, signal) => send(path, { method: "GET" }, !1, context3, signal), context2);
1372
1405
  }
1373
- if (!Array.isArray(names)) throw invalid2();
1406
+ if (!Array.isArray(names)) throw invalid3();
1374
1407
  let distinct = [], seen = /* @__PURE__ */ new Set();
1375
1408
  for (let name of names) {
1376
- if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid2();
1409
+ if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid3();
1377
1410
  if (!seen.has(name)) {
1378
- if (seen.size === 16) throw invalid2();
1411
+ if (seen.size === 16) throw invalid3();
1379
1412
  seen.add(name), distinct.push(name);
1380
1413
  }
1381
1414
  }
@@ -1407,11 +1440,11 @@ var files = {
1407
1440
  async create(input, options) {
1408
1441
  let context2 = await resolveFilesContext();
1409
1442
  if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "size", "path", "name", "storage", "csv"].includes(key)) || options !== void 0 && (!options || typeof options != "object" || Array.isArray(options) || Object.keys(options).some((key) => key !== "requestKey")))
1410
- throw invalid2();
1443
+ throw invalid3();
1411
1444
  let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
1412
- if (!blob && !array2 && !stream) throw invalid2();
1445
+ if (!blob && !array2 && !stream) throw invalid3();
1413
1446
  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();
1447
+ if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid3();
1415
1448
  let name = input.name;
1416
1449
  name === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
1417
1450
  let metadata = parseFileCreateMetadata({
@@ -1424,7 +1457,7 @@ var files = {
1424
1457
  }, FILE_ORIGINAL_MAX_BYTES);
1425
1458
  if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
1426
1459
  let header = encodeURIComponent(JSON.stringify(metadata));
1427
- if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1460
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
1428
1461
  if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
1429
1462
  if (active++, context2.localFiles && metadata.size > MAX_BYTES) {
1430
1463
  let controller = new AbortController(), file = input.file, offset = 0, body2 = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
@@ -1452,7 +1485,7 @@ var files = {
1452
1485
  blob ? input.file.stream() : input.file,
1453
1486
  metadata.size
1454
1487
  );
1455
- if (bytes.byteLength !== metadata.size) throw invalid2();
1488
+ if (bytes.byteLength !== metadata.size) throw invalid3();
1456
1489
  if (signal.aborted) throw unavailable();
1457
1490
  return await send("", {
1458
1491
  method: "POST",
@@ -1468,7 +1501,7 @@ var files = {
1468
1501
  },
1469
1502
  async enrich(input) {
1470
1503
  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();
1504
+ if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "name", "content", "mediaType", "replace"].includes(key)) || typeof input.content != "string" && !(input.content instanceof Uint8Array)) throw invalid3();
1472
1505
  let path = `${idPath(input.file)}/enrichments`;
1473
1506
  if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
1474
1507
  throw new FileOperationError("request_too_large", 413);
@@ -1485,7 +1518,7 @@ var files = {
1485
1518
  ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
1486
1519
  ...input.replace === void 0 ? {} : { replace: input.replace }
1487
1520
  }, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
1488
- if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1521
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
1489
1522
  let result2 = await send(path, { method: "POST", headers: {
1490
1523
  "content-type": "application/octet-stream",
1491
1524
  "x-oddington-enrichment": header
@@ -1500,7 +1533,9 @@ var files = {
1500
1533
  read
1501
1534
  };
1502
1535
 
1503
- var sessionKey = "_os.app.session", AppResponseReadError = class extends Error {
1536
+ var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap(), AppResponseReadError = class extends Error {
1537
+ }, AppContextDenied = class extends Error {
1538
+ }, AppRenewalUnavailable = class extends Error {
1504
1539
  };
1505
1540
  async function appResponse(response, maximum, signal) {
1506
1541
  if (signal.aborted || !response.body || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json") throw new Error();
@@ -1519,7 +1554,11 @@ async function appResponse(response, maximum, signal) {
1519
1554
  throw new AppResponseReadError();
1520
1555
  }
1521
1556
  if (signal.aborted) throw new Error();
1522
- if (chunk.done) return JSON.parse(text3 + decoder.decode());
1557
+ if (chunk.done) {
1558
+ let value = JSON.parse(text3 + decoder.decode());
1559
+ if (await responseChecks.get(response)?.(value), signal.aborted) throw new Error();
1560
+ return value;
1561
+ }
1523
1562
  if (bytes += chunk.value.byteLength, bytes > maximum)
1524
1563
  throw abort(), new Error();
1525
1564
  text3 += decoder.decode(chunk.value, { stream: !0 });
@@ -1537,27 +1576,180 @@ function isAppOrigin(value) {
1537
1576
  }
1538
1577
  }
1539
1578
  function appContext(origin, active2) {
1540
- let binding, credential, denied = !1, controller = new AbortController(), expiry, invalidate = () => {
1541
- denied = !0, controller.abort(), clearTimeout(expiry), clearInterval(monitor);
1579
+ let binding, credential, denied = !1, controller = new AbortController(), expiry, startup, renewalTimer, renewal, observation, resolution2, disposed = !1, stored = () => globalThis.localStorage.getItem(sessionKey), alive = () => {
1580
+ try {
1581
+ return !denied && !disposed && active2() && globalThis.location.origin === origin && (!binding || binding.expiresAt > Date.now());
1582
+ } catch {
1583
+ return !1;
1584
+ }
1585
+ }, cancel = () => {
1586
+ controller.abort(), clearTimeout(expiry), clearTimeout(startup), clearTimeout(renewalTimer), clearInterval(monitor), globalThis.removeEventListener?.("storage", storageChanged);
1587
+ }, invalidate = () => {
1588
+ denied = !0, cancel();
1542
1589
  try {
1543
- active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential) && globalThis.localStorage.removeItem(sessionKey);
1590
+ !disposed && active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential) && globalThis.localStorage.removeItem(sessionKey);
1544
1591
  } catch {
1545
1592
  }
1546
1593
  }, current2 = () => {
1547
1594
  try {
1548
- return !denied && active2() && globalThis.location.origin === origin && globalThis.localStorage.getItem(sessionKey) === credential && (!binding || binding.expiresAt > Date.now());
1595
+ return alive() && stored() === credential;
1549
1596
  } catch {
1550
1597
  return !1;
1551
1598
  }
1599
+ }, sameSession = (value) => !binding || value.appId === binding.appId && value.memberId === binding.memberId && value.workspace === binding.workspace && value.sessionId === binding.sessionId, bounded = (value) => validBinding(value) && value.expiresAt > Date.now() && value.expiresAt <= Date.now() + 6e5 && sameSession(value);
1600
+ async function proof(candidate, signal = controller.signal) {
1601
+ if (!alive() || !/^[A-Za-z0-9_-]{43}$/.test(candidate)) throw new Error();
1602
+ let url = `${origin}/_os/app/context`, response = await fetch(url, {
1603
+ credentials: "omit",
1604
+ redirect: "error",
1605
+ cache: "no-store",
1606
+ referrerPolicy: "no-referrer",
1607
+ headers: { authorization: `Bearer ${candidate}` },
1608
+ signal
1609
+ });
1610
+ if (response.redirected || response.url && response.url !== url) throw new Error();
1611
+ if (response.status === 401 || response.status === 403) throw new AppContextDenied();
1612
+ if (!response.ok) throw new Error();
1613
+ let value = await appResponse(response, 4096, signal);
1614
+ if (!alive() || !bounded(value)) throw new Error();
1615
+ return value;
1616
+ }
1617
+ function install(value, candidate) {
1618
+ binding = { ...value }, credential = candidate, clearTimeout(expiry), clearTimeout(renewalTimer);
1619
+ let remaining = binding.expiresAt - Date.now();
1620
+ expiry = setTimeout(invalidate, remaining), renewalTimer = setTimeout(() => {
1621
+ renew();
1622
+ }, remaining - Math.min(6e4, remaining / 5));
1623
+ }
1624
+ async function synchronize() {
1625
+ if (!alive()) throw new Error();
1626
+ if (credential && stored() !== credential) {
1627
+ if (!binding) throw new Error();
1628
+ if (observation) return observation;
1629
+ observation = (async () => {
1630
+ for (; alive(); ) {
1631
+ let candidate = stored();
1632
+ if (!candidate) throw new Error();
1633
+ let value;
1634
+ try {
1635
+ value = await proof(candidate);
1636
+ } catch {
1637
+ if (stored() !== candidate) continue;
1638
+ throw new Error();
1639
+ }
1640
+ if (stored() === candidate) {
1641
+ install(value, candidate);
1642
+ return;
1643
+ }
1644
+ }
1645
+ throw new Error();
1646
+ })();
1647
+ try {
1648
+ await observation;
1649
+ } catch {
1650
+ throw invalidate(), new Error("Local app context unavailable.");
1651
+ } finally {
1652
+ observation = void 0;
1653
+ }
1654
+ }
1655
+ }
1656
+ async function renew() {
1657
+ if (!(renewal || !binding || !alive())) {
1658
+ renewal = (async () => {
1659
+ let deadline = binding.expiresAt, requestController = new AbortController(), abort = () => requestController.abort();
1660
+ controller.signal.addEventListener("abort", abort, { once: !0 });
1661
+ let timeout = setTimeout(abort, Math.min(2e4, deadline - Date.now())), previous = credential;
1662
+ try {
1663
+ if (await synchronize(), credential !== previous) return;
1664
+ let url = `${origin}/_os/app/renew`, response;
1665
+ try {
1666
+ response = await fetch(url, {
1667
+ method: "POST",
1668
+ credentials: "omit",
1669
+ redirect: "error",
1670
+ cache: "no-store",
1671
+ referrerPolicy: "no-referrer",
1672
+ signal: requestController.signal,
1673
+ headers: { authorization: `Bearer ${previous}` }
1674
+ });
1675
+ } catch {
1676
+ throw new AppRenewalUnavailable();
1677
+ }
1678
+ if (response.redirected || response.url && response.url !== url) throw new Error();
1679
+ let value = await appResponse(response, 4096, requestController.signal);
1680
+ if (response.status === 503 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_unavailable")
1681
+ throw new AppRenewalUnavailable();
1682
+ if (response.status === 403 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_conflict") {
1683
+ for (; alive() && stored() === previous && !requestController.signal.aborted; )
1684
+ await new Promise((resolve2, reject) => {
1685
+ let abortWait = () => {
1686
+ clearTimeout(timer), reject(new Error());
1687
+ }, timer = setTimeout(() => {
1688
+ requestController.signal.removeEventListener("abort", abortWait), resolve2();
1689
+ }, 100);
1690
+ requestController.signal.addEventListener("abort", abortWait, { once: !0 });
1691
+ });
1692
+ if (requestController.signal.aborted) throw new Error();
1693
+ await synchronize();
1694
+ return;
1695
+ }
1696
+ if (!response.ok) throw new Error();
1697
+ if (!alive() || Date.now() >= deadline || !value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential") throw new Error();
1698
+ let result2 = value;
1699
+ if (!bounded(result2.context) || typeof result2.credential != "string" || !/^[A-Za-z0-9_-]{43}$/.test(result2.credential) || result2.credential === previous) throw new Error();
1700
+ if (stored() !== previous) {
1701
+ await synchronize();
1702
+ return;
1703
+ }
1704
+ globalThis.localStorage.setItem(sessionKey, result2.credential), install(result2.context, result2.credential);
1705
+ } catch (error) {
1706
+ try {
1707
+ if (alive() && stored() && stored() !== previous) {
1708
+ await synchronize();
1709
+ return;
1710
+ }
1711
+ } catch {
1712
+ }
1713
+ try {
1714
+ if (alive() && stored() && stored() !== previous) {
1715
+ await synchronize();
1716
+ return;
1717
+ }
1718
+ } catch {
1719
+ }
1720
+ (error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && current2() && Date.now() < deadline ? renewalTimer = setTimeout(() => {
1721
+ renew();
1722
+ }, Math.min(5e3, (deadline - Date.now()) / 5)) : invalidate();
1723
+ } finally {
1724
+ clearTimeout(timeout), controller.signal.removeEventListener("abort", abort);
1725
+ }
1726
+ })();
1727
+ try {
1728
+ await renewal;
1729
+ } finally {
1730
+ renewal = void 0;
1731
+ }
1732
+ }
1733
+ }
1734
+ let storageChanged = () => {
1735
+ if (credential) {
1736
+ if (!alive()) {
1737
+ invalidate();
1738
+ return;
1739
+ }
1740
+ synchronize().catch(invalidate);
1741
+ }
1552
1742
  }, monitor = setInterval(() => {
1553
- credential && !current2() && invalidate();
1554
- }, 100), dispose = () => {
1555
- controller.abort(), clearTimeout(expiry), clearInterval(monitor);
1743
+ storageChanged();
1744
+ }, 100);
1745
+ globalThis.addEventListener?.("storage", storageChanged);
1746
+ let dispose = () => {
1747
+ disposed = !0, cancel();
1556
1748
  };
1557
1749
  async function requestData(operation2, input) {
1558
1750
  try {
1559
1751
  if (operation2 !== "data/query" || !current2()) throw new Error();
1560
- let url = `${origin}/_os/app/data`, response = await fetch(url, {
1752
+ let url = `${origin}/_os/app/data`, admitted = credential, response = await fetch(url, {
1561
1753
  method: "POST",
1562
1754
  credentials: "omit",
1563
1755
  redirect: "error",
@@ -1567,10 +1759,12 @@ function appContext(origin, active2) {
1567
1759
  headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
1568
1760
  body: JSON.stringify(input)
1569
1761
  });
1762
+ for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
1570
1763
  if (!current2() || response.redirected || response.url && response.url !== url) throw new Error();
1571
1764
  if (response.status === 401 || response.status === 403)
1572
- throw invalidate(), new DataOperationError("access_denied");
1765
+ throw admitted === credential && invalidate(), new DataOperationError("access_denied");
1573
1766
  let result2 = await appResponse(response, Number.MAX_SAFE_INTEGER, controller.signal);
1767
+ for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
1574
1768
  if (!current2()) throw new Error();
1575
1769
  if (!response.ok) throw dataTransportError(response.status, result2);
1576
1770
  return result2;
@@ -1581,7 +1775,7 @@ function appContext(origin, active2) {
1581
1775
  async function requestRoutine(key, suffix, init2 = {}) {
1582
1776
  if (!current2())
1583
1777
  throw invalidate(), new Error("Local app context unavailable.");
1584
- let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, response = await fetch(url, {
1778
+ let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, admitted = credential, response = await fetch(url, {
1585
1779
  ...init2,
1586
1780
  credentials: "omit",
1587
1781
  redirect: "error",
@@ -1590,45 +1784,69 @@ function appContext(origin, active2) {
1590
1784
  signal: controller.signal,
1591
1785
  headers: { ...init2.body === void 0 ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
1592
1786
  });
1787
+ for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
1593
1788
  if (!current2() || response.redirected || response.url && response.url !== url)
1594
1789
  throw invalidate(), new Error("Local app context unavailable.");
1595
- return (response.status === 401 || response.status === 403) && invalidate(), response;
1790
+ return (response.status === 401 || response.status === 403) && admitted === credential && invalidate(), responseChecks.set(response, async (value) => {
1791
+ for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
1792
+ admitted === credential && value && typeof value == "object" && "error" in value && value.error === "authority_unavailable" && invalidate();
1793
+ }), response;
1596
1794
  }
1597
- return { resolve: async () => {
1795
+ function context2() {
1796
+ let issued = credential;
1797
+ return {
1798
+ kind: "local",
1799
+ ...binding,
1800
+ get expiresAt() {
1801
+ return binding.expiresAt;
1802
+ },
1803
+ isCurrent: current2,
1804
+ signal: controller.signal,
1805
+ invalidate: () => {
1806
+ issued === credential && invalidate();
1807
+ },
1808
+ requestRoutine,
1809
+ requestData,
1810
+ requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
1811
+ };
1812
+ }
1813
+ let resolveOnce = async () => {
1814
+ let attempted;
1598
1815
  try {
1599
1816
  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.");
1817
+ credential && await synchronize();
1818
+ let candidate = stored();
1819
+ if (!candidate || !/^[A-Za-z0-9_-]{43}$/.test(candidate)) throw new Error();
1820
+ if (credential = candidate, attempted = candidate, !current2()) throw new Error();
1821
+ let value = await proof(candidate);
1822
+ if (stored() !== candidate || credential !== candidate)
1823
+ return binding || (binding = { ...value }), await synchronize(), context2();
1824
+ if (!current2()) throw new Error();
1825
+ if (binding && value.expiresAt > binding.expiresAt) throw new Error();
1826
+ return (!binding || value.expiresAt !== binding.expiresAt) && install(value, candidate), context2();
1827
+ } catch (error) {
1828
+ try {
1829
+ if (binding && current2() && attempted && attempted !== credential) return context2();
1830
+ if (binding && alive() && stored() && stored() !== credential)
1831
+ return await synchronize(), context2();
1832
+ } catch {
1833
+ }
1834
+ throw error instanceof AppContextDenied && binding && alive() && renewal ? new Error("Local app context unavailable.") : (invalidate(), new Error("Local app context unavailable."));
1625
1835
  }
1836
+ }, resolve = () => resolution2 || (resolution2 = resolveOnce().finally(() => {
1837
+ resolution2 = void 0;
1838
+ }), resolution2);
1839
+ return { resolve, start: () => {
1840
+ startup = setTimeout(() => {
1841
+ alive() && !resolution2 && !binding && resolve().catch(() => {
1842
+ });
1843
+ }, 0);
1626
1844
  }, invalidate, dispose };
1627
1845
  }
1628
1846
  function validBinding(value) {
1629
- if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,workspace") return !1;
1847
+ if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,sessionId,workspace") return !1;
1630
1848
  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);
1849
+ 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.sessionId == "string" && /^[A-Za-z0-9_-]{43}$/.test(binding.sessionId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
1632
1850
  }
1633
1851
 
1634
1852
  var installedDispose;
@@ -1664,7 +1882,7 @@ function init({ auth }) {
1664
1882
  }), dispose = () => {
1665
1883
  active2 && (active2 = !1, local?.dispose(), revision++, subscription.unsubscribe(), uninstall(), installedDispose === dispose && (installedDispose = void 0));
1666
1884
  };
1667
- installedDispose = dispose;
1885
+ installedDispose = dispose, local?.start();
1668
1886
  }
1669
1887
  var os = { init };
1670
1888