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.
@@ -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 };