schema-components 1.28.2 → 2.0.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 +38 -16
- package/dist/core/adapter.d.mts +213 -3
- package/dist/core/adapter.mjs +21 -2
- package/dist/core/constraintHint.d.mts +15 -0
- package/dist/core/constraintHint.mjs +24 -0
- package/dist/core/constraints.d.mts +34 -2
- package/dist/core/constraints.mjs +33 -1
- package/dist/core/cssClasses.d.mts +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +22 -12
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +7 -1
- package/dist/core/formats.mjs +6 -0
- package/dist/core/idPath.d.mts +35 -5
- package/dist/core/idPath.mjs +79 -7
- package/dist/core/inferValue.d.mts +2 -0
- package/dist/core/inferValue.mjs +1 -0
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/limits.mjs +6 -0
- package/dist/core/merge.d.mts +22 -1
- package/dist/core/merge.mjs +66 -3
- package/dist/core/normalise.d.mts +17 -2
- package/dist/core/normalise.mjs +1 -1
- package/dist/core/openapi30.mjs +1 -1
- package/dist/core/openapiConstants.d.mts +1 -0
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/refChain.d.mts +3 -4
- package/dist/core/refChain.mjs +2 -3
- package/dist/core/renderer.d.mts +199 -2
- package/dist/core/renderer.mjs +5 -0
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +3 -3
- package/dist/core/types.d.mts +1 -1
- package/dist/core/types.mjs +17 -0
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/uri.d.mts +12 -4
- package/dist/core/uri.mjs +30 -4
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +63 -6
- package/dist/core/walkBuilders.mjs +33 -1
- package/dist/core/walker.d.mts +14 -1
- package/dist/core/walker.mjs +18 -0
- package/dist/{diagnostics-Cbwak-ZX.d.mts → diagnostics-BTrm3O6J.d.mts} +9 -1
- package/dist/{errors-DQSIK4n1.d.mts → errors-Dki7tji4.d.mts} +23 -13
- package/dist/html/a11y.d.mts +3 -7
- package/dist/html/a11y.mjs +1 -16
- package/dist/html/html.d.mts +11 -0
- package/dist/html/html.mjs +11 -0
- package/dist/html/renderToHtml.d.mts +45 -12
- package/dist/html/renderToHtml.mjs +20 -4
- package/dist/html/renderToHtmlStream.d.mts +63 -18
- package/dist/html/renderToHtmlStream.mjs +34 -8
- package/dist/html/renderers.d.mts +6 -31
- package/dist/html/renderers.mjs +45 -91
- package/dist/html/streamRenderers.d.mts +31 -3
- package/dist/html/streamRenderers.mjs +41 -8
- package/dist/inferValue-PPXWJpbN.d.mts +77 -0
- package/dist/{limits-DJhgx5Ay.d.mts → limits-x4OiyJxh.d.mts} +6 -0
- package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
- package/dist/openapi/ApiCallbacks.d.mts +13 -1
- package/dist/openapi/ApiCallbacks.mjs +7 -0
- package/dist/openapi/ApiLinks.d.mts +13 -1
- package/dist/openapi/ApiLinks.mjs +7 -0
- package/dist/openapi/ApiResponseHeaders.d.mts +13 -1
- package/dist/openapi/ApiResponseHeaders.mjs +7 -0
- package/dist/openapi/ApiSecurity.d.mts +14 -1
- package/dist/openapi/ApiSecurity.mjs +29 -8
- package/dist/openapi/bundle.d.mts +31 -0
- package/dist/openapi/components.d.mts +135 -20
- package/dist/openapi/components.mjs +90 -15
- package/dist/openapi/parser.d.mts +140 -13
- package/dist/openapi/parser.mjs +84 -12
- package/dist/openapi/resolve.d.mts +42 -47
- package/dist/openapi/resolve.mjs +62 -56
- package/dist/react/SchemaComponent.d.mts +90 -88
- package/dist/react/SchemaComponent.mjs +74 -2
- package/dist/react/SchemaErrorBoundary.d.mts +18 -1
- package/dist/react/SchemaErrorBoundary.mjs +13 -1
- package/dist/react/SchemaView.d.mts +39 -11
- package/dist/react/SchemaView.mjs +23 -6
- package/dist/react/a11y.d.mts +74 -7
- package/dist/react/a11y.mjs +67 -6
- package/dist/react/fieldPath.d.mts +16 -1
- package/dist/react/fieldPath.mjs +25 -1
- package/dist/react/fieldShell.d.mts +49 -0
- package/dist/react/fieldShell.mjs +37 -0
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +13 -2
- package/dist/react/headlessRenderers.mjs +134 -54
- package/dist/{ref-TdeMfaV_.d.mts → ref-DdsbekXX.d.mts} +33 -1
- package/dist/themes/mantine.d.mts +54 -12
- package/dist/themes/mantine.mjs +195 -140
- package/dist/themes/mui.d.mts +64 -11
- package/dist/themes/mui.mjs +277 -213
- package/dist/themes/radix.d.mts +67 -15
- package/dist/themes/radix.mjs +235 -170
- package/dist/themes/shadcn.d.mts +25 -1
- package/dist/themes/shadcn.mjs +112 -91
- package/dist/{types-BTB73MB8.d.mts → types-BrYbjC7_.d.mts} +30 -0
- package/dist/{version-ZzL5R6cS.d.mts → version-DL8U5RuA.d.mts} +6 -0
- package/package.json +8 -1
- package/dist/adapter-DqlAnZ_w.d.mts +0 -172
- package/dist/renderer-Ul9taFYp.d.mts +0 -169
|
@@ -1,13 +1,41 @@
|
|
|
1
|
-
import { j as WalkedField } from "../types-
|
|
2
|
-
import { i as DiagnosticsOptions } from "../diagnostics-
|
|
3
|
-
import {
|
|
1
|
+
import { j as WalkedField } from "../types-BrYbjC7_.mjs";
|
|
2
|
+
import { i as DiagnosticsOptions } from "../diagnostics-BTrm3O6J.mjs";
|
|
3
|
+
import { HtmlResolver } from "../core/renderer.mjs";
|
|
4
4
|
import { HtmlElement } from "./html.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/html/streamRenderers.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Serialise the opening tag of an element so a generator can yield it
|
|
9
|
+
* without the matching closing tag. Void elements (e.g. `input`) are
|
|
10
|
+
* emitted self-closed so the chunk is structurally valid on its own.
|
|
11
|
+
*/
|
|
7
12
|
declare function yieldOpen(el: HtmlElement): string;
|
|
13
|
+
/**
|
|
14
|
+
* Serialise the closing tag of an element so a generator can yield it
|
|
15
|
+
* after its children. Returns an empty string for void elements (their
|
|
16
|
+
* opening tag was emitted self-closed by {@link yieldOpen}).
|
|
17
|
+
*/
|
|
8
18
|
declare function yieldClose(el: HtmlElement): string;
|
|
19
|
+
/**
|
|
20
|
+
* Render a leaf {@link WalkedField} entirely as a single HTML chunk.
|
|
21
|
+
* Used inside the streaming generators when descent into containers is
|
|
22
|
+
* complete. Falls back to a `<span>`-wrapped value when no renderer is
|
|
23
|
+
* registered for the field type.
|
|
24
|
+
*/
|
|
9
25
|
declare function renderLeaf(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Drain {@link streamField} into a single string. Used when a streamed
|
|
28
|
+
* sub-tree needs to be embedded inside a non-streaming chunk (e.g. as
|
|
29
|
+
* children of a parent element).
|
|
30
|
+
*/
|
|
10
31
|
declare function renderFieldSync(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string, rawResolver: HtmlResolver, currentDepth: number, diagnostics: DiagnosticsOptions | undefined): string;
|
|
32
|
+
/**
|
|
33
|
+
* Render a {@link WalkedField} as a generator that yields HTML chunks
|
|
34
|
+
* at natural boundaries (opening tag, one chunk per child, closing
|
|
35
|
+
* tag). Threads `currentDepth` so cyclic walked-field graphs terminate
|
|
36
|
+
* at `MAX_RENDER_DEPTH` with a recursion sentinel rather than
|
|
37
|
+
* overflowing the stack.
|
|
38
|
+
*/
|
|
11
39
|
declare function streamField(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string, rawResolver: HtmlResolver, currentDepth?: number, diagnostics?: DiagnosticsOptions): Iterable<string, void, undefined>;
|
|
12
40
|
//#endregion
|
|
13
41
|
export { renderFieldSync, renderLeaf, streamField, yieldClose, yieldOpen };
|
|
@@ -2,22 +2,38 @@ import { isObject } from "../core/guards.mjs";
|
|
|
2
2
|
import "../core/limits.mjs";
|
|
3
3
|
import { emitDiagnostic } from "../core/diagnostics.mjs";
|
|
4
4
|
import { SC_CLASSES } from "../core/cssClasses.mjs";
|
|
5
|
+
import { panelIdFor, tabIdFor } from "../core/idPath.mjs";
|
|
5
6
|
import { getHtmlRenderFn } from "../core/renderer.mjs";
|
|
6
7
|
import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
|
|
7
8
|
import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
|
|
8
9
|
import { ariaLabelAttrs, buildHintElement, buildInputId, joinPath, requiredIndicator } from "./a11y.mjs";
|
|
9
|
-
import { panelId, tabId } from "./renderers.mjs";
|
|
10
10
|
import { recursionSentinelHtml } from "./renderToHtml.mjs";
|
|
11
11
|
//#region src/html/streamRenderers.ts
|
|
12
|
+
/**
|
|
13
|
+
* Serialise the opening tag of an element so a generator can yield it
|
|
14
|
+
* without the matching closing tag. Void elements (e.g. `input`) are
|
|
15
|
+
* emitted self-closed so the chunk is structurally valid on its own.
|
|
16
|
+
*/
|
|
12
17
|
function yieldOpen(el) {
|
|
13
18
|
const attrStr = serializeAttributes(el.attributes);
|
|
14
19
|
if (VOID_ELEMENTS.has(el.tag)) return `<${el.tag}${attrStr} />`;
|
|
15
20
|
return `<${el.tag}${attrStr}>`;
|
|
16
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Serialise the closing tag of an element so a generator can yield it
|
|
24
|
+
* after its children. Returns an empty string for void elements (their
|
|
25
|
+
* opening tag was emitted self-closed by {@link yieldOpen}).
|
|
26
|
+
*/
|
|
17
27
|
function yieldClose(el) {
|
|
18
28
|
if (VOID_ELEMENTS.has(el.tag)) return "";
|
|
19
29
|
return `</${el.tag}>`;
|
|
20
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Render a leaf {@link WalkedField} entirely as a single HTML chunk.
|
|
33
|
+
* Used inside the streaming generators when descent into containers is
|
|
34
|
+
* complete. Falls back to a `<span>`-wrapped value when no renderer is
|
|
35
|
+
* registered for the field type.
|
|
36
|
+
*/
|
|
21
37
|
function renderLeaf(tree, value, mergedResolver, path) {
|
|
22
38
|
const renderFn = getHtmlRenderFn(tree.type, mergedResolver);
|
|
23
39
|
if (renderFn !== void 0) return renderFn({
|
|
@@ -33,6 +49,11 @@ function renderLeaf(tree, value, mergedResolver, path) {
|
|
|
33
49
|
if (value === void 0 || value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
34
50
|
return serialize(h("span", { class: SC_CLASSES.value }, typeof value === "string" ? value : JSON.stringify(value)));
|
|
35
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Drain {@link streamField} into a single string. Used when a streamed
|
|
54
|
+
* sub-tree needs to be embedded inside a non-streaming chunk (e.g. as
|
|
55
|
+
* children of a parent element).
|
|
56
|
+
*/
|
|
36
57
|
function renderFieldSync(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
|
|
37
58
|
return [...streamField(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics)].join("");
|
|
38
59
|
}
|
|
@@ -51,6 +72,13 @@ function typeMismatchPlaceholder(expectedShape) {
|
|
|
51
72
|
role: "alert"
|
|
52
73
|
}, `invalid value (expected ${expectedShape})`));
|
|
53
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Render a {@link WalkedField} as a generator that yields HTML chunks
|
|
77
|
+
* at natural boundaries (opening tag, one chunk per child, closing
|
|
78
|
+
* tag). Threads `currentDepth` so cyclic walked-field graphs terminate
|
|
79
|
+
* at `MAX_RENDER_DEPTH` with a recursion sentinel rather than
|
|
80
|
+
* overflowing the stack.
|
|
81
|
+
*/
|
|
54
82
|
function* streamField(tree, value, mergedResolver, path, rawResolver, currentDepth = 0, diagnostics) {
|
|
55
83
|
if (currentDepth >= 10) {
|
|
56
84
|
yield recursionSentinelHtml(typeof tree.meta.description === "string" ? tree.meta.description : "schema");
|
|
@@ -136,7 +164,7 @@ function* streamObject(tree, value, mergedResolver, path, rawResolver, currentDe
|
|
|
136
164
|
class: SC_CLASSES.label,
|
|
137
165
|
for: fieldId
|
|
138
166
|
}, ...labelContent), raw(childChunks)];
|
|
139
|
-
const hint = buildHintElement(
|
|
167
|
+
const hint = buildHintElement(fieldId, field.constraints);
|
|
140
168
|
if (hint !== void 0) fieldChildren.push(hint);
|
|
141
169
|
yield serialize(h("div", { class: SC_CLASSES.field }, ...fieldChildren));
|
|
142
170
|
}
|
|
@@ -209,8 +237,12 @@ function* streamRecord(tree, value, mergedResolver, path, rawResolver, currentDe
|
|
|
209
237
|
const container = h("div", attrs);
|
|
210
238
|
yield yieldOpen(container);
|
|
211
239
|
for (const [key, val] of Object.entries(obj)) {
|
|
240
|
+
const childInputId = buildInputId(path, key);
|
|
212
241
|
const childHtml = renderFieldSync(valueType, val, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics);
|
|
213
|
-
yield serialize(h("div", { class: SC_CLASSES.field }, h("label", {
|
|
242
|
+
yield serialize(h("div", { class: SC_CLASSES.field }, h("label", {
|
|
243
|
+
class: SC_CLASSES.label,
|
|
244
|
+
for: childInputId
|
|
245
|
+
}, key), raw(childHtml)));
|
|
214
246
|
}
|
|
215
247
|
yield yieldClose(container);
|
|
216
248
|
}
|
|
@@ -239,7 +271,7 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
|
|
|
239
271
|
if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
|
|
240
272
|
return;
|
|
241
273
|
}
|
|
242
|
-
const tabPanelId =
|
|
274
|
+
const tabPanelId = panelIdFor(path);
|
|
243
275
|
const wrapper = h("div", { class: SC_CLASSES.discriminatedUnion });
|
|
244
276
|
yield yieldOpen(wrapper);
|
|
245
277
|
const tabButtons = options.map((_opt, i) => {
|
|
@@ -247,8 +279,8 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
|
|
|
247
279
|
type: "button",
|
|
248
280
|
role: "tab",
|
|
249
281
|
class: i === activeIndex ? SC_CLASSES.tabActive : SC_CLASSES.tab,
|
|
250
|
-
id:
|
|
251
|
-
"aria-selected": i === activeIndex ? "true" :
|
|
282
|
+
id: tabIdFor(path, i),
|
|
283
|
+
"aria-selected": i === activeIndex ? "true" : "false",
|
|
252
284
|
"aria-controls": tabPanelId,
|
|
253
285
|
tabindex: i === activeIndex ? "0" : "-1"
|
|
254
286
|
}, optionLabels[i]);
|
|
@@ -256,12 +288,13 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
|
|
|
256
288
|
yield serialize(h("div", {
|
|
257
289
|
role: "tablist",
|
|
258
290
|
class: SC_CLASSES.tabs,
|
|
259
|
-
"aria-label": "Select variant"
|
|
291
|
+
"aria-label": "Select variant",
|
|
292
|
+
"aria-orientation": "horizontal"
|
|
260
293
|
}, ...tabButtons));
|
|
261
294
|
const panelOpen = h("div", {
|
|
262
295
|
role: "tabpanel",
|
|
263
296
|
id: tabPanelId,
|
|
264
|
-
"aria-labelledby":
|
|
297
|
+
"aria-labelledby": tabIdFor(path, activeIndex)
|
|
265
298
|
});
|
|
266
299
|
yield yieldOpen(panelOpen);
|
|
267
300
|
if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { d as FieldOverrides, u as FieldOverride } from "./types-BrYbjC7_.mjs";
|
|
2
|
+
import { SchemaIoSide } from "./core/adapter.mjs";
|
|
3
|
+
import { FromJSONSchema, FromJSONSchemaMode, IsSwagger2Doc, ResolveOpenAPIRef, TypeAtPath, __SchemaInferenceFellBack } from "./core/typeInference.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/core/inferValue.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Recursive mapped type that mirrors a schema's shape for per-field
|
|
9
|
+
* overrides. Dispatches on the schema kind in the same order as
|
|
10
|
+
* {@link InferSchemaValue} so the inferred override map tracks the
|
|
11
|
+
* inferred value shape.
|
|
12
|
+
*
|
|
13
|
+
* Exported so `<SchemaView>` and other consumers can type their
|
|
14
|
+
* `fields` prop against the same machinery `<SchemaComponent>` uses.
|
|
15
|
+
*
|
|
16
|
+
* @group Components
|
|
17
|
+
*/
|
|
18
|
+
type InferFields<T, Ref extends string | undefined> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
|
|
19
|
+
openapi: unknown;
|
|
20
|
+
} ? Ref extends string ? FieldOverrides<ResolveOpenAPIRef<T & Record<string, unknown>, Ref>> : Record<string, FieldOverride> : T extends object ? unknown extends FromJSONSchema<T> ? Record<string, FieldOverride> : FieldOverrides<FromJSONSchema<T>> : Record<string, FieldOverride>;
|
|
21
|
+
/**
|
|
22
|
+
* Infer the data type carried by the schema input.
|
|
23
|
+
*
|
|
24
|
+
* Mirrors {@link InferFields}'s dispatch order: Zod schema → `z.infer`,
|
|
25
|
+
* OpenAPI doc + ref → `ResolveOpenAPIRef`, plain JSON Schema object →
|
|
26
|
+
* `FromJSONSchema`, everything else → `unknown`. The `Mode` parameter
|
|
27
|
+
* is plumbed through to `FromJSONSchema` / `ResolveOpenAPIRef` so
|
|
28
|
+
* `readOnly` / `writeOnly` keywords participate in the inferred
|
|
29
|
+
* object shape — `"output"` for the rendered value, `"input"` for the
|
|
30
|
+
* `onChange` argument.
|
|
31
|
+
*
|
|
32
|
+
* When the schema's value type cannot be statically determined (e.g.
|
|
33
|
+
* a runtime `Record<string, unknown>` JSON Schema, or an OpenAPI doc
|
|
34
|
+
* without a ref), the result falls back to `unknown` so callers can
|
|
35
|
+
* still supply arbitrary values.
|
|
36
|
+
*/
|
|
37
|
+
type InferSchemaValue<T, Ref extends string | undefined, Mode extends FromJSONSchemaMode> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : T extends z.ZodType ? Mode extends "input" ? z.input<T> : z.output<T> : T extends {
|
|
38
|
+
openapi: unknown;
|
|
39
|
+
} ? Ref extends string ? ResolveOpenAPIRef<T & Record<string, unknown>, Ref, [], Mode> : unknown : T extends object ? FromJSONSchema<T, Record<string, never>, [], Mode> | (unknown extends FromJSONSchema<T> ? unknown : never) extends infer V ? V : unknown : unknown;
|
|
40
|
+
/**
|
|
41
|
+
* Narrow an inferred value type to the sub-shape at `P`, or return
|
|
42
|
+
* the original value type when `P` is `undefined` (no path supplied).
|
|
43
|
+
*/
|
|
44
|
+
type NarrowAtPath<V, P extends string | undefined> = P extends string ? TypeAtPath<V, P> : V;
|
|
45
|
+
/**
|
|
46
|
+
* Public alias mapping a schema input to the rendered value type.
|
|
47
|
+
*
|
|
48
|
+
* Picks the OUTPUT side (server → client) of every transform / pipe /
|
|
49
|
+
* codec. For an `<SchemaComponent io="output">` or `<SchemaView
|
|
50
|
+
* io="output">` (both defaults), this is the inferred shape of
|
|
51
|
+
* `value` and the parameter of `onChange`.
|
|
52
|
+
*/
|
|
53
|
+
type InferredOutputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>;
|
|
54
|
+
/**
|
|
55
|
+
* Companion to {@link InferredOutputValue} for `"input"`-mode shapes.
|
|
56
|
+
*
|
|
57
|
+
* Picks the INPUT side (client → server) of every transform / pipe /
|
|
58
|
+
* codec. Surfaces as the inferred shape of `value` / `onChange` when
|
|
59
|
+
* a consumer renders `<SchemaComponent io="input">`. For JSON Schema
|
|
60
|
+
* inputs with `readOnly`/`writeOnly` annotations, the INPUT mode
|
|
61
|
+
* omits properties marked `readOnly: true`.
|
|
62
|
+
*/
|
|
63
|
+
type InferredInputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "input">, P>;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the schema-driven value type for either I/O direction.
|
|
66
|
+
*
|
|
67
|
+
* Thin convenience over {@link InferredOutputValue} /
|
|
68
|
+
* {@link InferredInputValue} so consumers that decide between the
|
|
69
|
+
* two at the type level (e.g. a generic wrapper component) can pass
|
|
70
|
+
* the chosen direction as a type argument rather than branch on it
|
|
71
|
+
* with conditional types. Falls back to `unknown` when the schema's
|
|
72
|
+
* value type cannot be statically inferred, identical to the
|
|
73
|
+
* underlying helpers.
|
|
74
|
+
*/
|
|
75
|
+
type InferredValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> = NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { InferredValue as a, InferredOutputValue as i, InferSchemaValue as n, InferredInputValue as r, InferFields as t };
|
|
@@ -18,11 +18,17 @@ declare const MAX_RENDER_DEPTH = 10;
|
|
|
18
18
|
* and compile-time bounds agree.
|
|
19
19
|
*/
|
|
20
20
|
type MaxRefDepth = 64;
|
|
21
|
+
/** Runtime constant matching the type-level {@link MaxRefDepth} bound. */
|
|
21
22
|
declare const MAX_REF_DEPTH: MaxRefDepth;
|
|
22
23
|
/**
|
|
23
24
|
* Maximum number of `$ref` hops permitted when walking a chain of
|
|
24
25
|
* OpenAPI Path Item Object references. Beyond this a
|
|
25
26
|
* `path-item-ref-too-deep` diagnostic is emitted and resolution stops.
|
|
27
|
+
*
|
|
28
|
+
* Also the default `maxHops` for the generic `resolveRefChain` helper
|
|
29
|
+
* in `core/refChain.ts`, so every ref-chain walker — Path Item refs,
|
|
30
|
+
* Parameter / Response refs, Reference Object chains, etc. — shares
|
|
31
|
+
* the same hop cap.
|
|
26
32
|
*/
|
|
27
33
|
declare const MAX_PATH_ITEM_REF_HOPS = 8;
|
|
28
34
|
//#endregion
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isObject } from "./core/guards.mjs";
|
|
2
2
|
import { appendPointer, emitDiagnostic } from "./core/diagnostics.mjs";
|
|
3
|
+
import { isPrototypePollutingKey } from "./core/uri.mjs";
|
|
3
4
|
import { RECURSIVE_ANCHOR_SENTINEL } from "./core/ref.mjs";
|
|
4
5
|
import { isOpenApi30, isOpenApi31, isSwagger2, matchJsonSchemaDraftUri, readJsonSchemaDialect } from "./core/version.mjs";
|
|
5
6
|
import { SWAGGER_2_METHODS, rewriteSwaggerRefPrefix } from "./core/openapiConstants.mjs";
|
|
@@ -801,7 +802,18 @@ function normaliseSwaggerOperation(operation, doc, path, method, diagnostics) {
|
|
|
801
802
|
const producesResolution = resolveSwaggerContentTypes(operation.produces, doc.produces);
|
|
802
803
|
const produces = producesResolution.types;
|
|
803
804
|
const consumes = consumesResolution.types;
|
|
804
|
-
for (const [key, value] of Object.entries(operation)) if (key !== "parameters" && key !== "responses" && key !== "produces" && key !== "consumes")
|
|
805
|
+
for (const [key, value] of Object.entries(operation)) if (key !== "parameters" && key !== "responses" && key !== "produces" && key !== "consumes") {
|
|
806
|
+
if (isPrototypePollutingKey(key)) {
|
|
807
|
+
emitDiagnostic(diagnostics, {
|
|
808
|
+
code: "prototype-polluting-property",
|
|
809
|
+
message: `Refusing to copy prototype-polluting property name into normalised operation: ${key}`,
|
|
810
|
+
pointer: appendPointer(`/paths/${path}/${method}`, key),
|
|
811
|
+
detail: { propertyName: key }
|
|
812
|
+
});
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
result[key] = value;
|
|
816
|
+
}
|
|
805
817
|
const params = operation.parameters;
|
|
806
818
|
if (Array.isArray(params)) {
|
|
807
819
|
const nonBodyParams = [];
|
|
@@ -1038,6 +1050,15 @@ function normaliseSwaggerParameter(param, doc, diagnostics, pointer = "") {
|
|
|
1038
1050
|
const result = {};
|
|
1039
1051
|
for (const [key, value] of Object.entries(param)) {
|
|
1040
1052
|
if (PARAM_KEYWORDS_LIFTED_INTO_SCHEMA.has(key)) continue;
|
|
1053
|
+
if (isPrototypePollutingKey(key)) {
|
|
1054
|
+
emitDiagnostic(diagnostics, {
|
|
1055
|
+
code: "prototype-polluting-property",
|
|
1056
|
+
message: `Refusing to copy prototype-polluting property name into normalised parameter: ${key}`,
|
|
1057
|
+
pointer: appendPointer(pointer, key),
|
|
1058
|
+
detail: { propertyName: key }
|
|
1059
|
+
});
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1041
1062
|
result[key] = value;
|
|
1042
1063
|
}
|
|
1043
1064
|
if (typeof param.type === "string") if (param.type === "file" && param.in !== "formData") {
|
|
@@ -1186,7 +1207,18 @@ function normaliseSwaggerResponses(responses, doc, produces, producesSource, dia
|
|
|
1186
1207
|
function normaliseSwaggerSingleResponse(response, doc, produces, producesSource = "synthesised", diagnostics, path, method, statusCode) {
|
|
1187
1208
|
const resolved = resolveSwaggerResponse(response, doc);
|
|
1188
1209
|
const normalised = {};
|
|
1189
|
-
for (const [key, value] of Object.entries(resolved)) if (key !== "schema" && key !== "headers")
|
|
1210
|
+
for (const [key, value] of Object.entries(resolved)) if (key !== "schema" && key !== "headers") {
|
|
1211
|
+
if (isPrototypePollutingKey(key)) {
|
|
1212
|
+
emitDiagnostic(diagnostics, {
|
|
1213
|
+
code: "prototype-polluting-property",
|
|
1214
|
+
message: `Refusing to copy prototype-polluting property name into normalised response: ${key}`,
|
|
1215
|
+
pointer: path !== void 0 && method !== void 0 && statusCode !== void 0 ? appendPointer(`/paths/${path}/${method}/responses/${statusCode}`, key) : key,
|
|
1216
|
+
detail: { propertyName: key }
|
|
1217
|
+
});
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
normalised[key] = value;
|
|
1221
|
+
}
|
|
1190
1222
|
const schema = resolved.schema;
|
|
1191
1223
|
if (isObject(schema)) {
|
|
1192
1224
|
const content = {};
|
|
@@ -1232,6 +1264,15 @@ function normaliseSwaggerHeader(header, diagnostics, pointer = "") {
|
|
|
1232
1264
|
const result = {};
|
|
1233
1265
|
for (const [key, value] of Object.entries(header)) {
|
|
1234
1266
|
if (PARAM_KEYWORDS_LIFTED_INTO_SCHEMA.has(key)) continue;
|
|
1267
|
+
if (isPrototypePollutingKey(key)) {
|
|
1268
|
+
emitDiagnostic(diagnostics, {
|
|
1269
|
+
code: "prototype-polluting-property",
|
|
1270
|
+
message: `Refusing to copy prototype-polluting property name into normalised header: ${key}`,
|
|
1271
|
+
pointer: appendPointer(pointer, key),
|
|
1272
|
+
detail: { propertyName: key }
|
|
1273
|
+
});
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1235
1276
|
result[key] = value;
|
|
1236
1277
|
}
|
|
1237
1278
|
if (typeof header.type === "string") result.schema = buildSchemaFromSwaggerParameterShape(header);
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { w as SchemaMeta } from "../types-
|
|
1
|
+
import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
|
|
2
2
|
import { CallbackInfo } from "./parser.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/openapi/ApiCallbacks.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Props accepted by {@link ApiCallbacks}.
|
|
8
|
+
*
|
|
9
|
+
* @group OpenAPI
|
|
10
|
+
*/
|
|
6
11
|
interface ApiCallbacksProps {
|
|
7
12
|
/** Callback definitions for this operation. */
|
|
8
13
|
callbacks: CallbackInfo[];
|
|
9
14
|
/** Optional meta overrides. */
|
|
10
15
|
meta?: SchemaMeta;
|
|
11
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Render OpenAPI callback definitions declared on an operation. Each
|
|
19
|
+
* callback name is displayed alongside its operations. Read-only —
|
|
20
|
+
* intended for documentation rather than interactive editing.
|
|
21
|
+
*
|
|
22
|
+
* @group OpenAPI
|
|
23
|
+
*/
|
|
12
24
|
declare function ApiCallbacks({
|
|
13
25
|
callbacks
|
|
14
26
|
}: ApiCallbacksProps): ReactNode;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/openapi/ApiCallbacks.tsx
|
|
3
|
+
/**
|
|
4
|
+
* Render OpenAPI callback definitions declared on an operation. Each
|
|
5
|
+
* callback name is displayed alongside its operations. Read-only —
|
|
6
|
+
* intended for documentation rather than interactive editing.
|
|
7
|
+
*
|
|
8
|
+
* @group OpenAPI
|
|
9
|
+
*/
|
|
3
10
|
function ApiCallbacks({ callbacks }) {
|
|
4
11
|
if (callbacks.length === 0) return null;
|
|
5
12
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { w as SchemaMeta } from "../types-
|
|
1
|
+
import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
|
|
2
2
|
import { LinkInfo } from "./parser.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/openapi/ApiLinks.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Props accepted by {@link ApiLinks}.
|
|
8
|
+
*
|
|
9
|
+
* @group OpenAPI
|
|
10
|
+
*/
|
|
6
11
|
interface ApiLinksProps {
|
|
7
12
|
/** Link definitions for a response. */
|
|
8
13
|
links: LinkInfo[];
|
|
9
14
|
/** Optional meta overrides. */
|
|
10
15
|
meta?: SchemaMeta;
|
|
11
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Render OpenAPI link definitions attached to a response. Displays each
|
|
19
|
+
* link's name, target operation (`operationId` or `operationRef`),
|
|
20
|
+
* description, and parameter mappings. Read-only.
|
|
21
|
+
*
|
|
22
|
+
* @group OpenAPI
|
|
23
|
+
*/
|
|
12
24
|
declare function ApiLinks({
|
|
13
25
|
links
|
|
14
26
|
}: ApiLinksProps): ReactNode;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/openapi/ApiLinks.tsx
|
|
3
|
+
/**
|
|
4
|
+
* Render OpenAPI link definitions attached to a response. Displays each
|
|
5
|
+
* link's name, target operation (`operationId` or `operationRef`),
|
|
6
|
+
* description, and parameter mappings. Read-only.
|
|
7
|
+
*
|
|
8
|
+
* @group OpenAPI
|
|
9
|
+
*/
|
|
3
10
|
function ApiLinks({ links }) {
|
|
4
11
|
if (links.length === 0) return null;
|
|
5
12
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { w as SchemaMeta } from "../types-
|
|
1
|
+
import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
|
|
2
2
|
import { HeaderInfo } from "./parser.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/openapi/ApiResponseHeaders.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Props accepted by {@link ApiResponseHeaders}.
|
|
8
|
+
*
|
|
9
|
+
* @group OpenAPI
|
|
10
|
+
*/
|
|
6
11
|
interface ApiResponseHeadersProps {
|
|
7
12
|
/** Header definitions for a response. */
|
|
8
13
|
headers: Map<string, HeaderInfo>;
|
|
9
14
|
/** Optional meta overrides. */
|
|
10
15
|
meta?: SchemaMeta;
|
|
11
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Render the header definitions declared on an OpenAPI response. Shows
|
|
19
|
+
* each header's name, description, type, required flag, and deprecation
|
|
20
|
+
* marker. Read-only.
|
|
21
|
+
*
|
|
22
|
+
* @group OpenAPI
|
|
23
|
+
*/
|
|
12
24
|
declare function ApiResponseHeaders({
|
|
13
25
|
headers
|
|
14
26
|
}: ApiResponseHeadersProps): ReactNode;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/openapi/ApiResponseHeaders.tsx
|
|
3
|
+
/**
|
|
4
|
+
* Render the header definitions declared on an OpenAPI response. Shows
|
|
5
|
+
* each header's name, description, type, required flag, and deprecation
|
|
6
|
+
* marker. Read-only.
|
|
7
|
+
*
|
|
8
|
+
* @group OpenAPI
|
|
9
|
+
*/
|
|
3
10
|
function ApiResponseHeaders({ headers }) {
|
|
4
11
|
if (headers.size === 0) return null;
|
|
5
12
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { w as SchemaMeta } from "../types-
|
|
1
|
+
import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
|
|
2
2
|
import { SecurityRequirement, SecurityScheme } from "./parser.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/openapi/ApiSecurity.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Props accepted by {@link ApiSecurity}.
|
|
8
|
+
*
|
|
9
|
+
* @group OpenAPI
|
|
10
|
+
*/
|
|
6
11
|
interface ApiSecurityProps {
|
|
7
12
|
/** Security requirements for this operation. */
|
|
8
13
|
requirements: SecurityRequirement[];
|
|
@@ -11,6 +16,14 @@ interface ApiSecurityProps {
|
|
|
11
16
|
/** Optional meta overrides. */
|
|
12
17
|
meta?: SchemaMeta;
|
|
13
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Render the security requirements that apply to an OpenAPI operation,
|
|
21
|
+
* resolved against the document's component security schemes. Displays
|
|
22
|
+
* each scheme's type, description, location, scopes, and the full set
|
|
23
|
+
* of OAuth 2 flows when applicable. Read-only.
|
|
24
|
+
*
|
|
25
|
+
* @group OpenAPI
|
|
26
|
+
*/
|
|
14
27
|
declare function ApiSecurity({
|
|
15
28
|
requirements,
|
|
16
29
|
schemes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
+
import { isSafeHyperlink } from "../core/uri.mjs";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
//#region src/openapi/ApiSecurity.tsx
|
|
4
5
|
/**
|
|
@@ -53,6 +54,14 @@ function extractFlows(flows) {
|
|
|
53
54
|
}
|
|
54
55
|
return result;
|
|
55
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Render the security requirements that apply to an OpenAPI operation,
|
|
59
|
+
* resolved against the document's component security schemes. Displays
|
|
60
|
+
* each scheme's type, description, location, scopes, and the full set
|
|
61
|
+
* of OAuth 2 flows when applicable. Read-only.
|
|
62
|
+
*
|
|
63
|
+
* @group OpenAPI
|
|
64
|
+
*/
|
|
56
65
|
function ApiSecurity({ requirements, schemes }) {
|
|
57
66
|
if (requirements.length === 0) return null;
|
|
58
67
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -105,11 +114,14 @@ function SchemeDetails({ scheme }) {
|
|
|
105
114
|
"data-security-apikey-in": true,
|
|
106
115
|
children: scheme.location
|
|
107
116
|
}),
|
|
108
|
-
scheme.openIdConnectUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
117
|
+
scheme.openIdConnectUrl !== void 0 && (isSafeHyperlink(scheme.openIdConnectUrl) ? /* @__PURE__ */ jsx("a", {
|
|
109
118
|
"data-security-openid-url": true,
|
|
110
119
|
href: scheme.openIdConnectUrl,
|
|
111
120
|
children: scheme.openIdConnectUrl
|
|
112
|
-
}),
|
|
121
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
122
|
+
"data-security-openid-url": true,
|
|
123
|
+
children: scheme.openIdConnectUrl
|
|
124
|
+
})),
|
|
113
125
|
flows.length > 0 && /* @__PURE__ */ jsx("section", {
|
|
114
126
|
"data-security-flows": true,
|
|
115
127
|
children: flows.map((flow) => /* @__PURE__ */ jsx(FlowDetails, { flow }, flow.name))
|
|
@@ -124,21 +136,30 @@ function FlowDetails({ flow }) {
|
|
|
124
136
|
"data-security-flow-name": true,
|
|
125
137
|
children: flow.name
|
|
126
138
|
}),
|
|
127
|
-
flow.authorizationUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
139
|
+
flow.authorizationUrl !== void 0 && (isSafeHyperlink(flow.authorizationUrl) ? /* @__PURE__ */ jsx("a", {
|
|
128
140
|
"data-security-flow-authorization-url": true,
|
|
129
141
|
href: flow.authorizationUrl,
|
|
130
142
|
children: flow.authorizationUrl
|
|
131
|
-
}),
|
|
132
|
-
|
|
143
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
144
|
+
"data-security-flow-authorization-url": true,
|
|
145
|
+
children: flow.authorizationUrl
|
|
146
|
+
})),
|
|
147
|
+
flow.tokenUrl !== void 0 && (isSafeHyperlink(flow.tokenUrl) ? /* @__PURE__ */ jsx("a", {
|
|
133
148
|
"data-security-flow-token-url": true,
|
|
134
149
|
href: flow.tokenUrl,
|
|
135
150
|
children: flow.tokenUrl
|
|
136
|
-
}),
|
|
137
|
-
|
|
151
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
152
|
+
"data-security-flow-token-url": true,
|
|
153
|
+
children: flow.tokenUrl
|
|
154
|
+
})),
|
|
155
|
+
flow.refreshUrl !== void 0 && (isSafeHyperlink(flow.refreshUrl) ? /* @__PURE__ */ jsx("a", {
|
|
138
156
|
"data-security-flow-refresh-url": true,
|
|
139
157
|
href: flow.refreshUrl,
|
|
140
158
|
children: flow.refreshUrl
|
|
141
|
-
}),
|
|
159
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
160
|
+
"data-security-flow-refresh-url": true,
|
|
161
|
+
children: flow.refreshUrl
|
|
162
|
+
})),
|
|
142
163
|
flow.scopes.size > 0 && /* @__PURE__ */ jsx("dl", {
|
|
143
164
|
"data-security-flow-scopes": true,
|
|
144
165
|
children: [...flow.scopes.entries()].map(([name, description]) => /* @__PURE__ */ jsxs("div", {
|
|
@@ -27,6 +27,37 @@
|
|
|
27
27
|
* Resolver function for external documents.
|
|
28
28
|
* Called with the URI portion of an external $ref (everything before `#`).
|
|
29
29
|
* Returns the parsed JSON document.
|
|
30
|
+
*
|
|
31
|
+
* ### Security warning — SSRF and local-file disclosure
|
|
32
|
+
*
|
|
33
|
+
* Consumers MUST validate the URI before fetching the target document.
|
|
34
|
+
* The bundler hands the resolver the raw `$ref` URI from the OpenAPI
|
|
35
|
+
* document — which is typically user-controlled — and any network or
|
|
36
|
+
* filesystem access the resolver performs runs with the host
|
|
37
|
+
* application's full privileges. An attacker-crafted document that
|
|
38
|
+
* references an internal endpoint or a local filesystem path will
|
|
39
|
+
* happily exfiltrate or expose data the application never intended to
|
|
40
|
+
* surface.
|
|
41
|
+
*
|
|
42
|
+
* At a minimum the resolver should:
|
|
43
|
+
*
|
|
44
|
+
* - Refuse non-`https:` schemes by default. Permit `http:` only on an
|
|
45
|
+
* explicit allow-list. Refuse `file:`, `data:`, `javascript:`,
|
|
46
|
+
* `ftp:`, `gopher:`, and every other scheme outright.
|
|
47
|
+
* - Resolve the URI's hostname and refuse loopback addresses
|
|
48
|
+
* (`127.0.0.0/8`, `::1`), link-local addresses (`169.254.0.0/16`,
|
|
49
|
+
* `fe80::/10`), private ranges (`10.0.0.0/8`, `172.16.0.0/12`,
|
|
50
|
+
* `192.168.0.0/16`, `fc00::/7`), and cloud-metadata IPs
|
|
51
|
+
* (`169.254.169.254`, `fd00:ec2::254`).
|
|
52
|
+
* - Apply a strict allow-list of permitted hosts where possible.
|
|
53
|
+
* - Set request timeouts and a maximum response size.
|
|
54
|
+
* - Disable HTTP redirects, or re-validate the redirected URL against
|
|
55
|
+
* the same denylist before following.
|
|
56
|
+
* - Reject responses that are not `application/json` or
|
|
57
|
+
* `application/yaml`.
|
|
58
|
+
*
|
|
59
|
+
* The bundler itself performs no validation — that responsibility sits
|
|
60
|
+
* exclusively with the resolver implementation supplied by the caller.
|
|
30
61
|
*/
|
|
31
62
|
type BundleResolver = (uri: string) => unknown;
|
|
32
63
|
/**
|