attaform 0.15.1 → 0.16.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/README.md +21 -11
- package/dist/chunks/devtools.cjs +4 -4
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +2 -2
- 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 +23 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -70
- package/dist/index.d.mts +75 -70
- package/dist/index.d.ts +75 -70
- package/dist/index.mjs +6 -6
- package/dist/nuxt.cjs +5 -11
- package/dist/nuxt.cjs.map +1 -1
- package/dist/nuxt.d.cts +8 -0
- package/dist/nuxt.d.mts +8 -0
- package/dist/nuxt.d.ts +8 -0
- package/dist/nuxt.mjs +6 -12
- package/dist/nuxt.mjs.map +1 -1
- package/dist/runtime/plugins/attaform.cjs +3 -2
- package/dist/runtime/plugins/attaform.cjs.map +1 -1
- package/dist/runtime/plugins/attaform.mjs +2 -1
- package/dist/runtime/plugins/attaform.mjs.map +1 -1
- package/dist/shared/{attaform.BwaYWtMs.d.cts → attaform.B7rzpK1U.d.cts} +34 -5
- package/dist/shared/{attaform.BwaYWtMs.d.mts → attaform.B7rzpK1U.d.mts} +34 -5
- package/dist/shared/{attaform.BwaYWtMs.d.ts → attaform.B7rzpK1U.d.ts} +34 -5
- package/dist/shared/attaform.BAuJTWuT.d.mts +84 -0
- package/dist/shared/{attaform.CRk8NhlD.mjs → attaform.BfMxsfmE.mjs} +428 -49
- package/dist/shared/attaform.BfMxsfmE.mjs.map +1 -0
- package/dist/shared/attaform.Bp1c-uGF.cjs +1561 -0
- package/dist/shared/attaform.Bp1c-uGF.cjs.map +1 -0
- package/dist/shared/{attaform.CDJVeoJU.cjs → attaform.C9Ph2SMx.cjs} +49 -42
- package/dist/shared/{attaform.qxyip_aN.mjs.map → attaform.C9Ph2SMx.cjs.map} +1 -1
- package/dist/shared/attaform.CINUMjPq.mjs +29 -0
- package/dist/shared/attaform.CINUMjPq.mjs.map +1 -0
- package/dist/shared/attaform.CJttVxRj.cjs +32 -0
- package/dist/shared/attaform.CJttVxRj.cjs.map +1 -0
- package/dist/shared/attaform.CvOXSpCb.mjs +1908 -0
- package/dist/shared/attaform.CvOXSpCb.mjs.map +1 -0
- package/dist/shared/{attaform.qxyip_aN.mjs → attaform.DILbdvfo.mjs} +12 -5
- package/dist/shared/{attaform.CDJVeoJU.cjs.map → attaform.DILbdvfo.mjs.map} +1 -1
- package/dist/shared/attaform.DdnithOf.mjs +1555 -0
- package/dist/shared/attaform.DdnithOf.mjs.map +1 -0
- package/dist/shared/attaform.DfrYByDj.cjs +1916 -0
- package/dist/shared/attaform.DfrYByDj.cjs.map +1 -0
- package/dist/shared/{attaform.BOi138GE.cjs → attaform.c_NzdRyc.cjs} +4 -4
- package/dist/shared/{attaform.BOi138GE.cjs.map → attaform.c_NzdRyc.cjs.map} +1 -1
- package/dist/shared/{attaform.DXye3JKf.mjs → attaform.jrxE_xZw.mjs} +2 -2
- package/dist/shared/{attaform.DXye3JKf.mjs.map → attaform.jrxE_xZw.mjs.map} +1 -1
- package/dist/shared/attaform.ls_7jBYc.d.ts +84 -0
- package/dist/shared/{attaform.BgYBU8gV.cjs → attaform.rIRYSUI1.cjs} +461 -61
- package/dist/shared/attaform.rIRYSUI1.cjs.map +1 -0
- package/dist/shared/attaform.xIcmqscx.d.cts +84 -0
- package/dist/vite.cjs +62 -9
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +23 -32
- package/dist/vite.d.mts +23 -32
- package/dist/vite.d.ts +23 -32
- package/dist/vite.mjs +62 -9
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +9 -1553
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.mjs +3 -1553
- package/dist/zod-v3.mjs.map +1 -1
- package/dist/zod-v4.cjs +21 -0
- package/dist/zod-v4.cjs.map +1 -0
- package/dist/zod-v4.d.cts +104 -0
- package/dist/zod-v4.d.mts +104 -0
- package/dist/zod-v4.d.ts +104 -0
- package/dist/zod-v4.mjs +4 -0
- package/dist/zod-v4.mjs.map +1 -0
- package/dist/zod.cjs +19 -1900
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +27 -155
- package/dist/zod.d.mts +27 -155
- package/dist/zod.d.ts +27 -155
- package/dist/zod.mjs +19 -1896
- package/dist/zod.mjs.map +1 -1
- package/package.json +6 -2
- package/dist/shared/attaform.BgYBU8gV.cjs.map +0 -1
- package/dist/shared/attaform.CRk8NhlD.mjs.map +0 -1
- package/dist/shared/attaform.RypIkgVy.cjs +0 -417
- package/dist/shared/attaform.RypIkgVy.cjs.map +0 -1
- package/dist/shared/attaform.a99dQV7Q.mjs +0 -392
- package/dist/shared/attaform.a99dQV7Q.mjs.map +0 -1
|
@@ -1,8 +1,144 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const sensitiveNames = require('./attaform.RypIkgVy.cjs');
|
|
4
3
|
const vue = require('vue');
|
|
5
4
|
|
|
5
|
+
const __DEV__ = typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
10
|
+
class AttaformError extends Error {
|
|
11
|
+
constructor(message, options) {
|
|
12
|
+
super(message, options);
|
|
13
|
+
this.name = new.target.name;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class InvalidPathError extends AttaformError {
|
|
17
|
+
}
|
|
18
|
+
class InvalidUseFormConfigError extends AttaformError {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(
|
|
21
|
+
"[attaform] useForm received an invalid configuration (a schema directly, no argument, or no `schema` field). Pass it as `useForm({ schema })` \u2014 the schema is one of several configuration options. See https://attaform.com/docs/api/use-form-return for the full configuration shape."
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class SubmitErrorHandlerError extends AttaformError {
|
|
26
|
+
}
|
|
27
|
+
class RegistryNotInstalledError extends AttaformError {
|
|
28
|
+
constructor() {
|
|
29
|
+
super(
|
|
30
|
+
"[attaform] No registry attached to this Vue app. Component-level useForm / injectForm / useRegister auto-install the registry, but SSR helpers (renderAttaformState, hydrateAttaformState) run outside setup and require an explicit `app.use(createAttaform())` at server-render time. Add it to your SSR entry, before `renderToString`."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
class OutsideSetupError extends AttaformError {
|
|
35
|
+
constructor() {
|
|
36
|
+
super(
|
|
37
|
+
"[attaform] useForm / injectForm called outside Vue setup(). Move into setup or mount a child component to trigger from an event."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class ReservedFormKeyError extends AttaformError {
|
|
42
|
+
constructor(key) {
|
|
43
|
+
super(
|
|
44
|
+
`[attaform] Form key "${key}" uses the reserved "__atta:" namespace. Use a different prefix \u2014 "__atta:" is for library-internal synthetic keys (anonymous useForm() calls without an explicit key).`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class SensitivePersistFieldError extends AttaformError {
|
|
49
|
+
constructor(path) {
|
|
50
|
+
const display = Array.isArray(path) ? path.join(".") : String(path);
|
|
51
|
+
super(
|
|
52
|
+
`[attaform] Refusing to persist "${display}" \u2014 this path matches a sensitive-name pattern (password / cvv / ssn / token / etc.). Storing sensitive data in client-side storage is a compliance risk (HIPAA / PII / PCI-DSS / SOC2). Fix: persist this server-side, OR pass \`acknowledgeSensitive: true\` to register() (or form.persist()) if the client-side persistence is intentional.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class AnonPersistError extends AttaformError {
|
|
57
|
+
constructor(opts) {
|
|
58
|
+
super(formatAnonPersistMessage(opts));
|
|
59
|
+
__publicField(this, "schemaFields");
|
|
60
|
+
__publicField(this, "callSite");
|
|
61
|
+
__publicField(this, "cause");
|
|
62
|
+
this.schemaFields = opts.schemaFields;
|
|
63
|
+
this.callSite = opts.callSite;
|
|
64
|
+
this.cause = opts.cause;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function formatAnonPersistMessage(opts) {
|
|
68
|
+
const head = opts.cause === "no-key" ? `useForm({ persist: ... }) requires an explicit \`key:\`. Anonymous synthetic keys (\`__atta:anon:*\`) drift across mounts and can collide between unrelated forms \u2014 refusing to persist to a non-deterministic location.` : `register(_, { persist: true }) declared on a form whose useForm() options have no \`persist:\` configured. The opt-in is recorded but nothing would ever land in storage.`;
|
|
69
|
+
const fields = opts.schemaFields !== void 0 && opts.schemaFields.length > 0 ? ` Form fields: { ${opts.schemaFields.join(", ")} }.` : "";
|
|
70
|
+
const fix = opts.cause === "no-key" ? ` Fix: add \`key: '<stable-id>'\` to useForm().` : ` Fix: add \`persist: 'session'\` (or 'local') and \`key:\` to useForm(), or remove \`{ persist: true }\` from this register() call.`;
|
|
71
|
+
const where = opts.callSite !== void 0 ? ` ${opts.callSite}` : "";
|
|
72
|
+
return `[attaform] ${head}${fields}${fix}${where}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function detectSSR(options = {}) {
|
|
76
|
+
if (options.override !== void 0) return options.override;
|
|
77
|
+
return typeof window === "undefined" && typeof document === "undefined";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const kAttaformRegistry = Symbol.for("attaform:registry");
|
|
81
|
+
const kFormContext = Symbol.for("attaform:form-context");
|
|
82
|
+
const kFormInstanceId = Symbol.for("attaform:form-instance-id");
|
|
83
|
+
function createRegistry(options = {}) {
|
|
84
|
+
const ssr = detectSSR(options);
|
|
85
|
+
const defaults = Object.freeze({ ...options.defaults ?? {} });
|
|
86
|
+
const forms = vue.shallowReactive(/* @__PURE__ */ new Map());
|
|
87
|
+
const pendingHydration = vue.shallowReactive(/* @__PURE__ */ new Map());
|
|
88
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
89
|
+
const evicting = /* @__PURE__ */ new Set();
|
|
90
|
+
function trackConsumer(key) {
|
|
91
|
+
consumers.set(key, (consumers.get(key) ?? 0) + 1);
|
|
92
|
+
let disposed = false;
|
|
93
|
+
return () => {
|
|
94
|
+
if (disposed) return;
|
|
95
|
+
disposed = true;
|
|
96
|
+
const remaining = (consumers.get(key) ?? 1) - 1;
|
|
97
|
+
if (remaining <= 0) {
|
|
98
|
+
const state = forms.get(key);
|
|
99
|
+
consumers.delete(key);
|
|
100
|
+
forms.delete(key);
|
|
101
|
+
if (state !== void 0) {
|
|
102
|
+
evicting.add(state);
|
|
103
|
+
void state.awaitPendingWrites().catch(() => void 0).finally(() => {
|
|
104
|
+
evicting.delete(state);
|
|
105
|
+
state.dispose();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
consumers.set(key, remaining);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async function shutdown() {
|
|
114
|
+
const states = [...forms.values(), ...evicting];
|
|
115
|
+
await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
|
|
116
|
+
}
|
|
117
|
+
return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
|
|
118
|
+
}
|
|
119
|
+
function useRegistry() {
|
|
120
|
+
const instance = vue.getCurrentInstance();
|
|
121
|
+
if (instance === null) {
|
|
122
|
+
throw new OutsideSetupError();
|
|
123
|
+
}
|
|
124
|
+
const registry = vue.inject(kAttaformRegistry, null);
|
|
125
|
+
if (registry === null) {
|
|
126
|
+
throw new RegistryNotInstalledError();
|
|
127
|
+
}
|
|
128
|
+
return registry;
|
|
129
|
+
}
|
|
130
|
+
function getRegistryFromApp(app) {
|
|
131
|
+
const registry = app._attaform;
|
|
132
|
+
if (registry === void 0) {
|
|
133
|
+
throw new RegistryNotInstalledError();
|
|
134
|
+
}
|
|
135
|
+
return registry;
|
|
136
|
+
}
|
|
137
|
+
function attachRegistryToApp(app, registry) {
|
|
138
|
+
app.provide(kAttaformRegistry, registry);
|
|
139
|
+
app._attaform = registry;
|
|
140
|
+
}
|
|
141
|
+
|
|
6
142
|
const isArray = Array.isArray;
|
|
7
143
|
function isFunction(value) {
|
|
8
144
|
return typeof value === "function";
|
|
@@ -74,6 +210,267 @@ function invokeArrayFns(fns, ...args) {
|
|
|
74
210
|
}
|
|
75
211
|
}
|
|
76
212
|
|
|
213
|
+
function captureUserCallSite() {
|
|
214
|
+
const raw = new Error().stack;
|
|
215
|
+
if (typeof raw !== "string") return void 0;
|
|
216
|
+
const lines = raw.split("\n");
|
|
217
|
+
for (let i = 1; i < lines.length; i++) {
|
|
218
|
+
const frame = lines[i];
|
|
219
|
+
if (frame === void 0) continue;
|
|
220
|
+
if (/attaform[/-]forms?/i.test(frame)) continue;
|
|
221
|
+
if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
|
|
222
|
+
const trimmed = frame.trim();
|
|
223
|
+
if (trimmed.length === 0) continue;
|
|
224
|
+
return shortenSourceFrame(trimmed);
|
|
225
|
+
}
|
|
226
|
+
return void 0;
|
|
227
|
+
}
|
|
228
|
+
function shortenSourceFrame(frame) {
|
|
229
|
+
const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
|
|
230
|
+
if (match === null) return frame;
|
|
231
|
+
const [, urlOrPath, line] = match;
|
|
232
|
+
if (urlOrPath === void 0 || line === void 0) return frame;
|
|
233
|
+
let path = urlOrPath;
|
|
234
|
+
path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
|
|
235
|
+
path = path.replace(/^_nuxt\//, "");
|
|
236
|
+
path = path.replace(/^\//, "");
|
|
237
|
+
return `(${path}:${line})`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const REGISTER_OWNER_MARKER = Symbol.for("attaform:register-owner-marker");
|
|
241
|
+
const warnedNoParentRV = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
242
|
+
let warnedOutsideSetup = false;
|
|
243
|
+
function makeRegisterValueProxy(capturedRegisterValue) {
|
|
244
|
+
return new Proxy({}, {
|
|
245
|
+
get(_target, prop) {
|
|
246
|
+
if (prop === "__v_isRef") return true;
|
|
247
|
+
if (prop === "value") return capturedRegisterValue.value;
|
|
248
|
+
const v = capturedRegisterValue.value;
|
|
249
|
+
if (v === void 0) return void 0;
|
|
250
|
+
return Reflect.get(v, prop);
|
|
251
|
+
},
|
|
252
|
+
has(_target, prop) {
|
|
253
|
+
if (prop === "__v_isRef" || prop === "value") return true;
|
|
254
|
+
const v = capturedRegisterValue.value;
|
|
255
|
+
if (v === void 0) return false;
|
|
256
|
+
return Reflect.has(v, prop);
|
|
257
|
+
},
|
|
258
|
+
ownKeys(_target) {
|
|
259
|
+
const v = capturedRegisterValue.value;
|
|
260
|
+
if (v === void 0) return [];
|
|
261
|
+
return Reflect.ownKeys(v);
|
|
262
|
+
},
|
|
263
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
264
|
+
const v = capturedRegisterValue.value;
|
|
265
|
+
if (v === void 0) return void 0;
|
|
266
|
+
const desc = Reflect.getOwnPropertyDescriptor(v, prop);
|
|
267
|
+
if (desc !== void 0) {
|
|
268
|
+
desc.configurable = true;
|
|
269
|
+
}
|
|
270
|
+
return desc;
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function useRegister() {
|
|
275
|
+
const instance = vue.getCurrentInstance();
|
|
276
|
+
if (instance === null) {
|
|
277
|
+
warnOutsideSetup();
|
|
278
|
+
return makeRegisterValueProxy(vue.shallowRef(void 0));
|
|
279
|
+
}
|
|
280
|
+
ensureAttaformInstalled(instance.appContext.app);
|
|
281
|
+
const capturedRegisterValue = vue.shallowRef(void 0);
|
|
282
|
+
const refreshAndStripBridgeAttrs = () => {
|
|
283
|
+
const rawAttrs = instance.attrs;
|
|
284
|
+
if ("registerValue" in rawAttrs) {
|
|
285
|
+
capturedRegisterValue.value = rawAttrs["registerValue"];
|
|
286
|
+
delete rawAttrs["registerValue"];
|
|
287
|
+
}
|
|
288
|
+
if ("value" in rawAttrs) delete rawAttrs["value"];
|
|
289
|
+
};
|
|
290
|
+
refreshAndStripBridgeAttrs();
|
|
291
|
+
vue.onBeforeMount(refreshAndStripBridgeAttrs);
|
|
292
|
+
vue.onBeforeUpdate(refreshAndStripBridgeAttrs);
|
|
293
|
+
vue.onMounted(() => {
|
|
294
|
+
const el = instance.vnode.el;
|
|
295
|
+
if (el !== null && el !== void 0 && typeof el === "object") {
|
|
296
|
+
el[REGISTER_OWNER_MARKER] = true;
|
|
297
|
+
}
|
|
298
|
+
if (capturedRegisterValue.value === void 0) {
|
|
299
|
+
warnNoParentRV(instance);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
return makeRegisterValueProxy(capturedRegisterValue);
|
|
303
|
+
}
|
|
304
|
+
function warnOutsideSetup() {
|
|
305
|
+
if (!__DEV__) return;
|
|
306
|
+
if (warnedOutsideSetup) return;
|
|
307
|
+
warnedOutsideSetup = true;
|
|
308
|
+
const frame = captureUserCallSite();
|
|
309
|
+
console.warn(
|
|
310
|
+
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function \u2014 not from an event handler or async callback.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
function warnNoParentRV(instance) {
|
|
314
|
+
if (!__DEV__ || warnedNoParentRV === null) return;
|
|
315
|
+
if (warnedNoParentRV.has(instance)) return;
|
|
316
|
+
warnedNoParentRV.add(instance);
|
|
317
|
+
const frame = captureUserCallSite();
|
|
318
|
+
console.warn(
|
|
319
|
+
`[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. Pass v-register on the parent: \`<YourComponent v-register="form.register('field')" />\`.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const idGenerator = /* @__PURE__ */ (() => {
|
|
324
|
+
let counter = 0;
|
|
325
|
+
return () => `el-${++counter}`;
|
|
326
|
+
})();
|
|
327
|
+
const elementIds = /* @__PURE__ */ new WeakMap();
|
|
328
|
+
function getOrAssignElementId(el) {
|
|
329
|
+
let id = elementIds.get(el);
|
|
330
|
+
if (id === void 0) {
|
|
331
|
+
id = idGenerator();
|
|
332
|
+
elementIds.set(el, id);
|
|
333
|
+
}
|
|
334
|
+
return id;
|
|
335
|
+
}
|
|
336
|
+
function createPersistOptInRegistry() {
|
|
337
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
338
|
+
function add(elementId, path) {
|
|
339
|
+
const existing = byPath.get(path);
|
|
340
|
+
if (existing === void 0) {
|
|
341
|
+
byPath.set(path, /* @__PURE__ */ new Set([elementId]));
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
existing.add(elementId);
|
|
345
|
+
}
|
|
346
|
+
function remove(elementId, path) {
|
|
347
|
+
const existing = byPath.get(path);
|
|
348
|
+
if (existing === void 0) return;
|
|
349
|
+
existing.delete(elementId);
|
|
350
|
+
if (existing.size === 0) byPath.delete(path);
|
|
351
|
+
}
|
|
352
|
+
function removeAllFor(elementId) {
|
|
353
|
+
for (const [path, ids] of byPath) {
|
|
354
|
+
if (!ids.delete(elementId)) continue;
|
|
355
|
+
if (ids.size === 0) byPath.delete(path);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function hasOptIn(elementId, path) {
|
|
359
|
+
return byPath.get(path)?.has(elementId) ?? false;
|
|
360
|
+
}
|
|
361
|
+
function hasAnyOptInForPath(path) {
|
|
362
|
+
const ids = byPath.get(path);
|
|
363
|
+
return ids !== void 0 && ids.size > 0;
|
|
364
|
+
}
|
|
365
|
+
function optedInPaths() {
|
|
366
|
+
return byPath.keys();
|
|
367
|
+
}
|
|
368
|
+
function isEmpty() {
|
|
369
|
+
return byPath.size === 0;
|
|
370
|
+
}
|
|
371
|
+
function clear() {
|
|
372
|
+
byPath.clear();
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
add,
|
|
376
|
+
remove,
|
|
377
|
+
removeAllFor,
|
|
378
|
+
hasOptIn,
|
|
379
|
+
hasAnyOptInForPath,
|
|
380
|
+
optedInPaths,
|
|
381
|
+
isEmpty,
|
|
382
|
+
clear
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const SENSITIVE_NAME_PATTERNS = [
|
|
387
|
+
// Passwords and PIN-like
|
|
388
|
+
/password/i,
|
|
389
|
+
/passwd/i,
|
|
390
|
+
/passwords/i,
|
|
391
|
+
/\bpwd\b/i,
|
|
392
|
+
/\bpin\b/i,
|
|
393
|
+
// Card / payment
|
|
394
|
+
/\bcvv\b/i,
|
|
395
|
+
/\bcvc\b/i,
|
|
396
|
+
/card[_\s-]?(?:number|num)/i,
|
|
397
|
+
/\bcard\b/i,
|
|
398
|
+
/\biban\b/i,
|
|
399
|
+
/routing[_\s-]?number/i,
|
|
400
|
+
/account[_\s-]?number/i,
|
|
401
|
+
// Government / identity
|
|
402
|
+
/\bssn\b/i,
|
|
403
|
+
/social[_\s-]?security/i,
|
|
404
|
+
/\bdob\b/i,
|
|
405
|
+
/date[_\s-]?of[_\s-]?birth/i,
|
|
406
|
+
/passport/i,
|
|
407
|
+
/driver[_\s-]?license/i,
|
|
408
|
+
// Tax IDs (US + international common variants)
|
|
409
|
+
/\btin\b/i,
|
|
410
|
+
/\bein\b/i,
|
|
411
|
+
/\bitin\b/i,
|
|
412
|
+
/tax[_\s-]?id/i,
|
|
413
|
+
// Tokens, secrets, API/auth credentials
|
|
414
|
+
/\btoken\b/i,
|
|
415
|
+
/\btokens\b/i,
|
|
416
|
+
/secret/i,
|
|
417
|
+
/secrets/i,
|
|
418
|
+
/api[_\s-]?key/i,
|
|
419
|
+
/api[_\s-]?secret/i,
|
|
420
|
+
/api[_\s-]?token/i,
|
|
421
|
+
/private[_\s-]?key/i,
|
|
422
|
+
/\bbearer\b/i,
|
|
423
|
+
/\boauth\b/i,
|
|
424
|
+
/auth[_\s-]?token/i,
|
|
425
|
+
/access[_\s-]?token/i,
|
|
426
|
+
/refresh[_\s-]?token/i,
|
|
427
|
+
/session[_\s-]?(?:id|key|token)/i,
|
|
428
|
+
// MFA / OTP
|
|
429
|
+
/\botp\b/i,
|
|
430
|
+
/one[_\s-]?time[_\s-]?(?:password|code)/i,
|
|
431
|
+
/mfa[_\s-]?(?:secret|seed|code|token)/i,
|
|
432
|
+
/two[_\s-]?factor[_\s-]?(?:code|token)/i,
|
|
433
|
+
/\b2fa[_\s-]?(?:code|token)?\b/i,
|
|
434
|
+
/recovery[_\s-]?code/i,
|
|
435
|
+
/backup[_\s-]?code/i
|
|
436
|
+
];
|
|
437
|
+
function segmentMatchesSensitive(segment) {
|
|
438
|
+
if (typeof segment !== "string") return false;
|
|
439
|
+
for (const pattern of SENSITIVE_NAME_PATTERNS) {
|
|
440
|
+
if (pattern.test(segment)) return true;
|
|
441
|
+
}
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
function isSensitivePath(path) {
|
|
445
|
+
if (typeof path !== "string") {
|
|
446
|
+
for (const segment of path) {
|
|
447
|
+
if (segmentMatchesSensitive(segment)) return true;
|
|
448
|
+
}
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
if (path.startsWith("[")) {
|
|
452
|
+
try {
|
|
453
|
+
const parsed = JSON.parse(path);
|
|
454
|
+
if (Array.isArray(parsed)) {
|
|
455
|
+
for (const segment of parsed) {
|
|
456
|
+
if (segmentMatchesSensitive(segment)) return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
} catch {
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
for (const segment of path.split(".")) {
|
|
464
|
+
if (segmentMatchesSensitive(segment)) return true;
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
function enforceSensitiveCheck(path, acknowledged) {
|
|
469
|
+
if (acknowledged) return;
|
|
470
|
+
if (!isSensitivePath(path)) return;
|
|
471
|
+
throw new SensitivePersistFieldError(path);
|
|
472
|
+
}
|
|
473
|
+
|
|
77
474
|
const assignKey = Symbol.for("attaform:assign-key");
|
|
78
475
|
const listenersKey = Symbol.for("attaform:directive-listeners");
|
|
79
476
|
function isRegisterValue(val) {
|
|
@@ -106,7 +503,7 @@ function writeLastTypedForm(rv, next) {
|
|
|
106
503
|
rv.lastTypedForm.value = next;
|
|
107
504
|
}
|
|
108
505
|
function computePersistMeta(el, registerValue) {
|
|
109
|
-
const elementId =
|
|
506
|
+
const elementId = getOrAssignElementId(el);
|
|
110
507
|
return { persist: registerValue.persistOptIns.hasOptIn(elementId, registerValue.path) };
|
|
111
508
|
}
|
|
112
509
|
const DEFAULT_ASSIGNER_TAG = Symbol.for("attaform:default-assigner-tag");
|
|
@@ -139,7 +536,7 @@ function runTransforms(initial, registerValue) {
|
|
|
139
536
|
return { ok: true, value: v };
|
|
140
537
|
}
|
|
141
538
|
function logTransformFailure(path, index, fn, err) {
|
|
142
|
-
if (
|
|
539
|
+
if (__DEV__) {
|
|
143
540
|
const namePart = fn.name !== "" ? `, '${fn.name}'` : "";
|
|
144
541
|
console.error(
|
|
145
542
|
`[attaform] transform threw for path '${path}' (index ${index}${namePart}) \u2014 write aborted. Transforms must not throw; wrap your own try/catch if the throw is recoverable. Original error:`,
|
|
@@ -158,7 +555,7 @@ function applyElementCoerce(value, registerValue) {
|
|
|
158
555
|
return registerValue.coerceElement !== void 0 ? registerValue.coerceElement(value) : value;
|
|
159
556
|
}
|
|
160
557
|
function logTransformAsync(path) {
|
|
161
|
-
if (
|
|
558
|
+
if (__DEV__) {
|
|
162
559
|
console.error(
|
|
163
560
|
`[attaform] transform pipeline for path '${path}' returned a Promise \u2014 transforms must be sync. Use async field validation for canonicalize-before-write patterns. Write aborted.`
|
|
164
561
|
);
|
|
@@ -202,7 +599,7 @@ function syncPersistOptIn(el, value, oldValue) {
|
|
|
202
599
|
const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
|
|
203
600
|
const wantsOptIn = isRegisterValue(value) && value.persist === true;
|
|
204
601
|
if (!wasOptedIn && !wantsOptIn) return;
|
|
205
|
-
const elementId =
|
|
602
|
+
const elementId = getOrAssignElementId(el);
|
|
206
603
|
if (wasOptedIn) {
|
|
207
604
|
const old = oldValue;
|
|
208
605
|
const samePathAndRegistry = wantsOptIn && value.path === old.path && value.persistOptIns === old.persistOptIns;
|
|
@@ -212,7 +609,7 @@ function syncPersistOptIn(el, value, oldValue) {
|
|
|
212
609
|
}
|
|
213
610
|
if (wantsOptIn) {
|
|
214
611
|
const v = value;
|
|
215
|
-
|
|
612
|
+
enforceSensitiveCheck(v.path, v.acknowledgeSensitive);
|
|
216
613
|
v.persistOptIns.add(elementId, v.path);
|
|
217
614
|
}
|
|
218
615
|
}
|
|
@@ -604,7 +1001,7 @@ function setSelected(el, value) {
|
|
|
604
1001
|
const isMultiple = el.multiple;
|
|
605
1002
|
const isArrayValue = isArray(externalValue);
|
|
606
1003
|
if (isMultiple && !isArrayValue && !isSet(externalValue)) {
|
|
607
|
-
if (
|
|
1004
|
+
if (__DEV__) {
|
|
608
1005
|
vue.warn(
|
|
609
1006
|
`<select multiple v-register> expected an Array or Set, got ${Object.prototype.toString.call(externalValue).slice(8, -1)}. Bind to a list-typed schema (e.g. z.array(z.string()) or z.set(z.string())).`
|
|
610
1007
|
);
|
|
@@ -612,7 +1009,7 @@ function setSelected(el, value) {
|
|
|
612
1009
|
return;
|
|
613
1010
|
}
|
|
614
1011
|
if (!isMultiple && (isArrayValue || isSet(externalValue))) {
|
|
615
|
-
if (
|
|
1012
|
+
if (__DEV__) {
|
|
616
1013
|
vue.warn(
|
|
617
1014
|
`<select v-register> (no \`multiple\` attribute) expected a scalar value for its binding, but got ${Object.prototype.toString.call(externalValue).slice(8, -1)}. Add the \`multiple\` attribute to bind to a list, or use a scalar schema (e.g. \`z.string()\`) for a single-select binding.`
|
|
618
1015
|
);
|
|
@@ -660,15 +1057,15 @@ function getCheckboxValue(el, checked) {
|
|
|
660
1057
|
return key in el ? el[key] : checked;
|
|
661
1058
|
}
|
|
662
1059
|
const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
|
|
663
|
-
const warnedUnsupportedElements =
|
|
1060
|
+
const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
664
1061
|
const vRegisterDynamic = {
|
|
665
1062
|
created(el, binding, vnode) {
|
|
666
1063
|
syncPersistOptIn(el, binding.value, void 0);
|
|
667
1064
|
callModelHook(el, binding, vnode, null, "created");
|
|
668
|
-
if (
|
|
1065
|
+
if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
|
|
669
1066
|
void vue.nextTick(() => {
|
|
670
1067
|
if (warnedUnsupportedElements.has(el)) return;
|
|
671
|
-
const hasMarker = el[
|
|
1068
|
+
const hasMarker = el[REGISTER_OWNER_MARKER] === true;
|
|
672
1069
|
const hasUserAssigner = !isDefaultAssigner(
|
|
673
1070
|
el[assignKey]
|
|
674
1071
|
);
|
|
@@ -694,7 +1091,7 @@ const vRegisterDynamic = {
|
|
|
694
1091
|
beforeUnmount(el, { value }) {
|
|
695
1092
|
removeTrackedListeners(el);
|
|
696
1093
|
if (isRegisterValue(value)) {
|
|
697
|
-
value.persistOptIns.removeAllFor(
|
|
1094
|
+
value.persistOptIns.removeAllFor(getOrAssignElementId(el));
|
|
698
1095
|
}
|
|
699
1096
|
if (!isRegisterValue(value)) return;
|
|
700
1097
|
value.deregisterElement(el);
|
|
@@ -707,7 +1104,7 @@ const vRegisterFileNoop = {
|
|
|
707
1104
|
created(el, { value }) {
|
|
708
1105
|
if (!isRegisterValue(value)) return;
|
|
709
1106
|
value.registerElement(el);
|
|
710
|
-
if (
|
|
1107
|
+
if (__DEV__) {
|
|
711
1108
|
vue.warn(
|
|
712
1109
|
'[attaform] v-register on <input type="file"> is not supported. Handle uploads with a manual @change listener.'
|
|
713
1110
|
);
|
|
@@ -735,63 +1132,66 @@ function callModelHook(el, binding, vnode, prevVNode, hook) {
|
|
|
735
1132
|
}
|
|
736
1133
|
const vRegister = vRegisterDynamic;
|
|
737
1134
|
|
|
1135
|
+
function installAttaformOnApp(app, options, source) {
|
|
1136
|
+
if (app._attaform !== void 0) {
|
|
1137
|
+
if (__DEV__ && source === "explicit") {
|
|
1138
|
+
console.warn(
|
|
1139
|
+
"[attaform] createAttaform() install was called twice on the same app; the second call is a no-op. Likely cause: registering the plugin via both the Nuxt module AND a manual `app.use(...)`."
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
return app._attaform;
|
|
1143
|
+
}
|
|
1144
|
+
const registry = createRegistry(options);
|
|
1145
|
+
attachRegistryToApp(app, registry);
|
|
1146
|
+
app.directive("register", vRegister);
|
|
1147
|
+
if (options.devtools !== false && !registry.ssr) {
|
|
1148
|
+
void (async () => {
|
|
1149
|
+
try {
|
|
1150
|
+
const { setupAttaformDevtools } = await import('../chunks/devtools.cjs');
|
|
1151
|
+
await setupAttaformDevtools(app, registry);
|
|
1152
|
+
} catch {
|
|
1153
|
+
}
|
|
1154
|
+
})();
|
|
1155
|
+
}
|
|
1156
|
+
return registry;
|
|
1157
|
+
}
|
|
1158
|
+
function ensureAttaformInstalled(app) {
|
|
1159
|
+
return installAttaformOnApp(app, {}, "lazy");
|
|
1160
|
+
}
|
|
738
1161
|
function createAttaform(options = {}) {
|
|
739
1162
|
const plugin = {
|
|
740
1163
|
install(app) {
|
|
741
|
-
|
|
742
|
-
if (sensitiveNames.__DEV__) {
|
|
743
|
-
console.warn(
|
|
744
|
-
"[attaform] createAttaform() install was called twice on the same app; the second call is a no-op. Likely cause: registering the plugin via both the Nuxt module AND a manual `app.use(...)`."
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
const registry = sensitiveNames.createRegistry(options);
|
|
750
|
-
sensitiveNames.attachRegistryToApp(app, registry);
|
|
751
|
-
app.directive("register", vRegister);
|
|
752
|
-
if (options.devtools !== false && !registry.ssr) {
|
|
753
|
-
void (async () => {
|
|
754
|
-
try {
|
|
755
|
-
const { setupAttaformDevtools } = await import('../chunks/devtools.cjs');
|
|
756
|
-
await setupAttaformDevtools(app, registry);
|
|
757
|
-
} catch {
|
|
758
|
-
}
|
|
759
|
-
})();
|
|
760
|
-
}
|
|
1164
|
+
installAttaformOnApp(app, options, "explicit");
|
|
761
1165
|
}
|
|
762
1166
|
};
|
|
763
1167
|
return plugin;
|
|
764
1168
|
}
|
|
765
1169
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
userErrors: Array.from(state.userErrors.entries()),
|
|
777
|
-
fields: Array.from(state.fields.entries()),
|
|
778
|
-
...transientList.length > 0 ? { blankPaths: transientList } : {}
|
|
779
|
-
}
|
|
780
|
-
]);
|
|
781
|
-
}
|
|
782
|
-
return { forms };
|
|
783
|
-
}
|
|
784
|
-
function hydrateAttaformState(app, payload) {
|
|
785
|
-
const registry = sensitiveNames.getRegistryFromApp(app);
|
|
786
|
-
for (const [key, data] of payload.forms) {
|
|
787
|
-
registry.pendingHydration.set(key, data);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
1170
|
+
exports.AnonPersistError = AnonPersistError;
|
|
1171
|
+
exports.AttaformError = AttaformError;
|
|
1172
|
+
exports.InvalidPathError = InvalidPathError;
|
|
1173
|
+
exports.InvalidUseFormConfigError = InvalidUseFormConfigError;
|
|
1174
|
+
exports.OutsideSetupError = OutsideSetupError;
|
|
1175
|
+
exports.RegistryNotInstalledError = RegistryNotInstalledError;
|
|
1176
|
+
exports.ReservedFormKeyError = ReservedFormKeyError;
|
|
1177
|
+
exports.SensitivePersistFieldError = SensitivePersistFieldError;
|
|
1178
|
+
exports.SubmitErrorHandlerError = SubmitErrorHandlerError;
|
|
1179
|
+
exports.__DEV__ = __DEV__;
|
|
791
1180
|
exports.assignKey = assignKey;
|
|
1181
|
+
exports.captureUserCallSite = captureUserCallSite;
|
|
792
1182
|
exports.createAttaform = createAttaform;
|
|
793
|
-
exports.
|
|
1183
|
+
exports.createPersistOptInRegistry = createPersistOptInRegistry;
|
|
1184
|
+
exports.createRegistry = createRegistry;
|
|
1185
|
+
exports.enforceSensitiveCheck = enforceSensitiveCheck;
|
|
1186
|
+
exports.ensureAttaformInstalled = ensureAttaformInstalled;
|
|
1187
|
+
exports.getRegistryFromApp = getRegistryFromApp;
|
|
794
1188
|
exports.isRegisterValue = isRegisterValue;
|
|
795
|
-
exports.
|
|
1189
|
+
exports.isSensitivePath = isSensitivePath;
|
|
1190
|
+
exports.kAttaformRegistry = kAttaformRegistry;
|
|
1191
|
+
exports.kFormContext = kFormContext;
|
|
1192
|
+
exports.kFormInstanceId = kFormInstanceId;
|
|
1193
|
+
exports.segmentMatchesSensitive = segmentMatchesSensitive;
|
|
1194
|
+
exports.useRegister = useRegister;
|
|
1195
|
+
exports.useRegistry = useRegistry;
|
|
796
1196
|
exports.vRegister = vRegister;
|
|
797
|
-
//# sourceMappingURL=attaform.
|
|
1197
|
+
//# sourceMappingURL=attaform.rIRYSUI1.cjs.map
|