@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,14 @@
1
+ import type { GraphqlVariables } from '../types.js';
2
+ import type { ReplicaOperationArtifact, ReplicaSurfaceTrustedPresetDescriptor } from './types.js';
3
+ export type ValidatedReplicaOperationBinding = {
4
+ readonly version: 1;
5
+ readonly schemaHash: string;
6
+ readonly operation: string;
7
+ readonly surfaceIdentity: string;
8
+ readonly trustedPresets: readonly ReplicaSurfaceTrustedPresetDescriptor[];
9
+ };
10
+ /**
11
+ * Validate the exact compiler-owned protocol identity before an operation may
12
+ * acquire a cache key, register a maintenance plan, or reach a transport.
13
+ */
14
+ export declare function validateReplicaOperationBinding<TData, TVariables extends GraphqlVariables>(artifact: ReplicaOperationArtifact<TData, TVariables>): ValidatedReplicaOperationBinding;
@@ -0,0 +1,76 @@
1
+ import { isDistributedTrustedPresetCodec } from '../protocol.js';
2
+ /**
3
+ * Validate the exact compiler-owned protocol identity before an operation may
4
+ * acquire a cache key, register a maintenance plan, or reach a transport.
5
+ */
6
+ export function validateReplicaOperationBinding(artifact) {
7
+ if (typeof artifact.id !== 'string' || artifact.id.length === 0) {
8
+ throw new TypeError('replica artifact id must be a non-empty string');
9
+ }
10
+ const binding = artifact.protocol;
11
+ if (binding === undefined ||
12
+ binding.version !== 1 ||
13
+ typeof binding.schemaHash !== 'string' ||
14
+ binding.schemaHash.length === 0 ||
15
+ typeof binding.operation !== 'string' ||
16
+ binding.operation.length === 0 ||
17
+ binding.operation !== artifact.id) {
18
+ throw new TypeError('replica artifact protocol binding is invalid');
19
+ }
20
+ if (artifact.variableCodec === undefined) {
21
+ throw new TypeError('protocol-v1 replica artifact requires variableCodec');
22
+ }
23
+ return Object.freeze({
24
+ version: binding.version,
25
+ schemaHash: binding.schemaHash,
26
+ operation: binding.operation,
27
+ surfaceIdentity: validateReplicaSurfaceIdentity(binding.surface),
28
+ trustedPresets: canonicalTrustedPresetDescriptors(binding.trustedPresets)
29
+ });
30
+ }
31
+ function validateReplicaSurfaceIdentity(value) {
32
+ if (value === null ||
33
+ typeof value !== 'object' ||
34
+ typeof value.name !== 'string' ||
35
+ value.name.length === 0) {
36
+ throw new TypeError('replica artifact client surface is invalid');
37
+ }
38
+ if (value.kind === 'role') {
39
+ return JSON.stringify(['role', value.name]);
40
+ }
41
+ if (value.kind !== 'application' ||
42
+ !Array.isArray(value.roles) ||
43
+ value.roles.length === 0 ||
44
+ value.roles.some((role) => typeof role !== 'string' || role.length === 0) ||
45
+ new Set(value.roles).size !== value.roles.length ||
46
+ [...value.roles].sort().some((role, index) => role !== value.roles[index])) {
47
+ throw new TypeError('replica artifact client surface is invalid');
48
+ }
49
+ return JSON.stringify(['application', value.name, value.roles]);
50
+ }
51
+ function canonicalTrustedPresetDescriptors(value) {
52
+ if (!Array.isArray(value)) {
53
+ throw new TypeError('replica artifact trusted preset contract is invalid');
54
+ }
55
+ const names = new Set();
56
+ return Object.freeze(value
57
+ .map((candidate) => {
58
+ if (candidate === null ||
59
+ typeof candidate !== 'object' ||
60
+ typeof candidate.name !== 'string' ||
61
+ candidate.name.length === 0 ||
62
+ candidate.name.length > 128 ||
63
+ candidate.name.trim() !== candidate.name ||
64
+ /[\u0000-\u001f\u007f-\u009f]/.test(candidate.name) ||
65
+ names.has(candidate.name) ||
66
+ !isDistributedTrustedPresetCodec(candidate.codec)) {
67
+ throw new TypeError('replica artifact trusted preset contract is invalid');
68
+ }
69
+ names.add(candidate.name);
70
+ return Object.freeze({
71
+ name: candidate.name,
72
+ codec: candidate.codec
73
+ });
74
+ })
75
+ .sort(({ name: left }, { name: right }) => left < right ? -1 : left > right ? 1 : 0));
76
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReplicaAuthoritativeScope, ReplicaDehydratedState } from '../types.js';
2
+ import type { ReplicaIndexedDbFactory, ReplicaIndexedDbPersistence, ReplicaIndexedDbPersistenceOptions } from './types.js';
3
+ import { type NormalizedPolicy } from './state.js';
4
+ export declare class IndexedDbConfirmedStatePersistence implements ReplicaIndexedDbPersistence {
5
+ #private;
6
+ readonly supportsOfflineCommandOutbox: false;
7
+ constructor(factory: ReplicaIndexedDbFactory, databaseName: string, policy: NormalizedPolicy);
8
+ save(state: ReplicaDehydratedState): Promise<boolean>;
9
+ restore(authoritativeScope: ReplicaAuthoritativeScope): Promise<ReplicaDehydratedState | undefined>;
10
+ discard(authoritativeScope: ReplicaAuthoritativeScope): Promise<void>;
11
+ close(): void;
12
+ }
13
+ /**
14
+ * Create opt-in confirmed-state persistence. Merely creating a replica never
15
+ * opens IndexedDB; applications must explicitly create and call this adapter.
16
+ */
17
+ export declare function createReplicaIndexedDbPersistence(options?: ReplicaIndexedDbPersistenceOptions): ReplicaIndexedDbPersistence;
18
+ export declare function openDatabase(factory: ReplicaIndexedDbFactory, name: string): Promise<IDBDatabase>;
19
+ export declare function requestResult<T>(request: IDBRequest<T>): Promise<T>;
20
+ export declare function transactionResult(transaction: IDBTransaction): Promise<void>;
@@ -0,0 +1,193 @@
1
+ import { DATABASE_VERSION, DEFAULT_DATABASE_NAME, STORE_NAME, filterReplicaState, normalizePolicy, parseAuthoritativeScope, parseReplicaState, parseStoredEntry, persistenceIdentity, sameScope } from './state.js';
2
+ export class IndexedDbConfirmedStatePersistence {
3
+ supportsOfflineCommandOutbox = false;
4
+ #factory;
5
+ #databaseName;
6
+ #policy;
7
+ #databasePromise;
8
+ #closed = false;
9
+ constructor(factory, databaseName, policy) {
10
+ this.#factory = factory;
11
+ this.#databaseName = databaseName;
12
+ this.#policy = policy;
13
+ }
14
+ async save(state) {
15
+ this.#assertOpen();
16
+ const parsed = parseReplicaState(state);
17
+ const filtered = filterReplicaState(parsed, this.#policy);
18
+ const identity = persistenceIdentity(parsed.scope);
19
+ if (filtered === undefined) {
20
+ // A newly restrictive policy must not leave a previously durable copy.
21
+ await this.#delete(identity);
22
+ return false;
23
+ }
24
+ const entry = Object.freeze({
25
+ formatVersion: 1,
26
+ identity,
27
+ storedAt: Date.now(),
28
+ state: filtered
29
+ });
30
+ await this.#write(entry);
31
+ return true;
32
+ }
33
+ async restore(authoritativeScope) {
34
+ this.#assertOpen();
35
+ const scope = parseAuthoritativeScope(authoritativeScope);
36
+ const identity = persistenceIdentity(scope);
37
+ const raw = await this.#read(identity);
38
+ if (raw === undefined)
39
+ return undefined;
40
+ try {
41
+ const entry = parseStoredEntry(raw);
42
+ const parsed = parseReplicaState(entry.state);
43
+ if (entry.identity !== identity ||
44
+ !sameScope(parsed.scope, scope)) {
45
+ throw new TypeError('persisted replica scope does not match its key');
46
+ }
47
+ const filtered = filterReplicaState(parsed, this.#policy);
48
+ if (filtered === undefined) {
49
+ await this.#delete(identity);
50
+ return undefined;
51
+ }
52
+ // Reapply the current policy to the durable copy as well as the value
53
+ // returned to memory. This removes data made memory-only by a newer
54
+ // manifest instead of leaving it at rest under an older policy.
55
+ await this.#write(Object.freeze({
56
+ formatVersion: 1,
57
+ identity,
58
+ storedAt: entry.storedAt,
59
+ state: filtered
60
+ }));
61
+ return filtered;
62
+ }
63
+ catch {
64
+ // Never consume a questionable snapshot. Deletion is best-effort so an
65
+ // IndexedDB failure cannot turn corrupt data into accepted state.
66
+ try {
67
+ await this.#delete(identity);
68
+ }
69
+ catch {
70
+ // A later restore will reject the same entry again.
71
+ }
72
+ return undefined;
73
+ }
74
+ }
75
+ async discard(authoritativeScope) {
76
+ this.#assertOpen();
77
+ await this.#delete(persistenceIdentity(parseAuthoritativeScope(authoritativeScope)));
78
+ }
79
+ close() {
80
+ if (this.#closed)
81
+ return;
82
+ this.#closed = true;
83
+ const pending = this.#databasePromise;
84
+ if (pending !== undefined) {
85
+ void pending.then((database) => database.close(), () => undefined);
86
+ }
87
+ }
88
+ async #read(identity) {
89
+ return this.#transaction('readonly', (store) => requestResult(store.get(identity)));
90
+ }
91
+ async #write(entry) {
92
+ await this.#transaction('readwrite', async (store) => {
93
+ await requestResult(store.put(entry));
94
+ });
95
+ }
96
+ async #delete(identity) {
97
+ await this.#transaction('readwrite', async (store) => {
98
+ await requestResult(store.delete(identity));
99
+ });
100
+ }
101
+ async #transaction(mode, operation) {
102
+ const database = await this.#database();
103
+ this.#assertOpen();
104
+ const transaction = database.transaction(STORE_NAME, mode);
105
+ const completion = transactionResult(transaction);
106
+ try {
107
+ const result = await operation(transaction.objectStore(STORE_NAME));
108
+ await completion;
109
+ return result;
110
+ }
111
+ catch (error) {
112
+ try {
113
+ transaction.abort();
114
+ }
115
+ catch {
116
+ // It may already have completed or aborted.
117
+ }
118
+ await completion.catch(() => undefined);
119
+ throw error;
120
+ }
121
+ }
122
+ #database() {
123
+ this.#assertOpen();
124
+ this.#databasePromise ??= openDatabase(this.#factory, this.#databaseName);
125
+ return this.#databasePromise;
126
+ }
127
+ #assertOpen() {
128
+ if (this.#closed) {
129
+ throw new Error('replica IndexedDB persistence is closed');
130
+ }
131
+ }
132
+ }
133
+ /**
134
+ * Create opt-in confirmed-state persistence. Merely creating a replica never
135
+ * opens IndexedDB; applications must explicitly create and call this adapter.
136
+ */
137
+ export function createReplicaIndexedDbPersistence(options = {}) {
138
+ const factory = options.indexedDB ??
139
+ globalThis.indexedDB;
140
+ if (factory === undefined || typeof factory.open !== 'function') {
141
+ throw new TypeError('IndexedDB is unavailable in this runtime');
142
+ }
143
+ const databaseName = options.databaseName ?? DEFAULT_DATABASE_NAME;
144
+ if (typeof databaseName !== 'string' || databaseName.length === 0) {
145
+ throw new TypeError('replica persistence databaseName must be non-empty');
146
+ }
147
+ return new IndexedDbConfirmedStatePersistence(factory, databaseName, normalizePolicy(options.policy));
148
+ }
149
+ export function openDatabase(factory, name) {
150
+ return new Promise((resolve, reject) => {
151
+ let settled = false;
152
+ const request = factory.open(name, DATABASE_VERSION);
153
+ request.onupgradeneeded = () => {
154
+ const database = request.result;
155
+ if (!database.objectStoreNames.contains(STORE_NAME)) {
156
+ database.createObjectStore(STORE_NAME, { keyPath: 'identity' });
157
+ }
158
+ };
159
+ request.onsuccess = () => {
160
+ if (settled) {
161
+ request.result.close();
162
+ return;
163
+ }
164
+ settled = true;
165
+ resolve(request.result);
166
+ };
167
+ request.onerror = () => {
168
+ if (settled)
169
+ return;
170
+ settled = true;
171
+ reject(request.error ?? new Error('failed to open replica IndexedDB'));
172
+ };
173
+ request.onblocked = () => {
174
+ if (settled)
175
+ return;
176
+ settled = true;
177
+ reject(new Error('replica IndexedDB upgrade is blocked'));
178
+ };
179
+ });
180
+ }
181
+ export function requestResult(request) {
182
+ return new Promise((resolve, reject) => {
183
+ request.onsuccess = () => resolve(request.result);
184
+ request.onerror = () => reject(request.error ?? new Error('replica IndexedDB request failed'));
185
+ });
186
+ }
187
+ export function transactionResult(transaction) {
188
+ return new Promise((resolve, reject) => {
189
+ transaction.oncomplete = () => resolve();
190
+ transaction.onabort = () => reject(transaction.error ?? new Error('replica IndexedDB transaction aborted'));
191
+ transaction.onerror = () => reject(transaction.error ?? new Error('replica IndexedDB transaction failed'));
192
+ });
193
+ }
@@ -0,0 +1,4 @@
1
+ /** Re-export surface for persistence modules (body-split). */
2
+ export * from './types.js';
3
+ export * from './state.js';
4
+ export * from './idb.js';
@@ -0,0 +1,4 @@
1
+ /** Re-export surface for persistence modules (body-split). */
2
+ export * from './types.js';
3
+ export * from './state.js';
4
+ export * from './idb.js';
@@ -0,0 +1,2 @@
1
+ export { createReplicaIndexedDbPersistence, REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED } from './implementation.js';
2
+ export type { ReplicaIndexedDbFactory, ReplicaIndexedDbPersistence, ReplicaIndexedDbPersistenceOptions, ReplicaPersistenceModelPolicy, ReplicaPersistencePolicy } from './implementation.js';
@@ -0,0 +1 @@
1
+ export { createReplicaIndexedDbPersistence, REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED } from './implementation.js';
@@ -0,0 +1,104 @@
1
+ import { type CacheEngineSnapshot, type RecordLink } from '../../internal/cache-engine.js';
2
+ import { type DistributedTrustedPreset } from '../../protocol.js';
3
+ import type { ReplicaAuthoritativeScope, ReplicaDehydratedState } from '../types.js';
4
+ import type { ReplicaPersistenceModelPolicy, ReplicaPersistencePolicy } from './types.js';
5
+ export declare const DATABASE_VERSION = 1;
6
+ export declare const ENTRY_FORMAT_VERSION = 1;
7
+ export declare const STORE_NAME = "confirmed-replicas";
8
+ export declare const DEFAULT_DATABASE_NAME = "@hops-ops/distributed:replica";
9
+ export declare const GRAPHQL_NAME: RegExp;
10
+ export declare const DECIMAL: RegExp;
11
+ export type RecordClockV1 = {
12
+ readonly scopeToken: string;
13
+ readonly incarnation: string;
14
+ readonly revision: string;
15
+ readonly tombstone: boolean;
16
+ };
17
+ export type AnonymousRecordClockV1 = {
18
+ readonly model: string;
19
+ readonly clock: RecordClockV1;
20
+ };
21
+ export type OperationProtocolStateV1 = {
22
+ readonly operation: string;
23
+ readonly snapshotScope?: string;
24
+ readonly indexClocks: readonly (readonly [
25
+ string,
26
+ Readonly<{
27
+ scopeToken: string;
28
+ position: string;
29
+ }>
30
+ ])[];
31
+ readonly indexRevision?: string;
32
+ readonly indexKeys: readonly string[];
33
+ readonly pathRecords: readonly (readonly [string, string])[];
34
+ readonly cursors: readonly Readonly<{
35
+ projection: string;
36
+ position: string;
37
+ token: string;
38
+ }>[];
39
+ };
40
+ export type OperationProtocolGroupV1 = {
41
+ readonly key: string;
42
+ readonly query?: OperationProtocolStateV1;
43
+ readonly live?: OperationProtocolStateV1;
44
+ readonly active?: 'query' | 'live';
45
+ readonly generation: number;
46
+ };
47
+ export type ReplicaPersistencePayloadV1 = {
48
+ readonly cache: CacheEngineSnapshot;
49
+ readonly operations: readonly OperationProtocolGroupV1[];
50
+ readonly recordClocks: readonly (readonly [string, RecordClockV1])[];
51
+ readonly anonymousRecordClocks: readonly (readonly [
52
+ string,
53
+ AnonymousRecordClockV1
54
+ ])[];
55
+ readonly trustedPresets: readonly DistributedTrustedPreset[];
56
+ readonly nextIndexRevision: string;
57
+ };
58
+ export type ParsedReplicaState = {
59
+ readonly state: ReplicaDehydratedState;
60
+ readonly scope: ReplicaAuthoritativeScope;
61
+ readonly payload: ReplicaPersistencePayloadV1;
62
+ };
63
+ export type StoredReplicaEntry = {
64
+ readonly formatVersion: 1;
65
+ readonly identity: string;
66
+ readonly storedAt: number;
67
+ readonly state: ReplicaDehydratedState;
68
+ };
69
+ export type NormalizedPolicy = ReadonlyMap<string, ReplicaPersistenceModelPolicy>;
70
+ export declare function normalizePolicy(policy: ReplicaPersistencePolicy | undefined): NormalizedPolicy;
71
+ export declare function filterReplicaState(parsed: ParsedReplicaState, policy: NormalizedPolicy): ReplicaDehydratedState | undefined;
72
+ export declare function operationStateAllowed(state: OperationProtocolStateV1, indexes: ReadonlyMap<string, CacheEngineSnapshot['indexes'][number]>, recordClockKeys: ReadonlySet<string>, allowedRecord: (key: string) => boolean): boolean;
73
+ export declare function indexesProvenByOperations(candidates: ReadonlyMap<string, CacheEngineSnapshot['indexes'][number]>, states: Iterable<OperationProtocolStateV1>): Set<string>;
74
+ export declare function linkReferencesOnly(link: RecordLink, allowedLiveRecordKeys: ReadonlySet<string>): boolean;
75
+ export declare function parseReplicaState(value: unknown): ParsedReplicaState;
76
+ export declare function parseAuthoritativeScope(value: unknown): ReplicaAuthoritativeScope;
77
+ export declare function parseCacheSnapshot(value: unknown): CacheEngineSnapshot;
78
+ export declare function canonicalCacheSnapshot(value: CacheEngineSnapshot): CacheEngineSnapshot;
79
+ export declare function assertIndexMetadata(value: unknown, path: string): void;
80
+ export declare function assertCoverage(value: unknown, path: string): void;
81
+ export declare function parseOperations(value: unknown): readonly OperationProtocolGroupV1[];
82
+ export declare function parseOperationState(value: unknown, path: string): OperationProtocolStateV1;
83
+ export declare function parseRecordClocks(value: unknown): readonly (readonly [string, RecordClockV1])[];
84
+ export declare function parseAnonymousRecordClocks(value: unknown, recordClocks: readonly (readonly [string, RecordClockV1])[]): readonly (readonly [string, AnonymousRecordClockV1])[];
85
+ export declare function parseRecordClock(value: unknown, path: string): RecordClockV1;
86
+ export declare function assertMetadataConsistent(payload: ReplicaPersistencePayloadV1): void;
87
+ export declare function parseStoredEntry(value: unknown): StoredReplicaEntry;
88
+ export declare function persistenceIdentity(scope: ReplicaAuthoritativeScope): string;
89
+ export declare function sameScope(left: ReplicaAuthoritativeScope, right: ReplicaAuthoritativeScope): boolean;
90
+ export declare function modelFromRecordKey(key: string): string | undefined;
91
+ export declare function parseUniquePairs<T>(value: unknown, path: string, parseValue: (value: unknown, path: string) => T): readonly (readonly [string, T])[];
92
+ export declare function uniqueStrings(value: unknown, path: string): readonly string[];
93
+ export declare function assertRecordLink(value: unknown, path: string): void;
94
+ export declare function assertJsonValue(value: unknown, path: string, ancestors?: Set<object>): void;
95
+ export declare function exactRecord(value: unknown, path: string, allowed: readonly string[], required?: readonly string[]): Record<string, unknown>;
96
+ export declare function exactKeys(record: Record<string, unknown>, path: string, allowed: readonly string[], required?: readonly string[]): void;
97
+ export declare function plainRecord(value: unknown, path: string): Record<string, unknown>;
98
+ export declare function arrayValue(value: unknown, path: string): unknown[];
99
+ export declare function nonEmptyString(value: unknown, path: string): string;
100
+ export declare function decimalString(value: unknown, path: string): string;
101
+ export declare function booleanValue(value: unknown, path: string): boolean;
102
+ export declare function operationSource(value: unknown, path: string): 'query' | 'live';
103
+ export declare function nonNegativeInteger(value: unknown, path: string): number;
104
+ export declare function compareDecimal(left: string, right: string): -1 | 0 | 1;