schema-components 2.0.2 → 2.1.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 +98 -1
- package/dist/SchemaComponent-B__6-5-E.d.mts +277 -0
- package/dist/SchemaComponent-BxzzsHsK.mjs +668 -0
- package/dist/adapter-ktQaheWB.d.mts +213 -0
- package/dist/constructorTypes-BdCiMS6e.d.mts +30 -0
- package/dist/core/adapter.d.mts +3 -213
- package/dist/core/constraintHint.d.mts +1 -1
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/contexts.d.mts +71 -0
- package/dist/core/contexts.mjs +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/{react → core}/fieldPath.d.mts +2 -2
- package/dist/{react → core}/fieldPath.mjs +3 -3
- package/dist/core/formats.d.mts +1 -1
- package/dist/core/inferValue.d.mts +1 -1
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/merge.d.mts +1 -1
- package/dist/core/normalise.d.mts +2 -2
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderField.d.mts +147 -0
- package/dist/core/renderField.mjs +81 -0
- package/dist/core/renderer.d.mts +2 -199
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/typeInference.d.mts +1 -982
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +3 -3
- package/dist/core/walker.d.mts +1 -1
- package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/renderToHtml.d.mts +5 -5
- package/dist/html/renderToHtml.mjs +33 -18
- package/dist/html/renderToHtmlStream.d.mts +5 -5
- package/dist/html/renderers.d.mts +1 -1
- package/dist/html/streamRenderers.d.mts +3 -3
- package/dist/{inferValue-Ce-PviSD.d.mts → inferValue-eAnh50EM.d.mts} +3 -3
- package/dist/lit/SchemaComponent.d.mts +125 -0
- package/dist/lit/SchemaComponent.mjs +2 -0
- package/dist/lit/SchemaField.d.mts +65 -0
- package/dist/lit/SchemaField.mjs +2 -0
- package/dist/lit/SchemaView.d.mts +14 -0
- package/dist/lit/SchemaView.mjs +2 -0
- package/dist/lit/constructorTypes.d.mts +2 -0
- package/dist/lit/constructorTypes.mjs +1 -0
- package/dist/lit/contexts.d.mts +78 -0
- package/dist/lit/contexts.mjs +238 -0
- package/dist/lit/defaultResolver.d.mts +33 -0
- package/dist/lit/defaultResolver.mjs +2 -0
- package/dist/lit/registry.d.mts +66 -0
- package/dist/lit/registry.mjs +2 -0
- package/dist/lit/renderers/baseElement.d.mts +131 -0
- package/dist/lit/renderers/baseElement.mjs +109 -0
- package/dist/lit/renderers/recordHelpers.d.mts +25 -0
- package/dist/lit/renderers/recordHelpers.mjs +55 -0
- package/dist/lit/renderers/scArray.d.mts +14 -0
- package/dist/lit/renderers/scArray.mjs +86 -0
- package/dist/lit/renderers/scBoolean.d.mts +15 -0
- package/dist/lit/renderers/scBoolean.mjs +47 -0
- package/dist/lit/renderers/scConditional.d.mts +23 -0
- package/dist/lit/renderers/scConditional.mjs +65 -0
- package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
- package/dist/lit/renderers/scDiscriminated.mjs +138 -0
- package/dist/lit/renderers/scEnum.d.mts +16 -0
- package/dist/lit/renderers/scEnum.mjs +66 -0
- package/dist/lit/renderers/scFile.d.mts +15 -0
- package/dist/lit/renderers/scFile.mjs +53 -0
- package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
- package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
- package/dist/lit/renderers/scNumber.d.mts +15 -0
- package/dist/lit/renderers/scNumber.mjs +64 -0
- package/dist/lit/renderers/scObject.d.mts +14 -0
- package/dist/lit/renderers/scObject.mjs +57 -0
- package/dist/lit/renderers/scRecord.d.mts +14 -0
- package/dist/lit/renderers/scRecord.mjs +112 -0
- package/dist/lit/renderers/scString.d.mts +19 -0
- package/dist/lit/renderers/scString.mjs +165 -0
- package/dist/lit/renderers/scTuple.d.mts +14 -0
- package/dist/lit/renderers/scTuple.mjs +58 -0
- package/dist/lit/renderers/scUnion.d.mts +14 -0
- package/dist/lit/renderers/scUnion.mjs +44 -0
- package/dist/lit/renderers/scUnknown.d.mts +15 -0
- package/dist/lit/renderers/scUnknown.mjs +45 -0
- package/dist/lit/ssr.d.mts +37 -0
- package/dist/lit/ssr.mjs +9565 -0
- package/dist/lit/types.d.mts +2 -0
- package/dist/lit/types.mjs +1 -0
- package/dist/lit/widget.d.mts +71 -0
- package/dist/lit/widget.mjs +87 -0
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/components.d.mts +4 -4
- package/dist/openapi/parser.d.mts +2 -2
- package/dist/openapi/resolve.d.mts +1 -1
- package/dist/preact/SchemaComponent.d.mts +3 -0
- package/dist/preact/SchemaComponent.mjs +26 -0
- package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
- package/dist/preact/SchemaErrorBoundary.mjs +20 -0
- package/dist/preact/SchemaView.d.mts +2 -0
- package/dist/preact/SchemaView.mjs +22 -0
- package/dist/preact/headless.d.mts +2 -0
- package/dist/preact/headless.mjs +18 -0
- package/dist/react/SchemaComponent.d.mts +3 -270
- package/dist/react/SchemaComponent.mjs +41 -32
- package/dist/react/SchemaView.d.mts +6 -6
- package/dist/react/SchemaView.mjs +32 -29
- package/dist/react/a11y.d.mts +2 -2
- package/dist/react/fieldShell.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
- package/dist/renderer-ab9E52Bp.d.mts +245 -0
- package/dist/solid/SchemaComponent.d.mts +136 -0
- package/dist/solid/SchemaComponent.mjs +391 -0
- package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
- package/dist/solid/SchemaErrorBoundary.mjs +57 -0
- package/dist/solid/SchemaField.d.mts +40 -0
- package/dist/solid/SchemaField.mjs +113 -0
- package/dist/solid/SchemaView.d.mts +54 -0
- package/dist/solid/SchemaView.mjs +168 -0
- package/dist/solid/a11y.d.mts +70 -0
- package/dist/solid/a11y.mjs +71 -0
- package/dist/solid/contexts.d.mts +37 -0
- package/dist/solid/contexts.mjs +66 -0
- package/dist/solid/headless.d.mts +10 -0
- package/dist/solid/headless.mjs +27 -0
- package/dist/solid/renderers.d.mts +79 -0
- package/dist/solid/renderers.mjs +840 -0
- package/dist/solid/types.d.mts +90 -0
- package/dist/solid/types.mjs +1 -0
- package/dist/solid/widget.d.mts +29 -0
- package/dist/solid/widget.mjs +35 -0
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
- package/dist/types-BCy7K3nk.d.mts +125 -0
- package/package.json +71 -1
- package/src/svelte/SchemaComponent.svelte +427 -0
- package/src/svelte/SchemaErrorBoundary.svelte +66 -0
- package/src/svelte/SchemaField.svelte +216 -0
- package/src/svelte/SchemaProvider.svelte +46 -0
- package/src/svelte/SchemaView.svelte +244 -0
- package/src/svelte/a11y.ts +112 -0
- package/src/svelte/contexts.ts +79 -0
- package/src/svelte/dispatch.ts +267 -0
- package/src/svelte/headless.ts +73 -0
- package/src/svelte/headlessFns.ts +124 -0
- package/src/svelte/renderers/Array.svelte +98 -0
- package/src/svelte/renderers/Boolean.svelte +43 -0
- package/src/svelte/renderers/Conditional.svelte +67 -0
- package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
- package/src/svelte/renderers/Enum.svelte +53 -0
- package/src/svelte/renderers/Fallback.svelte +24 -0
- package/src/svelte/renderers/File.svelte +46 -0
- package/src/svelte/renderers/Literal.svelte +29 -0
- package/src/svelte/renderers/Mount.svelte +24 -0
- package/src/svelte/renderers/Negation.svelte +35 -0
- package/src/svelte/renderers/Never.svelte +24 -0
- package/src/svelte/renderers/Null.svelte +19 -0
- package/src/svelte/renderers/Number.svelte +68 -0
- package/src/svelte/renderers/Object.svelte +74 -0
- package/src/svelte/renderers/Record.svelte +134 -0
- package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
- package/src/svelte/renderers/String.svelte +152 -0
- package/src/svelte/renderers/Tuple.svelte +84 -0
- package/src/svelte/renderers/Union.svelte +49 -0
- package/src/svelte/renderers/Unknown.svelte +42 -0
- package/src/svelte/svelte-modules.d.ts +25 -0
- package/src/svelte/types.ts +238 -0
- package/src/svelte/widget.ts +62 -0
- /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
- /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
- /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
- /package/dist/{version-DL8U5RuA.d.mts → version-BEBx10ND.d.mts} +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { j as WalkedField, w as SchemaMeta } from "./types-BBQaEPfE.mjs";
|
|
2
|
+
import { t as AllConstraints } from "./renderer-ab9E52Bp.mjs";
|
|
3
|
+
import { TemplateResult } from "lit";
|
|
4
|
+
|
|
5
|
+
//#region src/lit/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Props handed to a Lit render function.
|
|
8
|
+
*
|
|
9
|
+
* Shares the same per-field data as `RenderProps` (React) and
|
|
10
|
+
* `HtmlRenderProps` (HTML-string) but with a `renderChild` typed
|
|
11
|
+
* over Lit's {@link TemplateResult}. Editable fields receive a
|
|
12
|
+
* `change` callback that emits a Custom Event up the DOM tree —
|
|
13
|
+
* unlike React's prop-callback model, Custom Elements communicate via
|
|
14
|
+
* DOM events, so the renderer wires the input event to a
|
|
15
|
+
* `dispatchEvent(new CustomEvent("sc-change", { detail: { value } }))`
|
|
16
|
+
* on the host element.
|
|
17
|
+
*
|
|
18
|
+
* Renderers narrow on `tree.type` for per-variant data
|
|
19
|
+
* (object fields, array element schema, union options, etc.) — the
|
|
20
|
+
* walker's discriminated union enforces type-correct access.
|
|
21
|
+
*/
|
|
22
|
+
interface LitRenderProps {
|
|
23
|
+
/** Current field value. */
|
|
24
|
+
value: unknown;
|
|
25
|
+
/** Whether to render as read-only display. */
|
|
26
|
+
readOnly: boolean;
|
|
27
|
+
/** Whether to render as an empty input. */
|
|
28
|
+
writeOnly: boolean;
|
|
29
|
+
/** Schema metadata for this field. */
|
|
30
|
+
meta: SchemaMeta;
|
|
31
|
+
/** Constraints from schema checks. */
|
|
32
|
+
constraints: AllConstraints;
|
|
33
|
+
/** Dot-separated path from root (e.g. "address.city"). */
|
|
34
|
+
path: string;
|
|
35
|
+
/** Example values from the schema's `examples` keyword. */
|
|
36
|
+
examples?: unknown[];
|
|
37
|
+
/** Walked field tree for recursive rendering. */
|
|
38
|
+
tree: WalkedField;
|
|
39
|
+
/**
|
|
40
|
+
* Callback to propagate the next value back to the host element.
|
|
41
|
+
*
|
|
42
|
+
* Renderers wire DOM events (`@input`, `@change`, `@click` on add /
|
|
43
|
+
* remove controls) to this callback. The host element catches the
|
|
44
|
+
* resulting change and emits a `change` Custom Event on itself so
|
|
45
|
+
* framework consumers can observe via standard `addEventListener`.
|
|
46
|
+
*/
|
|
47
|
+
change: (value: unknown) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Render a child field. Resolver overrides call this to recursively
|
|
50
|
+
* render nested structures (object fields, array elements, union
|
|
51
|
+
* options) without re-running the resolver dispatch loop.
|
|
52
|
+
*
|
|
53
|
+
* @param tree - The walked field tree for the child
|
|
54
|
+
* @param value - The child's current value
|
|
55
|
+
* @param change - Callback receiving the child's next value
|
|
56
|
+
* @param pathSuffix - Path segment from the parent (e.g. `city` for
|
|
57
|
+
* an object key, `[0]` for an array index). Required for every
|
|
58
|
+
* container — without it children inherit no path and DOM id
|
|
59
|
+
* derivation throws.
|
|
60
|
+
*/
|
|
61
|
+
renderChild: (tree: WalkedField, value: unknown, change: (next: unknown) => void, pathSuffix?: string) => TemplateResult;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Signature for a Lit render function attached to a
|
|
65
|
+
* {@link LitComponentResolver}. Receives the per-field
|
|
66
|
+
* {@link LitRenderProps} built by the walker and returns a Lit
|
|
67
|
+
* {@link TemplateResult} for direct interpolation into the host
|
|
68
|
+
* element's `render()` method.
|
|
69
|
+
*
|
|
70
|
+
* The return type is `TemplateResult`, the type produced by
|
|
71
|
+
* the `html`-tagged template literal. Unlike `RenderFunction` (React)
|
|
72
|
+
* which returns `unknown` so React elements, primitive children, and
|
|
73
|
+
* arbitrary value types can flow through, Lit templates have a single
|
|
74
|
+
* canonical type — narrowing makes the resolver dispatch loop simpler
|
|
75
|
+
* and forces every renderer to return a structurally compatible value.
|
|
76
|
+
*/
|
|
77
|
+
type LitRenderFunction = (props: LitRenderProps) => TemplateResult;
|
|
78
|
+
/**
|
|
79
|
+
* Theme adapter — maps every schema field type to its Lit renderer.
|
|
80
|
+
*
|
|
81
|
+
* Structurally mirrors `ComponentResolver` (React) and `HtmlResolver`
|
|
82
|
+
* (HTML-string) so the resolver-key tuple in `core/renderer.ts`
|
|
83
|
+
* (`RESOLVER_KEYS`) drives every dispatch surface from a single
|
|
84
|
+
* source-of-truth list.
|
|
85
|
+
*
|
|
86
|
+
* Unset keys fall through to the default Lit resolver
|
|
87
|
+
* (`defaultLitResolver` in `lit/renderers/registry.ts`). The default
|
|
88
|
+
* resolver is composed from the built-in `<sc-*>` Custom Elements
|
|
89
|
+
* registered by {@link registerSchemaComponents} — overriding a key
|
|
90
|
+
* here lets a consumer bypass the Custom Element registry for a
|
|
91
|
+
* specific schema type without unregistering the element.
|
|
92
|
+
*/
|
|
93
|
+
interface LitComponentResolver {
|
|
94
|
+
string?: LitRenderFunction;
|
|
95
|
+
number?: LitRenderFunction;
|
|
96
|
+
boolean?: LitRenderFunction;
|
|
97
|
+
null?: LitRenderFunction;
|
|
98
|
+
enum?: LitRenderFunction;
|
|
99
|
+
object?: LitRenderFunction;
|
|
100
|
+
array?: LitRenderFunction;
|
|
101
|
+
tuple?: LitRenderFunction;
|
|
102
|
+
record?: LitRenderFunction;
|
|
103
|
+
union?: LitRenderFunction;
|
|
104
|
+
discriminatedUnion?: LitRenderFunction;
|
|
105
|
+
conditional?: LitRenderFunction;
|
|
106
|
+
negation?: LitRenderFunction;
|
|
107
|
+
literal?: LitRenderFunction;
|
|
108
|
+
file?: LitRenderFunction;
|
|
109
|
+
never?: LitRenderFunction;
|
|
110
|
+
unknown?: LitRenderFunction;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Compile-time witness that {@link LitRenderFunction} is a
|
|
114
|
+
* `(props: P) => O` shape exactly matching the parallel signatures
|
|
115
|
+
* carried by `core/renderer.ts`. The walker's dispatch loop is
|
|
116
|
+
* parameterised over `O` (React's `unknown`, HTML's `string`, Lit's
|
|
117
|
+
* `TemplateResult`) and `P` (the matching per-renderer props), so this
|
|
118
|
+
* alias documents the contract without forcing `core/renderer.ts` to
|
|
119
|
+
* import Lit.
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
type _LitFunctionShapeAssertion = LitRenderFunction extends ((props: LitRenderProps) => TemplateResult) ? true : false;
|
|
124
|
+
//#endregion
|
|
125
|
+
export { _LitFunctionShapeAssertion as i, LitRenderFunction as n, LitRenderProps as r, LitComponentResolver as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "schema-components",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"types": "./dist/react/*.d.mts",
|
|
14
14
|
"import": "./dist/react/*.mjs"
|
|
15
15
|
},
|
|
16
|
+
"./preact/*": {
|
|
17
|
+
"types": "./dist/preact/*.d.mts",
|
|
18
|
+
"import": "./dist/preact/*.mjs"
|
|
19
|
+
},
|
|
16
20
|
"./openapi/*": {
|
|
17
21
|
"types": "./dist/openapi/*.d.mts",
|
|
18
22
|
"import": "./dist/openapi/*.mjs"
|
|
@@ -24,10 +28,30 @@
|
|
|
24
28
|
"./themes/*": {
|
|
25
29
|
"types": "./dist/themes/*.d.mts",
|
|
26
30
|
"import": "./dist/themes/*.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./vue/*": {
|
|
33
|
+
"types": "./dist/vue/*.d.mts",
|
|
34
|
+
"import": "./dist/vue/*.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./svelte/*.svelte": "./src/svelte/*.svelte",
|
|
37
|
+
"./svelte/renderers/*.svelte": "./src/svelte/renderers/*.svelte",
|
|
38
|
+
"./svelte/*": {
|
|
39
|
+
"types": "./src/svelte/*.ts",
|
|
40
|
+
"svelte": "./src/svelte/*.ts",
|
|
41
|
+
"import": "./src/svelte/*.ts"
|
|
42
|
+
},
|
|
43
|
+
"./solid/*": {
|
|
44
|
+
"types": "./dist/solid/*.d.mts",
|
|
45
|
+
"import": "./dist/solid/*.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./lit/*": {
|
|
48
|
+
"types": "./dist/lit/*.d.mts",
|
|
49
|
+
"import": "./dist/lit/*.mjs"
|
|
27
50
|
}
|
|
28
51
|
},
|
|
29
52
|
"files": [
|
|
30
53
|
"dist",
|
|
54
|
+
"src/svelte",
|
|
31
55
|
"LICENSE",
|
|
32
56
|
"README.md",
|
|
33
57
|
"CHANGELOG.md"
|
|
@@ -38,6 +62,11 @@
|
|
|
38
62
|
"_lint:fix": "eslint --cache --fix 'src/**/*.{ts,tsx}'",
|
|
39
63
|
"_test": "vitest run --project=unit",
|
|
40
64
|
"_test:e2e": "vitest run --project=e2e",
|
|
65
|
+
"_test:preact": "vitest run --project=unit-preact",
|
|
66
|
+
"_test:vue": "vitest run --project=unit-vue",
|
|
67
|
+
"_test:svelte": "vitest run --project=unit-svelte",
|
|
68
|
+
"_test:solid": "vitest run --project=unit-solid",
|
|
69
|
+
"_test:lit": "vitest run --project=unit-lit",
|
|
41
70
|
"_test:coverage": "vitest run --project=unit --coverage",
|
|
42
71
|
"_build": "tsdown && cp src/html/styles.css dist/html/styles.css",
|
|
43
72
|
"_typedoc": "typedoc",
|
|
@@ -47,6 +76,10 @@
|
|
|
47
76
|
"lint": "turbo run _lint",
|
|
48
77
|
"lint:fix": "turbo run _lint:fix",
|
|
49
78
|
"test": "turbo run _test",
|
|
79
|
+
"test:preact": "turbo run _test:preact",
|
|
80
|
+
"test:svelte": "turbo run _test:svelte",
|
|
81
|
+
"test:solid": "turbo run _test:solid",
|
|
82
|
+
"test:lit": "turbo run _test:lit",
|
|
50
83
|
"test:coverage": "turbo run _test:coverage",
|
|
51
84
|
"check": "turbo run _check",
|
|
52
85
|
"validate": "turbo run _validate",
|
|
@@ -77,17 +110,47 @@
|
|
|
77
110
|
"provenance": true
|
|
78
111
|
},
|
|
79
112
|
"peerDependencies": {
|
|
113
|
+
"lit": ">=3.0.0",
|
|
114
|
+
"preact": ">=10.0.0",
|
|
80
115
|
"react": "^18.0.0 || ^19.0.0",
|
|
116
|
+
"solid-js": ">=1.8.0",
|
|
117
|
+
"svelte": ">=5.0.0",
|
|
118
|
+
"vue": ">=3.5.0",
|
|
81
119
|
"zod": "^4.0.0"
|
|
82
120
|
},
|
|
121
|
+
"peerDependenciesMeta": {
|
|
122
|
+
"lit": {
|
|
123
|
+
"optional": true
|
|
124
|
+
},
|
|
125
|
+
"preact": {
|
|
126
|
+
"optional": true
|
|
127
|
+
},
|
|
128
|
+
"solid-js": {
|
|
129
|
+
"optional": true
|
|
130
|
+
},
|
|
131
|
+
"svelte": {
|
|
132
|
+
"optional": true
|
|
133
|
+
},
|
|
134
|
+
"vue": {
|
|
135
|
+
"optional": true
|
|
136
|
+
}
|
|
137
|
+
},
|
|
83
138
|
"devDependencies": {
|
|
84
139
|
"@eslint/js": "10.0.1",
|
|
140
|
+
"@lit-labs/ssr": "4.0.0",
|
|
141
|
+
"@lit-labs/ssr-client": "1.1.8",
|
|
142
|
+
"@lit/context": "1.1.6",
|
|
143
|
+
"@solidjs/testing-library": "0.8.10",
|
|
144
|
+
"@sveltejs/vite-plugin-svelte": "7.1.2",
|
|
85
145
|
"@testing-library/react": "16.3.2",
|
|
146
|
+
"@testing-library/svelte": "5.3.1",
|
|
86
147
|
"@testing-library/user-event": "14.6.1",
|
|
87
148
|
"@types/node": "25.6.2",
|
|
88
149
|
"@types/react": "19.2.14",
|
|
89
150
|
"@types/react-dom": "19.2.3",
|
|
151
|
+
"@vitejs/plugin-vue": "6.0.6",
|
|
90
152
|
"@vitest/coverage-v8": "4.1.5",
|
|
153
|
+
"@vue/test-utils": "2.4.10",
|
|
91
154
|
"eslint": "10.3.0",
|
|
92
155
|
"eslint-config-prettier": "10.1.8",
|
|
93
156
|
"eslint-plugin-import": "2.32.0",
|
|
@@ -96,9 +159,14 @@
|
|
|
96
159
|
"eslint-plugin-prettier": "5.5.5",
|
|
97
160
|
"eslint-plugin-tsdoc": "0.5.2",
|
|
98
161
|
"happy-dom": "20.9.0",
|
|
162
|
+
"lit": "3.3.2",
|
|
163
|
+
"preact": "10.29.1",
|
|
164
|
+
"preact-render-to-string": "6.6.7",
|
|
99
165
|
"prettier": "3.8.3",
|
|
100
166
|
"react": "19.2.6",
|
|
101
167
|
"react-dom": "19.2.6",
|
|
168
|
+
"solid-js": "1.9.12",
|
|
169
|
+
"svelte": "5.55.5",
|
|
102
170
|
"tsdown": "0.22.0",
|
|
103
171
|
"tslib": "2.8.1",
|
|
104
172
|
"typedoc": "0.28.19",
|
|
@@ -107,7 +175,9 @@
|
|
|
107
175
|
"typedoc-plugin-missing-exports": "4.1.3",
|
|
108
176
|
"typescript": "6.0.3",
|
|
109
177
|
"typescript-eslint": "8.59.2",
|
|
178
|
+
"vite-plugin-solid": "2.11.12",
|
|
110
179
|
"vitest": "4.1.5",
|
|
180
|
+
"vue": "3.5.34",
|
|
111
181
|
"zod": "4.4.3"
|
|
112
182
|
}
|
|
113
183
|
}
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Editable Svelte 5 entry point that renders UI from a Zod schema,
|
|
3
|
+
JSON Schema, or OpenAPI document.
|
|
4
|
+
|
|
5
|
+
Mirror of `react/SchemaComponent.tsx :: SchemaComponent` adapted
|
|
6
|
+
to Svelte 5 runes:
|
|
7
|
+
|
|
8
|
+
- `$props<…>()` destructures inputs (no React `useContext` /
|
|
9
|
+
`useId` / `useMemo` / `useCallback`).
|
|
10
|
+
- `$derived(...)` for memoised values (the merged meta, the
|
|
11
|
+
normalised JSON schema, the walked tree, the dispatcher's
|
|
12
|
+
rootPath).
|
|
13
|
+
- `$state` for the per-mount default `idPrefix` (computed once
|
|
14
|
+
and held; `useId()` equivalent provided by Svelte 5's
|
|
15
|
+
`$props.id()` rune is intentionally not used here — every
|
|
16
|
+
path generated downstream is structurally suffixed and
|
|
17
|
+
passing a deterministic `idPrefix` is the contracted
|
|
18
|
+
override for snapshot tests).
|
|
19
|
+
- The dispatcher is the same `dispatchRenderField` shared with
|
|
20
|
+
the React and HTML adapters, wired through `renderFieldSvelte`
|
|
21
|
+
in `./dispatch.ts`.
|
|
22
|
+
|
|
23
|
+
External-value reactivity contract: `value` is consumed as an
|
|
24
|
+
immutable prop and any internal edit fires `onChange(next)` with
|
|
25
|
+
a freshly cloned object. Consumers can either:
|
|
26
|
+
|
|
27
|
+
- Pass a plain object and a function `onChange` that mutates
|
|
28
|
+
local state — typical controlled-component pattern; works
|
|
29
|
+
identically across React / Solid / Svelte.
|
|
30
|
+
- Use Svelte's `bind:value` ergonomics — Svelte translates
|
|
31
|
+
`bind:value` into an `onChange` that mutates the bound
|
|
32
|
+
reference, so this component does not need to know about
|
|
33
|
+
the binding.
|
|
34
|
+
|
|
35
|
+
Svelte's reactivity tracks reference identity on the `value`
|
|
36
|
+
prop. Internal edits emit fresh objects (`{ ...obj, key: v }`)
|
|
37
|
+
rather than mutating the prop, so consumers using
|
|
38
|
+
`$state(value)` see new identities propagated correctly. Deep
|
|
39
|
+
mutations of the prop object from outside the component — e.g.
|
|
40
|
+
`value.foo = "bar"` — would not be observed; that is the same
|
|
41
|
+
constraint Vue, Solid, and React impose on shared mutable
|
|
42
|
+
state and is documented in the package README.
|
|
43
|
+
-->
|
|
44
|
+
<script lang="ts" generics="T = unknown, Ref extends string | undefined = undefined">
|
|
45
|
+
import { z } from "zod";
|
|
46
|
+
import { walk } from "../core/walker.ts";
|
|
47
|
+
import type { WalkOptions } from "../core/walkBuilders.ts";
|
|
48
|
+
import {
|
|
49
|
+
isCodecSchema,
|
|
50
|
+
normaliseSchema,
|
|
51
|
+
type SchemaIoSide,
|
|
52
|
+
} from "../core/adapter.ts";
|
|
53
|
+
import type {
|
|
54
|
+
DiagnosticsOptions,
|
|
55
|
+
Diagnostic,
|
|
56
|
+
} from "../core/diagnostics.ts";
|
|
57
|
+
import { SchemaNormalisationError } from "../core/errors.ts";
|
|
58
|
+
import type { SchemaMeta, WalkedField } from "../core/types.ts";
|
|
59
|
+
import type { SchemaError } from "../core/errors.ts";
|
|
60
|
+
import { isObject, toRecordOrUndefined } from "../core/guards.ts";
|
|
61
|
+
import type {
|
|
62
|
+
InferFields,
|
|
63
|
+
InferSchemaValue,
|
|
64
|
+
} from "../core/inferValue.ts";
|
|
65
|
+
import { resolverContext, widgetsContext } from "./contexts.ts";
|
|
66
|
+
import { renderFieldSvelte } from "./dispatch.ts";
|
|
67
|
+
import RecursionSentinel from "./renderers/RecursionSentinel.svelte";
|
|
68
|
+
import Fallback from "./renderers/Fallback.svelte";
|
|
69
|
+
import Mount from "./renderers/Mount.svelte";
|
|
70
|
+
import type {
|
|
71
|
+
SvelteComponentResolver,
|
|
72
|
+
SvelteRenderDescriptor,
|
|
73
|
+
SvelteRenderProps,
|
|
74
|
+
SvelteWidgetMap,
|
|
75
|
+
} from "./types.ts";
|
|
76
|
+
|
|
77
|
+
interface Props {
|
|
78
|
+
/** Zod 4, JSON Schema, or OpenAPI document. */
|
|
79
|
+
schema: T;
|
|
80
|
+
/** OpenAPI ref string, e.g. "#/components/schemas/User". */
|
|
81
|
+
ref?: Ref;
|
|
82
|
+
/** Direction (`"output"` / `"input"`) for codec / transform schemas. */
|
|
83
|
+
io?: SchemaIoSide;
|
|
84
|
+
/** Current value to render. */
|
|
85
|
+
value?: InferSchemaValue<T, Ref, "output">;
|
|
86
|
+
/** Called when the value changes. */
|
|
87
|
+
onChange?: (value: InferSchemaValue<T, Ref, "output">) => void;
|
|
88
|
+
/** Run `safeParse` / `safeEncode` on change. */
|
|
89
|
+
validate?: boolean;
|
|
90
|
+
/** Called with the ZodError on validation failure. */
|
|
91
|
+
onValidationError?: (error: unknown) => void;
|
|
92
|
+
/** Called when schema normalisation or rendering fails. */
|
|
93
|
+
onError?: (error: SchemaError) => void;
|
|
94
|
+
/** Called with each diagnostic emitted during processing. */
|
|
95
|
+
onDiagnostic?: (diagnostic: Diagnostic) => void;
|
|
96
|
+
/** When true, any diagnostic becomes a thrown error. */
|
|
97
|
+
strict?: boolean;
|
|
98
|
+
/** Per-field meta overrides. */
|
|
99
|
+
fields?: InferFields<T, Ref>;
|
|
100
|
+
/** Meta overrides applied to the root schema. */
|
|
101
|
+
meta?: SchemaMeta;
|
|
102
|
+
/** Convenience: sets readOnly on all fields. */
|
|
103
|
+
readOnly?: boolean;
|
|
104
|
+
/** Convenience: sets writeOnly on all fields. */
|
|
105
|
+
writeOnly?: boolean;
|
|
106
|
+
/** Convenience: sets description on the root. */
|
|
107
|
+
description?: string;
|
|
108
|
+
/** Instance-scoped widgets. */
|
|
109
|
+
widgets?: SvelteWidgetMap;
|
|
110
|
+
/**
|
|
111
|
+
* Prefix used for every input `id` / label `for` in this
|
|
112
|
+
* component subtree. Defaults to a sanitised, mount-stable
|
|
113
|
+
* value derived from `crypto.randomUUID()` when available
|
|
114
|
+
* (falling back to a counter). Override for deterministic
|
|
115
|
+
* ids in snapshot tests.
|
|
116
|
+
*/
|
|
117
|
+
idPrefix?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const {
|
|
121
|
+
schema,
|
|
122
|
+
ref,
|
|
123
|
+
io,
|
|
124
|
+
value,
|
|
125
|
+
onChange,
|
|
126
|
+
validate,
|
|
127
|
+
onValidationError,
|
|
128
|
+
onError,
|
|
129
|
+
onDiagnostic,
|
|
130
|
+
strict,
|
|
131
|
+
fields,
|
|
132
|
+
meta: componentMeta,
|
|
133
|
+
readOnly,
|
|
134
|
+
writeOnly,
|
|
135
|
+
description,
|
|
136
|
+
widgets: instanceWidgets,
|
|
137
|
+
idPrefix,
|
|
138
|
+
}: Props = $props();
|
|
139
|
+
|
|
140
|
+
const userResolver = resolverContext.consume();
|
|
141
|
+
const contextWidgets = widgetsContext.consume();
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Per-mount fallback prefix used when the consumer doesn't pass
|
|
145
|
+
* `idPrefix`. Module-level counter incremented once per
|
|
146
|
+
* component instance — deterministic enough for typical
|
|
147
|
+
* applications without requiring `crypto.randomUUID()` in
|
|
148
|
+
* non-browser environments.
|
|
149
|
+
*/
|
|
150
|
+
const fallbackPrefix = `sc-svelte-${String(nextInstanceId())}`;
|
|
151
|
+
const rootPath = $derived(idPrefix ?? fallbackPrefix);
|
|
152
|
+
|
|
153
|
+
const mergedMeta = $derived<SchemaMeta>({
|
|
154
|
+
...componentMeta,
|
|
155
|
+
...(readOnly === true ? { readOnly: true } : {}),
|
|
156
|
+
...(writeOnly === true ? { writeOnly: true } : {}),
|
|
157
|
+
...(description !== undefined ? { description } : {}),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const diagnostics: DiagnosticsOptions | undefined = $derived(
|
|
161
|
+
onDiagnostic !== undefined || strict === true
|
|
162
|
+
? {
|
|
163
|
+
...(onDiagnostic !== undefined
|
|
164
|
+
? { diagnostics: onDiagnostic }
|
|
165
|
+
: {}),
|
|
166
|
+
...(strict !== undefined ? { strict } : {}),
|
|
167
|
+
}
|
|
168
|
+
: undefined
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
interface NormalisedShape {
|
|
172
|
+
jsonSchema: Record<string, unknown>;
|
|
173
|
+
zodSchema: unknown;
|
|
174
|
+
rootMeta: SchemaMeta | undefined;
|
|
175
|
+
rootDocument: Record<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const normalisedResult = $derived<NormalisedShape | SchemaError>(
|
|
179
|
+
normaliseSafely(schema, ref, io, diagnostics)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* If normalisation failed and the consumer wired up `onError`,
|
|
184
|
+
* surface the structured error through the callback once per
|
|
185
|
+
* change. Mirrors the React adapter's behaviour where
|
|
186
|
+
* `SchemaNormalisationError` is routed through `onError` rather
|
|
187
|
+
* than thrown at render time.
|
|
188
|
+
*/
|
|
189
|
+
$effect(() => {
|
|
190
|
+
if (normalisedResult instanceof Error && onError !== undefined) {
|
|
191
|
+
onError(normalisedResult);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const fieldsRecord = $derived(toRecordOrUndefined(fields));
|
|
196
|
+
|
|
197
|
+
const walkOptions = $derived<WalkOptions | undefined>(
|
|
198
|
+
normalisedResult instanceof Error
|
|
199
|
+
? undefined
|
|
200
|
+
: {
|
|
201
|
+
componentMeta: mergedMeta,
|
|
202
|
+
...(normalisedResult.rootMeta !== undefined
|
|
203
|
+
? { rootMeta: normalisedResult.rootMeta }
|
|
204
|
+
: {}),
|
|
205
|
+
...(fieldsRecord !== undefined
|
|
206
|
+
? { fieldOverrides: fieldsRecord }
|
|
207
|
+
: {}),
|
|
208
|
+
rootDocument: normalisedResult.rootDocument,
|
|
209
|
+
...(diagnostics !== undefined ? { diagnostics } : {}),
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const tree = $derived<WalkedField | undefined>(
|
|
214
|
+
normalisedResult instanceof Error || walkOptions === undefined
|
|
215
|
+
? undefined
|
|
216
|
+
: walk(normalisedResult.jsonSchema, walkOptions)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
function handleChange(nextValue: unknown): void {
|
|
220
|
+
if (validate === true && !(normalisedResult instanceof Error)) {
|
|
221
|
+
const error = runValidation(
|
|
222
|
+
normalisedResult.zodSchema,
|
|
223
|
+
normalisedResult.jsonSchema,
|
|
224
|
+
nextValue,
|
|
225
|
+
io,
|
|
226
|
+
onDiagnostic
|
|
227
|
+
);
|
|
228
|
+
if (error !== undefined) onValidationError?.(error);
|
|
229
|
+
}
|
|
230
|
+
if (onChange !== undefined) {
|
|
231
|
+
// Library boundary identical to the React adapter — the
|
|
232
|
+
// walker produces `unknown` typed values that downstream
|
|
233
|
+
// call sites receive as the inferred schema shape. The
|
|
234
|
+
// contravariant assignment cannot be proven by
|
|
235
|
+
// TypeScript and is the same pattern used in
|
|
236
|
+
// `react/SchemaComponent.tsx`.
|
|
237
|
+
onChange(nextValue as InferSchemaValue<T, Ref, "output">);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function makeRenderChild(
|
|
242
|
+
currentDepth: number,
|
|
243
|
+
parentPath: string,
|
|
244
|
+
currentValue: unknown,
|
|
245
|
+
currentOnChange: (v: unknown) => void
|
|
246
|
+
): SvelteRenderProps["renderChild"] {
|
|
247
|
+
return (
|
|
248
|
+
childTree: WalkedField,
|
|
249
|
+
childValue: unknown,
|
|
250
|
+
childOnChange: (v: unknown) => void,
|
|
251
|
+
pathSuffix?: string
|
|
252
|
+
): SvelteRenderDescriptor | null => {
|
|
253
|
+
const childPath = joinPath(parentPath, pathSuffix);
|
|
254
|
+
return renderFieldSvelte(
|
|
255
|
+
childTree,
|
|
256
|
+
childValue,
|
|
257
|
+
childOnChange,
|
|
258
|
+
userResolver,
|
|
259
|
+
makeRenderChild(
|
|
260
|
+
currentDepth + 1,
|
|
261
|
+
childPath,
|
|
262
|
+
childValue,
|
|
263
|
+
childOnChange
|
|
264
|
+
),
|
|
265
|
+
childPath,
|
|
266
|
+
instanceWidgets,
|
|
267
|
+
contextWidgets,
|
|
268
|
+
currentDepth + 1,
|
|
269
|
+
Fallback,
|
|
270
|
+
RecursionSentinel
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const renderChild = $derived(
|
|
276
|
+
tree === undefined
|
|
277
|
+
? undefined
|
|
278
|
+
: makeRenderChild(0, rootPath, value ?? tree.defaultValue, handleChange)
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const effectiveValue = $derived(
|
|
282
|
+
tree === undefined ? value : (value ?? tree.defaultValue)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const rootDescriptor = $derived<SvelteRenderDescriptor | null>(
|
|
286
|
+
tree === undefined || renderChild === undefined
|
|
287
|
+
? null
|
|
288
|
+
: renderFieldSvelte(
|
|
289
|
+
tree,
|
|
290
|
+
effectiveValue,
|
|
291
|
+
handleChange,
|
|
292
|
+
userResolver,
|
|
293
|
+
renderChild,
|
|
294
|
+
rootPath,
|
|
295
|
+
instanceWidgets,
|
|
296
|
+
contextWidgets,
|
|
297
|
+
0,
|
|
298
|
+
Fallback,
|
|
299
|
+
RecursionSentinel
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Append a child path suffix to a parent path. When the suffix
|
|
305
|
+
* is omitted (e.g. transparent wrappers like union options), the
|
|
306
|
+
* parent path is returned unchanged so the child inherits the
|
|
307
|
+
* parent's id. Matches `react/SchemaComponent.tsx :: joinPath`.
|
|
308
|
+
*/
|
|
309
|
+
function joinPath(parent: string, suffix: string | undefined): string {
|
|
310
|
+
if (suffix === undefined || suffix.length === 0) return parent;
|
|
311
|
+
if (parent.length === 0) return suffix;
|
|
312
|
+
if (suffix.startsWith("[")) return `${parent}${suffix}`;
|
|
313
|
+
return `${parent}.${suffix}`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function normaliseSafely(
|
|
317
|
+
schemaInput: unknown,
|
|
318
|
+
refInput: string | undefined,
|
|
319
|
+
ioSide: SchemaIoSide | undefined,
|
|
320
|
+
diags: DiagnosticsOptions | undefined
|
|
321
|
+
): NormalisedShape | SchemaError {
|
|
322
|
+
try {
|
|
323
|
+
const opts =
|
|
324
|
+
diags !== undefined || ioSide !== undefined
|
|
325
|
+
? {
|
|
326
|
+
...(diags !== undefined ? { diagnostics: diags } : {}),
|
|
327
|
+
...(ioSide !== undefined ? { io: ioSide } : {}),
|
|
328
|
+
}
|
|
329
|
+
: undefined;
|
|
330
|
+
const out = normaliseSchema(schemaInput, refInput, opts);
|
|
331
|
+
return out;
|
|
332
|
+
} catch (err: unknown) {
|
|
333
|
+
if (err instanceof SchemaNormalisationError) return err;
|
|
334
|
+
return new SchemaNormalisationError(
|
|
335
|
+
err instanceof Error
|
|
336
|
+
? err.message
|
|
337
|
+
: "Failed to normalise schema",
|
|
338
|
+
schemaInput,
|
|
339
|
+
"unknown"
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function runValidation(
|
|
345
|
+
zodSchema: unknown,
|
|
346
|
+
jsonSchema: Record<string, unknown>,
|
|
347
|
+
nextValue: unknown,
|
|
348
|
+
ioSide: SchemaIoSide | undefined,
|
|
349
|
+
diag: ((diagnostic: Diagnostic) => void) | undefined
|
|
350
|
+
): unknown {
|
|
351
|
+
if (zodSchema !== undefined && isObject(zodSchema)) {
|
|
352
|
+
const resolvedIo: SchemaIoSide = ioSide ?? "output";
|
|
353
|
+
const useSafeEncode =
|
|
354
|
+
isCodecSchema(zodSchema) && resolvedIo === "output";
|
|
355
|
+
const validateFn = useSafeEncode
|
|
356
|
+
? zodSchema.safeEncode
|
|
357
|
+
: zodSchema.safeParse;
|
|
358
|
+
if (typeof validateFn === "function") {
|
|
359
|
+
const result: unknown = validateFn(nextValue);
|
|
360
|
+
if (
|
|
361
|
+
isObject(result) &&
|
|
362
|
+
"success" in result &&
|
|
363
|
+
result.success !== true
|
|
364
|
+
) {
|
|
365
|
+
return result.error;
|
|
366
|
+
}
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
let parsed: unknown;
|
|
371
|
+
try {
|
|
372
|
+
parsed = z.fromJSONSchema(jsonSchema);
|
|
373
|
+
} catch (err: unknown) {
|
|
374
|
+
if (diag !== undefined) {
|
|
375
|
+
const message =
|
|
376
|
+
err instanceof Error
|
|
377
|
+
? err.message
|
|
378
|
+
: "z.fromJSONSchema threw a non-Error value";
|
|
379
|
+
diag({
|
|
380
|
+
code: "unsupported-type",
|
|
381
|
+
message:
|
|
382
|
+
"Skipping fallback validation: z.fromJSONSchema could not " +
|
|
383
|
+
`round-trip the normalised JSON Schema. Original message: ${message}`,
|
|
384
|
+
pointer: "",
|
|
385
|
+
detail: { source: "z.fromJSONSchema" },
|
|
386
|
+
});
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
if (isObject(parsed) && typeof parsed.safeParse === "function") {
|
|
392
|
+
const result: unknown = parsed.safeParse(nextValue);
|
|
393
|
+
if (
|
|
394
|
+
isObject(result) &&
|
|
395
|
+
"success" in result &&
|
|
396
|
+
result.success !== true
|
|
397
|
+
) {
|
|
398
|
+
return result.error;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
</script>
|
|
404
|
+
|
|
405
|
+
<script lang="ts" module>
|
|
406
|
+
let instanceCounter = 0;
|
|
407
|
+
/**
|
|
408
|
+
* Module-scoped counter feeding the default `idPrefix` for every
|
|
409
|
+
* `<SchemaComponent>` instance. Bumped once per mount so two
|
|
410
|
+
* components on the same page never share generated ids.
|
|
411
|
+
*
|
|
412
|
+
* Counter rather than `crypto.randomUUID()` because the renderer
|
|
413
|
+
* also runs in SSR / Node environments where the Web Crypto API
|
|
414
|
+
* is conditional. A monotonically increasing integer per process
|
|
415
|
+
* is sufficient for the per-page-uniqueness contract.
|
|
416
|
+
*
|
|
417
|
+
* @returns The next per-instance integer (1, 2, …).
|
|
418
|
+
*/
|
|
419
|
+
export function nextInstanceId(): number {
|
|
420
|
+
instanceCounter += 1;
|
|
421
|
+
return instanceCounter;
|
|
422
|
+
}
|
|
423
|
+
</script>
|
|
424
|
+
|
|
425
|
+
{#if rootDescriptor !== null}
|
|
426
|
+
<Mount descriptor={rootDescriptor} />
|
|
427
|
+
{/if}
|