@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,380 @@
1
+ import { compareDistributedDecimal, parseDistributedTrustedPresetInventory } from '../../protocol.js';
2
+ import { modelFromRecordKey } from './clocks.js';
3
+ import { canonicalTrustedPresets } from './helpers.js';
4
+ export function serializeOperationProtocolGroup(key, group, generation) {
5
+ return Object.freeze({
6
+ key,
7
+ ...(group.query === undefined
8
+ ? {}
9
+ : { query: serializeOperationProtocolState(group.query) }),
10
+ ...(group.live === undefined
11
+ ? {}
12
+ : { live: serializeOperationProtocolState(group.live) }),
13
+ ...(group.active === undefined ? {} : { active: group.active }),
14
+ generation
15
+ });
16
+ }
17
+ export function serializeOperationProtocolState(state) {
18
+ return Object.freeze({
19
+ operation: state.operation,
20
+ ...(state.snapshotScope === undefined
21
+ ? {}
22
+ : { snapshotScope: state.snapshotScope }),
23
+ indexClocks: Object.freeze([...state.indexClocks]
24
+ .sort(([left], [right]) => left.localeCompare(right))
25
+ .map(([projection, clock]) => Object.freeze([
26
+ projection,
27
+ Object.freeze({
28
+ scopeToken: clock.scopeToken,
29
+ position: clock.position
30
+ })
31
+ ]))),
32
+ ...(state.indexRevision === undefined
33
+ ? {}
34
+ : { indexRevision: state.indexRevision }),
35
+ indexKeys: Object.freeze([...state.indexKeys].sort()),
36
+ pathRecords: Object.freeze([...state.pathRecords]
37
+ .sort(([left], [right]) => left.localeCompare(right))
38
+ .map(([path, key]) => Object.freeze([path, key]))),
39
+ cursors: Object.freeze(state.cursors.map((cursor) => Object.freeze({
40
+ projection: cursor.projection,
41
+ position: cursor.position,
42
+ token: cursor.token
43
+ })))
44
+ });
45
+ }
46
+ export function parseAuthoritativeScope(value) {
47
+ try {
48
+ const scope = hydrationRecord(value, 'authoritativeScope', ['protocolVersion', 'schemaHash', 'cacheScope']);
49
+ if (scope.protocolVersion !== 1) {
50
+ hydrationInvalid('authoritativeScope.protocolVersion');
51
+ }
52
+ return Object.freeze({
53
+ protocolVersion: 1,
54
+ schemaHash: hydrationString(scope.schemaHash, 'authoritativeScope.schemaHash'),
55
+ cacheScope: hydrationOpaque(scope.cacheScope, 'authoritativeScope.cacheScope')
56
+ });
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ }
62
+ export function hydrationMetadataConsistent(parsed) {
63
+ try {
64
+ const recordByKey = new Map(parsed.cache.records.map((record) => [record.key, record]));
65
+ for (const record of parsed.cache.records) {
66
+ if (modelFromRecordKey(record.key) === undefined)
67
+ continue;
68
+ const clock = parsed.recordClocks.get(record.key);
69
+ if (clock === undefined ||
70
+ record.incarnation === undefined ||
71
+ clock.incarnation !== record.incarnation ||
72
+ clock.revision !== record.revision) {
73
+ return false;
74
+ }
75
+ if (clock.tombstone) {
76
+ if (record.tombstoneRevision !== clock.revision)
77
+ return false;
78
+ }
79
+ else if (record.tombstoneRevision !== undefined) {
80
+ return false;
81
+ }
82
+ }
83
+ for (const [recordKey, clock] of parsed.recordClocks) {
84
+ const record = recordByKey.get(recordKey);
85
+ if (record?.tombstoneRevision !== undefined &&
86
+ (!clock.tombstone ||
87
+ record.tombstoneRevision !== clock.revision)) {
88
+ return false;
89
+ }
90
+ }
91
+ const revisionsByIndex = new Map();
92
+ for (const group of parsed.operationProtocols.values()) {
93
+ for (const state of [group.query, group.live]) {
94
+ if (state === undefined)
95
+ continue;
96
+ for (const recordKey of state.pathRecords.values()) {
97
+ if (modelFromRecordKey(recordKey) !== undefined &&
98
+ !parsed.recordClocks.has(recordKey)) {
99
+ return false;
100
+ }
101
+ }
102
+ if (state.indexRevision === undefined) {
103
+ if (state.indexKeys.size > 0)
104
+ return false;
105
+ continue;
106
+ }
107
+ for (const indexKey of state.indexKeys) {
108
+ let revisions = revisionsByIndex.get(indexKey);
109
+ if (revisions === undefined) {
110
+ revisions = new Set();
111
+ revisionsByIndex.set(indexKey, revisions);
112
+ }
113
+ revisions.add(state.indexRevision);
114
+ }
115
+ }
116
+ }
117
+ const nextIndexRevision = parsed.nextIndexRevision;
118
+ for (const index of parsed.cache.indexes) {
119
+ const revision = hydrationDecimal(index.revision, 'state.payload.cache.indexes.revision');
120
+ if (compareDistributedDecimal(revision, nextIndexRevision) > 0 ||
121
+ !revisionsByIndex.get(index.key)?.has(index.revision)) {
122
+ return false;
123
+ }
124
+ }
125
+ return true;
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ export function parseReplicaHydration(value) {
132
+ try {
133
+ const state = hydrationRecord(value, 'state', ['version', 'scope', 'payload']);
134
+ if (state.version !== 1)
135
+ hydrationInvalid('state.version');
136
+ const scopeValue = hydrationRecord(state.scope, 'state.scope', ['protocolVersion', 'schemaHash', 'cacheScope']);
137
+ if (scopeValue.protocolVersion !== 1) {
138
+ hydrationInvalid('state.scope.protocolVersion');
139
+ }
140
+ const scope = Object.freeze({
141
+ protocolVersion: 1,
142
+ schemaHash: hydrationString(scopeValue.schemaHash, 'state.scope.schemaHash'),
143
+ cacheScope: hydrationOpaque(scopeValue.cacheScope, 'state.scope.cacheScope')
144
+ });
145
+ const payload = hydrationRecord(state.payload, 'state.payload', [
146
+ 'cache',
147
+ 'operations',
148
+ 'recordClocks',
149
+ 'anonymousRecordClocks',
150
+ 'trustedPresets',
151
+ 'nextIndexRevision'
152
+ ]);
153
+ const cache = payload.cache;
154
+ if (!isHydrationRecord(cache))
155
+ hydrationInvalid('state.payload.cache');
156
+ const operationsValue = hydrationArray(payload.operations, 'state.payload.operations');
157
+ const operationProtocols = new Map();
158
+ const operationGenerations = new Map();
159
+ for (const [index, entry] of operationsValue.entries()) {
160
+ const path = `state.payload.operations[${index}]`;
161
+ const raw = hydrationRecord(entry, path, ['key', 'query', 'live', 'active', 'generation'], ['key', 'generation']);
162
+ const key = hydrationString(raw.key, `${path}.key`);
163
+ if (!key.startsWith('protocol:') || operationProtocols.has(key)) {
164
+ hydrationInvalid(`${path}.key`);
165
+ }
166
+ const query = raw.query === undefined
167
+ ? undefined
168
+ : parseOperationProtocolState(raw.query, `${path}.query`);
169
+ const live = raw.live === undefined
170
+ ? undefined
171
+ : parseOperationProtocolState(raw.live, `${path}.live`);
172
+ if (query === undefined && live === undefined)
173
+ hydrationInvalid(path);
174
+ const active = raw.active === undefined
175
+ ? undefined
176
+ : hydrationOperationSource(raw.active, `${path}.active`);
177
+ if ((active === 'query' && query === undefined) ||
178
+ (active === 'live' && live === undefined)) {
179
+ hydrationInvalid(`${path}.active`);
180
+ }
181
+ operationProtocols.set(key, {
182
+ ...(query === undefined ? {} : { query }),
183
+ ...(live === undefined ? {} : { live }),
184
+ ...(active === undefined ? {} : { active })
185
+ });
186
+ operationGenerations.set(key, hydrationGeneration(raw.generation, `${path}.generation`));
187
+ }
188
+ const recordClocks = new Map();
189
+ const recordKeysByScope = new Map();
190
+ for (const [index, entry] of hydrationArray(payload.recordClocks, 'state.payload.recordClocks').entries()) {
191
+ const path = `state.payload.recordClocks[${index}]`;
192
+ const pair = hydrationPair(entry, path);
193
+ const key = hydrationString(pair[0], `${path}[0]`);
194
+ if (recordClocks.has(key))
195
+ hydrationInvalid(`${path}[0]`);
196
+ const clock = parseRecordClock(pair[1], `${path}[1]`);
197
+ if (recordKeysByScope.has(clock.scopeToken)) {
198
+ hydrationInvalid(`${path}[1].scopeToken`);
199
+ }
200
+ recordClocks.set(key, clock);
201
+ recordKeysByScope.set(clock.scopeToken, key);
202
+ }
203
+ const anonymousRecordClocks = new Map();
204
+ for (const [index, entry] of hydrationArray(payload.anonymousRecordClocks, 'state.payload.anonymousRecordClocks').entries()) {
205
+ const path = `state.payload.anonymousRecordClocks[${index}]`;
206
+ const pair = hydrationPair(entry, path);
207
+ const scopeToken = hydrationOpaque(pair[0], `${path}[0]`);
208
+ if (anonymousRecordClocks.has(scopeToken) ||
209
+ recordKeysByScope.has(scopeToken)) {
210
+ hydrationInvalid(`${path}[0]`);
211
+ }
212
+ const raw = hydrationRecord(pair[1], `${path}[1]`, ['model', 'clock']);
213
+ const clock = parseRecordClock(raw.clock, `${path}[1].clock`);
214
+ if (clock.scopeToken !== scopeToken) {
215
+ hydrationInvalid(`${path}[1].clock.scopeToken`);
216
+ }
217
+ anonymousRecordClocks.set(scopeToken, Object.freeze({
218
+ model: hydrationString(raw.model, `${path}[1].model`),
219
+ clock
220
+ }));
221
+ }
222
+ const trustedPresets = canonicalTrustedPresets(parseDistributedTrustedPresetInventory(payload.trustedPresets, 'state.payload.trustedPresets'));
223
+ const nextIndexRevision = hydrationDecimal(payload.nextIndexRevision, 'state.payload.nextIndexRevision');
224
+ for (const group of operationProtocols.values()) {
225
+ for (const operation of [group.query, group.live]) {
226
+ if (operation?.indexRevision !== undefined &&
227
+ compareDistributedDecimal(operation.indexRevision, nextIndexRevision) > 0) {
228
+ hydrationInvalid('state.payload.nextIndexRevision');
229
+ }
230
+ }
231
+ }
232
+ return {
233
+ scope,
234
+ cache,
235
+ operationProtocols,
236
+ operationGenerations,
237
+ recordClocks,
238
+ recordKeysByScope,
239
+ anonymousRecordClocks,
240
+ trustedPresets,
241
+ nextIndexRevision
242
+ };
243
+ }
244
+ catch {
245
+ return undefined;
246
+ }
247
+ }
248
+ export function parseOperationProtocolState(value, path) {
249
+ const raw = hydrationRecord(value, path, [
250
+ 'operation',
251
+ 'snapshotScope',
252
+ 'indexClocks',
253
+ 'indexRevision',
254
+ 'indexKeys',
255
+ 'pathRecords',
256
+ 'cursors'
257
+ ], ['operation', 'indexClocks', 'indexKeys', 'pathRecords', 'cursors']);
258
+ const indexClocks = new Map();
259
+ for (const [index, entry] of hydrationArray(raw.indexClocks, `${path}.indexClocks`).entries()) {
260
+ const entryPath = `${path}.indexClocks[${index}]`;
261
+ const pair = hydrationPair(entry, entryPath);
262
+ const projection = hydrationString(pair[0], `${entryPath}[0]`);
263
+ if (indexClocks.has(projection))
264
+ hydrationInvalid(`${entryPath}[0]`);
265
+ const clock = hydrationRecord(pair[1], `${entryPath}[1]`, ['scopeToken', 'position']);
266
+ indexClocks.set(projection, Object.freeze({
267
+ scopeToken: hydrationOpaque(clock.scopeToken, `${entryPath}[1].scopeToken`),
268
+ position: hydrationDecimal(clock.position, `${entryPath}[1].position`)
269
+ }));
270
+ }
271
+ const indexKeys = new Set();
272
+ for (const [index, entry] of hydrationArray(raw.indexKeys, `${path}.indexKeys`).entries()) {
273
+ const key = hydrationString(entry, `${path}.indexKeys[${index}]`);
274
+ if (indexKeys.has(key))
275
+ hydrationInvalid(`${path}.indexKeys[${index}]`);
276
+ indexKeys.add(key);
277
+ }
278
+ const pathRecords = new Map();
279
+ for (const [index, entry] of hydrationArray(raw.pathRecords, `${path}.pathRecords`).entries()) {
280
+ const entryPath = `${path}.pathRecords[${index}]`;
281
+ const pair = hydrationPair(entry, entryPath);
282
+ const responsePath = hydrationString(pair[0], `${entryPath}[0]`);
283
+ if (pathRecords.has(responsePath))
284
+ hydrationInvalid(`${entryPath}[0]`);
285
+ pathRecords.set(responsePath, hydrationString(pair[1], `${entryPath}[1]`));
286
+ }
287
+ const cursors = hydrationArray(raw.cursors, `${path}.cursors`).map((entry, index) => {
288
+ const entryPath = `${path}.cursors[${index}]`;
289
+ const cursor = hydrationRecord(entry, entryPath, ['projection', 'position', 'token']);
290
+ return Object.freeze({
291
+ projection: hydrationString(cursor.projection, `${entryPath}.projection`),
292
+ position: hydrationDecimal(cursor.position, `${entryPath}.position`),
293
+ token: hydrationOpaque(cursor.token, `${entryPath}.token`)
294
+ });
295
+ });
296
+ return {
297
+ operation: hydrationString(raw.operation, `${path}.operation`),
298
+ ...(raw.snapshotScope === undefined
299
+ ? {}
300
+ : {
301
+ snapshotScope: hydrationOpaque(raw.snapshotScope, `${path}.snapshotScope`)
302
+ }),
303
+ indexClocks,
304
+ ...(raw.indexRevision === undefined
305
+ ? {}
306
+ : {
307
+ indexRevision: hydrationDecimal(raw.indexRevision, `${path}.indexRevision`)
308
+ }),
309
+ indexKeys,
310
+ pathRecords,
311
+ cursors: Object.freeze(cursors)
312
+ };
313
+ }
314
+ export function parseRecordClock(value, path) {
315
+ const raw = hydrationRecord(value, path, ['scopeToken', 'incarnation', 'revision', 'tombstone']);
316
+ if (typeof raw.tombstone !== 'boolean')
317
+ hydrationInvalid(`${path}.tombstone`);
318
+ return Object.freeze({
319
+ scopeToken: hydrationOpaque(raw.scopeToken, `${path}.scopeToken`),
320
+ incarnation: hydrationDecimal(raw.incarnation, `${path}.incarnation`),
321
+ revision: hydrationDecimal(raw.revision, `${path}.revision`),
322
+ tombstone: raw.tombstone
323
+ });
324
+ }
325
+ export function hydrationRecord(value, path, allowed, required = allowed) {
326
+ if (!isHydrationRecord(value))
327
+ hydrationInvalid(path);
328
+ const allowedKeys = new Set(allowed);
329
+ for (const key of Object.keys(value)) {
330
+ if (!allowedKeys.has(key))
331
+ hydrationInvalid(`${path}.${key}`);
332
+ }
333
+ for (const key of required) {
334
+ if (!Object.prototype.hasOwnProperty.call(value, key)) {
335
+ hydrationInvalid(`${path}.${key}`);
336
+ }
337
+ }
338
+ return value;
339
+ }
340
+ export function isHydrationRecord(value) {
341
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
342
+ }
343
+ export function hydrationArray(value, path) {
344
+ if (!Array.isArray(value))
345
+ hydrationInvalid(path);
346
+ return value;
347
+ }
348
+ export function hydrationPair(value, path) {
349
+ if (!Array.isArray(value) || value.length !== 2)
350
+ hydrationInvalid(path);
351
+ return value;
352
+ }
353
+ export function hydrationString(value, path) {
354
+ if (typeof value !== 'string' || value.length === 0)
355
+ hydrationInvalid(path);
356
+ return value;
357
+ }
358
+ export function hydrationOpaque(value, path) {
359
+ return hydrationString(value, path);
360
+ }
361
+ export function hydrationDecimal(value, path) {
362
+ const string = hydrationString(value, path);
363
+ if (!/^(0|[1-9][0-9]*)$/.test(string))
364
+ hydrationInvalid(path);
365
+ return string;
366
+ }
367
+ export function hydrationGeneration(value, path) {
368
+ if (!Number.isSafeInteger(value) || value < 0) {
369
+ hydrationInvalid(path);
370
+ }
371
+ return value;
372
+ }
373
+ export function hydrationOperationSource(value, path) {
374
+ if (value !== 'query' && value !== 'live')
375
+ hydrationInvalid(path);
376
+ return value;
377
+ }
378
+ export function hydrationInvalid(path) {
379
+ throw new TypeError(`invalid replica hydration state at ${path}`);
380
+ }
@@ -0,0 +1,22 @@
1
+ import type { CacheEngine } from '../../internal/cache-engine.js';
2
+ import type { ReplicaDiagnosticEventInput, ReplicaDiagnosticLayerInput, ReplicaDiagnosticsSink } from '../diagnostics.js';
3
+ import type { GraphqlVariables } from '../../types.js';
4
+ import type { ReplicaOperationArtifact } from '../types.js';
5
+ import type { OptimisticReceiptState, ProtocolGeneration } from './types.js';
6
+ /**
7
+ * Host for diagnostics sync and event emission.
8
+ */
9
+ export type DiagnosticsHost = {
10
+ readonly engine: CacheEngine;
11
+ readonly diagnostics: ReplicaDiagnosticsSink | undefined;
12
+ readonly diagnosticLayers: Map<string, ReplicaDiagnosticLayerInput> | undefined;
13
+ readonly optimisticReceipts: Map<string, OptimisticReceiptState>;
14
+ readonly reportObserverError: (error: AggregateError) => void;
15
+ getProtocolGeneration(): ProtocolGeneration | undefined;
16
+ getProtocolGenerationSequence(): number;
17
+ };
18
+ export declare function syncDiagnostics(host: DiagnosticsHost): void;
19
+ export declare function diagnosticEvent(host: DiagnosticsHost, event: ReplicaDiagnosticEventInput): void;
20
+ export declare function diagnosticOperation<TData, TVariables extends GraphqlVariables>(host: DiagnosticsHost, artifact: ReplicaOperationArtifact<TData, TVariables>): void;
21
+ export declare function diagnosticScopeTransition(host: DiagnosticsHost, previous: ProtocolGeneration | undefined, next: ProtocolGeneration): void;
22
+ export declare function retireDiagnosticLayer(host: DiagnosticsHost, id: string, action: 'retired' | 'rejected', receiptState: 'projected' | 'rejected', receipt?: OptimisticReceiptState): void;
@@ -0,0 +1,189 @@
1
+ import { modelFromRecordKey } from './clocks.js';
2
+ import { diagnosticReceiptCounts, diagnosticReceiptExpectations } from './optimistic.js';
3
+ import { reportSafely } from './helpers.js';
4
+ export function syncDiagnostics(host) {
5
+ const diagnostics = host.diagnostics;
6
+ if (diagnostics === undefined)
7
+ return;
8
+ try {
9
+ const cache = host.engine.extract();
10
+ const records = cache.records.map((record) => {
11
+ const model = modelFromRecordKey(record.key);
12
+ const tombstone = record.tombstoneRevision !== undefined;
13
+ const values = {};
14
+ if (!tombstone && diagnostics.redactRecordValue !== undefined) {
15
+ for (const [field, entry] of Object.entries(record.fields)) {
16
+ const value = diagnostics.redactRecordValue(Object.freeze({
17
+ recordKey: record.key,
18
+ ...(model === undefined ? {} : { model }),
19
+ field,
20
+ kind: 'field'
21
+ }), entry.value);
22
+ if (value !== undefined)
23
+ values[field] = value;
24
+ }
25
+ for (const [field, entry] of Object.entries(record.links)) {
26
+ const value = diagnostics.redactRecordValue(Object.freeze({
27
+ recordKey: record.key,
28
+ ...(model === undefined ? {} : { model }),
29
+ field,
30
+ kind: 'link'
31
+ }), entry.value);
32
+ if (value !== undefined)
33
+ values[field] = value;
34
+ }
35
+ }
36
+ return Object.freeze({
37
+ key: record.key,
38
+ ...(model === undefined ? {} : { model }),
39
+ revision: record.revision,
40
+ incarnation: record.incarnation ?? record.revision,
41
+ tombstone,
42
+ ...(record.tombstoneRevision === undefined
43
+ ? {}
44
+ : { tombstoneRevision: record.tombstoneRevision }),
45
+ presentFields: Object.freeze(tombstone ? [] : Object.keys(record.fields).sort()),
46
+ presentLinks: Object.freeze(tombstone ? [] : Object.keys(record.links).sort()),
47
+ ...(Object.keys(values).length === 0
48
+ ? {}
49
+ : { values: Object.freeze(values) })
50
+ });
51
+ });
52
+ const indexes = cache.indexes.map((index) => Object.freeze({
53
+ key: index.key,
54
+ revision: index.revision,
55
+ ...(index.staleRevision === undefined
56
+ ? {}
57
+ : { staleRevision: index.staleRevision }),
58
+ records: index.records,
59
+ complete: index.complete,
60
+ deleted: index.deleted,
61
+ ...(index.metadata === undefined
62
+ ? {}
63
+ : {
64
+ field: index.metadata.field,
65
+ ...(index.metadata.parent === undefined
66
+ ? {}
67
+ : { parent: index.metadata.parent }),
68
+ argumentNames: Object.freeze(Object.keys(index.metadata.arguments).sort()),
69
+ ...(diagnostics.includeStructuralIdentities
70
+ ? { arguments: index.metadata.arguments }
71
+ : {}),
72
+ coverage: index.metadata.coverage,
73
+ dependencies: index.metadata.dependencies,
74
+ ...(index.metadata.staleReason === undefined
75
+ ? {}
76
+ : { staleReason: index.metadata.staleReason }),
77
+ nullValue: index.metadata.nullValue === true
78
+ })
79
+ }));
80
+ const receipts = [];
81
+ for (const layer of host.diagnosticLayers?.values() ?? []) {
82
+ const receipt = host.optimisticReceipts.get(layer.id);
83
+ receipts.push(Object.freeze({
84
+ commandId: layer.id,
85
+ state: receipt === undefined
86
+ ? 'optimistic'
87
+ : receipt.expectations.size === 0
88
+ ? 'accepted'
89
+ : 'accepted_pending_projection',
90
+ expectations: receipt === undefined
91
+ ? Object.freeze([])
92
+ : diagnosticReceiptExpectations(receipt)
93
+ }));
94
+ }
95
+ const scope = host.getProtocolGeneration();
96
+ diagnostics.update(Object.freeze({
97
+ scope: scope === undefined
98
+ ? Object.freeze({
99
+ generation: host.getProtocolGenerationSequence(),
100
+ established: false
101
+ })
102
+ : Object.freeze({
103
+ generation: host.getProtocolGenerationSequence(),
104
+ established: true,
105
+ protocolVersion: 1,
106
+ schemaHash: scope.schemaHash
107
+ }),
108
+ records: Object.freeze(records),
109
+ indexes: Object.freeze(indexes),
110
+ layers: Object.freeze([
111
+ ...(host.diagnosticLayers?.values() ?? [])
112
+ ]),
113
+ receipts: Object.freeze(receipts)
114
+ }));
115
+ }
116
+ catch (error) {
117
+ reportSafely(host.reportObserverError, new AggregateError([error], 'replica diagnostics update failed'));
118
+ }
119
+ }
120
+ export function diagnosticEvent(host, event) {
121
+ if (host.diagnostics === undefined)
122
+ return;
123
+ try {
124
+ host.diagnostics.event(event);
125
+ }
126
+ catch (error) {
127
+ reportSafely(host.reportObserverError, new AggregateError([error], 'replica diagnostics event failed'));
128
+ }
129
+ }
130
+ export function diagnosticOperation(host, artifact) {
131
+ if (host.diagnostics === undefined)
132
+ return;
133
+ try {
134
+ host.diagnostics.operation(artifact);
135
+ }
136
+ catch (error) {
137
+ reportSafely(host.reportObserverError, new AggregateError([error], 'replica diagnostic artifact failed'));
138
+ }
139
+ }
140
+ export function diagnosticScopeTransition(host, previous, next) {
141
+ if (host.diagnostics === undefined)
142
+ return;
143
+ if (previous !== undefined &&
144
+ previous.cacheScope === next.cacheScope &&
145
+ previous.schemaHash === next.schemaHash) {
146
+ return;
147
+ }
148
+ diagnosticEvent(host, Object.freeze({
149
+ kind: 'scope',
150
+ action: previous === undefined ? 'established' : 'changed',
151
+ generation: host.getProtocolGenerationSequence(),
152
+ schemaHash: next.schemaHash
153
+ }));
154
+ }
155
+ export function retireDiagnosticLayer(host, id, action, receiptState, receipt) {
156
+ const layers = host.diagnosticLayers;
157
+ const removed = layers?.get(id);
158
+ if (removed === undefined)
159
+ return;
160
+ layers.delete(id);
161
+ diagnosticEvent(host, Object.freeze({
162
+ kind: 'layer',
163
+ layer: id,
164
+ action,
165
+ recordChanges: removed.recordChanges,
166
+ indexChanges: removed.indexChanges
167
+ }));
168
+ const causal = receipt ?? host.optimisticReceipts.get(id);
169
+ const counts = diagnosticReceiptCounts(causal);
170
+ diagnosticEvent(host, Object.freeze({
171
+ kind: 'receipt',
172
+ command: id,
173
+ state: receiptState,
174
+ obligations: counts.obligations,
175
+ observed: counts.observed
176
+ }));
177
+ for (const layer of layers.values()) {
178
+ if (layer.sequence <= removed.sequence)
179
+ continue;
180
+ diagnosticEvent(host, Object.freeze({
181
+ kind: 'layer',
182
+ layer: layer.id,
183
+ action: 'rebased',
184
+ recordChanges: layer.recordChanges,
185
+ indexChanges: layer.indexChanges,
186
+ reason: `${action}-earlier-layer`
187
+ }));
188
+ }
189
+ }
@@ -0,0 +1,39 @@
1
+ import type { GraphqlVariables } from '../../types.js';
2
+ import { type DistributedLiveCursor, type DistributedProtocolEnvelope } from '../../protocol.js';
3
+ import type { ReplicaDiagnosticEventInput } from '../diagnostics.js';
4
+ import type { ReplicaOperationArtifact, ReplicaResultEnvelope, ReplicaTransport, ReplicaWriteSource } from '../types.js';
5
+ import type { LiveEntry, OperationProtocolGroup, ProtocolGeneration, QueryState } from './types.js';
6
+ import type { ReplicaWatchState } from './watch.js';
7
+ /**
8
+ * Host accessors for fetch / live subscription orchestration.
9
+ * Free functions never read private class fields; the class supplies state.
10
+ */
11
+ export type FetchLiveHost = {
12
+ readonly transport: ReplicaTransport | undefined;
13
+ readonly inFlight: Map<string, Promise<void>>;
14
+ readonly inFlightAborts: Map<string, AbortController>;
15
+ readonly lives: Map<string, LiveEntry>;
16
+ readonly watches: Map<string, Set<ReplicaWatchState<unknown, GraphqlVariables>>>;
17
+ readonly operationProtocols: Map<string, OperationProtocolGroup>;
18
+ readonly diagnostics: {
19
+ readonly enabled: boolean;
20
+ };
21
+ protocolGenerationSequence(): number;
22
+ projectionGeneration(): number;
23
+ protocolGeneration(): ProtocolGeneration | undefined;
24
+ queryState(key: string): QueryState;
25
+ emitState(key: string, allowFetch: boolean): void;
26
+ operationGeneration(key: string): number;
27
+ allocateIndexRevision(): string;
28
+ writeCanonicalResult<TData, TVariables extends GraphqlVariables>(artifact: ReplicaOperationArtifact<TData, TVariables>, stableVariables: TVariables, envelope: ReplicaResultEnvelope<TData>, source: ReplicaWriteSource, requestRevision?: string, responseProjectionGeneration?: number): DistributedProtocolEnvelope;
29
+ diagnosticEvent(event: ReplicaDiagnosticEventInput): void;
30
+ resumeCursors(key: string): readonly DistributedLiveCursor[];
31
+ };
32
+ export declare function emitWatchState(host: FetchLiveHost, key: string, allowFetch: boolean): void;
33
+ export declare function closeActiveTransports(host: FetchLiveHost): void;
34
+ export declare function fetchWatch<TData, TVariables extends GraphqlVariables>(host: FetchLiveHost, watch: ReplicaWatchState<TData, TVariables>, force: boolean): Promise<void>;
35
+ export declare function retainLive<TData, TVariables extends GraphqlVariables>(host: FetchLiveHost, watch: ReplicaWatchState<TData, TVariables>): void;
36
+ export declare function fallbackFromLive<TData, TVariables extends GraphqlVariables>(host: FetchLiveHost, watch: ReplicaWatchState<TData, TVariables>, entry: LiveEntry): void;
37
+ export declare function restartLive(host: FetchLiveHost, key: string): void;
38
+ export declare function resumeLiveWatches(host: FetchLiveHost): void;
39
+ export declare function releaseLive(host: FetchLiveHost, key: string): void;