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
package/dist/openapi/resolve.mjs
CHANGED
|
@@ -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-
|
|
6
|
+
import { o as normaliseOpenApiSchemas, r as documentContainsKeyword } from "../normalise-DB-Xtjmn.mjs";
|
|
7
7
|
import { resolveRefChain } from "../core/refChain.mjs";
|
|
8
|
-
import {
|
|
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
|
-
* (`
|
|
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
|
|
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
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
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
|
|
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:
|
|
335
|
-
requestBody:
|
|
336
|
-
responses:
|
|
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
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
* `
|
|
344
|
-
*
|
|
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
|
|
347
|
-
|
|
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
|
-
*
|
|
351
|
-
*
|
|
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
|
|
354
|
-
return
|
|
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
|
-
*
|
|
360
|
-
*
|
|
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
|
|
376
|
+
return extractParameters(ensureParsed(doc, diagnostics), path, method);
|
|
364
377
|
}
|
|
365
378
|
/**
|
|
366
|
-
* Resolve
|
|
367
|
-
*
|
|
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
|
-
*
|
|
376
|
-
*
|
|
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
|
|
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
|
-
*
|
|
394
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
403
|
-
*
|
|
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
|
|
412
|
+
return extractResponses(ensureParsed(doc, diagnostics), path, method);
|
|
407
413
|
}
|
|
408
414
|
//#endregion
|
|
409
|
-
export { getParsed, resolveOperation,
|
|
415
|
+
export { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
|
|
@@ -1,25 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as Diagnostic } from "../diagnostics-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { FromJSONSchema,
|
|
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
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Provide a theme resolver and scoped widgets to every `<SchemaComponent>`
|
|
15
|
+
* and `<SchemaView>` rendered inside the subtree.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Wrap an application (or a region of it) with `<SchemaProvider>` so a
|
|
18
|
+
* single theme — typically 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
|
-
*
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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.
|
|
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?:
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
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 };
|