schema-components 1.12.11 → 1.14.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.
Files changed (83) hide show
  1. package/README.md +35 -0
  2. package/dist/core/adapter.d.mts +8 -3
  3. package/dist/core/adapter.mjs +58 -14
  4. package/dist/core/constraints.d.mts +17 -0
  5. package/dist/core/constraints.mjs +150 -0
  6. package/dist/core/diagnostics.d.mts +2 -0
  7. package/dist/core/diagnostics.mjs +33 -0
  8. package/dist/core/errors.d.mts +1 -1
  9. package/dist/core/formats.d.mts +33 -0
  10. package/dist/core/formats.mjs +67 -0
  11. package/dist/core/merge.d.mts +48 -0
  12. package/dist/core/merge.mjs +125 -0
  13. package/dist/core/normalise.d.mts +41 -0
  14. package/dist/core/normalise.mjs +2 -0
  15. package/dist/core/openapi30.d.mts +41 -0
  16. package/dist/core/openapi30.mjs +2 -0
  17. package/dist/core/ref.d.mts +2 -0
  18. package/dist/core/ref.mjs +165 -0
  19. package/dist/core/renderer.d.mts +2 -2
  20. package/dist/core/renderer.mjs +8 -0
  21. package/dist/core/swagger2.d.mts +11 -0
  22. package/dist/core/swagger2.mjs +2 -0
  23. package/dist/core/typeInference.d.mts +2 -0
  24. package/dist/core/typeInference.mjs +1 -0
  25. package/dist/core/types.d.mts +2 -3
  26. package/dist/core/types.mjs +58 -2
  27. package/dist/core/version.d.mts +2 -0
  28. package/dist/core/version.mjs +151 -0
  29. package/dist/core/walkBuilders.d.mts +66 -0
  30. package/dist/core/walkBuilders.mjs +152 -0
  31. package/dist/core/walker.d.mts +3 -10
  32. package/dist/core/walker.mjs +245 -233
  33. package/dist/diagnostics-DzbZmcLI.d.mts +64 -0
  34. package/dist/html/a11y.d.mts +5 -4
  35. package/dist/html/renderToHtml.d.mts +3 -3
  36. package/dist/html/renderToHtml.mjs +23 -379
  37. package/dist/html/renderToHtmlStream.d.mts +29 -47
  38. package/dist/html/renderToHtmlStream.mjs +33 -305
  39. package/dist/html/renderers.d.mts +14 -0
  40. package/dist/html/renderers.mjs +406 -0
  41. package/dist/html/streamRenderers.d.mts +13 -0
  42. package/dist/html/streamRenderers.mjs +243 -0
  43. package/dist/normalise-tL9FckAk.mjs +748 -0
  44. package/dist/openapi/ApiCallbacks.d.mts +16 -0
  45. package/dist/openapi/ApiCallbacks.mjs +34 -0
  46. package/dist/openapi/ApiLinks.d.mts +16 -0
  47. package/dist/openapi/ApiLinks.mjs +42 -0
  48. package/dist/openapi/ApiResponseHeaders.d.mts +16 -0
  49. package/dist/openapi/ApiResponseHeaders.mjs +35 -0
  50. package/dist/openapi/ApiSecurity.d.mts +19 -0
  51. package/dist/openapi/ApiSecurity.mjs +33 -0
  52. package/dist/openapi/bundle.d.mts +47 -0
  53. package/dist/openapi/bundle.mjs +95 -0
  54. package/dist/openapi/components.d.mts +7 -1
  55. package/dist/openapi/components.mjs +30 -6
  56. package/dist/openapi/parser.d.mts +59 -2
  57. package/dist/openapi/parser.mjs +189 -8
  58. package/dist/react/SchemaComponent.d.mts +13 -4
  59. package/dist/react/SchemaComponent.mjs +51 -91
  60. package/dist/react/SchemaView.d.mts +10 -2
  61. package/dist/react/SchemaView.mjs +33 -15
  62. package/dist/react/fieldPath.d.mts +20 -0
  63. package/dist/react/fieldPath.mjs +81 -0
  64. package/dist/react/headless.d.mts +2 -4
  65. package/dist/react/headless.mjs +3 -492
  66. package/dist/react/headlessRenderers.d.mts +23 -0
  67. package/dist/react/headlessRenderers.mjs +507 -0
  68. package/dist/ref-DvWoULcy.d.mts +44 -0
  69. package/dist/renderer-BdSqllx5.d.mts +160 -0
  70. package/dist/themes/mantine.d.mts +1 -1
  71. package/dist/themes/mantine.mjs +2 -1
  72. package/dist/themes/mui.d.mts +1 -1
  73. package/dist/themes/mui.mjs +3 -2
  74. package/dist/themes/radix.d.mts +1 -1
  75. package/dist/themes/radix.mjs +2 -1
  76. package/dist/themes/shadcn.d.mts +1 -1
  77. package/dist/themes/shadcn.mjs +10 -6
  78. package/dist/typeInference-k7FXfTVO.d.mts +335 -0
  79. package/dist/types-D_5ST7SS.d.mts +269 -0
  80. package/dist/version-B5NV-35j.d.mts +69 -0
  81. package/package.json +1 -1
  82. package/dist/types-BJzEgJdX.d.mts +0 -335
  83. /package/dist/{errors-DIKI2C78.d.mts → errors-C5zRC2PU.d.mts} +0 -0
@@ -1,196 +1,63 @@
1
1
  import { isObject } from "./guards.mjs";
2
- import { resolveEditability } from "./types.mjs";
2
+ import { appendPointer, emitDiagnostic } from "./diagnostics.mjs";
3
+ import { countDistinctRefs, resolveRef } from "./ref.mjs";
4
+ import { extractArrayConstraints, extractObjectConstraints, stripInapplicableConstraints } from "./constraints.mjs";
5
+ import { ANNOTATION_SIBLINGS, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf } from "./merge.mjs";
6
+ import { buildBase, buildBooleanField, buildFileField, buildNullField, buildNumberField, buildStringField, buildUnknownField, extractChildOverride, extractSchemaMetaFields, getArray, getObject, getString, isPrimitive, walkDependentRequiredMap, walkSubSchemaMap, withoutKeys } from "./walkBuilders.mjs";
3
7
  //#region src/core/walker.ts
4
- function getString(obj, key) {
5
- const value = obj[key];
6
- return typeof value === "string" ? value : void 0;
7
- }
8
- function getNumber(obj, key) {
9
- const value = obj[key];
10
- return typeof value === "number" ? value : void 0;
11
- }
12
- function getArray(obj, key) {
13
- const value = obj[key];
14
- return Array.isArray(value) ? value : void 0;
15
- }
16
- function getObject(obj, key) {
17
- const value = obj[key];
18
- return isObject(value) ? value : void 0;
19
- }
20
- const MAX_REF_DEPTH = 10;
21
- function resolveRef(schema, rootDocument, visited) {
22
- const ref = getString(schema, "$ref");
23
- if (ref === void 0) return schema;
24
- if (visited.has(ref)) return { type: "unknown" };
25
- if (visited.size >= MAX_REF_DEPTH) return { type: "unknown" };
26
- const resolved = dereference(ref, rootDocument);
27
- if (resolved === void 0) return { type: "unknown" };
28
- const nextVisited = new Set(visited);
29
- nextVisited.add(ref);
30
- return resolveRef(resolved, rootDocument, nextVisited);
31
- }
32
- function dereference(ref, root) {
33
- if (ref === "#") return root;
34
- if (!ref.startsWith("#/")) return void 0;
35
- const parts = ref.slice(2).split("/");
36
- if (parts.length === 1 && parts[0] === "") return root;
37
- let current = root;
38
- for (const part of parts) {
39
- if (!isObject(current)) return void 0;
40
- const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
41
- current = current[decoded];
42
- }
43
- return isObject(current) ? current : void 0;
44
- }
45
- /**
46
- * Merge multiple JSON Schema objects from allOf into one.
47
- * Merges: properties, required, meta fields, and constraints.
48
- */
49
- function mergeAllOf(schemas) {
50
- const merged = {};
51
- const properties = {};
52
- const required = [];
53
- for (const entry of schemas) {
54
- if (!isObject(entry)) continue;
55
- const props = getObject(entry, "properties");
56
- if (props !== void 0) for (const [key, value] of Object.entries(props)) properties[key] = value;
57
- const req = getArray(entry, "required");
58
- if (req !== void 0) {
59
- for (const r of req) if (typeof r === "string" && !required.includes(r)) required.push(r);
60
- }
61
- for (const [key, value] of Object.entries(entry)) {
62
- if (key === "properties" || key === "required" || key === "allOf" || key === "type") continue;
63
- if (!(key in merged)) merged[key] = value;
64
- }
65
- if (!("type" in merged)) {
66
- const type = getString(entry, "type");
67
- if (type !== void 0) merged.type = type;
68
- }
69
- }
70
- if (Object.keys(properties).length > 0) merged.properties = properties;
71
- if (required.length > 0) merged.required = required;
72
- return merged;
73
- }
74
8
  /**
75
- * Detect `anyOf: [T, { type: "null" }]` → nullable T.
76
- * Returns the non-null schema and a nullable flag.
9
+ * Handle JSON Schema boolean values (Draft 06+).
10
+ * - `true` permissive (unknown, editable)
11
+ * - `false` → never (cannot hold any value)
77
12
  */
78
- function normaliseAnyOf(options) {
79
- if (options.length !== 2) return void 0;
80
- let inner;
81
- let hasNull = false;
82
- for (const opt of options) {
83
- if (!isObject(opt)) return void 0;
84
- if (opt.type === "null") hasNull = true;
85
- else inner = opt;
86
- }
87
- if (!hasNull || inner === void 0) return void 0;
13
+ function walkBooleanSchema(value) {
14
+ if (value) return {
15
+ type: "unknown",
16
+ editability: "editable",
17
+ meta: {},
18
+ constraints: {}
19
+ };
88
20
  return {
89
- inner,
90
- isNullable: true
21
+ type: "never",
22
+ editability: "presentation",
23
+ meta: { rejected: true },
24
+ constraints: {}
91
25
  };
92
26
  }
93
27
  /**
94
- * Detect oneOf where every option is an object with a property
95
- * that has a `const` value → discriminated union.
28
+ * Walk a sub-schema that may be an object, a boolean, or neither.
29
+ * Dispatches to walkNode (object), walkBooleanSchema (boolean),
30
+ * or returns unknown with a diagnostic.
96
31
  */
97
- function detectDiscriminated(options) {
98
- if (options.length === 0) return void 0;
99
- let discriminator;
100
- for (const opt of options) {
101
- if (!isObject(opt)) return void 0;
102
- const props = getObject(opt, "properties");
103
- if (props === void 0) return void 0;
104
- let foundKey;
105
- for (const [key, value] of Object.entries(props)) if (isObject(value) && "const" in value) {
106
- foundKey = key;
107
- break;
108
- }
109
- if (foundKey === void 0) return void 0;
110
- if (discriminator === void 0) discriminator = foundKey;
111
- else if (discriminator !== foundKey) return;
112
- }
113
- if (discriminator === void 0) return void 0;
32
+ function walkSubSchema(value, ctx) {
33
+ if (isObject(value)) return walkNode(value, ctx);
34
+ if (typeof value === "boolean") return walkBooleanSchema(value);
114
35
  return {
115
- options: options.filter(isObject),
116
- discriminator
36
+ type: "unknown",
37
+ editability: "editable",
38
+ meta: {},
39
+ constraints: {}
117
40
  };
118
41
  }
119
- const META_KEYWORDS = new Set([
120
- "readOnly",
121
- "writeOnly",
122
- "description",
123
- "title",
124
- "deprecated",
125
- "default",
126
- "component",
127
- "example",
128
- "examples"
129
- ]);
130
- function extractMetaFromJson(schema) {
131
- const meta = {};
132
- for (const [key, value] of Object.entries(schema)) if (META_KEYWORDS.has(key)) meta[key] = value;
133
- return meta;
134
- }
135
- function extractConstraintsFromJson(schema) {
136
- const constraints = {};
137
- const minLength = getNumber(schema, "minLength");
138
- if (minLength !== void 0) constraints.minLength = minLength;
139
- const maxLength = getNumber(schema, "maxLength");
140
- if (maxLength !== void 0) constraints.maxLength = maxLength;
141
- const minimum = getNumber(schema, "minimum");
142
- if (minimum !== void 0) constraints.minimum = minimum;
143
- const maximum = getNumber(schema, "maximum");
144
- if (maximum !== void 0) constraints.maximum = maximum;
145
- const pattern = getString(schema, "pattern");
146
- if (pattern !== void 0) constraints.pattern = pattern;
147
- const format = getString(schema, "format");
148
- if (format !== void 0) constraints.format = format;
149
- const minItems = getNumber(schema, "minItems");
150
- if (minItems !== void 0) constraints.minItems = minItems;
151
- const maxItems = getNumber(schema, "maxItems");
152
- if (maxItems !== void 0) constraints.maxItems = maxItems;
153
- if (format === "binary") {
154
- const contentMediaType = getString(schema, "contentMediaType");
155
- if (contentMediaType !== void 0) constraints.mimeTypes = [contentMediaType];
156
- }
157
- return constraints;
158
- }
159
- const OVERRIDE_META_KEYS = new Set([
160
- "readOnly",
161
- "writeOnly",
162
- "description",
163
- "title",
164
- "deprecated",
165
- "component",
166
- "visible",
167
- "order"
168
- ]);
169
- function extractSchemaMetaFields(overrides) {
170
- if (overrides === void 0) return void 0;
171
- const meta = {};
172
- for (const key of Object.keys(overrides)) if (OVERRIDE_META_KEYS.has(key)) meta[key] = overrides[key];
173
- return Object.keys(meta).length > 0 ? meta : void 0;
174
- }
175
- function extractChildOverride(overrides, key) {
176
- if (overrides === void 0) return void 0;
177
- const child = overrides[key];
178
- if (child === void 0 || child === null) return void 0;
179
- if (typeof child !== "object" || Array.isArray(child)) return void 0;
180
- const result = {};
181
- for (const [k, v] of Object.entries(child)) result[k] = v;
182
- return Object.keys(result).length > 0 ? result : void 0;
183
- }
184
42
  function walk(schema, options = {}) {
185
- const { componentMeta, rootMeta, fieldOverrides, rootDocument } = options;
43
+ const { componentMeta, rootMeta, fieldOverrides, rootDocument, diagnostics, externalResolver } = options;
44
+ if (typeof schema === "boolean") return walkBooleanSchema(schema);
186
45
  if (!isObject(schema)) return {
187
46
  type: "unknown",
188
47
  editability: "editable",
189
48
  meta: {},
190
49
  constraints: {}
191
50
  };
51
+ const topRef = typeof schema.$ref === "string" ? schema.$ref : void 0;
52
+ if (topRef !== void 0 && !topRef.startsWith("#")) emitDiagnostic(diagnostics, {
53
+ code: "external-ref",
54
+ message: `External $ref not supported: ${topRef}`,
55
+ pointer: "",
56
+ detail: { ref: topRef }
57
+ });
192
58
  const doc = rootDocument ?? schema;
193
- return walkNode(resolveRef(schema, doc, /* @__PURE__ */ new Set()), {
59
+ const maxRefDepth = countDistinctRefs(doc);
60
+ return walkNode(resolveRef(schema, doc, /* @__PURE__ */ new Set(), diagnostics, maxRefDepth, externalResolver), {
194
61
  componentMeta,
195
62
  rootMeta,
196
63
  fieldOverrides,
@@ -198,7 +65,11 @@ function walk(schema, options = {}) {
198
65
  isNullable: false,
199
66
  isOptional: false,
200
67
  defaultValue: void 0,
201
- refResults: /* @__PURE__ */ new Map()
68
+ refResults: /* @__PURE__ */ new Map(),
69
+ pointer: "",
70
+ diagnostics,
71
+ maxRefDepth,
72
+ externalResolver
202
73
  });
203
74
  }
204
75
  function walkNode(schema, ctx) {
@@ -223,7 +94,8 @@ function walkNode(schema, ctx) {
223
94
  if (ref !== void 0) {
224
95
  const cached = ctx.refResults.get(ref);
225
96
  if (cached !== void 0) return cached;
226
- const resolved = resolveRef(schema, ctx.rootDocument, /* @__PURE__ */ new Set());
97
+ const hasSiblings = [...ANNOTATION_SIBLINGS].some((k) => k in schema);
98
+ const resolved = resolveRef(schema, ctx.rootDocument, /* @__PURE__ */ new Set(), ctx.diagnostics, ctx.maxRefDepth, ctx.externalResolver);
227
99
  const placeholder = {
228
100
  type: "unknown",
229
101
  editability: "editable",
@@ -231,68 +103,172 @@ function walkNode(schema, ctx) {
231
103
  constraints: {}
232
104
  };
233
105
  ctx.refResults.set(ref, placeholder);
234
- const result = walkNode(resolved, ctx);
106
+ let result = walkNode(resolved, ctx);
107
+ if (hasSiblings) result = {
108
+ ...result,
109
+ meta: mergeRefSiblings(schema, result.meta)
110
+ };
235
111
  Object.assign(placeholder, result);
236
112
  return placeholder;
237
113
  }
114
+ const ifSchema = getObject(schema, "if");
115
+ if (ifSchema !== void 0) {
116
+ emitDiagnostic(ctx.diagnostics, {
117
+ code: "conditional-fallback",
118
+ message: "if/then/else rendered as base schema; conditionals require runtime evaluation",
119
+ pointer: ctx.pointer
120
+ });
121
+ const base = buildBase(withoutKeys(schema, [
122
+ "if",
123
+ "then",
124
+ "else"
125
+ ]), ctx);
126
+ const thenSchema = getObject(schema, "then");
127
+ const elseSchema = getObject(schema, "else");
128
+ const conditional = {
129
+ ...base,
130
+ type: "conditional",
131
+ constraints: {},
132
+ ifClause: walkNode(ifSchema, ctx)
133
+ };
134
+ if (thenSchema !== void 0) conditional.thenClause = walkNode(thenSchema, ctx);
135
+ if (elseSchema !== void 0) conditional.elseClause = walkNode(elseSchema, ctx);
136
+ return conditional;
137
+ }
138
+ const notSchema = getObject(schema, "not");
139
+ if (notSchema !== void 0) {
140
+ emitDiagnostic(ctx.diagnostics, {
141
+ code: "type-negation-fallback",
142
+ message: "not schema rendered as negation; TypeScript cannot negate types",
143
+ pointer: ctx.pointer
144
+ });
145
+ return {
146
+ ...buildBase(withoutKeys(schema, ["not"]), ctx),
147
+ type: "negation",
148
+ constraints: {},
149
+ negated: walkNode(notSchema, ctx)
150
+ };
151
+ }
238
152
  const enumValues = getArray(schema, "enum");
239
153
  if (enumValues !== void 0) return walkEnum(schema, enumValues, ctx);
240
- if ("const" in schema) return walkLiteral(schema, ctx);
154
+ if ("const" in schema) {
155
+ if (!isPrimitive(schema.const)) emitDiagnostic(ctx.diagnostics, {
156
+ code: "invalid-const",
157
+ message: `const value is not a primitive: ${typeof schema.const}`,
158
+ pointer: ctx.pointer,
159
+ detail: { constValue: schema.const }
160
+ });
161
+ return walkLiteral(schema, ctx);
162
+ }
241
163
  const type = getString(schema, "type");
242
- if (type === void 0) return buildField(schema, "unknown", ctx);
164
+ const typeArray = getArray(schema, "type");
165
+ if (type === void 0 && typeArray !== void 0) {
166
+ const nonNullTypes = typeArray.filter((t) => typeof t === "string" && t !== "null");
167
+ const hasNull = typeArray.includes("null");
168
+ if (nonNullTypes.length === 0) return buildNullField(schema, ctx);
169
+ if (nonNullTypes.length === 1) {
170
+ const walkCtx = hasNull ? {
171
+ ...ctx,
172
+ isNullable: true
173
+ } : ctx;
174
+ const singleType = nonNullTypes[0];
175
+ if (singleType === void 0) return buildUnknownField(schema, ctx);
176
+ return walkNode({
177
+ ...stripInapplicableConstraints(schema, singleType),
178
+ type: singleType
179
+ }, walkCtx);
180
+ }
181
+ const options = nonNullTypes.map((t) => ({
182
+ ...stripInapplicableConstraints(schema, t),
183
+ type: t
184
+ }));
185
+ if (hasNull) return walkUnion([...options, { type: "null" }], {
186
+ ...ctx,
187
+ isNullable: true
188
+ });
189
+ return walkUnion(options, ctx);
190
+ }
191
+ if (type === void 0) {
192
+ emitDiagnostic(ctx.diagnostics, {
193
+ code: "unsupported-type",
194
+ message: "Schema has no type, composition, enum, or const; rendering as unknown",
195
+ pointer: ctx.pointer
196
+ });
197
+ return buildUnknownField(schema, ctx);
198
+ }
243
199
  if (type === "string") return walkString(schema, ctx);
244
200
  if (type === "number" || type === "integer") return walkNumber(schema, ctx);
245
201
  if (type === "boolean") return walkBoolean(schema, ctx);
246
- if (type === "null") return buildField(schema, "null", ctx);
202
+ if (type === "null") return buildNullField(schema, ctx);
247
203
  if (type === "object") {
248
204
  const properties = getObject(schema, "properties");
249
205
  if (properties !== void 0) return walkObject(schema, properties, ctx);
250
206
  const additionalProps = getObject(schema, "additionalProperties");
251
207
  if (additionalProps !== void 0) return walkRecord(schema, additionalProps, ctx);
252
- return buildField(schema, "object", ctx);
208
+ return {
209
+ ...buildBase(schema, ctx),
210
+ type: "object",
211
+ constraints: extractObjectConstraints(schema),
212
+ fields: {},
213
+ requiredFields: []
214
+ };
253
215
  }
254
216
  if (type === "array") return walkArray(schema, ctx);
255
- return buildField(schema, "unknown", ctx);
217
+ emitDiagnostic(ctx.diagnostics, {
218
+ code: "unsupported-type",
219
+ message: `Unknown schema type: ${type}`,
220
+ pointer: ctx.pointer,
221
+ detail: { type }
222
+ });
223
+ return buildUnknownField(schema, ctx);
256
224
  }
257
225
  function walkString(schema, ctx) {
258
- if (getString(schema, "format") === "binary") return buildField(schema, "file", ctx);
259
- return buildField(schema, "string", ctx);
226
+ if (getString(schema, "format") === "binary") return buildFileField(schema, ctx);
227
+ const field = buildStringField(schema, ctx);
228
+ const contentSchema = getObject(schema, "contentSchema");
229
+ if (contentSchema !== void 0) field.meta.decodedSchema = walkNode(contentSchema, ctx);
230
+ return field;
260
231
  }
261
232
  function walkNumber(schema, ctx) {
262
- return buildField(schema, "number", ctx);
233
+ return buildNumberField(schema, ctx);
263
234
  }
264
235
  function walkBoolean(schema, ctx) {
265
- return buildField(schema, "boolean", ctx);
236
+ return buildBooleanField(schema, ctx);
266
237
  }
267
238
  function walkEnum(schema, enumValues, ctx) {
268
239
  return {
269
- ...buildField(schema, "enum", ctx),
270
- enumValues: enumValues.filter((v) => typeof v === "string")
240
+ ...buildBase(schema, ctx),
241
+ type: "enum",
242
+ constraints: {},
243
+ enumValues: enumValues.filter((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null)
271
244
  };
272
245
  }
273
246
  function walkLiteral(schema, ctx) {
274
247
  const constValue = schema.const;
275
248
  const values = isPrimitive(constValue) ? [constValue] : [];
276
249
  return {
277
- ...buildField(schema, "literal", ctx),
250
+ ...buildBase(schema, ctx),
251
+ type: "literal",
252
+ constraints: {},
278
253
  literalValues: values
279
254
  };
280
255
  }
281
256
  function walkObject(schema, properties, ctx) {
282
- const base = buildField(schema, "object", ctx);
283
- const required = getArray(schema, "required");
257
+ const requiredFields = getArray(schema, "required")?.filter((r) => typeof r === "string") ?? [];
284
258
  const fields = {};
285
259
  for (const [key, propSchema] of Object.entries(properties)) {
286
260
  const childOverride = extractChildOverride(ctx.fieldOverrides, key);
287
- const isRequired = required?.includes(key) === true;
261
+ const isRequired = requiredFields.includes(key);
288
262
  const childCtx = {
289
263
  ...ctx,
290
264
  fieldOverrides: childOverride,
291
- isOptional: !isRequired
265
+ isOptional: !isRequired,
266
+ pointer: appendPointer(ctx.pointer, key)
292
267
  };
293
268
  const overrideMeta = extractSchemaMetaFields(childOverride);
294
269
  if (overrideMeta !== void 0 && ("readOnly" in overrideMeta || "writeOnly" in overrideMeta)) childCtx.componentMeta = void 0;
295
270
  if (isObject(propSchema)) fields[key] = walkNode(propSchema, childCtx);
271
+ else if (typeof propSchema === "boolean") fields[key] = walkBooleanSchema(propSchema);
296
272
  else fields[key] = {
297
273
  type: "unknown",
298
274
  editability: "editable",
@@ -300,13 +276,53 @@ function walkObject(schema, properties, ctx) {
300
276
  constraints: {}
301
277
  };
302
278
  }
279
+ const patternProps = getObject(schema, "patternProperties");
280
+ const walkedPatternProps = patternProps !== void 0 ? walkSubSchemaMap(patternProps, walkNode, ctx) : void 0;
281
+ let additionalPropertiesClosed;
282
+ let additionalPropertiesSchema;
283
+ const additionalProps = schema.additionalProperties;
284
+ if (additionalProps === false) additionalPropertiesClosed = true;
285
+ else if (additionalProps === true) additionalPropertiesSchema = {
286
+ type: "unknown",
287
+ editability: "editable",
288
+ meta: {},
289
+ constraints: {}
290
+ };
291
+ else if (isObject(additionalProps)) additionalPropertiesSchema = walkNode(additionalProps, ctx);
292
+ const depSchemas = getObject(schema, "dependentSchemas");
293
+ const walkedDepSchemas = depSchemas !== void 0 ? walkSubSchemaMap(depSchemas, walkNode, ctx) : void 0;
294
+ const depReq = getObject(schema, "dependentRequired");
295
+ const walkedDepReq = depReq !== void 0 ? walkDependentRequiredMap(depReq) : void 0;
296
+ let unevaluatedProperties;
297
+ let unevaluatedPropertiesClosed;
298
+ const unevalProps = schema.unevaluatedProperties;
299
+ if (unevalProps === false) unevaluatedPropertiesClosed = true;
300
+ else if (unevalProps === true) unevaluatedProperties = {
301
+ type: "unknown",
302
+ editability: "editable",
303
+ meta: {},
304
+ constraints: {}
305
+ };
306
+ else if (isObject(unevalProps)) unevaluatedProperties = walkNode(unevalProps, ctx);
307
+ const propertyNamesSchema = getObject(schema, "propertyNames");
308
+ const walkedPropertyNames = propertyNamesSchema !== void 0 ? walkNode(propertyNamesSchema, ctx) : void 0;
303
309
  return {
304
- ...base,
305
- fields
310
+ ...buildBase(schema, ctx),
311
+ type: "object",
312
+ constraints: extractObjectConstraints(schema),
313
+ fields,
314
+ requiredFields,
315
+ ...walkedPatternProps !== void 0 && Object.keys(walkedPatternProps).length > 0 ? { patternProperties: walkedPatternProps } : {},
316
+ ...additionalPropertiesClosed ? { additionalPropertiesClosed } : {},
317
+ ...additionalPropertiesSchema !== void 0 ? { additionalPropertiesSchema } : {},
318
+ ...walkedDepSchemas !== void 0 && Object.keys(walkedDepSchemas).length > 0 ? { dependentSchemas: walkedDepSchemas } : {},
319
+ ...walkedDepReq !== void 0 && Object.keys(walkedDepReq).length > 0 ? { dependentRequired: walkedDepReq } : {},
320
+ ...unevaluatedProperties !== void 0 ? { unevaluatedProperties } : {},
321
+ ...unevaluatedPropertiesClosed ? { unevaluatedPropertiesClosed } : {},
322
+ ...walkedPropertyNames !== void 0 ? { propertyNames: walkedPropertyNames } : {}
306
323
  };
307
324
  }
308
325
  function walkRecord(schema, valueSchema, ctx) {
309
- const base = buildField(schema, "record", ctx);
310
326
  const propertyNames = getObject(schema, "propertyNames");
311
327
  const keyType = propertyNames !== void 0 ? walkNode(propertyNames, ctx) : {
312
328
  type: "string",
@@ -316,39 +332,61 @@ function walkRecord(schema, valueSchema, ctx) {
316
332
  };
317
333
  const valueType = walkNode(valueSchema, ctx);
318
334
  return {
319
- ...base,
335
+ ...buildBase(schema, ctx),
336
+ type: "record",
337
+ constraints: extractObjectConstraints(schema),
320
338
  keyType,
321
339
  valueType
322
340
  };
323
341
  }
324
342
  function walkArray(schema, ctx) {
325
- const base = buildField(schema, "array", ctx);
343
+ const prefixItems = getArray(schema, "prefixItems");
344
+ if (prefixItems !== void 0) {
345
+ const walkedItems = prefixItems.filter(isObject).map((item) => walkNode(item, ctx));
346
+ return {
347
+ ...buildBase(schema, ctx),
348
+ type: "tuple",
349
+ constraints: extractArrayConstraints(schema),
350
+ prefixItems: walkedItems
351
+ };
352
+ }
353
+ const unevaluatedItemsSchema = getObject(schema, "unevaluatedItems");
354
+ const walkedUnevaluatedItems = unevaluatedItemsSchema !== void 0 ? walkNode(unevaluatedItemsSchema, ctx) : void 0;
326
355
  const items = getObject(schema, "items");
327
356
  if (items !== void 0) {
328
357
  const elementOverride = extractChildOverride(ctx.fieldOverrides, "[]");
329
358
  return {
330
- ...base,
359
+ ...buildBase(schema, ctx),
360
+ type: "array",
361
+ constraints: extractArrayConstraints(schema),
331
362
  element: walkNode(items, {
332
363
  ...ctx,
333
364
  fieldOverrides: elementOverride
334
- })
365
+ }),
366
+ ...walkedUnevaluatedItems !== void 0 ? { unevaluatedItems: walkedUnevaluatedItems } : {}
335
367
  };
336
368
  }
337
- return base;
369
+ return {
370
+ ...buildBase(schema, ctx),
371
+ type: "array",
372
+ constraints: extractArrayConstraints(schema),
373
+ ...walkedUnevaluatedItems !== void 0 ? { unevaluatedItems: walkedUnevaluatedItems } : {}
374
+ };
338
375
  }
339
376
  function walkUnion(options, ctx) {
340
- const optionsArray = options.filter(isObject);
377
+ const walkedOptions = options.map((opt) => walkSubSchema(opt, ctx));
341
378
  return {
342
- ...buildField({}, "union", ctx),
343
- options: optionsArray.map((opt) => walkNode(opt, {
344
- ...ctx,
345
- fieldOverrides: void 0
346
- }))
379
+ ...buildBase({}, ctx),
380
+ type: "union",
381
+ constraints: {},
382
+ options: walkedOptions
347
383
  };
348
384
  }
349
385
  function walkDiscriminatedUnion(discriminated, ctx) {
350
386
  return {
351
- ...buildField({}, "discriminatedUnion", ctx),
387
+ ...buildBase({}, ctx),
388
+ type: "discriminatedUnion",
389
+ constraints: {},
352
390
  options: discriminated.options.map((opt) => walkNode(opt, {
353
391
  ...ctx,
354
392
  fieldOverrides: void 0
@@ -356,31 +394,5 @@ function walkDiscriminatedUnion(discriminated, ctx) {
356
394
  discriminator: discriminated.discriminator
357
395
  };
358
396
  }
359
- function buildField(schema, type, ctx) {
360
- const propertyMeta = extractMetaFromJson(schema);
361
- const overrideMeta = extractSchemaMetaFields(ctx.fieldOverrides);
362
- const mergedMeta = {
363
- ...propertyMeta,
364
- ...overrideMeta
365
- };
366
- const defaultValue = "default" in schema ? schema.default : void 0;
367
- const editability = resolveEditability(mergedMeta, ctx.componentMeta, ctx.rootMeta);
368
- if ((overrideMeta !== void 0 && ("readOnly" in overrideMeta || "writeOnly" in overrideMeta) || Boolean(propertyMeta.readOnly) || Boolean(propertyMeta.writeOnly)) && ctx.componentMeta !== void 0) ctx = {
369
- ...ctx,
370
- componentMeta: void 0
371
- };
372
- return {
373
- type,
374
- editability,
375
- meta: mergedMeta,
376
- isOptional: ctx.isOptional,
377
- isNullable: ctx.isNullable,
378
- defaultValue: defaultValue ?? ctx.defaultValue,
379
- constraints: extractConstraintsFromJson(schema)
380
- };
381
- }
382
- function isPrimitive(value) {
383
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
384
- }
385
397
  //#endregion
386
398
  export { walk };
@@ -0,0 +1,64 @@
1
+ //#region src/core/diagnostics.d.ts
2
+ /**
3
+ * Diagnostics channel for schema-components.
4
+ *
5
+ * Provides a structured way to surface silent fallbacks — unresolved `$ref`,
6
+ * unknown keywords, unknown `format` values, invalid `const` values,
7
+ * unsupported `type` entries, dropped Swagger 2.0 features, external
8
+ * `$ref`, type-negation fallbacks, and conditional fallbacks.
9
+ *
10
+ * Consumers pass a `DiagnosticSink` callback to receive diagnostics
11
+ * as they occur. By default, diagnostics are silently discarded.
12
+ * Setting `strict: true` converts any diagnostic into a thrown
13
+ * `SchemaCompatibilityError`.
14
+ */
15
+ /**
16
+ * Machine-readable codes identifying each class of diagnostic.
17
+ * Stable across releases — consumers can pattern-match on these.
18
+ */
19
+ type DiagnosticCode = "unresolved-ref" | "unknown-keyword" | "unknown-format" | "invalid-const" | "unsupported-type" | "dropped-swagger-feature" | "external-ref" | "type-negation-fallback" | "conditional-fallback" | "assumed-draft" | "depth-exceeded";
20
+ /**
21
+ * A single diagnostic emitted during schema processing.
22
+ */
23
+ interface Diagnostic {
24
+ /** Machine-readable code for programmatic handling. */
25
+ code: DiagnosticCode;
26
+ /** Human-readable description of the issue. */
27
+ message: string;
28
+ /** JSON Pointer to the schema node that triggered the diagnostic. */
29
+ pointer: string;
30
+ /** Additional context specific to the diagnostic code. */
31
+ detail?: Record<string, unknown>;
32
+ }
33
+ /**
34
+ * Callback that receives each diagnostic as it is emitted.
35
+ */
36
+ type DiagnosticSink = (d: Diagnostic) => void;
37
+ /**
38
+ * Diagnostics configuration threaded through the processing pipeline.
39
+ */
40
+ interface DiagnosticsOptions {
41
+ /**
42
+ * Callback for receiving diagnostics. When omitted, diagnostics
43
+ * are silently discarded (preserving backward compatibility).
44
+ */
45
+ diagnostics?: DiagnosticSink;
46
+ /**
47
+ * When `true`, any diagnostic is converted to a thrown
48
+ * `SchemaCompatibilityError`. Useful in CI or strict mode
49
+ * to catch schema drift early.
50
+ */
51
+ strict?: boolean;
52
+ }
53
+ /**
54
+ * Emit a diagnostic through the configured sink.
55
+ * When `strict` is enabled, throws a `SchemaCompatibilityError` instead.
56
+ */
57
+ declare function emitDiagnostic(opts: DiagnosticsOptions | undefined, diagnostic: Diagnostic): void;
58
+ /**
59
+ * Append a segment to a JSON Pointer.
60
+ * Encodes `/` and `~` per RFC 6901.
61
+ */
62
+ declare function appendPointer(base: string, segment: string): string;
63
+ //#endregion
64
+ export { appendPointer as a, DiagnosticsOptions as i, DiagnosticCode as n, emitDiagnostic as o, DiagnosticSink as r, Diagnostic as t };