@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,77 @@
|
|
|
1
|
+
import { createReplicaCommandId } from '../command-id.js';
|
|
2
|
+
import { validateArtifact } from './validate.js';
|
|
3
|
+
import { selectReplicaTrustedPresetInventory } from './presets.js';
|
|
4
|
+
import { materializeInput, resolveConfirmations, resolveDirectProjection, resolveEffect } from './resolve.js';
|
|
5
|
+
import { cloneClientSurface, cloneRevalidation, cloneTrustedPresetDescriptors } from './clone.js';
|
|
6
|
+
import { validateUuidV7 } from './util.js';
|
|
7
|
+
export function prepareReplicaCommand(artifact, input, options = {}) {
|
|
8
|
+
return prepareReplicaCommandInternal(artifact, input, options);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Finalize a command with values obtained from the replica's current
|
|
12
|
+
* authoritative scope generation.
|
|
13
|
+
*
|
|
14
|
+
* The incoming inventory is scope-wide, so values owned by other commands are
|
|
15
|
+
* ignored. Every descriptor consumed by this artifact must nevertheless have
|
|
16
|
+
* one exact name/codec match before defaults, optimism, or transport state is
|
|
17
|
+
* created.
|
|
18
|
+
*
|
|
19
|
+
* This seam is package-internal and deliberately omitted from the public
|
|
20
|
+
* `@hops-ops/distributed/replica` entry point.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export function prepareReplicaCommandWithTrustedPresets(artifact, input, authoritativePresets, options = {}) {
|
|
25
|
+
return prepareReplicaCommandInternal(artifact, input, options, authoritativePresets);
|
|
26
|
+
}
|
|
27
|
+
export function prepareReplicaCommandInternal(artifact, input, options, authoritativePresets) {
|
|
28
|
+
const descriptors = validateArtifact(artifact, authoritativePresets !== undefined);
|
|
29
|
+
const trustedPresets = authoritativePresets === undefined
|
|
30
|
+
? undefined
|
|
31
|
+
: selectReplicaTrustedPresetInventory(descriptors, authoritativePresets);
|
|
32
|
+
const defaults = artifact.inputDefaults?.defaults ?? [];
|
|
33
|
+
const finalizedInput = materializeInput(artifact.input, input, defaults, options.generators);
|
|
34
|
+
const commandId = validateUuidV7(options.commandId ?? createReplicaCommandId(), 'options.commandId', 'REPLICA_COMMAND_INPUT_INVALID');
|
|
35
|
+
const variables = Object.freeze({
|
|
36
|
+
commandId,
|
|
37
|
+
...(artifact.input.kind === 'none' ? {} : { input: finalizedInput })
|
|
38
|
+
});
|
|
39
|
+
const operations = Object.freeze(artifact.effects.operations.map((effect, index) => resolveEffect(effect, finalizedInput, `artifact.effects.operations[${index}]`, trustedPresets)));
|
|
40
|
+
const confirmations = resolveConfirmations(artifact.confirmations, finalizedInput, trustedPresets);
|
|
41
|
+
const directProjection = resolveDirectProjection(artifact.directProjection, finalizedInput, trustedPresets);
|
|
42
|
+
const protocol = Object.freeze({
|
|
43
|
+
...artifact.protocol,
|
|
44
|
+
surface: cloneClientSurface(artifact.protocol.surface),
|
|
45
|
+
trustedPresets: cloneTrustedPresetDescriptors(artifact.protocol.trustedPresets)
|
|
46
|
+
});
|
|
47
|
+
const revalidation = cloneRevalidation(artifact.revalidation);
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
name: artifact.name,
|
|
50
|
+
commandId,
|
|
51
|
+
consistency: artifact.consistency,
|
|
52
|
+
input: finalizedInput,
|
|
53
|
+
transport: Object.freeze({
|
|
54
|
+
mutationField: artifact.mutationField,
|
|
55
|
+
document: artifact.document,
|
|
56
|
+
operationHash: artifact.operationHash,
|
|
57
|
+
protocol,
|
|
58
|
+
variables
|
|
59
|
+
}),
|
|
60
|
+
optimistic: Object.freeze({
|
|
61
|
+
version: 1,
|
|
62
|
+
operations,
|
|
63
|
+
fallback: 'revalidate'
|
|
64
|
+
}),
|
|
65
|
+
...(confirmations === undefined ? {} : { confirmations }),
|
|
66
|
+
...(directProjection === undefined ? {} : { directProjection }),
|
|
67
|
+
revalidation
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Match two command-local inventories as exact name/codec sets.
|
|
72
|
+
*
|
|
73
|
+
* Both sides are reparsed instead of trusting TypeScript annotations. Missing,
|
|
74
|
+
* extra, duplicate, unsupported, or codec-mismatched entries fail closed.
|
|
75
|
+
*
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type DistributedTrustedPreset } from '../../protocol.js';
|
|
2
|
+
import type { ReplicaMatchedTrustedPresetInventory, ReplicaTrustedPresetDescriptor } from './types.js';
|
|
3
|
+
export declare function matchReplicaTrustedPresetInventory(expected: readonly ReplicaTrustedPresetDescriptor[], authoritative: readonly DistributedTrustedPreset[]): ReplicaMatchedTrustedPresetInventory;
|
|
4
|
+
export declare function selectReplicaTrustedPresetInventory(expected: readonly ReplicaTrustedPresetDescriptor[], authoritative: readonly DistributedTrustedPreset[]): ReplicaMatchedTrustedPresetInventory;
|
|
5
|
+
/**
|
|
6
|
+
* Verify one parsed server receipt against the immutable prepared contract.
|
|
7
|
+
*
|
|
8
|
+
* This does not drive command status or overlay retirement. It only decides
|
|
9
|
+
* whether task 9 may safely consume the receipt as matching evidence.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseReplicaTrustedPresetDescriptors(value: unknown, path?: string): readonly ReplicaTrustedPresetDescriptor[];
|
|
12
|
+
export declare function trustedPresetName(value: unknown, path: string): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { isDistributedTrustedPresetCodec, parseDistributedTrustedPresetInventory } from '../../protocol.js';
|
|
2
|
+
import { isPlainRecord } from '../../lib/is-plain-record.js';
|
|
3
|
+
import { artifactInvalid, trustedPresetMismatch } from './util.js';
|
|
4
|
+
export function matchReplicaTrustedPresetInventory(expected, authoritative) {
|
|
5
|
+
const descriptors = parseReplicaTrustedPresetDescriptors(expected);
|
|
6
|
+
let values;
|
|
7
|
+
try {
|
|
8
|
+
values = parseDistributedTrustedPresetInventory(authoritative, 'authoritativePresets');
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
trustedPresetMismatch('authoritativePresets');
|
|
12
|
+
}
|
|
13
|
+
if (descriptors.length !== values.length) {
|
|
14
|
+
trustedPresetMismatch('authoritativePresets');
|
|
15
|
+
}
|
|
16
|
+
const byName = new Map(values.map((preset) => [preset.name, preset]));
|
|
17
|
+
for (let index = 0; index < descriptors.length; index += 1) {
|
|
18
|
+
const descriptor = descriptors[index];
|
|
19
|
+
const value = byName.get(descriptor.name);
|
|
20
|
+
if (value === undefined || value.codec !== descriptor.codec) {
|
|
21
|
+
trustedPresetMismatch(`authoritativePresets.${descriptor.name}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const resolve = (name) => {
|
|
25
|
+
const value = byName.get(name);
|
|
26
|
+
if (value === undefined) {
|
|
27
|
+
trustedPresetMismatch(`authoritativePresets.${name}`);
|
|
28
|
+
}
|
|
29
|
+
return value.value;
|
|
30
|
+
};
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
descriptors,
|
|
33
|
+
values,
|
|
34
|
+
resolve
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function selectReplicaTrustedPresetInventory(expected, authoritative) {
|
|
38
|
+
let values;
|
|
39
|
+
try {
|
|
40
|
+
values = parseDistributedTrustedPresetInventory(authoritative, 'authoritativePresets');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
trustedPresetMismatch('authoritativePresets');
|
|
44
|
+
}
|
|
45
|
+
const names = new Set(expected.map(({ name }) => name));
|
|
46
|
+
return matchReplicaTrustedPresetInventory(expected, values.filter(({ name }) => names.has(name)));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Verify one parsed server receipt against the immutable prepared contract.
|
|
50
|
+
*
|
|
51
|
+
* This does not drive command status or overlay retirement. It only decides
|
|
52
|
+
* whether task 9 may safely consume the receipt as matching evidence.
|
|
53
|
+
*/
|
|
54
|
+
export function parseReplicaTrustedPresetDescriptors(value, path = 'artifact.trustedPresets') {
|
|
55
|
+
if (!Array.isArray(value))
|
|
56
|
+
artifactInvalid(path);
|
|
57
|
+
const names = new Set();
|
|
58
|
+
return Object.freeze(value.map((candidate, index) => {
|
|
59
|
+
const itemPath = `${path}[${index}]`;
|
|
60
|
+
if (!isPlainRecord(candidate))
|
|
61
|
+
artifactInvalid(itemPath);
|
|
62
|
+
const name = trustedPresetName(candidate.name, `${itemPath}.name`);
|
|
63
|
+
if (names.has(name))
|
|
64
|
+
artifactInvalid(`${itemPath}.name`);
|
|
65
|
+
names.add(name);
|
|
66
|
+
if (!isDistributedTrustedPresetCodec(candidate.codec)) {
|
|
67
|
+
artifactInvalid(`${itemPath}.codec`);
|
|
68
|
+
}
|
|
69
|
+
return Object.freeze({
|
|
70
|
+
name,
|
|
71
|
+
codec: candidate.codec
|
|
72
|
+
});
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
export function trustedPresetName(value, path) {
|
|
76
|
+
if (typeof value !== 'string' ||
|
|
77
|
+
value.length === 0 ||
|
|
78
|
+
value.length > 128 ||
|
|
79
|
+
value.trim() !== value ||
|
|
80
|
+
/[\u0000-\u001f\u007f-\u009f]/.test(value)) {
|
|
81
|
+
artifactInvalid(path);
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DistributedCommandMetadata } from '../../protocol.js';
|
|
2
|
+
import type { ReplicaPreparedCommand, ReplicaReceiptVerification } from './types.js';
|
|
3
|
+
export declare function verifyReplicaCommandReceipt<TInput, TOutput>(prepared: ReplicaPreparedCommand<TInput, TOutput>, receipt: DistributedCommandMetadata): ReplicaReceiptVerification;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { receiptMismatch, sameProjectionMultiset } from './util.js';
|
|
2
|
+
export function verifyReplicaCommandReceipt(prepared, receipt) {
|
|
3
|
+
if (receipt.commandId !== prepared.commandId) {
|
|
4
|
+
receiptMismatch('receipt.commandId');
|
|
5
|
+
}
|
|
6
|
+
if (receipt.consistency !== prepared.consistency) {
|
|
7
|
+
receiptMismatch('receipt.consistency');
|
|
8
|
+
}
|
|
9
|
+
const contract = prepared.confirmations;
|
|
10
|
+
if (contract?.kind === 'unavailable') {
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
kind: 'revalidate',
|
|
13
|
+
revalidate: true,
|
|
14
|
+
reason: 'confirmation_unavailable'
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const expected = contract?.kind === 'finite'
|
|
18
|
+
? contract.expected.map(({ projector, model }) => ({
|
|
19
|
+
projection: projector,
|
|
20
|
+
model
|
|
21
|
+
}))
|
|
22
|
+
: [];
|
|
23
|
+
if (receipt.state === 'in_progress' && receipt.expects.length === 0) {
|
|
24
|
+
return Object.freeze({
|
|
25
|
+
kind: 'deferred',
|
|
26
|
+
revalidate: prepared.revalidation.required
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!sameProjectionMultiset(expected, receipt.expects)) {
|
|
30
|
+
receiptMismatch('receipt.expects');
|
|
31
|
+
}
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
kind: receipt.state === 'in_progress' ? 'deferred' : 'matched',
|
|
34
|
+
revalidate: prepared.revalidation.required
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReplicaValue } from '../types.js';
|
|
2
|
+
import type { ReplicaCommandConfirmations, ReplicaCommandDirectProjection, ReplicaCommandEffect, ReplicaCommandEffectExpression, ReplicaCommandEffectField, ReplicaCommandEffectKey, ReplicaCommandGenerators, ReplicaCommandInputDefault, ReplicaCommandShape, ReplicaCommandTypeDefinition, ReplicaCommandTypeField, ReplicaMatchedTrustedPresetInventory, ReplicaPreparedCommand, ReplicaPreparedCommandEffect, ReplicaPreparedConfirmations, ReplicaPreparedEffectField, ReplicaPreparedEffectKey } from './types.js';
|
|
3
|
+
export declare function materializeInput(shape: ReplicaCommandShape, input: unknown, defaults: readonly ReplicaCommandInputDefault[], generators: ReplicaCommandGenerators | undefined): unknown;
|
|
4
|
+
export declare function resolveEffect(effect: ReplicaCommandEffect, input: unknown, path: string, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): ReplicaPreparedCommandEffect;
|
|
5
|
+
export declare function resolveKey(key: ReplicaCommandEffectKey, input: unknown, path: string, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): ReplicaPreparedEffectKey;
|
|
6
|
+
export declare function resolveFields(fields: readonly ReplicaCommandEffectField[], input: unknown, path: string, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): readonly ReplicaPreparedEffectField[];
|
|
7
|
+
export declare function resolveExpression(expression: ReplicaCommandEffectExpression, input: unknown, path: string, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): ReplicaValue;
|
|
8
|
+
export declare function resolveInputPath(input: unknown, segments: readonly string[], path: string): ReplicaValue;
|
|
9
|
+
export declare function resolveConfirmations(confirmations: ReplicaCommandConfirmations | undefined, input: unknown, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): ReplicaPreparedConfirmations | undefined;
|
|
10
|
+
export declare function resolveDirectProjection(direct: ReplicaCommandDirectProjection | undefined, input: unknown, trustedPresets: ReplicaMatchedTrustedPresetInventory | undefined): ReplicaPreparedCommand<unknown>['directProjection'] | undefined;
|
|
11
|
+
export declare function fieldAtPath(definition: ReplicaCommandTypeDefinition, segments: readonly string[], path: string): ReplicaCommandTypeField;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { isPlainRecord } from '../../lib/is-plain-record.js';
|
|
2
|
+
import { GRAPHQL_NAME } from './constants.js';
|
|
3
|
+
import { artifactInvalid, inputInvalid, requiredString } from './util.js';
|
|
4
|
+
import { cloneDefinitionValue, cloneJson, cloneRelationship } from './clone.js';
|
|
5
|
+
export function materializeInput(shape, input, defaults, generators) {
|
|
6
|
+
switch (shape.kind) {
|
|
7
|
+
case 'none':
|
|
8
|
+
if (input !== undefined)
|
|
9
|
+
inputInvalid('input');
|
|
10
|
+
return undefined;
|
|
11
|
+
case 'object':
|
|
12
|
+
return cloneDefinitionValue(shape.definition, input, [], defaults, generators, 'input');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function resolveEffect(effect, input, path, trustedPresets) {
|
|
16
|
+
switch (effect.kind) {
|
|
17
|
+
case 'upsert':
|
|
18
|
+
case 'patch':
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
kind: effect.kind,
|
|
21
|
+
model: requiredString(effect.model, `${path}.model`),
|
|
22
|
+
key: resolveKey(effect.key, input, `${path}.key`, trustedPresets),
|
|
23
|
+
fields: resolveFields(effect.fields, input, `${path}.fields`, trustedPresets)
|
|
24
|
+
});
|
|
25
|
+
case 'delete':
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
kind: effect.kind,
|
|
28
|
+
model: requiredString(effect.model, `${path}.model`),
|
|
29
|
+
key: resolveKey(effect.key, input, `${path}.key`, trustedPresets)
|
|
30
|
+
});
|
|
31
|
+
case 'link':
|
|
32
|
+
case 'unlink':
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
kind: effect.kind,
|
|
35
|
+
relationship: cloneRelationship(effect.relationship, `${path}.relationship`),
|
|
36
|
+
source: resolveKey(effect.source, input, `${path}.source`, trustedPresets),
|
|
37
|
+
target: resolveKey(effect.target, input, `${path}.target`, trustedPresets)
|
|
38
|
+
});
|
|
39
|
+
case 'invalidate_model':
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
kind: effect.kind,
|
|
42
|
+
model: requiredString(effect.model, `${path}.model`)
|
|
43
|
+
});
|
|
44
|
+
case 'invalidate_relationship':
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
kind: effect.kind,
|
|
47
|
+
relationship: cloneRelationship(effect.relationship, `${path}.relationship`),
|
|
48
|
+
source: resolveKey(effect.source, input, `${path}.source`, trustedPresets)
|
|
49
|
+
});
|
|
50
|
+
default:
|
|
51
|
+
artifactInvalid(`${path}.kind`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function resolveKey(key, input, path, trustedPresets) {
|
|
55
|
+
if (key === null ||
|
|
56
|
+
typeof key !== 'object' ||
|
|
57
|
+
!Array.isArray(key.fields) ||
|
|
58
|
+
key.fields.length === 0) {
|
|
59
|
+
artifactInvalid(path);
|
|
60
|
+
}
|
|
61
|
+
return Object.freeze({
|
|
62
|
+
fields: resolveFields(key.fields, input, `${path}.fields`, trustedPresets)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function resolveFields(fields, input, path, trustedPresets) {
|
|
66
|
+
if (!Array.isArray(fields))
|
|
67
|
+
artifactInvalid(path);
|
|
68
|
+
const names = new Set();
|
|
69
|
+
return Object.freeze(fields.map((field, index) => {
|
|
70
|
+
const fieldPath = `${path}[${index}]`;
|
|
71
|
+
const name = requiredString(field.field, `${fieldPath}.field`);
|
|
72
|
+
if (names.has(name))
|
|
73
|
+
artifactInvalid(`${fieldPath}.field`);
|
|
74
|
+
names.add(name);
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
field: name,
|
|
77
|
+
value: resolveExpression(field.value, input, `${fieldPath}.value`, trustedPresets)
|
|
78
|
+
});
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
export function resolveExpression(expression, input, path, trustedPresets) {
|
|
82
|
+
switch (expression.kind) {
|
|
83
|
+
case 'input':
|
|
84
|
+
return resolveInputPath(input, expression.path, path);
|
|
85
|
+
case 'constant':
|
|
86
|
+
return cloneJson(expression.value, `${path}.value`);
|
|
87
|
+
case 'null':
|
|
88
|
+
return null;
|
|
89
|
+
case 'trusted_preset':
|
|
90
|
+
if (trustedPresets === undefined)
|
|
91
|
+
artifactInvalid(path);
|
|
92
|
+
return trustedPresets.resolve(expression.name);
|
|
93
|
+
default:
|
|
94
|
+
artifactInvalid(`${path}.kind`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function resolveInputPath(input, segments, path) {
|
|
98
|
+
if (!Array.isArray(segments) ||
|
|
99
|
+
segments.length === 0 ||
|
|
100
|
+
segments.some((segment) => !GRAPHQL_NAME.test(segment))) {
|
|
101
|
+
artifactInvalid(`${path}.path`);
|
|
102
|
+
}
|
|
103
|
+
let current = input;
|
|
104
|
+
for (const segment of segments) {
|
|
105
|
+
if (!isPlainRecord(current) ||
|
|
106
|
+
!Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
107
|
+
inputInvalid(`input.${segments.join('.')}`);
|
|
108
|
+
}
|
|
109
|
+
current = current[segment];
|
|
110
|
+
}
|
|
111
|
+
if (current === undefined)
|
|
112
|
+
inputInvalid(`input.${segments.join('.')}`);
|
|
113
|
+
return current;
|
|
114
|
+
}
|
|
115
|
+
export function resolveConfirmations(confirmations, input, trustedPresets) {
|
|
116
|
+
if (confirmations === undefined)
|
|
117
|
+
return undefined;
|
|
118
|
+
if (confirmations.kind === 'unavailable') {
|
|
119
|
+
return Object.freeze({ kind: 'unavailable' });
|
|
120
|
+
}
|
|
121
|
+
return Object.freeze({
|
|
122
|
+
kind: 'finite',
|
|
123
|
+
expected: Object.freeze(confirmations.expected.map((confirmation, index) => {
|
|
124
|
+
const path = `artifact.confirmations.expected[${index}]`;
|
|
125
|
+
const partition = confirmation.partition === undefined
|
|
126
|
+
? undefined
|
|
127
|
+
: resolveExpression(confirmation.partition, input, `${path}.partition`, trustedPresets);
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
projector: requiredString(confirmation.projector, `${path}.projector`),
|
|
130
|
+
model: requiredString(confirmation.model, `${path}.model`),
|
|
131
|
+
key: resolveKey(confirmation.key, input, `${path}.key`, trustedPresets),
|
|
132
|
+
...(partition === undefined ? {} : { partition })
|
|
133
|
+
});
|
|
134
|
+
}))
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
export function resolveDirectProjection(direct, input, trustedPresets) {
|
|
138
|
+
if (direct === undefined)
|
|
139
|
+
return undefined;
|
|
140
|
+
const partition = direct.partition === undefined
|
|
141
|
+
? undefined
|
|
142
|
+
: resolveExpression(direct.partition, input, 'artifact.directProjection.partition', trustedPresets);
|
|
143
|
+
return Object.freeze({
|
|
144
|
+
topology: Object.freeze({ ...direct.topology }),
|
|
145
|
+
model: direct.model,
|
|
146
|
+
identityFields: Object.freeze([...direct.identityFields]),
|
|
147
|
+
...(partition === undefined ? {} : { partition }),
|
|
148
|
+
changeEpoch: direct.changeEpoch
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export function fieldAtPath(definition, segments, path) {
|
|
152
|
+
let current = definition;
|
|
153
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
154
|
+
const field = current.fields.find(({ name }) => name === segments[index]);
|
|
155
|
+
if (field === undefined)
|
|
156
|
+
artifactInvalid(`${path}.path`);
|
|
157
|
+
if (index + 1 === segments.length)
|
|
158
|
+
return field;
|
|
159
|
+
if (field.list || field.nested === undefined) {
|
|
160
|
+
artifactInvalid(`${path}.path`);
|
|
161
|
+
}
|
|
162
|
+
current = field.nested;
|
|
163
|
+
}
|
|
164
|
+
artifactInvalid(`${path}.path`);
|
|
165
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { DistributedCommandConsistency, DistributedTrustedPreset, DistributedTrustedPresetCodec } from '../../protocol.js';
|
|
2
|
+
import type { ReplicaClientSurface, ReplicaValue } from '../types.js';
|
|
3
|
+
export type ReplicaCommandScalarCodec = DistributedTrustedPresetCodec;
|
|
4
|
+
/** Static name/codec declaration emitted without a server-derived value. */
|
|
5
|
+
export type ReplicaTrustedPresetDescriptor = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly codec: ReplicaCommandScalarCodec;
|
|
8
|
+
};
|
|
9
|
+
export type ReplicaCommandShape = {
|
|
10
|
+
readonly kind: 'none';
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'object';
|
|
13
|
+
readonly definition: ReplicaCommandTypeDefinition;
|
|
14
|
+
};
|
|
15
|
+
export type ReplicaCommandTypeDefinition = {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly fields: readonly ReplicaCommandTypeField[];
|
|
18
|
+
};
|
|
19
|
+
export type ReplicaCommandTypeField = {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly typeName: string;
|
|
22
|
+
readonly nullable: boolean;
|
|
23
|
+
readonly list: boolean;
|
|
24
|
+
readonly itemNullable: boolean;
|
|
25
|
+
readonly codec?: ReplicaCommandScalarCodec;
|
|
26
|
+
readonly nested?: ReplicaCommandTypeDefinition;
|
|
27
|
+
};
|
|
28
|
+
export type ReplicaCommandInputDefault = {
|
|
29
|
+
readonly path: readonly string[];
|
|
30
|
+
readonly generator: 'uuid_v7' | 'ulid';
|
|
31
|
+
};
|
|
32
|
+
export type ReplicaCommandInputDefaults = {
|
|
33
|
+
readonly version: 1;
|
|
34
|
+
readonly defaults: readonly ReplicaCommandInputDefault[];
|
|
35
|
+
};
|
|
36
|
+
export type ReplicaCommandEffectExpression = {
|
|
37
|
+
readonly kind: 'input';
|
|
38
|
+
readonly path: readonly string[];
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: 'trusted_preset';
|
|
41
|
+
readonly name: string;
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: 'constant';
|
|
44
|
+
readonly value: ReplicaValue;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'null';
|
|
47
|
+
};
|
|
48
|
+
export type ReplicaCommandEffectField = {
|
|
49
|
+
readonly field: string;
|
|
50
|
+
readonly value: ReplicaCommandEffectExpression;
|
|
51
|
+
};
|
|
52
|
+
export type ReplicaCommandEffectKey = {
|
|
53
|
+
readonly fields: readonly ReplicaCommandEffectField[];
|
|
54
|
+
};
|
|
55
|
+
export type ReplicaCommandEffectRelationship = {
|
|
56
|
+
readonly sourceModel: string;
|
|
57
|
+
readonly field: string;
|
|
58
|
+
readonly targetModel: string;
|
|
59
|
+
};
|
|
60
|
+
export type ReplicaCommandEffect = {
|
|
61
|
+
readonly kind: 'upsert' | 'patch';
|
|
62
|
+
readonly model: string;
|
|
63
|
+
readonly key: ReplicaCommandEffectKey;
|
|
64
|
+
readonly fields: readonly ReplicaCommandEffectField[];
|
|
65
|
+
} | {
|
|
66
|
+
readonly kind: 'delete';
|
|
67
|
+
readonly model: string;
|
|
68
|
+
readonly key: ReplicaCommandEffectKey;
|
|
69
|
+
} | {
|
|
70
|
+
readonly kind: 'link' | 'unlink';
|
|
71
|
+
readonly relationship: ReplicaCommandEffectRelationship;
|
|
72
|
+
readonly source: ReplicaCommandEffectKey;
|
|
73
|
+
readonly target: ReplicaCommandEffectKey;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: 'invalidate_model';
|
|
76
|
+
readonly model: string;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: 'invalidate_relationship';
|
|
79
|
+
readonly relationship: ReplicaCommandEffectRelationship;
|
|
80
|
+
readonly source: ReplicaCommandEffectKey;
|
|
81
|
+
};
|
|
82
|
+
export type ReplicaCommandEffects = {
|
|
83
|
+
readonly version: 1;
|
|
84
|
+
readonly operations: readonly ReplicaCommandEffect[];
|
|
85
|
+
readonly fallback: 'revalidate';
|
|
86
|
+
};
|
|
87
|
+
export type ReplicaCommandConfirmation = {
|
|
88
|
+
readonly projector: string;
|
|
89
|
+
readonly model: string;
|
|
90
|
+
readonly key: ReplicaCommandEffectKey;
|
|
91
|
+
readonly partition?: ReplicaCommandEffectExpression;
|
|
92
|
+
};
|
|
93
|
+
export type ReplicaCommandConfirmations = {
|
|
94
|
+
readonly version: 1;
|
|
95
|
+
readonly kind: 'finite';
|
|
96
|
+
readonly expected: readonly ReplicaCommandConfirmation[];
|
|
97
|
+
readonly fallback: 'revalidate';
|
|
98
|
+
} | {
|
|
99
|
+
readonly version: 1;
|
|
100
|
+
readonly kind: 'unavailable';
|
|
101
|
+
readonly expected: readonly [];
|
|
102
|
+
readonly fallback: 'revalidate';
|
|
103
|
+
};
|
|
104
|
+
export type ReplicaCommandDirectProjection = {
|
|
105
|
+
readonly topology: {
|
|
106
|
+
readonly version: 1;
|
|
107
|
+
readonly name: string;
|
|
108
|
+
readonly digest: string;
|
|
109
|
+
};
|
|
110
|
+
readonly model: string;
|
|
111
|
+
readonly identityFields: readonly string[];
|
|
112
|
+
readonly partition?: ReplicaCommandEffectExpression;
|
|
113
|
+
readonly changeEpoch: string;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Conservative invalidation inventory emitted by the compiler.
|
|
117
|
+
*
|
|
118
|
+
* `dependencies` are the role-visible physical/model dependencies which task 9
|
|
119
|
+
* may hand to the replica revalidator. `required` means the command cannot be
|
|
120
|
+
* proven by its local effect/confirmation contract alone.
|
|
121
|
+
*/
|
|
122
|
+
export type ReplicaCommandRevalidationPlan = {
|
|
123
|
+
readonly version: 1;
|
|
124
|
+
readonly required: boolean;
|
|
125
|
+
readonly dependencies: readonly string[];
|
|
126
|
+
readonly models: readonly string[];
|
|
127
|
+
readonly relationships: readonly ReplicaCommandEffectRelationship[];
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Framework-neutral executable command descriptor emitted by `dctl client`.
|
|
131
|
+
*
|
|
132
|
+
* The compiler has already validated the Rust-owned declaration. Runtime
|
|
133
|
+
* validation remains fail-closed so a stale or hand-edited artifact cannot
|
|
134
|
+
* create optimistic cache truth.
|
|
135
|
+
*/
|
|
136
|
+
export type ReplicaCommandArtifact<TInput = void, TOutput = unknown> = {
|
|
137
|
+
readonly version: 1;
|
|
138
|
+
readonly name: string;
|
|
139
|
+
readonly mutationField: string;
|
|
140
|
+
readonly document: string;
|
|
141
|
+
readonly operationHash: string;
|
|
142
|
+
readonly protocol: {
|
|
143
|
+
readonly version: 1;
|
|
144
|
+
readonly schemaHash: string;
|
|
145
|
+
readonly protocolHash: string;
|
|
146
|
+
readonly surface: ReplicaClientSurface;
|
|
147
|
+
readonly operation: string;
|
|
148
|
+
/** Exact scope-wide preset inventory for this generated client surface. */
|
|
149
|
+
readonly trustedPresets: readonly ReplicaTrustedPresetDescriptor[];
|
|
150
|
+
};
|
|
151
|
+
readonly input: ReplicaCommandShape;
|
|
152
|
+
readonly output: ReplicaCommandShape;
|
|
153
|
+
readonly inputDefaults?: ReplicaCommandInputDefaults;
|
|
154
|
+
readonly consistency: DistributedCommandConsistency;
|
|
155
|
+
readonly effects: ReplicaCommandEffects;
|
|
156
|
+
readonly confirmations?: ReplicaCommandConfirmations;
|
|
157
|
+
readonly directProjection?: ReplicaCommandDirectProjection;
|
|
158
|
+
readonly trustedPresets?: readonly ReplicaTrustedPresetDescriptor[];
|
|
159
|
+
readonly revalidation: ReplicaCommandRevalidationPlan;
|
|
160
|
+
/** Phantom slots preserve generated input/output types. */
|
|
161
|
+
readonly __input?: TInput;
|
|
162
|
+
readonly __output?: TOutput;
|
|
163
|
+
};
|
|
164
|
+
export type ReplicaPreparedEffectField = {
|
|
165
|
+
readonly field: string;
|
|
166
|
+
readonly value: ReplicaValue;
|
|
167
|
+
};
|
|
168
|
+
export type ReplicaPreparedEffectKey = {
|
|
169
|
+
readonly fields: readonly ReplicaPreparedEffectField[];
|
|
170
|
+
};
|
|
171
|
+
export type ReplicaPreparedCommandEffect = {
|
|
172
|
+
readonly kind: 'upsert' | 'patch';
|
|
173
|
+
readonly model: string;
|
|
174
|
+
readonly key: ReplicaPreparedEffectKey;
|
|
175
|
+
readonly fields: readonly ReplicaPreparedEffectField[];
|
|
176
|
+
} | {
|
|
177
|
+
readonly kind: 'delete';
|
|
178
|
+
readonly model: string;
|
|
179
|
+
readonly key: ReplicaPreparedEffectKey;
|
|
180
|
+
} | {
|
|
181
|
+
readonly kind: 'link' | 'unlink';
|
|
182
|
+
readonly relationship: ReplicaCommandEffectRelationship;
|
|
183
|
+
readonly source: ReplicaPreparedEffectKey;
|
|
184
|
+
readonly target: ReplicaPreparedEffectKey;
|
|
185
|
+
} | {
|
|
186
|
+
readonly kind: 'invalidate_model';
|
|
187
|
+
readonly model: string;
|
|
188
|
+
} | {
|
|
189
|
+
readonly kind: 'invalidate_relationship';
|
|
190
|
+
readonly relationship: ReplicaCommandEffectRelationship;
|
|
191
|
+
readonly source: ReplicaPreparedEffectKey;
|
|
192
|
+
};
|
|
193
|
+
export type ReplicaPreparedConfirmation = {
|
|
194
|
+
readonly projector: string;
|
|
195
|
+
readonly model: string;
|
|
196
|
+
readonly key: ReplicaPreparedEffectKey;
|
|
197
|
+
readonly partition?: ReplicaValue;
|
|
198
|
+
};
|
|
199
|
+
export type ReplicaPreparedConfirmations = {
|
|
200
|
+
readonly kind: 'finite';
|
|
201
|
+
readonly expected: readonly ReplicaPreparedConfirmation[];
|
|
202
|
+
} | {
|
|
203
|
+
readonly kind: 'unavailable';
|
|
204
|
+
};
|
|
205
|
+
export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer TItem)[] ? readonly DeepReadonly<TItem>[] : T extends object ? {
|
|
206
|
+
readonly [TKey in keyof T]: DeepReadonly<T[TKey]>;
|
|
207
|
+
} : T;
|
|
208
|
+
export type ReplicaCommandVariables<TInput> = [TInput] extends [void] ? Readonly<{
|
|
209
|
+
commandId: string;
|
|
210
|
+
}> : Readonly<{
|
|
211
|
+
commandId: string;
|
|
212
|
+
input: DeepReadonly<TInput>;
|
|
213
|
+
}>;
|
|
214
|
+
export type ReplicaPreparedCommand<TInput = void, TOutput = unknown> = {
|
|
215
|
+
readonly name: string;
|
|
216
|
+
readonly commandId: string;
|
|
217
|
+
readonly consistency: DistributedCommandConsistency;
|
|
218
|
+
readonly input: DeepReadonly<TInput>;
|
|
219
|
+
readonly transport: {
|
|
220
|
+
readonly mutationField: string;
|
|
221
|
+
readonly document: string;
|
|
222
|
+
readonly operationHash: string;
|
|
223
|
+
readonly protocol: ReplicaCommandArtifact<TInput, TOutput>['protocol'];
|
|
224
|
+
readonly variables: ReplicaCommandVariables<TInput>;
|
|
225
|
+
};
|
|
226
|
+
readonly optimistic: {
|
|
227
|
+
readonly version: 1;
|
|
228
|
+
readonly operations: readonly ReplicaPreparedCommandEffect[];
|
|
229
|
+
readonly fallback: 'revalidate';
|
|
230
|
+
};
|
|
231
|
+
readonly confirmations?: ReplicaPreparedConfirmations;
|
|
232
|
+
readonly directProjection?: {
|
|
233
|
+
readonly topology: ReplicaCommandDirectProjection['topology'];
|
|
234
|
+
readonly model: string;
|
|
235
|
+
readonly identityFields: readonly string[];
|
|
236
|
+
readonly partition?: ReplicaValue;
|
|
237
|
+
readonly changeEpoch: string;
|
|
238
|
+
};
|
|
239
|
+
readonly revalidation: ReplicaCommandRevalidationPlan;
|
|
240
|
+
/** Phantom output slot for generated wrappers. */
|
|
241
|
+
readonly __output?: TOutput;
|
|
242
|
+
};
|
|
243
|
+
export type ReplicaCommandGenerators = {
|
|
244
|
+
readonly uuidV7?: () => string;
|
|
245
|
+
readonly ulid?: () => string;
|
|
246
|
+
};
|
|
247
|
+
export type PrepareReplicaCommandOptions = {
|
|
248
|
+
readonly commandId?: string;
|
|
249
|
+
readonly generators?: ReplicaCommandGenerators;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Exact immutable command-local view over a server-derived scope inventory.
|
|
253
|
+
*
|
|
254
|
+
* The backing lookup is intentionally not exposed as a mutable Map.
|
|
255
|
+
*
|
|
256
|
+
* @internal
|
|
257
|
+
*/
|
|
258
|
+
export type ReplicaMatchedTrustedPresetInventory = Readonly<{
|
|
259
|
+
descriptors: readonly ReplicaTrustedPresetDescriptor[];
|
|
260
|
+
values: readonly DistributedTrustedPreset[];
|
|
261
|
+
resolve(name: string): ReplicaValue;
|
|
262
|
+
}>;
|
|
263
|
+
export type ReplicaReceiptVerification = {
|
|
264
|
+
readonly kind: 'matched';
|
|
265
|
+
readonly revalidate: boolean;
|
|
266
|
+
} | {
|
|
267
|
+
readonly kind: 'deferred';
|
|
268
|
+
readonly revalidate: boolean;
|
|
269
|
+
} | {
|
|
270
|
+
readonly kind: 'revalidate';
|
|
271
|
+
readonly revalidate: true;
|
|
272
|
+
readonly reason: 'confirmation_unavailable';
|
|
273
|
+
};
|
|
274
|
+
export type ReplicaCommandContractErrorCode = 'REPLICA_COMMAND_ARTIFACT_INVALID' | 'REPLICA_COMMAND_INPUT_INVALID' | 'REPLICA_COMMAND_RECEIPT_MISMATCH' | 'REPLICA_COMMAND_TRUSTED_PRESET_MISMATCH';
|
|
275
|
+
export type TrustedPresetReference = Readonly<{
|
|
276
|
+
name: string;
|
|
277
|
+
path: string;
|
|
278
|
+
}>;
|