attaform 0.19.0 → 0.20.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 (110) hide show
  1. package/dist/chunks/devtools.cjs +1 -1
  2. package/dist/chunks/devtools.mjs +1 -1
  3. package/dist/chunks/indexeddb.cjs +1 -1
  4. package/dist/chunks/indexeddb.mjs +1 -1
  5. package/dist/chunks/local-storage.cjs +1 -1
  6. package/dist/chunks/local-storage.mjs +1 -1
  7. package/dist/chunks/session-storage.cjs +1 -1
  8. package/dist/chunks/session-storage.mjs +1 -1
  9. package/dist/index.cjs +3 -6
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +14 -40
  12. package/dist/index.d.mts +14 -40
  13. package/dist/index.d.ts +14 -40
  14. package/dist/index.mjs +5 -5
  15. package/dist/nuxt.d.cts +1 -1
  16. package/dist/nuxt.d.mts +1 -1
  17. package/dist/nuxt.d.ts +1 -1
  18. package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
  19. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
  20. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
  21. package/dist/runtime/plugins/attaform.cjs +2 -2
  22. package/dist/runtime/plugins/attaform.mjs +2 -2
  23. package/dist/shared/{attaform.CrpjyXdO.mjs → attaform.BKozEdTr.mjs} +275 -266
  24. package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
  25. package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
  26. package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
  27. package/dist/shared/{attaform.CoxJ8Qm8.cjs → attaform.BPxsYtTe.cjs} +2 -26
  28. package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
  29. package/dist/shared/{attaform.BqEfHpVB.cjs → attaform.BPy-4qRx.cjs} +275 -268
  30. package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
  31. package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
  32. package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
  33. package/dist/shared/{attaform.BTpuvGec.d.ts → attaform.Bh3ACtts.d.ts} +109 -101
  34. package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
  35. package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
  36. package/dist/shared/{attaform.JBx8cfMA.cjs → attaform.CIn4bMsD.cjs} +263 -799
  37. package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
  38. package/dist/shared/{attaform.BTi-PsHr.mjs → attaform.CKFbKFb6.mjs} +1818 -1472
  39. package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
  40. package/dist/shared/{attaform.ePUcKxId.d.cts → attaform.D5-1XGQU.d.cts} +109 -101
  41. package/dist/shared/{attaform.a3uBo-gw.mjs → attaform.DEBvCjeH.mjs} +257 -793
  42. package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
  43. package/dist/shared/{attaform.C1msmO2v.cjs → attaform.DL4CQ-oW.cjs} +1823 -1477
  44. package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
  45. package/dist/shared/{attaform.D4I63aBV.d.ts → attaform.DSD85fHb.d.cts} +1 -19
  46. package/dist/shared/{attaform.CBjmobqk.d.cts → attaform.DSD85fHb.d.mts} +1 -19
  47. package/dist/shared/{attaform.DXYHL99q.d.mts → attaform.DSD85fHb.d.ts} +1 -19
  48. package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
  49. package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
  50. package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
  51. package/dist/shared/{attaform.CJ-e9gYI.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
  52. package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
  53. package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
  54. package/dist/shared/{attaform.CRNA0vrd.d.mts → attaform.DoKXru-a.d.mts} +1 -1
  55. package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
  56. package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
  57. package/dist/shared/{attaform.BtBmfLQN.d.mts → attaform.EMzJcQci.d.mts} +109 -101
  58. package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
  59. package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
  60. package/dist/shared/{attaform.QvygsFGh.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
  61. package/dist/shared/{attaform.C0uGZQ4M.d.ts → attaform.SfhU0OEY.d.cts} +134 -30
  62. package/dist/shared/{attaform.C0uGZQ4M.d.cts → attaform.SfhU0OEY.d.mts} +134 -30
  63. package/dist/shared/{attaform.C0uGZQ4M.d.mts → attaform.SfhU0OEY.d.ts} +134 -30
  64. package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
  65. package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
  66. package/dist/transforms.cjs +2 -2
  67. package/dist/transforms.d.cts +22 -13
  68. package/dist/transforms.d.mts +22 -13
  69. package/dist/transforms.d.ts +22 -13
  70. package/dist/transforms.mjs +1 -1
  71. package/dist/vite.cjs +8 -7
  72. package/dist/vite.cjs.map +1 -1
  73. package/dist/vite.mjs +8 -7
  74. package/dist/vite.mjs.map +1 -1
  75. package/dist/zod-v3.cjs +3 -3
  76. package/dist/zod-v3.d.cts +32 -6
  77. package/dist/zod-v3.d.mts +32 -6
  78. package/dist/zod-v3.d.ts +32 -6
  79. package/dist/zod-v3.mjs +3 -3
  80. package/dist/zod-v4.cjs +3 -3
  81. package/dist/zod-v4.d.cts +12 -8
  82. package/dist/zod-v4.d.mts +12 -8
  83. package/dist/zod-v4.d.ts +12 -8
  84. package/dist/zod-v4.mjs +3 -3
  85. package/dist/zod.cjs +8 -8
  86. package/dist/zod.cjs.map +1 -1
  87. package/dist/zod.d.cts +6 -6
  88. package/dist/zod.d.mts +6 -6
  89. package/dist/zod.d.ts +6 -6
  90. package/dist/zod.mjs +6 -6
  91. package/package.json +1 -1
  92. package/dist/shared/attaform.BTi-PsHr.mjs.map +0 -1
  93. package/dist/shared/attaform.BqEfHpVB.cjs.map +0 -1
  94. package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
  95. package/dist/shared/attaform.C1msmO2v.cjs.map +0 -1
  96. package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
  97. package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
  98. package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
  99. package/dist/shared/attaform.Cghpuav8.mjs +0 -57
  100. package/dist/shared/attaform.Cghpuav8.mjs.map +0 -1
  101. package/dist/shared/attaform.CiMqJHDm.mjs +0 -1594
  102. package/dist/shared/attaform.CiMqJHDm.mjs.map +0 -1
  103. package/dist/shared/attaform.CoxJ8Qm8.cjs.map +0 -1
  104. package/dist/shared/attaform.CrpjyXdO.mjs.map +0 -1
  105. package/dist/shared/attaform.D13GMFgK.mjs +0 -32
  106. package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
  107. package/dist/shared/attaform.JBx8cfMA.cjs.map +0 -1
  108. package/dist/shared/attaform.OznWyOPy.cjs +0 -1600
  109. package/dist/shared/attaform.OznWyOPy.cjs.map +0 -1
  110. package/dist/shared/attaform.a3uBo-gw.mjs.map +0 -1
@@ -45,14 +45,6 @@ class ReservedFormKeyError extends AttaformError {
45
45
  );
46
46
  }
47
47
  }
48
- class SensitivePersistFieldError extends AttaformError {
49
- constructor(path) {
50
- const display = Array.isArray(path) ? path.join(".") : String(path);
51
- super(
52
- `[attaform] Refusing to persist "${display}" \u2014 this path matches a sensitive-name pattern (password / cvv / ssn / token / etc.). Storing sensitive data in client-side storage is a compliance risk (HIPAA / PII / PCI-DSS / SOC2). Fix: persist this server-side, OR pass \`acknowledgeSensitive: true\` to register() (or form.persist()) if the client-side persistence is intentional.`
53
- );
54
- }
55
- }
56
48
  class AnonPersistError extends AttaformError {
57
49
  constructor(opts) {
58
50
  super(formatAnonPersistMessage(opts));
@@ -406,6 +398,197 @@ function warnNoParentRV(instance) {
406
398
  );
407
399
  }
408
400
 
401
+ const MANAGED_ARIA_ATTRS = [
402
+ "aria-invalid",
403
+ "aria-busy",
404
+ "aria-required",
405
+ "aria-describedby"
406
+ ];
407
+ const ariaLockKey = Symbol.for("attaform:aria-locks");
408
+ const ariaScopeKey = Symbol.for("attaform:aria-scope");
409
+ const EMPTY_ARIA_LOCKS = /* @__PURE__ */ new Set();
410
+ function mergeAriaLocks(el, vnode) {
411
+ let locks = el[ariaLockKey];
412
+ if (locks === void 0) {
413
+ locks = /* @__PURE__ */ new Set();
414
+ el[ariaLockKey] = locks;
415
+ }
416
+ const props = vnode.props;
417
+ if (props !== null) {
418
+ for (const attr of MANAGED_ARIA_ATTRS) {
419
+ if (attr in props) locks.add(attr);
420
+ }
421
+ }
422
+ return locks;
423
+ }
424
+ function setAriaAttr(el, attr, value) {
425
+ if (value === null) el.removeAttribute(attr);
426
+ else el.setAttribute(attr, value);
427
+ }
428
+ function resolveAriaValue(attr, rv, ds) {
429
+ switch (attr) {
430
+ case "aria-invalid":
431
+ return ds === "error" ? "true" : null;
432
+ case "aria-busy":
433
+ return ds === "pending" ? "true" : null;
434
+ case "aria-required":
435
+ return rv.isRequired === true ? "true" : null;
436
+ case "aria-describedby":
437
+ return ds === "error" && rv.aria?.errorId !== void 0 ? rv.aria.errorId : null;
438
+ default:
439
+ return null;
440
+ }
441
+ }
442
+ function applyAria(el, rv) {
443
+ if (rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) return;
444
+ const locks = el[ariaLockKey] ?? EMPTY_ARIA_LOCKS;
445
+ const ds = rv.ariaDisplayState.value;
446
+ for (const attr of MANAGED_ARIA_ATTRS) {
447
+ if (!locks.has(attr)) setAriaAttr(el, attr, resolveAriaValue(attr, rv, ds));
448
+ }
449
+ }
450
+ function setupAria(el, rv, vnode) {
451
+ if (rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) return;
452
+ mergeAriaLocks(el, vnode);
453
+ applyAria(el, rv);
454
+ const displayState = rv.ariaDisplayState;
455
+ const scope = vue.effectScope(true);
456
+ scope.run(() => {
457
+ vue.watch(displayState, () => applyAria(el, rv), { flush: "post" });
458
+ });
459
+ el[ariaScopeKey] = () => scope.stop();
460
+ }
461
+ function getSSRAriaProps(rv, vnode) {
462
+ if (rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) return void 0;
463
+ const props = vnode?.props ?? null;
464
+ const ds = rv.ariaDisplayState.value;
465
+ const out = {};
466
+ for (const attr of MANAGED_ARIA_ATTRS) {
467
+ if (props !== null && attr in props) continue;
468
+ const value = resolveAriaValue(attr, rv, ds);
469
+ if (value !== null) out[attr] = value;
470
+ }
471
+ return out;
472
+ }
473
+ function teardownAria(el) {
474
+ const stop = el[ariaScopeKey];
475
+ if (stop === void 0) return;
476
+ stop();
477
+ delete el[ariaScopeKey];
478
+ const locks = el[ariaLockKey] ?? EMPTY_ARIA_LOCKS;
479
+ for (const attr of MANAGED_ARIA_ATTRS) {
480
+ if (!locks.has(attr)) el.removeAttribute(attr);
481
+ }
482
+ delete el[ariaLockKey];
483
+ }
484
+
485
+ const listenersKey = Symbol.for("attaform:directive-listeners");
486
+ function addTrackedListener(el, event, handler, options) {
487
+ el.addEventListener(event, handler, options);
488
+ const carrier = el;
489
+ const bag = carrier[listenersKey] ?? [];
490
+ bag.push({ event, handler, options });
491
+ carrier[listenersKey] = bag;
492
+ }
493
+ function removeTrackedListeners(el) {
494
+ const carrier = el;
495
+ const bag = carrier[listenersKey];
496
+ if (bag === void 0) return;
497
+ for (const { event, handler, options } of bag) {
498
+ el.removeEventListener(event, handler, options);
499
+ }
500
+ delete carrier[listenersKey];
501
+ }
502
+ function noteInteraction(value) {
503
+ if (isRegisterValueLike(value)) value.markInteracted();
504
+ }
505
+ function isRegisterValueLike(val) {
506
+ return typeof val === "object" && val !== null && "markInteracted" in val && typeof val.markInteracted === "function";
507
+ }
508
+
509
+ function isBlankFileValue(value) {
510
+ if (value === null || value === void 0) return true;
511
+ if (Array.isArray(value) && value.length === 0) return true;
512
+ if (typeof FileList !== "undefined" && value instanceof FileList && value.length === 0)
513
+ return true;
514
+ return false;
515
+ }
516
+ function readFilesFromInput(el) {
517
+ const files = el.files;
518
+ if (el.multiple) {
519
+ return files === null ? [] : Array.from(files);
520
+ }
521
+ if (files === null || files.length === 0) return null;
522
+ return files.item(0);
523
+ }
524
+ const warnedPersistedFileForms = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
525
+ function maybeWarnPersistedFile(value) {
526
+ if (!__DEV__ || warnedPersistedFileForms === null) return;
527
+ if (value.persist !== true) return;
528
+ let warnedPaths = warnedPersistedFileForms.get(value.persistOptIns);
529
+ if (warnedPaths === void 0) {
530
+ warnedPaths = /* @__PURE__ */ new Set();
531
+ warnedPersistedFileForms.set(value.persistOptIns, warnedPaths);
532
+ }
533
+ if (warnedPaths.has(value.path)) return;
534
+ warnedPaths.add(value.path);
535
+ vue.warn(
536
+ `[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.`
537
+ );
538
+ }
539
+ const fileScopeKey = Symbol.for("attaform:file-scope");
540
+ const vRegisterFile = {
541
+ created(el, { value }) {
542
+ if (!isRegisterValue(value)) return;
543
+ const input = el;
544
+ value.registerElement(input);
545
+ maybeWarnPersistedFile(value);
546
+ const currentRaw = value.innerRef.value;
547
+ if (isBlankFileValue(currentRaw)) {
548
+ const blankShape = input.multiple ? [] : null;
549
+ value.setValueWithInternalPath(blankShape, { blank: true });
550
+ }
551
+ addTrackedListener(input, "change", () => {
552
+ noteInteraction(value);
553
+ const next = readFilesFromInput(input);
554
+ const blank = isBlankFileValue(next);
555
+ value.setValueWithInternalPath(next, blank ? { blank: true } : void 0);
556
+ });
557
+ const scope = vue.effectScope(true);
558
+ scope.run(() => {
559
+ vue.watch(
560
+ value.innerRef,
561
+ (next) => {
562
+ if (!isBlankFileValue(next)) return;
563
+ value.setValueWithInternalPath(next, { blank: true });
564
+ if (input.value !== "") input.value = "";
565
+ },
566
+ { flush: "post" }
567
+ );
568
+ });
569
+ input[fileScopeKey] = () => scope.stop();
570
+ },
571
+ beforeUpdate(el, { value }) {
572
+ if (!isRegisterValue(value)) return;
573
+ const input = el;
574
+ const currentRaw = value.innerRef.value;
575
+ if (isBlankFileValue(currentRaw)) {
576
+ value.setValueWithInternalPath(currentRaw, { blank: true });
577
+ if (input.value !== "") input.value = "";
578
+ }
579
+ },
580
+ beforeUnmount(el, { value }) {
581
+ removeTrackedListeners(el);
582
+ const stop = el[fileScopeKey];
583
+ if (stop !== void 0) {
584
+ stop();
585
+ delete el[fileScopeKey];
586
+ }
587
+ if (!isRegisterValue(value)) return;
588
+ value.deregisterElement(el);
589
+ }
590
+ };
591
+
409
592
  const idGenerator = /* @__PURE__ */ (() => {
410
593
  let counter = 0;
411
594
  return () => `el-${++counter}`;
@@ -590,22 +773,81 @@ function createIsSensitivePath(names = DEFAULT_SENSITIVE_NAMES) {
590
773
  return false;
591
774
  };
592
775
  }
593
- const defaultSegmentMatches = createSegmentMatchesSensitive();
594
776
  const defaultIsSensitivePath = createIsSensitivePath();
595
- function segmentMatchesSensitive(segment) {
596
- return defaultSegmentMatches(segment);
597
- }
598
777
  function isSensitivePath(path) {
599
778
  return defaultIsSensitivePath(path);
600
779
  }
601
- function enforceSensitiveCheck(path, acknowledged, isSensitive = defaultIsSensitivePath) {
602
- if (acknowledged) return;
603
- if (!isSensitive(path)) return;
604
- throw new SensitivePersistFieldError(path);
780
+ const warnedSensitivePersist = __DEV__ ? /* @__PURE__ */ new Set() : null;
781
+ function allowSensitivePersist(path, acknowledged, isSensitive = defaultIsSensitivePath) {
782
+ if (acknowledged) return true;
783
+ if (!isSensitive(path)) return true;
784
+ if (warnedSensitivePersist !== null) {
785
+ const display = Array.isArray(path) ? path.join(".") : String(path);
786
+ if (!warnedSensitivePersist.has(display)) {
787
+ warnedSensitivePersist.add(display);
788
+ console.warn(
789
+ `[attaform] Not persisting "${display}" \u2014 it matches a sensitive-name pattern (password / cvv / ssn / token / etc.) and was opted into persistence without \`acknowledgeSensitive: true\`. Storing sensitive data in client-side storage is a compliance risk (PII / PCI-DSS / HIPAA / SOC2). Persist it server-side, or pass \`acknowledgeSensitive: true\` to register() / form.persist() if this is intentional.`
790
+ );
791
+ }
792
+ }
793
+ return false;
605
794
  }
606
795
 
796
+ function syncPersistOptIn(el, value, oldValue, vnodeType) {
797
+ const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
798
+ const isFileInput = el.tagName === "INPUT" && (vnodeType === "file" || el.type === "file");
799
+ const wantsOptIn = !isFileInput && isRegisterValue(value) && value.persist === true;
800
+ if (!wasOptedIn && !wantsOptIn) return;
801
+ const elementId = getOrAssignElementId(el);
802
+ if (wasOptedIn) {
803
+ const old = oldValue;
804
+ const samePathAndRegistry = wantsOptIn && value.path === old.path && value.persistOptIns === old.persistOptIns;
805
+ if (!samePathAndRegistry) {
806
+ old.persistOptIns.remove(elementId, old.path);
807
+ }
808
+ }
809
+ if (wantsOptIn) {
810
+ const v = value;
811
+ if (allowSensitivePersist(v.path, v.acknowledgeSensitive, v.isSensitivePath)) {
812
+ v.persistOptIns.add(elementId, v.path);
813
+ }
814
+ }
815
+ }
816
+ function syncMultiTabOptOut(value, oldValue) {
817
+ const wasOptedOut = isRegisterValue(oldValue) && oldValue.unmarkNoSync !== void 0;
818
+ const wantsOptOut = isRegisterValue(value) && value.markNoSync !== void 0;
819
+ if (!wasOptedOut && !wantsOptOut) return;
820
+ if (wasOptedOut) {
821
+ const old = oldValue;
822
+ const samePath = wantsOptOut && value.path === old.path;
823
+ if (!samePath) old.unmarkNoSync?.();
824
+ }
825
+ if (wantsOptOut) {
826
+ const v = value;
827
+ const samePathOld = wasOptedOut && oldValue.path === v.path;
828
+ if (!samePathOld) v.markNoSync?.();
829
+ }
830
+ }
831
+ function syncElementRegistration(el, value, oldValue) {
832
+ const wasRegistered = isRegisterValue(oldValue);
833
+ const isRegistered = isRegisterValue(value);
834
+ if (!wasRegistered && !isRegistered) return;
835
+ if (wasRegistered && isRegistered) {
836
+ const old = oldValue;
837
+ const next = value;
838
+ if (old.path === next.path && old.persistOptIns === next.persistOptIns) return;
839
+ }
840
+ if (wasRegistered) {
841
+ oldValue.deregisterElement(el);
842
+ }
843
+ if (isRegistered) {
844
+ value.registerElement(el);
845
+ }
846
+ }
847
+
848
+ const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
849
+
607
850
  const assignKey = Symbol.for("attaform:assign-key");
608
- const listenersKey = Symbol.for("attaform:directive-listeners");
609
851
  function isRegisterValue(val) {
610
852
  if (typeof val !== "object" || val === null) return false;
611
853
  if (!("innerRef" in val)) return false;
@@ -616,22 +858,6 @@ function isRegisterValue(val) {
616
858
  if (typeof val.setValueWithInternalPath !== "function") return false;
617
859
  return true;
618
860
  }
619
- function addEventListener(el, event, handler, options) {
620
- el.addEventListener(event, handler, options);
621
- const carrier = el;
622
- const bag = carrier[listenersKey] ?? [];
623
- bag.push({ event, handler, options });
624
- carrier[listenersKey] = bag;
625
- }
626
- function removeTrackedListeners(el) {
627
- const carrier = el;
628
- const bag = carrier[listenersKey];
629
- if (bag === void 0) return;
630
- for (const { event, handler, options } of bag) {
631
- el.removeEventListener(event, handler, options);
632
- }
633
- delete carrier[listenersKey];
634
- }
635
861
  function writeLastTypedForm(rv, next) {
636
862
  rv.lastTypedForm.value = next;
637
863
  }
@@ -644,7 +870,7 @@ function isDefaultAssigner(fn) {
644
870
  return typeof fn === "function" && fn[DEFAULT_ASSIGNER_TAG] === true;
645
871
  }
646
872
  function shouldBailListener(el) {
647
- if (SUPPORTED_TAGS.has(el.tagName)) return false;
873
+ if (INTERACTIVE_TAG_NAMES.has(el.tagName)) return false;
648
874
  return isDefaultAssigner(el[assignKey]);
649
875
  }
650
876
  function runTransforms(initial, registerValue) {
@@ -734,56 +960,6 @@ const getModelAssigner = (el, vnode, registerValue) => {
734
960
  defaultAssigner[DEFAULT_ASSIGNER_TAG] = true;
735
961
  return defaultAssigner;
736
962
  };
737
- function syncPersistOptIn(el, value, oldValue, vnodeType) {
738
- const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
739
- const isFileInput = el.tagName === "INPUT" && (vnodeType === "file" || el.type === "file");
740
- const wantsOptIn = !isFileInput && isRegisterValue(value) && value.persist === true;
741
- if (!wasOptedIn && !wantsOptIn) return;
742
- const elementId = getOrAssignElementId(el);
743
- if (wasOptedIn) {
744
- const old = oldValue;
745
- const samePathAndRegistry = wantsOptIn && value.path === old.path && value.persistOptIns === old.persistOptIns;
746
- if (!samePathAndRegistry) {
747
- old.persistOptIns.remove(elementId, old.path);
748
- }
749
- }
750
- if (wantsOptIn) {
751
- const v = value;
752
- enforceSensitiveCheck(v.path, v.acknowledgeSensitive, v.isSensitivePath);
753
- v.persistOptIns.add(elementId, v.path);
754
- }
755
- }
756
- function syncMultiTabOptOut(value, oldValue) {
757
- const wasOptedOut = isRegisterValue(oldValue) && oldValue.unmarkNoSync !== void 0;
758
- const wantsOptOut = isRegisterValue(value) && value.markNoSync !== void 0;
759
- if (!wasOptedOut && !wantsOptOut) return;
760
- if (wasOptedOut) {
761
- const old = oldValue;
762
- const samePath = wantsOptOut && value.path === old.path;
763
- if (!samePath) old.unmarkNoSync?.();
764
- }
765
- if (wantsOptOut) {
766
- const v = value;
767
- const samePathOld = wasOptedOut && oldValue.path === v.path;
768
- if (!samePathOld) v.markNoSync?.();
769
- }
770
- }
771
- function syncElementRegistration(el, value, oldValue) {
772
- const wasRegistered = isRegisterValue(oldValue);
773
- const isRegistered = isRegisterValue(value);
774
- if (!wasRegistered && !isRegistered) return;
775
- if (wasRegistered && isRegistered) {
776
- const old = oldValue;
777
- const next = value;
778
- if (old.path === next.path && old.persistOptIns === next.persistOptIns) return;
779
- }
780
- if (wasRegistered) {
781
- oldValue.deregisterElement(el);
782
- }
783
- if (isRegistered) {
784
- value.registerElement(el);
785
- }
786
- }
787
963
  function onCompositionStart(e) {
788
964
  const target = e.target;
789
965
  if (!target) return;
@@ -818,9 +994,6 @@ function setAssignFunction(el, vnode, value) {
818
994
  }
819
995
  el[assignKey] = getModelAssigner(el, vnode, value);
820
996
  }
821
- function noteInteraction(value) {
822
- if (isRegisterValue(value)) value.markInteracted();
823
- }
824
997
  const vRegisterText = {
825
998
  created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
826
999
  const castToNumber = number === true || vnode.props?.["type"] === "number";
@@ -828,7 +1001,7 @@ const vRegisterText = {
828
1001
  value.registerElement(el);
829
1002
  setAssignFunction(el, vnode, value);
830
1003
  }
831
- addEventListener(el, lazy === true ? "change" : "input", (e) => {
1004
+ addTrackedListener(el, lazy === true ? "change" : "input", (e) => {
832
1005
  if (shouldBailListener(el)) return;
833
1006
  const target = e.target;
834
1007
  if (target === null || target.composing) return;
@@ -884,7 +1057,7 @@ const vRegisterText = {
884
1057
  }
885
1058
  });
886
1059
  if (trim === true || castToNumber) {
887
- addEventListener(el, "change", () => {
1060
+ addTrackedListener(el, "change", () => {
888
1061
  if (shouldBailListener(el)) return;
889
1062
  let normalized = el.value;
890
1063
  if (trim === true) normalized = normalized.trim();
@@ -910,12 +1083,12 @@ const vRegisterText = {
910
1083
  });
911
1084
  }
912
1085
  if (lazy !== true) {
913
- addEventListener(el, "compositionstart", onCompositionStart);
914
- addEventListener(el, "compositionend", onCompositionEnd);
915
- addEventListener(el, "change", onCompositionEnd);
1086
+ addTrackedListener(el, "compositionstart", onCompositionStart);
1087
+ addTrackedListener(el, "compositionend", onCompositionEnd);
1088
+ addTrackedListener(el, "change", onCompositionEnd);
916
1089
  }
917
1090
  if (number === true && vnode.props?.["type"] !== "number") {
918
- addEventListener(el, "beforeinput", (e) => {
1091
+ addTrackedListener(el, "beforeinput", (e) => {
919
1092
  const ev = e;
920
1093
  if (ev.inputType !== "insertText" && ev.inputType !== "insertFromPaste" && ev.inputType !== "insertFromDrop") {
921
1094
  return;
@@ -962,7 +1135,7 @@ const vRegisterCheckbox = {
962
1135
  if (!isRegisterValue(value)) return;
963
1136
  value.registerElement(el);
964
1137
  setAssignFunction(el, vnode, value);
965
- addEventListener(el, "change", () => {
1138
+ addTrackedListener(el, "change", () => {
966
1139
  if (shouldBailListener(el)) return;
967
1140
  noteInteraction(value);
968
1141
  const modelValue = value.innerRef.value ?? [];
@@ -1060,7 +1233,7 @@ const vRegisterRadio = {
1060
1233
  if (!isRegisterValue(value)) return;
1061
1234
  value.registerElement(el);
1062
1235
  setAssignFunction(el, vnode, value);
1063
- addEventListener(el, "change", () => {
1236
+ addTrackedListener(el, "change", () => {
1064
1237
  if (shouldBailListener(el)) return;
1065
1238
  noteInteraction(value);
1066
1239
  el[assignKey]?.(getValue(el));
@@ -1109,7 +1282,7 @@ const vRegisterSelect = {
1109
1282
  if (!isRegisterValue(value)) return;
1110
1283
  value.registerElement(el);
1111
1284
  const isSetModel = isSet(value.innerRef.value);
1112
- addEventListener(el, "change", () => {
1285
+ addTrackedListener(el, "change", () => {
1113
1286
  if (shouldBailListener(el)) return;
1114
1287
  noteInteraction(value);
1115
1288
  const selectedVal = Array.prototype.filter.call(el.options, (o) => o.selected).map((o) => number === true ? looseToNumber(getValue(o)) : getValue(o));
@@ -1224,86 +1397,14 @@ function getCheckboxValue(el, checked) {
1224
1397
  const key = checked ? "_trueValue" : "_falseValue";
1225
1398
  return key in el ? el[key] : checked;
1226
1399
  }
1227
- const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
1228
1400
  const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1229
- const MANAGED_ARIA_ATTRS = [
1230
- "aria-invalid",
1231
- "aria-busy",
1232
- "aria-required",
1233
- "aria-describedby"
1234
- ];
1235
- const ariaLockKey = Symbol.for("attaform:aria-locks");
1236
- const ariaScopeKey = Symbol.for("attaform:aria-scope");
1237
- const EMPTY_ARIA_LOCKS = /* @__PURE__ */ new Set();
1238
- function mergeAriaLocks(el, vnode) {
1239
- let locks = el[ariaLockKey];
1240
- if (locks === void 0) {
1241
- locks = /* @__PURE__ */ new Set();
1242
- el[ariaLockKey] = locks;
1243
- }
1244
- const props = vnode.props;
1245
- if (props !== null) {
1246
- for (const attr of MANAGED_ARIA_ATTRS) {
1247
- if (attr in props) locks.add(attr);
1248
- }
1249
- }
1250
- return locks;
1251
- }
1252
- function setAriaAttr(el, attr, value) {
1253
- if (value === null) el.removeAttribute(attr);
1254
- else el.setAttribute(attr, value);
1255
- }
1256
- function resolveAriaValue(attr, rv, ds) {
1257
- switch (attr) {
1258
- case "aria-invalid":
1259
- return ds === "error" ? "true" : null;
1260
- case "aria-busy":
1261
- return ds === "pending" ? "true" : null;
1262
- case "aria-required":
1263
- return rv.isRequired === true ? "true" : null;
1264
- case "aria-describedby":
1265
- return ds === "error" && rv.aria?.errorId !== void 0 ? rv.aria.errorId : null;
1266
- default:
1267
- return null;
1268
- }
1269
- }
1270
- function applyAria(el, rv) {
1271
- if (rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) return;
1272
- const locks = el[ariaLockKey] ?? EMPTY_ARIA_LOCKS;
1273
- const ds = rv.ariaDisplayState.value;
1274
- for (const attr of MANAGED_ARIA_ATTRS) {
1275
- if (!locks.has(attr)) setAriaAttr(el, attr, resolveAriaValue(attr, rv, ds));
1276
- }
1277
- }
1278
- function setupAria(el, rv, vnode) {
1279
- if (rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) return;
1280
- mergeAriaLocks(el, vnode);
1281
- applyAria(el, rv);
1282
- const displayState = rv.ariaDisplayState;
1283
- const scope = vue.effectScope(true);
1284
- scope.run(() => {
1285
- vue.watch(displayState, () => applyAria(el, rv), { flush: "post" });
1286
- });
1287
- el[ariaScopeKey] = () => scope.stop();
1288
- }
1289
- function teardownAria(el) {
1290
- const stop = el[ariaScopeKey];
1291
- if (stop === void 0) return;
1292
- stop();
1293
- delete el[ariaScopeKey];
1294
- const locks = el[ariaLockKey] ?? EMPTY_ARIA_LOCKS;
1295
- for (const attr of MANAGED_ARIA_ATTRS) {
1296
- if (!locks.has(attr)) el.removeAttribute(attr);
1297
- }
1298
- delete el[ariaLockKey];
1299
- }
1300
1401
  const vRegisterDynamic = {
1301
1402
  created(el, binding, vnode) {
1302
1403
  syncPersistOptIn(el, binding.value, void 0, vnode.props?.["type"]);
1303
1404
  syncMultiTabOptOut(binding.value, void 0);
1304
1405
  callModelHook(el, binding, vnode, null, "created");
1305
1406
  if (isRegisterValue(binding.value)) setupAria(el, binding.value, vnode);
1306
- if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
1407
+ if (__DEV__ && warnedUnsupportedElements !== null && !INTERACTIVE_TAG_NAMES.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
1307
1408
  void vue.nextTick(() => {
1308
1409
  if (warnedUnsupportedElements.has(el)) return;
1309
1410
  const hasMarker = el[REGISTER_OWNER_MARKER] === true;
@@ -1367,100 +1468,8 @@ const vRegisterDynamic = {
1367
1468
  // describedby matches the client after hydration.
1368
1469
  getSSRProps(binding, vnode) {
1369
1470
  const rv = binding.value;
1370
- if (!isRegisterValue(rv) || rv.ariaEnabled !== true || rv.ariaDisplayState === void 0) {
1371
- return void 0;
1372
- }
1373
- const props = vnode?.props ?? null;
1374
- const ds = rv.ariaDisplayState.value;
1375
- const out = {};
1376
- for (const attr of MANAGED_ARIA_ATTRS) {
1377
- if (props !== null && attr in props) continue;
1378
- const value = resolveAriaValue(attr, rv, ds);
1379
- if (value !== null) out[attr] = value;
1380
- }
1381
- return out;
1382
- }
1383
- };
1384
- function isBlankFileValue(value) {
1385
- if (value === null || value === void 0) return true;
1386
- if (Array.isArray(value) && value.length === 0) return true;
1387
- if (typeof FileList !== "undefined" && value instanceof FileList && value.length === 0)
1388
- return true;
1389
- return false;
1390
- }
1391
- function readFilesFromInput(el) {
1392
- const files = el.files;
1393
- if (el.multiple) {
1394
- return files === null ? [] : Array.from(files);
1395
- }
1396
- if (files === null || files.length === 0) return null;
1397
- return files.item(0);
1398
- }
1399
- const warnedPersistedFileForms = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
1400
- function maybeWarnPersistedFile(value) {
1401
- if (!__DEV__ || warnedPersistedFileForms === null) return;
1402
- if (value.persist !== true) return;
1403
- let warnedPaths = warnedPersistedFileForms.get(value.persistOptIns);
1404
- if (warnedPaths === void 0) {
1405
- warnedPaths = /* @__PURE__ */ new Set();
1406
- warnedPersistedFileForms.set(value.persistOptIns, warnedPaths);
1407
- }
1408
- if (warnedPaths.has(value.path)) return;
1409
- warnedPaths.add(value.path);
1410
- vue.warn(
1411
- `[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.`
1412
- );
1413
- }
1414
- const fileScopeKey = Symbol.for("attaform:file-scope");
1415
- const vRegisterFile = {
1416
- created(el, { value }) {
1417
- if (!isRegisterValue(value)) return;
1418
- const input = el;
1419
- value.registerElement(input);
1420
- maybeWarnPersistedFile(value);
1421
- const currentRaw = value.innerRef.value;
1422
- if (isBlankFileValue(currentRaw)) {
1423
- const blankShape = input.multiple ? [] : null;
1424
- value.setValueWithInternalPath(blankShape, { blank: true });
1425
- }
1426
- addEventListener(input, "change", () => {
1427
- noteInteraction(value);
1428
- const next = readFilesFromInput(input);
1429
- const blank = isBlankFileValue(next);
1430
- value.setValueWithInternalPath(next, blank ? { blank: true } : void 0);
1431
- });
1432
- const scope = vue.effectScope(true);
1433
- scope.run(() => {
1434
- vue.watch(
1435
- value.innerRef,
1436
- (next) => {
1437
- if (!isBlankFileValue(next)) return;
1438
- value.setValueWithInternalPath(next, { blank: true });
1439
- if (input.value !== "") input.value = "";
1440
- },
1441
- { flush: "post" }
1442
- );
1443
- });
1444
- input[fileScopeKey] = () => scope.stop();
1445
- },
1446
- beforeUpdate(el, { value }) {
1447
- if (!isRegisterValue(value)) return;
1448
- const input = el;
1449
- const currentRaw = value.innerRef.value;
1450
- if (isBlankFileValue(currentRaw)) {
1451
- value.setValueWithInternalPath(currentRaw, { blank: true });
1452
- if (input.value !== "") input.value = "";
1453
- }
1454
- },
1455
- beforeUnmount(el, { value }) {
1456
- removeTrackedListeners(el);
1457
- const stop = el[fileScopeKey];
1458
- if (stop !== void 0) {
1459
- stop();
1460
- delete el[fileScopeKey];
1461
- }
1462
- if (!isRegisterValue(value)) return;
1463
- value.deregisterElement(el);
1471
+ if (!isRegisterValue(rv)) return void 0;
1472
+ return getSSRAriaProps(rv, vnode ?? null);
1464
1473
  }
1465
1474
  };
1466
1475
  function resolveDynamicModel(tagName, type) {
@@ -1629,6 +1638,7 @@ exports.AttaformError = AttaformError;
1629
1638
  exports.DEFAULT_SENSITIVE_NAMES = DEFAULT_SENSITIVE_NAMES;
1630
1639
  exports.FORM_ERRORS_PATH = FORM_ERRORS_PATH;
1631
1640
  exports.FORM_ERRORS_PATH_KEY = FORM_ERRORS_PATH_KEY;
1641
+ exports.INTERACTIVE_TAG_NAMES = INTERACTIVE_TAG_NAMES;
1632
1642
  exports.InvalidPathError = InvalidPathError;
1633
1643
  exports.InvalidUseFormConfigError = InvalidUseFormConfigError;
1634
1644
  exports.OutsideSetupError = OutsideSetupError;
@@ -1636,9 +1646,9 @@ exports.ROOT_PATH = ROOT_PATH;
1636
1646
  exports.ROOT_PATH_KEY = ROOT_PATH_KEY;
1637
1647
  exports.RegistryNotInstalledError = RegistryNotInstalledError;
1638
1648
  exports.ReservedFormKeyError = ReservedFormKeyError;
1639
- exports.SensitivePersistFieldError = SensitivePersistFieldError;
1640
1649
  exports.SubmitErrorHandlerError = SubmitErrorHandlerError;
1641
1650
  exports.__DEV__ = __DEV__;
1651
+ exports.allowSensitivePersist = allowSensitivePersist;
1642
1652
  exports.assignKey = assignKey;
1643
1653
  exports.canonicalizePath = canonicalizePath;
1644
1654
  exports.captureUserCallSite = captureUserCallSite;
@@ -1647,8 +1657,6 @@ exports.createAttaform = createAttaform;
1647
1657
  exports.createIsSensitivePath = createIsSensitivePath;
1648
1658
  exports.createPersistOptInRegistry = createPersistOptInRegistry;
1649
1659
  exports.createRegistry = createRegistry;
1650
- exports.createSegmentMatchesSensitive = createSegmentMatchesSensitive;
1651
- exports.enforceSensitiveCheck = enforceSensitiveCheck;
1652
1660
  exports.ensureAttaformInstalled = ensureAttaformInstalled;
1653
1661
  exports.getRegistryFromApp = getRegistryFromApp;
1654
1662
  exports.isPathPrefix = isPathPrefix;
@@ -1661,9 +1669,8 @@ exports.kFormContext = kFormContext;
1661
1669
  exports.kFormInstanceId = kFormInstanceId;
1662
1670
  exports.parseDottedPath = parseDottedPath;
1663
1671
  exports.pathKeyToDotted = pathKeyToDotted;
1664
- exports.segmentMatchesSensitive = segmentMatchesSensitive;
1665
1672
  exports.segmentsForPathKey = segmentsForPathKey;
1666
1673
  exports.useRegister = useRegister;
1667
1674
  exports.useRegistry = useRegistry;
1668
1675
  exports.vRegister = vRegister;
1669
- //# sourceMappingURL=attaform.BqEfHpVB.cjs.map
1676
+ //# sourceMappingURL=attaform.BPy-4qRx.cjs.map