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.
Files changed (105) hide show
  1. package/README.md +38 -16
  2. package/dist/core/adapter.d.mts +213 -3
  3. package/dist/core/adapter.mjs +21 -2
  4. package/dist/core/constraintHint.d.mts +15 -0
  5. package/dist/core/constraintHint.mjs +24 -0
  6. package/dist/core/constraints.d.mts +34 -2
  7. package/dist/core/constraints.mjs +33 -1
  8. package/dist/core/cssClasses.d.mts +1 -0
  9. package/dist/core/diagnostics.d.mts +1 -1
  10. package/dist/core/errors.d.mts +1 -1
  11. package/dist/core/errors.mjs +22 -12
  12. package/dist/core/fieldOrder.d.mts +1 -1
  13. package/dist/core/formats.d.mts +7 -1
  14. package/dist/core/formats.mjs +6 -0
  15. package/dist/core/idPath.d.mts +35 -5
  16. package/dist/core/idPath.mjs +79 -7
  17. package/dist/core/inferValue.d.mts +2 -0
  18. package/dist/core/inferValue.mjs +1 -0
  19. package/dist/core/limits.d.mts +1 -1
  20. package/dist/core/limits.mjs +6 -0
  21. package/dist/core/merge.d.mts +22 -1
  22. package/dist/core/merge.mjs +66 -3
  23. package/dist/core/normalise.d.mts +17 -2
  24. package/dist/core/normalise.mjs +1 -1
  25. package/dist/core/openapi30.mjs +1 -1
  26. package/dist/core/openapiConstants.d.mts +1 -0
  27. package/dist/core/ref.d.mts +1 -1
  28. package/dist/core/refChain.d.mts +3 -4
  29. package/dist/core/refChain.mjs +2 -3
  30. package/dist/core/renderer.d.mts +199 -2
  31. package/dist/core/renderer.mjs +5 -0
  32. package/dist/core/swagger2.d.mts +1 -1
  33. package/dist/core/swagger2.mjs +1 -1
  34. package/dist/core/typeInference.d.mts +3 -3
  35. package/dist/core/types.d.mts +1 -1
  36. package/dist/core/types.mjs +17 -0
  37. package/dist/core/unionMatch.d.mts +1 -1
  38. package/dist/core/uri.d.mts +12 -4
  39. package/dist/core/uri.mjs +30 -4
  40. package/dist/core/version.d.mts +1 -1
  41. package/dist/core/walkBuilders.d.mts +63 -6
  42. package/dist/core/walkBuilders.mjs +33 -1
  43. package/dist/core/walker.d.mts +14 -1
  44. package/dist/core/walker.mjs +18 -0
  45. package/dist/{diagnostics-Cbwak-ZX.d.mts → diagnostics-BTrm3O6J.d.mts} +9 -1
  46. package/dist/{errors-DQSIK4n1.d.mts → errors-Dki7tji4.d.mts} +23 -13
  47. package/dist/html/a11y.d.mts +3 -7
  48. package/dist/html/a11y.mjs +1 -16
  49. package/dist/html/html.d.mts +11 -0
  50. package/dist/html/html.mjs +11 -0
  51. package/dist/html/renderToHtml.d.mts +45 -12
  52. package/dist/html/renderToHtml.mjs +20 -4
  53. package/dist/html/renderToHtmlStream.d.mts +63 -18
  54. package/dist/html/renderToHtmlStream.mjs +34 -8
  55. package/dist/html/renderers.d.mts +6 -31
  56. package/dist/html/renderers.mjs +45 -91
  57. package/dist/html/streamRenderers.d.mts +31 -3
  58. package/dist/html/streamRenderers.mjs +41 -8
  59. package/dist/inferValue-PPXWJpbN.d.mts +77 -0
  60. package/dist/{limits-DJhgx5Ay.d.mts → limits-x4OiyJxh.d.mts} +6 -0
  61. package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
  62. package/dist/openapi/ApiCallbacks.d.mts +13 -1
  63. package/dist/openapi/ApiCallbacks.mjs +7 -0
  64. package/dist/openapi/ApiLinks.d.mts +13 -1
  65. package/dist/openapi/ApiLinks.mjs +7 -0
  66. package/dist/openapi/ApiResponseHeaders.d.mts +13 -1
  67. package/dist/openapi/ApiResponseHeaders.mjs +7 -0
  68. package/dist/openapi/ApiSecurity.d.mts +14 -1
  69. package/dist/openapi/ApiSecurity.mjs +29 -8
  70. package/dist/openapi/bundle.d.mts +31 -0
  71. package/dist/openapi/components.d.mts +135 -20
  72. package/dist/openapi/components.mjs +90 -15
  73. package/dist/openapi/parser.d.mts +140 -13
  74. package/dist/openapi/parser.mjs +84 -12
  75. package/dist/openapi/resolve.d.mts +42 -47
  76. package/dist/openapi/resolve.mjs +62 -56
  77. package/dist/react/SchemaComponent.d.mts +90 -88
  78. package/dist/react/SchemaComponent.mjs +74 -2
  79. package/dist/react/SchemaErrorBoundary.d.mts +18 -1
  80. package/dist/react/SchemaErrorBoundary.mjs +13 -1
  81. package/dist/react/SchemaView.d.mts +39 -11
  82. package/dist/react/SchemaView.mjs +23 -6
  83. package/dist/react/a11y.d.mts +74 -7
  84. package/dist/react/a11y.mjs +67 -6
  85. package/dist/react/fieldPath.d.mts +16 -1
  86. package/dist/react/fieldPath.mjs +25 -1
  87. package/dist/react/fieldShell.d.mts +49 -0
  88. package/dist/react/fieldShell.mjs +37 -0
  89. package/dist/react/headless.d.mts +1 -1
  90. package/dist/react/headlessRenderers.d.mts +13 -2
  91. package/dist/react/headlessRenderers.mjs +134 -54
  92. package/dist/{ref-TdeMfaV_.d.mts → ref-DdsbekXX.d.mts} +33 -1
  93. package/dist/themes/mantine.d.mts +54 -12
  94. package/dist/themes/mantine.mjs +195 -140
  95. package/dist/themes/mui.d.mts +64 -11
  96. package/dist/themes/mui.mjs +277 -213
  97. package/dist/themes/radix.d.mts +67 -15
  98. package/dist/themes/radix.mjs +235 -170
  99. package/dist/themes/shadcn.d.mts +25 -1
  100. package/dist/themes/shadcn.mjs +112 -91
  101. package/dist/{types-BTB73MB8.d.mts → types-BrYbjC7_.d.mts} +30 -0
  102. package/dist/{version-ZzL5R6cS.d.mts → version-DL8U5RuA.d.mts} +6 -0
  103. package/package.json +8 -1
  104. package/dist/adapter-DqlAnZ_w.d.mts +0 -172
  105. package/dist/renderer-Ul9taFYp.d.mts +0 -169
@@ -1,13 +1,41 @@
1
- import { j as WalkedField } from "../types-BTB73MB8.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { o as HtmlResolver } from "../renderer-Ul9taFYp.mjs";
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(key, field.constraints);
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", { class: SC_CLASSES.label }, key), raw(childHtml)));
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 = panelId(path);
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: tabId(path, i),
251
- "aria-selected": i === activeIndex ? "true" : void 0,
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": tabId(path, activeIndex)
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") result[key] = value;
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") normalised[key] = value;
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-BTB73MB8.mjs";
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-BTB73MB8.mjs";
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-BTB73MB8.mjs";
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-BTB73MB8.mjs";
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
- flow.tokenUrl !== void 0 && /* @__PURE__ */ jsx("a", {
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
- flow.refreshUrl !== void 0 && /* @__PURE__ */ jsx("a", {
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
  /**