@hops-ops/distributed 0.0.0 → 4.0.1

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.
Files changed (78) hide show
  1. package/README.md +26 -13
  2. package/dist/auth-headers.js +3 -3
  3. package/dist/internal/cache-engine/engine.d.ts +3 -1
  4. package/dist/internal/cache-engine/engine.js +137 -22
  5. package/dist/internal/cache-engine/errors.d.ts +4 -0
  6. package/dist/internal/cache-engine/errors.js +8 -0
  7. package/dist/internal/cache-engine/index.d.ts +2 -2
  8. package/dist/internal/cache-engine/index.js +1 -1
  9. package/dist/internal/cache-engine/types.d.ts +16 -0
  10. package/dist/internal/cache-engine.d.ts +2 -2
  11. package/dist/internal/cache-engine.js +1 -1
  12. package/dist/protocol.d.ts +9 -2
  13. package/dist/protocol.js +35 -7
  14. package/dist/replica/command-runtime/create.js +499 -57
  15. package/dist/replica/command-runtime/index.d.ts +1 -1
  16. package/dist/replica/command-runtime/index.js +1 -1
  17. package/dist/replica/command-runtime/lib/effects.d.ts +22 -9
  18. package/dist/replica/command-runtime/lib/effects.js +118 -20
  19. package/dist/replica/command-runtime/lib/projection.d.ts +2 -2
  20. package/dist/replica/command-runtime/lib/projection.js +14 -6
  21. package/dist/replica/command-runtime/lib/status.js +18 -10
  22. package/dist/replica/command-runtime/lib/transport.js +4 -0
  23. package/dist/replica/command-runtime/lib/util.js +8 -3
  24. package/dist/replica/command-runtime/symbols.d.ts +9 -0
  25. package/dist/replica/command-runtime/symbols.js +9 -0
  26. package/dist/replica/command-runtime/types.d.ts +15 -6
  27. package/dist/replica/command-runtime/types.js +1 -1
  28. package/dist/replica/command-runtime.d.ts +1 -1
  29. package/dist/replica/command-runtime.js +1 -1
  30. package/dist/replica/commands/clone.js +2 -1
  31. package/dist/replica/commands/index.d.ts +1 -0
  32. package/dist/replica/commands/prepare.js +7 -4
  33. package/dist/replica/commands/receipt.js +30 -16
  34. package/dist/replica/commands/types.d.ts +10 -5
  35. package/dist/replica/commands/validate.d.ts +5 -1
  36. package/dist/replica/commands/validate.js +129 -62
  37. package/dist/replica/diagnostics/inspect.js +51 -19
  38. package/dist/replica/diagnostics/types.d.ts +7 -7
  39. package/dist/replica/distributed-replica/helpers.js +22 -8
  40. package/dist/replica/distributed-replica/hydration.js +14 -2
  41. package/dist/replica/distributed-replica/impl-diagnostics.d.ts +1 -1
  42. package/dist/replica/distributed-replica/impl-diagnostics.js +4 -3
  43. package/dist/replica/distributed-replica/impl-hydration-orchestrate.d.ts +1 -1
  44. package/dist/replica/distributed-replica/impl-hydration-orchestrate.js +76 -42
  45. package/dist/replica/distributed-replica/impl-optimistic.d.ts +11 -2
  46. package/dist/replica/distributed-replica/impl-optimistic.js +22 -6
  47. package/dist/replica/distributed-replica/impl-protocol.js +4 -2
  48. package/dist/replica/distributed-replica/impl.d.ts +8 -2
  49. package/dist/replica/distributed-replica/impl.js +22 -5
  50. package/dist/replica/distributed-replica/optimistic.js +31 -3
  51. package/dist/replica/distributed-replica/types.d.ts +1 -0
  52. package/dist/replica/index-maintenance/engine.js +14 -6
  53. package/dist/replica/index.d.ts +4 -0
  54. package/dist/replica/index.js +2 -0
  55. package/dist/replica/materialize.js +25 -0
  56. package/dist/replica/mutation-cache.d.ts +62 -0
  57. package/dist/replica/mutation-cache.js +91 -0
  58. package/dist/replica/operation-binding.js +17 -6
  59. package/dist/replica/persistence/state.js +9 -1
  60. package/dist/replica/projection-delta/canonical.d.ts +6 -0
  61. package/dist/replica/projection-delta/canonical.js +29 -0
  62. package/dist/replica/projection-delta/index.d.ts +5 -0
  63. package/dist/replica/projection-delta/index.js +3 -0
  64. package/dist/replica/projection-delta/resolve.d.ts +8 -0
  65. package/dist/replica/projection-delta/resolve.js +266 -0
  66. package/dist/replica/projection-delta/types.d.ts +415 -0
  67. package/dist/replica/projection-delta/types.js +6 -0
  68. package/dist/replica/projection-delta/validate.d.ts +18 -0
  69. package/dist/replica/projection-delta/validate.js +1718 -0
  70. package/dist/replica/projection-delta/wasm-pure.d.ts +36 -0
  71. package/dist/replica/projection-delta/wasm-pure.js +71 -0
  72. package/dist/replica/query-plan/pagination.js +18 -8
  73. package/dist/replica/types.d.ts +13 -2
  74. package/dist/sveltekit/replica.d.ts +1 -1
  75. package/dist/sveltekit/replica.js +3 -0
  76. package/dist/sveltekit/vite.d.ts +6 -6
  77. package/dist/sveltekit/vite.js +6 -6
  78. package/package.json +3 -3
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Role-safe cache lowering for event-independent mutation IR (v1).
3
+ *
4
+ * Mirrors `distributed::mutation::cache::lower_mutation_cache`. Mutation IR is
5
+ * the semantic source; this module produces atomic optimistic-layer effects
6
+ * without a second browser CRUD interpreter.
7
+ */
8
+ export type MutationCacheVisibility = {
9
+ readonly authorized: boolean;
10
+ readonly hasBaseRecord: boolean;
11
+ readonly relationshipCovered: boolean;
12
+ };
13
+ export type MutationTarget = {
14
+ readonly model: string;
15
+ readonly storage: string;
16
+ };
17
+ export type MutationFieldAssignment = {
18
+ readonly kind: "set" | "unset" | "unknown";
19
+ readonly expression?: unknown;
20
+ };
21
+ export type MutationField = {
22
+ readonly name: string;
23
+ readonly assignment: MutationFieldAssignment;
24
+ };
25
+ export type MutationOperation = {
26
+ readonly kind: string;
27
+ readonly target: MutationTarget;
28
+ readonly fields?: readonly MutationField[];
29
+ readonly invalidations?: readonly unknown[];
30
+ };
31
+ export type MutationProgram = {
32
+ readonly ir_version: number;
33
+ readonly operations: readonly MutationOperation[];
34
+ readonly name?: string;
35
+ };
36
+ export type MutationCacheEffect = {
37
+ readonly kind: "upsert";
38
+ readonly target: MutationTarget;
39
+ readonly fields: readonly string[];
40
+ } | {
41
+ readonly kind: "patch";
42
+ readonly target: MutationTarget;
43
+ readonly fields: readonly string[];
44
+ } | {
45
+ readonly kind: "delete";
46
+ readonly target: MutationTarget;
47
+ } | {
48
+ readonly kind: "invalidate";
49
+ readonly invalidations: readonly unknown[];
50
+ };
51
+ export type MutationCacheProgram = {
52
+ readonly effects: readonly MutationCacheEffect[];
53
+ };
54
+ export declare const MUTATION_CACHE_VISIBILITY_FULL: MutationCacheVisibility;
55
+ export declare const MUTATION_CACHE_VISIBILITY_UNAUTHORIZED: MutationCacheVisibility;
56
+ /**
57
+ * Lower a canonical mutation program to role-safe cache effects.
58
+ *
59
+ * Partial patches never create records. Unauthorized or unprovable operations
60
+ * fail closed to the narrowest model invalidation.
61
+ */
62
+ export declare function lowerMutationCache(program: MutationProgram, visibility?: MutationCacheVisibility): MutationCacheProgram;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Role-safe cache lowering for event-independent mutation IR (v1).
3
+ *
4
+ * Mirrors `distributed::mutation::cache::lower_mutation_cache`. Mutation IR is
5
+ * the semantic source; this module produces atomic optimistic-layer effects
6
+ * without a second browser CRUD interpreter.
7
+ */
8
+ export const MUTATION_CACHE_VISIBILITY_FULL = Object.freeze({
9
+ authorized: true,
10
+ hasBaseRecord: true,
11
+ relationshipCovered: true,
12
+ });
13
+ export const MUTATION_CACHE_VISIBILITY_UNAUTHORIZED = Object.freeze({
14
+ authorized: false,
15
+ hasBaseRecord: true,
16
+ relationshipCovered: true,
17
+ });
18
+ /**
19
+ * Lower a canonical mutation program to role-safe cache effects.
20
+ *
21
+ * Partial patches never create records. Unauthorized or unprovable operations
22
+ * fail closed to the narrowest model invalidation.
23
+ */
24
+ export function lowerMutationCache(program, visibility = MUTATION_CACHE_VISIBILITY_FULL) {
25
+ const effects = (program.operations ?? []).map((operation) => lowerOperation(operation, visibility));
26
+ return Object.freeze({ effects: Object.freeze(effects) });
27
+ }
28
+ function lowerOperation(operation, visibility) {
29
+ if (!visibility.authorized) {
30
+ return invalidateModel(operation);
31
+ }
32
+ switch (operation.kind) {
33
+ case "insert":
34
+ case "upsert":
35
+ case "recreate":
36
+ case "insert_related":
37
+ case "upsert_related": {
38
+ const fields = concreteFieldNames(operation);
39
+ if (fields.length === 0) {
40
+ return invalidateModel(operation);
41
+ }
42
+ return Object.freeze({
43
+ kind: "upsert",
44
+ target: operation.target,
45
+ fields: Object.freeze(fields),
46
+ });
47
+ }
48
+ case "patch":
49
+ case "upsert_patch": {
50
+ if (!visibility.hasBaseRecord) {
51
+ return invalidateModel(operation);
52
+ }
53
+ const fields = concreteFieldNames(operation);
54
+ if (fields.length === 0) {
55
+ return invalidateModel(operation);
56
+ }
57
+ return Object.freeze({
58
+ kind: "patch",
59
+ target: operation.target,
60
+ fields: Object.freeze(fields),
61
+ });
62
+ }
63
+ case "delete":
64
+ return Object.freeze({
65
+ kind: "delete",
66
+ target: operation.target,
67
+ });
68
+ case "invalidate":
69
+ return Object.freeze({
70
+ kind: "invalidate",
71
+ invalidations: Object.freeze(operation.invalidations?.length
72
+ ? [...operation.invalidations]
73
+ : [{ kind: "model", model: operation.target.model }]),
74
+ });
75
+ default:
76
+ throw new Error(`unsupported mutation kind: ${operation.kind}`);
77
+ }
78
+ }
79
+ function concreteFieldNames(operation) {
80
+ return (operation.fields ?? [])
81
+ .filter((field) => field.assignment?.kind === "set" || field.assignment?.kind === "unset")
82
+ .map((field) => field.name);
83
+ }
84
+ function invalidateModel(operation) {
85
+ return Object.freeze({
86
+ kind: "invalidate",
87
+ invalidations: Object.freeze([
88
+ { kind: "model", model: operation.target.model },
89
+ ]),
90
+ });
91
+ }
@@ -39,14 +39,25 @@ function validateReplicaSurfaceIdentity(value) {
39
39
  return JSON.stringify(['role', value.name]);
40
40
  }
41
41
  if (value.kind !== 'application' ||
42
- !Array.isArray(value.roles) ||
43
- value.roles.length === 0 ||
44
- value.roles.some((role) => typeof role !== 'string' || role.length === 0) ||
45
- new Set(value.roles).size !== value.roles.length ||
46
- [...value.roles].sort().some((role, index) => role !== value.roles[index])) {
42
+ !Array.isArray(value.eligible_roles) ||
43
+ !Array.isArray(value.schema_roles) ||
44
+ value.eligible_roles.length === 0 ||
45
+ value.schema_roles.length === 0 ||
46
+ value.eligible_roles.some((role) => typeof role !== 'string' || role.length === 0) ||
47
+ value.schema_roles.some((role) => typeof role !== 'string' || role.length === 0) ||
48
+ new Set(value.eligible_roles).size !== value.eligible_roles.length ||
49
+ new Set(value.schema_roles).size !== value.schema_roles.length ||
50
+ [...value.eligible_roles].sort().some((role, index) => role !== value.eligible_roles[index]) ||
51
+ [...value.schema_roles].sort().some((role, index) => role !== value.schema_roles[index]) ||
52
+ value.schema_roles.some((role) => !value.eligible_roles.includes(role))) {
47
53
  throw new TypeError('replica artifact client surface is invalid');
48
54
  }
49
- return JSON.stringify(['application', value.name, value.roles]);
55
+ return JSON.stringify([
56
+ 'application',
57
+ value.name,
58
+ value.eligible_roles,
59
+ value.schema_roles
60
+ ]);
50
61
  }
51
62
  function canonicalTrustedPresetDescriptors(value) {
52
63
  if (!Array.isArray(value)) {
@@ -214,13 +214,19 @@ export function parseReplicaState(value) {
214
214
  return Object.freeze({ state: normalizedState, scope, payload });
215
215
  }
216
216
  export function parseAuthoritativeScope(value) {
217
- const scope = exactRecord(value, 'authoritative scope', ['protocolVersion', 'schemaHash', 'cacheScope']);
217
+ const scope = exactRecord(value, 'authoritative scope', [
218
+ 'protocolVersion',
219
+ 'schemaHash',
220
+ 'authorizationGeneration',
221
+ 'cacheScope'
222
+ ]);
218
223
  if (scope.protocolVersion !== 1) {
219
224
  throw new TypeError('unsupported authoritative protocol version');
220
225
  }
221
226
  return Object.freeze({
222
227
  protocolVersion: 1,
223
228
  schemaHash: nonEmptyString(scope.schemaHash, 'authoritative scope schemaHash'),
229
+ authorizationGeneration: nonEmptyString(scope.authorizationGeneration, 'authoritative scope authorizationGeneration'),
224
230
  cacheScope: nonEmptyString(scope.cacheScope, 'authoritative scope cacheScope')
225
231
  });
226
232
  }
@@ -569,12 +575,14 @@ export function persistenceIdentity(scope) {
569
575
  ENTRY_FORMAT_VERSION,
570
576
  scope.protocolVersion,
571
577
  scope.schemaHash,
578
+ scope.authorizationGeneration,
572
579
  scope.cacheScope
573
580
  ]);
574
581
  }
575
582
  export function sameScope(left, right) {
576
583
  return (left.protocolVersion === right.protocolVersion &&
577
584
  left.schemaHash === right.schemaHash &&
585
+ left.authorizationGeneration === right.authorizationGeneration &&
578
586
  left.cacheScope === right.cacheScope);
579
587
  }
580
588
  export function modelFromRecordKey(key) {
@@ -0,0 +1,6 @@
1
+ import type { CommandProjectionMetadata, ProjectionDelta } from './types.js';
2
+ /** Canonical bytes use the exact field order of the frozen Rust serde structs. */
3
+ export declare function canonicalProjectionDelta(delta: ProjectionDelta): string;
4
+ export declare function canonicalCommandProjectionMetadata(metadata: CommandProjectionMetadata): string;
5
+ export declare function projectionDeltaByteLength(delta: ProjectionDelta): number;
6
+ export declare function commandProjectionMetadataByteLength(metadata: CommandProjectionMetadata): number;
@@ -0,0 +1,29 @@
1
+ const encoder = new TextEncoder();
2
+ /** Canonical bytes use the exact field order of the frozen Rust serde structs. */
3
+ export function canonicalProjectionDelta(delta) {
4
+ return JSON.stringify({
5
+ wire_version: delta.wire_version,
6
+ identity: delta.identity,
7
+ projections: delta.projections,
8
+ occurrences: delta.occurrences,
9
+ operations: delta.operations,
10
+ ...(delta.recoveries.length === 0 ? {} : { recoveries: delta.recoveries })
11
+ });
12
+ }
13
+ export function canonicalCommandProjectionMetadata(metadata) {
14
+ return JSON.stringify({
15
+ wireVersion: metadata.wireVersion,
16
+ issuedAtUnixMs: metadata.issuedAtUnixMs,
17
+ expiresAtUnixMs: metadata.expiresAtUnixMs,
18
+ delta: JSON.parse(canonicalProjectionDelta(metadata.delta)),
19
+ lifecycleProofs: metadata.lifecycleProofs,
20
+ obligations: metadata.obligations,
21
+ revalidate: metadata.revalidate
22
+ });
23
+ }
24
+ export function projectionDeltaByteLength(delta) {
25
+ return encoder.encode(canonicalProjectionDelta(delta)).byteLength;
26
+ }
27
+ export function commandProjectionMetadataByteLength(metadata) {
28
+ return encoder.encode(canonicalCommandProjectionMetadata(metadata)).byteLength;
29
+ }
@@ -0,0 +1,5 @@
1
+ export { canonicalCommandProjectionMetadata, canonicalProjectionDelta, parseCommandProjectionMetadata, parseProjectionDelta, ProjectionDeltaValidationError, validateCommandProjectionArtifact, validateProjectionMetadataAuthority } from './validate.js';
2
+ export { deltaValue, operationsFromProjectionDelta, prepareCommandProjection } from './resolve.js';
3
+ export { createWasmJsonPure } from './wasm-pure.js';
4
+ export type { CreateWasmJsonPureOptions, WasmJsonModule, WasmJsonPureHost } from './wasm-pure.js';
5
+ export type { AppliedProjectionDelta, CommandProjectionMetadata, PreparedCommandProjection, PreparedProjectionOperation, PreparedProjectionScope, ProjectionCapabilityArm, ProjectionCapabilityMutation, ProjectionDelta, ProjectionDeltaMutation, ProjectionDeltaRecoveryTarget, ProjectionDeltaScope, ProjectionDeltaValue, ProjectionPreviewPureReduce, ProjectionPreviewScope, ProjectionPreviewValue, ReplicaCommandProjection, ReplicaPureFunction } from './types.js';
@@ -0,0 +1,3 @@
1
+ export { canonicalCommandProjectionMetadata, canonicalProjectionDelta, parseCommandProjectionMetadata, parseProjectionDelta, ProjectionDeltaValidationError, validateCommandProjectionArtifact, validateProjectionMetadataAuthority } from './validate.js';
2
+ export { deltaValue, operationsFromProjectionDelta, prepareCommandProjection } from './resolve.js';
3
+ export { createWasmJsonPure } from './wasm-pure.js';
@@ -0,0 +1,8 @@
1
+ import type { DistributedTrustedPreset } from '../../protocol.js';
2
+ import type { ReplicaValue } from '../types.js';
3
+ import type { PreparedCommandProjection, PreparedProjectionOperation, ProjectionDeltaValue, ReplicaCommandProjection } from './types.js';
4
+ export declare function prepareCommandProjection(contract: ReplicaCommandProjection, input: unknown, trustedPresets: readonly DistributedTrustedPreset[]): PreparedCommandProjection;
5
+ export declare function operationsFromProjectionDelta(scopes: readonly {
6
+ readonly mutation: import('./types.js').ProjectionDeltaMutation;
7
+ }[]): readonly PreparedProjectionOperation[];
8
+ export declare function deltaValue(value: ProjectionDeltaValue): ReplicaValue;
@@ -0,0 +1,266 @@
1
+ const missing = Symbol('projection-preview-missing');
2
+ const emptyUnsetFields = Object.freeze([]);
3
+ export function prepareCommandProjection(contract, input, trustedPresets) {
4
+ const presets = new Map(trustedPresets.map((preset) => [preset.name, preset]));
5
+ try {
6
+ const preview = contract.preview.operations.map(({ mutation }) => resolvePreviewMutation(mutation, input, presets));
7
+ const pure = (contract.pureReduces ?? []).map((reduce) => resolvePureReduce(reduce, input, presets));
8
+ return Object.freeze({
9
+ contract,
10
+ preview: Object.freeze([...preview, ...pure]),
11
+ revalidate: contract.preview.recoveries.length !== 0
12
+ });
13
+ }
14
+ catch {
15
+ // Preview is only a convenience. Missing client authority must never be
16
+ // promoted into invented cache truth.
17
+ return Object.freeze({
18
+ contract,
19
+ preview: Object.freeze([]),
20
+ revalidate: true
21
+ });
22
+ }
23
+ }
24
+ function resolvePureReduce(reduce, input, presets) {
25
+ const args = Object.create(null);
26
+ for (const { name, value } of reduce.args) {
27
+ args[name] = requireValue(resolvePreviewValue(value, input, presets));
28
+ }
29
+ return Object.freeze({
30
+ kind: 'reduce_known_record',
31
+ fn: reduce.fn,
32
+ scope: previewScope(reduce.scope, input, presets),
33
+ args: Object.freeze(args),
34
+ assign: Object.freeze([...reduce.assign])
35
+ });
36
+ }
37
+ export function operationsFromProjectionDelta(scopes) {
38
+ return Object.freeze(scopes.map(({ mutation }) => {
39
+ switch (mutation.op) {
40
+ case 'upsert':
41
+ return Object.freeze({
42
+ kind: 'upsert',
43
+ scope: actualScope(mutation.scope),
44
+ fields: fieldsFromDelta(mutation.fields),
45
+ replace: mutation.replace
46
+ });
47
+ case 'patch':
48
+ return Object.freeze({
49
+ kind: 'patch',
50
+ scope: actualScope(mutation.scope),
51
+ fields: fieldsFromDelta(mutation.set),
52
+ unset: mutation.unset,
53
+ ifPresent: true
54
+ });
55
+ case 'delete':
56
+ return Object.freeze({
57
+ kind: 'delete',
58
+ scope: actualScope(mutation.scope)
59
+ });
60
+ case 'link':
61
+ case 'unlink':
62
+ return Object.freeze({
63
+ kind: mutation.op,
64
+ relationship: mutation.relationship,
65
+ source: actualScope(mutation.source),
66
+ target: actualScope(mutation.target)
67
+ });
68
+ case 'invalidate_model':
69
+ return Object.freeze({
70
+ kind: 'invalidate_model',
71
+ model: mutation.model
72
+ });
73
+ case 'invalidate_relationship':
74
+ return Object.freeze({
75
+ kind: 'invalidate_relationship',
76
+ relationship: mutation.relationship,
77
+ source: actualScope(mutation.source)
78
+ });
79
+ }
80
+ }));
81
+ }
82
+ function resolvePreviewMutation(mutation, input, presets) {
83
+ switch (mutation.op) {
84
+ case 'upsert':
85
+ return Object.freeze({
86
+ kind: 'upsert',
87
+ scope: previewScope(mutation.scope, input, presets),
88
+ fields: previewFields(mutation.fields, input, presets),
89
+ replace: mutation.replace
90
+ });
91
+ case 'patch':
92
+ return Object.freeze({
93
+ kind: 'patch',
94
+ scope: previewScope(mutation.scope, input, presets),
95
+ fields: previewFields(mutation.set, input, presets),
96
+ unset: mutation.unset ?? emptyUnsetFields,
97
+ ifPresent: true
98
+ });
99
+ case 'delete':
100
+ return Object.freeze({
101
+ kind: 'delete',
102
+ scope: previewScope(mutation.scope, input, presets)
103
+ });
104
+ case 'link':
105
+ case 'unlink':
106
+ return Object.freeze({
107
+ kind: mutation.op,
108
+ relationship: mutation.relationship,
109
+ source: previewScope(mutation.source, input, presets),
110
+ target: previewScope(mutation.target, input, presets)
111
+ });
112
+ case 'invalidate_model':
113
+ if (mutation.partition?.kind === 'expression') {
114
+ requireValue(resolvePreviewValue(mutation.partition.expression, input, presets));
115
+ }
116
+ return Object.freeze({
117
+ kind: 'invalidate_model',
118
+ model: mutation.model
119
+ });
120
+ case 'invalidate_relationship':
121
+ return Object.freeze({
122
+ kind: 'invalidate_relationship',
123
+ relationship: mutation.relationship,
124
+ source: previewScope(mutation.source, input, presets)
125
+ });
126
+ }
127
+ }
128
+ function previewScope(scope, input, presets) {
129
+ if (scope.partition.kind === 'expression') {
130
+ requireValue(resolvePreviewValue(scope.partition.expression, input, presets));
131
+ }
132
+ return Object.freeze({
133
+ model: scope.model,
134
+ key: Object.freeze(scope.key.map(({ field, value }) => Object.freeze({
135
+ field,
136
+ value: requireScalar(requireValue(resolvePreviewValue(value, input, presets)))
137
+ })))
138
+ });
139
+ }
140
+ function actualScope(scope) {
141
+ return Object.freeze({
142
+ model: scope.model,
143
+ key: Object.freeze(scope.key.map(({ field, value }) => Object.freeze({ field, value: requireScalar(deltaValue(value)) })))
144
+ });
145
+ }
146
+ function previewFields(fields, input, presets) {
147
+ return Object.freeze(Object.fromEntries(fields.map(({ field, value }) => [
148
+ field,
149
+ requireValue(resolvePreviewValue(value, input, presets))
150
+ ])));
151
+ }
152
+ function fieldsFromDelta(fields) {
153
+ return Object.freeze(Object.fromEntries(fields.map(({ field, value }) => [field, deltaValue(value)])));
154
+ }
155
+ function resolvePreviewValue(expression, input, presets) {
156
+ switch (expression.kind) {
157
+ case 'input':
158
+ case 'generated_default':
159
+ return valueAtPath(input, expression.path);
160
+ case 'trusted_preset': {
161
+ const preset = presets.get(expression.name);
162
+ if (preset === undefined || preset.codec !== expression.codec)
163
+ return missing;
164
+ return preset.value;
165
+ }
166
+ case 'constant':
167
+ return deltaValue(expression.value);
168
+ case 'null':
169
+ return null;
170
+ case 'list': {
171
+ const values = expression.values.map((value) => resolvePreviewValue(value, input, presets));
172
+ return values.some((value) => value === missing)
173
+ ? missing
174
+ : Object.freeze(values);
175
+ }
176
+ case 'object': {
177
+ const fields = [];
178
+ for (const field of expression.fields) {
179
+ const value = resolvePreviewValue(field.value, input, presets);
180
+ if (value === missing)
181
+ return missing;
182
+ fields.push([field.name, value]);
183
+ }
184
+ return Object.freeze(Object.fromEntries(fields));
185
+ }
186
+ case 'transform':
187
+ if (expression.transform === 'first_present') {
188
+ for (const argument of expression.arguments) {
189
+ const value = resolvePreviewValue(argument, input, presets);
190
+ if (value !== missing)
191
+ return value;
192
+ }
193
+ return missing;
194
+ }
195
+ return expression.arguments
196
+ .map((argument) => requireValue(resolvePreviewValue(argument, input, presets)))
197
+ .map((value) => {
198
+ if (typeof value !== 'string')
199
+ throw new TypeError('projection');
200
+ return value;
201
+ })
202
+ .join('');
203
+ }
204
+ }
205
+ function valueAtPath(value, path) {
206
+ let current = value;
207
+ for (const segment of path) {
208
+ if (current === null ||
209
+ typeof current !== 'object' ||
210
+ !Object.hasOwn(current, segment))
211
+ return missing;
212
+ current = current[segment];
213
+ }
214
+ return isReplicaValue(current) ? current : missing;
215
+ }
216
+ export function deltaValue(value) {
217
+ switch (value.type) {
218
+ case 'null':
219
+ return null;
220
+ case 'boolean':
221
+ return value.value;
222
+ case 'string':
223
+ return value.value;
224
+ case 'enum':
225
+ return value.value.variant;
226
+ case 'i64':
227
+ case 'u64': {
228
+ const parsed = BigInt(value.value);
229
+ return parsed >= BigInt(Number.MIN_SAFE_INTEGER) &&
230
+ parsed <= BigInt(Number.MAX_SAFE_INTEGER)
231
+ ? Number(parsed)
232
+ : value.value;
233
+ }
234
+ case 'f64':
235
+ return Number(value.value);
236
+ case 'list':
237
+ return Object.freeze(value.value.map(deltaValue));
238
+ case 'object':
239
+ return Object.freeze(Object.fromEntries(value.value.map((field) => [field.field, deltaValue(field.value)])));
240
+ }
241
+ }
242
+ function requireValue(value) {
243
+ if (value === missing)
244
+ throw new TypeError('projection preview is unavailable');
245
+ return value;
246
+ }
247
+ function requireScalar(value) {
248
+ if (Array.isArray(value) || (value !== null && typeof value === 'object')) {
249
+ throw new TypeError('projection identity must be scalar');
250
+ }
251
+ return value;
252
+ }
253
+ function isReplicaValue(value, depth = 0) {
254
+ if (depth > 64)
255
+ return false;
256
+ if (value === null ||
257
+ typeof value === 'string' ||
258
+ typeof value === 'boolean' ||
259
+ (typeof value === 'number' && Number.isFinite(value)))
260
+ return true;
261
+ if (Array.isArray(value))
262
+ return value.every((item) => isReplicaValue(item, depth + 1));
263
+ if (typeof value !== 'object')
264
+ return false;
265
+ return Object.entries(value).every(([, item]) => isReplicaValue(item, depth + 1));
266
+ }