@githolon/dsl 0.101.6 → 0.102.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 +13 -0
- package/dart/lib/nomos_client.dart +3 -52
- package/dart/lib/src/dev_cloud.dart +5 -0
- package/dart/lib/src/home.dart +10 -15
- package/dart/lib/src/nms_conformance_runtime.dart +12 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/nms_test_environment.dart +8 -0
- package/dart/lib/src/node_runner_transport.dart +56 -18
- package/dart/lib/src/provider.dart +5 -1
- package/dart/lib/src/record_origin.dart +55 -0
- package/dart/lib/src/runner/node-entry.mjs +9105 -0
- package/dart/lib/src/runner/offline-install.mjs +4729 -0
- package/dart/lib/src/session_bound.dart +23 -10
- package/dart/lib/src/source_trust.dart +51 -0
- package/dart/lib/src/test_harness.dart +490 -58
- package/dart/lib/src/webview_bridge.dart +167 -58
- package/dart/lib/src/workspace_name.dart +7 -1
- package/dart/pubspec.lock +3 -3
- package/dart/pubspec.yaml +4 -2
- package/dart/test/acquire_session_credentials_test.dart +34 -17
- package/dart/test/acquire_session_test.dart +109 -24
- package/dart/test/adv_realm_interference_test.dart +227 -99
- package/dart/test/bundle_version_pairing_test.dart +1 -1
- package/dart/test/cloud_bound_topology_test.dart +1 -1
- package/dart/test/cluster_attested_test.dart +52 -35
- package/dart/test/cluster_births_test.dart +5 -2
- package/dart/test/cluster_device_keyed_acceptance_test.dart +5 -2
- package/dart/test/cluster_local_autosync_test.dart +5 -2
- package/dart/test/cluster_scale_test.dart +5 -2
- package/dart/test/compat_test.dart +1 -1
- package/dart/test/dlq_session_surface_test.dart +2 -1
- package/dart/test/errors_test.dart +1 -1
- package/dart/test/first_app_gauntlet_test.dart +78 -51
- package/dart/test/four_layer_nested_topology_test.dart +1 -1
- package/dart/test/harness_phase_timeout_test.dart +1 -1
- package/dart/test/home_parity_test.dart +9 -5
- package/dart/test/local_sync_inert_test.dart +22 -14
- package/dart/test/node_runner_transport_test.dart +9 -5
- package/dart/test/null_slot_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +5 -2
- package/dart/test/schema_validation_test.dart +1 -1
- package/dart/test/sealing_client_test.dart +2 -1
- package/dart/test/session_bound_test.dart +92 -119
- package/dart/test/stale_bundle_test.dart +1 -1
- package/dart/test/test_harness_test.dart +77 -22
- package/dart/test/vo_read_typing_test.dart +32 -14
- package/dart/test/watch_after_offer_stall_test.dart +2 -1
- package/dart/test/webview_bridge_test.dart +17 -4
- package/dart/test/wire_test.dart +1 -1
- package/dart_types/CHANGELOG.md +8 -0
- package/dart_types/lib/nomos_types.dart +7 -6
- package/dart_types/lib/src/activity.dart +23 -13
- package/dart_types/lib/src/foreign_ref.dart +61 -112
- package/dart_types/lib/src/invitation.dart +96 -0
- package/dart_types/lib/src/nomos_ref.dart +97 -59
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +125 -19
- package/package.json +7 -3
- package/runtime/generated-application-host.js +8851 -0
- package/src/activity.ts +2 -2
- package/src/aggregate.ts +24 -1
- package/src/application_composition.ts +282 -0
- package/src/application_topology.ts +151 -0
- package/src/attested_core.ts +2 -2
- package/src/authoring_context.ts +9 -0
- package/src/birth_meter_ensure.ts +1 -1
- package/src/births.ts +53 -5
- package/src/build_package.ts +79 -71
- package/src/business_birth.ts +93 -10
- package/src/clear_directive.ts +3 -1
- package/src/codegen_dart.ts +369 -420
- package/src/codegen_dart_application.ts +1508 -0
- package/src/codegen_dart_client.ts +156 -95
- package/src/codegen_dart_scenario.ts +299 -0
- package/src/codegen_openapi.ts +8 -3
- package/src/codegen_proof.ts +1113 -1137
- package/src/codegen_provider_dart.ts +12 -1
- package/src/codegen_ts.ts +1176 -496
- package/src/compat_package_main.ts +22 -10
- package/src/compile_package_main.ts +1536 -337
- package/src/contract.ts +10 -4
- package/src/dart_barrel_guard.ts +5 -1
- package/src/describe_model.ts +13 -5
- package/src/directive.ts +244 -5
- package/src/engine_entry.ts +56 -13
- package/src/engine_javascript.ts +31 -0
- package/src/evolve_preview.ts +3 -12
- package/src/framework/application_home.ts +24 -10
- package/src/framework/capability.ts +15 -1
- package/src/framework/domain_lifecycle.ts +8 -0
- package/src/framework/framework_aggregates.ts +2 -2
- package/src/framework/genesis.ts +11 -1
- package/src/framework/home.ts +178 -75
- package/src/framework/impure_capability.ts +24 -7
- package/src/framework/relations.ts +13 -5
- package/src/framework/workspaces.ts +13 -12
- package/src/generated_client_names.ts +42 -3
- package/src/idof_lint.ts +65 -13
- package/src/index.ts +46 -12
- package/src/intent.ts +587 -0
- package/src/law_check.ts +64 -16
- package/src/precondition.ts +16 -4
- package/src/read_projection_namespace.ts +2 -7
- package/src/recipient.ts +33 -0
- package/src/relation_schema.ts +93 -0
- package/src/secondary_ensure.ts +11 -2
- package/src/shape_compat.ts +11 -1
- package/src/usd.ts +30 -8
- package/src/usd_layers.ts +447 -246
- package/src/usd_state.ts +1 -1
- package/src/value_object.ts +49 -13
- package/src/wire_encode.ts +124 -26
- package/src/workspace_sharing.ts +269 -0
- package/src/zod_introspection.ts +84 -0
- package/dart/example/nomos_secure_key_store.dart +0 -82
- package/dart/example/nomos_webview_transport.dart +0 -63
- package/dart/lib/nomos_headless.dart +0 -50
- package/dart/lib/testing.dart +0 -14
- package/dart/test/co2_product_gauntlet_test.dart +0 -244
- package/src/framework/identity.ts +0 -537
package/src/usd_layers.ts
CHANGED
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
* (and a real `.usda` text layer standard USD tooling opens), instead of
|
|
20
20
|
* lowering only the composed result.
|
|
21
21
|
* * `flattenLayeredUsd` is the ordered opinion-map fold over those layers —
|
|
22
|
-
* and the proof obligation is
|
|
22
|
+
* and the proof obligation is CANONICAL LAW EQUALITY:
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* canonicalJson(flattenLayeredUsd(layers)) === canonicalJson(emitUsd([composed]))
|
|
25
25
|
*
|
|
26
26
|
* i.e. Φ(fold_modules(A, B)) == fold_layers(Φ(A), Φ(B)) — flatten-after-lower
|
|
27
|
-
* equals lower-after-compose
|
|
28
|
-
*
|
|
27
|
+
* equals lower-after-compose. `nomos-compile` ASSERTS this fail-closed on every
|
|
28
|
+
* compile (never assumed); release builds can additionally require stock OpenUSD's
|
|
29
|
+
* `usdcat --flatten` to resolve the same canonical law.
|
|
29
30
|
*
|
|
30
31
|
* WHERE THE TWO FOLDS COULD DIVERGE (the lemma, stated honestly): USD composes
|
|
31
32
|
* PER-ATTRIBUTE (a weaker layer's attribute shows through a stronger prim that is
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
* for a directive's required attributes (target/marker/requires/payload). The gap
|
|
37
38
|
* is omit-when-empty boundaries (`reads`/`emits`) on a RE-declared directive:
|
|
38
39
|
* "omitted" means "none" to the module fold but "no opinion" (show-through) to
|
|
39
|
-
* USD. The compile-time
|
|
40
|
+
* USD. The compile-time canonical-equality assertion fail-closes that case rather than
|
|
40
41
|
* silently shipping either answer.
|
|
41
42
|
*
|
|
42
43
|
* STATE vs LAW (why this is sound): law composition is this non-commutative
|
|
@@ -44,8 +45,8 @@
|
|
|
44
45
|
* commutative semilattice world (Lww/AddWins/MapOf merge drivers — order-free by
|
|
45
46
|
* construction). The domainHash is deliberately NON-homomorphic: it is sha256 over
|
|
46
47
|
* the COMPOSED canonical bytes, so identity lives on the flattened law, never on
|
|
47
|
-
* any decomposition of it.
|
|
48
|
-
*
|
|
48
|
+
* any decomposition of it. Every package therefore emits its authored layer stack;
|
|
49
|
+
* the executable `.package.usda` envelope carries that resolved law and its hash.
|
|
49
50
|
*
|
|
50
51
|
* LIVRPS note: the module list maps onto the SUBLAYER arc only (local opinions per
|
|
51
52
|
* layer, stronger-over-weaker). The reference/variant arcs of `usd.ts` are the
|
|
@@ -54,10 +55,11 @@
|
|
|
54
55
|
* re-authoring its own module stack is opinion strength; a foreign layer composing
|
|
55
56
|
* over deployed law is a monotonic move.
|
|
56
57
|
*
|
|
57
|
-
* BUILD-TIME ONLY (Buffer for
|
|
58
|
+
* BUILD-TIME ONLY (Buffer for lossless UTF-8 identifier tokens): reached via `@githolon/dsl/usd-layers`, never
|
|
58
59
|
* the runtime barrel.
|
|
59
60
|
*/
|
|
60
61
|
import type { AggregateHandle } from "./aggregate.js";
|
|
62
|
+
import { createHash } from "node:crypto";
|
|
61
63
|
import { READ_PROJECTION_DELIMITER } from "./read_projection_namespace.js";
|
|
62
64
|
import type { Field } from "./fields.js";
|
|
63
65
|
import type { DomainModule } from "./codegen_dart.js";
|
|
@@ -78,21 +80,14 @@ import { withBusinessCorrectionLayer } from "./framework/business_corrections.js
|
|
|
78
80
|
export const NOMOS_USD_LAYER_FORMAT = "nomos.usd-layer.v1";
|
|
79
81
|
/** The layered-root format marker — the subLayers document. */
|
|
80
82
|
export const NOMOS_USD_LAYERED_PACKAGE_FORMAT = "nomos.usd-layered-package.v1";
|
|
83
|
+
/** The self-contained ledger/runtime stage: native law plus indexed executable plan assets. */
|
|
84
|
+
export const NOMOS_USD_PLAN_PACKAGE_FORMAT = "nomos.openusd-plan-package.v2";
|
|
81
85
|
|
|
82
86
|
// ── small codecs ───────────────────────────────────────────────────────────────────
|
|
83
87
|
|
|
84
|
-
/** Lowercase hex of the UTF-8 bytes (same convention as the package envelope). */
|
|
85
|
-
function hexUtf8(text: string): string {
|
|
86
|
-
return Buffer.from(text, "utf8").toString("hex");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function unhexUtf8(hex: string): string {
|
|
90
|
-
return Buffer.from(hex, "hex").toString("utf8");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
88
|
/** Escape a string for a double-quoted usda string literal. */
|
|
94
89
|
export function usdaString(s: string): string {
|
|
95
|
-
return
|
|
90
|
+
return JSON.stringify(s);
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
/** A usda `string[]` literal. */
|
|
@@ -149,6 +144,79 @@ export function primName(id: string): string {
|
|
|
149
144
|
return /^[A-Za-z_]/.test(sanitized) ? sanitized : `_${sanitized}`;
|
|
150
145
|
}
|
|
151
146
|
|
|
147
|
+
/** Lossless USD identifier for an arbitrary business field label. */
|
|
148
|
+
export function fieldToken(field: string): string {
|
|
149
|
+
return `f${Buffer.from(field, "utf8").toString("hex")}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Inverse of {@link fieldToken}. */
|
|
153
|
+
export function parseFieldToken(token: string): string {
|
|
154
|
+
if (!/^f(?:[0-9a-f]{2})+$/.test(token)) throw new Error(`usd-layers: malformed field token '${token}'`);
|
|
155
|
+
return Buffer.from(token.slice(1), "hex").toString("utf8");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Canonical JSON text used only to content-stamp a native USD value tree. */
|
|
159
|
+
function stableValueJson(value: unknown): string {
|
|
160
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
161
|
+
if (Array.isArray(value)) return `[${value.map(stableValueJson).join(",")}]`;
|
|
162
|
+
return `{${Object.keys(value as Record<string, unknown>).sort().map((key) =>
|
|
163
|
+
`${JSON.stringify(key)}:${stableValueJson((value as Record<string, unknown>)[key])}`).join(",")}}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Encode an arbitrary law value as an ordinary USD prim tree. No JSON text is carried. Every node
|
|
168
|
+
* bears the root content stamp so a stronger layer replaces the whole value: weaker descendants
|
|
169
|
+
* may show through structurally under USD composition, but the reader ignores their old stamp.
|
|
170
|
+
*/
|
|
171
|
+
export function nativeValueLines(name: string, value: unknown, indent: string): string[] {
|
|
172
|
+
const hash = createHash("sha256").update(stableValueJson(value)).digest("hex");
|
|
173
|
+
const keyHex = (key: string): string => Buffer.from(key, "utf8").toString("hex");
|
|
174
|
+
const scalarLine = (nodeValue: unknown, attr: string, at: string): string => {
|
|
175
|
+
if (nodeValue === null) return `${at}custom bool nomos:zv:${attr} = true`;
|
|
176
|
+
if (typeof nodeValue === "string") return `${at}custom string nomos:sv:${attr} = ${usdaString(nodeValue)}`;
|
|
177
|
+
if (typeof nodeValue === "boolean") return `${at}custom bool nomos:bv:${attr} = ${nodeValue ? "true" : "false"}`;
|
|
178
|
+
if (typeof nodeValue === "number" && Number.isFinite(nodeValue)) {
|
|
179
|
+
const usdType = Number.isSafeInteger(nodeValue) && !Object.is(nodeValue, -0) ? "int64" : "double";
|
|
180
|
+
return `${at}custom ${usdType} nomos:nv:${attr} = ${Object.is(nodeValue, -0) ? "-0" : String(nodeValue)}`;
|
|
181
|
+
}
|
|
182
|
+
throw new Error(`usd-layers: '${name}' contains a non-JSON value (${typeof nodeValue})`);
|
|
183
|
+
};
|
|
184
|
+
const emitContainer = (nodeName: string, nodeValue: unknown, at: string): string[] => {
|
|
185
|
+
const isArray = Array.isArray(nodeValue);
|
|
186
|
+
const isObject = nodeValue !== null && typeof nodeValue === "object" && !isArray;
|
|
187
|
+
const lines = [
|
|
188
|
+
`${at}def Scope ${usdaString(nodeName)}`,
|
|
189
|
+
`${at}{`,
|
|
190
|
+
`${at} custom string nomos:containerKind = ${usdaString(isArray ? "array" : isObject ? "object" : "scalar")}`,
|
|
191
|
+
];
|
|
192
|
+
const entries: readonly [string | undefined, unknown][] = isArray
|
|
193
|
+
? nodeValue.map((child) => [undefined, child] as const)
|
|
194
|
+
: isObject
|
|
195
|
+
// The content hash canonicalizes object keys. The physical tree MUST use the same order:
|
|
196
|
+
// two layers carrying the same object with different JS insertion order otherwise share a
|
|
197
|
+
// root hash but compose incompatible positional children under that root.
|
|
198
|
+
? Object.entries(nodeValue as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b))
|
|
199
|
+
: [[undefined, nodeValue]];
|
|
200
|
+
entries.forEach(([key, child], index) => {
|
|
201
|
+
const position = `i${String(index).padStart(6, "0")}`;
|
|
202
|
+
const identity = key === undefined ? position : `${position}:k${keyHex(key)}`;
|
|
203
|
+
if (child !== null && typeof child === "object") {
|
|
204
|
+
lines.push("", ...emitContainer(identity.replace(/:/g, "_"), child, `${at} `));
|
|
205
|
+
} else {
|
|
206
|
+
lines.push(scalarLine(child, identity, `${at} `));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
lines.push(`${at}}`);
|
|
210
|
+
return lines;
|
|
211
|
+
};
|
|
212
|
+
const rootName = `__nms_${primName(name)}_${hash}`;
|
|
213
|
+
return [
|
|
214
|
+
`${indent}custom string nomos:value:${name} = ${usdaString(hash)}`,
|
|
215
|
+
"",
|
|
216
|
+
...emitContainer(rootName, value, indent),
|
|
217
|
+
];
|
|
218
|
+
}
|
|
219
|
+
|
|
152
220
|
/** Field→driver map with keys sorted (the canonical schema shape `manifest.ts` emits). */
|
|
153
221
|
function sortedSchema(schema: WireSchema): WireSchema {
|
|
154
222
|
const out: WireSchema = {};
|
|
@@ -157,82 +225,26 @@ function sortedSchema(schema: WireSchema): WireSchema {
|
|
|
157
225
|
}
|
|
158
226
|
|
|
159
227
|
function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string): string[] {
|
|
160
|
-
|
|
228
|
+
const attrs = [
|
|
161
229
|
`${indent}custom string nomos:kind = "domain"`,
|
|
162
230
|
`${indent}custom string nomos:domain = ${usdaString(domain)}`,
|
|
163
|
-
...(layer.nomosIdentityManifest !== undefined
|
|
164
|
-
? [`${indent}custom string nomos:identityManifestHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosIdentityManifest)))}`]
|
|
165
|
-
: []),
|
|
166
|
-
...(layer.nomosReadProjection !== undefined
|
|
167
|
-
? [`${indent}custom string nomos:readProjectionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadProjection)))}`]
|
|
168
|
-
: []),
|
|
169
|
-
...(layer.nomosStableIds !== undefined
|
|
170
|
-
? [`${indent}custom string nomos:stableIdsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosStableIds)))}`]
|
|
171
|
-
: []),
|
|
172
|
-
...(layer.nomosInterface !== undefined
|
|
173
|
-
? [`${indent}custom string nomos:interfaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosInterface)))}`]
|
|
174
|
-
: []),
|
|
175
|
-
...(layer.nomosReadGate !== undefined
|
|
176
|
-
? [`${indent}custom string nomos:readGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadGate)))}`]
|
|
177
|
-
: []),
|
|
178
|
-
...(layer.nomosReadVisibility !== undefined
|
|
179
|
-
? [`${indent}custom string nomos:readVisibilityHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadVisibility)))}`]
|
|
180
|
-
: []),
|
|
181
|
-
...(layer.nomosEncryption !== undefined
|
|
182
|
-
? [`${indent}custom string nomos:encryptionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEncryption)))}`]
|
|
183
|
-
: []),
|
|
184
|
-
...(layer.nomosAttributedGate !== undefined
|
|
185
|
-
? [`${indent}custom string nomos:attributedGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttributedGate)))}`]
|
|
186
|
-
: []),
|
|
187
|
-
...(layer.nomosRelations !== undefined
|
|
188
|
-
? [`${indent}custom string nomos:relationsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelations)))}`]
|
|
189
|
-
: []),
|
|
190
|
-
...(layer.nomosRelationNamespace !== undefined
|
|
191
|
-
? [`${indent}custom string nomos:relationNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationNamespace)))}`]
|
|
192
|
-
: []),
|
|
193
|
-
...(layer.nomosReadProjectionNamespace !== undefined
|
|
194
|
-
? [`${indent}custom string nomos:readProjectionNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadProjectionNamespace)))}`]
|
|
195
|
-
: []),
|
|
196
|
-
...(layer.nomosRelationGate !== undefined
|
|
197
|
-
? [`${indent}custom string nomos:relationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationGate)))}`]
|
|
198
|
-
: []),
|
|
199
|
-
...(layer.nomosAttestationGate !== undefined
|
|
200
|
-
? [`${indent}custom string nomos:attestationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttestationGate)))}`]
|
|
201
|
-
: []),
|
|
202
|
-
...(layer.nomosDelegationGate !== undefined
|
|
203
|
-
? [`${indent}custom string nomos:delegationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosDelegationGate)))}`]
|
|
204
|
-
: []),
|
|
205
|
-
...(layer.nomosAttestedReadGate !== undefined
|
|
206
|
-
? [`${indent}custom string nomos:attestedReadGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttestedReadGate)))}`]
|
|
207
|
-
: []),
|
|
208
|
-
...(layer.nomosBirthGate !== undefined
|
|
209
|
-
? [`${indent}custom string nomos:birthGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthGate)))}`]
|
|
210
|
-
: []),
|
|
211
|
-
...(layer.nomosBirthRecordGate !== undefined
|
|
212
|
-
? [`${indent}custom string nomos:birthRecordGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthRecordGate)))}`]
|
|
213
|
-
: []),
|
|
214
|
-
...(layer.nomosParentAttestedBirth !== undefined
|
|
215
|
-
? [`${indent}custom string nomos:parentAttestedBirthHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosParentAttestedBirth)))}`]
|
|
216
|
-
: []),
|
|
217
|
-
...(layer.nomosVerifiedFirstEnroll !== undefined
|
|
218
|
-
? [`${indent}custom string nomos:verifiedFirstEnrollHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosVerifiedFirstEnroll)))}`]
|
|
219
|
-
: []),
|
|
220
|
-
...(layer.nomosEraCertGate !== undefined
|
|
221
|
-
? [`${indent}custom string nomos:eraCertGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEraCertGate)))}`]
|
|
222
|
-
: []),
|
|
223
|
-
...(layer.nomosValueObjects !== undefined
|
|
224
|
-
? [`${indent}custom string nomos:valueObjectsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosValueObjects)))}`]
|
|
225
|
-
: []),
|
|
226
|
-
...(layer.nomosContracts !== undefined
|
|
227
|
-
? [`${indent}custom string nomos:contractsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosContracts)))}`]
|
|
228
|
-
: []),
|
|
229
|
-
...(layer.nomosForeignRefs !== undefined
|
|
230
|
-
? [`${indent}custom string nomos:foreignRefsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosForeignRefs)))}`]
|
|
231
|
-
: []),
|
|
232
|
-
...(layer.nomosFlowGate !== undefined
|
|
233
|
-
? [`${indent}custom string nomos:flowGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosFlowGate)))}`]
|
|
234
|
-
: []),
|
|
235
231
|
];
|
|
232
|
+
const values: readonly [string, unknown][] = [
|
|
233
|
+
["identityManifest", layer.nomosIdentityManifest], ["readProjection", layer.nomosReadProjection],
|
|
234
|
+
["stableIds", layer.nomosStableIds], ["interface", layer.nomosInterface], ["readGate", layer.nomosReadGate],
|
|
235
|
+
["invariantGate", layer.nomosInvariantGate], ["readVisibility", layer.nomosReadVisibility],
|
|
236
|
+
["encryption", layer.nomosEncryption], ["capabilities", layer.nomosCapabilities],
|
|
237
|
+
["attributedGate", layer.nomosAttributedGate], ["relations", layer.nomosRelations],
|
|
238
|
+
["relationNamespace", layer.nomosRelationNamespace], ["readProjectionNamespace", layer.nomosReadProjectionNamespace],
|
|
239
|
+
["relationGate", layer.nomosRelationGate], ["attestationGate", layer.nomosAttestationGate],
|
|
240
|
+
["delegationGate", layer.nomosDelegationGate], ["attestedReadGate", layer.nomosAttestedReadGate],
|
|
241
|
+
["birthGate", layer.nomosBirthGate], ["birthRecordGate", layer.nomosBirthRecordGate],
|
|
242
|
+
["parentAttestedBirth", layer.nomosParentAttestedBirth], ["verifiedFirstEnroll", layer.nomosVerifiedFirstEnroll],
|
|
243
|
+
["eraCertGate", layer.nomosEraCertGate], ["valueObjects", layer.nomosValueObjects],
|
|
244
|
+
["contracts", layer.nomosContracts], ["foreignRefs", layer.nomosForeignRefs], ["flowGate", layer.nomosFlowGate],
|
|
245
|
+
];
|
|
246
|
+
for (const [name, value] of values) if (value !== undefined) attrs.push("", ...nativeValueLines(name, value, indent));
|
|
247
|
+
return attrs;
|
|
236
248
|
}
|
|
237
249
|
|
|
238
250
|
// ── per-module layer emission ────────────────────────────────────────────────────────
|
|
@@ -265,11 +277,18 @@ export interface EmittedModuleLayer {
|
|
|
265
277
|
export function emitLayeredDomain(
|
|
266
278
|
domain: string,
|
|
267
279
|
modules: readonly LayeredModuleInput[],
|
|
280
|
+
options: { readonly applyBusinessCorrectionLayer?: boolean } = {},
|
|
268
281
|
): EmittedModuleLayer[] {
|
|
269
282
|
if (modules.length === 0) {
|
|
270
283
|
throw new Error(`usd-layers: domain '${domain}' has no modules to layer`);
|
|
271
284
|
}
|
|
272
|
-
|
|
285
|
+
// Raw callers need the cross-module correction prelude. The package compiler,
|
|
286
|
+
// however, passes modules already lowered through composeDomainModule; re-inferring
|
|
287
|
+
// the prelude here would turn framework-expanded capability directives into public
|
|
288
|
+
// business verbs and invent law that the canonical fold never declared.
|
|
289
|
+
const normalizedModules = options.applyBusinessCorrectionLayer === false
|
|
290
|
+
? modules.map((entry) => entry.module)
|
|
291
|
+
: withBusinessCorrectionLayer(modules.map((entry) => entry.module));
|
|
273
292
|
const out: EmittedModuleLayer[] = [];
|
|
274
293
|
const seenPaths = new Set<string>();
|
|
275
294
|
for (const [index, m] of modules.entries()) {
|
|
@@ -328,8 +347,8 @@ function relationLines(
|
|
|
328
347
|
* One module layer as REAL `.usda` text: `/Nomos/<domain>` scopes, one prim per
|
|
329
348
|
* aggregate (field→merge-driver as readable `nomos:driver:<field>` attributes,
|
|
330
349
|
* `t.ref`/`t.hasMany` as USD relationships), one prim per directive, one prim per
|
|
331
|
-
* declared query/derived/combined.
|
|
332
|
-
*
|
|
350
|
+
* declared query/derived/combined. Structured values are ordinary stamped USD prim trees,
|
|
351
|
+
* so the round-trip is byte-faithful without a hidden JSON authority. Re-touched prim paths use
|
|
333
352
|
* the `over` specifier; everything carries `nomos:id` so the sanitized prim name
|
|
334
353
|
* never loses the wire id.
|
|
335
354
|
*/
|
|
@@ -337,12 +356,12 @@ export function usdaLayerText(
|
|
|
337
356
|
domain: string,
|
|
338
357
|
moduleName: string,
|
|
339
358
|
layer: UsdLayer,
|
|
340
|
-
module: DomainModule,
|
|
359
|
+
module: DomainModule | undefined,
|
|
341
360
|
overPaths: ReadonlySet<string>,
|
|
342
361
|
): string {
|
|
343
362
|
const domainScope = `/Nomos/${primName(domain)}`;
|
|
344
363
|
const aggById = new Map<string, AggregateHandle>();
|
|
345
|
-
for (const a of module
|
|
364
|
+
for (const a of (module?.aggregates ?? []) as AggregateHandle[]) aggById.set(a.id, a);
|
|
346
365
|
|
|
347
366
|
const ind2 = " ";
|
|
348
367
|
const ind3 = " ";
|
|
@@ -358,7 +377,7 @@ export function usdaLayerText(
|
|
|
358
377
|
`${ind3}custom string nomos:id = ${usdaString(prim.type)}`,
|
|
359
378
|
);
|
|
360
379
|
for (const [field, driver] of Object.entries(prim.schema)) {
|
|
361
|
-
body.push(`${ind3}custom string nomos:driver:${field} = ${usdaString(driverToken(driver))}`);
|
|
380
|
+
body.push(`${ind3}custom string nomos:driver:${fieldToken(field)} = ${usdaString(driverToken(driver))}`);
|
|
362
381
|
}
|
|
363
382
|
body.push(...relationLines(domainScope, aggById.get(prim.type), ind3));
|
|
364
383
|
} else {
|
|
@@ -373,21 +392,13 @@ export function usdaLayerText(
|
|
|
373
392
|
body.push(`${ind3}custom string[] nomos:reads = ${usdaStringArray(prim.reads)}`);
|
|
374
393
|
}
|
|
375
394
|
if (prim.emits !== undefined) {
|
|
376
|
-
body.push(
|
|
377
|
-
`${ind3}custom string nomos:emitsHex = ${usdaString(hexUtf8(JSON.stringify(prim.emits)))}`,
|
|
378
|
-
);
|
|
395
|
+
body.push("", ...nativeValueLines("emits", prim.emits, ind3));
|
|
379
396
|
}
|
|
380
397
|
if (prim.secondaryOutputs !== undefined) {
|
|
381
|
-
body.push(
|
|
382
|
-
`${ind3}custom string nomos:secondaryOutputsHex = ${usdaString(hexUtf8(JSON.stringify(prim.secondaryOutputs)))}`,
|
|
383
|
-
);
|
|
398
|
+
body.push("", ...nativeValueLines("secondaryOutputs", prim.secondaryOutputs, ind3));
|
|
384
399
|
}
|
|
385
400
|
if (prim.payloadJsonSchema !== undefined) {
|
|
386
|
-
body.push(
|
|
387
|
-
`${ind3}custom string nomos:payloadJsonSchemaHex = ${usdaString(
|
|
388
|
-
hexUtf8(JSON.stringify(prim.payloadJsonSchema)),
|
|
389
|
-
)}`,
|
|
390
|
-
);
|
|
401
|
+
body.push("", ...nativeValueLines("payloadJsonSchema", prim.payloadJsonSchema, ind3));
|
|
391
402
|
}
|
|
392
403
|
}
|
|
393
404
|
body.push(`${ind2}}`, ``);
|
|
@@ -412,7 +423,8 @@ export function usdaLayerText(
|
|
|
412
423
|
`${ind3}custom string nomos:kind = "derived"`,
|
|
413
424
|
`${ind3}custom string nomos:id = ${usdaString(d.id)}`,
|
|
414
425
|
`${ind3}custom string nomos:of = ${usdaString(d.of)}`,
|
|
415
|
-
|
|
426
|
+
``,
|
|
427
|
+
...nativeValueLines("returns", d.returns, ind3),
|
|
416
428
|
`${ind2}}`,
|
|
417
429
|
``,
|
|
418
430
|
);
|
|
@@ -426,7 +438,8 @@ export function usdaLayerText(
|
|
|
426
438
|
`${ind3}custom string nomos:of = ${usdaString(c.of)}`,
|
|
427
439
|
`${ind3}custom string nomos:refField = ${usdaString(c.refField)}`,
|
|
428
440
|
`${ind3}custom string nomos:readsFrom = ${usdaString(c.reads)}`,
|
|
429
|
-
|
|
441
|
+
``,
|
|
442
|
+
...nativeValueLines("returns", c.returns, ind3),
|
|
430
443
|
`${ind2}}`,
|
|
431
444
|
``,
|
|
432
445
|
);
|
|
@@ -448,8 +461,7 @@ export function usdaLayerText(
|
|
|
448
461
|
`{`,
|
|
449
462
|
` def Scope ${usdaString(primName(domain))}`,
|
|
450
463
|
` {`,
|
|
451
|
-
// Stable ids + holon-owned manifests: layer-level law
|
|
452
|
-
// so the .usda round-trips byte-faithfully to the IR layer.
|
|
464
|
+
// Stable ids + holon-owned manifests: native layer-level law values.
|
|
453
465
|
...domainLayerAttrLines(domain, layer, " "),
|
|
454
466
|
``,
|
|
455
467
|
...body,
|
|
@@ -510,6 +522,119 @@ export function layeredRootUsda(
|
|
|
510
522
|
].join("\n");
|
|
511
523
|
}
|
|
512
524
|
|
|
525
|
+
export interface UsdPlanAsset {
|
|
526
|
+
readonly sha256: string;
|
|
527
|
+
readonly javascript: string;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export interface UsdPlanRoute {
|
|
531
|
+
readonly selector: string;
|
|
532
|
+
readonly planSha256: string;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Emit the one self-contained stage stored on the ledger. Law remains ordinary USD prims and
|
|
537
|
+
* attributes; executable implementation details are separate, content-addressed plan assets.
|
|
538
|
+
* The route index is data, not another config format, and therefore composes/travels with the law.
|
|
539
|
+
*/
|
|
540
|
+
export function nativePlanPackageUsda(
|
|
541
|
+
packageName: string,
|
|
542
|
+
document: UsdDocument,
|
|
543
|
+
modules: readonly DomainModule[],
|
|
544
|
+
plans: readonly UsdPlanAsset[],
|
|
545
|
+
routes: readonly UsdPlanRoute[],
|
|
546
|
+
): string {
|
|
547
|
+
const modulesByDomain = new Map(modules.map((module) => [module.domain ?? module.name, module] as const));
|
|
548
|
+
const planByHash = new Map<string, UsdPlanAsset>();
|
|
549
|
+
for (const plan of plans) {
|
|
550
|
+
const actual = createHash("sha256").update(plan.javascript, "utf8").digest("hex");
|
|
551
|
+
if (actual !== plan.sha256) {
|
|
552
|
+
throw new Error(`usd-layers: plan '${plan.sha256}' content hashes to '${actual}'`);
|
|
553
|
+
}
|
|
554
|
+
planByHash.set(plan.sha256, plan);
|
|
555
|
+
}
|
|
556
|
+
for (const route of routes) {
|
|
557
|
+
if (!planByHash.has(route.planSha256)) {
|
|
558
|
+
throw new Error(`usd-layers: route '${route.selector}' names missing plan '${route.planSha256}'`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const domainBlocks: string[] = [];
|
|
563
|
+
for (const layer of document.layers) {
|
|
564
|
+
const domain = layer.path.slice(layer.path.lastIndexOf("/") + 1);
|
|
565
|
+
const module = modulesByDomain.get(domain);
|
|
566
|
+
const lines = usdaLayerText(domain, "composed", layer, module, new Set()).split("\n");
|
|
567
|
+
const marker = ` def Scope ${usdaString(primName(domain))}`;
|
|
568
|
+
const start = lines.indexOf(marker);
|
|
569
|
+
if (start < 0) throw new Error(`usd-layers: could not extract native domain '${domain}'`);
|
|
570
|
+
let balance = 0;
|
|
571
|
+
let opened = false;
|
|
572
|
+
let end = -1;
|
|
573
|
+
for (let index = start; index < lines.length; index += 1) {
|
|
574
|
+
const line = lines[index]!;
|
|
575
|
+
for (const char of line) {
|
|
576
|
+
if (char === "{") { balance += 1; opened = true; }
|
|
577
|
+
if (char === "}") balance -= 1;
|
|
578
|
+
}
|
|
579
|
+
if (opened && balance === 0) { end = index; break; }
|
|
580
|
+
}
|
|
581
|
+
if (end < 0) throw new Error(`usd-layers: native domain '${domain}' has unbalanced scopes`);
|
|
582
|
+
domainBlocks.push(...lines.slice(start, end + 1), "");
|
|
583
|
+
}
|
|
584
|
+
if (domainBlocks[domainBlocks.length - 1] === "") domainBlocks.pop();
|
|
585
|
+
|
|
586
|
+
const sortedPlans = [...planByHash.values()].sort((a, b) => a.sha256.localeCompare(b.sha256));
|
|
587
|
+
const sortedRoutes = [...routes].sort((a, b) => a.selector.localeCompare(b.selector));
|
|
588
|
+
const runtime: string[] = [
|
|
589
|
+
`def Scope "__NomosRuntime_7e9c"`,
|
|
590
|
+
`{`,
|
|
591
|
+
` custom string nomos:kind = "runtime"`,
|
|
592
|
+
` custom string nomos:format = ${usdaString(NOMOS_USD_PLAN_PACKAGE_FORMAT)}`,
|
|
593
|
+
` custom string nomos:package = ${usdaString(packageName)}`,
|
|
594
|
+
``,
|
|
595
|
+
` def Scope "Routes"`,
|
|
596
|
+
` {`,
|
|
597
|
+
];
|
|
598
|
+
sortedRoutes.forEach((route, index) => runtime.push(
|
|
599
|
+
` def Scope "r${String(index).padStart(6, "0")}"`,
|
|
600
|
+
` {`,
|
|
601
|
+
` custom string nomos:selector = ${usdaString(route.selector)}`,
|
|
602
|
+
` custom string nomos:plan = ${usdaString(route.planSha256)}`,
|
|
603
|
+
` }`,
|
|
604
|
+
``,
|
|
605
|
+
));
|
|
606
|
+
if (runtime[runtime.length - 1] === "") runtime.pop();
|
|
607
|
+
runtime.push(` }`, ``, ` def Scope "Plans"`, ` {`);
|
|
608
|
+
sortedPlans.forEach((plan) => runtime.push(
|
|
609
|
+
` def Scope ${usdaString(`p_${plan.sha256}`)}`,
|
|
610
|
+
` {`,
|
|
611
|
+
` custom string nomos:sha256 = ${usdaString(plan.sha256)}`,
|
|
612
|
+
` custom string nomos:language = "javascript"`,
|
|
613
|
+
` custom string nomos:source = ${usdaString(plan.javascript)}`,
|
|
614
|
+
` }`,
|
|
615
|
+
``,
|
|
616
|
+
));
|
|
617
|
+
if (runtime[runtime.length - 1] === "") runtime.pop();
|
|
618
|
+
runtime.push(` }`, `}`);
|
|
619
|
+
|
|
620
|
+
return [
|
|
621
|
+
`#usda 1.0`,
|
|
622
|
+
`(`,
|
|
623
|
+
` customLayerData = {`,
|
|
624
|
+
` string "nomos:format" = ${usdaString(NOMOS_USD_PLAN_PACKAGE_FORMAT)}`,
|
|
625
|
+
` }`,
|
|
626
|
+
`)`,
|
|
627
|
+
``,
|
|
628
|
+
`def Scope "Nomos"`,
|
|
629
|
+
`{`,
|
|
630
|
+
...domainBlocks,
|
|
631
|
+
`}`,
|
|
632
|
+
``,
|
|
633
|
+
...runtime,
|
|
634
|
+
``,
|
|
635
|
+
].join("\n");
|
|
636
|
+
}
|
|
637
|
+
|
|
513
638
|
// ── the ordered opinion-map fold (flatten) ───────────────────────────────────────────
|
|
514
639
|
|
|
515
640
|
/** Strongest-opinion-per-attribute merge of two prims at one path (over = stronger). */
|
|
@@ -609,9 +734,25 @@ function foldIdentityManifest(
|
|
|
609
734
|
const workspaceTypes = foldById(manifests.map((m) => m.workspaceTypes));
|
|
610
735
|
const homes = foldRecord(manifests.map((m) => m.homes));
|
|
611
736
|
const routes = foldByKey(manifests.map((m) => m.routes), (r) => r.directive);
|
|
737
|
+
const aggregatesById = new Map<string, NonNullable<UsdLayer["nomosIdentityManifest"]>["aggregates"][number]>();
|
|
738
|
+
for (const manifest of manifests) {
|
|
739
|
+
for (const aggregate of manifest.aggregates) aggregatesById.set(aggregate.id, aggregate);
|
|
740
|
+
}
|
|
741
|
+
// Aggregate namespace qualification happens after the canonical manifest has
|
|
742
|
+
// sorted its bare business labels. Preserve that order here: sorting the final
|
|
743
|
+
// `co2.Asset` spelling would incorrectly move every framework aggregate ahead
|
|
744
|
+
// of the tenant model even though the laws are otherwise identical.
|
|
745
|
+
const identityLabel = (id: string): string => id.slice(id.lastIndexOf(".") + 1);
|
|
746
|
+
const aggregates = [...aggregatesById.values()].sort((a, b) => {
|
|
747
|
+
const aLabel = identityLabel(a.id);
|
|
748
|
+
const bLabel = identityLabel(b.id);
|
|
749
|
+
return aLabel !== bLabel
|
|
750
|
+
? aLabel < bLabel ? -1 : 1
|
|
751
|
+
: a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
752
|
+
});
|
|
612
753
|
return {
|
|
613
754
|
domain,
|
|
614
|
-
aggregates
|
|
755
|
+
aggregates,
|
|
615
756
|
directives: foldById(manifests.map((m) => m.directives)) ?? [],
|
|
616
757
|
...(stableIds !== undefined ? { stableIds } : {}),
|
|
617
758
|
...(workspaceInvariants !== undefined ? { workspaceInvariants } : {}),
|
|
@@ -702,14 +843,18 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
702
843
|
stableIds = { v: 1, domain: domainSid, aggregates };
|
|
703
844
|
}
|
|
704
845
|
}
|
|
705
|
-
// THE STRUCTURAL INTERFACE (#72): fold per-module interfaces — directives
|
|
706
|
-
// later-wins,
|
|
846
|
+
// THE STRUCTURAL INTERFACE (#72): fold per-module interfaces — directives, aggregates and their
|
|
847
|
+
// declared cross-workspace read edges merge later-wins, with sorted keys (matching the composed
|
|
848
|
+
// emission's sorted-key `domainInterface`). `foreignReads` is optional only at the final boundary;
|
|
849
|
+
// dropping it while folding makes a layered package describe a weaker law than its module fold.
|
|
707
850
|
let iface: UsdLayer["nomosInterface"];
|
|
708
851
|
{
|
|
709
852
|
type Dir = NonNullable<UsdLayer["nomosInterface"]>["directives"][string];
|
|
710
853
|
type Agg = NonNullable<UsdLayer["nomosInterface"]>["aggregates"][string];
|
|
854
|
+
type ForeignReads = NonNullable<NonNullable<UsdLayer["nomosInterface"]>["foreignReads"]>[string];
|
|
711
855
|
const dirs = new Map<string, Dir>();
|
|
712
856
|
const aggsI = new Map<string, Agg>();
|
|
857
|
+
const foreignReads = new Map<string, ForeignReads>();
|
|
713
858
|
let domainI: string | undefined;
|
|
714
859
|
let domainSidI: string | undefined;
|
|
715
860
|
for (const l of stack) {
|
|
@@ -718,18 +863,22 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
718
863
|
domainSidI = l.nomosInterface.domainSid;
|
|
719
864
|
for (const [id, spec] of Object.entries(l.nomosInterface.directives)) dirs.set(id, spec);
|
|
720
865
|
for (const [sid, spec] of Object.entries(l.nomosInterface.aggregates)) aggsI.set(sid, spec);
|
|
866
|
+
for (const [id, edges] of Object.entries(l.nomosInterface.foreignReads ?? {})) foreignReads.set(id, edges);
|
|
721
867
|
}
|
|
722
868
|
if (domainI !== undefined) {
|
|
723
869
|
const directivesRec: Record<string, Dir> = {};
|
|
724
870
|
for (const k of [...dirs.keys()].sort()) directivesRec[k] = dirs.get(k)!;
|
|
725
871
|
const aggregatesRec: Record<string, Agg> = {};
|
|
726
872
|
for (const k of [...aggsI.keys()].sort()) aggregatesRec[k] = aggsI.get(k)!;
|
|
873
|
+
const foreignReadsRec: Record<string, ForeignReads> = {};
|
|
874
|
+
for (const k of [...foreignReads.keys()].sort()) foreignReadsRec[k] = foreignReads.get(k)!;
|
|
727
875
|
iface = {
|
|
728
876
|
v: 1,
|
|
729
877
|
domain: domainI,
|
|
730
878
|
...(domainSidI !== undefined ? { domainSid: domainSidI } : {}),
|
|
731
879
|
directives: directivesRec,
|
|
732
880
|
aggregates: aggregatesRec,
|
|
881
|
+
...(foreignReads.size > 0 ? { foreignReads: foreignReadsRec } : {}),
|
|
733
882
|
};
|
|
734
883
|
}
|
|
735
884
|
}
|
|
@@ -754,6 +903,29 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
754
903
|
readGate = { v: 1, queries: queriesRec, directives: directivesRec };
|
|
755
904
|
}
|
|
756
905
|
}
|
|
906
|
+
// THE INVARIANT GATE: aggregate invariant presence is a set; named workspace
|
|
907
|
+
// invariants are later-wins by id. Both are sorted exactly as the canonical emit.
|
|
908
|
+
let invariantGate: UsdLayer["nomosInvariantGate"];
|
|
909
|
+
{
|
|
910
|
+
const aggregates = new Set<string>();
|
|
911
|
+
const workspaceInvariants = new Map<string, { id: string; on: string }>();
|
|
912
|
+
for (const layer of stack) {
|
|
913
|
+
if (layer.nomosInvariantGate === undefined) continue;
|
|
914
|
+
for (const aggregate of layer.nomosInvariantGate.aggregates ?? []) aggregates.add(aggregate);
|
|
915
|
+
for (const invariant of layer.nomosInvariantGate.workspaceInvariants ?? []) {
|
|
916
|
+
workspaceInvariants.set(invariant.id, invariant);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
if (aggregates.size > 0 || workspaceInvariants.size > 0) {
|
|
920
|
+
invariantGate = {
|
|
921
|
+
v: 1,
|
|
922
|
+
...(aggregates.size > 0 ? { aggregates: [...aggregates].sort() } : {}),
|
|
923
|
+
...(workspaceInvariants.size > 0
|
|
924
|
+
? { workspaceInvariants: [...workspaceInvariants.values()].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0) }
|
|
925
|
+
: {}),
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
}
|
|
757
929
|
// The read-visibility gate (slice 5.1): per-aggregate required-read caps merged
|
|
758
930
|
// later-wins, SORTED keys (matching the composed emission). Omit-when-empty.
|
|
759
931
|
let readVisibility: UsdLayer["nomosReadVisibility"];
|
|
@@ -784,6 +956,27 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
784
956
|
encryption = { v: 1, aggregates: aggregatesRec };
|
|
785
957
|
}
|
|
786
958
|
}
|
|
959
|
+
// DECLARED IMPURE CAPABILITIES: compiler composition dedupes by task aggregate;
|
|
960
|
+
// mirror that exact rule here, with the stronger layer winning and canonical
|
|
961
|
+
// aggregate-id order. Omit-when-empty (the capability era rule).
|
|
962
|
+
let capabilities: UsdLayer["nomosCapabilities"];
|
|
963
|
+
{
|
|
964
|
+
type Capability = NonNullable<UsdLayer["nomosCapabilities"]>["capabilities"][number];
|
|
965
|
+
const byAggregate = new Map<string, Capability>();
|
|
966
|
+
for (const layer of stack) {
|
|
967
|
+
for (const capability of layer.nomosCapabilities?.capabilities ?? []) {
|
|
968
|
+
byAggregate.set(capability.aggregateId, capability);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (byAggregate.size > 0) {
|
|
972
|
+
capabilities = {
|
|
973
|
+
v: 1,
|
|
974
|
+
capabilities: [...byAggregate.values()].sort((a, b) =>
|
|
975
|
+
a.aggregateId < b.aggregateId ? -1 : a.aggregateId > b.aggregateId ? 1 : 0,
|
|
976
|
+
),
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
}
|
|
787
980
|
// THE ATTRIBUTED-READS GATE (field_provenance.md): per-aggregate attributed-field
|
|
788
981
|
// sets merged later-wins, SORTED keys (matching the composed emission). Omit-when-empty.
|
|
789
982
|
let attributedGate: UsdLayer["nomosAttributedGate"];
|
|
@@ -1074,7 +1267,7 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
1074
1267
|
};
|
|
1075
1268
|
}
|
|
1076
1269
|
}
|
|
1077
|
-
return {
|
|
1270
|
+
return {
|
|
1078
1271
|
path,
|
|
1079
1272
|
prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
|
|
1080
1273
|
...(identityManifest !== undefined ? { nomosIdentityManifest: identityManifest } : {}),
|
|
@@ -1085,8 +1278,10 @@ return {
|
|
|
1085
1278
|
...(stableIds !== undefined ? { nomosStableIds: stableIds } : {}),
|
|
1086
1279
|
...(iface !== undefined ? { nomosInterface: iface } : {}),
|
|
1087
1280
|
...(readGate !== undefined ? { nomosReadGate: readGate } : {}),
|
|
1281
|
+
...(invariantGate !== undefined ? { nomosInvariantGate: invariantGate } : {}),
|
|
1088
1282
|
...(readVisibility !== undefined ? { nomosReadVisibility: readVisibility } : {}),
|
|
1089
1283
|
...(encryption !== undefined ? { nomosEncryption: encryption } : {}),
|
|
1284
|
+
...(capabilities !== undefined ? { nomosCapabilities: capabilities } : {}),
|
|
1090
1285
|
...(attributedGate !== undefined ? { nomosAttributedGate: attributedGate } : {}),
|
|
1091
1286
|
...(relations !== undefined ? { nomosRelations: relations } : {}),
|
|
1092
1287
|
...(relationNamespace !== undefined ? { nomosRelationNamespace: relationNamespace } : {}),
|
|
@@ -1118,15 +1313,21 @@ return {
|
|
|
1118
1313
|
interface ParsedPrim {
|
|
1119
1314
|
/** Sanitized prim path, e.g. `/Nomos/guestbook/GuestbookEntry`. */
|
|
1120
1315
|
readonly path: string;
|
|
1121
|
-
readonly attrs: Map<string, string | string[]>;
|
|
1316
|
+
readonly attrs: Map<string, string | string[] | number | boolean>;
|
|
1122
1317
|
}
|
|
1123
1318
|
|
|
1124
1319
|
const ATTR_STRING = /^custom\s+string\s+([\w:]+)\s*=\s*"((?:[^"\\]|\\.)*)"$/;
|
|
1125
1320
|
const ATTR_STRING_ARRAY = /^custom\s+string\[\]\s+([\w:]+)\s*=\s*\[(.*)\]$/;
|
|
1321
|
+
const ATTR_NUMBER = /^custom\s+(?:double|int64)\s+([\w:]+)\s*=\s*(-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)$/;
|
|
1322
|
+
const ATTR_BOOL = /^custom\s+bool\s+([\w:]+)\s*=\s*(true|false|0|1)$/;
|
|
1126
1323
|
const PRIM_HEADER = /^(?:def|over|class)(?:\s+\w+)?\s+"([^"]+)"/;
|
|
1127
1324
|
|
|
1128
1325
|
function unescapeUsda(s: string): string {
|
|
1129
|
-
|
|
1326
|
+
try {
|
|
1327
|
+
return JSON.parse(`"${s}"`) as string;
|
|
1328
|
+
} catch {
|
|
1329
|
+
throw new Error("usd-layers: invalid escaped USDA string");
|
|
1330
|
+
}
|
|
1130
1331
|
}
|
|
1131
1332
|
|
|
1132
1333
|
function parseStringArrayItems(inner: string): string[] {
|
|
@@ -1194,10 +1395,98 @@ function parseUsdaPrims(text: string): ParsedPrim[] {
|
|
|
1194
1395
|
top.attrs.set(str[1]!, unescapeUsda(str[2]!));
|
|
1195
1396
|
continue;
|
|
1196
1397
|
}
|
|
1398
|
+
const number = ATTR_NUMBER.exec(line);
|
|
1399
|
+
if (number !== null) {
|
|
1400
|
+
top.attrs.set(number[1]!, Number(number[2]!));
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
const bool = ATTR_BOOL.exec(line);
|
|
1404
|
+
if (bool !== null) top.attrs.set(bool[1]!, bool[2] === "true" || bool[2] === "1");
|
|
1197
1405
|
}
|
|
1198
1406
|
return prims;
|
|
1199
1407
|
}
|
|
1200
1408
|
|
|
1409
|
+
/** Decode one content-addressed native USD value tree immediately beneath `parentPath`. */
|
|
1410
|
+
function nativeValueAt(prims: readonly ParsedPrim[], parentPath: string, name: string): unknown | undefined {
|
|
1411
|
+
const depth = (path: string) => path.split("/").length;
|
|
1412
|
+
const parent = prims.find((prim) => prim.path === parentPath);
|
|
1413
|
+
const hash = parent?.attrs.get(`nomos:value:${name}`);
|
|
1414
|
+
if (hash === undefined) return undefined;
|
|
1415
|
+
if (typeof hash !== "string" || !/^[0-9a-f]{64}$/.test(hash)) {
|
|
1416
|
+
throw new Error(`usd-layers: native value '${name}' has an invalid content address`);
|
|
1417
|
+
}
|
|
1418
|
+
const rootPath = `${parentPath}/__nms_${primName(name)}_${hash}`;
|
|
1419
|
+
const root = prims.find((prim) => prim.path === rootPath);
|
|
1420
|
+
if (root === undefined) {
|
|
1421
|
+
throw new Error(`usd-layers: native value '${name}' points to missing content '${hash}'`);
|
|
1422
|
+
}
|
|
1423
|
+
const descendants = prims.filter((prim) => prim.path === rootPath || prim.path.startsWith(`${rootPath}/`));
|
|
1424
|
+
const directChildren = (path: string): ParsedPrim[] => descendants.filter((candidate) =>
|
|
1425
|
+
candidate.path.startsWith(`${path}/`) && depth(candidate.path) === depth(path) + 1);
|
|
1426
|
+
const decodeKey = (hex: string, path: string): string => {
|
|
1427
|
+
if (!/^(?:[0-9a-f]{2})*$/.test(hex)) {
|
|
1428
|
+
throw new Error(`usd-layers: native object child '${path}' has an invalid key`);
|
|
1429
|
+
}
|
|
1430
|
+
return Buffer.from(hex, "hex").toString("utf8");
|
|
1431
|
+
};
|
|
1432
|
+
type NativeEntry = { readonly index: number; readonly key?: string; readonly value: unknown };
|
|
1433
|
+
const decode = (prim: ParsedPrim): unknown => {
|
|
1434
|
+
const kind = prim.attrs.get("nomos:containerKind");
|
|
1435
|
+
if (kind !== "scalar" && kind !== "array" && kind !== "object") {
|
|
1436
|
+
throw new Error(`usd-layers: native value container '${prim.path}' has no valid kind`);
|
|
1437
|
+
}
|
|
1438
|
+
const entries: NativeEntry[] = [];
|
|
1439
|
+
for (const [attr, value] of prim.attrs) {
|
|
1440
|
+
const match = /^nomos:(sv|bv|nv|zv):i(\d{6})(?::k([0-9a-f]*))?$/.exec(attr);
|
|
1441
|
+
if (match === null) continue;
|
|
1442
|
+
const scalarKind = match[1]!;
|
|
1443
|
+
const key = match[3] === undefined ? undefined : decodeKey(match[3], prim.path);
|
|
1444
|
+
const scalar = scalarKind === "zv" ? null : value;
|
|
1445
|
+
if ((scalarKind === "sv" && typeof scalar !== "string") ||
|
|
1446
|
+
(scalarKind === "bv" && typeof scalar !== "boolean") ||
|
|
1447
|
+
(scalarKind === "nv" && typeof scalar !== "number") ||
|
|
1448
|
+
(scalarKind === "zv" && value !== true)) {
|
|
1449
|
+
throw new Error(`usd-layers: native scalar '${attr}' has the wrong USD type`);
|
|
1450
|
+
}
|
|
1451
|
+
entries.push({ index: Number(match[2]!), ...(key === undefined ? {} : { key }), value: scalar });
|
|
1452
|
+
}
|
|
1453
|
+
for (const child of directChildren(prim.path)) {
|
|
1454
|
+
const childName = child.path.slice(child.path.lastIndexOf("/") + 1);
|
|
1455
|
+
const match = /^i(\d{6})(?:_k([0-9a-f]*))?$/.exec(childName);
|
|
1456
|
+
if (match === null) {
|
|
1457
|
+
throw new Error(`usd-layers: native child '${child.path}' has an invalid position`);
|
|
1458
|
+
}
|
|
1459
|
+
const key = match[2] === undefined ? undefined : decodeKey(match[2], child.path);
|
|
1460
|
+
entries.push({ index: Number(match[1]!), ...(key === undefined ? {} : { key }), value: decode(child) });
|
|
1461
|
+
}
|
|
1462
|
+
entries.sort((a, b) => a.index - b.index);
|
|
1463
|
+
if (entries.some((entry, index) => entry.index !== index)) {
|
|
1464
|
+
throw new Error(`usd-layers: native value container '${prim.path}' has missing or duplicate positions`);
|
|
1465
|
+
}
|
|
1466
|
+
if (kind === "scalar") {
|
|
1467
|
+
if (entries.length !== 1 || entries[0]!.key !== undefined) {
|
|
1468
|
+
throw new Error(`usd-layers: native scalar container '${prim.path}' must contain exactly one unkeyed value`);
|
|
1469
|
+
}
|
|
1470
|
+
return entries[0]!.value;
|
|
1471
|
+
}
|
|
1472
|
+
if (kind === "array") {
|
|
1473
|
+
if (entries.some((entry) => entry.key !== undefined)) {
|
|
1474
|
+
throw new Error(`usd-layers: native array container '${prim.path}' contains an object key`);
|
|
1475
|
+
}
|
|
1476
|
+
return entries.map((entry) => entry.value);
|
|
1477
|
+
}
|
|
1478
|
+
{
|
|
1479
|
+
const out: Record<string, unknown> = {};
|
|
1480
|
+
for (const entry of entries) {
|
|
1481
|
+
if (entry.key === undefined) throw new Error(`usd-layers: native object container '${prim.path}' contains an unkeyed value`);
|
|
1482
|
+
out[entry.key] = entry.value;
|
|
1483
|
+
}
|
|
1484
|
+
return out;
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
return decode(root);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1201
1490
|
function attrString(prim: ParsedPrim, name: string): string {
|
|
1202
1491
|
const v = prim.attrs.get(name);
|
|
1203
1492
|
if (typeof v !== "string") {
|
|
@@ -1218,8 +1507,8 @@ function attrStringArray(prim: ParsedPrim, name: string): string[] {
|
|
|
1218
1507
|
* Reconstruct the IR `UsdDocument` from usda text this module emitted — either ONE
|
|
1219
1508
|
* module layer or a usdcat-FLATTENED stage. Domains are identified by their scope
|
|
1220
1509
|
* prim (`nomos:kind = "domain"`); every child prim rebuilds from its `nomos:*`
|
|
1221
|
-
* attributes into the canonical prim shape (so
|
|
1222
|
-
* `
|
|
1510
|
+
* attributes into the canonical prim shape (so canonicalJson comparison with
|
|
1511
|
+
* `emitUsd` / `flattenLayeredUsd` compares law rather than USD formatting).
|
|
1223
1512
|
*/
|
|
1224
1513
|
export function parseUsdaDocument(text: string): UsdDocument {
|
|
1225
1514
|
const prims = parseUsdaPrims(text);
|
|
@@ -1229,8 +1518,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1229
1518
|
const stableIdsByDomain = new Map<string, UsdLayer["nomosStableIds"]>();
|
|
1230
1519
|
const interfaceByDomain = new Map<string, UsdLayer["nomosInterface"]>();
|
|
1231
1520
|
const readGateByDomain = new Map<string, UsdLayer["nomosReadGate"]>();
|
|
1521
|
+
const invariantGateByDomain = new Map<string, UsdLayer["nomosInvariantGate"]>();
|
|
1232
1522
|
const readVisibilityByDomain = new Map<string, UsdLayer["nomosReadVisibility"]>();
|
|
1233
1523
|
const encryptionByDomain = new Map<string, UsdLayer["nomosEncryption"]>();
|
|
1524
|
+
const capabilitiesByDomain = new Map<string, UsdLayer["nomosCapabilities"]>();
|
|
1234
1525
|
const attributedGateByDomain = new Map<string, UsdLayer["nomosAttributedGate"]>();
|
|
1235
1526
|
const relationsByDomain = new Map<string, UsdLayer["nomosRelations"]>();
|
|
1236
1527
|
const relationGateByDomain = new Map<string, UsdLayer["nomosRelationGate"]>();
|
|
@@ -1252,120 +1543,24 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1252
1543
|
if (p.attrs.get("nomos:kind") === "domain") {
|
|
1253
1544
|
const domain = attrString(p, "nomos:domain");
|
|
1254
1545
|
domainByScope.set(p.path, domain);
|
|
1255
|
-
//
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
const rgHex = p.attrs.get("nomos:readGateHex");
|
|
1274
|
-
if (typeof rgHex === "string") {
|
|
1275
|
-
readGateByDomain.set(domain, JSON.parse(unhexUtf8(rgHex)) as UsdLayer["nomosReadGate"]);
|
|
1276
|
-
}
|
|
1277
|
-
// THE READ-VISIBILITY GATE (slice 5.1): the hex-carried per-aggregate cap map.
|
|
1278
|
-
const rvHex = p.attrs.get("nomos:readVisibilityHex");
|
|
1279
|
-
if (typeof rvHex === "string") {
|
|
1280
|
-
readVisibilityByDomain.set(domain, JSON.parse(unhexUtf8(rvHex)) as UsdLayer["nomosReadVisibility"]);
|
|
1281
|
-
}
|
|
1282
|
-
// THE ENCRYPTION GATE (slice 2): the hex-carried per-aggregate encrypted-field map.
|
|
1283
|
-
const encHex = p.attrs.get("nomos:encryptionHex");
|
|
1284
|
-
if (typeof encHex === "string") {
|
|
1285
|
-
encryptionByDomain.set(domain, JSON.parse(unhexUtf8(encHex)) as UsdLayer["nomosEncryption"]);
|
|
1286
|
-
}
|
|
1287
|
-
// THE ATTRIBUTED-READS GATE (field_provenance.md): the hex-carried per-aggregate map.
|
|
1288
|
-
const attrHex = p.attrs.get("nomos:attributedGateHex");
|
|
1289
|
-
if (typeof attrHex === "string") {
|
|
1290
|
-
attributedGateByDomain.set(domain, JSON.parse(unhexUtf8(attrHex)) as UsdLayer["nomosAttributedGate"]);
|
|
1291
|
-
}
|
|
1292
|
-
// THE RELATION SCHEMA (P1): the hex-carried per-type relation rewrite map.
|
|
1293
|
-
const relHex = p.attrs.get("nomos:relationsHex");
|
|
1294
|
-
if (typeof relHex === "string") {
|
|
1295
|
-
relationsByDomain.set(domain, JSON.parse(unhexUtf8(relHex)) as UsdLayer["nomosRelations"]);
|
|
1296
|
-
}
|
|
1297
|
-
// THE RELATION GATE (P4): the hex-carried directiveId → {relation, object} map.
|
|
1298
|
-
const relGateHex = p.attrs.get("nomos:relationGateHex");
|
|
1299
|
-
if (typeof relGateHex === "string") {
|
|
1300
|
-
relationGateByDomain.set(domain, JSON.parse(unhexUtf8(relGateHex)) as UsdLayer["nomosRelationGate"]);
|
|
1301
|
-
}
|
|
1302
|
-
// THE RELATION-NAMESPACE ERA KEY (relation-namespacing): the hex-carried qualified-type witness.
|
|
1303
|
-
const relNsHex = p.attrs.get("nomos:relationNamespaceHex");
|
|
1304
|
-
if (typeof relNsHex === "string") {
|
|
1305
|
-
relationNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(relNsHex)) as UsdLayer["nomosRelationNamespace"]);
|
|
1306
|
-
}
|
|
1307
|
-
// THE READ-PROJECTION-NAMESPACE ERA KEY: the hex-carried qualified-aggregate witness.
|
|
1308
|
-
const rpNsHex = p.attrs.get("nomos:readProjectionNamespaceHex");
|
|
1309
|
-
if (typeof rpNsHex === "string") {
|
|
1310
|
-
readProjectionNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(rpNsHex)) as UsdLayer["nomosReadProjectionNamespace"]);
|
|
1311
|
-
}
|
|
1312
|
-
// THE ATTESTATION GATE (home_holons §3/§5): the hex-carried attestation-authorized directive set.
|
|
1313
|
-
const attGateHex = p.attrs.get("nomos:attestationGateHex");
|
|
1314
|
-
if (typeof attGateHex === "string") {
|
|
1315
|
-
attestationGateByDomain.set(domain, JSON.parse(unhexUtf8(attGateHex)) as UsdLayer["nomosAttestationGate"]);
|
|
1316
|
-
}
|
|
1317
|
-
// THE DELEGATED-AUTHORITY GATE (delegated_authority_births.md): the hex-carried delegation spec.
|
|
1318
|
-
const delGateHex = p.attrs.get("nomos:delegationGateHex");
|
|
1319
|
-
if (typeof delGateHex === "string") {
|
|
1320
|
-
delegationGateByDomain.set(domain, JSON.parse(unhexUtf8(delGateHex)) as UsdLayer["nomosDelegationGate"]);
|
|
1321
|
-
}
|
|
1322
|
-
// THE ATTESTED-READ GATE (slice B): the hex-carried trust-edge declaration.
|
|
1323
|
-
const attReadGateHex = p.attrs.get("nomos:attestedReadGateHex");
|
|
1324
|
-
if (typeof attReadGateHex === "string") {
|
|
1325
|
-
attestedReadGateByDomain.set(domain, JSON.parse(unhexUtf8(attReadGateHex)) as UsdLayer["nomosAttestedReadGate"]);
|
|
1326
|
-
}
|
|
1327
|
-
// THE BIRTH GATE (G3): the hex-carried `.births()` directive set.
|
|
1328
|
-
const birthGateHex = p.attrs.get("nomos:birthGateHex");
|
|
1329
|
-
if (typeof birthGateHex === "string") {
|
|
1330
|
-
birthGateByDomain.set(domain, JSON.parse(unhexUtf8(birthGateHex)) as UsdLayer["nomosBirthGate"]);
|
|
1331
|
-
}
|
|
1332
|
-
// THE BIRTH-RECORD GATE (metered birth, A1): the hex-carried record declaration.
|
|
1333
|
-
const birthRecordGateHex = p.attrs.get("nomos:birthRecordGateHex");
|
|
1334
|
-
if (typeof birthRecordGateHex === "string") {
|
|
1335
|
-
birthRecordGateByDomain.set(domain, JSON.parse(unhexUtf8(birthRecordGateHex)) as UsdLayer["nomosBirthRecordGate"]);
|
|
1336
|
-
}
|
|
1337
|
-
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the hex-carried parent-attested-birth era marker.
|
|
1338
|
-
const pabHex = p.attrs.get("nomos:parentAttestedBirthHex");
|
|
1339
|
-
if (typeof pabHex === "string") {
|
|
1340
|
-
parentAttestedBirthByDomain.set(domain, JSON.parse(unhexUtf8(pabHex)) as UsdLayer["nomosParentAttestedBirth"]);
|
|
1341
|
-
}
|
|
1342
|
-
// THE KEYLESS VERIFIED-FIRST-DEVICE GATE: the hex-carried era marker.
|
|
1343
|
-
const vfeHex = p.attrs.get("nomos:verifiedFirstEnrollHex");
|
|
1344
|
-
if (typeof vfeHex === "string") {
|
|
1345
|
-
verifiedFirstEnrollByDomain.set(domain, JSON.parse(unhexUtf8(vfeHex)) as UsdLayer["nomosVerifiedFirstEnroll"]);
|
|
1346
|
-
}
|
|
1347
|
-
const ecgHex = p.attrs.get("nomos:eraCertGateHex");
|
|
1348
|
-
if (typeof ecgHex === "string") {
|
|
1349
|
-
eraCertGateByDomain.set(domain, JSON.parse(unhexUtf8(ecgHex)) as UsdLayer["nomosEraCertGate"]);
|
|
1350
|
-
}
|
|
1351
|
-
// FIRST-CLASS VALUE OBJECTS: the hex-carried descriptor set (descriptive, not a gate).
|
|
1352
|
-
const voHex = p.attrs.get("nomos:valueObjectsHex");
|
|
1353
|
-
if (typeof voHex === "string") {
|
|
1354
|
-
valueObjectsByDomain.set(domain, JSON.parse(unhexUtf8(voHex)) as UsdLayer["nomosValueObjects"]);
|
|
1355
|
-
}
|
|
1356
|
-
const frHex = p.attrs.get("nomos:foreignRefsHex");
|
|
1357
|
-
if (typeof frHex === "string") {
|
|
1358
|
-
foreignRefsByDomain.set(domain, JSON.parse(unhexUtf8(frHex)) as UsdLayer["nomosForeignRefs"]);
|
|
1359
|
-
}
|
|
1360
|
-
// DOMAIN CONTRACTS: the hex-carried public DTO descriptor set (descriptive, not a gate).
|
|
1361
|
-
const ctHex = p.attrs.get("nomos:contractsHex");
|
|
1362
|
-
if (typeof ctHex === "string") {
|
|
1363
|
-
contractsByDomain.set(domain, JSON.parse(unhexUtf8(ctHex)) as UsdLayer["nomosContracts"]);
|
|
1364
|
-
}
|
|
1365
|
-
const flowGateHex = p.attrs.get("nomos:flowGateHex");
|
|
1366
|
-
if (typeof flowGateHex === "string") {
|
|
1367
|
-
flowGateByDomain.set(domain, JSON.parse(unhexUtf8(flowGateHex)) as UsdLayer["nomosFlowGate"]);
|
|
1368
|
-
}
|
|
1546
|
+
// Structured law is native USD; no embedded serialization has an alternate vote.
|
|
1547
|
+
const native = (name: string): unknown | undefined => nativeValueAt(prims, p.path, name);
|
|
1548
|
+
const set = <T>(name: string, target: Map<string, T>): void => {
|
|
1549
|
+
const value = native(name);
|
|
1550
|
+
if (value !== undefined) target.set(domain, value as T);
|
|
1551
|
+
};
|
|
1552
|
+
set("identityManifest", identityManifestByDomain); set("readProjection", readProjectionByDomain);
|
|
1553
|
+
set("stableIds", stableIdsByDomain); set("interface", interfaceByDomain); set("readGate", readGateByDomain);
|
|
1554
|
+
set("invariantGate", invariantGateByDomain); set("readVisibility", readVisibilityByDomain);
|
|
1555
|
+
set("encryption", encryptionByDomain); set("capabilities", capabilitiesByDomain);
|
|
1556
|
+
set("attributedGate", attributedGateByDomain); set("relations", relationsByDomain);
|
|
1557
|
+
set("relationGate", relationGateByDomain); set("relationNamespace", relationNamespaceByDomain);
|
|
1558
|
+
set("readProjectionNamespace", readProjectionNamespaceByDomain); set("attestationGate", attestationGateByDomain);
|
|
1559
|
+
set("delegationGate", delegationGateByDomain); set("attestedReadGate", attestedReadGateByDomain);
|
|
1560
|
+
set("birthGate", birthGateByDomain); set("birthRecordGate", birthRecordGateByDomain);
|
|
1561
|
+
set("parentAttestedBirth", parentAttestedBirthByDomain); set("verifiedFirstEnroll", verifiedFirstEnrollByDomain);
|
|
1562
|
+
set("eraCertGate", eraCertGateByDomain); set("valueObjects", valueObjectsByDomain);
|
|
1563
|
+
set("foreignRefs", foreignRefsByDomain); set("contracts", contractsByDomain); set("flowGate", flowGateByDomain);
|
|
1369
1564
|
}
|
|
1370
1565
|
}
|
|
1371
1566
|
|
|
@@ -1388,6 +1583,9 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1388
1583
|
for (const p of prims) {
|
|
1389
1584
|
const kind = p.attrs.get("nomos:kind");
|
|
1390
1585
|
if (typeof kind !== "string" || kind === "domain") continue;
|
|
1586
|
+
// A self-contained runtime package also carries the obscure framework-owned plan index at a
|
|
1587
|
+
// separate root. It is USD, but it is not domain law and must not be projected into the IR.
|
|
1588
|
+
if (!p.path.startsWith("/Nomos/")) continue;
|
|
1391
1589
|
const scopePath = p.path.slice(0, p.path.lastIndexOf("/"));
|
|
1392
1590
|
const domain = domainByScope.get(scopePath);
|
|
1393
1591
|
if (domain === undefined) {
|
|
@@ -1401,21 +1599,18 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1401
1599
|
const schema: WireSchema = {};
|
|
1402
1600
|
for (const [name, value] of p.attrs) {
|
|
1403
1601
|
if (name.startsWith("nomos:driver:") && typeof value === "string") {
|
|
1404
|
-
schema[name.slice("nomos:driver:".length)] = parseDriverToken(value);
|
|
1602
|
+
schema[parseFieldToken(name.slice("nomos:driver:".length))] = parseDriverToken(value);
|
|
1405
1603
|
}
|
|
1406
1604
|
}
|
|
1407
1605
|
b.prims.push({ kind: "aggregate", path: irPath, type: id, schema: sortedSchema(schema) });
|
|
1408
1606
|
} else if (kind === "directive") {
|
|
1409
1607
|
const reads = p.attrs.has("nomos:reads") ? attrStringArray(p, "nomos:reads") : undefined;
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
const payloadJsonSchema = p.attrs.has("nomos:payloadJsonSchemaHex")
|
|
1417
|
-
? (JSON.parse(unhexUtf8(attrString(p, "nomos:payloadJsonSchemaHex"))) as unknown)
|
|
1418
|
-
: undefined;
|
|
1608
|
+
const nativeEmits = nativeValueAt(prims, p.path, "emits");
|
|
1609
|
+
const nativeSecondaryOutputs = nativeValueAt(prims, p.path, "secondaryOutputs");
|
|
1610
|
+
const nativePayloadJsonSchema = nativeValueAt(prims, p.path, "payloadJsonSchema");
|
|
1611
|
+
const emits = nativeEmits as Record<string, { max?: number }> | undefined;
|
|
1612
|
+
const secondaryOutputs = nativeSecondaryOutputs as { target: string; marker: WireRefMode }[] | undefined;
|
|
1613
|
+
const payloadJsonSchema = nativePayloadJsonSchema;
|
|
1419
1614
|
b.prims.push({
|
|
1420
1615
|
kind: "directive",
|
|
1421
1616
|
path: irPath,
|
|
@@ -1434,18 +1629,20 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1434
1629
|
returns: attrString(p, "nomos:returns"),
|
|
1435
1630
|
});
|
|
1436
1631
|
} else if (kind === "derived") {
|
|
1632
|
+
const nativeReturns = nativeValueAt(prims, p.path, "returns");
|
|
1437
1633
|
b.deriveds.push({
|
|
1438
1634
|
id,
|
|
1439
1635
|
of: attrString(p, "nomos:of"),
|
|
1440
|
-
returns:
|
|
1636
|
+
returns: nativeReturns as UsdDerived["returns"],
|
|
1441
1637
|
});
|
|
1442
1638
|
} else if (kind === "combined") {
|
|
1639
|
+
const nativeReturns = nativeValueAt(prims, p.path, "returns");
|
|
1443
1640
|
b.combineds.push({
|
|
1444
1641
|
id,
|
|
1445
1642
|
of: attrString(p, "nomos:of"),
|
|
1446
1643
|
refField: attrString(p, "nomos:refField"),
|
|
1447
1644
|
reads: attrString(p, "nomos:readsFrom"),
|
|
1448
|
-
returns:
|
|
1645
|
+
returns: nativeReturns as UsdCombined["returns"],
|
|
1449
1646
|
});
|
|
1450
1647
|
} else {
|
|
1451
1648
|
throw new Error(`usd-layers: prim ${p.path} has unknown nomos:kind '${kind}'`);
|
|
@@ -1462,8 +1659,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1462
1659
|
const sids = stableIdsByDomain.get(domain);
|
|
1463
1660
|
const iface = interfaceByDomain.get(domain);
|
|
1464
1661
|
const rg = readGateByDomain.get(domain);
|
|
1662
|
+
const invariantGate = invariantGateByDomain.get(domain);
|
|
1465
1663
|
const rv = readVisibilityByDomain.get(domain);
|
|
1466
1664
|
const enc = encryptionByDomain.get(domain);
|
|
1665
|
+
const capabilities = capabilitiesByDomain.get(domain);
|
|
1467
1666
|
const attrGate = attributedGateByDomain.get(domain);
|
|
1468
1667
|
const rel = relationsByDomain.get(domain);
|
|
1469
1668
|
const relGate = relationGateByDomain.get(domain);
|
|
@@ -1492,8 +1691,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1492
1691
|
...(sids !== undefined ? { nomosStableIds: sids } : {}),
|
|
1493
1692
|
...(iface !== undefined ? { nomosInterface: iface } : {}),
|
|
1494
1693
|
...(rg !== undefined ? { nomosReadGate: rg } : {}),
|
|
1694
|
+
...(invariantGate !== undefined ? { nomosInvariantGate: invariantGate } : {}),
|
|
1495
1695
|
...(rv !== undefined ? { nomosReadVisibility: rv } : {}),
|
|
1496
1696
|
...(enc !== undefined ? { nomosEncryption: enc } : {}),
|
|
1697
|
+
...(capabilities !== undefined ? { nomosCapabilities: capabilities } : {}),
|
|
1497
1698
|
...(attrGate !== undefined ? { nomosAttributedGate: attrGate } : {}),
|
|
1498
1699
|
...(rel !== undefined ? { nomosRelations: rel } : {}),
|
|
1499
1700
|
...(relNs !== undefined ? { nomosRelationNamespace: relNs } : {}),
|