@narrative.io/jsonforms-provider-protocols 2.11.0 → 3.0.0-beta.10
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 +101 -29
- package/dist/core/initFormData.d.ts +10 -0
- package/dist/core/initFormData.d.ts.map +1 -0
- package/dist/core/initFormData.js +99 -0
- package/dist/core/initFormData.js.map +1 -0
- package/dist/core/projection.d.ts +32 -0
- package/dist/core/projection.d.ts.map +1 -0
- package/dist/core/projection.js +74 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/resolveScope.d.ts +11 -0
- package/dist/core/resolveScope.d.ts.map +1 -0
- package/dist/core/resolveScope.js +22 -0
- package/dist/core/resolveScope.js.map +1 -0
- package/dist/core/transforms.d.ts +8 -10
- package/dist/core/transforms.d.ts.map +1 -1
- package/dist/core/transforms.js +58 -13
- package/dist/core/transforms.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/jsonforms-provider-protocols.css +2 -2
- package/dist/vue/components/ProviderAutocomplete.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderAutocomplete.vue.js +10 -5
- package/dist/vue/components/ProviderAutocomplete.vue.js.map +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue.js +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue2.js +12 -7
- package/dist/vue/components/ProviderMultiSelect.vue2.js.map +1 -1
- package/dist/vue/components/ProviderSelect.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderSelect.vue.js +1 -1
- package/dist/vue/components/ProviderSelect.vue2.js +13 -6
- package/dist/vue/components/ProviderSelect.vue2.js.map +1 -1
- package/dist/vue/composables/useDataLayer.d.ts +10 -0
- package/dist/vue/composables/useDataLayer.d.ts.map +1 -0
- package/dist/vue/composables/useDataLayer.js +26 -0
- package/dist/vue/composables/useDataLayer.js.map +1 -0
- package/dist/vue/composables/useDerive.d.ts +5 -2
- package/dist/vue/composables/useDerive.d.ts.map +1 -1
- package/dist/vue/composables/useDerive.js +29 -12
- package/dist/vue/composables/useDerive.js.map +1 -1
- package/dist/vue/composables/useDeriveInitialValue.d.ts +36 -0
- package/dist/vue/composables/useDeriveInitialValue.d.ts.map +1 -0
- package/dist/vue/composables/useDeriveInitialValue.js +125 -0
- package/dist/vue/composables/useDeriveInitialValue.js.map +1 -0
- package/dist/vue/composables/useDirtyValidation.d.ts +9 -0
- package/dist/vue/composables/useDirtyValidation.d.ts.map +1 -0
- package/dist/vue/composables/useDirtyValidation.js +15 -0
- package/dist/vue/composables/useDirtyValidation.js.map +1 -0
- package/dist/vue/composables/useProjection.d.ts +41 -0
- package/dist/vue/composables/useProjection.d.ts.map +1 -0
- package/dist/vue/composables/useProjection.js +84 -0
- package/dist/vue/composables/useProjection.js.map +1 -0
- package/dist/vue/index.d.ts +7 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +35 -27
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.d.ts +9 -0
- package/dist/vue/primevue/JfBoolean.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.js +35 -13
- package/dist/vue/primevue/JfBoolean.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.d.ts +9 -0
- package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.js +31 -22
- package/dist/vue/primevue/JfEnum.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.d.ts +9 -0
- package/dist/vue/primevue/JfEnumArray.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.js +33 -18
- package/dist/vue/primevue/JfEnumArray.vue.js.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.d.ts +9 -0
- package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.js +31 -22
- package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
- package/dist/vue/primevue/JfText.vue.d.ts +9 -0
- package/dist/vue/primevue/JfText.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfText.vue.js +40 -32
- package/dist/vue/primevue/JfText.vue.js.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.d.ts +9 -0
- package/dist/vue/primevue/JfTextArea.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.js +32 -18
- package/dist/vue/primevue/JfTextArea.vue.js.map +1 -1
- package/dist/vue/primevue/index.d.ts.map +1 -1
- package/dist/vue/primevue/index.js +100 -8
- package/dist/vue/primevue/index.js.map +1 -1
- package/package.json +3 -1
- package/src/core/initFormData.ts +189 -0
- package/src/core/projection.ts +136 -0
- package/src/core/resolveScope.ts +39 -0
- package/src/core/transforms.ts +118 -26
- package/src/core/types.ts +9 -0
- package/src/index.ts +7 -1
- package/src/vue/components/ProviderAutocomplete.vue +10 -5
- package/src/vue/components/ProviderMultiSelect.vue +14 -7
- package/src/vue/components/ProviderSelect.vue +15 -6
- package/src/vue/composables/useDataLayer.ts +43 -0
- package/src/vue/composables/useDerive.ts +62 -16
- package/src/vue/composables/useDeriveInitialValue.ts +195 -0
- package/src/vue/composables/useDirtyValidation.ts +20 -0
- package/src/vue/composables/useProjection.ts +181 -0
- package/src/vue/index.ts +35 -41
- package/src/vue/primevue/JfBoolean.vue +25 -7
- package/src/vue/primevue/JfEnum.vue +29 -22
- package/src/vue/primevue/JfEnumArray.vue +31 -16
- package/src/vue/primevue/JfNumber.vue +29 -22
- package/src/vue/primevue/JfText.vue +34 -27
- package/src/vue/primevue/JfTextArea.vue +29 -17
- package/src/vue/primevue/index.ts +114 -8
- package/src/vue/styles.css +26 -1
|
@@ -4,6 +4,8 @@ import _sfc_main$2 from "./JfNumber.vue.js";
|
|
|
4
4
|
import _sfc_main$3 from "./JfEnum.vue.js";
|
|
5
5
|
import _sfc_main$4 from "./JfEnumArray.vue.js";
|
|
6
6
|
import _sfc_main$5 from "./JfBoolean.vue.js";
|
|
7
|
+
import { getProjectedSchema } from "../../core/projection.js";
|
|
8
|
+
import { resolveScopeSchema } from "../../core/resolveScope.js";
|
|
7
9
|
const injectLayoutStyles = () => {
|
|
8
10
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
9
11
|
if (!document.getElementById("jsonforms-primevue-styles")) {
|
|
@@ -15,13 +17,38 @@ const injectLayoutStyles = () => {
|
|
|
15
17
|
display: flex;
|
|
16
18
|
flex-direction: column;
|
|
17
19
|
align-items: flex-start;
|
|
18
|
-
gap:
|
|
20
|
+
gap: 24px;
|
|
19
21
|
width: 100%;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
.vertical-layout-item {
|
|
23
25
|
width: 100%;
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
/* Form control wrapper */
|
|
29
|
+
.jf-control {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: 12px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Form control label typography */
|
|
36
|
+
.jf-label {
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
font-size: 14px;
|
|
39
|
+
line-height: 14px;
|
|
40
|
+
color: #031553;
|
|
41
|
+
text-align: left;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Form control description typography */
|
|
45
|
+
.jf-description {
|
|
46
|
+
font-weight: 400;
|
|
47
|
+
font-size: 14px;
|
|
48
|
+
line-height: 14px;
|
|
49
|
+
color: #415290;
|
|
50
|
+
text-align: left;
|
|
51
|
+
}
|
|
25
52
|
`;
|
|
26
53
|
document.head.appendChild(style);
|
|
27
54
|
}
|
|
@@ -40,6 +67,7 @@ function registerPrimevueRenderers(jsonformsCore) {
|
|
|
40
67
|
isNumberControl,
|
|
41
68
|
isIntegerControl,
|
|
42
69
|
and,
|
|
70
|
+
or,
|
|
43
71
|
isControl,
|
|
44
72
|
schemaMatches,
|
|
45
73
|
isBooleanControl
|
|
@@ -61,27 +89,91 @@ function registerPrimevueRenderers(jsonformsCore) {
|
|
|
61
89
|
{}
|
|
62
90
|
);
|
|
63
91
|
};
|
|
92
|
+
const projectedSchemaMatches = (
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
((check) => (uischema, schema) => {
|
|
95
|
+
const ui = uischema;
|
|
96
|
+
const projection = ui?.options?.projection;
|
|
97
|
+
if (!projection || ui?.type !== "Control" || !ui?.scope) return false;
|
|
98
|
+
const propertySchema = resolveScopeSchema(
|
|
99
|
+
ui.scope,
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
schema
|
|
102
|
+
);
|
|
103
|
+
if (!propertySchema) return false;
|
|
104
|
+
return check(getProjectedSchema(propertySchema, projection));
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
const isMultilineProjection = (uischema, schema) => {
|
|
108
|
+
const ui = uischema;
|
|
109
|
+
return ui?.options?.multi === true && projectedSchemaMatches((s) => s?.type === "string")(uischema, schema);
|
|
110
|
+
};
|
|
64
111
|
const renderers = [
|
|
65
112
|
// Multiline text has higher priority than regular text
|
|
66
|
-
{ tester: rankWith(PRIME + 4, isMultilineString), renderer: _sfc_main },
|
|
67
|
-
{ tester: rankWith(PRIME + 3, isStringControl), renderer: _sfc_main$1 },
|
|
68
113
|
{
|
|
69
|
-
tester: rankWith(PRIME +
|
|
114
|
+
tester: rankWith(PRIME + 4, or(isMultilineString, isMultilineProjection)),
|
|
115
|
+
renderer: _sfc_main
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
tester: rankWith(
|
|
119
|
+
PRIME + 3,
|
|
120
|
+
or(
|
|
121
|
+
isStringControl,
|
|
122
|
+
projectedSchemaMatches((s) => s?.type === "string")
|
|
123
|
+
)
|
|
124
|
+
),
|
|
125
|
+
renderer: _sfc_main$1
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
tester: rankWith(
|
|
129
|
+
PRIME + 6,
|
|
130
|
+
or(
|
|
131
|
+
isIntegerControl,
|
|
132
|
+
projectedSchemaMatches((s) => s?.type === "integer")
|
|
133
|
+
)
|
|
134
|
+
),
|
|
70
135
|
renderer: _sfc_main$2
|
|
71
136
|
},
|
|
72
137
|
{
|
|
73
|
-
tester: rankWith(
|
|
138
|
+
tester: rankWith(
|
|
139
|
+
PRIME + 4,
|
|
140
|
+
or(
|
|
141
|
+
isNumberControl,
|
|
142
|
+
projectedSchemaMatches((s) => s?.type === "number")
|
|
143
|
+
)
|
|
144
|
+
),
|
|
74
145
|
renderer: _sfc_main$2
|
|
75
146
|
},
|
|
76
147
|
{
|
|
77
|
-
tester: rankWith(
|
|
148
|
+
tester: rankWith(
|
|
149
|
+
PRIME + 7,
|
|
150
|
+
or(
|
|
151
|
+
and(isControl, schemaMatches(isScalarEnum)),
|
|
152
|
+
and(isControl, projectedSchemaMatches(isScalarEnum))
|
|
153
|
+
)
|
|
154
|
+
),
|
|
78
155
|
renderer: _sfc_main$3
|
|
79
156
|
},
|
|
80
157
|
{
|
|
81
|
-
tester: rankWith(
|
|
158
|
+
tester: rankWith(
|
|
159
|
+
PRIME + 8,
|
|
160
|
+
or(
|
|
161
|
+
and(isControl, schemaMatches(isEnumArray)),
|
|
162
|
+
and(isControl, projectedSchemaMatches(isEnumArray))
|
|
163
|
+
)
|
|
164
|
+
),
|
|
82
165
|
renderer: _sfc_main$4
|
|
83
166
|
},
|
|
84
|
-
{
|
|
167
|
+
{
|
|
168
|
+
tester: rankWith(
|
|
169
|
+
PRIME + 3,
|
|
170
|
+
or(
|
|
171
|
+
isBooleanControl,
|
|
172
|
+
projectedSchemaMatches((s) => s?.type === "boolean")
|
|
173
|
+
)
|
|
174
|
+
),
|
|
175
|
+
renderer: _sfc_main$5
|
|
176
|
+
}
|
|
85
177
|
];
|
|
86
178
|
primevueRenderers.splice(0, primevueRenderers.length, ...renderers);
|
|
87
179
|
renderersInitialized = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/vue/primevue/index.ts"],"sourcesContent":["import JfText from \"./JfText.vue\";\nimport JfTextArea from \"./JfTextArea.vue\";\nimport JfNumber from \"./JfNumber.vue\";\nimport JfEnum from \"./JfEnum.vue\";\nimport JfEnumArray from \"./JfEnumArray.vue\";\nimport JfBoolean from \"./JfBoolean.vue\";\n\n// Auto-inject layout styles\nconst injectLayoutStyles = () => {\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n if (!document.getElementById(\"jsonforms-primevue-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"jsonforms-primevue-styles\";\n style.textContent = `\n/* JSONForms PrimeVue Provider Protocols Layout Styles */\n.vertical-layout {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap:
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/vue/primevue/index.ts"],"sourcesContent":["import JfText from \"./JfText.vue\";\nimport JfTextArea from \"./JfTextArea.vue\";\nimport JfNumber from \"./JfNumber.vue\";\nimport JfEnum from \"./JfEnum.vue\";\nimport JfEnumArray from \"./JfEnumArray.vue\";\nimport JfBoolean from \"./JfBoolean.vue\";\nimport { getProjectedSchema } from \"../../core/projection\";\nimport { resolveScopeSchema } from \"../../core/resolveScope\";\n\n// Auto-inject layout styles\nconst injectLayoutStyles = () => {\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n if (!document.getElementById(\"jsonforms-primevue-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"jsonforms-primevue-styles\";\n style.textContent = `\n/* JSONForms PrimeVue Provider Protocols Layout Styles */\n.vertical-layout {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 24px;\n width: 100%;\n}\n\n.vertical-layout-item {\n width: 100%;\n}\n\n/* Form control wrapper */\n.jf-control {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* Form control label typography */\n.jf-label {\n font-weight: 600;\n font-size: 14px;\n line-height: 14px;\n color: #031553;\n text-align: left;\n}\n\n/* Form control description typography */\n.jf-description {\n font-weight: 400;\n font-size: 14px;\n line-height: 14px;\n color: #415290;\n text-align: left;\n}\n `;\n document.head.appendChild(style);\n }\n }\n};\n\n// Inject styles when this module is imported\ninjectLayoutStyles();\n\n// Track whether renderers have been registered to avoid duplicate registrations\nlet renderersInitialized = false;\n\n// Return empty renderers array initially\nexport const primevueRenderers: unknown[] = [];\n\n// Export a function that can be called by the consumer to register renderers\n// This should only be called once during application initialization\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerPrimevueRenderers(jsonformsCore: any): unknown[] {\n // Prevent duplicate registration which causes AJV schema recompilation\n if (renderersInitialized) {\n return primevueRenderers as unknown[];\n }\n\n const {\n rankWith,\n isStringControl,\n isNumberControl,\n isIntegerControl,\n and,\n or,\n isControl,\n schemaMatches,\n isBooleanControl,\n } = jsonformsCore;\n\n // Give PrimeVue renderers a high base rank; vanilla commonly uses small ranks (2–5)\n const PRIME = 100;\n\n // helpers for enum detection - using simple schema checks instead of JSONForms internal functions\n const isScalarEnum = (s?: object) => {\n const schema = s as {\n type?: string;\n enum?: unknown[];\n oneOf?: unknown[];\n };\n return (\n schema &&\n schema.type !== \"array\" &&\n (Array.isArray(schema.enum) || Array.isArray(schema.oneOf))\n );\n };\n\n const isEnumArray = (s?: object) => {\n const schema = s as {\n type?: string;\n items?: { enum?: unknown[]; oneOf?: unknown[] };\n };\n return (\n schema?.type === \"array\" &&\n (Array.isArray(schema.items?.enum) || Array.isArray(schema.items?.oneOf))\n );\n };\n\n // helper for multiline detection\n const isMultilineString = (uischema: unknown, schema: unknown) => {\n const controlUischema = uischema as { options?: { multi?: boolean } };\n return and(isStringControl, () => controlUischema?.options?.multi === true)(\n uischema as Parameters<typeof isStringControl>[0],\n schema as Parameters<typeof isStringControl>[1],\n {} as Parameters<typeof isStringControl>[2],\n );\n };\n\n // Projection-aware schema check: when options.projection is set,\n // resolve the projected schema and test against it instead of the original\n\n const projectedSchemaMatches =\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (check: (schema: any) => boolean) =>\n (uischema: unknown, schema: unknown): boolean => {\n const ui = uischema as {\n type?: string;\n scope?: string;\n options?: { projection?: string };\n };\n const projection = ui?.options?.projection;\n if (!projection || ui?.type !== \"Control\" || !ui?.scope) return false;\n\n const propertySchema = resolveScopeSchema(\n ui.scope,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema as Record<string, any>,\n );\n if (!propertySchema) return false;\n\n return check(getProjectedSchema(propertySchema, projection));\n };\n\n const isMultilineProjection = (uischema: unknown, schema: unknown) => {\n const ui = uischema as { options?: { multi?: boolean } };\n return (\n ui?.options?.multi === true &&\n projectedSchemaMatches((s) => s?.type === \"string\")(uischema, schema)\n );\n };\n\n const renderers = [\n // Multiline text has higher priority than regular text\n {\n tester: rankWith(PRIME + 4, or(isMultilineString, isMultilineProjection)),\n renderer: JfTextArea,\n },\n {\n tester: rankWith(\n PRIME + 3,\n or(\n isStringControl,\n projectedSchemaMatches((s) => s?.type === \"string\"),\n ),\n ),\n renderer: JfText,\n },\n {\n tester: rankWith(\n PRIME + 6,\n or(\n isIntegerControl,\n projectedSchemaMatches((s) => s?.type === \"integer\"),\n ),\n ),\n renderer: JfNumber,\n },\n {\n tester: rankWith(\n PRIME + 4,\n or(\n isNumberControl,\n projectedSchemaMatches((s) => s?.type === \"number\"),\n ),\n ),\n renderer: JfNumber,\n },\n {\n tester: rankWith(\n PRIME + 7,\n or(\n and(isControl, schemaMatches(isScalarEnum)),\n and(isControl, projectedSchemaMatches(isScalarEnum)),\n ),\n ),\n renderer: JfEnum,\n },\n {\n tester: rankWith(\n PRIME + 8,\n or(\n and(isControl, schemaMatches(isEnumArray)),\n and(isControl, projectedSchemaMatches(isEnumArray)),\n ),\n ),\n renderer: JfEnumArray,\n },\n {\n tester: rankWith(\n PRIME + 3,\n or(\n isBooleanControl,\n projectedSchemaMatches((s) => s?.type === \"boolean\"),\n ),\n ),\n renderer: JfBoolean,\n },\n ];\n\n // Update the exported array\n primevueRenderers.splice(0, primevueRenderers.length, ...renderers);\n\n // Mark as initialized to prevent duplicate registrations\n renderersInitialized = true;\n\n return renderers;\n}\n\nexport { JfText, JfTextArea, JfNumber, JfEnum, JfEnumArray, JfBoolean };\n"],"names":["JfTextArea","JfText","JfNumber","JfEnum","JfEnumArray","JfBoolean"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narrative.io/jsonforms-provider-protocols",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta.10",
|
|
4
4
|
"description": "Dynamic data provider capabilities for JSONForms with Vue 3 integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Narrative I/O",
|
|
@@ -84,12 +84,14 @@
|
|
|
84
84
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
85
85
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
86
86
|
"@vue/runtime-core": "^3.5.20",
|
|
87
|
+
"@vue/test-utils": "^2.4.6",
|
|
87
88
|
"bun-git-hooks": "^0.2.19",
|
|
88
89
|
"commitlint": "^20.0.0",
|
|
89
90
|
"eslint": "^9.34.0",
|
|
90
91
|
"eslint-config-prettier": "^10.1.8",
|
|
91
92
|
"eslint-plugin-vue": "^10.4.0",
|
|
92
93
|
"globals": "^16.3.0",
|
|
94
|
+
"happy-dom": "^20.6.3",
|
|
93
95
|
"prettier": "^3.6.2",
|
|
94
96
|
"primevue": "^4.3.8",
|
|
95
97
|
"typescript": "^5.9.2",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize a form data object from a JSON Schema.
|
|
3
|
+
* Resolves $ref, const, default, oneOf/discriminator, and typed empty values.
|
|
4
|
+
*
|
|
5
|
+
* @param schema - The full JSON Schema (must include $defs if $refs are used)
|
|
6
|
+
* @param seed - Optional existing data to merge (seed values take priority)
|
|
7
|
+
* @returns A data object with all schema-defined fields initialized
|
|
8
|
+
*/
|
|
9
|
+
export function initFormDataFromSchema(
|
|
10
|
+
schema: Record<string, unknown>,
|
|
11
|
+
seed?: Record<string, unknown>,
|
|
12
|
+
): Record<string, unknown> {
|
|
13
|
+
const result = initProperty(schema, schema, seed) as Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
// If result is an object and seed has extra keys not in schema, preserve them
|
|
16
|
+
if (
|
|
17
|
+
result &&
|
|
18
|
+
typeof result === "object" &&
|
|
19
|
+
!Array.isArray(result) &&
|
|
20
|
+
seed &&
|
|
21
|
+
typeof seed === "object"
|
|
22
|
+
) {
|
|
23
|
+
const schemaKeys = new Set(
|
|
24
|
+
Object.keys(
|
|
25
|
+
(resolveRef(schema, schema) as Record<string, unknown>)?.properties ??
|
|
26
|
+
{},
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
for (const key of Object.keys(seed)) {
|
|
30
|
+
if (!schemaKeys.has(key) && !(key in result)) {
|
|
31
|
+
result[key] = seed[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result ?? {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a $ref pointer against the root schema's $defs.
|
|
41
|
+
* Supports nested $ref chains.
|
|
42
|
+
*/
|
|
43
|
+
function resolveRef(
|
|
44
|
+
property: Record<string, unknown>,
|
|
45
|
+
root: Record<string, unknown>,
|
|
46
|
+
seen?: Set<string>,
|
|
47
|
+
): Record<string, unknown> {
|
|
48
|
+
if (!property || typeof property !== "object") return property;
|
|
49
|
+
|
|
50
|
+
const ref = property.$ref as string | undefined;
|
|
51
|
+
if (!ref) return property;
|
|
52
|
+
|
|
53
|
+
// Guard against circular refs
|
|
54
|
+
const visited = seen ?? new Set<string>();
|
|
55
|
+
if (visited.has(ref)) return property;
|
|
56
|
+
visited.add(ref);
|
|
57
|
+
|
|
58
|
+
const resolved = resolvePointer(root, ref);
|
|
59
|
+
if (!resolved) return property;
|
|
60
|
+
|
|
61
|
+
// Continue resolving if the result itself has a $ref
|
|
62
|
+
return resolveRef(resolved as Record<string, unknown>, root, visited);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a JSON pointer like "#/$defs/Price" against an object.
|
|
67
|
+
*/
|
|
68
|
+
function resolvePointer(
|
|
69
|
+
obj: Record<string, unknown>,
|
|
70
|
+
pointer: string,
|
|
71
|
+
): unknown {
|
|
72
|
+
if (!pointer.startsWith("#/")) return undefined;
|
|
73
|
+
const parts = pointer.slice(2).split("/");
|
|
74
|
+
let current: unknown = obj;
|
|
75
|
+
for (const part of parts) {
|
|
76
|
+
if (current && typeof current === "object" && part in current) {
|
|
77
|
+
current = (current as Record<string, unknown>)[part];
|
|
78
|
+
} else {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Initialize a single property value based on its schema definition.
|
|
87
|
+
*/
|
|
88
|
+
function initProperty(
|
|
89
|
+
property: Record<string, unknown>,
|
|
90
|
+
root: Record<string, unknown>,
|
|
91
|
+
seed?: unknown,
|
|
92
|
+
): unknown {
|
|
93
|
+
if (!property || typeof property !== "object") return null;
|
|
94
|
+
|
|
95
|
+
// Resolve $ref first
|
|
96
|
+
const resolved = resolveRef(property, root);
|
|
97
|
+
|
|
98
|
+
// Handle oneOf with discriminator — pick first variant
|
|
99
|
+
if (Array.isArray(resolved.oneOf)) {
|
|
100
|
+
return initOneOf(resolved, root, seed);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Priority 1: seed wins (for object types, we merge recursively below)
|
|
104
|
+
// For non-object types, return seed directly if present
|
|
105
|
+
const type = resolved.type as string | undefined;
|
|
106
|
+
if (seed !== undefined && seed !== null && type !== "object") {
|
|
107
|
+
return seed;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Priority 2: const
|
|
111
|
+
if ("const" in resolved) {
|
|
112
|
+
return resolved.const;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Priority 3: single-value enum
|
|
116
|
+
if (
|
|
117
|
+
Array.isArray(resolved.enum) &&
|
|
118
|
+
(resolved.enum as unknown[]).length === 1
|
|
119
|
+
) {
|
|
120
|
+
return (resolved.enum as unknown[])[0];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Priority 4: default
|
|
124
|
+
if ("default" in resolved) {
|
|
125
|
+
return resolved.default;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Priority 5: typed empty values
|
|
129
|
+
switch (type) {
|
|
130
|
+
case "object":
|
|
131
|
+
return initObject(resolved, root, seed as Record<string, unknown>);
|
|
132
|
+
case "array":
|
|
133
|
+
return seed !== undefined && seed !== null ? seed : [];
|
|
134
|
+
case "string":
|
|
135
|
+
return "";
|
|
136
|
+
case "boolean":
|
|
137
|
+
return false;
|
|
138
|
+
case "number":
|
|
139
|
+
case "integer":
|
|
140
|
+
return null;
|
|
141
|
+
default:
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Initialize an object type by recursing into its properties.
|
|
148
|
+
*/
|
|
149
|
+
function initObject(
|
|
150
|
+
schema: Record<string, unknown>,
|
|
151
|
+
root: Record<string, unknown>,
|
|
152
|
+
seed?: Record<string, unknown>,
|
|
153
|
+
): Record<string, unknown> {
|
|
154
|
+
const properties = schema.properties as
|
|
155
|
+
| Record<string, Record<string, unknown>>
|
|
156
|
+
| undefined;
|
|
157
|
+
if (!properties) {
|
|
158
|
+
// Object with no defined properties — return seed or empty object
|
|
159
|
+
return seed && typeof seed === "object" ? { ...seed } : {};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result: Record<string, unknown> = {};
|
|
163
|
+
|
|
164
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
165
|
+
const seedValue = seed && typeof seed === "object" ? seed[key] : undefined;
|
|
166
|
+
result[key] = initProperty(propSchema, root, seedValue);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Handle oneOf schemas — pick the first variant and initialize it.
|
|
174
|
+
*/
|
|
175
|
+
function initOneOf(
|
|
176
|
+
schema: Record<string, unknown>,
|
|
177
|
+
root: Record<string, unknown>,
|
|
178
|
+
seed?: unknown,
|
|
179
|
+
): unknown {
|
|
180
|
+
const variants = schema.oneOf as Record<string, unknown>[];
|
|
181
|
+
if (!variants || variants.length === 0) return null;
|
|
182
|
+
|
|
183
|
+
const first = variants[0];
|
|
184
|
+
if (!first) return null;
|
|
185
|
+
|
|
186
|
+
// Pick first variant, resolve its $ref if needed
|
|
187
|
+
const firstVariant = resolveRef(first, root);
|
|
188
|
+
return initProperty(firstVariant, root, seed);
|
|
189
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection utilities for navigating complex data structures
|
|
3
|
+
* through a dot-separated path where numeric segments are array indices.
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* "0" → first element of an array
|
|
7
|
+
* "include" → the `include` property of an object
|
|
8
|
+
* "0.video_rate_usd" → nested property inside the first array element
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type ProjectionSegment = string | number;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a projection path string into typed segments.
|
|
15
|
+
* Numeric strings become numbers (array indices), others stay as strings (object keys).
|
|
16
|
+
*/
|
|
17
|
+
export function parseProjectionPath(path: string): ProjectionSegment[] {
|
|
18
|
+
if (!path) return [];
|
|
19
|
+
return path.split(".").map((s) => {
|
|
20
|
+
const n = Number(s);
|
|
21
|
+
return Number.isInteger(n) && n >= 0 ? n : s;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read a value from `data` by following the projection path.
|
|
27
|
+
* Returns `undefined` if any segment along the path is missing.
|
|
28
|
+
*/
|
|
29
|
+
export function getProjectedValue(data: unknown, path: string): unknown {
|
|
30
|
+
const segments = parseProjectionPath(path);
|
|
31
|
+
let current: unknown = data;
|
|
32
|
+
|
|
33
|
+
for (const seg of segments) {
|
|
34
|
+
if (current === null || current === undefined) return undefined;
|
|
35
|
+
|
|
36
|
+
if (typeof seg === "number") {
|
|
37
|
+
if (!Array.isArray(current)) return undefined;
|
|
38
|
+
current = current[seg];
|
|
39
|
+
} else {
|
|
40
|
+
if (typeof current !== "object") return undefined;
|
|
41
|
+
current = (current as Record<string, unknown>)[seg];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Immutably set a value at the projection path, preserving all sibling data.
|
|
50
|
+
* Constructs missing intermediate structures (arrays for numeric segments, objects for string segments).
|
|
51
|
+
*/
|
|
52
|
+
export function setProjectedValue(
|
|
53
|
+
data: unknown,
|
|
54
|
+
path: string,
|
|
55
|
+
value: unknown,
|
|
56
|
+
): unknown {
|
|
57
|
+
const segments = parseProjectionPath(path);
|
|
58
|
+
return setAtPath(data, segments, 0, value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function setAtPath(
|
|
62
|
+
current: unknown,
|
|
63
|
+
segments: ProjectionSegment[],
|
|
64
|
+
index: number,
|
|
65
|
+
value: unknown,
|
|
66
|
+
): unknown {
|
|
67
|
+
if (index === segments.length) {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const seg = segments[index]!;
|
|
72
|
+
|
|
73
|
+
if (typeof seg === "number") {
|
|
74
|
+
// Array index — ensure we have an array
|
|
75
|
+
const arr = Array.isArray(current) ? [...current] : [];
|
|
76
|
+
// Pad array if index is out of bounds
|
|
77
|
+
while (arr.length <= seg) {
|
|
78
|
+
arr.push(undefined);
|
|
79
|
+
}
|
|
80
|
+
arr[seg] = setAtPath(arr[seg], segments, index + 1, value);
|
|
81
|
+
return arr;
|
|
82
|
+
} else {
|
|
83
|
+
// Object key — ensure we have an object
|
|
84
|
+
const obj: Record<string, unknown> =
|
|
85
|
+
current !== null &&
|
|
86
|
+
current !== undefined &&
|
|
87
|
+
typeof current === "object" &&
|
|
88
|
+
!Array.isArray(current)
|
|
89
|
+
? { ...(current as Record<string, unknown>) }
|
|
90
|
+
: {};
|
|
91
|
+
obj[seg] = setAtPath(obj[seg], segments, index + 1, value);
|
|
92
|
+
return obj;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the schema at the projected path.
|
|
98
|
+
* Numeric segments traverse into `items` (array item schema).
|
|
99
|
+
* String segments traverse into `properties[segment]`.
|
|
100
|
+
*/
|
|
101
|
+
export function getProjectedSchema(
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
schema: Record<string, any>,
|
|
104
|
+
path: string,
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
): Record<string, any> {
|
|
107
|
+
const segments = parseProjectionPath(path);
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
|
+
let current: Record<string, any> = schema;
|
|
110
|
+
|
|
111
|
+
for (const seg of segments) {
|
|
112
|
+
if (!current) return {};
|
|
113
|
+
|
|
114
|
+
if (typeof seg === "number") {
|
|
115
|
+
// Array index → traverse into items schema
|
|
116
|
+
const items = current.items;
|
|
117
|
+
if (items && typeof items === "object") {
|
|
118
|
+
current = items as Record<string, unknown>;
|
|
119
|
+
} else {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// Object key → traverse into properties[key]
|
|
124
|
+
const properties = current.properties as
|
|
125
|
+
| Record<string, Record<string, unknown>>
|
|
126
|
+
| undefined;
|
|
127
|
+
if (properties && properties[seg]) {
|
|
128
|
+
current = properties[seg];
|
|
129
|
+
} else {
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return current;
|
|
136
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a JSON Forms scope path to its schema within a root schema.
|
|
3
|
+
* Handles nested paths like "#/properties/parent/properties/child".
|
|
4
|
+
*
|
|
5
|
+
* Follows JSON Schema structure:
|
|
6
|
+
* - "properties" segments navigate into object `.properties`
|
|
7
|
+
* - "items" segments navigate into array `.items`
|
|
8
|
+
* - all other segments index directly into the current object
|
|
9
|
+
*/
|
|
10
|
+
export function resolveScopeSchema(
|
|
11
|
+
scope: string,
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
rootSchema: Record<string, any>,
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
): Record<string, any> | undefined {
|
|
16
|
+
if (!scope || !rootSchema) return undefined;
|
|
17
|
+
|
|
18
|
+
// Remove the leading "#/" and split into segments
|
|
19
|
+
const path = scope.replace(/^#\/?/, "");
|
|
20
|
+
if (!path) return rootSchema;
|
|
21
|
+
|
|
22
|
+
const segments = path.split("/");
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
let current: any = rootSchema;
|
|
25
|
+
|
|
26
|
+
for (const segment of segments) {
|
|
27
|
+
if (!current || typeof current !== "object") return undefined;
|
|
28
|
+
|
|
29
|
+
if (segment === "properties") {
|
|
30
|
+
current = current.properties;
|
|
31
|
+
} else if (segment === "items") {
|
|
32
|
+
current = current.items;
|
|
33
|
+
} else {
|
|
34
|
+
current = current[segment];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return current;
|
|
39
|
+
}
|