@ai-matrx/alchemy 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/checks.cjs +249 -0
- package/dist/checks.cjs.map +1 -0
- package/dist/checks.d.cts +259 -0
- package/dist/checks.d.ts +259 -0
- package/dist/checks.js +226 -0
- package/dist/checks.js.map +1 -0
- package/dist/declare/schema.json +414 -0
- package/dist/declare-host.cjs +19 -0
- package/dist/declare-host.cjs.map +1 -0
- package/dist/declare-host.d.cts +78 -0
- package/dist/declare-host.d.ts +78 -0
- package/dist/declare-host.js +1 -0
- package/dist/declare-host.js.map +1 -0
- package/dist/declare.cjs +637 -0
- package/dist/declare.cjs.map +1 -0
- package/dist/declare.d.cts +334 -0
- package/dist/declare.d.ts +334 -0
- package/dist/declare.js +614 -0
- package/dist/declare.js.map +1 -0
- package/package.json +41 -7
package/dist/declare.js
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
// src/declare/keys.ts
|
|
2
|
+
var DECLARATION_NAME_RE = /^[a-z][a-z0-9_]*$/;
|
|
3
|
+
var SURFACE_NAME_RE = /^[a-z][a-z0-9-]*\/[a-z0-9-/]+$/;
|
|
4
|
+
var QUALIFIED_VALUE_KEY_RE = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)?$/;
|
|
5
|
+
function parseQualifiedValueKey(key) {
|
|
6
|
+
if (!QUALIFIED_VALUE_KEY_RE.test(key)) return null;
|
|
7
|
+
const dot = key.indexOf(".");
|
|
8
|
+
if (dot === -1) return { itemType: "", name: key };
|
|
9
|
+
return { itemType: key.slice(0, dot), name: key.slice(dot + 1) };
|
|
10
|
+
}
|
|
11
|
+
function qualifyValueKey(itemType, name) {
|
|
12
|
+
return itemType ? `${itemType}.${name}` : name;
|
|
13
|
+
}
|
|
14
|
+
function surfaceClientOf(surfaceName) {
|
|
15
|
+
return surfaceName.split("/")[0] ?? "";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/declare/hash.ts
|
|
19
|
+
function canonicalJson(value) {
|
|
20
|
+
if (value === void 0) return "null";
|
|
21
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
22
|
+
if (Array.isArray(value)) return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
|
|
23
|
+
const record = value;
|
|
24
|
+
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
25
|
+
}
|
|
26
|
+
var K = new Uint32Array([
|
|
27
|
+
1116352408,
|
|
28
|
+
1899447441,
|
|
29
|
+
3049323471,
|
|
30
|
+
3921009573,
|
|
31
|
+
961987163,
|
|
32
|
+
1508970993,
|
|
33
|
+
2453635748,
|
|
34
|
+
2870763221,
|
|
35
|
+
3624381080,
|
|
36
|
+
310598401,
|
|
37
|
+
607225278,
|
|
38
|
+
1426881987,
|
|
39
|
+
1925078388,
|
|
40
|
+
2162078206,
|
|
41
|
+
2614888103,
|
|
42
|
+
3248222580,
|
|
43
|
+
3835390401,
|
|
44
|
+
4022224774,
|
|
45
|
+
264347078,
|
|
46
|
+
604807628,
|
|
47
|
+
770255983,
|
|
48
|
+
1249150122,
|
|
49
|
+
1555081692,
|
|
50
|
+
1996064986,
|
|
51
|
+
2554220882,
|
|
52
|
+
2821834349,
|
|
53
|
+
2952996808,
|
|
54
|
+
3210313671,
|
|
55
|
+
3336571891,
|
|
56
|
+
3584528711,
|
|
57
|
+
113926993,
|
|
58
|
+
338241895,
|
|
59
|
+
666307205,
|
|
60
|
+
773529912,
|
|
61
|
+
1294757372,
|
|
62
|
+
1396182291,
|
|
63
|
+
1695183700,
|
|
64
|
+
1986661051,
|
|
65
|
+
2177026350,
|
|
66
|
+
2456956037,
|
|
67
|
+
2730485921,
|
|
68
|
+
2820302411,
|
|
69
|
+
3259730800,
|
|
70
|
+
3345764771,
|
|
71
|
+
3516065817,
|
|
72
|
+
3600352804,
|
|
73
|
+
4094571909,
|
|
74
|
+
275423344,
|
|
75
|
+
430227734,
|
|
76
|
+
506948616,
|
|
77
|
+
659060556,
|
|
78
|
+
883997877,
|
|
79
|
+
958139571,
|
|
80
|
+
1322822218,
|
|
81
|
+
1537002063,
|
|
82
|
+
1747873779,
|
|
83
|
+
1955562222,
|
|
84
|
+
2024104815,
|
|
85
|
+
2227730452,
|
|
86
|
+
2361852424,
|
|
87
|
+
2428436474,
|
|
88
|
+
2756734187,
|
|
89
|
+
3204031479,
|
|
90
|
+
3329325298
|
|
91
|
+
]);
|
|
92
|
+
function sha256Hex(input) {
|
|
93
|
+
const bytes = new TextEncoder().encode(input);
|
|
94
|
+
const bitLength = bytes.length * 8;
|
|
95
|
+
const padded = new Uint8Array(bytes.length + 9 + 63 >> 6 << 6);
|
|
96
|
+
padded.set(bytes);
|
|
97
|
+
padded[bytes.length] = 128;
|
|
98
|
+
const view = new DataView(padded.buffer);
|
|
99
|
+
view.setUint32(padded.length - 8, Math.floor(bitLength / 4294967296));
|
|
100
|
+
view.setUint32(padded.length - 4, bitLength >>> 0);
|
|
101
|
+
const h = new Uint32Array([
|
|
102
|
+
1779033703,
|
|
103
|
+
3144134277,
|
|
104
|
+
1013904242,
|
|
105
|
+
2773480762,
|
|
106
|
+
1359893119,
|
|
107
|
+
2600822924,
|
|
108
|
+
528734635,
|
|
109
|
+
1541459225
|
|
110
|
+
]);
|
|
111
|
+
const w = new Uint32Array(64);
|
|
112
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
113
|
+
for (let i = 0; i < 16; i += 1) w[i] = view.getUint32(offset + i * 4);
|
|
114
|
+
for (let i = 16; i < 64; i += 1) {
|
|
115
|
+
const a2 = w[i - 15];
|
|
116
|
+
const b2 = w[i - 2];
|
|
117
|
+
const s0 = (a2 >>> 7 | a2 << 25) ^ (a2 >>> 18 | a2 << 14) ^ a2 >>> 3;
|
|
118
|
+
const s1 = (b2 >>> 17 | b2 << 15) ^ (b2 >>> 19 | b2 << 13) ^ b2 >>> 10;
|
|
119
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
120
|
+
}
|
|
121
|
+
let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
|
|
122
|
+
for (let i = 0; i < 64; i += 1) {
|
|
123
|
+
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
124
|
+
const ch = e & f ^ ~e & g;
|
|
125
|
+
const t1 = hh + S1 + ch + K[i] + w[i] >>> 0;
|
|
126
|
+
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
127
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
128
|
+
const t2 = S0 + maj >>> 0;
|
|
129
|
+
hh = g;
|
|
130
|
+
g = f;
|
|
131
|
+
f = e;
|
|
132
|
+
e = d + t1 >>> 0;
|
|
133
|
+
d = c;
|
|
134
|
+
c = b;
|
|
135
|
+
b = a;
|
|
136
|
+
a = t1 + t2 >>> 0;
|
|
137
|
+
}
|
|
138
|
+
h[0] = h[0] + a >>> 0;
|
|
139
|
+
h[1] = h[1] + b >>> 0;
|
|
140
|
+
h[2] = h[2] + c >>> 0;
|
|
141
|
+
h[3] = h[3] + d >>> 0;
|
|
142
|
+
h[4] = h[4] + e >>> 0;
|
|
143
|
+
h[5] = h[5] + f >>> 0;
|
|
144
|
+
h[6] = h[6] + g >>> 0;
|
|
145
|
+
h[7] = h[7] + hh >>> 0;
|
|
146
|
+
}
|
|
147
|
+
return Array.from(h, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
148
|
+
}
|
|
149
|
+
function declarationHash(declaration) {
|
|
150
|
+
return sha256Hex(canonicalJson(declaration));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/declare/validate.ts
|
|
154
|
+
var VALUE_TYPES = ["string", "number", "boolean", "object", "array", "document"];
|
|
155
|
+
var WRITE_MODES = ["draft", "entity", "ui", "stream"];
|
|
156
|
+
var APPLY_POLICIES = ["manual", "ask", "auto", "queued"];
|
|
157
|
+
var STREAM_OPS = ["replace", "insert_before", "insert_after", "prepend", "append", "patch"];
|
|
158
|
+
var EXECUTION_MODES = ["python-stream", "nextjs-stream", "browser-realtime", "local-runtime"];
|
|
159
|
+
var READINESS = ["verified", "partial", "stub"];
|
|
160
|
+
var LIVE_SLOTS = ["selection", "on-demand", "errors", "requests"];
|
|
161
|
+
var CLASSIFICATIONS = ["ordinary", "secret", "credential"];
|
|
162
|
+
var STRUCTURED = /* @__PURE__ */ new Set(["object", "array"]);
|
|
163
|
+
function has(list, value) {
|
|
164
|
+
return typeof value === "string" && list.includes(value);
|
|
165
|
+
}
|
|
166
|
+
function validateDeclarations(declarations, options = {}) {
|
|
167
|
+
const issues = [];
|
|
168
|
+
const seenSurfaces = /* @__PURE__ */ new Set();
|
|
169
|
+
const labelsByClient = /* @__PURE__ */ new Map();
|
|
170
|
+
for (const d of declarations) {
|
|
171
|
+
const s = d.surfaceName;
|
|
172
|
+
const push = (path, sentence, remedy, severity) => issues.push({ surfaceName: s, path, sentence, remedy, ...severity ? { severity } : {} });
|
|
173
|
+
if (seenSurfaces.has(s)) {
|
|
174
|
+
push("surfaceName", `Duplicate manifest surfaceName: "${s}" appears more than once.`, "Keep one declaration per surface.");
|
|
175
|
+
}
|
|
176
|
+
seenSurfaces.add(s);
|
|
177
|
+
if (!SURFACE_NAME_RE.test(s)) {
|
|
178
|
+
push("surfaceName", `Surface "${s}" doesn't match the "<client>/<local>" convention.`, "Rename it to <client>/<local>.");
|
|
179
|
+
}
|
|
180
|
+
const prefix = surfaceClientOf(s);
|
|
181
|
+
if (d.client !== prefix) {
|
|
182
|
+
push("client", `Surface "${s}" declares client "${String(d.client)}" but its name prefix is "${prefix}" \u2014 they must match.`, `Set client: "${prefix}".`);
|
|
183
|
+
}
|
|
184
|
+
if (!has(EXECUTION_MODES, d.executionMode)) {
|
|
185
|
+
push("executionMode", `Surface "${s}" has invalid executionMode "${String(d.executionMode)}".`, `Use one of ${EXECUTION_MODES.join(" | ")}.`);
|
|
186
|
+
}
|
|
187
|
+
if (typeof d.description !== "string") {
|
|
188
|
+
push("description", `Surface "${s}" has no description.`, 'Add one sentence on what the surface is ("" only where the live row is empty).');
|
|
189
|
+
}
|
|
190
|
+
const label = d.label?.trim();
|
|
191
|
+
if (!label) {
|
|
192
|
+
push("label", `Surface "${s}" has no canonical label \u2014 label is required.`, "Add the ONE canonical human display name.");
|
|
193
|
+
} else {
|
|
194
|
+
const clientLabels = labelsByClient.get(prefix) ?? /* @__PURE__ */ new Map();
|
|
195
|
+
const clash = clientLabels.get(label.toLowerCase());
|
|
196
|
+
if (clash && clash !== s) {
|
|
197
|
+
push("label", `Canonical label "${label}" is used by both "${clash}" and "${s}" \u2014 labels must be unique per client.`, "Give one of them a distinct label.");
|
|
198
|
+
}
|
|
199
|
+
clientLabels.set(label.toLowerCase(), s);
|
|
200
|
+
labelsByClient.set(prefix, clientLabels);
|
|
201
|
+
}
|
|
202
|
+
if (!has(READINESS, d.readiness)) {
|
|
203
|
+
push("readiness", `Surface "${s}" has invalid readiness "${String(d.readiness)}" (expected verified | partial | stub).`, "Set a valid readiness.");
|
|
204
|
+
} else if (d.readiness !== "verified" && !d.readinessNote?.trim()) {
|
|
205
|
+
push("readinessNote", `Surface "${s}" is readiness "${d.readiness}" but has no readinessNote \u2014 say what's missing.`, "Add a one-line readinessNote.");
|
|
206
|
+
}
|
|
207
|
+
const groupKeys = /* @__PURE__ */ new Set();
|
|
208
|
+
for (const g of d.groups ?? []) {
|
|
209
|
+
groupKeys.add(g.key);
|
|
210
|
+
const reserved = g.key === "general" || g.key === "baseline" || g.key.startsWith("inherited:");
|
|
211
|
+
if (reserved) continue;
|
|
212
|
+
if (!DECLARATION_NAME_RE.test(g.key)) push(`groups/${g.key}`, `Surface "${s}" group "${g.key}" doesn't match /^[a-z][a-z0-9_]*$/.`, "Rename the group key to lower_snake_case.");
|
|
213
|
+
if (g.sortOrder < 0 || g.sortOrder > 899) push(`groups/${g.key}/sortOrder`, `Surface "${s}" group "${g.key}" sortOrder ${g.sortOrder} is outside the curated band 0\u2013899.`, "Use a sortOrder between 0 and 899.");
|
|
214
|
+
if (!g.label?.trim()) push(`groups/${g.key}/label`, `Surface "${s}" group "${g.key}" has no canonical label.`, "Add a label.");
|
|
215
|
+
}
|
|
216
|
+
const seenNamespaces = /* @__PURE__ */ new Set();
|
|
217
|
+
for (const ns of d.configNamespaces ?? []) {
|
|
218
|
+
if (seenNamespaces.has(ns.namespace)) push(`configNamespaces/${ns.namespace}`, `Surface "${s}" declares duplicate config namespace "${ns.namespace}".`, "Remove the duplicate.");
|
|
219
|
+
seenNamespaces.add(ns.namespace);
|
|
220
|
+
if (options.knownNamespaces && !options.knownNamespaces.has(ns.namespace)) {
|
|
221
|
+
push(`configNamespaces/${ns.namespace}`, `Surface "${s}" references config namespace "${ns.namespace}" which is not registered.`, "Register the namespace first, or remove the reference.");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (d.values.length === 0) {
|
|
225
|
+
push("values", `Surface "${s}" declares no values.`, "Declare at least one value.");
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const seenValues = /* @__PURE__ */ new Set();
|
|
229
|
+
for (const v of d.values) {
|
|
230
|
+
const at = `values/${v.name}`;
|
|
231
|
+
if (seenValues.has(v.name)) push(at, `Surface "${s}" declares duplicate value "${v.name}".`, "Remove the duplicate.");
|
|
232
|
+
seenValues.add(v.name);
|
|
233
|
+
if (!DECLARATION_NAME_RE.test(v.name)) push(at, `Surface "${s}" value "${v.name}" doesn't match /^[a-z][a-z0-9_]*$/ \u2014 the DB CHECK constraint will reject this.`, "Rename it to lower_snake_case.");
|
|
234
|
+
if (!has(VALUE_TYPES, v.valueType)) push(`${at}/valueType`, `Surface "${s}" value "${v.name}" has invalid valueType "${String(v.valueType)}".`, `Use one of ${VALUE_TYPES.join(" | ")}.`);
|
|
235
|
+
if (typeof v.typicalCharCount !== "number" || v.typicalCharCount < 0) push(`${at}/typicalCharCount`, `Surface "${s}" value "${v.name}" has invalid typicalCharCount.`, "Use a non-negative number.");
|
|
236
|
+
if (v.sortOrder !== void 0 && (typeof v.sortOrder !== "number" || v.sortOrder < 0)) push(`${at}/sortOrder`, `Surface "${s}" value "${v.name}" has invalid sortOrder.`, "Use a non-negative number.");
|
|
237
|
+
if (v.groupKey && !groupKeys.has(v.groupKey)) push(`${at}/group`, `Surface "${s}" value "${v.name}" resolved to groupKey "${v.groupKey}" which is missing from the resolved groups list \u2014 registry group synthesis is broken.`, "Fix the registry resolution.");
|
|
238
|
+
if (v.liveSlot !== void 0 && !has(LIVE_SLOTS, v.liveSlot)) push(`${at}/liveSlot`, `Surface "${s}" value "${v.name}" has invalid liveSlot "${String(v.liveSlot)}".`, `Use one of ${LIVE_SLOTS.join(" | ")}.`);
|
|
239
|
+
if (v.role !== void 0 && v.role !== "data" && v.role !== "request") push(`${at}/role`, `Surface "${s}" value "${v.name}" has invalid role "${String(v.role)}".`, "Use data | request.");
|
|
240
|
+
if (v.sensitivity?.classification !== void 0 && !has(CLASSIFICATIONS, v.sensitivity.classification)) push(`${at}/sensitivity/classification`, `Surface "${s}" value "${v.name}" has invalid classification "${String(v.sensitivity.classification)}".`, `Use one of ${CLASSIFICATIONS.join(" | ")}.`);
|
|
241
|
+
}
|
|
242
|
+
for (const slot of d.liveSlots ?? []) {
|
|
243
|
+
if (!has(LIVE_SLOTS, slot)) push("liveSlots", `Surface "${s}" declares unknown live slot "${String(slot)}".`, `Use one of ${LIVE_SLOTS.join(" | ")}.`);
|
|
244
|
+
}
|
|
245
|
+
const itemTypeNames = /* @__PURE__ */ new Set();
|
|
246
|
+
for (const item of d.itemTypes ?? []) {
|
|
247
|
+
const at = `itemTypes/${item.name}`;
|
|
248
|
+
if (itemTypeNames.has(item.name)) push(at, `Surface "${s}" declares duplicate item type "${item.name}".`, "Remove the duplicate.");
|
|
249
|
+
itemTypeNames.add(item.name);
|
|
250
|
+
if (!DECLARATION_NAME_RE.test(item.name)) push(at, `Surface "${s}" item type "${item.name}" doesn't match /^[a-z][a-z0-9_]*$/.`, "Rename it to lower_snake_case.");
|
|
251
|
+
if (!item.label?.trim() || !item.description?.trim()) push(at, `Surface "${s}" item type "${item.name}" needs a label and a description.`, "Add both.");
|
|
252
|
+
const itemValues = new Set(item.values.map((v) => v.name));
|
|
253
|
+
if (item.identity.length === 0) push(`${at}/identity`, `Surface "${s}" item type "${item.name}" has an empty identity.`, "Name the value(s) that identify one item.");
|
|
254
|
+
for (const key of item.identity) {
|
|
255
|
+
if (!itemValues.has(key)) push(`${at}/identity`, `Surface "${s}" item type "${item.name}" identity names "${key}", which is not one of its values.`, "Declare it as an item value.");
|
|
256
|
+
}
|
|
257
|
+
for (const t of item.writeTargets ?? []) {
|
|
258
|
+
if (t.updatesValue && !itemValues.has(t.updatesValue)) push(`${at}/writeTargets/${t.name}`, `Surface "${s}" item type "${item.name}" write target "${t.name}" updatesValue "${t.updatesValue}" which is not an item value.`, "Point it at a declared item value.");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const seenTargets = /* @__PURE__ */ new Set();
|
|
262
|
+
const structuredWithoutKind = [];
|
|
263
|
+
for (const w of d.writeTargets ?? []) {
|
|
264
|
+
const at = `writeTargets/${w.name}`;
|
|
265
|
+
if (seenTargets.has(w.name)) push(at, `Surface "${s}" declares duplicate write target "${w.name}".`, "Remove the duplicate.");
|
|
266
|
+
seenTargets.add(w.name);
|
|
267
|
+
if (!DECLARATION_NAME_RE.test(w.name)) push(at, `Surface "${s}" write target "${w.name}" doesn't match /^[a-z][a-z0-9_]*$/.`, "Rename it to lower_snake_case.");
|
|
268
|
+
if (!w.label?.trim() || !w.description?.trim()) push(at, `Surface "${s}" write target "${w.name}" needs a label and a description.`, "Add both.");
|
|
269
|
+
if (!has(VALUE_TYPES, w.valueType)) push(`${at}/valueType`, `Surface "${s}" write target "${w.name}" has invalid valueType "${String(w.valueType)}".`, `Use one of ${VALUE_TYPES.join(" | ")}.`);
|
|
270
|
+
if (!has(WRITE_MODES, w.mode)) push(`${at}/mode`, `Surface "${s}" write target "${w.name}" has invalid mode "${String(w.mode)}".`, `Use one of ${WRITE_MODES.join(" | ")}.`);
|
|
271
|
+
if (w.applyPolicy !== void 0 && !has(APPLY_POLICIES, w.applyPolicy)) push(`${at}/applyPolicy`, `Surface "${s}" write target "${w.name}" has invalid applyPolicy "${String(w.applyPolicy)}".`, `Use one of ${APPLY_POLICIES.join(" | ")}.`);
|
|
272
|
+
if (w.itemType !== void 0 && !itemTypeNames.has(w.itemType)) push(`${at}/itemType`, `Surface "${s}" write target "${w.name}" names item type "${w.itemType}", which the surface does not declare.`, "Declare the item type or drop itemType.");
|
|
273
|
+
const readTwinDeclared = w.updatesValue ? seenValues.has(w.updatesValue) : false;
|
|
274
|
+
if (w.updatesValue && !w.itemType && !readTwinDeclared) push(`${at}/updatesValue`, `Surface "${s}" write target "${w.name}" updatesValue "${w.updatesValue}" which is not a declared value.`, "Point updatesValue at a declared value.");
|
|
275
|
+
if (w.patchable && !w.updatesValue) push(`${at}/patchable`, `Surface "${s}" write target "${w.name}" is patchable but declares no updatesValue \u2014 an anchored edit has nothing to read.`, "Declare the read twin in updatesValue.");
|
|
276
|
+
if (w.patchable && w.valueType !== "string" && w.valueType !== "document") push(`${at}/patchable`, `Surface "${s}" write target "${w.name}" is patchable but valueType is "${w.valueType}" (patching is for "string" | "document").`, "Drop patchable or change the value type.");
|
|
277
|
+
if (w.mode === "stream") {
|
|
278
|
+
if (!w.streamOps || w.streamOps.length === 0) push(`${at}/streamOps`, `Surface "${s}" write target "${w.name}" is a stream target with no streamOps.`, "List the ops it services.");
|
|
279
|
+
if (!w.updatesValue) push(`${at}/updatesValue`, `Surface "${s}" write target "${w.name}" is a stream target with no read twin.`, "Declare updatesValue.");
|
|
280
|
+
} else if (w.streamOps !== void 0) {
|
|
281
|
+
push(`${at}/streamOps`, `Surface "${s}" write target "${w.name}" lists streamOps but its mode is "${w.mode}".`, 'streamOps belong only to mode "stream".');
|
|
282
|
+
}
|
|
283
|
+
for (const op of w.streamOps ?? []) {
|
|
284
|
+
if (!has(STREAM_OPS, op)) push(`${at}/streamOps`, `Surface "${s}" write target "${w.name}" lists unknown stream op "${String(op)}".`, `Use ${STREAM_OPS.join(" | ")}.`);
|
|
285
|
+
}
|
|
286
|
+
if (w.group && !groupKeys.has(w.group)) push(`${at}/group`, `Surface "${s}" write target "${w.name}" references undeclared group "${w.group}".`, "Declare the group or drop it.");
|
|
287
|
+
if (w.valueKind !== void 0) {
|
|
288
|
+
if (typeof w.valueKind !== "string" || !w.valueKind.trim()) {
|
|
289
|
+
push(`${at}/valueKind`, `Surface "${s}" write target "${w.name}" has an empty valueKind.`, "Name a registered kind slug or omit the field.");
|
|
290
|
+
} else if (options.isKnownKind && !options.isKnownKind(w.valueKind)) {
|
|
291
|
+
push(`${at}/valueKind`, `Surface "${s}" write target "${w.name}" declares valueKind "${w.valueKind}", which is not a registered active kind.`, "Name a registered kind slug, or register the kind first; never inline a JSON schema.");
|
|
292
|
+
}
|
|
293
|
+
} else if (STRUCTURED.has(w.valueType)) {
|
|
294
|
+
structuredWithoutKind.push(w.name);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const name of structuredWithoutKind) {
|
|
298
|
+
push(`writeTargets/${name}/valueKind`, `Surface "${s}" structured write target "${name}" declares no valueKind.`, "Name a registered kind slug (the value contract ratchet).", "advisory");
|
|
299
|
+
}
|
|
300
|
+
if (!d.skipBaselineValues && options.baselineNames) {
|
|
301
|
+
const missing = options.baselineNames.filter((name) => !seenValues.has(name));
|
|
302
|
+
if (missing.length > 0) {
|
|
303
|
+
push("values", `Surface "${s}" is missing baseline value(s): ${missing.join(", ")}.`, "The registry injects baselines automatically; opt out only with skipBaselineValues.");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const extension of options.extensions ?? []) {
|
|
308
|
+
if (!extension.validate) continue;
|
|
309
|
+
for (const d of declarations) issues.push(...extension.validate(d, { all: declarations }));
|
|
310
|
+
}
|
|
311
|
+
return issues;
|
|
312
|
+
}
|
|
313
|
+
function validateLoadedValues(declaration, loaded, options = {}) {
|
|
314
|
+
const declared = new Set(declaration.values.map((v) => v.name));
|
|
315
|
+
const ignore = new Set(options.ignore ?? []);
|
|
316
|
+
const issues = [];
|
|
317
|
+
for (const key of Object.keys(loaded)) {
|
|
318
|
+
if (key === "__item" || declared.has(key) || ignore.has(key)) continue;
|
|
319
|
+
issues.push({
|
|
320
|
+
surfaceName: declaration.surfaceName,
|
|
321
|
+
path: `values/${key}`,
|
|
322
|
+
sentence: `Surface "${declaration.surfaceName}" loaded a value "${key}" it never declared.`,
|
|
323
|
+
remedy: `Declare "${key}" in the surface's values, or stop emitting it.`
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
const item = loaded.__item;
|
|
327
|
+
if (item && typeof item === "object") {
|
|
328
|
+
const type = declaration.itemTypes?.find((t) => t.name === item.itemType);
|
|
329
|
+
if (!type) {
|
|
330
|
+
issues.push({
|
|
331
|
+
surfaceName: declaration.surfaceName,
|
|
332
|
+
path: "__item/itemType",
|
|
333
|
+
sentence: `Surface "${declaration.surfaceName}" loaded an item of type "${String(item.itemType)}" it never declared.`,
|
|
334
|
+
remedy: "Declare the item type, or stop emitting the item."
|
|
335
|
+
});
|
|
336
|
+
} else if (item.values && typeof item.values === "object") {
|
|
337
|
+
const itemDeclared = new Set(type.values.map((v) => v.name));
|
|
338
|
+
for (const key of Object.keys(item.values)) {
|
|
339
|
+
if (itemDeclared.has(key)) continue;
|
|
340
|
+
issues.push({
|
|
341
|
+
surfaceName: declaration.surfaceName,
|
|
342
|
+
path: `__item/${type.name}.${key}`,
|
|
343
|
+
sentence: `Surface "${declaration.surfaceName}" loaded item value "${type.name}.${key}" it never declared.`,
|
|
344
|
+
remedy: `Declare "${key}" on item type "${type.name}".`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return issues;
|
|
350
|
+
}
|
|
351
|
+
function findOrphanRows(rowNames, declaredNames, retired = []) {
|
|
352
|
+
const owned = new Set(declaredNames);
|
|
353
|
+
const archived = new Set(retired);
|
|
354
|
+
return rowNames.filter((name) => !owned.has(name) && !archived.has(name)).sort();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/declare/registry.ts
|
|
358
|
+
var RESERVED_GROUP_KEYS = {
|
|
359
|
+
general: "general",
|
|
360
|
+
baseline: "baseline",
|
|
361
|
+
inheritedPrefix: "inherited:"
|
|
362
|
+
};
|
|
363
|
+
var GENERAL_GROUP_ORDER = 850;
|
|
364
|
+
var INHERITED_GROUP_ORDER_BASE = 9e3;
|
|
365
|
+
var BASELINE_GROUP_ORDER = 9900;
|
|
366
|
+
var DEFAULT_MAX_INHERITANCE_DEPTH = 3;
|
|
367
|
+
var DEFAULT_SENSITIVITY = Object.freeze({
|
|
368
|
+
exportable: true,
|
|
369
|
+
classification: "ordinary",
|
|
370
|
+
includedByDefault: true
|
|
371
|
+
});
|
|
372
|
+
function resolveSensitivity(value) {
|
|
373
|
+
return {
|
|
374
|
+
exportable: value.sensitivity?.exportable ?? DEFAULT_SENSITIVITY.exportable,
|
|
375
|
+
classification: value.sensitivity?.classification ?? DEFAULT_SENSITIVITY.classification,
|
|
376
|
+
includedByDefault: value.sensitivity?.includedByDefault ?? DEFAULT_SENSITIVITY.includedByDefault
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function createDeclarationRegistry(options = {}) {
|
|
380
|
+
const baselineValues = options.baselineValues ?? [];
|
|
381
|
+
const baselineNames = new Set(baselineValues.map((v) => v.name));
|
|
382
|
+
const reservedValues = new Set(options.reservedValueNames ?? []);
|
|
383
|
+
const reservedTargets = new Set(options.reservedWriteTargetNames ?? []);
|
|
384
|
+
const maxDepth = options.maxInheritanceDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;
|
|
385
|
+
const declarations = [];
|
|
386
|
+
const index = /* @__PURE__ */ new Map();
|
|
387
|
+
const extensionList = [];
|
|
388
|
+
let resolvedCache = null;
|
|
389
|
+
let resolvedIndex = /* @__PURE__ */ new Map();
|
|
390
|
+
function ancestry(surfaceName) {
|
|
391
|
+
const chain = [];
|
|
392
|
+
const seen = /* @__PURE__ */ new Set([surfaceName]);
|
|
393
|
+
let current = index.get(surfaceName)?.inheritsFrom;
|
|
394
|
+
while (current) {
|
|
395
|
+
if (seen.has(current)) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`[surfaces] inheritsFrom CYCLE detected at "${current}" (from "${surfaceName}"). Fix the manifest chain: ${[...seen].join(" \u2192 ")} \u2192 ${current}`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
const parent = index.get(current);
|
|
401
|
+
if (!parent) {
|
|
402
|
+
throw new Error(
|
|
403
|
+
`[surfaces] "${surfaceName}" inheritsFrom unknown surface "${current}" \u2014 the parent must be a registered declaration`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
seen.add(current);
|
|
407
|
+
chain.unshift(current);
|
|
408
|
+
if (chain.length > maxDepth) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`[surfaces] inheritance chain for "${surfaceName}" exceeds depth ${maxDepth}: ` + chain.join(" \u2192 ")
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
current = parent.inheritsFrom;
|
|
414
|
+
}
|
|
415
|
+
return chain;
|
|
416
|
+
}
|
|
417
|
+
function groupKeyFor(value, provenance) {
|
|
418
|
+
if (provenance.kind === "own" && value.group) return value.group;
|
|
419
|
+
if (baselineNames.has(value.name)) return RESERVED_GROUP_KEYS.baseline;
|
|
420
|
+
if (provenance.kind === "inherited") return `${RESERVED_GROUP_KEYS.inheritedPrefix}${provenance.from}`;
|
|
421
|
+
if (provenance.kind === "baseline") return RESERVED_GROUP_KEYS.baseline;
|
|
422
|
+
return RESERVED_GROUP_KEYS.general;
|
|
423
|
+
}
|
|
424
|
+
function resolve(declaration) {
|
|
425
|
+
const chain = ancestry(declaration.surfaceName);
|
|
426
|
+
const lineage = [
|
|
427
|
+
...chain.map((name) => {
|
|
428
|
+
const layer = index.get(name);
|
|
429
|
+
if (!layer) throw new Error(`[surfaces] missing inherited declaration "${name}"`);
|
|
430
|
+
return { layer, from: name };
|
|
431
|
+
}),
|
|
432
|
+
{ layer: declaration, from: null }
|
|
433
|
+
];
|
|
434
|
+
const curatedKeys = /* @__PURE__ */ new Set();
|
|
435
|
+
for (const group of declaration.groups ?? []) {
|
|
436
|
+
if (group.key === RESERVED_GROUP_KEYS.general || group.key === RESERVED_GROUP_KEYS.baseline || group.key.startsWith(RESERVED_GROUP_KEYS.inheritedPrefix)) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`[surfaces] "${declaration.surfaceName}" declares reserved group key "${group.key}" \u2014 general/baseline/inherited:* are synthesized by the registry`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
if (curatedKeys.has(group.key)) {
|
|
442
|
+
throw new Error(`[surfaces] "${declaration.surfaceName}" declares duplicate group key "${group.key}"`);
|
|
443
|
+
}
|
|
444
|
+
curatedKeys.add(group.key);
|
|
445
|
+
}
|
|
446
|
+
for (const value of declaration.values) {
|
|
447
|
+
if (reservedValues.has(value.name)) {
|
|
448
|
+
throw new Error(
|
|
449
|
+
`[surfaces] "${declaration.surfaceName}" declares value "${value.name}" \u2014 that name is written by the platform; choose another name`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
if (value.group && !curatedKeys.has(value.group)) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
`[surfaces] "${declaration.surfaceName}" value "${value.name}" references undeclared group "${value.group}" \u2014 declare it in the manifest's \`groups\``
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
for (const target of declaration.writeTargets ?? []) {
|
|
459
|
+
if (reservedTargets.has(target.name)) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`[surfaces] "${declaration.surfaceName}" declares write target "${target.name}" \u2014 that name is written by the platform; choose another name`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const valuesByName = /* @__PURE__ */ new Map();
|
|
466
|
+
const inheritedLists = /* @__PURE__ */ new Map();
|
|
467
|
+
const coreLists = [
|
|
468
|
+
{ field: "configNamespaces", identity: (entry) => entry.namespace },
|
|
469
|
+
{
|
|
470
|
+
field: "evidenceSources",
|
|
471
|
+
identity: (entry) => `${entry.kind}:${entry.idValue}`
|
|
472
|
+
}
|
|
473
|
+
];
|
|
474
|
+
const lists = [...coreLists, ...extensionList.flatMap((extension) => extension.inheritedLists ?? [])];
|
|
475
|
+
for (const { layer, from } of lineage) {
|
|
476
|
+
for (const value of layer.values) {
|
|
477
|
+
const provenance = from ? { kind: "inherited", from } : { kind: "own" };
|
|
478
|
+
valuesByName.set(value.name, {
|
|
479
|
+
...value,
|
|
480
|
+
provenance,
|
|
481
|
+
groupKey: groupKeyFor(value, provenance),
|
|
482
|
+
resolvedSensitivity: resolveSensitivity(value)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
for (const list of lists) {
|
|
486
|
+
const entries = layer[list.field];
|
|
487
|
+
if (!Array.isArray(entries)) continue;
|
|
488
|
+
const byIdentity = inheritedLists.get(list.field) ?? /* @__PURE__ */ new Map();
|
|
489
|
+
for (const entry of entries) byIdentity.set(list.identity(entry), entry);
|
|
490
|
+
inheritedLists.set(list.field, byIdentity);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (!declaration.skipBaselineValues) {
|
|
494
|
+
for (const baseline of baselineValues) {
|
|
495
|
+
if (valuesByName.has(baseline.name)) continue;
|
|
496
|
+
valuesByName.set(baseline.name, {
|
|
497
|
+
...baseline,
|
|
498
|
+
provenance: { kind: "baseline" },
|
|
499
|
+
groupKey: RESERVED_GROUP_KEYS.baseline,
|
|
500
|
+
resolvedSensitivity: resolveSensitivity(baseline)
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const usedGroupKeys = new Set(Array.from(valuesByName.values(), (value) => value.groupKey));
|
|
505
|
+
const groups = [...declaration.groups ?? []];
|
|
506
|
+
if (usedGroupKeys.has(RESERVED_GROUP_KEYS.general)) {
|
|
507
|
+
groups.push({ key: RESERVED_GROUP_KEYS.general, label: "General", sortOrder: GENERAL_GROUP_ORDER });
|
|
508
|
+
}
|
|
509
|
+
for (let i = chain.length - 1, distance = 0; i >= 0; i -= 1, distance += 1) {
|
|
510
|
+
const parentName = chain[i];
|
|
511
|
+
const key = `${RESERVED_GROUP_KEYS.inheritedPrefix}${parentName}`;
|
|
512
|
+
if (!usedGroupKeys.has(key)) continue;
|
|
513
|
+
const parentLabel = index.get(parentName)?.label ?? parentName;
|
|
514
|
+
groups.push({
|
|
515
|
+
key,
|
|
516
|
+
label: `Inherited from ${parentLabel}`,
|
|
517
|
+
sortOrder: INHERITED_GROUP_ORDER_BASE + distance * 10
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (usedGroupKeys.has(RESERVED_GROUP_KEYS.baseline)) {
|
|
521
|
+
groups.push({ key: RESERVED_GROUP_KEYS.baseline, label: "Generic baselines", sortOrder: BASELINE_GROUP_ORDER });
|
|
522
|
+
}
|
|
523
|
+
groups.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
524
|
+
const groupOrder = new Map(groups.map((group) => [group.key, group.sortOrder]));
|
|
525
|
+
const values = Array.from(valuesByName.values()).sort((a, b) => {
|
|
526
|
+
const ga = groupOrder.get(a.groupKey) ?? GENERAL_GROUP_ORDER;
|
|
527
|
+
const gb = groupOrder.get(b.groupKey) ?? GENERAL_GROUP_ORDER;
|
|
528
|
+
if (ga !== gb) return ga - gb;
|
|
529
|
+
return (a.sortOrder ?? 1e3) - (b.sortOrder ?? 1e3);
|
|
530
|
+
});
|
|
531
|
+
const merged = {};
|
|
532
|
+
for (const [field, byIdentity] of inheritedLists) {
|
|
533
|
+
if (byIdentity.size > 0) merged[field] = Array.from(byIdentity.values());
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
...declaration,
|
|
537
|
+
values,
|
|
538
|
+
groups,
|
|
539
|
+
...merged,
|
|
540
|
+
contentHash: declarationHash(declaration)
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function resolvedAll() {
|
|
544
|
+
if (!resolvedCache) {
|
|
545
|
+
resolvedCache = declarations.map(resolve);
|
|
546
|
+
resolvedIndex = new Map(resolvedCache.map((entry) => [entry.surfaceName, entry]));
|
|
547
|
+
}
|
|
548
|
+
return resolvedCache;
|
|
549
|
+
}
|
|
550
|
+
const registry = {
|
|
551
|
+
register(declaration) {
|
|
552
|
+
if (index.has(declaration.surfaceName)) {
|
|
553
|
+
throw new Error(`[surfaces] "${declaration.surfaceName}" is declared twice \u2014 one surface, one declaration`);
|
|
554
|
+
}
|
|
555
|
+
declarations.push(declaration);
|
|
556
|
+
index.set(declaration.surfaceName, declaration);
|
|
557
|
+
resolvedCache = null;
|
|
558
|
+
},
|
|
559
|
+
registerExtension(extension) {
|
|
560
|
+
if (extensionList.some((existing) => existing.key === extension.key)) {
|
|
561
|
+
throw new Error(`[surfaces] declaration extension "${extension.key}" is registered twice`);
|
|
562
|
+
}
|
|
563
|
+
extensionList.push(extension);
|
|
564
|
+
resolvedCache = null;
|
|
565
|
+
},
|
|
566
|
+
extensions: () => extensionList,
|
|
567
|
+
raw: () => declarations,
|
|
568
|
+
getRaw: (surfaceName) => index.get(surfaceName),
|
|
569
|
+
ancestry,
|
|
570
|
+
children: (surfaceName) => declarations.filter((d) => d.inheritsFrom === surfaceName).map((d) => d.surfaceName),
|
|
571
|
+
all: () => resolvedAll(),
|
|
572
|
+
get(surfaceName) {
|
|
573
|
+
resolvedAll();
|
|
574
|
+
return resolvedIndex.get(surfaceName);
|
|
575
|
+
},
|
|
576
|
+
validate: (validateOptions) => validateDeclarations(resolvedAll(), {
|
|
577
|
+
...validateOptions,
|
|
578
|
+
extensions: [...validateOptions?.extensions ?? [], ...extensionList],
|
|
579
|
+
baselineNames: validateOptions?.baselineNames ?? [...baselineNames]
|
|
580
|
+
})
|
|
581
|
+
};
|
|
582
|
+
return registry;
|
|
583
|
+
}
|
|
584
|
+
export {
|
|
585
|
+
APPLY_POLICIES,
|
|
586
|
+
BASELINE_GROUP_ORDER,
|
|
587
|
+
CLASSIFICATIONS,
|
|
588
|
+
DECLARATION_NAME_RE,
|
|
589
|
+
DEFAULT_MAX_INHERITANCE_DEPTH,
|
|
590
|
+
DEFAULT_SENSITIVITY,
|
|
591
|
+
EXECUTION_MODES,
|
|
592
|
+
GENERAL_GROUP_ORDER,
|
|
593
|
+
INHERITED_GROUP_ORDER_BASE,
|
|
594
|
+
LIVE_SLOTS,
|
|
595
|
+
QUALIFIED_VALUE_KEY_RE,
|
|
596
|
+
READINESS,
|
|
597
|
+
RESERVED_GROUP_KEYS,
|
|
598
|
+
STREAM_OPS,
|
|
599
|
+
SURFACE_NAME_RE,
|
|
600
|
+
VALUE_TYPES,
|
|
601
|
+
WRITE_MODES,
|
|
602
|
+
canonicalJson,
|
|
603
|
+
createDeclarationRegistry,
|
|
604
|
+
declarationHash,
|
|
605
|
+
findOrphanRows,
|
|
606
|
+
parseQualifiedValueKey,
|
|
607
|
+
qualifyValueKey,
|
|
608
|
+
resolveSensitivity,
|
|
609
|
+
sha256Hex,
|
|
610
|
+
surfaceClientOf,
|
|
611
|
+
validateDeclarations,
|
|
612
|
+
validateLoadedValues
|
|
613
|
+
};
|
|
614
|
+
//# sourceMappingURL=declare.js.map
|