halfcode-compiler.xnl 0.2.8 → 0.3.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/README.md +5 -3
- package/dist/application-assembly.d.ts +1 -1
- package/dist/application-assembly.js +1 -1
- package/dist/authoring-runtime.d.ts +167 -1
- package/dist/authoring-runtime.js +2 -2
- package/dist/effective-content-identities-CV6XFBZy.js +118 -0
- package/dist/effective-content-identities-drMRhYE2.d.ts +14 -0
- package/dist/{index-B515mMkO.d.ts → index-CR7wJSSX.d.ts} +10 -6
- package/dist/{index-Cqh9pEBa.d.ts → index-DEikbOFh.d.ts} +2 -1
- package/dist/{index-CF4nEkOi.d.ts → index-DI3WtuXq.d.ts} +1 -1
- package/dist/index-DboIOEr8.d.ts +474 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +7 -5
- package/dist/kind-definition.d.ts +118 -10
- package/dist/kind-definition.js +826 -15
- package/dist/resource-core-Brj8naBr.d.ts +47 -0
- package/dist/resource-core.d.ts +4 -3
- package/dist/resource-core.js +40 -141
- package/dist/resource-projection.d.ts +1 -1
- package/dist/skill-capsule.d.ts +2 -2
- package/dist/skill-capsule.js +1 -1
- package/dist/{src-3pnDAjBD.js → src-B0Hja_dk.js} +5 -7
- package/dist/{src-CaQ25rMj.js → src-DLJUCH0M.js} +11 -7
- package/dist/src-hrUy4DJ9.js +640 -0
- package/dist/{src-D3qSzGFn.js → src-jggOQtq3.js} +511 -113
- package/dist/system-skills/resource-dsl/KindDefinitions/SkillCapsule/manifest.xnl +15 -3
- package/dist/system-skills/resource-dsl/KindDefinitions/WikiPage/manifest.xnl +15 -3
- package/dist/system-skills/resource-dsl/SkillCapsules/System/SKILL.metadata.yaml +1 -0
- package/dist/system-skills/resource-dsl/SkillCapsules/System/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/SkillCapsules/System/resource-mappings.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/documents/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/files/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/index/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/language/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/migration/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/projections/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/WikiPages/skill-capsules/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/documents.md +33 -17
- package/dist/system-skills/resource-dsl/examples/function.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/kind-definition.xnl +15 -3
- package/dist/system-skills/resource-dsl/examples/resource-mappings.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/resource-package.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/skill-capsule-authoring.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/skill-capsule-devops.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/skill-capsule-kind-definition.xnl +14 -3
- package/dist/system-skills/resource-dsl/examples/skill-capsule-resource-dsl.xnl +1 -1
- package/dist/system-skills/resource-dsl/examples/skill-capsule-run.xnl +1 -1
- package/dist/system-skills/resource-dsl/index.md +5 -5
- package/dist/system-skills/resource-dsl/language.md +10 -8
- package/dist/system-skills/resource-dsl/manifest.xnl +1 -1
- package/dist/system-skills/resource-dsl/migration.md +6 -3
- package/dist/system-skills/resource-dsl/projections.md +7 -6
- package/dist/system-skills/resource-dsl/skill-capsules.md +8 -7
- package/dist/system-skills/sys-halfcode-resource-dsl.plan.js +84 -59
- package/dist/testing.js +1 -1
- package/package.json +6 -2
- package/dist/index-CDIT65pI.d.ts +0 -192
- package/dist/resource-core-C8IonwKw.d.ts +0 -41
- package/dist/src-B87eJQat.js +0 -39
package/dist/kind-definition.js
CHANGED
|
@@ -1,34 +1,845 @@
|
|
|
1
|
+
import { C as digestCanonical, S as compareCodeUnits, s as CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS } from "./src-jggOQtq3.js";
|
|
2
|
+
//#region ../kind-definition/src/versioned-contracts.ts
|
|
3
|
+
var KindContractRegistry = class {
|
|
4
|
+
ownersByKind = /* @__PURE__ */ new Map();
|
|
5
|
+
ownersBySubject = /* @__PURE__ */ new Map();
|
|
6
|
+
revisionsBySubject = /* @__PURE__ */ new Map();
|
|
7
|
+
registerOwner(input) {
|
|
8
|
+
const owner = freezeOwner(input);
|
|
9
|
+
const byKind = this.ownersByKind.get(owner.kind);
|
|
10
|
+
const bySubject = this.ownersBySubject.get(owner.subjectFqn);
|
|
11
|
+
if (byKind && !sameOwner(byKind, owner) || bySubject && !sameOwner(bySubject, owner)) fail$1("KIND_SUBJECT_OWNER_CONFLICT", `Kind '${owner.kind}' or subject '${owner.subjectFqn}' already has a different owner.`);
|
|
12
|
+
this.ownersByKind.set(owner.kind, owner);
|
|
13
|
+
this.ownersBySubject.set(owner.subjectFqn, owner);
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
registerRevision(input) {
|
|
17
|
+
const revision = freezeRevision(input);
|
|
18
|
+
const owner = this.ownersBySubject.get(revision.subjectFqn);
|
|
19
|
+
if (!owner || owner.kind !== revision.kind) fail$1("KIND_SPEC_OWNER_NOT_ADMITTED", `Subject '${revision.subjectFqn}' has no admitted owner for Kind '${revision.kind}'.`);
|
|
20
|
+
if (revision.sourceContractFingerprint !== owner.sourceContract.sourceContractFingerprint) fail$1("KIND_SPEC_SOURCE_CONTRACT_MISMATCH", `Revision '${revision.subjectFqn}@${revision.specVersion}' does not bind its admitted owner source contract.`);
|
|
21
|
+
const revisions = this.revisionsBySubject.get(revision.subjectFqn) ?? /* @__PURE__ */ new Map();
|
|
22
|
+
const previous = revisions.get(revision.specVersion);
|
|
23
|
+
if (previous) {
|
|
24
|
+
if (previous.contractFingerprint !== revision.contractFingerprint || previous.schemaFingerprint !== revision.schemaFingerprint || digestCanonical(previous.specSchema) !== digestCanonical(revision.specSchema)) fail$1("KIND_SPEC_CONTRACT_CONFLICT", `Revision '${revision.subjectFqn}@${revision.specVersion}' is immutable.`);
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
revisions.set(revision.specVersion, revision);
|
|
28
|
+
this.revisionsBySubject.set(revision.subjectFqn, revisions);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
subject(kind) {
|
|
32
|
+
const value = this.ownersByKind.get(nonEmpty(kind, "kind"));
|
|
33
|
+
if (!value) fail$1("KIND_SUBJECT_MISSING", `No admitted subject owns Kind '${kind}'.`);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
owner(subjectFqn) {
|
|
37
|
+
const value = this.ownersBySubject.get(nonEmpty(subjectFqn, "subjectFqn"));
|
|
38
|
+
if (!value) fail$1("KIND_SUBJECT_MISSING", `No admitted owner exists for subject '${subjectFqn}'.`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
revision(subjectFqn, specVersion) {
|
|
42
|
+
const version = exactSpecVersion(specVersion);
|
|
43
|
+
const value = this.revisionsBySubject.get(nonEmpty(subjectFqn, "subjectFqn"))?.get(version);
|
|
44
|
+
if (!value) fail$1("KIND_SPEC_REVISION_MISSING", `Revision '${subjectFqn}@${version}' is not registered.`);
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
owners() {
|
|
48
|
+
return Object.freeze([...this.ownersBySubject.values()].sort((left, right) => compare(left.subjectFqn, right.subjectFqn)));
|
|
49
|
+
}
|
|
50
|
+
revisions(subjectFqn) {
|
|
51
|
+
const values = subjectFqn ? [...this.revisionsBySubject.get(subjectFqn)?.values() ?? []] : [...this.revisionsBySubject.values()].flatMap((revisions) => [...revisions.values()]);
|
|
52
|
+
return Object.freeze(values.sort((left, right) => compare(left.subjectFqn, right.subjectFqn) || left.specVersion - right.specVersion));
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var KindWriterRegistry = class {
|
|
56
|
+
contracts;
|
|
57
|
+
registrations = /* @__PURE__ */ new Map();
|
|
58
|
+
constructor(contracts) {
|
|
59
|
+
this.contracts = contracts;
|
|
60
|
+
}
|
|
61
|
+
register(input) {
|
|
62
|
+
const subjectFqn = nonEmpty(input.subjectFqn, "writer.subjectFqn");
|
|
63
|
+
const writerSpecVersion = exactSpecVersion(input.writerSpecVersion);
|
|
64
|
+
const contractFingerprint = validateFingerprint(input.contractFingerprint, "writer.contractFingerprint");
|
|
65
|
+
if (typeof input.write !== "function") fail$1("KIND_WRITER_INVALID", "Writer registration requires a write function.");
|
|
66
|
+
if (this.contracts.revision(subjectFqn, writerSpecVersion).contractFingerprint !== contractFingerprint) fail$1("KIND_WRITER_CONTRACT_MISMATCH", `Writer '${subjectFqn}@${writerSpecVersion}' contract fingerprint does not match its revision.`);
|
|
67
|
+
const key = revisionKey(subjectFqn, writerSpecVersion);
|
|
68
|
+
const previous = this.registrations.get(key);
|
|
69
|
+
if (previous && (previous.contractFingerprint !== contractFingerprint || previous.write !== input.write)) fail$1("KIND_WRITER_REGISTRATION_CONFLICT", `Writer '${key}' is already registered with a different implementation.`);
|
|
70
|
+
this.registrations.set(key, Object.freeze({
|
|
71
|
+
subjectFqn,
|
|
72
|
+
writerSpecVersion,
|
|
73
|
+
contractFingerprint,
|
|
74
|
+
write: input.write
|
|
75
|
+
}));
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
require(subjectFqn, writerSpecVersion) {
|
|
79
|
+
const key = revisionKey(nonEmpty(subjectFqn, "subjectFqn"), exactSpecVersion(writerSpecVersion));
|
|
80
|
+
const writer = this.registrations.get(key);
|
|
81
|
+
if (!writer) fail$1("KIND_WRITER_MISSING", `Writer '${key}' is not registered.`);
|
|
82
|
+
return writer;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var KindReaderRegistry = class {
|
|
86
|
+
registrations = /* @__PURE__ */ new Map();
|
|
87
|
+
register(input) {
|
|
88
|
+
const registration = freezeReader(input);
|
|
89
|
+
const previous = this.registrations.get(registration.readerId);
|
|
90
|
+
if (previous && !sameReader(previous, registration)) fail$1("KIND_READER_REGISTRATION_CONFLICT", `Reader '${registration.readerId}' is already registered with a different contract.`);
|
|
91
|
+
this.registrations.set(registration.readerId, registration);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
require(target) {
|
|
95
|
+
const exactTarget = freezeTarget(target);
|
|
96
|
+
const reader = this.registrations.get(exactTarget.readerId);
|
|
97
|
+
if (!reader) fail$1("KIND_READER_MISSING", `Reader '${exactTarget.readerId}' is not registered.`);
|
|
98
|
+
if (reader.subjectFqn !== exactTarget.subjectFqn || reader.readerSpecVersion !== exactTarget.readerSpecVersion) fail$1("KIND_READER_TARGET_MISMATCH", `Reader '${exactTarget.readerId}' does not implement the requested subject/version.`);
|
|
99
|
+
if (reader.contractFingerprint !== exactTarget.contractFingerprint || reader.readerImplementationFingerprint !== exactTarget.readerImplementationFingerprint) fail$1("KIND_READER_FINGERPRINT_MISMATCH", `Reader '${exactTarget.readerId}' fingerprint does not match its target.`);
|
|
100
|
+
return reader;
|
|
101
|
+
}
|
|
102
|
+
all() {
|
|
103
|
+
return Object.freeze([...this.registrations.values()].sort((left, right) => compare(left.readerId, right.readerId)));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var ExactReaderProfile = class {
|
|
107
|
+
profileId;
|
|
108
|
+
readers;
|
|
109
|
+
constructor(profileId, targets) {
|
|
110
|
+
this.profileId = profileId;
|
|
111
|
+
nonEmpty(profileId, "profileId");
|
|
112
|
+
const readers = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const input of targets) {
|
|
114
|
+
const target = freezeTarget(input);
|
|
115
|
+
if (readers.has(target.subjectFqn)) fail$1("KIND_READER_TARGET_CONFLICT", `Profile '${profileId}' declares subject '${target.subjectFqn}' more than once.`);
|
|
116
|
+
readers.set(target.subjectFqn, target);
|
|
117
|
+
}
|
|
118
|
+
this.readers = readonlyMap$1(readers);
|
|
119
|
+
Object.freeze(this);
|
|
120
|
+
}
|
|
121
|
+
readerFor(subjectFqn) {
|
|
122
|
+
const target = this.readers.get(subjectFqn);
|
|
123
|
+
if (!target) fail$1("KIND_READER_TARGET_MISSING", `Profile '${this.profileId}' has no reader for '${subjectFqn}'.`);
|
|
124
|
+
return target;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
function createReaderProfile(profileId, targets) {
|
|
128
|
+
return new ExactReaderProfile(profileId, targets);
|
|
129
|
+
}
|
|
130
|
+
var SpecResolutionRegistry = class {
|
|
131
|
+
contracts;
|
|
132
|
+
definitions = [];
|
|
133
|
+
ids = /* @__PURE__ */ new Set();
|
|
134
|
+
constructor(contracts) {
|
|
135
|
+
this.contracts = contracts;
|
|
136
|
+
}
|
|
137
|
+
register(input) {
|
|
138
|
+
const definition = freezeResolution(input);
|
|
139
|
+
if (this.ids.has(definition.id)) fail$1("SPEC_RESOLUTION_ID_CONFLICT", `Resolution '${definition.id}' is already registered.`);
|
|
140
|
+
const owner = this.contracts.owner(definition.subjectFqn);
|
|
141
|
+
if (owner.ownerPackageId !== definition.ownerPackageId || owner.ownerPackageFingerprint !== definition.ownerPackageFingerprint) fail$1("SPEC_RESOLUTION_OWNER_NOT_ADMITTED", `Resolution '${definition.id}' is not provided by admitted owner '${owner.ownerPackageId}'.`);
|
|
142
|
+
const writer = this.contracts.revision(definition.subjectFqn, definition.writerSpecVersion);
|
|
143
|
+
const reader = this.contracts.revision(definition.subjectFqn, definition.readerSpecVersion);
|
|
144
|
+
if (writer.contractFingerprint !== definition.writerContractFingerprint || reader.contractFingerprint !== definition.readerContractFingerprint) fail$1("SPEC_RESOLUTION_ENDPOINT_MISMATCH", `Resolution '${definition.id}' endpoint fingerprint does not match its revisions.`);
|
|
145
|
+
if (definition.writerSpecVersion === definition.readerSpecVersion) fail$1("SPEC_RESOLUTION_IDENTITY_EXPLICIT", "Same-version resolution is implicit and must not register executable code.");
|
|
146
|
+
const sameEndpoints = this.definitions.find((candidate) => candidate.subjectFqn === definition.subjectFqn && candidate.writerSpecVersion === definition.writerSpecVersion && candidate.readerSpecVersion === definition.readerSpecVersion);
|
|
147
|
+
if (sameEndpoints) fail$1("SPEC_RESOLUTION_EDGE_CONFLICT", `Resolution edge '${definition.subjectFqn}' ${definition.writerSpecVersion}→${definition.readerSpecVersion} is already owned by '${sameEndpoints.id}'.`);
|
|
148
|
+
this.ids.add(definition.id);
|
|
149
|
+
this.definitions.push(definition);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
plan(subjectFqn, writerSpecVersion, readerSpecVersion) {
|
|
153
|
+
const from = exactSpecVersion(writerSpecVersion);
|
|
154
|
+
const to = exactSpecVersion(readerSpecVersion);
|
|
155
|
+
this.contracts.revision(subjectFqn, from);
|
|
156
|
+
this.contracts.revision(subjectFqn, to);
|
|
157
|
+
if (from === to) return Object.freeze([]);
|
|
158
|
+
this.validateGraph(subjectFqn);
|
|
159
|
+
const paths = [];
|
|
160
|
+
const visit = (version, path, seen) => {
|
|
161
|
+
if (paths.length > 1) return;
|
|
162
|
+
if (version === to) {
|
|
163
|
+
paths.push(path);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
for (const edge of this.outgoing(subjectFqn, version)) {
|
|
167
|
+
if (seen.has(edge.readerSpecVersion)) continue;
|
|
168
|
+
visit(edge.readerSpecVersion, [...path, edge], /* @__PURE__ */ new Set([...seen, edge.readerSpecVersion]));
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
visit(from, [], /* @__PURE__ */ new Set([from]));
|
|
172
|
+
if (paths.length === 0) fail$1("SPEC_RESOLUTION_PATH_MISSING", `No resolution path exists for '${subjectFqn}' ${from}→${to}.`);
|
|
173
|
+
if (paths.length > 1) fail$1("SPEC_RESOLUTION_PATH_AMBIGUOUS", `More than one resolution path exists for '${subjectFqn}' ${from}→${to}.`);
|
|
174
|
+
return Object.freeze([...paths[0]]);
|
|
175
|
+
}
|
|
176
|
+
validateGraph(subjectFqn) {
|
|
177
|
+
const subjects = subjectFqn ? [subjectFqn] : [...new Set(this.definitions.map((definition) => definition.subjectFqn))];
|
|
178
|
+
for (const subject of subjects) {
|
|
179
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
180
|
+
const visited = /* @__PURE__ */ new Set();
|
|
181
|
+
const walk = (version) => {
|
|
182
|
+
if (visiting.has(version)) fail$1("SPEC_RESOLUTION_GRAPH_CYCLIC", `Resolution graph for '${subject}' contains a cycle at version ${version}.`);
|
|
183
|
+
if (visited.has(version)) return;
|
|
184
|
+
visiting.add(version);
|
|
185
|
+
for (const edge of this.outgoing(subject, version)) walk(edge.readerSpecVersion);
|
|
186
|
+
visiting.delete(version);
|
|
187
|
+
visited.add(version);
|
|
188
|
+
};
|
|
189
|
+
for (const revision of this.contracts.revisions(subject)) walk(revision.specVersion);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
validateReaderCompatibility(registration) {
|
|
193
|
+
const target = registration.readerSpecVersion;
|
|
194
|
+
if (registration.compatibilityPolicy === "exact") return;
|
|
195
|
+
const older = this.contracts.revisions(registration.subjectFqn).filter((revision) => revision.specVersion < target);
|
|
196
|
+
if (registration.compatibilityPolicy === "backward") {
|
|
197
|
+
const previous = older.at(-1);
|
|
198
|
+
if (previous && this.plan(registration.subjectFqn, previous.specVersion, target).length !== 1) fail$1("KIND_READER_BACKWARD_COVERAGE_INVALID", `Reader '${registration.readerId}' requires one direct previous-version resolution.`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
for (const revision of older) this.plan(registration.subjectFqn, revision.specVersion, target);
|
|
202
|
+
}
|
|
203
|
+
all() {
|
|
204
|
+
return Object.freeze([...this.definitions].sort((left, right) => compare(left.subjectFqn, right.subjectFqn) || left.writerSpecVersion - right.writerSpecVersion || left.readerSpecVersion - right.readerSpecVersion || compare(left.id, right.id)));
|
|
205
|
+
}
|
|
206
|
+
outgoing(subjectFqn, writerSpecVersion) {
|
|
207
|
+
return this.definitions.filter((definition) => definition.subjectFqn === subjectFqn && definition.writerSpecVersion === writerSpecVersion).sort((left, right) => left.readerSpecVersion - right.readerSpecVersion || compare(left.id, right.id));
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function resolveSpec(input) {
|
|
211
|
+
const resourceId = nonEmpty(input.resourceId, "resourceId");
|
|
212
|
+
const owner = input.contracts.subject(input.kind);
|
|
213
|
+
const writer = input.contracts.revision(owner.subjectFqn, input.writerSpecVersion);
|
|
214
|
+
validateFingerprint(input.sourceContentDigest, "sourceContentDigest");
|
|
215
|
+
let resolvedSpec = clonePortableSpec(input.authoredSpec);
|
|
216
|
+
validateSchema(input.validator, writer, resolvedSpec, "WRITER_SCHEMA_INVALID");
|
|
217
|
+
const target = input.readerProfile.readerFor(owner.subjectFqn);
|
|
218
|
+
const reader = input.readers.require(target);
|
|
219
|
+
const readerRevision = input.contracts.revision(owner.subjectFqn, reader.readerSpecVersion);
|
|
220
|
+
if (readerRevision.contractFingerprint !== reader.contractFingerprint) fail$1("KIND_READER_CONTRACT_MISMATCH", `Reader '${reader.readerId}' contract fingerprint does not match revision ${reader.readerSpecVersion}.`);
|
|
221
|
+
input.resolutions.validateReaderCompatibility(reader);
|
|
222
|
+
if (reader.compatibilityPolicy === "exact" && writer.specVersion !== reader.readerSpecVersion) fail$1("KIND_READER_EXACT_VERSION_REQUIRED", `Reader '${reader.readerId}' only accepts writer version ${reader.readerSpecVersion}.`);
|
|
223
|
+
if (reader.compatibilityPolicy !== "exact" && writer.specVersion > reader.readerSpecVersion) fail$1("KIND_READER_FORWARD_VERSION_UNSUPPORTED", `Reader '${reader.readerId}' uses '${reader.compatibilityPolicy}' compatibility and cannot read newer writer version ${writer.specVersion}.`);
|
|
224
|
+
const path = input.resolutions.plan(owner.subjectFqn, writer.specVersion, reader.readerSpecVersion);
|
|
225
|
+
if (reader.compatibilityPolicy === "backward" && path.length > 1) fail$1("KIND_READER_BACKWARD_PATH_NOT_DIRECT", `Reader '${reader.readerId}' requires a direct resolution.`);
|
|
226
|
+
for (const edge of path) {
|
|
227
|
+
const next = clonePortableSpec(edge.resolve(resolvedSpec));
|
|
228
|
+
const targetRevision = input.contracts.revision(owner.subjectFqn, edge.readerSpecVersion);
|
|
229
|
+
validateSchema(input.validator, targetRevision, next, "SPEC_RESOLUTION_TARGET_SCHEMA_INVALID");
|
|
230
|
+
resolvedSpec = next;
|
|
231
|
+
}
|
|
232
|
+
validateSchema(input.validator, readerRevision, resolvedSpec, "READER_SCHEMA_INVALID");
|
|
233
|
+
const readerValue = reader.read(resolvedSpec);
|
|
234
|
+
const resolvedSpecDigest = digestCanonical(resolvedSpec);
|
|
235
|
+
const pathReceipt = Object.freeze(path.map((edge) => Object.freeze({
|
|
236
|
+
resolutionId: edge.id,
|
|
237
|
+
resolutionFingerprint: edge.resolutionFingerprint
|
|
238
|
+
})));
|
|
239
|
+
const receipt = Object.freeze({
|
|
240
|
+
resourceId,
|
|
241
|
+
subjectFqn: owner.subjectFqn,
|
|
242
|
+
sourceContentDigest: input.sourceContentDigest,
|
|
243
|
+
writer: Object.freeze({
|
|
244
|
+
specVersion: writer.specVersion,
|
|
245
|
+
schemaFingerprint: writer.schemaFingerprint,
|
|
246
|
+
contractFingerprint: writer.contractFingerprint,
|
|
247
|
+
sourceContractFingerprint: writer.sourceContractFingerprint
|
|
248
|
+
}),
|
|
249
|
+
reader: Object.freeze({
|
|
250
|
+
readerId: reader.readerId,
|
|
251
|
+
specVersion: reader.readerSpecVersion,
|
|
252
|
+
schemaFingerprint: readerRevision.schemaFingerprint,
|
|
253
|
+
contractFingerprint: reader.contractFingerprint,
|
|
254
|
+
implementationFingerprint: reader.readerImplementationFingerprint,
|
|
255
|
+
sourceContractFingerprint: readerRevision.sourceContractFingerprint
|
|
256
|
+
}),
|
|
257
|
+
path: pathReceipt,
|
|
258
|
+
resolvedSpecDigest
|
|
259
|
+
});
|
|
260
|
+
return Object.freeze({
|
|
261
|
+
resolvedSpec,
|
|
262
|
+
readerValue,
|
|
263
|
+
receipt,
|
|
264
|
+
effectiveContentDigest: digestCanonical({
|
|
265
|
+
sourceContentDigest: input.sourceContentDigest,
|
|
266
|
+
writer: receipt.writer,
|
|
267
|
+
reader: receipt.reader,
|
|
268
|
+
path: pathReceipt,
|
|
269
|
+
resolvedSpecDigest
|
|
270
|
+
})
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function createKindContractLock(input) {
|
|
274
|
+
const envelopeVersion = nonEmpty(input.envelopeVersion, "envelopeVersion");
|
|
275
|
+
const envelopeFingerprint = validateFingerprint(input.envelopeFingerprint, "envelopeFingerprint");
|
|
276
|
+
const owners = Object.freeze(input.contracts.owners().map(freezeOwner));
|
|
277
|
+
const readers = Object.freeze([...input.readerProfile.readers.values()].map((target) => {
|
|
278
|
+
const reader = input.readers.require(target);
|
|
279
|
+
if (input.contracts.revision(target.subjectFqn, target.readerSpecVersion).contractFingerprint !== target.contractFingerprint) fail$1("KIND_READER_CONTRACT_MISMATCH", `Reader target '${target.readerId}' does not match revision '${target.subjectFqn}@${target.readerSpecVersion}'.`);
|
|
280
|
+
input.resolutions.validateReaderCompatibility(reader);
|
|
281
|
+
return Object.freeze({
|
|
282
|
+
...freezeTarget(target),
|
|
283
|
+
compatibilityPolicy: reader.compatibilityPolicy
|
|
284
|
+
});
|
|
285
|
+
}).sort((left, right) => compare(left.subjectFqn, right.subjectFqn)));
|
|
286
|
+
const revisions = Object.freeze(input.contracts.revisions().map((revision) => {
|
|
287
|
+
const owner = input.contracts.owner(revision.subjectFqn);
|
|
288
|
+
return Object.freeze({
|
|
289
|
+
subjectFqn: revision.subjectFqn,
|
|
290
|
+
specVersion: revision.specVersion,
|
|
291
|
+
schemaFingerprint: revision.schemaFingerprint,
|
|
292
|
+
contractFingerprint: revision.contractFingerprint,
|
|
293
|
+
ownerPackageId: owner.ownerPackageId,
|
|
294
|
+
ownerPackageFingerprint: owner.ownerPackageFingerprint,
|
|
295
|
+
sourceContractFingerprint: revision.sourceContractFingerprint
|
|
296
|
+
});
|
|
297
|
+
}));
|
|
298
|
+
input.resolutions.validateGraph();
|
|
299
|
+
const facts = {
|
|
300
|
+
envelopeVersion,
|
|
301
|
+
envelopeFingerprint,
|
|
302
|
+
owners,
|
|
303
|
+
readers,
|
|
304
|
+
revisions,
|
|
305
|
+
resolutions: Object.freeze(input.resolutions.all().map((resolution) => Object.freeze({
|
|
306
|
+
resolutionId: resolution.id,
|
|
307
|
+
subjectFqn: resolution.subjectFqn,
|
|
308
|
+
writerSpecVersion: resolution.writerSpecVersion,
|
|
309
|
+
readerSpecVersion: resolution.readerSpecVersion,
|
|
310
|
+
resolutionFingerprint: resolution.resolutionFingerprint
|
|
311
|
+
})))
|
|
312
|
+
};
|
|
313
|
+
return Object.freeze({
|
|
314
|
+
...facts,
|
|
315
|
+
lockDigest: digestCanonical(facts)
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function exactSpecVersion(value) {
|
|
319
|
+
if (!Number.isSafeInteger(value) || value <= 0) fail$1("SPEC_VERSION_INVALID", "specVersion must be a positive safe integer.");
|
|
320
|
+
return value;
|
|
321
|
+
}
|
|
322
|
+
function createKindSubjectOwner(input) {
|
|
323
|
+
const kind = nonEmpty(input.kind, "owner.kind");
|
|
324
|
+
const subjectFqn = nonEmpty(input.subjectFqn, "owner.subjectFqn");
|
|
325
|
+
const ownerPackageId = nonEmpty(input.ownerPackageId, "owner.ownerPackageId");
|
|
326
|
+
const ownerPackageFingerprint = validateFingerprint(input.ownerPackageFingerprint, "owner.ownerPackageFingerprint");
|
|
327
|
+
const sourceShapes = Object.freeze([...new Set(input.sourceShapes.map((shape) => validateSourceShape(shape)))].sort(compare));
|
|
328
|
+
if (sourceShapes.length === 0) fail$1("KIND_SOURCE_CONTRACT_INVALID", "Kind source contract requires at least one source shape.");
|
|
329
|
+
const documentCardinality = input.documentCardinality ?? "one";
|
|
330
|
+
if (documentCardinality !== "one" && documentCardinality !== "many") fail$1("KIND_SOURCE_CONTRACT_INVALID", `Unknown document cardinality '${documentCardinality}'.`);
|
|
331
|
+
if (documentCardinality === "many" && (sourceShapes.length !== 1 || sourceShapes[0] !== "single-file")) fail$1("KIND_SOURCE_CONTRACT_INVALID", "documentCardinality 'many' requires exactly the single-file source shape.");
|
|
332
|
+
const requiredFiles = Object.freeze([...new Set((input.requiredFiles ?? []).map((name) => nonEmpty(name, "owner.requiredFile")))].sort(compare));
|
|
333
|
+
const sourceFacts = Object.freeze({
|
|
334
|
+
sourceShapes,
|
|
335
|
+
documentCardinality,
|
|
336
|
+
requiredFiles
|
|
337
|
+
});
|
|
338
|
+
const sourceContract = Object.freeze({
|
|
339
|
+
...sourceFacts,
|
|
340
|
+
sourceContractFingerprint: digestCanonical(sourceFacts)
|
|
341
|
+
});
|
|
342
|
+
return Object.freeze({
|
|
343
|
+
kind,
|
|
344
|
+
subjectFqn,
|
|
345
|
+
ownerPackageId,
|
|
346
|
+
ownerPackageFingerprint,
|
|
347
|
+
sourceContract
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function createKindSpecRevision(input) {
|
|
351
|
+
const kind = nonEmpty(input.kind, "revision.kind");
|
|
352
|
+
const subjectFqn = nonEmpty(input.subjectFqn, "revision.subjectFqn");
|
|
353
|
+
const specVersion = exactSpecVersion(input.specVersion);
|
|
354
|
+
const specSchema = cloneJson(input.specSchema);
|
|
355
|
+
const semanticContract = freezeSemanticContract(input.semanticContract);
|
|
356
|
+
const sourceContractFingerprint = validateFingerprint(input.sourceContractFingerprint, "revision.sourceContractFingerprint");
|
|
357
|
+
const schemaFingerprint = digestCanonical(specSchema);
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
kind,
|
|
360
|
+
subjectFqn,
|
|
361
|
+
specVersion,
|
|
362
|
+
specSchema,
|
|
363
|
+
semanticContract,
|
|
364
|
+
sourceContractFingerprint,
|
|
365
|
+
schemaFingerprint,
|
|
366
|
+
contractFingerprint: digestCanonical({
|
|
367
|
+
kind,
|
|
368
|
+
subjectFqn,
|
|
369
|
+
specVersion,
|
|
370
|
+
schemaFingerprint,
|
|
371
|
+
semanticContract,
|
|
372
|
+
sourceContractFingerprint
|
|
373
|
+
}),
|
|
374
|
+
stability: input.stability
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function validateSchema(validator, revision, value, code) {
|
|
378
|
+
const issues = validator.validate(revision, value);
|
|
379
|
+
if (issues.length > 0) fail$1(code, `${revision.subjectFqn}@${revision.specVersion}: ${issues.join("; ")}`);
|
|
380
|
+
}
|
|
381
|
+
function freezeOwner(input) {
|
|
382
|
+
const owner = createKindSubjectOwner({
|
|
383
|
+
kind: input.kind,
|
|
384
|
+
subjectFqn: input.subjectFqn,
|
|
385
|
+
ownerPackageId: input.ownerPackageId,
|
|
386
|
+
ownerPackageFingerprint: input.ownerPackageFingerprint,
|
|
387
|
+
sourceShapes: input.sourceContract.sourceShapes,
|
|
388
|
+
documentCardinality: input.sourceContract.documentCardinality,
|
|
389
|
+
requiredFiles: input.sourceContract.requiredFiles
|
|
390
|
+
});
|
|
391
|
+
if (owner.sourceContract.sourceContractFingerprint !== validateFingerprint(input.sourceContract.sourceContractFingerprint, "owner.sourceContract.sourceContractFingerprint")) fail$1("KIND_SOURCE_CONTRACT_FINGERPRINT_MISMATCH", `Kind '${owner.kind}' source contract fingerprint is not canonical.`);
|
|
392
|
+
return owner;
|
|
393
|
+
}
|
|
394
|
+
function freezeRevision(input) {
|
|
395
|
+
const revision = createKindSpecRevision({
|
|
396
|
+
kind: nonEmpty(input.kind, "revision.kind"),
|
|
397
|
+
subjectFqn: nonEmpty(input.subjectFqn, "revision.subjectFqn"),
|
|
398
|
+
specVersion: exactSpecVersion(input.specVersion),
|
|
399
|
+
specSchema: cloneJson(input.specSchema),
|
|
400
|
+
semanticContract: input.semanticContract,
|
|
401
|
+
sourceContractFingerprint: input.sourceContractFingerprint,
|
|
402
|
+
stability: input.stability
|
|
403
|
+
});
|
|
404
|
+
if (revision.schemaFingerprint !== validateFingerprint(input.schemaFingerprint, "revision.schemaFingerprint") || revision.contractFingerprint !== validateFingerprint(input.contractFingerprint, "revision.contractFingerprint")) fail$1("KIND_SPEC_FINGERPRINT_MISMATCH", `Revision '${revision.subjectFqn}@${revision.specVersion}' fingerprints do not match canonical contract content.`);
|
|
405
|
+
return revision;
|
|
406
|
+
}
|
|
407
|
+
function freezeReader(input) {
|
|
408
|
+
if (typeof input.read !== "function") fail$1("KIND_READER_INVALID", "Reader registration requires a read function.");
|
|
409
|
+
if (![
|
|
410
|
+
"exact",
|
|
411
|
+
"backward",
|
|
412
|
+
"backward-transitive"
|
|
413
|
+
].includes(input.compatibilityPolicy)) fail$1("KIND_READER_POLICY_INVALID", `Unknown compatibility policy '${input.compatibilityPolicy}'.`);
|
|
414
|
+
return Object.freeze({
|
|
415
|
+
readerId: nonEmpty(input.readerId, "reader.readerId"),
|
|
416
|
+
subjectFqn: nonEmpty(input.subjectFqn, "reader.subjectFqn"),
|
|
417
|
+
readerSpecVersion: exactSpecVersion(input.readerSpecVersion),
|
|
418
|
+
contractFingerprint: validateFingerprint(input.contractFingerprint, "reader.contractFingerprint"),
|
|
419
|
+
readerImplementationFingerprint: validateFingerprint(input.readerImplementationFingerprint, "reader.readerImplementationFingerprint"),
|
|
420
|
+
compatibilityPolicy: input.compatibilityPolicy,
|
|
421
|
+
read: input.read
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function freezeTarget(input) {
|
|
425
|
+
return Object.freeze({
|
|
426
|
+
readerId: nonEmpty(input.readerId, "target.readerId"),
|
|
427
|
+
subjectFqn: nonEmpty(input.subjectFqn, "target.subjectFqn"),
|
|
428
|
+
readerSpecVersion: exactSpecVersion(input.readerSpecVersion),
|
|
429
|
+
contractFingerprint: validateFingerprint(input.contractFingerprint, "target.contractFingerprint"),
|
|
430
|
+
readerImplementationFingerprint: validateFingerprint(input.readerImplementationFingerprint, "target.readerImplementationFingerprint")
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function freezeResolution(input) {
|
|
434
|
+
if (typeof input.resolve !== "function") fail$1("SPEC_RESOLUTION_INVALID", "Resolution registration requires a resolve function.");
|
|
435
|
+
return Object.freeze({
|
|
436
|
+
id: nonEmpty(input.id, "resolution.id"),
|
|
437
|
+
subjectFqn: nonEmpty(input.subjectFqn, "resolution.subjectFqn"),
|
|
438
|
+
ownerPackageId: nonEmpty(input.ownerPackageId, "resolution.ownerPackageId"),
|
|
439
|
+
ownerPackageFingerprint: validateFingerprint(input.ownerPackageFingerprint, "resolution.ownerPackageFingerprint"),
|
|
440
|
+
writerSpecVersion: exactSpecVersion(input.writerSpecVersion),
|
|
441
|
+
readerSpecVersion: exactSpecVersion(input.readerSpecVersion),
|
|
442
|
+
writerContractFingerprint: validateFingerprint(input.writerContractFingerprint, "resolution.writerContractFingerprint"),
|
|
443
|
+
readerContractFingerprint: validateFingerprint(input.readerContractFingerprint, "resolution.readerContractFingerprint"),
|
|
444
|
+
resolutionFingerprint: validateFingerprint(input.resolutionFingerprint, "resolution.resolutionFingerprint"),
|
|
445
|
+
resolve: input.resolve
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function freezeSemanticContract(input) {
|
|
449
|
+
return Object.freeze({
|
|
450
|
+
semanticValidatorFingerprint: validateFingerprint(input.semanticValidatorFingerprint, "semanticContract.semanticValidatorFingerprint"),
|
|
451
|
+
referenceProjectionFingerprint: validateFingerprint(input.referenceProjectionFingerprint, "semanticContract.referenceProjectionFingerprint"),
|
|
452
|
+
compilerInputFingerprint: validateFingerprint(input.compilerInputFingerprint, "semanticContract.compilerInputFingerprint")
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
function sameOwner(left, right) {
|
|
456
|
+
return left.kind === right.kind && left.subjectFqn === right.subjectFqn && left.ownerPackageId === right.ownerPackageId && left.ownerPackageFingerprint === right.ownerPackageFingerprint && left.sourceContract.sourceContractFingerprint === right.sourceContract.sourceContractFingerprint;
|
|
457
|
+
}
|
|
458
|
+
function validateSourceShape(value) {
|
|
459
|
+
if (value === "single-file" || value === "directory" || value === "manifest") return value;
|
|
460
|
+
fail$1("KIND_SOURCE_CONTRACT_INVALID", `Unknown source shape '${value}'.`);
|
|
461
|
+
}
|
|
462
|
+
function sameReader(left, right) {
|
|
463
|
+
return left.readerId === right.readerId && left.subjectFqn === right.subjectFqn && left.readerSpecVersion === right.readerSpecVersion && left.contractFingerprint === right.contractFingerprint && left.readerImplementationFingerprint === right.readerImplementationFingerprint && left.compatibilityPolicy === right.compatibilityPolicy && left.read === right.read;
|
|
464
|
+
}
|
|
465
|
+
function validateFingerprint(value, label) {
|
|
466
|
+
if (!/^sha256:[0-9a-f]{64}$/u.test(value)) fail$1("CONTRACT_FINGERPRINT_INVALID", `${label} must be a lowercase SHA-256 digest.`);
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
function nonEmpty(value, label) {
|
|
470
|
+
if (typeof value !== "string" || value.trim().length === 0) fail$1("CONTRACT_FIELD_INVALID", `${label} must be a non-empty string.`);
|
|
471
|
+
return value.trim();
|
|
472
|
+
}
|
|
473
|
+
function compare(left, right) {
|
|
474
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
475
|
+
}
|
|
476
|
+
function revisionKey(subjectFqn, specVersion) {
|
|
477
|
+
return `${subjectFqn}@${specVersion}`;
|
|
478
|
+
}
|
|
479
|
+
function readonlyMap$1(source) {
|
|
480
|
+
const target = new Map(source);
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
get size() {
|
|
483
|
+
return target.size;
|
|
484
|
+
},
|
|
485
|
+
has: (key) => target.has(key),
|
|
486
|
+
get: (key) => target.get(key),
|
|
487
|
+
forEach: (callback, thisArg) => {
|
|
488
|
+
target.forEach((value, key) => callback.call(thisArg, value, key, target));
|
|
489
|
+
},
|
|
490
|
+
entries: () => target.entries(),
|
|
491
|
+
keys: () => target.keys(),
|
|
492
|
+
values: () => target.values(),
|
|
493
|
+
[Symbol.iterator]: () => target[Symbol.iterator]()
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function clonePortableSpec(value) {
|
|
497
|
+
const cloned = cloneJson(value);
|
|
498
|
+
if (!cloned || Array.isArray(cloned) || typeof cloned !== "object") fail$1("PORTABLE_SPEC_INVALID", "Spec must be a JSON-compatible object.");
|
|
499
|
+
return cloned;
|
|
500
|
+
}
|
|
501
|
+
function cloneJson(value) {
|
|
502
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
503
|
+
if (typeof value === "number") {
|
|
504
|
+
if (!Number.isFinite(value)) fail$1("PORTABLE_VALUE_INVALID", "Portable values require finite numbers.");
|
|
505
|
+
return value;
|
|
506
|
+
}
|
|
507
|
+
if (Array.isArray(value)) return Object.freeze(value.map(cloneJson));
|
|
508
|
+
if (typeof value === "object") {
|
|
509
|
+
const output = {};
|
|
510
|
+
for (const key of Object.keys(value).sort(compare)) {
|
|
511
|
+
const item = value[key];
|
|
512
|
+
if (item !== void 0) output[key] = cloneJson(item);
|
|
513
|
+
}
|
|
514
|
+
return Object.freeze(output);
|
|
515
|
+
}
|
|
516
|
+
fail$1("PORTABLE_VALUE_INVALID", `Unsupported portable value type '${typeof value}'.`);
|
|
517
|
+
}
|
|
518
|
+
function fail$1(code, message) {
|
|
519
|
+
throw new Error(`${code}: ${message}`);
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
522
|
+
//#region ../kind-definition/src/resource-tree-resolution.ts
|
|
523
|
+
function resolveResourceTree(input) {
|
|
524
|
+
admitKindDefinitionDescriptions(input);
|
|
525
|
+
const authored = uniqueAuthoredRecords(input.tree);
|
|
526
|
+
const resolvedById = /* @__PURE__ */ new Map();
|
|
527
|
+
for (const source of authored) {
|
|
528
|
+
const owner = input.contracts.subject(source.kind);
|
|
529
|
+
if (source.subjectFqn !== owner.subjectFqn) fail("AUTHORED_RESOURCE_SUBJECT_MISMATCH", `Resource '${source.resourceId}' was decoded as '${source.subjectFqn}' but admitted Kind '${source.kind}' owns '${owner.subjectFqn}'.`);
|
|
530
|
+
const result = resolveSpec({
|
|
531
|
+
resourceId: source.resourceId,
|
|
532
|
+
kind: source.kind,
|
|
533
|
+
writerSpecVersion: source.metadata.specVersion,
|
|
534
|
+
authoredSpec: source.authoredSpec,
|
|
535
|
+
sourceContentDigest: source.sourceContentDigest,
|
|
536
|
+
readerProfile: input.readerProfile,
|
|
537
|
+
contracts: input.contracts,
|
|
538
|
+
readers: input.readers,
|
|
539
|
+
resolutions: input.resolutions,
|
|
540
|
+
validator: input.validator
|
|
541
|
+
});
|
|
542
|
+
const record = Object.freeze({
|
|
543
|
+
stage: "resolved",
|
|
544
|
+
kind: source.kind,
|
|
545
|
+
subjectFqn: owner.subjectFqn,
|
|
546
|
+
resourceId: source.resourceId,
|
|
547
|
+
fqn: source.fqn,
|
|
548
|
+
...source.name ? { name: source.name } : {},
|
|
549
|
+
...source.description ? { description: source.description } : {},
|
|
550
|
+
metadata: source.metadata,
|
|
551
|
+
sourceShape: source.sourceShape,
|
|
552
|
+
logicalPath: source.logicalPath,
|
|
553
|
+
documentUri: source.documentUri,
|
|
554
|
+
format: source.format,
|
|
555
|
+
node: resolvedNode(source.node, result.resolvedSpec),
|
|
556
|
+
source,
|
|
557
|
+
readerId: result.receipt.reader.readerId,
|
|
558
|
+
readerSpecVersion: result.receipt.reader.specVersion,
|
|
559
|
+
resolvedSpec: result.resolvedSpec,
|
|
560
|
+
readerValue: result.readerValue,
|
|
561
|
+
resolution: result.receipt,
|
|
562
|
+
effectiveContentDigest: result.effectiveContentDigest
|
|
563
|
+
});
|
|
564
|
+
resolvedById.set(record.resourceId, record);
|
|
565
|
+
}
|
|
566
|
+
const manifest = resolvedById.get(input.tree.manifest.resourceId);
|
|
567
|
+
if (!manifest) fail("RESOLVED_MANIFEST_MISSING", `Manifest '${input.tree.manifest.resourceId}' was not resolved.`);
|
|
568
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
569
|
+
for (const [kind, records] of input.tree.registry.byKind) byKind.set(kind, Object.freeze(records.map((record) => {
|
|
570
|
+
const resolved = resolvedById.get(record.resourceId);
|
|
571
|
+
if (!resolved) fail("RESOLVED_RESOURCE_MISSING", `Resource '${record.resourceId}' was not resolved.`);
|
|
572
|
+
return resolved;
|
|
573
|
+
})));
|
|
574
|
+
const receipts = Object.freeze([...resolvedById.values()].sort((left, right) => compareCodeUnits(left.resourceId, right.resourceId)).map((record) => record.resolution));
|
|
575
|
+
const effectiveContentIdentities = readonlyMap([...resolvedById.values()].sort((left, right) => compareCodeUnits(left.resourceId, right.resourceId)).map((record) => [record.resourceId, record.effectiveContentDigest]));
|
|
576
|
+
return Object.freeze({
|
|
577
|
+
stage: "resolved",
|
|
578
|
+
source: input.tree,
|
|
579
|
+
manifest,
|
|
580
|
+
registry: Object.freeze({
|
|
581
|
+
byKind: readonlyMap([...byKind.entries()].sort(([left], [right]) => compareCodeUnits(left, right))),
|
|
582
|
+
kindDefinitions: input.tree.registry.kindDefinitions
|
|
583
|
+
}),
|
|
584
|
+
diagnostics: Object.freeze([]),
|
|
585
|
+
readerProfileId: input.readerProfile.profileId,
|
|
586
|
+
receipts,
|
|
587
|
+
effectiveContentIdentities
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
function admitKindDefinitionDescriptions(input) {
|
|
591
|
+
for (const definition of input.tree.registry.kindDefinitions.values()) {
|
|
592
|
+
const owner = input.contracts.subject(definition.resourceKind);
|
|
593
|
+
if (owner.subjectFqn !== definition.subjectFqn) fail("KIND_DEFINITION_SUBJECT_MISMATCH", `KindDefinition '${definition.resourceId}' describes '${definition.subjectFqn}', admitted owner describes '${owner.subjectFqn}'.`);
|
|
594
|
+
const expectedSource = owner.sourceContract;
|
|
595
|
+
const actualSourceShapes = Object.freeze([...new Set(definition.sourceShapes)].sort(compareCodeUnits));
|
|
596
|
+
const actualRequiredFiles = Object.freeze([...new Set(definition.requiredFiles)].sort(compareCodeUnits));
|
|
597
|
+
if (actualSourceShapes.length !== expectedSource.sourceShapes.length || actualSourceShapes.some((shape, index) => shape !== expectedSource.sourceShapes[index]) || definition.documentCardinality !== expectedSource.documentCardinality || actualRequiredFiles.length !== expectedSource.requiredFiles.length || actualRequiredFiles.some((name, index) => name !== expectedSource.requiredFiles[index])) fail("KIND_DEFINITION_SOURCE_CONTRACT_MISMATCH", `KindDefinition '${definition.resourceId}' source shapes, cardinality, or required files differ from admitted owner '${owner.subjectFqn}'.`);
|
|
598
|
+
if (input.contracts.revisions(definition.subjectFqn).length !== definition.specRevisions.length) fail("KIND_DEFINITION_REVISION_SET_MISMATCH", `KindDefinition '${definition.resourceId}' revision set does not match admitted registrations.`);
|
|
599
|
+
for (const descriptor of definition.specRevisions) {
|
|
600
|
+
const revision = input.contracts.revision(definition.subjectFqn, descriptor.specVersion);
|
|
601
|
+
if (revision.schemaFingerprint !== descriptor.schemaFingerprint || revision.contractFingerprint !== descriptor.contractFingerprint || revision.stability !== descriptor.stability || revision.semanticContract.semanticValidatorFingerprint !== descriptor.semanticContract.semanticValidatorFingerprint || revision.semanticContract.referenceProjectionFingerprint !== descriptor.semanticContract.referenceProjectionFingerprint || revision.semanticContract.compilerInputFingerprint !== descriptor.semanticContract.compilerInputFingerprint) fail("KIND_DEFINITION_REVISION_MISMATCH", `KindDefinition '${definition.resourceId}' revision ${descriptor.specVersion} differs from admitted contract content.`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
function uniqueAuthoredRecords(tree) {
|
|
606
|
+
const byId = /* @__PURE__ */ new Map([[tree.manifest.resourceId, tree.manifest]]);
|
|
607
|
+
for (const records of tree.registry.byKind.values()) for (const record of records) byId.set(record.resourceId, record);
|
|
608
|
+
return Object.freeze([...byId.values()].sort((left, right) => compareCodeUnits(left.resourceId, right.resourceId)));
|
|
609
|
+
}
|
|
610
|
+
function resolvedNode(source, spec) {
|
|
611
|
+
return Object.freeze({
|
|
612
|
+
...source,
|
|
613
|
+
properties: asResourceMap(spec.properties, source.properties),
|
|
614
|
+
body: asResourceList(spec.body, source.body),
|
|
615
|
+
subdomains: asResourceNodeMap(spec.subdomains, source.subdomains),
|
|
616
|
+
...typeof spec.text === "string" ? { text: spec.text } : source.text === void 0 ? {} : { text: source.text }
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function asResourceMap(value, fallback) {
|
|
620
|
+
return isRecord(value) ? value : fallback;
|
|
621
|
+
}
|
|
622
|
+
function asResourceList(value, fallback) {
|
|
623
|
+
return Array.isArray(value) ? value : fallback;
|
|
624
|
+
}
|
|
625
|
+
function asResourceNodeMap(value, fallback) {
|
|
626
|
+
return isRecord(value) ? value : fallback;
|
|
627
|
+
}
|
|
628
|
+
function isRecord(value) {
|
|
629
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
630
|
+
}
|
|
631
|
+
function readonlyMap(entries) {
|
|
632
|
+
const target = new Map(entries);
|
|
633
|
+
return Object.freeze({
|
|
634
|
+
get size() {
|
|
635
|
+
return target.size;
|
|
636
|
+
},
|
|
637
|
+
has: (key) => target.has(key),
|
|
638
|
+
get: (key) => target.get(key),
|
|
639
|
+
forEach: (callback, thisArg) => {
|
|
640
|
+
target.forEach((value, key) => callback.call(thisArg, value, key, target));
|
|
641
|
+
},
|
|
642
|
+
entries: () => target.entries(),
|
|
643
|
+
keys: () => target.keys(),
|
|
644
|
+
values: () => target.values(),
|
|
645
|
+
[Symbol.iterator]: () => target[Symbol.iterator]()
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
function fail(code, message) {
|
|
649
|
+
throw new Error(`${code}: ${message}`);
|
|
650
|
+
}
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region ../kind-definition/src/core-kind-contracts.ts
|
|
653
|
+
const CORE_KIND_OWNER_PACKAGE_ID = "halfcode-compiler.xnl";
|
|
654
|
+
const CORE_KIND_OWNER_PACKAGE_FINGERPRINT = digestCanonical(Object.freeze({
|
|
655
|
+
authority: "halfcode-compiler.xnl/core-kind-contracts/v1",
|
|
656
|
+
ownerPackageId: CORE_KIND_OWNER_PACKAGE_ID,
|
|
657
|
+
subjects: Object.freeze(["Halfcode.ResourceKind.KindDefinition", "Halfcode.ResourceKind.ResourcePackage"])
|
|
658
|
+
}));
|
|
659
|
+
const RESOURCE_PACKAGE_KIND_OWNER = createKindSubjectOwner({
|
|
660
|
+
kind: "ResourcePackage",
|
|
661
|
+
subjectFqn: "Halfcode.ResourceKind.ResourcePackage",
|
|
662
|
+
ownerPackageId: CORE_KIND_OWNER_PACKAGE_ID,
|
|
663
|
+
ownerPackageFingerprint: CORE_KIND_OWNER_PACKAGE_FINGERPRINT,
|
|
664
|
+
sourceShapes: Object.freeze(["manifest"]),
|
|
665
|
+
documentCardinality: "one"
|
|
666
|
+
});
|
|
667
|
+
const KIND_DEFINITION_KIND_OWNER = createKindSubjectOwner({
|
|
668
|
+
kind: "KindDefinition",
|
|
669
|
+
subjectFqn: "Halfcode.ResourceKind.KindDefinition",
|
|
670
|
+
ownerPackageId: CORE_KIND_OWNER_PACKAGE_ID,
|
|
671
|
+
ownerPackageFingerprint: CORE_KIND_OWNER_PACKAGE_FINGERPRINT,
|
|
672
|
+
sourceShapes: Object.freeze(["single-file", "directory"]),
|
|
673
|
+
documentCardinality: "one"
|
|
674
|
+
});
|
|
675
|
+
const AUTHORED_SPEC_PROPERTIES = Object.freeze({
|
|
676
|
+
properties: Object.freeze({ type: "object" }),
|
|
677
|
+
body: Object.freeze({ type: "array" }),
|
|
678
|
+
subdomains: Object.freeze({ type: "object" }),
|
|
679
|
+
text: Object.freeze({ type: "string" })
|
|
680
|
+
});
|
|
681
|
+
const RESOURCE_PACKAGE_SPEC_SCHEMA_V1 = Object.freeze({
|
|
682
|
+
type: "object",
|
|
683
|
+
required: Object.freeze([
|
|
684
|
+
"properties",
|
|
685
|
+
"body",
|
|
686
|
+
"subdomains"
|
|
687
|
+
]),
|
|
688
|
+
properties: Object.freeze({
|
|
689
|
+
...AUTHORED_SPEC_PROPERTIES,
|
|
690
|
+
subdomains: Object.freeze({
|
|
691
|
+
type: "object",
|
|
692
|
+
required: Object.freeze(["Catalogs"])
|
|
693
|
+
})
|
|
694
|
+
}),
|
|
695
|
+
additionalProperties: false
|
|
696
|
+
});
|
|
697
|
+
const KIND_DEFINITION_SPEC_SCHEMA_V1 = Object.freeze({
|
|
698
|
+
type: "object",
|
|
699
|
+
required: Object.freeze([
|
|
700
|
+
"properties",
|
|
701
|
+
"body",
|
|
702
|
+
"subdomains"
|
|
703
|
+
]),
|
|
704
|
+
properties: Object.freeze({
|
|
705
|
+
...AUTHORED_SPEC_PROPERTIES,
|
|
706
|
+
properties: Object.freeze({
|
|
707
|
+
type: "object",
|
|
708
|
+
required: Object.freeze([
|
|
709
|
+
"resourceKind",
|
|
710
|
+
"subjectFqn",
|
|
711
|
+
"sourceShapes"
|
|
712
|
+
]),
|
|
713
|
+
properties: Object.freeze({
|
|
714
|
+
resourceKind: Object.freeze({
|
|
715
|
+
type: "string",
|
|
716
|
+
minLength: 1
|
|
717
|
+
}),
|
|
718
|
+
subjectFqn: Object.freeze({
|
|
719
|
+
type: "string",
|
|
720
|
+
minLength: 1
|
|
721
|
+
}),
|
|
722
|
+
sourceShapes: Object.freeze({
|
|
723
|
+
type: "array",
|
|
724
|
+
minItems: 1,
|
|
725
|
+
items: Object.freeze({ enum: Object.freeze([
|
|
726
|
+
"single-file",
|
|
727
|
+
"directory",
|
|
728
|
+
"manifest"
|
|
729
|
+
]) })
|
|
730
|
+
}),
|
|
731
|
+
documentCardinality: Object.freeze({ enum: Object.freeze(["one", "many"]) })
|
|
732
|
+
})
|
|
733
|
+
}),
|
|
734
|
+
subdomains: Object.freeze({
|
|
735
|
+
type: "object",
|
|
736
|
+
required: Object.freeze(["SpecRevisions"])
|
|
737
|
+
})
|
|
738
|
+
}),
|
|
739
|
+
additionalProperties: false
|
|
740
|
+
});
|
|
741
|
+
function coreSemanticContract(kind) {
|
|
742
|
+
return Object.freeze({
|
|
743
|
+
semanticValidatorFingerprint: digestCanonical(Object.freeze({
|
|
744
|
+
authority: "halfcode-compiler.xnl/core-kind-contracts/v1",
|
|
745
|
+
kind,
|
|
746
|
+
role: "bootstrap-structural-admission"
|
|
747
|
+
})),
|
|
748
|
+
referenceProjectionFingerprint: digestCanonical(Object.freeze({
|
|
749
|
+
authority: "halfcode-compiler.xnl/core-kind-contracts/v1",
|
|
750
|
+
kind,
|
|
751
|
+
role: kind === "ResourcePackage" ? "catalog-reference-projection" : "spec-revision-reference-projection"
|
|
752
|
+
})),
|
|
753
|
+
compilerInputFingerprint: digestCanonical(Object.freeze({
|
|
754
|
+
authority: "halfcode-compiler.xnl/core-kind-contracts/v1",
|
|
755
|
+
kind,
|
|
756
|
+
role: "authored-spec-envelope-projection",
|
|
757
|
+
fields: Object.freeze([
|
|
758
|
+
"properties",
|
|
759
|
+
"body",
|
|
760
|
+
"subdomains",
|
|
761
|
+
"text"
|
|
762
|
+
])
|
|
763
|
+
}))
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
const RESOURCE_PACKAGE_KIND_SPEC_REVISION_V1 = createKindSpecRevision({
|
|
767
|
+
kind: RESOURCE_PACKAGE_KIND_OWNER.kind,
|
|
768
|
+
subjectFqn: RESOURCE_PACKAGE_KIND_OWNER.subjectFqn,
|
|
769
|
+
specVersion: CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS.ResourcePackage,
|
|
770
|
+
specSchema: RESOURCE_PACKAGE_SPEC_SCHEMA_V1,
|
|
771
|
+
semanticContract: coreSemanticContract("ResourcePackage"),
|
|
772
|
+
sourceContractFingerprint: RESOURCE_PACKAGE_KIND_OWNER.sourceContract.sourceContractFingerprint,
|
|
773
|
+
stability: "stable"
|
|
774
|
+
});
|
|
775
|
+
const KIND_DEFINITION_KIND_SPEC_REVISION_V1 = createKindSpecRevision({
|
|
776
|
+
kind: KIND_DEFINITION_KIND_OWNER.kind,
|
|
777
|
+
subjectFqn: KIND_DEFINITION_KIND_OWNER.subjectFqn,
|
|
778
|
+
specVersion: CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS.KindDefinition,
|
|
779
|
+
specSchema: KIND_DEFINITION_SPEC_SCHEMA_V1,
|
|
780
|
+
semanticContract: coreSemanticContract("KindDefinition"),
|
|
781
|
+
sourceContractFingerprint: KIND_DEFINITION_KIND_OWNER.sourceContract.sourceContractFingerprint,
|
|
782
|
+
stability: "stable"
|
|
783
|
+
});
|
|
784
|
+
const CORE_KIND_SUBJECT_OWNERS = Object.freeze([KIND_DEFINITION_KIND_OWNER, RESOURCE_PACKAGE_KIND_OWNER]);
|
|
785
|
+
const CORE_KIND_SPEC_REVISIONS = Object.freeze([KIND_DEFINITION_KIND_SPEC_REVISION_V1, RESOURCE_PACKAGE_KIND_SPEC_REVISION_V1]);
|
|
786
|
+
function coreReaderRegistration(readerId, revision) {
|
|
787
|
+
return Object.freeze({
|
|
788
|
+
readerId,
|
|
789
|
+
subjectFqn: revision.subjectFqn,
|
|
790
|
+
readerSpecVersion: revision.specVersion,
|
|
791
|
+
contractFingerprint: revision.contractFingerprint,
|
|
792
|
+
readerImplementationFingerprint: digestCanonical(Object.freeze({
|
|
793
|
+
authority: "halfcode-compiler.xnl/core-kind-contracts/v1",
|
|
794
|
+
readerId,
|
|
795
|
+
subjectFqn: revision.subjectFqn,
|
|
796
|
+
readerSpecVersion: revision.specVersion,
|
|
797
|
+
operation: "identity-read-of-validated-authored-spec"
|
|
798
|
+
})),
|
|
799
|
+
compatibilityPolicy: "exact",
|
|
800
|
+
read: (spec) => spec
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
const KIND_DEFINITION_KIND_READER_REGISTRATION_V1 = coreReaderRegistration("halfcode.compiler.kind-definition.reader.v1", KIND_DEFINITION_KIND_SPEC_REVISION_V1);
|
|
804
|
+
const RESOURCE_PACKAGE_KIND_READER_REGISTRATION_V1 = coreReaderRegistration("halfcode.compiler.resource-package.reader.v1", RESOURCE_PACKAGE_KIND_SPEC_REVISION_V1);
|
|
805
|
+
const CORE_KIND_READER_REGISTRATIONS = Object.freeze([KIND_DEFINITION_KIND_READER_REGISTRATION_V1, RESOURCE_PACKAGE_KIND_READER_REGISTRATION_V1]);
|
|
806
|
+
const CORE_RESOURCE_CONTRACT_REGISTRATIONS = Object.freeze({
|
|
807
|
+
owners: CORE_KIND_SUBJECT_OWNERS,
|
|
808
|
+
revisions: CORE_KIND_SPEC_REVISIONS,
|
|
809
|
+
readers: CORE_KIND_READER_REGISTRATIONS
|
|
810
|
+
});
|
|
811
|
+
//#endregion
|
|
1
812
|
//#region ../kind-definition/src/index.ts
|
|
2
|
-
var
|
|
813
|
+
var ResourceSourceMigrationRegistry = class {
|
|
3
814
|
definitions = [];
|
|
4
815
|
ids = /* @__PURE__ */ new Set();
|
|
5
816
|
register(definition) {
|
|
6
817
|
if (this.ids.has(definition.id)) throw new Error(`Duplicate migration id '${definition.id}'`);
|
|
7
|
-
if (!definition.id || !definition.
|
|
8
|
-
if (definition.
|
|
818
|
+
if (!definition.id || !definition.subjectFqn || !Number.isSafeInteger(definition.fromWriterSpecVersion) || definition.fromWriterSpecVersion <= 0 || !Number.isSafeInteger(definition.toWriterSpecVersion) || definition.toWriterSpecVersion <= 0) throw new Error("Source migration definitions require id, subjectFqn and positive writer spec versions");
|
|
819
|
+
if (definition.fromWriterSpecVersion === definition.toWriterSpecVersion) throw new Error(`Migration '${definition.id}' must change writer specVersion`);
|
|
9
820
|
this.ids.add(definition.id);
|
|
10
821
|
this.definitions.push(Object.freeze({ ...definition }));
|
|
11
822
|
return this;
|
|
12
823
|
}
|
|
13
|
-
plan(
|
|
14
|
-
if (
|
|
824
|
+
plan(subjectFqn, fromWriterSpecVersion, toWriterSpecVersion) {
|
|
825
|
+
if (fromWriterSpecVersion === toWriterSpecVersion) return [];
|
|
15
826
|
const steps = [];
|
|
16
827
|
const visited = /* @__PURE__ */ new Set();
|
|
17
|
-
let current =
|
|
18
|
-
while (current !==
|
|
19
|
-
if (visited.has(current)) throw new Error(`
|
|
828
|
+
let current = fromWriterSpecVersion;
|
|
829
|
+
while (current !== toWriterSpecVersion) {
|
|
830
|
+
if (visited.has(current)) throw new Error(`Source migration cycle detected for '${subjectFqn}' at writer specVersion ${current}`);
|
|
20
831
|
visited.add(current);
|
|
21
|
-
const candidates = this.definitions.filter((definition) => definition.
|
|
22
|
-
if (candidates.length === 0) throw new Error(`No migration for
|
|
23
|
-
if (candidates.length > 1) throw new Error(`Ambiguous migration for
|
|
832
|
+
const candidates = this.definitions.filter((definition) => definition.subjectFqn === subjectFqn && definition.fromWriterSpecVersion === current);
|
|
833
|
+
if (candidates.length === 0) throw new Error(`No source migration for '${subjectFqn}' from writer specVersion ${current} to ${toWriterSpecVersion}`);
|
|
834
|
+
if (candidates.length > 1) throw new Error(`Ambiguous source migration for '${subjectFqn}' from writer specVersion ${current}: ${candidates.map((candidate) => candidate.id).join(", ")}`);
|
|
24
835
|
const step = candidates[0];
|
|
25
836
|
steps.push(step);
|
|
26
|
-
current = step.
|
|
837
|
+
current = step.toWriterSpecVersion;
|
|
27
838
|
}
|
|
28
839
|
return Object.freeze(steps);
|
|
29
840
|
}
|
|
30
|
-
migrate(
|
|
31
|
-
return this.plan(
|
|
841
|
+
migrate(subjectFqn, fromWriterSpecVersion, toWriterSpecVersion, resource) {
|
|
842
|
+
return this.plan(subjectFqn, fromWriterSpecVersion, toWriterSpecVersion).reduce((value, step) => step.migrate(value), resource);
|
|
32
843
|
}
|
|
33
844
|
};
|
|
34
845
|
const kindDefinitionPackage = {
|
|
@@ -37,4 +848,4 @@ const kindDefinitionPackage = {
|
|
|
37
848
|
owns: "resource kind validation authority"
|
|
38
849
|
};
|
|
39
850
|
//#endregion
|
|
40
|
-
export {
|
|
851
|
+
export { CORE_KIND_READER_REGISTRATIONS, CORE_KIND_SPEC_REVISIONS, CORE_KIND_SUBJECT_OWNERS, CORE_RESOURCE_CONTRACT_REGISTRATIONS, KIND_DEFINITION_KIND_OWNER, KIND_DEFINITION_KIND_READER_REGISTRATION_V1, KIND_DEFINITION_KIND_SPEC_REVISION_V1, KIND_DEFINITION_SPEC_SCHEMA_V1, KindContractRegistry, KindReaderRegistry, KindWriterRegistry, RESOURCE_PACKAGE_KIND_OWNER, RESOURCE_PACKAGE_KIND_READER_REGISTRATION_V1, RESOURCE_PACKAGE_KIND_SPEC_REVISION_V1, RESOURCE_PACKAGE_SPEC_SCHEMA_V1, ResourceSourceMigrationRegistry, SpecResolutionRegistry, createKindContractLock, createKindSpecRevision, createKindSubjectOwner, createReaderProfile, exactSpecVersion, kindDefinitionPackage, resolveResourceTree, resolveSpec };
|