@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,558 @@
1
+ /**
2
+ * Versioned framework metadata carried in GraphQL's top-level
3
+ * `extensions.distributed` response envelope.
4
+ *
5
+ * Domain result objects never contain these values. Hidden identities and
6
+ * positions remain opaque strings: the JavaScript client compares or returns
7
+ * them, but never parses them as numbers or reconstructs server scopes.
8
+ */
9
+ /** The only Distributed GraphQL protocol version understood by this package. */
10
+ export const DISTRIBUTED_PROTOCOL_VERSION = 1;
11
+ /** Safe parse failure that reports structure, never hidden server values. */
12
+ export class DistributedProtocolError extends Error {
13
+ code;
14
+ path;
15
+ constructor(code, path) {
16
+ super(code === 'DISTRIBUTED_PROTOCOL_VERSION_UNSUPPORTED'
17
+ ? 'Unsupported Distributed GraphQL protocol version'
18
+ : `Invalid Distributed GraphQL protocol envelope at ${path}`);
19
+ this.name = 'DistributedProtocolError';
20
+ this.code = code;
21
+ this.path = path;
22
+ }
23
+ }
24
+ const COMMAND_STATES = new Set([
25
+ 'in_progress',
26
+ 'accepted',
27
+ 'accepted_pending_projection',
28
+ 'projected',
29
+ 'rejected',
30
+ 'projection_failed',
31
+ 'expired',
32
+ 'unknown'
33
+ ]);
34
+ const COMMAND_CONSISTENCIES = new Set([
35
+ 'accepted',
36
+ 'fact',
37
+ 'projected'
38
+ ]);
39
+ const MAX_PUBLIC_NAME_LENGTH = 512;
40
+ const MAX_OPAQUE_STRING_LENGTH = 16_384;
41
+ const MAX_EVIDENCE_ITEMS = 4_096;
42
+ const MAX_LIVE_RESUME_CURSORS = 64;
43
+ const MAX_PATH_SEGMENTS = 256;
44
+ const MAX_TRUSTED_PRESETS = 4_096;
45
+ const MAX_TRUSTED_PRESET_NAME_LENGTH = 128;
46
+ const MAX_TRUSTED_PRESET_VALUE_DEPTH = 64;
47
+ const MAX_UNSIGNED_64 = '18446744073709551615';
48
+ const MAX_SAFE_INTEGER = 9_007_199_254_740_991;
49
+ const CANONICAL_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
50
+ /**
51
+ * Parse GraphQL's optional top-level `extensions` object.
52
+ *
53
+ * Non-Distributed extension keys are preserved. When `distributed` is present,
54
+ * its required version/scope/schema fields and known receipt/evidence fields are
55
+ * validated. A malformed or incompatible envelope throws
56
+ * {@link DistributedProtocolError}; transports convert that into a fail-closed
57
+ * GraphQL result/error rather than exposing the accompanying data as trusted.
58
+ */
59
+ export function parseGraphqlResponseExtensions(value) {
60
+ if (value === undefined)
61
+ return undefined;
62
+ const extensions = record(value, 'extensions');
63
+ if (extensions.distributed === undefined) {
64
+ return Object.freeze({ ...extensions });
65
+ }
66
+ return Object.freeze({
67
+ ...extensions,
68
+ distributed: parseDistributedProtocolEnvelope(extensions.distributed)
69
+ });
70
+ }
71
+ /** Parse one `extensions.distributed` value independently of a transport. */
72
+ export function parseDistributedProtocolEnvelope(value) {
73
+ const envelope = record(value, 'extensions.distributed');
74
+ if (envelope.protocolVersion !== DISTRIBUTED_PROTOCOL_VERSION) {
75
+ if (typeof envelope.protocolVersion === 'number' &&
76
+ Number.isInteger(envelope.protocolVersion)) {
77
+ throw new DistributedProtocolError('DISTRIBUTED_PROTOCOL_VERSION_UNSUPPORTED', 'extensions.distributed.protocolVersion');
78
+ }
79
+ invalid('extensions.distributed.protocolVersion');
80
+ }
81
+ const schemaHash = publicString(envelope.schemaHash, 'extensions.distributed.schemaHash');
82
+ const cacheScope = opaqueString(envelope.cacheScope, 'extensions.distributed.cacheScope');
83
+ const operation = envelope.operation === undefined
84
+ ? undefined
85
+ : publicString(envelope.operation, 'extensions.distributed.operation');
86
+ const command = envelope.command === undefined
87
+ ? undefined
88
+ : parseCommand(envelope.command);
89
+ const snapshot = envelope.snapshot === undefined
90
+ ? undefined
91
+ : parseSnapshot(envelope.snapshot);
92
+ const live = envelope.live === undefined
93
+ ? undefined
94
+ : parseLive(envelope.live);
95
+ validateLiveSnapshot(snapshot, live);
96
+ const trustedPresets = parseDistributedTrustedPresetInventory(envelope.trustedPresets);
97
+ return Object.freeze({
98
+ ...envelope,
99
+ protocolVersion: DISTRIBUTED_PROTOCOL_VERSION,
100
+ schemaHash,
101
+ cacheScope,
102
+ ...(operation === undefined ? {} : { operation }),
103
+ ...(command === undefined ? {} : { command }),
104
+ ...(snapshot === undefined ? {} : { snapshot }),
105
+ ...(live === undefined ? {} : { live }),
106
+ trustedPresets
107
+ });
108
+ }
109
+ /**
110
+ * Parse and deeply freeze one scope-wide trusted-preset value inventory.
111
+ *
112
+ * This is exported for the framework-neutral replica's internal command seam.
113
+ * Applications receive the same values through
114
+ * {@link parseDistributedProtocolEnvelope}; they must never supply this
115
+ * inventory as command authority.
116
+ *
117
+ * @internal
118
+ */
119
+ export function parseDistributedTrustedPresetInventory(value, path = 'extensions.distributed.trustedPresets') {
120
+ if (value === undefined)
121
+ return Object.freeze([]);
122
+ if (!Array.isArray(value) || value.length > MAX_TRUSTED_PRESETS) {
123
+ invalid(path);
124
+ }
125
+ const names = new Set();
126
+ const presets = value.map((candidate, index) => {
127
+ const itemPath = `${path}[${index}]`;
128
+ const item = record(candidate, itemPath);
129
+ const name = trustedPresetName(item.name, `${itemPath}.name`);
130
+ if (names.has(name))
131
+ invalid(`${itemPath}.name`);
132
+ names.add(name);
133
+ const codec = trustedPresetCodec(item.codec, `${itemPath}.codec`);
134
+ return Object.freeze({
135
+ name,
136
+ codec,
137
+ value: parseTrustedPresetValue(item.value, codec, `${itemPath}.value`)
138
+ });
139
+ });
140
+ return Object.freeze(presets);
141
+ }
142
+ /**
143
+ * Build the private request extension for a generated live resume.
144
+ *
145
+ * The server remains authoritative and verifies every token. This client-side
146
+ * check only prevents malformed, duplicate, or unbounded state from leaving
147
+ * the replica.
148
+ *
149
+ * @internal
150
+ */
151
+ export function distributedLiveResumeExtensions(value) {
152
+ if (!Array.isArray(value) ||
153
+ value.length === 0 ||
154
+ value.length > MAX_LIVE_RESUME_CURSORS) {
155
+ invalid('request.extensions.distributed.resume.cursors');
156
+ }
157
+ const cursors = Object.freeze(value.map((cursor, index) => parseLiveCursor(cursor, `request.extensions.distributed.resume.cursors[${index}]`)));
158
+ assertUnique(cursors.map((cursor) => cursor.projection), 'request.extensions.distributed.resume.cursors');
159
+ return Object.freeze({
160
+ distributed: Object.freeze({
161
+ resume: Object.freeze({ cursors })
162
+ })
163
+ });
164
+ }
165
+ function parseCommand(value) {
166
+ const command = record(value, 'extensions.distributed.command');
167
+ const state = enumString(command.state, COMMAND_STATES, 'extensions.distributed.command.state');
168
+ const consistency = enumString(command.consistency, COMMAND_CONSISTENCIES, 'extensions.distributed.command.consistency');
169
+ if (!Array.isArray(command.expects)) {
170
+ invalid('extensions.distributed.command.expects');
171
+ }
172
+ if (command.expects.length > MAX_EVIDENCE_ITEMS) {
173
+ invalid('extensions.distributed.command.expects');
174
+ }
175
+ const expects = command.expects.map((expectation, index) => {
176
+ const path = `extensions.distributed.command.expects[${index}]`;
177
+ const item = record(expectation, path);
178
+ return Object.freeze({
179
+ ...item,
180
+ projection: publicString(item.projection, `${path}.projection`),
181
+ model: publicString(item.model, `${path}.model`),
182
+ scopeToken: opaqueString(item.scopeToken, `${path}.scopeToken`)
183
+ });
184
+ });
185
+ const observations = parseOptionalEvidenceArray(command.observations, 'extensions.distributed.command.observations', parseProjectionObservation);
186
+ const records = parseOptionalEvidenceArray(command.records, 'extensions.distributed.command.records', parseRecordRevision);
187
+ const commandId = opaqueString(command.commandId, 'extensions.distributed.command.commandId');
188
+ const causationId = opaqueString(command.causationId, 'extensions.distributed.command.causationId');
189
+ const expectationKeys = new Set(expects.map(projectionExpectationKey));
190
+ const seenObservations = new Set();
191
+ for (const observation of observations) {
192
+ const key = projectionObservationKey(observation);
193
+ if (observation.causationId !== causationId ||
194
+ !expectationKeys.has(projectionExpectationKey(observation)) ||
195
+ seenObservations.has(key)) {
196
+ invalid('extensions.distributed.command.observations');
197
+ }
198
+ seenObservations.add(key);
199
+ }
200
+ return Object.freeze({
201
+ ...command,
202
+ commandId,
203
+ causationId,
204
+ state,
205
+ consistency,
206
+ expects: Object.freeze(expects),
207
+ observations,
208
+ records
209
+ });
210
+ }
211
+ function parseSnapshot(value) {
212
+ const path = 'extensions.distributed.snapshot';
213
+ const snapshot = record(value, path);
214
+ if (typeof snapshot.recordsComplete !== 'boolean') {
215
+ invalid(`${path}.recordsComplete`);
216
+ }
217
+ if (typeof snapshot.indexesComparable !== 'boolean') {
218
+ invalid(`${path}.indexesComparable`);
219
+ }
220
+ const records = parseRequiredEvidenceArray(snapshot.records, `${path}.records`, parseRecordRevision);
221
+ const indexes = parseRequiredEvidenceArray(snapshot.indexes, `${path}.indexes`, parseIndexRevision);
222
+ const observations = parseRequiredEvidenceArray(snapshot.observations, `${path}.observations`, parseProjectionObservation);
223
+ assertUnique(indexes.map((index) => index.projection), `${path}.indexes`);
224
+ if (indexes.filter((index) => index.resume !== undefined).length >
225
+ MAX_LIVE_RESUME_CURSORS) {
226
+ invalid(`${path}.indexes`);
227
+ }
228
+ if (!snapshot.indexesComparable &&
229
+ (indexes.length > 0 || observations.length > 0)) {
230
+ invalid(`${path}.indexesComparable`);
231
+ }
232
+ return Object.freeze({
233
+ ...snapshot,
234
+ scopeToken: opaqueString(snapshot.scopeToken, `${path}.scopeToken`),
235
+ recordsComplete: snapshot.recordsComplete,
236
+ indexesComparable: snapshot.indexesComparable,
237
+ records,
238
+ indexes,
239
+ observations
240
+ });
241
+ }
242
+ function parseLive(value) {
243
+ const path = 'extensions.distributed.live';
244
+ const live = record(value, path);
245
+ if (typeof live.supported !== 'boolean')
246
+ invalid(`${path}.supported`);
247
+ if (typeof live.reset !== 'boolean')
248
+ invalid(`${path}.reset`);
249
+ if (!Array.isArray(live.cursors) ||
250
+ live.cursors.length > MAX_LIVE_RESUME_CURSORS) {
251
+ invalid(`${path}.cursors`);
252
+ }
253
+ const cursors = parseRequiredEvidenceArray(live.cursors, `${path}.cursors`, parseLiveCursor);
254
+ assertUnique(cursors.map((cursor) => cursor.projection), `${path}.cursors`);
255
+ if (!live.supported && (!live.reset || cursors.length !== 0)) {
256
+ invalid(path);
257
+ }
258
+ return Object.freeze({
259
+ ...live,
260
+ supported: live.supported,
261
+ reset: live.reset,
262
+ cursors
263
+ });
264
+ }
265
+ function validateLiveSnapshot(snapshot, live) {
266
+ if (live === undefined)
267
+ return;
268
+ if (snapshot === undefined)
269
+ invalid('extensions.distributed.snapshot');
270
+ if (!live.supported)
271
+ return;
272
+ if (!snapshot.indexesComparable) {
273
+ invalid('extensions.distributed.snapshot.indexesComparable');
274
+ }
275
+ if (live.cursors.length === 0 ||
276
+ snapshot.indexes.length !== live.cursors.length) {
277
+ invalid('extensions.distributed.live.cursors');
278
+ }
279
+ const indexes = new Map(snapshot.indexes.map((index) => [index.projection, index]));
280
+ for (const cursor of live.cursors) {
281
+ const index = indexes.get(cursor.projection);
282
+ if (index === undefined ||
283
+ index.position !== cursor.position ||
284
+ index.resume === undefined ||
285
+ index.resume.token !== cursor.token) {
286
+ invalid('extensions.distributed.live.cursors');
287
+ }
288
+ }
289
+ }
290
+ function parseRecordRevision(value, path) {
291
+ const item = record(value, path);
292
+ let responsePath;
293
+ if (item.path !== undefined) {
294
+ if (!Array.isArray(item.path) ||
295
+ item.path.length === 0 ||
296
+ item.path.length > MAX_PATH_SEGMENTS) {
297
+ invalid(`${path}.path`);
298
+ }
299
+ responsePath = Object.freeze(item.path.map((segment, index) => publicString(segment, `${path}.path[${index}]`)));
300
+ }
301
+ if (typeof item.tombstone !== 'boolean')
302
+ invalid(`${path}.tombstone`);
303
+ return Object.freeze({
304
+ ...item,
305
+ ...(responsePath === undefined ? {} : { path: responsePath }),
306
+ model: publicString(item.model, `${path}.model`),
307
+ scopeToken: opaqueString(item.scopeToken, `${path}.scopeToken`),
308
+ incarnation: canonicalDecimal(item.incarnation, `${path}.incarnation`),
309
+ revision: canonicalDecimal(item.revision, `${path}.revision`),
310
+ tombstone: item.tombstone
311
+ });
312
+ }
313
+ function parseProjectionObservation(value, path) {
314
+ const item = record(value, path);
315
+ return Object.freeze({
316
+ ...item,
317
+ causationId: opaqueString(item.causationId, `${path}.causationId`),
318
+ projection: publicString(item.projection, `${path}.projection`),
319
+ model: publicString(item.model, `${path}.model`),
320
+ scopeToken: opaqueString(item.scopeToken, `${path}.scopeToken`)
321
+ });
322
+ }
323
+ function parseIndexRevision(value, path) {
324
+ const item = record(value, path);
325
+ const projection = publicString(item.projection, `${path}.projection`);
326
+ const position = canonicalDecimal(item.position, `${path}.position`);
327
+ const resume = item.resume === undefined
328
+ ? undefined
329
+ : parseLiveCursor(item.resume, `${path}.resume`);
330
+ if (resume !== undefined &&
331
+ (resume.projection !== projection || resume.position !== position)) {
332
+ invalid(`${path}.resume`);
333
+ }
334
+ return Object.freeze({
335
+ ...item,
336
+ projection,
337
+ scopeToken: opaqueString(item.scopeToken, `${path}.scopeToken`),
338
+ position,
339
+ ...(resume === undefined ? {} : { resume })
340
+ });
341
+ }
342
+ function parseLiveCursor(value, path) {
343
+ const item = record(value, path);
344
+ return Object.freeze({
345
+ ...item,
346
+ projection: publicString(item.projection, `${path}.projection`),
347
+ position: canonicalDecimal(item.position, `${path}.position`),
348
+ token: opaqueString(item.token, `${path}.token`)
349
+ });
350
+ }
351
+ function parseRequiredEvidenceArray(value, path, parse) {
352
+ if (!Array.isArray(value) || value.length > MAX_EVIDENCE_ITEMS)
353
+ invalid(path);
354
+ return Object.freeze(value.map((item, index) => parse(item, `${path}[${index}]`)));
355
+ }
356
+ function parseOptionalEvidenceArray(value, path, parse) {
357
+ return value === undefined
358
+ ? Object.freeze([])
359
+ : parseRequiredEvidenceArray(value, path, parse);
360
+ }
361
+ function projectionExpectationKey(value) {
362
+ return JSON.stringify([value.projection, value.model, value.scopeToken]);
363
+ }
364
+ function projectionObservationKey(value) {
365
+ return JSON.stringify([
366
+ value.causationId,
367
+ value.projection,
368
+ value.model,
369
+ value.scopeToken
370
+ ]);
371
+ }
372
+ function assertUnique(values, path) {
373
+ if (new Set(values).size !== values.length)
374
+ invalid(path);
375
+ }
376
+ function record(value, path) {
377
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
378
+ invalid(path);
379
+ }
380
+ const prototype = Object.getPrototypeOf(value);
381
+ if (prototype !== Object.prototype && prototype !== null)
382
+ invalid(path);
383
+ return value;
384
+ }
385
+ function publicString(value, path) {
386
+ if (typeof value !== 'string' ||
387
+ value.length === 0 ||
388
+ value.length > MAX_PUBLIC_NAME_LENGTH) {
389
+ invalid(path);
390
+ }
391
+ return value;
392
+ }
393
+ function trustedPresetName(value, path) {
394
+ if (typeof value !== 'string' ||
395
+ value.length === 0 ||
396
+ value.length > MAX_TRUSTED_PRESET_NAME_LENGTH ||
397
+ value.trim() !== value ||
398
+ /[\u0000-\u001f\u007f-\u009f]/.test(value)) {
399
+ invalid(path);
400
+ }
401
+ return value;
402
+ }
403
+ /** @internal */
404
+ export function isDistributedTrustedPresetCodec(value) {
405
+ return (value === 'string' ||
406
+ value === 'string_unvalidated_timestamp' ||
407
+ value === 'base64' ||
408
+ value === 'boolean' ||
409
+ value === 'int32' ||
410
+ value === 'float64' ||
411
+ value === 'json_number_precision_limited' ||
412
+ value === 'json');
413
+ }
414
+ function trustedPresetCodec(value, path) {
415
+ if (!isDistributedTrustedPresetCodec(value))
416
+ invalid(path);
417
+ return value;
418
+ }
419
+ function parseTrustedPresetValue(value, codec, path) {
420
+ switch (codec) {
421
+ case 'string':
422
+ case 'string_unvalidated_timestamp':
423
+ if (typeof value !== 'string')
424
+ invalid(path);
425
+ return value;
426
+ case 'base64':
427
+ if (typeof value !== 'string' || !CANONICAL_BASE64.test(value)) {
428
+ invalid(path);
429
+ }
430
+ return value;
431
+ case 'boolean':
432
+ if (typeof value !== 'boolean')
433
+ invalid(path);
434
+ return value;
435
+ case 'int32':
436
+ if (typeof value !== 'number' ||
437
+ !Number.isInteger(value) ||
438
+ Object.is(value, -0) ||
439
+ value < -2_147_483_648 ||
440
+ value > 2_147_483_647) {
441
+ invalid(path);
442
+ }
443
+ return value;
444
+ case 'json_number_precision_limited':
445
+ if (typeof value !== 'number' ||
446
+ !Number.isInteger(value) ||
447
+ Object.is(value, -0) ||
448
+ value < -MAX_SAFE_INTEGER ||
449
+ value > MAX_SAFE_INTEGER) {
450
+ invalid(path);
451
+ }
452
+ return value;
453
+ case 'float64':
454
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
455
+ invalid(path);
456
+ }
457
+ return Object.is(value, -0) ? 0 : value;
458
+ case 'json':
459
+ return cloneProtocolValue(value, path);
460
+ }
461
+ }
462
+ function cloneProtocolValue(value, path, active = new Set(), depth = 0) {
463
+ if (depth > MAX_TRUSTED_PRESET_VALUE_DEPTH)
464
+ invalid(path);
465
+ if (value === null ||
466
+ typeof value === 'string' ||
467
+ typeof value === 'boolean') {
468
+ return value;
469
+ }
470
+ if (typeof value === 'number') {
471
+ if (!Number.isFinite(value))
472
+ invalid(path);
473
+ return Object.is(value, -0) ? 0 : value;
474
+ }
475
+ if (typeof value !== 'object' || active.has(value))
476
+ invalid(path);
477
+ active.add(value);
478
+ if (Array.isArray(value)) {
479
+ if (Object.getPrototypeOf(value) !== Array.prototype)
480
+ invalid(path);
481
+ const ownKeys = Reflect.ownKeys(value);
482
+ if (ownKeys.some((key) => key !== 'length' &&
483
+ (typeof key !== 'string' ||
484
+ !/^(0|[1-9][0-9]*)$/.test(key) ||
485
+ Number(key) >= value.length))) {
486
+ invalid(path);
487
+ }
488
+ const result = [];
489
+ for (let index = 0; index < value.length; index += 1) {
490
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
491
+ if (descriptor === undefined ||
492
+ !('value' in descriptor) ||
493
+ descriptor.value === undefined) {
494
+ invalid(`${path}[${index}]`);
495
+ }
496
+ result.push(cloneProtocolValue(descriptor.value, `${path}[${index}]`, active, depth + 1));
497
+ }
498
+ active.delete(value);
499
+ return Object.freeze(result);
500
+ }
501
+ const prototype = Object.getPrototypeOf(value);
502
+ if (prototype !== Object.prototype && prototype !== null)
503
+ invalid(path);
504
+ const keys = Reflect.ownKeys(value);
505
+ if (keys.some((key) => typeof key !== 'string'))
506
+ invalid(path);
507
+ const result = {};
508
+ for (const key of keys.sort()) {
509
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
510
+ if (descriptor === undefined ||
511
+ !('value' in descriptor) ||
512
+ descriptor.value === undefined) {
513
+ invalid(`${path}.${key}`);
514
+ }
515
+ Object.defineProperty(result, key, {
516
+ value: cloneProtocolValue(descriptor.value, `${path}.${key}`, active, depth + 1),
517
+ enumerable: true,
518
+ configurable: false,
519
+ writable: false
520
+ });
521
+ }
522
+ active.delete(value);
523
+ return Object.freeze(result);
524
+ }
525
+ function opaqueString(value, path) {
526
+ if (typeof value !== 'string' ||
527
+ value.length === 0 ||
528
+ value.length > MAX_OPAQUE_STRING_LENGTH) {
529
+ invalid(path);
530
+ }
531
+ return value;
532
+ }
533
+ function canonicalDecimal(value, path) {
534
+ if (typeof value !== 'string' ||
535
+ !/^(0|[1-9][0-9]*)$/.test(value) ||
536
+ value.length > MAX_UNSIGNED_64.length ||
537
+ (value.length === MAX_UNSIGNED_64.length && value > MAX_UNSIGNED_64)) {
538
+ invalid(path);
539
+ }
540
+ return value;
541
+ }
542
+ /**
543
+ * Compare two already-validated protocol decimals without converting either
544
+ * through JavaScript's numeric types.
545
+ */
546
+ export function compareDistributedDecimal(left, right) {
547
+ if (left.length !== right.length)
548
+ return left.length < right.length ? -1 : 1;
549
+ return left === right ? 0 : left < right ? -1 : 1;
550
+ }
551
+ function enumString(value, values, path) {
552
+ if (typeof value !== 'string' || !values.has(value))
553
+ invalid(path);
554
+ return value;
555
+ }
556
+ function invalid(path) {
557
+ throw new DistributedProtocolError('DISTRIBUTED_PROTOCOL_INVALID', path);
558
+ }
@@ -0,0 +1,20 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ import type { DistributedReplica } from '../replica/types.js';
3
+ export type DistributedProviderProps = {
4
+ /**
5
+ * One framework-neutral replica for this browser authorization lifecycle, or
6
+ * one fresh replica for this server-render request.
7
+ */
8
+ readonly replica: DistributedReplica;
9
+ readonly children?: ReactNode;
10
+ };
11
+ /**
12
+ * Makes an existing framework-neutral replica available to React.
13
+ *
14
+ * The provider deliberately does not create, hydrate, authorize, or retain a
15
+ * replica. Those lifecycles stay in the shared core and application
16
+ * composition, which keeps SSR request isolation explicit.
17
+ */
18
+ export declare function DistributedProvider({ replica, children }: DistributedProviderProps): ReactElement;
19
+ /** Read the exact replica supplied by the nearest DistributedProvider. */
20
+ export declare function useDistributedReplica(): DistributedReplica;
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+ import { createContext, createElement, useContext } from 'react';
3
+ const DistributedReplicaContext = createContext(undefined);
4
+ /**
5
+ * Makes an existing framework-neutral replica available to React.
6
+ *
7
+ * The provider deliberately does not create, hydrate, authorize, or retain a
8
+ * replica. Those lifecycles stay in the shared core and application
9
+ * composition, which keeps SSR request isolation explicit.
10
+ */
11
+ export function DistributedProvider({ replica, children }) {
12
+ if (replica === undefined || replica === null) {
13
+ throw new TypeError('DistributedProvider requires a replica');
14
+ }
15
+ return createElement(DistributedReplicaContext.Provider, { value: replica }, children);
16
+ }
17
+ /** Read the exact replica supplied by the nearest DistributedProvider. */
18
+ export function useDistributedReplica() {
19
+ const replica = useContext(DistributedReplicaContext);
20
+ if (replica === undefined) {
21
+ throw new Error('useDistributedReplica must be used inside a DistributedProvider');
22
+ }
23
+ return replica;
24
+ }
@@ -0,0 +1,2 @@
1
+ export { DistributedProvider, useDistributedReplica, type DistributedProviderProps } from './context.js';
2
+ export { useDistributedQuery, type DistributedQueryResult } from './query.js';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ export { DistributedProvider, useDistributedReplica } from './context.js';
3
+ export { useDistributedQuery } from './query.js';
@@ -0,0 +1,20 @@
1
+ import type { GraphqlVariables } from '../types.js';
2
+ import type { ReplicaOperationArtifact, ReplicaSnapshot, WatchReplicaOptions } from '../replica/types.js';
3
+ type UseDistributedQueryArguments<TVariables extends GraphqlVariables> = keyof TVariables extends never ? readonly [
4
+ variables?: TVariables,
5
+ options?: WatchReplicaOptions
6
+ ] : readonly [
7
+ variables: TVariables,
8
+ options?: WatchReplicaOptions
9
+ ];
10
+ export type DistributedQueryResult<TData> = ReplicaSnapshot<TData> & {
11
+ /** Force the shared cache-and-live coordinator to revalidate this operation. */
12
+ readonly refresh: () => Promise<void>;
13
+ };
14
+ /**
15
+ * Bind a generated operation artifact to React without introducing a React
16
+ * cache. All reads, fetches, live updates, optimism, and authorization fences
17
+ * continue through the supplied DistributedReplica.
18
+ */
19
+ export declare function useDistributedQuery<TData, TVariables extends GraphqlVariables>(artifact: ReplicaOperationArtifact<TData, TVariables>, ...args: UseDistributedQueryArguments<TVariables>): DistributedQueryResult<TData>;
20
+ export {};