@ai-matrx/content-ir 0.9.0 → 0.10.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 +63 -0
- package/README.md +24 -5
- package/dist/convert.cjs +1680 -0
- package/dist/convert.cjs.map +1 -0
- package/dist/convert.d.cts +230 -0
- package/dist/convert.d.ts +230 -0
- package/dist/convert.js +1666 -0
- package/dist/convert.js.map +1 -0
- package/dist/core.cjs +2493 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +370 -0
- package/dist/core.d.ts +370 -0
- package/dist/core.js +2452 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -2030
- package/dist/index.d.ts +9 -2030
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ir-tree-DbLVxbf1.d.cts +441 -0
- package/dist/ir-tree-Dsc_66ek.d.ts +441 -0
- package/dist/ir-types-95bA2cXH.d.cts +119 -0
- package/dist/ir-types-95bA2cXH.d.ts +119 -0
- package/dist/kind-schema.types-CwncWj9U.d.cts +139 -0
- package/dist/kind-schema.types-CwncWj9U.d.ts +139 -0
- package/dist/registry.cjs +468 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.cts +357 -0
- package/dist/registry.d.ts +357 -0
- package/dist/registry.js +456 -0
- package/dist/registry.js.map +1 -0
- package/dist/session.cjs +2052 -0
- package/dist/session.cjs.map +1 -0
- package/dist/session.d.cts +75 -0
- package/dist/session.d.ts +75 -0
- package/dist/session.js +2047 -0
- package/dist/session.js.map +1 -0
- package/dist/wire.cjs +310 -0
- package/dist/wire.cjs.map +1 -0
- package/dist/wire.d.cts +326 -0
- package/dist/wire.d.ts +326 -0
- package/dist/wire.js +291 -0
- package/dist/wire.js.map +1 -0
- package/package.json +73 -1
package/dist/registry.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
|
|
3
|
+
// registry/kind-storage-transform.ts
|
|
4
|
+
var ROOT_STORAGE_NAME = "__root";
|
|
5
|
+
var KindStorageError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "KindStorageError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var PATH_SEP = ".";
|
|
12
|
+
function base(field) {
|
|
13
|
+
const out = {
|
|
14
|
+
name: ""
|
|
15
|
+
};
|
|
16
|
+
if (field.required) out.required = true;
|
|
17
|
+
if (field.nullable) out.nullable = true;
|
|
18
|
+
if (field.description !== void 0) out.description = field.description;
|
|
19
|
+
if (field.default !== void 0) out.default = field.default;
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function storeField(name, field, path, edges) {
|
|
23
|
+
const b = { ...base(field), name };
|
|
24
|
+
switch (field.type) {
|
|
25
|
+
case "string":
|
|
26
|
+
case "boolean":
|
|
27
|
+
case "number[]":
|
|
28
|
+
case "boolean[]":
|
|
29
|
+
case "json":
|
|
30
|
+
case "json[]":
|
|
31
|
+
return { ...b, type: field.type };
|
|
32
|
+
case "number":
|
|
33
|
+
return {
|
|
34
|
+
...b,
|
|
35
|
+
type: "number",
|
|
36
|
+
...field.min !== void 0 ? { min: field.min } : {},
|
|
37
|
+
...field.max !== void 0 ? { max: field.max } : {},
|
|
38
|
+
...field.step !== void 0 ? { step: field.step } : {}
|
|
39
|
+
};
|
|
40
|
+
case "string[]":
|
|
41
|
+
return {
|
|
42
|
+
...b,
|
|
43
|
+
type: "string[]",
|
|
44
|
+
...field.values !== void 0 ? { values: [...field.values] } : {},
|
|
45
|
+
...field.open ? { open: true } : {}
|
|
46
|
+
};
|
|
47
|
+
case "record":
|
|
48
|
+
return { ...b, type: "record", values: field.values };
|
|
49
|
+
case "enum":
|
|
50
|
+
return {
|
|
51
|
+
...b,
|
|
52
|
+
type: "enum",
|
|
53
|
+
values: [...field.values],
|
|
54
|
+
...field.open ? { open: true } : {}
|
|
55
|
+
};
|
|
56
|
+
case "union": {
|
|
57
|
+
if (field.kinds && field.kinds.length > 0) {
|
|
58
|
+
field.kinds.forEach((childKind, position) => {
|
|
59
|
+
edges.push({ fieldPath: path, childKind, position });
|
|
60
|
+
});
|
|
61
|
+
return { ...b, type: "union", scalars: [...field.scalars], hasKinds: true };
|
|
62
|
+
}
|
|
63
|
+
return { ...b, type: "union", scalars: [...field.scalars] };
|
|
64
|
+
}
|
|
65
|
+
case "object":
|
|
66
|
+
edges.push({ fieldPath: path, childKind: field.kind, position: null });
|
|
67
|
+
return { ...b, type: "object" };
|
|
68
|
+
case "array":
|
|
69
|
+
field.itemKinds.forEach((childKind, position) => {
|
|
70
|
+
edges.push({ fieldPath: path, childKind, position });
|
|
71
|
+
});
|
|
72
|
+
return { ...b, type: "array" };
|
|
73
|
+
case "inline_object": {
|
|
74
|
+
const fields = [];
|
|
75
|
+
for (const [childName, childField] of Object.entries(field.fields)) {
|
|
76
|
+
fields.push(
|
|
77
|
+
storeField(
|
|
78
|
+
childName,
|
|
79
|
+
childField,
|
|
80
|
+
`${path}${PATH_SEP}${childName}`,
|
|
81
|
+
edges
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return field.open ? { ...b, type: "inline_object", fields, open: true } : { ...b, type: "inline_object", fields };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function kindSchemaToStorage(schema) {
|
|
90
|
+
const data = [];
|
|
91
|
+
const edges = [];
|
|
92
|
+
if (schema.root) {
|
|
93
|
+
if (Object.keys(schema.fields).length > 0) {
|
|
94
|
+
throw new KindStorageError(
|
|
95
|
+
`kind "${schema.kind}": root form and a non-empty fields map are mutually exclusive.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
data.push(storeField(ROOT_STORAGE_NAME, schema.root, ROOT_STORAGE_NAME, edges));
|
|
99
|
+
return { data, edges };
|
|
100
|
+
}
|
|
101
|
+
for (const [name, field] of Object.entries(schema.fields)) {
|
|
102
|
+
if (name === ROOT_STORAGE_NAME) {
|
|
103
|
+
throw new KindStorageError(
|
|
104
|
+
`kind "${schema.kind}": field name "${ROOT_STORAGE_NAME}" is reserved for the non-object root form.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
data.push(storeField(name, field, name, edges));
|
|
108
|
+
}
|
|
109
|
+
return { data, edges };
|
|
110
|
+
}
|
|
111
|
+
function indexEdges(edges) {
|
|
112
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const edge of edges) {
|
|
114
|
+
const list = byPath.get(edge.fieldPath);
|
|
115
|
+
if (list) list.push(edge);
|
|
116
|
+
else byPath.set(edge.fieldPath, [edge]);
|
|
117
|
+
}
|
|
118
|
+
return byPath;
|
|
119
|
+
}
|
|
120
|
+
function restoreField(element, path, byPath) {
|
|
121
|
+
const b = {};
|
|
122
|
+
if (element.required) b.required = true;
|
|
123
|
+
if (element.nullable) b.nullable = true;
|
|
124
|
+
if (element.description !== void 0) b.description = element.description;
|
|
125
|
+
if (element.default !== void 0) b.default = element.default;
|
|
126
|
+
switch (element.type) {
|
|
127
|
+
case "string":
|
|
128
|
+
case "boolean":
|
|
129
|
+
case "number[]":
|
|
130
|
+
case "boolean[]":
|
|
131
|
+
case "json":
|
|
132
|
+
case "json[]":
|
|
133
|
+
return { ...b, type: element.type };
|
|
134
|
+
case "number":
|
|
135
|
+
return {
|
|
136
|
+
...b,
|
|
137
|
+
type: "number",
|
|
138
|
+
...element.min !== void 0 ? { min: element.min } : {},
|
|
139
|
+
...element.max !== void 0 ? { max: element.max } : {},
|
|
140
|
+
...element.step !== void 0 ? { step: element.step } : {}
|
|
141
|
+
};
|
|
142
|
+
case "string[]":
|
|
143
|
+
return {
|
|
144
|
+
...b,
|
|
145
|
+
type: "string[]",
|
|
146
|
+
...element.values !== void 0 ? { values: [...element.values] } : {},
|
|
147
|
+
...element.open ? { open: true } : {}
|
|
148
|
+
};
|
|
149
|
+
case "record":
|
|
150
|
+
return { ...b, type: "record", values: element.values };
|
|
151
|
+
case "enum":
|
|
152
|
+
return {
|
|
153
|
+
...b,
|
|
154
|
+
type: "enum",
|
|
155
|
+
values: [...element.values],
|
|
156
|
+
...element.open ? { open: true } : {}
|
|
157
|
+
};
|
|
158
|
+
case "union": {
|
|
159
|
+
const list = byPath.get(path) ?? [];
|
|
160
|
+
if (element.hasKinds) {
|
|
161
|
+
if (list.length === 0) {
|
|
162
|
+
throw new KindStorageError(
|
|
163
|
+
`union field "${path}" declares kind members (hasKinds) but has no edges.`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const kinds = [...list].sort((a, z) => (a.position ?? 0) - (z.position ?? 0)).map((e) => e.childKind);
|
|
167
|
+
return { ...b, type: "union", scalars: [...element.scalars], kinds };
|
|
168
|
+
}
|
|
169
|
+
if (list.length > 0) {
|
|
170
|
+
throw new KindStorageError(
|
|
171
|
+
`union field "${path}" has ${list.length} edge(s) but does not declare hasKinds.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
return { ...b, type: "union", scalars: [...element.scalars] };
|
|
175
|
+
}
|
|
176
|
+
case "object": {
|
|
177
|
+
const list = byPath.get(path) ?? [];
|
|
178
|
+
if (list.length !== 1) {
|
|
179
|
+
throw new KindStorageError(
|
|
180
|
+
`object field "${path}" must have exactly one edge, found ${list.length}.`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const [edge] = list;
|
|
184
|
+
if (!edge) {
|
|
185
|
+
throw new KindStorageError(
|
|
186
|
+
`object field "${path}" must have exactly one edge, found 0.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return { ...b, type: "object", kind: edge.childKind };
|
|
190
|
+
}
|
|
191
|
+
case "array": {
|
|
192
|
+
const list = byPath.get(path) ?? [];
|
|
193
|
+
if (list.length === 0) {
|
|
194
|
+
throw new KindStorageError(
|
|
195
|
+
`array field "${path}" must have at least one edge.`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const itemKinds = [...list].sort((a, z) => (a.position ?? 0) - (z.position ?? 0)).map((e) => e.childKind);
|
|
199
|
+
return { ...b, type: "array", itemKinds };
|
|
200
|
+
}
|
|
201
|
+
case "inline_object": {
|
|
202
|
+
const fields = {};
|
|
203
|
+
for (const child of element.fields) {
|
|
204
|
+
fields[child.name] = restoreField(
|
|
205
|
+
child,
|
|
206
|
+
`${path}${PATH_SEP}${child.name}`,
|
|
207
|
+
byPath
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return element.open ? { ...b, type: "inline_object", fields, open: true } : { ...b, type: "inline_object", fields };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function storageToKindSchema(kind, shape) {
|
|
215
|
+
const byPath = indexEdges(shape.edges);
|
|
216
|
+
const [first] = shape.data;
|
|
217
|
+
if (first && first.name === ROOT_STORAGE_NAME) {
|
|
218
|
+
if (shape.data.length !== 1) {
|
|
219
|
+
throw new KindStorageError(
|
|
220
|
+
`kind "${kind}": a "${ROOT_STORAGE_NAME}" element must be the only data element (found ${shape.data.length}).`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
kind,
|
|
225
|
+
fields: {},
|
|
226
|
+
root: restoreField(first, ROOT_STORAGE_NAME, byPath)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const fields = {};
|
|
230
|
+
for (const element of shape.data) {
|
|
231
|
+
if (element.name === ROOT_STORAGE_NAME) {
|
|
232
|
+
throw new KindStorageError(
|
|
233
|
+
`kind "${kind}": "${ROOT_STORAGE_NAME}" must be the first and only data element.`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
fields[element.name] = restoreField(element, element.name, byPath);
|
|
237
|
+
}
|
|
238
|
+
return { kind, fields };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// core/kind-schema.types.ts
|
|
242
|
+
var KIND_KEY = "__kind";
|
|
243
|
+
|
|
244
|
+
// core/fingerprint.ts
|
|
245
|
+
var SEED_A = 2166136261;
|
|
246
|
+
var SEED_B = 16777619;
|
|
247
|
+
function fnv1aStep(hash, input) {
|
|
248
|
+
let h = hash >>> 0;
|
|
249
|
+
for (let i = 0; i < input.length; i++) {
|
|
250
|
+
h ^= input.charCodeAt(i);
|
|
251
|
+
h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
|
|
252
|
+
}
|
|
253
|
+
return h >>> 0;
|
|
254
|
+
}
|
|
255
|
+
function createFingerprinter() {
|
|
256
|
+
let a = SEED_A;
|
|
257
|
+
let b = SEED_B;
|
|
258
|
+
let length = 0;
|
|
259
|
+
return {
|
|
260
|
+
push(chunk) {
|
|
261
|
+
a = fnv1aStep(a, chunk);
|
|
262
|
+
b = fnv1aStep(b, chunk);
|
|
263
|
+
length += chunk.length;
|
|
264
|
+
},
|
|
265
|
+
current() {
|
|
266
|
+
return `${length.toString(36)}-${a.toString(36)}${b.toString(36)}`;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function fingerprintText(source) {
|
|
271
|
+
const hasher = createFingerprinter();
|
|
272
|
+
hasher.push(source);
|
|
273
|
+
return hasher.current();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// core/ir-types.ts
|
|
277
|
+
var IR_VERSION = 1;
|
|
278
|
+
|
|
279
|
+
// core/normalize.ts
|
|
280
|
+
function envelopeFromCompleteValue(value, kind, options) {
|
|
281
|
+
return {
|
|
282
|
+
v: IR_VERSION,
|
|
283
|
+
engine: "fe-kind-parser",
|
|
284
|
+
fingerprint: fingerprintText(JSON.stringify(value)),
|
|
285
|
+
root: {
|
|
286
|
+
role: "structured",
|
|
287
|
+
kind,
|
|
288
|
+
kindState: "resolved",
|
|
289
|
+
discriminator: options?.discriminator ?? { format: "json", key: KIND_KEY },
|
|
290
|
+
path: [],
|
|
291
|
+
status: "complete",
|
|
292
|
+
value,
|
|
293
|
+
residue: null
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// registry/kind-dual-gate.ts
|
|
299
|
+
var GENERIC_FALLBACK_COMPONENT_KEY = "generic_structured";
|
|
300
|
+
function satisfies(resolved) {
|
|
301
|
+
return Boolean(
|
|
302
|
+
resolved?.isActive && resolved.componentKey !== GENERIC_FALLBACK_COMPONENT_KEY
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
var ajv = new Ajv({ allErrors: true, strict: false });
|
|
306
|
+
function stripKind(value) {
|
|
307
|
+
if (Array.isArray(value)) return value.map(stripKind);
|
|
308
|
+
if (value && typeof value === "object") {
|
|
309
|
+
const out = {};
|
|
310
|
+
for (const [k, v] of Object.entries(value)) {
|
|
311
|
+
if (k === KIND_KEY) continue;
|
|
312
|
+
out[k] = stripKind(v);
|
|
313
|
+
}
|
|
314
|
+
return out;
|
|
315
|
+
}
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
function describeAjvErrors(errors) {
|
|
319
|
+
return (errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? ""}`.trim()).slice(0, 8);
|
|
320
|
+
}
|
|
321
|
+
function schemaDeclaresKind(emittedJsonSchema) {
|
|
322
|
+
if (!emittedJsonSchema || typeof emittedJsonSchema !== "object") return false;
|
|
323
|
+
const properties = emittedJsonSchema.properties;
|
|
324
|
+
return !!properties && typeof properties === "object" && KIND_KEY in properties;
|
|
325
|
+
}
|
|
326
|
+
function validateStructuralLeg(sample, emittedJsonSchema) {
|
|
327
|
+
let validate;
|
|
328
|
+
try {
|
|
329
|
+
validate = ajv.compile(emittedJsonSchema);
|
|
330
|
+
} catch (err) {
|
|
331
|
+
return {
|
|
332
|
+
ok: false,
|
|
333
|
+
detail: `emitted_json_schema failed to compile: ${err instanceof Error ? err.message : String(err)}`
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (validate(sample)) return { ok: true };
|
|
337
|
+
const markedErrors = describeAjvErrors(validate.errors);
|
|
338
|
+
if (validate(stripKind(sample))) return { ok: true };
|
|
339
|
+
const strippedErrors = describeAjvErrors(validate.errors);
|
|
340
|
+
const errors = schemaDeclaresKind(emittedJsonSchema) ? markedErrors : strippedErrors;
|
|
341
|
+
return { ok: false, detail: `sample failed schema: ${errors.join("; ")}` };
|
|
342
|
+
}
|
|
343
|
+
var SERVER_DATA_ANNOTATION_KEYS = /* @__PURE__ */ new Set(["language"]);
|
|
344
|
+
var SUBSTANCE_DEPTH_LIMIT = 8;
|
|
345
|
+
function isSubstantiveValue(value, depth = 0) {
|
|
346
|
+
if (value === null || value === void 0) return false;
|
|
347
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
348
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
349
|
+
if (typeof value === "boolean") return true;
|
|
350
|
+
if (depth >= SUBSTANCE_DEPTH_LIMIT) return true;
|
|
351
|
+
if (Array.isArray(value)) {
|
|
352
|
+
return value.some((entry) => isSubstantiveValue(entry, depth + 1));
|
|
353
|
+
}
|
|
354
|
+
if (typeof value === "object") {
|
|
355
|
+
return Object.values(value).some(
|
|
356
|
+
(entry) => isSubstantiveValue(entry, depth + 1)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
function describeUnrenderableBridgeOutput(serverData) {
|
|
362
|
+
if (serverData === void 0 || serverData === null) {
|
|
363
|
+
return "bridge returned no serverData (undefined)";
|
|
364
|
+
}
|
|
365
|
+
if (typeof serverData !== "object") {
|
|
366
|
+
return `bridge returned a non-object serverData (${typeof serverData})`;
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(serverData)) {
|
|
369
|
+
return "bridge returned an array, not a serverData record";
|
|
370
|
+
}
|
|
371
|
+
const record = serverData;
|
|
372
|
+
const keys = Object.keys(record);
|
|
373
|
+
if (keys.length === 0) return "bridge returned an empty object ({})";
|
|
374
|
+
const contentKeys = keys.filter((key) => !SERVER_DATA_ANNOTATION_KEYS.has(key));
|
|
375
|
+
if (contentKeys.length === 0) {
|
|
376
|
+
return `bridge returned only annotation keys [${keys.join(", ")}] \u2014 that is the raw code-region annotation, not kind data`;
|
|
377
|
+
}
|
|
378
|
+
if (!contentKeys.some((key) => isSubstantiveValue(record[key]))) {
|
|
379
|
+
return `bridge returned serverData whose every content value is empty (keys: ${contentKeys.join(", ")}) \u2014 structurally present, semantically empty`;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
function validateRender(kind, sample, definition, resolvedComponent, dataOnly) {
|
|
384
|
+
if (dataOnly) {
|
|
385
|
+
return {
|
|
386
|
+
ok: true,
|
|
387
|
+
detail: "data-only contract kind \u2014 render leg is structurally inapplicable (n/a)"
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const onlyFallback = resolvedComponent?.isActive && resolvedComponent.componentKey === GENERIC_FALLBACK_COMPONENT_KEY;
|
|
391
|
+
const noComponentDetail = onlyFallback ? `the only active role='output' component for kind "${kind}" is '${GENERIC_FALLBACK_COMPONENT_KEY}' \u2014 that IS the generic viewer, i.e. no component. A reader would get a key/value dump. Author a real source='db' component (or register a compiled one), then retire the generic row.` : null;
|
|
392
|
+
if (!definition && !satisfies(resolvedComponent)) {
|
|
393
|
+
return {
|
|
394
|
+
ok: false,
|
|
395
|
+
detail: noComponentDetail ?? `kind "${kind}" has no component (not in the compiled registry, and no active role='output' kind_component row) \u2014 nothing to render`
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (definition && !definition.legacyBlockType && !definition.component && !definition.toLegacyServerData && !satisfies(resolvedComponent)) {
|
|
399
|
+
return {
|
|
400
|
+
ok: false,
|
|
401
|
+
detail: noComponentDetail ?? `kind "${kind}" has no component (no compiled legacyBlockType/component facet, and no active role='output' kind_component row) \u2014 nothing to render`
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (definition?.toLegacyServerData) {
|
|
405
|
+
let serverData;
|
|
406
|
+
try {
|
|
407
|
+
serverData = definition.toLegacyServerData(
|
|
408
|
+
envelopeFromCompleteValue(sample, kind)
|
|
409
|
+
);
|
|
410
|
+
} catch (err) {
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
detail: `toLegacyServerData threw: ${err instanceof Error ? err.message : String(err)}`
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const problem = describeUnrenderableBridgeOutput(serverData);
|
|
417
|
+
if (problem) {
|
|
418
|
+
return {
|
|
419
|
+
ok: false,
|
|
420
|
+
detail: `${problem} (the "No ${kind} available" failure)`
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
return { ok: true };
|
|
424
|
+
}
|
|
425
|
+
const satisfier = definition?.legacyBlockType ? `compiled component "${definition.legacyBlockType}"` : definition?.component ? "compiled component facet" : satisfies(resolvedComponent) && resolvedComponent ? `resolved ${resolvedComponent.source} component "${resolvedComponent.componentKey}"` : "component";
|
|
426
|
+
return {
|
|
427
|
+
ok: true,
|
|
428
|
+
detail: `bridgeless kind \u2014 ${satisfier} parses content itself; full DOM render check deferred to an RTL harness`
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function runKindDualGate(input) {
|
|
432
|
+
const structural = validateStructuralLeg(input.sample, input.emittedJsonSchema);
|
|
433
|
+
const render = validateRender(
|
|
434
|
+
input.kind,
|
|
435
|
+
input.sample,
|
|
436
|
+
input.definition,
|
|
437
|
+
input.resolvedComponent,
|
|
438
|
+
input.dataOnly
|
|
439
|
+
);
|
|
440
|
+
return { isActive: structural.ok && render.ok, structural, render };
|
|
441
|
+
}
|
|
442
|
+
function describeDualGateFailure(kind, result) {
|
|
443
|
+
if (result.isActive) return "";
|
|
444
|
+
const parts = [];
|
|
445
|
+
if (!result.structural.ok) {
|
|
446
|
+
parts.push(`structural(Pydantic): ${result.structural.detail ?? "failed"}`);
|
|
447
|
+
}
|
|
448
|
+
if (!result.render.ok) {
|
|
449
|
+
parts.push(`render(UI): ${result.render.detail ?? "failed"}`);
|
|
450
|
+
}
|
|
451
|
+
return `kind "${kind}" failed the dual gate \u2014 ${parts.join(" | ")}`;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export { KindStorageError, ROOT_STORAGE_NAME, describeDualGateFailure, kindSchemaToStorage, runKindDualGate, storageToKindSchema, validateStructuralLeg };
|
|
455
|
+
//# sourceMappingURL=registry.js.map
|
|
456
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../registry/kind-storage-transform.ts","../core/kind-schema.types.ts","../core/fingerprint.ts","../core/ir-types.ts","../core/normalize.ts","../registry/kind-dual-gate.ts"],"names":[],"mappings":"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","file":"registry.js","sourcesContent":["/**\n * Kind ↔ content_ir storage shape — the pure, DB-free reshaping between the\n * consumed `KindSchema` and the `content_ir.kind_definition.data` array +\n * `content_ir.kind_edge` rows.\n *\n * Two exact inverses:\n * - `kindSchemaToStorage` — the MIGRATION / write direction: a parsed\n * `KindSchema` (fields Record, targets inline) → an ORDERED `data` array\n * (targets stripped) + externalized `kind_edge` specs (targets, keyed by\n * field PATH). This is what pours `flexible_data` (and future authored\n * kinds) into the canonical tables.\n * - `storageToKindSchema` — the ADAPTER / read direction: `data` array +\n * edges → the same `KindSchema` the parser/emitter consume, re-attaching\n * `object.kind` / `array.itemKinds` from the edges by path.\n *\n * Design invariants (from the 2026-07-05 kind-registry storage design, since deleted):\n * - The `data` element is `FieldSchema` + `name`, MINUS ref targets. Order is\n * intrinsic to the array (fixes the jsonb key-reorder bug).\n * - `kind_edge` is the SINGLE source of truth for kind→kind refs: `object`\n * → one edge (position null); `array` → N edges (union) ordered by\n * `position`. `field_name` is a dot-PATH so a ref nested inside an\n * `inline_object` still gets a distinct, collision-free edge.\n * - `inline_object` is structural: its `fields` is an ordered array, it never\n * becomes a registry row and never carries `__kind`. Its nested refs DO get\n * edges (path-prefixed) so cascade/pinning see every dependency. Its\n * `open` flag persists on the element (losing it is the open-empty-object\n * defect).\n * - `union.kinds` refs externalize to edges exactly like `array.itemKinds`;\n * the stored element keeps `hasKinds: true` so lost edges scream on read.\n * - A NON-OBJECT ROOT (`KindSchema.root`) stores as ONE reserved element\n * named `ROOT_STORAGE_NAME` (\"__root\") — the only element allowed in that\n * kind's `data`. Real fields may never use the reserved name.\n *\n * NO imports of supabase / the DB — this is pure and unit-tested by round-trip\n * (`__tests__/kind-storage-transform.test.ts`).\n */\n\nimport type {\n FieldSchema,\n KindSchema,\n RecordValueType,\n} from \"../core/kind-schema.types\";\n\n/**\n * Reserved `data[]` element name for a NON-OBJECT ROOT form (`KindSchema.root`).\n * A root-form kind stores exactly one element under this name; the read\n * direction reconstructs `{ root }` instead of a field map. The write\n * direction rejects any REAL field with this name — the name is the marker.\n */\nexport const ROOT_STORAGE_NAME = \"__root\";\n\n// ---------------------------------------------------------------------------\n// Stored shapes (mirror content_ir.kind_definition.data + content_ir.kind_edge)\n// ---------------------------------------------------------------------------\n\ntype StoredFieldBase = {\n name: string;\n required?: boolean;\n nullable?: boolean;\n /** Human guidance — mirrors FieldBase.description. */\n description?: string;\n /** Default VALUE (annotation-level) — mirrors FieldBase.default. */\n default?: unknown;\n};\n\n/** One element of `kind_definition.data` — FieldSchema + name, ref targets removed. */\nexport type StoredFieldElement =\n | (StoredFieldBase & { type: \"string\" | \"boolean\" })\n | (StoredFieldBase & {\n type: \"number\";\n min?: number;\n max?: number;\n step?: number;\n })\n | (StoredFieldBase & { type: \"string[]\"; values?: string[]; open?: boolean })\n | (StoredFieldBase & { type: \"number[]\" | \"boolean[]\" })\n | (StoredFieldBase & { type: \"json\" })\n | (StoredFieldBase & { type: \"json[]\" })\n | (StoredFieldBase & { type: \"array\" }) // ref array — targets live in edges\n | (StoredFieldBase & { type: \"object\" }) // single ref — target lives in edges\n | (StoredFieldBase & {\n type: \"inline_object\";\n fields: StoredFieldElement[];\n open?: boolean;\n })\n | (StoredFieldBase & { type: \"record\"; values: RecordValueType })\n | (StoredFieldBase & { type: \"enum\"; values: string[]; open?: boolean })\n | (StoredFieldBase & {\n type: \"union\";\n scalars: Array<\"string\" | \"number\" | \"boolean\">;\n /** Marker that this union's kind refs live in edges (positions ordered). */\n hasKinds?: boolean;\n });\n\n/** One `content_ir.kind_edge` row (child resolved to an id at insert time). */\nexport type KindEdgeSpec = {\n /** Field PATH in the parent's `data` (dot-notation into inline_objects). */\n fieldPath: string;\n /** Child kind slug — insert resolves this to `child_definition_id`. */\n childKind: string;\n /** Union (anyOf) ordering for array refs; null for a single object ref. */\n position: number | null;\n};\n\n/** The full write payload for one kind: the ordered data array + its edges. */\nexport type KindStorageShape = {\n data: StoredFieldElement[];\n edges: KindEdgeSpec[];\n};\n\nexport class KindStorageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"KindStorageError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// WRITE direction: KindSchema → { data[], edges[] }\n// ---------------------------------------------------------------------------\n\nconst PATH_SEP = \".\";\n\nfunction base(field: FieldSchema): StoredFieldBase & { name: string } {\n // name is filled by the caller; required/nullable copied only when true so\n // the stored element stays minimal (matches the emitter's optional reads).\n const out: {\n name: string;\n required?: boolean;\n nullable?: boolean;\n description?: string;\n default?: unknown;\n } = {\n name: \"\",\n };\n if (field.required) out.required = true;\n if (field.nullable) out.nullable = true;\n if (field.description !== undefined) out.description = field.description;\n // `null` is a legitimate default; only ABSENCE is absence.\n if (field.default !== undefined) out.default = field.default;\n return out;\n}\n\n/**\n * Reshape a field into its stored element, pushing any ref targets into `edges`\n * keyed by the field's full path. `path` is the dot-path of THIS field.\n */\nfunction storeField(\n name: string,\n field: FieldSchema,\n path: string,\n edges: KindEdgeSpec[],\n): StoredFieldElement {\n const b = { ...base(field), name };\n\n switch (field.type) {\n case \"string\":\n case \"boolean\":\n case \"number[]\":\n case \"boolean[]\":\n case \"json\":\n case \"json[]\":\n return { ...b, type: field.type };\n\n case \"number\":\n return {\n ...b,\n type: \"number\",\n ...(field.min !== undefined ? { min: field.min } : {}),\n ...(field.max !== undefined ? { max: field.max } : {}),\n ...(field.step !== undefined ? { step: field.step } : {}),\n };\n\n case \"string[]\":\n return {\n ...b,\n type: \"string[]\",\n ...(field.values !== undefined ? { values: [...field.values] } : {}),\n ...(field.open ? { open: true } : {}),\n };\n\n case \"record\":\n return { ...b, type: \"record\", values: field.values };\n\n case \"enum\":\n return {\n ...b,\n type: \"enum\",\n values: [...field.values],\n ...(field.open ? { open: true } : {}),\n };\n\n case \"union\": {\n // Object-union members are refs → edges (positions ordered), exactly\n // like array itemKinds. `hasKinds` marks the element so the read\n // direction can scream about lost edges instead of silently narrowing.\n if (field.kinds && field.kinds.length > 0) {\n field.kinds.forEach((childKind, position) => {\n edges.push({ fieldPath: path, childKind, position });\n });\n return { ...b, type: \"union\", scalars: [...field.scalars], hasKinds: true };\n }\n return { ...b, type: \"union\", scalars: [...field.scalars] };\n }\n\n case \"object\":\n // Single ref → exactly one edge, no position.\n edges.push({ fieldPath: path, childKind: field.kind, position: null });\n return { ...b, type: \"object\" };\n\n case \"array\":\n // Ref array (union) → one edge per itemKind, ordered by position.\n field.itemKinds.forEach((childKind, position) => {\n edges.push({ fieldPath: path, childKind, position });\n });\n return { ...b, type: \"array\" };\n\n case \"inline_object\": {\n // Structural: recurse, prefixing nested paths so nested refs get their\n // own collision-free edges. Field order preserved (Object.entries).\n const fields: StoredFieldElement[] = [];\n for (const [childName, childField] of Object.entries(field.fields)) {\n fields.push(\n storeField(\n childName,\n childField,\n `${path}${PATH_SEP}${childName}`,\n edges,\n ),\n );\n }\n // `open` persists on the element — losing it is the open-empty-object\n // defect (an open {} materializing as CLOSED).\n return field.open\n ? { ...b, type: \"inline_object\", fields, open: true }\n : { ...b, type: \"inline_object\", fields };\n }\n }\n}\n\n/**\n * MIGRATION / write direction. Order of `data` follows `Object.entries(fields)`\n * — the caller supplies the KindSchema whose field order is authoritative\n * (prefer the compiled `system-kinds.ts` order for system kinds during the\n * one-time flexible_data migration; jsonb order is the fallback for user kinds).\n */\nexport function kindSchemaToStorage(schema: KindSchema): KindStorageShape {\n const data: StoredFieldElement[] = [];\n const edges: KindEdgeSpec[] = [];\n\n if (schema.root) {\n // Non-object root form: exactly one reserved element carries the root\n // field; a field map alongside it would be two contradictory shapes.\n if (Object.keys(schema.fields).length > 0) {\n throw new KindStorageError(\n `kind \"${schema.kind}\": root form and a non-empty fields map are mutually exclusive.`,\n );\n }\n data.push(storeField(ROOT_STORAGE_NAME, schema.root, ROOT_STORAGE_NAME, edges));\n return { data, edges };\n }\n\n for (const [name, field] of Object.entries(schema.fields)) {\n if (name === ROOT_STORAGE_NAME) {\n throw new KindStorageError(\n `kind \"${schema.kind}\": field name \"${ROOT_STORAGE_NAME}\" is reserved for the non-object root form.`,\n );\n }\n data.push(storeField(name, field, name, edges));\n }\n return { data, edges };\n}\n\n// ---------------------------------------------------------------------------\n// READ direction: { data[], edges[] } → KindSchema (the adapter's core)\n// ---------------------------------------------------------------------------\n\n/** Index edges by field path for O(1) re-attachment. */\nfunction indexEdges(edges: KindEdgeSpec[]): Map<string, KindEdgeSpec[]> {\n const byPath = new Map<string, KindEdgeSpec[]>();\n for (const edge of edges) {\n const list = byPath.get(edge.fieldPath);\n if (list) list.push(edge);\n else byPath.set(edge.fieldPath, [edge]);\n }\n return byPath;\n}\n\nfunction restoreField(\n element: StoredFieldElement,\n path: string,\n byPath: Map<string, KindEdgeSpec[]>,\n): FieldSchema {\n const b: {\n required?: boolean;\n nullable?: boolean;\n description?: string;\n default?: unknown;\n } = {};\n if (element.required) b.required = true;\n if (element.nullable) b.nullable = true;\n if (element.description !== undefined) b.description = element.description;\n if (element.default !== undefined) b.default = element.default;\n\n switch (element.type) {\n case \"string\":\n case \"boolean\":\n case \"number[]\":\n case \"boolean[]\":\n case \"json\":\n case \"json[]\":\n return { ...b, type: element.type };\n\n case \"number\":\n return {\n ...b,\n type: \"number\",\n ...(element.min !== undefined ? { min: element.min } : {}),\n ...(element.max !== undefined ? { max: element.max } : {}),\n ...(element.step !== undefined ? { step: element.step } : {}),\n };\n\n case \"string[]\":\n return {\n ...b,\n type: \"string[]\",\n ...(element.values !== undefined\n ? { values: [...element.values] }\n : {}),\n ...(element.open ? { open: true } : {}),\n };\n\n case \"record\":\n return { ...b, type: \"record\", values: element.values };\n\n case \"enum\":\n return {\n ...b,\n type: \"enum\",\n values: [...element.values],\n ...(element.open ? { open: true } : {}),\n };\n\n case \"union\": {\n const list = byPath.get(path) ?? [];\n if (element.hasKinds) {\n if (list.length === 0) {\n throw new KindStorageError(\n `union field \"${path}\" declares kind members (hasKinds) but has no edges.`,\n );\n }\n const kinds = [...list]\n .sort((a, z) => (a.position ?? 0) - (z.position ?? 0))\n .map((e) => e.childKind);\n return { ...b, type: \"union\", scalars: [...element.scalars], kinds };\n }\n if (list.length > 0) {\n throw new KindStorageError(\n `union field \"${path}\" has ${list.length} edge(s) but does not declare hasKinds.`,\n );\n }\n return { ...b, type: \"union\", scalars: [...element.scalars] };\n }\n\n case \"object\": {\n const list = byPath.get(path) ?? [];\n if (list.length !== 1) {\n throw new KindStorageError(\n `object field \"${path}\" must have exactly one edge, found ${list.length}.`,\n );\n }\n const [edge] = list;\n if (!edge) {\n throw new KindStorageError(\n `object field \"${path}\" must have exactly one edge, found 0.`,\n );\n }\n return { ...b, type: \"object\", kind: edge.childKind };\n }\n\n case \"array\": {\n const list = byPath.get(path) ?? [];\n if (list.length === 0) {\n throw new KindStorageError(\n `array field \"${path}\" must have at least one edge.`,\n );\n }\n const itemKinds = [...list]\n .sort((a, z) => (a.position ?? 0) - (z.position ?? 0))\n .map((e) => e.childKind);\n return { ...b, type: \"array\", itemKinds };\n }\n\n case \"inline_object\": {\n const fields: Record<string, FieldSchema> = {};\n for (const child of element.fields) {\n fields[child.name] = restoreField(\n child,\n `${path}${PATH_SEP}${child.name}`,\n byPath,\n );\n }\n return element.open\n ? { ...b, type: \"inline_object\", fields, open: true }\n : { ...b, type: \"inline_object\", fields };\n }\n }\n}\n\n/** ADAPTER / read direction — the exact inverse of `kindSchemaToStorage`. */\nexport function storageToKindSchema(\n kind: string,\n shape: KindStorageShape,\n): KindSchema {\n const byPath = indexEdges(shape.edges);\n\n // Non-object root form: the single reserved element IS the root field.\n const [first] = shape.data;\n if (first && first.name === ROOT_STORAGE_NAME) {\n if (shape.data.length !== 1) {\n throw new KindStorageError(\n `kind \"${kind}\": a \"${ROOT_STORAGE_NAME}\" element must be the only data element (found ${shape.data.length}).`,\n );\n }\n return {\n kind,\n fields: {},\n root: restoreField(first, ROOT_STORAGE_NAME, byPath),\n };\n }\n\n const fields: Record<string, FieldSchema> = {};\n for (const element of shape.data) {\n if (element.name === ROOT_STORAGE_NAME) {\n throw new KindStorageError(\n `kind \"${kind}\": \"${ROOT_STORAGE_NAME}\" must be the first and only data element.`,\n );\n }\n fields[element.name] = restoreField(element, element.name, byPath);\n }\n return { kind, fields };\n}\n","/**\n * KindSchema — the data-defined field model for a registered kind.\n *\n * `__kind` (KIND_KEY) is the carried discriminator: it is NOT part of a\n * kind's field map; the parser enforces it via `KindSchema.kind` and stamps\n * it onto every compliant snapshot.\n *\n * Moved from app/(dev)/demos/json-block-detector/kind-schemas.ts.\n *\n * 2026-07-15 expressivity extension (A2) — four constructs the Python-owned\n * pydantic schemas need that the v1 vocabulary could not express:\n * - `{type:\"json\"}` / `{type:\"json[]\"}` — any JSON value / array of any\n * JSON values (pydantic bare `Any` fields, `items: {}` arrays, `{}`\n * schemas). A `json` value is implicitly nullable — `null` IS a JSON\n * value — so `nullable` is meaningless (and ignored) on it.\n * - `record` values widened to `\"json\"` (pydantic `dict[str, Any]` /\n * `additionalProperties: true`).\n * - `union` may now carry `kinds` (object unions — anyOf over kind refs,\n * optionally mixed with scalars). Refs externalize to `kind_edge` rows\n * exactly like `array.itemKinds`.\n * - `KindSchema.root` — a NON-OBJECT root form: the kind's VALUE is the\n * root field itself (scalar / array / json / open object), not a `__kind`\n * object with fields. Root-form kinds are data-only: the streaming\n * `__kind` parser cannot type them (a scalar cannot carry a\n * discriminator) and refuses them loudly; validation goes through the\n * emitted JSON Schema (ajv / Pydantic). `root` and a non-empty `fields`\n * are mutually exclusive.\n * - `inline_object.open` — `additionalProperties: true`; fixes the\n * open-empty-object defect where an open `inline_object{fields:{}}`\n * materialized as CLOSED (schema_proposal / item_presentation class).\n *\n * 2026-07-15 input-semantics extension (W3-A, agent-input bridge) — the\n * constructs the Wave-1 sufficiency survey of all 1,529 live agent variables\n * showed FieldSchema could not carry, added so `AgentVariable` ⇄ kind\n * conversion is faithful:\n * - `FieldBase.description` — human guidance; round-trips JSON Schema\n * `description` and VariableDefinition `helpText`.\n * - `FieldBase.default` — the field's default VALUE (JSON Schema `default`,\n * VariableDefinition `defaultValue`). Annotation-level: validators never\n * apply it; emitters carry it verbatim.\n * - `enum.open` — \"one of these options OR any string\" (the FE's\n * `allowOther`). Emits as `anyOf: [{type:\"string\", enum}, {type:\"string\"}]`\n * so the option set survives instead of widening to bare `string`.\n * - `number` bounds `min`/`max`/`step` — JSON Schema\n * `minimum`/`maximum`/`multipleOf` (number/slider components).\n * - `string[].values` (+ `open`) — an items-enum: array of strings drawn\n * from an option set (checkbox components), `open` meaning the set is\n * advisory (`allowOther` on a multi-select).\n * Picklist bindings, scope bindings, and media component identity are\n * PROVENANCE, not structure — they never enter FieldSchema; the bridge\n * carries them out-of-band (see convert/kind-variable-bridge.ts sidecar).\n */\n\n/** System discriminator — hardcoded, not part of per-kind field schemas. */\nexport const KIND_KEY = \"__kind\";\n\nexport type ScalarFieldType = \"string\" | \"number\" | \"boolean\";\n\nexport type ArrayItemScalarType = \"string\" | \"number\" | \"boolean\";\n\n/** Value domain of a `record` field — typed scalars, or any JSON value. */\nexport type RecordValueType = ArrayItemScalarType | \"json\";\n\ntype FieldBase = {\n required?: boolean;\n nullable?: boolean;\n /** Human guidance — JSON Schema `description` / variable `helpText`. */\n description?: string;\n /**\n * Default VALUE (JSON Schema `default` / variable `defaultValue`).\n * Annotation-level: validators never apply it; emitters carry it verbatim.\n */\n default?: unknown;\n};\n\nexport type FieldSchema =\n | (FieldBase & { type: \"string\" | \"boolean\" })\n | (FieldBase & {\n type: \"number\";\n /** Inclusive lower bound — JSON Schema `minimum`. */\n min?: number;\n /** Inclusive upper bound — JSON Schema `maximum`. */\n max?: number;\n /** Increment — JSON Schema `multipleOf`. Annotation-level in the parser. */\n step?: number;\n })\n | (FieldBase & {\n type: \"string[]\";\n /** Items-enum: each item must be one of these values (checkbox option sets). */\n values?: string[];\n /** With `values`: the set is advisory — any string item is also legal (`allowOther`). */\n open?: boolean;\n })\n | (FieldBase & { type: \"number[]\" | \"boolean[]\" })\n | (FieldBase & { type: \"json\" })\n | (FieldBase & { type: \"json[]\" })\n | (FieldBase & { type: \"array\"; itemKinds: string[] })\n | (FieldBase & { type: \"object\"; kind: string })\n | (FieldBase & {\n type: \"inline_object\";\n fields: Record<string, FieldSchema>;\n /** additionalProperties: true — unknown keys are legal, not residue-only. */\n open?: boolean;\n })\n | (FieldBase & { type: \"record\"; values: RecordValueType })\n | (FieldBase & {\n type: \"enum\";\n values: string[];\n /** \"One of these OR any string\" — the option set is advisory (`allowOther`). */\n open?: boolean;\n })\n | (FieldBase & {\n type: \"union\";\n scalars: Array<\"string\" | \"number\" | \"boolean\">;\n /** Object union members — kind refs (anyOf of $refs), may mix with scalars. */\n kinds?: string[];\n });\n\n/**\n * Domain fields only — __kind is enforced by the parser via KindSchema.kind\n * (block slug). A kind with `root` set has NO field map (fields stays `{}`):\n * its value is the root field's type at the top level. See the module header.\n */\nexport type KindSchema = {\n kind: string;\n fields: Record<string, FieldSchema>;\n /** Non-object root form — mutually exclusive with a non-empty `fields`. */\n root?: FieldSchema;\n};\n\nexport function readObjectKind(value: Record<string, unknown>): string | null {\n const kind = value[KIND_KEY];\n return typeof kind === \"string\" ? kind : null;\n}\n\nexport function isScalarArrayType(\n type: FieldSchema[\"type\"],\n): type is \"string[]\" | \"number[]\" | \"boolean[]\" {\n return type === \"string[]\" || type === \"number[]\" || type === \"boolean[]\";\n}\n\nexport function scalarArrayItemType(\n type: \"string[]\" | \"number[]\" | \"boolean[]\",\n): ArrayItemScalarType {\n if (type === \"number[]\") return \"number\";\n if (type === \"boolean[]\") return \"boolean\";\n return \"string\";\n}\n\n/**\n * Does this field's value domain accept ANY JSON shape (object/array/scalar/\n * null alike)? True for `json` and `json[]` ITEMS — the parser treats the\n * subtree under such a field as opaque (no kind identification, no raw_object\n * degradation: unknown structure is the declared contract, not a failure).\n */\nexport function isJsonAnyField(field: FieldSchema): boolean {\n return field.type === \"json\" || field.type === \"json[]\";\n}\n","/**\n * Stable, fast content fingerprint for IR envelopes.\n *\n * Used as the idempotence / cache key: a persisted CanonicalBlockIR is only\n * reused when its fingerprint matches the region source text it claims to\n * represent. Not cryptographic — collision resistance at the \"same message,\n * same block\" scale is all that's required, and it must be synchronous and\n * dependency-free (runs per region on the hot streaming path).\n *\n * FNV-1a 32-bit, applied twice with different seeds and concatenated, so a\n * single 32-bit collision doesn't alias two regions.\n *\n * `createFingerprinter` is the incremental form for live streams: feeding\n * chunks one at a time yields EXACTLY the same fingerprint as\n * `fingerprintText` over the concatenation — sessions never re-hash the\n * whole source per flush.\n */\n\nconst SEED_A = 0x811c9dc5;\nconst SEED_B = 0x01000193;\n\nfunction fnv1aStep(hash: number, input: string): number {\n let h = hash >>> 0;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n // h *= 16777619 (FNV prime), in 32-bit space without BigInt.\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return h >>> 0;\n}\n\nexport interface Fingerprinter {\n push(chunk: string): void;\n /** Fingerprint of everything pushed so far. */\n current(): string;\n}\n\nexport function createFingerprinter(): Fingerprinter {\n let a = SEED_A;\n let b = SEED_B;\n let length = 0;\n\n return {\n push(chunk: string): void {\n a = fnv1aStep(a, chunk);\n b = fnv1aStep(b, chunk);\n length += chunk.length;\n },\n current(): string {\n return `${length.toString(36)}-${a.toString(36)}${b.toString(36)}`;\n },\n };\n}\n\nexport function fingerprintText(source: string): string {\n const hasher = createFingerprinter();\n hasher.push(source);\n return hasher.current();\n}\n","/**\n * The canonical IR contract for structured content.\n *\n * Every source (live agent stream, DB reload, Python-preprocessed results,\n * notes, any future surface) is normalized into these shapes exactly once.\n * Anything already carrying this IR passes through downstream layers by\n * reference — see `core/normalize.ts` for the idempotence law.\n *\n * This file is pure types + path helpers. No React, no Redux, no IO.\n */\n\n/** Path of a value inside a parsed region (object keys + array indices). */\nexport type IrPath = Array<string | number>;\n\n/**\n * How a node's kind was (or wasn't) established.\n *\n * 🚨 `unverified` IS NOT `raw`, AND THE DIFFERENCE IS LOAD-BEARING (Arman's\n * ruling, 2026-08-29). \"We checked it and it is wrong\" and \"we had nothing to\n * check it with\" are opposite facts about a payload, and collapsing them cost\n * ~221 live kinds their component overnight: a kind whose schema never loaded\n * degraded to `raw`, the render route read `raw` as \"broken instance\", and\n * perfectly valid payloads were dumped as key/value lists instead of reaching\n * the component the user built. A consumer that treats `unverified` as a\n * failure is reintroducing that outage — the data is intact and MAY be\n * entirely valid; all that is missing is the schema that would prove it.\n */\nexport type IrKindState =\n | \"resolved\" // __kind seen + schema validated\n | \"speculative\" // committed from parent itemKinds before __kind arrived\n | \"pending_kind\" // object open, no __kind yet, no speculation possible\n | \"pending_schema\" // kind known, registry cold-fetch in flight\n | \"unverified\" // kind known, NO schema was ever available — never checked\n | \"raw\"; // checked and FAILED, or structurally broken\n\n/**\n * System-level zero-data-loss channel. Unknown keys are NEVER merged into a\n * node's `value` (they'd be indistinguishable from schema fields); they are\n * carried here verbatim — Protobuf unknown-fields discipline. Distinct from\n * any domain-level `additionalDetails` field, which is an ordinary schema\n * field inside `value`.\n */\nexport interface IrResidue {\n /** Keys present in the source object but absent from the kind schema. */\n extra: Record<string, unknown> | null;\n /** Optional schema fields the source never provided. */\n optionalMissing: string[] | null;\n /** Structured warnings attached during parsing (speculation backtracks, truncation, …). */\n notices: Array<{ code: string; message: string; at?: number }> | null;\n}\n\n/** Which wire syntax carried the kind discriminator for a node. */\nexport type IrDiscriminator =\n | { format: \"json\"; key: \"__kind\" }\n | { format: \"xml\"; tag: string }\n | { format: \"fence\"; language: string };\n\n/** A schema-shaped structured node. Children live inside `value` (and carry their own metadata via `nodeIndex`). */\nexport interface IrStructuredNode {\n role: \"structured\";\n /** Canonical kind slug. Empty string while pending. */\n kind: string;\n kindState: IrKindState;\n discriminator: IrDiscriminator;\n /** Region-relative path ([] = region root). */\n path: IrPath;\n status: \"streaming\" | \"complete\" | \"error\";\n /** Compliant snapshot: schema fields + __kind ONLY. Unknown keys → residue. */\n value: Record<string, unknown>;\n residue: IrResidue | null;\n}\n\n/** Envelope version. Bump = migration point for persisted envelopes. */\nexport const IR_VERSION = 1 as const;\n\n/** Reserved key carrying the envelope inside RenderBlockPayload.data. */\nexport const IR_ENVELOPE_KEY = \"__ir\" as const;\n\n/**\n * One parsed region's canonical form. Serializable (plain JSON), carried on\n * render blocks (`data.__ir`), persisted on artifacts and message metadata,\n * and — with `engine: \"py-block-detector\"` — accepted pre-built from Python.\n */\nexport interface CanonicalBlockIR {\n v: typeof IR_VERSION;\n /** Provenance: which implementation produced this envelope. */\n engine: \"fe-kind-parser\" | \"py-block-detector\";\n /** Stable hash of the region source text — the idempotence / cache key. */\n fingerprint: string;\n root: IrStructuredNode;\n /**\n * pathKey → node metadata for per-path readers (child kinds under root).\n * Carries each child node's residue — child snapshot values inside\n * `root.value` hold schema fields only, so WITHOUT this the envelope would\n * silently drop nested unknown keys (zero-data-loss violation).\n */\n nodeIndex?: Record<\n string,\n Pick<IrStructuredNode, \"kind\" | \"kindState\" | \"status\"> & {\n residue?: IrResidue | null;\n }\n >;\n}\n\n/** Normalizer output segment: prose stays raw text; structured regions carry IR. */\nexport type CanonicalSegment =\n | { role: \"text\"; content: string }\n | {\n role: \"block\";\n blockType: string;\n content: string;\n ir: CanonicalBlockIR | null;\n };\n\nexport interface CanonicalContent {\n v: typeof IR_VERSION;\n segments: CanonicalSegment[];\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers — the ONE implementation. Consumers must not roll their own.\n// ---------------------------------------------------------------------------\n\nexport function irPathKey(path: IrPath): string {\n return path.map((segment) => String(segment)).join(\".\");\n}\n\nexport function irPathsEqual(left: IrPath, right: IrPath): boolean {\n if (left.length !== right.length) return false;\n for (let i = 0; i < left.length; i++) {\n if (left[i] !== right[i]) return false;\n }\n return true;\n}\n\nexport function irPathIsUnderOrEqual(path: IrPath, prefix: IrPath): boolean {\n if (path.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (path[i] !== prefix[i]) return false;\n }\n return true;\n}\n\n/** Human label for a path (\"root\", \"cards[2].front\"). */\nexport function irPathLabel(path: IrPath): string {\n if (path.length === 0) return \"root\";\n\n const parts: string[] = [];\n for (const segment of path) {\n if (typeof segment === \"number\") {\n parts[parts.length - 1] += `[${segment}]`;\n } else {\n parts.push(String(segment));\n }\n }\n return parts.join(\".\");\n}\n\n/** True when every residue channel is empty — normalize to null instead. */\nexport function isEmptyResidue(residue: IrResidue): boolean {\n return (\n (residue.extra === null || Object.keys(residue.extra).length === 0) &&\n (residue.optionalMissing === null ||\n residue.optionalMissing.length === 0) &&\n (residue.notices === null || residue.notices.length === 0)\n );\n}\n","/**\n * Idempotent normalizer — the \"recognizes its own work\" property.\n *\n * THE LAW: anything already carrying a current CanonicalBlockIR envelope is\n * returned BY REFERENCE — zero reprocessing, reference equality holds, React\n * bails out. Only raw text (a detected region's source) is ever parsed, and\n * it is parsed exactly once per fingerprint.\n *\n * `normalizeJsonRegion` is the one-shot mode: the same KindStreamParser that\n * powers live streams, run over a complete region string (DB reloads,\n * reconcile passes), assembled through the same IrTree the live session uses\n * — stream and static output are structurally identical by construction.\n */\n\nimport { fingerprintText } from \"./fingerprint\";\nimport {\n IR_VERSION,\n type CanonicalBlockIR,\n type IrDiscriminator,\n type IrStructuredNode,\n} from \"./ir-types\";\nimport { KIND_KEY, type KindSchema } from \"./kind-schema.types\";\nimport { IrTree } from \"./ir-tree\";\nimport {\n createKindStreamParser,\n type SchemaResolver,\n} from \"./kind-parser\";\n\nexport function isCanonicalBlockIR(value: unknown): value is CanonicalBlockIR {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as Partial<CanonicalBlockIR>;\n return (\n candidate.v === IR_VERSION &&\n typeof candidate.fingerprint === \"string\" &&\n typeof candidate.engine === \"string\" &&\n typeof candidate.root === \"object\" &&\n candidate.root !== null &&\n (candidate.root as IrStructuredNode).role === \"structured\"\n );\n}\n\n/**\n * The idempotence fast path: return the existing envelope by reference when\n * it still describes this source text; null means \"parse needed\".\n */\nexport function reuseEnvelopeIfCurrent(\n source: string,\n candidate: unknown,\n): CanonicalBlockIR | null {\n if (!isCanonicalBlockIR(candidate)) return null;\n return candidate.fingerprint === fingerprintText(source) ? candidate : null;\n}\n\nexport interface NormalizeJsonRegionOptions {\n schemas: Record<string, KindSchema> | SchemaResolver;\n /** Known-context root prediction (agent output schema, fence hint). */\n expectedRootKind?: string;\n /**\n * Pass a previously persisted envelope (message metadata, artifact row);\n * when its fingerprint matches, it is returned as-is and nothing parses.\n */\n existing?: unknown;\n}\n\nexport interface CompleteValueEnvelopeOptions {\n /**\n * Wire discriminator recorded on the root node — which syntax established\n * the kind. Defaults to the JSON `__kind` key; XML surfaces converging at\n * region finalize pass `xmlDiscriminator(tag)` so round-trip serializers\n * know the original arrival format.\n */\n discriminator?: IrDiscriminator;\n}\n\n/**\n * Build a resolved, complete envelope directly from an already-structured\n * value (a persisted artifact's `content.data` object, or a completed XML\n * region's strategy output). This is the zero-reprocessing rehydration path:\n * the value IS the reconstructed region value (schema fields + residue extras\n * merged at persist time), so no tokenizer/parser run is needed — the\n * envelope wraps it verbatim. The fingerprint hashes the canonical value\n * serialization, so any two paths producing the same value produce the SAME\n * envelope (stream ≡ static by construction).\n */\nexport function envelopeFromCompleteValue(\n value: Record<string, unknown>,\n kind: string,\n options?: CompleteValueEnvelopeOptions,\n): CanonicalBlockIR {\n return {\n v: IR_VERSION,\n engine: \"fe-kind-parser\",\n fingerprint: fingerprintText(JSON.stringify(value)),\n root: {\n role: \"structured\",\n kind,\n kindState: \"resolved\",\n discriminator: options?.discriminator ?? { format: \"json\", key: KIND_KEY },\n path: [],\n status: \"complete\",\n value,\n residue: null,\n },\n };\n}\n\n/** One-shot: complete region text in → canonical envelope out. */\nexport function normalizeJsonRegion(\n source: string,\n options: NormalizeJsonRegionOptions,\n): CanonicalBlockIR {\n const reused = reuseEnvelopeIfCurrent(source, options.existing);\n if (reused) return reused;\n\n const tree = new IrTree();\n const parser = createKindStreamParser({\n schemas: options.schemas,\n ...(options.expectedRootKind !== undefined && {\n expectedRootKind: options.expectedRootKind,\n }),\n onEvent(event) {\n tree.applyEvent(event);\n },\n });\n parser.push(source);\n parser.end();\n\n return tree.buildEnvelope(fingerprintText(source));\n}\n","/**\n * The dual gate — Arman's law as executable code: a kind is only `is_active`\n * when its canonical `sample_data` passes BOTH systems.\n *\n * 1. Structural (Pydantic): the sample validates against the kind's\n * `emitted_json_schema`. Python's Pydantic is the AUTHORITATIVE owner of\n * this leg, but it reads the SAME materialized `emitted_json_schema` — so\n * this TS ajv check and the Python check validate the same sample against\n * the same schema and agree by construction. A disagreement IS the\n * screamer (a schema Pydantic can't express, or an ajv/Pydantic gap).\n * 2. Render (UI): the sample lights up the kind's real component. TS is the\n * AUTHORITATIVE owner of this leg. It is a PROXY for a DOM render, not a\n * DOM render. Exactly what it checks, and nothing more:\n * · the kind has something to render with (`legacyBlockType` or\n * `component`);\n * · for BRIDGED kinds, `toLegacyServerData(sample)` returns a plain\n * object that is SEMANTICALLY non-empty — see\n * `describeUnrenderableBridgeOutput` for the exact predicate;\n * · BRIDGELESS kinds pass with a recorded caveat — nothing is verified.\n *\n * This catches the 2026-07-04 \"No flashcards available yet\" class: a\n * bridge that returns `undefined`, `{}`, `{language:\"json\"}` (the raw\n * code-region annotation), or an object whose every value is empty.\n *\n * What it does NOT do — stated plainly, because a gate that overclaims is\n * worse than no gate: it does not mount the component, and it cannot know\n * WHICH key carries the payload. `{title:\"Cell Biology\", cards:[]}` passes\n * this leg on `title` alone. Proving the payload key is populated needs\n * per-kind knowledge the gate deliberately does not have; a DOM-level\n * render check is the deeper leg, deferred to an RTL harness.\n *\n * Both legs necessary, neither sufficient. Fail either → `isActive: false` and\n * the caller reports it loudly (Error Inspector, `content-ir`) and holds the\n * row out of production. This module is PURE (deps injected) so it runs in the\n * harness, in CI, and in a browser author-save alike.\n *\n * Ownership split (the 2026-07-05 kind-registry storage design, since deleted; the live contract is the code below): the caller writes the outcome\n * to the LIVE `content_ir.kind_definition` row's `is_active`; the canonical\n * `_version_capture` trigger snapshots that state into `history.row_versions`\n * (never a post-hoc history mutation).\n */\n\nimport Ajv, { type ValidateFunction } from \"ajv\";\nimport type { CanonicalBlockIR } from \"../core/ir-types\";\nimport { KIND_KEY } from \"../core/kind-schema.types\";\nimport { envelopeFromCompleteValue } from \"../core/normalize\";\n\n/** The facets the render leg needs — a structural subset of KindDefinition. */\nexport interface DualGateDefinition {\n legacyBlockType?: string;\n toLegacyServerData?: (\n envelope: CanonicalBlockIR,\n ) => Record<string, unknown> | undefined;\n component?: { load: () => Promise<unknown> };\n}\n\n/**\n * The render leg's second satisfier: an ACTIVE `role='output'` component\n * resolved from `content_ir.kind_component`. Feed this from\n * `resolveComponent(kind, \"web\", \"output\")`.\n *\n * Why this exists: the render leg originally consulted only the compiled TS\n * registry, so an agent-authored kind whose renderer is a `source='db'` row was\n * structurally unable to pass — `definition` came back null and the gate said\n * \"no component\" about a kind that had a live, working one. Six authored kinds\n * (wine_tasting, employee_card, employee_roster, employee_of_the_week,\n * flashcard_deck, arman_video_prompt) sat permanently inactive because of it.\n *\n * NOT a mirror of `content_ir.evaluate_kind_activation`. The SQL render leg is\n * presence-only — it checks that an active `role='output'` row exists, because\n * SQL cannot execute a TypeScript bridge. THIS leg is strictly stronger: for a\n * compiled kind it also runs `toLegacyServerData` and rejects semantically\n * empty output (the \"No <kind> available\" class).\n *\n * The asymmetry is deliberate and bounded: SQL is the FLOOR (necessary, and\n * sufficient for DB-authored components, which own no bridge), while this leg\n * is the CEILING for compiled kinds. Consequence to know: activating a COMPILED\n * kind whose bridge is broken would pass the RPC and fail here. Compiled kinds\n * are activated by developers through `scripts/shape/activate-kinds.ts`, which\n * runs this leg — the studio control only ever reaches owner-authored kinds.\n * If that ever stops being true, the browser control must run this gate before\n * enabling its button.\n */\nexport interface DualGateResolvedComponent {\n componentKey: string;\n /** The row's own render-trust verdict (R6). Inactive rows do not satisfy. */\n isActive: boolean;\n /** \"bundled\" | \"db\" — reported in the leg detail, never gates the verdict. */\n source: string;\n}\n\n/**\n * THE FALLBACK IS NOT A COMPONENT (Arman, 2026-08-23). MUST equal\n * `GENERIC_STRUCTURED_COMPONENT_KEY` (../react/kind-route) and\n * `content_ir.evaluate_kind_activation`'s render leg. Duplicated rather than\n * imported: this registry module must not pull in the react layer.\n */\nconst GENERIC_FALLBACK_COMPONENT_KEY = \"generic_structured\";\n\n/**\n * A resolved component SATISFIES the render leg only when it is active AND is\n * not the generic fallback.\n *\n * `resolveComponent(kind, \"web\", \"output\")` ALWAYS answers — the platform\n * guarantees a kind can never fail to render, so it falls back to\n * `generic_structured`. Feeding that answer straight in (KindGateTab.tsx does)\n * made the render leg pass for every kind ever built, including kinds whose\n * only \"component\" is the key/value dump: an unfalsifiable green\n * (conversion-campaigns.md § Law 4b). The gate's own detail string was\n * literally printing `resolved bundled component \"generic_structured\"` as the\n * reason it passed.\n */\nfunction satisfies(resolved?: DualGateResolvedComponent | null): boolean {\n return Boolean(\n resolved?.isActive &&\n resolved.componentKey !== GENERIC_FALLBACK_COMPONENT_KEY,\n );\n}\n\nexport interface DualGateInput {\n kind: string;\n /** The canonical instance (kind_definition.sample_data). */\n sample: Record<string, unknown>;\n /** The materialized kind_definition.emitted_json_schema (plain, no __kind). */\n emittedJsonSchema: unknown;\n /** The registry definition for the kind (or null when unregistered). */\n definition: DualGateDefinition | null;\n /**\n * The resolver's `(kind, web, output)` answer, when the caller has one.\n * Omit (or pass null) to check the compiled registry alone.\n */\n resolvedComponent?: DualGateResolvedComponent | null;\n /**\n * True when the kind is a generated data-only contract\n * (`metadata.family` ∈ workflow_io | tool_io | action_io | agent_io).\n * Those are passed between nodes and never rendered, so the render leg is\n * structurally inapplicable — the same `n/a` doctrine the shape doctor uses.\n * Failing them would be noise, and noise erodes the gate.\n */\n dataOnly?: boolean;\n}\n\nexport interface LegResult {\n ok: boolean;\n detail?: string;\n}\n\nexport interface DualGateResult {\n /** True only when BOTH legs pass — the value the caller writes to is_active. */\n isActive: boolean;\n structural: LegResult;\n render: LegResult;\n}\n\n// ajv authoring-strictness is OFF (tolerate provider schema keywords like the\n// recursive \"#\" root ref); DATA strictness comes from the schema itself\n// (additionalProperties:false + required), which ajv enforces during validate.\nconst ajv = new Ajv({ allErrors: true, strict: false });\n\n/**\n * BACKWARD-COMPATIBILITY TOLERANCE ONLY — never the contract.\n *\n * This existed because `emitted_json_schema` was the marker-free \"wire\" export:\n * the dead doctrine held that `__kind` was envelope framing injected at emit\n * time, so a sample had to be stripped to validate. That doctrine is GONE\n * (Arman, 2026-08-23): every kind schema now DECLARES `__kind` at every object\n * position — const + required where the kind is known — so a marked sample\n * validates AS-IS and stripping it is not a no-op, it is a FAILURE\n * (`must have required property '__kind'`).\n *\n * So the strip is a SECOND ATTEMPT, never the first one: the leg validates the\n * caller's value verbatim, and only if that fails retries a marker-free LOCAL\n * copy, so a row still pinned to a pre-2026-08-23 marker-free schema keeps\n * validating while it is repinned. The caller's value is never touched. Delete\n * it once no live row pins a marker-free version. KINDS_EVERYWHERE_PLAN §4.2.\n */\nfunction stripKind(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stripKind);\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (k === KIND_KEY) continue;\n out[k] = stripKind(v);\n }\n return out;\n }\n return value;\n}\n\nfunction describeAjvErrors(errors: ValidateFunction[\"errors\"]): string[] {\n return (errors ?? [])\n .map((e) => `${e.instancePath || \"(root)\"} ${e.message ?? \"\"}`.trim())\n .slice(0, 8);\n}\n\n/** True when the root schema declares the `__kind` marker as a property. */\nfunction schemaDeclaresKind(emittedJsonSchema: unknown): boolean {\n if (!emittedJsonSchema || typeof emittedJsonSchema !== \"object\") return false;\n const properties = (emittedJsonSchema as Record<string, unknown>).properties;\n return (\n !!properties &&\n typeof properties === \"object\" &&\n KIND_KEY in (properties as Record<string, unknown>)\n );\n}\n\n/**\n * The structural leg, exported on its own so the shape doctor\n * (`shape-doctor.ts`) RECOMPUTES gate validation with the exact same ajv\n * config + marker semantics as activation (verbatim first, marker-free retry\n * second) — never a parallel validator. `sample` is `unknown` (not `Record`) because kind examples may\n * legitimately be scalars/arrays (workflow I/O kinds like `text`/`number`).\n */\nexport function validateStructuralLeg(\n sample: unknown,\n emittedJsonSchema: unknown,\n): LegResult {\n let validate: ValidateFunction;\n try {\n validate = ajv.compile(emittedJsonSchema as object);\n } catch (err) {\n return {\n ok: false,\n detail: `emitted_json_schema failed to compile: ${\n err instanceof Error ? err.message : String(err)\n }`,\n };\n }\n // The marker is DATA: validate exactly what the caller holds. Only if that\n // fails do we retry the marker-free reduction, for a value still pinned to a\n // pre-2026-08-23 schema that never declared `__kind` (see `stripKind`).\n if (validate(sample)) return { ok: true };\n const markedErrors = describeAjvErrors(validate.errors);\n if (validate(stripKind(sample))) return { ok: true };\n const strippedErrors = describeAjvErrors(validate.errors);\n\n // Report the attempt that matches the schema in hand, so the message names\n // the real defect instead of the fallback's collateral damage.\n const errors = schemaDeclaresKind(emittedJsonSchema)\n ? markedErrors\n : strippedErrors;\n return { ok: false, detail: `sample failed schema: ${errors.join(\"; \")}` };\n}\n\n/**\n * Keys a bridge may emit that carry NO kind content — pure region annotation.\n *\n * `language` is the sole member, and it is here because of a real, shipped bug.\n * `StreamBlockAccumulator.buildBlockData()` emits `data: { language: \"json\" }`\n * for an UNTYPED code region (a bare ```json fence), and\n * `render-block-to-content-block.ts` maps `rb.data` → `block.serverData`. That\n * annotation therefore arrives downstream as a TRUTHY `serverData` holding zero\n * kind data. On 2026-07-04 it reached the flashcards component in place of\n * `cards` and rendered \"No flashcards available yet\" (see `react/kind-route.ts`,\n * which now REPLACES or CLEARS it rather than forwarding it).\n *\n * This list only removes keys from the \"is anything substantive here?\" tally —\n * it never fails a bridge on its own. A kind whose serverData is\n * `{ language: \"python\", code: \"...\" }` still passes, on `code`. Only a bridge\n * whose ENTIRE output is annotation has produced nothing renderable.\n *\n * Add a key here only with evidence that the region-annotation path emits it and\n * that it can never itself be kind content.\n */\nconst SERVER_DATA_ANNOTATION_KEYS: ReadonlySet<string> = new Set([\"language\"]);\n\n/** serverData is JSON-shaped; this only guards pathological/cyclic nesting. */\nconst SUBSTANCE_DEPTH_LIMIT = 8;\n\n/**\n * Is `value` real content that a component could render?\n *\n * string → non-empty after trim (\"\" and \" \" are not content)\n * number → any finite number (0 IS content: `completedItems: 0`)\n * boolean → always (false IS content: `isComplete: false`)\n * array → ≥1 substantive ELEMENT ([] and [\"\"] are not content)\n * object → ≥1 substantive VALUE ({} and {a:\"\"} are not content)\n * null | undefined | NaN | function | symbol | bigint → never content\n *\n * Recursive by design: `{ data: { items: [] } }` is structurally present and\n * semantically empty, and telling those two apart is this leg's entire job.\n */\nfunction isSubstantiveValue(value: unknown, depth = 0): boolean {\n if (value === null || value === undefined) return false;\n if (typeof value === \"string\") return value.trim().length > 0;\n if (typeof value === \"number\") return Number.isFinite(value);\n if (typeof value === \"boolean\") return true;\n // Deep AND present — stop descending rather than claim it is empty.\n if (depth >= SUBSTANCE_DEPTH_LIMIT) return true;\n if (Array.isArray(value)) {\n return value.some((entry) => isSubstantiveValue(entry, depth + 1));\n }\n if (typeof value === \"object\") {\n return Object.values(value as Record<string, unknown>).some((entry) =>\n isSubstantiveValue(entry, depth + 1),\n );\n }\n return false;\n}\n\n/**\n * The render leg's real predicate: why is this bridge output NOT renderable?\n * Returns a specific reason, or `null` when the output is genuinely renderable.\n *\n * `serverData` is typed `unknown` on purpose — the facet's declared return type\n * is a promise the gate must not take on faith; a bridge is ordinary code that\n * can return anything at runtime.\n *\n * Renderable ⇔ a non-null, non-array object with at least one key that is not\n * pure annotation AND whose value is substantive (see `isSubstantiveValue`).\n */\nfunction describeUnrenderableBridgeOutput(serverData: unknown): string | null {\n if (serverData === undefined || serverData === null) {\n return \"bridge returned no serverData (undefined)\";\n }\n if (typeof serverData !== \"object\") {\n return `bridge returned a non-object serverData (${typeof serverData})`;\n }\n if (Array.isArray(serverData)) {\n return \"bridge returned an array, not a serverData record\";\n }\n\n const record = serverData as Record<string, unknown>;\n const keys = Object.keys(record);\n if (keys.length === 0) return \"bridge returned an empty object ({})\";\n\n const contentKeys = keys.filter((key) => !SERVER_DATA_ANNOTATION_KEYS.has(key));\n if (contentKeys.length === 0) {\n return `bridge returned only annotation keys [${keys.join(\", \")}] — that is the raw code-region annotation, not kind data`;\n }\n if (!contentKeys.some((key) => isSubstantiveValue(record[key]))) {\n return `bridge returned serverData whose every content value is empty (keys: ${contentKeys.join(\", \")}) — structurally present, semantically empty`;\n }\n return null;\n}\n\nfunction validateRender(\n kind: string,\n sample: Record<string, unknown>,\n definition: DualGateDefinition | null,\n resolvedComponent?: DualGateResolvedComponent | null,\n dataOnly?: boolean,\n): LegResult {\n // Data-only contract kinds are never rendered — the leg is n/a, not failed.\n if (dataOnly) {\n return {\n ok: true,\n detail:\n \"data-only contract kind — render leg is structurally inapplicable (n/a)\",\n };\n }\n\n // ORDER IS LOAD-BEARING. The compiled-bridge check below is the STRONGEST\n // satisfier — it actually exercises the bridge and catches the\n // \"No <kind> available\" class (the 2026-07-04 flashcards regression). Every\n // compiled kind also owns a `source='bundled'` kind_component row, so\n // checking `resolvedComponent` first would short-circuit past the bridge for\n // exactly the kinds that guard protects, silently disabling it. The resolved\n // component is therefore the LAST resort, not the first.\n // \"Only the fallback resolved\" is a DIFFERENT repair from \"nothing resolved\"\n // (author a component AND retire the decoy vs. just author one), so the\n // detail says which — mirroring evaluate_kind_activation's two branches.\n const onlyFallback =\n resolvedComponent?.isActive &&\n resolvedComponent.componentKey === GENERIC_FALLBACK_COMPONENT_KEY;\n const noComponentDetail = onlyFallback\n ? `the only active role='output' component for kind \"${kind}\" is ` +\n `'${GENERIC_FALLBACK_COMPONENT_KEY}' — that IS the generic viewer, i.e. no ` +\n `component. A reader would get a key/value dump. Author a real ` +\n `source='db' component (or register a compiled one), then retire the ` +\n `generic row.`\n : null;\n\n if (!definition && !satisfies(resolvedComponent)) {\n return {\n ok: false,\n detail:\n noComponentDetail ??\n `kind \"${kind}\" has no component (not in the compiled registry, and no active role='output' kind_component row) — nothing to render`,\n };\n }\n\n if (\n definition &&\n !definition.legacyBlockType &&\n !definition.component &&\n !definition.toLegacyServerData &&\n !satisfies(resolvedComponent)\n ) {\n return {\n ok: false,\n detail:\n noComponentDetail ??\n `kind \"${kind}\" has no component (no compiled legacyBlockType/component facet, and no active role='output' kind_component row) — nothing to render`,\n };\n }\n\n // Bridged kinds: the bridge MUST derive real serverData from the sample.\n if (definition?.toLegacyServerData) {\n let serverData: Record<string, unknown> | undefined;\n try {\n serverData = definition.toLegacyServerData(\n envelopeFromCompleteValue(sample, kind),\n );\n } catch (err) {\n return {\n ok: false,\n detail: `toLegacyServerData threw: ${\n err instanceof Error ? err.message : String(err)\n }`,\n };\n }\n const problem = describeUnrenderableBridgeOutput(serverData);\n if (problem) {\n return {\n ok: false,\n detail: `${problem} (the \"No ${kind} available\" failure)`,\n };\n }\n return { ok: true };\n }\n\n // Bridgeless kinds parse their own content — the bridge-data proxy can't\n // verify them; a full DOM render check is the deeper leg (deferred to an RTL\n // harness). Pass with a recorded caveat so it's never silently \"fully gated\",\n // and name the satisfier so a reader can tell a compiled bridgeless component\n // from a DB-authored one without re-deriving it.\n const satisfier = definition?.legacyBlockType\n ? `compiled component \"${definition.legacyBlockType}\"`\n : definition?.component\n ? \"compiled component facet\"\n : satisfies(resolvedComponent) && resolvedComponent\n ? `resolved ${resolvedComponent.source} component \"${resolvedComponent.componentKey}\"`\n : \"component\";\n return {\n ok: true,\n detail: `bridgeless kind — ${satisfier} parses content itself; full DOM render check deferred to an RTL harness`,\n };\n}\n\nexport function runKindDualGate(input: DualGateInput): DualGateResult {\n const structural = validateStructuralLeg(input.sample, input.emittedJsonSchema);\n const render = validateRender(\n input.kind,\n input.sample,\n input.definition,\n input.resolvedComponent,\n input.dataOnly,\n );\n return { isActive: structural.ok && render.ok, structural, render };\n}\n\n/**\n * One-line, Error-Inspector-ready reason for a failed gate (empty string when\n * it passed). The caller feeds this to `captureError({ source: \"content-ir\" })`\n * and sets `is_active=false`.\n */\nexport function describeDualGateFailure(\n kind: string,\n result: DualGateResult,\n): string {\n if (result.isActive) return \"\";\n const parts: string[] = [];\n if (!result.structural.ok) {\n parts.push(`structural(Pydantic): ${result.structural.detail ?? \"failed\"}`);\n }\n if (!result.render.ok) {\n parts.push(`render(UI): ${result.render.detail ?? \"failed\"}`);\n }\n return `kind \"${kind}\" failed the dual gate — ${parts.join(\" | \")}`;\n}\n"]}
|