@githolon/dsl 0.117.1 → 0.119.9

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.
Files changed (35) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/compile_proof.mjs +16 -0
  3. package/dart/CHANGELOG.md +41 -0
  4. package/dart/lib/src/application_status.dart +58 -2
  5. package/dart/lib/src/peer_application_streams.dart +7 -1
  6. package/dart/lib/src/reception/dart_reception.dart +92 -12
  7. package/dart/lib/src/reception/reception_session.dart +10 -2
  8. package/dart/lib/src/replica_readiness.dart +200 -50
  9. package/dart/lib/src/runner/node-entry.mjs +2480 -1021
  10. package/dart/lib/src/sync_status_projection.dart +346 -4
  11. package/dart/lib/src/sync_status_reducer.dart +44 -0
  12. package/dart/lib/src/test_harness.dart +1 -0
  13. package/dart/lib/src/typed_watch_registry.dart +67 -7
  14. package/dart/pubspec.lock +1 -1
  15. package/dart/pubspec.yaml +2 -2
  16. package/dart/test/copy_repair_status_test.dart +238 -0
  17. package/dart/test/reads_never_wait_test.dart +387 -0
  18. package/dart/test/replica_readiness_test.dart +223 -109
  19. package/dart/test/sync_status_reducer_test.dart +39 -0
  20. package/dart/test/typed_watch_registry_test.dart +69 -0
  21. package/dart/test/unshared_work_status_test.dart +269 -0
  22. package/dart_types/CHANGELOG.md +18 -0
  23. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +320 -85
  24. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +57 -1
  25. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +14 -0
  26. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +25 -1
  27. package/dart_types/lib/src/runtime_outcome.dart +17 -0
  28. package/dart_types/pubspec.yaml +1 -1
  29. package/package.json +5 -2
  30. package/src/codegen_ts.ts +7 -8
  31. package/src/compile_package_main.ts +11 -4
  32. package/src/reception_unit_artifact.ts +105 -0
  33. package/src/reception_unit_artifact_contract.ts +220 -0
  34. package/src/reception_unit_context.ts +47 -0
  35. package/src/reception_unit_source.ts +205 -0
@@ -0,0 +1,220 @@
1
+ /** Additive CAP-884 prototype. Content checks do not authenticate law provenance:
2
+ * the caller must obtain this exact unit/template association from installed law. */
3
+ import { receptionSha256, RECEPTION_RUNTIME_CONTRACT, type ReceptionOperation, type ReceptionTemplate } from "./reception_artifact_contract.js";
4
+ export { receptionSha256, RECEPTION_RUNTIME_CONTRACT };
5
+ export const RECEPTION_UNIT_GENERATOR = "nomos-typescript-unit-reception-v1";
6
+ export const UNIT_RECEPTION_ARTIFACT_FORMAT = "nomos.reception-unit-artifact.v1";
7
+ export const CLOSURE_RECEPTION_MANIFEST_FORMAT = "nomos.reception-closure-manifest.v1";
8
+ export const UNIT_RECEPTION_SOURCE_LIMIT = 16 * 1024 * 1024;
9
+ export interface UnitReceptionInput {
10
+ readonly unitKey: string;
11
+ readonly unitHash: string;
12
+ readonly domain: string;
13
+ readonly generator: string;
14
+ readonly runtimeContract: string;
15
+ readonly template: ReceptionTemplate;
16
+ }
17
+ export interface UnitReceptionManifest {
18
+ readonly format: typeof UNIT_RECEPTION_ARTIFACT_FORMAT;
19
+ readonly artifactId: string;
20
+ readonly unitKey: string;
21
+ readonly unitHash: string;
22
+ readonly domain: string;
23
+ readonly generator: string;
24
+ readonly runtimeContract: string;
25
+ readonly entrypoint: "reception-unit.mjs";
26
+ readonly moduleSha256: string;
27
+ readonly factory: string;
28
+ readonly factorySha256: string;
29
+ readonly sourceSha256: string;
30
+ readonly bindingSha256: string;
31
+ readonly protoSha256: string;
32
+ readonly operations: readonly ReceptionOperation[];
33
+ }
34
+ export interface UnitReceptionArtifact { readonly manifest: UnitReceptionManifest; readonly moduleSource: string }
35
+ export interface ClosureReceptionOperation extends ReceptionOperation {
36
+ readonly domain: string;
37
+ readonly unitKey: string;
38
+ readonly unitHash: string;
39
+ readonly artifactId: string;
40
+ readonly moduleSha256: string;
41
+ }
42
+ export interface ClosureReceptionManifest {
43
+ readonly format: typeof CLOSURE_RECEPTION_MANIFEST_FORMAT;
44
+ readonly manifestId: string;
45
+ readonly closure: string;
46
+ readonly generator: string;
47
+ readonly runtimeContract: string;
48
+ readonly units: readonly UnitReceptionManifest[];
49
+ readonly operations: readonly ClosureReceptionOperation[];
50
+ }
51
+ export interface ClosureReceptionManifestInput {
52
+ readonly closure: string;
53
+ readonly generator: string;
54
+ readonly runtimeContract: string;
55
+ readonly units: readonly UnitReceptionManifest[];
56
+ /** Exact public facet coverage supplied by authoritative installed declarations. */
57
+ readonly domains: readonly string[];
58
+ }
59
+ export interface UnitReceptionSelection {
60
+ readonly closure: string;
61
+ readonly domain: string;
62
+ readonly member: string;
63
+ }
64
+ export interface UnitReceptionInvocationBinding extends ClosureReceptionOperation { readonly closure: string }
65
+ export interface PreparedUnitReceptionBinding {
66
+ readonly closure: string;
67
+ readonly unit: UnitReceptionManifest;
68
+ bind(selection: UnitReceptionSelection): UnitReceptionInvocationBinding;
69
+ }
70
+ type UnitExpectation = Pick<UnitReceptionManifest, "unitKey" | "unitHash" | "domain" | "generator" | "runtimeContract">;
71
+ type ClosureExpectation = Pick<ClosureReceptionManifest, "closure" | "generator" | "runtimeContract">;
72
+ const HEX = /^[0-9a-f]{64}$/;
73
+ const compare = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;
74
+ const dangerous = new Set(["constructor", "prototype", "__proto__"]);
75
+ export function unitReceptionUnavailable(message: string): never {
76
+ throw Object.assign(new Error(message), { code: "nomos.reception.unit.unavailable" });
77
+ }
78
+ /** Detach before the first await. No getters, inherited state or mutable caller aliases. */
79
+ export function snapshotUnitReceptionValue<T>(input: T): T {
80
+ const visit = (value: unknown, depth: number): unknown => {
81
+ if (depth > 64) unitReceptionUnavailable("Reception metadata nesting exceeds runtime safety limit");
82
+ if (value === null || value === undefined || ["string", "boolean"].includes(typeof value)) return value;
83
+ if (typeof value === "number" && Number.isFinite(value)) return value;
84
+ if (!value || typeof value !== "object" || ![Object.prototype, Array.prototype, null].includes(Object.getPrototypeOf(value))) unitReceptionUnavailable("invalid Reception metadata value");
85
+ const descriptors = Object.getOwnPropertyDescriptors(value);
86
+ for (const key of Reflect.ownKeys(value)) {
87
+ if (typeof key !== "string" || dangerous.has(key) || descriptors[key]!.get || descriptors[key]!.set) unitReceptionUnavailable("unsafe Reception metadata property");
88
+ }
89
+ if (Array.isArray(value)) {
90
+ const keys = Object.keys(descriptors).filter(key => key !== "length");
91
+ if (keys.some(key => !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length)) unitReceptionUnavailable("invalid Reception array property");
92
+ // Refuse sparse lengths before allocating a second array of that length.
93
+ if (keys.length !== value.length) unitReceptionUnavailable("sparse Reception array");
94
+ return Object.freeze(Array.from({ length: value.length }, (_item, index) => {
95
+ const item = descriptors[String(index)];
96
+ if (!item) unitReceptionUnavailable("sparse Reception array");
97
+ return visit(item.value, depth + 1);
98
+ }));
99
+ }
100
+ return Object.freeze(Object.fromEntries(Object.entries(descriptors).map(([key, descriptor]) => [key, visit(descriptor.value, depth + 1)])));
101
+ };
102
+ return visit(input, 0) as T;
103
+ }
104
+ function record(value: unknown, keys: readonly string[]): asserts value is Record<string, unknown> {
105
+ if (!value || typeof value !== "object" || Array.isArray(value) ||
106
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))) unitReceptionUnavailable("invalid Reception record");
107
+ const descriptors = Object.getOwnPropertyDescriptors(value);
108
+ if (Reflect.ownKeys(value).some(key => typeof key !== "string" || !keys.includes(key) || descriptors[key]!.get || descriptors[key]!.set) ||
109
+ keys.some(key => !Object.hasOwn(descriptors, key))) unitReceptionUnavailable("Reception record has missing or unknown fields");
110
+ }
111
+ export { record as assertUnitReceptionRecord };
112
+ function text(value: unknown, pattern: RegExp, name: string): asserts value is string {
113
+ if (typeof value !== "string" || value.length > 256 || !pattern.test(value) || dangerous.has(value)) unitReceptionUnavailable(`invalid Reception ${name}`);
114
+ }
115
+ function digest(value: unknown): asserts value is string {
116
+ if (typeof value !== "string" || !HEX.test(value)) unitReceptionUnavailable("invalid Reception digest");
117
+ }
118
+ export function validateUnitReceptionIdentity(value: UnitExpectation): void {
119
+ text(value.unitKey, /^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "unit key");
120
+ text(value.domain, /^[A-Za-z0-9][A-Za-z0-9_.-]*$/, "domain");
121
+ digest(value.unitHash);
122
+ if (value.generator !== RECEPTION_UNIT_GENERATOR || value.runtimeContract !== RECEPTION_RUNTIME_CONTRACT) unitReceptionUnavailable("unsupported unit Reception generator/runtime");
123
+ }
124
+ export function canonicalReceptionOperations(value: readonly ReceptionOperation[]): readonly ReceptionOperation[] {
125
+ if (!Array.isArray(value)) unitReceptionUnavailable("invalid Reception operation inventory");
126
+ const seen = new Set<string>();
127
+ const result = value.map(operation => {
128
+ record(operation, ["member", "kind", "selector"]);
129
+ text(operation.member, /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/, "member");
130
+ if (operation.member.split(".").some(part => !part || dangerous.has(part) || part.startsWith("__") || part.startsWith("watch"))) unitReceptionUnavailable("unsupported stateless Reception member");
131
+ if (typeof operation.selector !== "string" || !operation.selector || operation.selector.length > 256 || dangerous.has(operation.selector)) unitReceptionUnavailable("invalid Reception selector");
132
+ if (typeof operation.kind !== "string" || !["offer", "query", "byType", "byId"].includes(operation.kind) || seen.has(operation.member)) unitReceptionUnavailable("duplicate or invalid Reception operation");
133
+ seen.add(operation.member);
134
+ return { member: operation.member, kind: operation.kind, selector: operation.selector } as ReceptionOperation;
135
+ });
136
+ return result.sort((a, b) => compare(a.member, b.member));
137
+ }
138
+ const unitKeys = ["format", "artifactId", "unitKey", "unitHash", "domain", "generator", "runtimeContract", "entrypoint", "moduleSha256", "factory", "factorySha256", "sourceSha256", "bindingSha256", "protoSha256", "operations"];
139
+ function unitPreimage(unit: Omit<UnitReceptionManifest, "artifactId">): unknown {
140
+ return [unit.format, unit.unitKey, unit.unitHash, unit.domain, unit.generator, unit.runtimeContract, unit.entrypoint,
141
+ unit.moduleSha256, unit.factory, unit.factorySha256, unit.sourceSha256, unit.bindingSha256, unit.protoSha256,
142
+ unit.operations.map(operation => [operation.member, operation.kind, operation.selector])];
143
+ }
144
+ export async function unitReceptionArtifactIdentity(unit: Omit<UnitReceptionManifest, "artifactId">): Promise<string> {
145
+ return receptionSha256(JSON.stringify(unitPreimage(unit)));
146
+ }
147
+ export async function verifyUnitReceptionManifest(unit: UnitReceptionManifest, expected?: UnitExpectation): Promise<void> {
148
+ unit = snapshotUnitReceptionValue(unit);
149
+ if (expected) expected = snapshotUnitReceptionValue(expected);
150
+ record(unit, unitKeys);
151
+ validateUnitReceptionIdentity(unit);
152
+ if (unit.format !== UNIT_RECEPTION_ARTIFACT_FORMAT || unit.entrypoint !== "reception-unit.mjs") unitReceptionUnavailable("unsupported unit Reception format");
153
+ text(unit.factory, /^[A-Za-z_$][A-Za-z0-9_$]*$/, "factory");
154
+ for (const hash of [unit.artifactId, unit.moduleSha256, unit.factorySha256, unit.sourceSha256, unit.bindingSha256, unit.protoSha256]) digest(hash);
155
+ const operations = canonicalReceptionOperations(unit.operations);
156
+ if (operations.some((operation, index) => operation.member !== unit.operations[index]!.member)) unitReceptionUnavailable("noncanonical Reception operations");
157
+ if (unit.artifactId !== await unitReceptionArtifactIdentity(unit)) unitReceptionUnavailable("unit Reception identity mismatch");
158
+ if (expected && (["unitKey", "unitHash", "domain", "generator", "runtimeContract"] as const).some(key => unit[key] !== expected[key])) unitReceptionUnavailable("unit Reception provenance mismatch");
159
+ }
160
+ export async function verifyUnitReceptionArtifact(artifact: UnitReceptionArtifact, expected?: UnitExpectation): Promise<void> {
161
+ artifact = snapshotUnitReceptionValue(artifact);
162
+ if (expected) expected = snapshotUnitReceptionValue(expected);
163
+ record(artifact, ["manifest", "moduleSource"]);
164
+ await verifyUnitReceptionManifest(artifact.manifest, expected);
165
+ if (typeof artifact.moduleSource !== "string" || artifact.moduleSource.length > UNIT_RECEPTION_SOURCE_LIMIT || new TextEncoder().encode(artifact.moduleSource).byteLength > UNIT_RECEPTION_SOURCE_LIMIT ||
166
+ await receptionSha256(artifact.moduleSource) !== artifact.manifest.moduleSha256) unitReceptionUnavailable("unit Reception module digest mismatch");
167
+ }
168
+ export function closureReceptionOperations(units: readonly UnitReceptionManifest[]): readonly ClosureReceptionOperation[] {
169
+ return units.flatMap(unit => unit.operations.map(operation => ({ domain: unit.domain, ...operation,
170
+ unitKey: unit.unitKey, unitHash: unit.unitHash, artifactId: unit.artifactId, moduleSha256: unit.moduleSha256 })))
171
+ .sort((a, b) => compare(a.domain, b.domain) || compare(a.member, b.member));
172
+ }
173
+ export async function closureReceptionManifestIdentity(manifest: Omit<ClosureReceptionManifest, "manifestId">): Promise<string> {
174
+ return receptionSha256(JSON.stringify([manifest.format, manifest.closure, manifest.generator, manifest.runtimeContract,
175
+ manifest.units.map(unit => unit.artifactId), manifest.operations.map(operation => [operation.domain, operation.member, operation.kind,
176
+ operation.selector, operation.unitKey, operation.unitHash, operation.artifactId, operation.moduleSha256])]));
177
+ }
178
+ export async function verifyClosureReceptionManifest(manifest: ClosureReceptionManifest, expected?: ClosureExpectation): Promise<void> {
179
+ manifest = snapshotUnitReceptionValue(manifest);
180
+ if (expected) expected = snapshotUnitReceptionValue(expected);
181
+ record(manifest, ["format", "manifestId", "closure", "generator", "runtimeContract", "units", "operations"]);
182
+ digest(manifest.closure); digest(manifest.manifestId);
183
+ if (manifest.format !== CLOSURE_RECEPTION_MANIFEST_FORMAT || manifest.generator !== RECEPTION_UNIT_GENERATOR || manifest.runtimeContract !== RECEPTION_RUNTIME_CONTRACT) unitReceptionUnavailable("unsupported closure Reception format");
184
+ if (!Array.isArray(manifest.units) || manifest.units.length === 0) unitReceptionUnavailable("invalid closure Reception units");
185
+ const keys = new Set<string>(), domains = new Set<string>();
186
+ for (const unit of manifest.units) {
187
+ await verifyUnitReceptionManifest(unit);
188
+ if (unit.generator !== manifest.generator || unit.runtimeContract !== manifest.runtimeContract || keys.has(unit.unitKey) || domains.has(unit.domain)) unitReceptionUnavailable("duplicate or mismatched closure Reception unit/domain");
189
+ keys.add(unit.unitKey); domains.add(unit.domain);
190
+ }
191
+ if (manifest.units.some((unit, index) => index > 0 && compare(manifest.units[index - 1]!.unitKey, unit.unitKey) >= 0)) unitReceptionUnavailable("noncanonical closure Reception units");
192
+ if (!Array.isArray(manifest.operations)) unitReceptionUnavailable("invalid closure Reception operations");
193
+ for (const operation of manifest.operations) record(operation, ["domain", "member", "kind", "selector", "unitKey", "unitHash", "artifactId", "moduleSha256"]);
194
+ const tuple = (operation: ClosureReceptionOperation) => [operation.domain, operation.member, operation.kind, operation.selector, operation.unitKey, operation.unitHash, operation.artifactId, operation.moduleSha256];
195
+ if (JSON.stringify(closureReceptionOperations(manifest.units).map(tuple)) !== JSON.stringify(manifest.operations.map(tuple))) unitReceptionUnavailable("closure Reception operation membership mismatch");
196
+ if (manifest.manifestId !== await closureReceptionManifestIdentity(manifest)) unitReceptionUnavailable("closure Reception identity mismatch");
197
+ if (expected && (["closure", "generator", "runtimeContract"] as const).some(key => manifest[key] !== expected[key])) unitReceptionUnavailable("closure Reception provenance mismatch");
198
+ }
199
+ /** Load-boundary verification; the returned lookup captures a detached immutable snapshot. */
200
+ export async function prepareUnitReceptionBinding(manifest: ClosureReceptionManifest, artifact: UnitReceptionArtifact): Promise<PreparedUnitReceptionBinding> {
201
+ manifest = snapshotUnitReceptionValue(manifest);
202
+ artifact = snapshotUnitReceptionValue(artifact);
203
+ await verifyClosureReceptionManifest(manifest);
204
+ await verifyUnitReceptionArtifact(artifact);
205
+ const found = manifest.units.find(unit => unit.unitKey === artifact.manifest.unitKey);
206
+ if (!found || found.artifactId !== artifact.manifest.artifactId) unitReceptionUnavailable("unit Reception is absent from closure membership");
207
+ const unit = Object.freeze({ ...artifact.manifest, operations: Object.freeze(artifact.manifest.operations.map(operation => Object.freeze({ ...operation }))) });
208
+ const closure = manifest.closure;
209
+ const lookup = new Map(manifest.operations.filter(operation => operation.unitKey === unit.unitKey)
210
+ .map(operation => [operation.member, Object.freeze({ ...operation, closure })]));
211
+ return Object.freeze({ closure, unit, bind(selection: UnitReceptionSelection) {
212
+ if (selection.closure !== closure || selection.domain !== unit.domain) unitReceptionUnavailable("unit Reception invocation closure/domain mismatch");
213
+ const selected = lookup.get(selection.member);
214
+ if (!selected) unitReceptionUnavailable("unit Reception invocation member is absent");
215
+ return selected;
216
+ } });
217
+ }
218
+ export function bindUnitReceptionInvocation(prepared: PreparedUnitReceptionBinding, selection: UnitReceptionSelection): UnitReceptionInvocationBinding {
219
+ return prepared.bind(selection);
220
+ }
@@ -0,0 +1,47 @@
1
+ // A unit Reception's ABI requirements follow the compiler's resolved nominal dependency
2
+ // edges. Package membership alone never makes an unrelated domain part of its context.
3
+ import type { DomainModule } from "./codegen_dart.js";
4
+ import type { LawGraph } from "./law_graph.js";
5
+ import type { LawDependencyIndex } from "./law_dependency_index.js";
6
+ import { requiredKernelContracts } from "./kernel_capabilities.js";
7
+
8
+ type Node = LawGraph["nodes"][number];
9
+
10
+ /** Reuse the exact graphs, emitted paths and resolver-backed dependency index used to lower
11
+ * the composed package. Ownership is by graph/node identity, never a first matching label.
12
+ * This selects compiler context only; it changes no law identities or admission decisions. */
13
+ export class ReceptionUnitContextIndex {
14
+ private readonly owners = new Map<Node, DomainModule>();
15
+ private readonly modules = new Map<LawGraph, DomainModule>();
16
+
17
+ constructor(
18
+ composition: readonly { readonly module: DomainModule; readonly graph: LawGraph }[],
19
+ private readonly dependencies: LawDependencyIndex,
20
+ private readonly emittedPaths: ReadonlyMap<Node, string>,
21
+ ) {
22
+ for (const { module, graph } of composition) {
23
+ if (this.modules.has(graph)) throw new Error("Reception unit context has duplicate graph ownership");
24
+ this.modules.set(graph, module);
25
+ for (const node of graph.nodes) {
26
+ if (this.owners.has(node)) throw new Error("Reception unit context has duplicate node ownership");
27
+ this.owners.set(node, module);
28
+ }
29
+ }
30
+ }
31
+
32
+ forGraph(graph: LawGraph) {
33
+ const owner = this.modules.get(graph);
34
+ if (!owner) throw new Error("Reception unit context requires an owned compiler graph");
35
+ // Source-only anonymous occurrences do not emit prims. The shared dependency index still
36
+ // refuses any semantic edge that reaches one without an emitted path.
37
+ const slice = this.dependencies.select(graph.nodes.filter(node => this.emittedPaths.has(node)), true);
38
+ const modules = new Set<DomainModule>([owner]);
39
+ for (const node of slice.nodes) {
40
+ const dependency = this.owners.get(node);
41
+ if (!dependency) throw new Error("Reception unit dependency has no exact module owner");
42
+ modules.add(dependency);
43
+ }
44
+ const selectedModules = [...modules].sort((a, b) => a.name.localeCompare(b.name));
45
+ return { modules: selectedModules, kernelContracts: requiredKernelContracts(selectedModules) };
46
+ }
47
+ }
@@ -0,0 +1,205 @@
1
+ /** Verify compiler metadata from one actually installed law unit. Installed membership/head
2
+ * authentication belongs to the source port; this verifier binds its opaque key/hash to the
3
+ * canonical manifest and owned asset bytes. It never executes source or infers a unit from a domain. */
4
+ import { lawUnitHashOf, parseUnitManifest, sha256Hex } from "./release_publication.js";
5
+ import { RECEPTION_GENERATOR, RECEPTION_RUNTIME_CONTRACT, RECEPTION_SOURCE_BYTES_LIMIT,
6
+ validateReceptionGenerationInput } from "./reception_artifact_contract.js";
7
+ import { RECEPTION_UNIT_GENERATOR, snapshotUnitReceptionValue, type UnitReceptionInput } from "./reception_unit_artifact_contract.js";
8
+ import { usdaUnquote } from "./usda_text.js";
9
+
10
+ export interface InstalledReceptionUnit { readonly unitKey: string; readonly unitHash: string }
11
+ export interface UnitReceptionSourceLimits {
12
+ readonly maxManifestBytes: number;
13
+ readonly maxDomainAssetBytes: number;
14
+ /** Per-unit decoded compiler sources; the existing template decoder also enforces its 16MiB ceiling. */
15
+ readonly maxDecodedSourceBytes: number;
16
+ }
17
+ export interface InspectInstalledUnitReceptionManifestInput {
18
+ readonly installed: InstalledReceptionUnit;
19
+ readonly expectedDomain?: string;
20
+ readonly manifestBytes: Uint8Array;
21
+ readonly limits: UnitReceptionSourceLimits;
22
+ }
23
+ interface InspectedUnitBase {
24
+ readonly unitKey: string;
25
+ readonly unitHash: string;
26
+ readonly manifestSha256: string;
27
+ readonly manifestBytes: number;
28
+ readonly assets: readonly { readonly logicalPath: string; readonly sha256: string }[];
29
+ readonly routes: readonly { readonly selector: string; readonly planSha256: string }[];
30
+ }
31
+ export type InspectedUnitReceptionManifest = InspectedUnitBase & (
32
+ { readonly kind: "domain"; readonly domain: string; readonly domainAsset: { readonly logicalPath: string; readonly sha256: string } }
33
+ | { readonly kind: "framework" | "composition" }
34
+ );
35
+ export interface VerifyInstalledUnitReceptionSourceInput extends InspectInstalledUnitReceptionManifestInput {
36
+ readonly generator: string;
37
+ readonly runtimeContract: string;
38
+ readonly domainAsset?: { readonly logicalPath: string; readonly bytes: Uint8Array };
39
+ }
40
+ export type VerifiedInstalledUnitReceptionSource =
41
+ | { readonly kind: "public"; readonly unit: InspectedUnitReceptionManifest & { readonly kind: "domain" }; readonly domainAssetBytes: number; readonly input: UnitReceptionInput }
42
+ | { readonly kind: "private"; readonly unit: InspectedUnitReceptionManifest & { readonly kind: "domain" }; readonly domainAssetBytes: number }
43
+ | { readonly kind: "no-runtime"; readonly unit: InspectedUnitReceptionManifest & { readonly kind: "framework" | "composition" } };
44
+ export type UnitReceptionSourceFailureCode = "input" | "limit" | "manifest" | "membership" | "asset" | "domain" | "template-unavailable" | "metadata";
45
+ export class UnitReceptionSourceUnavailable extends Error {
46
+ readonly code: UnitReceptionSourceFailureCode;
47
+ constructor(code: UnitReceptionSourceFailureCode) {
48
+ super(`nomos.reception.unit_source_${code}`);
49
+ this.code = code;
50
+ }
51
+ }
52
+ function refuse(code: UnitReceptionSourceFailureCode): never { throw new UnitReceptionSourceUnavailable(code); }
53
+ const HEX = /^[0-9a-f]{64}$/;
54
+ const DOMAIN = /^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/;
55
+ const safePath = (path: string) => path.length > 0 && path.length <= 512 && !path.startsWith("/") && !path.includes("\\")
56
+ && !/[\x00-\x1f\x7f]/.test(path) && path.split("/").every(part => part !== "" && part !== "." && part !== "..");
57
+ function record(value: unknown, keys: readonly string[]): void {
58
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) refuse("input");
59
+ const properties = Object.getOwnPropertyDescriptors(value);
60
+ if (Reflect.ownKeys(properties).some(key => typeof key !== "string" || !keys.includes(key)) || keys.some(key => !Object.hasOwn(properties, key))
61
+ || Object.values(properties).some(property => property.get || property.set)) refuse("input");
62
+ }
63
+ function limitsOf(input: UnitReceptionSourceLimits): UnitReceptionSourceLimits {
64
+ record(input, ["maxManifestBytes", "maxDomainAssetBytes", "maxDecodedSourceBytes"]);
65
+ const result = snapshotUnitReceptionValue(input);
66
+ if (Object.values(result).some(value => !Number.isSafeInteger(value) || value <= 0) || result.maxDecodedSourceBytes > RECEPTION_SOURCE_BYTES_LIMIT) refuse("limit");
67
+ return result;
68
+ }
69
+ function bytesOf(value: Uint8Array, limit: number): Uint8Array {
70
+ if (!ArrayBuffer.isView(value) || !(value instanceof Uint8Array)) refuse("input");
71
+ // Read/copy internal typed-array storage, never a caller's iterator, slice, buffer or
72
+ // byteLength accessor. Enumerating every byte's property descriptor would amplify memory.
73
+ const size = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "byteLength")!.get!.call(value) as number;
74
+ if (size === 0 || size > limit) refuse("limit");
75
+ return new Uint8Array(value);
76
+ }
77
+ function detached(input: InspectInstalledUnitReceptionManifestInput, verify: boolean) {
78
+ if (!input || typeof input !== "object") refuse("input");
79
+ const keys = ["installed", "manifestBytes", "limits", ...(Object.hasOwn(input, "expectedDomain") ? ["expectedDomain"] : []),
80
+ ...(verify ? ["generator", "runtimeContract", ...(Object.hasOwn(input, "domainAsset") ? ["domainAsset"] : [])] : [])];
81
+ record(input, keys);
82
+ record(input.installed, ["unitKey", "unitHash"]);
83
+ const installed = snapshotUnitReceptionValue(input.installed);
84
+ if (typeof installed.unitKey !== "string" || !installed.unitKey || installed.unitKey.length > 256 || typeof installed.unitHash !== "string" || !HEX.test(installed.unitHash)) refuse("membership");
85
+ const limits = limitsOf(input.limits);
86
+ const expectedDomain = input.expectedDomain;
87
+ if (expectedDomain !== undefined && (typeof expectedDomain !== "string" || !DOMAIN.test(expectedDomain))) refuse("domain");
88
+ const manifestBytes = bytesOf(input.manifestBytes, limits.maxManifestBytes);
89
+ return { installed, limits, manifestBytes, expectedDomain };
90
+ }
91
+ const decode = (bytes: Uint8Array, code: UnitReceptionSourceFailureCode): string => {
92
+ // Preserve a BOM so the canonical manifest round trip refuses those extra bytes.
93
+ try { return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes); } catch { return refuse(code); }
94
+ };
95
+ /** Inspect the manifest before the source port fetches its explicitly inventoried domain asset. */
96
+ export async function inspectInstalledUnitReceptionManifest(input: InspectInstalledUnitReceptionManifestInput): Promise<InspectedUnitReceptionManifest> {
97
+ const source = detached(input, false);
98
+ let parsed: Awaited<ReturnType<typeof parseUnitManifest>>;
99
+ try { parsed = await parseUnitManifest(decode(source.manifestBytes, "manifest"), source.installed.unitKey); } catch { return refuse("manifest"); }
100
+ if (parsed.key !== source.installed.unitKey || parsed.hash !== source.installed.unitHash) refuse("membership");
101
+ const ordered = (names: readonly string[]) => names.every((name, index) => index === 0 || names[index - 1]! < name);
102
+ if (!HEX.test(parsed.hash) || !ordered(parsed.assets.map(asset => asset.logicalPath)) || !ordered(parsed.routes.map(route => route.selector))
103
+ || parsed.assets.some(asset => !safePath(asset.logicalPath) || asset.logicalPath === "unit.usda" || !HEX.test(asset.sha256))
104
+ || parsed.routes.some(route => !route.selector || route.selector.length > 512 || /[\x00-\x1f\x7f]/.test(route.selector) || !HEX.test(route.planSha256))) refuse("manifest");
105
+ if (await lawUnitHashOf(parsed.key, parsed.assets, parsed.routes) !== source.installed.unitHash) refuse("membership");
106
+ const base: InspectedUnitBase = { unitKey: parsed.key, unitHash: parsed.hash, manifestSha256: await sha256Hex(source.manifestBytes),
107
+ manifestBytes: source.manifestBytes.byteLength, assets: parsed.assets, routes: parsed.routes };
108
+ const layers = parsed.assets.filter(asset => /^domains\/[^/]+\.usda$/.test(asset.logicalPath));
109
+ if (parsed.kind !== "domain") {
110
+ if (source.expectedDomain !== undefined || layers.length !== 0) refuse("domain");
111
+ return snapshotUnitReceptionValue({ ...base, kind: parsed.kind });
112
+ }
113
+ if (!parsed.domain || !DOMAIN.test(parsed.domain) || layers.length !== 1 || (source.expectedDomain !== undefined && source.expectedDomain !== parsed.domain)) refuse("domain");
114
+ return snapshotUnitReceptionValue({ ...base, kind: "domain", domain: parsed.domain, domainAsset: layers[0]! });
115
+ }
116
+ /** Inspect only the owning domain's compiler metadata declarations, in the same USDA dialect
117
+ * the existing template validator accepts. There is no tenant expression or business-plan parsing. */
118
+ function domainMetadata(layer: string): { label: string; private: boolean; hasTemplate: boolean } {
119
+ const frames: string[] = [];
120
+ let pending: { name: string; type: string } | undefined;
121
+ let dictionaryDepth = 0;
122
+ let hasTemplate = false;
123
+ const domains = new Map<string, { label?: string; visibility?: string }>();
124
+ for (const raw of layer.split("\n")) {
125
+ const line = raw.trim();
126
+ if (line.endsWith("= {")) { dictionaryDepth++; continue; }
127
+ if (dictionaryDepth) { if (line === "}") dictionaryDepth--; continue; }
128
+ const prim = /^(?:def|over)\s+(?:(\w+)\s+)?"([^"\\]+)"(?:\s*\()?\s*$/.exec(line);
129
+ if (prim) { pending = { name: prim[2]!, type: prim[1] ?? "" }; continue; }
130
+ if (line === "{") {
131
+ if (!pending) continue;
132
+ frames.push(pending.name);
133
+ if (pending.type === "NomosReceptionTemplate") hasTemplate = true;
134
+ pending = undefined;
135
+ const path = `/${frames.join("/")}`;
136
+ if (/^\/Nomos\/Domains\/d_[0-9a-f]{16}$/.test(path)) {
137
+ if (domains.has(path)) refuse("domain");
138
+ domains.set(path, {});
139
+ }
140
+ continue;
141
+ }
142
+ if (line === "}") { frames.pop(); continue; }
143
+ const domain = domains.get(`/${frames.join("/")}`);
144
+ if (!domain) continue;
145
+ const field = /^(?:custom\s+)?(?:uniform\s+)?(?:string|token)\s+nomos:(label|receptionVisibility)\s*=\s*("(?:[^"\\]|\\.)*")$/.exec(line);
146
+ if (field) {
147
+ const key = field[1] === "label" ? "label" : "visibility";
148
+ if (domain[key] !== undefined) refuse("domain");
149
+ domain[key] = usdaUnquote(field[2]!);
150
+ }
151
+ }
152
+ const domain = [...domains.values()][0];
153
+ if (domains.size !== 1 || !domain?.label || (domain.visibility !== undefined && domain.visibility !== "private" && domain.visibility !== "public")) refuse("domain");
154
+ return { label: domain.label, private: domain.visibility === "private", hasTemplate };
155
+ }
156
+ function preflightSources(layer: string, limit: number): void {
157
+ let total = 0;
158
+ for (const match of layer.matchAll(/^\s*(?:custom\s+)?(?:uniform\s+)?string\s+nomos:(sourcesDecodedBytes|factorySourceBase64|clientSourceBase64|operationBindingBase64|operationProtoBase64)\s*=\s*("(?:[^"\\]|\\.)*")\s*$/gm)) {
159
+ const value = usdaUnquote(match[2]!);
160
+ const bytes = match[1] === "sourcesDecodedBytes" ? Number(value) : Math.ceil(value.length * 3 / 4);
161
+ if ((match[1] === "sourcesDecodedBytes" && !/^[1-9][0-9]*$/.test(value)) || !Number.isSafeInteger(bytes) || bytes > limit - total) refuse("limit");
162
+ total += bytes;
163
+ }
164
+ }
165
+ /** Reverify the raw manifest instead of trusting an arbitrary caller's inspected object. */
166
+ export async function verifyInstalledUnitReceptionSource(input: VerifyInstalledUnitReceptionSourceInput): Promise<VerifiedInstalledUnitReceptionSource> {
167
+ const source = detached(input, true);
168
+ const generator = input.generator, runtimeContract = input.runtimeContract;
169
+ if (generator !== RECEPTION_UNIT_GENERATOR || runtimeContract !== RECEPTION_RUNTIME_CONTRACT) refuse("input");
170
+ let domainAsset: { logicalPath: string; bytes: Uint8Array } | undefined;
171
+ if (input.domainAsset !== undefined) {
172
+ record(input.domainAsset, ["logicalPath", "bytes"]);
173
+ if (typeof input.domainAsset.logicalPath !== "string" || !safePath(input.domainAsset.logicalPath)) refuse("asset");
174
+ domainAsset = { logicalPath: input.domainAsset.logicalPath, bytes: bytesOf(input.domainAsset.bytes, source.limits.maxDomainAssetBytes) };
175
+ }
176
+ const unit = await inspectInstalledUnitReceptionManifest({ installed: source.installed, manifestBytes: source.manifestBytes, limits: source.limits,
177
+ ...(source.expectedDomain === undefined ? {} : { expectedDomain: source.expectedDomain }) });
178
+ if (unit.kind !== "domain") {
179
+ if (domainAsset !== undefined) refuse("asset");
180
+ return snapshotUnitReceptionValue({ kind: "no-runtime", unit });
181
+ }
182
+ if (!domainAsset || domainAsset.logicalPath !== unit.domainAsset.logicalPath || await sha256Hex(domainAsset.bytes) !== unit.domainAsset.sha256) refuse("asset");
183
+ const layer = decode(domainAsset.bytes, "metadata");
184
+ const declaration = domainMetadata(layer);
185
+ if (declaration.label !== unit.domain) refuse("domain");
186
+ if (declaration.private) return snapshotUnitReceptionValue({ kind: "private", unit, domainAssetBytes: domainAsset.bytes.byteLength });
187
+ if (!declaration.hasTemplate) refuse("template-unavailable");
188
+ preflightSources(layer, source.limits.maxDecodedSourceBytes);
189
+ try {
190
+ // This existing validator only shape-checks its identity argument. The unit hash
191
+ // is a local validation placeholder, never a claimed installed closure binding.
192
+ const templates = await validateReceptionGenerationInput({ closure: unit.unitHash, generator: RECEPTION_GENERATOR,
193
+ runtimeContract, semanticLayer: layer, domains: [unit.domain] });
194
+ if (templates.length !== 1 || templates[0]!.domain !== unit.domain) refuse("metadata");
195
+ const template = templates[0]!;
196
+ const decoded = [template.factorySource, template.clientSource, template.operationBinding, template.operationProto]
197
+ .reduce((total, text) => total + new TextEncoder().encode(text).byteLength, 0);
198
+ if (decoded > source.limits.maxDecodedSourceBytes) refuse("limit");
199
+ return snapshotUnitReceptionValue({ kind: "public", unit, domainAssetBytes: domainAsset.bytes.byteLength,
200
+ input: { unitKey: unit.unitKey, unitHash: unit.unitHash, domain: unit.domain, generator, runtimeContract, template } });
201
+ } catch (error) {
202
+ if (error instanceof UnitReceptionSourceUnavailable) throw error;
203
+ return refuse("metadata");
204
+ }
205
+ }