@githolon/dsl 0.107.4 → 0.108.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.
- package/dart/CHANGELOG.md +24 -0
- package/dart/lib/src/application_framework_calls.dart +299 -0
- package/dart/lib/src/generated_call.dart +96 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/node_runner_transport.dart +77 -1
- package/dart/lib/src/result_usda.dart +487 -0
- package/dart/lib/src/runner/node-entry.mjs +10634 -6122
- package/dart/lib/src/runner/offline-install.mjs +4187 -2716
- package/dart/lib/src/runtime_v2_gateway.dart +54 -0
- package/dart/lib/src/runtime_v2_transport.dart +117 -0
- package/dart/lib/src/session_bound.dart +7 -2
- package/dart/lib/src/test_harness.dart +373 -282
- package/dart/lib/src/webview_bridge.dart +2146 -725
- package/dart/pubspec.lock +17 -1
- package/dart/pubspec.yaml +3 -2
- package/dart/test/acquire_session_credentials_test.dart +1 -1
- package/dart/test/acquire_session_test.dart +68 -14
- package/dart/test/adv_realm_interference_test.dart +6 -4
- package/dart/test/cluster_births_test.dart +11 -8
- package/dart/test/cluster_local_autosync_test.dart +1 -1
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +52 -35
- package/dart/test/first_app_gauntlet_test.dart +1 -1
- package/dart/test/local_sync_inert_test.dart +23 -26
- package/dart/test/offer_authentication_proof_test.dart +193 -0
- package/dart/test/result_usda_test.dart +49 -0
- package/dart/test/session_bound_test.dart +8 -11
- package/dart/test/stale_bundle_test.dart +18 -3
- package/dart/test/test_harness_test.dart +14 -27
- package/dart/test/webview_bridge_test.dart +504 -83
- package/dart_types/CHANGELOG.md +17 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +11 -0
- package/dart_types/lib/src/custody.dart +64 -19
- package/dart_types/lib/src/local_work_rejected.dart +30 -45
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pb.dart +169 -0
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +17945 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +933 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pb.dart +245 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbenum.dart +160 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +8664 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +369 -0
- package/dart_types/lib/src/surfaced.dart +30 -25
- package/dart_types/pubspec.lock +16 -0
- package/dart_types/pubspec.yaml +3 -1
- package/dart_types/test/surfaced_test.dart +2 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +5760 -1879
- package/src/authoring.ts +3 -2
- package/src/birth_meter_ensure.ts +2 -0
- package/src/births.ts +21 -0
- package/src/build_package.ts +30 -0
- package/src/business_birth.ts +41 -35
- package/src/codegen_call_usda.ts +1111 -0
- package/src/codegen_dart.ts +207 -282
- package/src/codegen_dart_application.ts +195 -252
- package/src/codegen_dart_client.ts +1978 -593
- package/src/codegen_dart_framework_calls.ts +291 -0
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +3 -4
- package/src/codegen_provider_dart.ts +21 -7
- package/src/codegen_query_usda.ts +214 -0
- package/src/codegen_result_usda.ts +685 -0
- package/src/codegen_ts.ts +3945 -1111
- package/src/compile_package_main.ts +392 -46
- package/src/directive.ts +19 -4
- package/src/engine_entry.ts +45 -4
- package/src/framework/application_home.ts +4 -0
- package/src/framework/auth_provider_law.ts +296 -20
- package/src/framework/birth_certificate_law.ts +24 -0
- package/src/framework/home.ts +83 -40
- package/src/framework/owned_custody.ts +25 -12
- package/src/framework/owned_custody_value.ts +51 -0
- package/src/framework/relations.ts +47 -21
- package/src/framework/signing_identity.ts +100 -17
- package/src/framework/workspaces.ts +39 -10
- package/src/framework_call_definitions.ts +53 -0
- package/src/framework_read_definitions.ts +45 -0
- package/src/identity_frontier.ts +58 -0
- package/src/index.ts +22 -1
- package/src/intent.ts +216 -16
- package/src/law_check.ts +19 -1
- package/src/law_graph.ts +57 -8
- package/src/law_graph_usda.ts +73 -5
- package/src/law_value_type.ts +106 -19
- package/src/read.ts +47 -0
- package/src/read_projection_namespace.ts +7 -0
- package/src/shape_compat.ts +44 -42
- package/src/stable_ids.ts +37 -2
- package/src/test_call_usda.ts +83 -0
- package/src/wire.ts +36 -2
- package/src/wire_encode.ts +45 -1
- package/src/workspace_sharing.ts +27 -10
package/src/intent.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type { Ports } from "./ctx.js";
|
|
|
19
19
|
import type { PlannedOp } from "./ops.js";
|
|
20
20
|
import type { Field } from "./fields.js";
|
|
21
21
|
import type { AggregateHandle } from "./aggregate.js";
|
|
22
|
+
import type { QueryDecl } from "./query.js";
|
|
22
23
|
import type {
|
|
23
24
|
InvariantReadBinding,
|
|
24
25
|
InvariantReadId,
|
|
@@ -45,6 +46,18 @@ export interface IntentStep<I = unknown, P = unknown> {
|
|
|
45
46
|
readonly payload: (facts: I) => P;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/** One cross-workspace consequence authored by source law. The destination mapper supplies only
|
|
50
|
+
* the target workspace address; the nominal target directive and payload type remain sealed by the
|
|
51
|
+
* compiled source graph and its generated USDA encoder. */
|
|
52
|
+
export interface WorkspaceEffectStep<I = unknown, P = unknown> {
|
|
53
|
+
readonly target: Directive<P>;
|
|
54
|
+
readonly destination:
|
|
55
|
+
| { readonly kind: "workspace"; readonly address: (facts: I) => string }
|
|
56
|
+
| { readonly kind: "verifiedParent" }
|
|
57
|
+
| { readonly kind: "verifiedSource" };
|
|
58
|
+
readonly payload: (facts: I) => P;
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
const PAYLOAD_TRACE_PATH = Symbol("nomos.intent.payload-trace-path");
|
|
49
62
|
|
|
50
63
|
type PayloadTrace = {
|
|
@@ -175,6 +188,10 @@ export interface BusinessIntent<I = unknown> extends Directive<I> {
|
|
|
175
188
|
readonly [INTENT_BRAND]: true;
|
|
176
189
|
readonly [BUSINESS_BIRTH_POLICY_BINDING]?: (domains: readonly string[]) => void;
|
|
177
190
|
readonly steps: readonly IntentStep<I, any>[];
|
|
191
|
+
readonly workspaceEffects: readonly WorkspaceEffectStep<I, any>[];
|
|
192
|
+
/** Law-selected proof for this exact public offer. Omitted means the ordinary session proof
|
|
193
|
+
* posture. Tenant callers never choose this at invocation time. */
|
|
194
|
+
readonly offerAuthenticationProof?: "enrolledDevice";
|
|
178
195
|
/** The complete public business boundary. Runtime lowering still uses Directive.payloadSchema privately. */
|
|
179
196
|
readonly contract: IntentContract<I>;
|
|
180
197
|
}
|
|
@@ -311,6 +328,11 @@ class IntentBuilder<I> {
|
|
|
311
328
|
private readonly id: string,
|
|
312
329
|
private readonly contractDeclaration: IntentContractDeclaration<I>,
|
|
313
330
|
private readonly steps: readonly IntentStep<I, any>[] = [],
|
|
331
|
+
private readonly workspaceEffects: readonly WorkspaceEffectStep<I, any>[] = [],
|
|
332
|
+
private readonly intentReads: readonly (string | QueryDecl)[] = [],
|
|
333
|
+
private readonly acceptsWorkspaceEffects = false,
|
|
334
|
+
private readonly intentRelationGate?: NonNullable<Directive["requiresRelationGate"]>,
|
|
335
|
+
private readonly offerAuthenticationProof?: "enrolledDevice",
|
|
314
336
|
) {}
|
|
315
337
|
|
|
316
338
|
/** Add one private directive consequence to this business intent. */
|
|
@@ -324,7 +346,148 @@ class IntentBuilder<I> {
|
|
|
324
346
|
return new IntentBuilder(this.id, this.contractDeclaration, [
|
|
325
347
|
...this.steps,
|
|
326
348
|
{ directive, payload },
|
|
327
|
-
]);
|
|
349
|
+
], this.workspaceEffects, this.intentReads, this.acceptsWorkspaceEffects, this.intentRelationGate, this.offerAuthenticationProof);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Author one exact nominal call into another workspace. The caller chooses only the target
|
|
353
|
+
* workspace address. Passing the target Directive object preserves the application type and lets
|
|
354
|
+
* the compiler bind its stable USDA identity; no operation string or host-selected mapping exists. */
|
|
355
|
+
directsWorkspace<P>(
|
|
356
|
+
target: Directive<P>,
|
|
357
|
+
effect: { readonly workspace: (facts: I) => string; readonly payload: (facts: I) => P },
|
|
358
|
+
): IntentBuilder<I> {
|
|
359
|
+
if (isBusinessIntent(target) && target.acceptsWorkspaceEffectGate !== true) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`intent('${this.id}') workspace effect may target a public intent only when target law explicitly accepts workspace effects`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return new IntentBuilder(this.id, this.contractDeclaration, this.steps, [
|
|
365
|
+
...this.workspaceEffects,
|
|
366
|
+
{
|
|
367
|
+
target,
|
|
368
|
+
destination: { kind: "workspace", address: effect.workspace },
|
|
369
|
+
payload: effect.payload,
|
|
370
|
+
},
|
|
371
|
+
], this.intentReads, this.acceptsWorkspaceEffects, this.intentRelationGate, this.offerAuthenticationProof);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Send an exact nominal request to this workspace's verified immediate parent. The route is
|
|
375
|
+
* resolved by the kernel from birth custody after the source intent admits; no workspace string
|
|
376
|
+
* enters the public payload or the JavaScript execution boundary. */
|
|
377
|
+
directsParentWorkspace<P>(
|
|
378
|
+
target: Directive<P>,
|
|
379
|
+
effect: { readonly payload: (facts: I) => P },
|
|
380
|
+
): IntentBuilder<I> {
|
|
381
|
+
if (isBusinessIntent(target) && target.acceptsWorkspaceEffectGate !== true) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`intent('${this.id}') parent request may target a public intent only when target law explicitly accepts workspace effects`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return new IntentBuilder(this.id, this.contractDeclaration, this.steps, [
|
|
387
|
+
...this.workspaceEffects,
|
|
388
|
+
{
|
|
389
|
+
target,
|
|
390
|
+
destination: { kind: "verifiedParent" },
|
|
391
|
+
payload: effect.payload,
|
|
392
|
+
},
|
|
393
|
+
], this.intentReads, this.acceptsWorkspaceEffects, this.intentRelationGate, this.offerAuthenticationProof);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Reply to the exact workspace whose carried effect invoked this intent. The destination is
|
|
397
|
+
* kernel-captured only after the source custody proof succeeds; it is neither public payload nor
|
|
398
|
+
* an authority subject. This keeps request/reply routing out of application data while target law
|
|
399
|
+
* still authorizes the verified workspace actor through its ordinary relation gate. */
|
|
400
|
+
repliesWorkspace<P>(
|
|
401
|
+
target: Directive<P>,
|
|
402
|
+
effect: { readonly payload: (facts: I) => P },
|
|
403
|
+
): IntentBuilder<I> {
|
|
404
|
+
if (isBusinessIntent(target)) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
`intent('${this.id}') workspace reply must target a private accepted directive, not another public intent`,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return new IntentBuilder(this.id, this.contractDeclaration, this.steps, [
|
|
410
|
+
...this.workspaceEffects,
|
|
411
|
+
{
|
|
412
|
+
target,
|
|
413
|
+
destination: { kind: "verifiedSource" },
|
|
414
|
+
payload: effect.payload,
|
|
415
|
+
},
|
|
416
|
+
], this.intentReads, this.acceptsWorkspaceEffects, this.intentRelationGate, this.offerAuthenticationProof);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Admit this public request only through the cross-workspace effect door. Authority remains the
|
|
420
|
+
* intent's ordinary static `.requires(..., {object:"workspace:self"})` relation gate. */
|
|
421
|
+
acceptsWorkspaceEffect(): IntentBuilder<I> {
|
|
422
|
+
return new IntentBuilder(
|
|
423
|
+
this.id,
|
|
424
|
+
this.contractDeclaration,
|
|
425
|
+
this.steps,
|
|
426
|
+
this.workspaceEffects,
|
|
427
|
+
this.intentReads,
|
|
428
|
+
true,
|
|
429
|
+
this.intentRelationGate,
|
|
430
|
+
this.offerAuthenticationProof,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Require this law-declared offer to prove the author through the workspace's enrolled device
|
|
435
|
+
* relation. Generated clients therefore withhold any session provider token for this intent;
|
|
436
|
+
* callers cannot downgrade or override the proof at invocation time. */
|
|
437
|
+
authenticatesWithEnrolledDevice(): IntentBuilder<I> {
|
|
438
|
+
return new IntentBuilder(
|
|
439
|
+
this.id,
|
|
440
|
+
this.contractDeclaration,
|
|
441
|
+
this.steps,
|
|
442
|
+
this.workspaceEffects,
|
|
443
|
+
this.intentReads,
|
|
444
|
+
this.acceptsWorkspaceEffects,
|
|
445
|
+
this.intentRelationGate,
|
|
446
|
+
"enrolledDevice",
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Put the authorization requirement on the business action itself. This is the honest shape for
|
|
451
|
+
* a pure request/reply action that has no local storage consequence on which to hang a gate. */
|
|
452
|
+
requires(
|
|
453
|
+
relation: string,
|
|
454
|
+
options: { readonly object: "workspace:self" } = { object: "workspace:self" },
|
|
455
|
+
): IntentBuilder<I> {
|
|
456
|
+
if (!relation.trim()) throw new Error(`intent('${this.id}') requires a non-empty relation`);
|
|
457
|
+
const gate = { relation, object: options.object };
|
|
458
|
+
if (this.intentRelationGate !== undefined && stable(this.intentRelationGate) !== stable(gate)) {
|
|
459
|
+
throw new Error(`intent('${this.id}') already has a different authorization gate`);
|
|
460
|
+
}
|
|
461
|
+
return new IntentBuilder(
|
|
462
|
+
this.id,
|
|
463
|
+
this.contractDeclaration,
|
|
464
|
+
this.steps,
|
|
465
|
+
this.workspaceEffects,
|
|
466
|
+
this.intentReads,
|
|
467
|
+
this.acceptsWorkspaceEffects,
|
|
468
|
+
gate,
|
|
469
|
+
this.offerAuthenticationProof,
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Declare the bounded source reads used to author this business action's consequences.
|
|
474
|
+
* This belongs on the intent because a pure cross-workspace effect has no honest local
|
|
475
|
+
* directive on which to hang its captured-read contract. */
|
|
476
|
+
reads(...reads: readonly (string | QueryDecl)[]): IntentBuilder<I> {
|
|
477
|
+
const byKey = new Map<string, string | QueryDecl>();
|
|
478
|
+
for (const read of [...this.intentReads, ...reads]) {
|
|
479
|
+
byKey.set(typeof read === "string" ? `ref:${read}` : `query:${read.id}`, read);
|
|
480
|
+
}
|
|
481
|
+
return new IntentBuilder(
|
|
482
|
+
this.id,
|
|
483
|
+
this.contractDeclaration,
|
|
484
|
+
this.steps,
|
|
485
|
+
this.workspaceEffects,
|
|
486
|
+
[...byKey.values()],
|
|
487
|
+
this.acceptsWorkspaceEffects,
|
|
488
|
+
this.intentRelationGate,
|
|
489
|
+
this.offerAuthenticationProof,
|
|
490
|
+
);
|
|
328
491
|
}
|
|
329
492
|
|
|
330
493
|
/** Add one declared child birth as a consequence; Nomos owns the private genesis recipe. */
|
|
@@ -336,12 +499,12 @@ class IntentBuilder<I> {
|
|
|
336
499
|
return new IntentBuilder(this.id, this.contractDeclaration, [
|
|
337
500
|
...this.steps,
|
|
338
501
|
{ directive, payload },
|
|
339
|
-
]);
|
|
502
|
+
], this.workspaceEffects, this.intentReads, this.acceptsWorkspaceEffects, this.intentRelationGate, this.offerAuthenticationProof);
|
|
340
503
|
}
|
|
341
504
|
|
|
342
505
|
/** Finish the intent and lower its directive graph to one atomic offer. */
|
|
343
506
|
atomically(): BusinessIntent<I> {
|
|
344
|
-
if (this.steps.length === 0) {
|
|
507
|
+
if (this.steps.length === 0 && this.workspaceEffects.length === 0) {
|
|
345
508
|
throw new Error(`intent('${this.id}') needs at least one directed consequence`);
|
|
346
509
|
}
|
|
347
510
|
const duplicate = this.steps.find(
|
|
@@ -364,7 +527,8 @@ class IntentBuilder<I> {
|
|
|
364
527
|
}
|
|
365
528
|
|
|
366
529
|
const primary = this.steps.find((step) => step.directive.birthsChild !== true)?.directive
|
|
367
|
-
?? this.steps[0]
|
|
530
|
+
?? this.steps[0]?.directive
|
|
531
|
+
?? this.workspaceEffects[0]!.target;
|
|
368
532
|
const allOutputs: SecondaryOutput[] = [];
|
|
369
533
|
for (const step of this.steps) {
|
|
370
534
|
const candidate = step.directive;
|
|
@@ -392,19 +556,21 @@ class IntentBuilder<I> {
|
|
|
392
556
|
? { optional: true }
|
|
393
557
|
: {}),
|
|
394
558
|
});
|
|
395
|
-
const primaryOutput =
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
559
|
+
const primaryOutput = this.steps.length === 0
|
|
560
|
+
? undefined
|
|
561
|
+
: composedOutput(
|
|
562
|
+
outputGroups.get(`${primary.marker}\0${primary.aggregateId}`) ?? [{
|
|
563
|
+
aggregateId: primary.aggregateId,
|
|
564
|
+
marker: primary.marker,
|
|
565
|
+
}],
|
|
566
|
+
);
|
|
401
567
|
const secondaryOutputs = [...outputGroups.values()]
|
|
402
568
|
.map(composedOutput)
|
|
403
569
|
.filter(
|
|
404
570
|
(output) => !(output.aggregateId === primary.aggregateId && output.marker === primary.marker),
|
|
405
571
|
);
|
|
406
572
|
const inferredReturns = [
|
|
407
|
-
primary.aggregateId,
|
|
573
|
+
...(primaryOutput === undefined ? [] : [primary.aggregateId]),
|
|
408
574
|
...secondaryOutputs.map((output) => output.aggregateId),
|
|
409
575
|
...birthSteps.flatMap((step) => {
|
|
410
576
|
const aggregateId = (step.directive as BusinessBirthDirective).businessBirth?.aggregateId;
|
|
@@ -451,8 +617,14 @@ class IntentBuilder<I> {
|
|
|
451
617
|
};
|
|
452
618
|
}),
|
|
453
619
|
);
|
|
620
|
+
const intentQueryReads = this.intentReads.filter(
|
|
621
|
+
(read): read is QueryDecl => typeof read !== "string",
|
|
622
|
+
);
|
|
623
|
+
const intentRefReads = this.intentReads.filter(
|
|
624
|
+
(read): read is string => typeof read === "string",
|
|
625
|
+
);
|
|
454
626
|
const queryReads = dedupeBy(
|
|
455
|
-
this.steps.flatMap((step) => step.directive.declaredQueryReads ?? []),
|
|
627
|
+
[...intentQueryReads, ...this.steps.flatMap((step) => step.directive.declaredQueryReads ?? [])],
|
|
456
628
|
(query) => query.id,
|
|
457
629
|
);
|
|
458
630
|
const foreignReads = dedupeBy(
|
|
@@ -472,7 +644,24 @@ class IntentBuilder<I> {
|
|
|
472
644
|
}
|
|
473
645
|
}
|
|
474
646
|
}
|
|
475
|
-
const
|
|
647
|
+
const inheritedRelationGate = oneStaticRelationGate(this.steps);
|
|
648
|
+
if (
|
|
649
|
+
this.intentRelationGate !== undefined &&
|
|
650
|
+
inheritedRelationGate !== undefined &&
|
|
651
|
+
stable(this.intentRelationGate) !== stable(inheritedRelationGate)
|
|
652
|
+
) {
|
|
653
|
+
throw new Error(`intent('${this.id}') has conflicting intent and consequence authorization gates`);
|
|
654
|
+
}
|
|
655
|
+
const relationGate = this.intentRelationGate ?? inheritedRelationGate;
|
|
656
|
+
if (
|
|
657
|
+
this.acceptsWorkspaceEffects &&
|
|
658
|
+
(relationGate === undefined || relationGate.object !== "workspace:self" || relationGate.objectFrom !== undefined)
|
|
659
|
+
) {
|
|
660
|
+
throw new Error(
|
|
661
|
+
`intent('${this.id}') .acceptsWorkspaceEffect() requires one static ` +
|
|
662
|
+
`.requires(relation, { object: "workspace:self" }) consequence gate`,
|
|
663
|
+
);
|
|
664
|
+
}
|
|
476
665
|
const sourceTrusts = this.steps.flatMap((step) =>
|
|
477
666
|
step.directive.businessSourceTrust === undefined
|
|
478
667
|
? []
|
|
@@ -520,16 +709,26 @@ class IntentBuilder<I> {
|
|
|
520
709
|
// engine cannot rebuild its aggregate registry entry and silently drops
|
|
521
710
|
// compound intents that also birth a child.
|
|
522
711
|
...(primary.aggregate === undefined ? {} : { aggregate: primary.aggregate }),
|
|
523
|
-
...(primaryOutput
|
|
712
|
+
...(primaryOutput?.many === true
|
|
524
713
|
? { outputCardinality: "many" as const }
|
|
525
|
-
: primaryOutput
|
|
714
|
+
: primaryOutput?.optional === true
|
|
526
715
|
? { outputCardinality: "optional" as const }
|
|
527
716
|
: {}),
|
|
528
717
|
...(secondaryOutputs.length > 0 ? { secondaryOutputs } : {}),
|
|
529
718
|
payloadSchema: (birthSteps[0]?.directive.payloadSchema ?? this.contractDeclaration.accepts) as unknown as z.ZodType<I>,
|
|
530
719
|
plan,
|
|
531
720
|
steps,
|
|
532
|
-
|
|
721
|
+
workspaceEffects: this.workspaceEffects,
|
|
722
|
+
...(this.offerAuthenticationProof === undefined
|
|
723
|
+
? {}
|
|
724
|
+
: { offerAuthenticationProof: this.offerAuthenticationProof }),
|
|
725
|
+
...(this.acceptsWorkspaceEffects ? { acceptsWorkspaceEffectGate: true as const } : {}),
|
|
726
|
+
declaredReads: [
|
|
727
|
+
...new Set([
|
|
728
|
+
...intentRefReads,
|
|
729
|
+
...this.steps.flatMap((step) => step.directive.declaredReads),
|
|
730
|
+
]),
|
|
731
|
+
].sort(),
|
|
533
732
|
...(queryReads.length > 0 ? { declaredQueryReads: queryReads } : {}),
|
|
534
733
|
...(foreignReads.length > 0 ? { declaredForeignReads: foreignReads } : {}),
|
|
535
734
|
declaredEmits: emits,
|
|
@@ -597,6 +796,7 @@ class IntentContractStep {
|
|
|
597
796
|
refuses: Object.freeze([]),
|
|
598
797
|
},
|
|
599
798
|
steps: [step],
|
|
799
|
+
workspaceEffects: [],
|
|
600
800
|
plan: (facts, ctx) => effect
|
|
601
801
|
.plan(effect.payloadSchema.parse(facts), ctx)
|
|
602
802
|
.map((op) => composePrimaryConsequenceOp(effect, op)),
|
package/src/law_check.ts
CHANGED
|
@@ -50,6 +50,7 @@ import { finishExtremum } from "./extremum.js";
|
|
|
50
50
|
import { finishExists } from "./exists.js";
|
|
51
51
|
import { clearDirectiveId } from "./clear_directive.js";
|
|
52
52
|
import type { ForeignReadEdge } from "./attested_core.js";
|
|
53
|
+
import { isBusinessIntent } from "./intent.js";
|
|
53
54
|
|
|
54
55
|
/** One author-time finding — the gate refusal pre-fired at compile, or a courtesy note. */
|
|
55
56
|
export interface Finding {
|
|
@@ -116,7 +117,7 @@ function probeDirective(dir: Directive<unknown>, where: string): ProbeResult | u
|
|
|
116
117
|
const probePayload: Record<string, unknown> = { ...synth.value };
|
|
117
118
|
for (const f of synth.autoStamped) probePayload[f] = "2026-01-01T00:00:00Z";
|
|
118
119
|
if ((dir as { businessBirth?: unknown }).businessBirth !== undefined) {
|
|
119
|
-
probePayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD] =
|
|
120
|
+
probePayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD] = { ...probePayload };
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
const shapeKeys = new Set(Object.keys(safeShape(dir.payloadSchema as z.ZodTypeAny)));
|
|
@@ -141,6 +142,23 @@ function probeDirective(dir: Directive<unknown>, where: string): ProbeResult | u
|
|
|
141
142
|
returned = dir.plan(tracked as never, deterministicPorts({ physical: 1, replica: 1, principal: "principal:probe" }));
|
|
142
143
|
const drained: PlannedOp[] = [...__drainAuthoring(), ...(Array.isArray(returned) ? returned : [])];
|
|
143
144
|
ops = drained.filter((o): o is FieldOp => (o as { kind?: string }).kind !== "strike");
|
|
145
|
+
if (isBusinessIntent(dir)) {
|
|
146
|
+
for (const effect of dir.workspaceEffects) {
|
|
147
|
+
// These two mappers are source law just as surely as the local plan. Run them against
|
|
148
|
+
// the same tracking payload so route/payload fields are not falsely diagnosed as dead.
|
|
149
|
+
// A captured read may deliberately refuse this isolated empty-state probe; that does not
|
|
150
|
+
// turn a successful local plan into a plan-return warning, but reads reached before the
|
|
151
|
+
// refusal are still drained and checked below.
|
|
152
|
+
try {
|
|
153
|
+
if (effect.destination.kind === "workspace") {
|
|
154
|
+
effect.destination.address(tracked as never);
|
|
155
|
+
}
|
|
156
|
+
effect.payload(tracked as never);
|
|
157
|
+
} catch {
|
|
158
|
+
// Inconclusive mapper branch on the synthesized fixture; runtime/generator proof remains.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
144
162
|
reads = __drainReads();
|
|
145
163
|
} catch (e) {
|
|
146
164
|
threw = true;
|
package/src/law_graph.ts
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type LawLiteral,
|
|
35
35
|
type LawValueMember,
|
|
36
36
|
type LawValueConstraint,
|
|
37
|
+
type LawValueSemantic,
|
|
37
38
|
type LawValueType,
|
|
38
39
|
} from "./law_value_type.js";
|
|
39
40
|
import { harvestPrincipalFlows } from "./wire_encode.js";
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
import { isNamespacedAggregate, qualifyReadProjectionType } from "./read_projection_namespace.js";
|
|
51
52
|
import { assertFlowGateModule } from "./flow_gate.js";
|
|
52
53
|
import type { InvariantReadBinding } from "./framework/workspace_invariant.js";
|
|
54
|
+
import { OwnedCustodyValue } from "./framework/owned_custody_value.js";
|
|
53
55
|
|
|
54
56
|
declare const LAW_SID: unique symbol;
|
|
55
57
|
export type LawSid<K extends LawEntityKind> = string & { readonly [LAW_SID]: K };
|
|
@@ -195,7 +197,7 @@ const OWNED_AUTHORING_PAYLOAD_MEMBERS = [
|
|
|
195
197
|
{
|
|
196
198
|
name: OWNED_CUSTODY_PAYLOAD_FIELD,
|
|
197
199
|
required: false,
|
|
198
|
-
value:
|
|
200
|
+
value: lowerLawValueType(OwnedCustodyValue.zod),
|
|
199
201
|
},
|
|
200
202
|
{
|
|
201
203
|
name: OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
|
|
@@ -378,7 +380,7 @@ export interface LawRelationPredicateNode {
|
|
|
378
380
|
readonly expression: LawRelationPredicateExpression;
|
|
379
381
|
}
|
|
380
382
|
|
|
381
|
-
|
|
383
|
+
type LawAnonymousValueTypeShape =
|
|
382
384
|
| {
|
|
383
385
|
readonly kind: "string" | "number" | "integer";
|
|
384
386
|
readonly constraints: readonly LawValueConstraint[];
|
|
@@ -398,6 +400,10 @@ export type LawAnonymousValueTypeExpression =
|
|
|
398
400
|
}
|
|
399
401
|
| { readonly kind: "recursive"; readonly value: LawStructuralRef<"value-type"> };
|
|
400
402
|
|
|
403
|
+
export type LawAnonymousValueTypeExpression = LawAnonymousValueTypeShape & {
|
|
404
|
+
readonly semantic?: LawValueSemantic;
|
|
405
|
+
};
|
|
406
|
+
|
|
401
407
|
export interface LawAnonymousValueTypeNode {
|
|
402
408
|
readonly kind: "structural";
|
|
403
409
|
readonly structuralKind: "value-type";
|
|
@@ -587,6 +593,13 @@ export interface LawIntentNode {
|
|
|
587
593
|
readonly foreignReads: readonly LawStructuralRef<"foreign-read">[];
|
|
588
594
|
/** Attested cross-workspace contribution contracts which may propose this intent. */
|
|
589
595
|
readonly workspaceRelations: readonly LawStructuralRef<"workspace-relation">[];
|
|
596
|
+
/** Exact signed parent-workspace effects may propose this call; authority is still `.requires`. */
|
|
597
|
+
readonly acceptsWorkspaceEffect: boolean;
|
|
598
|
+
/** Client proof selected by law for this exact public offer. Absence retains the ordinary
|
|
599
|
+
* session-credential posture. */
|
|
600
|
+
readonly offerAuthenticationProof?: "enrolledDevice";
|
|
601
|
+
/** Exact nominal target directives this source intent may author into another workspace. */
|
|
602
|
+
readonly directsWorkspaceEffects: readonly LawTargetRef<"intent">[];
|
|
590
603
|
/** Zanzibar relation required to author this intent. */
|
|
591
604
|
readonly authorization?: LawIntentAuthorization;
|
|
592
605
|
readonly requiresHostCapabilities: readonly string[];
|
|
@@ -1172,6 +1185,16 @@ export function discoverLawGraph(
|
|
|
1172
1185
|
): LawTargetRef<"aggregate"> => {
|
|
1173
1186
|
const local = aggregateByLabel.get(aggregateId);
|
|
1174
1187
|
if (local !== undefined) return ref(local.node.entity);
|
|
1188
|
+
if (
|
|
1189
|
+
isBusinessIntent(intent) &&
|
|
1190
|
+
intent.steps.length === 0 &&
|
|
1191
|
+
intent.workspaceEffects.some((effect) => effect.target.aggregateId === aggregateId)
|
|
1192
|
+
) {
|
|
1193
|
+
// A workspace-effect-only intent has no local aggregate consequence. Its nominal target
|
|
1194
|
+
// aggregate belongs to the destination package; retain that as a dependency export instead
|
|
1195
|
+
// of fabricating a local audit row merely to satisfy the older directive-shaped IR.
|
|
1196
|
+
return { kind: "dependency-export", entityKind: "aggregate", label: aggregateId };
|
|
1197
|
+
}
|
|
1175
1198
|
const birth = (intent as { businessBirth?: { aggregateId?: unknown; domain?: unknown } }).businessBirth;
|
|
1176
1199
|
if (birth?.aggregateId === aggregateId && typeof birth.domain === "string") {
|
|
1177
1200
|
return { kind: "dependency-export", entityKind: "aggregate", label: aggregateId, domain: birth.domain };
|
|
@@ -1318,11 +1341,11 @@ export function discoverLawGraph(
|
|
|
1318
1341
|
}
|
|
1319
1342
|
payloadFieldsByIntent.set(intent.id, payloadFields);
|
|
1320
1343
|
const outputs: Array<Pick<LawIntentEffectNode, "marker" | "aggregate" | "cardinality">> = [
|
|
1321
|
-
{
|
|
1344
|
+
...(isBusinessIntent(intent) && intent.steps.length === 0 ? [] : [{
|
|
1322
1345
|
marker: intent.marker,
|
|
1323
1346
|
aggregate: intentAggregateTarget(intent, intent.aggregateId),
|
|
1324
|
-
cardinality: intent.outputCardinality ?? "one",
|
|
1325
|
-
},
|
|
1347
|
+
cardinality: intent.outputCardinality ?? "one" as const,
|
|
1348
|
+
}]),
|
|
1326
1349
|
...(intent.secondaryOutputs ?? []).map((output) => ({
|
|
1327
1350
|
marker: output.marker,
|
|
1328
1351
|
aggregate: aggregateRef(aggregateByLabel, output.aggregateId, `intent '${intent.id}'`),
|
|
@@ -1460,6 +1483,20 @@ export function discoverLawGraph(
|
|
|
1460
1483
|
reads: [],
|
|
1461
1484
|
foreignReads: [],
|
|
1462
1485
|
workspaceRelations,
|
|
1486
|
+
acceptsWorkspaceEffect: intent.acceptsWorkspaceEffectGate === true,
|
|
1487
|
+
...(isBusinessIntent(intent) && intent.offerAuthenticationProof !== undefined
|
|
1488
|
+
? { offerAuthenticationProof: intent.offerAuthenticationProof }
|
|
1489
|
+
: {}),
|
|
1490
|
+
directsWorkspaceEffects: (isBusinessIntent(intent) ? intent.workspaceEffects : []).map((effect) => {
|
|
1491
|
+
const local = intentEntities.get(effect.target.id);
|
|
1492
|
+
return local === undefined
|
|
1493
|
+
? {
|
|
1494
|
+
kind: "dependency-export" as const,
|
|
1495
|
+
entityKind: "intent" as const,
|
|
1496
|
+
label: effect.target.id,
|
|
1497
|
+
}
|
|
1498
|
+
: ref(local);
|
|
1499
|
+
}),
|
|
1463
1500
|
requiresHostCapabilities: [...new Set(intent.declaredHostCapabilities ?? [])].sort(compareText),
|
|
1464
1501
|
...(resolvedField === undefined ? {} : { resolves: ref(resolvedField.entity) }),
|
|
1465
1502
|
births: intent.birthsChild === true,
|
|
@@ -1667,6 +1704,14 @@ export function discoverLawGraph(
|
|
|
1667
1704
|
for (const [index, intentNode] of intentNodes.entries()) {
|
|
1668
1705
|
const declaration = intents.find((intent) => intent.id === intentNode.entity.label)!;
|
|
1669
1706
|
const gate = declaration.requiresRelationGate;
|
|
1707
|
+
if (declaration.acceptsWorkspaceEffectGate === true) {
|
|
1708
|
+
if (gate === undefined || gate.object !== "workspace:self" || gate.objectFrom !== undefined) {
|
|
1709
|
+
fail(
|
|
1710
|
+
`intent '${declaration.id}' .acceptsWorkspaceEffect() requires a static ` +
|
|
1711
|
+
`.requires(relation, { object: "workspace:self" }) authorization gate`,
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1670
1715
|
if (gate === undefined) continue;
|
|
1671
1716
|
const objectType = gate.objectFrom?.recordType ?? gate.object.split(":", 1)[0]!;
|
|
1672
1717
|
const exactLabel = gate.relation.includes("#") ? gate.relation : `${objectType}#${gate.relation}`;
|
|
@@ -2783,6 +2828,7 @@ export function lowerAnonymousValueGraph<K extends LawEntityKind>(
|
|
|
2783
2828
|
case "recursive-ref":
|
|
2784
2829
|
throw new Error("unreachable recursive-ref lowering");
|
|
2785
2830
|
}
|
|
2831
|
+
if (value.semantic !== undefined) expression = { ...expression, semantic: value.semantic };
|
|
2786
2832
|
nodes.set(entity.id, {
|
|
2787
2833
|
kind: "structural",
|
|
2788
2834
|
structuralKind: "value-type",
|
|
@@ -2831,25 +2877,28 @@ export function lowerAnonymousContractFields(
|
|
|
2831
2877
|
}
|
|
2832
2878
|
|
|
2833
2879
|
function canonicalValueType(value: LawValueType): LawValueType {
|
|
2880
|
+
const semantic = value.semantic === undefined ? {} : { semantic: value.semantic };
|
|
2834
2881
|
switch (value.kind) {
|
|
2835
2882
|
case "record":
|
|
2836
2883
|
return {
|
|
2884
|
+
...semantic,
|
|
2837
2885
|
kind: "record",
|
|
2838
2886
|
fields: value.fields.map((field) => ({ ...field, value: canonicalValueType(field.value) }))
|
|
2839
2887
|
.sort((a, b) => compareText(a.name, b.name)),
|
|
2840
2888
|
};
|
|
2841
2889
|
case "sum":
|
|
2842
2890
|
return {
|
|
2891
|
+
...semantic,
|
|
2843
2892
|
kind: "sum",
|
|
2844
2893
|
options: value.options.map(canonicalValueType)
|
|
2845
2894
|
.sort((a, b) => compareText(JSON.stringify(a), JSON.stringify(b))),
|
|
2846
2895
|
};
|
|
2847
2896
|
case "list":
|
|
2848
|
-
return { kind: "list", element: canonicalValueType(value.element) };
|
|
2897
|
+
return { ...semantic, kind: "list", element: canonicalValueType(value.element) };
|
|
2849
2898
|
case "map":
|
|
2850
|
-
return { kind: "map", value: canonicalValueType(value.value) };
|
|
2899
|
+
return { ...semantic, kind: "map", value: canonicalValueType(value.value) };
|
|
2851
2900
|
case "nullable":
|
|
2852
|
-
return { kind: "nullable", value: canonicalValueType(value.value) };
|
|
2901
|
+
return { ...semantic, kind: "nullable", value: canonicalValueType(value.value) };
|
|
2853
2902
|
case "default":
|
|
2854
2903
|
return { ...value, value: canonicalValueType(value.value) };
|
|
2855
2904
|
case "recursive":
|
package/src/law_graph_usda.ts
CHANGED
|
@@ -159,12 +159,20 @@ function entityLabelOpinions(
|
|
|
159
159
|
];
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
/**
|
|
163
|
-
|
|
162
|
+
/** Exact path index produced while rendering one sparse law layer. Generated domain clients consume
|
|
163
|
+
* these paths in-memory during the same compile so their call USDA cannot drift from the law text.
|
|
164
|
+
* The index is compiler data, not a sidecar artifact. */
|
|
165
|
+
export interface CompiledSparseLawGraphUsda {
|
|
166
|
+
readonly text: string;
|
|
167
|
+
readonly paths: ReadonlyMap<string, string>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Render one sparse law layer and retain its exact identity-path projection for client codegen. */
|
|
171
|
+
export function compileSparseLawGraphUsda(
|
|
164
172
|
graph: LawGraph,
|
|
165
173
|
logicalPath: string,
|
|
166
174
|
closure: readonly LawGraph[] = [graph],
|
|
167
|
-
):
|
|
175
|
+
): CompiledSparseLawGraphUsda {
|
|
168
176
|
const domain = graph.domain;
|
|
169
177
|
const aggregates = graph.nodes.filter((node): node is LawAggregateNode => node.kind === "aggregate");
|
|
170
178
|
const contracts = graph.nodes.filter((node): node is LawContractNode => node.kind === "contract");
|
|
@@ -675,7 +683,16 @@ export function emitSparseLawGraphUsda(
|
|
|
675
683
|
(flow) => `w_${flow.entity.sid}`);
|
|
676
684
|
}
|
|
677
685
|
lines.push(" }", " }", "}", "");
|
|
678
|
-
return lines.join("\n");
|
|
686
|
+
return { text: lines.join("\n"), paths: new Map(pathById) };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/** First W4.4 vertical slice: render domain, aggregate, stored-field and reusable-contract opinions. */
|
|
690
|
+
export function emitSparseLawGraphUsda(
|
|
691
|
+
graph: LawGraph,
|
|
692
|
+
logicalPath: string,
|
|
693
|
+
closure: readonly LawGraph[] = [graph],
|
|
694
|
+
): string {
|
|
695
|
+
return compileSparseLawGraphUsda(graph, logicalPath, closure).text;
|
|
679
696
|
}
|
|
680
697
|
|
|
681
698
|
function emitRetirement(retirement: LawRetirement, indent: number): string[] {
|
|
@@ -966,6 +983,12 @@ function emitIntent(
|
|
|
966
983
|
`${pad}custom uniform bool nomos:active = true`,
|
|
967
984
|
`${pad}custom uniform bool nomos:public = ${intent.public}`,
|
|
968
985
|
`${pad}custom uniform string nomos:publicRoute = ${quote(intent.publicRoute ?? intent.entity.label)}`,
|
|
986
|
+
...(intent.acceptsWorkspaceEffect
|
|
987
|
+
? [`${pad}custom uniform bool nomos:acceptsWorkspaceEffect = true`]
|
|
988
|
+
: []),
|
|
989
|
+
...(intent.offerAuthenticationProof === "enrolledDevice"
|
|
990
|
+
? [`${pad}custom uniform token nomos:offerAuthenticationProof = "EnrolledDevice"`]
|
|
991
|
+
: []),
|
|
969
992
|
`${pad}custom rel nomos:target = ${targets([intent.target], paths, resolveDependency)}`,
|
|
970
993
|
`${pad}custom rel nomos:accepts = <${requiredPath(intent.accepts.id, paths)}>`,
|
|
971
994
|
...(intent.publicAccepts === undefined
|
|
@@ -975,6 +998,11 @@ function emitIntent(
|
|
|
975
998
|
if (intent.reads.length > 0) {
|
|
976
999
|
body.push(`${pad}custom rel nomos:reads = ${targets(intent.reads, paths, resolveDependency)}`);
|
|
977
1000
|
}
|
|
1001
|
+
if ((intent.directsWorkspaceEffects ?? []).length > 0) {
|
|
1002
|
+
body.push(
|
|
1003
|
+
`${pad}custom rel nomos:directsWorkspaceEffect = ${targets(intent.directsWorkspaceEffects, paths, resolveDependency)}`,
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
978
1006
|
if (intent.authorization !== undefined) {
|
|
979
1007
|
body.push(
|
|
980
1008
|
`${pad}custom rel nomos:requires = ${targets([intent.authorization.relation], paths, resolveDependency)}`,
|
|
@@ -1471,7 +1499,9 @@ function emitAnonymousType(
|
|
|
1471
1499
|
): string[] {
|
|
1472
1500
|
const pad = " ".repeat(indent);
|
|
1473
1501
|
const expression = type.expression;
|
|
1474
|
-
const body: string[] =
|
|
1502
|
+
const body: string[] = expression.semantic === undefined
|
|
1503
|
+
? []
|
|
1504
|
+
: emitAnonymousValueSemantic(expression.semantic, indent);
|
|
1475
1505
|
switch (expression.kind) {
|
|
1476
1506
|
case "string":
|
|
1477
1507
|
case "number":
|
|
@@ -1605,6 +1635,44 @@ function emitAnonymousType(
|
|
|
1605
1635
|
return body;
|
|
1606
1636
|
}
|
|
1607
1637
|
|
|
1638
|
+
function emitAnonymousValueSemantic(
|
|
1639
|
+
semantic: NonNullable<LawAnonymousValueTypeNode["expression"]["semantic"]>,
|
|
1640
|
+
indent: number,
|
|
1641
|
+
): string[] {
|
|
1642
|
+
const pad = " ".repeat(indent);
|
|
1643
|
+
const body = [`${pad}custom uniform token nomos:semantic = ${quote(semantic.kind)}`];
|
|
1644
|
+
switch (semantic.kind) {
|
|
1645
|
+
case "sha256-digest":
|
|
1646
|
+
case "git-object-id":
|
|
1647
|
+
break;
|
|
1648
|
+
case "branded-id":
|
|
1649
|
+
case "unit":
|
|
1650
|
+
case "value-object":
|
|
1651
|
+
body.push(`${pad}custom uniform string nomos:semanticName = ${quote(semantic.name)}`);
|
|
1652
|
+
break;
|
|
1653
|
+
case "recipient":
|
|
1654
|
+
body.push(`${pad}custom uniform token nomos:subject = ${quote(semantic.subject)}`);
|
|
1655
|
+
break;
|
|
1656
|
+
case "id-reference":
|
|
1657
|
+
case "address-reference":
|
|
1658
|
+
body.push(
|
|
1659
|
+
`${pad}custom uniform token nomos:targetKind = ${quote(semantic.targetKind)}`,
|
|
1660
|
+
`${pad}custom uniform string nomos:target = ${quote(semantic.target)}`,
|
|
1661
|
+
);
|
|
1662
|
+
break;
|
|
1663
|
+
case "versioned-reference":
|
|
1664
|
+
body.push(
|
|
1665
|
+
`${pad}custom uniform token nomos:targetKind = ${quote(semantic.targetKind)}`,
|
|
1666
|
+
`${pad}custom uniform string nomos:target = ${quote(semantic.target)}`,
|
|
1667
|
+
`${pad}custom uniform token nomos:seriesKind = ${quote(semantic.seriesKind)}`,
|
|
1668
|
+
`${pad}custom uniform string nomos:series = ${quote(semantic.series)}`,
|
|
1669
|
+
`${pad}custom uniform string nomos:currentField = ${quote(semantic.currentField)}`,
|
|
1670
|
+
);
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
return body;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1608
1676
|
function emitAnonymousField(
|
|
1609
1677
|
field: LawAnonymousValueFieldNode,
|
|
1610
1678
|
types: ReadonlyMap<string, LawAnonymousValueTypeNode>,
|