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
@@ -3,9 +3,9 @@ import "../core/limits.mjs";
3
3
  import { emitDiagnostic } from "../core/diagnostics.mjs";
4
4
  import { isPrototypePollutingKey } from "../core/uri.mjs";
5
5
  import { detectOpenApiVersion } from "../core/version.mjs";
6
- import { o as normaliseOpenApiSchemas, r as documentContainsKeyword } from "../normalise-Db1xaxgx.mjs";
6
+ import { o as normaliseOpenApiSchemas, r as documentContainsKeyword } from "../normalise-DB-Xtjmn.mjs";
7
7
  import { resolveRefChain } from "../core/refChain.mjs";
8
- import { getParameters, getRequestBody, getResponses, listAllOperations, parseOpenApiDocument } from "./parser.mjs";
8
+ import { extractParameters, extractRequestBody, extractResponses, listAllOperations, parseOpenApiDocument } from "./parser.mjs";
9
9
  //#region src/openapi/resolve.ts
10
10
  /**
11
11
  * OpenAPI document resolution and caching.
@@ -24,7 +24,7 @@ const docCache = /* @__PURE__ */ new WeakMap();
24
24
  * keywords (`nullable`, `discriminator`, `example`), OpenAPI 3.1.x
25
25
  * `discriminator`, and Swagger 2.0 documents are all converted to
26
26
  * canonical Draft 2020-12 form. The parser and downstream extractors
27
- * (`getRequestBody`, `getResponses`, etc.) then observe schemas in the
27
+ * (`extractRequestBody`, `extractResponses`, etc.) then observe schemas in the
28
28
  * same form `<SchemaComponent>` does, keeping the OpenAPI components on
29
29
  * the same pipeline as the top-level adapter.
30
30
  *
@@ -314,16 +314,20 @@ function extractPathItemInfo(pathItem) {
314
314
  };
315
315
  }
316
316
  /**
317
- * Resolve an operation against an already-parsed document. Throws if
318
- * the operation is not found.
317
+ * Resolve an operation from an OpenAPI document by path and method.
318
+ * Throws if the operation is not found.
319
319
  *
320
- * Used by callers that have already obtained a parsed document via
321
- * {@link getParsed} most importantly the React components, which
322
- * supply `diagnostics` to `getParsed` and must avoid re-running the
323
- * normalisation pipeline (every re-run would emit each diagnostic
324
- * again into the sink).
320
+ * Accepts either a raw document (parsed lazily via {@link getParsed}'s
321
+ * WeakMap cache) or an already-parsed {@link OpenApiDocument}. Callers
322
+ * that have a parsed document at hand can pass it directly to avoid
323
+ * an extra cache lookup; everyone else trusts the cache.
324
+ *
325
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
326
+ * events surface to the caller's sink exactly once per `(doc, sink)`
327
+ * pair, no matter how many times this function is called.
325
328
  */
326
- function resolveOperationFromParsed(parsed, path, method, diagnostics) {
329
+ function resolveOperation(doc, path, method, diagnostics) {
330
+ const parsed = ensureParsed(doc, diagnostics);
327
331
  const pathItemNode = lookupPathItemNode(parsed, path, diagnostics);
328
332
  const operation = listAllOperations(parsed).find((op) => op.path === path && op.method === method);
329
333
  if (operation === void 0) throw new Error(`Operation not found: ${method.toUpperCase()} ${path}`);
@@ -331,79 +335,81 @@ function resolveOperationFromParsed(parsed, path, method, diagnostics) {
331
335
  return {
332
336
  operation,
333
337
  pathItem: extractPathItemInfo(pathItemNode),
334
- parameters: getParameters(parsed, path, method),
335
- requestBody: getRequestBody(parsed, path, method),
336
- responses: getResponses(parsed, path, method)
338
+ parameters: extractParameters(parsed, path, method),
339
+ requestBody: extractRequestBody(parsed, path, method),
340
+ responses: extractResponses(parsed, path, method)
337
341
  };
338
342
  }
339
343
  /**
340
- * Resolve an operation from an OpenAPI document by path and method.
341
- * Throws if the operation is not found.
342
- *
343
- * `diagnostics` is forwarded to {@link getParsed} so normalisation
344
- * events surface to the caller's sink.
344
+ * Coerce the first argument of every `resolveX` function either a
345
+ * raw OpenAPI document or an already-parsed {@link OpenApiDocument} —
346
+ * into a parsed view. Distinguishes the two by the presence of the
347
+ * `schemas` map on the parsed shape; falls through to {@link getParsed}
348
+ * for raw documents (which itself short-circuits on a WeakMap cache).
345
349
  */
346
- function resolveOperation(doc, path, method, diagnostics) {
347
- return resolveOperationFromParsed(getParsed(doc, diagnostics), path, method, diagnostics);
350
+ function ensureParsed(doc, diagnostics) {
351
+ if (isParsedDocument(doc)) {
352
+ const cached = docCache.get(doc.doc);
353
+ if (cached !== void 0 && (diagnostics?.diagnostics !== void 0 || diagnostics?.strict === true)) replayCapturedDiagnostics(cached, diagnostics);
354
+ return doc;
355
+ }
356
+ return getParsed(doc, diagnostics);
348
357
  }
349
358
  /**
350
- * Resolve parameters against an already-parsed document. See
351
- * {@link resolveOperationFromParsed} for the rationale.
359
+ * Decide whether `value` is an already-parsed {@link OpenApiDocument}.
360
+ * The parsed shape carries a `schemas` Map alongside the raw `doc`
361
+ * object; a raw OpenAPI document has neither — its keys are
362
+ * `openapi`/`swagger`, `info`, `paths`, etc.
352
363
  */
353
- function resolveParametersFromParsed(parsed, path, method) {
354
- return getParameters(parsed, path, method);
364
+ function isParsedDocument(value) {
365
+ return isObject(value.doc) && value.schemas instanceof Map;
355
366
  }
356
367
  /**
357
- * Resolve parameters for an operation. Returns empty array if none.
368
+ * Resolve parameters for an operation. Returns an empty array if none.
358
369
  *
359
- * `diagnostics` is forwarded to {@link getParsed} so normalisation
360
- * events surface to the caller's sink.
370
+ * Accepts either a raw document or an already-parsed
371
+ * {@link OpenApiDocument}. `diagnostics` is forwarded to
372
+ * {@link getParsed} so normalisation events surface to the caller's
373
+ * sink.
361
374
  */
362
375
  function resolveParameters(doc, path, method, diagnostics) {
363
- return resolveParametersFromParsed(getParsed(doc, diagnostics), path, method);
376
+ return extractParameters(ensureParsed(doc, diagnostics), path, method);
364
377
  }
365
378
  /**
366
- * Resolve a request body against an already-parsed document. See
367
- * {@link resolveOperationFromParsed} for the rationale.
368
- */
369
- function resolveRequestBodyFromParsed(parsed, path, method) {
370
- return getRequestBody(parsed, path, method);
371
- }
372
- /**
373
- * Resolve request body for an operation. Returns undefined if none.
379
+ * Resolve the request body for an operation. Returns `undefined` if
380
+ * the operation declares no request body.
374
381
  *
375
- * `diagnostics` is forwarded to {@link getParsed} so normalisation
376
- * events surface to the caller's sink.
382
+ * Accepts either a raw document or an already-parsed
383
+ * {@link OpenApiDocument}. `diagnostics` is forwarded to
384
+ * {@link getParsed} so normalisation events surface to the caller's
385
+ * sink.
377
386
  */
378
387
  function resolveRequestBody(doc, path, method, diagnostics) {
379
- return resolveRequestBodyFromParsed(getParsed(doc, diagnostics), path, method);
380
- }
381
- /**
382
- * Resolve a specific response against an already-parsed document. See
383
- * {@link resolveOperationFromParsed} for the rationale.
384
- */
385
- function resolveResponseFromParsed(parsed, path, method, statusCode) {
386
- const response = getResponses(parsed, path, method).find((r) => r.statusCode === statusCode);
387
- if (response === void 0) throw new Error(`Response not found: ${statusCode}`);
388
- return response;
388
+ return extractRequestBody(ensureParsed(doc, diagnostics), path, method);
389
389
  }
390
390
  /**
391
391
  * Resolve a specific response by status code. Throws if not found.
392
392
  *
393
- * `diagnostics` is forwarded to {@link getParsed} so normalisation
394
- * events surface to the caller's sink.
393
+ * Accepts either a raw document or an already-parsed
394
+ * {@link OpenApiDocument}. `diagnostics` is forwarded to
395
+ * {@link getParsed} so normalisation events surface to the caller's
396
+ * sink.
395
397
  */
396
398
  function resolveResponse(doc, path, method, statusCode, diagnostics) {
397
- return resolveResponseFromParsed(getParsed(doc, diagnostics), path, method, statusCode);
399
+ const response = extractResponses(ensureParsed(doc, diagnostics), path, method).find((r) => r.statusCode === statusCode);
400
+ if (response === void 0) throw new Error(`Response not found: ${statusCode}`);
401
+ return response;
398
402
  }
399
403
  /**
400
404
  * Resolve all responses for an operation.
401
405
  *
402
- * `diagnostics` is forwarded to {@link getParsed} so normalisation
403
- * events surface to the caller's sink.
406
+ * Accepts either a raw document or an already-parsed
407
+ * {@link OpenApiDocument}. `diagnostics` is forwarded to
408
+ * {@link getParsed} so normalisation events surface to the caller's
409
+ * sink.
404
410
  */
405
411
  function resolveResponses(doc, path, method, diagnostics) {
406
- return getResponses(getParsed(doc, diagnostics), path, method);
412
+ return extractResponses(ensureParsed(doc, diagnostics), path, method);
407
413
  }
408
414
  //#endregion
409
- export { getParsed, resolveOperation, resolveOperationFromParsed, resolveParameters, resolveParametersFromParsed, resolveRequestBody, resolveRequestBodyFromParsed, resolveResponse, resolveResponseFromParsed, resolveResponses, toDoc };
415
+ export { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
@@ -1,25 +1,36 @@
1
- import { d as FieldOverrides, j as WalkedField, u as FieldOverride, w as SchemaMeta } from "../types-BTB73MB8.mjs";
2
- import { t as Diagnostic } from "../diagnostics-Cbwak-ZX.mjs";
3
- import { i as SchemaIoSide } from "../adapter-DqlAnZ_w.mjs";
4
- import { t as SchemaError } from "../errors-DQSIK4n1.mjs";
5
- import { l as RenderProps, r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
6
- import { FromJSONSchema, FromJSONSchemaMode, IsSwagger2Doc, PathOfType, RejectUnrepresentableZod, ResolveOpenAPIRef, TypeAtPath, __SchemaInferenceFellBack } from "../core/typeInference.mjs";
1
+ import { j as WalkedField, w as SchemaMeta } from "../types-BrYbjC7_.mjs";
2
+ import { t as Diagnostic } from "../diagnostics-BTrm3O6J.mjs";
3
+ import { SchemaIoSide } from "../core/adapter.mjs";
4
+ import { ComponentResolver, RenderProps, WidgetMap } from "../core/renderer.mjs";
5
+ import { t as SchemaError } from "../errors-Dki7tji4.mjs";
6
+ import { FromJSONSchema, PathOfType, RejectUnrepresentableZod } from "../core/typeInference.mjs";
7
+ import { a as InferredValue, i as InferredOutputValue, n as InferSchemaValue, r as InferredInputValue, t as InferFields } from "../inferValue-PPXWJpbN.mjs";
7
8
  import { z } from "zod";
8
- import { ReactNode } from "react";
9
9
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
10
+ import { ReactNode } from "react";
10
11
 
11
12
  //#region src/react/SchemaComponent.d.ts
12
13
  /**
13
- * Widget map maps component hints (from `.meta({ component })`) to render
14
- * functions. Scoped at three levels:
14
+ * Provide a theme resolver and scoped widgets to every `<SchemaComponent>`
15
+ * and `<SchemaView>` rendered inside the subtree.
15
16
  *
16
- * 1. **Per-instance** `widgets` prop on `<SchemaComponent>`
17
- * 2. **Context-scoped**`widgets` prop on `<SchemaProvider>`
18
- * 3. **Global** `registerWidget()` (app-wide defaults)
17
+ * Wrap an application (or a region of it) with `<SchemaProvider>` so a
18
+ * single themetypically one of the bundled adapters
19
+ * (`shadcnResolver`, `muiResolver`, `mantineResolver`, `radixResolver`)
20
+ * or a custom one — drives every schema render. Without a provider,
21
+ * schema-components fall back to the headless HTML renderer.
19
22
  *
20
- * Resolution order: instance → context → global → resolver → headless.
23
+ * @group Components
24
+ * @example
25
+ * ```tsx
26
+ * import { SchemaProvider } from "schema-components/react/SchemaComponent";
27
+ * import { shadcnResolver } from "schema-components/themes/shadcn";
28
+ *
29
+ * <SchemaProvider resolver={shadcnResolver}>
30
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
31
+ * </SchemaProvider>
32
+ * ```
21
33
  */
22
- type WidgetMap = ReadonlyMap<string, (props: RenderProps) => unknown>;
23
34
  declare function SchemaProvider({
24
35
  resolver,
25
36
  widgets,
@@ -37,65 +48,25 @@ declare function SchemaProvider({
37
48
  * or `<SchemaProvider>` instead.
38
49
  */
39
50
  declare function registerWidget(name: string, render: (props: RenderProps) => unknown): void;
40
- type InferFields<T, Ref extends string | undefined> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
41
- openapi: unknown;
42
- } ? 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>;
43
51
  /**
44
- * Infer the data type carried by the schema input.
45
- *
46
- * Mirrors {@link InferFields}'s dispatch order: Zod schema `z.infer`,
47
- * OpenAPI doc + ref `ResolveOpenAPIRef`, plain JSON Schema object →
48
- * `FromJSONSchema`, everything else → `unknown`. The `Mode` parameter
49
- * is plumbed through to `FromJSONSchema` / `ResolveOpenAPIRef` so
50
- * `readOnly` / `writeOnly` keywords participate in the inferred
51
- * object shape — `"output"` for the rendered value, `"input"` for the
52
- * `onChange` argument.
52
+ * Clear every globally registered widget. Intended for test isolation —
53
+ * `registerWidget` writes to module-level state and that state otherwise
54
+ * leaks across test cases, making the test suite order-dependent. Tests
55
+ * should call this from an `afterEach` hook.
53
56
  *
54
- * When the schema's value type cannot be statically determined (e.g.
55
- * a runtime `Record<string, unknown>` JSON Schema, or an OpenAPI doc
56
- * without a ref), the result falls back to `unknown` so callers can
57
- * still supply arbitrary values.
57
+ * @internal
58
58
  */
59
- 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 {
60
- openapi: unknown;
61
- } ? 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;
59
+ declare function __clearGlobalWidgets(): void;
62
60
  /**
63
- * Narrow an inferred value type to the sub-shape at `P`, or return
64
- * the original value type when `P` is `undefined` (no path supplied).
65
- */
66
- type NarrowAtPath<V, P extends string | undefined> = P extends string ? TypeAtPath<V, P> : V;
67
- /**
68
- * Public alias mapping a schema input to the rendered value type.
61
+ * Props accepted by {@link SchemaComponent}.
69
62
  *
70
- * Picks the OUTPUT side (server client) of every transform / pipe /
71
- * codec. For an `<SchemaComponent io="output">` or `<SchemaView
72
- * io="output">` (both defaults), this is the inferred shape of
73
- * `value` and the parameter of `onChange`.
74
- */
75
- type InferredOutputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>;
76
- /**
77
- * Companion to {@link InferredOutputValue} for `"input"`-mode shapes.
63
+ * The generic parameters carry the inferred schema shape through to
64
+ * `value`, `onChange`, and `fields` so a typed `schema` prop drives
65
+ * typed props on the rest of the component.
78
66
  *
79
- * Picks the INPUT side (client → server) of every transform / pipe /
80
- * codec. Surfaces as the inferred shape of `value` / `onChange` when
81
- * a consumer renders `<SchemaComponent io="input">`. For JSON Schema
82
- * inputs with `readOnly`/`writeOnly` annotations, the INPUT mode
83
- * omits properties marked `readOnly: true`.
67
+ * @group Components
84
68
  */
85
- type InferredInputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "input">, P>;
86
- /**
87
- * Resolve the schema-driven value type for either I/O direction.
88
- *
89
- * Thin convenience over {@link InferredOutputValue} /
90
- * {@link InferredInputValue} so consumers that decide between the
91
- * two at the type level (e.g. a generic wrapper component) can pass
92
- * the chosen direction as a type argument rather than branch on it
93
- * with conditional types. Falls back to `unknown` when the schema's
94
- * value type cannot be statically inferred, identical to the
95
- * underlying helpers.
96
- */
97
- type InferredValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> = NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
98
- interface SchemaComponentProps<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
69
+ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
99
70
  /**
100
71
  * Zod schema, JSON Schema object, or OpenAPI document.
101
72
  *
@@ -109,21 +80,6 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
109
80
  schema: RejectUnrepresentableZod<T>;
110
81
  /** For OpenAPI: a ref string like "#/components/schemas/User" or "/users/post". */
111
82
  ref?: Ref;
112
- /**
113
- * Optional dot-separated path used purely for type narrowing.
114
- * When the schema is typed, the path is restricted to the valid
115
- * dot-paths reachable through the schema's inferred value.
116
- *
117
- * RUNTIME CAVEAT: this prop is currently type-level only. The
118
- * render pipeline still operates on the root schema; sub-path
119
- * value resolution is provided by the dedicated `<SchemaField>`
120
- * component, which already implements `resolvePath` /
121
- * `setNestedValue`. The `path` prop here documents intent at the
122
- * type level (and prepares the API surface) so consumers can
123
- * declare narrow typed wrappers without runtime regressions. Use
124
- * `<SchemaField>` when a sub-path render is required at runtime.
125
- */
126
- path?: P;
127
83
  /**
128
84
  * Which side of every transform / pipe / codec to render.
129
85
  *
@@ -148,9 +104,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
148
104
  /**
149
105
  * Current value to render. Typed against `InferSchemaValue<T,
150
106
  * Ref, Mode>` so the prop tracks the schema's inferred shape for
151
- * the chosen `io` direction. Narrowed further by `P` when a
152
- * `path` is supplied (currently type-level only — see the JSDoc
153
- * on {@link SchemaComponentProps.path}).
107
+ * the chosen `io` direction.
154
108
  *
155
109
  * Falls back to `unknown` when the schema's value type cannot be
156
110
  * statically inferred (runtime `Record<string, unknown>` JSON
@@ -165,7 +119,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
165
119
  * <SchemaComponent schema={userSchema} value={user} readOnly />
166
120
  * ```
167
121
  */
168
- value?: NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
122
+ value?: InferSchemaValue<T, Ref, Mode>;
169
123
  /**
170
124
  * Called when the value changes (editable fields). The parameter
171
125
  * shares the same shape as {@link SchemaComponentProps.value} so
@@ -175,7 +129,7 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
175
129
  * Falls back to `unknown` for schemas whose value type cannot be
176
130
  * statically inferred — see {@link SchemaComponentProps.value}.
177
131
  */
178
- onChange?: (value: NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>) => void;
132
+ onChange?: (value: InferSchemaValue<T, Ref, Mode>) => void;
179
133
  /** Run schema.safeParse() on change and surface errors via onValidationError. */
180
134
  validate?: boolean;
181
135
  /** Called with the ZodError when validation fails. */
@@ -206,7 +160,30 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
206
160
  */
207
161
  idPrefix?: string;
208
162
  }
209
- declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(props: SchemaComponentProps<T, Ref, P, Mode>): ReactNode;
163
+ /**
164
+ * Render an editable (or read-only) UI from a Zod schema, JSON Schema, or
165
+ * OpenAPI document.
166
+ *
167
+ * Auto-detects the input format, normalises to JSON Schema via the
168
+ * adapter, walks the JSON Schema tree, and delegates per-field rendering
169
+ * to the {@link ComponentResolver} supplied via {@link SchemaProvider} —
170
+ * falling back to a headless HTML renderer when no provider is present.
171
+ *
172
+ * Pass `readOnly` to render a presentational view instead of inputs, or
173
+ * wrap with `<SchemaProvider resolver={…}>` to swap the theme.
174
+ *
175
+ * @group Components
176
+ * @example
177
+ * ```tsx
178
+ * import { z } from "zod";
179
+ * import { SchemaComponent } from "schema-components/react/SchemaComponent";
180
+ *
181
+ * const userSchema = z.object({ name: z.string(), email: z.email() });
182
+ *
183
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
184
+ * ```
185
+ */
186
+ declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(props: SchemaComponentProps<T, Ref, Mode>): ReactNode;
210
187
  /**
211
188
  * Append a child path suffix to a parent path. When the suffix is omitted
212
189
  * (e.g. transparent wrappers like union options), the parent path is
@@ -225,11 +202,26 @@ declare function joinPath(parent: string, suffix: string | undefined): string;
225
202
  * with a single hyphen and trim leading/trailing hyphens.
226
203
  */
227
204
  declare function sanitisePrefix(value: string): string;
205
+ /**
206
+ * Render a single walked field through the resolved widget /
207
+ * resolver / headless pipeline. Used internally by
208
+ * {@link SchemaComponent} and {@link SchemaField}, exported so other
209
+ * React-side components (e.g. the OpenAPI renderers) can dispatch
210
+ * into the same fallback chain.
211
+ */
228
212
  declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void, pathSuffix?: string) => ReactNode, path: string, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap, depth?: number): ReactNode;
229
213
  /**
230
214
  * Infer the schema's output type for SchemaField path inference.
231
215
  */
232
216
  type InferSchemaType<T> = T extends z.ZodType ? z.infer<T> : T extends object ? unknown extends FromJSONSchema<T> ? unknown : FromJSONSchema<T> : unknown;
217
+ /**
218
+ * Props accepted by {@link SchemaField}. The generic `P` constrains
219
+ * `path` to dot-paths reachable through the schema's inferred value
220
+ * type — typed schemas get autocomplete; runtime schemas fall back to
221
+ * `string`.
222
+ *
223
+ * @group Components
224
+ */
233
225
  interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)> {
234
226
  /**
235
227
  * Dot-separated path to the field (e.g. "address.city").
@@ -254,6 +246,16 @@ interface SchemaFieldProps<T = unknown, Ref extends string | undefined = undefin
254
246
  validate?: boolean;
255
247
  onValidationError?: (error: unknown) => void;
256
248
  }
249
+ /**
250
+ * Render a single field from a schema by dot-separated `path`.
251
+ *
252
+ * Walks the full schema tree and resolves the field at the supplied
253
+ * `path`, then renders only that field through the same resolver
254
+ * pipeline as {@link SchemaComponent}. Useful for embedding individual
255
+ * fields inside bespoke layouts.
256
+ *
257
+ * @group Components
258
+ */
257
259
  declare function SchemaField<T = unknown, Ref extends string | undefined = undefined, P extends string = PathOfType<InferSchemaType<T>> | (string extends PathOfType<InferSchemaType<T>> ? string : never)>({
258
260
  path,
259
261
  schema: schemaInput,
@@ -265,4 +267,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
265
267
  onValidationError
266
268
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
267
269
  //#endregion
268
- export { InferredInputValue, InferredOutputValue, InferredValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
270
+ export { type InferFields, type InferredInputValue, type InferredOutputValue, type InferredValue, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, __clearGlobalWidgets, joinPath, registerWidget, renderField, sanitisePrefix };
@@ -8,8 +8,8 @@ import { walk } from "../core/walker.mjs";
8
8
  import { headlessResolver } from "./headless.mjs";
9
9
  import { resolvePath, resolveValue, setNestedValue } from "./fieldPath.mjs";
10
10
  import { z } from "zod";
11
- import { createContext, isValidElement, useCallback, useContext, useId, useMemo } from "react";
12
11
  import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { createContext, isValidElement, useCallback, useContext, useId, useMemo } from "react";
13
13
  //#region src/react/SchemaComponent.tsx
14
14
  /**
15
15
  * <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
@@ -26,6 +26,27 @@ import { jsx, jsxs } from "react/jsx-runtime";
26
26
  */
27
27
  const UserResolverContext = createContext(void 0);
28
28
  const WidgetsContext = createContext(void 0);
29
+ /**
30
+ * Provide a theme resolver and scoped widgets to every `<SchemaComponent>`
31
+ * and `<SchemaView>` rendered inside the subtree.
32
+ *
33
+ * Wrap an application (or a region of it) with `<SchemaProvider>` so a
34
+ * single theme — typically one of the bundled adapters
35
+ * (`shadcnResolver`, `muiResolver`, `mantineResolver`, `radixResolver`)
36
+ * or a custom one — drives every schema render. Without a provider,
37
+ * schema-components fall back to the headless HTML renderer.
38
+ *
39
+ * @group Components
40
+ * @example
41
+ * ```tsx
42
+ * import { SchemaProvider } from "schema-components/react/SchemaComponent";
43
+ * import { shadcnResolver } from "schema-components/themes/shadcn";
44
+ *
45
+ * <SchemaProvider resolver={shadcnResolver}>
46
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
47
+ * </SchemaProvider>
48
+ * ```
49
+ */
29
50
  function SchemaProvider({ resolver, widgets, children }) {
30
51
  return /* @__PURE__ */ jsx(UserResolverContext.Provider, {
31
52
  value: resolver,
@@ -47,6 +68,40 @@ const globalWidgets = /* @__PURE__ */ new Map();
47
68
  function registerWidget(name, render) {
48
69
  globalWidgets.set(name, render);
49
70
  }
71
+ /**
72
+ * Clear every globally registered widget. Intended for test isolation —
73
+ * `registerWidget` writes to module-level state and that state otherwise
74
+ * leaks across test cases, making the test suite order-dependent. Tests
75
+ * should call this from an `afterEach` hook.
76
+ *
77
+ * @internal
78
+ */
79
+ function __clearGlobalWidgets() {
80
+ globalWidgets.clear();
81
+ }
82
+ /**
83
+ * Render an editable (or read-only) UI from a Zod schema, JSON Schema, or
84
+ * OpenAPI document.
85
+ *
86
+ * Auto-detects the input format, normalises to JSON Schema via the
87
+ * adapter, walks the JSON Schema tree, and delegates per-field rendering
88
+ * to the {@link ComponentResolver} supplied via {@link SchemaProvider} —
89
+ * falling back to a headless HTML renderer when no provider is present.
90
+ *
91
+ * Pass `readOnly` to render a presentational view instead of inputs, or
92
+ * wrap with `<SchemaProvider resolver={…}>` to swap the theme.
93
+ *
94
+ * @group Components
95
+ * @example
96
+ * ```tsx
97
+ * import { z } from "zod";
98
+ * import { SchemaComponent } from "schema-components/react/SchemaComponent";
99
+ *
100
+ * const userSchema = z.object({ name: z.string(), email: z.email() });
101
+ *
102
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
103
+ * ```
104
+ */
50
105
  function SchemaComponent(props) {
51
106
  const { schema: schemaInput, ref: refInput, io, value, onChange, validate, onValidationError, onError, onDiagnostic, strict, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets, idPrefix } = props;
52
107
  const userResolver = useContext(UserResolverContext);
@@ -220,6 +275,13 @@ function runValidation(zodSchema, jsonSchema, value, io, onDiagnostic) {
220
275
  }
221
276
  }
222
277
  }
278
+ /**
279
+ * Render a single walked field through the resolved widget /
280
+ * resolver / headless pipeline. Used internally by
281
+ * {@link SchemaComponent} and {@link SchemaField}, exported so other
282
+ * React-side components (e.g. the OpenAPI renderers) can dispatch
283
+ * into the same fallback chain.
284
+ */
223
285
  function renderField(tree, value, onChange, userResolver, renderChild, path, instanceWidgets, contextWidgets, depth = 0) {
224
286
  if (path.length === 0) throw new Error("renderField requires a non-empty path. Pass the root path (derived from `idPrefix` or `useId()`) for the root field, and use renderChild's pathSuffix to derive child paths.");
225
287
  if (depth >= 10) return /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsxs("em", { children: [
@@ -255,6 +317,16 @@ function renderField(tree, value, onChange, userResolver, renderChild, path, ins
255
317
  if (value === void 0 || value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
256
318
  return /* @__PURE__ */ jsx("span", { children: typeof value === "string" ? value : JSON.stringify(value) });
257
319
  }
320
+ /**
321
+ * Render a single field from a schema by dot-separated `path`.
322
+ *
323
+ * Walks the full schema tree and resolves the field at the supplied
324
+ * `path`, then renders only that field through the same resolver
325
+ * pipeline as {@link SchemaComponent}. Useful for embedding individual
326
+ * fields inside bespoke layouts.
327
+ *
328
+ * @group Components
329
+ */
258
330
  function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange, meta: fieldMeta, validate, onValidationError }) {
259
331
  const userResolver = useContext(UserResolverContext);
260
332
  const contextWidgets = useContext(WidgetsContext);
@@ -344,4 +416,4 @@ function isCallable(value) {
344
416
  return typeof value === "function";
345
417
  }
346
418
  //#endregion
347
- export { SchemaComponent, SchemaField, SchemaProvider, joinPath, registerWidget, renderField, sanitisePrefix };
419
+ export { SchemaComponent, SchemaField, SchemaProvider, __clearGlobalWidgets, joinPath, registerWidget, renderField, sanitisePrefix };
@@ -1,6 +1,11 @@
1
1
  import { Component, ReactNode } from "react";
2
2
 
3
3
  //#region src/react/SchemaErrorBoundary.d.ts
4
+ /**
5
+ * Props accepted by {@link SchemaErrorBoundary}.
6
+ *
7
+ * @group Components
8
+ */
4
9
  interface SchemaErrorBoundaryProps {
5
10
  /** Called with the caught error. Returns fallback ReactNode to render. */
6
11
  fallback: (error: Error, reset: () => void) => ReactNode;
@@ -10,10 +15,22 @@ interface ErrorBoundaryState {
10
15
  error: Error | undefined;
11
16
  }
12
17
  /**
13
- * React error boundary that catches schema rendering errors.
18
+ * React error boundary that catches rendering errors from
19
+ * `SchemaComponent`, theme adapters, and any descendant.
14
20
  *
15
21
  * Provides a `reset` callback that clears the error state, allowing
16
22
  * the children to re-render (e.g. after fixing a bad schema prop).
23
+ * Does not catch errors thrown from event handlers, async work, or
24
+ * server-side rendering — those paths must be handled by the host
25
+ * application.
26
+ *
27
+ * @group Components
28
+ * @example
29
+ * ```tsx
30
+ * <SchemaErrorBoundary fallback={(error) => <p>{error.message}</p>}>
31
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
32
+ * </SchemaErrorBoundary>
33
+ * ```
17
34
  */
18
35
  declare class SchemaErrorBoundary extends Component<SchemaErrorBoundaryProps, ErrorBoundaryState> {
19
36
  state: ErrorBoundaryState;
@@ -23,10 +23,22 @@ import { Component } from "react";
23
23
  * - Errors in server-side rendering
24
24
  */
25
25
  /**
26
- * React error boundary that catches schema rendering errors.
26
+ * React error boundary that catches rendering errors from
27
+ * `SchemaComponent`, theme adapters, and any descendant.
27
28
  *
28
29
  * Provides a `reset` callback that clears the error state, allowing
29
30
  * the children to re-render (e.g. after fixing a bad schema prop).
31
+ * Does not catch errors thrown from event handlers, async work, or
32
+ * server-side rendering — those paths must be handled by the host
33
+ * application.
34
+ *
35
+ * @group Components
36
+ * @example
37
+ * ```tsx
38
+ * <SchemaErrorBoundary fallback={(error) => <p>{error.message}</p>}>
39
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
40
+ * </SchemaErrorBoundary>
41
+ * ```
30
42
  */
31
43
  var SchemaErrorBoundary = class extends Component {
32
44
  state = { error: void 0 };