@hops-ops/distributed 0.0.0

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.
Files changed (242) hide show
  1. package/README.md +454 -0
  2. package/dist/auth-headers.d.ts +8 -0
  3. package/dist/auth-headers.js +35 -0
  4. package/dist/diagnostics.d.ts +2 -0
  5. package/dist/diagnostics.js +1 -0
  6. package/dist/document.d.ts +8 -0
  7. package/dist/document.js +6 -0
  8. package/dist/identity.d.ts +23 -0
  9. package/dist/identity.js +73 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.js +6 -0
  12. package/dist/internal/cache-engine/create.d.ts +5 -0
  13. package/dist/internal/cache-engine/create.js +7 -0
  14. package/dist/internal/cache-engine/engine.d.ts +32 -0
  15. package/dist/internal/cache-engine/engine.js +1287 -0
  16. package/dist/internal/cache-engine/errors.d.ts +5 -0
  17. package/dist/internal/cache-engine/errors.js +10 -0
  18. package/dist/internal/cache-engine/helpers.d.ts +55 -0
  19. package/dist/internal/cache-engine/helpers.js +545 -0
  20. package/dist/internal/cache-engine/index.d.ts +3 -0
  21. package/dist/internal/cache-engine/index.js +2 -0
  22. package/dist/internal/cache-engine/types.d.ts +300 -0
  23. package/dist/internal/cache-engine/types.js +1 -0
  24. package/dist/internal/cache-engine.d.ts +3 -0
  25. package/dist/internal/cache-engine.js +2 -0
  26. package/dist/lib/assert-name.d.ts +2 -0
  27. package/dist/lib/assert-name.js +6 -0
  28. package/dist/lib/compare-code-units.d.ts +2 -0
  29. package/dist/lib/compare-code-units.js +4 -0
  30. package/dist/lib/deep-equal.d.ts +2 -0
  31. package/dist/lib/deep-equal.js +24 -0
  32. package/dist/lib/freeze-record.d.ts +2 -0
  33. package/dist/lib/freeze-record.js +13 -0
  34. package/dist/lib/index.d.ts +6 -0
  35. package/dist/lib/index.js +6 -0
  36. package/dist/lib/is-plain-record.d.ts +2 -0
  37. package/dist/lib/is-plain-record.js +8 -0
  38. package/dist/lib/report.d.ts +7 -0
  39. package/dist/lib/report.js +26 -0
  40. package/dist/protocol.d.ts +179 -0
  41. package/dist/protocol.js +558 -0
  42. package/dist/react/context.d.ts +20 -0
  43. package/dist/react/context.js +24 -0
  44. package/dist/react/index.d.ts +2 -0
  45. package/dist/react/index.js +3 -0
  46. package/dist/react/query.d.ts +20 -0
  47. package/dist/react/query.js +99 -0
  48. package/dist/replica/command-id.d.ts +2 -0
  49. package/dist/replica/command-id.js +19 -0
  50. package/dist/replica/command-runtime/constants.d.ts +5 -0
  51. package/dist/replica/command-runtime/constants.js +5 -0
  52. package/dist/replica/command-runtime/create.d.ts +2 -0
  53. package/dist/replica/command-runtime/create.js +644 -0
  54. package/dist/replica/command-runtime/errors.d.ts +16 -0
  55. package/dist/replica/command-runtime/errors.js +41 -0
  56. package/dist/replica/command-runtime/index.d.ts +5 -0
  57. package/dist/replica/command-runtime/index.js +4 -0
  58. package/dist/replica/command-runtime/lib/binding.d.ts +4 -0
  59. package/dist/replica/command-runtime/lib/binding.js +64 -0
  60. package/dist/replica/command-runtime/lib/effects.d.ts +11 -0
  61. package/dist/replica/command-runtime/lib/effects.js +89 -0
  62. package/dist/replica/command-runtime/lib/index.d.ts +9 -0
  63. package/dist/replica/command-runtime/lib/index.js +9 -0
  64. package/dist/replica/command-runtime/lib/inventory.d.ts +10 -0
  65. package/dist/replica/command-runtime/lib/inventory.js +141 -0
  66. package/dist/replica/command-runtime/lib/output.d.ts +13 -0
  67. package/dist/replica/command-runtime/lib/output.js +190 -0
  68. package/dist/replica/command-runtime/lib/projection.d.ts +20 -0
  69. package/dist/replica/command-runtime/lib/projection.js +255 -0
  70. package/dist/replica/command-runtime/lib/status.d.ts +7 -0
  71. package/dist/replica/command-runtime/lib/status.js +127 -0
  72. package/dist/replica/command-runtime/lib/transport.d.ts +16 -0
  73. package/dist/replica/command-runtime/lib/transport.js +112 -0
  74. package/dist/replica/command-runtime/lib/util.d.ts +16 -0
  75. package/dist/replica/command-runtime/lib/util.js +127 -0
  76. package/dist/replica/command-runtime/lifecycle.d.ts +11 -0
  77. package/dist/replica/command-runtime/lifecycle.js +13 -0
  78. package/dist/replica/command-runtime/symbols.d.ts +30 -0
  79. package/dist/replica/command-runtime/symbols.js +30 -0
  80. package/dist/replica/command-runtime/types.d.ts +222 -0
  81. package/dist/replica/command-runtime/types.js +1 -0
  82. package/dist/replica/command-runtime.d.ts +3 -0
  83. package/dist/replica/command-runtime.js +2 -0
  84. package/dist/replica/commands/clone.d.ts +12 -0
  85. package/dist/replica/commands/clone.js +187 -0
  86. package/dist/replica/commands/constants.d.ts +6 -0
  87. package/dist/replica/commands/constants.js +6 -0
  88. package/dist/replica/commands/errors.d.ts +7 -0
  89. package/dist/replica/commands/errors.js +22 -0
  90. package/dist/replica/commands/implementation.d.ts +8 -0
  91. package/dist/replica/commands/implementation.js +8 -0
  92. package/dist/replica/commands/index.d.ts +5 -0
  93. package/dist/replica/commands/index.js +4 -0
  94. package/dist/replica/commands/prepare.d.ts +27 -0
  95. package/dist/replica/commands/prepare.js +77 -0
  96. package/dist/replica/commands/presets.d.ts +12 -0
  97. package/dist/replica/commands/presets.js +84 -0
  98. package/dist/replica/commands/receipt.d.ts +3 -0
  99. package/dist/replica/commands/receipt.js +36 -0
  100. package/dist/replica/commands/resolve.d.ts +11 -0
  101. package/dist/replica/commands/resolve.js +165 -0
  102. package/dist/replica/commands/types.d.ts +278 -0
  103. package/dist/replica/commands/types.js +2 -0
  104. package/dist/replica/commands/util.d.ts +21 -0
  105. package/dist/replica/commands/util.js +96 -0
  106. package/dist/replica/commands/validate.d.ts +18 -0
  107. package/dist/replica/commands/validate.js +477 -0
  108. package/dist/replica/commands.d.ts +3 -0
  109. package/dist/replica/commands.js +2 -0
  110. package/dist/replica/diagnostics/event-log.d.ts +4 -0
  111. package/dist/replica/diagnostics/event-log.js +463 -0
  112. package/dist/replica/diagnostics/implementation.d.ts +4 -0
  113. package/dist/replica/diagnostics/implementation.js +4 -0
  114. package/dist/replica/diagnostics/index.d.ts +2 -0
  115. package/dist/replica/diagnostics/index.js +1 -0
  116. package/dist/replica/diagnostics/inspect.d.ts +6 -0
  117. package/dist/replica/diagnostics/inspect.js +170 -0
  118. package/dist/replica/diagnostics/types.d.ts +295 -0
  119. package/dist/replica/diagnostics/types.js +2 -0
  120. package/dist/replica/diagnostics.d.ts +3 -0
  121. package/dist/replica/diagnostics.js +2 -0
  122. package/dist/replica/distributed-replica/clocks.d.ts +23 -0
  123. package/dist/replica/distributed-replica/clocks.js +156 -0
  124. package/dist/replica/distributed-replica/constants.d.ts +12 -0
  125. package/dist/replica/distributed-replica/constants.js +10 -0
  126. package/dist/replica/distributed-replica/helpers.d.ts +48 -0
  127. package/dist/replica/distributed-replica/helpers.js +445 -0
  128. package/dist/replica/distributed-replica/hydration.d.ts +19 -0
  129. package/dist/replica/distributed-replica/hydration.js +380 -0
  130. package/dist/replica/distributed-replica/impl-diagnostics.d.ts +22 -0
  131. package/dist/replica/distributed-replica/impl-diagnostics.js +189 -0
  132. package/dist/replica/distributed-replica/impl-fetch-live.d.ts +39 -0
  133. package/dist/replica/distributed-replica/impl-fetch-live.js +395 -0
  134. package/dist/replica/distributed-replica/impl-hydration-orchestrate.d.ts +56 -0
  135. package/dist/replica/distributed-replica/impl-hydration-orchestrate.js +299 -0
  136. package/dist/replica/distributed-replica/impl-optimistic.d.ts +31 -0
  137. package/dist/replica/distributed-replica/impl-optimistic.js +172 -0
  138. package/dist/replica/distributed-replica/impl-protocol.d.ts +53 -0
  139. package/dist/replica/distributed-replica/impl-protocol.js +108 -0
  140. package/dist/replica/distributed-replica/impl.d.ts +44 -0
  141. package/dist/replica/distributed-replica/impl.js +1776 -0
  142. package/dist/replica/distributed-replica/index.d.ts +2 -0
  143. package/dist/replica/distributed-replica/index.js +4 -0
  144. package/dist/replica/distributed-replica/optimistic.d.ts +30 -0
  145. package/dist/replica/distributed-replica/optimistic.js +221 -0
  146. package/dist/replica/distributed-replica/types.d.ts +147 -0
  147. package/dist/replica/distributed-replica/types.js +1 -0
  148. package/dist/replica/distributed-replica/watch.d.ts +19 -0
  149. package/dist/replica/distributed-replica/watch.js +95 -0
  150. package/dist/replica/distributed-replica.d.ts +2 -0
  151. package/dist/replica/distributed-replica.js +2 -0
  152. package/dist/replica/graphql-transport.d.ts +24 -0
  153. package/dist/replica/graphql-transport.js +122 -0
  154. package/dist/replica/identity/clone.d.ts +6 -0
  155. package/dist/replica/identity/clone.js +54 -0
  156. package/dist/replica/identity/codec.d.ts +48 -0
  157. package/dist/replica/identity/codec.js +789 -0
  158. package/dist/replica/identity/constants.d.ts +3 -0
  159. package/dist/replica/identity/constants.js +18 -0
  160. package/dist/replica/identity/implementation.d.ts +5 -0
  161. package/dist/replica/identity/implementation.js +5 -0
  162. package/dist/replica/identity/index.d.ts +3 -0
  163. package/dist/replica/identity/index.js +3 -0
  164. package/dist/replica/identity/keys.d.ts +29 -0
  165. package/dist/replica/identity/keys.js +173 -0
  166. package/dist/replica/identity.d.ts +2 -0
  167. package/dist/replica/identity.js +2 -0
  168. package/dist/replica/index-maintenance/engine.d.ts +84 -0
  169. package/dist/replica/index-maintenance/engine.js +677 -0
  170. package/dist/replica/index-maintenance/implementation.d.ts +4 -0
  171. package/dist/replica/index-maintenance/implementation.js +4 -0
  172. package/dist/replica/index-maintenance/index.d.ts +2 -0
  173. package/dist/replica/index-maintenance/index.js +1 -0
  174. package/dist/replica/index-maintenance/registry.d.ts +2 -0
  175. package/dist/replica/index-maintenance/registry.js +49 -0
  176. package/dist/replica/index-maintenance/types.d.ts +58 -0
  177. package/dist/replica/index-maintenance/types.js +1 -0
  178. package/dist/replica/index-maintenance.d.ts +3 -0
  179. package/dist/replica/index-maintenance.js +2 -0
  180. package/dist/replica/index.d.ts +17 -0
  181. package/dist/replica/index.js +9 -0
  182. package/dist/replica/materialize.d.ts +10 -0
  183. package/dist/replica/materialize.js +163 -0
  184. package/dist/replica/normalize.d.ts +27 -0
  185. package/dist/replica/normalize.js +298 -0
  186. package/dist/replica/operation-binding.d.ts +14 -0
  187. package/dist/replica/operation-binding.js +76 -0
  188. package/dist/replica/persistence/idb.d.ts +20 -0
  189. package/dist/replica/persistence/idb.js +193 -0
  190. package/dist/replica/persistence/implementation.d.ts +4 -0
  191. package/dist/replica/persistence/implementation.js +4 -0
  192. package/dist/replica/persistence/index.d.ts +2 -0
  193. package/dist/replica/persistence/index.js +1 -0
  194. package/dist/replica/persistence/state.d.ts +104 -0
  195. package/dist/replica/persistence/state.js +734 -0
  196. package/dist/replica/persistence/types.d.ts +54 -0
  197. package/dist/replica/persistence/types.js +1 -0
  198. package/dist/replica/persistence.d.ts +3 -0
  199. package/dist/replica/persistence.js +2 -0
  200. package/dist/replica/query-plan/constants.d.ts +13 -0
  201. package/dist/replica/query-plan/constants.js +7 -0
  202. package/dist/replica/query-plan/filter.d.ts +39 -0
  203. package/dist/replica/query-plan/filter.js +544 -0
  204. package/dist/replica/query-plan/index.d.ts +4 -0
  205. package/dist/replica/query-plan/index.js +3 -0
  206. package/dist/replica/query-plan/order.d.ts +11 -0
  207. package/dist/replica/query-plan/order.js +158 -0
  208. package/dist/replica/query-plan/pagination.d.ts +6 -0
  209. package/dist/replica/query-plan/pagination.js +90 -0
  210. package/dist/replica/query-plan/resolve.d.ts +14 -0
  211. package/dist/replica/query-plan/resolve.js +163 -0
  212. package/dist/replica/query-plan/types.d.ts +89 -0
  213. package/dist/replica/query-plan/types.js +1 -0
  214. package/dist/replica/query-plan/util.d.ts +13 -0
  215. package/dist/replica/query-plan/util.js +62 -0
  216. package/dist/replica/query-plan.d.ts +3 -0
  217. package/dist/replica/query-plan.js +2 -0
  218. package/dist/replica/revalidation.d.ts +9 -0
  219. package/dist/replica/revalidation.js +86 -0
  220. package/dist/replica/selection.d.ts +20 -0
  221. package/dist/replica/selection.js +109 -0
  222. package/dist/replica/types.d.ts +657 -0
  223. package/dist/replica/types.js +1 -0
  224. package/dist/request.d.ts +17 -0
  225. package/dist/request.js +123 -0
  226. package/dist/sveltekit/auth.d.ts +18 -0
  227. package/dist/sveltekit/auth.js +9 -0
  228. package/dist/sveltekit/context.d.ts +28 -0
  229. package/dist/sveltekit/context.js +58 -0
  230. package/dist/sveltekit/index.d.ts +4 -0
  231. package/dist/sveltekit/index.js +4 -0
  232. package/dist/sveltekit/replica.d.ts +145 -0
  233. package/dist/sveltekit/replica.js +491 -0
  234. package/dist/sveltekit/server-replica.d.ts +52 -0
  235. package/dist/sveltekit/server-replica.js +176 -0
  236. package/dist/sveltekit/vite.d.ts +103 -0
  237. package/dist/sveltekit/vite.js +878 -0
  238. package/dist/types.d.ts +33 -0
  239. package/dist/types.js +1 -0
  240. package/dist/websocket.d.ts +41 -0
  241. package/dist/websocket.js +187 -0
  242. package/package.json +95 -0
@@ -0,0 +1,5 @@
1
+ export declare class CacheRevisionConflictError extends Error {
2
+ readonly dependency: string;
3
+ readonly revision: string;
4
+ constructor(dependency: string, revision: bigint);
5
+ }
@@ -0,0 +1,10 @@
1
+ export class CacheRevisionConflictError extends Error {
2
+ dependency;
3
+ revision;
4
+ constructor(dependency, revision) {
5
+ super(`conflicting cache values at revision ${revision} for ${dependency}`);
6
+ this.name = 'CacheRevisionConflictError';
7
+ this.dependency = dependency;
8
+ this.revision = revision.toString(10);
9
+ }
10
+ }
@@ -0,0 +1,55 @@
1
+ import type { CacheEngineSnapshot, CacheIndexCoverage, CacheIndexMetadata, CacheValue, DerivedIndexMutation, DerivedIndexOperation, DerivedIndexReconciler, IndexKey, OptimisticLayerView, OverlayOperation, RecordKey, RecordLink, Revision, StoredIndex, StoredRecord, VisibleRecord } from './types.js';
2
+ import { assertName } from '../../lib/assert-name.js';
3
+ import { deepEqual } from '../../lib/deep-equal.js';
4
+ import { freezeRecord } from '../../lib/freeze-record.js';
5
+ import { reportSafely, reportUnhandledError } from '../../lib/report.js';
6
+ export declare function parseSnapshot(snapshot: CacheEngineSnapshot): {
7
+ records: Map<RecordKey, StoredRecord>;
8
+ indexes: Map<IndexKey, StoredIndex>;
9
+ };
10
+ export declare function runDerivedIndexReconciler(reconciler: DerivedIndexReconciler, confirmed: CacheEngineSnapshot, layers: readonly OptimisticLayerView[]): readonly DerivedIndexMutation[];
11
+ export declare function cloneDerivedIndexOperations(input: readonly DerivedIndexMutation[]): readonly DerivedIndexOperation[];
12
+ export declare function derivedIndexKeys(operations: readonly DerivedIndexOperation[]): readonly IndexKey[];
13
+ export declare function derivedIndexOperationKey(operation: DerivedIndexOperation): IndexKey;
14
+ export declare function assertExactKeys(value: Record<string, unknown>, allowed: readonly string[], description: string, required?: readonly string[]): void;
15
+ export declare function operationDependencies(operation: OverlayOperation): readonly string[];
16
+ export declare function recordSeenDependency(key: RecordKey): string;
17
+ export declare function recordWildcardDependency(key: RecordKey): string;
18
+ export declare function recordFieldDependency(key: RecordKey, field: string): string;
19
+ export declare function indexDependency(key: IndexKey): string;
20
+ export declare function dependenciesChanged(dependencies: ReadonlySet<string>, changed: ReadonlySet<string>): boolean;
21
+ export declare function emptyVisibleRecord(): VisibleRecord;
22
+ export declare function isVisibleRecordLive(record: VisibleRecord | undefined): boolean;
23
+ export declare function validateRecordKey(key: RecordKey): void;
24
+ export declare function validateIndexWrite(write: {
25
+ key: IndexKey;
26
+ records: readonly RecordKey[];
27
+ metadata?: CacheIndexMetadata;
28
+ }): void;
29
+ export declare function indexMetadataWithoutStaleReason(metadata: CacheIndexMetadata | undefined): Omit<CacheIndexMetadata, 'staleReason'> | undefined;
30
+ export declare function isOrderedSubsequence(known: readonly RecordKey[], complete: readonly RecordKey[]): boolean;
31
+ export declare function refinementMetadataCompatible(current: CacheIndexMetadata | undefined, next: CacheIndexMetadata | undefined): boolean;
32
+ export declare function refinementMetadataIdentity(metadata: CacheIndexMetadata): unknown;
33
+ export declare function coverageRequestIdentity(coverage: CacheIndexCoverage): unknown;
34
+ export declare function cloneIndexMetadata(metadata: CacheIndexMetadata): CacheIndexMetadata;
35
+ export declare function cloneIndexCoverage(coverage: CacheIndexCoverage): CacheIndexCoverage;
36
+ export declare function assertNonNegativeSafeInteger(value: number, description: string): void;
37
+ export declare function assertWriterActive(active: boolean): void;
38
+ export declare function assertSynchronousResult(result: unknown, description: string): void;
39
+ export declare function validateRecordKeys(keys: readonly RecordKey[]): void;
40
+ export declare function revisionToken(value: Revision): bigint;
41
+ export declare function compareRecordTuple(leftIncarnation: bigint, leftRevision: bigint, rightIncarnation: bigint, rightRevision: bigint): -1 | 0 | 1;
42
+ export declare function revisionString(value: bigint): string;
43
+ export declare function cloneFields(fields: Readonly<Record<string, CacheValue>> | undefined): Readonly<Record<string, CacheValue>>;
44
+ export declare function cloneLinks(links: Readonly<Record<string, RecordLink>> | undefined): Readonly<Record<string, RecordLink>>;
45
+ export declare function cloneLink(value: RecordLink): RecordLink;
46
+ export declare function linkKeys(value: RecordLink): readonly RecordKey[];
47
+ export declare function cloneCacheValue(value: CacheValue, ancestors?: Set<object>): CacheValue;
48
+ export declare function canonicalValue(value: CacheValue): string;
49
+ export declare function cacheIndexKey(input: {
50
+ parent?: RecordKey;
51
+ field: string;
52
+ arguments?: Readonly<Record<string, CacheValue>>;
53
+ }): IndexKey;
54
+ export { assertName, deepEqual, freezeRecord, reportSafely };
55
+ export declare const reportUnhandledWatcherError: typeof reportUnhandledError;
@@ -0,0 +1,545 @@
1
+ import { assertName } from '../../lib/assert-name.js';
2
+ import { deepEqual } from '../../lib/deep-equal.js';
3
+ import { freezeRecord } from '../../lib/freeze-record.js';
4
+ import { reportSafely, reportUnhandledError } from '../../lib/report.js';
5
+ export function parseSnapshot(snapshot) {
6
+ if (!snapshot || snapshot.version !== 1)
7
+ throw new TypeError('unsupported cache snapshot');
8
+ if (!Array.isArray(snapshot.records) || !Array.isArray(snapshot.indexes)) {
9
+ throw new TypeError('invalid cache snapshot collections');
10
+ }
11
+ const records = new Map();
12
+ for (const input of snapshot.records) {
13
+ validateRecordKey(input.key);
14
+ if (records.has(input.key))
15
+ throw new TypeError(`duplicate snapshot record: ${input.key}`);
16
+ const revision = revisionToken(input.revision);
17
+ const incarnation = revisionToken(input.incarnation ?? input.revision);
18
+ const tombstoneRevision = input.tombstoneRevision === undefined
19
+ ? undefined
20
+ : revisionToken(input.tombstoneRevision);
21
+ if (tombstoneRevision !== undefined && tombstoneRevision !== revision) {
22
+ throw new TypeError(`invalid tombstone revision for ${input.key}`);
23
+ }
24
+ const fields = new Map();
25
+ for (const [name, field] of Object.entries(input.fields)) {
26
+ assertName(name, 'record field');
27
+ const fieldRevision = revisionToken(field.revision);
28
+ if (fieldRevision > revision)
29
+ throw new TypeError(`field revision exceeds ${input.key}`);
30
+ fields.set(name, { revision: fieldRevision, value: cloneCacheValue(field.value) });
31
+ }
32
+ const links = new Map();
33
+ for (const [name, link] of Object.entries(input.links)) {
34
+ assertName(name, 'record link');
35
+ const linkRevision = revisionToken(link.revision);
36
+ if (linkRevision > revision)
37
+ throw new TypeError(`link revision exceeds ${input.key}`);
38
+ links.set(name, { revision: linkRevision, value: cloneLink(link.value) });
39
+ }
40
+ if (tombstoneRevision !== undefined && (fields.size > 0 || links.size > 0)) {
41
+ throw new TypeError(`tombstone record contains live fields: ${input.key}`);
42
+ }
43
+ records.set(input.key, {
44
+ revision,
45
+ incarnation,
46
+ tombstoneRevision,
47
+ fields,
48
+ links
49
+ });
50
+ }
51
+ const indexes = new Map();
52
+ for (const input of snapshot.indexes) {
53
+ validateIndexWrite(input);
54
+ if (indexes.has(input.key))
55
+ throw new TypeError(`duplicate snapshot index: ${input.key}`);
56
+ validateRecordKeys(input.records);
57
+ const revision = revisionToken(input.revision);
58
+ const staleRevision = input.staleRevision === undefined
59
+ ? undefined
60
+ : revisionToken(input.staleRevision);
61
+ if (staleRevision !== undefined && staleRevision < revision) {
62
+ throw new TypeError(`stale index revision precedes its snapshot: ${input.key}`);
63
+ }
64
+ indexes.set(input.key, {
65
+ revision,
66
+ staleRevision,
67
+ records: [...input.records],
68
+ complete: Boolean(input.complete),
69
+ deleted: Boolean(input.deleted),
70
+ metadata: input.metadata === undefined ? undefined : cloneIndexMetadata(input.metadata)
71
+ });
72
+ }
73
+ return { records, indexes };
74
+ }
75
+ export function runDerivedIndexReconciler(reconciler, confirmed, layers) {
76
+ const result = reconciler(confirmed, Object.freeze([...layers]));
77
+ assertSynchronousResult(result, 'derived index reconciler');
78
+ if (!Array.isArray(result)) {
79
+ throw new TypeError('derived index reconciler must return an array');
80
+ }
81
+ return result;
82
+ }
83
+ export function cloneDerivedIndexOperations(input) {
84
+ const operations = [];
85
+ const keys = new Set();
86
+ for (const [index, value] of input.entries()) {
87
+ const path = `derived index mutation ${index}`;
88
+ if (value === null ||
89
+ typeof value !== 'object' ||
90
+ Array.isArray(value) ||
91
+ (Object.getPrototypeOf(value) !== Object.prototype &&
92
+ Object.getPrototypeOf(value) !== null)) {
93
+ throw new TypeError(`${path} must be a plain object`);
94
+ }
95
+ if (value.kind === 'write') {
96
+ assertExactKeys(value, ['kind', 'write'], path);
97
+ const write = value.write;
98
+ if (write === null ||
99
+ typeof write !== 'object' ||
100
+ Array.isArray(write) ||
101
+ (Object.getPrototypeOf(write) !== Object.prototype &&
102
+ Object.getPrototypeOf(write) !== null)) {
103
+ throw new TypeError(`${path}.write must be a plain object`);
104
+ }
105
+ assertExactKeys(write, ['key', 'records', 'complete', 'metadata'], `${path}.write`, ['key', 'records']);
106
+ validateIndexWrite(write);
107
+ if (write.complete !== undefined &&
108
+ typeof write.complete !== 'boolean') {
109
+ throw new TypeError(`${path}.write.complete must be a boolean`);
110
+ }
111
+ if (keys.has(write.key)) {
112
+ throw new TypeError(`duplicate derived index mutation: ${write.key}`);
113
+ }
114
+ keys.add(write.key);
115
+ operations.push(Object.freeze({
116
+ kind: 'write-index',
117
+ write: Object.freeze({
118
+ key: write.key,
119
+ records: Object.freeze([...write.records]),
120
+ complete: write.complete ?? false,
121
+ ...(write.metadata === undefined
122
+ ? {}
123
+ : { metadata: cloneIndexMetadata(write.metadata) })
124
+ })
125
+ }));
126
+ continue;
127
+ }
128
+ if (value.kind === 'stale') {
129
+ assertExactKeys(value, ['kind', 'key', 'reason'], path);
130
+ assertName(value.key, `${path} key`);
131
+ assertName(value.reason, `${path} reason`);
132
+ if (keys.has(value.key)) {
133
+ throw new TypeError(`duplicate derived index mutation: ${value.key}`);
134
+ }
135
+ keys.add(value.key);
136
+ operations.push(Object.freeze({
137
+ kind: 'mark-index-stale',
138
+ key: value.key,
139
+ reason: value.reason
140
+ }));
141
+ continue;
142
+ }
143
+ if (value.kind === 'delete') {
144
+ assertExactKeys(value, ['kind', 'key'], path);
145
+ assertName(value.key, `${path} key`);
146
+ if (keys.has(value.key)) {
147
+ throw new TypeError(`duplicate derived index mutation: ${value.key}`);
148
+ }
149
+ keys.add(value.key);
150
+ operations.push(Object.freeze({ kind: 'delete-index', key: value.key }));
151
+ continue;
152
+ }
153
+ throw new TypeError(`${path} has unsupported kind`);
154
+ }
155
+ return Object.freeze(operations.sort((left, right) => derivedIndexOperationKey(left).localeCompare(derivedIndexOperationKey(right))));
156
+ }
157
+ export function derivedIndexKeys(operations) {
158
+ return Object.freeze([...new Set(operations.map(derivedIndexOperationKey))].sort());
159
+ }
160
+ export function derivedIndexOperationKey(operation) {
161
+ return operation.kind === 'write-index'
162
+ ? operation.write.key
163
+ : operation.key;
164
+ }
165
+ export function assertExactKeys(value, allowed, description, required = allowed) {
166
+ const allowedKeys = new Set(allowed);
167
+ for (const key of Object.keys(value)) {
168
+ if (!allowedKeys.has(key)) {
169
+ throw new TypeError(`${description} contains unknown field ${key}`);
170
+ }
171
+ }
172
+ for (const key of required) {
173
+ if (!Object.prototype.hasOwnProperty.call(value, key)) {
174
+ throw new TypeError(`${description} is missing field ${key}`);
175
+ }
176
+ }
177
+ }
178
+ export function operationDependencies(operation) {
179
+ if (operation.kind === 'write-record') {
180
+ return [
181
+ recordSeenDependency(operation.write.key),
182
+ ...Object.keys(operation.write.fields ?? {}).map((name) => recordFieldDependency(operation.write.key, `field:${name}`)),
183
+ ...Object.keys(operation.write.links ?? {}).map((name) => recordFieldDependency(operation.write.key, `link:${name}`))
184
+ ];
185
+ }
186
+ if (operation.kind === 'tombstone-record') {
187
+ return [recordSeenDependency(operation.key), recordWildcardDependency(operation.key)];
188
+ }
189
+ return [indexDependency(operation.kind === 'write-index' ? operation.write.key : operation.key)];
190
+ }
191
+ export function recordSeenDependency(key) {
192
+ return JSON.stringify(['record-seen', key]);
193
+ }
194
+ export function recordWildcardDependency(key) {
195
+ return JSON.stringify(['record', key, '*']);
196
+ }
197
+ export function recordFieldDependency(key, field) {
198
+ return JSON.stringify(['record', key, field]);
199
+ }
200
+ export function indexDependency(key) {
201
+ return JSON.stringify(['index', key]);
202
+ }
203
+ export function dependenciesChanged(dependencies, changed) {
204
+ if (changed.size === 0 || changed.has('*'))
205
+ return true;
206
+ for (const dependency of dependencies) {
207
+ if (changed.has(dependency))
208
+ return true;
209
+ }
210
+ return false;
211
+ }
212
+ export function emptyVisibleRecord() {
213
+ return {
214
+ revision: 0n,
215
+ incarnation: 0n,
216
+ tombstoned: false,
217
+ fields: new Map(),
218
+ links: new Map()
219
+ };
220
+ }
221
+ export function isVisibleRecordLive(record) {
222
+ return record !== undefined && !record.tombstoned;
223
+ }
224
+ export function validateRecordKey(key) {
225
+ assertName(key, 'record key');
226
+ }
227
+ export function validateIndexWrite(write) {
228
+ assertName(write.key, 'index key');
229
+ if (!Array.isArray(write.records))
230
+ throw new TypeError('index records must be an array');
231
+ validateRecordKeys(write.records);
232
+ if ('metadata' in write && write.metadata !== undefined) {
233
+ const metadata = cloneIndexMetadata(write.metadata);
234
+ const expectedKey = cacheIndexKey({
235
+ ...(metadata.parent === undefined ? {} : { parent: metadata.parent }),
236
+ field: metadata.field,
237
+ arguments: metadata.arguments
238
+ });
239
+ if (write.key !== expectedKey) {
240
+ throw new TypeError(`index key does not match its metadata: expected ${expectedKey}`);
241
+ }
242
+ }
243
+ }
244
+ export function indexMetadataWithoutStaleReason(metadata) {
245
+ if (metadata === undefined)
246
+ return undefined;
247
+ const { staleReason: _staleReason, ...rest } = metadata;
248
+ return rest;
249
+ }
250
+ export function isOrderedSubsequence(known, complete) {
251
+ let knownIndex = 0;
252
+ for (const key of complete) {
253
+ if (key === known[knownIndex])
254
+ knownIndex += 1;
255
+ }
256
+ return knownIndex === known.length;
257
+ }
258
+ export function refinementMetadataCompatible(current, next) {
259
+ if (current === undefined || next === undefined)
260
+ return current === next;
261
+ return deepEqual(refinementMetadataIdentity(current), refinementMetadataIdentity(next));
262
+ }
263
+ export function refinementMetadataIdentity(metadata) {
264
+ return {
265
+ parent: metadata.parent,
266
+ parentRevision: metadata.parentRevision,
267
+ parentIncarnation: metadata.parentIncarnation,
268
+ field: metadata.field,
269
+ arguments: metadata.arguments,
270
+ dependencies: metadata.dependencies,
271
+ nullValue: metadata.nullValue,
272
+ coverage: coverageRequestIdentity(metadata.coverage)
273
+ };
274
+ }
275
+ export function coverageRequestIdentity(coverage) {
276
+ if (coverage.kind === 'complete' || coverage.kind === 'unknown') {
277
+ return { kind: coverage.kind };
278
+ }
279
+ if (coverage.kind === 'offset') {
280
+ return { kind: coverage.kind, offset: coverage.offset, limit: coverage.limit };
281
+ }
282
+ return {
283
+ kind: coverage.kind,
284
+ after: coverage.after,
285
+ before: coverage.before,
286
+ first: coverage.first,
287
+ last: coverage.last
288
+ };
289
+ }
290
+ export function cloneIndexMetadata(metadata) {
291
+ if (!metadata || typeof metadata !== 'object') {
292
+ throw new TypeError('index metadata must be an object');
293
+ }
294
+ assertName(metadata.field, 'index field');
295
+ if (metadata.parent !== undefined)
296
+ validateRecordKey(metadata.parent);
297
+ if (metadata.parentRevision !== undefined)
298
+ revisionToken(metadata.parentRevision);
299
+ if (metadata.parentIncarnation !== undefined)
300
+ revisionToken(metadata.parentIncarnation);
301
+ const argumentsValue = cloneCacheValue(metadata.arguments);
302
+ if (argumentsValue === null ||
303
+ Array.isArray(argumentsValue) ||
304
+ typeof argumentsValue !== 'object') {
305
+ throw new TypeError('index arguments must be a plain object');
306
+ }
307
+ if (!Array.isArray(metadata.dependencies)) {
308
+ throw new TypeError('index dependencies must be an array');
309
+ }
310
+ const dependencies = [...metadata.dependencies];
311
+ const seen = new Set();
312
+ for (const dependency of dependencies) {
313
+ assertName(dependency, 'index dependency');
314
+ if (seen.has(dependency)) {
315
+ throw new TypeError(`duplicate index dependency: ${dependency}`);
316
+ }
317
+ seen.add(dependency);
318
+ }
319
+ if (metadata.staleReason !== undefined) {
320
+ assertName(metadata.staleReason, 'index stale reason');
321
+ }
322
+ if (metadata.nullValue !== undefined && typeof metadata.nullValue !== 'boolean') {
323
+ throw new TypeError('index nullValue must be a boolean');
324
+ }
325
+ const coverage = cloneIndexCoverage(metadata.coverage);
326
+ return Object.freeze({
327
+ ...(metadata.parent === undefined ? {} : { parent: metadata.parent }),
328
+ ...(metadata.parentRevision === undefined
329
+ ? {}
330
+ : { parentRevision: metadata.parentRevision }),
331
+ ...(metadata.parentIncarnation === undefined
332
+ ? {}
333
+ : { parentIncarnation: metadata.parentIncarnation }),
334
+ field: metadata.field,
335
+ arguments: argumentsValue,
336
+ coverage,
337
+ dependencies: Object.freeze(dependencies),
338
+ ...(metadata.staleReason === undefined
339
+ ? {}
340
+ : { staleReason: metadata.staleReason }),
341
+ ...(metadata.nullValue === undefined ? {} : { nullValue: metadata.nullValue })
342
+ });
343
+ }
344
+ export function cloneIndexCoverage(coverage) {
345
+ if (!coverage || typeof coverage !== 'object') {
346
+ throw new TypeError('index coverage must be an object');
347
+ }
348
+ if (coverage.kind === 'complete' || coverage.kind === 'unknown') {
349
+ return Object.freeze({ kind: coverage.kind });
350
+ }
351
+ if (coverage.kind === 'offset') {
352
+ assertNonNegativeSafeInteger(coverage.offset, 'offset coverage offset');
353
+ if (coverage.limit !== undefined) {
354
+ assertNonNegativeSafeInteger(coverage.limit, 'offset coverage limit');
355
+ }
356
+ if (coverage.returned !== undefined) {
357
+ assertNonNegativeSafeInteger(coverage.returned, 'offset coverage returned');
358
+ }
359
+ if (coverage.hasNext !== undefined && typeof coverage.hasNext !== 'boolean') {
360
+ throw new TypeError('offset coverage hasNext must be a boolean');
361
+ }
362
+ return Object.freeze({
363
+ kind: 'offset',
364
+ offset: coverage.offset,
365
+ ...(coverage.limit === undefined ? {} : { limit: coverage.limit }),
366
+ ...(coverage.returned === undefined ? {} : { returned: coverage.returned }),
367
+ ...(coverage.hasNext === undefined ? {} : { hasNext: coverage.hasNext })
368
+ });
369
+ }
370
+ if (coverage.kind === 'cursor') {
371
+ if (coverage.first !== undefined) {
372
+ assertNonNegativeSafeInteger(coverage.first, 'cursor coverage first');
373
+ }
374
+ if (coverage.last !== undefined) {
375
+ assertNonNegativeSafeInteger(coverage.last, 'cursor coverage last');
376
+ }
377
+ if (coverage.hasNext !== undefined && typeof coverage.hasNext !== 'boolean') {
378
+ throw new TypeError('cursor coverage hasNext must be a boolean');
379
+ }
380
+ if (coverage.hasPrevious !== undefined &&
381
+ typeof coverage.hasPrevious !== 'boolean') {
382
+ throw new TypeError('cursor coverage hasPrevious must be a boolean');
383
+ }
384
+ return Object.freeze({
385
+ kind: 'cursor',
386
+ ...(coverage.after === undefined
387
+ ? {}
388
+ : { after: cloneCacheValue(coverage.after) }),
389
+ ...(coverage.before === undefined
390
+ ? {}
391
+ : { before: cloneCacheValue(coverage.before) }),
392
+ ...(coverage.first === undefined ? {} : { first: coverage.first }),
393
+ ...(coverage.last === undefined ? {} : { last: coverage.last }),
394
+ ...(coverage.start === undefined
395
+ ? {}
396
+ : { start: cloneCacheValue(coverage.start) }),
397
+ ...(coverage.end === undefined ? {} : { end: cloneCacheValue(coverage.end) }),
398
+ ...(coverage.hasNext === undefined ? {} : { hasNext: coverage.hasNext }),
399
+ ...(coverage.hasPrevious === undefined
400
+ ? {}
401
+ : { hasPrevious: coverage.hasPrevious })
402
+ });
403
+ }
404
+ throw new TypeError('unsupported index coverage kind');
405
+ }
406
+ export function assertNonNegativeSafeInteger(value, description) {
407
+ if (!Number.isSafeInteger(value) || value < 0) {
408
+ throw new TypeError(`${description} must be a non-negative safe integer`);
409
+ }
410
+ }
411
+ export function assertWriterActive(active) {
412
+ if (!active)
413
+ throw new Error('cache writer is no longer active');
414
+ }
415
+ export function assertSynchronousResult(result, description) {
416
+ if (result !== null &&
417
+ (typeof result === 'object' || typeof result === 'function') &&
418
+ typeof result.then === 'function') {
419
+ void Promise.resolve(result).catch(() => undefined);
420
+ throw new TypeError(`${description} must be synchronous`);
421
+ }
422
+ }
423
+ export function validateRecordKeys(keys) {
424
+ const seen = new Set();
425
+ for (const key of keys) {
426
+ validateRecordKey(key);
427
+ if (seen.has(key))
428
+ throw new TypeError(`duplicate record in index: ${key}`);
429
+ seen.add(key);
430
+ }
431
+ }
432
+ export function revisionToken(value) {
433
+ if (typeof value === 'bigint') {
434
+ if (value < 0n)
435
+ throw new TypeError('revision must be an unsigned integer');
436
+ return value;
437
+ }
438
+ if (typeof value === 'number') {
439
+ if (!Number.isSafeInteger(value) || value < 0) {
440
+ throw new TypeError('numeric revision must be an unsigned safe integer');
441
+ }
442
+ return BigInt(value);
443
+ }
444
+ if (!/^(0|[1-9][0-9]*)$/.test(value)) {
445
+ throw new TypeError('string revision must be a canonical unsigned integer');
446
+ }
447
+ return BigInt(value);
448
+ }
449
+ export function compareRecordTuple(leftIncarnation, leftRevision, rightIncarnation, rightRevision) {
450
+ if (leftIncarnation < rightIncarnation)
451
+ return -1;
452
+ if (leftIncarnation > rightIncarnation)
453
+ return 1;
454
+ if (leftRevision < rightRevision)
455
+ return -1;
456
+ if (leftRevision > rightRevision)
457
+ return 1;
458
+ return 0;
459
+ }
460
+ export function revisionString(value) {
461
+ return value.toString(10);
462
+ }
463
+ export function cloneFields(fields) {
464
+ if (fields === undefined)
465
+ return Object.freeze({});
466
+ return freezeRecord(Object.entries(fields).map(([name, value]) => {
467
+ assertName(name, 'record field');
468
+ return [name, cloneCacheValue(value)];
469
+ }));
470
+ }
471
+ export function cloneLinks(links) {
472
+ if (links === undefined)
473
+ return Object.freeze({});
474
+ return freezeRecord(Object.entries(links).map(([name, value]) => {
475
+ assertName(name, 'record link');
476
+ return [name, cloneLink(value)];
477
+ }));
478
+ }
479
+ export function cloneLink(value) {
480
+ if (value === null)
481
+ return null;
482
+ if (typeof value === 'string') {
483
+ validateRecordKey(value);
484
+ return value;
485
+ }
486
+ if (!Array.isArray(value))
487
+ throw new TypeError('record link must be a key, key array, or null');
488
+ validateRecordKeys(value);
489
+ return Object.freeze([...value]);
490
+ }
491
+ export function linkKeys(value) {
492
+ if (value === null)
493
+ return [];
494
+ return typeof value === 'string' ? [value] : value;
495
+ }
496
+ export function cloneCacheValue(value, ancestors = new Set()) {
497
+ if (value === null ||
498
+ typeof value === 'string' ||
499
+ typeof value === 'boolean' ||
500
+ typeof value === 'number') {
501
+ if (typeof value === 'number' && !Number.isFinite(value)) {
502
+ throw new TypeError('cache numbers must be finite');
503
+ }
504
+ return value;
505
+ }
506
+ if (typeof value !== 'object') {
507
+ throw new TypeError('cache fields must contain JSON-compatible values; omit absent fields');
508
+ }
509
+ if (ancestors.has(value))
510
+ throw new TypeError('cache fields must not contain cycles');
511
+ ancestors.add(value);
512
+ let cloned;
513
+ if (Array.isArray(value)) {
514
+ cloned = Object.freeze(value.map((entry) => cloneCacheValue(entry, ancestors)));
515
+ }
516
+ else {
517
+ const prototype = Object.getPrototypeOf(value);
518
+ if (prototype !== Object.prototype && prototype !== null) {
519
+ throw new TypeError('cache objects must be plain JSON objects');
520
+ }
521
+ cloned = freezeRecord(Object.entries(value).map(([key, entry]) => [key, cloneCacheValue(entry, ancestors)]));
522
+ }
523
+ ancestors.delete(value);
524
+ return cloned;
525
+ }
526
+ export function canonicalValue(value) {
527
+ if (value === null || typeof value !== 'object')
528
+ return JSON.stringify(value);
529
+ if (Array.isArray(value))
530
+ return `[${value.map(canonicalValue).join(',')}]`;
531
+ const record = value;
532
+ return `{${Object.keys(record)
533
+ .sort()
534
+ .map((key) => `${JSON.stringify(key)}:${canonicalValue(record[key])}`)
535
+ .join(',')}}`;
536
+ }
537
+ export function cacheIndexKey(input) {
538
+ assertName(input.field, 'index field');
539
+ if (input.parent !== undefined)
540
+ validateRecordKey(input.parent);
541
+ const argumentsValue = cloneCacheValue(input.arguments ?? {});
542
+ return `${input.parent ?? '$root'}.${input.field}(${canonicalValue(argumentsValue)})`;
543
+ }
544
+ export { assertName, deepEqual, freezeRecord, reportSafely };
545
+ export const reportUnhandledWatcherError = reportUnhandledError;
@@ -0,0 +1,3 @@
1
+ export type { BaseCacheWriter, BaseRecordClock, CacheEngine, CacheEngineOptions, CacheEngineSnapshot, CacheIndex, CacheIndexCoverage, CacheIndexMetadata, CacheListener, CachePresence, CacheReader, CacheSelector, CacheValue, DerivedIndexMutation, DerivedIndexReconciler, IndexKey, IndexWrite, OptimisticCacheWriter, OptimisticIndexWrite, OptimisticLayerContext, OptimisticLayerState, OptimisticLayerView, OptimisticRecordWrite, RecordKey, RecordLink, RecordWrite, Revision, SparseRecord, SparseRecordMeta, WatchOptions } from './types.js';
2
+ export { CacheRevisionConflictError } from './errors.js';
3
+ export { cacheIndexKey, createCacheEngine } from './create.js';
@@ -0,0 +1,2 @@
1
+ export { CacheRevisionConflictError } from './errors.js';
2
+ export { cacheIndexKey, createCacheEngine } from './create.js';