@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,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;
|