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.
- package/README.md +3 -1
- package/dist/core/adapter.d.mts +115 -4
- package/dist/core/adapter.mjs +405 -75
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/constraints.mjs +0 -7
- package/dist/core/cssClasses.d.mts +52 -0
- package/dist/core/cssClasses.mjs +51 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +5 -13
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +30 -2
- package/dist/core/formats.mjs +33 -1
- package/dist/core/idPath.d.mts +54 -0
- package/dist/core/idPath.mjs +66 -0
- package/dist/core/limits.d.mts +2 -0
- package/dist/core/limits.mjs +23 -0
- package/dist/core/merge.d.mts +10 -1
- package/dist/core/merge.mjs +49 -10
- package/dist/core/normalise.d.mts +40 -3
- package/dist/core/normalise.mjs +2 -2
- package/dist/core/openapi30.d.mts +15 -1
- package/dist/core/openapi30.mjs +2 -2
- package/dist/core/openapiConstants.d.mts +67 -0
- package/dist/core/openapiConstants.mjs +90 -0
- package/dist/core/ref.d.mts +2 -2
- package/dist/core/ref.mjs +85 -6
- package/dist/core/refChain.d.mts +70 -0
- package/dist/core/refChain.mjs +44 -0
- package/dist/core/renderer.d.mts +1 -1
- package/dist/core/renderer.mjs +0 -2
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +982 -2
- package/dist/core/types.d.mts +2 -2
- package/dist/core/types.mjs +1 -4
- package/dist/core/unionMatch.d.mts +36 -0
- package/dist/core/unionMatch.mjs +53 -0
- package/dist/core/version.d.mts +1 -1
- package/dist/core/version.mjs +29 -17
- package/dist/core/walkBuilders.d.mts +23 -4
- package/dist/core/walkBuilders.mjs +27 -7
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +123 -47
- package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-BS2kaUyE.d.mts} +1 -1
- package/dist/{errors-QEwOtQAA.d.mts → errors-g_MCTQel.d.mts} +10 -16
- package/dist/html/a11y.d.mts +9 -4
- package/dist/html/a11y.mjs +10 -12
- package/dist/html/renderToHtml.d.mts +10 -3
- package/dist/html/renderToHtml.mjs +13 -3
- package/dist/html/renderToHtmlStream.d.mts +2 -2
- package/dist/html/renderToHtmlStream.mjs +12 -1
- package/dist/html/renderers.d.mts +43 -8
- package/dist/html/renderers.mjs +136 -116
- package/dist/html/streamRenderers.d.mts +6 -6
- package/dist/html/streamRenderers.mjs +129 -89
- package/dist/limits-Cw5QZND8.d.mts +29 -0
- package/dist/{normalise-DaSrnr8g.mjs → normalise-DCYp06Sr.mjs} +770 -227
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/ApiSecurity.mjs +16 -2
- package/dist/openapi/components.d.mts +234 -23
- package/dist/openapi/components.mjs +183 -52
- package/dist/openapi/parser.d.mts +9 -8
- package/dist/openapi/parser.mjs +252 -70
- package/dist/openapi/resolve.d.mts +31 -15
- package/dist/openapi/resolve.mjs +260 -40
- package/dist/react/SchemaComponent.d.mts +126 -36
- package/dist/react/SchemaComponent.mjs +95 -57
- package/dist/react/SchemaView.d.mts +30 -10
- package/dist/react/SchemaView.mjs +2 -2
- package/dist/react/a11y.d.mts +21 -0
- package/dist/react/a11y.mjs +24 -0
- package/dist/react/fieldPath.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headless.mjs +1 -2
- package/dist/react/headlessRenderers.d.mts +9 -11
- package/dist/react/headlessRenderers.mjs +51 -102
- package/dist/{ref-si8ViYun.d.mts → ref-DjLEKa_E.d.mts} +38 -3
- package/dist/{renderer-DI6ZYf7a.d.mts → renderer-CXJ8y0qw.d.mts} +2 -2
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +2 -1
- package/dist/{types-BnxPEElk.d.mts → types-BTB73MB8.d.mts} +35 -14
- package/dist/{version-D-u7aMfy.d.mts → version-BFTVLsdb.d.mts} +7 -1
- package/package.json +1 -3
- 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-
|
|
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 };
|
package/dist/core/errors.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-
|
|
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 };
|
package/dist/core/errors.mjs
CHANGED
|
@@ -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
|
-
/**
|
|
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);
|
package/dist/core/formats.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as DiagnosticsOptions } from "../diagnostics-
|
|
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 };
|
package/dist/core/formats.mjs
CHANGED
|
@@ -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,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 };
|
package/dist/core/merge.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as DiagnosticsOptions } from "../diagnostics-
|
|
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
|
*/
|
package/dist/core/merge.mjs
CHANGED
|
@@ -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 (!
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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-
|
|
2
|
-
import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-
|
|
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 };
|
package/dist/core/normalise.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
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 };
|
package/dist/core/openapi30.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
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 };
|