@rocicorp/zero 1.10.0-canary.21 → 1.10.0-canary.22
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/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/replicache/src/dag/gc.d.ts.map +1 -1
- package/out/replicache/src/dag/gc.js +2 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
- package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
- package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
- package/out/replicache/src/dag/store-impl.d.ts +12 -2
- package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
- package/out/replicache/src/dag/store-impl.js +22 -8
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +15 -5
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +25 -25
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
- package/out/replicache/src/kv/idb-store.d.ts +9 -0
- package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store.js +14 -2
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.d.ts +1 -0
- package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
- package/out/replicache/src/kv/mem-store.js +3 -0
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +2 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +6 -1
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/store.d.ts +6 -0
- package/out/replicache/src/kv/store.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +10 -5
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +16 -9
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +14 -3
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +104 -9
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/shared/src/arrays.js +1 -15
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/hash.d.ts +1 -0
- package/out/shared/src/hash.d.ts.map +1 -1
- package/out/shared/src/hash.js +10 -28
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.d.ts +2 -0
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +18 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json.d.ts.map +1 -1
- package/out/shared/src/json.js +2 -2
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +8 -0
- package/out/shared/src/ring-buffer.d.ts.map +1 -1
- package/out/shared/src/ring-buffer.js +22 -1
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/shared/src/xxhash32.d.ts +36 -0
- package/out/shared/src/xxhash32.d.ts.map +1 -0
- package/out/shared/src/xxhash32.js +51 -0
- package/out/shared/src/xxhash32.js.map +1 -0
- package/out/z2s/src/compiler.js +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +3 -2
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +1 -1
- package/out/zero-cache/src/services/run-ast.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -10
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts +20 -0
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +32 -14
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
- package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.js +3 -1
- package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +6 -0
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js +2 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.d.ts +8 -11
- package/out/zero-client/src/client/context.d.ts.map +1 -1
- package/out/zero-client/src/client/context.js +11 -15
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/custom.js +2 -2
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/error.js +1 -1
- package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js +2 -1
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +3 -0
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.d.ts +2 -0
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.js +48 -9
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +18 -3
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +59 -12
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/types/query-result.d.ts +2 -0
- package/out/zero-client/src/types/query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +64 -1
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +223 -51
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/query-hash-visitor.d.ts +81 -0
- package/out/zero-protocol/src/query-hash-visitor.d.ts.map +1 -0
- package/out/zero-protocol/src/query-hash-visitor.js +324 -0
- package/out/zero-protocol/src/query-hash-visitor.js.map +1 -0
- package/out/zero-protocol/src/query-hash.d.ts +7 -2
- package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
- package/out/zero-protocol/src/query-hash.js +12 -9
- package/out/zero-protocol/src/query-hash.js.map +1 -1
- package/out/zero-react/src/use-query.d.ts +5 -4
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +86 -9
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts +10 -0
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +27 -0
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.js +5 -3
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zql/src/ivm/array-view.d.ts +10 -0
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +20 -8
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +5 -3
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
- package/out/zql/src/ivm/deferred-input.js +3 -2
- package/out/zql/src/ivm/deferred-input.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts +1 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +1 -1
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +2 -2
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js +6 -5
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +2 -2
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +4 -4
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +10 -8
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +2 -2
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js +7 -4
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +2 -2
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +2 -2
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +9 -9
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +3 -4
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +9 -7
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +3 -3
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js +2 -2
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +2 -0
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +58 -31
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +11 -1
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.d.ts +25 -14
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +414 -94
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.d.ts +21 -11
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-transitions.d.ts +171 -0
- package/out/zql/src/query/query-transitions.d.ts.map +1 -0
- package/out/zql/src/query/query-transitions.js +207 -0
- package/out/zql/src/query/query-transitions.js.map +1 -0
- package/out/zql/src/query/query.d.ts +7 -1
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts +3 -2
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +23 -2
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/typed-view.d.ts +1 -1
- package/out/zql/src/query/typed-view.d.ts.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +7 -5
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iterables.js","names":["#iterator"],"sources":["../../../../shared/src/iterables.ts"],"sourcesContent":["export function* joinIterables<T>(...iters: Iterable<T>[]) {\n for (const iter of iters) {\n yield* iter;\n }\n}\n\nfunction* filterIter<T>(\n iter: Iterable<T>,\n p: (t: T, index: number) => boolean,\n): Iterable<T> {\n let index = 0;\n for (const t of iter) {\n if (p(t, index++)) {\n yield t;\n }\n }\n}\n\nfunction* mapIter<T, U>(\n iter: Iterable<T>,\n f: (t: T, index: number) => U,\n): Iterable<U> {\n let index = 0;\n for (const t of iter) {\n yield f(t, index++);\n }\n}\n\nexport function first<T>(stream: Iterable<T>): T | undefined {\n const it = stream[Symbol.iterator]();\n const {value} = it.next();\n it.return?.();\n return value;\n}\n\nexport function* once<T>(stream: Iterable<T>): Iterable<T> {\n const it = stream[Symbol.iterator]();\n const {value} = it.next();\n if (value !== undefined) {\n yield value;\n }\n it.return?.();\n}\n\n// ES2024 Iterator helpers are available in Node 22+ and part of ES2024.\n// https://github.com/tc39/proposal-iterator-helpers\n\ntype IteratorWithHelpers<T> = Iterator<T> & {\n map<U>(f: (t: T, index: number) => U): IteratorWithHelpers<U>;\n filter(p: (t: T, index: number) => boolean): IteratorWithHelpers<T>;\n some(p: (t: T, index: number) => boolean): boolean;\n [Symbol.iterator](): IteratorWithHelpers<T>;\n};\n\ntype IteratorConstructor = {\n from<T>(this: void, iter: Iterable<T>): IteratorWithHelpers<T>;\n};\n\n// Check if native Iterator.from is available and bind it once at startup\n// We use globalThis to access the runtime value safely\nconst iteratorCtor = (globalThis as {Iterator?: IteratorConstructor}).Iterator;\nconst nativeIteratorFrom = iteratorCtor?.from as\n | (<T>(iter: Iterable<T>) => IteratorWithHelpers<T>)\n | undefined;\n\nconst iteratorFrom: <T>(e: Iterable<T>) => IteratorWithHelpers<T> =\n nativeIteratorFrom ?? (e => new IterWrapper(e));\n\n// Fallback implementation for environments without ES2024 Iterator helpers\nclass IterWrapper<T> implements IteratorWithHelpers<T>, IterableIterator<T> {\n readonly #iterator: Iterator<T>;\n\n constructor(iterable: Iterable<T>) {\n this.#iterator = iterable[Symbol.iterator]();\n }\n\n next(): IteratorResult<T> {\n return this.#iterator.next();\n }\n\n [Symbol.iterator](): IteratorWithHelpers<T> {\n return this;\n }\n\n map<U>(f: (t: T, index: number) => U): IterWrapper<U> {\n return new IterWrapper(mapIter(this, f));\n }\n\n filter(p: (t: T, index: number) => boolean): IterWrapper<T> {\n return new IterWrapper(filterIter(this, p));\n }\n\n some(p: (t: T, index: number) => boolean): boolean {\n return some(this, p);\n }\n}\n\nexport function wrapIterable<T>(iter: Iterable<T>): IteratorWithHelpers<T> {\n return iteratorFrom(iter);\n}\n\n/**\n * This will make a new array where the elements are the same as the iterable\n * but sorted according to the compare function. If the compare function is not\n * provided, it will sort the elements in JS standard way which is string\n * compare.\n */\nexport function toSorted<T>(\n iter: Iterable<T>,\n compare?: (a: T, b: T) => number,\n): T[] {\n // oxlint-disable-next-line e18e/prefer-array-to-sorted\n return [...iter].sort(compare);\n}\n\nexport function some<T>(\n iterable: Iterable<T>,\n predicate: (item: T, index: number) => boolean,\n): boolean {\n let index = 0;\n for (const item of iterable) {\n if (predicate(item, index++)) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":";AAAA,UAAiB,cAAiB,GAAG,OAAsB;CACzD,KAAK,MAAM,QAAQ,OACjB,OAAO;AAEX;AAEA,UAAU,WACR,MACA,GACa;CACb,IAAI,QAAQ;CACZ,KAAK,MAAM,KAAK,MACd,IAAI,EAAE,GAAG,OAAO,GACd,MAAM;AAGZ;AAEA,UAAU,QACR,MACA,GACa;CACb,IAAI,QAAQ;CACZ,KAAK,MAAM,KAAK,MACd,MAAM,EAAE,GAAG,OAAO;AAEtB;AASA,UAAiB,KAAQ,QAAkC;CACzD,MAAM,KAAK,OAAO,OAAO,UAAU;CACnC,MAAM,EAAC,UAAS,GAAG,KAAK;CACxB,IAAI,UAAU,KAAA,GACZ,MAAM;CAER,GAAG,SAAS;AACd;AAuBA,IAAM,eALgB,WAAgD,UAC7B,UAKhB,MAAK,IAAI,YAAY,CAAC;AAG/C,IAAM,cAAN,MAAM,YAAsE;CAC1E;CAEA,YAAY,UAAuB;EACjC,KAAKA,YAAY,SAAS,OAAO,UAAU;CAC7C;CAEA,OAA0B;EACxB,OAAO,KAAKA,UAAU,KAAK;CAC7B;CAEA,CAAC,OAAO,YAAoC;EAC1C,OAAO;CACT;CAEA,IAAO,GAA+C;EACpD,OAAO,IAAI,YAAY,QAAQ,MAAM,CAAC,CAAC;CACzC;CAEA,OAAO,GAAqD;EAC1D,OAAO,IAAI,YAAY,WAAW,MAAM,CAAC,CAAC;CAC5C;CAEA,KAAK,GAA8C;EACjD,OAAO,KAAK,MAAM,CAAC;CACrB;AACF;AAEA,SAAgB,aAAgB,MAA2C;CACzE,OAAO,aAAa,IAAI;AAC1B;;;;;;;AAQA,SAAgB,SACd,MACA,SACK;CAEL,OAAO,CAAC,GAAG,IAAI,EAAE,KAAK,OAAO;AAC/B;AAEA,SAAgB,KACd,UACA,WACS;CACT,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,UACjB,IAAI,UAAU,MAAM,OAAO,GACzB,OAAO;CAGX,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"iterables.js","names":["#iterator"],"sources":["../../../../shared/src/iterables.ts"],"sourcesContent":["export function* joinIterables<T>(...iters: Iterable<T>[]) {\n for (const iter of iters) {\n yield* iter;\n }\n}\n\nfunction* filterIter<T>(\n iter: Iterable<T>,\n p: (t: T, index: number) => boolean,\n): Iterable<T> {\n let index = 0;\n for (const t of iter) {\n if (p(t, index++)) {\n yield t;\n }\n }\n}\n\nfunction* mapIter<T, U>(\n iter: Iterable<T>,\n f: (t: T, index: number) => U,\n): Iterable<U> {\n let index = 0;\n for (const t of iter) {\n yield f(t, index++);\n }\n}\n\nexport function first<T>(stream: Iterable<T>): T | undefined {\n const it = stream[Symbol.iterator]();\n const {value} = it.next();\n it.return?.();\n return value;\n}\n\nexport function* once<T>(stream: Iterable<T>): Iterable<T> {\n const it = stream[Symbol.iterator]();\n const {value} = it.next();\n if (value !== undefined) {\n yield value;\n }\n it.return?.();\n}\n\n// ES2024 Iterator helpers are available in Node 22+ and part of ES2024.\n// https://github.com/tc39/proposal-iterator-helpers\n\ntype IteratorWithHelpers<T> = Iterator<T> & {\n map<U>(f: (t: T, index: number) => U): IteratorWithHelpers<U>;\n filter(p: (t: T, index: number) => boolean): IteratorWithHelpers<T>;\n some(p: (t: T, index: number) => boolean): boolean;\n [Symbol.iterator](): IteratorWithHelpers<T>;\n};\n\ntype IteratorConstructor = {\n from<T>(this: void, iter: Iterable<T>): IteratorWithHelpers<T>;\n};\n\n// Check if native Iterator.from is available and bind it once at startup\n// We use globalThis to access the runtime value safely\nconst iteratorCtor = (globalThis as {Iterator?: IteratorConstructor}).Iterator;\nconst nativeIteratorFrom = iteratorCtor?.from as\n | (<T>(iter: Iterable<T>) => IteratorWithHelpers<T>)\n | undefined;\n\nconst iteratorFrom: <T>(e: Iterable<T>) => IteratorWithHelpers<T> =\n nativeIteratorFrom ?? (e => new IterWrapper(e));\n\n// Fallback implementation for environments without ES2024 Iterator helpers\nclass IterWrapper<T> implements IteratorWithHelpers<T>, IterableIterator<T> {\n readonly #iterator: Iterator<T>;\n\n constructor(iterable: Iterable<T>) {\n this.#iterator = iterable[Symbol.iterator]();\n }\n\n next(): IteratorResult<T> {\n return this.#iterator.next();\n }\n\n [Symbol.iterator](): IteratorWithHelpers<T> {\n return this;\n }\n\n map<U>(f: (t: T, index: number) => U): IterWrapper<U> {\n return new IterWrapper(mapIter(this, f));\n }\n\n filter(p: (t: T, index: number) => boolean): IterWrapper<T> {\n return new IterWrapper(filterIter(this, p));\n }\n\n some(p: (t: T, index: number) => boolean): boolean {\n return some(this, p);\n }\n}\n\nexport function wrapIterable<T>(iter: Iterable<T>): IteratorWithHelpers<T> {\n return iteratorFrom(iter);\n}\n\n/**\n * This will make a new array where the elements are the same as the iterable\n * but sorted according to the compare function. If the compare function is not\n * provided, it will sort the elements in JS standard way which is string\n * compare.\n */\nexport function toSorted<T>(\n iter: Iterable<T>,\n compare?: (a: T, b: T) => number,\n): T[] {\n // oxlint-disable-next-line e18e/prefer-array-to-sorted\n return [...iter].sort(compare);\n}\n\nexport function some<T>(\n iterable: Iterable<T>,\n predicate: (item: T, index: number) => boolean,\n): boolean {\n let index = 0;\n for (const item of iterable) {\n if (predicate(item, index++)) {\n return true;\n }\n }\n return false;\n}\n\nclass EmptyIteratorWithReturn<T>\n // oxlint-disable-next-line typescript/no-explicit-any\n implements IterableIterator<any, T, any>, IteratorReturnResult<T>\n{\n readonly value: T;\n readonly done = true;\n\n constructor(value: T) {\n this.value = value;\n }\n\n [Symbol.iterator]() {\n return this;\n }\n\n next() {\n return this;\n }\n}\n\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const emptyIterator: IterableIterator<any, undefined, any> =\n new EmptyIteratorWithReturn(undefined);\n\nexport function makeEmptyIteratorWithReturn<T>(\n value: T,\n // oxlint-disable-next-line typescript/no-explicit-any\n): IterableIterator<any, T, any> {\n return new EmptyIteratorWithReturn(value);\n}\n"],"mappings":";AAAA,UAAiB,cAAiB,GAAG,OAAsB;CACzD,KAAK,MAAM,QAAQ,OACjB,OAAO;AAEX;AAEA,UAAU,WACR,MACA,GACa;CACb,IAAI,QAAQ;CACZ,KAAK,MAAM,KAAK,MACd,IAAI,EAAE,GAAG,OAAO,GACd,MAAM;AAGZ;AAEA,UAAU,QACR,MACA,GACa;CACb,IAAI,QAAQ;CACZ,KAAK,MAAM,KAAK,MACd,MAAM,EAAE,GAAG,OAAO;AAEtB;AASA,UAAiB,KAAQ,QAAkC;CACzD,MAAM,KAAK,OAAO,OAAO,UAAU;CACnC,MAAM,EAAC,UAAS,GAAG,KAAK;CACxB,IAAI,UAAU,KAAA,GACZ,MAAM;CAER,GAAG,SAAS;AACd;AAuBA,IAAM,eALgB,WAAgD,UAC7B,UAKhB,MAAK,IAAI,YAAY,CAAC;AAG/C,IAAM,cAAN,MAAM,YAAsE;CAC1E;CAEA,YAAY,UAAuB;EACjC,KAAKA,YAAY,SAAS,OAAO,UAAU;CAC7C;CAEA,OAA0B;EACxB,OAAO,KAAKA,UAAU,KAAK;CAC7B;CAEA,CAAC,OAAO,YAAoC;EAC1C,OAAO;CACT;CAEA,IAAO,GAA+C;EACpD,OAAO,IAAI,YAAY,QAAQ,MAAM,CAAC,CAAC;CACzC;CAEA,OAAO,GAAqD;EAC1D,OAAO,IAAI,YAAY,WAAW,MAAM,CAAC,CAAC;CAC5C;CAEA,KAAK,GAA8C;EACjD,OAAO,KAAK,MAAM,CAAC;CACrB;AACF;AAEA,SAAgB,aAAgB,MAA2C;CACzE,OAAO,aAAa,IAAI;AAC1B;;;;;;;AAQA,SAAgB,SACd,MACA,SACK;CAEL,OAAO,CAAC,GAAG,IAAI,EAAE,KAAK,OAAO;AAC/B;AAEA,SAAgB,KACd,UACA,WACS;CACT,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,UACjB,IAAI,UAAU,MAAM,OAAO,GACzB,OAAO;CAGX,OAAO;AACT;AAEA,IAAM,0BAAN,MAGA;CACE;CACA,OAAgB;CAEhB,YAAY,OAAU;EACpB,KAAK,QAAQ;CACf;CAEA,CAAC,OAAO,YAAY;EAClB,OAAO;CACT;CAEA,OAAO;EACL,OAAO;CACT;AACF;AAGA,IAAa,gBACX,IAAI,wBAAwB,KAAA,CAAS;AAEvC,SAAgB,4BACd,OAE+B;CAC/B,OAAO,IAAI,wBAAwB,KAAK;AAC1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../../shared/src/json.ts"],"names":[],"mappings":"AAIA,iDAAiD;AACjD,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,MAAM,GACN,OAAO,GACP,MAAM,GACN,KAAK,CAAC,SAAS,CAAC,GAChB,UAAU,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,UAAU,GAAG;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAAC,CAAC;AAEhE,iDAAiD;AACjD,MAAM,MAAM,iBAAiB,GACzB,IAAI,GACJ,MAAM,GACN,OAAO,GACP,MAAM,GACN,aAAa,CAAC,iBAAiB,CAAC,GAChC,kBAAkB,CAAC;AAEvB,kDAAkD;AAClD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,CAAC,EAAE,iBAAiB,GAAG,SAAS,EAChC,CAAC,EAAE,iBAAiB,GAAG,SAAS,GAC/B,OAAO,
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../../shared/src/json.ts"],"names":[],"mappings":"AAIA,iDAAiD;AACjD,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,MAAM,GACN,OAAO,GACP,MAAM,GACN,KAAK,CAAC,SAAS,CAAC,GAChB,UAAU,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,UAAU,GAAG;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAAC,CAAC;AAEhE,iDAAiD;AACjD,MAAM,MAAM,iBAAiB,GACzB,IAAI,GACJ,MAAM,GACN,OAAO,GACP,MAAM,GACN,aAAa,CAAC,iBAAiB,CAAC,GAChC,kBAAkB,CAAC;AAEvB,kDAAkD;AAClD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,CAAC,EAAE,iBAAiB,GAAG,SAAS,EAChC,CAAC,EAAE,iBAAiB,GAAG,SAAS,GAC/B,OAAO,CA6ET;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,IAAI,SAAS,CAmBlE;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,IAAI,UAAU,CAGpE;AAqBD,UAAU,IAAI;IACZ,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACjC,GAAG,IAAI,IAAI,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,IAAI,SAAS,CAgBlE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,IAAI,UAAU,CAKpE;AA8BD,sEAAsE;AACtE,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,SAC3B,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,GACT,CAAC,GACD;IAAC,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAC,CAAC"}
|
package/out/shared/src/json.js
CHANGED
|
@@ -33,12 +33,12 @@ function deepEqual(a, b) {
|
|
|
33
33
|
a = a;
|
|
34
34
|
b = b;
|
|
35
35
|
let aSize = 0;
|
|
36
|
-
for (const key in a) if (hasOwn(a, key)) {
|
|
36
|
+
for (const key in a) if (hasOwn(a, key) && a[key] !== void 0) {
|
|
37
37
|
if (!deepEqual(a[key], b[key])) return false;
|
|
38
38
|
aSize++;
|
|
39
39
|
}
|
|
40
40
|
let bSize = 0;
|
|
41
|
-
for (const key in b) if (hasOwn(b, key)) bSize++;
|
|
41
|
+
for (const key in b) if (hasOwn(b, key) && b[key] !== void 0) bSize++;
|
|
42
42
|
return aSize === bSize;
|
|
43
43
|
}
|
|
44
44
|
function assertJSONValue(v) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","names":[],"sources":["../../../../shared/src/json.ts"],"sourcesContent":["import {assertObject, throwInvalidType} from './asserts.ts';\nimport {skipAssertJSONValue} from './config.ts';\nimport {hasOwn} from './has-own.ts';\n\n/** The values that can be represented in JSON */\nexport type JSONValue =\n | null\n | string\n | boolean\n | number\n | Array<JSONValue>\n | JSONObject;\n\n/**\n * A JSON object. This is a map from strings to JSON values or `undefined`. We\n * allow `undefined` values as a convenience... but beware that the `undefined`\n * values do not round trip to the server. For example:\n *\n * ```\n * // Time t1\n * await tx.set('a', {a: undefined});\n *\n * // time passes, in a new transaction\n * const v = await tx.get('a');\n * console.log(v); // either {a: undefined} or {}\n * ```\n */\nexport type JSONObject = {[key: string]: JSONValue | undefined};\n\n/** Like {@link JSONValue} but deeply readonly */\nexport type ReadonlyJSONValue =\n | null\n | string\n | boolean\n | number\n | ReadonlyArray<ReadonlyJSONValue>\n | ReadonlyJSONObject;\n\n/** Like {@link JSONObject} but deeply readonly */\nexport type ReadonlyJSONObject = {\n readonly [key: string]: ReadonlyJSONValue | undefined;\n};\n\n/**\n * Checks deep equality of two JSON value with (almost) same semantics as\n * `JSON.stringify`. The only difference is that with `JSON.stringify` the\n * ordering of the properties in an object/map/dictionary matters. In\n * {@link deepEqual} the following two values are consider equal, even though the\n * strings JSON.stringify would produce is different:\n *\n * ```js\n * assert(deepEqual(t({a: 1, b: 2}, {b: 2, a: 1}))\n * ```\n */\nexport function deepEqual(\n a: ReadonlyJSONValue | undefined,\n b: ReadonlyJSONValue | undefined,\n): boolean {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n switch (typeof a) {\n case 'boolean':\n case 'number':\n case 'string':\n return false;\n }\n\n // a cannot be undefined here because either a and b are undefined or their\n // types are different.\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n a = a!;\n\n // 'object'\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) {\n return false;\n }\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n\n if (a === null || b === null) {\n return false;\n }\n\n if (Array.isArray(b)) {\n return false;\n }\n\n // We know a and b are objects here but type inference is not smart enough.\n a = a as ReadonlyJSONObject;\n b = b as ReadonlyJSONObject;\n\n // We use for-in loops instead of for of Object.keys() to make sure deepEquals\n // does not allocate any objects.\n\n let aSize = 0;\n for (const key in a) {\n if (hasOwn(a, key)) {\n if (!deepEqual(a[key], b[key])) {\n return false;\n }\n aSize++;\n }\n }\n\n let bSize = 0;\n for (const key in b) {\n if (hasOwn(b, key)) {\n bSize++;\n }\n }\n\n return aSize === bSize;\n}\n\nexport function assertJSONValue(v: unknown): asserts v is JSONValue {\n if (skipAssertJSONValue) {\n return;\n }\n switch (typeof v) {\n case 'boolean':\n case 'number':\n case 'string':\n return;\n case 'object':\n if (v === null) {\n return;\n }\n if (Array.isArray(v)) {\n return assertJSONArray(v);\n }\n return assertObjectIsJSONObject(v as Record<string, unknown>);\n }\n throwInvalidType(v, 'JSON value');\n}\n\nexport function assertJSONObject(v: unknown): asserts v is JSONObject {\n assertObject(v);\n assertObjectIsJSONObject(v);\n}\n\nfunction assertObjectIsJSONObject(\n v: Record<string, unknown>,\n): asserts v is JSONObject {\n for (const k in v) {\n if (hasOwn(v, k)) {\n const value = v[k];\n if (value !== undefined) {\n assertJSONValue(value);\n }\n }\n }\n}\n\nfunction assertJSONArray(v: unknown[]): asserts v is JSONValue[] {\n for (const item of v) {\n assertJSONValue(item);\n }\n}\n\ninterface Path {\n push(key: string | number): void;\n pop(): void;\n}\n\n/**\n * Checks if a value is a JSON value. If there is a value that is not a JSON\n * value, the path parameter is updated to the path of the invalid value.\n */\nexport function isJSONValue(v: unknown, path: Path): v is JSONValue {\n switch (typeof v) {\n case 'boolean':\n case 'number':\n case 'string':\n return true;\n case 'object':\n if (v === null) {\n return true;\n }\n if (Array.isArray(v)) {\n return isJSONArray(v, path);\n }\n return objectIsJSONObject(v as Record<string, unknown>, path);\n }\n return false;\n}\n\nexport function isJSONObject(v: unknown, path: Path): v is JSONObject {\n if (typeof v !== 'object' || v === null) {\n return false;\n }\n return objectIsJSONObject(v as Record<string, unknown>, path);\n}\n\nfunction objectIsJSONObject(\n v: Record<string, unknown>,\n path: Path,\n): v is JSONObject {\n for (const k in v) {\n if (hasOwn(v, k)) {\n path.push(k);\n const value = v[k];\n if (value !== undefined && !isJSONValue(value, path)) {\n return false;\n }\n path.pop();\n }\n }\n return true;\n}\n\nfunction isJSONArray(v: unknown[], path: Path): v is JSONValue[] {\n for (let i = 0; i < v.length; i++) {\n path.push(i);\n if (!isJSONValue(v[i], path)) {\n return false;\n }\n path.pop();\n }\n return true;\n}\n\n/** Basic deep readonly type. It works for {@link JSONValue} types. */\nexport type DeepReadonly<T> = T extends\n | null\n | boolean\n | string\n | number\n | undefined\n ? T\n : {readonly [K in keyof T]: DeepReadonly<T[K]>};\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,SAAgB,UACd,GACA,GACS;CACT,IAAI,MAAM,GACR,OAAO;CAGT,IAAI,OAAO,MAAM,OAAO,GACtB,OAAO;CAGT,QAAQ,OAAO,GAAf;EACE,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;CACX;CAKA,IAAI;CAGJ,IAAI,MAAM,QAAQ,CAAC,GAAG;EACpB,IAAI,CAAC,MAAM,QAAQ,CAAC,GAClB,OAAO;EAET,IAAI,EAAE,WAAW,EAAE,QACjB,OAAO;EAET,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,GACvB,OAAO;EAGX,OAAO;CACT;CAEA,IAAI,MAAM,QAAQ,MAAM,MACtB,OAAO;CAGT,IAAI,MAAM,QAAQ,CAAC,GACjB,OAAO;CAIT,IAAI;CACJ,IAAI;
|
|
1
|
+
{"version":3,"file":"json.js","names":[],"sources":["../../../../shared/src/json.ts"],"sourcesContent":["import {assertObject, throwInvalidType} from './asserts.ts';\nimport {skipAssertJSONValue} from './config.ts';\nimport {hasOwn} from './has-own.ts';\n\n/** The values that can be represented in JSON */\nexport type JSONValue =\n | null\n | string\n | boolean\n | number\n | Array<JSONValue>\n | JSONObject;\n\n/**\n * A JSON object. This is a map from strings to JSON values or `undefined`. We\n * allow `undefined` values as a convenience... but beware that the `undefined`\n * values do not round trip to the server. For example:\n *\n * ```\n * // Time t1\n * await tx.set('a', {a: undefined});\n *\n * // time passes, in a new transaction\n * const v = await tx.get('a');\n * console.log(v); // either {a: undefined} or {}\n * ```\n */\nexport type JSONObject = {[key: string]: JSONValue | undefined};\n\n/** Like {@link JSONValue} but deeply readonly */\nexport type ReadonlyJSONValue =\n | null\n | string\n | boolean\n | number\n | ReadonlyArray<ReadonlyJSONValue>\n | ReadonlyJSONObject;\n\n/** Like {@link JSONObject} but deeply readonly */\nexport type ReadonlyJSONObject = {\n readonly [key: string]: ReadonlyJSONValue | undefined;\n};\n\n/**\n * Checks deep equality of two JSON value with (almost) same semantics as\n * `JSON.stringify`. The only difference is that with `JSON.stringify` the\n * ordering of the properties in an object/map/dictionary matters. In\n * {@link deepEqual} the following two values are consider equal, even though the\n * strings JSON.stringify would produce is different:\n *\n * ```js\n * assert(deepEqual(t({a: 1, b: 2}, {b: 2, a: 1}))\n * ```\n */\nexport function deepEqual(\n a: ReadonlyJSONValue | undefined,\n b: ReadonlyJSONValue | undefined,\n): boolean {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n switch (typeof a) {\n case 'boolean':\n case 'number':\n case 'string':\n return false;\n }\n\n // a cannot be undefined here because either a and b are undefined or their\n // types are different.\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n a = a!;\n\n // 'object'\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) {\n return false;\n }\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n\n if (a === null || b === null) {\n return false;\n }\n\n if (Array.isArray(b)) {\n return false;\n }\n\n // We know a and b are objects here but type inference is not smart enough.\n a = a as ReadonlyJSONObject;\n b = b as ReadonlyJSONObject;\n\n // We use for-in loops instead of for of Object.keys() to make sure deepEquals\n // does not allocate any objects.\n\n // The behavior we want is that property values that are undefined are treated\n // as if they do not exist. For example, the following two values are\n // considered equal:\n // ```\n // deepEqual({a: undefined}, {})\n // ```\n\n let aSize = 0;\n for (const key in a) {\n if (hasOwn(a, key) && a[key] !== undefined) {\n if (!deepEqual(a[key], b[key])) {\n return false;\n }\n aSize++;\n }\n }\n\n let bSize = 0;\n for (const key in b) {\n if (hasOwn(b, key) && b[key] !== undefined) {\n bSize++;\n }\n }\n\n return aSize === bSize;\n}\n\nexport function assertJSONValue(v: unknown): asserts v is JSONValue {\n if (skipAssertJSONValue) {\n return;\n }\n switch (typeof v) {\n case 'boolean':\n case 'number':\n case 'string':\n return;\n case 'object':\n if (v === null) {\n return;\n }\n if (Array.isArray(v)) {\n return assertJSONArray(v);\n }\n return assertObjectIsJSONObject(v as Record<string, unknown>);\n }\n throwInvalidType(v, 'JSON value');\n}\n\nexport function assertJSONObject(v: unknown): asserts v is JSONObject {\n assertObject(v);\n assertObjectIsJSONObject(v);\n}\n\nfunction assertObjectIsJSONObject(\n v: Record<string, unknown>,\n): asserts v is JSONObject {\n for (const k in v) {\n if (hasOwn(v, k)) {\n const value = v[k];\n if (value !== undefined) {\n assertJSONValue(value);\n }\n }\n }\n}\n\nfunction assertJSONArray(v: unknown[]): asserts v is JSONValue[] {\n for (const item of v) {\n assertJSONValue(item);\n }\n}\n\ninterface Path {\n push(key: string | number): void;\n pop(): void;\n}\n\n/**\n * Checks if a value is a JSON value. If there is a value that is not a JSON\n * value, the path parameter is updated to the path of the invalid value.\n */\nexport function isJSONValue(v: unknown, path: Path): v is JSONValue {\n switch (typeof v) {\n case 'boolean':\n case 'number':\n case 'string':\n return true;\n case 'object':\n if (v === null) {\n return true;\n }\n if (Array.isArray(v)) {\n return isJSONArray(v, path);\n }\n return objectIsJSONObject(v as Record<string, unknown>, path);\n }\n return false;\n}\n\nexport function isJSONObject(v: unknown, path: Path): v is JSONObject {\n if (typeof v !== 'object' || v === null) {\n return false;\n }\n return objectIsJSONObject(v as Record<string, unknown>, path);\n}\n\nfunction objectIsJSONObject(\n v: Record<string, unknown>,\n path: Path,\n): v is JSONObject {\n for (const k in v) {\n if (hasOwn(v, k)) {\n path.push(k);\n const value = v[k];\n if (value !== undefined && !isJSONValue(value, path)) {\n return false;\n }\n path.pop();\n }\n }\n return true;\n}\n\nfunction isJSONArray(v: unknown[], path: Path): v is JSONValue[] {\n for (let i = 0; i < v.length; i++) {\n path.push(i);\n if (!isJSONValue(v[i], path)) {\n return false;\n }\n path.pop();\n }\n return true;\n}\n\n/** Basic deep readonly type. It works for {@link JSONValue} types. */\nexport type DeepReadonly<T> = T extends\n | null\n | boolean\n | string\n | number\n | undefined\n ? T\n : {readonly [K in keyof T]: DeepReadonly<T[K]>};\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,SAAgB,UACd,GACA,GACS;CACT,IAAI,MAAM,GACR,OAAO;CAGT,IAAI,OAAO,MAAM,OAAO,GACtB,OAAO;CAGT,QAAQ,OAAO,GAAf;EACE,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;CACX;CAKA,IAAI;CAGJ,IAAI,MAAM,QAAQ,CAAC,GAAG;EACpB,IAAI,CAAC,MAAM,QAAQ,CAAC,GAClB,OAAO;EAET,IAAI,EAAE,WAAW,EAAE,QACjB,OAAO;EAET,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,GACvB,OAAO;EAGX,OAAO;CACT;CAEA,IAAI,MAAM,QAAQ,MAAM,MACtB,OAAO;CAGT,IAAI,MAAM,QAAQ,CAAC,GACjB,OAAO;CAIT,IAAI;CACJ,IAAI;CAYJ,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,GAChB,IAAI,OAAO,GAAG,GAAG,KAAK,EAAE,SAAS,KAAA,GAAW;EAC1C,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAC3B,OAAO;EAET;CACF;CAGF,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,GAChB,IAAI,OAAO,GAAG,GAAG,KAAK,EAAE,SAAS,KAAA,GAC/B;CAIJ,OAAO,UAAU;AACnB;AAEA,SAAgB,gBAAgB,GAAoC;CAClE,IAAI,QACF;CAEF,QAAQ,OAAO,GAAf;EACE,KAAK;EACL,KAAK;EACL,KAAK,UACH;EACF,KAAK;GACH,IAAI,MAAM,MACR;GAEF,IAAI,MAAM,QAAQ,CAAC,GACjB,OAAO,gBAAgB,CAAC;GAE1B,OAAO,yBAAyB,CAA4B;CAChE;CACA,iBAAiB,GAAG,YAAY;AAClC;AAEA,SAAgB,iBAAiB,GAAqC;CACpE,aAAa,CAAC;CACd,yBAAyB,CAAC;AAC5B;AAEA,SAAS,yBACP,GACyB;CACzB,KAAK,MAAM,KAAK,GACd,IAAI,OAAO,GAAG,CAAC,GAAG;EAChB,MAAM,QAAQ,EAAE;EAChB,IAAI,UAAU,KAAA,GACZ,gBAAgB,KAAK;CAEzB;AAEJ;AAEA,SAAS,gBAAgB,GAAwC;CAC/D,KAAK,MAAM,QAAQ,GACjB,gBAAgB,IAAI;AAExB;;;;;AAWA,SAAgB,YAAY,GAAY,MAA4B;CAClE,QAAQ,OAAO,GAAf;EACE,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;EACT,KAAK;GACH,IAAI,MAAM,MACR,OAAO;GAET,IAAI,MAAM,QAAQ,CAAC,GACjB,OAAO,YAAY,GAAG,IAAI;GAE5B,OAAO,mBAAmB,GAA8B,IAAI;CAChE;CACA,OAAO;AACT;AAEA,SAAgB,aAAa,GAAY,MAA6B;CACpE,IAAI,OAAO,MAAM,YAAY,MAAM,MACjC,OAAO;CAET,OAAO,mBAAmB,GAA8B,IAAI;AAC9D;AAEA,SAAS,mBACP,GACA,MACiB;CACjB,KAAK,MAAM,KAAK,GACd,IAAI,OAAO,GAAG,CAAC,GAAG;EAChB,KAAK,KAAK,CAAC;EACX,MAAM,QAAQ,EAAE;EAChB,IAAI,UAAU,KAAA,KAAa,CAAC,YAAY,OAAO,IAAI,GACjD,OAAO;EAET,KAAK,IAAI;CACX;CAEF,OAAO;AACT;AAEA,SAAS,YAAY,GAAc,MAA8B;CAC/D,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;EACjC,KAAK,KAAK,CAAC;EACX,IAAI,CAAC,YAAY,EAAE,IAAI,IAAI,GACzB,OAAO;EAET,KAAK,IAAI;CACX;CACA,OAAO;AACT"}
|
|
@@ -14,6 +14,10 @@ export declare class RingBuffer<T> {
|
|
|
14
14
|
constructor(initialCapacity?: number);
|
|
15
15
|
get size(): number;
|
|
16
16
|
push(value: T): void;
|
|
17
|
+
/** Returns the last element, or `undefined` if empty. */
|
|
18
|
+
last(): T | undefined;
|
|
19
|
+
/** Replaces the last element, or appends it if empty. */
|
|
20
|
+
replaceLast(value: T): void;
|
|
17
21
|
/** Removes and returns the front element, or `undefined` if empty. */
|
|
18
22
|
shift(): T | undefined;
|
|
19
23
|
/**
|
|
@@ -28,5 +32,9 @@ export declare class RingBuffer<T> {
|
|
|
28
32
|
* Removes and returns all elements in FIFO order, resetting the buffer.
|
|
29
33
|
*/
|
|
30
34
|
drain(): T[];
|
|
35
|
+
/** Copies all elements in FIFO order without removing them. */
|
|
36
|
+
toArray(): T[];
|
|
37
|
+
/** Removes all elements and releases extra capacity. */
|
|
38
|
+
clear(): void;
|
|
31
39
|
}
|
|
32
40
|
//# sourceMappingURL=ring-buffer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ring-buffer.d.ts","sourceRoot":"","sources":["../../../../shared/src/ring-buffer.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,qBAAa,UAAU,CAAC,CAAC;;gBAMX,eAAe,SAA2B;IAKtD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAQpB,sEAAsE;IACtE,KAAK,IAAI,CAAC,GAAG,SAAS;IAqBtB;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM;IAsBxB;;OAEG;IACH,KAAK,IAAI,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"ring-buffer.d.ts","sourceRoot":"","sources":["../../../../shared/src/ring-buffer.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,qBAAa,UAAU,CAAC,CAAC;;gBAMX,eAAe,SAA2B;IAKtD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAQpB,yDAAyD;IACzD,IAAI,IAAI,CAAC,GAAG,SAAS;IAMrB,yDAAyD;IACzD,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAQ3B,sEAAsE;IACtE,KAAK,IAAI,CAAC,GAAG,SAAS;IAqBtB;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM;IAsBxB;;OAEG;IACH,KAAK,IAAI,CAAC,EAAE;IAQZ,+DAA+D;IAC/D,OAAO,IAAI,CAAC,EAAE;IAQd,wDAAwD;IACxD,KAAK,IAAI,IAAI;CA+Cd"}
|
|
@@ -29,6 +29,15 @@ var RingBuffer = class {
|
|
|
29
29
|
this.#buffer[(this.#head + this.#size) % this.#capacity] = value;
|
|
30
30
|
this.#size++;
|
|
31
31
|
}
|
|
32
|
+
/** Returns the last element, or `undefined` if empty. */
|
|
33
|
+
last() {
|
|
34
|
+
return this.#size === 0 ? void 0 : this.#buffer[(this.#head + this.#size - 1) % this.#capacity];
|
|
35
|
+
}
|
|
36
|
+
/** Replaces the last element, or appends it if empty. */
|
|
37
|
+
replaceLast(value) {
|
|
38
|
+
if (this.#size === 0) this.push(value);
|
|
39
|
+
else this.#buffer[(this.#head + this.#size - 1) % this.#capacity] = value;
|
|
40
|
+
}
|
|
32
41
|
/** Removes and returns the front element, or `undefined` if empty. */
|
|
33
42
|
shift() {
|
|
34
43
|
if (this.#size === 0) return;
|
|
@@ -65,12 +74,24 @@ var RingBuffer = class {
|
|
|
65
74
|
* Removes and returns all elements in FIFO order, resetting the buffer.
|
|
66
75
|
*/
|
|
67
76
|
drain() {
|
|
77
|
+
const result = this.toArray();
|
|
78
|
+
this.#head = 0;
|
|
79
|
+
this.#size = 0;
|
|
80
|
+
this.#buffer = Array.from({ length: this.#capacity });
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/** Copies all elements in FIFO order without removing them. */
|
|
84
|
+
toArray() {
|
|
68
85
|
const result = Array.from({ length: this.#size });
|
|
69
86
|
for (let i = 0; i < this.#size; i++) result[i] = this.#buffer[(this.#head + i) % this.#capacity];
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/** Removes all elements and releases extra capacity. */
|
|
90
|
+
clear() {
|
|
70
91
|
this.#head = 0;
|
|
71
92
|
this.#size = 0;
|
|
93
|
+
this.#capacity = MIN_CAPACITY;
|
|
72
94
|
this.#buffer = Array.from({ length: this.#capacity });
|
|
73
|
-
return result;
|
|
74
95
|
}
|
|
75
96
|
/**
|
|
76
97
|
* Grows the buffer in place by doubling its length and relocating
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ring-buffer.js","names":["#capacity","#buffer","#size","#grow","#head","#shrink"],"sources":["../../../../shared/src/ring-buffer.ts"],"sourcesContent":["const DEFAULT_INITIAL_CAPACITY = 16;\nconst MIN_CAPACITY = 16;\n\n/**\n * A FIFO ring buffer backed by a circular array. Provides:\n *\n * - `push()` — O(1) amortized (O(n) on resize)\n * - `shift()` — O(1)\n * - `size` — O(1)\n * - `drain()` — O(n)\n *\n * The buffer grows (doubles) when full and shrinks (halves) when\n * utilization drops below 25%, bounded by {@link MIN_CAPACITY}.\n */\nexport class RingBuffer<T> {\n #buffer: (T | undefined)[];\n #head = 0;\n #size = 0;\n #capacity: number;\n\n constructor(initialCapacity = DEFAULT_INITIAL_CAPACITY) {\n this.#capacity = Math.max(MIN_CAPACITY, initialCapacity);\n this.#buffer = Array.from({length: initialCapacity});\n }\n\n get size(): number {\n return this.#size;\n }\n\n push(value: T): void {\n if (this.#size === this.#capacity) {\n this.#grow();\n }\n this.#buffer[(this.#head + this.#size) % this.#capacity] = value;\n this.#size++;\n }\n\n /** Removes and returns the front element, or `undefined` if empty. */\n shift(): T | undefined {\n if (this.#size === 0) {\n return undefined;\n }\n const value = this.#buffer[this.#head];\n this.#buffer[this.#head] = undefined; // allow GC of removed element\n this.#head = (this.#head + 1) % this.#capacity;\n this.#size--;\n\n // Shrink when utilization drops below 25%, keeping a minimum capacity.\n if (\n this.#size > 0 &&\n this.#size < this.#capacity >> 2 &&\n this.#capacity > MIN_CAPACITY\n ) {\n this.#shrink();\n }\n\n return value;\n }\n\n /**\n * Removes all elements matching `value` (by identity `===`).\n *\n * This is O(n) — suitable for rare operations like cancellation.\n *\n * @returns The number of elements removed.\n */\n delete(value: T): number {\n if (this.#size === 0) {\n return 0;\n }\n // Rebuild the buffer without matching elements.\n const newBuffer: (T | undefined)[] = Array.from({length: this.#capacity});\n let newSize = 0;\n let count = 0;\n for (let i = 0; i < this.#size; i++) {\n const item = this.#buffer[(this.#head + i) % this.#capacity];\n if (item === value) {\n count++;\n } else {\n newBuffer[newSize++] = item;\n }\n }\n this.#buffer = newBuffer;\n this.#head = 0;\n this.#size = newSize;\n return count;\n }\n\n /**\n * Removes and returns all elements in FIFO order, resetting the buffer.\n */\n drain(): T[] {\n const result = Array.from<T>({length: this.#size});\n for (let i = 0; i < this.#size; i++) {\n result[i] = this.#buffer[(this.#head + i) % this.#capacity] as T;\n }\n this.#head = 0;\n this.#size = 0;\n this.#buffer = Array.from({length: this.#capacity});\n
|
|
1
|
+
{"version":3,"file":"ring-buffer.js","names":["#capacity","#buffer","#size","#grow","#head","#shrink"],"sources":["../../../../shared/src/ring-buffer.ts"],"sourcesContent":["const DEFAULT_INITIAL_CAPACITY = 16;\nconst MIN_CAPACITY = 16;\n\n/**\n * A FIFO ring buffer backed by a circular array. Provides:\n *\n * - `push()` — O(1) amortized (O(n) on resize)\n * - `shift()` — O(1)\n * - `size` — O(1)\n * - `drain()` — O(n)\n *\n * The buffer grows (doubles) when full and shrinks (halves) when\n * utilization drops below 25%, bounded by {@link MIN_CAPACITY}.\n */\nexport class RingBuffer<T> {\n #buffer: (T | undefined)[];\n #head = 0;\n #size = 0;\n #capacity: number;\n\n constructor(initialCapacity = DEFAULT_INITIAL_CAPACITY) {\n this.#capacity = Math.max(MIN_CAPACITY, initialCapacity);\n this.#buffer = Array.from({length: initialCapacity});\n }\n\n get size(): number {\n return this.#size;\n }\n\n push(value: T): void {\n if (this.#size === this.#capacity) {\n this.#grow();\n }\n this.#buffer[(this.#head + this.#size) % this.#capacity] = value;\n this.#size++;\n }\n\n /** Returns the last element, or `undefined` if empty. */\n last(): T | undefined {\n return this.#size === 0\n ? undefined\n : this.#buffer[(this.#head + this.#size - 1) % this.#capacity];\n }\n\n /** Replaces the last element, or appends it if empty. */\n replaceLast(value: T): void {\n if (this.#size === 0) {\n this.push(value);\n } else {\n this.#buffer[(this.#head + this.#size - 1) % this.#capacity] = value;\n }\n }\n\n /** Removes and returns the front element, or `undefined` if empty. */\n shift(): T | undefined {\n if (this.#size === 0) {\n return undefined;\n }\n const value = this.#buffer[this.#head];\n this.#buffer[this.#head] = undefined; // allow GC of removed element\n this.#head = (this.#head + 1) % this.#capacity;\n this.#size--;\n\n // Shrink when utilization drops below 25%, keeping a minimum capacity.\n if (\n this.#size > 0 &&\n this.#size < this.#capacity >> 2 &&\n this.#capacity > MIN_CAPACITY\n ) {\n this.#shrink();\n }\n\n return value;\n }\n\n /**\n * Removes all elements matching `value` (by identity `===`).\n *\n * This is O(n) — suitable for rare operations like cancellation.\n *\n * @returns The number of elements removed.\n */\n delete(value: T): number {\n if (this.#size === 0) {\n return 0;\n }\n // Rebuild the buffer without matching elements.\n const newBuffer: (T | undefined)[] = Array.from({length: this.#capacity});\n let newSize = 0;\n let count = 0;\n for (let i = 0; i < this.#size; i++) {\n const item = this.#buffer[(this.#head + i) % this.#capacity];\n if (item === value) {\n count++;\n } else {\n newBuffer[newSize++] = item;\n }\n }\n this.#buffer = newBuffer;\n this.#head = 0;\n this.#size = newSize;\n return count;\n }\n\n /**\n * Removes and returns all elements in FIFO order, resetting the buffer.\n */\n drain(): T[] {\n const result = this.toArray();\n this.#head = 0;\n this.#size = 0;\n this.#buffer = Array.from({length: this.#capacity});\n return result;\n }\n\n /** Copies all elements in FIFO order without removing them. */\n toArray(): T[] {\n const result = Array.from<T>({length: this.#size});\n for (let i = 0; i < this.#size; i++) {\n result[i] = this.#buffer[(this.#head + i) % this.#capacity] as T;\n }\n return result;\n }\n\n /** Removes all elements and releases extra capacity. */\n clear(): void {\n this.#head = 0;\n this.#size = 0;\n this.#capacity = MIN_CAPACITY;\n this.#buffer = Array.from({length: this.#capacity});\n }\n\n /**\n * Grows the buffer in place by doubling its length and relocating\n * only the wrapped-around elements (those before #head) into the\n * newly available space. No new array is allocated.\n */\n #grow(): void {\n const oldLen = this.#buffer.length;\n const newLen = oldLen * 2;\n this.#buffer.length = newLen;\n this.#capacity = newLen;\n\n // Copy wrapped-around elements (indices 0..head-1) into the new space.\n for (let i = 0; i < this.#head; i++) {\n this.#buffer[oldLen + i] = this.#buffer[i];\n this.#buffer[i] = undefined; // ensure gc when the element is removed\n }\n }\n\n /**\n * Shrinks the buffer in place by halving its length. Elements whose\n * indices fall in the removed half are relocated into the lower half,\n * then the array is truncated.\n */\n #shrink(): void {\n const oldLen = this.#buffer.length;\n const newLen = Math.max(MIN_CAPACITY, oldLen >> 1);\n\n // Move elements that sit in the upper half down by newLen.\n const left = Math.max(newLen, this.#head);\n const right = Math.min(this.#head + this.#size, oldLen);\n for (let i = left; i < right; i++) {\n this.#buffer[i - newLen] = this.#buffer[i];\n }\n\n if (this.#head >= newLen) {\n this.#head -= newLen;\n }\n this.#buffer.length = newLen;\n this.#capacity = newLen;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two arithmetic steps at the core of xxHash32, shared by everything here
|
|
3
|
+
* that builds a hash out of it.
|
|
4
|
+
*
|
|
5
|
+
* xxHash32 is defined over a byte stream, but its round and avalanche steps
|
|
6
|
+
* are just 32-bit word arithmetic and are reusable on their own. `hash.ts`
|
|
7
|
+
* feeds them UTF-8 bytes to produce digests bit-compatible with the reference
|
|
8
|
+
* implementation; `zero-protocol`'s AST hash feeds them words taken straight
|
|
9
|
+
* from a structure, never materializing bytes at all. Same mixing, different
|
|
10
|
+
* ingestion.
|
|
11
|
+
*
|
|
12
|
+
* These constants and steps are frozen: both callers' digests are persisted or
|
|
13
|
+
* compared against stored values, so changing anything here silently changes
|
|
14
|
+
* hashes across the system. `hash.test.ts` pins the byte-oriented side against
|
|
15
|
+
* the js-xxhash reference; `query-hash-visitor.test.ts` pins the word-oriented
|
|
16
|
+
* side against a corpus.
|
|
17
|
+
*/
|
|
18
|
+
export declare const PRIME32_1 = 2654435761;
|
|
19
|
+
export declare const PRIME32_2 = 2246822519;
|
|
20
|
+
export declare const PRIME32_3 = 3266489917;
|
|
21
|
+
export declare const PRIME32_4 = 668265263;
|
|
22
|
+
export declare const PRIME32_5 = 374761393;
|
|
23
|
+
/**
|
|
24
|
+
* One xxHash32 round: fold a 32-bit word into an accumulator.
|
|
25
|
+
*
|
|
26
|
+
* `Math.imul` is the whole multiply. Reference JS ports write it as two 16-bit
|
|
27
|
+
* half-multiplies recombined; that computes the same low 32 bits by a longer
|
|
28
|
+
* route, which is why moving to `Math.imul` left every digest unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export declare function round32(acc: number, word: number): number;
|
|
31
|
+
/**
|
|
32
|
+
* xxHash32's final mix, spreading every input bit across the whole word.
|
|
33
|
+
* Returns an unsigned 32-bit number.
|
|
34
|
+
*/
|
|
35
|
+
export declare function avalanche32(acc: number): number;
|
|
36
|
+
//# sourceMappingURL=xxhash32.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xxhash32.d.ts","sourceRoot":"","sources":["../../../../shared/src/xxhash32.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,SAAS,aAAa,CAAC;AACpC,eAAO,MAAM,SAAS,aAAa,CAAC;AACpC,eAAO,MAAM,SAAS,aAAa,CAAC;AACpC,eAAO,MAAM,SAAS,YAAY,CAAC;AACnC,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region ../shared/src/xxhash32.ts
|
|
2
|
+
/**
|
|
3
|
+
* The two arithmetic steps at the core of xxHash32, shared by everything here
|
|
4
|
+
* that builds a hash out of it.
|
|
5
|
+
*
|
|
6
|
+
* xxHash32 is defined over a byte stream, but its round and avalanche steps
|
|
7
|
+
* are just 32-bit word arithmetic and are reusable on their own. `hash.ts`
|
|
8
|
+
* feeds them UTF-8 bytes to produce digests bit-compatible with the reference
|
|
9
|
+
* implementation; `zero-protocol`'s AST hash feeds them words taken straight
|
|
10
|
+
* from a structure, never materializing bytes at all. Same mixing, different
|
|
11
|
+
* ingestion.
|
|
12
|
+
*
|
|
13
|
+
* These constants and steps are frozen: both callers' digests are persisted or
|
|
14
|
+
* compared against stored values, so changing anything here silently changes
|
|
15
|
+
* hashes across the system. `hash.test.ts` pins the byte-oriented side against
|
|
16
|
+
* the js-xxhash reference; `query-hash-visitor.test.ts` pins the word-oriented
|
|
17
|
+
* side against a corpus.
|
|
18
|
+
*/
|
|
19
|
+
var PRIME32_1 = 2654435761;
|
|
20
|
+
var PRIME32_2 = 2246822519;
|
|
21
|
+
var PRIME32_3 = 3266489917;
|
|
22
|
+
var PRIME32_4 = 668265263;
|
|
23
|
+
var PRIME32_5 = 374761393;
|
|
24
|
+
/**
|
|
25
|
+
* One xxHash32 round: fold a 32-bit word into an accumulator.
|
|
26
|
+
*
|
|
27
|
+
* `Math.imul` is the whole multiply. Reference JS ports write it as two 16-bit
|
|
28
|
+
* half-multiplies recombined; that computes the same low 32 bits by a longer
|
|
29
|
+
* route, which is why moving to `Math.imul` left every digest unchanged.
|
|
30
|
+
*/
|
|
31
|
+
function round32(acc, word) {
|
|
32
|
+
acc = acc + Math.imul(word, PRIME32_2) | 0;
|
|
33
|
+
acc = acc << 13 | acc >>> 19;
|
|
34
|
+
return Math.imul(acc, PRIME32_1);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* xxHash32's final mix, spreading every input bit across the whole word.
|
|
38
|
+
* Returns an unsigned 32-bit number.
|
|
39
|
+
*/
|
|
40
|
+
function avalanche32(acc) {
|
|
41
|
+
acc ^= acc >>> 15;
|
|
42
|
+
acc = Math.imul(acc, PRIME32_2);
|
|
43
|
+
acc ^= acc >>> 13;
|
|
44
|
+
acc = Math.imul(acc, PRIME32_3);
|
|
45
|
+
acc ^= acc >>> 16;
|
|
46
|
+
return acc >>> 0;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { PRIME32_1, PRIME32_2, PRIME32_3, PRIME32_4, PRIME32_5, avalanche32, round32 };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=xxhash32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xxhash32.js","names":[],"sources":["../../../../shared/src/xxhash32.ts"],"sourcesContent":["/**\n * The two arithmetic steps at the core of xxHash32, shared by everything here\n * that builds a hash out of it.\n *\n * xxHash32 is defined over a byte stream, but its round and avalanche steps\n * are just 32-bit word arithmetic and are reusable on their own. `hash.ts`\n * feeds them UTF-8 bytes to produce digests bit-compatible with the reference\n * implementation; `zero-protocol`'s AST hash feeds them words taken straight\n * from a structure, never materializing bytes at all. Same mixing, different\n * ingestion.\n *\n * These constants and steps are frozen: both callers' digests are persisted or\n * compared against stored values, so changing anything here silently changes\n * hashes across the system. `hash.test.ts` pins the byte-oriented side against\n * the js-xxhash reference; `query-hash-visitor.test.ts` pins the word-oriented\n * side against a corpus.\n */\n\nexport const PRIME32_1 = 2654435761;\nexport const PRIME32_2 = 2246822519;\nexport const PRIME32_3 = 3266489917;\nexport const PRIME32_4 = 668265263;\nexport const PRIME32_5 = 374761393;\n\n/**\n * One xxHash32 round: fold a 32-bit word into an accumulator.\n *\n * `Math.imul` is the whole multiply. Reference JS ports write it as two 16-bit\n * half-multiplies recombined; that computes the same low 32 bits by a longer\n * route, which is why moving to `Math.imul` left every digest unchanged.\n */\nexport function round32(acc: number, word: number): number {\n acc = (acc + Math.imul(word, PRIME32_2)) | 0;\n acc = (acc << 13) | (acc >>> 19);\n return Math.imul(acc, PRIME32_1);\n}\n\n/**\n * xxHash32's final mix, spreading every input bit across the whole word.\n * Returns an unsigned 32-bit number.\n */\nexport function avalanche32(acc: number): number {\n acc ^= acc >>> 15;\n acc = Math.imul(acc, PRIME32_2);\n acc ^= acc >>> 13;\n acc = Math.imul(acc, PRIME32_3);\n acc ^= acc >>> 16;\n return acc >>> 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAa,YAAY;AACzB,IAAa,YAAY;AACzB,IAAa,YAAY;AACzB,IAAa,YAAY;AACzB,IAAa,YAAY;;;;;;;;AASzB,SAAgB,QAAQ,KAAa,MAAsB;CACzD,MAAO,MAAM,KAAK,KAAK,MAAM,SAAS,IAAK;CAC3C,MAAO,OAAO,KAAO,QAAQ;CAC7B,OAAO,KAAK,KAAK,KAAK,SAAS;AACjC;;;;;AAMA,SAAgB,YAAY,KAAqB;CAC/C,OAAO,QAAQ;CACf,MAAM,KAAK,KAAK,KAAK,SAAS;CAC9B,OAAO,QAAQ;CACf,MAAM,KAAK,KAAK,KAAK,SAAS;CAC9B,OAAO,QAAQ;CACf,OAAO,QAAQ;AACjB"}
|
package/out/z2s/src/compiler.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../shared/src/asserts.js";
|
|
2
2
|
import { hasOwn } from "../../shared/src/has-own.js";
|
|
3
3
|
import "../../shared/src/json.js";
|
|
4
|
-
import { last, zip } from "../../shared/src/arrays.js";
|
|
5
4
|
import { must } from "../../shared/src/must.js";
|
|
6
5
|
import { clientToServer } from "../../zero-schema/src/name-mapper.js";
|
|
7
6
|
import { completeOrdering } from "../../zql/src/query/complete-ordering.js";
|
|
7
|
+
import { last, zip } from "../../shared/src/arrays.js";
|
|
8
8
|
import { parse } from "../../shared/src/bigint-json.js";
|
|
9
9
|
import { sql, sqlConvertColumnArg, sqlConvertPluralLiteralArg, sqlConvertSingularLiteralArg } from "./sql.js";
|
|
10
10
|
//#region ../z2s/src/compiler.ts
|
package/out/zero/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var package_default = {
|
|
2
2
|
name: "@rocicorp/zero",
|
|
3
|
-
version: "1.10.0-canary.
|
|
3
|
+
version: "1.10.0-canary.22",
|
|
4
4
|
description: "Zero is a web framework for serverless web development.",
|
|
5
5
|
homepage: "https://zero.rocicorp.dev",
|
|
6
6
|
bugs: { "url": "https://bugs.rocicorp.dev" },
|
package/out/zero/package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.10.0-canary.
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.10.0-canary.22\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"license\": \"Apache-2.0\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"bin\": {\n \"analyze-query\": \"./out/zero/src/analyze-query.js\",\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\",\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ],\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./analyze\": {\n \"types\": \"./out/zero/src/analyze.d.ts\",\n \"default\": \"./out/zero/src/analyze.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/kysely\": {\n \"types\": \"./out/zero/src/adapters/kysely.d.ts\",\n \"default\": \"./out/zero/src/adapters/kysely.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --quiet --config ../../oxlint.config.ts src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\",\n \"fmt\": \"oxfmt .\",\n \"check-fmt\": \"oxfmt --check .\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.3.11\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.39.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.0.0\",\n \"@fastify/websocket\": \"^11.0.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/api-logs\": \"^0.218.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.76.0\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.218.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-metrics\": \"^2.7.1\",\n \"@opentelemetry/sdk-node\": \"^0.218.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.41.1\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^6.1.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.1.4\",\n \"@standard-schema/spec\": \"^1.0.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.5.12\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk-template\": \"^1.1.0\",\n \"chokidar\": \"^4.0.1\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.1\",\n \"command-line-usage\": \"^7.0.3\",\n \"compare-utf8\": \"^0.2.3\",\n \"defu\": \"^6.1.4\",\n \"eventemitter3\": \"^5.0.1\",\n \"fastify\": \"^5.0.0\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.9.3\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.0\",\n \"nanoid\": \"^5.1.2\",\n \"oxfmt\": \"^0.45.0\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.5.4\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.18.1\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"@vitest/runner\": \"^4.1.6\",\n \"analyze-query\": \"workspace:*\",\n \"ast-to-zql\": \"workspace:*\",\n \"expo-sqlite\": \">=15\",\n \"oxc-parser\": \"^0.129.0\",\n \"replicache\": \"workspace:*\",\n \"shared\": \"workspace:*\",\n \"syncpack\": \"^14.3.0\",\n \"typedoc\": \"^0.28.17\",\n \"typedoc-plugin-markdown\": \"^4.10.0\",\n \"typescript\": \"~6.0.2\",\n \"vite\": \"^8.0.13\",\n \"vitest\": \"^4.1.6\",\n \"zero-cache\": \"workspace:*\",\n \"zero-client\": \"workspace:*\",\n \"zero-pg\": \"workspace:*\",\n \"zero-react\": \"workspace:*\",\n \"zero-server\": \"workspace:*\",\n \"zero-solid\": \"workspace:*\",\n \"zqlite\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@11.11.0\"\n}\n"],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { hashOfAST } from "../../../zero-protocol/src/query-hash.js";
|
|
2
1
|
import { simplifyCondition } from "../../../zql/src/query/expression.js";
|
|
2
|
+
import { hashOfAST } from "../../../zero-protocol/src/query-hash.js";
|
|
3
3
|
import { bindStaticParameters } from "../../../zql/src/builder/builder.js";
|
|
4
4
|
//#region ../zero-cache/src/auth/read-authorizer.ts
|
|
5
5
|
/**
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { hashOfAST } from "../../../zero-protocol/src/query-hash.js";
|
|
2
1
|
import { getErrorMessage } from "../../../shared/src/error.js";
|
|
3
2
|
import { TransformFailed } from "../../../zero-protocol/src/error-kind-enum.js";
|
|
4
3
|
import { ZeroCache } from "../../../zero-protocol/src/error-origin-enum.js";
|
|
5
4
|
import { Internal } from "../../../zero-protocol/src/error-reason-enum.js";
|
|
6
5
|
import { isProtocolError } from "../../../zero-protocol/src/error.js";
|
|
7
6
|
import "../../../zero-protocol/src/custom-queries.js";
|
|
7
|
+
import { hashOfAST } from "../../../zero-protocol/src/query-hash.js";
|
|
8
8
|
import { startAsyncSpan } from "../../../otel/src/span.js";
|
|
9
9
|
import { TimedCache } from "../../../shared/src/cache.js";
|
|
10
10
|
import { sortedEntries } from "../../../shared/src/sorted-entries.js";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
-
import {
|
|
2
|
+
import { hashNameAndArgs } from "../../../zero-protocol/src/query-hash-visitor.js";
|
|
3
3
|
import { mapValues } from "../../../shared/src/objects.js";
|
|
4
4
|
import { isServerMetric } from "../../../zql/src/query/metrics-delegate.js";
|
|
5
|
+
import "../../../zero-protocol/src/query-hash.js";
|
|
5
6
|
import { TDigest } from "../../../shared/src/tdigest.js";
|
|
6
7
|
import { isDevelopmentMode } from "../config/normalize.js";
|
|
7
8
|
import { ProtocolErrorWithLevel } from "../types/error-with-level.js";
|
|
@@ -78,7 +79,7 @@ var InspectorDelegate = class {
|
|
|
78
79
|
async transformCustomQuery(name, args, ctx) {
|
|
79
80
|
assert(this.#customQueryTransformer, "Custom query transformation requested but no CustomQueryTransformer is configured");
|
|
80
81
|
const queries = [{
|
|
81
|
-
id:
|
|
82
|
+
id: hashNameAndArgs(name, args),
|
|
82
83
|
type: "custom",
|
|
83
84
|
name,
|
|
84
85
|
args,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-delegate.js","names":["#globalMetrics","#perQueryHydrateMs","#perQueryUpdateMetrics","#queryIDToAST","#customQueryTransformer"],"sources":["../../../../../zero-cache/src/server/inspector-delegate.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {mapValues} from '../../../shared/src/objects.ts';\nimport {TDigest} from '../../../shared/src/tdigest.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {QueryServerMetrics as QueryServerMetricsJSON} from '../../../zero-protocol/src/inspect-down.ts';\nimport {hashOfNameAndArgs} from '../../../zero-protocol/src/query-hash.ts';\nimport {\n isServerMetric,\n type MetricMap,\n type MetricsDelegate,\n} from '../../../zql/src/query/metrics-delegate.ts';\nimport {isDevelopmentMode} from '../config/normalize.ts';\nimport type {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport type {ConnectionContext} from '../services/view-syncer/connection-context-manager.ts';\nimport type {CustomQueryRecord} from '../services/view-syncer/schema/types.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\n\n/**\n * Server-side metrics collected for queries during materialization and update.\n * These metrics are reported via the inspector and complement client-side metrics.\n * Used for the global aggregate (all queries combined).\n */\nexport type ServerMetrics = {\n 'query-materialization-server': TDigest;\n 'query-update-server': TDigest;\n};\n\ntype ClientGroupID = string;\n\n/**\n * Set of authenticated client group IDs. We keep this outside of the class to\n * share this state across all instances of the InspectorDelegate.\n */\nconst authenticatedClientGroupIDs = new Set<ClientGroupID>();\n\nexport class InspectorDelegate implements MetricsDelegate {\n readonly #globalMetrics: ServerMetrics = newMetrics();\n readonly #perQueryHydrateMs = new Map<string, number>();\n readonly #perQueryUpdateMetrics = new Map<string, TDigest>();\n readonly #queryIDToAST: Map<string, AST> = new Map();\n readonly #customQueryTransformer: CustomQueryTransformer | undefined;\n\n constructor(customQueryTransformer: CustomQueryTransformer | undefined) {\n this.#customQueryTransformer = customQueryTransformer;\n }\n\n addMetric<K extends keyof MetricMap>(\n metric: K,\n value: number,\n ...args: MetricMap[K]\n ): void {\n assert(isServerMetric(metric), `Invalid server metric: ${metric}`);\n const queryID = args[0];\n if (metric === 'query-materialization-server') {\n this.#perQueryHydrateMs.set(queryID, value);\n } else {\n let digest = this.#perQueryUpdateMetrics.get(queryID);\n if (!digest) {\n digest = new TDigest();\n this.#perQueryUpdateMetrics.set(queryID, digest);\n }\n digest.add(value);\n }\n this.#globalMetrics[metric].add(value);\n }\n\n getMetricsJSONForQuery(queryID: string): QueryServerMetricsJSON | null {\n const hydrateMs = this.#perQueryHydrateMs.get(queryID);\n const updateMetrics = this.#perQueryUpdateMetrics.get(queryID);\n if (hydrateMs === undefined && updateMetrics === undefined) {\n return null;\n }\n return {\n 'query-hydration-server-ms': hydrateMs,\n 'query-update-server': (updateMetrics ?? new TDigest()).toJSON(),\n };\n }\n\n getMetricsJSON() {\n return mapValues(this.#globalMetrics, v => v.toJSON());\n }\n\n getASTForQuery(queryID: string): AST | undefined {\n return this.#queryIDToAST.get(queryID);\n }\n\n removeQuery(queryID: string): void {\n this.#perQueryHydrateMs.delete(queryID);\n this.#perQueryUpdateMetrics.delete(queryID);\n this.#queryIDToAST.delete(queryID);\n }\n\n addQuery(queryID: string, ast: AST): void {\n this.#queryIDToAST.set(queryID, ast);\n }\n\n /**\n * Check if the client is authenticated. We only require authentication once\n * per \"worker\".\n */\n isAuthenticated(clientGroupID: ClientGroupID): boolean {\n return (\n isDevelopmentMode() || authenticatedClientGroupIDs.has(clientGroupID)\n );\n }\n\n setAuthenticated(clientGroupID: ClientGroupID): void {\n authenticatedClientGroupIDs.add(clientGroupID);\n }\n\n clearAuthenticated(clientGroupID: ClientGroupID) {\n authenticatedClientGroupIDs.delete(clientGroupID);\n }\n\n /**\n * Transforms a single custom query by name and args using the configured\n * CustomQueryTransformer. This is primarily used by the inspector to transform\n * queries for analysis.\n */\n async transformCustomQuery(\n name: string,\n args: readonly ReadonlyJSONValue[],\n ctx: ConnectionContext,\n ): Promise<AST> {\n assert(\n this.#customQueryTransformer,\n 'Custom query transformation requested but no CustomQueryTransformer is configured',\n );\n\n // Create a fake CustomQueryRecord for the single query\n const queryID = hashOfNameAndArgs(name, args);\n const queries: CustomQueryRecord[] = [\n {\n id: queryID,\n type: 'custom',\n name,\n args,\n clientState: {},\n },\n ];\n\n const results = await this.#customQueryTransformer.transform(ctx, queries);\n\n if ('kind' in results.result) {\n throw new ProtocolErrorWithLevel(results.result, 'warn');\n }\n\n const result = results.result[0];\n if (!result) {\n throw new Error('No transformation result returned');\n }\n\n if ('error' in result) {\n const message =\n result.message ?? 'Unknown application error from custom query';\n throw new Error(\n `Error transforming custom query ${name} (${result.error}): ${message} ${JSON.stringify(result.details)}`,\n );\n }\n\n return result.transformedAst;\n }\n}\n\nfunction newMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"inspector-delegate.js","names":["#globalMetrics","#perQueryHydrateMs","#perQueryUpdateMetrics","#queryIDToAST","#customQueryTransformer"],"sources":["../../../../../zero-cache/src/server/inspector-delegate.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {mapValues} from '../../../shared/src/objects.ts';\nimport {TDigest} from '../../../shared/src/tdigest.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {QueryServerMetrics as QueryServerMetricsJSON} from '../../../zero-protocol/src/inspect-down.ts';\nimport {hashOfNameAndArgs} from '../../../zero-protocol/src/query-hash.ts';\nimport {\n isServerMetric,\n type MetricMap,\n type MetricsDelegate,\n} from '../../../zql/src/query/metrics-delegate.ts';\nimport {isDevelopmentMode} from '../config/normalize.ts';\nimport type {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport type {ConnectionContext} from '../services/view-syncer/connection-context-manager.ts';\nimport type {CustomQueryRecord} from '../services/view-syncer/schema/types.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\n\n/**\n * Server-side metrics collected for queries during materialization and update.\n * These metrics are reported via the inspector and complement client-side metrics.\n * Used for the global aggregate (all queries combined).\n */\nexport type ServerMetrics = {\n 'query-materialization-server': TDigest;\n 'query-update-server': TDigest;\n};\n\ntype ClientGroupID = string;\n\n/**\n * Set of authenticated client group IDs. We keep this outside of the class to\n * share this state across all instances of the InspectorDelegate.\n */\nconst authenticatedClientGroupIDs = new Set<ClientGroupID>();\n\nexport class InspectorDelegate implements MetricsDelegate {\n readonly #globalMetrics: ServerMetrics = newMetrics();\n readonly #perQueryHydrateMs = new Map<string, number>();\n readonly #perQueryUpdateMetrics = new Map<string, TDigest>();\n readonly #queryIDToAST: Map<string, AST> = new Map();\n readonly #customQueryTransformer: CustomQueryTransformer | undefined;\n\n constructor(customQueryTransformer: CustomQueryTransformer | undefined) {\n this.#customQueryTransformer = customQueryTransformer;\n }\n\n addMetric<K extends keyof MetricMap>(\n metric: K,\n value: number,\n ...args: MetricMap[K]\n ): void {\n assert(isServerMetric(metric), `Invalid server metric: ${metric}`);\n const queryID = args[0];\n if (metric === 'query-materialization-server') {\n this.#perQueryHydrateMs.set(queryID, value);\n } else {\n let digest = this.#perQueryUpdateMetrics.get(queryID);\n if (!digest) {\n digest = new TDigest();\n this.#perQueryUpdateMetrics.set(queryID, digest);\n }\n digest.add(value);\n }\n this.#globalMetrics[metric].add(value);\n }\n\n getMetricsJSONForQuery(queryID: string): QueryServerMetricsJSON | null {\n const hydrateMs = this.#perQueryHydrateMs.get(queryID);\n const updateMetrics = this.#perQueryUpdateMetrics.get(queryID);\n if (hydrateMs === undefined && updateMetrics === undefined) {\n return null;\n }\n return {\n 'query-hydration-server-ms': hydrateMs,\n 'query-update-server': (updateMetrics ?? new TDigest()).toJSON(),\n };\n }\n\n getMetricsJSON() {\n return mapValues(this.#globalMetrics, v => v.toJSON());\n }\n\n getASTForQuery(queryID: string): AST | undefined {\n return this.#queryIDToAST.get(queryID);\n }\n\n removeQuery(queryID: string): void {\n this.#perQueryHydrateMs.delete(queryID);\n this.#perQueryUpdateMetrics.delete(queryID);\n this.#queryIDToAST.delete(queryID);\n }\n\n addQuery(queryID: string, ast: AST): void {\n this.#queryIDToAST.set(queryID, ast);\n }\n\n /**\n * Check if the client is authenticated. We only require authentication once\n * per \"worker\".\n */\n isAuthenticated(clientGroupID: ClientGroupID): boolean {\n return (\n isDevelopmentMode() || authenticatedClientGroupIDs.has(clientGroupID)\n );\n }\n\n setAuthenticated(clientGroupID: ClientGroupID): void {\n authenticatedClientGroupIDs.add(clientGroupID);\n }\n\n clearAuthenticated(clientGroupID: ClientGroupID) {\n authenticatedClientGroupIDs.delete(clientGroupID);\n }\n\n /**\n * Transforms a single custom query by name and args using the configured\n * CustomQueryTransformer. This is primarily used by the inspector to transform\n * queries for analysis.\n */\n async transformCustomQuery(\n name: string,\n args: readonly ReadonlyJSONValue[],\n ctx: ConnectionContext,\n ): Promise<AST> {\n assert(\n this.#customQueryTransformer,\n 'Custom query transformation requested but no CustomQueryTransformer is configured',\n );\n\n // Create a fake CustomQueryRecord for the single query\n const queryID = hashOfNameAndArgs(name, args);\n const queries: CustomQueryRecord[] = [\n {\n id: queryID,\n type: 'custom',\n name,\n args,\n clientState: {},\n },\n ];\n\n const results = await this.#customQueryTransformer.transform(ctx, queries);\n\n if ('kind' in results.result) {\n throw new ProtocolErrorWithLevel(results.result, 'warn');\n }\n\n const result = results.result[0];\n if (!result) {\n throw new Error('No transformation result returned');\n }\n\n if ('error' in result) {\n const message =\n result.message ?? 'Unknown application error from custom query';\n throw new Error(\n `Error transforming custom query ${name} (${result.error}): ${message} ${JSON.stringify(result.details)}`,\n );\n }\n\n return result.transformedAst;\n }\n}\n\nfunction newMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAkCA,IAAM,8CAA8B,IAAI,IAAmB;AAE3D,IAAa,oBAAb,MAA0D;CACxD,iBAAyC,WAAW;CACpD,qCAA8B,IAAI,IAAoB;CACtD,yCAAkC,IAAI,IAAqB;CAC3D,gCAA2C,IAAI,IAAI;CACnD;CAEA,YAAY,wBAA4D;EACtE,KAAKI,0BAA0B;CACjC;CAEA,UACE,QACA,OACA,GAAG,MACG;EACN,OAAO,eAAe,MAAM,GAAG,0BAA0B,QAAQ;EACjE,MAAM,UAAU,KAAK;EACrB,IAAI,WAAW,gCACb,KAAKH,mBAAmB,IAAI,SAAS,KAAK;OACrC;GACL,IAAI,SAAS,KAAKC,uBAAuB,IAAI,OAAO;GACpD,IAAI,CAAC,QAAQ;IACX,SAAS,IAAI,QAAQ;IACrB,KAAKA,uBAAuB,IAAI,SAAS,MAAM;GACjD;GACA,OAAO,IAAI,KAAK;EAClB;EACA,KAAKF,eAAe,QAAQ,IAAI,KAAK;CACvC;CAEA,uBAAuB,SAAgD;EACrE,MAAM,YAAY,KAAKC,mBAAmB,IAAI,OAAO;EACrD,MAAM,gBAAgB,KAAKC,uBAAuB,IAAI,OAAO;EAC7D,IAAI,cAAc,KAAA,KAAa,kBAAkB,KAAA,GAC/C,OAAO;EAET,OAAO;GACL,6BAA6B;GAC7B,wBAAwB,iBAAiB,IAAI,QAAQ,GAAG,OAAO;EACjE;CACF;CAEA,iBAAiB;EACf,OAAO,UAAU,KAAKF,iBAAgB,MAAK,EAAE,OAAO,CAAC;CACvD;CAEA,eAAe,SAAkC;EAC/C,OAAO,KAAKG,cAAc,IAAI,OAAO;CACvC;CAEA,YAAY,SAAuB;EACjC,KAAKF,mBAAmB,OAAO,OAAO;EACtC,KAAKC,uBAAuB,OAAO,OAAO;EAC1C,KAAKC,cAAc,OAAO,OAAO;CACnC;CAEA,SAAS,SAAiB,KAAgB;EACxC,KAAKA,cAAc,IAAI,SAAS,GAAG;CACrC;;;;;CAMA,gBAAgB,eAAuC;EACrD,OACE,kBAAkB,KAAK,4BAA4B,IAAI,aAAa;CAExE;CAEA,iBAAiB,eAAoC;EACnD,4BAA4B,IAAI,aAAa;CAC/C;CAEA,mBAAmB,eAA8B;EAC/C,4BAA4B,OAAO,aAAa;CAClD;;;;;;CAOA,MAAM,qBACJ,MACA,MACA,KACc;EACd,OACE,KAAKC,yBACL,mFACF;EAIA,MAAM,UAA+B,CACnC;GACE,IAHY,gBAAkB,MAAM,IAGhC;GACJ,MAAM;GACN;GACA;GACA,aAAa,CAAC;EAChB,CACF;EAEA,MAAM,UAAU,MAAM,KAAKA,wBAAwB,UAAU,KAAK,OAAO;EAEzE,IAAI,UAAU,QAAQ,QACpB,MAAM,IAAI,uBAAuB,QAAQ,QAAQ,MAAM;EAGzD,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;EAGrD,IAAI,WAAW,QAAQ;GACrB,MAAM,UACJ,OAAO,WAAW;GACpB,MAAM,IAAI,MACR,mCAAmC,KAAK,IAAI,OAAO,MAAM,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,OAAO,GACxG;EACF;EAEA,OAAO,OAAO;CAChB;AACF;AAEA,SAAS,aAA4B;CACnC,OAAO;EACL,gCAAgC,IAAI,QAAQ;EAC5C,uBAAuB,IAAI,QAAQ;CACrC;AACF"}
|
|
@@ -13,7 +13,7 @@ import { ChangeProcessor } from "../../replicator/change-processor.js";
|
|
|
13
13
|
import { ReplicationStatusPublisher } from "../../replicator/replication-status.js";
|
|
14
14
|
import { restoreReplica } from "../common/replica-restore.js";
|
|
15
15
|
import { initReplica } from "../common/replica-schema.js";
|
|
16
|
-
import { WebSocket } from "ws";
|
|
16
|
+
import { WebSocket as WebSocket$1 } from "ws";
|
|
17
17
|
//#region ../zero-cache/src/services/change-source/custom/change-source.ts
|
|
18
18
|
/**
|
|
19
19
|
* Initializes a Custom change source before streaming changes from the
|
|
@@ -78,7 +78,7 @@ var CustomChangeSource = class {
|
|
|
78
78
|
url.searchParams.set("lastWatermark", clientWatermark);
|
|
79
79
|
url.searchParams.set("replicaVersion", replicaVersion);
|
|
80
80
|
}
|
|
81
|
-
const ws = new WebSocket(url);
|
|
81
|
+
const ws = new WebSocket$1(url);
|
|
82
82
|
const { instream, outstream } = stream(this.#lc, ws, changeStreamMessageSchema, { coalesce: (curr) => curr });
|
|
83
83
|
return {
|
|
84
84
|
changes: instream,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {\n restoreReplica,\n type InitializeResult,\n type RestoreOptions,\n} from '../common/replica-restore.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n {litestream, constraints}: RestoreOptions = {},\n): Promise<InitializeResult> {\n // At the moment, the custom change-source implementation does not support\n // per-RM backups (and thus does not support litestream v5 or RMv2). The\n // current proposal to support this without requiring additional upstream\n // state tracking is to:\n // - use litestream to (ltx) query a fixed pool of subfolders within the\n // backupURL (e.g. /a/, /b/, /c/, /d/, /e/)\n // - restore from the subfolder with the latest ltx file\n // - backup to the subfolder with the oldest (or non-existent) ltx file\n if (litestream?.backupURL) {\n await restoreReplica(lc, litestream, replicaDbFile, constraints);\n }\n\n let initialSynced = false;\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => {\n initialSynced = true;\n return initialSync(log, shard, tx, upstreamURI, context);\n },\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = shard.publications.toSorted();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {\n subscriptionState,\n changeSource,\n destinationBackupURL: litestream?.backupURL,\n // A custom change source has no upstream `replicas` table to identify\n // itself from, so the generation is the whole of the SQLite change log's\n // identity here.\n replicaID: null,\n waitForBackupBeforeServing: initialSynced,\n };\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n stop(): Promise<void> {\n return Promise.resolve();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n publications.toSorted(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {\n restoreReplica,\n type InitializeResult,\n type RestoreOptions,\n} from '../common/replica-restore.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n {litestream, constraints}: RestoreOptions = {},\n): Promise<InitializeResult> {\n // At the moment, the custom change-source implementation does not support\n // per-RM backups (and thus does not support litestream v5 or RMv2). The\n // current proposal to support this without requiring additional upstream\n // state tracking is to:\n // - use litestream to (ltx) query a fixed pool of subfolders within the\n // backupURL (e.g. /a/, /b/, /c/, /d/, /e/)\n // - restore from the subfolder with the latest ltx file\n // - backup to the subfolder with the oldest (or non-existent) ltx file\n if (litestream?.backupURL) {\n await restoreReplica(lc, litestream, replicaDbFile, constraints);\n }\n\n let initialSynced = false;\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => {\n initialSynced = true;\n return initialSync(log, shard, tx, upstreamURI, context);\n },\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = shard.publications.toSorted();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {\n subscriptionState,\n changeSource,\n destinationBackupURL: litestream?.backupURL,\n // A custom change source has no upstream `replicas` table to identify\n // itself from, so the generation is the whole of the SQLite change log's\n // identity here.\n replicaID: null,\n waitForBackupBeforeServing: initialSynced,\n };\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n stop(): Promise<void> {\n return Promise.resolve();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n publications.toSorted(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import { discoverChangeStreamerAddress } from "./schema/tables.js";
|
|
|
10
10
|
import { URLParams } from "../../types/url-params.js";
|
|
11
11
|
import { downstreamSchema } from "./change-streamer.js";
|
|
12
12
|
import { snapshotMessageSchema } from "./snapshot.js";
|
|
13
|
-
import WebSocket
|
|
13
|
+
import WebSocket from "ws";
|
|
14
14
|
import websocket from "@fastify/websocket";
|
|
15
15
|
//#region ../zero-cache/src/services/change-streamer/change-streamer-http.ts
|
|
16
16
|
var MIN_SUPPORTED_PROTOCOL_VERSION = 4;
|
|
@@ -114,11 +114,11 @@ var ChangeStreamerHttpClient = class {
|
|
|
114
114
|
return uri;
|
|
115
115
|
}
|
|
116
116
|
async reserveSnapshot(taskID) {
|
|
117
|
-
const ws = new WebSocket
|
|
117
|
+
const ws = new WebSocket(await this.#resolveChangeStreamer(SNAPSHOT_PATH) + `?${new URLSearchParams({ taskID }).toString()}`);
|
|
118
118
|
return streamIn(this.#lc, ws, snapshotMessageSchema);
|
|
119
119
|
}
|
|
120
120
|
async subscribe(ctx) {
|
|
121
|
-
const ws = new WebSocket
|
|
121
|
+
const ws = new WebSocket(await this.#resolveChangeStreamer(CHANGES_PATH) + `?${getParams(ctx).toString()}`);
|
|
122
122
|
return streamInStringified(this.#lc, ws, downstreamSchema);
|
|
123
123
|
}
|
|
124
124
|
};
|