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