@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.
- package/README.md +454 -0
- package/dist/auth-headers.d.ts +8 -0
- package/dist/auth-headers.js +35 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +1 -0
- package/dist/document.d.ts +8 -0
- package/dist/document.js +6 -0
- package/dist/identity.d.ts +23 -0
- package/dist/identity.js +73 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/internal/cache-engine/create.d.ts +5 -0
- package/dist/internal/cache-engine/create.js +7 -0
- package/dist/internal/cache-engine/engine.d.ts +32 -0
- package/dist/internal/cache-engine/engine.js +1287 -0
- package/dist/internal/cache-engine/errors.d.ts +5 -0
- package/dist/internal/cache-engine/errors.js +10 -0
- package/dist/internal/cache-engine/helpers.d.ts +55 -0
- package/dist/internal/cache-engine/helpers.js +545 -0
- package/dist/internal/cache-engine/index.d.ts +3 -0
- package/dist/internal/cache-engine/index.js +2 -0
- package/dist/internal/cache-engine/types.d.ts +300 -0
- package/dist/internal/cache-engine/types.js +1 -0
- package/dist/internal/cache-engine.d.ts +3 -0
- package/dist/internal/cache-engine.js +2 -0
- package/dist/lib/assert-name.d.ts +2 -0
- package/dist/lib/assert-name.js +6 -0
- package/dist/lib/compare-code-units.d.ts +2 -0
- package/dist/lib/compare-code-units.js +4 -0
- package/dist/lib/deep-equal.d.ts +2 -0
- package/dist/lib/deep-equal.js +24 -0
- package/dist/lib/freeze-record.d.ts +2 -0
- package/dist/lib/freeze-record.js +13 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +6 -0
- package/dist/lib/is-plain-record.d.ts +2 -0
- package/dist/lib/is-plain-record.js +8 -0
- package/dist/lib/report.d.ts +7 -0
- package/dist/lib/report.js +26 -0
- package/dist/protocol.d.ts +179 -0
- package/dist/protocol.js +558 -0
- package/dist/react/context.d.ts +20 -0
- package/dist/react/context.js +24 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +3 -0
- package/dist/react/query.d.ts +20 -0
- package/dist/react/query.js +99 -0
- package/dist/replica/command-id.d.ts +2 -0
- package/dist/replica/command-id.js +19 -0
- package/dist/replica/command-runtime/constants.d.ts +5 -0
- package/dist/replica/command-runtime/constants.js +5 -0
- package/dist/replica/command-runtime/create.d.ts +2 -0
- package/dist/replica/command-runtime/create.js +644 -0
- package/dist/replica/command-runtime/errors.d.ts +16 -0
- package/dist/replica/command-runtime/errors.js +41 -0
- package/dist/replica/command-runtime/index.d.ts +5 -0
- package/dist/replica/command-runtime/index.js +4 -0
- package/dist/replica/command-runtime/lib/binding.d.ts +4 -0
- package/dist/replica/command-runtime/lib/binding.js +64 -0
- package/dist/replica/command-runtime/lib/effects.d.ts +11 -0
- package/dist/replica/command-runtime/lib/effects.js +89 -0
- package/dist/replica/command-runtime/lib/index.d.ts +9 -0
- package/dist/replica/command-runtime/lib/index.js +9 -0
- package/dist/replica/command-runtime/lib/inventory.d.ts +10 -0
- package/dist/replica/command-runtime/lib/inventory.js +141 -0
- package/dist/replica/command-runtime/lib/output.d.ts +13 -0
- package/dist/replica/command-runtime/lib/output.js +190 -0
- package/dist/replica/command-runtime/lib/projection.d.ts +20 -0
- package/dist/replica/command-runtime/lib/projection.js +255 -0
- package/dist/replica/command-runtime/lib/status.d.ts +7 -0
- package/dist/replica/command-runtime/lib/status.js +127 -0
- package/dist/replica/command-runtime/lib/transport.d.ts +16 -0
- package/dist/replica/command-runtime/lib/transport.js +112 -0
- package/dist/replica/command-runtime/lib/util.d.ts +16 -0
- package/dist/replica/command-runtime/lib/util.js +127 -0
- package/dist/replica/command-runtime/lifecycle.d.ts +11 -0
- package/dist/replica/command-runtime/lifecycle.js +13 -0
- package/dist/replica/command-runtime/symbols.d.ts +30 -0
- package/dist/replica/command-runtime/symbols.js +30 -0
- package/dist/replica/command-runtime/types.d.ts +222 -0
- package/dist/replica/command-runtime/types.js +1 -0
- package/dist/replica/command-runtime.d.ts +3 -0
- package/dist/replica/command-runtime.js +2 -0
- package/dist/replica/commands/clone.d.ts +12 -0
- package/dist/replica/commands/clone.js +187 -0
- package/dist/replica/commands/constants.d.ts +6 -0
- package/dist/replica/commands/constants.js +6 -0
- package/dist/replica/commands/errors.d.ts +7 -0
- package/dist/replica/commands/errors.js +22 -0
- package/dist/replica/commands/implementation.d.ts +8 -0
- package/dist/replica/commands/implementation.js +8 -0
- package/dist/replica/commands/index.d.ts +5 -0
- package/dist/replica/commands/index.js +4 -0
- package/dist/replica/commands/prepare.d.ts +27 -0
- package/dist/replica/commands/prepare.js +77 -0
- package/dist/replica/commands/presets.d.ts +12 -0
- package/dist/replica/commands/presets.js +84 -0
- package/dist/replica/commands/receipt.d.ts +3 -0
- package/dist/replica/commands/receipt.js +36 -0
- package/dist/replica/commands/resolve.d.ts +11 -0
- package/dist/replica/commands/resolve.js +165 -0
- package/dist/replica/commands/types.d.ts +278 -0
- package/dist/replica/commands/types.js +2 -0
- package/dist/replica/commands/util.d.ts +21 -0
- package/dist/replica/commands/util.js +96 -0
- package/dist/replica/commands/validate.d.ts +18 -0
- package/dist/replica/commands/validate.js +477 -0
- package/dist/replica/commands.d.ts +3 -0
- package/dist/replica/commands.js +2 -0
- package/dist/replica/diagnostics/event-log.d.ts +4 -0
- package/dist/replica/diagnostics/event-log.js +463 -0
- package/dist/replica/diagnostics/implementation.d.ts +4 -0
- package/dist/replica/diagnostics/implementation.js +4 -0
- package/dist/replica/diagnostics/index.d.ts +2 -0
- package/dist/replica/diagnostics/index.js +1 -0
- package/dist/replica/diagnostics/inspect.d.ts +6 -0
- package/dist/replica/diagnostics/inspect.js +170 -0
- package/dist/replica/diagnostics/types.d.ts +295 -0
- package/dist/replica/diagnostics/types.js +2 -0
- package/dist/replica/diagnostics.d.ts +3 -0
- package/dist/replica/diagnostics.js +2 -0
- package/dist/replica/distributed-replica/clocks.d.ts +23 -0
- package/dist/replica/distributed-replica/clocks.js +156 -0
- package/dist/replica/distributed-replica/constants.d.ts +12 -0
- package/dist/replica/distributed-replica/constants.js +10 -0
- package/dist/replica/distributed-replica/helpers.d.ts +48 -0
- package/dist/replica/distributed-replica/helpers.js +445 -0
- package/dist/replica/distributed-replica/hydration.d.ts +19 -0
- package/dist/replica/distributed-replica/hydration.js +380 -0
- package/dist/replica/distributed-replica/impl-diagnostics.d.ts +22 -0
- package/dist/replica/distributed-replica/impl-diagnostics.js +189 -0
- package/dist/replica/distributed-replica/impl-fetch-live.d.ts +39 -0
- package/dist/replica/distributed-replica/impl-fetch-live.js +395 -0
- package/dist/replica/distributed-replica/impl-hydration-orchestrate.d.ts +56 -0
- package/dist/replica/distributed-replica/impl-hydration-orchestrate.js +299 -0
- package/dist/replica/distributed-replica/impl-optimistic.d.ts +31 -0
- package/dist/replica/distributed-replica/impl-optimistic.js +172 -0
- package/dist/replica/distributed-replica/impl-protocol.d.ts +53 -0
- package/dist/replica/distributed-replica/impl-protocol.js +108 -0
- package/dist/replica/distributed-replica/impl.d.ts +44 -0
- package/dist/replica/distributed-replica/impl.js +1776 -0
- package/dist/replica/distributed-replica/index.d.ts +2 -0
- package/dist/replica/distributed-replica/index.js +4 -0
- package/dist/replica/distributed-replica/optimistic.d.ts +30 -0
- package/dist/replica/distributed-replica/optimistic.js +221 -0
- package/dist/replica/distributed-replica/types.d.ts +147 -0
- package/dist/replica/distributed-replica/types.js +1 -0
- package/dist/replica/distributed-replica/watch.d.ts +19 -0
- package/dist/replica/distributed-replica/watch.js +95 -0
- package/dist/replica/distributed-replica.d.ts +2 -0
- package/dist/replica/distributed-replica.js +2 -0
- package/dist/replica/graphql-transport.d.ts +24 -0
- package/dist/replica/graphql-transport.js +122 -0
- package/dist/replica/identity/clone.d.ts +6 -0
- package/dist/replica/identity/clone.js +54 -0
- package/dist/replica/identity/codec.d.ts +48 -0
- package/dist/replica/identity/codec.js +789 -0
- package/dist/replica/identity/constants.d.ts +3 -0
- package/dist/replica/identity/constants.js +18 -0
- package/dist/replica/identity/implementation.d.ts +5 -0
- package/dist/replica/identity/implementation.js +5 -0
- package/dist/replica/identity/index.d.ts +3 -0
- package/dist/replica/identity/index.js +3 -0
- package/dist/replica/identity/keys.d.ts +29 -0
- package/dist/replica/identity/keys.js +173 -0
- package/dist/replica/identity.d.ts +2 -0
- package/dist/replica/identity.js +2 -0
- package/dist/replica/index-maintenance/engine.d.ts +84 -0
- package/dist/replica/index-maintenance/engine.js +677 -0
- package/dist/replica/index-maintenance/implementation.d.ts +4 -0
- package/dist/replica/index-maintenance/implementation.js +4 -0
- package/dist/replica/index-maintenance/index.d.ts +2 -0
- package/dist/replica/index-maintenance/index.js +1 -0
- package/dist/replica/index-maintenance/registry.d.ts +2 -0
- package/dist/replica/index-maintenance/registry.js +49 -0
- package/dist/replica/index-maintenance/types.d.ts +58 -0
- package/dist/replica/index-maintenance/types.js +1 -0
- package/dist/replica/index-maintenance.d.ts +3 -0
- package/dist/replica/index-maintenance.js +2 -0
- package/dist/replica/index.d.ts +17 -0
- package/dist/replica/index.js +9 -0
- package/dist/replica/materialize.d.ts +10 -0
- package/dist/replica/materialize.js +163 -0
- package/dist/replica/normalize.d.ts +27 -0
- package/dist/replica/normalize.js +298 -0
- package/dist/replica/operation-binding.d.ts +14 -0
- package/dist/replica/operation-binding.js +76 -0
- package/dist/replica/persistence/idb.d.ts +20 -0
- package/dist/replica/persistence/idb.js +193 -0
- package/dist/replica/persistence/implementation.d.ts +4 -0
- package/dist/replica/persistence/implementation.js +4 -0
- package/dist/replica/persistence/index.d.ts +2 -0
- package/dist/replica/persistence/index.js +1 -0
- package/dist/replica/persistence/state.d.ts +104 -0
- package/dist/replica/persistence/state.js +734 -0
- package/dist/replica/persistence/types.d.ts +54 -0
- package/dist/replica/persistence/types.js +1 -0
- package/dist/replica/persistence.d.ts +3 -0
- package/dist/replica/persistence.js +2 -0
- package/dist/replica/query-plan/constants.d.ts +13 -0
- package/dist/replica/query-plan/constants.js +7 -0
- package/dist/replica/query-plan/filter.d.ts +39 -0
- package/dist/replica/query-plan/filter.js +544 -0
- package/dist/replica/query-plan/index.d.ts +4 -0
- package/dist/replica/query-plan/index.js +3 -0
- package/dist/replica/query-plan/order.d.ts +11 -0
- package/dist/replica/query-plan/order.js +158 -0
- package/dist/replica/query-plan/pagination.d.ts +6 -0
- package/dist/replica/query-plan/pagination.js +90 -0
- package/dist/replica/query-plan/resolve.d.ts +14 -0
- package/dist/replica/query-plan/resolve.js +163 -0
- package/dist/replica/query-plan/types.d.ts +89 -0
- package/dist/replica/query-plan/types.js +1 -0
- package/dist/replica/query-plan/util.d.ts +13 -0
- package/dist/replica/query-plan/util.js +62 -0
- package/dist/replica/query-plan.d.ts +3 -0
- package/dist/replica/query-plan.js +2 -0
- package/dist/replica/revalidation.d.ts +9 -0
- package/dist/replica/revalidation.js +86 -0
- package/dist/replica/selection.d.ts +20 -0
- package/dist/replica/selection.js +109 -0
- package/dist/replica/types.d.ts +657 -0
- package/dist/replica/types.js +1 -0
- package/dist/request.d.ts +17 -0
- package/dist/request.js +123 -0
- package/dist/sveltekit/auth.d.ts +18 -0
- package/dist/sveltekit/auth.js +9 -0
- package/dist/sveltekit/context.d.ts +28 -0
- package/dist/sveltekit/context.js +58 -0
- package/dist/sveltekit/index.d.ts +4 -0
- package/dist/sveltekit/index.js +4 -0
- package/dist/sveltekit/replica.d.ts +145 -0
- package/dist/sveltekit/replica.js +491 -0
- package/dist/sveltekit/server-replica.d.ts +52 -0
- package/dist/sveltekit/server-replica.js +176 -0
- package/dist/sveltekit/vite.d.ts +103 -0
- package/dist/sveltekit/vite.js +878 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.js +1 -0
- package/dist/websocket.d.ts +41 -0
- package/dist/websocket.js +187 -0
- 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;
|