@holo-js/forms 0.3.12 → 0.3.15
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/dist/internal/client.d.ts +22 -3
- package/dist/internal/client.mjs +78 -23
- package/package.json +6 -3
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { WebFileLike, SchemaInputShape, FieldBuilderInput, InferFieldOutput
|
|
1
|
+
import { S as SerializedFormSubmission, c as FormFailurePayload, i as FormSuccessPayload, g as FormSubmissionResult } from '../contracts-BOhBkI9c.js';
|
|
2
|
+
import { ValidationErrorBag, WebFileLike, SchemaInputShape, FieldBuilderInput, InferFieldOutput } from '@holo-js/validation';
|
|
3
3
|
import { FormSchema, InferFormData } from '../schema.js';
|
|
4
4
|
|
|
5
|
+
declare function collectFormDirtyPaths(current: unknown, initial: unknown, prefix?: string): readonly string[];
|
|
6
|
+
declare class FormClientState<TValues, TSuccess = unknown> {
|
|
7
|
+
#private;
|
|
8
|
+
values: TValues;
|
|
9
|
+
initialValues: TValues;
|
|
10
|
+
flattenedErrors: Record<string, readonly string[]>;
|
|
11
|
+
touched: Set<string>;
|
|
12
|
+
dirty: Set<string>;
|
|
13
|
+
lastSubmission?: SerializedFormSubmission<TValues> | FormFailurePayload<TValues> | FormSuccessPayload<TSuccess>;
|
|
14
|
+
readonly listeners: Set<() => void>;
|
|
15
|
+
validationSequence: number;
|
|
16
|
+
constructor(values: TValues, initialState?: SerializedFormSubmission<TValues> | FormFailurePayload<TValues>);
|
|
17
|
+
get submitting(): boolean;
|
|
18
|
+
get errors(): ValidationErrorBag<TValues>;
|
|
19
|
+
get dirtyPaths(): readonly string[];
|
|
20
|
+
replace(values: TValues, initialValues: TValues, errors: Record<string, readonly string[]>, touched: ReadonlySet<string>): void;
|
|
21
|
+
startSubmission(signal?: AbortSignal): () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
5
24
|
type PrimitiveLike = string | number | boolean | bigint | symbol | null | undefined | Date | Blob | WebFileLike;
|
|
6
25
|
type ValidateOnMode = 'submit' | 'blur' | 'change';
|
|
7
26
|
interface ClientSubmitContext<TData> {
|
|
@@ -66,4 +85,4 @@ declare function runWithBrowserFormElement<TData, TSuccess>(formClient: Pick<Use
|
|
|
66
85
|
*/
|
|
67
86
|
declare function createFormClient<TSchema extends FormSchema, TSuccess = unknown>(schemaDefinition: TSchema, options?: UseFormOptions<InferFormData<TSchema>, TSuccess>): UseFormResult<InferFormData<TSchema>, TSuccess, InferFormFieldTree<TSchema>>;
|
|
68
87
|
|
|
69
|
-
export { type ClientSubmitContext, type ClientSubmitResult, type FormFieldState, type FormFieldTree, type InferFormFieldTree, type UseFormOptions, type UseFormResult, type ValidateOnMode, createFormClient, markClientSubmitControlFlowError, runWithBrowserFormElement };
|
|
88
|
+
export { type ClientSubmitContext, type ClientSubmitResult, FormClientState, type FormFieldState, type FormFieldTree, type InferFormFieldTree, type UseFormOptions, type UseFormResult, type ValidateOnMode, collectFormDirtyPaths, createFormClient, markClientSubmitControlFlowError, runWithBrowserFormElement };
|
package/dist/internal/client.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
// src/internal/client.ts
|
|
18
18
|
import {
|
|
19
|
-
createErrorBag,
|
|
19
|
+
createErrorBag as createErrorBag2,
|
|
20
20
|
safeParse
|
|
21
21
|
} from "@holo-js/validation";
|
|
22
22
|
|
|
@@ -72,9 +72,9 @@ async function getClientCsrfField() {
|
|
|
72
72
|
|
|
73
73
|
// src/internal/validation-exception.ts
|
|
74
74
|
import {
|
|
75
|
-
isValidationException
|
|
76
|
-
validationInternals
|
|
75
|
+
isValidationException
|
|
77
76
|
} from "@holo-js/validation";
|
|
77
|
+
import { parseValidationExceptionDigest } from "@holo-js/validation/internal/compiled";
|
|
78
78
|
function isPlainObject(value) {
|
|
79
79
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
80
80
|
}
|
|
@@ -104,7 +104,7 @@ function formFailurePayloadFromError(error) {
|
|
|
104
104
|
return candidates.find((candidate) => isFormFailurePayload(candidate));
|
|
105
105
|
}
|
|
106
106
|
function validationExceptionToFailure(error, fallbackValues = {}) {
|
|
107
|
-
const payload = isValidationException(error) ? error.toJSON() :
|
|
107
|
+
const payload = isValidationException(error) ? error.toJSON() : parseValidationExceptionDigest(error);
|
|
108
108
|
if (payload) {
|
|
109
109
|
return {
|
|
110
110
|
ok: false,
|
|
@@ -133,6 +133,9 @@ function validationExceptionToFailure(error, fallbackValues = {}) {
|
|
|
133
133
|
return void 0;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// src/internal/state.ts
|
|
137
|
+
import { createErrorBag } from "@holo-js/validation";
|
|
138
|
+
|
|
136
139
|
// src/internal/formValues.ts
|
|
137
140
|
function isPlainFormObject(value) {
|
|
138
141
|
return !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob);
|
|
@@ -250,6 +253,69 @@ function buildFormData(value, path = "", formData = new FormData()) {
|
|
|
250
253
|
return formData;
|
|
251
254
|
}
|
|
252
255
|
|
|
256
|
+
// src/internal/state.ts
|
|
257
|
+
function collectFormDirtyPaths(current, initial, prefix = "") {
|
|
258
|
+
if (areFormValuesEqual(current, initial)) return [];
|
|
259
|
+
if (Array.isArray(current) && Array.isArray(initial)) {
|
|
260
|
+
const paths = Array.from(
|
|
261
|
+
{ length: Math.max(current.length, initial.length) },
|
|
262
|
+
(_, index) => collectFormDirtyPaths(current[index], initial[index], prefix ? `${prefix}.${index}` : String(index))
|
|
263
|
+
).flat();
|
|
264
|
+
return paths.length > 0 ? paths : [prefix];
|
|
265
|
+
}
|
|
266
|
+
if (isPlainFormObject(current) && isPlainFormObject(initial)) {
|
|
267
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(current), ...Object.keys(initial)])].flatMap(
|
|
268
|
+
(key) => collectFormDirtyPaths(current[key], initial[key], prefix ? `${prefix}.${key}` : key)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return prefix ? [prefix] : [];
|
|
272
|
+
}
|
|
273
|
+
var FormClientState = class {
|
|
274
|
+
values;
|
|
275
|
+
initialValues;
|
|
276
|
+
flattenedErrors;
|
|
277
|
+
touched = /* @__PURE__ */ new Set();
|
|
278
|
+
dirty = /* @__PURE__ */ new Set();
|
|
279
|
+
lastSubmission;
|
|
280
|
+
listeners = /* @__PURE__ */ new Set();
|
|
281
|
+
validationSequence = 0;
|
|
282
|
+
#submissions = /* @__PURE__ */ new Set();
|
|
283
|
+
constructor(values, initialState) {
|
|
284
|
+
this.values = cloneFormValue(values);
|
|
285
|
+
this.initialValues = cloneFormValue(values);
|
|
286
|
+
this.flattenedErrors = { ...initialState?.errors };
|
|
287
|
+
this.lastSubmission = initialState;
|
|
288
|
+
}
|
|
289
|
+
get submitting() {
|
|
290
|
+
return this.#submissions.size > 0;
|
|
291
|
+
}
|
|
292
|
+
get errors() {
|
|
293
|
+
return createErrorBag(this.flattenedErrors);
|
|
294
|
+
}
|
|
295
|
+
get dirtyPaths() {
|
|
296
|
+
return collectFormDirtyPaths(this.values, this.initialValues);
|
|
297
|
+
}
|
|
298
|
+
replace(values, initialValues, errors, touched) {
|
|
299
|
+
this.values = values;
|
|
300
|
+
this.initialValues = initialValues;
|
|
301
|
+
this.flattenedErrors = errors;
|
|
302
|
+
this.touched = new Set(touched);
|
|
303
|
+
this.dirty = new Set(this.dirtyPaths);
|
|
304
|
+
}
|
|
305
|
+
startSubmission(signal) {
|
|
306
|
+
const id = /* @__PURE__ */ Symbol();
|
|
307
|
+
const finish = () => {
|
|
308
|
+
signal?.removeEventListener("abort", finish);
|
|
309
|
+
this.#submissions.delete(id);
|
|
310
|
+
};
|
|
311
|
+
if (!signal?.aborted) {
|
|
312
|
+
this.#submissions.add(id);
|
|
313
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
314
|
+
}
|
|
315
|
+
return finish;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
253
319
|
// src/internal/client.ts
|
|
254
320
|
var browserFormSubmitSymbol = /* @__PURE__ */ Symbol("browserFormSubmit");
|
|
255
321
|
var clientSubmitControlFlowErrors = /* @__PURE__ */ new WeakSet();
|
|
@@ -322,14 +388,14 @@ function createSubmission(valid, values, errors, failureStatus = 422, schemaDefi
|
|
|
322
388
|
});
|
|
323
389
|
}
|
|
324
390
|
function createSuccessfulSubmission(schemaDefinition, data) {
|
|
325
|
-
return createSubmission(true, data,
|
|
391
|
+
return createSubmission(true, data, createErrorBag2(), 422, schemaDefinition);
|
|
326
392
|
}
|
|
327
393
|
function createFailedSubmission(schemaDefinition, values, flattenedErrors, status = 422) {
|
|
328
394
|
void schemaDefinition;
|
|
329
395
|
return createSubmission(
|
|
330
396
|
false,
|
|
331
397
|
values,
|
|
332
|
-
|
|
398
|
+
createErrorBag2(flattenedErrors),
|
|
333
399
|
normalizeStatus(status, 422),
|
|
334
400
|
schemaDefinition
|
|
335
401
|
);
|
|
@@ -380,9 +446,6 @@ async function runWithBrowserFormElement(formClient, form) {
|
|
|
380
446
|
}
|
|
381
447
|
return await formClient.submit();
|
|
382
448
|
}
|
|
383
|
-
function createTypedErrorBag(flattenedErrors) {
|
|
384
|
-
return createErrorBag(flattenedErrors);
|
|
385
|
-
}
|
|
386
449
|
function notifyListeners(state) {
|
|
387
450
|
for (const listener of state.listeners) {
|
|
388
451
|
listener();
|
|
@@ -634,17 +697,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
634
697
|
normalizeFormObject(options.initialValues),
|
|
635
698
|
normalizeFormObject(initialState?.values)
|
|
636
699
|
);
|
|
637
|
-
const state =
|
|
638
|
-
values: cloneFormValue(initialValues),
|
|
639
|
-
initialValues: cloneFormValue(initialValues),
|
|
640
|
-
flattenedErrors: { ...initialState?.errors ?? {} },
|
|
641
|
-
touched: /* @__PURE__ */ new Set(),
|
|
642
|
-
dirty: /* @__PURE__ */ new Set(),
|
|
643
|
-
submitting: false,
|
|
644
|
-
lastSubmission: initialState,
|
|
645
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
646
|
-
validationSequence: 0
|
|
647
|
-
};
|
|
700
|
+
const state = new FormClientState(initialValues, initialState);
|
|
648
701
|
const validateOn = options.validateOn ?? "submit";
|
|
649
702
|
const fieldPaths = flattenFormLeafPaths(schemaDefinition.fields);
|
|
650
703
|
const fields = buildFieldsTree(
|
|
@@ -702,7 +755,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
702
755
|
return normalizedSubmission;
|
|
703
756
|
}
|
|
704
757
|
async function submit(browserForm) {
|
|
705
|
-
|
|
758
|
+
const finishSubmission = state.startSubmission();
|
|
706
759
|
notifyListeners(state);
|
|
707
760
|
try {
|
|
708
761
|
const submitter = options.submitter ?? defaultSubmitter;
|
|
@@ -743,7 +796,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
743
796
|
data: void 0
|
|
744
797
|
});
|
|
745
798
|
} finally {
|
|
746
|
-
|
|
799
|
+
finishSubmission();
|
|
747
800
|
notifyListeners(state);
|
|
748
801
|
}
|
|
749
802
|
}
|
|
@@ -753,7 +806,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
753
806
|
return state.values;
|
|
754
807
|
},
|
|
755
808
|
get errors() {
|
|
756
|
-
return
|
|
809
|
+
return state.errors;
|
|
757
810
|
},
|
|
758
811
|
get submitting() {
|
|
759
812
|
return state.submitting;
|
|
@@ -813,6 +866,8 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
813
866
|
});
|
|
814
867
|
}
|
|
815
868
|
export {
|
|
869
|
+
FormClientState,
|
|
870
|
+
collectFormDirtyPaths,
|
|
816
871
|
createFormClient,
|
|
817
872
|
markClientSubmitControlFlowError,
|
|
818
873
|
runWithBrowserFormElement
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/forms",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"description": "Holo-JS Framework - forms-first validation and submission contracts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"test": "vitest --run"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@holo-js/validation": "^0.3.
|
|
36
|
+
"@holo-js/validation": "^0.3.15"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@holo-js/security": "^0.3.
|
|
39
|
+
"@holo-js/security": "^0.3.15"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@holo-js/security": {
|
|
@@ -48,5 +48,8 @@
|
|
|
48
48
|
"tsup": "^8.3.5",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.5"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22.12.0"
|
|
51
54
|
}
|
|
52
55
|
}
|