attaform 0.21.2 → 0.23.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.
Files changed (101) hide show
  1. package/README.md +7 -10
  2. package/dist/chunks/dev-key-collision-warnings.cjs +0 -33
  3. package/dist/chunks/dev-key-collision-warnings.cjs.map +1 -1
  4. package/dist/chunks/dev-key-collision-warnings.mjs +1 -33
  5. package/dist/chunks/dev-key-collision-warnings.mjs.map +1 -1
  6. package/dist/chunks/devtools.cjs +3 -5
  7. package/dist/chunks/devtools.cjs.map +1 -1
  8. package/dist/chunks/devtools.mjs +3 -5
  9. package/dist/chunks/devtools.mjs.map +1 -1
  10. package/dist/chunks/fingerprint2.cjs +1 -1
  11. package/dist/chunks/fingerprint2.mjs +1 -1
  12. package/dist/index.cjs +3 -5
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +6 -41
  15. package/dist/index.d.mts +6 -41
  16. package/dist/index.d.ts +6 -41
  17. package/dist/index.mjs +5 -5
  18. package/dist/nuxt.d.cts +1 -1
  19. package/dist/nuxt.d.mts +1 -1
  20. package/dist/nuxt.d.ts +1 -1
  21. package/dist/runtime/components/AttaformDevtoolsPanel.vue +3 -11
  22. package/dist/runtime/plugins/attaform.cjs +2 -2
  23. package/dist/runtime/plugins/attaform.mjs +2 -2
  24. package/dist/shared/{attaform.BGf_J22U.d.ts → attaform.BGMRvckW.d.ts} +11 -70
  25. package/dist/shared/{attaform.ory-3WhV.d.mts → attaform.BJnNK75Y.d.cts} +41 -491
  26. package/dist/shared/{attaform.ory-3WhV.d.ts → attaform.BJnNK75Y.d.mts} +41 -491
  27. package/dist/shared/{attaform.ory-3WhV.d.cts → attaform.BJnNK75Y.d.ts} +41 -491
  28. package/dist/shared/{attaform.DP-u7_tk.mjs → attaform.BhI9Icek.mjs} +17 -291
  29. package/dist/shared/attaform.BhI9Icek.mjs.map +1 -0
  30. package/dist/shared/{attaform.BwLp9KM7.cjs → attaform.BibT5AS_.cjs} +2 -2
  31. package/dist/shared/{attaform.BwLp9KM7.cjs.map → attaform.BibT5AS_.cjs.map} +1 -1
  32. package/dist/shared/{attaform.DkA5J8NW.d.cts → attaform.CO0e7YVY.d.cts} +1 -46
  33. package/dist/shared/{attaform.DkA5J8NW.d.ts → attaform.CO0e7YVY.d.mts} +1 -46
  34. package/dist/shared/{attaform.DkA5J8NW.d.mts → attaform.CO0e7YVY.d.ts} +1 -46
  35. package/dist/shared/{attaform.BwrowMp2.cjs → attaform.CaYj3ZfY.cjs} +3 -3
  36. package/dist/shared/{attaform.BwrowMp2.cjs.map → attaform.CaYj3ZfY.cjs.map} +1 -1
  37. package/dist/shared/{attaform.BBDIKtKY.cjs → attaform.Cmb_LCie.cjs} +4 -4
  38. package/dist/shared/{attaform.BVeLgfEh.mjs.map → attaform.Cmb_LCie.cjs.map} +1 -1
  39. package/dist/shared/{attaform.CTheKoTc.mjs → attaform.CtJOd7ox.mjs} +446 -525
  40. package/dist/shared/attaform.CtJOd7ox.mjs.map +1 -0
  41. package/dist/shared/{attaform.CrD73S4m.mjs → attaform.CzVta5o2.mjs} +116 -47
  42. package/dist/shared/attaform.CzVta5o2.mjs.map +1 -0
  43. package/dist/shared/{attaform.CnEl--PF.d.mts → attaform.D52oJiYC.d.cts} +1 -1
  44. package/dist/shared/{attaform.BoY6RZUl.d.cts → attaform.DCkSNnPr.d.mts} +1 -1
  45. package/dist/shared/{attaform.B5LNzqQh.cjs → attaform.Db4E4IW6.cjs} +18 -297
  46. package/dist/shared/attaform.Db4E4IW6.cjs.map +1 -0
  47. package/dist/shared/{attaform.CcnF1AKJ.cjs → attaform.DbyTD8N2.cjs} +116 -47
  48. package/dist/shared/attaform.DbyTD8N2.cjs.map +1 -0
  49. package/dist/shared/{attaform.D6GYGshL.mjs → attaform.Dd1Kmmaj.mjs} +3 -3
  50. package/dist/shared/{attaform.D6GYGshL.mjs.map → attaform.Dd1Kmmaj.mjs.map} +1 -1
  51. package/dist/shared/{attaform.BCcrLApm.d.mts → attaform.DrY8srOp.d.mts} +11 -70
  52. package/dist/shared/{attaform.D2ZuIOCf.cjs → attaform.DsQkXE3o.cjs} +445 -534
  53. package/dist/shared/attaform.DsQkXE3o.cjs.map +1 -0
  54. package/dist/shared/{attaform.BkjJfMvJ.d.cts → attaform.DuPneYR0.d.cts} +11 -70
  55. package/dist/shared/{attaform.C41gjp-a.mjs → attaform.Dx9-QQE2.mjs} +2 -2
  56. package/dist/shared/{attaform.C41gjp-a.mjs.map → attaform.Dx9-QQE2.mjs.map} +1 -1
  57. package/dist/shared/{attaform.BYgioWLF.d.ts → attaform.WEwfXcHq.d.ts} +1 -1
  58. package/dist/shared/{attaform.BVeLgfEh.mjs → attaform.alpG7rT7.mjs} +4 -4
  59. package/dist/shared/{attaform.BBDIKtKY.cjs.map → attaform.alpG7rT7.mjs.map} +1 -1
  60. package/dist/zod-v3.cjs +2 -2
  61. package/dist/zod-v3.d.cts +3 -3
  62. package/dist/zod-v3.d.mts +3 -3
  63. package/dist/zod-v3.d.ts +3 -3
  64. package/dist/zod-v3.mjs +2 -2
  65. package/dist/zod-v4.cjs +2 -2
  66. package/dist/zod-v4.d.cts +5 -5
  67. package/dist/zod-v4.d.mts +5 -5
  68. package/dist/zod-v4.d.ts +5 -5
  69. package/dist/zod-v4.mjs +2 -2
  70. package/dist/zod.cjs +5 -5
  71. package/dist/zod.d.cts +5 -5
  72. package/dist/zod.d.mts +5 -5
  73. package/dist/zod.d.ts +5 -5
  74. package/dist/zod.mjs +5 -5
  75. package/package.json +2 -2
  76. package/dist/chunks/indexeddb.cjs +0 -119
  77. package/dist/chunks/indexeddb.cjs.map +0 -1
  78. package/dist/chunks/indexeddb.mjs +0 -117
  79. package/dist/chunks/indexeddb.mjs.map +0 -1
  80. package/dist/chunks/local-storage.cjs +0 -58
  81. package/dist/chunks/local-storage.cjs.map +0 -1
  82. package/dist/chunks/local-storage.mjs +0 -56
  83. package/dist/chunks/local-storage.mjs.map +0 -1
  84. package/dist/chunks/multi-tab-sync.cjs +0 -367
  85. package/dist/chunks/multi-tab-sync.cjs.map +0 -1
  86. package/dist/chunks/multi-tab-sync.mjs +0 -364
  87. package/dist/chunks/multi-tab-sync.mjs.map +0 -1
  88. package/dist/chunks/session-storage.cjs +0 -58
  89. package/dist/chunks/session-storage.cjs.map +0 -1
  90. package/dist/chunks/session-storage.mjs +0 -56
  91. package/dist/chunks/session-storage.mjs.map +0 -1
  92. package/dist/chunks/wire-persistence.cjs +0 -396
  93. package/dist/chunks/wire-persistence.cjs.map +0 -1
  94. package/dist/chunks/wire-persistence.mjs +0 -394
  95. package/dist/chunks/wire-persistence.mjs.map +0 -1
  96. package/dist/shared/attaform.B5LNzqQh.cjs.map +0 -1
  97. package/dist/shared/attaform.CTheKoTc.mjs.map +0 -1
  98. package/dist/shared/attaform.CcnF1AKJ.cjs.map +0 -1
  99. package/dist/shared/attaform.CrD73S4m.mjs.map +0 -1
  100. package/dist/shared/attaform.D2ZuIOCf.cjs.map +0 -1
  101. package/dist/shared/attaform.DP-u7_tk.mjs.map +0 -1
@@ -1,396 +0,0 @@
1
- 'use strict';
2
-
3
- const vue = require('vue');
4
- const injectWizard = require('../shared/attaform.D2ZuIOCf.cjs');
5
- const paths = require('../shared/attaform.B5LNzqQh.cjs');
6
-
7
- const PERSISTED_ENVELOPE_VERSION = 6;
8
- function readPersistedPayload(value) {
9
- if (value === null || value === void 0 || typeof value !== "object") return null;
10
- const envelope = value;
11
- if (typeof envelope.v !== "number") return null;
12
- if (envelope.v !== PERSISTED_ENVELOPE_VERSION) {
13
- warnVersionMismatch(envelope.v);
14
- return null;
15
- }
16
- if (envelope.data === void 0 || typeof envelope.data !== "object") return null;
17
- return envelope;
18
- }
19
- const warnedVersions = paths.__DEV__ ? /* @__PURE__ */ new Set() : null;
20
- function warnVersionMismatch(observedVersion) {
21
- if (warnedVersions === null) return;
22
- if (warnedVersions.has(observedVersion)) return;
23
- warnedVersions.add(observedVersion);
24
- console.warn(
25
- `[attaform] Dropping persisted draft \u2014 envelope v=${observedVersion}, but this version of the library expects v=${PERSISTED_ENVELOPE_VERSION}. The persisted shape changed across releases; older drafts can't be restored. New drafts saved this session will use the current envelope.`
26
- );
27
- }
28
- function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
29
- let transientList;
30
- if (blankPaths !== void 0 && blankPaths.size > 0) {
31
- transientList = [...blankPaths];
32
- }
33
- if (include === "form") {
34
- if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
35
- return {
36
- v: PERSISTED_ENVELOPE_VERSION,
37
- data: { form, blankPaths: transientList }
38
- };
39
- }
40
- return {
41
- v: PERSISTED_ENVELOPE_VERSION,
42
- data: {
43
- form,
44
- schemaErrors: [...schemaErrors.entries()].map(([k, v]) => [k, [...v]]),
45
- userErrors: [...userErrors.entries()].map(([k, v]) => [k, [...v]]),
46
- ...transientList !== void 0 ? { blankPaths: transientList } : {}
47
- }
48
- };
49
- }
50
- function createDebouncedWriter(write, debounceMs) {
51
- let timer = null;
52
- let pending = null;
53
- let writeGeneration = 0;
54
- function runWrite() {
55
- const gen = ++writeGeneration;
56
- pending = write().finally(() => {
57
- if (writeGeneration === gen) pending = null;
58
- });
59
- }
60
- function schedule() {
61
- if (timer !== null) clearTimeout(timer);
62
- if (debounceMs === 0) {
63
- runWrite();
64
- return;
65
- }
66
- timer = setTimeout(() => {
67
- timer = null;
68
- runWrite();
69
- }, debounceMs);
70
- }
71
- async function flush() {
72
- if (timer !== null) {
73
- clearTimeout(timer);
74
- timer = null;
75
- runWrite();
76
- }
77
- while (pending !== null) {
78
- const awaited = pending;
79
- await awaited;
80
- if (pending === awaited) break;
81
- }
82
- }
83
- function cancel() {
84
- if (timer !== null) {
85
- clearTimeout(timer);
86
- timer = null;
87
- }
88
- }
89
- return { schedule, flush, cancel };
90
- }
91
- function pluckPaths(form, pathKeys) {
92
- let sparse = void 0;
93
- for (const pathKey of pathKeys) {
94
- const segments = paths.segmentsForPathKey(pathKey);
95
- if (segments === null) continue;
96
- const value = injectWizard.getAtPath(form, segments);
97
- if (value === void 0) continue;
98
- sparse = injectWizard.setAtPath(sparse ?? {}, segments, value);
99
- }
100
- return sparse ?? {};
101
- }
102
- function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
103
- const acknowledgedSensitive = [];
104
- for (const key of optedInPaths) {
105
- const segs = paths.segmentsForPathKey(key);
106
- if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
107
- }
108
- const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => paths.isPathPrefix(prefix, path));
109
- const walk = (path, value) => {
110
- if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
111
- return void 0;
112
- }
113
- if (value === null || typeof value !== "object") return value;
114
- if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
115
- if (!injectWizard.isPlainRecord(value)) return value;
116
- const out = {};
117
- for (const key of Object.keys(value)) {
118
- const walked = walk([...path, key], value[key]);
119
- if (walked !== void 0) injectWizard.safeAssign(out, key, walked);
120
- }
121
- return out;
122
- };
123
- return walk([], form);
124
- }
125
- function filterErrorsByPaths(errors, pathKeys) {
126
- const out = /* @__PURE__ */ new Map();
127
- for (const [key, value] of errors) {
128
- if (pathKeys.has(key)) out.set(key, value);
129
- }
130
- return out;
131
- }
132
-
133
- function wirePersistence(state, config, adapterPromise, fingerprintToken) {
134
- const base = injectWizard.resolveStorageKeyBase(config, state.formKey);
135
- const key = `${base}:${fingerprintToken}`;
136
- const debounceMs = injectWizard.normalizeNumericOption({
137
- value: config.debounceMs ?? injectWizard.DEFAULT_PERSISTENCE_DEBOUNCE_MS,
138
- source: "useForm.persist.debounceMs",
139
- allowInfinity: false,
140
- min: 0,
141
- defaultValue: injectWizard.DEFAULT_PERSISTENCE_DEBOUNCE_MS
142
- });
143
- const include = config.include ?? "form";
144
- const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
145
- let disposed = false;
146
- const isDisposed = () => disposed;
147
- let inFlightFinalFlush = null;
148
- let pendingOptedInPaths = null;
149
- const writer = createDebouncedWriter(async () => {
150
- const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
151
- pendingOptedInPaths = null;
152
- const adapter = await adapterPromise;
153
- if (isDisposed()) return;
154
- if (optedInPaths.size === 0) {
155
- await adapter.removeItem(key);
156
- return;
157
- }
158
- const rawForm = vue.toRaw(state.form.value);
159
- const filteredForm = stripUnacknowledgedSensitiveLeaves(
160
- pluckPaths(rawForm, optedInPaths),
161
- optedInPaths,
162
- state.isSensitivePath
163
- );
164
- const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
165
- const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
166
- const filteredTransientEmpty = /* @__PURE__ */ new Set();
167
- for (const tk of state.blankPaths) {
168
- if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
169
- }
170
- const payload = buildPersistedPayload(
171
- filteredForm,
172
- include,
173
- filteredSchemaErrors,
174
- filteredUserErrors,
175
- filteredTransientEmpty
176
- );
177
- await adapter.setItem(key, payload);
178
- }, debounceMs);
179
- const unsubscribeChange = state.onFormChange((_next, meta) => {
180
- if (isDisposed() || inFlightFinalFlush !== null) return;
181
- if (meta?.crossTab === true) return;
182
- if (meta?.persist !== true) return;
183
- pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
184
- writer.schedule();
185
- });
186
- const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
187
- if (isDisposed()) return;
188
- void (async () => {
189
- await writer.flush();
190
- if (isDisposed()) return;
191
- const adapter = await adapterPromise;
192
- if (isDisposed()) return;
193
- await adapter.removeItem(key);
194
- })();
195
- }) : () => void 0;
196
- const handlePageHide = () => {
197
- if (isDisposed()) return;
198
- void writer.flush();
199
- };
200
- if (typeof window !== "undefined") {
201
- window.addEventListener("pagehide", handlePageHide);
202
- }
203
- void (async () => {
204
- const adapter = await adapterPromise;
205
- if (isDisposed()) return;
206
- void injectWizard.cleanupOrphanKeys(adapter, base, key);
207
- try {
208
- const raw = await adapter.getItem(key);
209
- const payload = readPersistedPayload(raw);
210
- if (payload === null) {
211
- if (raw !== null && raw !== void 0) {
212
- await adapter.removeItem(key);
213
- }
214
- return;
215
- }
216
- if (isDisposed()) return;
217
- const merged = injectWizard.mergeSparseHydration(
218
- vue.toRaw(state.form.value),
219
- payload.data.form,
220
- state.schema
221
- );
222
- state.applyFormReplacement(merged, { hydration: true });
223
- const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
224
- for (const k of persistedLeafPaths) {
225
- state.blankPaths.delete(k);
226
- state.originalBlankPaths.delete(k);
227
- }
228
- for (const k of payload.data.blankPaths ?? []) {
229
- state.blankPaths.add(k);
230
- state.originalBlankPaths.add(k);
231
- }
232
- if (include === "form+errors") {
233
- if (payload.data.schemaErrors !== void 0) {
234
- const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
235
- state.setAllSchemaErrors(flat);
236
- }
237
- if (payload.data.userErrors !== void 0) {
238
- const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
239
- state.setAllUserErrors(flat);
240
- }
241
- }
242
- state.scheduleFieldValidation(
243
- [],
244
- true
245
- /* immediate */
246
- );
247
- } catch {
248
- }
249
- })();
250
- async function writePathImmediately(path) {
251
- if (isDisposed()) return;
252
- await writer.flush();
253
- if (isDisposed()) return;
254
- const adapter = await adapterPromise;
255
- if (isDisposed()) return;
256
- const raw = await adapter.getItem(key);
257
- const existing = readPersistedPayload(raw);
258
- const value = injectWizard.getAtPath(vue.toRaw(state.form.value), path);
259
- const nextForm = injectWizard.setAtPath(existing?.data.form ?? {}, path, value);
260
- const { key: pathKey } = paths.canonicalizePath(path);
261
- const transientSet = new Set(
262
- (existing?.data.blankPaths ?? []).filter(
263
- (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
264
- )
265
- );
266
- for (const liveKey of state.blankPaths) {
267
- if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
268
- transientSet.add(liveKey);
269
- }
270
- }
271
- if (include === "form") {
272
- await adapter.setItem(
273
- key,
274
- buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
275
- );
276
- return;
277
- }
278
- const schemaMap = new Map(existing?.data.schemaErrors ?? []);
279
- const userMap = new Map(existing?.data.userErrors ?? []);
280
- const currentSchema = state.schemaErrors.get(pathKey);
281
- const currentUser = state.userErrors.get(pathKey);
282
- if (currentSchema !== void 0 && currentSchema.length > 0) {
283
- schemaMap.set(pathKey, [...currentSchema]);
284
- } else {
285
- schemaMap.delete(pathKey);
286
- }
287
- if (currentUser !== void 0 && currentUser.length > 0) {
288
- userMap.set(pathKey, [...currentUser]);
289
- } else {
290
- userMap.delete(pathKey);
291
- }
292
- await adapter.setItem(
293
- key,
294
- buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
295
- );
296
- }
297
- async function clearPersistedDraft(path) {
298
- if (isDisposed()) return;
299
- await writer.flush();
300
- if (isDisposed()) return;
301
- const adapter = await adapterPromise;
302
- if (isDisposed()) return;
303
- if (path === void 0) {
304
- await adapter.removeItem(key);
305
- return;
306
- }
307
- const raw = await adapter.getItem(key);
308
- const existing = readPersistedPayload(raw);
309
- if (existing === null) return;
310
- const nextForm = injectWizard.deleteAtPath(existing.data.form, path);
311
- if (isEmptyContainer(nextForm)) {
312
- await adapter.removeItem(key);
313
- return;
314
- }
315
- const { key: pathKey } = paths.canonicalizePath(path);
316
- const transientSet = new Set(
317
- (existing.data.blankPaths ?? []).filter(
318
- (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
319
- )
320
- );
321
- if (include === "form") {
322
- await adapter.setItem(
323
- key,
324
- buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
325
- );
326
- return;
327
- }
328
- const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
329
- const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
330
- const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
331
- const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
332
- await adapter.setItem(
333
- key,
334
- buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
335
- );
336
- }
337
- function awaitPendingWrites() {
338
- if (inFlightFinalFlush !== null) return inFlightFinalFlush;
339
- if (isDisposed()) return Promise.resolve();
340
- return writer.flush().catch(() => void 0);
341
- }
342
- function dispose() {
343
- if (isDisposed() || inFlightFinalFlush !== null) return;
344
- unsubscribeChange();
345
- unsubscribeSuccess();
346
- if (typeof window !== "undefined") {
347
- window.removeEventListener("pagehide", handlePageHide);
348
- }
349
- inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
350
- disposed = true;
351
- inFlightFinalFlush = null;
352
- });
353
- }
354
- return {
355
- writePathImmediately,
356
- clearPersistedDraft,
357
- awaitPendingWrites,
358
- dispose
359
- };
360
- }
361
- function isEmptyContainer(value) {
362
- if (value === void 0 || value === null) return true;
363
- if (Array.isArray(value)) return value.length === 0;
364
- if (injectWizard.isPlainRecord(value)) return Object.keys(value).length === 0;
365
- return false;
366
- }
367
- function collectPersistedLeafPaths(form) {
368
- const out = [];
369
- walk(form, []);
370
- return out;
371
- function walk(node, prefix) {
372
- if (Array.isArray(node)) {
373
- for (let i = 0; i < node.length; i++) {
374
- walk(node[i], [...prefix, i]);
375
- }
376
- return;
377
- }
378
- if (injectWizard.isPlainRecord(node)) {
379
- for (const key of Object.keys(node)) {
380
- walk(node[key], [...prefix, key]);
381
- }
382
- return;
383
- }
384
- if (prefix.length === 0) return;
385
- out.push(paths.canonicalizePath(prefix).key);
386
- }
387
- }
388
- function isDescendantPathKey(candidate, ancestor) {
389
- if (candidate.length <= ancestor.length) return false;
390
- if (!ancestor.endsWith("]")) return false;
391
- const childPrefix = `${ancestor.slice(0, -1)},`;
392
- return candidate.startsWith(childPrefix);
393
- }
394
-
395
- exports.wirePersistence = wirePersistence;
396
- //# sourceMappingURL=wire-persistence.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wire-persistence.cjs","sources":["../../src/runtime/core/persistence/payload.ts","../../src/runtime/core/persistence/wire-persistence.ts"],"sourcesContent":["import type { ValidationError } from '../../types/types-api'\nimport { __DEV__ } from '../dev'\nimport { isPathPrefix, segmentsForPathKey, type Path, type PathKey } from '../paths'\nimport { getAtPath, isPlainRecord, setAtPath } from '../path-walker'\nimport { safeAssign } from '../safe-assign'\n\n/**\n * Persisted payload envelope.\n *\n * `v` is a attaform-INTERNAL storage-format version — bumped only when the\n * library's persisted payload schema itself changes (e.g. adding a new\n * field, restructuring `data`). It is NOT consumer-controlled.\n * Schema-driven invalidation uses the storage key's `:${fingerprint}`\n * suffix instead, so consumers don't need to manage versioning at all.\n *\n * `data` mirrors the SSR `SerializedFormData` shape so one deserialiser\n * handles both.\n *\n * Errors are stored source-segregated (matching FormStore's split):\n * - `schemaErrors` is validation-owned; cleared by reset / submit-success.\n * - `userErrors` is consumer-owned (written via setFieldErrors* APIs);\n * persists across schema revalidation and successful submits.\n */\nexport type PersistedPayload<Form> = {\n readonly v: number\n readonly data: {\n readonly form: Form\n readonly schemaErrors?: ReadonlyArray<readonly [string, ValidationError[]]>\n readonly userErrors?: ReadonlyArray<readonly [string, ValidationError[]]>\n /**\n * Canonical `PathKey` JSON entries (`'[\"profile\",\"bio\"]'`) for the\n * paths that were in the form's `blankPaths` set at serialisation\n * time. Optional — forms with no blank paths skip the field.\n * Replayed into the reactive Set on the next mount so an accidental\n * refresh preserves the user's \"displayed empty\" state across\n * sessions. Introduced in envelope v=3.\n *\n * The encoding restored the JSON-array shape at v=6 (after a v=5\n * dotted-string detour) so literal-dot record keys\n * (`record[\"foo.bar\"]` vs `record.foo.bar`) and integer-looking\n * record keys (`record[\"1\"]` vs `record[1]`) survive the round-trip\n * unambiguously — the PathKey carries segment kind, dotted\n * notation does not.\n */\n readonly blankPaths?: ReadonlyArray<PathKey>\n }\n}\n\n/**\n * Current attaform-internal envelope version. Bumped only when the library\n * changes the persisted payload's structural shape — readers reject\n * envelopes with a different `v`. Schema-content invalidation is\n * handled at the storage key level (the `:${fingerprint}` suffix), so\n * consumers shouldn't see this number.\n *\n * v=3: adds `data.blankPaths` for round-tripping the\n * blank UI state across persistence + SSR. v=2 envelopes\n * are dropped with a one-time dev-warn (commit 6 of the unset feature).\n *\n * v=4: `ValidationError` gained a required `code` field. Persisted\n * `schemaErrors` / `userErrors` now include `code`; v=3 payloads are\n * dropped with a one-time dev-warn.\n *\n * v=5: `data.blankPaths` switched from canonical `PathKey` JSON\n * strings (`'[\"profile\",\"bio\"]'`) to dotted public-path strings\n * (`'profile.bio'`), matching the path notation everywhere else in\n * the public API. v=4 payloads are dropped with a one-time dev-warn.\n *\n * v=6: `data.blankPaths` switched BACK to the canonical `PathKey` JSON\n * shape (`'[\"profile\",\"bio\"]'`). The dotted notation collapses literal-\n * dot record keys (`record[\"foo.bar\"]` vs. `record.foo.bar`) and\n * integer-looking record keys (`record[\"1\"]` vs. `record[1]`) onto\n * the same wire shape, silently flipping the blank-mark onto a sibling\n * slot on hydrate. The JSON-array carries segment kind, so the\n * distinction round-trips losslessly. v=5 payloads are dropped with a\n * one-time dev-warn.\n */\nexport const PERSISTED_ENVELOPE_VERSION = 6\n\n/**\n * `value` is expected to be a raw `PersistedPayload` (parsed JSON or\n * structured-cloned object). Returns `null` if the shape doesn't match\n * — the caller falls back to schema defaults.\n *\n * The attaform-internal envelope `v` must match `PERSISTED_ENVELOPE_VERSION`;\n * mismatches (older library versions' payloads) are dropped. Schema\n * change detection lives at the storage-key level via the fingerprint\n * suffix.\n */\nexport function readPersistedPayload<Form>(value: unknown): PersistedPayload<Form> | null {\n if (value === null || value === undefined || typeof value !== 'object') return null\n const envelope = value as Partial<PersistedPayload<Form>>\n if (typeof envelope.v !== 'number') return null\n if (envelope.v !== PERSISTED_ENVELOPE_VERSION) {\n warnVersionMismatch(envelope.v)\n return null\n }\n if (envelope.data === undefined || typeof envelope.data !== 'object') return null\n return envelope as PersistedPayload<Form>\n}\n\n/**\n * Tracks envelope versions we've already warned about during this\n * session. The reader hits this for every form mount that finds\n * stale persisted state, so a page with N saved drafts at an old\n * version would otherwise produce N warnings of the same content.\n * Module-scoped Set survives the test-suite hot-reload cycle but\n * resets on each fresh page load — exactly the dedup window we want.\n *\n * `null` in production so the Set allocation tree-shakes out.\n */\nconst warnedVersions: Set<number> | null = __DEV__ ? new Set<number>() : null\n\nfunction warnVersionMismatch(observedVersion: number): void {\n if (warnedVersions === null) return\n if (warnedVersions.has(observedVersion)) return\n warnedVersions.add(observedVersion)\n console.warn(\n `[attaform] Dropping persisted draft — envelope v=${observedVersion}, ` +\n `but this version of the library expects v=${PERSISTED_ENVELOPE_VERSION}. ` +\n `The persisted shape changed across releases; older drafts can't be restored. ` +\n `New drafts saved this session will use the current envelope.`\n )\n}\n\nexport function buildPersistedPayload<Form>(\n form: Form,\n include: 'form' | 'form+errors',\n schemaErrors: ReadonlyMap<string, ValidationError[]>,\n userErrors: ReadonlyMap<string, ValidationError[]>,\n blankPaths?: ReadonlySet<string>\n): PersistedPayload<Form> {\n // The blank list is part of the form's restorable UI state — its\n // visibility doesn't depend on the `include` mode (which only governs\n // whether errors come along for the ride). Skip the field when the\n // set is empty so v=6 round-trips with unchanged minimal payload\n // size for forms that never go empty. The PathKey JSON shape (a\n // JSON-array string) goes through to disk verbatim so segment kind\n // survives the round-trip — see [[PASS2-8]] and the v=6 docblock\n // on `PERSISTED_ENVELOPE_VERSION`.\n let transientList: ReadonlyArray<PathKey> | undefined\n if (blankPaths !== undefined && blankPaths.size > 0) {\n transientList = [...blankPaths] as PathKey[]\n }\n\n if (include === 'form') {\n if (transientList === undefined) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } }\n return {\n v: PERSISTED_ENVELOPE_VERSION,\n data: { form, blankPaths: transientList },\n }\n }\n return {\n v: PERSISTED_ENVELOPE_VERSION,\n data: {\n form,\n schemaErrors: [...schemaErrors.entries()].map(([k, v]) => [k, [...v]] as const),\n userErrors: [...userErrors.entries()].map(([k, v]) => [k, [...v]] as const),\n ...(transientList !== undefined ? { blankPaths: transientList } : {}),\n },\n }\n}\n\n/**\n * Tiny debounce utility. Returns a `{ schedule, flush, cancel }`\n * triple — `schedule` delays a single pending write, `flush` runs it\n * immediately, `cancel` drops it. Unlike a library `debounce`, this\n * one awaits the underlying async write inside `flush` so callers\n * can await full completion on consumer teardown.\n *\n * `debounceMs: 0` is the off switch — `schedule()` fires the write\n * synchronously rather than queueing through `setTimeout(fn, 0)`\n * (which is a macrotask the browser clamps to ~4 ms anyway).\n */\nexport function createDebouncedWriter(\n write: () => Promise<void>,\n debounceMs: number\n): {\n schedule(): void\n flush(): Promise<void>\n cancel(): void\n} {\n let timer: ReturnType<typeof setTimeout> | null = null\n let pending: Promise<void> | null = null\n // Per-write generation counter (PASS2-S2). Each `runWrite` call\n // captures its generation; the inline `.finally` only nulls the\n // shared `pending` when its capture still matches the live counter.\n // Without this guard, an older write's settle silently nullified\n // `pending` even when a newer write had replaced it, and any\n // `flush()` awaiting the OLD pending then resolved before the\n // newer write settled — an early drain signal even though no\n // bytes were lost (each write is a full idempotent snapshot).\n let writeGeneration = 0\n\n function runWrite(): void {\n const gen = ++writeGeneration\n pending = write().finally(() => {\n if (writeGeneration === gen) pending = null\n })\n }\n\n function schedule(): void {\n if (timer !== null) clearTimeout(timer)\n // `debounceMs: 0` is the off switch — fire the write synchronously\n // rather than punting through `setTimeout(fn, 0)` (which queues a\n // macrotask and the browser clamps to ~4 ms anyway).\n if (debounceMs === 0) {\n runWrite()\n return\n }\n timer = setTimeout(() => {\n timer = null\n runWrite()\n }, debounceMs)\n }\n\n async function flush(): Promise<void> {\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n runWrite()\n }\n // Drain-loop: if a new `schedule()` lands while we're awaiting an\n // older `pending`, the latest pending might be a different promise\n // by the time the older one settles. Re-read `pending` after the\n // await and keep looping until it goes null (or stabilises on the\n // same promise across an await tick — guards against an infinite\n // loop if a sync arm immediately re-schedules during the await).\n while (pending !== null) {\n const awaited = pending\n await awaited\n if (pending === awaited) break\n }\n }\n\n function cancel(): void {\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n }\n\n return { schedule, flush, cancel }\n}\n\n/**\n * Build a sparse object containing only the values at `pathKeys` from\n * `form`. Each PathKey is the canonical JSON-array form\n * (`'[\"profile\",\"name\"]'`) emitted by `canonicalizePath`. Paths whose\n * value is `undefined` in the source (e.g. an optional schema field\n * the user never touched) are skipped — the caller's\n * `mergeSparseHydration` re-fills from schema defaults on read.\n *\n * The returned object structurally-shares with the source: a path that\n * names a container (e.g. `'contacts'` resolving to a whole array) is\n * copied by reference into the sparse output. Per-leaf opt-ins\n * (`'contacts.0.name'`) construct intermediate containers via\n * `setAtPath`.\n */\nexport function pluckPaths(form: unknown, pathKeys: Iterable<PathKey>): unknown {\n let sparse: unknown = undefined\n for (const pathKey of pathKeys) {\n // PathKeys arrive from the opt-in registry (canonical) — cache hit\n // every iteration. The null branch covers persistence payloads\n // round-tripped from disk that were corrupted before reaching the\n // restoration code.\n const segments = segmentsForPathKey(pathKey)\n if (segments === null) continue\n const value = getAtPath(form, segments)\n if (value === undefined) continue\n sparse = setAtPath(sparse ?? {}, segments, value)\n }\n return sparse ?? {}\n}\n\n/**\n * Strip sensitive-named leaves from an already-plucked persisted form\n * unless their sensitivity was acknowledged, then return a new object\n * (the input is not mutated). A container opt-in\n * (`register('payment', { persist: true })`) copies its whole subtree\n * via `pluckPaths`, so nested `cvv` / `card_number` leaves would\n * otherwise reach storage in cleartext even though they were never\n * individually acknowledged.\n *\n * A sensitive path is kept only when an opted-in path that COVERS it\n * (the leaf itself, or an ancestor container) is itself sensitive.\n * Because the persist opt-in gate (`allowSensitivePersist`) only admits\n * a sensitive path when `acknowledgeSensitive: true` was set, an opted-in\n * sensitive path is, by construction, an acknowledged one — so this\n * keeps a directly-acknowledged leaf AND the subtree of an acknowledged\n * sensitive container, while shedding the unacknowledged secrets a\n * non-sensitive container opt-in dragged along.\n *\n * Mirrors multi-tab's `stripSensitivePathsDeep`, but keyed off the\n * persist opt-in set rather than stripping every sensitive path.\n */\nexport function stripUnacknowledgedSensitiveLeaves(\n form: unknown,\n optedInPaths: ReadonlySet<PathKey>,\n isSensitivePath: (path: Path) => boolean\n): unknown {\n // Opted-in paths that are themselves sensitive could only reach the\n // set by being acknowledged; a sensitive value survives the scrub iff\n // one of these covers it.\n const acknowledgedSensitive: Path[] = []\n for (const key of optedInPaths) {\n const segs = segmentsForPathKey(key)\n if (segs !== null && isSensitivePath(segs as Path)) acknowledgedSensitive.push(segs as Path)\n }\n const coveredByAcknowledged = (path: Path): boolean =>\n acknowledgedSensitive.some((prefix) => isPathPrefix(prefix, path))\n\n const walk = (path: Path, value: unknown): unknown => {\n if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {\n return undefined // strip this leaf / subtree\n }\n if (value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item))\n if (!isPlainRecord(value)) return value\n // Scrub container carries `Object.prototype` plus `safeAssign`\n // for the per-key write so a hostile persisted payload landing a\n // literal `__proto__` key is written as an own data property, not\n // through the inherited setter. The sensitive-leaf scrub feeds\n // back into the persisted payload; shape stays consistent with\n // what `mergeDeep` produces on the way back in.\n const out: Record<string, unknown> = {}\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const walked = walk([...path, key], (value as Record<string, unknown>)[key])\n if (walked !== undefined) safeAssign(out, key, walked)\n }\n return out\n }\n return walk([], form)\n}\n\n/**\n * Restrict a `(PathKey → ValidationError[])` map to entries whose key\n * appears in `pathKeys`. Used by the persistence writer to drop errors\n * on non-opted-in paths from the persisted envelope — a persisted\n * error without a persisted value would dangle on rehydration (the\n * form would resurrect with no value but a complaint about it).\n */\nexport function filterErrorsByPaths(\n errors: ReadonlyMap<string, ValidationError[]>,\n pathKeys: ReadonlySet<PathKey>\n): Map<string, ValidationError[]> {\n const out = new Map<string, ValidationError[]>()\n for (const [key, value] of errors) {\n if (pathKeys.has(key as PathKey)) out.set(key, value)\n }\n return out\n}\n","import { toRaw } from 'vue'\nimport type { FormStorage, PersistConfigOptions, ValidationError } from '../../types/types-api'\nimport type { GenericForm } from '../../types/types-core'\nimport type { FormStore } from '../create-form-store'\nimport { DEFAULT_PERSISTENCE_DEBOUNCE_MS, normalizeNumericOption } from '../defaults'\nimport { canonicalizePath, type Path, type PathKey } from '../paths'\nimport { deleteAtPath, getAtPath, isPlainRecord, setAtPath } from '../path-walker'\nimport {\n buildPersistedPayload,\n createDebouncedWriter,\n filterErrorsByPaths,\n pluckPaths,\n readPersistedPayload,\n stripUnacknowledgedSensitiveLeaves,\n} from './payload'\nimport {\n cleanupOrphanKeys,\n mergeSparseHydration,\n resolveStorageKeyBase,\n type PersistenceModule,\n} from '../persistence'\n\n/**\n * Wire persistence to a fresh FormStore:\n *\n * 1. Resolve the storage adapter (dynamic-imported — `'local'` never\n * pulls IDB code; tree-shakes cleanly).\n * 2. Async-read any persisted payload and apply it via\n * `applyFormReplacement`. First render shows schema defaults\n * (the \"flash of default state\" — documented tradeoff for\n * async backends).\n * 3. Subscribe a debounced writer to `onFormChange`; every mutation\n * schedules a write.\n * 4. Subscribe a `removeItem` on submit-success (when\n * `clearOnSubmitSuccess` is not explicitly false).\n * 5. Return a disposer that flushes any pending write, cancels\n * the debounce, and removes subscribers. Called on consumer\n * teardown.\n */\nexport function wirePersistence<F extends GenericForm>(\n state: FormStore<F, GenericForm>,\n config: PersistConfigOptions,\n adapterPromise: Promise<FormStorage>,\n fingerprintToken: string\n): PersistenceModule {\n // `fingerprintToken` is the schema's structural fingerprint, hashed to a\n // fixed-size token by the caller. Baking it into the storage key makes\n // any structural schema change (added / removed / renamed field, type\n // swap) look up a fresh key, so the old draft becomes an orphan cleaned\n // up in this same mount by `cleanupOrphanKeys` below (no manual version\n // protocol). The caller hashes the raw fingerprint (a 200+ char\n // stringified shape that would otherwise bloat the key and leak the\n // schema's structure into client storage) and owns the fallback for the\n // adapters whose `fingerprint()` rejects, so persistence never crashes a\n // consumer's mount.\n const base = resolveStorageKeyBase(config, state.formKey)\n const key = `${base}:${fingerprintToken}`\n // Sanitise the persistence debounce — same rules as field validation:\n // `NaN` would fire synchronously, `Infinity` would stall the event\n // loop for ~24.8 days then wrap. Both fall back to the library default.\n const debounceMs = normalizeNumericOption({\n value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,\n source: 'useForm.persist.debounceMs',\n allowInfinity: false,\n min: 0,\n defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS,\n })\n const include = config.include ?? 'form'\n const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true\n\n // Single shared adapter promise — both the hydration path and the\n // write/clear paths await it. Injected by the caller, which kicks off\n // the adapter's dynamic import in parallel with the dynamic import of\n // this module, so the persisted-draft read isn't serialised behind\n // the chunk load (the flash-of-defaults window stays the same as when\n // persistence shipped eagerly). Sharing it also avoids a race where an\n // early write (fast debounceMs) would see `adapter === null` and skip\n // silently because the dynamic-import hadn't resolved yet.\n // Routed through `isDisposed()` so each read is a function call,\n // not a direct variable / property access. TS's control-flow\n // analysis would narrow either `let disposed = false` or\n // `isDisposed()` to literal `false` after the first early-\n // return in a closure (every subsequent check in the same closure\n // then trips `no-unnecessary-condition` as \"always falsy\"), because\n // the flag's only `true` write lives in the `dispose()` finally\n // callback flow analysis can't reach. A function call is opaque to\n // the narrower — the type returns `boolean` at every site, which\n // is the truth (`disposed` flips asynchronously across awaits).\n let disposed = false\n const isDisposed = (): boolean => disposed\n // Tracks the in-flight final flush kicked off by `dispose()`. Returned\n // by `awaitPendingWrites` so the registry can drain pending storage\n // writes before evicting the FormStore — without this, the last\n // debounced keystroke is silently dropped on unmount.\n let inFlightFinalFlush: Promise<void> | null = null\n // Snapshot of opt-in paths captured at SCHEDULE time. Vue's unmount\n // lifecycle runs directive `beforeUnmount` (which clears per-element\n // opt-ins) BEFORE the effect-scope dispose that triggers our drain.\n // Without a snapshot, a write flushed during the drain sees zero\n // opt-ins and wipes the storage entry. The snapshot lets the eventual\n // write reflect the moment-in-time opt-ins as the user typed.\n let pendingOptedInPaths: Set<PathKey> | null = null\n\n const writer = createDebouncedWriter(async () => {\n // No bail at entry: this closure runs from two paths — the\n // debounce timer firing (which already gated on `disposed` via the\n // schedule call) and the final flush from `dispose()`. The final\n // flush's invocation order (flush BEFORE disposed flips) lets this\n // write complete; the post-await guards below catch the rare case\n // where adapter resolution is slow enough to overlap with the\n // disposed flip.\n //\n // Use the schedule-time snapshot if present (handles the unmount\n // race where beforeUnmount has cleared the live registry). Falls\n // back to the live registry for any non-listener-triggered path.\n const optedInPaths = pendingOptedInPaths ?? new Set<PathKey>(state.persistOptIns.optedInPaths())\n pendingOptedInPaths = null\n const adapter = await adapterPromise\n if (isDisposed()) return\n // Sparse-payload reshape: the persisted form contains only paths\n // that were opted in via `register('foo', { persist: true })`. If\n // every opt-in has been torn down, wipe the entry rather than\n // write a hollow envelope (matches the per-element security model\n // — no opt-ins → nothing to persist).\n if (optedInPaths.size === 0) {\n await adapter.removeItem(key)\n return\n }\n // Unwrap the reactive form to a plain object before handing it to\n // the adapter — IDB's `structuredClone` can't serialise Vue\n // proxies (DATA_CLONE_ERR), and local/session stringify the\n // proxy's own-enumerable keys anyway.\n const rawForm = toRaw(state.form.value)\n // Shed sensitive leaves a container opt-in dragged in that weren't\n // individually acknowledged — they must never reach storage in\n // cleartext (SEC-1). Directly- or container-acknowledged secrets are\n // kept; the opt-in gate guarantees a sensitive opted-in path was\n // acknowledged.\n const filteredForm = stripUnacknowledgedSensitiveLeaves(\n pluckPaths(rawForm, optedInPaths),\n optedInPaths,\n state.isSensitivePath as (path: Path) => boolean\n ) as F\n // Build the envelope with the attaform-internal envelope version baked\n // in by `buildPersistedPayload`. Consumers no longer manage `v` —\n // schema-content invalidation lives at the storage-key level via\n // the fingerprint suffix.\n const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths)\n const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths)\n // Blank paths are part of the restorable UI state, so\n // they ride the same opt-in gate as form values: only persist\n // the entries whose paths are also opted in for persistence.\n const filteredTransientEmpty = new Set<string>()\n for (const tk of state.blankPaths) {\n if (optedInPaths.has(tk as PathKey)) filteredTransientEmpty.add(tk)\n }\n const payload = buildPersistedPayload<F>(\n filteredForm,\n include,\n filteredSchemaErrors,\n filteredUserErrors,\n filteredTransientEmpty\n )\n await adapter.setItem(key, payload)\n }, debounceMs)\n\n const unsubscribeChange = state.onFormChange((_next, meta) => {\n if (isDisposed() || inFlightFinalFlush !== null) return\n // Cross-tab apply: a sibling tab already wrote this value to its\n // own persistence layer; double-persisting from the receiving\n // tab would be wasted I/O. The multi-tab sync module sets\n // `persist: false` for this reason, which the next check already\n // catches — but adding the explicit `crossTab` early return makes\n // the intent legible at the listener boundary.\n if (meta?.crossTab === true) return\n // Per-element opt-in: only writes whose source declared `persist: true`\n // reach the storage adapter. Programmatic `form.setValue`, history\n // undo without opt-ins, devtools edits to non-opted paths, and\n // `reset()` all bypass this gate by passing no meta (or `persist:\n // false`).\n if (meta?.persist !== true) return\n // Snapshot opt-in paths NOW — Vue's unmount fires directive\n // beforeUnmount (which calls persistOptIns.removeAllFor) BEFORE\n // the scope-dispose that drives our drain. Capturing at schedule\n // time means the eventual write sees the opt-ins as they were\n // when the user typed, not as they were stripped.\n pendingOptedInPaths = new Set<PathKey>(state.persistOptIns.optedInPaths())\n writer.schedule()\n })\n\n const unsubscribeSuccess = clearOnSubmitSuccess\n ? state.onSubmitSuccess(() => {\n if (isDisposed()) return\n // Flush any pending/in-flight write BEFORE removing — otherwise\n // a timer that fires between submit and removeItem re-persists\n // the now-stale state. `flush()` awaits the in-flight promise\n // if one exists; if there's only a timer, it fires it\n // immediately and awaits. After that, removeItem wins.\n void (async () => {\n await writer.flush()\n if (isDisposed()) return\n const adapter = await adapterPromise\n if (isDisposed()) return\n await adapter.removeItem(key)\n })()\n })\n : () => undefined\n\n // Tab-close / bfcache-eviction flush (PASS2-13). The debounced\n // writer holds the last ≤debounceMs of edits before commit; without\n // a synchronous trigger, those keystrokes vanish when the user\n // closes the tab inside the debounce window. `pagehide` is the\n // modern recommendation — fires on bfcache eviction (where\n // `beforeunload` doesn't, since the page is preserved) AND on\n // tab-close. Sync-storage adapters (localStorage, sessionStorage)\n // commit inside the same task before the browser kills the JS\n // context; IDB transactions are best-effort.\n //\n // SSR guard: `window` is undefined during server rendering. The\n // listener attaches client-side only; SSR mounts complete their\n // own dispose path before the response flushes.\n const handlePageHide = (): void => {\n if (isDisposed()) return\n void writer.flush()\n }\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', handlePageHide)\n }\n\n // Async setup: resolve the adapter, then read back the persisted\n // payload. If the caller unmounts before this finishes, `disposed`\n // is true — the restore is skipped.\n void (async () => {\n const adapter = await adapterPromise\n if (isDisposed()) return\n // Orphan cleanup: delete any attaform-managed key under the same base\n // whose fingerprint suffix doesn't match the current schema. Runs\n // once per mount, fire-and-forget. Bounded cost: typically 0-1\n // orphans per form.\n void cleanupOrphanKeys(adapter, base, key)\n try {\n const raw = await adapter.getItem(key)\n const payload = readPersistedPayload<F>(raw)\n if (payload === null) {\n // Truly-absent entries are a no-op. A non-null raw that didn't\n // parse is a stale payload — wrong attaform envelope version, or\n // malformed shape — wipe so the next mount reads cleanly.\n if (raw !== null && raw !== undefined) {\n await adapter.removeItem(key)\n }\n return\n }\n if (isDisposed()) return\n // Sparse-aware replacement: the persisted form may contain only\n // a subset of paths (the ones opted into persistence on the\n // previous mount). Merge over the current form (which carries\n // schema defaults at this point — wirePersistence runs before\n // any user mutation could have happened) so non-persisted paths\n // keep their schema defaults.\n const merged = mergeSparseHydration(\n toRaw(state.form.value) as F,\n payload.data.form,\n state.schema as unknown as Parameters<typeof mergeSparseHydration>[2]\n )\n // `hydration: true` tells listeners (notably the history module)\n // that this replacement is the baseline, not a user mutation —\n // history wipes its stacks and reseeds with the post-hydration\n // snapshot so `undo()` can't reach the transient pre-hydration\n // default the form briefly held between mount and hydrate.\n state.applyFormReplacement(merged, { hydration: true })\n // payload. Persistence is per-element opt-in, so the persisted\n // payload only covers paths within the opt-in scope (the leaf\n // paths populated in `payload.data.form`). Construction-time\n // auto-marks for paths OUTSIDE that scope must survive —\n // without this, a non-opted-in numeric field's slim default\n // (`0`) would lose its blank mark on hydrate and surface as\n // `'0'` in its <input> instead of empty.\n //\n // Within the opt-in scope, the persisted state IS the truth: a\n // persisted path that's no longer blank (the user\n // typed) clears the construction-time mark, and a persisted\n // path that IS blank (still slim default) re-asserts.\n const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form)\n for (const k of persistedLeafPaths) {\n state.blankPaths.delete(k)\n state.originalBlankPaths.delete(k)\n }\n for (const k of payload.data.blankPaths ?? []) {\n // v=6 stores blankPaths in the canonical `PathKey` JSON shape,\n // matching the in-memory representation. No conversion needed\n // at the I/O boundary — see the v=6 docblock on\n // `PERSISTED_ENVELOPE_VERSION`.\n state.blankPaths.add(k)\n state.originalBlankPaths.add(k)\n }\n if (include === 'form+errors') {\n // Each store rebuilds independently from its persisted entries.\n // Consumers who bumped `version` already had their payload\n // rejected above.\n if (payload.data.schemaErrors !== undefined) {\n const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs)\n state.setAllSchemaErrors(flat)\n }\n if (payload.data.userErrors !== undefined) {\n const flat = payload.data.userErrors.flatMap(([, errs]) => errs)\n state.setAllUserErrors(flat)\n }\n }\n // Post-hydration revalidation: the construction-time seed ran\n // against the empty default, so its errors describe a stale\n // value. Async-only verdicts additionally never fire at\n // construction (the sync seed contract can't surface them;\n // schemas with async refinements / transforms / pipes degrade\n // to success there). A full async run at the root path wipes\n // `schemaErrors` and re-stamps with the authoritative result\n // for the rehydrated value — sync errors get refreshed, async\n // verdicts fire, the form lands in the state the persisted\n // value actually deserves.\n state.scheduleFieldValidation([], true /* immediate */)\n } catch {\n // Adapter IO errors shouldn't surface; storage adapters are\n // \"best-effort\" and already log their own warnings.\n }\n })()\n\n // Note: a \"configured but no fields opted in\" check used to live here\n // (microtask-deferred warn). Removed — having the persist capability\n // configured without spending it on a register() call is a valid\n // dormant state (scaffolding, A/B'd opt-ins, future-flagged fields).\n // The library shouldn't lecture the consumer for not exercising every\n // configured option. Eager throws only fire on actual contradictions\n // (no-key + persist; register-with-persist + no useForm-persist).\n\n /**\n * Imperative one-shot write. Read-merge-write strategy: flush any\n * pending debounced write first (so it can't overwrite our update),\n * read the existing payload, set the path's current value, optionally\n * merge in this path's errors, and write back. Preserves untouched\n * paths in storage.\n */\n async function writePathImmediately(path: Path): Promise<void> {\n if (isDisposed()) return\n await writer.flush()\n if (isDisposed()) return\n const adapter = await adapterPromise\n if (isDisposed()) return\n const raw = await adapter.getItem(key)\n const existing = readPersistedPayload<F>(raw)\n const value = getAtPath(toRaw(state.form.value), path)\n const nextForm = setAtPath(existing?.data.form ?? {}, path, value) as F\n // Refresh this path's blank entry — and any descendants\n // — while preserving entries for OTHER paths the previous mount\n // persisted. Non-leaf writes (`writePathImmediately('user')`)\n // overwrite the entire subtree, so any disk entries below the\n // write path are dropped first; the live in-memory set then\n // contributes whatever marks are still active under that subtree.\n const { key: pathKey } = canonicalizePath(path)\n const transientSet = new Set<string>(\n (existing?.data.blankPaths ?? []).filter(\n (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)\n )\n )\n for (const liveKey of state.blankPaths) {\n if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {\n transientSet.add(liveKey)\n }\n }\n if (include === 'form') {\n await adapter.setItem(\n key,\n buildPersistedPayload<F>(nextForm, 'form', new Map(), new Map(), transientSet)\n )\n return\n }\n // include === 'form+errors': preserve the rest of the persisted\n // error map and refresh the entry for this path's canonical key.\n const schemaMap = new Map<string, ValidationError[]>(existing?.data.schemaErrors ?? [])\n const userMap = new Map<string, ValidationError[]>(existing?.data.userErrors ?? [])\n const currentSchema = state.schemaErrors.get(pathKey)\n const currentUser = state.userErrors.get(pathKey)\n if (currentSchema !== undefined && currentSchema.length > 0) {\n schemaMap.set(pathKey, [...currentSchema])\n } else {\n schemaMap.delete(pathKey)\n }\n if (currentUser !== undefined && currentUser.length > 0) {\n userMap.set(pathKey, [...currentUser])\n } else {\n userMap.delete(pathKey)\n }\n await adapter.setItem(\n key,\n buildPersistedPayload<F>(nextForm, 'form+errors', schemaMap, userMap, transientSet)\n )\n }\n\n /**\n * Wipe the persisted entry. Without `path`, removes the whole key.\n * With `path`, deletes only that subpath (and any matching error\n * entries) and writes back; the entry is removed entirely if the\n * resulting form value is empty.\n */\n async function clearPersistedDraft(path?: Path): Promise<void> {\n if (isDisposed()) return\n await writer.flush()\n if (isDisposed()) return\n const adapter = await adapterPromise\n if (isDisposed()) return\n if (path === undefined) {\n await adapter.removeItem(key)\n return\n }\n const raw = await adapter.getItem(key)\n const existing = readPersistedPayload<F>(raw)\n if (existing === null) return\n const nextForm = deleteAtPath(existing.data.form, path) as F\n if (isEmptyContainer(nextForm)) {\n await adapter.removeItem(key)\n return\n }\n const { key: pathKey } = canonicalizePath(path)\n // Drop the cleared path AND every descendant from the persisted\n // blank list so a later mount doesn't restore an\n // \"empty\" UI state for a path that no longer has any value\n // behind it. Non-leaf clears (`clearPersistedDraft('user')`)\n // wipe the whole user.* subtree.\n const transientSet = new Set(\n (existing.data.blankPaths ?? []).filter(\n (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)\n )\n )\n if (include === 'form') {\n await adapter.setItem(\n key,\n buildPersistedPayload<F>(nextForm, 'form', new Map(), new Map(), transientSet)\n )\n return\n }\n const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey)\n const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey)\n const schemaMap = new Map<string, ValidationError[]>(schemaErrors.map(([k, v]) => [k, [...v]]))\n const userMap = new Map<string, ValidationError[]>(userErrors.map(([k, v]) => [k, [...v]]))\n await adapter.setItem(\n key,\n buildPersistedPayload<F>(nextForm, 'form+errors', schemaMap, userMap, transientSet)\n )\n }\n\n function awaitPendingWrites(): Promise<void> {\n // If dispose() already kicked off the final flush, return THAT\n // promise so the registry awaits the same drain instead of\n // scheduling a parallel one.\n if (inFlightFinalFlush !== null) return inFlightFinalFlush\n if (isDisposed()) return Promise.resolve()\n return writer.flush().catch(() => undefined)\n }\n\n function dispose(): void {\n if (isDisposed() || inFlightFinalFlush !== null) return\n unsubscribeChange()\n unsubscribeSuccess()\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', handlePageHide)\n }\n // CRITICAL: flush BEFORE flipping `disposed`. The previous order\n // (set disposed=true, then call writer.flush()) caused the writer\n // closure to bail immediately, silently dropping the last\n // keystroke whenever a component unmounted within the debounce\n // window. Now we kick off the flush, then flip `disposed` only\n // after the in-flight write finishes.\n inFlightFinalFlush = writer\n .flush()\n .catch(() => undefined)\n .finally(() => {\n disposed = true\n inFlightFinalFlush = null\n })\n // Fire-and-forget — `awaitPendingWrites` exposes the promise for\n // callers that need to drain (the registry on consumer-eviction;\n // SSR shutdown).\n void inFlightFinalFlush\n }\n\n return {\n writePathImmediately,\n clearPersistedDraft,\n awaitPendingWrites,\n dispose,\n }\n}\n\n/**\n * Treat `null`, `undefined`, `[]`, and `{}` as \"nothing left to keep.\"\n * Used by `clearPersistedDraft(path)` to decide whether to wipe the\n * entire entry instead of writing a hollow envelope back.\n */\nfunction isEmptyContainer(value: unknown): boolean {\n if (value === undefined || value === null) return true\n if (Array.isArray(value)) return value.length === 0\n if (isPlainRecord(value)) return Object.keys(value).length === 0\n return false\n}\n\n/**\n * Walk a sparse persisted form and collect the canonical PathKey of\n * every leaf. \"Leaf\" = anything that isn't a plain object or array\n * (so primitives, null, deserialized Dates / strings all count). The\n * persisted form's leaves correspond 1:1 with the per-element opt-in\n * scope at the time persistence wrote, which the hydration path uses\n * to bound which blank entries to overwrite.\n */\nfunction collectPersistedLeafPaths(form: unknown): PathKey[] {\n const out: PathKey[] = []\n walk(form, [])\n return out\n\n function walk(node: unknown, prefix: Path): void {\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n walk(node[i], [...prefix, i])\n }\n return\n }\n if (isPlainRecord(node)) {\n for (const key of Object.keys(node)) {\n walk((node as Record<string, unknown>)[key], [...prefix, key])\n }\n return\n }\n if (prefix.length === 0) return // root scalar — no path to canonicalize\n out.push(canonicalizePath(prefix).key)\n }\n}\n\n/**\n * `true` when `candidate` names a strict descendant of `ancestor` in\n * canonical PathKey form (`JSON.stringify(segments)`).\n *\n * `'[\"user\"]'` is the ancestor; `'[\"user\",\"age\"]'` and\n * `'[\"user\",\"address\",\"line1\"]'` are descendants. A pure prefix match\n * isn't enough — `'[\"userId\"]'` shares the `'[\"user'` prefix with\n * `'[\"user\"]'` but is not a descendant. The check anchors on the\n * comma that separates the parent's last segment from its first\n * child segment.\n */\nfunction isDescendantPathKey(candidate: string, ancestor: string): boolean {\n if (candidate.length <= ancestor.length) return false\n if (!ancestor.endsWith(']')) return false\n const childPrefix = `${ancestor.slice(0, -1)},`\n return candidate.startsWith(childPrefix)\n}\n"],"names":["__DEV__","segmentsForPathKey","getAtPath","setAtPath","isPathPrefix","isPlainRecord","safeAssign","resolveStorageKeyBase","normalizeNumericOption","DEFAULT_PERSISTENCE_DEBOUNCE_MS","toRaw","cleanupOrphanKeys","mergeSparseHydration","canonicalizePath","deleteAtPath"],"mappings":"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"}