schema-components 1.21.0 → 1.23.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 (91) hide show
  1. package/README.md +3 -1
  2. package/dist/core/adapter.d.mts +115 -4
  3. package/dist/core/adapter.mjs +405 -75
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -7
  6. package/dist/core/cssClasses.d.mts +52 -0
  7. package/dist/core/cssClasses.mjs +51 -0
  8. package/dist/core/diagnostics.d.mts +1 -1
  9. package/dist/core/errors.d.mts +1 -1
  10. package/dist/core/errors.mjs +5 -13
  11. package/dist/core/fieldOrder.d.mts +1 -1
  12. package/dist/core/formats.d.mts +30 -2
  13. package/dist/core/formats.mjs +33 -1
  14. package/dist/core/idPath.d.mts +54 -0
  15. package/dist/core/idPath.mjs +66 -0
  16. package/dist/core/limits.d.mts +2 -0
  17. package/dist/core/limits.mjs +23 -0
  18. package/dist/core/merge.d.mts +10 -1
  19. package/dist/core/merge.mjs +49 -10
  20. package/dist/core/normalise.d.mts +40 -3
  21. package/dist/core/normalise.mjs +2 -2
  22. package/dist/core/openapi30.d.mts +15 -1
  23. package/dist/core/openapi30.mjs +2 -2
  24. package/dist/core/openapiConstants.d.mts +67 -0
  25. package/dist/core/openapiConstants.mjs +90 -0
  26. package/dist/core/ref.d.mts +2 -2
  27. package/dist/core/ref.mjs +85 -6
  28. package/dist/core/refChain.d.mts +70 -0
  29. package/dist/core/refChain.mjs +44 -0
  30. package/dist/core/renderer.d.mts +1 -1
  31. package/dist/core/renderer.mjs +0 -2
  32. package/dist/core/swagger2.d.mts +1 -1
  33. package/dist/core/swagger2.mjs +1 -1
  34. package/dist/core/typeInference.d.mts +982 -2
  35. package/dist/core/types.d.mts +2 -2
  36. package/dist/core/types.mjs +1 -4
  37. package/dist/core/unionMatch.d.mts +36 -0
  38. package/dist/core/unionMatch.mjs +53 -0
  39. package/dist/core/version.d.mts +1 -1
  40. package/dist/core/version.mjs +29 -17
  41. package/dist/core/walkBuilders.d.mts +23 -4
  42. package/dist/core/walkBuilders.mjs +27 -7
  43. package/dist/core/walker.d.mts +1 -1
  44. package/dist/core/walker.mjs +123 -47
  45. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-BS2kaUyE.d.mts} +1 -1
  46. package/dist/{errors-QEwOtQAA.d.mts → errors-g_MCTQel.d.mts} +10 -16
  47. package/dist/html/a11y.d.mts +9 -4
  48. package/dist/html/a11y.mjs +10 -12
  49. package/dist/html/renderToHtml.d.mts +10 -3
  50. package/dist/html/renderToHtml.mjs +13 -3
  51. package/dist/html/renderToHtmlStream.d.mts +2 -2
  52. package/dist/html/renderToHtmlStream.mjs +12 -1
  53. package/dist/html/renderers.d.mts +43 -8
  54. package/dist/html/renderers.mjs +136 -116
  55. package/dist/html/streamRenderers.d.mts +6 -6
  56. package/dist/html/streamRenderers.mjs +129 -89
  57. package/dist/limits-Cw5QZND8.d.mts +29 -0
  58. package/dist/{normalise-DaSrnr8g.mjs → normalise-DCYp06Sr.mjs} +770 -227
  59. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  60. package/dist/openapi/ApiLinks.d.mts +1 -1
  61. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  62. package/dist/openapi/ApiSecurity.d.mts +1 -1
  63. package/dist/openapi/ApiSecurity.mjs +16 -2
  64. package/dist/openapi/components.d.mts +234 -23
  65. package/dist/openapi/components.mjs +183 -52
  66. package/dist/openapi/parser.d.mts +9 -8
  67. package/dist/openapi/parser.mjs +252 -70
  68. package/dist/openapi/resolve.d.mts +31 -15
  69. package/dist/openapi/resolve.mjs +260 -40
  70. package/dist/react/SchemaComponent.d.mts +126 -36
  71. package/dist/react/SchemaComponent.mjs +95 -57
  72. package/dist/react/SchemaView.d.mts +30 -10
  73. package/dist/react/SchemaView.mjs +2 -2
  74. package/dist/react/a11y.d.mts +21 -0
  75. package/dist/react/a11y.mjs +24 -0
  76. package/dist/react/fieldPath.d.mts +1 -1
  77. package/dist/react/headless.d.mts +1 -1
  78. package/dist/react/headless.mjs +1 -2
  79. package/dist/react/headlessRenderers.d.mts +9 -11
  80. package/dist/react/headlessRenderers.mjs +51 -102
  81. package/dist/{ref-si8ViYun.d.mts → ref-DjLEKa_E.d.mts} +38 -3
  82. package/dist/{renderer-DI6ZYf7a.d.mts → renderer-CXJ8y0qw.d.mts} +2 -2
  83. package/dist/themes/mantine.d.mts +1 -1
  84. package/dist/themes/mui.d.mts +1 -1
  85. package/dist/themes/radix.d.mts +1 -1
  86. package/dist/themes/shadcn.d.mts +1 -1
  87. package/dist/themes/shadcn.mjs +2 -1
  88. package/dist/{types-BnxPEElk.d.mts → types-BTB73MB8.d.mts} +35 -14
  89. package/dist/{version-D-u7aMfy.d.mts → version-BFTVLsdb.d.mts} +7 -1
  90. package/package.json +1 -3
  91. package/dist/typeInference-Bxw3NOG1.d.mts +0 -647
@@ -0,0 +1,52 @@
1
+ //#region src/core/cssClasses.d.ts
2
+ /**
3
+ * Shared CSS class names and visual placeholders used by every render
4
+ * pipeline (React, HTML-sync, HTML-stream). Centralising avoids drift
5
+ * between renderers and the bundled default stylesheet (`styles.css`).
6
+ *
7
+ * Class names are intentionally namespaced under `sc-` so consumer themes
8
+ * can pattern-match or override without collision risk.
9
+ */
10
+ /** Common em-dash placeholder for empty / unset values. */
11
+ declare const EM_DASH = "\u2014";
12
+ /** Single-character ellipsis used in placeholders like "Select…". */
13
+ declare const ELLIPSIS = "\u2026";
14
+ /** Prefix applied to every generated DOM id by `buildInputId`. */
15
+ declare const SC_ID_PREFIX = "sc-";
16
+ /**
17
+ * Canonical CSS class names exposed by the default render pipelines.
18
+ * Keys are stable identifiers; values are the raw class strings emitted
19
+ * to the DOM. Add new entries here rather than embedding class literals
20
+ * in renderers — `styles.css` cross-references the same names.
21
+ */
22
+ declare const SC_CLASSES: {
23
+ readonly value: "sc-value";
24
+ readonly valueEmpty: "sc-value sc-value--empty";
25
+ readonly field: "sc-field";
26
+ readonly label: "sc-label";
27
+ readonly input: "sc-input";
28
+ readonly hint: "sc-hint";
29
+ readonly required: "sc-required";
30
+ readonly object: "sc-object";
31
+ readonly array: "sc-array";
32
+ readonly record: "sc-record";
33
+ readonly tuple: "sc-tuple";
34
+ readonly tupleItem: "sc-tuple-item";
35
+ readonly tupleRest: "sc-tuple-rest";
36
+ readonly tupleIndex: "sc-tuple-index";
37
+ readonly discriminatedUnion: "sc-discriminated-union";
38
+ readonly tabs: "sc-tabs";
39
+ readonly tab: "sc-tab";
40
+ readonly tabActive: "sc-tab sc-tab--active";
41
+ readonly tabPanel: "sc-tab-panel";
42
+ readonly conditional: "sc-conditional";
43
+ readonly conditionalIf: "sc-conditional-if";
44
+ readonly conditionalThen: "sc-conditional-then";
45
+ readonly conditionalElse: "sc-conditional-else";
46
+ readonly negation: "sc-negation";
47
+ readonly never: "sc-never";
48
+ readonly recursive: "sc-recursive";
49
+ };
50
+ type ScClassKey = keyof typeof SC_CLASSES;
51
+ //#endregion
52
+ export { ELLIPSIS, EM_DASH, SC_CLASSES, SC_ID_PREFIX, ScClassKey };
@@ -0,0 +1,51 @@
1
+ //#region src/core/cssClasses.ts
2
+ /**
3
+ * Shared CSS class names and visual placeholders used by every render
4
+ * pipeline (React, HTML-sync, HTML-stream). Centralising avoids drift
5
+ * between renderers and the bundled default stylesheet (`styles.css`).
6
+ *
7
+ * Class names are intentionally namespaced under `sc-` so consumer themes
8
+ * can pattern-match or override without collision risk.
9
+ */
10
+ /** Common em-dash placeholder for empty / unset values. */
11
+ const EM_DASH = "—";
12
+ /** Single-character ellipsis used in placeholders like "Select…". */
13
+ const ELLIPSIS = "…";
14
+ /** Prefix applied to every generated DOM id by `buildInputId`. */
15
+ const SC_ID_PREFIX = "sc-";
16
+ /**
17
+ * Canonical CSS class names exposed by the default render pipelines.
18
+ * Keys are stable identifiers; values are the raw class strings emitted
19
+ * to the DOM. Add new entries here rather than embedding class literals
20
+ * in renderers — `styles.css` cross-references the same names.
21
+ */
22
+ const SC_CLASSES = {
23
+ value: "sc-value",
24
+ valueEmpty: "sc-value sc-value--empty",
25
+ field: "sc-field",
26
+ label: "sc-label",
27
+ input: "sc-input",
28
+ hint: "sc-hint",
29
+ required: "sc-required",
30
+ object: "sc-object",
31
+ array: "sc-array",
32
+ record: "sc-record",
33
+ tuple: "sc-tuple",
34
+ tupleItem: "sc-tuple-item",
35
+ tupleRest: "sc-tuple-rest",
36
+ tupleIndex: "sc-tuple-index",
37
+ discriminatedUnion: "sc-discriminated-union",
38
+ tabs: "sc-tabs",
39
+ tab: "sc-tab",
40
+ tabActive: "sc-tab sc-tab--active",
41
+ tabPanel: "sc-tab-panel",
42
+ conditional: "sc-conditional",
43
+ conditionalIf: "sc-conditional-if",
44
+ conditionalThen: "sc-conditional-then",
45
+ conditionalElse: "sc-conditional-else",
46
+ negation: "sc-negation",
47
+ never: "sc-never",
48
+ recursive: "sc-recursive"
49
+ };
50
+ //#endregion
51
+ export { ELLIPSIS, EM_DASH, SC_CLASSES, SC_ID_PREFIX };
@@ -1,2 +1,2 @@
1
- import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-BS2kaUyE.mjs";
2
2
  export { Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticsOptions, appendPointer, emitDiagnostic };
@@ -1,2 +1,2 @@
1
- import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-QEwOtQAA.mjs";
1
+ import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-g_MCTQel.mjs";
2
2
  export { SchemaError, SchemaFieldError, SchemaNormalisationError, SchemaRenderError };
@@ -1,17 +1,5 @@
1
1
  //#region src/core/errors.ts
2
2
  /**
3
- * Structured error types for schema-components.
4
- *
5
- * Every error produced by the library is one of these three types:
6
- *
7
- * - SchemaNormalisationError — the adapter failed to convert the input
8
- * to JSON Schema (invalid Zod, bad OpenAPI ref, malformed schema)
9
- * - SchemaRenderError — a theme adapter's render function threw
10
- * - SchemaFieldError — a field path couldn't be resolved
11
- *
12
- * All extend `SchemaError` so consumers can catch the base class.
13
- */
14
- /**
15
3
  * Base class for all schema-components errors.
16
4
  * Catch this to handle any library error uniformly.
17
5
  *
@@ -55,7 +43,11 @@ var SchemaNormalisationError = class extends SchemaError {
55
43
  * The `cause` is the original error from the render function.
56
44
  */
57
45
  var SchemaRenderError = class extends SchemaError {
58
- /** The schema type being rendered when the error occurred. */
46
+ /**
47
+ * The schema type being rendered when the error occurred. Drawn from
48
+ * the walker's discriminant union so consumers can switch on it
49
+ * exhaustively without a wider `string` fallback.
50
+ */
59
51
  schemaType;
60
52
  constructor(message, schema, schemaType, cause) {
61
53
  super(message, schema, cause);
@@ -1,4 +1,4 @@
1
- import { M as WalkedField } from "../types-BnxPEElk.mjs";
1
+ import { j as WalkedField } from "../types-BTB73MB8.mjs";
2
2
 
3
3
  //#region src/core/fieldOrder.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BS2kaUyE.mjs";
2
2
 
3
3
  //#region src/core/formats.d.ts
4
4
  /**
@@ -9,6 +9,13 @@ import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
9
9
  * it reaches the regex engine.
10
10
  */
11
11
  declare const MAX_REGEX_PATTERN_LENGTH = 500;
12
+ /**
13
+ * Map a JSON Schema string `format` to the matching HTML `<input type="…">`
14
+ * value, or `undefined` when the format does not correspond to a date/time
15
+ * input. Shared by the HTML and headless React renderers so a single
16
+ * mapping table governs both pipelines.
17
+ */
18
+ declare function dateInputType(format: string | undefined): string | undefined;
12
19
  /**
13
20
  * A format validator: either a RegExp pattern or a predicate function.
14
21
  */
@@ -16,6 +23,27 @@ type FormatValidator = RegExp | ((value: string) => boolean);
16
23
  /**
17
24
  * Recognised JSON Schema formats with their validation patterns.
18
25
  * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
26
+ *
27
+ * Draft origin reference — formats first standardised by each draft:
28
+ *
29
+ * - Draft 04: `date-time`, `email`, `hostname`, `ipv4`, `ipv6`, `uri`
30
+ * - Draft 06: `uri-reference`, `uri-template`, `json-pointer`
31
+ * - Draft 07: `date`, `time`, `idn-email`, `idn-hostname`, `iri`,
32
+ * `iri-reference`, `regex`, `relative-json-pointer`
33
+ * - Draft 2019-09: `duration`, `uuid`
34
+ * - Vocabulary extensions / non-standard: `binary` (OpenAPI), and the
35
+ * Zod-emitted formats `cuid`, `cuid2`, `nanoid`, `cidrv4`, `cidrv6`,
36
+ * `base64`, `base64url`, `e164`, `emoji`, `ulid`, `xid`, `ksuid`,
37
+ * `lowercase`, `uppercase`, `jwt`, `json-string`
38
+ *
39
+ * Policy: schema-components accepts ALL formats in ALL drafts. We do
40
+ * not reject (e.g.) `uri-reference` on a Draft 04 schema or `uuid` on
41
+ * a Draft 06 schema, even though the spec did not standardise those
42
+ * names until a later draft. This matches the behaviour of every
43
+ * mainstream JSON Schema validator (Ajv, jsonschema, etc.) and avoids
44
+ * spurious failures on legitimate real-world schemas that pre-date or
45
+ * post-date the dialect they declare. Authors who want strict draft-
46
+ * locked behaviour should validate with a dedicated meta-schema tool.
19
47
  */
20
48
  /**
21
49
  * Email format pattern, exported as a named const so callers that need a
@@ -37,4 +65,4 @@ declare const FORMAT_PATTERNS: Readonly<Record<string, RegExp>>;
37
65
  */
38
66
  declare function validateFormat(value: string, format: string, diagnostics?: DiagnosticsOptions, pointer?: string): boolean | undefined;
39
67
  //#endregion
40
- export { EMAIL_FORMAT_PATTERN, FORMAT_PATTERNS, FormatValidator, MAX_REGEX_PATTERN_LENGTH, validateFormat };
68
+ export { EMAIL_FORMAT_PATTERN, FORMAT_PATTERNS, FormatValidator, MAX_REGEX_PATTERN_LENGTH, dateInputType, validateFormat };
@@ -9,8 +9,40 @@ import { emitDiagnostic } from "./diagnostics.mjs";
9
9
  */
10
10
  const MAX_REGEX_PATTERN_LENGTH = 500;
11
11
  /**
12
+ * Map a JSON Schema string `format` to the matching HTML `<input type="…">`
13
+ * value, or `undefined` when the format does not correspond to a date/time
14
+ * input. Shared by the HTML and headless React renderers so a single
15
+ * mapping table governs both pipelines.
16
+ */
17
+ function dateInputType(format) {
18
+ if (format === "date") return "date";
19
+ if (format === "time") return "time";
20
+ if (format === "date-time" || format === "datetime") return "datetime-local";
21
+ }
22
+ /**
12
23
  * Recognised JSON Schema formats with their validation patterns.
13
24
  * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
25
+ *
26
+ * Draft origin reference — formats first standardised by each draft:
27
+ *
28
+ * - Draft 04: `date-time`, `email`, `hostname`, `ipv4`, `ipv6`, `uri`
29
+ * - Draft 06: `uri-reference`, `uri-template`, `json-pointer`
30
+ * - Draft 07: `date`, `time`, `idn-email`, `idn-hostname`, `iri`,
31
+ * `iri-reference`, `regex`, `relative-json-pointer`
32
+ * - Draft 2019-09: `duration`, `uuid`
33
+ * - Vocabulary extensions / non-standard: `binary` (OpenAPI), and the
34
+ * Zod-emitted formats `cuid`, `cuid2`, `nanoid`, `cidrv4`, `cidrv6`,
35
+ * `base64`, `base64url`, `e164`, `emoji`, `ulid`, `xid`, `ksuid`,
36
+ * `lowercase`, `uppercase`, `jwt`, `json-string`
37
+ *
38
+ * Policy: schema-components accepts ALL formats in ALL drafts. We do
39
+ * not reject (e.g.) `uri-reference` on a Draft 04 schema or `uuid` on
40
+ * a Draft 06 schema, even though the spec did not standardise those
41
+ * names until a later draft. This matches the behaviour of every
42
+ * mainstream JSON Schema validator (Ajv, jsonschema, etc.) and avoids
43
+ * spurious failures on legitimate real-world schemas that pre-date or
44
+ * post-date the dialect they declare. Authors who want strict draft-
45
+ * locked behaviour should validate with a dedicated meta-schema tool.
14
46
  */
15
47
  /**
16
48
  * Email format pattern, exported as a named const so callers that need a
@@ -156,4 +188,4 @@ function validateFormat(value, format, diagnostics, pointer = "") {
156
188
  if (predicate !== void 0) return predicate(value);
157
189
  }
158
190
  //#endregion
159
- export { EMAIL_FORMAT_PATTERN, FORMAT_PATTERNS, MAX_REGEX_PATTERN_LENGTH, validateFormat };
191
+ export { EMAIL_FORMAT_PATTERN, FORMAT_PATTERNS, MAX_REGEX_PATTERN_LENGTH, dateInputType, validateFormat };
@@ -0,0 +1,54 @@
1
+ //#region src/core/idPath.d.ts
2
+ /**
3
+ * Canonical DOM-id generation from structural paths.
4
+ *
5
+ * Every render pipeline (React headless, HTML sync, HTML stream) needs to
6
+ * derive stable DOM ids from the same path so `aria-controls`, `aria-labelledby`,
7
+ * and `htmlFor` references resolve consistently across pipelines.
8
+ *
9
+ * Previously each pipeline carried its own copy with subtly different
10
+ * normalisation (raw path in one place, dot/bracket-collapsed in another,
11
+ * whitelist-collapsed in a third). The streaming renderer's tab panel ids
12
+ * silently diverged from the sync renderer's because of that drift.
13
+ *
14
+ * Pipelines should import the helpers below rather than re-deriving them.
15
+ */
16
+ /**
17
+ * Normalise a structural path into the id segment used after the `sc-`
18
+ * prefix. Whitelist-based: any run of characters outside `[A-Za-z0-9_-]`
19
+ * collapses to a single hyphen, with trailing hyphens stripped.
20
+ *
21
+ * Whitelist (not blacklist) so unexpected characters from free-text sources
22
+ * — `meta.description`, label-derived suffixes, encoded JSON Pointers —
23
+ * cannot leak into ids and break CSS selectors or aria associations.
24
+ */
25
+ declare function normaliseIdSegment(value: string): string;
26
+ /**
27
+ * Build the canonical `sc-`-prefixed DOM id for a structural path.
28
+ * Use this as the base id for an input element; derived ids (panel, tab,
29
+ * hint) compose suffixes onto the returned string.
30
+ *
31
+ * Throws on an empty path: a previous "sc-field" fallback caused every
32
+ * input across a form to share the same id, breaking label-input pairing
33
+ * and screen reader navigation.
34
+ */
35
+ declare function fieldDomId(path: string): string;
36
+ /**
37
+ * Derive the constraint-hint element id for a given field id.
38
+ * The hint element is wired to inputs via `aria-describedby`.
39
+ */
40
+ declare function hintIdFor(fieldId: string): string;
41
+ /**
42
+ * Derive the tab panel id for a discriminated-union container at `path`.
43
+ * Used by every renderer that emits a WAI-ARIA tabs widget so that the
44
+ * `aria-controls` on each tab and the `id` on the matching panel match.
45
+ */
46
+ declare function panelIdFor(path: string): string;
47
+ /**
48
+ * Derive the id for tab `i` within a discriminated-union container at `path`.
49
+ * Used to pair `aria-labelledby` on the active panel with the active tab's
50
+ * `id` across all renderers.
51
+ */
52
+ declare function tabIdFor(path: string, index: number): string;
53
+ //#endregion
54
+ export { fieldDomId, hintIdFor, normaliseIdSegment, panelIdFor, tabIdFor };
@@ -0,0 +1,66 @@
1
+ import "./cssClasses.mjs";
2
+ //#region src/core/idPath.ts
3
+ /**
4
+ * Canonical DOM-id generation from structural paths.
5
+ *
6
+ * Every render pipeline (React headless, HTML sync, HTML stream) needs to
7
+ * derive stable DOM ids from the same path so `aria-controls`, `aria-labelledby`,
8
+ * and `htmlFor` references resolve consistently across pipelines.
9
+ *
10
+ * Previously each pipeline carried its own copy with subtly different
11
+ * normalisation (raw path in one place, dot/bracket-collapsed in another,
12
+ * whitelist-collapsed in a third). The streaming renderer's tab panel ids
13
+ * silently diverged from the sync renderer's because of that drift.
14
+ *
15
+ * Pipelines should import the helpers below rather than re-deriving them.
16
+ */
17
+ /**
18
+ * Normalise a structural path into the id segment used after the `sc-`
19
+ * prefix. Whitelist-based: any run of characters outside `[A-Za-z0-9_-]`
20
+ * collapses to a single hyphen, with trailing hyphens stripped.
21
+ *
22
+ * Whitelist (not blacklist) so unexpected characters from free-text sources
23
+ * — `meta.description`, label-derived suffixes, encoded JSON Pointers —
24
+ * cannot leak into ids and break CSS selectors or aria associations.
25
+ */
26
+ function normaliseIdSegment(value) {
27
+ return value.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/-+$/g, "");
28
+ }
29
+ /**
30
+ * Build the canonical `sc-`-prefixed DOM id for a structural path.
31
+ * Use this as the base id for an input element; derived ids (panel, tab,
32
+ * hint) compose suffixes onto the returned string.
33
+ *
34
+ * Throws on an empty path: a previous "sc-field" fallback caused every
35
+ * input across a form to share the same id, breaking label-input pairing
36
+ * and screen reader navigation.
37
+ */
38
+ function fieldDomId(path) {
39
+ if (path.length === 0) throw new Error("fieldDomId requires a non-empty path. Thread a root path from the renderer entry point and derive children via joinPath().");
40
+ return `sc-${normaliseIdSegment(path)}`;
41
+ }
42
+ /**
43
+ * Derive the constraint-hint element id for a given field id.
44
+ * The hint element is wired to inputs via `aria-describedby`.
45
+ */
46
+ function hintIdFor(fieldId) {
47
+ return `${fieldId}-hint`;
48
+ }
49
+ /**
50
+ * Derive the tab panel id for a discriminated-union container at `path`.
51
+ * Used by every renderer that emits a WAI-ARIA tabs widget so that the
52
+ * `aria-controls` on each tab and the `id` on the matching panel match.
53
+ */
54
+ function panelIdFor(path) {
55
+ return `${fieldDomId(path)}-panel`;
56
+ }
57
+ /**
58
+ * Derive the id for tab `i` within a discriminated-union container at `path`.
59
+ * Used to pair `aria-labelledby` on the active panel with the active tab's
60
+ * `id` across all renderers.
61
+ */
62
+ function tabIdFor(path, index) {
63
+ return `${fieldDomId(path)}-tab-${String(index)}`;
64
+ }
65
+ //#endregion
66
+ export { fieldDomId, hintIdFor, normaliseIdSegment, panelIdFor, tabIdFor };
@@ -0,0 +1,2 @@
1
+ import { i as MaxRefDepth, n as MAX_REF_DEPTH, r as MAX_RENDER_DEPTH, t as MAX_PATH_ITEM_REF_HOPS } from "../limits-Cw5QZND8.mjs";
2
+ export { MAX_PATH_ITEM_REF_HOPS, MAX_REF_DEPTH, MAX_RENDER_DEPTH, MaxRefDepth };
@@ -0,0 +1,23 @@
1
+ //#region src/core/limits.ts
2
+ /**
3
+ * Shared depth caps and hop counts used to bound recursion across
4
+ * schema-components. All numeric limits live here so the renderer, the
5
+ * ref resolver, the OpenAPI parser, and the type-level inference engine
6
+ * agree on the same constants.
7
+ */
8
+ /**
9
+ * Maximum recursion depth for the schema walker, the React renderers,
10
+ * the streaming HTML renderer, and the server-side renderer. Beyond
11
+ * this depth a recursion sentinel is emitted instead of further descent
12
+ * — the only safe response to a cyclic walked-field graph.
13
+ */
14
+ const MAX_RENDER_DEPTH = 10;
15
+ const MAX_REF_DEPTH = 64;
16
+ /**
17
+ * Maximum number of `$ref` hops permitted when walking a chain of
18
+ * OpenAPI Path Item Object references. Beyond this a
19
+ * `path-item-ref-too-deep` diagnostic is emitted and resolution stops.
20
+ */
21
+ const MAX_PATH_ITEM_REF_HOPS = 8;
22
+ //#endregion
23
+ export { MAX_PATH_ITEM_REF_HOPS, MAX_REF_DEPTH, MAX_RENDER_DEPTH };
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BS2kaUyE.mjs";
2
2
 
3
3
  //#region src/core/merge.d.ts
4
4
  /**
@@ -32,6 +32,15 @@ declare function mergeRefSiblings(referencer: Record<string, unknown>, resolvedM
32
32
  * - \`true\` is the always-valid schema and contributes no constraints —
33
33
  * skip silently.
34
34
  *
35
+ * Type compatibility is enforced rather than papered over: two
36
+ * branches asserting incompatible primitive `type` keywords
37
+ * (e.g. `string` ∩ `number`) describe an unsatisfiable conjunction.
38
+ * `mergeAllOf` returns `false` and emits
39
+ * `schema-allof-incompatible` so the walker produces the same
40
+ * `NeverField` shape it gives a top-level `false` schema. Pretending
41
+ * the first type wins silently would silently weaken the constraint
42
+ * for any consumer that reads the merged result.
43
+ *
35
44
  * Non-boolean, non-object entries (e.g. arrays, numbers) are malformed
36
45
  * inputs that cannot represent a schema; skip them as before.
37
46
  */
@@ -116,6 +116,15 @@ function mergeRefSiblings(referencer, resolvedMeta) {
116
116
  * - \`true\` is the always-valid schema and contributes no constraints —
117
117
  * skip silently.
118
118
  *
119
+ * Type compatibility is enforced rather than papered over: two
120
+ * branches asserting incompatible primitive `type` keywords
121
+ * (e.g. `string` ∩ `number`) describe an unsatisfiable conjunction.
122
+ * `mergeAllOf` returns `false` and emits
123
+ * `schema-allof-incompatible` so the walker produces the same
124
+ * `NeverField` shape it gives a top-level `false` schema. Pretending
125
+ * the first type wins silently would silently weaken the constraint
126
+ * for any consumer that reads the merged result.
127
+ *
119
128
  * Non-boolean, non-object entries (e.g. arrays, numbers) are malformed
120
129
  * inputs that cannot represent a schema; skip them as before.
121
130
  */
@@ -150,16 +159,15 @@ function mergeAllOf(schemas, diagnostics, pointer = "") {
150
159
  const entryType = getString(entry, "type");
151
160
  if (entryType !== void 0) {
152
161
  if (!("type" in merged)) merged.type = entryType;
153
- else if (!deepEqual(merged.type, entryType)) emitDiagnostic(diagnostics, {
154
- code: "allof-conflict",
155
- message: `allOf branches define conflicting values for "type"; keeping the first occurrence and discarding subsequent values`,
156
- pointer,
157
- detail: {
158
- key: "type",
159
- kept: merged.type,
160
- discarded: entryType
161
- }
162
- });
162
+ else if (!areCompatibleTypes(merged.type, entryType)) {
163
+ emitDiagnostic(diagnostics, {
164
+ code: "schema-allof-incompatible",
165
+ message: `allOf branches assert incompatible \`type\` keywords (${describeType(merged.type)} ∩ ${describeType(entryType)}); the conjunction is unsatisfiable`,
166
+ pointer,
167
+ detail: { types: [merged.type, entryType] }
168
+ });
169
+ return false;
170
+ }
163
171
  }
164
172
  }
165
173
  if (Object.keys(properties).length > 0) merged.properties = properties;
@@ -167,6 +175,37 @@ function mergeAllOf(schemas, diagnostics, pointer = "") {
167
175
  return merged;
168
176
  }
169
177
  /**
178
+ * Two `type` keyword values are compatible when their intersection is
179
+ * non-empty. Identical strings always agree; `"integer"` is a subset
180
+ * of `"number"` per JSON Schema 2020-12 §6.1.1 so the conjunction
181
+ * collapses to `"integer"` and is treated as compatible here.
182
+ * Mismatched primitives (`"string"` ∩ `"number"`, `"boolean"` ∩
183
+ * `"object"`, etc.) describe an unsatisfiable intersection.
184
+ *
185
+ * `kept` carries the running merged value, which may already be an
186
+ * array form produced by an earlier merge — in that case we conservatively
187
+ * require deep equality to count as compatible, since narrowing the
188
+ * intersection of two arbitrary type-array sets is out of scope here.
189
+ */
190
+ function areCompatibleTypes(kept, incoming) {
191
+ if (typeof kept === "string") {
192
+ if (kept === incoming) return true;
193
+ if (kept === "integer" && incoming === "number" || kept === "number" && incoming === "integer") return true;
194
+ return false;
195
+ }
196
+ return deepEqual(kept, incoming);
197
+ }
198
+ /**
199
+ * Human-readable label for a `type` keyword value used in the
200
+ * `schema-allof-incompatible` diagnostic message. Strings render
201
+ * quoted; non-strings (array forms, malformed values) render via
202
+ * `JSON.stringify` so the message still carries useful context.
203
+ */
204
+ function describeType(value) {
205
+ if (typeof value === "string") return `"${value}"`;
206
+ return JSON.stringify(value);
207
+ }
208
+ /**
170
209
  * Detect `anyOf: [T, { type: "null" }]` → nullable T.
171
210
  * Returns the non-null schema and a nullable flag.
172
211
  */
@@ -1,5 +1,5 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
2
- import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-D-u7aMfy.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BS2kaUyE.mjs";
2
+ import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-BFTVLsdb.mjs";
3
3
 
4
4
  //#region src/core/normalise.d.ts
5
5
  type NodeTransform = (node: Record<string, unknown>) => Record<string, unknown>;
@@ -20,10 +20,28 @@ declare function deepNormalise(schema: Record<string, unknown>, transform: NodeT
20
20
  * Carries the diagnostics sink and the JSON Pointer to the current
21
21
  * node so per-node transforms can emit pointer-accurate diagnostics
22
22
  * when they translate or reject legacy constructs.
23
+ *
24
+ * `documentHasDynamicAnchor` is set once at the entry point by scanning
25
+ * the input for any `$dynamicAnchor`/`$recursiveAnchor` keyword. Per-
26
+ * node transforms read it to decide whether a `$dynamicRef`/`$recursiveRef`
27
+ * rewrite needs a `dynamic-ref-degraded` diagnostic — a ref pointing at
28
+ * an anchor in the document body cannot be statically resolved without
29
+ * losing dynamic-scope semantics, while a document with no dynamic
30
+ * anchors at all has no semantics to lose.
31
+ *
32
+ * `documentHasRecursiveAnchor` is the same idea for Draft 2019-09's
33
+ * `$recursiveAnchor` keyword.
34
+ *
35
+ * `declaredDraft` carries the draft the normaliser is operating under
36
+ * (when known) so per-node transforms can emit `keyword-out-of-draft`
37
+ * diagnostics for keywords introduced in a later draft.
23
38
  */
24
39
  interface NodeContext {
25
40
  diagnostics: DiagnosticsOptions | undefined;
26
41
  pointer: string;
42
+ documentHasDynamicAnchor: boolean;
43
+ documentHasRecursiveAnchor: boolean;
44
+ declaredDraft: JsonSchemaDraft | undefined;
27
45
  }
28
46
  type NodeTransformWithContext = (node: Record<string, unknown>, ctx: NodeContext) => Record<string, unknown>;
29
47
  /**
@@ -58,6 +76,25 @@ declare function deepNormaliseWithContext(schema: Record<string, unknown>, trans
58
76
  * {@link deepNormaliseWithContext} to thread diagnostics.
59
77
  */
60
78
  declare function normaliseDraft04Node(node: Record<string, unknown>): Record<string, unknown>;
79
+ /**
80
+ * Pick the per-node transform that normalises a single Schema Object to
81
+ * canonical Draft 2020-12 form for the supplied draft. Exposed so the
82
+ * OpenAPI 3.1 path can honour a non-default `jsonSchemaDialect`
83
+ * declaration by routing each Schema Object through the matching
84
+ * transform without re-implementing the dispatch.
85
+ */
86
+ declare function selectDraftTransform(draft: JsonSchemaDraft): NodeTransformWithContext;
87
+ /**
88
+ * Scan a JSON document body for the presence of a named keyword
89
+ * anywhere in the structure. Walks both arrays and objects without
90
+ * regard to schema-vs-data position — the caller is responsible for
91
+ * passing a keyword whose presence is meaningful at any depth.
92
+ *
93
+ * Cycle-safe: cyclic references introduced by the OpenAPI bundler's
94
+ * `structuredClone` of external refs are short-circuited via the
95
+ * `visited` set so the scan terminates.
96
+ */
97
+ declare function documentContainsKeyword(value: unknown, keyword: string, visited?: WeakSet<object>): boolean;
61
98
  /**
62
99
  * Normalise a JSON Schema to canonical Draft 2020-12 form.
63
100
  * Deep-clones the input — the original is never mutated.
@@ -77,4 +114,4 @@ declare function normaliseJsonSchema(schema: Record<string, unknown>, draft: Jso
77
114
  */
78
115
  declare function normaliseOpenApiSchemas(doc: Record<string, unknown>, version: OpenApiVersionInfo, diagnostics?: DiagnosticsOptions): Record<string, unknown>;
79
116
  //#endregion
80
- export { NodeContext, NodeTransform, NodeTransformWithContext, deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
117
+ export { NodeContext, NodeTransform, NodeTransformWithContext, deepNormalise, deepNormaliseWithContext, documentContainsKeyword, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas, selectDraftTransform };
@@ -1,2 +1,2 @@
1
- import { a as normaliseOpenApiSchemas, i as normaliseJsonSchema, n as deepNormaliseWithContext, r as normaliseDraft04Node, t as deepNormalise } from "../normalise-DaSrnr8g.mjs";
2
- export { deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
1
+ import { a as normaliseJsonSchema, i as normaliseDraft04Node, n as deepNormaliseWithContext, o as normaliseOpenApiSchemas, r as documentContainsKeyword, s as selectDraftTransform, t as deepNormalise } from "../normalise-DCYp06Sr.mjs";
2
+ export { deepNormalise, deepNormaliseWithContext, documentContainsKeyword, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas, selectDraftTransform };
@@ -1,6 +1,20 @@
1
1
  import { NodeTransform } from "./normalise.mjs";
2
2
 
3
3
  //#region src/core/openapi30.d.ts
4
+ /**
5
+ * Lift OpenAPI 3.x singular `example` onto the plural `examples` key.
6
+ *
7
+ * Two output shapes are spec-correct depending on the parent object type:
8
+ * - `"array"` — Schema Object: `examples: [example]` (Draft 2020-12 plural).
9
+ * - `"map"` — Parameter / Header / Media Type Object: an Examples Map
10
+ * keyed by name. The single value is wrapped under the
11
+ * synthetic key `default` to produce a valid one-entry map
12
+ * of one Example Object.
13
+ *
14
+ * When both `example` and `examples` coexist the spec declares them mutually
15
+ * exclusive — `example` is dropped and `examples` wins.
16
+ */
17
+ declare function liftExampleToExamples(node: Record<string, unknown>, shape: "array" | "map"): void;
4
18
  /**
5
19
  * Normalise OpenAPI 3.0.x `nullable` keyword to `anyOf [T, null]`.
6
20
  *
@@ -90,4 +104,4 @@ declare function deepNormaliseOpenApiDoc(doc: Record<string, unknown>, normalise
90
104
  */
91
105
  declare function deepNormaliseOpenApi30Doc(doc: Record<string, unknown>, deepNormalise: (schema: Record<string, unknown>, transform: NodeTransform) => Record<string, unknown>): Record<string, unknown>;
92
106
  //#endregion
93
- export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
107
+ export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, liftExampleToExamples, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
@@ -1,2 +1,2 @@
1
- import { c as deepNormaliseOpenApi30Doc, d as normaliseOpenApi30Discriminator, f as normaliseOpenApi30Node, l as deepNormaliseOpenApiDoc, s as applyDiscriminatorAllOfPrepass, u as normaliseOpenApi30Combined } from "../normalise-DaSrnr8g.mjs";
2
- export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
1
+ import { d as deepNormaliseOpenApiDoc, f as liftExampleToExamples, h as normaliseOpenApi30Node, l as applyDiscriminatorAllOfPrepass, m as normaliseOpenApi30Discriminator, p as normaliseOpenApi30Combined, u as deepNormaliseOpenApi30Doc } from "../normalise-DCYp06Sr.mjs";
2
+ export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, liftExampleToExamples, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };