jsonisch 0.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/form-ref-BEri3JKv.js +1123 -0
- package/dist/form-ref-D_xHSaqL.d.ts +994 -0
- package/dist/handle-submit-CD3WY9gW.js +208 -0
- package/dist/handle-submit-Dk7QW3_q.d.ts +34 -0
- package/dist/index.d.ts +724 -0
- package/dist/index.js +1521 -0
- package/dist/plugin/index.d.ts +188 -0
- package/dist/plugin/index.js +4 -0
- package/dist/plugin-BCsAE8RK.d.ts +612 -0
- package/dist/plugin-Qka-vaO1.js +1545 -0
- package/dist/react/index.d.ts +476 -0
- package/dist/react/index.js +456 -0
- package/docs/decode-fork.md +24 -0
- package/docs/plugin-lifecycle.md +44 -0
- package/docs/rerender-history.md +61 -0
- package/docs/wire-shapes.md +70 -0
- package/package.json +87 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { A as unwrapLeafInput, B as getFieldBool, L as untrack, M as batch, V as walkFieldStore, _ as resolveValueInput, b as dispatchReseedScope, c as isPresenceEqual, g as readOwn, j as createId, l as isSemanticEqual, m as containerPresence, n as validateFormInput, o as getFieldStoreChain, p as initializeFieldStore, t as internalOf, w as dispatchSyncInput } from "./form-ref-BEri3JKv.js";
|
|
2
|
+
|
|
3
|
+
//#region src/core/field/reset-item-state.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resets the state of a field store (signal values) deeply. Clears
|
|
6
|
+
* `elements`, `errors`, `isTouched`, `isEdited` and `isDirty`, and sets
|
|
7
|
+
* `startInput`, `input`, `startItems` and `items` to the new input value.
|
|
8
|
+
* Keeps `initialInput` and `initialItems` unchanged for form reset.
|
|
9
|
+
*
|
|
10
|
+
* @param internalFormStore The form store providing the empty input config.
|
|
11
|
+
* @param internalFieldStore The field store to reset.
|
|
12
|
+
* @param input The new input value.
|
|
13
|
+
* @param keepStart Whether to keep `startInput` and `startItems` as the
|
|
14
|
+
* dirty baseline instead of resetting them to the new input. Used when a
|
|
15
|
+
* field store is reused for an in-place edit (array shrink-then-regrow) so
|
|
16
|
+
* its dirty state stays detectable against the original baseline.
|
|
17
|
+
*/
|
|
18
|
+
function resetItemState(internalFormStore, internalFieldStore, input, keepStart = false) {
|
|
19
|
+
batch(() => {
|
|
20
|
+
const elements = [];
|
|
21
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
22
|
+
internalFieldStore.elements = elements;
|
|
23
|
+
internalFieldStore.validationErrors.value = null;
|
|
24
|
+
internalFieldStore.isTouched.value = false;
|
|
25
|
+
internalFieldStore.isEdited.value = false;
|
|
26
|
+
internalFieldStore.isDirty.value = false;
|
|
27
|
+
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
28
|
+
const presence = containerPresence(internalFieldStore.isNullish, input);
|
|
29
|
+
if (!keepStart) internalFieldStore.startInput.value = presence;
|
|
30
|
+
internalFieldStore.input.value = presence;
|
|
31
|
+
if (internalFieldStore.kind === "array") if (Array.isArray(input)) {
|
|
32
|
+
const newItems = input.map(() => createId());
|
|
33
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
34
|
+
internalFieldStore.items.value = newItems;
|
|
35
|
+
for (let index = 0; index < input.length; index++) if (internalFieldStore.children[index]) resetItemState(internalFormStore, internalFieldStore.children[index], input[index], keepStart);
|
|
36
|
+
else {
|
|
37
|
+
internalFieldStore.children[index] = {};
|
|
38
|
+
initializeFieldStore(internalFormStore, internalFieldStore.children[index], internalFieldStore.itemSchema, input[index], [...internalFieldStore.path, index]);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
42
|
+
internalFieldStore.items.value = [];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
for (const key in internalFieldStore.children) resetItemState(internalFormStore, internalFieldStore.children[key], readOwn(input, key), keepStart);
|
|
46
|
+
if (typeof internalFieldStore.path[internalFieldStore.path.length - 1] === "number") dispatchReseedScope(internalFormStore, internalFieldStore, input);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
const valueInput = resolveValueInput(internalFormStore.emptyInput, internalFieldStore.schema, internalFieldStore.isNullish, unwrapLeafInput(internalFormStore, internalFieldStore.control, input));
|
|
50
|
+
if (!keepStart) internalFieldStore.startInput.value = valueInput;
|
|
51
|
+
internalFieldStore.input.value = valueInput;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/core/field/set-field-input.ts
|
|
58
|
+
/**
|
|
59
|
+
* Recomputes a container's dirty flag from its presence sentinel (and, for
|
|
60
|
+
* arrays, its item count) against the start baseline. Content changes live
|
|
61
|
+
* on the children, not here.
|
|
62
|
+
*/
|
|
63
|
+
function computeContainerDirty(internalFieldStore) {
|
|
64
|
+
const presenceDirty = !isPresenceEqual(internalFieldStore.startInput.value, internalFieldStore.input.value);
|
|
65
|
+
if (internalFieldStore.kind === "array") return presenceDirty || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
|
|
66
|
+
return presenceDirty;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Sets the input for a nested field store and all its children, updating
|
|
70
|
+
* touched, edited and dirty states. Handles dynamic array resizing with
|
|
71
|
+
* child-store reuse (a regrown index keeps its dirty baseline, so
|
|
72
|
+
* shrink-then-regrow behaves exactly like a direct edit).
|
|
73
|
+
*
|
|
74
|
+
* @param internalFormStore The form store providing the empty input config.
|
|
75
|
+
* @param internalFieldStore The field store to update.
|
|
76
|
+
* @param input The new input value.
|
|
77
|
+
*/
|
|
78
|
+
function setNestedInput(internalFormStore, internalFieldStore, input) {
|
|
79
|
+
internalFieldStore.isTouched.value = true;
|
|
80
|
+
internalFieldStore.isEdited.value = true;
|
|
81
|
+
if (internalFieldStore.kind === "array") {
|
|
82
|
+
const arrayInput = Array.isArray(input) ? input : [];
|
|
83
|
+
const items = internalFieldStore.items.value;
|
|
84
|
+
const length = arrayInput.length;
|
|
85
|
+
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
86
|
+
else if (length > items.length) {
|
|
87
|
+
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFormStore, internalFieldStore.children[index], arrayInput[index], true);
|
|
88
|
+
else {
|
|
89
|
+
internalFieldStore.children[index] = {};
|
|
90
|
+
initializeFieldStore(internalFormStore, internalFieldStore.children[index], internalFieldStore.itemSchema, arrayInput[index], [...internalFieldStore.path, index]);
|
|
91
|
+
}
|
|
92
|
+
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, () => createId())];
|
|
93
|
+
}
|
|
94
|
+
for (let index = 0; index < length; index++) setNestedInput(internalFormStore, internalFieldStore.children[index], arrayInput[index]);
|
|
95
|
+
internalFieldStore.input.value = input == null ? input : true;
|
|
96
|
+
internalFieldStore.isDirty.value = computeContainerDirty(internalFieldStore);
|
|
97
|
+
} else if (internalFieldStore.kind === "object") {
|
|
98
|
+
for (const key in internalFieldStore.children) setNestedInput(internalFormStore, internalFieldStore.children[key], readOwn(input, key));
|
|
99
|
+
internalFieldStore.input.value = input == null ? input : true;
|
|
100
|
+
internalFieldStore.isDirty.value = computeContainerDirty(internalFieldStore);
|
|
101
|
+
} else {
|
|
102
|
+
if (dispatchSyncInput(internalFormStore, internalFieldStore, input)) return;
|
|
103
|
+
internalFieldStore.input.value = input;
|
|
104
|
+
internalFieldStore.isDirty.value = !isSemanticEqual(input, internalFieldStore.startInput.value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Sets the input for the field at the specified path in the form store,
|
|
109
|
+
* marking all parent containers along the way as present and recomputing
|
|
110
|
+
* their presence dirtiness (a nullish container transitioned to present by
|
|
111
|
+
* a nested set is a real change and must reach the dirty projections).
|
|
112
|
+
*
|
|
113
|
+
* @param internalFormStore The form store containing the field.
|
|
114
|
+
* @param path The path to the field.
|
|
115
|
+
* @param input The new input value.
|
|
116
|
+
*/
|
|
117
|
+
function setFieldInput(internalFormStore, path, input) {
|
|
118
|
+
batch(() => {
|
|
119
|
+
untrack(() => {
|
|
120
|
+
const chain = getFieldStoreChain(internalFormStore, path);
|
|
121
|
+
const target = chain[chain.length - 1];
|
|
122
|
+
for (let index = 1; index < chain.length - 1; index++) {
|
|
123
|
+
const ancestor = chain[index];
|
|
124
|
+
if (ancestor.kind === "value") continue;
|
|
125
|
+
ancestor.input.value = true;
|
|
126
|
+
ancestor.isDirty.value = computeContainerDirty(ancestor);
|
|
127
|
+
}
|
|
128
|
+
setNestedInput(internalFormStore, target, input);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/core/form/validate-if-required.ts
|
|
135
|
+
/**
|
|
136
|
+
* Validates the form input if required by the configured modes: the form's
|
|
137
|
+
* `validate` mode applies until the form is in the "already validated"
|
|
138
|
+
* state — submitted (for `validate: "submit"`), or the triggering subtree
|
|
139
|
+
* has errors (any other mode) — after which the `revalidate` mode takes
|
|
140
|
+
* over. `validate: "initial"` forms always run in revalidate mode.
|
|
141
|
+
*
|
|
142
|
+
* @param internalFormStore The form store to validate.
|
|
143
|
+
* @param internalFieldStore The field store that triggered validation.
|
|
144
|
+
* @param validationMode The validation mode of the triggering event.
|
|
145
|
+
*/
|
|
146
|
+
function validateIfRequired(internalFormStore, internalFieldStore, validationMode) {
|
|
147
|
+
if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/core/field/set-field-bool.ts
|
|
152
|
+
/**
|
|
153
|
+
* Sets the specified boolean property for the field store and all nested
|
|
154
|
+
* children.
|
|
155
|
+
*
|
|
156
|
+
* @param internalFieldStore The field store to update.
|
|
157
|
+
* @param type The boolean property type to set.
|
|
158
|
+
* @param bool The boolean value to set.
|
|
159
|
+
*/
|
|
160
|
+
function setFieldBool(internalFieldStore, type, bool) {
|
|
161
|
+
batch(() => {
|
|
162
|
+
untrack(() => {
|
|
163
|
+
walkFieldStore(internalFieldStore, (fieldStore) => {
|
|
164
|
+
fieldStore[type].value = bool;
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/methods/handle-submit.ts
|
|
172
|
+
/**
|
|
173
|
+
* Creates a submit event handler for the form: prevents default browser
|
|
174
|
+
* submission, marks every field touched (errors must be visible everywhere
|
|
175
|
+
* after a submit attempt), validates the form input, and calls the provided
|
|
176
|
+
* handler with the validated output if validation succeeds — an invalid
|
|
177
|
+
* form blocks the handler and focuses the first erroring field. A handler
|
|
178
|
+
* throw lands as a form-level (root) error. Re-entrant submits while
|
|
179
|
+
* `isSubmitting` are ignored.
|
|
180
|
+
*
|
|
181
|
+
* @param form The form store to handle submission for.
|
|
182
|
+
* @param handler The submit handler called with the validated output.
|
|
183
|
+
*
|
|
184
|
+
* @returns A submit event handler to attach to the form element.
|
|
185
|
+
*/
|
|
186
|
+
function handleSubmit(form, handler) {
|
|
187
|
+
return async (event) => {
|
|
188
|
+
event?.preventDefault();
|
|
189
|
+
const internalFormStore = internalOf(form);
|
|
190
|
+
if (untrack(() => internalFormStore.isSubmitting.value)) return;
|
|
191
|
+
batch(() => {
|
|
192
|
+
internalFormStore.isSubmitted.value = true;
|
|
193
|
+
internalFormStore.isSubmitting.value = true;
|
|
194
|
+
setFieldBool(internalFormStore, "isTouched", true);
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
const result = validateFormInput(internalFormStore, { shouldFocus: true });
|
|
198
|
+
if (result.success) await handler(result.output, event);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
internalFormStore.validationErrors.value = [error && typeof error === "object" && "message" in error && typeof error.message === "string" ? error.message : "An unknown error has occurred."];
|
|
201
|
+
} finally {
|
|
202
|
+
internalFormStore.isSubmitting.value = false;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
export { setFieldInput as a, computeContainerDirty as i, setFieldBool as n, resetItemState as o, validateIfRequired as r, handleSubmit as t };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { t as FormRef } from "./form-ref-D_xHSaqL.js";
|
|
2
|
+
|
|
3
|
+
//#region src/methods/handle-submit.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The minimal submit event shape the handler needs — structurally
|
|
7
|
+
* compatible with both the native `SubmitEvent` and React's synthetic form
|
|
8
|
+
* event.
|
|
9
|
+
*/
|
|
10
|
+
interface SubmitLikeEvent {
|
|
11
|
+
preventDefault: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The submit handler called with the validated form output when validation
|
|
15
|
+
* succeeds.
|
|
16
|
+
*/
|
|
17
|
+
type SubmitHandler = (output: Record<string, unknown>, event?: SubmitLikeEvent) => unknown | Promise<unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a submit event handler for the form: prevents default browser
|
|
20
|
+
* submission, marks every field touched (errors must be visible everywhere
|
|
21
|
+
* after a submit attempt), validates the form input, and calls the provided
|
|
22
|
+
* handler with the validated output if validation succeeds — an invalid
|
|
23
|
+
* form blocks the handler and focuses the first erroring field. A handler
|
|
24
|
+
* throw lands as a form-level (root) error. Re-entrant submits while
|
|
25
|
+
* `isSubmitting` are ignored.
|
|
26
|
+
*
|
|
27
|
+
* @param form The form store to handle submission for.
|
|
28
|
+
* @param handler The submit handler called with the validated output.
|
|
29
|
+
*
|
|
30
|
+
* @returns A submit event handler to attach to the form element.
|
|
31
|
+
*/
|
|
32
|
+
declare function handleSubmit(form: FormRef, handler: SubmitHandler): (event?: SubmitLikeEvent) => Promise<void>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { handleSubmit as n, SubmitHandler as t };
|