attaform 0.17.1 → 0.18.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 (115) hide show
  1. package/README.md +77 -36
  2. package/dist/chunks/devtools.cjs +10 -37
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +10 -37
  5. package/dist/chunks/devtools.mjs.map +1 -1
  6. package/dist/chunks/indexeddb.cjs +4 -4
  7. package/dist/chunks/indexeddb.cjs.map +1 -1
  8. package/dist/chunks/indexeddb.mjs +1 -1
  9. package/dist/chunks/local-storage.cjs +2 -2
  10. package/dist/chunks/local-storage.cjs.map +1 -1
  11. package/dist/chunks/local-storage.mjs +1 -1
  12. package/dist/chunks/session-storage.cjs +2 -2
  13. package/dist/chunks/session-storage.cjs.map +1 -1
  14. package/dist/chunks/session-storage.mjs +1 -1
  15. package/dist/index.cjs +42 -37
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +159 -196
  18. package/dist/index.d.mts +159 -196
  19. package/dist/index.d.ts +159 -196
  20. package/dist/index.mjs +5 -7
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/nuxt.cjs +31 -40
  23. package/dist/nuxt.cjs.map +1 -1
  24. package/dist/nuxt.d.cts +8 -1
  25. package/dist/nuxt.d.mts +8 -1
  26. package/dist/nuxt.d.ts +8 -1
  27. package/dist/nuxt.mjs +32 -41
  28. package/dist/nuxt.mjs.map +1 -1
  29. package/dist/runtime/components/AttaformDevtoolsPanel.d.vue.ts +7 -0
  30. package/dist/runtime/components/AttaformDevtoolsPanel.vue +453 -0
  31. package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +7 -0
  32. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +37 -0
  33. package/dist/runtime/components/DevtoolsValueTree.vue +192 -0
  34. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +37 -0
  35. package/dist/runtime/plugins/attaform.cjs +17 -6
  36. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  37. package/dist/runtime/plugins/attaform.mjs +15 -4
  38. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  39. package/dist/shared/attaform.5UhpSVFI.cjs +63 -0
  40. package/dist/shared/attaform.5UhpSVFI.cjs.map +1 -0
  41. package/dist/shared/attaform.BDdFdjeX.mjs +57 -0
  42. package/dist/shared/attaform.BDdFdjeX.mjs.map +1 -0
  43. package/dist/shared/attaform.Bgu9l6OG.d.cts +1651 -0
  44. package/dist/shared/attaform.BmDBu4ql.d.ts +1651 -0
  45. package/dist/shared/{attaform.Dee2rU1P.cjs → attaform.BqK_L4gK.cjs} +310 -24
  46. package/dist/shared/attaform.BqK_L4gK.cjs.map +1 -0
  47. package/dist/shared/{attaform.C_5aB6EQ.d.mts → attaform.BsMdl-35.d.cts} +754 -146
  48. package/dist/shared/{attaform.C_5aB6EQ.d.ts → attaform.BsMdl-35.d.mts} +754 -146
  49. package/dist/shared/{attaform.C_5aB6EQ.d.cts → attaform.BsMdl-35.d.ts} +754 -146
  50. package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
  51. package/dist/shared/attaform.C3x1hKJC.d.mts +53 -0
  52. package/dist/shared/{attaform.CuE-bS1C.d.mts → attaform.CWs1Z3p7.d.ts} +57 -23
  53. package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
  54. package/dist/shared/attaform.CjmJpfLH.d.ts +53 -0
  55. package/dist/shared/{attaform.CVCmBKZX.mjs → attaform.CtNUB9nf.mjs} +74 -72
  56. package/dist/shared/attaform.CtNUB9nf.mjs.map +1 -0
  57. package/dist/shared/{attaform.C0iFnTN0.d.ts → attaform.D-hDvb98.d.cts} +57 -23
  58. package/dist/shared/attaform.DAH3kvav.d.mts +1651 -0
  59. package/dist/shared/{attaform.B5qiXQwN.cjs → attaform.DUHru0OF.cjs} +83 -81
  60. package/dist/shared/attaform.DUHru0OF.cjs.map +1 -0
  61. package/dist/shared/{attaform.BV40t5y2.cjs → attaform.Dlk1jMuv.cjs} +245 -108
  62. package/dist/shared/attaform.Dlk1jMuv.cjs.map +1 -0
  63. package/dist/shared/{attaform.B3ZaPIzS.mjs → attaform.DsC3rZHG.mjs} +1804 -219
  64. package/dist/shared/attaform.DsC3rZHG.mjs.map +1 -0
  65. package/dist/shared/attaform.Dzi89x8N.d.cts +53 -0
  66. package/dist/shared/{attaform.Cer8JO_P.cjs → attaform.II89Pcf4.cjs} +1860 -272
  67. package/dist/shared/attaform.II89Pcf4.cjs.map +1 -0
  68. package/dist/shared/{attaform.CIEQgJnM.mjs → attaform.Xhg0AYNa.mjs} +300 -26
  69. package/dist/shared/attaform.Xhg0AYNa.mjs.map +1 -0
  70. package/dist/shared/{attaform.CpERWz3u.mjs → attaform.Xt0A3QUd.mjs} +232 -95
  71. package/dist/shared/attaform.Xt0A3QUd.mjs.map +1 -0
  72. package/dist/shared/{attaform.CHorcsIU.d.cts → attaform.bH7WvNad.d.mts} +57 -23
  73. package/dist/vite.cjs +270 -2
  74. package/dist/vite.cjs.map +1 -1
  75. package/dist/vite.mjs +266 -2
  76. package/dist/vite.mjs.map +1 -1
  77. package/dist/zod-v3.cjs +11 -8
  78. package/dist/zod-v3.cjs.map +1 -1
  79. package/dist/zod-v3.d.cts +36 -84
  80. package/dist/zod-v3.d.mts +36 -84
  81. package/dist/zod-v3.d.ts +36 -84
  82. package/dist/zod-v3.mjs +3 -3
  83. package/dist/zod-v4.cjs +11 -8
  84. package/dist/zod-v4.cjs.map +1 -1
  85. package/dist/zod-v4.d.cts +5 -5
  86. package/dist/zod-v4.d.mts +5 -5
  87. package/dist/zod-v4.d.ts +5 -5
  88. package/dist/zod-v4.mjs +3 -3
  89. package/dist/zod.cjs +13 -10
  90. package/dist/zod.cjs.map +1 -1
  91. package/dist/zod.d.cts +127 -15
  92. package/dist/zod.d.mts +127 -15
  93. package/dist/zod.d.ts +127 -15
  94. package/dist/zod.mjs +5 -5
  95. package/dist/zod.mjs.map +1 -1
  96. package/package.json +19 -5
  97. package/dist/shared/attaform.B1jvxsOF.d.mts +0 -156
  98. package/dist/shared/attaform.B3ZaPIzS.mjs.map +0 -1
  99. package/dist/shared/attaform.B5qiXQwN.cjs.map +0 -1
  100. package/dist/shared/attaform.BBM2muQ9.cjs +0 -101
  101. package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
  102. package/dist/shared/attaform.BV40t5y2.cjs.map +0 -1
  103. package/dist/shared/attaform.C6qzEdIM.d.cts +0 -156
  104. package/dist/shared/attaform.C8LVFVVe.cjs +0 -32
  105. package/dist/shared/attaform.C8LVFVVe.cjs.map +0 -1
  106. package/dist/shared/attaform.CIEQgJnM.mjs.map +0 -1
  107. package/dist/shared/attaform.CTwNcpLE.d.ts +0 -156
  108. package/dist/shared/attaform.CVCmBKZX.mjs.map +0 -1
  109. package/dist/shared/attaform.Cer8JO_P.cjs.map +0 -1
  110. package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
  111. package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
  112. package/dist/shared/attaform.Vo-Kft0t.mjs +0 -29
  113. package/dist/shared/attaform.Vo-Kft0t.mjs.map +0 -1
  114. package/dist/shared/attaform.h1sq3BFu.mjs +0 -92
  115. package/dist/shared/attaform.h1sq3BFu.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { shallowReactive, getCurrentInstance, inject, shallowRef, onBeforeMount, onBeforeUpdate, onMounted, nextTick, warn, isRef } from 'vue';
1
+ import { shallowReactive, getCurrentInstance, inject, shallowRef, onBeforeMount, onBeforeUpdate, onMounted, nextTick, warn, isRef, effectScope, watch } from 'vue';
2
2
 
3
3
  const __DEV__ = typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
4
4
 
@@ -76,43 +76,118 @@ function detectSSR(options = {}) {
76
76
  }
77
77
 
78
78
  const kAttaformRegistry = Symbol.for("attaform:registry");
79
+ const kAttaformAncestorWizard = Symbol.for(
80
+ "attaform:ancestor-wizard"
81
+ );
82
+ const kAttaformWizardActiveStepResolver = Symbol.for(
83
+ "attaform:wizard-active-step-resolver"
84
+ );
79
85
  const kFormContext = Symbol.for("attaform:form-context");
80
86
  const kFormInstanceId = Symbol.for("attaform:form-instance-id");
81
87
  function createRegistry(options = {}) {
82
88
  const ssr = detectSSR(options);
83
89
  const defaults = Object.freeze({ ...options.defaults ?? {} });
84
90
  const forms = shallowReactive(/* @__PURE__ */ new Map());
91
+ const wizards = shallowReactive(/* @__PURE__ */ new Map());
85
92
  const pendingHydration = shallowReactive(/* @__PURE__ */ new Map());
86
93
  const consumers = /* @__PURE__ */ new Map();
87
94
  const evicting = /* @__PURE__ */ new Set();
95
+ const pendingEvictions = /* @__PURE__ */ new Map();
96
+ function cancelPendingEviction(key) {
97
+ const pending = pendingEvictions.get(key);
98
+ if (pending === void 0) return;
99
+ pending.cancelled = true;
100
+ pendingEvictions.delete(key);
101
+ }
88
102
  function trackConsumer(key) {
103
+ cancelPendingEviction(key);
89
104
  consumers.set(key, (consumers.get(key) ?? 0) + 1);
90
105
  let disposed = false;
91
106
  return () => {
92
107
  if (disposed) return;
93
108
  disposed = true;
94
109
  const remaining = (consumers.get(key) ?? 1) - 1;
95
- if (remaining <= 0) {
110
+ if (remaining > 0) {
111
+ consumers.set(key, remaining);
112
+ return;
113
+ }
114
+ consumers.delete(key);
115
+ const token = { cancelled: false };
116
+ pendingEvictions.set(key, token);
117
+ queueMicrotask(() => {
118
+ if (token.cancelled) return;
119
+ if (pendingEvictions.get(key) !== token) return;
120
+ pendingEvictions.delete(key);
96
121
  const state = forms.get(key);
97
- consumers.delete(key);
98
122
  forms.delete(key);
99
- if (state !== void 0) {
100
- evicting.add(state);
101
- void state.awaitPendingWrites().catch(() => void 0).finally(() => {
102
- evicting.delete(state);
103
- state.dispose();
104
- });
105
- }
106
- } else {
107
- consumers.set(key, remaining);
123
+ if (state === void 0) return;
124
+ evicting.add(state);
125
+ void state.awaitPendingWrites().catch(() => void 0).finally(() => {
126
+ evicting.delete(state);
127
+ state.dispose();
128
+ });
129
+ });
130
+ };
131
+ }
132
+ const wizardConsumers = /* @__PURE__ */ new Map();
133
+ const pendingWizardEvictions = /* @__PURE__ */ new Map();
134
+ function cancelPendingWizardEviction(key) {
135
+ const pending = pendingWizardEvictions.get(key);
136
+ if (pending === void 0) return;
137
+ pending.cancelled = true;
138
+ pendingWizardEvictions.delete(key);
139
+ }
140
+ function trackWizardConsumer(key) {
141
+ cancelPendingWizardEviction(key);
142
+ wizardConsumers.set(key, (wizardConsumers.get(key) ?? 0) + 1);
143
+ let disposed = false;
144
+ return () => {
145
+ if (disposed) return;
146
+ disposed = true;
147
+ const remaining = (wizardConsumers.get(key) ?? 1) - 1;
148
+ if (remaining > 0) {
149
+ wizardConsumers.set(key, remaining);
150
+ return;
108
151
  }
152
+ wizardConsumers.delete(key);
153
+ const token = { cancelled: false };
154
+ pendingWizardEvictions.set(key, token);
155
+ queueMicrotask(() => {
156
+ if (token.cancelled) return;
157
+ if (pendingWizardEvictions.get(key) !== token) return;
158
+ pendingWizardEvictions.delete(key);
159
+ wizards.delete(key);
160
+ });
109
161
  };
110
162
  }
111
163
  async function shutdown() {
112
164
  const states = [...forms.values(), ...evicting];
113
165
  await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
114
166
  }
115
- return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
167
+ const prefetchEnqueued = /* @__PURE__ */ new Set();
168
+ const prefetchSkipped = /* @__PURE__ */ new Set();
169
+ function enqueuePrefetch(key) {
170
+ prefetchEnqueued.add(key);
171
+ }
172
+ function skipPrefetch(key) {
173
+ prefetchSkipped.add(key);
174
+ }
175
+ function shouldPrefetch(key) {
176
+ return prefetchEnqueued.has(key) && !prefetchSkipped.has(key);
177
+ }
178
+ return {
179
+ forms,
180
+ wizards,
181
+ pendingHydration,
182
+ ssr,
183
+ defaults,
184
+ trackConsumer,
185
+ trackWizardConsumer,
186
+ enqueuePrefetch,
187
+ skipPrefetch,
188
+ shouldPrefetch,
189
+ shutdown
190
+ };
116
191
  }
117
192
  function useRegistry() {
118
193
  const instance = getCurrentInstance();
@@ -282,6 +357,17 @@ function useRegister() {
282
357
  if ("registerValue" in rawAttrs) {
283
358
  capturedRegisterValue.value = rawAttrs["registerValue"];
284
359
  delete rawAttrs["registerValue"];
360
+ } else {
361
+ const dirs = instance.vnode.dirs;
362
+ if (dirs !== null && dirs !== void 0) {
363
+ for (const dir of dirs) {
364
+ const marked = dir.dir?.[V_REGISTER_MARKER];
365
+ if (marked === true) {
366
+ capturedRegisterValue.value = dir.value;
367
+ break;
368
+ }
369
+ }
370
+ }
285
371
  }
286
372
  if ("value" in rawAttrs) delete rawAttrs["value"];
287
373
  };
@@ -632,6 +718,12 @@ const getModelAssigner = (el, vnode, registerValue) => {
632
718
  };
633
719
  }
634
720
  const defaultAssigner = (value) => {
721
+ if (value === void 0 && registerValue.acceptsUndefined) {
722
+ return registerValue.setValueWithInternalPath(
723
+ void 0,
724
+ computePersistMeta(el, registerValue)
725
+ );
726
+ }
635
727
  const r = runTransforms(value, registerValue);
636
728
  if (!r.ok) return false;
637
729
  const coerced = applyCoerce(r.value, registerValue);
@@ -640,9 +732,10 @@ const getModelAssigner = (el, vnode, registerValue) => {
640
732
  defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
641
733
  return defaultAssigner;
642
734
  };
643
- function syncPersistOptIn(el, value, oldValue) {
735
+ function syncPersistOptIn(el, value, oldValue, vnodeType) {
644
736
  const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
645
- const wantsOptIn = isRegisterValue(value) && value.persist === true;
737
+ const isFileInput = el.tagName === "INPUT" && (vnodeType === "file" || el.type === "file");
738
+ const wantsOptIn = !isFileInput && isRegisterValue(value) && value.persist === true;
646
739
  if (!wasOptedIn && !wantsOptIn) return;
647
740
  const elementId = getOrAssignElementId(el);
648
741
  if (wasOptedIn) {
@@ -768,7 +861,13 @@ const vRegisterText = {
768
861
  }
769
862
  if (isRegisterValue(value)) writeLastTypedForm(value, typedString);
770
863
  }
771
- el[assignKey]?.(domValue);
864
+ if (domValue === "" && isRegisterValue(value) && !value.acceptsString && !value.acceptsUndefined) {
865
+ writeLastTypedForm(value, null);
866
+ value.markBlank();
867
+ return;
868
+ }
869
+ const commit = domValue === "" && isRegisterValue(value) && value.acceptsUndefined ? void 0 : domValue;
870
+ el[assignKey]?.(commit);
772
871
  if (isRegisterValue(value) && isDefaultAssigner(el[assignKey])) {
773
872
  const storage = value.innerRef.value;
774
873
  if (storage !== domValue) {
@@ -1120,7 +1219,7 @@ const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
1120
1219
  const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1121
1220
  const vRegisterDynamic = {
1122
1221
  created(el, binding, vnode) {
1123
- syncPersistOptIn(el, binding.value, void 0);
1222
+ syncPersistOptIn(el, binding.value, void 0, vnode.props?.["type"]);
1124
1223
  syncMultiTabOptOut(binding.value, void 0);
1125
1224
  callModelHook(el, binding, vnode, null, "created");
1126
1225
  if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
@@ -1142,7 +1241,7 @@ const vRegisterDynamic = {
1142
1241
  callModelHook(el, binding, vnode, null, "mounted");
1143
1242
  },
1144
1243
  beforeUpdate(el, binding, vnode, prevVNode) {
1145
- syncPersistOptIn(el, binding.value, binding.oldValue);
1244
+ syncPersistOptIn(el, binding.value, binding.oldValue, vnode.props?.["type"]);
1146
1245
  syncMultiTabOptOut(binding.value, binding.oldValue);
1147
1246
  syncElementRegistration(el, binding.value, binding.oldValue);
1148
1247
  callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
@@ -1163,18 +1262,83 @@ const vRegisterDynamic = {
1163
1262
  delete el[assignKey];
1164
1263
  }
1165
1264
  };
1166
- const vRegisterFileNoop = {
1265
+ function isBlankFileValue(value) {
1266
+ if (value === null || value === void 0) return true;
1267
+ if (Array.isArray(value) && value.length === 0) return true;
1268
+ if (typeof FileList !== "undefined" && value instanceof FileList && value.length === 0)
1269
+ return true;
1270
+ return false;
1271
+ }
1272
+ function readFilesFromInput(el) {
1273
+ const files = el.files;
1274
+ if (el.multiple) {
1275
+ return files === null ? [] : Array.from(files);
1276
+ }
1277
+ if (files === null || files.length === 0) return null;
1278
+ return files.item(0);
1279
+ }
1280
+ const warnedPersistedFileForms = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
1281
+ function maybeWarnPersistedFile(value) {
1282
+ if (!__DEV__ || warnedPersistedFileForms === null) return;
1283
+ if (value.persist !== true) return;
1284
+ let warnedPaths = warnedPersistedFileForms.get(value.persistOptIns);
1285
+ if (warnedPaths === void 0) {
1286
+ warnedPaths = /* @__PURE__ */ new Set();
1287
+ warnedPersistedFileForms.set(value.persistOptIns, warnedPaths);
1288
+ }
1289
+ if (warnedPaths.has(value.path)) return;
1290
+ warnedPaths.add(value.path);
1291
+ warn(
1292
+ `[attaform] register('${value.path}', { persist: true }) on <input type="file"> \u2014 files can't ride a refresh (browsers block programmatic writes to <input type="file">), so this path won't be saved. For long-lived flows, upload on selection and persist the resulting URL or ID in a sibling string field.`
1293
+ );
1294
+ }
1295
+ const fileScopeKey = Symbol.for("attaform:file-scope");
1296
+ const vRegisterFile = {
1167
1297
  created(el, { value }) {
1168
1298
  if (!isRegisterValue(value)) return;
1169
- value.registerElement(el);
1170
- if (__DEV__) {
1171
- warn(
1172
- '[attaform] v-register on <input type="file"> is not supported. Handle uploads with a manual @change listener.'
1299
+ const input = el;
1300
+ value.registerElement(input);
1301
+ maybeWarnPersistedFile(value);
1302
+ const currentRaw = value.innerRef.value;
1303
+ if (isBlankFileValue(currentRaw)) {
1304
+ const blankShape = input.multiple ? [] : null;
1305
+ value.setValueWithInternalPath(blankShape, { blank: true });
1306
+ }
1307
+ addEventListener(input, "change", () => {
1308
+ const next = readFilesFromInput(input);
1309
+ const blank = isBlankFileValue(next);
1310
+ value.setValueWithInternalPath(next, blank ? { blank: true } : void 0);
1311
+ });
1312
+ const scope = effectScope(true);
1313
+ scope.run(() => {
1314
+ watch(
1315
+ value.innerRef,
1316
+ (next) => {
1317
+ if (!isBlankFileValue(next)) return;
1318
+ value.setValueWithInternalPath(next, { blank: true });
1319
+ if (input.value !== "") input.value = "";
1320
+ },
1321
+ { flush: "post" }
1173
1322
  );
1323
+ });
1324
+ input[fileScopeKey] = () => scope.stop();
1325
+ },
1326
+ beforeUpdate(el, { value }) {
1327
+ if (!isRegisterValue(value)) return;
1328
+ const input = el;
1329
+ const currentRaw = value.innerRef.value;
1330
+ if (isBlankFileValue(currentRaw)) {
1331
+ value.setValueWithInternalPath(currentRaw, { blank: true });
1332
+ if (input.value !== "") input.value = "";
1174
1333
  }
1175
1334
  },
1176
1335
  beforeUnmount(el, { value }) {
1177
1336
  removeTrackedListeners(el);
1337
+ const stop = el[fileScopeKey];
1338
+ if (stop !== void 0) {
1339
+ stop();
1340
+ delete el[fileScopeKey];
1341
+ }
1178
1342
  if (!isRegisterValue(value)) return;
1179
1343
  value.deregisterElement(el);
1180
1344
  }
@@ -1183,7 +1347,7 @@ function resolveDynamicModel(tagName, type) {
1183
1347
  if (tagName === "SELECT") return vRegisterSelect;
1184
1348
  if (tagName === "TEXTAREA") return vRegisterText;
1185
1349
  if (typeof type !== "string") return vRegisterText;
1186
- if (type === "file") return vRegisterFileNoop;
1350
+ if (type === "file") return vRegisterFile;
1187
1351
  if (type === "checkbox") return vRegisterCheckbox;
1188
1352
  if (type === "radio") return vRegisterRadio;
1189
1353
  return vRegisterText;
@@ -1193,7 +1357,9 @@ function callModelHook(el, binding, vnode, prevVNode, hook) {
1193
1357
  const fn = modelToUse[hook];
1194
1358
  fn?.(el, binding, vnode, prevVNode);
1195
1359
  }
1360
+ const V_REGISTER_MARKER = Symbol.for("attaform:v-register-directive");
1196
1361
  const vRegister = vRegisterDynamic;
1362
+ vRegisterDynamic[V_REGISTER_MARKER] = true;
1197
1363
 
1198
1364
  function installAttaformOnApp(app, options, source) {
1199
1365
  if (app._attaform !== void 0) {
@@ -1230,5 +1396,113 @@ function createAttaform(options = {}) {
1230
1396
  return plugin;
1231
1397
  }
1232
1398
 
1233
- export { AnonPersistError as A, DEFAULT_SENSITIVE_NAMES as D, InvalidPathError as I, OutsideSetupError as O, RegistryNotInstalledError as R, SensitivePersistFieldError as S, __DEV__ as _, AttaformError as a, InvalidUseFormConfigError as b, createAttaform as c, ReservedFormKeyError as d, SubmitErrorHandlerError as e, assignKey as f, getRegistryFromApp as g, createRegistry as h, isRegisterValue as i, useRegistry as j, kAttaformRegistry as k, captureUserCallSite as l, enforceSensitiveCheck as m, isSensitivePath as n, createPersistOptInRegistry as o, ensureAttaformInstalled as p, kFormContext as q, kFormInstanceId as r, segmentMatchesSensitive as s, createIsSensitivePath as t, useRegister as u, vRegister as v, createSegmentMatchesSensitive as w };
1234
- //# sourceMappingURL=attaform.CIEQgJnM.mjs.map
1399
+ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
1400
+ function normalizeSegment(raw) {
1401
+ if (typeof raw === "number") {
1402
+ if (!Number.isInteger(raw) || raw < 0) {
1403
+ throw new InvalidPathError(
1404
+ `Path segments must be non-negative integers when numeric; got ${String(raw)}`
1405
+ );
1406
+ }
1407
+ return raw;
1408
+ }
1409
+ if (INTEGER_SEGMENT.test(raw)) return Number(raw);
1410
+ return raw;
1411
+ }
1412
+ function parseDottedPath(path) {
1413
+ if (path.length === 0) return [""];
1414
+ const rawSegments = path.split(".");
1415
+ const segments = [];
1416
+ for (const raw of rawSegments) {
1417
+ if (raw.length === 0) {
1418
+ throw new InvalidPathError(
1419
+ `Path '${path}' has an empty segment; use the array form for empty keys.`
1420
+ );
1421
+ }
1422
+ segments.push(normalizeSegment(raw));
1423
+ }
1424
+ return segments;
1425
+ }
1426
+ const CANONICAL_STRING_CACHE_MAX = 128;
1427
+ const canonicalStringCache = /* @__PURE__ */ new Map();
1428
+ const PATHKEY_TO_SEGMENTS_MAX = 4096;
1429
+ const pathKeyToSegments = /* @__PURE__ */ new Map();
1430
+ function rememberSegmentsForPathKey(key, segments) {
1431
+ if (!pathKeyToSegments.has(key) && pathKeyToSegments.size >= PATHKEY_TO_SEGMENTS_MAX) {
1432
+ const oldest = pathKeyToSegments.keys().next().value;
1433
+ if (oldest !== void 0) pathKeyToSegments.delete(oldest);
1434
+ }
1435
+ pathKeyToSegments.set(key, segments);
1436
+ }
1437
+ function segmentsForPathKey(key) {
1438
+ const cached = pathKeyToSegments.get(key);
1439
+ if (cached !== void 0) return cached;
1440
+ let parsed;
1441
+ try {
1442
+ parsed = JSON.parse(key);
1443
+ } catch {
1444
+ return null;
1445
+ }
1446
+ if (!Array.isArray(parsed)) return null;
1447
+ const segments = [];
1448
+ for (const raw of parsed) {
1449
+ if (typeof raw !== "string" && typeof raw !== "number") return null;
1450
+ segments.push(normalizeSegment(raw));
1451
+ }
1452
+ rememberSegmentsForPathKey(key, segments);
1453
+ return segments;
1454
+ }
1455
+ function canonicalizePath(input) {
1456
+ if (typeof input === "string") {
1457
+ const cached = canonicalStringCache.get(input);
1458
+ if (cached !== void 0) return cached;
1459
+ const segments2 = parseDottedPath(input);
1460
+ const key2 = JSON.stringify(segments2);
1461
+ const entry = { segments: segments2, key: key2 };
1462
+ if (canonicalStringCache.size >= CANONICAL_STRING_CACHE_MAX) {
1463
+ const oldest = canonicalStringCache.keys().next().value;
1464
+ if (oldest !== void 0) canonicalStringCache.delete(oldest);
1465
+ }
1466
+ canonicalStringCache.set(input, entry);
1467
+ rememberSegmentsForPathKey(key2, segments2);
1468
+ return entry;
1469
+ }
1470
+ const segments = Array.from(input).map(normalizeSegment);
1471
+ const key = JSON.stringify(segments);
1472
+ rememberSegmentsForPathKey(key, segments);
1473
+ return { segments, key };
1474
+ }
1475
+ function segmentsToDotted(segments) {
1476
+ return segments.join(".");
1477
+ }
1478
+ function pathKeyToDotted(key) {
1479
+ const segments = segmentsForPathKey(key);
1480
+ if (segments === null) return null;
1481
+ return segmentsToDotted(segments);
1482
+ }
1483
+ function coerceToPathKey(input) {
1484
+ if (input.length > 0 && input.charCodeAt(0) === 91) {
1485
+ try {
1486
+ const parsed = JSON.parse(input);
1487
+ if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string" || typeof s === "number")) {
1488
+ return input;
1489
+ }
1490
+ } catch {
1491
+ }
1492
+ }
1493
+ return canonicalizePath(input).key;
1494
+ }
1495
+ const ROOT_PATH = Object.freeze([]);
1496
+ const ROOT_PATH_KEY = "[]";
1497
+ const FORM_ERRORS_PATH = Object.freeze([""]);
1498
+ const FORM_ERRORS_PATH_KEY = '[""]';
1499
+ function isPathPrefix(prefix, path) {
1500
+ if (path.length < prefix.length) return false;
1501
+ for (let i = 0; i < prefix.length; i++) {
1502
+ if (path[i] !== prefix[i]) return false;
1503
+ }
1504
+ return true;
1505
+ }
1506
+
1507
+ export { AnonPersistError as A, isSensitivePath as B, createPersistOptInRegistry as C, DEFAULT_SENSITIVE_NAMES as D, ensureAttaformInstalled as E, FORM_ERRORS_PATH_KEY as F, kFormContext as G, kFormInstanceId as H, InvalidPathError as I, createIsSensitivePath as J, createSegmentMatchesSensitive as K, kAttaformAncestorWizard as L, OutsideSetupError as O, ROOT_PATH as R, SensitivePersistFieldError as S, __DEV__ as _, canonicalizePath as a, AttaformError as b, createAttaform as c, InvalidUseFormConfigError as d, ROOT_PATH_KEY as e, RegistryNotInstalledError as f, getRegistryFromApp as g, ReservedFormKeyError as h, SubmitErrorHandlerError as i, assignKey as j, kAttaformWizardActiveStepResolver as k, createRegistry as l, isPathPrefix as m, isRegisterValue as n, kAttaformRegistry as o, parseDottedPath as p, useRegistry as q, pathKeyToDotted as r, segmentsForPathKey as s, captureUserCallSite as t, useRegister as u, vRegister as v, enforceSensitiveCheck as w, FORM_ERRORS_PATH as x, coerceToPathKey as y, segmentMatchesSensitive as z };
1508
+ //# sourceMappingURL=attaform.Xhg0AYNa.mjs.map