@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,395 @@
1
+ import { CacheRevisionConflictError } from '../../internal/cache-engine.js';
2
+ import { parseGraphqlResponseExtensions } from '../../protocol.js';
3
+ import { graphqlError, replicaClientRequestExtensions, stableErrors } from './helpers.js';
4
+ export function emitWatchState(host, key, allowFetch) {
5
+ for (const watch of host.watches.get(key) ?? [])
6
+ watch._stateChanged(allowFetch);
7
+ }
8
+ export function closeActiveTransports(host) {
9
+ for (const controller of host.inFlightAborts.values())
10
+ controller.abort();
11
+ host.inFlightAborts.clear();
12
+ for (const entry of host.lives.values()) {
13
+ entry.active = false;
14
+ try {
15
+ entry.unsubscribe();
16
+ }
17
+ catch {
18
+ // The generation fence is already closed; transport cleanup is best effort.
19
+ }
20
+ }
21
+ host.lives.clear();
22
+ host.inFlight.clear();
23
+ }
24
+ export function fetchWatch(host, watch, force) {
25
+ if (!host.transport)
26
+ return Promise.resolve();
27
+ if (!force && watch.materialized.complete && !watch.materialized.stale) {
28
+ if (host.diagnostics.enabled) {
29
+ host.diagnosticEvent(Object.freeze({
30
+ kind: 'revalidation',
31
+ operation: watch.artifact.id,
32
+ action: 'skipped-complete',
33
+ reason: 'watch'
34
+ }));
35
+ }
36
+ return Promise.resolve();
37
+ }
38
+ const existing = host.inFlight.get(watch.key);
39
+ if (existing) {
40
+ if (host.diagnostics.enabled) {
41
+ host.diagnosticEvent(Object.freeze({
42
+ kind: 'revalidation',
43
+ operation: watch.artifact.id,
44
+ action: 'deduplicated',
45
+ reason: force
46
+ ? 'refresh'
47
+ : watch.materialized.stale
48
+ ? 'stale'
49
+ : 'watch'
50
+ }));
51
+ }
52
+ return existing;
53
+ }
54
+ const state = host.queryState(watch.key);
55
+ if (host.diagnostics.enabled) {
56
+ host.diagnosticEvent(Object.freeze({
57
+ kind: 'revalidation',
58
+ operation: watch.artifact.id,
59
+ action: 'requested',
60
+ reason: force
61
+ ? 'refresh'
62
+ : watch.materialized.stale
63
+ ? 'stale'
64
+ : 'watch'
65
+ }));
66
+ }
67
+ state.fetching = true;
68
+ host.emitState(watch.key, false);
69
+ const operationGeneration = host.operationGeneration(watch.key);
70
+ const authorizationGeneration = host.protocolGenerationSequence();
71
+ const projectionGeneration = host.projectionGeneration();
72
+ /*
73
+ * Reserve local index ordering and the projection-fence generation when
74
+ * the request starts, not when it finishes. Distinct operation artifacts
75
+ * may share the same semantic index key; a slower earlier request must
76
+ * not replace a later-started result merely because its response arrived
77
+ * last, nor may it supersede a direct projection installed in between.
78
+ */
79
+ const requestRevision = host.allocateIndexRevision();
80
+ const controller = new AbortController();
81
+ const request = Object.freeze({
82
+ operation: 'query',
83
+ operationId: watch.artifact.id,
84
+ document: watch.artifact.document,
85
+ variables: watch.variables,
86
+ artifact: watch.artifact,
87
+ ...replicaClientRequestExtensions(watch.artifact),
88
+ signal: controller.signal
89
+ });
90
+ let flight;
91
+ flight = Promise.resolve()
92
+ .then(() => host.transport.fetch(request))
93
+ .then((result) => {
94
+ if (host.protocolGenerationSequence() !== authorizationGeneration) {
95
+ if (host.diagnostics.enabled) {
96
+ host.diagnosticEvent(Object.freeze({
97
+ kind: 'response-fenced',
98
+ operation: watch.artifact.id,
99
+ transport: 'http',
100
+ reason: 'authorization-generation'
101
+ }));
102
+ }
103
+ return;
104
+ }
105
+ if (host.inFlight.get(watch.key) !== flight) {
106
+ if (host.diagnostics.enabled) {
107
+ host.diagnosticEvent(Object.freeze({
108
+ kind: 'response-fenced',
109
+ operation: watch.artifact.id,
110
+ transport: 'http',
111
+ reason: 'superseded'
112
+ }));
113
+ }
114
+ return;
115
+ }
116
+ if (host.operationGeneration(watch.key) !== operationGeneration) {
117
+ if (host.diagnostics.enabled) {
118
+ host.diagnosticEvent(Object.freeze({
119
+ kind: 'response-fenced',
120
+ operation: watch.artifact.id,
121
+ transport: 'http',
122
+ reason: 'operation-generation'
123
+ }));
124
+ }
125
+ return;
126
+ }
127
+ host.writeCanonicalResult(watch.artifact, watch.variables, result, 'network', requestRevision, projectionGeneration);
128
+ })
129
+ .catch((error) => {
130
+ if (host.inFlight.get(watch.key) !== flight)
131
+ return;
132
+ if (controller.signal.aborted)
133
+ return;
134
+ state.errors = stableErrors(state.errors, [graphqlError(error)]);
135
+ })
136
+ .finally(() => {
137
+ if (host.inFlight.get(watch.key) !== flight)
138
+ return;
139
+ host.inFlight.delete(watch.key);
140
+ host.inFlightAborts.delete(watch.key);
141
+ state.fetching = false;
142
+ host.emitState(watch.key, false);
143
+ });
144
+ host.inFlight.set(watch.key, flight);
145
+ host.inFlightAborts.set(watch.key, controller);
146
+ return flight;
147
+ }
148
+ export function retainLive(host, watch) {
149
+ if (!watch.artifact.live || !host.transport?.subscribe)
150
+ return;
151
+ const existing = host.lives.get(watch.key);
152
+ if (existing) {
153
+ existing.count += 1;
154
+ return;
155
+ }
156
+ const state = host.queryState(watch.key);
157
+ state.live = 'connecting';
158
+ const entry = {
159
+ count: 1,
160
+ unsubscribe: () => undefined,
161
+ active: true,
162
+ protocolGeneration: host.protocolGenerationSequence()
163
+ };
164
+ host.lives.set(watch.key, entry);
165
+ const resume = host.resumeCursors(watch.key);
166
+ try {
167
+ const unsubscribe = host.transport.subscribe(Object.freeze({
168
+ operation: 'live',
169
+ operationId: watch.artifact.live.id,
170
+ document: watch.artifact.live.document,
171
+ variables: watch.variables,
172
+ artifact: watch.artifact,
173
+ ...replicaClientRequestExtensions(watch.artifact),
174
+ ...(resume === undefined || resume.length === 0
175
+ ? {}
176
+ : { resume })
177
+ }), {
178
+ next: (result) => {
179
+ // A live frame begins at this synchronous ingress boundary.
180
+ const projectionGeneration = host.projectionGeneration();
181
+ if (entry.protocolGeneration !== host.protocolGenerationSequence()) {
182
+ if (host.diagnostics.enabled) {
183
+ host.diagnosticEvent(Object.freeze({
184
+ kind: 'response-fenced',
185
+ operation: watch.artifact.live.id,
186
+ transport: 'live',
187
+ reason: 'authorization-generation'
188
+ }));
189
+ }
190
+ return;
191
+ }
192
+ if (entry.operationGeneration !== undefined &&
193
+ entry.operationGeneration !==
194
+ host.operationGeneration(watch.key)) {
195
+ if (host.diagnostics.enabled) {
196
+ host.diagnosticEvent(Object.freeze({
197
+ kind: 'response-fenced',
198
+ operation: watch.artifact.live.id,
199
+ transport: 'live',
200
+ reason: 'operation-generation'
201
+ }));
202
+ }
203
+ return;
204
+ }
205
+ if (!entry.active ||
206
+ host.lives.get(watch.key) !== entry) {
207
+ if (host.diagnostics.enabled) {
208
+ host.diagnosticEvent(Object.freeze({
209
+ kind: 'response-fenced',
210
+ operation: watch.artifact.live.id,
211
+ transport: 'live',
212
+ reason: 'superseded'
213
+ }));
214
+ }
215
+ return;
216
+ }
217
+ let unsupportedLive = false;
218
+ try {
219
+ unsupportedLive =
220
+ parseGraphqlResponseExtensions(result.extensions)
221
+ ?.distributed?.live?.supported === false;
222
+ if (unsupportedLive)
223
+ state.live = 'off';
224
+ const distributed = host.writeCanonicalResult(watch.artifact, watch.variables, result, 'live', undefined, projectionGeneration);
225
+ if (distributed.live?.supported === false) {
226
+ fallbackFromLive(host, watch, entry);
227
+ return;
228
+ }
229
+ state.live = 'active';
230
+ entry.operationGeneration =
231
+ host.operationGeneration(watch.key);
232
+ }
233
+ catch (error) {
234
+ if (unsupportedLive &&
235
+ error instanceof CacheRevisionConflictError) {
236
+ /*
237
+ * Revision zero is shared by provisional fallbacks.
238
+ * Another operation may already have filled the same
239
+ * semantic index differently; HTTP remains authoritative.
240
+ */
241
+ fallbackFromLive(host, watch, entry);
242
+ return;
243
+ }
244
+ state.live = 'error';
245
+ state.errors = stableErrors(state.errors, [graphqlError(error)]);
246
+ host.emitState(watch.key, false);
247
+ }
248
+ },
249
+ error: (error) => {
250
+ if (!entry.active || host.lives.get(watch.key) !== entry)
251
+ return;
252
+ entry.active = false;
253
+ host.lives.delete(watch.key);
254
+ const unsub = entry.unsubscribe;
255
+ entry.unsubscribe = () => undefined;
256
+ try {
257
+ unsub();
258
+ }
259
+ catch {
260
+ // The terminal stream is fenced; cleanup is best effort.
261
+ }
262
+ state.live = 'error';
263
+ state.errors = stableErrors(state.errors, [graphqlError(error)]);
264
+ host.emitState(watch.key, false);
265
+ },
266
+ complete: () => {
267
+ if (!entry.active || host.lives.get(watch.key) !== entry)
268
+ return;
269
+ fallbackFromLive(host, watch, entry);
270
+ }
271
+ });
272
+ entry.unsubscribe = unsubscribe;
273
+ if (!entry.active || host.lives.get(watch.key) !== entry) {
274
+ entry.unsubscribe = () => undefined;
275
+ try {
276
+ unsubscribe();
277
+ }
278
+ catch {
279
+ // A closed/superseded subscription is already fenced.
280
+ }
281
+ return;
282
+ }
283
+ state.live = 'active';
284
+ }
285
+ catch (error) {
286
+ entry.active = false;
287
+ host.lives.delete(watch.key);
288
+ state.live = 'error';
289
+ state.errors = stableErrors(state.errors, [graphqlError(error)]);
290
+ }
291
+ host.emitState(watch.key, false);
292
+ }
293
+ export function fallbackFromLive(host, watch, entry) {
294
+ if (host.lives.get(watch.key) !== entry) {
295
+ void fetchWatch(host, watch, true);
296
+ return;
297
+ }
298
+ const protocol = host.operationProtocols.get(watch.key);
299
+ if (protocol?.active === 'live')
300
+ protocol.active = undefined;
301
+ entry.active = false;
302
+ const unsubscribe = entry.unsubscribe;
303
+ entry.unsubscribe = () => undefined;
304
+ try {
305
+ unsubscribe();
306
+ }
307
+ catch {
308
+ // The inactive stream is fenced; transport cleanup is best effort.
309
+ }
310
+ const state = host.queryState(watch.key);
311
+ state.live = 'off';
312
+ host.emitState(watch.key, false);
313
+ const authorizationGeneration = host.protocolGenerationSequence();
314
+ const supersededFlight = entry.operationGeneration === undefined
315
+ ? undefined
316
+ : host.inFlight.get(watch.key);
317
+ const refresh = () => {
318
+ if (host.protocolGenerationSequence() !== authorizationGeneration ||
319
+ host.lives.get(watch.key) !== entry ||
320
+ entry.active) {
321
+ return;
322
+ }
323
+ void fetchWatch(host, watch, true);
324
+ };
325
+ /*
326
+ * Keep the inactive entry as an authorization-generation-scoped
327
+ * sentinel. Query ingestion calls resumeLiveWatches(); deleting this
328
+ * entry would otherwise reopen an unsupported or completed stream
329
+ * immediately. Authorization invalidation clears it and may retry.
330
+ *
331
+ * A supported live frame advances the operation generation. Any HTTP
332
+ * request that was already running is therefore doomed by its response
333
+ * fence; drain it before starting the authoritative fallback. A first
334
+ * unsupported frame never advances the generation, so its overlapping
335
+ * HTTP request remains valid and can be reused directly.
336
+ */
337
+ if (supersededFlight === undefined) {
338
+ refresh();
339
+ }
340
+ else {
341
+ void supersededFlight.then(refresh, refresh);
342
+ }
343
+ }
344
+ export function restartLive(host, key) {
345
+ const previous = host.lives.get(key);
346
+ if (previous === undefined)
347
+ return;
348
+ const count = previous.count;
349
+ previous.active = false;
350
+ host.lives.delete(key);
351
+ try {
352
+ previous.unsubscribe();
353
+ }
354
+ catch {
355
+ // The old generation is already fenced; cleanup is best effort.
356
+ }
357
+ const watch = [...(host.watches.get(key) ?? [])].find((candidate) => candidate.liveRequested);
358
+ if (watch === undefined) {
359
+ host.queryState(key).live = 'off';
360
+ return;
361
+ }
362
+ retainLive(host, watch);
363
+ const replacement = host.lives.get(key);
364
+ if (replacement !== undefined)
365
+ replacement.count = count;
366
+ }
367
+ export function resumeLiveWatches(host) {
368
+ if (host.protocolGeneration() === undefined)
369
+ return;
370
+ for (const [key, watches] of host.watches) {
371
+ if (host.lives.has(key))
372
+ continue;
373
+ const liveWatches = [...watches].filter((watch) => watch.liveRequested);
374
+ const first = liveWatches[0];
375
+ if (first === undefined)
376
+ continue;
377
+ retainLive(host, first);
378
+ const entry = host.lives.get(key);
379
+ if (entry !== undefined)
380
+ entry.count = liveWatches.length;
381
+ }
382
+ }
383
+ export function releaseLive(host, key) {
384
+ const entry = host.lives.get(key);
385
+ if (!entry)
386
+ return;
387
+ entry.count -= 1;
388
+ if (entry.count > 0)
389
+ return;
390
+ entry.active = false;
391
+ host.lives.delete(key);
392
+ entry.unsubscribe();
393
+ host.queryState(key).live = 'off';
394
+ host.emitState(key, false);
395
+ }
@@ -0,0 +1,56 @@
1
+ import { type CacheEngine, type CacheEngineSnapshot } from '../../internal/cache-engine.js';
2
+ import type { DistributedOpaqueString, DistributedTrustedPreset } from '../../protocol.js';
3
+ import type { ReplicaDiagnosticEventInput } from '../diagnostics.js';
4
+ import type { ReplicaIndexPlanRegistration } from '../index-maintenance.js';
5
+ import type { ReplicaAuthoritativeScope, ReplicaDehydratedState } from '../types.js';
6
+ import type { AnonymousRecordProtocolClock, OperationProtocolGroup, OptimisticReceiptState, ProtocolGeneration, QueryState, RecordProtocolClock, RegisteredCommandAuthorityContract, RenderedOperation, ReplicaArtifactBinding } from './types.js';
7
+ import type { ReplicaDiagnosticLayerInput } from '../diagnostics.js';
8
+ /**
9
+ * Host for dehydrate / hydrate orchestration.
10
+ */
11
+ export type HydrationHost = {
12
+ readonly engine: CacheEngine;
13
+ readonly operationProtocols: Map<string, OperationProtocolGroup>;
14
+ readonly operationGenerations: Map<string, number>;
15
+ readonly recordClocks: Map<string, RecordProtocolClock>;
16
+ readonly recordKeysByScope: Map<DistributedOpaqueString, string>;
17
+ readonly anonymousRecordClocks: Map<DistributedOpaqueString, AnonymousRecordProtocolClock>;
18
+ readonly optimisticReceipts: Map<string, OptimisticReceiptState>;
19
+ readonly diagnosticLayers: Map<string, ReplicaDiagnosticLayerInput> | undefined;
20
+ readonly renderedOperations: Map<string, RenderedOperation>;
21
+ readonly readOperationKeys: Set<string>;
22
+ readonly watchRenderCounts: Map<string, number>;
23
+ readonly indexPlanRegistrations: Map<string, ReplicaIndexPlanRegistration>;
24
+ readonly queryStates: Map<string, QueryState>;
25
+ readonly diagnostics: {
26
+ readonly enabled: boolean;
27
+ };
28
+ getProtocolGeneration(): ProtocolGeneration | undefined;
29
+ setProtocolGeneration(value: ProtocolGeneration | undefined): void;
30
+ getProtocolGenerationSequence(): number;
31
+ getTrustedPresets(): readonly DistributedTrustedPreset[];
32
+ setTrustedPresets(value: readonly DistributedTrustedPreset[]): void;
33
+ getNextIndexRevision(): string;
34
+ setNextIndexRevision(value: string): void;
35
+ getArtifactBinding(): ReplicaArtifactBinding | undefined;
36
+ setArtifactBinding(value: ReplicaArtifactBinding | undefined): void;
37
+ getCommandAuthorityContract(): RegisteredCommandAuthorityContract | undefined;
38
+ setDiagnosticLayerSequence(value: number): void;
39
+ operationGeneration(key: string): number;
40
+ closeActiveTransports(): void;
41
+ closeAuthorizationGeneration(): void;
42
+ resumeLiveWatches(): void;
43
+ syncDiagnostics(): void;
44
+ diagnosticEvent(event: ReplicaDiagnosticEventInput): void;
45
+ refreshIndexMaintenance(): void;
46
+ finishDehydration(): void;
47
+ };
48
+ export declare function reachableConfirmedState(host: HydrationHost): {
49
+ readonly cache: CacheEngineSnapshot;
50
+ readonly recordKeys: ReadonlySet<string>;
51
+ readonly clockRecordKeys: ReadonlySet<string>;
52
+ readonly models: ReadonlySet<string>;
53
+ };
54
+ export declare function finishDehydration(host: HydrationHost): void;
55
+ export declare function dehydrateReplica(host: HydrationHost): ReplicaDehydratedState;
56
+ export declare function hydrateReplica(host: HydrationHost, state: ReplicaDehydratedState, authoritativeScope: ReplicaAuthoritativeScope): boolean;