attaform 0.17.2 → 0.18.1
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 +77 -36
- package/dist/chunks/devtools.cjs +10 -37
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +10 -37
- package/dist/chunks/devtools.mjs.map +1 -1
- package/dist/chunks/indexeddb.cjs +4 -4
- package/dist/chunks/indexeddb.cjs.map +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +2 -2
- package/dist/chunks/local-storage.cjs.map +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +2 -2
- package/dist/chunks/session-storage.cjs.map +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +42 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -196
- package/dist/index.d.mts +159 -196
- package/dist/index.d.ts +159 -196
- package/dist/index.mjs +5 -7
- package/dist/index.mjs.map +1 -1
- package/dist/nuxt.cjs +31 -40
- package/dist/nuxt.cjs.map +1 -1
- package/dist/nuxt.d.cts +8 -1
- package/dist/nuxt.d.mts +8 -1
- package/dist/nuxt.d.ts +8 -1
- package/dist/nuxt.mjs +32 -41
- package/dist/nuxt.mjs.map +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.d.vue.ts +7 -0
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +453 -0
- package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +7 -0
- package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +37 -0
- package/dist/runtime/components/DevtoolsValueTree.vue +192 -0
- package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +37 -0
- package/dist/runtime/plugins/attaform.cjs +17 -6
- package/dist/runtime/plugins/attaform.cjs.map +1 -1
- package/dist/runtime/plugins/attaform.mjs +15 -4
- package/dist/runtime/plugins/attaform.mjs.map +1 -1
- package/dist/shared/{attaform.C0iFnTN0.d.ts → attaform.2b7M2mww.d.mts} +57 -23
- package/dist/shared/attaform.5UhpSVFI.cjs +63 -0
- package/dist/shared/attaform.5UhpSVFI.cjs.map +1 -0
- package/dist/shared/attaform.BDdFdjeX.mjs +57 -0
- package/dist/shared/attaform.BDdFdjeX.mjs.map +1 -0
- package/dist/shared/{attaform.Dee2rU1P.cjs → attaform.BqK_L4gK.cjs} +310 -24
- package/dist/shared/attaform.BqK_L4gK.cjs.map +1 -0
- package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
- package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
- package/dist/shared/{attaform.Drt6fivF.mjs → attaform.CtNUB9nf.mjs} +74 -76
- package/dist/shared/attaform.CtNUB9nf.mjs.map +1 -0
- package/dist/shared/{attaform.C5MH4lNh.d.mts → attaform.DF8wo-ry.d.ts} +4 -4
- package/dist/shared/attaform.DK9aj0N8.d.ts +1651 -0
- package/dist/shared/{attaform.BPRHR3Zs.cjs → attaform.DUHru0OF.cjs} +83 -85
- package/dist/shared/attaform.DUHru0OF.cjs.map +1 -0
- package/dist/shared/{attaform.C6lbmMUe.d.ts → attaform.DVLB6CAn.d.mts} +4 -4
- package/dist/shared/{attaform.C_5aB6EQ.d.ts → attaform.Dj9mwbaV.d.cts} +756 -148
- package/dist/shared/{attaform.C_5aB6EQ.d.mts → attaform.Dj9mwbaV.d.mts} +756 -148
- package/dist/shared/{attaform.C_5aB6EQ.d.cts → attaform.Dj9mwbaV.d.ts} +756 -148
- package/dist/shared/{attaform.BV40t5y2.cjs → attaform.Dlk1jMuv.cjs} +245 -108
- package/dist/shared/attaform.Dlk1jMuv.cjs.map +1 -0
- package/dist/shared/attaform.DoSuaKMd.d.cts +1651 -0
- package/dist/shared/{attaform.B3ZaPIzS.mjs → attaform.DsC3rZHG.mjs} +1804 -219
- package/dist/shared/attaform.DsC3rZHG.mjs.map +1 -0
- package/dist/shared/{attaform.Cer8JO_P.cjs → attaform.II89Pcf4.cjs} +1860 -272
- package/dist/shared/attaform.II89Pcf4.cjs.map +1 -0
- package/dist/shared/{attaform.CHorcsIU.d.cts → attaform.M33WKVV4.d.cts} +57 -23
- package/dist/shared/{attaform.CIEQgJnM.mjs → attaform.Xhg0AYNa.mjs} +300 -26
- package/dist/shared/attaform.Xhg0AYNa.mjs.map +1 -0
- package/dist/shared/{attaform.CpERWz3u.mjs → attaform.Xt0A3QUd.mjs} +232 -95
- package/dist/shared/attaform.Xt0A3QUd.mjs.map +1 -0
- package/dist/shared/attaform.iTqxvl-P.d.mts +1651 -0
- package/dist/shared/{attaform.CuE-bS1C.d.mts → attaform.tsNFcEW7.d.ts} +57 -23
- package/dist/shared/{attaform.DtMN-MAm.d.cts → attaform.tts_OM7j.d.cts} +4 -4
- package/dist/vite.cjs +288 -2
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +288 -2
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +11 -8
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.d.cts +6 -6
- package/dist/zod-v3.d.mts +6 -6
- package/dist/zod-v3.d.ts +6 -6
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +11 -8
- package/dist/zod-v4.cjs.map +1 -1
- package/dist/zod-v4.d.cts +5 -5
- package/dist/zod-v4.d.mts +5 -5
- package/dist/zod-v4.d.ts +5 -5
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +15 -16
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +127 -40
- package/dist/zod.d.mts +127 -40
- package/dist/zod.d.ts +127 -40
- package/dist/zod.mjs +7 -11
- package/dist/zod.mjs.map +1 -1
- package/package.json +18 -7
- package/dist/shared/attaform.B1jvxsOF.d.mts +0 -156
- package/dist/shared/attaform.B3ZaPIzS.mjs.map +0 -1
- package/dist/shared/attaform.BBM2muQ9.cjs +0 -101
- package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
- package/dist/shared/attaform.BPRHR3Zs.cjs.map +0 -1
- package/dist/shared/attaform.BV40t5y2.cjs.map +0 -1
- package/dist/shared/attaform.C6qzEdIM.d.cts +0 -156
- package/dist/shared/attaform.C8LVFVVe.cjs +0 -32
- package/dist/shared/attaform.C8LVFVVe.cjs.map +0 -1
- package/dist/shared/attaform.CIEQgJnM.mjs.map +0 -1
- package/dist/shared/attaform.CTwNcpLE.d.ts +0 -156
- package/dist/shared/attaform.Cer8JO_P.cjs.map +0 -1
- package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
- package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
- package/dist/shared/attaform.Drt6fivF.mjs.map +0 -1
- package/dist/shared/attaform.Vo-Kft0t.mjs +0 -29
- package/dist/shared/attaform.Vo-Kft0t.mjs.map +0 -1
- package/dist/shared/attaform.h1sq3BFu.mjs +0 -92
- package/dist/shared/attaform.h1sq3BFu.mjs.map +0 -1
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { G as GenericForm,
|
|
2
|
+
import { G as GenericForm, t as FlatPath, O as NestedType, F as FormKey, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, ak as ValidateOnConfig, b as UseFormReturnType } from './attaform.Dj9mwbaV.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The shape `form.values.<key>` returns at runtime.
|
|
6
6
|
*
|
|
7
7
|
* Per leaf:
|
|
8
8
|
*
|
|
9
|
-
* 1. `z.preprocess(fn, inner)`
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
9
|
+
* 1. Schema-side input normalizers — `z.preprocess(fn, inner)` AND
|
|
10
|
+
* `z.coerce.X()`. Zod v4 represents these differently
|
|
11
|
+
* (`ZodPipe<ZodTransform, inner>` for preprocess; a primitive
|
|
12
|
+
* with `def.coerce === true` for coerce), but they share the
|
|
13
|
+
* same type-level marker: `_zod.input` is `unknown`. The
|
|
14
|
+
* normalizer runs at parse time, NOT at the write boundary, so
|
|
15
|
+
* storage holds the consumer's raw input and the type collapses
|
|
16
|
+
* to `unknown` to match. Reach the typed value via
|
|
17
|
+
* `handleSubmit`, `validate`, or `validateAsync` — they re-parse
|
|
18
|
+
* storage through the wrapper.
|
|
14
19
|
*
|
|
15
20
|
* 2. `inner.transform(fn)` — compiles to `ZodPipe<inner, ZodTransform>`.
|
|
16
21
|
* Transforms fire at submit / validate, NOT at the write boundary,
|
|
17
22
|
* so storage holds whatever `inner` stores. Recurse `StorageShape`
|
|
18
|
-
* on `inner`
|
|
23
|
+
* on `inner` so a defaulted leaf inside `inner` still reads `T`
|
|
24
|
+
* (not `T | undefined`).
|
|
19
25
|
*
|
|
20
26
|
* A bare top-level `ZodTransform` (no `in` schema) reads
|
|
21
27
|
* `_zod.input` directly — there's no inner to recurse into.
|
|
@@ -30,12 +36,12 @@ import { G as GenericForm, s as FlatPath, B as NestedType, U as UseFormConfigura
|
|
|
30
36
|
* Nested objects delegate to Zod's own recursion on `_zod.output`,
|
|
31
37
|
* which peels nested defaults inside structural containers.
|
|
32
38
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
+
* Detection order: case 1 wins via the `_zod.input` IsUnknown check
|
|
40
|
+
* before the pipe/transform cascade fires, so wrapped variants like
|
|
41
|
+
* `z.coerce.number().optional()` (input = `unknown | undefined` →
|
|
42
|
+
* collapses to `unknown`) and `z.preprocess(fn, z.object(...))` (input
|
|
43
|
+
* = `unknown`) both land on `unknown` without descending. The IsAny
|
|
44
|
+
* filter keeps `z.any()` from being mistaken for coerce.
|
|
39
45
|
*
|
|
40
46
|
* Implementation note: direct `_zod` property access mirrors Zod's
|
|
41
47
|
* own `$InferObjectOutput` / `$InferObjectInput`, which read
|
|
@@ -61,7 +67,26 @@ type StorageShape<S> = S extends {
|
|
|
61
67
|
} ? {
|
|
62
68
|
[K in keyof Shape]-?: StorageLeaf<Shape[K]>;
|
|
63
69
|
} : StorageLeaf<S>;
|
|
64
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Detects a schema whose `_zod.input` is `unknown` — the marker both
|
|
72
|
+
* `z.preprocess(fn, _)` and `z.coerce.X()` share in Zod v4. Excludes
|
|
73
|
+
* `any` via the canonical `0 extends 1 & T` test so `z.any()` leaves
|
|
74
|
+
* fall through to the cascade below and keep their `any` typing.
|
|
75
|
+
*/
|
|
76
|
+
type InputIsUnknown<L> = L extends {
|
|
77
|
+
_zod: {
|
|
78
|
+
input: infer In;
|
|
79
|
+
};
|
|
80
|
+
} ? 0 extends 1 & In ? false : unknown extends In ? true : false : false;
|
|
81
|
+
/**
|
|
82
|
+
* Implementation-detail per-leaf branching for `StorageShape`.
|
|
83
|
+
* Exported so the bundled `.d.ts` carries a single alias body —
|
|
84
|
+
* every leaf of a Zod object schema otherwise re-emits the full
|
|
85
|
+
* pipe / transform / default conditional ladder, which compounds
|
|
86
|
+
* badly with multiple complex schemas in the same scope. Consumers
|
|
87
|
+
* should reach for `StorageShape` instead.
|
|
88
|
+
*/
|
|
89
|
+
type StorageLeaf<L> = InputIsUnknown<L> extends true ? unknown : L extends {
|
|
65
90
|
_zod: {
|
|
66
91
|
def: {
|
|
67
92
|
type: 'pipe';
|
|
@@ -69,13 +94,7 @@ type StorageLeaf<L> = L extends {
|
|
|
69
94
|
out: infer B;
|
|
70
95
|
};
|
|
71
96
|
};
|
|
72
|
-
} ?
|
|
73
|
-
_zod: {
|
|
74
|
-
def: {
|
|
75
|
-
type: 'transform';
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
} ? StorageShape<B> : B extends {
|
|
97
|
+
} ? B extends {
|
|
79
98
|
_zod: {
|
|
80
99
|
def: {
|
|
81
100
|
type: 'transform';
|
|
@@ -157,9 +176,24 @@ type PathOutput<Schema extends z.ZodType, Path extends string> = z.output<Schema
|
|
|
157
176
|
*
|
|
158
177
|
* For Zod v3, import from `attaform/zod-v3` instead.
|
|
159
178
|
*/
|
|
160
|
-
|
|
179
|
+
/**
|
|
180
|
+
* `FormOf` / `OutOf` / `ReadOf` factor the three identical-shape
|
|
181
|
+
* conditionals out of `useForm`'s public signature. The bundled
|
|
182
|
+
* `.d.ts` then carries one alias per shape rather than re-inlining
|
|
183
|
+
* `z.input<Schema> extends GenericForm ? z.input<Schema> : never`
|
|
184
|
+
* four times — which is what produces TS2589 ("Type instantiation
|
|
185
|
+
* is excessively deep") on consumer call sites with complex schemas
|
|
186
|
+
* (discriminated unions, transform pipes, deep `.register()` chains).
|
|
187
|
+
* Each alias is computed once per `Schema` instantiation; downstream
|
|
188
|
+
* generics ride on the alias rather than re-evaluating the
|
|
189
|
+
* conditional from scratch.
|
|
190
|
+
*/
|
|
191
|
+
type FormOf<Schema extends z.ZodObject> = z.input<Schema> extends GenericForm ? z.input<Schema> : never;
|
|
192
|
+
type OutOf<Schema extends z.ZodObject> = z.output<Schema> extends GenericForm ? z.output<Schema> : never;
|
|
193
|
+
type ReadOf<Schema extends z.ZodObject> = StorageShape<Schema> extends GenericForm ? StorageShape<Schema> : never;
|
|
194
|
+
declare function useForm<Schema extends z.ZodObject, K extends FormKey = FormKey>(configuration: Omit<UseFormConfiguration<FormOf<Schema>, OutOf<Schema>, AbstractSchema<FormOf<Schema>, OutOf<Schema>>, DefaultValuesInput<FormOf<Schema>>, K>, 'schema' | 'validateOn' | 'debounceMs'> & {
|
|
161
195
|
schema: Schema;
|
|
162
|
-
} & ValidateOnConfig): UseFormReturnType<
|
|
196
|
+
} & ValidateOnConfig): UseFormReturnType<FormOf<Schema>, OutOf<Schema>, ReadOf<Schema>, K>;
|
|
163
197
|
|
|
164
198
|
export { useForm as u };
|
|
165
199
|
export type { PathInput as P, StorageShape as S, PathOutput as a };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowReactive, getCurrentInstance, inject, shallowRef, onBeforeMount, onBeforeUpdate, onMounted, nextTick, warn, isRef } from 'vue';
|
|
1
|
+
import { shallowReactive, getCurrentInstance, inject, shallowRef, onBeforeMount, onBeforeUpdate, onMounted, nextTick, warn, isRef, effectScope, watch } from 'vue';
|
|
2
2
|
|
|
3
3
|
const __DEV__ = typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
|
|
4
4
|
|
|
@@ -76,43 +76,118 @@ function detectSSR(options = {}) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const kAttaformRegistry = Symbol.for("attaform:registry");
|
|
79
|
+
const kAttaformAncestorWizard = Symbol.for(
|
|
80
|
+
"attaform:ancestor-wizard"
|
|
81
|
+
);
|
|
82
|
+
const kAttaformWizardActiveStepResolver = Symbol.for(
|
|
83
|
+
"attaform:wizard-active-step-resolver"
|
|
84
|
+
);
|
|
79
85
|
const kFormContext = Symbol.for("attaform:form-context");
|
|
80
86
|
const kFormInstanceId = Symbol.for("attaform:form-instance-id");
|
|
81
87
|
function createRegistry(options = {}) {
|
|
82
88
|
const ssr = detectSSR(options);
|
|
83
89
|
const defaults = Object.freeze({ ...options.defaults ?? {} });
|
|
84
90
|
const forms = shallowReactive(/* @__PURE__ */ new Map());
|
|
91
|
+
const wizards = shallowReactive(/* @__PURE__ */ new Map());
|
|
85
92
|
const pendingHydration = shallowReactive(/* @__PURE__ */ new Map());
|
|
86
93
|
const consumers = /* @__PURE__ */ new Map();
|
|
87
94
|
const evicting = /* @__PURE__ */ new Set();
|
|
95
|
+
const pendingEvictions = /* @__PURE__ */ new Map();
|
|
96
|
+
function cancelPendingEviction(key) {
|
|
97
|
+
const pending = pendingEvictions.get(key);
|
|
98
|
+
if (pending === void 0) return;
|
|
99
|
+
pending.cancelled = true;
|
|
100
|
+
pendingEvictions.delete(key);
|
|
101
|
+
}
|
|
88
102
|
function trackConsumer(key) {
|
|
103
|
+
cancelPendingEviction(key);
|
|
89
104
|
consumers.set(key, (consumers.get(key) ?? 0) + 1);
|
|
90
105
|
let disposed = false;
|
|
91
106
|
return () => {
|
|
92
107
|
if (disposed) return;
|
|
93
108
|
disposed = true;
|
|
94
109
|
const remaining = (consumers.get(key) ?? 1) - 1;
|
|
95
|
-
if (remaining
|
|
110
|
+
if (remaining > 0) {
|
|
111
|
+
consumers.set(key, remaining);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
consumers.delete(key);
|
|
115
|
+
const token = { cancelled: false };
|
|
116
|
+
pendingEvictions.set(key, token);
|
|
117
|
+
queueMicrotask(() => {
|
|
118
|
+
if (token.cancelled) return;
|
|
119
|
+
if (pendingEvictions.get(key) !== token) return;
|
|
120
|
+
pendingEvictions.delete(key);
|
|
96
121
|
const state = forms.get(key);
|
|
97
|
-
consumers.delete(key);
|
|
98
122
|
forms.delete(key);
|
|
99
|
-
if (state
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
if (state === void 0) return;
|
|
124
|
+
evicting.add(state);
|
|
125
|
+
void state.awaitPendingWrites().catch(() => void 0).finally(() => {
|
|
126
|
+
evicting.delete(state);
|
|
127
|
+
state.dispose();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const wizardConsumers = /* @__PURE__ */ new Map();
|
|
133
|
+
const pendingWizardEvictions = /* @__PURE__ */ new Map();
|
|
134
|
+
function cancelPendingWizardEviction(key) {
|
|
135
|
+
const pending = pendingWizardEvictions.get(key);
|
|
136
|
+
if (pending === void 0) return;
|
|
137
|
+
pending.cancelled = true;
|
|
138
|
+
pendingWizardEvictions.delete(key);
|
|
139
|
+
}
|
|
140
|
+
function trackWizardConsumer(key) {
|
|
141
|
+
cancelPendingWizardEviction(key);
|
|
142
|
+
wizardConsumers.set(key, (wizardConsumers.get(key) ?? 0) + 1);
|
|
143
|
+
let disposed = false;
|
|
144
|
+
return () => {
|
|
145
|
+
if (disposed) return;
|
|
146
|
+
disposed = true;
|
|
147
|
+
const remaining = (wizardConsumers.get(key) ?? 1) - 1;
|
|
148
|
+
if (remaining > 0) {
|
|
149
|
+
wizardConsumers.set(key, remaining);
|
|
150
|
+
return;
|
|
108
151
|
}
|
|
152
|
+
wizardConsumers.delete(key);
|
|
153
|
+
const token = { cancelled: false };
|
|
154
|
+
pendingWizardEvictions.set(key, token);
|
|
155
|
+
queueMicrotask(() => {
|
|
156
|
+
if (token.cancelled) return;
|
|
157
|
+
if (pendingWizardEvictions.get(key) !== token) return;
|
|
158
|
+
pendingWizardEvictions.delete(key);
|
|
159
|
+
wizards.delete(key);
|
|
160
|
+
});
|
|
109
161
|
};
|
|
110
162
|
}
|
|
111
163
|
async function shutdown() {
|
|
112
164
|
const states = [...forms.values(), ...evicting];
|
|
113
165
|
await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
|
|
114
166
|
}
|
|
115
|
-
|
|
167
|
+
const prefetchEnqueued = /* @__PURE__ */ new Set();
|
|
168
|
+
const prefetchSkipped = /* @__PURE__ */ new Set();
|
|
169
|
+
function enqueuePrefetch(key) {
|
|
170
|
+
prefetchEnqueued.add(key);
|
|
171
|
+
}
|
|
172
|
+
function skipPrefetch(key) {
|
|
173
|
+
prefetchSkipped.add(key);
|
|
174
|
+
}
|
|
175
|
+
function shouldPrefetch(key) {
|
|
176
|
+
return prefetchEnqueued.has(key) && !prefetchSkipped.has(key);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
forms,
|
|
180
|
+
wizards,
|
|
181
|
+
pendingHydration,
|
|
182
|
+
ssr,
|
|
183
|
+
defaults,
|
|
184
|
+
trackConsumer,
|
|
185
|
+
trackWizardConsumer,
|
|
186
|
+
enqueuePrefetch,
|
|
187
|
+
skipPrefetch,
|
|
188
|
+
shouldPrefetch,
|
|
189
|
+
shutdown
|
|
190
|
+
};
|
|
116
191
|
}
|
|
117
192
|
function useRegistry() {
|
|
118
193
|
const instance = getCurrentInstance();
|
|
@@ -282,6 +357,17 @@ function useRegister() {
|
|
|
282
357
|
if ("registerValue" in rawAttrs) {
|
|
283
358
|
capturedRegisterValue.value = rawAttrs["registerValue"];
|
|
284
359
|
delete rawAttrs["registerValue"];
|
|
360
|
+
} else {
|
|
361
|
+
const dirs = instance.vnode.dirs;
|
|
362
|
+
if (dirs !== null && dirs !== void 0) {
|
|
363
|
+
for (const dir of dirs) {
|
|
364
|
+
const marked = dir.dir?.[V_REGISTER_MARKER];
|
|
365
|
+
if (marked === true) {
|
|
366
|
+
capturedRegisterValue.value = dir.value;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
285
371
|
}
|
|
286
372
|
if ("value" in rawAttrs) delete rawAttrs["value"];
|
|
287
373
|
};
|
|
@@ -632,6 +718,12 @@ const getModelAssigner = (el, vnode, registerValue) => {
|
|
|
632
718
|
};
|
|
633
719
|
}
|
|
634
720
|
const defaultAssigner = (value) => {
|
|
721
|
+
if (value === void 0 && registerValue.acceptsUndefined) {
|
|
722
|
+
return registerValue.setValueWithInternalPath(
|
|
723
|
+
void 0,
|
|
724
|
+
computePersistMeta(el, registerValue)
|
|
725
|
+
);
|
|
726
|
+
}
|
|
635
727
|
const r = runTransforms(value, registerValue);
|
|
636
728
|
if (!r.ok) return false;
|
|
637
729
|
const coerced = applyCoerce(r.value, registerValue);
|
|
@@ -640,9 +732,10 @@ const getModelAssigner = (el, vnode, registerValue) => {
|
|
|
640
732
|
defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
|
|
641
733
|
return defaultAssigner;
|
|
642
734
|
};
|
|
643
|
-
function syncPersistOptIn(el, value, oldValue) {
|
|
735
|
+
function syncPersistOptIn(el, value, oldValue, vnodeType) {
|
|
644
736
|
const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
|
|
645
|
-
const
|
|
737
|
+
const isFileInput = el.tagName === "INPUT" && (vnodeType === "file" || el.type === "file");
|
|
738
|
+
const wantsOptIn = !isFileInput && isRegisterValue(value) && value.persist === true;
|
|
646
739
|
if (!wasOptedIn && !wantsOptIn) return;
|
|
647
740
|
const elementId = getOrAssignElementId(el);
|
|
648
741
|
if (wasOptedIn) {
|
|
@@ -768,7 +861,13 @@ const vRegisterText = {
|
|
|
768
861
|
}
|
|
769
862
|
if (isRegisterValue(value)) writeLastTypedForm(value, typedString);
|
|
770
863
|
}
|
|
771
|
-
|
|
864
|
+
if (domValue === "" && isRegisterValue(value) && !value.acceptsString && !value.acceptsUndefined) {
|
|
865
|
+
writeLastTypedForm(value, null);
|
|
866
|
+
value.markBlank();
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const commit = domValue === "" && isRegisterValue(value) && value.acceptsUndefined ? void 0 : domValue;
|
|
870
|
+
el[assignKey]?.(commit);
|
|
772
871
|
if (isRegisterValue(value) && isDefaultAssigner(el[assignKey])) {
|
|
773
872
|
const storage = value.innerRef.value;
|
|
774
873
|
if (storage !== domValue) {
|
|
@@ -1120,7 +1219,7 @@ const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
|
|
|
1120
1219
|
const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
1121
1220
|
const vRegisterDynamic = {
|
|
1122
1221
|
created(el, binding, vnode) {
|
|
1123
|
-
syncPersistOptIn(el, binding.value, void 0);
|
|
1222
|
+
syncPersistOptIn(el, binding.value, void 0, vnode.props?.["type"]);
|
|
1124
1223
|
syncMultiTabOptOut(binding.value, void 0);
|
|
1125
1224
|
callModelHook(el, binding, vnode, null, "created");
|
|
1126
1225
|
if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
|
|
@@ -1142,7 +1241,7 @@ const vRegisterDynamic = {
|
|
|
1142
1241
|
callModelHook(el, binding, vnode, null, "mounted");
|
|
1143
1242
|
},
|
|
1144
1243
|
beforeUpdate(el, binding, vnode, prevVNode) {
|
|
1145
|
-
syncPersistOptIn(el, binding.value, binding.oldValue);
|
|
1244
|
+
syncPersistOptIn(el, binding.value, binding.oldValue, vnode.props?.["type"]);
|
|
1146
1245
|
syncMultiTabOptOut(binding.value, binding.oldValue);
|
|
1147
1246
|
syncElementRegistration(el, binding.value, binding.oldValue);
|
|
1148
1247
|
callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
|
|
@@ -1163,18 +1262,83 @@ const vRegisterDynamic = {
|
|
|
1163
1262
|
delete el[assignKey];
|
|
1164
1263
|
}
|
|
1165
1264
|
};
|
|
1166
|
-
|
|
1265
|
+
function isBlankFileValue(value) {
|
|
1266
|
+
if (value === null || value === void 0) return true;
|
|
1267
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
1268
|
+
if (typeof FileList !== "undefined" && value instanceof FileList && value.length === 0)
|
|
1269
|
+
return true;
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
function readFilesFromInput(el) {
|
|
1273
|
+
const files = el.files;
|
|
1274
|
+
if (el.multiple) {
|
|
1275
|
+
return files === null ? [] : Array.from(files);
|
|
1276
|
+
}
|
|
1277
|
+
if (files === null || files.length === 0) return null;
|
|
1278
|
+
return files.item(0);
|
|
1279
|
+
}
|
|
1280
|
+
const warnedPersistedFileForms = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
1281
|
+
function maybeWarnPersistedFile(value) {
|
|
1282
|
+
if (!__DEV__ || warnedPersistedFileForms === null) return;
|
|
1283
|
+
if (value.persist !== true) return;
|
|
1284
|
+
let warnedPaths = warnedPersistedFileForms.get(value.persistOptIns);
|
|
1285
|
+
if (warnedPaths === void 0) {
|
|
1286
|
+
warnedPaths = /* @__PURE__ */ new Set();
|
|
1287
|
+
warnedPersistedFileForms.set(value.persistOptIns, warnedPaths);
|
|
1288
|
+
}
|
|
1289
|
+
if (warnedPaths.has(value.path)) return;
|
|
1290
|
+
warnedPaths.add(value.path);
|
|
1291
|
+
warn(
|
|
1292
|
+
`[attaform] register('${value.path}', { persist: true }) on <input type="file"> \u2014 files can't ride a refresh (browsers block programmatic writes to <input type="file">), so this path won't be saved. For long-lived flows, upload on selection and persist the resulting URL or ID in a sibling string field.`
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
const fileScopeKey = Symbol.for("attaform:file-scope");
|
|
1296
|
+
const vRegisterFile = {
|
|
1167
1297
|
created(el, { value }) {
|
|
1168
1298
|
if (!isRegisterValue(value)) return;
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1299
|
+
const input = el;
|
|
1300
|
+
value.registerElement(input);
|
|
1301
|
+
maybeWarnPersistedFile(value);
|
|
1302
|
+
const currentRaw = value.innerRef.value;
|
|
1303
|
+
if (isBlankFileValue(currentRaw)) {
|
|
1304
|
+
const blankShape = input.multiple ? [] : null;
|
|
1305
|
+
value.setValueWithInternalPath(blankShape, { blank: true });
|
|
1306
|
+
}
|
|
1307
|
+
addEventListener(input, "change", () => {
|
|
1308
|
+
const next = readFilesFromInput(input);
|
|
1309
|
+
const blank = isBlankFileValue(next);
|
|
1310
|
+
value.setValueWithInternalPath(next, blank ? { blank: true } : void 0);
|
|
1311
|
+
});
|
|
1312
|
+
const scope = effectScope(true);
|
|
1313
|
+
scope.run(() => {
|
|
1314
|
+
watch(
|
|
1315
|
+
value.innerRef,
|
|
1316
|
+
(next) => {
|
|
1317
|
+
if (!isBlankFileValue(next)) return;
|
|
1318
|
+
value.setValueWithInternalPath(next, { blank: true });
|
|
1319
|
+
if (input.value !== "") input.value = "";
|
|
1320
|
+
},
|
|
1321
|
+
{ flush: "post" }
|
|
1173
1322
|
);
|
|
1323
|
+
});
|
|
1324
|
+
input[fileScopeKey] = () => scope.stop();
|
|
1325
|
+
},
|
|
1326
|
+
beforeUpdate(el, { value }) {
|
|
1327
|
+
if (!isRegisterValue(value)) return;
|
|
1328
|
+
const input = el;
|
|
1329
|
+
const currentRaw = value.innerRef.value;
|
|
1330
|
+
if (isBlankFileValue(currentRaw)) {
|
|
1331
|
+
value.setValueWithInternalPath(currentRaw, { blank: true });
|
|
1332
|
+
if (input.value !== "") input.value = "";
|
|
1174
1333
|
}
|
|
1175
1334
|
},
|
|
1176
1335
|
beforeUnmount(el, { value }) {
|
|
1177
1336
|
removeTrackedListeners(el);
|
|
1337
|
+
const stop = el[fileScopeKey];
|
|
1338
|
+
if (stop !== void 0) {
|
|
1339
|
+
stop();
|
|
1340
|
+
delete el[fileScopeKey];
|
|
1341
|
+
}
|
|
1178
1342
|
if (!isRegisterValue(value)) return;
|
|
1179
1343
|
value.deregisterElement(el);
|
|
1180
1344
|
}
|
|
@@ -1183,7 +1347,7 @@ function resolveDynamicModel(tagName, type) {
|
|
|
1183
1347
|
if (tagName === "SELECT") return vRegisterSelect;
|
|
1184
1348
|
if (tagName === "TEXTAREA") return vRegisterText;
|
|
1185
1349
|
if (typeof type !== "string") return vRegisterText;
|
|
1186
|
-
if (type === "file") return
|
|
1350
|
+
if (type === "file") return vRegisterFile;
|
|
1187
1351
|
if (type === "checkbox") return vRegisterCheckbox;
|
|
1188
1352
|
if (type === "radio") return vRegisterRadio;
|
|
1189
1353
|
return vRegisterText;
|
|
@@ -1193,7 +1357,9 @@ function callModelHook(el, binding, vnode, prevVNode, hook) {
|
|
|
1193
1357
|
const fn = modelToUse[hook];
|
|
1194
1358
|
fn?.(el, binding, vnode, prevVNode);
|
|
1195
1359
|
}
|
|
1360
|
+
const V_REGISTER_MARKER = Symbol.for("attaform:v-register-directive");
|
|
1196
1361
|
const vRegister = vRegisterDynamic;
|
|
1362
|
+
vRegisterDynamic[V_REGISTER_MARKER] = true;
|
|
1197
1363
|
|
|
1198
1364
|
function installAttaformOnApp(app, options, source) {
|
|
1199
1365
|
if (app._attaform !== void 0) {
|
|
@@ -1230,5 +1396,113 @@ function createAttaform(options = {}) {
|
|
|
1230
1396
|
return plugin;
|
|
1231
1397
|
}
|
|
1232
1398
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1399
|
+
const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
1400
|
+
function normalizeSegment(raw) {
|
|
1401
|
+
if (typeof raw === "number") {
|
|
1402
|
+
if (!Number.isInteger(raw) || raw < 0) {
|
|
1403
|
+
throw new InvalidPathError(
|
|
1404
|
+
`Path segments must be non-negative integers when numeric; got ${String(raw)}`
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return raw;
|
|
1408
|
+
}
|
|
1409
|
+
if (INTEGER_SEGMENT.test(raw)) return Number(raw);
|
|
1410
|
+
return raw;
|
|
1411
|
+
}
|
|
1412
|
+
function parseDottedPath(path) {
|
|
1413
|
+
if (path.length === 0) return [""];
|
|
1414
|
+
const rawSegments = path.split(".");
|
|
1415
|
+
const segments = [];
|
|
1416
|
+
for (const raw of rawSegments) {
|
|
1417
|
+
if (raw.length === 0) {
|
|
1418
|
+
throw new InvalidPathError(
|
|
1419
|
+
`Path '${path}' has an empty segment; use the array form for empty keys.`
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
segments.push(normalizeSegment(raw));
|
|
1423
|
+
}
|
|
1424
|
+
return segments;
|
|
1425
|
+
}
|
|
1426
|
+
const CANONICAL_STRING_CACHE_MAX = 128;
|
|
1427
|
+
const canonicalStringCache = /* @__PURE__ */ new Map();
|
|
1428
|
+
const PATHKEY_TO_SEGMENTS_MAX = 4096;
|
|
1429
|
+
const pathKeyToSegments = /* @__PURE__ */ new Map();
|
|
1430
|
+
function rememberSegmentsForPathKey(key, segments) {
|
|
1431
|
+
if (!pathKeyToSegments.has(key) && pathKeyToSegments.size >= PATHKEY_TO_SEGMENTS_MAX) {
|
|
1432
|
+
const oldest = pathKeyToSegments.keys().next().value;
|
|
1433
|
+
if (oldest !== void 0) pathKeyToSegments.delete(oldest);
|
|
1434
|
+
}
|
|
1435
|
+
pathKeyToSegments.set(key, segments);
|
|
1436
|
+
}
|
|
1437
|
+
function segmentsForPathKey(key) {
|
|
1438
|
+
const cached = pathKeyToSegments.get(key);
|
|
1439
|
+
if (cached !== void 0) return cached;
|
|
1440
|
+
let parsed;
|
|
1441
|
+
try {
|
|
1442
|
+
parsed = JSON.parse(key);
|
|
1443
|
+
} catch {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
if (!Array.isArray(parsed)) return null;
|
|
1447
|
+
const segments = [];
|
|
1448
|
+
for (const raw of parsed) {
|
|
1449
|
+
if (typeof raw !== "string" && typeof raw !== "number") return null;
|
|
1450
|
+
segments.push(normalizeSegment(raw));
|
|
1451
|
+
}
|
|
1452
|
+
rememberSegmentsForPathKey(key, segments);
|
|
1453
|
+
return segments;
|
|
1454
|
+
}
|
|
1455
|
+
function canonicalizePath(input) {
|
|
1456
|
+
if (typeof input === "string") {
|
|
1457
|
+
const cached = canonicalStringCache.get(input);
|
|
1458
|
+
if (cached !== void 0) return cached;
|
|
1459
|
+
const segments2 = parseDottedPath(input);
|
|
1460
|
+
const key2 = JSON.stringify(segments2);
|
|
1461
|
+
const entry = { segments: segments2, key: key2 };
|
|
1462
|
+
if (canonicalStringCache.size >= CANONICAL_STRING_CACHE_MAX) {
|
|
1463
|
+
const oldest = canonicalStringCache.keys().next().value;
|
|
1464
|
+
if (oldest !== void 0) canonicalStringCache.delete(oldest);
|
|
1465
|
+
}
|
|
1466
|
+
canonicalStringCache.set(input, entry);
|
|
1467
|
+
rememberSegmentsForPathKey(key2, segments2);
|
|
1468
|
+
return entry;
|
|
1469
|
+
}
|
|
1470
|
+
const segments = Array.from(input).map(normalizeSegment);
|
|
1471
|
+
const key = JSON.stringify(segments);
|
|
1472
|
+
rememberSegmentsForPathKey(key, segments);
|
|
1473
|
+
return { segments, key };
|
|
1474
|
+
}
|
|
1475
|
+
function segmentsToDotted(segments) {
|
|
1476
|
+
return segments.join(".");
|
|
1477
|
+
}
|
|
1478
|
+
function pathKeyToDotted(key) {
|
|
1479
|
+
const segments = segmentsForPathKey(key);
|
|
1480
|
+
if (segments === null) return null;
|
|
1481
|
+
return segmentsToDotted(segments);
|
|
1482
|
+
}
|
|
1483
|
+
function coerceToPathKey(input) {
|
|
1484
|
+
if (input.length > 0 && input.charCodeAt(0) === 91) {
|
|
1485
|
+
try {
|
|
1486
|
+
const parsed = JSON.parse(input);
|
|
1487
|
+
if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string" || typeof s === "number")) {
|
|
1488
|
+
return input;
|
|
1489
|
+
}
|
|
1490
|
+
} catch {
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
return canonicalizePath(input).key;
|
|
1494
|
+
}
|
|
1495
|
+
const ROOT_PATH = Object.freeze([]);
|
|
1496
|
+
const ROOT_PATH_KEY = "[]";
|
|
1497
|
+
const FORM_ERRORS_PATH = Object.freeze([""]);
|
|
1498
|
+
const FORM_ERRORS_PATH_KEY = '[""]';
|
|
1499
|
+
function isPathPrefix(prefix, path) {
|
|
1500
|
+
if (path.length < prefix.length) return false;
|
|
1501
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
1502
|
+
if (path[i] !== prefix[i]) return false;
|
|
1503
|
+
}
|
|
1504
|
+
return true;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
export { AnonPersistError as A, isSensitivePath as B, createPersistOptInRegistry as C, DEFAULT_SENSITIVE_NAMES as D, ensureAttaformInstalled as E, FORM_ERRORS_PATH_KEY as F, kFormContext as G, kFormInstanceId as H, InvalidPathError as I, createIsSensitivePath as J, createSegmentMatchesSensitive as K, kAttaformAncestorWizard as L, OutsideSetupError as O, ROOT_PATH as R, SensitivePersistFieldError as S, __DEV__ as _, canonicalizePath as a, AttaformError as b, createAttaform as c, InvalidUseFormConfigError as d, ROOT_PATH_KEY as e, RegistryNotInstalledError as f, getRegistryFromApp as g, ReservedFormKeyError as h, SubmitErrorHandlerError as i, assignKey as j, kAttaformWizardActiveStepResolver as k, createRegistry as l, isPathPrefix as m, isRegisterValue as n, kAttaformRegistry as o, parseDottedPath as p, useRegistry as q, pathKeyToDotted as r, segmentsForPathKey as s, captureUserCallSite as t, useRegister as u, vRegister as v, enforceSensitiveCheck as w, FORM_ERRORS_PATH as x, coerceToPathKey as y, segmentMatchesSensitive as z };
|
|
1508
|
+
//# sourceMappingURL=attaform.Xhg0AYNa.mjs.map
|