@githolon/dsl 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dart/lib/src/errors.dart +105 -1
- package/dart/lib/src/webview_bridge.dart +6 -4
- package/dart/test/webview_bridge_test.dart +65 -0
- package/package.json +1 -1
- package/src/aggregate.ts +12 -1
- package/src/authoring.ts +4 -1
- package/src/birth_meter.ts +300 -0
- package/src/birth_meter_ensure.ts +67 -0
- package/src/build_package.ts +59 -0
- package/src/codegen_dart.ts +347 -3
- package/src/codegen_ts.ts +74 -1
- package/src/compile_engine.ts +13 -2
- package/src/compile_package_main.ts +42 -2
- package/src/directive.ts +10 -1
- package/src/engine_entry.ts +56 -9
- package/src/fields.ts +84 -0
- package/src/framework/birth_meter.ts +125 -0
- package/src/framework/impure_capability.ts +5 -2
- package/src/framework/platform_admin.ts +4 -1
- package/src/framework/relations.ts +19 -0
- package/src/framework/workspaces.ts +42 -1
- package/src/index.ts +53 -0
- package/src/read.ts +22 -0
- package/src/read_projection_composition.ts +117 -0
- package/src/read_projection_manifest.ts +94 -0
- package/src/read_projection_namespace.ts +152 -0
- package/src/usd.ts +141 -10
- package/src/usd_layers.ts +82 -0
- package/src/value_object.ts +456 -0
- package/src/wire_encode.ts +34 -3
package/src/usd.ts
CHANGED
|
@@ -45,6 +45,14 @@ import { domainInterface } from "./shape_compat.js";
|
|
|
45
45
|
import { relationType, user, key, anyUser, qualifiedTypeId, qualifyRelationType } from "./relation_schema.js";
|
|
46
46
|
import { harvestPrincipalFlows } from "./wire_encode.js";
|
|
47
47
|
import { composeSharedRelationTypes } from "./relation_composition.js";
|
|
48
|
+
import { collectValueObjectUsages } from "./value_object.js";
|
|
49
|
+
import {
|
|
50
|
+
namespacedAggregateIds,
|
|
51
|
+
qualifyReadProjectionType,
|
|
52
|
+
READ_PROJECTION_DELIMITER,
|
|
53
|
+
} from "./read_projection_namespace.js";
|
|
54
|
+
import { qualifyManifestAggregateLabels } from "./read_projection_manifest.js";
|
|
55
|
+
import { guardSharedReadProjectionKeys } from "./read_projection_composition.js";
|
|
48
56
|
|
|
49
57
|
/** The referential marker of a directive prim (kernel `RefMode` wire string). */
|
|
50
58
|
export type RefMode = WireRefMode;
|
|
@@ -276,6 +284,22 @@ export interface UsdLayer {
|
|
|
276
284
|
readonly delimiter: string;
|
|
277
285
|
readonly types: readonly string[];
|
|
278
286
|
};
|
|
287
|
+
/**
|
|
288
|
+
* THE READ-PROJECTION NAMESPACE ERA KEY (read-projection namespacing — the read-layer mirror of
|
|
289
|
+
* `nomosRelationNamespace`). Present IFF this layer qualified ≥1 (non-global) AGGREGATE TYPE by its
|
|
290
|
+
* owning domain key (`Entry` → `guestbook__Entry`), so two domains / two installs that each declare
|
|
291
|
+
* the same bare aggregate name no longer collide at the kernel's installed-read-manifest accumulator
|
|
292
|
+
* ("divergent read projection declaration for key …" — the fail-closed union stays the trust
|
|
293
|
+
* boundary; a qualified key is simply a NEW name to it). `types` is the SORTED set of qualified type
|
|
294
|
+
* names this layer produced; `delimiter` is the same ratified colon-free `"__"`. OMITTED for a layer
|
|
295
|
+
* whose aggregates are all GLOBAL (the explicit framework allowlist / `{global:true}`) — the marker
|
|
296
|
+
* is a witness; the qualification itself is baked into the layer's keys.
|
|
297
|
+
*/
|
|
298
|
+
readonly nomosReadProjectionNamespace?: {
|
|
299
|
+
readonly v: 1;
|
|
300
|
+
readonly delimiter: string;
|
|
301
|
+
readonly types: readonly string[];
|
|
302
|
+
};
|
|
279
303
|
/**
|
|
280
304
|
* THE CROSS-CUSTODY ATTESTATION GATE (home_holons §3/§5). Present IFF ≥1 directive declares
|
|
281
305
|
* `.attestationAuthorized()`: the SORTED set of their ids. A directive named here is authorized by a
|
|
@@ -339,6 +363,25 @@ export interface UsdLayer {
|
|
|
339
363
|
/** The directive ids declared `.births()` — SORTED. */
|
|
340
364
|
readonly directives: string[];
|
|
341
365
|
};
|
|
366
|
+
/**
|
|
367
|
+
* THE BIRTH-RECORD GATE (metered birth, slice A1 — `architecture/metered_birth_and_attested_reads.md`).
|
|
368
|
+
* Present IFF the layer declares ≥1 `.births()` directive AND was compiled under the metered-birth
|
|
369
|
+
* compiler: the law's EXPLICIT declaration that every birth it performs ALSO writes a structural
|
|
370
|
+
* `NomosBirthRecord` event (deterministic id `<prefix><child>`) in the SAME intent as the birth
|
|
371
|
+
* effect — the `.births()` lane appends it automatically, so the declaration is self-fulfilling for
|
|
372
|
+
* honestly-compiled law and the kernel arm only ever bites a forged/hand-built lump. The kernel's
|
|
373
|
+
* `verify_births_witness` reads THIS declaration (the SAME way it reads `nomosBirthGate` — the
|
|
374
|
+
* bundle declares, the kernel honors; NO kernel version-if) and refuses a new-era birth whose
|
|
375
|
+
* re-derived events carry no record (`birth-record-missing`). OMITTED for pre-feature law — the
|
|
376
|
+
* era rule: every chain sealed before this key replays byte-identical, births exactly as today.
|
|
377
|
+
*/
|
|
378
|
+
readonly nomosBirthRecordGate?: {
|
|
379
|
+
readonly v: 1;
|
|
380
|
+
/** The record aggregate TYPE name (the framework's `NomosBirthRecord`). */
|
|
381
|
+
readonly aggregate: string;
|
|
382
|
+
/** The deterministic instance-id prefix — the record for child `w` lives at `<prefix><w>`. */
|
|
383
|
+
readonly prefix: string;
|
|
384
|
+
};
|
|
342
385
|
/**
|
|
343
386
|
* THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain). Present IFF the law declares
|
|
344
387
|
* `requireParentAttestedBirth`: a warranted workspace running this law MUST travel with a SIGNED cert
|
|
@@ -464,6 +507,27 @@ export interface UsdLayer {
|
|
|
464
507
|
readonly reason: string | null;
|
|
465
508
|
}[];
|
|
466
509
|
};
|
|
510
|
+
/**
|
|
511
|
+
* FIRST-CLASS VALUE OBJECTS (`value_object.ts`) — the canonical descriptors of every
|
|
512
|
+
* named value object this layer's aggregates carry (deduped, SORTED by name, nested
|
|
513
|
+
* VOs included), so tooling/visualizers SEE the structure a VO field stores. PURELY
|
|
514
|
+
* DESCRIPTIVE: the kernel reads no gate off it (a VO field folds exactly like a
|
|
515
|
+
* `t.jsonObject()` leaf — one wholesale-replace json value). OMITTED for VO-free law
|
|
516
|
+
* (byte-identical hash — the era rule); declaring a VO moves the PACKAGE hash only
|
|
517
|
+
* (the canonical manifest/domainHash is untouched — the VO is not wire law).
|
|
518
|
+
*/
|
|
519
|
+
readonly nomosValueObjects?: {
|
|
520
|
+
readonly v: 1;
|
|
521
|
+
readonly valueObjects: readonly import("./fields.js").ValueObjectDescriptor[];
|
|
522
|
+
/**
|
|
523
|
+
* THE VO↔FIELD ATTACHMENT (additive, hash-inert): which aggregate FIELD carries
|
|
524
|
+
* which VO — `{aggregate, field, vo}` sorted by aggregate then field. Rides THIS
|
|
525
|
+
* key (never the aggregate prim's wire schema) so the canonical manifest/domainHash
|
|
526
|
+
* of a VO field stays byte-equal to a plain `t.jsonObject()` field. Optional for
|
|
527
|
+
* era tolerance: a 0.33.0-compiled package's key parses unchanged without it.
|
|
528
|
+
*/
|
|
529
|
+
readonly usages?: readonly import("./value_object.js").ValueObjectUsage[];
|
|
530
|
+
};
|
|
467
531
|
}
|
|
468
532
|
|
|
469
533
|
/** The reserved variant name declaring a set's DEFAULT (USD has a default variant). */
|
|
@@ -486,13 +550,15 @@ function sortPrims(prims: UsdPrim[]): UsdPrim[] {
|
|
|
486
550
|
* encoded schema; directive prims map `requiresCapability` → `requires: [cap]`, leave
|
|
487
551
|
* `scope`/`payloadRef` `undefined` (no static scope today; payload is stage 2).
|
|
488
552
|
*/
|
|
489
|
-
function encodeModuleToPrims(layerPath: string,
|
|
490
|
-
const manifest = domainManifest(mod);
|
|
553
|
+
function encodeModuleToPrims(layerPath: string, manifest: CanonicalManifest): UsdPrim[] {
|
|
491
554
|
const prims: UsdPrim[] = [];
|
|
492
555
|
|
|
493
556
|
for (const agg of manifest.aggregates) {
|
|
494
557
|
prims.push({
|
|
495
558
|
kind: "aggregate",
|
|
559
|
+
// The IR prim path carries the (possibly dotted-qualified) type verbatim; the `.usda`
|
|
560
|
+
// TEXT lane sanitizes the prim NAME (`usd_layers.ts::primName`) and round-trips the raw
|
|
561
|
+
// id via `nomos:id`, so real-USD identifier rules are already satisfied downstream.
|
|
496
562
|
path: `${layerPath}/${agg.id}`,
|
|
497
563
|
type: agg.id,
|
|
498
564
|
schema: agg.schema,
|
|
@@ -525,6 +591,7 @@ function encodeModuleToPrims(layerPath: string, mod: DomainModule): UsdPrim[] {
|
|
|
525
591
|
|
|
526
592
|
function aggregateFieldKindsForModule(
|
|
527
593
|
mod: DomainModule,
|
|
594
|
+
qa: (type: string) => string = (n) => n,
|
|
528
595
|
): Record<string, Record<string, UsdFieldKindDescriptor>> {
|
|
529
596
|
const out: Record<string, Record<string, UsdFieldKindDescriptor>> = {};
|
|
530
597
|
for (const agg of [...(mod.aggregates as AggregateHandle[])].sort((a, b) =>
|
|
@@ -538,7 +605,7 @@ function aggregateFieldKindsForModule(
|
|
|
538
605
|
...(field.mapValueKind !== undefined ? { mapValueKind: field.mapValueKind } : {}),
|
|
539
606
|
};
|
|
540
607
|
}
|
|
541
|
-
out[agg.id] = fields;
|
|
608
|
+
out[qa(agg.id)] = fields;
|
|
542
609
|
}
|
|
543
610
|
return out;
|
|
544
611
|
}
|
|
@@ -565,8 +632,12 @@ function readKeyFieldKind(
|
|
|
565
632
|
return descriptor.kind;
|
|
566
633
|
}
|
|
567
634
|
|
|
568
|
-
function readProjectionForModule(
|
|
569
|
-
|
|
635
|
+
function readProjectionForModule(
|
|
636
|
+
mod: DomainModule,
|
|
637
|
+
manifest: CanonicalManifest,
|
|
638
|
+
qa: (type: string) => string = (n) => n,
|
|
639
|
+
): UsdReadProjection {
|
|
640
|
+
const aggregateFieldKinds = aggregateFieldKindsForModule(mod, qa);
|
|
570
641
|
return {
|
|
571
642
|
aggregateFieldKinds,
|
|
572
643
|
queries: (manifest.queries ?? []).map((q) => ({
|
|
@@ -730,13 +801,35 @@ export function emitUsd(
|
|
|
730
801
|
// Carry the domain's declared queries straight from the canonical manifest,
|
|
731
802
|
// which already applies omit-when-empty: `queries` is present here ONLY when the
|
|
732
803
|
// module declared them, so a query-free layer is byte-identical to before.
|
|
733
|
-
const
|
|
804
|
+
const bareManifest = domainManifest(l.module);
|
|
805
|
+
// READ-PROJECTION NAMESPACING: every NON-GLOBAL aggregate's type name is qualified by the
|
|
806
|
+
// owning domain key — on the prims, the identity manifest, the stable-ids labels (bare name
|
|
807
|
+
// appended to `was` — the rename lineage), the read projection, and every `of`/`returns`
|
|
808
|
+
// reference — so two domains / two installs that each declare the same bare name never share
|
|
809
|
+
// an installed-read-manifest accumulator key. GLOBAL (framework allowlist / `{global:true}`)
|
|
810
|
+
// aggregates stay bare (the kernel addresses them by bare name). The engine stamps the SAME
|
|
811
|
+
// qualified name at plan runtime (`wire_encode.ts` via the dispatch context), so rows and
|
|
812
|
+
// schema keys agree by construction. `module.domain ?? module.name` — the SAME qualifier key
|
|
813
|
+
// relation namespacing uses (=== `intent.domain` at plan runtime).
|
|
814
|
+
const rpDomainKey = l.module.domain ?? l.module.name;
|
|
815
|
+
const namespacedAggs = namespacedAggregateIds(
|
|
816
|
+
l.module.aggregates as unknown as readonly { id: string; global?: boolean }[],
|
|
817
|
+
);
|
|
818
|
+
const qaType = (n: string): string =>
|
|
819
|
+
namespacedAggs.has(n) ? qualifyReadProjectionType(rpDomainKey, n) : n;
|
|
820
|
+
const manifest = qualifyManifestAggregateLabels(bareManifest, rpDomainKey, namespacedAggs);
|
|
821
|
+
// THE READ-PROJECTION NAMESPACE ERA KEY: present iff ≥1 aggregate was qualified (the witness
|
|
822
|
+
// marker — mirrors `nomosRelationNamespace`; the qualification itself is baked into the keys).
|
|
823
|
+
const nomosReadProjectionNamespace =
|
|
824
|
+
namespacedAggs.size > 0
|
|
825
|
+
? { v: 1 as const, delimiter: READ_PROJECTION_DELIMITER, types: [...namespacedAggs].map(qaType).sort() }
|
|
826
|
+
: undefined;
|
|
734
827
|
// THE INVARIANT-GATE DECLARATION (#41): presence-only, derived from the SAME module
|
|
735
828
|
// the lump's executable bodies come from — sorted, omit-when-empty (hash-stable for
|
|
736
829
|
// invariant-free law; see the `UsdLayer.nomosInvariantGate` banner for the era rule).
|
|
737
830
|
const invariantAggregates = l.module.aggregates
|
|
738
831
|
.filter((a) => a.hasInvariant === true)
|
|
739
|
-
.map((a) => a.id)
|
|
832
|
+
.map((a) => qaType(a.id))
|
|
740
833
|
.sort();
|
|
741
834
|
const wsInvariants = [...(l.module.workspaceInvariants ?? [])]
|
|
742
835
|
.map((w) => ({ id: w.id, on: w.on }))
|
|
@@ -869,6 +962,17 @@ export function emitUsd(
|
|
|
869
962
|
.sort();
|
|
870
963
|
const nomosBirthGate =
|
|
871
964
|
birthDirectives.length > 0 ? { v: 1 as const, directives: birthDirectives } : undefined;
|
|
965
|
+
// THE BIRTH-RECORD GATE (metered birth, slice A1 — the framework's new era key). Present IFF
|
|
966
|
+
// this layer declares ≥1 `.births()` directive AND was compiled under this compiler: the law's
|
|
967
|
+
// EXPLICIT declaration that every birth it performs ALSO writes a structural NomosBirthRecord
|
|
968
|
+
// in the SAME intent (the `.births()` lane appends it automatically). The kernel's
|
|
969
|
+
// `verify_births_witness` reads THIS key (never a version-if): a carried birth whose re-derived
|
|
970
|
+
// events contain no `<prefix><child>` record event is a typed refusal (`birth-record-missing`).
|
|
971
|
+
// Omit-when-empty (the era rule): every pre-feature bundle lacks the key and births as today.
|
|
972
|
+
const nomosBirthRecordGate =
|
|
973
|
+
birthDirectives.length > 0
|
|
974
|
+
? { v: 1 as const, aggregate: "NomosBirthRecord", prefix: "nomos-birth-record:" }
|
|
975
|
+
: undefined;
|
|
872
976
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the home/child law declared it requires a parent-attested-birth
|
|
873
977
|
// cert chain (the `requireParentAttestedBirth` sentinel). Emit the era key with the LAW-DECLARED owner +
|
|
874
978
|
// cert locators (so the kernel reads them, names neither); a bare marker (no locators) falls back to the
|
|
@@ -995,6 +1099,22 @@ export function emitUsd(
|
|
|
995
1099
|
// conversion's declared flow {directiveId, sink, facet, reason}. Hashed into nomosFlowGate +
|
|
996
1100
|
// witnessed fail-closed at runtime (Slice B). Omit-when-empty (a principal-free domain is
|
|
997
1101
|
// byte-identical). SORTED for determinism.
|
|
1102
|
+
// FIRST-CLASS VALUE OBJECTS: the module's collected descriptors (composeDomainModule
|
|
1103
|
+
// dedupes + sorts them). Omit-when-empty (era rule — VO-free law is byte-identical).
|
|
1104
|
+
const moduleValueObjects = (l.module as { valueObjects?: readonly import("./fields.js").ValueObjectDescriptor[] }).valueObjects;
|
|
1105
|
+
// THE VO↔FIELD ATTACHMENT: the usage pointers ride the SAME additive key (see
|
|
1106
|
+
// the UsdLayer type) — computed from the live Field metas, sorted, omit-when-empty.
|
|
1107
|
+
const voUsages = collectValueObjectUsages(
|
|
1108
|
+
l.module.aggregates as unknown as readonly { id: string; fields: Record<string, import("./fields.js").Field> }[],
|
|
1109
|
+
);
|
|
1110
|
+
const nomosValueObjects =
|
|
1111
|
+
moduleValueObjects !== undefined && moduleValueObjects.length > 0
|
|
1112
|
+
? {
|
|
1113
|
+
v: 1 as const,
|
|
1114
|
+
valueObjects: [...moduleValueObjects],
|
|
1115
|
+
...(voUsages.length > 0 ? { usages: voUsages } : {}),
|
|
1116
|
+
}
|
|
1117
|
+
: undefined;
|
|
998
1118
|
const flowEndorse = [...l.module.directives]
|
|
999
1119
|
.flatMap((d) => harvestPrincipalFlows(d as never).map((f) => ({ directiveId: d.id, sink: f.sink, facet: f.facet, reason: f.reason })))
|
|
1000
1120
|
.sort((a, b) =>
|
|
@@ -1004,9 +1124,9 @@ export function emitUsd(
|
|
|
1004
1124
|
);
|
|
1005
1125
|
return {
|
|
1006
1126
|
path: l.path,
|
|
1007
|
-
prims: encodeModuleToPrims(l.path,
|
|
1127
|
+
prims: encodeModuleToPrims(l.path, manifest),
|
|
1008
1128
|
nomosIdentityManifest: manifest,
|
|
1009
|
-
nomosReadProjection: readProjectionForModule(l.module, manifest),
|
|
1129
|
+
nomosReadProjection: readProjectionForModule(l.module, manifest, qaType),
|
|
1010
1130
|
...(manifest.queries !== undefined ? { queries: manifest.queries } : {}),
|
|
1011
1131
|
...(manifest.deriveds !== undefined ? { deriveds: manifest.deriveds } : {}),
|
|
1012
1132
|
...(manifest.combineds !== undefined ? { combineds: manifest.combineds } : {}),
|
|
@@ -1014,7 +1134,9 @@ export function emitUsd(
|
|
|
1014
1134
|
...(manifest.stableIds !== undefined ? { nomosStableIds: manifest.stableIds } : {}),
|
|
1015
1135
|
// THE STRUCTURAL INTERFACE (#72): the on-ledger OFFER — a client negotiates shape-
|
|
1016
1136
|
// compatibility against this, reactively, straight from the folded `policy:{hash}.bundle`.
|
|
1017
|
-
|
|
1137
|
+
// The client-facing structural offer stays BARE-labelled (labels are cosmetic there;
|
|
1138
|
+
// shape-compat negotiates by sid) — pass the pre-namespacing stable-ids.
|
|
1139
|
+
nomosInterface: domainInterface(l.module, bareManifest.stableIds),
|
|
1018
1140
|
...(Object.keys(readGateDirectives).length > 0
|
|
1019
1141
|
? {
|
|
1020
1142
|
nomosReadGate: {
|
|
@@ -1035,11 +1157,14 @@ export function emitUsd(
|
|
|
1035
1157
|
: {}),
|
|
1036
1158
|
...(nomosRelations !== undefined ? { nomosRelations } : {}),
|
|
1037
1159
|
...(nomosRelationNamespace !== undefined ? { nomosRelationNamespace } : {}),
|
|
1160
|
+
...(nomosReadProjectionNamespace !== undefined ? { nomosReadProjectionNamespace } : {}),
|
|
1038
1161
|
...(nomosRelationGate !== undefined ? { nomosRelationGate } : {}),
|
|
1039
1162
|
...(nomosAttestationGate !== undefined ? { nomosAttestationGate } : {}),
|
|
1040
1163
|
...(nomosBirthGate !== undefined ? { nomosBirthGate } : {}),
|
|
1164
|
+
...(nomosBirthRecordGate !== undefined ? { nomosBirthRecordGate } : {}),
|
|
1041
1165
|
...(nomosParentAttestedBirth !== undefined ? { nomosParentAttestedBirth } : {}),
|
|
1042
1166
|
...(nomosVerifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll } : {}),
|
|
1167
|
+
...(nomosValueObjects !== undefined ? { nomosValueObjects } : {}),
|
|
1043
1168
|
...(Object.keys(visAggregates).length > 0
|
|
1044
1169
|
? { nomosReadVisibility: { v: 1 as const, aggregates: visAggregates } }
|
|
1045
1170
|
: {}),
|
|
@@ -1065,6 +1190,12 @@ export function emitUsd(
|
|
|
1065
1190
|
// package's per-domain layers: UNION every domain's relations on a shared bare-id type (so nothing
|
|
1066
1191
|
// is silently dropped) and REFUSE on a divergent shared rewrite (the scalpel). Non-shared /
|
|
1067
1192
|
// auto-qualified types are untouched → a package with no shared global type is byte-identical.
|
|
1193
|
+
// THE READ-PROJECTION DIVERGENCE GUARD (the scalpel, extended to the read layer): for keys that
|
|
1194
|
+
// remain SHARED across this package's domains (GLOBAL framework aggregates / same-named declared
|
|
1195
|
+
// reads), identical declarations dedupe at the kernel accumulator; a DIVERGENT shared key would
|
|
1196
|
+
// wedge the workspace at serve time — refuse it AT COMPILE instead, naming the key and each
|
|
1197
|
+
// domain's shape. Validation only (never transforms), so a conflict-free package is untouched.
|
|
1198
|
+
guardSharedReadProjectionKeys(out);
|
|
1068
1199
|
return { layers: composeSharedRelationTypes(out) };
|
|
1069
1200
|
}
|
|
1070
1201
|
|
package/src/usd_layers.ts
CHANGED
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
* the runtime barrel.
|
|
59
59
|
*/
|
|
60
60
|
import type { AggregateHandle } from "./aggregate.js";
|
|
61
|
+
import { READ_PROJECTION_DELIMITER } from "./read_projection_namespace.js";
|
|
61
62
|
import type { Field } from "./fields.js";
|
|
62
63
|
import type { DomainModule } from "./codegen_dart.js";
|
|
63
64
|
import type { WireDriver, WireRefMode, WireSchema } from "./wire.js";
|
|
@@ -175,6 +176,9 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
175
176
|
...(layer.nomosRelationNamespace !== undefined
|
|
176
177
|
? [`${indent}custom string nomos:relationNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationNamespace)))}`]
|
|
177
178
|
: []),
|
|
179
|
+
...(layer.nomosReadProjectionNamespace !== undefined
|
|
180
|
+
? [`${indent}custom string nomos:readProjectionNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadProjectionNamespace)))}`]
|
|
181
|
+
: []),
|
|
178
182
|
...(layer.nomosRelationGate !== undefined
|
|
179
183
|
? [`${indent}custom string nomos:relationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationGate)))}`]
|
|
180
184
|
: []),
|
|
@@ -184,12 +188,18 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
184
188
|
...(layer.nomosBirthGate !== undefined
|
|
185
189
|
? [`${indent}custom string nomos:birthGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthGate)))}`]
|
|
186
190
|
: []),
|
|
191
|
+
...(layer.nomosBirthRecordGate !== undefined
|
|
192
|
+
? [`${indent}custom string nomos:birthRecordGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthRecordGate)))}`]
|
|
193
|
+
: []),
|
|
187
194
|
...(layer.nomosParentAttestedBirth !== undefined
|
|
188
195
|
? [`${indent}custom string nomos:parentAttestedBirthHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosParentAttestedBirth)))}`]
|
|
189
196
|
: []),
|
|
190
197
|
...(layer.nomosVerifiedFirstEnroll !== undefined
|
|
191
198
|
? [`${indent}custom string nomos:verifiedFirstEnrollHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosVerifiedFirstEnroll)))}`]
|
|
192
199
|
: []),
|
|
200
|
+
...(layer.nomosValueObjects !== undefined
|
|
201
|
+
? [`${indent}custom string nomos:valueObjectsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosValueObjects)))}`]
|
|
202
|
+
: []),
|
|
193
203
|
];
|
|
194
204
|
}
|
|
195
205
|
|
|
@@ -776,6 +786,18 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
776
786
|
}
|
|
777
787
|
if (ns.size > 0) relationNamespace = { v: 1, delimiter: "__", types: [...ns].sort() };
|
|
778
788
|
}
|
|
789
|
+
// THE READ-PROJECTION NAMESPACE ERA KEY (read-projection namespacing): the SORTED union of every
|
|
790
|
+
// layer's qualified aggregate type names. Present iff ANY layer namespaced >=1 aggregate;
|
|
791
|
+
// omit-when-empty (byte-identical for global-only packages). Delimiter is the ratified "__".
|
|
792
|
+
let readProjectionNamespace: UsdLayer["nomosReadProjectionNamespace"];
|
|
793
|
+
{
|
|
794
|
+
const ns = new Set<string>();
|
|
795
|
+
for (const l of stack) {
|
|
796
|
+
if (l.nomosReadProjectionNamespace === undefined) continue;
|
|
797
|
+
for (const id of l.nomosReadProjectionNamespace.types) ns.add(id);
|
|
798
|
+
}
|
|
799
|
+
if (ns.size > 0) readProjectionNamespace = { v: 1, delimiter: READ_PROJECTION_DELIMITER, types: [...ns].sort() };
|
|
800
|
+
}
|
|
779
801
|
// THE RELATION GATE (P4): directiveId → {relation, object} merged later-wins, SORTED. Omit-when-empty.
|
|
780
802
|
let relationGate: UsdLayer["nomosRelationGate"];
|
|
781
803
|
{
|
|
@@ -817,6 +839,16 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
817
839
|
}
|
|
818
840
|
if (b.size > 0) birthGate = { v: 1, directives: [...b].sort() };
|
|
819
841
|
}
|
|
842
|
+
// THE BIRTH-RECORD GATE (metered birth, A1): present iff ANY layer declares it (once declared,
|
|
843
|
+
// declared). Carry the FIRST layer's aggregate/prefix verbatim (the framework constants — do NOT
|
|
844
|
+
// reconstruct). Omit-when-absent (era rule).
|
|
845
|
+
let birthRecordGate: UsdLayer["nomosBirthRecordGate"];
|
|
846
|
+
for (const l of stack) {
|
|
847
|
+
if (l.nomosBirthRecordGate !== undefined) {
|
|
848
|
+
birthRecordGate = l.nomosBirthRecordGate;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
820
852
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the era key is present iff ANY layer declares it (once required,
|
|
821
853
|
// required). Carry the FIRST layer's declared owner/cert locators verbatim (preserve them — do NOT
|
|
822
854
|
// reconstruct {v:1}, which would drop the locators the kernel reads). Omit-when-absent (era rule).
|
|
@@ -843,6 +875,29 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
843
875
|
verifiedFirstEnroll = { v: 1, directives: rec };
|
|
844
876
|
}
|
|
845
877
|
}
|
|
878
|
+
// FIRST-CLASS VALUE OBJECTS (+ the VO↔field usage pointers): descriptors merged
|
|
879
|
+
// later-wins per NAME, usages later-wins per (aggregate, field), both SORTED —
|
|
880
|
+
// so the layered fold stays byte-homomorphic with `emitUsd`'s composed emission.
|
|
881
|
+
// Omit-when-empty (the era rule); `usages` omitted when none (0.33.0 tolerance).
|
|
882
|
+
let valueObjects: UsdLayer["nomosValueObjects"];
|
|
883
|
+
{
|
|
884
|
+
type VoDesc = NonNullable<UsdLayer["nomosValueObjects"]>["valueObjects"][number];
|
|
885
|
+
type VoUse = NonNullable<NonNullable<UsdLayer["nomosValueObjects"]>["usages"]>[number];
|
|
886
|
+
const byName = new Map<string, VoDesc>();
|
|
887
|
+
const byField = new Map<string, VoUse>();
|
|
888
|
+
for (const l of stack) {
|
|
889
|
+
if (l.nomosValueObjects === undefined) continue;
|
|
890
|
+
for (const d of l.nomosValueObjects.valueObjects) byName.set(d.name, d);
|
|
891
|
+
for (const u of l.nomosValueObjects.usages ?? []) byField.set(`${u.aggregate}\u0000${u.field}`, u);
|
|
892
|
+
}
|
|
893
|
+
if (byName.size > 0) {
|
|
894
|
+
const descs = [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
895
|
+
const uses = [...byField.values()].sort((a, b) =>
|
|
896
|
+
a.aggregate !== b.aggregate ? (a.aggregate < b.aggregate ? -1 : 1) : a.field < b.field ? -1 : a.field > b.field ? 1 : 0,
|
|
897
|
+
);
|
|
898
|
+
valueObjects = { v: 1, valueObjects: descs, ...(uses.length > 0 ? { usages: uses } : {}) };
|
|
899
|
+
}
|
|
900
|
+
}
|
|
846
901
|
return {
|
|
847
902
|
path,
|
|
848
903
|
prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
|
|
@@ -858,11 +913,14 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
858
913
|
...(encryption !== undefined ? { nomosEncryption: encryption } : {}),
|
|
859
914
|
...(relations !== undefined ? { nomosRelations: relations } : {}),
|
|
860
915
|
...(relationNamespace !== undefined ? { nomosRelationNamespace: relationNamespace } : {}),
|
|
916
|
+
...(readProjectionNamespace !== undefined ? { nomosReadProjectionNamespace: readProjectionNamespace } : {}),
|
|
861
917
|
...(relationGate !== undefined ? { nomosRelationGate: relationGate } : {}),
|
|
862
918
|
...(attestationGate !== undefined ? { nomosAttestationGate: attestationGate } : {}),
|
|
863
919
|
...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
|
|
920
|
+
...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
|
|
864
921
|
...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
|
|
865
922
|
...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
|
|
923
|
+
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
|
866
924
|
};
|
|
867
925
|
});
|
|
868
926
|
|
|
@@ -993,10 +1051,13 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
993
1051
|
const relationsByDomain = new Map<string, UsdLayer["nomosRelations"]>();
|
|
994
1052
|
const relationGateByDomain = new Map<string, UsdLayer["nomosRelationGate"]>();
|
|
995
1053
|
const relationNamespaceByDomain = new Map<string, UsdLayer["nomosRelationNamespace"]>();
|
|
1054
|
+
const readProjectionNamespaceByDomain = new Map<string, UsdLayer["nomosReadProjectionNamespace"]>();
|
|
996
1055
|
const attestationGateByDomain = new Map<string, UsdLayer["nomosAttestationGate"]>();
|
|
997
1056
|
const birthGateByDomain = new Map<string, UsdLayer["nomosBirthGate"]>();
|
|
1057
|
+
const birthRecordGateByDomain = new Map<string, UsdLayer["nomosBirthRecordGate"]>();
|
|
998
1058
|
const parentAttestedBirthByDomain = new Map<string, UsdLayer["nomosParentAttestedBirth"]>();
|
|
999
1059
|
const verifiedFirstEnrollByDomain = new Map<string, UsdLayer["nomosVerifiedFirstEnroll"]>();
|
|
1060
|
+
const valueObjectsByDomain = new Map<string, UsdLayer["nomosValueObjects"]>();
|
|
1000
1061
|
for (const p of prims) {
|
|
1001
1062
|
if (p.attrs.get("nomos:kind") === "domain") {
|
|
1002
1063
|
const domain = attrString(p, "nomos:domain");
|
|
@@ -1048,6 +1109,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1048
1109
|
if (typeof relNsHex === "string") {
|
|
1049
1110
|
relationNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(relNsHex)) as UsdLayer["nomosRelationNamespace"]);
|
|
1050
1111
|
}
|
|
1112
|
+
// THE READ-PROJECTION-NAMESPACE ERA KEY: the hex-carried qualified-aggregate witness.
|
|
1113
|
+
const rpNsHex = p.attrs.get("nomos:readProjectionNamespaceHex");
|
|
1114
|
+
if (typeof rpNsHex === "string") {
|
|
1115
|
+
readProjectionNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(rpNsHex)) as UsdLayer["nomosReadProjectionNamespace"]);
|
|
1116
|
+
}
|
|
1051
1117
|
// THE ATTESTATION GATE (home_holons §3/§5): the hex-carried attestation-authorized directive set.
|
|
1052
1118
|
const attGateHex = p.attrs.get("nomos:attestationGateHex");
|
|
1053
1119
|
if (typeof attGateHex === "string") {
|
|
@@ -1058,6 +1124,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1058
1124
|
if (typeof birthGateHex === "string") {
|
|
1059
1125
|
birthGateByDomain.set(domain, JSON.parse(unhexUtf8(birthGateHex)) as UsdLayer["nomosBirthGate"]);
|
|
1060
1126
|
}
|
|
1127
|
+
// THE BIRTH-RECORD GATE (metered birth, A1): the hex-carried record declaration.
|
|
1128
|
+
const birthRecordGateHex = p.attrs.get("nomos:birthRecordGateHex");
|
|
1129
|
+
if (typeof birthRecordGateHex === "string") {
|
|
1130
|
+
birthRecordGateByDomain.set(domain, JSON.parse(unhexUtf8(birthRecordGateHex)) as UsdLayer["nomosBirthRecordGate"]);
|
|
1131
|
+
}
|
|
1061
1132
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the hex-carried parent-attested-birth era marker.
|
|
1062
1133
|
const pabHex = p.attrs.get("nomos:parentAttestedBirthHex");
|
|
1063
1134
|
if (typeof pabHex === "string") {
|
|
@@ -1068,6 +1139,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1068
1139
|
if (typeof vfeHex === "string") {
|
|
1069
1140
|
verifiedFirstEnrollByDomain.set(domain, JSON.parse(unhexUtf8(vfeHex)) as UsdLayer["nomosVerifiedFirstEnroll"]);
|
|
1070
1141
|
}
|
|
1142
|
+
// FIRST-CLASS VALUE OBJECTS: the hex-carried descriptor set (descriptive, not a gate).
|
|
1143
|
+
const voHex = p.attrs.get("nomos:valueObjectsHex");
|
|
1144
|
+
if (typeof voHex === "string") {
|
|
1145
|
+
valueObjectsByDomain.set(domain, JSON.parse(unhexUtf8(voHex)) as UsdLayer["nomosValueObjects"]);
|
|
1146
|
+
}
|
|
1071
1147
|
}
|
|
1072
1148
|
}
|
|
1073
1149
|
|
|
@@ -1165,10 +1241,13 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1165
1241
|
const rel = relationsByDomain.get(domain);
|
|
1166
1242
|
const relGate = relationGateByDomain.get(domain);
|
|
1167
1243
|
const relNs = relationNamespaceByDomain.get(domain);
|
|
1244
|
+
const rpNs = readProjectionNamespaceByDomain.get(domain);
|
|
1168
1245
|
const attGate = attestationGateByDomain.get(domain);
|
|
1169
1246
|
const birthGate = birthGateByDomain.get(domain);
|
|
1247
|
+
const birthRecordGate = birthRecordGateByDomain.get(domain);
|
|
1170
1248
|
const parentAttestedBirth = parentAttestedBirthByDomain.get(domain);
|
|
1171
1249
|
const verifiedFirstEnroll = verifiedFirstEnrollByDomain.get(domain);
|
|
1250
|
+
const valueObjects = valueObjectsByDomain.get(domain);
|
|
1172
1251
|
return {
|
|
1173
1252
|
path: `/Nomos/${domain}`,
|
|
1174
1253
|
prims: [...b.prims].sort(byPath),
|
|
@@ -1184,11 +1263,14 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1184
1263
|
...(enc !== undefined ? { nomosEncryption: enc } : {}),
|
|
1185
1264
|
...(rel !== undefined ? { nomosRelations: rel } : {}),
|
|
1186
1265
|
...(relNs !== undefined ? { nomosRelationNamespace: relNs } : {}),
|
|
1266
|
+
...(rpNs !== undefined ? { nomosReadProjectionNamespace: rpNs } : {}),
|
|
1187
1267
|
...(relGate !== undefined ? { nomosRelationGate: relGate } : {}),
|
|
1188
1268
|
...(attGate !== undefined ? { nomosAttestationGate: attGate } : {}),
|
|
1189
1269
|
...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
|
|
1270
|
+
...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
|
|
1190
1271
|
...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
|
|
1191
1272
|
...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
|
|
1273
|
+
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
|
1192
1274
|
};
|
|
1193
1275
|
})
|
|
1194
1276
|
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|