@githolon/dsl 0.113.4 → 0.115.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/assets/operation-contract.wasm +0 -0
- package/compile_package.mjs +23 -3
- package/compile_proof.d.mts +7 -0
- package/compile_proof.mjs +38 -3
- package/dart/CHANGELOG.md +60 -0
- package/dart/lib/src/application_framework_calls.dart +16 -6
- package/dart/lib/src/console_enrolment.dart +7 -0
- package/dart/lib/src/console_http_io.dart +22 -15
- package/dart/lib/src/dev_cloud.dart +62 -0
- package/dart/lib/src/generated_call.dart +14 -3
- package/dart/lib/src/nms_generated_runtime.dart +7 -0
- package/dart/lib/src/node_runner_transport.dart +77 -8
- package/dart/lib/src/nomos_trace.dart +107 -0
- package/dart/lib/src/operation_receipts.dart +74 -7
- package/dart/lib/src/reception/dart_reception.dart +174 -17
- package/dart/lib/src/rows_watch.dart +390 -0
- package/dart/lib/src/runner/node-entry.mjs +7678 -2860
- package/dart/lib/src/runtime_v2_transport.dart +3 -0
- package/dart/lib/src/sync_status_projection.dart +94 -1
- package/dart/lib/src/sync_status_reducer.dart +5 -2
- package/dart/lib/src/test_harness.dart +107 -41
- package/dart/lib/src/testing/test_peer.dart +107 -99
- package/dart/lib/src/testing/test_peer_host.dart +305 -0
- package/dart/lib/src/typed_watch_registry.dart +26 -3
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/background_model_adoption_test.dart +1 -0
- package/dart/test/console_enrolment_test.dart +47 -0
- package/dart/test/custodian_warming_test.dart +105 -0
- package/dart/test/fixtures/offer_watch_order_peer.dart +74 -0
- package/dart/test/kernel_author_after_read_test.dart +6 -3
- package/dart/test/kernel_fence_test.dart +4 -1
- package/dart/test/model_adoption_test.dart +66 -0
- package/dart/test/nomos_trace_test.dart +91 -0
- package/dart/test/own_offer_watch_order_test.dart +90 -0
- package/dart/test/receiving_group_open_test.dart +4 -8
- package/dart/test/replica_readiness_test.dart +14 -4
- package/dart/test/rows_watch_test.dart +331 -0
- package/dart/test/runtime_projections_test.dart +41 -0
- package/dart/test/test_peer_host_selection_test.dart +49 -0
- package/dart_types/CHANGELOG.md +39 -0
- package/dart_types/lib/src/approved_model.dart +100 -6
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +1063 -3
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +89 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +14 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +2272 -130
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +118 -2
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +278 -9
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +7 -85
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +35 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +6 -2
- package/src/aggregate.ts +93 -0
- package/src/anonymous_value_interning.ts +205 -0
- package/src/baseline.ts +27 -0
- package/src/baseline_export.ts +432 -0
- package/src/baseline_import.ts +312 -0
- package/src/baseline_usda.ts +186 -0
- package/src/birth_key_rule.ts +472 -0
- package/src/births.ts +207 -24
- package/src/build_provenance.ts +17 -2
- package/src/business_birth.ts +50 -4
- package/src/check_package_main.ts +50 -6
- package/src/codegen_call_proto.ts +5 -18
- package/src/codegen_dart.ts +95 -3
- package/src/codegen_dart_client.ts +73 -15
- package/src/codegen_dart_facade.ts +58 -35
- package/src/codegen_dart_framework_calls.ts +32 -10
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +5 -2
- package/src/codegen_result_proto.ts +8 -6
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +135 -34
- package/src/compatibility_graph.ts +41 -4
- package/src/compatibility_usda.ts +11 -1
- package/src/compile_package_main.ts +272 -105
- package/src/composed_wire_identity.ts +24 -0
- package/src/directive.ts +6 -0
- package/src/domain_requirements.ts +75 -0
- package/src/engine_entry.ts +4 -48
- package/src/engine_javascript.ts +12 -6
- package/src/fields.ts +109 -0
- package/src/identity_usda.ts +77 -51
- package/src/index.ts +25 -1
- package/src/intent.ts +5 -0
- package/src/law_graph.ts +142 -1
- package/src/law_graph_usda.ts +25 -141
- package/src/law_layer_prims.ts +199 -0
- package/src/law_unit_selection.ts +45 -0
- package/src/law_unit_set_identity.ts +44 -0
- package/src/law_version.ts +107 -0
- package/src/package_config.ts +44 -0
- package/src/package_dependencies.ts +8 -0
- package/src/query/compile.ts +0 -0
- package/src/release_inspection.ts +24 -0
- package/src/release_publication.ts +298 -0
- package/src/value_object.ts +6 -3
- package/src/wire.ts +2 -0
- package/src/wire_paths.ts +17 -0
- package/src/workspace_routing.ts +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// NOMOS — The wire identity of an operation field under OpenUSD composition.
|
|
2
|
+
//
|
|
3
|
+
// Node-free on purpose: the compatibility graph reaches this from the law bundle (the release law's
|
|
4
|
+
// plans bundle for a neutral platform), where `codegen_call_proto.ts` cannot live (it mints stable
|
|
5
|
+
// ids through `node:crypto`). Both the generated codecs and the compatibility requirement derive a
|
|
6
|
+
// nested member's allocation here, so the two can never disagree (CAP-681).
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* OpenUSD composition may share structurally identical anonymous records between
|
|
10
|
+
* operation contracts. `compileSparseLawGraphUsda` records that sharing by
|
|
11
|
+
* redirecting each duplicate field path to the canonical field prim. The wire
|
|
12
|
+
* identity must follow that composed path too; using the duplicate graph-local
|
|
13
|
+
* SID makes generated clients speak a different Protobuf schema from the law the
|
|
14
|
+
* kernel actually installs.
|
|
15
|
+
*/
|
|
16
|
+
export function composedWireIdentity(
|
|
17
|
+
paths: ReadonlyMap<string, string>,
|
|
18
|
+
entity: { readonly id: string; readonly sid: string; readonly wireKey?: string },
|
|
19
|
+
): string {
|
|
20
|
+
if (entity.wireKey !== undefined) return entity.wireKey;
|
|
21
|
+
const path = paths.get(entity.id);
|
|
22
|
+
const canonical = path?.match(/\/(?:Fields\/f_|Variants\/v_)([0-9a-f]{16})$/i)?.[1];
|
|
23
|
+
return canonical ?? entity.sid;
|
|
24
|
+
}
|
package/src/directive.ts
CHANGED
|
@@ -536,6 +536,12 @@ export interface Directive<P = unknown> extends AuthenticatedMember {
|
|
|
536
536
|
* `attestationAuthorizedGate`/`.attestationAuthorized()` naming convention; the declaration is the noun.)
|
|
537
537
|
*/
|
|
538
538
|
readonly birthsChild?: boolean;
|
|
539
|
+
/** THE TYPED BIRTH KEY of a business birth (`birth(Aggregate, spec)`): the key's typed-id family, brand and
|
|
540
|
+
* the name's namespace. Lowered to the `nomos:birthKeyType` / `nomos:birthKeyBrand` /
|
|
541
|
+
* `nomos:birthKeyNamespace` era keys on the birthing intent; the kernel then requires every carried recipe
|
|
542
|
+
* of this intent to carry `nomos:birthKey`, checks its shape, and re-derives the child's name from it.
|
|
543
|
+
* Absent on every other directive (and on all historical law), so those chains replay unchanged. */
|
|
544
|
+
readonly birthKey?: import("./births.js").BirthKeyDeclaration;
|
|
539
545
|
/** Additional ordinary offers which the born workspace's typed law requires in genesis.
|
|
540
546
|
* This is compiler/runtime metadata attached only to a birthing directive. It contains no
|
|
541
547
|
* payload or fallback value: the kernel checks that the re-derived recipe invokes each named
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PER-DOMAIN UNIT REQUIREMENTS (CAP-666): what a generated client pins beside the unit set.
|
|
3
|
+
*
|
|
4
|
+
* An application is compiled against one law, named by its unit set (CAP-773). That one identity is
|
|
5
|
+
* what a workspace serves as a whole, so an app pinned to it alone goes stale the moment a rollout moves
|
|
6
|
+
* a domain the app never touches. Each application domain has its own unit in `build/<name>.units`
|
|
7
|
+
* (`domain:<key>`) and its own slice of the generated compatibility requirement
|
|
8
|
+
* (`<name>.compatibility.usda`, the `NomosCompatibilityDomain` the kernel's Compatibility Desk judges).
|
|
9
|
+
* This module names both per domain key, from the same in-memory units and interfaces the compiler
|
|
10
|
+
* just emitted, so the generated Dart/TS clients, `NomosApprovedModel`, the model-adoption desk and the
|
|
11
|
+
* client inventory all carry one contract per domain and never infer it from installed law.
|
|
12
|
+
*
|
|
13
|
+
* Era rule: the requirement is derived from existing artifacts and written into no law, unit or
|
|
14
|
+
* package. Unchanged law keeps every identity it had; only the generated client gains the table.
|
|
15
|
+
*/
|
|
16
|
+
import { emitApplicationCompatibilityUsda } from "./compatibility_usda.js";
|
|
17
|
+
import type { DomainInterface } from "./compatibility_graph.js";
|
|
18
|
+
import type { LawUnit } from "./law_units.js";
|
|
19
|
+
|
|
20
|
+
export interface DomainUnitRequirement {
|
|
21
|
+
/** The application domain key. */
|
|
22
|
+
readonly domainKey: string;
|
|
23
|
+
/** The hash of its law unit (`domain:<key>` in the unit set), 64 lowercase hex. */
|
|
24
|
+
readonly unitSha256: string;
|
|
25
|
+
/** sha256 of the domain's own compatibility requirement USDA (the one-domain `emitApplicationCompatibilityUsda`). */
|
|
26
|
+
readonly requirementSha256: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
30
|
+
|
|
31
|
+
/** The USDA requirement for exactly one domain: the slice a client carries and the kernel judges. */
|
|
32
|
+
export function domainRequirementUsda(domainKey: string, domainInterface: DomainInterface): string {
|
|
33
|
+
return emitApplicationCompatibilityUsda({ [domainKey]: domainInterface });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* One requirement per application domain the package exposes an interface for, sorted by domain key. A
|
|
38
|
+
* domain unit without an interface (a private or framework domain) names no requirement and is omitted;
|
|
39
|
+
* an interface whose domain has no unit is a compiler defect and refuses.
|
|
40
|
+
*/
|
|
41
|
+
export function domainRequirementsOf(
|
|
42
|
+
units: readonly Pick<LawUnit, "key" | "kind" | "domain" | "hash">[],
|
|
43
|
+
interfaces: Readonly<Record<string, DomainInterface>>,
|
|
44
|
+
sha256Hex: (utf8: string) => string,
|
|
45
|
+
): DomainUnitRequirement[] {
|
|
46
|
+
const unitByDomain = new Map<string, string>();
|
|
47
|
+
for (const unit of units) {
|
|
48
|
+
if (unit.kind === "domain" && unit.domain !== undefined) unitByDomain.set(unit.domain, unit.hash);
|
|
49
|
+
}
|
|
50
|
+
return Object.keys(interfaces).sort().map((domainKey) => {
|
|
51
|
+
const unitSha256 = unitByDomain.get(domainKey);
|
|
52
|
+
if (unitSha256 === undefined) {
|
|
53
|
+
throw new Error(`domain requirements: '${domainKey}' has a compatibility interface but no law unit; the unit split and the interface disagree`);
|
|
54
|
+
}
|
|
55
|
+
const requirementSha256 = sha256Hex(domainRequirementUsda(domainKey, interfaces[domainKey]!));
|
|
56
|
+
for (const [name, value] of [["unit", unitSha256], ["requirement", requirementSha256]] as const) {
|
|
57
|
+
if (!HEX64.test(value)) throw new Error(`domain requirements: '${domainKey}' ${name} identity must be 64 lowercase hex, not '${value}'`);
|
|
58
|
+
}
|
|
59
|
+
return { domainKey, unitSha256, requirementSha256 };
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Refuse a hand-supplied table that is not what the compiler would emit (generators take it as an input). */
|
|
64
|
+
export function assertDomainRequirements(requirements: readonly DomainUnitRequirement[]): void {
|
|
65
|
+
const seen = new Set<string>();
|
|
66
|
+
for (const requirement of requirements) {
|
|
67
|
+
if (!requirement.domainKey || seen.has(requirement.domainKey)) {
|
|
68
|
+
throw new Error(`domain requirements: '${requirement.domainKey}' is empty or named twice`);
|
|
69
|
+
}
|
|
70
|
+
seen.add(requirement.domainKey);
|
|
71
|
+
for (const [name, value] of [["unit", requirement.unitSha256], ["requirement", requirement.requirementSha256]] as const) {
|
|
72
|
+
if (!HEX64.test(value)) throw new Error(`domain requirements: '${requirement.domainKey}' ${name} identity must be 64 lowercase hex, not '${value}'`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/engine_entry.ts
CHANGED
|
@@ -73,7 +73,6 @@ import {
|
|
|
73
73
|
nomosSourceFrontierEnsure,
|
|
74
74
|
withAttestedReadExports,
|
|
75
75
|
} from "./attested.js";
|
|
76
|
-
import type { OpenApiDoc } from "./codegen_openapi.js";
|
|
77
76
|
import type { Directive } from "./directive.js";
|
|
78
77
|
import type { AggregateHandle, AggregateInvariantFn, AggregateInvariantVerdict } from "./aggregate.js";
|
|
79
78
|
import type { Ports } from "./ctx.js";
|
|
@@ -178,18 +177,6 @@ export interface EngineEntryConfig {
|
|
|
178
177
|
* `sideEffects: false` (a bare side-effect import would be tree-shaken away).
|
|
179
178
|
*/
|
|
180
179
|
readonly seeds?: readonly unknown[];
|
|
181
|
-
/**
|
|
182
|
-
* Compiler-derived self-description. It is a presentation projection held inside the separately
|
|
183
|
-
* routed `describe` plan, never executable law IR. Ordinary offer/read plans omit it completely,
|
|
184
|
-
* so OpenAPI/JSON-Schema generation cannot sit on their parse or readiness flame path.
|
|
185
|
-
*/
|
|
186
|
-
readonly openapiDocument?: OpenApiDoc;
|
|
187
|
-
/**
|
|
188
|
-
* The package's OpenAPI document title (the compiler's `"<name> (nomos package)"`). Baked into
|
|
189
|
-
* the lump so `describe` reproduces the compiler's public description byte-for-byte without
|
|
190
|
-
* the kernel knowing the package name. The kernel MAY override it (the workspace-level title).
|
|
191
|
-
*/
|
|
192
|
-
readonly packageTitle?: string;
|
|
193
180
|
/** Compiler-generated nominal encoders keyed by the exact target Directive singleton. Source law
|
|
194
181
|
* can name a target by type, while the runtime emits only canonical USDA. */
|
|
195
182
|
readonly workspaceEffectTargets?: readonly {
|
|
@@ -297,7 +284,7 @@ function mergeModules(mods: readonly DomainModuleExports[], domain: string): Dom
|
|
|
297
284
|
// without per-piece re-exports (capability_exports.ts).
|
|
298
285
|
// METERED BIRTH: a `.births()`-declaring domain gains the birth-meter structural exports
|
|
299
286
|
// (the sanctioning `nomosBirthRecordEnsure` + the meter aggregates) at this same seam, so
|
|
300
|
-
// the registry
|
|
287
|
+
// the registry sees the exact surface the composed manifest carries.
|
|
301
288
|
// ROLE SUGAR: lift every `role(...).emit()` into its grant/revoke/seed directives at the SAME
|
|
302
289
|
// engine merge point so the registry dispatches them (the compiler saw them via the same expander).
|
|
303
290
|
return expandRoleExports(
|
|
@@ -650,8 +637,6 @@ export interface RegisteredEngine {
|
|
|
650
637
|
prepareDomain(domain: string): { complete: boolean; initialized: number; total: number };
|
|
651
638
|
/** Compiler/tooling probe: force one domain and prove an exact routed offer is registered. */
|
|
652
639
|
hasDirective(domain: string, directiveId: string): boolean;
|
|
653
|
-
/** The G1 self-describe verb: the lump's own merged OpenAPI, derived from its modules. */
|
|
654
|
-
describe(opts?: { lawHash?: string; title?: string }): OpenApiDoc;
|
|
655
640
|
}
|
|
656
641
|
|
|
657
642
|
/**
|
|
@@ -1155,42 +1140,13 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
1155
1140
|
};
|
|
1156
1141
|
}
|
|
1157
1142
|
|
|
1158
|
-
/**
|
|
1159
|
-
* THE SELF-DESCRIBE VERB (G1) — the lump's OWN OpenAPI, derived from the SAME modules it
|
|
1160
|
-
* dispatches. The kernel selects the content-addressed describe plan from the native USD
|
|
1161
|
-
* package in custody, so the workspace's API surface is a pure,
|
|
1162
|
-
* deterministic function of installed law — NOT a host-staged blob. Its public `paths` reproduce
|
|
1163
|
-
* the public description the compiler derived; describe also carries a private, law-derived
|
|
1164
|
-
* framework routing manifest which the host consumes internally and never publishes.
|
|
1165
|
-
* `opts.lawHash`/`opts.title` are injected by the kernel at describe-time (the version stamp +
|
|
1166
|
-
* package title), matching the compiler's `domainHash`/`"<name> (nomos package)"`.
|
|
1167
|
-
*/
|
|
1168
|
-
function describe(opts?: { lawHash?: string; title?: string }): OpenApiDoc {
|
|
1169
|
-
const document = config.openapiDocument;
|
|
1170
|
-
if (document === undefined) {
|
|
1171
|
-
throw new Error("engine describe: selected plan has no compiled OpenAPI projection");
|
|
1172
|
-
}
|
|
1173
|
-
const lawHash = opts?.lawHash;
|
|
1174
|
-
const title = opts?.title ?? config.packageTitle ?? document.info["title"];
|
|
1175
|
-
const priorInfo = document.info;
|
|
1176
|
-
const info: Record<string, unknown> = {
|
|
1177
|
-
...priorInfo,
|
|
1178
|
-
...(title === undefined ? {} : { title }),
|
|
1179
|
-
version: lawHash === undefined ? (priorInfo["version"] ?? "0") : lawHash.slice(0, 12),
|
|
1180
|
-
};
|
|
1181
|
-
if (lawHash === undefined) delete info["x-nomos-law-hash"];
|
|
1182
|
-
else info["x-nomos-law-hash"] = lawHash;
|
|
1183
|
-
return { ...document, info };
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
1143
|
// The engine evals the bundle as a classic script and FREEZES globalThis before
|
|
1187
|
-
// dispatch — assign all entries NOW, at top-level eval. `describe`
|
|
1188
|
-
//
|
|
1144
|
+
// dispatch — assign all entries NOW, at top-level eval. There is no `describe` entry: the
|
|
1145
|
+
// kernel derives a workspace's description from its installed law (CAP-658).
|
|
1189
1146
|
(globalThis as { plan?: unknown }).plan = plan;
|
|
1190
1147
|
(globalThis as { planReport?: unknown }).planReport = planReport;
|
|
1191
1148
|
(globalThis as { __nomosPrepareDomain?: unknown }).__nomosPrepareDomain = prepareDomain;
|
|
1192
1149
|
(globalThis as { __nomosHasDirective?: unknown }).__nomosHasDirective = hasDirective;
|
|
1193
|
-
(globalThis as { describe?: unknown }).describe = describe;
|
|
1194
1150
|
|
|
1195
|
-
return { plan, planReport, prepareDomain, hasDirective
|
|
1151
|
+
return { plan, planReport, prepareDomain, hasDirective } as RegisteredEngine;
|
|
1196
1152
|
}
|
package/src/engine_javascript.ts
CHANGED
|
@@ -2,21 +2,27 @@
|
|
|
2
2
|
* Canonicalise esbuild's non-semantic install-topology noise before the executable law is hashed.
|
|
3
3
|
*
|
|
4
4
|
* A workspace dependency is labelled `../../dsl/src/...`; the same published dependency is labelled
|
|
5
|
-
* `node_modules/@githolon/dsl/src/...`.
|
|
5
|
+
* `node_modules/@githolon/dsl/src/...`. A dependency reached through a symlinked `node_modules` (a git
|
|
6
|
+
* worktree links the main checkout's) is labelled by its real path, climbing out and back down through
|
|
7
|
+
* another directory: `../../../../platform/nomos2/node_modules/zod/...`. Everything up to the last
|
|
8
|
+
* `node_modules/` in an esbuild label describes the install, never the law. Esbuild can also leave either one or two newlines after its
|
|
6
9
|
* `"use strict"` prologue. None of those bytes describe business law, so they must not give the same
|
|
7
10
|
* law two identities.
|
|
8
11
|
*
|
|
9
12
|
* Replacements are deliberately restricted to esbuild's generated comment/function labels. Tenant
|
|
10
13
|
* strings and executable imports remain byte-significant.
|
|
11
14
|
*/
|
|
15
|
+
/** An esbuild label's install prefix: an optional climb (`../`), any directories, then `node_modules/`. */
|
|
16
|
+
const INSTALLED = String.raw`(?:(?:\.\.\/)+(?:[^\s"/]+\/)*?)?node_modules\/`;
|
|
17
|
+
|
|
12
18
|
export function canonicalizeEngineJavascript(source: string): string {
|
|
13
19
|
const body = source
|
|
14
20
|
.replace(/\r\n?/g, "\n")
|
|
15
21
|
.replace(/^[ \t]*"use strict";[ \t]*(?:\n|$)/gm, "")
|
|
16
|
-
.replace(
|
|
17
|
-
.replace(
|
|
18
|
-
.replace(
|
|
19
|
-
.replace(
|
|
22
|
+
.replace(new RegExp(`^(\\s*\\/\\/ )(?:(?:\\.\\.\\/)+dsl\\/src\\/|${INSTALLED}@githolon\\/dsl\\/src\\/)`, "gm"), "$1@githolon/dsl/src/")
|
|
23
|
+
.replace(new RegExp(`"(?:(?:\\.\\.\\/)+dsl\\/src\\/|${INSTALLED}@githolon\\/dsl\\/src\\/)([^"]+)"\\(\\) \\{`, "g"), '"@githolon/dsl/src/$1"() {')
|
|
24
|
+
.replace(new RegExp(`^(\\s*\\/\\/ )${INSTALLED}zod\\/`, "gm"), "$1zod/")
|
|
25
|
+
.replace(new RegExp(`"${INSTALLED}zod\\/([^"]+)"\\(\\) \\{`, "g"), '"zod/$1"() {')
|
|
20
26
|
.replace(/\n[ \t]*\n/g, "\n")
|
|
21
27
|
.trim();
|
|
22
28
|
return `"use strict";\n${body}\n`;
|
|
@@ -25,7 +31,7 @@ export function canonicalizeEngineJavascript(source: string): string {
|
|
|
25
31
|
/** Remove the same install-topology label from the sealed generated application bundle. */
|
|
26
32
|
export function canonicalizeGeneratedClientJavascript(source: string): string {
|
|
27
33
|
return source.replace(
|
|
28
|
-
|
|
34
|
+
new RegExp(`^(\\s*\\/\\/ )(?:(?:\\.\\.\\/)+dsl\\/runtime\\/|${INSTALLED}@githolon\\/dsl\\/runtime\\/)`, "gm"),
|
|
29
35
|
"$1@githolon/dsl/runtime/",
|
|
30
36
|
);
|
|
31
37
|
}
|
package/src/fields.ts
CHANGED
|
@@ -243,6 +243,97 @@ export function isGitObjectIdSchema(schema: z.ZodTypeAny): boolean {
|
|
|
243
243
|
return false;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
// ── TYPED IDS (aggregate fields) ─────────────────────────────────────────────────────────────────────
|
|
247
|
+
// An identifier is never a plain string. `t.brandedId(Name)` and `t.uuid()` are the aggregate-field forms of
|
|
248
|
+
// the payload-side `brandedId(...)` schema: wire-identical to a string, nominal at compile time (a plain
|
|
249
|
+
// `string` is not assignable), and recognised by codegen, which emits the branded type in both generated
|
|
250
|
+
// clients. `t.ref(Aggregate)` is the aggregate-field form of `idOf(Aggregate)`. A birth key MUST be one of
|
|
251
|
+
// these (see `births.ts`).
|
|
252
|
+
|
|
253
|
+
declare const NOMOS_TYPED_ID: unique symbol;
|
|
254
|
+
/** A nominal id: a string at run time and on the wire, a distinct type per `Brand` at compile time. */
|
|
255
|
+
export type TypedId<Brand extends string> = string & { readonly [NOMOS_TYPED_ID]: Brand };
|
|
256
|
+
/** The brand a `t.uuid()` field carries. */
|
|
257
|
+
export type Uuid = TypedId<"Uuid">;
|
|
258
|
+
|
|
259
|
+
/** The same global symbol `value_object.ts` stamps on `brandedId(...)` payload schemas (kept as `Symbol.for`
|
|
260
|
+
* here because value_object.ts imports this module). */
|
|
261
|
+
const BRANDED_ID_SYMBOL: unique symbol = Symbol.for("nomos.brandedId") as never;
|
|
262
|
+
/** Marks a `t.uuid()` schema, so a birth key can be classified as a uuid without reading names. */
|
|
263
|
+
export const NOMOS_UUID_BRAND: unique symbol = Symbol.for("nomos.uuid");
|
|
264
|
+
|
|
265
|
+
const TYPED_ID_BRAND_RE = /^[A-Z][A-Za-z0-9_]*$/;
|
|
266
|
+
/** Canonical lowercase RFC 9562 text form (any version) — the one spelling a `t.uuid()` admits. */
|
|
267
|
+
export const CANONICAL_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
268
|
+
/** The longest id (UTF-8 bytes) a typed id admits — mirrored by the kernel's birth-key shape check. */
|
|
269
|
+
export const TYPED_ID_MAX_BYTES = 256;
|
|
270
|
+
|
|
271
|
+
/** Why `value` is not a well-formed typed id value (`undefined` when it is). One rule set, mirrored by
|
|
272
|
+
* the kernel (`kernel/src/birth_stage.rs` `typed_birth_key_refusal`): a non-empty string of at most
|
|
273
|
+
* {@link TYPED_ID_MAX_BYTES} UTF-8 bytes with no control character (U+0000–U+001F, U+007F — which also keeps
|
|
274
|
+
* the unit separator that delimits `(namespace, key)` out of every key). */
|
|
275
|
+
export function typedIdShapeRefusal(value: unknown): string | undefined {
|
|
276
|
+
if (typeof value !== "string") return `an id is a string; got ${value === null ? "null" : typeof value}`;
|
|
277
|
+
if (value.length === 0) return "an id is non-empty";
|
|
278
|
+
if (utf8ByteLength(value) > TYPED_ID_MAX_BYTES) {
|
|
279
|
+
return `an id is at most ${TYPED_ID_MAX_BYTES} UTF-8 bytes`;
|
|
280
|
+
}
|
|
281
|
+
// eslint-disable-next-line no-control-regex
|
|
282
|
+
if (/[\u0000-\u001f\u007f]/.test(value)) return "an id carries no control character";
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** UTF-8 length of a JS string, counted by code point. The sealed engine (QuickJS) has no TextEncoder, and
|
|
287
|
+
* this rule runs inside law plans and payload validation there. A lone surrogate counts 3 bytes (U+FFFD). */
|
|
288
|
+
function utf8ByteLength(value: string): number {
|
|
289
|
+
let bytes = 0;
|
|
290
|
+
for (const char of value) {
|
|
291
|
+
const code = char.codePointAt(0)!;
|
|
292
|
+
bytes += code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
|
|
293
|
+
}
|
|
294
|
+
return bytes;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function typedIdSchema(): z.ZodString {
|
|
298
|
+
return z.string().superRefine((value, ctx) => {
|
|
299
|
+
const refusal = typedIdShapeRefusal(value);
|
|
300
|
+
if (refusal !== undefined) ctx.addIssue({ code: "custom", message: refusal });
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Make a typed id VALUE of `brand` from its wire text, checking the shared shape. This is the one
|
|
306
|
+
* sanctioned door from text to an id (a constant the law owns, a value read off another system); a
|
|
307
|
+
* `"Uuid"` brand also requires the canonical uuid spelling. Never cast a string to {@link TypedId}.
|
|
308
|
+
*/
|
|
309
|
+
export function typedId<const Brand extends string>(brand: Brand, value: string): TypedId<Brand> {
|
|
310
|
+
if (!TYPED_ID_BRAND_RE.test(brand)) {
|
|
311
|
+
throw new Error(`typedId: '${brand}' must be a capitalized identifier (the brand's type name)`);
|
|
312
|
+
}
|
|
313
|
+
const refusal = typedIdShapeRefusal(value);
|
|
314
|
+
if (refusal !== undefined) throw new Error(`typedId(${brand}): ${refusal}`);
|
|
315
|
+
if (brand === "Uuid" && !CANONICAL_UUID_RE.test(value)) {
|
|
316
|
+
throw new Error("typedId(Uuid): expected a canonical lowercase uuid");
|
|
317
|
+
}
|
|
318
|
+
return value as TypedId<Brand>;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Whether a schema is a `t.uuid()` id (through omission/null wrappers). */
|
|
322
|
+
export function isUuidIdSchema(schema: z.ZodTypeAny): boolean {
|
|
323
|
+
let current: z.ZodTypeAny | undefined = schema;
|
|
324
|
+
for (let hops = 0; current !== undefined && hops < 16; hops++) {
|
|
325
|
+
if ((current as unknown as Record<symbol, unknown>)[NOMOS_UUID_BRAND] === true) return true;
|
|
326
|
+
const raw = current as unknown as {
|
|
327
|
+
_def?: { type?: unknown; innerType?: z.ZodTypeAny };
|
|
328
|
+
def?: { type?: unknown; innerType?: z.ZodTypeAny };
|
|
329
|
+
};
|
|
330
|
+
const def = raw._def ?? raw.def;
|
|
331
|
+
if (def?.type !== "optional" && def?.type !== "default" && def?.type !== "nullable") return false;
|
|
332
|
+
current = def.innerType;
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
246
337
|
export const nomosRefValueSchema: z.ZodType<NomosRefValue> = z.object({
|
|
247
338
|
workspace: z.string().min(1),
|
|
248
339
|
type: z.string().min(1),
|
|
@@ -1107,6 +1198,24 @@ export const t = {
|
|
|
1107
1198
|
gitObjectId(): Field<GitObjectId, "string"> {
|
|
1108
1199
|
return makeField<GitObjectId, "string">("string", gitObjectId(), FirstWriterWins);
|
|
1109
1200
|
},
|
|
1201
|
+
/** A NOMINAL id field: `estateId: t.brandedId("EstateId")`. Wire-identical to a string; the DSL value
|
|
1202
|
+
* type is {@link TypedId}`<Name>` (a plain string is a compile error) and both generated clients emit
|
|
1203
|
+
* the branded `Name` type. Values come from a payload of the same brand or {@link typedId}. */
|
|
1204
|
+
brandedId<const Name extends string>(name: Name): Field<TypedId<Name>, "string"> {
|
|
1205
|
+
if (!TYPED_ID_BRAND_RE.test(name)) {
|
|
1206
|
+
throw new Error(`t.brandedId: '${name}' must be a capitalized identifier — it becomes the generated type's name.`);
|
|
1207
|
+
}
|
|
1208
|
+
const schema = typedIdSchema();
|
|
1209
|
+
Object.defineProperty(schema, BRANDED_ID_SYMBOL, { value: name, enumerable: false });
|
|
1210
|
+
return makeField<TypedId<Name>, "string">("string", schema as unknown as z.ZodType<TypedId<Name>>, FirstWriterWins);
|
|
1211
|
+
},
|
|
1212
|
+
/** A uuid id field in its canonical lowercase spelling. Generated clients emit the branded `Uuid`. */
|
|
1213
|
+
uuid(): Field<Uuid, "string"> {
|
|
1214
|
+
const schema = z.string().regex(CANONICAL_UUID_RE, "expected a canonical lowercase uuid");
|
|
1215
|
+
Object.defineProperty(schema, BRANDED_ID_SYMBOL, { value: "Uuid", enumerable: false });
|
|
1216
|
+
Object.defineProperty(schema, NOMOS_UUID_BRAND, { value: true, enumerable: false });
|
|
1217
|
+
return makeField<Uuid, "string">("string", schema as unknown as z.ZodType<Uuid>, FirstWriterWins);
|
|
1218
|
+
},
|
|
1110
1219
|
int(rules: IntRules = {}): Field<number, "int"> {
|
|
1111
1220
|
for (const [name, value] of [["minimum", rules.minimum], ["maximum", rules.maximum]] as const) {
|
|
1112
1221
|
if (value !== undefined && !Number.isSafeInteger(value)) {
|
package/src/identity_usda.ts
CHANGED
|
@@ -180,11 +180,13 @@ function emitValue(value: PlainValue, primName: string, indent: number, key?: st
|
|
|
180
180
|
// general USD parser: it reads `def Scope "<name>" { … }` nesting and `custom uniform <type>
|
|
181
181
|
// nomos:<attr> = <literal>` attributes, which is all this layer ever contains.
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
/** One prim of the exact typed subset this module writes (also the baseline artifact's host format). */
|
|
184
|
+
export interface IdentityPrim {
|
|
184
185
|
readonly name: string;
|
|
185
186
|
readonly attrs: Record<string, unknown>;
|
|
186
|
-
readonly children:
|
|
187
|
+
readonly children: IdentityPrim[];
|
|
187
188
|
}
|
|
189
|
+
type Prim = IdentityPrim;
|
|
188
190
|
|
|
189
191
|
// Attributes are written bare when the `NomosEntity` schema declares their type, and with
|
|
190
192
|
// `custom uniform` only where a prim carries something the schema does not describe.
|
|
@@ -437,9 +439,14 @@ function readEntityForest(
|
|
|
437
439
|
}
|
|
438
440
|
}
|
|
439
441
|
|
|
440
|
-
/**
|
|
441
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Render the identity scopes (`Entities`, `ValueContracts`, `Domains`) at `indent`. The committed
|
|
444
|
+
* identity layer hosts them under `NomosIdentity`; a released baseline (baseline_usda.ts) hosts the
|
|
445
|
+
* same scopes under its own root so continuity has exactly one encoding.
|
|
446
|
+
*/
|
|
447
|
+
export function renderIdentityScopes(lock: CompilerIdentityModel, indent: number): string[] {
|
|
442
448
|
if (lock.identity !== undefined) validateLawIdentityRegistry(lock.identity);
|
|
449
|
+
const p = pad(indent);
|
|
443
450
|
const entities = Object.entries(lock.identity?.entities ?? {}).sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0));
|
|
444
451
|
const domains = Object.entries(lock.domains).sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0));
|
|
445
452
|
const contracts = new Map<string, unknown>();
|
|
@@ -456,7 +463,45 @@ export function serializeIdentityUsda(lock: CompilerIdentityModel): string {
|
|
|
456
463
|
renderedContracts.set(field.contract, rendered);
|
|
457
464
|
}
|
|
458
465
|
}
|
|
466
|
+
return [
|
|
467
|
+
`${p}def Scope "Entities"`,
|
|
468
|
+
`${p}{`,
|
|
469
|
+
...emitEntityForest(Object.fromEntries(entities), indent + 4),
|
|
470
|
+
`${p}}`,
|
|
471
|
+
`${p}def Scope "ValueContracts"`,
|
|
472
|
+
`${p}{`,
|
|
473
|
+
...[...contracts.entries()].sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0)).flatMap(([identity, shape]) => [
|
|
474
|
+
`${p} def Scope ${quote(`c_${identity}`)}`,
|
|
475
|
+
`${p} {`,
|
|
476
|
+
...emitValue(shape, "Shape", indent + 8),
|
|
477
|
+
`${p} }`,
|
|
478
|
+
]),
|
|
479
|
+
`${p}}`,
|
|
480
|
+
`${p}def Scope "Domains"`,
|
|
481
|
+
`${p}{`,
|
|
482
|
+
...domains.flatMap(([name, domain]) => {
|
|
483
|
+
const aggregates = Object.entries(domain.stableIds.aggregates).sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0));
|
|
484
|
+
return [
|
|
485
|
+
`${p} def Scope ${quote(`d_${domain.stableIds.domain}`)}`,
|
|
486
|
+
`${p} {`,
|
|
487
|
+
`${p} custom uniform string nomos:name = ${quote(name)}`,
|
|
488
|
+
`${p} custom uniform string nomos:sid = ${quote(domain.stableIds.domain)}`,
|
|
489
|
+
...(domain.schemas === undefined ? [] : emitValue(domain.schemas, "Schemas", indent + 8)),
|
|
490
|
+
...(aggregates.length === 0 ? [] : [
|
|
491
|
+
`${p} def Scope "Aggregates"`,
|
|
492
|
+
`${p} {`,
|
|
493
|
+
...aggregates.flatMap(([label, aggregate]) => emitAggregate(label, aggregate, indent + 12)),
|
|
494
|
+
`${p} }`,
|
|
495
|
+
]),
|
|
496
|
+
`${p} }`,
|
|
497
|
+
];
|
|
498
|
+
}),
|
|
499
|
+
`${p}}`,
|
|
500
|
+
];
|
|
501
|
+
}
|
|
459
502
|
|
|
503
|
+
/** Render the committed identity layer. Deterministic: every map is emitted in sorted key order. */
|
|
504
|
+
export function serializeIdentityUsda(lock: CompilerIdentityModel): string {
|
|
460
505
|
return [
|
|
461
506
|
"#usda 1.0",
|
|
462
507
|
"(",
|
|
@@ -470,65 +515,30 @@ export function serializeIdentityUsda(lock: CompilerIdentityModel): string {
|
|
|
470
515
|
"",
|
|
471
516
|
'def Scope "NomosIdentity"',
|
|
472
517
|
"{",
|
|
473
|
-
|
|
474
|
-
" {",
|
|
475
|
-
...emitEntityForest(Object.fromEntries(entities), 8),
|
|
476
|
-
" }",
|
|
477
|
-
' def Scope "ValueContracts"',
|
|
478
|
-
" {",
|
|
479
|
-
...[...contracts.entries()].sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0)).flatMap(([identity, shape]) => [
|
|
480
|
-
` def Scope ${quote(`c_${identity}`)}`,
|
|
481
|
-
" {",
|
|
482
|
-
...emitValue(shape, "Shape", 12),
|
|
483
|
-
" }",
|
|
484
|
-
]),
|
|
485
|
-
" }",
|
|
486
|
-
' def Scope "Domains"',
|
|
487
|
-
" {",
|
|
488
|
-
...domains.flatMap(([name, domain]) => {
|
|
489
|
-
const aggregates = Object.entries(domain.stableIds.aggregates).sort(([l], [r]) => (l < r ? -1 : l > r ? 1 : 0));
|
|
490
|
-
return [
|
|
491
|
-
` def Scope ${quote(`d_${domain.stableIds.domain}`)}`,
|
|
492
|
-
" {",
|
|
493
|
-
` custom uniform string nomos:name = ${quote(name)}`,
|
|
494
|
-
` custom uniform string nomos:sid = ${quote(domain.stableIds.domain)}`,
|
|
495
|
-
...(domain.schemas === undefined ? [] : emitValue(domain.schemas, "Schemas", 12)),
|
|
496
|
-
...(aggregates.length === 0 ? [] : [
|
|
497
|
-
' def Scope "Aggregates"',
|
|
498
|
-
" {",
|
|
499
|
-
...aggregates.flatMap(([label, aggregate]) => emitAggregate(label, aggregate, 16)),
|
|
500
|
-
" }",
|
|
501
|
-
]),
|
|
502
|
-
" }",
|
|
503
|
-
];
|
|
504
|
-
}),
|
|
505
|
-
" }",
|
|
518
|
+
...renderIdentityScopes(lock, 4),
|
|
506
519
|
"}",
|
|
507
520
|
"",
|
|
508
521
|
].join("\n");
|
|
509
522
|
}
|
|
510
523
|
|
|
511
|
-
/**
|
|
512
|
-
export
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (!text.includes(NOMOS_IDENTITY_LAYER_FORMAT)) {
|
|
517
|
-
throw new Error(
|
|
518
|
-
`${NOMOS_IDENTITY_FILENAME} is not ${NOMOS_IDENTITY_LAYER_FORMAT} — refusing to guess an identity encoding`,
|
|
519
|
-
);
|
|
520
|
-
}
|
|
524
|
+
/** The `NomosEntity` schema block a layer hosting identity scopes declares once at its top. */
|
|
525
|
+
export const NOMOS_IDENTITY_ENTITY_SCHEMA = NOMOS_ENTITY_SCHEMA;
|
|
526
|
+
|
|
527
|
+
/** Read the root prims of a layer written in this module's typed subset. */
|
|
528
|
+
export function parseIdentityPrims(text: string, filename: string = NOMOS_IDENTITY_FILENAME): IdentityPrim[] {
|
|
521
529
|
const roots: Prim[] = [];
|
|
522
530
|
// Skip the layer header before scanning. `customLayerData = { … }` closes with a braced line of
|
|
523
531
|
// its own, which the prim reader would otherwise treat as the end of the enclosing scope and stop
|
|
524
532
|
// before reaching the first prim. Root prims sit at column 0, so that is where parsing starts.
|
|
525
533
|
const lines = text.split(/\r?\n/);
|
|
526
534
|
const firstPrim = lines.findIndex((line) => /^def\s+Scope\s/.test(line));
|
|
527
|
-
if (firstPrim === -1) throw new Error(`${
|
|
535
|
+
if (firstPrim === -1) throw new Error(`${filename} has no root prim`);
|
|
528
536
|
parsePrims(lines.slice(firstPrim), 0, roots);
|
|
529
|
-
|
|
530
|
-
|
|
537
|
+
return roots;
|
|
538
|
+
}
|
|
531
539
|
|
|
540
|
+
/** Read identity scopes (`Entities`, `ValueContracts`, `Domains`) hosted under `root`. */
|
|
541
|
+
export function readIdentityScopes(root: IdentityPrim, filename: string = NOMOS_IDENTITY_FILENAME): CompilerIdentityModel {
|
|
532
542
|
const entities: Record<string, LawIdentityEntity> = {};
|
|
533
543
|
readEntityForest(childNamed(root, "Entities")?.children ?? [], undefined, entities);
|
|
534
544
|
const identity: LawIdentityRegistry = { v: 1, entities };
|
|
@@ -538,7 +548,7 @@ export function parseIdentityUsda(text: string): CompilerIdentityModel {
|
|
|
538
548
|
for (const prim of childNamed(root, "ValueContracts")?.children ?? []) {
|
|
539
549
|
const identity = prim.name.replace(/^c_/u, "");
|
|
540
550
|
const shape = childNamed(prim, "Shape");
|
|
541
|
-
if (!identity || shape === undefined) throw new Error(`${
|
|
551
|
+
if (!identity || shape === undefined) throw new Error(`${filename} has an invalid value-contract prim ${prim.name}`);
|
|
542
552
|
contracts.set(identity, readValue(shape));
|
|
543
553
|
}
|
|
544
554
|
|
|
@@ -569,6 +579,22 @@ export function parseIdentityUsda(text: string): CompilerIdentityModel {
|
|
|
569
579
|
return { v: 1, domains, ...(Object.keys(entities).length === 0 ? {} : { identity }) };
|
|
570
580
|
}
|
|
571
581
|
|
|
582
|
+
/** Read the committed identity layer back into the compiler's in-memory lock model. */
|
|
583
|
+
export function parseIdentityUsda(text: string): CompilerIdentityModel {
|
|
584
|
+
if (!text.startsWith("#usda")) {
|
|
585
|
+
throw new Error(`${NOMOS_IDENTITY_FILENAME} is not a USDA layer`);
|
|
586
|
+
}
|
|
587
|
+
if (!text.includes(NOMOS_IDENTITY_LAYER_FORMAT)) {
|
|
588
|
+
throw new Error(
|
|
589
|
+
`${NOMOS_IDENTITY_FILENAME} is not ${NOMOS_IDENTITY_LAYER_FORMAT} — refusing to guess an identity encoding`,
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
const roots = parseIdentityPrims(text);
|
|
593
|
+
const root = roots.find((prim) => prim.name === "NomosIdentity");
|
|
594
|
+
if (root === undefined) throw new Error(`${NOMOS_IDENTITY_FILENAME} has no NomosIdentity prim`);
|
|
595
|
+
return readIdentityScopes(root);
|
|
596
|
+
}
|
|
597
|
+
|
|
572
598
|
/** An empty identity layer — the first compile of a package that has never been compiled. */
|
|
573
599
|
export function emptyIdentityLock(): CompilerIdentityModel {
|
|
574
600
|
return { v: 1, domains: {}, identity: emptyLawIdentityRegistry() };
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,9 @@ export {
|
|
|
49
49
|
forwardRef,
|
|
50
50
|
requires,
|
|
51
51
|
OWNER_FIELD,
|
|
52
|
+
BIRTH_KEY_WRITE_ONCE_CODE,
|
|
53
|
+
isBirthKeyWriteOnceRefusal,
|
|
54
|
+
birthKeyWriteOnceFields,
|
|
52
55
|
type AggregateHandle,
|
|
53
56
|
type AudienceMethods,
|
|
54
57
|
type Audience,
|
|
@@ -89,6 +92,14 @@ export {
|
|
|
89
92
|
isGitObjectIdSchema,
|
|
90
93
|
NOMOS_SHA256_DIGEST_BRAND,
|
|
91
94
|
NOMOS_GIT_OBJECT_ID_BRAND,
|
|
95
|
+
// TYPED IDS: `t.brandedId(Name)` / `t.uuid()` / `t.idOf(Agg)` fields carry `TypedId<Brand>` values;
|
|
96
|
+
// `typedId(brand, text)` is the one sanctioned door from text to an id.
|
|
97
|
+
type TypedId,
|
|
98
|
+
type Uuid,
|
|
99
|
+
typedId,
|
|
100
|
+
typedIdShapeRefusal,
|
|
101
|
+
isUuidIdSchema,
|
|
102
|
+
NOMOS_UUID_BRAND,
|
|
92
103
|
// FIELD FACETS (the endorse model — security_model.md): authority/standing/attribution gate
|
|
93
104
|
// set() acceptance of PrincipalEvidence; secret/public are the confidentiality lattice.
|
|
94
105
|
authorityField,
|
|
@@ -391,9 +402,14 @@ export {
|
|
|
391
402
|
type GenesisChainOrBuilder,
|
|
392
403
|
type BusinessBirthDecl,
|
|
393
404
|
type BusinessBirthSpec,
|
|
405
|
+
CHOOSING_A_BIRTH_KEY_DOC,
|
|
394
406
|
type BusinessBirthPayload,
|
|
395
407
|
type BirthFactKeys,
|
|
396
|
-
type
|
|
408
|
+
type BirthKeyFactKeys,
|
|
409
|
+
type BirthKeyType,
|
|
410
|
+
type BirthKeyDeclaration,
|
|
411
|
+
birthKeyTypeOfField,
|
|
412
|
+
birthKeyShapeRefusal,
|
|
397
413
|
} from "./births.js";
|
|
398
414
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain): a home/child law exports
|
|
399
415
|
// `requireParentAttestedBirth` to declare it requires a parent-attested-birth cert chain (the
|
|
@@ -827,6 +843,14 @@ export {
|
|
|
827
843
|
seedGenesis,
|
|
828
844
|
type GenesisWarrantHandle,
|
|
829
845
|
} from "./framework/genesis.js";
|
|
846
|
+
// TOKEN-FREE FOUNDING (identity target model D22) — the founding record and the kernel-appended birth
|
|
847
|
+
// origin a founded child carries instead of a BirthAttestation: operator tooling reads its lineage by them.
|
|
848
|
+
export {
|
|
849
|
+
FoundingPrincipal,
|
|
850
|
+
FOUNDING_PRINCIPAL_ID,
|
|
851
|
+
BirthOrigin,
|
|
852
|
+
BIRTH_ORIGIN_ID,
|
|
853
|
+
} from "./framework/founding_genesis.js";
|
|
830
854
|
// E2E ENCRYPTION (slice 4) — the group-key events (RFC 9420 sender-keys on the ledger).
|
|
831
855
|
export {
|
|
832
856
|
KeyScope,
|
package/src/intent.ts
CHANGED
|
@@ -1136,6 +1136,11 @@ class IntentBuilder<I> {
|
|
|
1136
1136
|
? {}
|
|
1137
1137
|
: {
|
|
1138
1138
|
birthsChild: true,
|
|
1139
|
+
// The composed intent births through the declared birth's lowered plan, so it carries the same
|
|
1140
|
+
// typed-key declaration (the kernel's `nomos:birthKeyType` era key) as that birth directive.
|
|
1141
|
+
...(birthSteps[0]?.directive.birthKey === undefined
|
|
1142
|
+
? {}
|
|
1143
|
+
: { birthKey: birthSteps[0]!.directive.birthKey }),
|
|
1139
1144
|
...(bindBusinessBirthPolicy === undefined
|
|
1140
1145
|
? {}
|
|
1141
1146
|
: { [BUSINESS_BIRTH_POLICY_BINDING]: bindBusinessBirthPolicy }),
|