@githolon/dsl 0.102.5 → 0.103.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/compile_package.mjs +43 -2
- package/compile_proof.d.mts +47 -0
- package/compile_proof.mjs +343 -0
- package/dart/CHANGELOG.md +14 -0
- package/dart/lib/src/dev_cloud.dart +8 -8
- package/dart/lib/src/nms_generated_runtime.dart +2 -0
- package/dart/lib/src/runner/node-entry.mjs +5539 -1228
- package/dart/lib/src/runner/offline-install.mjs +4578 -528
- package/dart/lib/src/test_harness.dart +206 -152
- package/dart/lib/src/webview_bridge.dart +239 -102
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +4 -3
- package/dart/test/acquire_session_test.dart +9 -0
- package/dart/test/adv_realm_interference_test.dart +7 -6
- package/dart/test/cloud_bound_topology_test.dart +3 -3
- package/dart/test/cluster_births_test.dart +4 -3
- package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
- package/dart/test/cluster_local_autosync_test.dart +3 -2
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +7 -6
- package/dart/test/first_app_gauntlet_test.dart +14 -21
- package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
- package/dart/test/four_layer_nested_topology_test.dart +4 -4
- package/dart/test/harness_phase_timeout_test.dart +2 -2
- package/dart/test/local_sync_inert_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +2 -2
- package/dart/test/sealing_client_test.dart +4 -3
- package/dart/test/session_bound_test.dart +8 -7
- package/dart/test/test_harness_test.dart +13 -12
- package/dart/test/vo_read_typing_test.dart +4 -3
- package/dart/test/watch_after_offer_stall_test.dart +4 -3
- package/dart/test/webview_bridge_test.dart +102 -5
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/nomos_types.dart +9 -0
- package/dart_types/lib/src/nomos_ref.dart +20 -0
- package/dart_types/lib/src/optional_ref.dart +85 -0
- package/dart_types/lib/src/sha256_digest.dart +41 -0
- package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/optional_ref_test.dart +44 -0
- package/dart_types/test/sha256_digest_test.dart +18 -0
- package/package.json +9 -5
- package/runtime/generated-application-host.js +5365 -1105
- package/src/aggregate.ts +18 -0
- package/src/application_composition.ts +6 -32
- package/src/attested_core.ts +6 -3
- package/src/birth_meter.ts +1 -0
- package/src/birth_meter_ensure.ts +28 -2
- package/src/births.ts +1 -1
- package/src/build_package.ts +162 -538
- package/src/business_birth.ts +19 -3
- package/src/canonical_json.ts +21 -0
- package/src/check_package_main.ts +73 -33
- package/src/clear_directive.ts +19 -8
- package/src/codegen_dart.ts +305 -22
- package/src/codegen_dart_application.ts +862 -292
- package/src/codegen_dart_client.ts +1084 -407
- package/src/codegen_dart_scenario.ts +12 -12
- package/src/codegen_proof.ts +169 -39
- package/src/codegen_provider_dart.ts +17 -1
- package/src/codegen_ts.ts +298 -106
- package/src/compat_manifest.ts +2 -2
- package/src/compat_package_main.ts +8 -12
- package/src/compile_package_main.ts +1860 -592
- package/src/compose.ts +1 -1
- package/src/directive.ts +208 -15
- package/src/engine_entry.ts +165 -111
- package/src/evolve_preview.ts +29 -69
- package/src/extremum.ts +1 -1
- package/src/fields.ts +183 -4
- package/src/flow_gate.ts +75 -2
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +2 -2
- package/src/framework/birth_meter.ts +7 -19
- package/src/framework/bootstrap.ts +0 -2
- package/src/framework/business_corrections.ts +9 -5
- package/src/framework/domain_lifecycle.ts +15 -29
- package/src/framework/encryption_scope.ts +2 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +16 -14
- package/src/framework/home.ts +20 -14
- package/src/framework/owned_custody.ts +9 -3
- package/src/framework/platform_admin.ts +3 -3
- package/src/framework/relations.ts +21 -13
- package/src/framework/signing_identity.ts +45 -40
- package/src/framework/workspace_invariant.ts +245 -0
- package/src/framework/workspaces.ts +23 -29
- package/src/index.ts +36 -16
- package/src/intent.ts +178 -6
- package/src/law_check.ts +32 -11
- package/src/law_graph.ts +2902 -0
- package/src/law_graph_usda.ts +1799 -0
- package/src/law_identity.ts +435 -0
- package/src/law_value_type.ts +230 -0
- package/src/lsp_server.ts +58 -29
- package/src/ops.ts +7 -0
- package/src/package_config.ts +141 -0
- package/src/package_dependencies.ts +108 -0
- package/src/package_dependency_usage.ts +150 -0
- package/src/package_source_imports.ts +71 -0
- package/src/plan_source_slice.ts +335 -0
- package/src/point_guard.ts +192 -0
- package/src/precondition.ts +44 -9
- package/src/read_projection_namespace.ts +1 -1
- package/src/runtime_zod.ts +44 -0
- package/src/secondary_ensure.ts +67 -14
- package/src/shape_compat.ts +1 -1
- package/src/stable_id_identity_bridge.ts +149 -0
- package/src/stable_id_lockfile.ts +140 -6
- package/src/stable_ids.ts +1 -1
- package/src/stable_ids_for_module.ts +348 -0
- package/src/stable_ids_types.ts +1 -1
- package/src/sum.ts +1 -1
- package/src/usd_state.ts +1 -1
- package/src/usda_relationship_integrity.ts +66 -0
- package/src/usda_text.ts +18 -0
- package/src/usdz.ts +78 -0
- package/src/value_object.ts +95 -0
- package/src/wire.ts +19 -0
- package/src/wire_encode.ts +22 -0
- package/src/workspace_sharding.ts +52 -36
- package/src/workspace_sharing.ts +28 -4
- package/src/zod_introspection.ts +1 -1
- package/dart/test/cluster_attested_test.dart +0 -201
- package/src/compile_engine.ts +0 -595
- package/src/manifest.ts +0 -1476
- package/src/read_projection_composition.ts +0 -117
- package/src/read_projection_manifest.ts +0 -115
- package/src/relation_composition.ts +0 -286
- package/src/usd.ts +0 -1967
- package/src/usd_layers.ts +0 -1776
|
@@ -85,7 +85,7 @@ export function generateDartScenario(
|
|
|
85
85
|
mod: DomainModule,
|
|
86
86
|
clientPackage: string,
|
|
87
87
|
testPackage: string,
|
|
88
|
-
|
|
88
|
+
lawUsdzFile: string,
|
|
89
89
|
options: {
|
|
90
90
|
readonly surfaceName?: string;
|
|
91
91
|
readonly applicationFile?: string;
|
|
@@ -194,7 +194,7 @@ export function generateDartScenario(
|
|
|
194
194
|
` final person = signedInAs ?? ${testPerson}.named('test');`,
|
|
195
195
|
` final packageFiles = await _applicationPackages();`,
|
|
196
196
|
` final environment = await NomosTestHarness.ephemeral(`,
|
|
197
|
-
`
|
|
197
|
+
` lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
|
|
198
198
|
` ownerSubject: person._principal,`,
|
|
199
199
|
` workspace: nmsInternalReceivingGroupNameOf(person._principal),`,
|
|
200
200
|
` nodeEntry: Platform.environment['NOMOS_NODE_ENTRY'],`,
|
|
@@ -223,13 +223,13 @@ export function generateDartScenario(
|
|
|
223
223
|
` final packageFiles = await _applicationPackages();`,
|
|
224
224
|
` final cloud = await NomosTestCloud.start(`,
|
|
225
225
|
` serverEntry: serverEntry, platform: applicationName,`,
|
|
226
|
-
`
|
|
226
|
+
` lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
|
|
227
227
|
` onDiagnostic: Platform.environment['NOMOS_TEST_DIAGNOSTICS'] == '1' ? stderr.writeln : null,`,
|
|
228
228
|
` );`,
|
|
229
229
|
` late final NomosTestHarness environment;`,
|
|
230
230
|
` try {`,
|
|
231
231
|
` environment = await NomosTestHarness.cloudBound(`,
|
|
232
|
-
` cloud: cloud,
|
|
232
|
+
` cloud: cloud, lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
|
|
233
233
|
` platformWorkspace: applicationName, subject: person._principal,`,
|
|
234
234
|
` nodeEntry: Platform.environment['NOMOS_NODE_ENTRY'],`,
|
|
235
235
|
` phaseTimeout: const Duration(minutes: 2),`,
|
|
@@ -314,13 +314,13 @@ export function generateDartScenario(
|
|
|
314
314
|
` final packageAsset = await asset.readAsString();`,
|
|
315
315
|
` final identity = RegExp(r'asset\\s+identifier\\s*=\\s*@nomos:package:([^:]+):[0-9a-f]{64}@').firstMatch(packageAsset);`,
|
|
316
316
|
` if (identity == null) throw StateError('Application reference is not a compiled Nomos package: \${asset.path}');`,
|
|
317
|
-
` final
|
|
318
|
-
` if (!
|
|
319
|
-
` final prior = placements[
|
|
317
|
+
` final law = File('\${asset.parent.path}\${Platform.pathSeparator}\${identity.group(1)!}.law.usdz');`,
|
|
318
|
+
` if (!law.existsSync()) throw StateError('Application package has no law archive: \${law.path}');`,
|
|
319
|
+
` final prior = placements[law.path];`,
|
|
320
320
|
` if (prior != null && prior != payload) {`,
|
|
321
|
-
` throw StateError('The same package is both a serving reference and deferred payload: \${
|
|
321
|
+
` throw StateError('The same package is both a serving reference and deferred payload: \${law.path}');`,
|
|
322
322
|
` }`,
|
|
323
|
-
` placements[
|
|
323
|
+
` placements[law.path] = payload;`,
|
|
324
324
|
` }`,
|
|
325
325
|
` if (placements.isEmpty) throw StateError('The application stage contains no deployable Nomos packages.');`,
|
|
326
326
|
` final ordered = placements.entries.toList()`,
|
|
@@ -335,15 +335,15 @@ export function generateDartScenario(
|
|
|
335
335
|
` return files;`,
|
|
336
336
|
` }`,
|
|
337
337
|
` final assetKeys = [`,
|
|
338
|
-
` 'packages/${testPackage}/lib/src/generated/${
|
|
339
|
-
` 'lib/src/generated/${
|
|
338
|
+
` 'packages/${testPackage}/lib/src/generated/${lawUsdzFile}',`,
|
|
339
|
+
` 'lib/src/generated/${lawUsdzFile}',`,
|
|
340
340
|
` ];`,
|
|
341
341
|
` Object? lastError;`,
|
|
342
342
|
` for (final key in assetKeys) {`,
|
|
343
343
|
` try {`,
|
|
344
344
|
` final data = await rootBundle.load(key);`,
|
|
345
345
|
` final dir = await Directory.systemTemp.createTemp('nms-application-package-');`,
|
|
346
|
-
` final file = File('\${dir.path}\${Platform.pathSeparator}${
|
|
346
|
+
` final file = File('\${dir.path}\${Platform.pathSeparator}${lawUsdzFile}');`,
|
|
347
347
|
` await file.writeAsBytes(data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes), flush: true);`,
|
|
348
348
|
` return [file];`,
|
|
349
349
|
` } catch (error) {`,
|
package/src/codegen_proof.ts
CHANGED
|
@@ -45,7 +45,7 @@ import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js"
|
|
|
45
45
|
import { finishCount } from "./count.js";
|
|
46
46
|
import { clearDirectiveId } from "./clear_directive.js";
|
|
47
47
|
import type { DomainInterface } from "./shape_compat.js";
|
|
48
|
-
import { idOfMetaOf, idOfTargetKindOf, voMetaOf, voSample } from "./value_object.js";
|
|
48
|
+
import { idOfMetaOf, idOfTargetKindOf, versionedRefOfMetaOf, voMetaOf, voSample } from "./value_object.js";
|
|
49
49
|
import { mintedCreateField } from "./workspace_routing.js";
|
|
50
50
|
import { deterministicPorts } from "./ctx.js";
|
|
51
51
|
import { __setDispatchDomain, __setDispatchContext } from "./framework/relations.js";
|
|
@@ -209,6 +209,21 @@ function sampleOfZod(
|
|
|
209
209
|
voIncludeOptional = false,
|
|
210
210
|
visited: ReadonlySet<z.ZodTypeAny> = EMPTY_LAZY_VISITED,
|
|
211
211
|
): unknown {
|
|
212
|
+
const versioned = versionedRefOfMetaOf(zt);
|
|
213
|
+
if (versioned !== undefined) {
|
|
214
|
+
return {
|
|
215
|
+
pinned: {
|
|
216
|
+
workspace: "sample-version-workspace",
|
|
217
|
+
type: versioned.targetAggregateType,
|
|
218
|
+
id: "sample-pinned-version",
|
|
219
|
+
},
|
|
220
|
+
series: {
|
|
221
|
+
workspace: "sample-version-workspace",
|
|
222
|
+
type: versioned.seriesAggregateType,
|
|
223
|
+
id: "sample-version-series",
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
212
227
|
// RECURSIVE `z.lazy` (a self-referencing tree/JSON payload): unwrap one level, tracking the
|
|
213
228
|
// lazy object's IDENTITY on this descent. Re-entering the SAME lazy is a knot: sampling can go
|
|
214
229
|
// no deeper. The common case terminates NATURALLY one level up (the recursive branch is an
|
|
@@ -398,11 +413,25 @@ function typedPayloadLiteral(
|
|
|
398
413
|
payload: Readonly<Record<string, unknown>>,
|
|
399
414
|
referenceFields: Readonly<Record<string, string>>,
|
|
400
415
|
overrides: Readonly<Record<string, string>> = {},
|
|
416
|
+
optionalReferenceFields: ReadonlySet<string> = new Set(),
|
|
401
417
|
): string {
|
|
402
|
-
const keys = new Set([
|
|
418
|
+
const keys = new Set([
|
|
419
|
+
...Object.keys(payload),
|
|
420
|
+
...Object.keys(overrides),
|
|
421
|
+
...optionalReferenceFields,
|
|
422
|
+
]);
|
|
403
423
|
const entries = [...keys].map((field) => {
|
|
404
|
-
const
|
|
405
|
-
|
|
424
|
+
const override = overrides[field];
|
|
425
|
+
const referenceType = referenceFields[field];
|
|
426
|
+
const expression = override !== undefined
|
|
427
|
+
? override
|
|
428
|
+
: referenceType !== undefined
|
|
429
|
+
? optionalReferenceFields.has(field)
|
|
430
|
+
? payload[field] === undefined
|
|
431
|
+
? "NomosOptionalRef.absent()"
|
|
432
|
+
: `NomosOptionalRef.present(${proofRefGetter(referenceType)}())`
|
|
433
|
+
: `${proofRefGetter(referenceType)}()`
|
|
434
|
+
: JSON.stringify(payload[field]);
|
|
406
435
|
return `${JSON.stringify(field)}: ${expression}`;
|
|
407
436
|
});
|
|
408
437
|
return `{ ${entries.join(", ")} }`;
|
|
@@ -728,17 +757,34 @@ export function probeEnsuredTypes(dir: Directive<unknown>, where: string): strin
|
|
|
728
757
|
return [...out].sort();
|
|
729
758
|
}
|
|
730
759
|
|
|
731
|
-
/** The
|
|
732
|
-
*
|
|
733
|
-
|
|
734
|
-
|
|
760
|
+
/** The typed relationship leaf produced by current law, with one legacy string decoder so proof
|
|
761
|
+
* discovery remains able to inspect packages compiled before `RelationshipSubject` became a kernel
|
|
762
|
+
* value. New plans never manufacture the string arm. */
|
|
763
|
+
function subjectLeaf(op: FieldOp): Record<string, unknown> | undefined {
|
|
764
|
+
if (op.kind !== "setEntry" || op.field !== "subjects") return undefined;
|
|
765
|
+
const typed = op.kernelValue !== undefined && "RelationshipSubject" in op.kernelValue
|
|
766
|
+
? op.kernelValue.RelationshipSubject
|
|
767
|
+
: op.value;
|
|
768
|
+
if (typeof typed === "object" && typed !== null && !Array.isArray(typed)) {
|
|
769
|
+
return typed as Record<string, unknown>;
|
|
770
|
+
}
|
|
771
|
+
if (typeof typed !== "string") return undefined;
|
|
735
772
|
try {
|
|
736
|
-
|
|
773
|
+
const parsed = JSON.parse(typed) as unknown;
|
|
774
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
775
|
+
? parsed as Record<string, unknown>
|
|
776
|
+
: undefined;
|
|
737
777
|
} catch {
|
|
738
778
|
return undefined;
|
|
739
779
|
}
|
|
740
780
|
}
|
|
741
781
|
|
|
782
|
+
/** `"active"` = a POSITIVE grant; `"revoked"` = a REVOKE. */
|
|
783
|
+
function subjectLeafStatus(op: FieldOp): string | undefined {
|
|
784
|
+
const status = subjectLeaf(op)?.status;
|
|
785
|
+
return typeof status === "string" ? status : undefined;
|
|
786
|
+
}
|
|
787
|
+
|
|
742
788
|
/**
|
|
743
789
|
* The relations a directive's plan POSITIVELY GRANTS — judged PER RELATION-TUPLE (per `aggregateId`), never by
|
|
744
790
|
* relation-name alone. Both a grant (`writeTuple`, `status:"active"`) AND a revoke (`removeTuple`,
|
|
@@ -813,10 +859,16 @@ function resolveGrantSubjectFields(
|
|
|
813
859
|
const inner = m !== null ? fieldForValue(m[1]) : undefined;
|
|
814
860
|
if (inner !== undefined) wrappedSubjectFields.add(inner);
|
|
815
861
|
}
|
|
816
|
-
// grantedBy rides the
|
|
817
|
-
|
|
862
|
+
// grantedBy rides the typed leaf value — match its string fields to synthesized payload values.
|
|
863
|
+
const leaf = subjectLeaf(o);
|
|
864
|
+
if (leaf !== undefined) {
|
|
818
865
|
for (const [name, sv] of Object.entries(synth.value)) {
|
|
819
|
-
if (
|
|
866
|
+
if (
|
|
867
|
+
typeof sv === "string" &&
|
|
868
|
+
sv.length > 0 &&
|
|
869
|
+
name !== fieldForValue(o.entryKey) &&
|
|
870
|
+
Object.values(leaf).some((value) => value === sv)
|
|
871
|
+
) {
|
|
820
872
|
grantedByFields.add(name);
|
|
821
873
|
}
|
|
822
874
|
}
|
|
@@ -1947,6 +1999,8 @@ export interface ChildBirthLeg {
|
|
|
1947
1999
|
* generated client. Present only for an encrypted `.owned()` business birth. */
|
|
1948
2000
|
readonly businessEncryption?: {
|
|
1949
2001
|
readonly scope: string;
|
|
2002
|
+
readonly recordType: string;
|
|
2003
|
+
readonly recordId: string;
|
|
1950
2004
|
readonly namespace: string;
|
|
1951
2005
|
readonly keyField: string;
|
|
1952
2006
|
readonly fields: Readonly<Record<string, { readonly aggSid: string; readonly fieldSid: string }>>;
|
|
@@ -2101,7 +2155,7 @@ function validateGenesisSteps(
|
|
|
2101
2155
|
const mod = modules.find((m) => (m.domain ?? m.name) === s.domain);
|
|
2102
2156
|
if (mod === undefined) {
|
|
2103
2157
|
issues.push(
|
|
2104
|
-
`${at} (${s.domain}/${s.directiveId}) targets domain '${s.domain}', which is NOT composed in this package — add its module to nomos.
|
|
2158
|
+
`${at} (${s.domain}/${s.directiveId}) targets domain '${s.domain}', which is NOT composed in this package — add its module to nomos.project.ts \`domains\` (the one-package pattern: the parent must fold the child law so the kernel can resolve its bytes by hash; see bench/scale/fixtures/estate-birth)`,
|
|
2105
2159
|
);
|
|
2106
2160
|
return;
|
|
2107
2161
|
}
|
|
@@ -2381,9 +2435,9 @@ function discoverSingletonInstanceId(seedDir: Directive<unknown>, ownerMod?: Dom
|
|
|
2381
2435
|
// ── the generator ─────────────────────────────────────────────────────────────────
|
|
2382
2436
|
|
|
2383
2437
|
export interface TsProofOptions {
|
|
2384
|
-
/** The package name (`nomos.
|
|
2438
|
+
/** The package name (`nomos.project.ts` `name`) — names the artifacts the proof loads. */
|
|
2385
2439
|
readonly packageName: string;
|
|
2386
|
-
/**
|
|
2440
|
+
/** Content identity of the built typed USDA closure — checked against the install response. */
|
|
2387
2441
|
readonly domainHash: string;
|
|
2388
2442
|
/** Stable-id structural interface used to teach the offline harness the hidden owned-birth seal slots. */
|
|
2389
2443
|
readonly interface?: Readonly<Record<string, DomainInterface>>;
|
|
@@ -2512,14 +2566,20 @@ function isStandaloneProofSafe(mod: DomainModule, directive: Directive<unknown>
|
|
|
2512
2566
|
!(mod.workspaceInvariants ?? []).some((invariant) => invariant.on === directive!.id);
|
|
2513
2567
|
}
|
|
2514
2568
|
|
|
2515
|
-
/** Prefer the business create which demonstrates the richest typed relationship
|
|
2516
|
-
*
|
|
2517
|
-
*
|
|
2569
|
+
/** Prefer the strictest business create, then the one which demonstrates the richest typed relationship
|
|
2570
|
+
* journey. A capped aggregate is the stronger canonical proof: it exercises a workspace invariant and,
|
|
2571
|
+
* when the same action is also an authority seed, preserves isolation between standalone and bootstrap
|
|
2572
|
+
* legs. After that, a join/create consuming two NomosRefs is a better example than an isolated leaf create.
|
|
2573
|
+
* Stable source order breaks ties. */
|
|
2518
2574
|
function orderedStandaloneCreates(mod: DomainModule): Directive<unknown>[] {
|
|
2575
|
+
const aggregates = new Map((mod.aggregates as AggregateHandle[]).map((aggregate) => [aggregate.id, aggregate]));
|
|
2519
2576
|
return (mod.directives as Directive<unknown>[])
|
|
2520
2577
|
.map((directive, index) => ({ directive, index }))
|
|
2521
2578
|
.filter(({ directive }) => isStandaloneProofSafe(mod, directive))
|
|
2522
2579
|
.sort((a, b) => {
|
|
2580
|
+
const capA = aggregates.get(a.directive.aggregateId ?? "")?.cap;
|
|
2581
|
+
const capB = aggregates.get(b.directive.aggregateId ?? "")?.cap;
|
|
2582
|
+
if ((capA !== undefined) !== (capB !== undefined)) return capA !== undefined ? -1 : 1;
|
|
2523
2583
|
const refsA = Object.keys(aggregateReferenceFields(a.directive.payloadSchema as z.ZodTypeAny)).length;
|
|
2524
2584
|
const refsB = Object.keys(aggregateReferenceFields(b.directive.payloadSchema as z.ZodTypeAny)).length;
|
|
2525
2585
|
return refsB - refsA || a.index - b.index;
|
|
@@ -2532,6 +2592,7 @@ interface ProofReferenceSeed {
|
|
|
2532
2592
|
readonly aggregate: AggregateHandle;
|
|
2533
2593
|
readonly payload: Record<string, unknown>;
|
|
2534
2594
|
readonly referenceFields: Readonly<Record<string, string>>;
|
|
2595
|
+
readonly optionalReferenceFields: readonly string[];
|
|
2535
2596
|
readonly mintField?: string;
|
|
2536
2597
|
}
|
|
2537
2598
|
|
|
@@ -2564,22 +2625,38 @@ function discoverProofReferenceSeeds(mod: DomainModule, target: Directive<unknow
|
|
|
2564
2625
|
}
|
|
2565
2626
|
const mintField = mintedCreateField(directive, aggregate);
|
|
2566
2627
|
const referenceFields: Record<string, string> = { ...aggregateReferenceFields(directive.payloadSchema as z.ZodTypeAny) };
|
|
2628
|
+
const optionalReferenceFields = optionalAggregateReferenceFields(
|
|
2629
|
+
directive.payloadSchema as z.ZodTypeAny,
|
|
2630
|
+
);
|
|
2567
2631
|
if (mintField !== undefined) delete referenceFields[mintField];
|
|
2568
|
-
|
|
2632
|
+
const optionalReferenceSet = new Set(optionalReferenceFields);
|
|
2633
|
+
for (const [field, prerequisite] of Object.entries(referenceFields)) {
|
|
2634
|
+
if (!optionalReferenceSet.has(field)) visitType(prerequisite);
|
|
2635
|
+
}
|
|
2569
2636
|
const synth = synthesizeDirectivePayload(directive, `${domain}/${directive.id}`);
|
|
2570
2637
|
const payload: Record<string, unknown> = { ...synth.value };
|
|
2571
2638
|
if (mintField !== undefined) delete payload[mintField];
|
|
2572
|
-
out.push({
|
|
2639
|
+
out.push({
|
|
2640
|
+
directive,
|
|
2641
|
+
aggregate,
|
|
2642
|
+
payload,
|
|
2643
|
+
referenceFields,
|
|
2644
|
+
optionalReferenceFields,
|
|
2645
|
+
...(mintField !== undefined ? { mintField } : {}),
|
|
2646
|
+
});
|
|
2573
2647
|
visiting.delete(aggregateId);
|
|
2574
2648
|
emitted.add(aggregateId);
|
|
2575
2649
|
};
|
|
2576
2650
|
|
|
2577
2651
|
const targetReferences = { ...aggregateReferenceFields(target.payloadSchema as z.ZodTypeAny) };
|
|
2652
|
+
const targetOptionalReferences = new Set(
|
|
2653
|
+
optionalAggregateReferenceFields(target.payloadSchema as z.ZodTypeAny),
|
|
2654
|
+
);
|
|
2578
2655
|
const targetAggregate = aggregates.find((aggregate) => aggregate.id === target.aggregateId);
|
|
2579
2656
|
const targetMintField = targetAggregate === undefined ? undefined : mintedCreateField(target, targetAggregate);
|
|
2580
2657
|
if (targetMintField !== undefined) delete targetReferences[targetMintField];
|
|
2581
|
-
for (const aggregateId of Object.
|
|
2582
|
-
visitType(aggregateId);
|
|
2658
|
+
for (const [field, aggregateId] of Object.entries(targetReferences)) {
|
|
2659
|
+
if (!targetOptionalReferences.has(field)) visitType(aggregateId);
|
|
2583
2660
|
}
|
|
2584
2661
|
return out;
|
|
2585
2662
|
}
|
|
@@ -2728,6 +2805,11 @@ function attachBusinessEncryption(
|
|
|
2728
2805
|
`${leg.parentDomain}/${leg.birthDirectiveId}: Nomos could not derive the hidden owned-birth custody map`,
|
|
2729
2806
|
);
|
|
2730
2807
|
}
|
|
2808
|
+
if (leg.childSeedInstanceId === undefined) {
|
|
2809
|
+
throw new Error(
|
|
2810
|
+
`${leg.parentDomain}/${leg.birthDirectiveId}: Nomos could not derive the owned birth root identity`,
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2731
2813
|
const fields: Record<string, { aggSid: string; fieldSid: string }> = {};
|
|
2732
2814
|
for (const field of aggregate.encryptedFields ?? []) {
|
|
2733
2815
|
const slot = Object.entries(offeredAggregate.fields).find(([, candidate]) => candidate.label === field);
|
|
@@ -2742,6 +2824,10 @@ function attachBusinessEncryption(
|
|
|
2742
2824
|
...leg,
|
|
2743
2825
|
businessEncryption: {
|
|
2744
2826
|
scope: aggregate.visibility.requires,
|
|
2827
|
+
recordType: isNamespacedAggregate(aggregate)
|
|
2828
|
+
? qualifyReadProjectionType(leg.childDomain, aggregate.id)
|
|
2829
|
+
: aggregate.id,
|
|
2830
|
+
recordId: leg.childSeedInstanceId,
|
|
2745
2831
|
namespace,
|
|
2746
2832
|
keyField,
|
|
2747
2833
|
fields,
|
|
@@ -3132,6 +3218,7 @@ function generateSealedApplicationProof(
|
|
|
3132
3218
|
mod: DomainModule,
|
|
3133
3219
|
opts: TsProofOptions,
|
|
3134
3220
|
primaryLegs: ProofLegs,
|
|
3221
|
+
modules: readonly DomainModule[],
|
|
3135
3222
|
): string {
|
|
3136
3223
|
const domain = mod.domain ?? mod.name;
|
|
3137
3224
|
const applicationName = tsApplicationName(opts.packageName);
|
|
@@ -3141,9 +3228,22 @@ function generateSealedApplicationProof(
|
|
|
3141
3228
|
const businessBirth = primaryLegs.childBirth?.businessAggregateId !== undefined
|
|
3142
3229
|
? primaryLegs.childBirth
|
|
3143
3230
|
: undefined;
|
|
3144
|
-
const
|
|
3231
|
+
const selectedBirthDirective = businessBirth === undefined
|
|
3232
|
+
? undefined
|
|
3233
|
+
: (mod.applicationIntents ?? mod.directives).find(
|
|
3234
|
+
(candidate) => candidate.id === businessBirth.birthDirectiveId,
|
|
3235
|
+
) as Directive<unknown> | undefined;
|
|
3236
|
+
const applicationBirthMethod = businessBirth === undefined
|
|
3237
|
+
? undefined
|
|
3238
|
+
: (selectedBirthDirective as { businessBirth?: unknown } | undefined)?.businessBirth !== undefined
|
|
3239
|
+
? isBusinessIntent(selectedBirthDirective!) ? selectedBirthDirective!.id : "birth"
|
|
3240
|
+
: businessBirth.birthDirectiveId;
|
|
3241
|
+
const bornModule = businessBirth === undefined
|
|
3145
3242
|
? undefined
|
|
3146
|
-
: (
|
|
3243
|
+
: modules.find((candidate) => (candidate.domain ?? candidate.name) === businessBirth.childDomain);
|
|
3244
|
+
const businessBirthFollowup = businessBirth === undefined || bornModule === undefined
|
|
3245
|
+
? undefined
|
|
3246
|
+
: orderedStandaloneCreates(bornModule).flatMap((candidate) => {
|
|
3147
3247
|
const directive = candidate as Directive<unknown>;
|
|
3148
3248
|
if (
|
|
3149
3249
|
(directive as { businessBirthInternal?: unknown }).businessBirthInternal === true ||
|
|
@@ -3153,7 +3253,9 @@ function generateSealedApplicationProof(
|
|
|
3153
3253
|
typeof directive.aggregateId !== "string" ||
|
|
3154
3254
|
Object.keys(aggregateReferenceFields(directive.payloadSchema as z.ZodTypeAny)).length > 0
|
|
3155
3255
|
) return [];
|
|
3156
|
-
const target = (
|
|
3256
|
+
const target = (bornModule.aggregates as AggregateHandle[]).find(
|
|
3257
|
+
(aggregate) => aggregate.id === directive.aggregateId,
|
|
3258
|
+
);
|
|
3157
3259
|
if (target === undefined) return [];
|
|
3158
3260
|
try {
|
|
3159
3261
|
const synth = synthesizeDirectivePayload(directive, `${domain}/${directive.id}`);
|
|
@@ -3179,9 +3281,14 @@ function generateSealedApplicationProof(
|
|
|
3179
3281
|
...referenceSeeds.flatMap((seed) => Object.values(seed.referenceFields)),
|
|
3180
3282
|
...(action === undefined ? [] : Object.values(aggregateReferenceFields(action.payloadSchema as z.ZodTypeAny))),
|
|
3181
3283
|
])].filter((type) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(type)).sort();
|
|
3284
|
+
const hasOptionalReferenceChoices = [
|
|
3285
|
+
...referenceSeeds.flatMap((seed) => seed.optionalReferenceFields),
|
|
3286
|
+
...(action === undefined ? [] : optionalAggregateReferenceFields(action.payloadSchema as z.ZodTypeAny)),
|
|
3287
|
+
].length > 0;
|
|
3182
3288
|
const typeImports = referenceTypes.length > 0
|
|
3183
3289
|
? `, type NomosRef, ${referenceTypes.map((type) => `type ${type}`).join(", ")}`
|
|
3184
3290
|
: "";
|
|
3291
|
+
const optionalReferenceImport = hasOptionalReferenceChoices ? ", NomosOptionalRef" : "";
|
|
3185
3292
|
const refDeclarations = referenceTypes.flatMap((type) => [
|
|
3186
3293
|
`let ${proofRefSlot(type)}: NomosRef<${type}> | undefined;`,
|
|
3187
3294
|
`const ${proofRefGetter(type)} = (): NomosRef<${type}> => ${proofRefSlot(type)} ?? fail(${JSON.stringify(`generated proof ordering error: NomosRef<${type}> is not available yet`)});`,
|
|
@@ -3191,20 +3298,24 @@ function generateSealedApplicationProof(
|
|
|
3191
3298
|
`// Regenerated from the business law; do not edit or maintain a second client example.`,
|
|
3192
3299
|
`// The local proof runner executes every deep framework leg without exposing it here.`,
|
|
3193
3300
|
`// @nomos-proof-legs ${marker}`,
|
|
3194
|
-
`import { ${opener}, sameRecord${typeImports} } from "./${opts.packageName}.client.mjs";`,
|
|
3301
|
+
`import { ${opener}, sameRecord${optionalReferenceImport}${typeImports} } from "./${opts.packageName}.client.mjs";`,
|
|
3195
3302
|
``,
|
|
3196
3303
|
`const fail: (message: string) => never = (message) => { throw new Error(message); };`,
|
|
3197
3304
|
`const ok = (message: string) => console.log("✓ " + message);`,
|
|
3198
3305
|
`if (process.env.NOMOS_PROOF_MANAGED !== "1") fail("run the live journey through: githolon proof --live");`,
|
|
3199
|
-
`const cloud = (process.env.NOMOS_CLOUD || "https://nomos.
|
|
3306
|
+
`const cloud = (process.env.NOMOS_CLOUD || "https://nomos.cafe").replace(/\\/+$/, "");`,
|
|
3200
3307
|
`const applicationName = process.env.NOMOS_APPLICATION || ${JSON.stringify(opts.packageName)};`,
|
|
3308
|
+
`const proofUid = process.env.NOMOS_PROOF_SUBJECT || "";`,
|
|
3309
|
+
`const proofPrincipal = proofUid ? "user:" + proofUid : "";`,
|
|
3201
3310
|
`const signIn = async (request: { readonly deviceChallenge?: string } = {}) => {`,
|
|
3202
|
-
`
|
|
3203
|
-
`
|
|
3204
|
-
` if (!
|
|
3205
|
-
` const
|
|
3206
|
-
`
|
|
3207
|
-
`
|
|
3311
|
+
` const session = process.env.NOMOS_AUTH_TOKEN || "";`,
|
|
3312
|
+
` if (!request.deviceChallenge) return session;`,
|
|
3313
|
+
` if (!proofUid) fail("the generated blank-user proof has no authority subject");`,
|
|
3314
|
+
` const endpoint = process.env.NOMOS_DEVICE_TOKEN_ENDPOINT || "https://id.nomos.captainapp.co.uk/token";`,
|
|
3315
|
+
` const localTestIdp = endpoint.endsWith("/dev/mint-token");`,
|
|
3316
|
+
` const response = await fetch(endpoint, {`,
|
|
3317
|
+
` method: "POST", headers: { "content-type": "application/json", ...(session ? { authorization: "Bearer " + session } : {}) },`,
|
|
3318
|
+
` body: JSON.stringify(localTestIdp ? { sub: proofUid, nonce: request.deviceChallenge } : { nonce: request.deviceChallenge }),`,
|
|
3208
3319
|
` });`,
|
|
3209
3320
|
` const body = await response.json() as { token?: string; error?: string };`,
|
|
3210
3321
|
` if (!response.ok || !body.token) fail("the generated application could not attest its private device: " + (body.error || response.status));`,
|
|
@@ -3234,7 +3345,7 @@ function generateSealedApplicationProof(
|
|
|
3234
3345
|
);
|
|
3235
3346
|
out.push(
|
|
3236
3347
|
`const facts = ${JSON.stringify(businessPayload)};`,
|
|
3237
|
-
`const born = await application
|
|
3348
|
+
`const born = await application.${applicationBirthMethod!}(facts);`,
|
|
3238
3349
|
`if (born === undefined) fail("business birth returned no child application");`,
|
|
3239
3350
|
`await waitUntilSaved();`,
|
|
3240
3351
|
`ok(${JSON.stringify(`birth(${businessBirth.businessAggregateId}) saved through the generated application`)});`,
|
|
@@ -3253,7 +3364,12 @@ function generateSealedApplicationProof(
|
|
|
3253
3364
|
}
|
|
3254
3365
|
} else if (action !== undefined && aggregate !== undefined) {
|
|
3255
3366
|
for (const seed of referenceSeeds) {
|
|
3256
|
-
const payload = typedPayloadLiteral(
|
|
3367
|
+
const payload = typedPayloadLiteral(
|
|
3368
|
+
seed.payload,
|
|
3369
|
+
seed.referenceFields,
|
|
3370
|
+
{},
|
|
3371
|
+
new Set(seed.optionalReferenceFields),
|
|
3372
|
+
);
|
|
3257
3373
|
const variable = `seed${proofTypeIdent(seed.aggregate.id)}`;
|
|
3258
3374
|
out.push(
|
|
3259
3375
|
`const ${variable}Result = await application.${seed.directive.id}(${payload});`,
|
|
@@ -3266,11 +3382,25 @@ function generateSealedApplicationProof(
|
|
|
3266
3382
|
const mintField = mintedCreateField(action, aggregate);
|
|
3267
3383
|
const payload = { ...synth.value };
|
|
3268
3384
|
if (mintField !== undefined) delete payload[mintField];
|
|
3269
|
-
const
|
|
3385
|
+
const optionalRefs = new Set(optionalAggregateReferenceFields(action.payloadSchema as z.ZodTypeAny));
|
|
3386
|
+
const bornContext = primaryLegs.relationBootstrap?.bornContext;
|
|
3387
|
+
const authorityPlaceholder = bornContext?.ownerField === undefined
|
|
3388
|
+
? undefined
|
|
3389
|
+
: bornContext.birthPayload[bornContext.ownerField];
|
|
3390
|
+
const authorityOverrides = typeof authorityPlaceholder !== "string"
|
|
3391
|
+
? {}
|
|
3392
|
+
: Object.fromEntries(
|
|
3393
|
+
Object.entries(payload)
|
|
3394
|
+
.filter(([, value]) => value === authorityPlaceholder)
|
|
3395
|
+
.map(([field]) => [field, "proofPrincipal"]),
|
|
3396
|
+
);
|
|
3397
|
+
const payloadLiteral = typedPayloadLiteral(payload, refs, authorityOverrides, optionalRefs);
|
|
3270
3398
|
const nullArmLines = nullablePayloadFields(action.payloadSchema as z.ZodTypeAny).flatMap((field) => {
|
|
3271
3399
|
const nullPayload = { ...payload, [field]: null };
|
|
3400
|
+
const nullOverrides = { ...authorityOverrides };
|
|
3401
|
+
delete nullOverrides[field];
|
|
3272
3402
|
return [
|
|
3273
|
-
`await application.${action.id}(${typedPayloadLiteral(nullPayload, refs)});`,
|
|
3403
|
+
`await application.${action.id}(${typedPayloadLiteral(nullPayload, refs, nullOverrides, optionalRefs)});`,
|
|
3274
3404
|
`ok(${JSON.stringify(`NULL-ARM PROOF — ${domain}/${action.id} accepted ${field}=null`)});`,
|
|
3275
3405
|
];
|
|
3276
3406
|
});
|
|
@@ -3305,7 +3435,7 @@ function generateSealedApplicationProof(
|
|
|
3305
3435
|
...clearReadLines,
|
|
3306
3436
|
`const reopened = await ${opener}({ cloud, application: applicationName, signIn });`,
|
|
3307
3437
|
`const rows = await reopened.${generatedAggregateCollectionName(aggregate.id)}.all();`,
|
|
3308
|
-
`if (!rows.some((row) => sameRecord(row.ref, created))) fail("a fresh generated application could not read
|
|
3438
|
+
`if (!rows.some((row) => sameRecord(row.ref, created))) fail("a fresh generated application could not read " + created.address + "; restored addresses: " + (rows.map((row) => row.ref.address).join(", ") || "(none)"));`,
|
|
3309
3439
|
`ok("a fresh generated application read the saved record after synchronization");`,
|
|
3310
3440
|
);
|
|
3311
3441
|
} else {
|
|
@@ -3365,5 +3495,5 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
|
|
|
3365
3495
|
// marker, and the index can never describe different directives. The primary domain's legs carry whichever
|
|
3366
3496
|
// of standalone / child-birth / relation-bootstrap it has.
|
|
3367
3497
|
const primaryLegs = legsForDomain(mod, modules, opts.interface);
|
|
3368
|
-
return generateSealedApplicationProof(mod, opts, primaryLegs);
|
|
3498
|
+
return generateSealedApplicationProof(mod, opts, primaryLegs, modules);
|
|
3369
3499
|
}
|
|
@@ -67,6 +67,12 @@ import {
|
|
|
67
67
|
* fragile structural inference over the flattened directive list.
|
|
68
68
|
*/
|
|
69
69
|
export interface ImpureCapabilityDecl {
|
|
70
|
+
/** Law-facing binding name used by the outbox/provider registry. */
|
|
71
|
+
readonly name?: string;
|
|
72
|
+
/** HTTPS offer endpoint; absent means obligations remain locally requested. */
|
|
73
|
+
readonly endpoint?: string;
|
|
74
|
+
/** Maintained requested-task read used by the executor scan lane. */
|
|
75
|
+
readonly tasks?: { readonly id: string };
|
|
70
76
|
/** The task aggregate handle; `.id` is the wire type (e.g. `TranscribeVoiceNoteAggregate`). */
|
|
71
77
|
readonly aggregate: { readonly id: string };
|
|
72
78
|
/** The Obligation directive: `.creates`, payload = envelope ⊕ tenant params. */
|
|
@@ -184,7 +190,9 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
|
|
|
184
190
|
}
|
|
185
191
|
case "wrapped": {
|
|
186
192
|
// typed-id class (String wire) or unit extension type (num wire): wrap the raw scalar.
|
|
187
|
-
const wrap = (v: string) =>
|
|
193
|
+
const wrap = (v: string) => s.wrappedScalarKind === "number"
|
|
194
|
+
? `${s.dartType}((${v} as num).toDouble())`
|
|
195
|
+
: `${s.dartType}(${v} as String)`;
|
|
188
196
|
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|
|
189
197
|
}
|
|
190
198
|
case "ref": {
|
|
@@ -194,6 +202,14 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
|
|
|
194
202
|
`NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${wireType}', id: ${v} as String)`;
|
|
195
203
|
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|
|
196
204
|
}
|
|
205
|
+
case "versionedRef": {
|
|
206
|
+
const target = s.versionedRefTarget as string;
|
|
207
|
+
const series = s.versionedRefSeries as string;
|
|
208
|
+
const currentField = JSON.stringify(s.versionedRefCurrentField);
|
|
209
|
+
const wrap = (v: string) =>
|
|
210
|
+
`() { final m = (${v} as Map).cast<String, Object?>(); return NmsInternalVersionedNomosRefs.create<${target}, ${series}>(pinned: NmsInternalNomosRefs.fromJson<${target}>(m['pinned']), series: NmsInternalNomosRefs.fromJson<${series}>(m['series']), currentField: ${currentField}); }()`;
|
|
211
|
+
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|
|
212
|
+
}
|
|
197
213
|
case "recipient": {
|
|
198
214
|
const wrap = (v: string) => `NmsInternalInvitations.recipient<User>(${v} as String)`;
|
|
199
215
|
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|