@hops-ops/distributed 4.8.0 → 4.10.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 +46 -8
- package/dist/generation.d.ts +15 -0
- package/dist/generation.js +41 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.js +5 -0
- package/dist/replica/command-runtime/create.js +11 -0
- package/dist/replica/command-runtime/errors.js +2 -0
- package/dist/replica/command-runtime/types.d.ts +7 -1
- package/dist/replica/distributed-replica/impl-protocol.d.ts +1 -0
- package/dist/replica/distributed-replica/impl-protocol.js +1 -0
- package/dist/replica/distributed-replica/impl.js +11 -0
- package/dist/replica/distributed-replica/watch.js +13 -1
- package/dist/replica/index.d.ts +1 -1
- package/dist/replica/types.d.ts +38 -0
- package/dist/sveltekit/boundary-lifecycle.d.ts +37 -0
- package/dist/sveltekit/boundary-lifecycle.js +355 -0
- package/dist/sveltekit/boundary-variables.d.ts +57 -0
- package/dist/sveltekit/boundary-variables.js +290 -0
- package/dist/sveltekit/context.d.ts +3 -0
- package/dist/sveltekit/context.js +8 -0
- package/dist/sveltekit/index.d.ts +7 -3
- package/dist/sveltekit/index.js +6 -2
- package/dist/sveltekit/islands/boundaries.d.ts +104 -0
- package/dist/sveltekit/islands/boundaries.js +734 -0
- package/dist/sveltekit/lifecycle.d.ts +57 -0
- package/dist/sveltekit/lifecycle.js +454 -0
- package/dist/sveltekit/operation-identity.d.ts +4 -0
- package/dist/sveltekit/operation-identity.js +10 -0
- package/dist/sveltekit/replica.d.ts +29 -2
- package/dist/sveltekit/replica.js +102 -6
- package/dist/sveltekit/server-replica.d.ts +10 -26
- package/dist/sveltekit/server-replica.js +159 -132
- package/dist/sveltekit/vite.d.ts +46 -3
- package/dist/sveltekit/vite.js +643 -36
- package/package.json +4 -3
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { boundaryOperationIdentity } from './operation-identity.js';
|
|
2
|
+
const MAX_BOUNDARY_INSTANCES = 4_096;
|
|
3
|
+
const MAX_INSTANCE_ID_BYTES = 512;
|
|
4
|
+
const MAX_LOCATION_PATHNAME_BYTES = 8_192;
|
|
5
|
+
const MAX_LOCATION_SEGMENTS = 256;
|
|
6
|
+
export class DistributedSvelteKitBoundaryController {
|
|
7
|
+
#replica;
|
|
8
|
+
#operations;
|
|
9
|
+
#diagnostic;
|
|
10
|
+
#instances = new Map();
|
|
11
|
+
#identityOwners = new Map();
|
|
12
|
+
#destroyed = false;
|
|
13
|
+
constructor(replica, operations, diagnostic) {
|
|
14
|
+
this.#replica = replica;
|
|
15
|
+
this.#operations = operations;
|
|
16
|
+
this.#diagnostic = diagnostic;
|
|
17
|
+
}
|
|
18
|
+
retain(instance, context) {
|
|
19
|
+
if (this.#destroyed) {
|
|
20
|
+
throw new Error('Distributed SvelteKit boundary controller is destroyed');
|
|
21
|
+
}
|
|
22
|
+
const validated = validateInstance(instance);
|
|
23
|
+
const resolved = this.#resolve(validated, context);
|
|
24
|
+
const signature = JSON.stringify(resolved.map(({ identity }) => identity));
|
|
25
|
+
const existing = this.#instances.get(validated.id);
|
|
26
|
+
if (existing !== undefined) {
|
|
27
|
+
if (existing.signature !== signature ||
|
|
28
|
+
existing.boundary !== validated.boundary) {
|
|
29
|
+
throw new Error('Distributed SvelteKit boundary instance changed ownership while retained');
|
|
30
|
+
}
|
|
31
|
+
existing.owners += 1;
|
|
32
|
+
this.#emit({
|
|
33
|
+
action: 'retain',
|
|
34
|
+
boundary: existing.boundary,
|
|
35
|
+
owners: existing.owners
|
|
36
|
+
});
|
|
37
|
+
return this.#lease(validated.id, existing);
|
|
38
|
+
}
|
|
39
|
+
if (this.#instances.size >= MAX_BOUNDARY_INSTANCES) {
|
|
40
|
+
throw new Error(`Distributed SvelteKit cannot retain more than ${MAX_BOUNDARY_INSTANCES} boundary instances`);
|
|
41
|
+
}
|
|
42
|
+
const watches = [];
|
|
43
|
+
try {
|
|
44
|
+
for (const item of resolved) {
|
|
45
|
+
const watch = this.#replica.watch(item.operation.artifact, item.variables, { live: item.live });
|
|
46
|
+
watches.push(Object.freeze({
|
|
47
|
+
watch,
|
|
48
|
+
identity: item.identity,
|
|
49
|
+
operation: item.operation.plan.operation,
|
|
50
|
+
live: item.live
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
for (const { watch } of watches)
|
|
56
|
+
watch.destroy();
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
const retained = {
|
|
60
|
+
signature,
|
|
61
|
+
boundary: validated.boundary,
|
|
62
|
+
owners: 1,
|
|
63
|
+
watches: Object.freeze(watches)
|
|
64
|
+
};
|
|
65
|
+
this.#instances.set(validated.id, retained);
|
|
66
|
+
for (const item of watches) {
|
|
67
|
+
const owners = (this.#identityOwners.get(item.identity) ?? 0) + 1;
|
|
68
|
+
this.#identityOwners.set(item.identity, owners);
|
|
69
|
+
this.#emit({
|
|
70
|
+
action: 'acquire',
|
|
71
|
+
boundary: retained.boundary,
|
|
72
|
+
operation: item.operation,
|
|
73
|
+
live: item.live,
|
|
74
|
+
owners
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return this.#lease(validated.id, retained);
|
|
78
|
+
}
|
|
79
|
+
/** Retain the nearest generated page/layout boundary at a browser location. */
|
|
80
|
+
retainLocation(location, context) {
|
|
81
|
+
const matched = matchNearestBoundary(this.#operations, location.pathname, location.kind);
|
|
82
|
+
if (matched === undefined) {
|
|
83
|
+
return Object.freeze({ release: () => undefined });
|
|
84
|
+
}
|
|
85
|
+
return this.retain({
|
|
86
|
+
id: location.id,
|
|
87
|
+
route: matched.route,
|
|
88
|
+
kind: location.kind
|
|
89
|
+
}, Object.freeze({ ...context, params: matched.params }));
|
|
90
|
+
}
|
|
91
|
+
/** Warm every generated page and owning layout selection for one target URL. */
|
|
92
|
+
async prefetchLocation(pathname, context) {
|
|
93
|
+
if (this.#destroyed) {
|
|
94
|
+
throw new Error('Distributed SvelteKit boundary controller is destroyed');
|
|
95
|
+
}
|
|
96
|
+
const matches = matchLocationBoundaries(this.#operations, pathname);
|
|
97
|
+
const scheduled = new Map();
|
|
98
|
+
for (const matched of matches) {
|
|
99
|
+
for (const item of this.#resolve({
|
|
100
|
+
id: 'prefetch',
|
|
101
|
+
route: matched.route,
|
|
102
|
+
kind: matched.kind,
|
|
103
|
+
boundary: `${matched.kind}:${matched.route}`
|
|
104
|
+
}, Object.freeze({ ...context, params: matched.params }))) {
|
|
105
|
+
scheduled.set(item.identity, item);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
await Promise.all([...scheduled.values()].map(async (item) => {
|
|
109
|
+
const snapshot = this.#replica.read(item.operation.artifact, item.variables);
|
|
110
|
+
if (snapshot.complete && !snapshot.stale)
|
|
111
|
+
return;
|
|
112
|
+
const watch = this.#replica.watch(item.operation.artifact, item.variables, { live: false });
|
|
113
|
+
try {
|
|
114
|
+
await watch.refresh();
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
watch.destroy();
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
/** Close every old-scope owner while keeping the controller reusable. */
|
|
122
|
+
disposeScope() {
|
|
123
|
+
if (this.#destroyed)
|
|
124
|
+
return;
|
|
125
|
+
this.#disposeInstances(true);
|
|
126
|
+
}
|
|
127
|
+
destroy() {
|
|
128
|
+
if (this.#destroyed)
|
|
129
|
+
return;
|
|
130
|
+
this.#destroyed = true;
|
|
131
|
+
this.#disposeInstances(false);
|
|
132
|
+
}
|
|
133
|
+
#resolve(instance, context) {
|
|
134
|
+
const selected = this.#operations.filter(({ plan }) => plan.kind === instance.kind && normalizeRoute(plan.route) === instance.route);
|
|
135
|
+
if (selected.length === 0) {
|
|
136
|
+
throw new Error(`Distributed SvelteKit boundary plan has no ${instance.boundary} selection`);
|
|
137
|
+
}
|
|
138
|
+
return Object.freeze(selected.map((operation) => {
|
|
139
|
+
const variables = operation.binding.resolve(context);
|
|
140
|
+
return Object.freeze({
|
|
141
|
+
operation,
|
|
142
|
+
variables,
|
|
143
|
+
identity: boundaryOperationIdentity(operation.artifact, variables),
|
|
144
|
+
live: operation.artifact.live !== undefined
|
|
145
|
+
});
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
#lease(instanceId, retained) {
|
|
149
|
+
let released = false;
|
|
150
|
+
return Object.freeze({
|
|
151
|
+
release: () => {
|
|
152
|
+
if (released)
|
|
153
|
+
return;
|
|
154
|
+
released = true;
|
|
155
|
+
if (this.#instances.get(instanceId) !== retained)
|
|
156
|
+
return;
|
|
157
|
+
retained.owners -= 1;
|
|
158
|
+
this.#emit({
|
|
159
|
+
action: 'release',
|
|
160
|
+
boundary: retained.boundary,
|
|
161
|
+
owners: retained.owners
|
|
162
|
+
});
|
|
163
|
+
if (retained.owners > 0)
|
|
164
|
+
return;
|
|
165
|
+
this.#instances.delete(instanceId);
|
|
166
|
+
this.#releaseWatches(retained);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
#disposeInstances(scope) {
|
|
171
|
+
for (const [instanceId, retained] of [...this.#instances]) {
|
|
172
|
+
this.#instances.delete(instanceId);
|
|
173
|
+
if (scope) {
|
|
174
|
+
this.#emit({
|
|
175
|
+
action: 'scope-dispose',
|
|
176
|
+
boundary: retained.boundary,
|
|
177
|
+
owners: 0
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
this.#releaseWatches(retained);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
#releaseWatches(retained) {
|
|
184
|
+
for (const item of retained.watches) {
|
|
185
|
+
item.watch.destroy();
|
|
186
|
+
const owners = (this.#identityOwners.get(item.identity) ?? 1) - 1;
|
|
187
|
+
if (owners > 0) {
|
|
188
|
+
this.#identityOwners.set(item.identity, owners);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
this.#identityOwners.delete(item.identity);
|
|
192
|
+
if (item.live) {
|
|
193
|
+
this.#emit({
|
|
194
|
+
action: 'final-unsubscribe',
|
|
195
|
+
boundary: retained.boundary,
|
|
196
|
+
operation: item.operation,
|
|
197
|
+
live: true,
|
|
198
|
+
owners: 0
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
#emit(event) {
|
|
204
|
+
try {
|
|
205
|
+
this.#diagnostic?.(Object.freeze(event));
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Diagnostics are observational and cannot alter lifecycle ownership.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function validateInstance(instance) {
|
|
213
|
+
if (instance === null || typeof instance !== 'object') {
|
|
214
|
+
throw new TypeError('Distributed SvelteKit boundary instance is required');
|
|
215
|
+
}
|
|
216
|
+
const id = typeof instance.id === 'string' ? instance.id.trim() : undefined;
|
|
217
|
+
if (typeof id !== 'string' ||
|
|
218
|
+
id.length === 0 ||
|
|
219
|
+
new TextEncoder().encode(id).byteLength > MAX_INSTANCE_ID_BYTES) {
|
|
220
|
+
throw new TypeError('Distributed SvelteKit boundary instance id is invalid');
|
|
221
|
+
}
|
|
222
|
+
if (instance.kind !== 'layout' && instance.kind !== 'page') {
|
|
223
|
+
throw new TypeError('Distributed SvelteKit boundary instance kind is invalid');
|
|
224
|
+
}
|
|
225
|
+
const route = normalizeRoute(instance.route);
|
|
226
|
+
return Object.freeze({
|
|
227
|
+
id,
|
|
228
|
+
route,
|
|
229
|
+
kind: instance.kind,
|
|
230
|
+
boundary: `${instance.kind}:${route}`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function normalizeRoute(value) {
|
|
234
|
+
if (typeof value !== 'string' || !value.startsWith('/')) {
|
|
235
|
+
throw new TypeError('Distributed SvelteKit boundary route must start with /');
|
|
236
|
+
}
|
|
237
|
+
const normalized = value.length === 1 ? value : value.replace(/\/+$/, '');
|
|
238
|
+
return normalized.length === 0 ? '/' : normalized;
|
|
239
|
+
}
|
|
240
|
+
function matchNearestBoundary(operations, pathname, kind) {
|
|
241
|
+
const matches = matchLocationBoundaries(operations, pathname).filter((candidate) => candidate.kind === kind);
|
|
242
|
+
if (matches.length === 0)
|
|
243
|
+
return undefined;
|
|
244
|
+
const mostSpecific = matches[0];
|
|
245
|
+
if (matches[1] !== undefined &&
|
|
246
|
+
matches[1].specificity === mostSpecific.specificity) {
|
|
247
|
+
throw new Error(`Distributed SvelteKit boundary plan is ambiguous for this ${kind} location`);
|
|
248
|
+
}
|
|
249
|
+
return mostSpecific;
|
|
250
|
+
}
|
|
251
|
+
function matchLocationBoundaries(operations, pathname) {
|
|
252
|
+
const routes = new Map();
|
|
253
|
+
for (const { plan } of operations) {
|
|
254
|
+
routes.set(`${plan.kind}\u0000${plan.route}`, {
|
|
255
|
+
route: plan.route,
|
|
256
|
+
kind: plan.kind
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const matches = [];
|
|
260
|
+
for (const candidate of routes.values()) {
|
|
261
|
+
const matched = matchRoutePattern(candidate.route, pathname, candidate.kind === 'layout');
|
|
262
|
+
if (matched !== undefined) {
|
|
263
|
+
matches.push(Object.freeze({ ...candidate, ...matched }));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return Object.freeze(matches.sort((left, right) => right.specificity - left.specificity ||
|
|
267
|
+
left.kind.localeCompare(right.kind) ||
|
|
268
|
+
left.route.localeCompare(right.route)));
|
|
269
|
+
}
|
|
270
|
+
function matchRoutePattern(pattern, pathname, prefix) {
|
|
271
|
+
const route = normalizeRoute(pattern);
|
|
272
|
+
const path = normalizePathname(pathname);
|
|
273
|
+
const routeSegments = route
|
|
274
|
+
.split('/')
|
|
275
|
+
.filter((segment) => segment.length > 0 && !/^\(.+\)$/.test(segment));
|
|
276
|
+
const pathSegments = path.split('/').filter((segment) => segment.length > 0);
|
|
277
|
+
const params = Object.create(null);
|
|
278
|
+
let pathIndex = 0;
|
|
279
|
+
let specificity = 0;
|
|
280
|
+
for (let routeIndex = 0; routeIndex < routeSegments.length; routeIndex += 1) {
|
|
281
|
+
const segment = routeSegments[routeIndex];
|
|
282
|
+
// SvelteKit matcher functions are application code. The browser adapter
|
|
283
|
+
// cannot execute or guess them from a generated route pattern.
|
|
284
|
+
if (segment.startsWith('[') && segment.includes('='))
|
|
285
|
+
return undefined;
|
|
286
|
+
const rest = /^\[\[?\.\.\.([^\]=]+)(?:=[^\]]+)?\]?\]$/.exec(segment);
|
|
287
|
+
if (rest !== null) {
|
|
288
|
+
const values = pathSegments.slice(pathIndex).map(decodePathSegment);
|
|
289
|
+
if (values.some((value) => value === undefined))
|
|
290
|
+
return undefined;
|
|
291
|
+
params[rest[1]] =
|
|
292
|
+
values.length === 0 ? undefined : values.join('/');
|
|
293
|
+
pathIndex = pathSegments.length;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
const optional = /^\[\[([^\]=]+)(?:=[^\]]+)?\]\]$/.exec(segment);
|
|
297
|
+
if (optional !== null) {
|
|
298
|
+
const remainingRequired = routeSegments
|
|
299
|
+
.slice(routeIndex + 1)
|
|
300
|
+
.filter((part) => !/^\[\[/.test(part)).length;
|
|
301
|
+
if (pathSegments.length - pathIndex > remainingRequired) {
|
|
302
|
+
const value = decodePathSegment(pathSegments[pathIndex++]);
|
|
303
|
+
if (value === undefined)
|
|
304
|
+
return undefined;
|
|
305
|
+
params[optional[1]] = value;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
params[optional[1]] = undefined;
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const dynamic = /^\[([^\]=]+)(?:=[^\]]+)?\]$/.exec(segment);
|
|
313
|
+
if (dynamic !== null) {
|
|
314
|
+
if (pathSegments[pathIndex] === undefined)
|
|
315
|
+
return undefined;
|
|
316
|
+
const value = decodePathSegment(pathSegments[pathIndex++]);
|
|
317
|
+
if (value === undefined)
|
|
318
|
+
return undefined;
|
|
319
|
+
params[dynamic[1]] = value;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (pathSegments[pathIndex] === undefined)
|
|
323
|
+
return undefined;
|
|
324
|
+
const actual = decodePathSegment(pathSegments[pathIndex++]);
|
|
325
|
+
if (actual === undefined || actual !== segment)
|
|
326
|
+
return undefined;
|
|
327
|
+
specificity += 1;
|
|
328
|
+
}
|
|
329
|
+
if (!prefix && pathIndex !== pathSegments.length)
|
|
330
|
+
return undefined;
|
|
331
|
+
return Object.freeze({
|
|
332
|
+
params: Object.freeze(params),
|
|
333
|
+
specificity: specificity * 1_000 + routeSegments.length
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function normalizePathname(value) {
|
|
337
|
+
if (typeof value !== 'string' ||
|
|
338
|
+
!value.startsWith('/') ||
|
|
339
|
+
new TextEncoder().encode(value).byteLength > MAX_LOCATION_PATHNAME_BYTES ||
|
|
340
|
+
value.split('/').length - 1 > MAX_LOCATION_SEGMENTS) {
|
|
341
|
+
throw new TypeError('Distributed SvelteKit location pathname is invalid or exceeds adapter limits');
|
|
342
|
+
}
|
|
343
|
+
const withoutQuery = value.split(/[?#]/u, 1)[0];
|
|
344
|
+
return withoutQuery.length === 1
|
|
345
|
+
? withoutQuery
|
|
346
|
+
: withoutQuery.replace(/\/+$/, '');
|
|
347
|
+
}
|
|
348
|
+
function decodePathSegment(value) {
|
|
349
|
+
try {
|
|
350
|
+
return decodeURIComponent(value);
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ReplicaOperationArtifact } from '../replica/index.js';
|
|
2
|
+
import type { GraphqlVariables } from '../types.js';
|
|
3
|
+
export type DistributedBoundaryVariableSource<TValue = unknown> = Readonly<{
|
|
4
|
+
kind: 'route_param';
|
|
5
|
+
name: string;
|
|
6
|
+
}> | Readonly<{
|
|
7
|
+
kind: 'search_param';
|
|
8
|
+
name: string;
|
|
9
|
+
mode?: 'first' | 'all';
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: 'trusted_session';
|
|
12
|
+
path: readonly string[];
|
|
13
|
+
}> | Readonly<{
|
|
14
|
+
kind: 'constant';
|
|
15
|
+
value: TValue;
|
|
16
|
+
}> | Readonly<{
|
|
17
|
+
kind: 'forwarded_prop';
|
|
18
|
+
path: readonly string[];
|
|
19
|
+
}> | Readonly<{
|
|
20
|
+
kind: 'omit';
|
|
21
|
+
}>;
|
|
22
|
+
export type DistributedBoundaryVariableSources<TVariables extends GraphqlVariables> = Readonly<{
|
|
23
|
+
[K in keyof TVariables]?: DistributedBoundaryVariableSource<TVariables[K]>;
|
|
24
|
+
}>;
|
|
25
|
+
export type DistributedBoundaryVariableContext<TSession = unknown, TProps = Readonly<Record<string, unknown>>> = Readonly<{
|
|
26
|
+
params: Readonly<Record<string, string | undefined>>;
|
|
27
|
+
search: URLSearchParams | Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
28
|
+
session: TSession | null;
|
|
29
|
+
props: TProps;
|
|
30
|
+
}>;
|
|
31
|
+
export type DistributedBoundaryBinding<TVariables extends GraphqlVariables, TSession = unknown, TProps = Readonly<Record<string, unknown>>> = Readonly<{
|
|
32
|
+
version: 1;
|
|
33
|
+
id: string;
|
|
34
|
+
artifactId: string;
|
|
35
|
+
sources: DistributedBoundaryVariableSources<TVariables>;
|
|
36
|
+
resolve(context: DistributedBoundaryVariableContext<TSession, TProps>): TVariables;
|
|
37
|
+
canonicalBytes(context: DistributedBoundaryVariableContext<TSession, TProps>): string;
|
|
38
|
+
}>;
|
|
39
|
+
export type DistributedBoundaryPlan = Readonly<{
|
|
40
|
+
operation: string;
|
|
41
|
+
route: string;
|
|
42
|
+
kind: 'layout' | 'page';
|
|
43
|
+
sourcePath?: string;
|
|
44
|
+
discovery: 'component' | 'route_document' | 'explicit';
|
|
45
|
+
}>;
|
|
46
|
+
export type DistributedBoundaryOperation<TData = unknown, TVariables extends GraphqlVariables = GraphqlVariables, TSession = unknown, TProps = Readonly<Record<string, unknown>>> = Readonly<{
|
|
47
|
+
plan: DistributedBoundaryPlan;
|
|
48
|
+
artifact: ReplicaOperationArtifact<TData, TVariables>;
|
|
49
|
+
binding: DistributedBoundaryBinding<TVariables, TSession, TProps>;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Define one closed, inspectable variable binding for every boundary lifecycle.
|
|
53
|
+
* The operation artifact remains the sole owner of coercion and cache identity.
|
|
54
|
+
*/
|
|
55
|
+
export declare function defineDistributedBoundaryBinding<TData, TVariables extends GraphqlVariables, TSession = unknown, TProps = Readonly<Record<string, unknown>>>(artifact: ReplicaOperationArtifact<TData, TVariables>, sources: DistributedBoundaryVariableSources<TVariables>): DistributedBoundaryBinding<TVariables, TSession, TProps>;
|
|
56
|
+
export declare function defineDistributedBoundaryOperation<TData, TVariables extends GraphqlVariables, TSession = unknown, TProps = Readonly<Record<string, unknown>>>(plan: DistributedBoundaryPlan, artifact: ReplicaOperationArtifact<TData, TVariables>, binding: DistributedBoundaryBinding<TVariables, TSession, TProps>): DistributedBoundaryOperation<TData, TVariables, TSession, TProps>;
|
|
57
|
+
export declare function resolveDistributedBoundaryVariables<TData, TVariables extends GraphqlVariables, TSession, TProps>(artifact: ReplicaOperationArtifact<TData, TVariables>, sources: DistributedBoundaryVariableSources<TVariables>, context: DistributedBoundaryVariableContext<TSession, TProps>): TVariables;
|