@oddington/sdk 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -299,9 +299,37 @@ Each graph selection sends one request and returns only selected canonical
299
299
  values, expanded forward links and exact opaque public versions. Cursors
300
300
  continue the exact retained artifact and collection selection, even after
301
301
  replacement; repeat its options and links while omitting unrelated collections.
302
- There is no automatic retry or separate version fetch. History is not included.
302
+ There is no automatic retry or separate version fetch.
303
303
  Collection schemas omit Markdown storage hints.
304
304
 
305
+ Read published History through the same inert repository handle:
306
+
307
+ ```ts
308
+ const versions = await general.history.list({ limit: 50 });
309
+ const older = versions.length
310
+ ? await general.history.list({ before: versions.at(-1)!.version }) : [];
311
+ const close = await general.history.at('2026-10-09T16:30:00Z');
312
+ ```
313
+
314
+ List returns an entry array in descending published sequence, with an exclusive
315
+ visible `before` and a default limit of 50 (integer 1–100). Cutoff accepts a real
316
+ RFC 3339 instant with an explicit offset and returns the greatest visible sequence
317
+ published at or before it, or null. Entries contain exactly `version`, `publishedAt`,
318
+ `author`, `authoredAt`, `committer`, `committedAt`, `message`, `created`, `updated`,
319
+ and `deleted`. Git identities and complete messages are repository facts; changed
320
+ refs are lexical. Versions are opaque published identities, never Git expressions.
321
+ Each operation captures input before resolving authority and makes one request.
322
+ Incomplete selected metadata fails the whole result retryably with `unavailable`;
323
+ current Records remain independent. Hosted routines retain their captured ceiling
324
+ until their own authoritative publication advances it. History is served through
325
+ the member/run Data transport. Local apps and local routines use the same calls;
326
+ their OS parent may acquire one bounded page or cutoff result, retain it against
327
+ the exact repository target and published ceiling, and serve an identical warm
328
+ request without another hosted call. Authority loss, expiry, target replacement
329
+ or published-version replacement invalidates that retained result. Prepared local
330
+ changes do not appear in published History.
331
+ Historical Record selection and `history.changes` are not exposed by this slice.
332
+
305
333
  Use bounded plain-text lookup to find full Records and guards:
306
334
 
307
335
  ```ts
package/dist/browser.js CHANGED
@@ -717,31 +717,84 @@ 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
+ var historyCollection = (value) => typeof value == "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value), historyCursor = (value) => typeof value == "string" && value.length <= 128 * 1024 && /^history-cursor-v1\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]+$/.test(value);
730
+ function historyArray(value) {
731
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) return invalid();
732
+ let descriptors = Object.getOwnPropertyDescriptors(value);
733
+ if (Reflect.ownKeys(value).length !== value.length + 1) return invalid();
734
+ let result2 = [];
735
+ for (let i = 0; i < value.length; i++) {
736
+ let descriptor2 = descriptors[i];
737
+ if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value")) return invalid();
738
+ result2.push(descriptor2.value);
739
+ }
740
+ return result2;
741
+ }
742
+ function captureOptions(value, changes) {
743
+ if (value === void 0) return {};
744
+ let descriptors = historyProperties(value), result2 = {}, allowed = changes ? ["collections", "refs", "from", "through", "limit", "after"] : ["limit", "before"];
745
+ if (Object.keys(descriptors).some((key) => !allowed.includes(key))) return invalid();
746
+ for (let key of allowed) {
747
+ if (!Object.hasOwn(descriptors, key)) continue;
748
+ let scalar = descriptors[key].value;
749
+ if (key === "collections" || key === "refs") {
750
+ let values = historyArray(scalar);
751
+ if (!values.length || values.length > (key === "collections" ? 16 : 100) || !values.every(key === "collections" ? historyCollection : validRecordRef) || new Set(values).size !== values.length) return invalid();
752
+ result2[key] = values;
753
+ } else if (key === "limit") {
754
+ if (!Number.isInteger(scalar) || scalar < 1 || scalar > (changes ? 500 : 100)) return invalid();
755
+ result2.limit = scalar;
756
+ } else {
757
+ if (!(key === "after" ? historyCursor(scalar) : historyVersion(scalar))) return invalid();
758
+ result2[key] = scalar;
759
+ }
760
+ }
761
+ return result2;
762
+ }
763
+ var captureHistoryListOptions = (value) => captureOptions(value, !1), captureHistoryChangesOptions = (value) => captureOptions(value, !0);
764
+ function captureHistoryInstant(value) {
765
+ if (!historyText(value) || value.length > 1024) return invalid();
766
+ let match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(value);
767
+ if (!match) return invalid();
768
+ let [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number), days = new Date(Date.UTC(year, month, 0));
769
+ return days.setUTCFullYear(year), year < 1 || month < 1 || month > 12 || day < 1 || day > days.getUTCDate() || hour > 23 || minute > 59 || second > 59 || Number(match[9] ?? 0) > 23 || Number(match[10] ?? 0) > 59 ? invalid() : value;
770
+ }
771
+
772
+ var invalid2 = () => {
773
+ throw new DataOperationError("invalid_query");
774
+ };
721
775
  function captureRecordsSearchInput(query, options) {
722
- if (!scalarText(query)) return invalid();
776
+ if (!historyText(query)) return invalid2();
723
777
  let length = [...query.trim()].length;
724
- if (length < 1 || length > 256) return invalid();
778
+ if (length < 1 || length > 256) return invalid2();
725
779
  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();
780
+ let descriptors = historyProperties(options);
781
+ if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key))) return invalid2();
729
782
  let captured = {};
730
783
  if (Object.hasOwn(descriptors, "collections")) {
731
784
  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();
785
+ if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1) return invalid2();
733
786
  let collections = [];
734
787
  for (let index = 0; index < supplied.length; index++) {
735
788
  let descriptor2 = Object.getOwnPropertyDescriptor(supplied, String(index));
736
- if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !scalarText(descriptor2.value)) return invalid();
789
+ if (!descriptor2?.enumerable || !Object.hasOwn(descriptor2, "value") || !historyText(descriptor2.value)) return invalid2();
737
790
  collections.push(descriptor2.value);
738
791
  }
739
- if (new Set(collections).size !== collections.length) return invalid();
792
+ if (new Set(collections).size !== collections.length) return invalid2();
740
793
  captured.collections = collections;
741
794
  }
742
795
  if (Object.hasOwn(descriptors, "limit")) {
743
796
  let supplied = descriptors.limit.value;
744
- if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid();
797
+ if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100) return invalid2();
745
798
  captured.limit = supplied;
746
799
  }
747
800
  return { query, options: captured };
@@ -751,7 +804,11 @@ var DATA_REQUEST_LIMIT = 256 * 1024, invalidChange = () => {
751
804
  throw new DataOperationError("invalid_change", !1, "unapplied");
752
805
  };
753
806
  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;
807
+ try {
808
+ return historyProperties(value), value;
809
+ } catch {
810
+ return invalidChange();
811
+ }
755
812
  }
756
813
  function captureValue(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
757
814
  if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return value;
@@ -771,13 +828,18 @@ function captureDataUpdate(record2, set) {
771
828
  let identity = captureDataDelete(record2), fields3 = mapping(set);
772
829
  return !Object.keys(fields3).length || Object.keys(fields3).some((key) => key.startsWith("$") || ["id", "type"].includes(key)) ? invalidChange() : { ...identity, set: captureValue(fields3) };
773
830
  }
774
- function captureDataUpdates(value, allowEmpty = !1) {
831
+ function captureIdentities(value, update, allowEmpty = !0) {
775
832
  if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4) return invalidChange();
776
- let updates = value.map((item) => {
833
+ let captured = value.map((item) => {
777
834
  let row = mapping(item);
778
- return Object.keys(row).sort().join(",") !== "expectedVersion,ref,set" ? invalidChange() : captureDataUpdate({ $ref: row.ref, $version: row.expectedVersion }, row.set);
835
+ if (Object.keys(row).sort().join(",") !== (update ? "expectedVersion,ref,set" : "expectedVersion,ref")) return invalidChange();
836
+ let identity = { $ref: row.ref, $version: row.expectedVersion };
837
+ return update ? captureDataUpdate(identity, row.set) : captureDataDelete(identity);
779
838
  });
780
- return new Set(updates.map((update) => update.ref)).size !== updates.length ? invalidChange() : updates;
839
+ return new Set(captured.map((item) => item.ref)).size !== captured.length ? invalidChange() : captured;
840
+ }
841
+ function captureDataUpdates(value, allowEmpty = !1) {
842
+ return captureIdentities(value, !0, allowEmpty);
781
843
  }
782
844
  function captureDataCreate(value) {
783
845
  let row = mapping(value);
@@ -800,12 +862,7 @@ function captureDataChanges(updatesValue, createsValue = [], deletesValue = [])
800
862
  if (Object.keys(row).some((key) => !["ref", "fields", "filing"].includes(key)) || !Object.hasOwn(row, "ref") || !Object.hasOwn(row, "fields")) return invalidChange();
801
863
  let fields3 = mapping(row.fields);
802
864
  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);
865
+ }), deletes = captureIdentities(deletesValue, !1), refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
809
866
  return !refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length ? invalidChange() : { updates, creates, deletes };
810
867
  }
811
868
  function checkDataRequestSize(input) {
@@ -842,42 +899,42 @@ var FileOperationError = class extends Error {
842
899
  retryable;
843
900
  }, FILE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, FILE_ORIGINAL_MAX_BYTES = 5 * 1024 ** 3;
844
901
  function parseFileDescriptor(value) {
845
- let invalid3 = () => {
902
+ let invalid4 = () => {
846
903
  throw new FileOperationError("unavailable", 503, !0);
847
904
  };
848
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
905
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
849
906
  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();
907
+ if (Object.keys(f).sort().join(",") !== "details,enrichments,id,mediaType,name,path,sha256,size" || typeof f.id != "string" || !FILE_ID_PATTERN.test(f.id) || !safeFileName(f.name) || !safeFilePath(f.path) || !Number.isSafeInteger(f.size) || f.size < 0 || f.size > FILE_ORIGINAL_MAX_BYTES || typeof f.sha256 != "string" || !/^[0-9a-f]{64}$/.test(f.sha256) || !f.enrichments || typeof f.enrichments != "object" || Array.isArray(f.enrichments) || !f.details || typeof f.details != "object" || Array.isArray(f.details)) return invalid4();
851
908
  let d = f.details;
852
909
  if (d.kind === "other") {
853
- if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid3();
910
+ if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid4();
854
911
  } 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();
912
+ if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0) return invalid4();
913
+ } else return invalid4();
857
914
  for (let [name, descriptor2] of Object.entries(f.enrichments)) {
858
- if (!safeEnrichmentName(name)) return invalid3();
915
+ if (!safeEnrichmentName(name)) return invalid4();
859
916
  parseEnrichmentDescriptor(descriptor2);
860
917
  }
861
- return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid3() : f;
918
+ return new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024 ? invalid4() : f;
862
919
  }
863
920
  function parseEnrichmentDescriptor(value) {
864
- let invalid3 = () => {
921
+ let invalid4 = () => {
865
922
  throw new FileOperationError("unavailable", 503, !0);
866
923
  };
867
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
924
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
868
925
  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;
926
+ return Object.keys(d).sort().join(",") !== "mediaType,sha256,size" || !["application/octet-stream", "text/plain", "text/markdown", "application/json"].includes(d.mediaType) || !Number.isSafeInteger(d.size) || d.size < 0 || d.size > 16 * 1024 * 1024 || typeof d.sha256 != "string" || !/^[0-9a-f]{64}$/.test(d.sha256) ? invalid4() : d;
870
927
  }
871
928
  function safeEnrichmentName(value) {
872
929
  return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
873
930
  }
874
931
  function parseFileEnrichMetadata(value, maxBytes) {
875
- let invalid3 = () => {
932
+ let invalid4 = () => {
876
933
  throw new FileOperationError("invalid_input", 400);
877
934
  };
878
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
935
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
879
936
  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();
937
+ if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key)) || !safeEnrichmentName(d.name) || !Number.isSafeInteger(d.size) || d.size < 0 || d.mediaType !== void 0 && (typeof d.mediaType != "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType)) || d.replace !== void 0 && (typeof d.replace != "string" || !/^[0-9a-f]{64}$/.test(d.replace))) return invalid4();
881
938
  if (d.size > maxBytes) throw new FileOperationError("request_too_large", 413);
882
939
  return d;
883
940
  }
@@ -888,23 +945,23 @@ function safeFilePath(value) {
888
945
  return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
889
946
  }
890
947
  function parseFileCreateMetadata(value, maxBytes) {
891
- let invalid3 = () => {
948
+ let invalid4 = () => {
892
949
  throw new FileOperationError("invalid_input", 400);
893
950
  };
894
- if (!value || typeof value != "object" || Array.isArray(value)) return invalid3();
951
+ if (!value || typeof value != "object" || Array.isArray(value)) return invalid4();
895
952
  let input = value;
896
- if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid3();
953
+ if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key))) return invalid4();
897
954
  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();
955
+ if (!safeFileName(input.name) || !safeFilePath(path) || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid4();
899
956
  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();
957
+ if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid4();
901
958
  let csv;
902
959
  if (input.csv !== void 0) {
903
- if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid3();
960
+ if (!input.csv || typeof input.csv != "object" || Array.isArray(input.csv)) return invalid4();
904
961
  let hints = input.csv;
905
- if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid3();
962
+ if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key))) return invalid4();
906
963
  let encoding = hints.encoding === void 0 ? "utf-8" : hints.encoding, delimiter = hints.delimiter === void 0 ? "," : hints.delimiter, headerRow = hints.headerRow === void 0 ? 1 : hints.headerRow;
907
- if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid3();
964
+ if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", " "].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5) return invalid4();
908
965
  csv = { encoding, delimiter, headerRow };
909
966
  }
910
967
  return {
@@ -992,13 +1049,14 @@ var data = {
992
1049
  async function send2(input) {
993
1050
  let context2 = await resolveDataContext();
994
1051
  try {
995
- if (checkDataRequestSize({ repository, ...input }), "local" in context2) return await context2.local("data/query", { repository, ...input });
1052
+ let envelope = { repository, ...input };
1053
+ if (checkDataRequestSize(envelope), "local" in context2) return await context2.local("data/query", envelope);
996
1054
  let response = await fetch(context2.url, {
997
1055
  method: "POST",
998
1056
  redirect: "error",
999
1057
  cache: "no-store",
1000
1058
  headers: { "content-type": "application/json", ...context2.headers },
1001
- body: JSON.stringify({ repository, ...input })
1059
+ body: JSON.stringify(envelope)
1002
1060
  }), result2 = await response.json();
1003
1061
  if (!response.ok)
1004
1062
  throw dataTransportError(response.status, result2);
@@ -1014,21 +1072,18 @@ var data = {
1014
1072
  },
1015
1073
  records: {
1016
1074
  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
- }
1075
+ search: async (query, options) => send2({ operation: "records.search", ...captureRecordsSearchInput(query, options) })
1076
+ },
1077
+ history: {
1078
+ list: async (options) => send2({ operation: "history.list", options: captureHistoryListOptions(options) }),
1079
+ at: async (instant) => send2({ operation: "history.at", instant: captureHistoryInstant(instant) }),
1080
+ changes: async (options) => send2({ operation: "history.changes", options: captureHistoryChangesOptions(options) })
1024
1081
  },
1025
1082
  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
- };
1083
+ let updates = [], creates = [], deletes = [], publicationId, pending, confirmed, assertMutable = () => {
1084
+ if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1085
+ }, snapshot = (nextUpdates = updates, nextCreates = creates, nextDeletes = deletes) => {
1086
+ let { updates: updates2, creates: creates2, deletes: deletes2 } = captureDataChanges(nextUpdates, nextCreates, nextDeletes), input = { updates: updates2, ...creates2.length ? { creates: creates2 } : {}, ...deletes2.length ? { deletes: deletes2 } : {} };
1032
1087
  return checkDataRequestSize({
1033
1088
  repository,
1034
1089
  operation: "changes.publish",
@@ -1038,40 +1093,19 @@ var data = {
1038
1093
  };
1039
1094
  return {
1040
1095
  create(record2) {
1041
- if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1096
+ assertMutable();
1042
1097
  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;
1098
+ snapshot(updates, next, deletes), creates = next;
1051
1099
  },
1052
1100
  update(record2, set) {
1053
- if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1101
+ assertMutable();
1054
1102
  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;
1103
+ snapshot(next, creates, deletes), updates = next;
1063
1104
  },
1064
1105
  delete(record2) {
1065
- if (publicationId) throw new DataOperationError("invalid_change", !1, "unapplied");
1106
+ assertMutable();
1066
1107
  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;
1108
+ snapshot(updates, creates, next), deletes = next;
1075
1109
  },
1076
1110
  async validate() {
1077
1111
  await send2({ operation: "changes.validate", ...snapshot() });
@@ -1104,7 +1138,7 @@ var data = {
1104
1138
  }
1105
1139
  };
1106
1140
 
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 = {
1141
+ var MAX_BYTES = 16 * 1024 * 1024, PART_BYTES = 8 * 1024 * 1024, METADATA_BYTES = 4096, RESULT_BYTES = 16 * 1024, CONTROL_RESULT_BYTES = 128 * 1024, TIMEOUT_MS = 3e4, MAX_ACTIVE = 4, ATTEMPTS = 2, active = 0, invalid3 = () => new FileOperationError("invalid_input", 400), unavailable = () => new FileOperationError("unavailable", 503, !0), failures = {
1108
1142
  400: ["invalid_input", "unsupported_context"],
1109
1143
  401: ["unauthenticated"],
1110
1144
  403: ["access_denied"],
@@ -1121,7 +1155,7 @@ async function boundedBytes(stream, limit) {
1121
1155
  for (; ; ) {
1122
1156
  let { done, value } = await reader.read();
1123
1157
  if (done) break;
1124
- if (!(value instanceof Uint8Array)) throw invalid2();
1158
+ if (!(value instanceof Uint8Array)) throw invalid3();
1125
1159
  if (size += value.byteLength, size > limit) throw new FileOperationError("request_too_large", 413);
1126
1160
  bytes.set(value, size - value.byteLength);
1127
1161
  }
@@ -1249,7 +1283,7 @@ async function send(suffix, init2, descriptor2, initial, parent) {
1249
1283
  throw unavailable();
1250
1284
  }
1251
1285
  function idPath(id2) {
1252
- if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid2();
1286
+ if (typeof id2 != "string" || !FILE_ID_PATTERN.test(id2)) throw invalid3();
1253
1287
  return `/${id2}`;
1254
1288
  }
1255
1289
  async function multipartCreate(input, metadata, exchange, release = () => {
@@ -1292,18 +1326,18 @@ async function multipartCreate(input, metadata, exchange, release = () => {
1292
1326
  if (!chunk || offset === chunk.byteLength) {
1293
1327
  chunk = void 0, offset = 0;
1294
1328
  let value2 = await next();
1295
- if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid2();
1329
+ if (value2.done || !(value2.value instanceof Uint8Array) || (chunk = value2.value, chunk.byteLength > metadata.size - sent - filled)) throw invalid3();
1296
1330
  }
1297
1331
  let take = Math.min(length - filled, chunk.byteLength - offset);
1298
1332
  part.set(chunk.subarray(offset, offset + take), filled), filled += take, offset += take, offset === chunk.byteLength && (chunk = void 0, offset = 0);
1299
1333
  }
1300
1334
  }
1301
- if (part.byteLength !== length) throw invalid2();
1335
+ if (part.byteLength !== length) throw invalid3();
1302
1336
  if (reader && sent + length === metadata.size)
1303
1337
  for (lastProgress = Date.now(); ; ) {
1304
1338
  let value2 = await next();
1305
1339
  if (value2.done) break;
1306
- if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid2();
1340
+ if (!(value2.value instanceof Uint8Array) || value2.value.byteLength) throw invalid3();
1307
1341
  }
1308
1342
  let value = await request((signal) => exchange(`${route}/part`, {
1309
1343
  method: "POST",
@@ -1317,7 +1351,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
1317
1351
  }
1318
1352
  if (upload) for (let name of Object.keys(upload.file.enrichments)) {
1319
1353
  let bytes = await request(() => upload.enrichment(name));
1320
- if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid2();
1354
+ if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES) throw invalid3();
1321
1355
  await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
1322
1356
  method: "POST",
1323
1357
  headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
@@ -1361,7 +1395,7 @@ async function operation(work, initial, parent) {
1361
1395
  function contentPath(id2, name) {
1362
1396
  let path = idPath(id2);
1363
1397
  if (name === void 0) return `${path}/content`;
1364
- if (!safeEnrichmentName(name)) throw invalid2();
1398
+ if (!safeEnrichmentName(name)) throw invalid3();
1365
1399
  return `${path}/enrichments/${encodeURIComponent(name)}/content`;
1366
1400
  }
1367
1401
  async function read(id2, names) {
@@ -1370,12 +1404,12 @@ async function read(id2, names) {
1370
1404
  let path = contentPath(id2, names);
1371
1405
  return operation((context3, signal) => send(path, { method: "GET" }, !1, context3, signal), context2);
1372
1406
  }
1373
- if (!Array.isArray(names)) throw invalid2();
1407
+ if (!Array.isArray(names)) throw invalid3();
1374
1408
  let distinct = [], seen = /* @__PURE__ */ new Set();
1375
1409
  for (let name of names) {
1376
- if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid2();
1410
+ if (typeof name != "string" || !safeEnrichmentName(name)) throw invalid3();
1377
1411
  if (!seen.has(name)) {
1378
- if (seen.size === 16) throw invalid2();
1412
+ if (seen.size === 16) throw invalid3();
1379
1413
  seen.add(name), distinct.push(name);
1380
1414
  }
1381
1415
  }
@@ -1407,11 +1441,11 @@ var files = {
1407
1441
  async create(input, options) {
1408
1442
  let context2 = await resolveFilesContext();
1409
1443
  if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "size", "path", "name", "storage", "csv"].includes(key)) || options !== void 0 && (!options || typeof options != "object" || Array.isArray(options) || Object.keys(options).some((key) => key !== "requestKey")))
1410
- throw invalid2();
1444
+ throw invalid3();
1411
1445
  let blob = input.file instanceof Blob, array2 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
1412
- if (!blob && !array2 && !stream) throw invalid2();
1446
+ if (!blob && !array2 && !stream) throw invalid3();
1413
1447
  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();
1448
+ if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid3();
1415
1449
  let name = input.name;
1416
1450
  name === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
1417
1451
  let metadata = parseFileCreateMetadata({
@@ -1424,7 +1458,7 @@ var files = {
1424
1458
  }, FILE_ORIGINAL_MAX_BYTES);
1425
1459
  if (!context2.local && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
1426
1460
  let header = encodeURIComponent(JSON.stringify(metadata));
1427
- if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1461
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
1428
1462
  if (active >= MAX_ACTIVE) throw new FileOperationError("capacity", 429, !0);
1429
1463
  if (active++, context2.localFiles && metadata.size > MAX_BYTES) {
1430
1464
  let controller = new AbortController(), file = input.file, offset = 0, body2 = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
@@ -1452,7 +1486,7 @@ var files = {
1452
1486
  blob ? input.file.stream() : input.file,
1453
1487
  metadata.size
1454
1488
  );
1455
- if (bytes.byteLength !== metadata.size) throw invalid2();
1489
+ if (bytes.byteLength !== metadata.size) throw invalid3();
1456
1490
  if (signal.aborted) throw unavailable();
1457
1491
  return await send("", {
1458
1492
  method: "POST",
@@ -1468,7 +1502,7 @@ var files = {
1468
1502
  },
1469
1503
  async enrich(input) {
1470
1504
  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();
1505
+ if (!input || typeof input != "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "name", "content", "mediaType", "replace"].includes(key)) || typeof input.content != "string" && !(input.content instanceof Uint8Array)) throw invalid3();
1472
1506
  let path = `${idPath(input.file)}/enrichments`;
1473
1507
  if (typeof input.content == "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
1474
1508
  throw new FileOperationError("request_too_large", 413);
@@ -1485,7 +1519,7 @@ var files = {
1485
1519
  ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
1486
1520
  ...input.replace === void 0 ? {} : { replace: input.replace }
1487
1521
  }, MAX_BYTES), header = encodeURIComponent(JSON.stringify(metadata));
1488
- if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid2();
1522
+ if (new TextEncoder().encode(header).byteLength > METADATA_BYTES) throw invalid3();
1489
1523
  let result2 = await send(path, { method: "POST", headers: {
1490
1524
  "content-type": "application/octet-stream",
1491
1525
  "x-oddington-enrichment": header
package/dist/index.js CHANGED
@@ -21709,44 +21709,118 @@ class DataOperationError extends Error {
21709
21709
  }
21710
21710
  }
21711
21711
 
21712
- // ../../shared/data-search-input.ts
21712
+ // ../../shared/data-history-input.ts
21713
21713
  var invalid = () => {
21714
21714
  throw new DataOperationError("invalid_query");
21715
21715
  };
21716
- var scalarText = (value) => typeof value === "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
21717
- function captureRecordsSearchInput(query, options) {
21718
- if (!scalarText(query))
21716
+ var historyText = (value) => typeof value === "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
21717
+ function historyVersion(value) {
21718
+ 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);
21719
+ }
21720
+ function historyProperties(value) {
21721
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
21722
+ return invalid();
21723
+ const descriptors = Object.getOwnPropertyDescriptors(value);
21724
+ if (Reflect.ownKeys(value).some((key) => typeof key !== "string" || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")))
21725
+ return invalid();
21726
+ return descriptors;
21727
+ }
21728
+ var historyCollection = (value) => typeof value === "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value);
21729
+ var historyCursor = (value) => typeof value === "string" && value.length <= 128 * 1024 && /^history-cursor-v1\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]+$/.test(value);
21730
+ function historyArray(value) {
21731
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype)
21732
+ return invalid();
21733
+ const descriptors = Object.getOwnPropertyDescriptors(value);
21734
+ if (Reflect.ownKeys(value).length !== value.length + 1)
21735
+ return invalid();
21736
+ const result = [];
21737
+ for (let i = 0;i < value.length; i++) {
21738
+ const descriptor = descriptors[i];
21739
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, "value"))
21740
+ return invalid();
21741
+ result.push(descriptor.value);
21742
+ }
21743
+ return result;
21744
+ }
21745
+ function captureOptions(value, changes) {
21746
+ if (value === undefined)
21747
+ return {};
21748
+ const descriptors = historyProperties(value), result = {};
21749
+ const allowed = changes ? ["collections", "refs", "from", "through", "limit", "after"] : ["limit", "before"];
21750
+ if (Object.keys(descriptors).some((key) => !allowed.includes(key)))
21751
+ return invalid();
21752
+ for (const key of allowed) {
21753
+ if (!Object.hasOwn(descriptors, key))
21754
+ continue;
21755
+ const scalar = descriptors[key].value;
21756
+ if (key === "collections" || key === "refs") {
21757
+ const values = historyArray(scalar);
21758
+ if (!values.length || values.length > (key === "collections" ? 16 : 100) || !values.every(key === "collections" ? historyCollection : validRecordRef) || new Set(values).size !== values.length)
21759
+ return invalid();
21760
+ result[key] = values;
21761
+ } else if (key === "limit") {
21762
+ if (!Number.isInteger(scalar) || scalar < 1 || scalar > (changes ? 500 : 100))
21763
+ return invalid();
21764
+ result.limit = scalar;
21765
+ } else {
21766
+ if (!(key === "after" ? historyCursor(scalar) : historyVersion(scalar)))
21767
+ return invalid();
21768
+ result[key] = scalar;
21769
+ }
21770
+ }
21771
+ return result;
21772
+ }
21773
+ var captureHistoryListOptions = (value) => captureOptions(value, false);
21774
+ var captureHistoryChangesOptions = (value) => captureOptions(value, true);
21775
+ function captureHistoryInstant(value) {
21776
+ if (!historyText(value) || value.length > 1024)
21777
+ return invalid();
21778
+ const match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(value);
21779
+ if (!match)
21719
21780
  return invalid();
21781
+ const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
21782
+ const days = new Date(Date.UTC(year, month, 0));
21783
+ days.setUTCFullYear(year);
21784
+ if (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)
21785
+ return invalid();
21786
+ return value;
21787
+ }
21788
+
21789
+ // ../../shared/data-search-input.ts
21790
+ var invalid2 = () => {
21791
+ throw new DataOperationError("invalid_query");
21792
+ };
21793
+ function captureRecordsSearchInput(query, options) {
21794
+ if (!historyText(query))
21795
+ return invalid2();
21720
21796
  const length = [...query.trim()].length;
21721
21797
  if (length < 1 || length > 256)
21722
- return invalid();
21798
+ return invalid2();
21723
21799
  if (options === undefined)
21724
21800
  return { query };
21725
- if (!options || typeof options !== "object" || Array.isArray(options) || ![Object.prototype, null].includes(Object.getPrototypeOf(options)))
21726
- return invalid();
21727
- const descriptors = Object.getOwnPropertyDescriptors(options);
21728
- if (Reflect.ownKeys(options).some((key) => typeof key !== "string" || !["collections", "limit"].includes(key) || !descriptors[key]?.enumerable || !Object.hasOwn(descriptors[key], "value")))
21729
- return invalid();
21801
+ const descriptors = historyProperties(options);
21802
+ if (Object.keys(descriptors).some((key) => !["collections", "limit"].includes(key)))
21803
+ return invalid2();
21730
21804
  const captured = {};
21731
21805
  if (Object.hasOwn(descriptors, "collections")) {
21732
21806
  const supplied = descriptors.collections.value;
21733
21807
  if (!Array.isArray(supplied) || supplied.length < 1 || supplied.length > 16 || Reflect.ownKeys(supplied).length !== supplied.length + 1)
21734
- return invalid();
21808
+ return invalid2();
21735
21809
  const collections = [];
21736
21810
  for (let index = 0;index < supplied.length; index++) {
21737
21811
  const descriptor = Object.getOwnPropertyDescriptor(supplied, String(index));
21738
- if (!descriptor?.enumerable || !Object.hasOwn(descriptor, "value") || !scalarText(descriptor.value))
21739
- return invalid();
21812
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, "value") || !historyText(descriptor.value))
21813
+ return invalid2();
21740
21814
  collections.push(descriptor.value);
21741
21815
  }
21742
21816
  if (new Set(collections).size !== collections.length)
21743
- return invalid();
21817
+ return invalid2();
21744
21818
  captured.collections = collections;
21745
21819
  }
21746
21820
  if (Object.hasOwn(descriptors, "limit")) {
21747
21821
  const supplied = descriptors.limit.value;
21748
21822
  if (!Number.isInteger(supplied) || supplied < 1 || supplied > 100)
21749
- return invalid();
21823
+ return invalid2();
21750
21824
  captured.limit = supplied;
21751
21825
  }
21752
21826
  return { query, options: captured };
@@ -21758,11 +21832,12 @@ var invalidChange = () => {
21758
21832
  throw new DataOperationError("invalid_change", false, "unapplied");
21759
21833
  };
21760
21834
  function mapping(value) {
21761
- if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
21762
- return invalidChange();
21763
- if (Reflect.ownKeys(value).some((key) => typeof key !== "string" || !Object.getOwnPropertyDescriptor(value, key)?.enumerable || !Object.hasOwn(Object.getOwnPropertyDescriptor(value, key), "value")))
21835
+ try {
21836
+ historyProperties(value);
21837
+ return value;
21838
+ } catch {
21764
21839
  return invalidChange();
21765
- return value;
21840
+ }
21766
21841
  }
21767
21842
  function captureValue(value, ancestors = new Set, depth = 0) {
21768
21843
  if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value))
@@ -21797,18 +21872,22 @@ function captureDataUpdate(record2, set) {
21797
21872
  return invalidChange();
21798
21873
  return { ...identity, set: captureValue(fields3) };
21799
21874
  }
21800
- function captureDataUpdates(value, allowEmpty = false) {
21875
+ function captureIdentities(value, update, allowEmpty = true) {
21801
21876
  if (!Array.isArray(value) || !allowEmpty && !value.length || value.length > 1e4)
21802
21877
  return invalidChange();
21803
- const updates = value.map((item) => {
21878
+ const captured = value.map((item) => {
21804
21879
  const row = mapping(item);
21805
- if (Object.keys(row).sort().join(",") !== "expectedVersion,ref,set")
21880
+ if (Object.keys(row).sort().join(",") !== (update ? "expectedVersion,ref,set" : "expectedVersion,ref"))
21806
21881
  return invalidChange();
21807
- return captureDataUpdate({ $ref: row.ref, $version: row.expectedVersion }, row.set);
21882
+ const identity = { $ref: row.ref, $version: row.expectedVersion };
21883
+ return update ? captureDataUpdate(identity, row.set) : captureDataDelete(identity);
21808
21884
  });
21809
- if (new Set(updates.map((update) => update.ref)).size !== updates.length)
21885
+ if (new Set(captured.map((item) => item.ref)).size !== captured.length)
21810
21886
  return invalidChange();
21811
- return updates;
21887
+ return captured;
21888
+ }
21889
+ function captureDataUpdates(value, allowEmpty = false) {
21890
+ return captureIdentities(value, true, allowEmpty);
21812
21891
  }
21813
21892
  function captureDataCreate(value) {
21814
21893
  const row = mapping(value);
@@ -21837,14 +21916,7 @@ function captureDataChanges(updatesValue, createsValue = [], deletesValue = [])
21837
21916
  return invalidChange();
21838
21917
  return captureDataCreate({ ...fields3, $ref: row.ref, ...Object.hasOwn(row, "filing") ? { $filing: row.filing } : {} });
21839
21918
  });
21840
- if (!Array.isArray(deletesValue) || deletesValue.length > 1e4)
21841
- return invalidChange();
21842
- const deletes = deletesValue.map((item) => {
21843
- const row = mapping(item);
21844
- if (Object.keys(row).sort().join(",") !== "expectedVersion,ref")
21845
- return invalidChange();
21846
- return captureDataDelete({ $ref: row.ref, $version: row.expectedVersion });
21847
- });
21919
+ const deletes = captureIdentities(deletesValue, false);
21848
21920
  const refs = [...updates, ...creates, ...deletes].map((item) => item.ref);
21849
21921
  if (!refs.length || refs.length > 1e4 || new Set(refs).size !== refs.length)
21850
21922
  return invalidChange();
@@ -21886,55 +21958,55 @@ class FileOperationError extends Error {
21886
21958
  var 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}$/;
21887
21959
  var FILE_ORIGINAL_MAX_BYTES = 5 * 1024 ** 3;
21888
21960
  function parseFileDescriptor(value) {
21889
- const invalid2 = () => {
21961
+ const invalid3 = () => {
21890
21962
  throw new FileOperationError("unavailable", 503, true);
21891
21963
  };
21892
21964
  if (!value || typeof value !== "object" || Array.isArray(value))
21893
- return invalid2();
21965
+ return invalid3();
21894
21966
  const f = value;
21895
21967
  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))
21896
- return invalid2();
21968
+ return invalid3();
21897
21969
  const d = f.details;
21898
21970
  if (d.kind === "other") {
21899
21971
  if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream")
21900
- return invalid2();
21972
+ return invalid3();
21901
21973
  } else if (d.kind === "csv") {
21902
21974
  if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", "\t"].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0)
21903
- return invalid2();
21975
+ return invalid3();
21904
21976
  } else
21905
- return invalid2();
21977
+ return invalid3();
21906
21978
  for (const [name, descriptor] of Object.entries(f.enrichments)) {
21907
21979
  if (!safeEnrichmentName(name))
21908
- return invalid2();
21980
+ return invalid3();
21909
21981
  parseEnrichmentDescriptor(descriptor);
21910
21982
  }
21911
21983
  if (new TextEncoder().encode(JSON.stringify(f)).byteLength > 16 * 1024)
21912
- return invalid2();
21984
+ return invalid3();
21913
21985
  return f;
21914
21986
  }
21915
21987
  function parseEnrichmentDescriptor(value) {
21916
- const invalid2 = () => {
21988
+ const invalid3 = () => {
21917
21989
  throw new FileOperationError("unavailable", 503, true);
21918
21990
  };
21919
21991
  if (!value || typeof value !== "object" || Array.isArray(value))
21920
- return invalid2();
21992
+ return invalid3();
21921
21993
  const d = value;
21922
21994
  if (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))
21923
- return invalid2();
21995
+ return invalid3();
21924
21996
  return d;
21925
21997
  }
21926
21998
  function safeEnrichmentName(value) {
21927
21999
  return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
21928
22000
  }
21929
22001
  function parseFileEnrichMetadata(value, maxBytes) {
21930
- const invalid2 = () => {
22002
+ const invalid3 = () => {
21931
22003
  throw new FileOperationError("invalid_input", 400);
21932
22004
  };
21933
22005
  if (!value || typeof value !== "object" || Array.isArray(value))
21934
- return invalid2();
22006
+ return invalid3();
21935
22007
  const d = value;
21936
22008
  if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key)) || !safeEnrichmentName(d.name) || !Number.isSafeInteger(d.size) || d.size < 0 || d.mediaType !== undefined && (typeof d.mediaType !== "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType)) || d.replace !== undefined && (typeof d.replace !== "string" || !/^[0-9a-f]{64}$/.test(d.replace)))
21937
- return invalid2();
22009
+ return invalid3();
21938
22010
  if (d.size > maxBytes)
21939
22011
  throw new FileOperationError("request_too_large", 413);
21940
22012
  return d;
@@ -21946,37 +22018,37 @@ function safeFilePath(value) {
21946
22018
  return typeof value === "string" && value.length <= 1024 && value.split("/").every(safeFileName);
21947
22019
  }
21948
22020
  function parseFileCreateMetadata(value, maxBytes) {
21949
- const invalid2 = () => {
22021
+ const invalid3 = () => {
21950
22022
  throw new FileOperationError("invalid_input", 400);
21951
22023
  };
21952
22024
  if (!value || typeof value !== "object" || Array.isArray(value))
21953
- return invalid2();
22025
+ return invalid3();
21954
22026
  const input = value;
21955
22027
  if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "size", "requestKey"].includes(key)))
21956
- return invalid2();
22028
+ return invalid3();
21957
22029
  const path = input.path === undefined ? "Files" : input.path;
21958
22030
  if (!safeFileName(input.name) || !safeFilePath(path))
21959
- return invalid2();
22031
+ return invalid3();
21960
22032
  if (input.storage !== undefined && input.storage !== "remote" && input.storage !== "local")
21961
- return invalid2();
22033
+ return invalid3();
21962
22034
  if (!Number.isSafeInteger(input.size) || input.size < 0)
21963
- return invalid2();
22035
+ return invalid3();
21964
22036
  if (input.size > maxBytes)
21965
22037
  throw new FileOperationError("request_too_large", 413);
21966
22038
  if (typeof input.requestKey !== "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey))
21967
- return invalid2();
22039
+ return invalid3();
21968
22040
  let csv;
21969
22041
  if (input.csv !== undefined) {
21970
22042
  if (!input.csv || typeof input.csv !== "object" || Array.isArray(input.csv))
21971
- return invalid2();
22043
+ return invalid3();
21972
22044
  const hints = input.csv;
21973
22045
  if (Object.keys(hints).some((key) => !["encoding", "delimiter", "headerRow"].includes(key)))
21974
- return invalid2();
22046
+ return invalid3();
21975
22047
  const encoding = hints.encoding === undefined ? "utf-8" : hints.encoding;
21976
22048
  const delimiter = hints.delimiter === undefined ? "," : hints.delimiter;
21977
22049
  const headerRow = hints.headerRow === undefined ? 1 : hints.headerRow;
21978
22050
  if (encoding !== "utf-8" && encoding !== "windows-1252" || ![",", ";", "\t"].includes(delimiter) || !Number.isSafeInteger(headerRow) || headerRow < 1 || headerRow > 1e5)
21979
- return invalid2();
22051
+ return invalid3();
21980
22052
  csv = { encoding, delimiter, headerRow };
21981
22053
  }
21982
22054
  return {
@@ -22432,15 +22504,16 @@ var data = {
22432
22504
  async function send(input) {
22433
22505
  const context = await resolveDataContext();
22434
22506
  try {
22435
- checkDataRequestSize({ repository, ...input });
22507
+ const envelope = { repository, ...input };
22508
+ checkDataRequestSize(envelope);
22436
22509
  if ("local" in context)
22437
- return await context.local("data/query", { repository, ...input });
22510
+ return await context.local("data/query", envelope);
22438
22511
  const response = await fetch(context.url, {
22439
22512
  method: "POST",
22440
22513
  redirect: "error",
22441
22514
  cache: "no-store",
22442
22515
  headers: { "content-type": "application/json", ...context.headers },
22443
- body: JSON.stringify({ repository, ...input })
22516
+ body: JSON.stringify(envelope)
22444
22517
  });
22445
22518
  const result = await response.json();
22446
22519
  if (!response.ok) {
@@ -22460,24 +22533,23 @@ var data = {
22460
22533
  },
22461
22534
  records: {
22462
22535
  select: (query) => send({ operation: "records.select", query }),
22463
- search: (query, options) => {
22464
- try {
22465
- return send({ operation: "records.search", ...captureRecordsSearchInput(query, options) });
22466
- } catch (error) {
22467
- return Promise.reject(error);
22468
- }
22469
- }
22536
+ search: async (query, options) => send({ operation: "records.search", ...captureRecordsSearchInput(query, options) })
22537
+ },
22538
+ history: {
22539
+ list: async (options) => send({ operation: "history.list", options: captureHistoryListOptions(options) }),
22540
+ at: async (instant) => send({ operation: "history.at", instant: captureHistoryInstant(instant) }),
22541
+ changes: async (options) => send({ operation: "history.changes", options: captureHistoryChangesOptions(options) })
22470
22542
  },
22471
22543
  changes() {
22472
22544
  let updates = [], creates = [], deletes = [], publicationId;
22473
22545
  let pending, confirmed;
22474
- const snapshot = () => {
22475
- const captured = captureDataChanges(updates, creates, deletes);
22476
- const input = {
22477
- updates: captured.updates,
22478
- ...captured.creates.length ? { creates: captured.creates } : {},
22479
- ...captured.deletes.length ? { deletes: captured.deletes } : {}
22480
- };
22546
+ const assertMutable = () => {
22547
+ if (publicationId)
22548
+ throw new DataOperationError("invalid_change", false, "unapplied");
22549
+ };
22550
+ const snapshot = (nextUpdates = updates, nextCreates = creates, nextDeletes = deletes) => {
22551
+ const { updates: updates2, creates: creates2, deletes: deletes2 } = captureDataChanges(nextUpdates, nextCreates, nextDeletes);
22552
+ const input = { updates: updates2, ...creates2.length ? { creates: creates2 } : {}, ...deletes2.length ? { deletes: deletes2 } : {} };
22481
22553
  checkDataRequestSize({
22482
22554
  repository,
22483
22555
  operation: "changes.publish",
@@ -22488,48 +22560,21 @@ var data = {
22488
22560
  };
22489
22561
  return {
22490
22562
  create(record2) {
22491
- if (publicationId)
22492
- throw new DataOperationError("invalid_change", false, "unapplied");
22563
+ assertMutable();
22493
22564
  const next = [...creates, captureDataCreate(record2)];
22494
- captureDataChanges(updates, next, deletes);
22495
- checkDataRequestSize({
22496
- repository,
22497
- operation: "changes.publish",
22498
- updates,
22499
- creates: next,
22500
- ...deletes.length ? { deletes } : {},
22501
- publicationId: "00000000-0000-4000-8000-000000000000"
22502
- });
22565
+ snapshot(updates, next, deletes);
22503
22566
  creates = next;
22504
22567
  },
22505
22568
  update(record2, set) {
22506
- if (publicationId)
22507
- throw new DataOperationError("invalid_change", false, "unapplied");
22569
+ assertMutable();
22508
22570
  const next = [...updates, captureDataUpdate(record2, set)];
22509
- captureDataChanges(next, creates, deletes);
22510
- checkDataRequestSize({
22511
- repository,
22512
- operation: "changes.publish",
22513
- updates: next,
22514
- ...creates.length ? { creates } : {},
22515
- ...deletes.length ? { deletes } : {},
22516
- publicationId: "00000000-0000-4000-8000-000000000000"
22517
- });
22571
+ snapshot(next, creates, deletes);
22518
22572
  updates = next;
22519
22573
  },
22520
22574
  delete(record2) {
22521
- if (publicationId)
22522
- throw new DataOperationError("invalid_change", false, "unapplied");
22575
+ assertMutable();
22523
22576
  const next = [...deletes, captureDataDelete(record2)];
22524
- captureDataChanges(updates, creates, next);
22525
- checkDataRequestSize({
22526
- repository,
22527
- operation: "changes.publish",
22528
- updates,
22529
- deletes: next,
22530
- ...creates.length ? { creates } : {},
22531
- publicationId: "00000000-0000-4000-8000-000000000000"
22532
- });
22577
+ snapshot(updates, creates, next);
22533
22578
  deletes = next;
22534
22579
  },
22535
22580
  async validate() {
@@ -22574,7 +22619,7 @@ var TIMEOUT_MS = 30000;
22574
22619
  var MAX_ACTIVE = 4;
22575
22620
  var ATTEMPTS = 2;
22576
22621
  var active = 0;
22577
- var invalid2 = () => new FileOperationError("invalid_input", 400);
22622
+ var invalid3 = () => new FileOperationError("invalid_input", 400);
22578
22623
  var unavailable = () => new FileOperationError("unavailable", 503, true);
22579
22624
  var failures = {
22580
22625
  400: ["invalid_input", "unsupported_context"],
@@ -22598,7 +22643,7 @@ async function boundedBytes(stream, limit) {
22598
22643
  if (done)
22599
22644
  break;
22600
22645
  if (!(value instanceof Uint8Array))
22601
- throw invalid2();
22646
+ throw invalid3();
22602
22647
  size += value.byteLength;
22603
22648
  if (size > limit)
22604
22649
  throw new FileOperationError("request_too_large", 413);
@@ -22780,7 +22825,7 @@ async function send(suffix, init, descriptor, initial, parent) {
22780
22825
  }
22781
22826
  function idPath(id2) {
22782
22827
  if (typeof id2 !== "string" || !FILE_ID_PATTERN.test(id2))
22783
- throw invalid2();
22828
+ throw invalid3();
22784
22829
  return `/${id2}`;
22785
22830
  }
22786
22831
  async function multipartCreate(input, metadata, exchange, release = () => {
@@ -22844,12 +22889,12 @@ async function multipartCreate(input, metadata, exchange, release = () => {
22844
22889
  offset = 0;
22845
22890
  const value2 = await next();
22846
22891
  if (value2.done)
22847
- throw invalid2();
22892
+ throw invalid3();
22848
22893
  if (!(value2.value instanceof Uint8Array))
22849
- throw invalid2();
22894
+ throw invalid3();
22850
22895
  chunk = value2.value;
22851
22896
  if (chunk.byteLength > metadata.size - sent - filled)
22852
- throw invalid2();
22897
+ throw invalid3();
22853
22898
  }
22854
22899
  const take = Math.min(length - filled, chunk.byteLength - offset);
22855
22900
  part.set(chunk.subarray(offset, offset + take), filled);
@@ -22862,7 +22907,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
22862
22907
  }
22863
22908
  }
22864
22909
  if (part.byteLength !== length)
22865
- throw invalid2();
22910
+ throw invalid3();
22866
22911
  if (reader && sent + length === metadata.size) {
22867
22912
  lastProgress = Date.now();
22868
22913
  for (;; ) {
@@ -22870,7 +22915,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
22870
22915
  if (value2.done)
22871
22916
  break;
22872
22917
  if (!(value2.value instanceof Uint8Array) || value2.value.byteLength)
22873
- throw invalid2();
22918
+ throw invalid3();
22874
22919
  }
22875
22920
  }
22876
22921
  const value = await request((signal) => exchange(`${route}/part`, {
@@ -22890,7 +22935,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
22890
22935
  for (const name of Object.keys(upload.file.enrichments)) {
22891
22936
  const bytes = await request(() => upload.enrichment(name));
22892
22937
  if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES)
22893
- throw invalid2();
22938
+ throw invalid3();
22894
22939
  await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
22895
22940
  method: "POST",
22896
22941
  headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
@@ -22944,7 +22989,7 @@ function contentPath(id2, name) {
22944
22989
  if (name === undefined)
22945
22990
  return `${path}/content`;
22946
22991
  if (!safeEnrichmentName(name))
22947
- throw invalid2();
22992
+ throw invalid3();
22948
22993
  return `${path}/enrichments/${encodeURIComponent(name)}/content`;
22949
22994
  }
22950
22995
  async function read(id2, names) {
@@ -22955,16 +23000,16 @@ async function read(id2, names) {
22955
23000
  return operation((context2, signal) => send(path, { method: "GET" }, false, context2, signal), context);
22956
23001
  }
22957
23002
  if (!Array.isArray(names))
22958
- throw invalid2();
23003
+ throw invalid3();
22959
23004
  const distinct = [];
22960
23005
  const seen = new Set;
22961
23006
  for (const name of names) {
22962
23007
  if (typeof name !== "string" || !safeEnrichmentName(name))
22963
- throw invalid2();
23008
+ throw invalid3();
22964
23009
  if (seen.has(name))
22965
23010
  continue;
22966
23011
  if (seen.size === 16)
22967
- throw invalid2();
23012
+ throw invalid3();
22968
23013
  seen.add(name);
22969
23014
  distinct.push(name);
22970
23015
  }
@@ -22998,15 +23043,15 @@ var files = {
22998
23043
  async create(input, options) {
22999
23044
  const context = await resolveFilesContext();
23000
23045
  if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).some((key) => !["file", "size", "path", "name", "storage", "csv"].includes(key)) || options !== undefined && (!options || typeof options !== "object" || Array.isArray(options) || Object.keys(options).some((key) => key !== "requestKey")))
23001
- throw invalid2();
23046
+ throw invalid3();
23002
23047
  const blob = input.file instanceof Blob;
23003
23048
  const array2 = input.file instanceof Uint8Array;
23004
23049
  const stream = input.file instanceof ReadableStream;
23005
23050
  if (!blob && !array2 && !stream)
23006
- throw invalid2();
23051
+ throw invalid3();
23007
23052
  const intrinsic = blob ? input.file.size : array2 ? input.file.byteLength : undefined;
23008
23053
  if (intrinsic !== undefined && input.size !== undefined && input.size !== intrinsic)
23009
- throw invalid2();
23054
+ throw invalid3();
23010
23055
  let name = input.name;
23011
23056
  if (name === undefined && blob && "name" in input.file && typeof input.file.name === "string") {
23012
23057
  name = typeof globalThis.File !== "undefined" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1);
@@ -23023,7 +23068,7 @@ var files = {
23023
23068
  throw new FileOperationError("unsupported_context", 400);
23024
23069
  const header = encodeURIComponent(JSON.stringify(metadata));
23025
23070
  if (new TextEncoder().encode(header).byteLength > METADATA_BYTES)
23026
- throw invalid2();
23071
+ throw invalid3();
23027
23072
  if (active >= MAX_ACTIVE)
23028
23073
  throw new FileOperationError("capacity", 429, true);
23029
23074
  active++;
@@ -23057,7 +23102,7 @@ var files = {
23057
23102
  try {
23058
23103
  const bytes = array2 ? input.file.slice() : await boundedBytes(blob ? input.file.stream() : input.file, metadata.size);
23059
23104
  if (bytes.byteLength !== metadata.size)
23060
- throw invalid2();
23105
+ throw invalid3();
23061
23106
  if (signal.aborted)
23062
23107
  throw unavailable();
23063
23108
  return await send("", {
@@ -23075,7 +23120,7 @@ var files = {
23075
23120
  async enrich(input) {
23076
23121
  const context = await resolveFilesContext();
23077
23122
  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))
23078
- throw invalid2();
23123
+ throw invalid3();
23079
23124
  const path = `${idPath(input.file)}/enrichments`;
23080
23125
  if (typeof input.content === "string" ? input.content.length > MAX_BYTES : input.content.byteLength > MAX_BYTES)
23081
23126
  throw new FileOperationError("request_too_large", 413);
@@ -23097,7 +23142,7 @@ var files = {
23097
23142
  }, MAX_BYTES);
23098
23143
  const header = encodeURIComponent(JSON.stringify(metadata));
23099
23144
  if (new TextEncoder().encode(header).byteLength > METADATA_BYTES)
23100
- throw invalid2();
23145
+ throw invalid3();
23101
23146
  const result2 = await send(path, { method: "POST", headers: {
23102
23147
  "content-type": "application/octet-stream",
23103
23148
  "x-oddington-enrichment": header
@@ -13,7 +13,7 @@ export { AirtableOperationError } from '../../../shared/airtable-contract.js';
13
13
  export type { AirtableField, AirtableTable, AirtableRecord, AirtableListInput, AirtableGetInput, AirtableCreateInput, AirtableUpdateInput, AirtableDeleteInput, AirtableReceipt, AirtableWriteResult, AirtableClient, Connections } from '../../../shared/airtable-contract.js';
14
14
  export declare const connections: import('../../../shared/airtable-contract.js').Connections;
15
15
  export { DataOperationError } from '../../../shared/data-contract.js';
16
- export type { DataRepository, DataErrorCode, PublicValueDefinition, PublicField, CollectionSummary, CollectionSchema, QueryScalar, SelectionOptions, RecordSelection, RecordsQuery, DataValue, SelectedRecord, SelectionMetadata, RecordsResult, RecordsSearchOptions, RecordsSearchResult, RecordIdentity, RecordCreation, RecordUpdateSet, DataPublication, DataChangeSet, DataEffect } from '../../../shared/data-contract.js';
16
+ export type { DataRepository, DataErrorCode, PublicValueDefinition, PublicField, CollectionSummary, CollectionSchema, QueryScalar, SelectionOptions, RecordSelection, RecordsQuery, DataValue, SelectedRecord, SelectionMetadata, RecordsResult, RecordsSearchOptions, RecordsSearchResult, HistoryListOptions, HistoryEntry, HistoryChangesOptions, HistoryChange, HistoryChangesResult, RecordIdentity, RecordCreation, RecordUpdateSet, DataPublication, DataChangeSet, DataEffect } from '../../../shared/data-contract.js';
17
17
  export declare const data: {
18
18
  repository(name: string): import('../../../shared/data-contract.js').DataRepository;
19
19
  };
@@ -134,6 +134,49 @@ export type DataPublication = {
134
134
  updated: RecordIdentity[];
135
135
  deleted: RecordIdentity[];
136
136
  };
137
+ export type HistoryListOptions = {
138
+ limit?: number;
139
+ before?: string;
140
+ };
141
+ export type HistoryChangesOptions = {
142
+ collections?: string[];
143
+ refs?: string[];
144
+ from?: string;
145
+ through?: string;
146
+ limit?: number;
147
+ after?: string;
148
+ };
149
+ export type HistoryEntry = {
150
+ version: string;
151
+ publishedAt: string;
152
+ author: {
153
+ name: string;
154
+ email: string;
155
+ };
156
+ authoredAt: string;
157
+ committer: {
158
+ name: string;
159
+ email: string;
160
+ };
161
+ committedAt: string;
162
+ message: string;
163
+ created: string[];
164
+ updated: string[];
165
+ deleted: string[];
166
+ };
167
+ export type HistoryChange = Omit<HistoryEntry, 'created' | 'updated' | 'deleted'> & {
168
+ $ref: string;
169
+ change: 'created' | 'updated' | 'deleted';
170
+ before: SelectedRecord | null;
171
+ after: SelectedRecord | null;
172
+ };
173
+ export type HistoryChangesResult = {
174
+ $meta: {
175
+ through: string;
176
+ next?: string;
177
+ };
178
+ changes: HistoryChange[];
179
+ };
137
180
  export interface DataChangeSet {
138
181
  create(record: RecordCreation): void;
139
182
  update(record: RecordIdentity, set: RecordUpdateSet): void;
@@ -150,6 +193,11 @@ export interface DataRepository {
150
193
  select(query: RecordsQuery): Promise<RecordsResult>;
151
194
  search(query: string, options?: RecordsSearchOptions): Promise<RecordsSearchResult>;
152
195
  };
196
+ history: {
197
+ list(options?: HistoryListOptions): Promise<HistoryEntry[]>;
198
+ at(instant: string): Promise<HistoryEntry | null>;
199
+ changes(options?: HistoryChangesOptions): Promise<HistoryChangesResult>;
200
+ };
153
201
  changes(): DataChangeSet;
154
202
  }
155
203
  export type DataErrorCode = 'context_unavailable' | 'unauthenticated' | 'access_denied' | 'invalid_query' | 'request_too_large' | 'capacity' | 'unavailable' | 'invalid_change' | 'conflict' | 'publication_pending';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oddington/sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Oddington routine and response-handler SDK",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",