@hops-ops/distributed 4.8.0 → 4.9.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/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/sveltekit/index.d.ts +2 -0
- package/dist/sveltekit/index.js +2 -0
- package/dist/sveltekit/lifecycle.d.ts +57 -0
- package/dist/sveltekit/lifecycle.js +454 -0
- package/dist/sveltekit/replica.d.ts +5 -2
- package/dist/sveltekit/replica.js +30 -1
- package/dist/sveltekit/vite.d.ts +36 -0
- package/dist/sveltekit/vite.js +351 -13
- package/package.json +3 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type DistributedGenerationEnvelope = Readonly<{
|
|
2
|
+
version: 1;
|
|
3
|
+
generationId: string;
|
|
4
|
+
releaseId: string;
|
|
5
|
+
applicationId?: string;
|
|
6
|
+
clientId?: string;
|
|
7
|
+
schemaId?: string;
|
|
8
|
+
protocolId?: string;
|
|
9
|
+
topologyId?: string;
|
|
10
|
+
workerId?: string;
|
|
11
|
+
memberId?: string;
|
|
12
|
+
compatibilityId?: string;
|
|
13
|
+
}>;
|
|
14
|
+
/** Parse a generation envelope without trusting unbounded process metadata. */
|
|
15
|
+
export declare function parseDistributedGenerationEnvelope(value: unknown, path?: string): DistributedGenerationEnvelope;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Parse a generation envelope without trusting unbounded process metadata. */
|
|
2
|
+
export function parseDistributedGenerationEnvelope(value, path = 'generation') {
|
|
3
|
+
const record = object(value, path);
|
|
4
|
+
if (record.version !== 1)
|
|
5
|
+
throw new TypeError(`${path}.version must be 1`);
|
|
6
|
+
const parsed = {
|
|
7
|
+
version: 1,
|
|
8
|
+
generationId: identity(record.generationId, `${path}.generationId`),
|
|
9
|
+
releaseId: identity(record.releaseId, `${path}.releaseId`)
|
|
10
|
+
};
|
|
11
|
+
for (const key of [
|
|
12
|
+
'applicationId',
|
|
13
|
+
'clientId',
|
|
14
|
+
'schemaId',
|
|
15
|
+
'protocolId',
|
|
16
|
+
'topologyId',
|
|
17
|
+
'workerId',
|
|
18
|
+
'memberId',
|
|
19
|
+
'compatibilityId'
|
|
20
|
+
]) {
|
|
21
|
+
if (record[key] !== undefined)
|
|
22
|
+
parsed[key] = identity(record[key], `${path}.${key}`);
|
|
23
|
+
}
|
|
24
|
+
return Object.freeze(parsed);
|
|
25
|
+
}
|
|
26
|
+
function object(value, path) {
|
|
27
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
28
|
+
throw new TypeError(`${path} must be an object`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function identity(value, path) {
|
|
33
|
+
if (typeof value !== 'string' ||
|
|
34
|
+
value.length === 0 ||
|
|
35
|
+
value.length > 512 ||
|
|
36
|
+
value !== value.trim() ||
|
|
37
|
+
/[\u0000-\u001f\u007f]/.test(value)) {
|
|
38
|
+
throw new TypeError(`${path} must be a bounded stable identity`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { GqlAuth, GqlError, GqlErrorLocation, GqlResult, GraphqlVariables } from './types.js';
|
|
2
|
+
export { parseDistributedGenerationEnvelope, type DistributedGenerationEnvelope } from './generation.js';
|
|
2
3
|
export { DISTRIBUTED_PROTOCOL_VERSION, DistributedProtocolError, compareDistributedDecimal, distributedLiveResumeExtensions, parseDistributedProtocolEnvelope, parseGraphqlResponseExtensions, type DistributedDecimalString, type DistributedCommandConsistency, type DistributedCommandMetadata, type DistributedCommandState, type DistributedIndexRevision, type DistributedLiveCursor, type DistributedLiveMetadata, type DistributedLiveResumeExtensions, type DistributedOpaqueString, type DistributedProjectionExpectation, type DistributedProjectionObservation, type DistributedProtocolValue, type DistributedProtocolEnvelope, type DistributedProtocolErrorCode, type DistributedQuerySnapshot, type DistributedRecordRevision, type DistributedTrustedPreset, type DistributedTrustedPresetCodec, type GraphqlResponseExtensions } from './protocol.js';
|
|
3
4
|
export { documentToString, type GqlDocument } from './document.js';
|
|
4
5
|
export { applyWsDevHeaderParams, buildAuthHeaders, wsConnectionInitPayload } from './auth-headers.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { parseDistributedGenerationEnvelope } from './generation.js';
|
|
1
2
|
export { DISTRIBUTED_PROTOCOL_VERSION, DistributedProtocolError, compareDistributedDecimal, distributedLiveResumeExtensions, parseDistributedProtocolEnvelope, parseGraphqlResponseExtensions } from './protocol.js';
|
|
2
3
|
export { documentToString } from './document.js';
|
|
3
4
|
export { applyWsDevHeaderParams, buildAuthHeaders, wsConnectionInitPayload } from './auth-headers.js';
|
package/dist/protocol.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* them, but never parses them as numbers or reconstructs server scopes.
|
|
8
8
|
*/
|
|
9
9
|
import { type CommandProjectionMetadata } from './replica/projection-delta/index.js';
|
|
10
|
+
import { type DistributedGenerationEnvelope } from './generation.js';
|
|
10
11
|
/** The only Distributed GraphQL protocol version understood by this package. */
|
|
11
12
|
export declare const DISTRIBUTED_PROTOCOL_VERSION: 1;
|
|
12
13
|
declare const opaqueDistributedString: unique symbol;
|
|
@@ -125,6 +126,7 @@ export type DistributedProtocolEnvelope = Readonly<Record<string, unknown> & {
|
|
|
125
126
|
schemaHash: string;
|
|
126
127
|
authorizationGeneration: string;
|
|
127
128
|
cacheScope: DistributedOpaqueString;
|
|
129
|
+
generation?: DistributedGenerationEnvelope;
|
|
128
130
|
operation?: string;
|
|
129
131
|
command?: DistributedCommandMetadata;
|
|
130
132
|
snapshot?: DistributedQuerySnapshot;
|
package/dist/protocol.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* them, but never parses them as numbers or reconstructs server scopes.
|
|
8
8
|
*/
|
|
9
9
|
import { parseCommandProjectionMetadata } from './replica/projection-delta/index.js';
|
|
10
|
+
import { parseDistributedGenerationEnvelope } from './generation.js';
|
|
10
11
|
/** The only Distributed GraphQL protocol version understood by this package. */
|
|
11
12
|
export const DISTRIBUTED_PROTOCOL_VERSION = 1;
|
|
12
13
|
/** Safe parse failure that reports structure, never hidden server values. */
|
|
@@ -82,6 +83,9 @@ export function parseDistributedProtocolEnvelope(value) {
|
|
|
82
83
|
const schemaHash = publicString(envelope.schemaHash, 'extensions.distributed.schemaHash');
|
|
83
84
|
const authorizationGeneration = publicString(envelope.authorizationGeneration, 'extensions.distributed.authorizationGeneration');
|
|
84
85
|
const cacheScope = opaqueString(envelope.cacheScope, 'extensions.distributed.cacheScope');
|
|
86
|
+
const generation = envelope.generation === undefined
|
|
87
|
+
? undefined
|
|
88
|
+
: parseDistributedGenerationEnvelope(envelope.generation, 'extensions.distributed.generation');
|
|
85
89
|
const operation = envelope.operation === undefined
|
|
86
90
|
? undefined
|
|
87
91
|
: publicString(envelope.operation, 'extensions.distributed.operation');
|
|
@@ -102,6 +106,7 @@ export function parseDistributedProtocolEnvelope(value) {
|
|
|
102
106
|
schemaHash,
|
|
103
107
|
authorizationGeneration,
|
|
104
108
|
cacheScope,
|
|
109
|
+
...(generation === undefined ? {} : { generation }),
|
|
105
110
|
...(operation === undefined ? {} : { operation }),
|
|
106
111
|
...(command === undefined ? {} : { command }),
|
|
107
112
|
...(snapshot === undefined ? {} : { snapshot }),
|
|
@@ -372,6 +372,14 @@ export function createReplicaCommandRuntime(replica, transport, entries, options
|
|
|
372
372
|
if (disposed) {
|
|
373
373
|
throw new ReplicaCommandRuntimeError('REPLICA_COMMAND_DISPOSED');
|
|
374
374
|
}
|
|
375
|
+
try {
|
|
376
|
+
options.lifecycle?.assertDispatchOpen();
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
throw new ReplicaCommandRuntimeError('REPLICA_COMMAND_RELOADING', {
|
|
380
|
+
cause: error
|
|
381
|
+
});
|
|
382
|
+
}
|
|
375
383
|
const snapshot = authoritySnapshot();
|
|
376
384
|
const scope = snapshot.scope;
|
|
377
385
|
if (scope === undefined ||
|
|
@@ -1088,6 +1096,9 @@ export function createReplicaCommandRuntime(replica, transport, entries, options
|
|
|
1088
1096
|
return Object.freeze({
|
|
1089
1097
|
commands: commands,
|
|
1090
1098
|
observeResult,
|
|
1099
|
+
pendingCommandIds() {
|
|
1100
|
+
return Object.freeze([...new Set([...pending.keys(), ...unmanagedLayers])].sort());
|
|
1101
|
+
},
|
|
1091
1102
|
dispose() {
|
|
1092
1103
|
if (disposed)
|
|
1093
1104
|
return;
|
|
@@ -14,6 +14,8 @@ export function commandRuntimeErrorMessage(code) {
|
|
|
14
14
|
return 'Command response violated the generated protocol contract';
|
|
15
15
|
case 'REPLICA_COMMAND_REJECTED':
|
|
16
16
|
return 'Command was rejected';
|
|
17
|
+
case 'REPLICA_COMMAND_RELOADING':
|
|
18
|
+
return 'Command dispatch is paused during a coherent application reload';
|
|
17
19
|
case 'REPLICA_COMMAND_SCOPE_INVALIDATED':
|
|
18
20
|
return 'Command authorization scope changed';
|
|
19
21
|
case 'REPLICA_COMMAND_STATUS_UNAVAILABLE':
|
|
@@ -154,7 +154,7 @@ export type ReplicaCommandCallOptions<TOutput> = PrepareReplicaCommandOptions &
|
|
|
154
154
|
transportRetries?: number;
|
|
155
155
|
onSucceeded?: (receipt: ReplicaCommandReceipt<TOutput>) => void | Promise<void>;
|
|
156
156
|
}>;
|
|
157
|
-
export type ReplicaCommandRuntimeErrorCode = 'REPLICA_COMMAND_ABORTED' | 'REPLICA_COMMAND_AUTHORITY_UNAVAILABLE' | 'REPLICA_COMMAND_DISPOSED' | 'REPLICA_COMMAND_OUTCOME_PENDING' | 'REPLICA_COMMAND_PROJECTION_FAILED' | 'REPLICA_COMMAND_PROTOCOL_INVALID' | 'REPLICA_COMMAND_REJECTED' | 'REPLICA_COMMAND_SCOPE_INVALIDATED' | 'REPLICA_COMMAND_STATUS_UNAVAILABLE' | 'REPLICA_COMMAND_TRANSPORT_AMBIGUOUS';
|
|
157
|
+
export type ReplicaCommandRuntimeErrorCode = 'REPLICA_COMMAND_ABORTED' | 'REPLICA_COMMAND_AUTHORITY_UNAVAILABLE' | 'REPLICA_COMMAND_DISPOSED' | 'REPLICA_COMMAND_OUTCOME_PENDING' | 'REPLICA_COMMAND_PROJECTION_FAILED' | 'REPLICA_COMMAND_PROTOCOL_INVALID' | 'REPLICA_COMMAND_REJECTED' | 'REPLICA_COMMAND_RELOADING' | 'REPLICA_COMMAND_SCOPE_INVALIDATED' | 'REPLICA_COMMAND_STATUS_UNAVAILABLE' | 'REPLICA_COMMAND_TRANSPORT_AMBIGUOUS';
|
|
158
158
|
export type AnyCommandArtifact = ReplicaCommandArtifact<unknown, unknown>;
|
|
159
159
|
export type CommandEntry = AnyCommandArtifact | Readonly<{
|
|
160
160
|
artifact: AnyCommandArtifact;
|
|
@@ -188,6 +188,10 @@ export type ReplicaCommandRuntimeOptions = Readonly<{
|
|
|
188
188
|
* Generated clients ship their inventory; apps do not invent board sims ad hoc.
|
|
189
189
|
*/
|
|
190
190
|
pureFunctions?: Readonly<Record<string, ReplicaPureFunction>>;
|
|
191
|
+
/** Framework lifecycle fence checked immediately before command preparation. */
|
|
192
|
+
lifecycle?: Readonly<{
|
|
193
|
+
assertDispatchOpen(): void;
|
|
194
|
+
}>;
|
|
191
195
|
}>;
|
|
192
196
|
export interface ReplicaCommandRuntime<TEntries extends Readonly<Record<string, CommandEntry>>> {
|
|
193
197
|
readonly commands: ReplicaBoundCommands<TEntries>;
|
|
@@ -198,6 +202,8 @@ export interface ReplicaCommandRuntime<TEntries extends Readonly<Record<string,
|
|
|
198
202
|
* it does not normalize or confirm cache data itself.
|
|
199
203
|
*/
|
|
200
204
|
observeResult(envelope: ReplicaResultEnvelope<unknown>): void;
|
|
205
|
+
/** Opaque receipt identities safe to carry across a controlled reload. */
|
|
206
|
+
pendingCommandIds(): readonly string[];
|
|
201
207
|
dispose(): void;
|
|
202
208
|
}
|
|
203
209
|
export type CapturedAuthority = Readonly<{
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { authFromPageData, type PageGraphqlData } from './auth.js';
|
|
2
|
+
export { distributedReloadLifecycle, registerDistributedReloadClient, validateDistributedReloadLocation, validateDistributedReloadState, type DistributedReloadLifecycle, type DistributedReloadOptions, type DistributedReloadStateDeclaration } from './lifecycle.js';
|
|
3
|
+
export { parseDistributedGenerationEnvelope, type DistributedGenerationEnvelope } from '../generation.js';
|
|
2
4
|
export { defineDistributedSvelteKitOperation, provideDistributedSvelteKitClient, useDistributedSvelteKitClient, useDistributedSvelteKitCommands } from './context.js';
|
|
3
5
|
export { bindSveltekitOperation, createPageDataSessionSource, createDistributedSvelteKit, sessionSourceFromPageData, type CreateDistributedSvelteKitOptions, type DistributedSvelteKitClient, type SveltekitBoundOperation, type SveltekitCommandRuntimeFactory, type SveltekitCommandRuntimeFactoryOptions, type SveltekitCommandRuntimeLike, type SveltekitDistributedPageData, type SveltekitPageDataSessionSource, type SveltekitPageDataSource, type SveltekitQuerySnapshot, type SveltekitQueryStore, type SveltekitReplicaAuthority, type SveltekitReplicaHydration, type SveltekitSessionSource, type UseSveltekitOperationOptions } from './replica.js';
|
|
4
6
|
export { createDistributedSvelteKitServer, matchDistributedRoute, registerDistributedRoute, type CreateDistributedSvelteKitServerOptions, type DistributedRouteOperation, type DistributedRoutePlan, type DistributedRouteVariables, type DistributedSvelteKitServer, type SveltekitServerLoadEventLike } from './server-replica.js';
|
package/dist/sveltekit/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { authFromPageData } from './auth.js';
|
|
2
|
+
export { distributedReloadLifecycle, registerDistributedReloadClient, validateDistributedReloadLocation, validateDistributedReloadState } from './lifecycle.js';
|
|
3
|
+
export { parseDistributedGenerationEnvelope } from '../generation.js';
|
|
2
4
|
export { defineDistributedSvelteKitOperation, provideDistributedSvelteKitClient, useDistributedSvelteKitClient, useDistributedSvelteKitCommands } from './context.js';
|
|
3
5
|
export { bindSveltekitOperation, createPageDataSessionSource, createDistributedSvelteKit, sessionSourceFromPageData } from './replica.js';
|
|
4
6
|
export { createDistributedSvelteKitServer, matchDistributedRoute, registerDistributedRoute } from './server-replica.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DistributedReplica, ReplicaDehydratedState } from '../replica/index.js';
|
|
2
|
+
export type DistributedReloadStateDeclaration = Readonly<{
|
|
3
|
+
/** Stable application-owned partition name. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Changes when the serialized representation becomes incompatible. */
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
capture(): unknown;
|
|
8
|
+
restore(value: unknown): void | Promise<void>;
|
|
9
|
+
}>;
|
|
10
|
+
export type DistributedReloadOptions = Readonly<{
|
|
11
|
+
/** Compiler-owned surface key; generated clients provide this automatically. */
|
|
12
|
+
key: string;
|
|
13
|
+
/** Explicitly declared serializable application state. Nothing else is captured. */
|
|
14
|
+
state?: readonly DistributedReloadStateDeclaration[];
|
|
15
|
+
/** Recover ambiguous receipts by ID; commands are never replayed by the framework. */
|
|
16
|
+
recoverPendingCommands?: (commandIds: readonly string[]) => void | Promise<void>;
|
|
17
|
+
}>;
|
|
18
|
+
type ReloadParticipant = Readonly<{
|
|
19
|
+
key: string;
|
|
20
|
+
prepare(): Readonly<{
|
|
21
|
+
replica?: ReplicaDehydratedState;
|
|
22
|
+
pendingCommandIds: readonly string[];
|
|
23
|
+
state: readonly Readonly<{
|
|
24
|
+
key: string;
|
|
25
|
+
fingerprint: string;
|
|
26
|
+
value: unknown;
|
|
27
|
+
}>[];
|
|
28
|
+
}>;
|
|
29
|
+
/** Return false when restoration is valid but must be retried later. */
|
|
30
|
+
restore(value: ReloadParticipantCapsule, compatible: boolean): boolean | Promise<boolean>;
|
|
31
|
+
}>;
|
|
32
|
+
type ReloadParticipantCapsule = Readonly<{
|
|
33
|
+
key: string;
|
|
34
|
+
replica?: ReplicaDehydratedState;
|
|
35
|
+
pendingCommandIds: readonly string[];
|
|
36
|
+
state: readonly Readonly<{
|
|
37
|
+
key: string;
|
|
38
|
+
fingerprint: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}>[];
|
|
41
|
+
}>;
|
|
42
|
+
export interface DistributedReloadLifecycle {
|
|
43
|
+
assertDispatchOpen(): void;
|
|
44
|
+
register(participant: ReloadParticipant): () => void;
|
|
45
|
+
destroy(): void;
|
|
46
|
+
}
|
|
47
|
+
/** Validate one explicitly declared application-state partition before capture. */
|
|
48
|
+
export declare function validateDistributedReloadState(value: unknown, path?: string): unknown;
|
|
49
|
+
/** Preserve browser location only when it cannot copy an auth callback secret. */
|
|
50
|
+
export declare function validateDistributedReloadLocation(location: URL): string;
|
|
51
|
+
/** Register one generated client with the shared browser reload transaction. */
|
|
52
|
+
export declare function registerDistributedReloadClient(replica: DistributedReplica, runtime: Readonly<{
|
|
53
|
+
pendingCommandIds?(): readonly string[];
|
|
54
|
+
}> | undefined, options: DistributedReloadOptions): () => void;
|
|
55
|
+
/** Browser singleton used by every generated SvelteKit surface in one page. */
|
|
56
|
+
export declare function distributedReloadLifecycle(): DistributedReloadLifecycle;
|
|
57
|
+
export {};
|