@ai-matrx/content-ir 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/index.cjs +144 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +144 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 — 2026-08-29
|
|
4
|
+
|
|
5
|
+
- **`kindSchemaFromJsonSchema` — the registry-facing door for deriving a
|
|
6
|
+
kind's fields from its schema.** One schema in; the kind's `KindSchema` out,
|
|
7
|
+
plus every child kind the schema declared inline or through `$defs`, plus
|
|
8
|
+
the conversion problems. Pure and synchronous, so a host can call it from a
|
|
9
|
+
warm load, a cold fetch, a reducer, or a test. Accepts a bare JSON Schema
|
|
10
|
+
and the provider `{name, schema, strict}` envelope alike.
|
|
11
|
+
|
|
12
|
+
This is what makes a stored second copy of a kind's fields unnecessary. It
|
|
13
|
+
never returns a partial guess: an inexpressible field widens to `json`, and
|
|
14
|
+
only a structurally unusable root yields null.
|
|
15
|
+
|
|
16
|
+
## 0.6.0 — 2026-08-29
|
|
17
|
+
|
|
18
|
+
- **`$ref` / `$defs` resolution — a kind's child kinds are no longer invisible
|
|
19
|
+
to JSON Schema → KindSchema.** `convertAiSchemaToBlockFields` resolves `#`,
|
|
20
|
+
`#/$defs/<name>` and `#/definitions/<name>` in field position, in array
|
|
21
|
+
`items`, and inside `anyOf`/`oneOf` (including `items: {anyOf: [$ref, …]}`,
|
|
22
|
+
the multi-kind array). Referenced defs are registered as their own drafts,
|
|
23
|
+
with a cycle brake so a self-referencing kind terminates. An unresolvable
|
|
24
|
+
ref is a loud error, never a guess.
|
|
25
|
+
|
|
26
|
+
WHY: `kindSchemaToJsonSchema` externalizes every child kind as a `$def`, and
|
|
27
|
+
the reverse direction rejected `$ref` outright — so every
|
|
28
|
+
`{type:"object", kind}` and `{type:"array", itemKinds}` field came back
|
|
29
|
+
EMPTY. Measured over the 62 live kinds carrying both a stored field list and
|
|
30
|
+
a schema, 36 lost fields this way (sections, slides, nodes, edges, criteria,
|
|
31
|
+
questions, segments, …).
|
|
32
|
+
|
|
33
|
+
- **A `__kind`-carrying record reads back as a record.** An object whose only
|
|
34
|
+
declared property is `__kind`, with typed `additionalProperties`, is the
|
|
35
|
+
forward converter's emission for `{type:"record", values}` — it was widening
|
|
36
|
+
to an open `inline_object` and losing the value type.
|
|
37
|
+
|
|
38
|
+
- Live-corpus result: schema-derived KindSchemas now match the stored field
|
|
39
|
+
list byte-for-byte for 48 of 62 kinds, with 1 strictly richer. The remaining
|
|
40
|
+
13 are DIVERGENCE BETWEEN THE TWO STORED COPIES, not conversion gaps — 4
|
|
41
|
+
stored lists carry `__kind` as a data field (it is the discriminator, never
|
|
42
|
+
a field), and 9 disagree on enums, bounds, required flags or descriptions
|
|
43
|
+
the schema states and the copy does not. That is the evidence for retiring
|
|
44
|
+
the stored copy.
|
|
45
|
+
|
|
3
46
|
## 0.5.0 — 2026-08-29
|
|
4
47
|
|
|
5
48
|
- **A multi-type union is no longer silently narrowed to its first member.**
|
package/dist/index.cjs
CHANGED
|
@@ -3177,6 +3177,29 @@ function buildAgentSchemaWithRenderBlockSupport(input, rootKindSlug, arrayBindin
|
|
|
3177
3177
|
}
|
|
3178
3178
|
return updatedRoot;
|
|
3179
3179
|
}
|
|
3180
|
+
function resolveRef(ref, ctx) {
|
|
3181
|
+
if (ref === "#" || ref === "#/") {
|
|
3182
|
+
return { slug: ctx.schemaName, node: ctx.rootSchema };
|
|
3183
|
+
}
|
|
3184
|
+
const match = /^#\/(?:\$defs|definitions)\/(.+)$/.exec(ref);
|
|
3185
|
+
if (!match) return null;
|
|
3186
|
+
const name = decodeURIComponent(
|
|
3187
|
+
(match[1] ?? "").replace(/~1/g, "/").replace(/~0/g, "~")
|
|
3188
|
+
);
|
|
3189
|
+
const node = ctx.defs[name];
|
|
3190
|
+
if (!isRecord4(node)) return null;
|
|
3191
|
+
const declared = isRecord4(node.properties) ? readBlockKindFromProperties(node.properties) : null;
|
|
3192
|
+
return { slug: declared ?? name, node };
|
|
3193
|
+
}
|
|
3194
|
+
function refToObjectField(resolved, path, ctx) {
|
|
3195
|
+
if (ctx.refsInProgress.has(resolved.slug)) return;
|
|
3196
|
+
ctx.refsInProgress.add(resolved.slug);
|
|
3197
|
+
try {
|
|
3198
|
+
registerDeclaredKindDraft(resolved.slug, resolved.node, path, ctx);
|
|
3199
|
+
} finally {
|
|
3200
|
+
ctx.refsInProgress.delete(resolved.slug);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3180
3203
|
function isAnyValueSchema(node) {
|
|
3181
3204
|
return node.type === void 0 && node.enum === void 0 && node.const === void 0 && node.properties === void 0 && node.items === void 0 && node.additionalProperties === void 0 && node.anyOf === void 0 && node.oneOf === void 0 && node.allOf === void 0 && node.$ref === void 0;
|
|
3182
3205
|
}
|
|
@@ -3213,12 +3236,21 @@ function convertProperty(fieldName, node, required, path, ctx) {
|
|
|
3213
3236
|
}
|
|
3214
3237
|
function convertPropertyCore(fieldName, node, required, path, ctx) {
|
|
3215
3238
|
if (typeof node.$ref === "string") {
|
|
3216
|
-
ctx
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3239
|
+
const resolved = resolveRef(node.$ref, ctx);
|
|
3240
|
+
if (!resolved) {
|
|
3241
|
+
ctx.problems.push({
|
|
3242
|
+
severity: "error",
|
|
3243
|
+
path,
|
|
3244
|
+
message: `Unresolvable $ref ("${node.$ref}") \u2014 only "#", "#/$defs/<name>" and "#/definitions/<name>" are supported.`
|
|
3245
|
+
});
|
|
3246
|
+
return null;
|
|
3247
|
+
}
|
|
3248
|
+
refToObjectField(resolved, path, ctx);
|
|
3249
|
+
return {
|
|
3250
|
+
...requiredNullableFlags(required),
|
|
3251
|
+
type: "object",
|
|
3252
|
+
kind: resolved.slug
|
|
3253
|
+
};
|
|
3222
3254
|
}
|
|
3223
3255
|
if (Array.isArray(node.anyOf) || Array.isArray(node.oneOf)) {
|
|
3224
3256
|
const variants = node.anyOf ?? node.oneOf;
|
|
@@ -3234,12 +3266,18 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
|
|
|
3234
3266
|
continue;
|
|
3235
3267
|
}
|
|
3236
3268
|
if (typeof variant.$ref === "string") {
|
|
3237
|
-
ctx
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3269
|
+
const resolvedVariant = resolveRef(variant.$ref, ctx);
|
|
3270
|
+
if (!resolvedVariant) {
|
|
3271
|
+
ctx.problems.push({
|
|
3272
|
+
severity: "error",
|
|
3273
|
+
path,
|
|
3274
|
+
message: `Unresolvable $ref inside anyOf/oneOf ("${variant.$ref}").`
|
|
3275
|
+
});
|
|
3276
|
+
unsupported = true;
|
|
3277
|
+
continue;
|
|
3278
|
+
}
|
|
3279
|
+
refToObjectField(resolvedVariant, path, ctx);
|
|
3280
|
+
memberKinds.push(resolvedVariant.slug);
|
|
3243
3281
|
continue;
|
|
3244
3282
|
}
|
|
3245
3283
|
const { type: type2, nullable: nullable2 } = resolvePrimaryType(variant);
|
|
@@ -3442,6 +3480,27 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
|
|
|
3442
3480
|
});
|
|
3443
3481
|
return null;
|
|
3444
3482
|
}
|
|
3483
|
+
if (typeof itemNode.$ref === "string") {
|
|
3484
|
+
const resolvedItem = resolveRef(itemNode.$ref, ctx);
|
|
3485
|
+
if (!resolvedItem) {
|
|
3486
|
+
ctx.problems.push({
|
|
3487
|
+
severity: "error",
|
|
3488
|
+
path: `${path}[]`,
|
|
3489
|
+
message: `Unresolvable $ref in array items ("${itemNode.$ref}").`
|
|
3490
|
+
});
|
|
3491
|
+
return null;
|
|
3492
|
+
}
|
|
3493
|
+
refToObjectField(resolvedItem, `${path}[]`, ctx);
|
|
3494
|
+
ctx.arrayBindings.push({
|
|
3495
|
+
arrayField: fieldName,
|
|
3496
|
+
itemKindSlug: resolvedItem.slug
|
|
3497
|
+
});
|
|
3498
|
+
return {
|
|
3499
|
+
...requiredNullableFlags(required, nullable),
|
|
3500
|
+
type: "array",
|
|
3501
|
+
itemKinds: [resolvedItem.slug]
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3445
3504
|
if (isAnyValueSchema(itemNode)) {
|
|
3446
3505
|
return {
|
|
3447
3506
|
...requiredNullableFlags(required, nullable),
|
|
@@ -3476,11 +3535,29 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
|
|
|
3476
3535
|
}
|
|
3477
3536
|
const itemKinds = [];
|
|
3478
3537
|
for (const variant of itemVariants) {
|
|
3538
|
+
if (isRecord4(variant) && typeof variant.$ref === "string") {
|
|
3539
|
+
const resolvedVariant = resolveRef(variant.$ref, ctx);
|
|
3540
|
+
if (!resolvedVariant) {
|
|
3541
|
+
ctx.problems.push({
|
|
3542
|
+
severity: "error",
|
|
3543
|
+
path: `${path}[]`,
|
|
3544
|
+
message: `Unresolvable $ref in array items anyOf ("${variant.$ref}").`
|
|
3545
|
+
});
|
|
3546
|
+
return null;
|
|
3547
|
+
}
|
|
3548
|
+
itemKinds.push(resolvedVariant.slug);
|
|
3549
|
+
refToObjectField(resolvedVariant, `${path}[]<${resolvedVariant.slug}>`, ctx);
|
|
3550
|
+
ctx.arrayBindings.push({
|
|
3551
|
+
arrayField: fieldName,
|
|
3552
|
+
itemKindSlug: resolvedVariant.slug
|
|
3553
|
+
});
|
|
3554
|
+
continue;
|
|
3555
|
+
}
|
|
3479
3556
|
if (!isRecord4(variant) || !isRecord4(variant.properties)) {
|
|
3480
3557
|
ctx.problems.push({
|
|
3481
3558
|
severity: "error",
|
|
3482
3559
|
path: `${path}[]`,
|
|
3483
|
-
message: "Array items anyOf/oneOf variants must be inline objects declaring __kind."
|
|
3560
|
+
message: "Array items anyOf/oneOf variants must be inline objects declaring __kind, or $refs to defs."
|
|
3484
3561
|
});
|
|
3485
3562
|
return null;
|
|
3486
3563
|
}
|
|
@@ -3614,6 +3691,14 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
|
|
|
3614
3691
|
kind: referencedKind
|
|
3615
3692
|
};
|
|
3616
3693
|
}
|
|
3694
|
+
if (Object.keys(nestedFields).length === 0 && !apIsOpen && isRecord4(ap) && KIND_KEY in node.properties) {
|
|
3695
|
+
const apType = resolvePrimaryType(ap).type;
|
|
3696
|
+
return {
|
|
3697
|
+
...requiredNullableFlags(required, nullable),
|
|
3698
|
+
type: "record",
|
|
3699
|
+
values: apType === "string" || apType === "number" || apType === "boolean" ? apType : "json"
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3617
3702
|
let open = apIsOpen;
|
|
3618
3703
|
if (!apIsOpen && isRecord4(ap)) {
|
|
3619
3704
|
ctx.problems.push({
|
|
@@ -3730,12 +3815,21 @@ function normalizeAiSchemaInput(input) {
|
|
|
3730
3815
|
return { name: null, strict: null, rootSchema: null, parseErrors };
|
|
3731
3816
|
}
|
|
3732
3817
|
function convertAiSchemaToBlockFields(schemaName, rootSchema, strict) {
|
|
3818
|
+
const rawDefs = isRecord4(rootSchema.$defs) ? rootSchema.$defs : isRecord4(rootSchema.definitions) ? rootSchema.definitions : {};
|
|
3819
|
+
const defs = {};
|
|
3820
|
+
for (const [name, node] of Object.entries(rawDefs)) {
|
|
3821
|
+
if (isRecord4(node)) defs[name] = node;
|
|
3822
|
+
}
|
|
3733
3823
|
const ctx = {
|
|
3734
3824
|
schemaName,
|
|
3825
|
+
strict,
|
|
3735
3826
|
problems: [],
|
|
3736
3827
|
droppedMetadata: [],
|
|
3737
3828
|
blockSchemas: [],
|
|
3738
|
-
arrayBindings: []
|
|
3829
|
+
arrayBindings: [],
|
|
3830
|
+
defs,
|
|
3831
|
+
rootSchema,
|
|
3832
|
+
refsInProgress: /* @__PURE__ */ new Set()
|
|
3739
3833
|
};
|
|
3740
3834
|
ctx.droppedMetadata.push(...collectDropped(rootSchema, ""));
|
|
3741
3835
|
const rootAp = rootSchema.additionalProperties;
|
|
@@ -4201,6 +4295,41 @@ function kindSchemaToJsonSchema(kind, resolve, options = {}) {
|
|
|
4201
4295
|
return { name: kind, schema: { ...rootNode, $defs: defs }, strict, unresolved };
|
|
4202
4296
|
}
|
|
4203
4297
|
|
|
4298
|
+
// convert/json-schema-to-kind.ts
|
|
4299
|
+
function kindSchemaFromJsonSchema(kind, jsonSchema) {
|
|
4300
|
+
if (jsonSchema === null || jsonSchema === void 0) {
|
|
4301
|
+
return { schema: null, children: {}, problems: [] };
|
|
4302
|
+
}
|
|
4303
|
+
const normalized = normalizeAiSchemaInput(jsonSchema);
|
|
4304
|
+
const rootSchema = normalized.rootSchema ?? (typeof jsonSchema === "object" && !Array.isArray(jsonSchema) ? jsonSchema : null);
|
|
4305
|
+
if (!rootSchema) {
|
|
4306
|
+
return {
|
|
4307
|
+
schema: null,
|
|
4308
|
+
children: {},
|
|
4309
|
+
problems: [
|
|
4310
|
+
{
|
|
4311
|
+
severity: "error",
|
|
4312
|
+
path: "",
|
|
4313
|
+
message: `Kind "${kind}": emitted_json_schema is not a JSON object.`
|
|
4314
|
+
}
|
|
4315
|
+
]
|
|
4316
|
+
};
|
|
4317
|
+
}
|
|
4318
|
+
const converted = convertAiSchemaToBlockFields(kind, rootSchema, false);
|
|
4319
|
+
let schema = null;
|
|
4320
|
+
const children = {};
|
|
4321
|
+
for (const draft of converted.blockSchemas) {
|
|
4322
|
+
const asKindSchema = {
|
|
4323
|
+
kind: draft.slug,
|
|
4324
|
+
fields: draft.fields,
|
|
4325
|
+
...draft.root ? { root: draft.root } : {}
|
|
4326
|
+
};
|
|
4327
|
+
if (draft.slug === kind) schema = asKindSchema;
|
|
4328
|
+
else children[draft.slug] = asKindSchema;
|
|
4329
|
+
}
|
|
4330
|
+
return { schema, children, problems: converted.problems };
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4204
4333
|
// wire/partial-kind.ts
|
|
4205
4334
|
var IR_PARTIAL_KEY = "__ir_partial";
|
|
4206
4335
|
var PARTIAL_STATES = [
|
|
@@ -4532,6 +4661,7 @@ exports.isJsonAnyField = isJsonAnyField;
|
|
|
4532
4661
|
exports.isProvisionalKind = isProvisionalKind;
|
|
4533
4662
|
exports.isScalarArrayType = isScalarArrayType;
|
|
4534
4663
|
exports.isTerminalKindEvent = isTerminalKindEvent;
|
|
4664
|
+
exports.kindSchemaFromJsonSchema = kindSchemaFromJsonSchema;
|
|
4535
4665
|
exports.kindSchemaToJsonSchema = kindSchemaToJsonSchema;
|
|
4536
4666
|
exports.kindSchemaToStorage = kindSchemaToStorage;
|
|
4537
4667
|
exports.kindVerdictOf = kindVerdictOf;
|