attaform 0.17.2 → 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.ts → attaform.BsMdl-35.d.cts} +754 -146
  48. package/dist/shared/{attaform.C_5aB6EQ.d.mts → 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.C6lbmMUe.d.ts → attaform.C3x1hKJC.d.mts} +4 -4
  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.C5MH4lNh.d.mts → attaform.CjmJpfLH.d.ts} +4 -4
  55. package/dist/shared/{attaform.Drt6fivF.mjs → attaform.CtNUB9nf.mjs} +74 -76
  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.BPRHR3Zs.cjs → attaform.DUHru0OF.cjs} +83 -85
  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.DtMN-MAm.d.cts → attaform.Dzi89x8N.d.cts} +4 -4
  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 +6 -6
  80. package/dist/zod-v3.d.mts +6 -6
  81. package/dist/zod-v3.d.ts +6 -6
  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 +15 -16
  90. package/dist/zod.cjs.map +1 -1
  91. package/dist/zod.d.cts +127 -40
  92. package/dist/zod.d.mts +127 -40
  93. package/dist/zod.d.ts +127 -40
  94. package/dist/zod.mjs +7 -11
  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.BBM2muQ9.cjs +0 -101
  100. package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
  101. package/dist/shared/attaform.BPRHR3Zs.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.Cer8JO_P.cjs.map +0 -1
  109. package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
  110. package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
  111. package/dist/shared/attaform.Drt6fivF.mjs.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
@@ -78,43 +78,118 @@ function detectSSR(options = {}) {
78
78
  }
79
79
 
80
80
  const kAttaformRegistry = Symbol.for("attaform:registry");
81
+ const kAttaformAncestorWizard = Symbol.for(
82
+ "attaform:ancestor-wizard"
83
+ );
84
+ const kAttaformWizardActiveStepResolver = Symbol.for(
85
+ "attaform:wizard-active-step-resolver"
86
+ );
81
87
  const kFormContext = Symbol.for("attaform:form-context");
82
88
  const kFormInstanceId = Symbol.for("attaform:form-instance-id");
83
89
  function createRegistry(options = {}) {
84
90
  const ssr = detectSSR(options);
85
91
  const defaults = Object.freeze({ ...options.defaults ?? {} });
86
92
  const forms = vue.shallowReactive(/* @__PURE__ */ new Map());
93
+ const wizards = vue.shallowReactive(/* @__PURE__ */ new Map());
87
94
  const pendingHydration = vue.shallowReactive(/* @__PURE__ */ new Map());
88
95
  const consumers = /* @__PURE__ */ new Map();
89
96
  const evicting = /* @__PURE__ */ new Set();
97
+ const pendingEvictions = /* @__PURE__ */ new Map();
98
+ function cancelPendingEviction(key) {
99
+ const pending = pendingEvictions.get(key);
100
+ if (pending === void 0) return;
101
+ pending.cancelled = true;
102
+ pendingEvictions.delete(key);
103
+ }
90
104
  function trackConsumer(key) {
105
+ cancelPendingEviction(key);
91
106
  consumers.set(key, (consumers.get(key) ?? 0) + 1);
92
107
  let disposed = false;
93
108
  return () => {
94
109
  if (disposed) return;
95
110
  disposed = true;
96
111
  const remaining = (consumers.get(key) ?? 1) - 1;
97
- if (remaining <= 0) {
112
+ if (remaining > 0) {
113
+ consumers.set(key, remaining);
114
+ return;
115
+ }
116
+ consumers.delete(key);
117
+ const token = { cancelled: false };
118
+ pendingEvictions.set(key, token);
119
+ queueMicrotask(() => {
120
+ if (token.cancelled) return;
121
+ if (pendingEvictions.get(key) !== token) return;
122
+ pendingEvictions.delete(key);
98
123
  const state = forms.get(key);
99
- consumers.delete(key);
100
124
  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);
125
+ if (state === void 0) return;
126
+ evicting.add(state);
127
+ void state.awaitPendingWrites().catch(() => void 0).finally(() => {
128
+ evicting.delete(state);
129
+ state.dispose();
130
+ });
131
+ });
132
+ };
133
+ }
134
+ const wizardConsumers = /* @__PURE__ */ new Map();
135
+ const pendingWizardEvictions = /* @__PURE__ */ new Map();
136
+ function cancelPendingWizardEviction(key) {
137
+ const pending = pendingWizardEvictions.get(key);
138
+ if (pending === void 0) return;
139
+ pending.cancelled = true;
140
+ pendingWizardEvictions.delete(key);
141
+ }
142
+ function trackWizardConsumer(key) {
143
+ cancelPendingWizardEviction(key);
144
+ wizardConsumers.set(key, (wizardConsumers.get(key) ?? 0) + 1);
145
+ let disposed = false;
146
+ return () => {
147
+ if (disposed) return;
148
+ disposed = true;
149
+ const remaining = (wizardConsumers.get(key) ?? 1) - 1;
150
+ if (remaining > 0) {
151
+ wizardConsumers.set(key, remaining);
152
+ return;
110
153
  }
154
+ wizardConsumers.delete(key);
155
+ const token = { cancelled: false };
156
+ pendingWizardEvictions.set(key, token);
157
+ queueMicrotask(() => {
158
+ if (token.cancelled) return;
159
+ if (pendingWizardEvictions.get(key) !== token) return;
160
+ pendingWizardEvictions.delete(key);
161
+ wizards.delete(key);
162
+ });
111
163
  };
112
164
  }
113
165
  async function shutdown() {
114
166
  const states = [...forms.values(), ...evicting];
115
167
  await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
116
168
  }
117
- return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
169
+ const prefetchEnqueued = /* @__PURE__ */ new Set();
170
+ const prefetchSkipped = /* @__PURE__ */ new Set();
171
+ function enqueuePrefetch(key) {
172
+ prefetchEnqueued.add(key);
173
+ }
174
+ function skipPrefetch(key) {
175
+ prefetchSkipped.add(key);
176
+ }
177
+ function shouldPrefetch(key) {
178
+ return prefetchEnqueued.has(key) && !prefetchSkipped.has(key);
179
+ }
180
+ return {
181
+ forms,
182
+ wizards,
183
+ pendingHydration,
184
+ ssr,
185
+ defaults,
186
+ trackConsumer,
187
+ trackWizardConsumer,
188
+ enqueuePrefetch,
189
+ skipPrefetch,
190
+ shouldPrefetch,
191
+ shutdown
192
+ };
118
193
  }
119
194
  function useRegistry() {
120
195
  const instance = vue.getCurrentInstance();
@@ -284,6 +359,17 @@ function useRegister() {
284
359
  if ("registerValue" in rawAttrs) {
285
360
  capturedRegisterValue.value = rawAttrs["registerValue"];
286
361
  delete rawAttrs["registerValue"];
362
+ } else {
363
+ const dirs = instance.vnode.dirs;
364
+ if (dirs !== null && dirs !== void 0) {
365
+ for (const dir of dirs) {
366
+ const marked = dir.dir?.[V_REGISTER_MARKER];
367
+ if (marked === true) {
368
+ capturedRegisterValue.value = dir.value;
369
+ break;
370
+ }
371
+ }
372
+ }
287
373
  }
288
374
  if ("value" in rawAttrs) delete rawAttrs["value"];
289
375
  };
@@ -634,6 +720,12 @@ const getModelAssigner = (el, vnode, registerValue) => {
634
720
  };
635
721
  }
636
722
  const defaultAssigner = (value) => {
723
+ if (value === void 0 && registerValue.acceptsUndefined) {
724
+ return registerValue.setValueWithInternalPath(
725
+ void 0,
726
+ computePersistMeta(el, registerValue)
727
+ );
728
+ }
637
729
  const r = runTransforms(value, registerValue);
638
730
  if (!r.ok) return false;
639
731
  const coerced = applyCoerce(r.value, registerValue);
@@ -642,9 +734,10 @@ const getModelAssigner = (el, vnode, registerValue) => {
642
734
  defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
643
735
  return defaultAssigner;
644
736
  };
645
- function syncPersistOptIn(el, value, oldValue) {
737
+ function syncPersistOptIn(el, value, oldValue, vnodeType) {
646
738
  const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
647
- const wantsOptIn = isRegisterValue(value) && value.persist === true;
739
+ const isFileInput = el.tagName === "INPUT" && (vnodeType === "file" || el.type === "file");
740
+ const wantsOptIn = !isFileInput && isRegisterValue(value) && value.persist === true;
648
741
  if (!wasOptedIn && !wantsOptIn) return;
649
742
  const elementId = getOrAssignElementId(el);
650
743
  if (wasOptedIn) {
@@ -770,7 +863,13 @@ const vRegisterText = {
770
863
  }
771
864
  if (isRegisterValue(value)) writeLastTypedForm(value, typedString);
772
865
  }
773
- el[assignKey]?.(domValue);
866
+ if (domValue === "" && isRegisterValue(value) && !value.acceptsString && !value.acceptsUndefined) {
867
+ writeLastTypedForm(value, null);
868
+ value.markBlank();
869
+ return;
870
+ }
871
+ const commit = domValue === "" && isRegisterValue(value) && value.acceptsUndefined ? void 0 : domValue;
872
+ el[assignKey]?.(commit);
774
873
  if (isRegisterValue(value) && isDefaultAssigner(el[assignKey])) {
775
874
  const storage = value.innerRef.value;
776
875
  if (storage !== domValue) {
@@ -1122,7 +1221,7 @@ const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
1122
1221
  const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1123
1222
  const vRegisterDynamic = {
1124
1223
  created(el, binding, vnode) {
1125
- syncPersistOptIn(el, binding.value, void 0);
1224
+ syncPersistOptIn(el, binding.value, void 0, vnode.props?.["type"]);
1126
1225
  syncMultiTabOptOut(binding.value, void 0);
1127
1226
  callModelHook(el, binding, vnode, null, "created");
1128
1227
  if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
@@ -1144,7 +1243,7 @@ const vRegisterDynamic = {
1144
1243
  callModelHook(el, binding, vnode, null, "mounted");
1145
1244
  },
1146
1245
  beforeUpdate(el, binding, vnode, prevVNode) {
1147
- syncPersistOptIn(el, binding.value, binding.oldValue);
1246
+ syncPersistOptIn(el, binding.value, binding.oldValue, vnode.props?.["type"]);
1148
1247
  syncMultiTabOptOut(binding.value, binding.oldValue);
1149
1248
  syncElementRegistration(el, binding.value, binding.oldValue);
1150
1249
  callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
@@ -1165,18 +1264,83 @@ const vRegisterDynamic = {
1165
1264
  delete el[assignKey];
1166
1265
  }
1167
1266
  };
1168
- const vRegisterFileNoop = {
1267
+ function isBlankFileValue(value) {
1268
+ if (value === null || value === void 0) return true;
1269
+ if (Array.isArray(value) && value.length === 0) return true;
1270
+ if (typeof FileList !== "undefined" && value instanceof FileList && value.length === 0)
1271
+ return true;
1272
+ return false;
1273
+ }
1274
+ function readFilesFromInput(el) {
1275
+ const files = el.files;
1276
+ if (el.multiple) {
1277
+ return files === null ? [] : Array.from(files);
1278
+ }
1279
+ if (files === null || files.length === 0) return null;
1280
+ return files.item(0);
1281
+ }
1282
+ const warnedPersistedFileForms = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
1283
+ function maybeWarnPersistedFile(value) {
1284
+ if (!__DEV__ || warnedPersistedFileForms === null) return;
1285
+ if (value.persist !== true) return;
1286
+ let warnedPaths = warnedPersistedFileForms.get(value.persistOptIns);
1287
+ if (warnedPaths === void 0) {
1288
+ warnedPaths = /* @__PURE__ */ new Set();
1289
+ warnedPersistedFileForms.set(value.persistOptIns, warnedPaths);
1290
+ }
1291
+ if (warnedPaths.has(value.path)) return;
1292
+ warnedPaths.add(value.path);
1293
+ vue.warn(
1294
+ `[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.`
1295
+ );
1296
+ }
1297
+ const fileScopeKey = Symbol.for("attaform:file-scope");
1298
+ const vRegisterFile = {
1169
1299
  created(el, { value }) {
1170
1300
  if (!isRegisterValue(value)) return;
1171
- value.registerElement(el);
1172
- if (__DEV__) {
1173
- vue.warn(
1174
- '[attaform] v-register on <input type="file"> is not supported. Handle uploads with a manual @change listener.'
1301
+ const input = el;
1302
+ value.registerElement(input);
1303
+ maybeWarnPersistedFile(value);
1304
+ const currentRaw = value.innerRef.value;
1305
+ if (isBlankFileValue(currentRaw)) {
1306
+ const blankShape = input.multiple ? [] : null;
1307
+ value.setValueWithInternalPath(blankShape, { blank: true });
1308
+ }
1309
+ addEventListener(input, "change", () => {
1310
+ const next = readFilesFromInput(input);
1311
+ const blank = isBlankFileValue(next);
1312
+ value.setValueWithInternalPath(next, blank ? { blank: true } : void 0);
1313
+ });
1314
+ const scope = vue.effectScope(true);
1315
+ scope.run(() => {
1316
+ vue.watch(
1317
+ value.innerRef,
1318
+ (next) => {
1319
+ if (!isBlankFileValue(next)) return;
1320
+ value.setValueWithInternalPath(next, { blank: true });
1321
+ if (input.value !== "") input.value = "";
1322
+ },
1323
+ { flush: "post" }
1175
1324
  );
1325
+ });
1326
+ input[fileScopeKey] = () => scope.stop();
1327
+ },
1328
+ beforeUpdate(el, { value }) {
1329
+ if (!isRegisterValue(value)) return;
1330
+ const input = el;
1331
+ const currentRaw = value.innerRef.value;
1332
+ if (isBlankFileValue(currentRaw)) {
1333
+ value.setValueWithInternalPath(currentRaw, { blank: true });
1334
+ if (input.value !== "") input.value = "";
1176
1335
  }
1177
1336
  },
1178
1337
  beforeUnmount(el, { value }) {
1179
1338
  removeTrackedListeners(el);
1339
+ const stop = el[fileScopeKey];
1340
+ if (stop !== void 0) {
1341
+ stop();
1342
+ delete el[fileScopeKey];
1343
+ }
1180
1344
  if (!isRegisterValue(value)) return;
1181
1345
  value.deregisterElement(el);
1182
1346
  }
@@ -1185,7 +1349,7 @@ function resolveDynamicModel(tagName, type) {
1185
1349
  if (tagName === "SELECT") return vRegisterSelect;
1186
1350
  if (tagName === "TEXTAREA") return vRegisterText;
1187
1351
  if (typeof type !== "string") return vRegisterText;
1188
- if (type === "file") return vRegisterFileNoop;
1352
+ if (type === "file") return vRegisterFile;
1189
1353
  if (type === "checkbox") return vRegisterCheckbox;
1190
1354
  if (type === "radio") return vRegisterRadio;
1191
1355
  return vRegisterText;
@@ -1195,7 +1359,9 @@ function callModelHook(el, binding, vnode, prevVNode, hook) {
1195
1359
  const fn = modelToUse[hook];
1196
1360
  fn?.(el, binding, vnode, prevVNode);
1197
1361
  }
1362
+ const V_REGISTER_MARKER = Symbol.for("attaform:v-register-directive");
1198
1363
  const vRegister = vRegisterDynamic;
1364
+ vRegisterDynamic[V_REGISTER_MARKER] = true;
1199
1365
 
1200
1366
  function installAttaformOnApp(app, options, source) {
1201
1367
  if (app._attaform !== void 0) {
@@ -1232,19 +1398,133 @@ function createAttaform(options = {}) {
1232
1398
  return plugin;
1233
1399
  }
1234
1400
 
1401
+ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
1402
+ function normalizeSegment(raw) {
1403
+ if (typeof raw === "number") {
1404
+ if (!Number.isInteger(raw) || raw < 0) {
1405
+ throw new InvalidPathError(
1406
+ `Path segments must be non-negative integers when numeric; got ${String(raw)}`
1407
+ );
1408
+ }
1409
+ return raw;
1410
+ }
1411
+ if (INTEGER_SEGMENT.test(raw)) return Number(raw);
1412
+ return raw;
1413
+ }
1414
+ function parseDottedPath(path) {
1415
+ if (path.length === 0) return [""];
1416
+ const rawSegments = path.split(".");
1417
+ const segments = [];
1418
+ for (const raw of rawSegments) {
1419
+ if (raw.length === 0) {
1420
+ throw new InvalidPathError(
1421
+ `Path '${path}' has an empty segment; use the array form for empty keys.`
1422
+ );
1423
+ }
1424
+ segments.push(normalizeSegment(raw));
1425
+ }
1426
+ return segments;
1427
+ }
1428
+ const CANONICAL_STRING_CACHE_MAX = 128;
1429
+ const canonicalStringCache = /* @__PURE__ */ new Map();
1430
+ const PATHKEY_TO_SEGMENTS_MAX = 4096;
1431
+ const pathKeyToSegments = /* @__PURE__ */ new Map();
1432
+ function rememberSegmentsForPathKey(key, segments) {
1433
+ if (!pathKeyToSegments.has(key) && pathKeyToSegments.size >= PATHKEY_TO_SEGMENTS_MAX) {
1434
+ const oldest = pathKeyToSegments.keys().next().value;
1435
+ if (oldest !== void 0) pathKeyToSegments.delete(oldest);
1436
+ }
1437
+ pathKeyToSegments.set(key, segments);
1438
+ }
1439
+ function segmentsForPathKey(key) {
1440
+ const cached = pathKeyToSegments.get(key);
1441
+ if (cached !== void 0) return cached;
1442
+ let parsed;
1443
+ try {
1444
+ parsed = JSON.parse(key);
1445
+ } catch {
1446
+ return null;
1447
+ }
1448
+ if (!Array.isArray(parsed)) return null;
1449
+ const segments = [];
1450
+ for (const raw of parsed) {
1451
+ if (typeof raw !== "string" && typeof raw !== "number") return null;
1452
+ segments.push(normalizeSegment(raw));
1453
+ }
1454
+ rememberSegmentsForPathKey(key, segments);
1455
+ return segments;
1456
+ }
1457
+ function canonicalizePath(input) {
1458
+ if (typeof input === "string") {
1459
+ const cached = canonicalStringCache.get(input);
1460
+ if (cached !== void 0) return cached;
1461
+ const segments2 = parseDottedPath(input);
1462
+ const key2 = JSON.stringify(segments2);
1463
+ const entry = { segments: segments2, key: key2 };
1464
+ if (canonicalStringCache.size >= CANONICAL_STRING_CACHE_MAX) {
1465
+ const oldest = canonicalStringCache.keys().next().value;
1466
+ if (oldest !== void 0) canonicalStringCache.delete(oldest);
1467
+ }
1468
+ canonicalStringCache.set(input, entry);
1469
+ rememberSegmentsForPathKey(key2, segments2);
1470
+ return entry;
1471
+ }
1472
+ const segments = Array.from(input).map(normalizeSegment);
1473
+ const key = JSON.stringify(segments);
1474
+ rememberSegmentsForPathKey(key, segments);
1475
+ return { segments, key };
1476
+ }
1477
+ function segmentsToDotted(segments) {
1478
+ return segments.join(".");
1479
+ }
1480
+ function pathKeyToDotted(key) {
1481
+ const segments = segmentsForPathKey(key);
1482
+ if (segments === null) return null;
1483
+ return segmentsToDotted(segments);
1484
+ }
1485
+ function coerceToPathKey(input) {
1486
+ if (input.length > 0 && input.charCodeAt(0) === 91) {
1487
+ try {
1488
+ const parsed = JSON.parse(input);
1489
+ if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string" || typeof s === "number")) {
1490
+ return input;
1491
+ }
1492
+ } catch {
1493
+ }
1494
+ }
1495
+ return canonicalizePath(input).key;
1496
+ }
1497
+ const ROOT_PATH = Object.freeze([]);
1498
+ const ROOT_PATH_KEY = "[]";
1499
+ const FORM_ERRORS_PATH = Object.freeze([""]);
1500
+ const FORM_ERRORS_PATH_KEY = '[""]';
1501
+ function isPathPrefix(prefix, path) {
1502
+ if (path.length < prefix.length) return false;
1503
+ for (let i = 0; i < prefix.length; i++) {
1504
+ if (path[i] !== prefix[i]) return false;
1505
+ }
1506
+ return true;
1507
+ }
1508
+
1235
1509
  exports.AnonPersistError = AnonPersistError;
1236
1510
  exports.AttaformError = AttaformError;
1237
1511
  exports.DEFAULT_SENSITIVE_NAMES = DEFAULT_SENSITIVE_NAMES;
1512
+ exports.FORM_ERRORS_PATH = FORM_ERRORS_PATH;
1513
+ exports.FORM_ERRORS_PATH_KEY = FORM_ERRORS_PATH_KEY;
1238
1514
  exports.InvalidPathError = InvalidPathError;
1239
1515
  exports.InvalidUseFormConfigError = InvalidUseFormConfigError;
1240
1516
  exports.OutsideSetupError = OutsideSetupError;
1517
+ exports.ROOT_PATH = ROOT_PATH;
1518
+ exports.ROOT_PATH_KEY = ROOT_PATH_KEY;
1241
1519
  exports.RegistryNotInstalledError = RegistryNotInstalledError;
1242
1520
  exports.ReservedFormKeyError = ReservedFormKeyError;
1243
1521
  exports.SensitivePersistFieldError = SensitivePersistFieldError;
1244
1522
  exports.SubmitErrorHandlerError = SubmitErrorHandlerError;
1245
1523
  exports.__DEV__ = __DEV__;
1246
1524
  exports.assignKey = assignKey;
1525
+ exports.canonicalizePath = canonicalizePath;
1247
1526
  exports.captureUserCallSite = captureUserCallSite;
1527
+ exports.coerceToPathKey = coerceToPathKey;
1248
1528
  exports.createAttaform = createAttaform;
1249
1529
  exports.createIsSensitivePath = createIsSensitivePath;
1250
1530
  exports.createPersistOptInRegistry = createPersistOptInRegistry;
@@ -1253,13 +1533,19 @@ exports.createSegmentMatchesSensitive = createSegmentMatchesSensitive;
1253
1533
  exports.enforceSensitiveCheck = enforceSensitiveCheck;
1254
1534
  exports.ensureAttaformInstalled = ensureAttaformInstalled;
1255
1535
  exports.getRegistryFromApp = getRegistryFromApp;
1536
+ exports.isPathPrefix = isPathPrefix;
1256
1537
  exports.isRegisterValue = isRegisterValue;
1257
1538
  exports.isSensitivePath = isSensitivePath;
1539
+ exports.kAttaformAncestorWizard = kAttaformAncestorWizard;
1258
1540
  exports.kAttaformRegistry = kAttaformRegistry;
1541
+ exports.kAttaformWizardActiveStepResolver = kAttaformWizardActiveStepResolver;
1259
1542
  exports.kFormContext = kFormContext;
1260
1543
  exports.kFormInstanceId = kFormInstanceId;
1544
+ exports.parseDottedPath = parseDottedPath;
1545
+ exports.pathKeyToDotted = pathKeyToDotted;
1261
1546
  exports.segmentMatchesSensitive = segmentMatchesSensitive;
1547
+ exports.segmentsForPathKey = segmentsForPathKey;
1262
1548
  exports.useRegister = useRegister;
1263
1549
  exports.useRegistry = useRegistry;
1264
1550
  exports.vRegister = vRegister;
1265
- //# sourceMappingURL=attaform.Dee2rU1P.cjs.map
1551
+ //# sourceMappingURL=attaform.BqK_L4gK.cjs.map