attaform 0.28.0 → 0.29.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 (64) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +4 -4
  3. package/dist/chunks/fingerprint.mjs +1 -1
  4. package/dist/chunks/fingerprint2.mjs +2 -2
  5. package/dist/dev/abstract.mjs +4 -4
  6. package/dist/dev/chunks/devtools.mjs +1 -1
  7. package/dist/dev/chunks/fingerprint.mjs +1 -1
  8. package/dist/dev/chunks/fingerprint2.mjs +2 -2
  9. package/dist/dev/directive.mjs +1 -1
  10. package/dist/dev/history.mjs +1 -1
  11. package/dist/dev/index.mjs +5 -5
  12. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  13. package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.A-TpJW0B.mjs} +11 -1
  14. package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BO-71R54.mjs} +51 -158
  15. package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BgM6FdtA.mjs} +143 -36
  16. package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.ButTkwHd.mjs} +149 -16
  17. package/dist/{shared/attaform.BJGA_UOS.mjs → dev/shared/attaform-dev.BxfsuwBR.mjs} +8 -0
  18. package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D8aoKb89.mjs} +13 -4
  19. package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DD5m2W51.mjs} +1 -1
  20. package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.DHuydLAC.mjs} +344 -96
  21. package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.DrzObAZo.mjs} +4 -4
  22. package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.q6UutkCx.mjs} +170 -132
  23. package/dist/dev/zod-v3.mjs +6 -6
  24. package/dist/dev/zod-v4.mjs +6 -6
  25. package/dist/dev/zod.mjs +5 -5
  26. package/dist/directive.d.mts +2 -2
  27. package/dist/directive.mjs +1 -1
  28. package/dist/history.d.mts +1 -1
  29. package/dist/history.mjs +1 -1
  30. package/dist/index.d.mts +9 -7
  31. package/dist/index.mjs +5 -5
  32. package/dist/nuxt.d.mts +10 -10
  33. package/dist/runtime/plugins/attaform.mjs +1 -1
  34. package/dist/shared/{attaform.C09fpdLS.mjs → attaform.B0dKL8ST.mjs} +143 -36
  35. package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
  36. package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.BndjUUYX.mjs} +4 -4
  37. package/dist/{dev/shared/attaform-dev.BJGA_UOS.mjs → shared/attaform.BxfsuwBR.mjs} +8 -0
  38. package/dist/shared/{attaform.NLmerepI.mjs → attaform.C2MVyuxC.mjs} +42 -151
  39. package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.CIWLMY-R.d.mts} +1 -1
  40. package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.CZMAYwSK.mjs} +338 -95
  41. package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CZYt_5N_.mjs} +11 -1
  42. package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.CsoeRi3P.d.mts} +224 -32
  43. package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.Cvao8bW5.mjs} +132 -22
  44. package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.D0x-qSXm.mjs} +1 -1
  45. package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.D1fnTifF.mjs} +168 -132
  46. package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.DDtqHYv1.d.mts} +88 -7
  47. package/dist/shared/{attaform.uLbun31C.mjs → attaform.DffRL5pP.mjs} +12 -4
  48. package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.axHXVQO7.d.mts} +1 -1
  49. package/dist/transforms.d.mts +0 -22
  50. package/dist/transforms.mjs +1 -1
  51. package/dist/vite.d.mts +13 -4
  52. package/dist/vite.mjs +3 -2
  53. package/dist/zod-v3.d.mts +6 -4
  54. package/dist/zod-v3.mjs +6 -6
  55. package/dist/zod-v4.d.mts +14 -27
  56. package/dist/zod-v4.mjs +6 -6
  57. package/dist/zod.d.mts +4 -4
  58. package/dist/zod.mjs +5 -5
  59. package/package.json +17 -14
  60. package/skills/attaform/SKILL.md +5 -2
  61. package/skills/attaform/references/custom-components.md +7 -2
  62. package/skills/attaform/references/errors.md +8 -3
  63. package/skills/attaform/references/saving.md +77 -0
  64. package/skills/attaform/references/wizards.md +10 -8
@@ -1,9 +1,7 @@
1
- import { s as slimPrimitivesWalk, c as createAbstractSchema, w as walkPathSegments, d as deriveDefaultWalk, b as buildFieldMetaPathMap, g as getFieldMetaForSchema, m as mergeDeep, f as fixStructuralDefaults, a as getFieldMetaListForSchema } from './attaform.C09fpdLS.mjs';
2
- import { o as humanize, h as useAbstractForm } from './attaform.B8k8JrmK.mjs';
3
- import { A as AttaformError, e as canonicalizePath, a as InvalidUseFormConfigError } from './attaform.wfY8XFvm.mjs';
4
-
5
- class UnsupportedSchemaError extends AttaformError {
6
- }
1
+ import { w as walkPathSegments, s as slimPrimitivesWalk, c as createAbstractSchema, b as buildFieldMetaPathMap, g as getFieldMetaForSchema, m as mergeDeep, f as fixStructuralDefaults, d as deriveDefaultWalk, a as getFieldMetaListForSchema } from './attaform.B0dKL8ST.mjs';
2
+ import { p as getAtPath, q as humanize, h as useAbstractForm } from './attaform.CZMAYwSK.mjs';
3
+ import { c as callConsumerSchemaFn } from './attaform.Cvao8bW5.mjs';
4
+ import { A as AttaformError, e as canonicalizePath, a as InvalidUseFormConfigError } from './attaform.CZYt_5N_.mjs';
7
5
 
8
6
  function isZodSchemaType(schema, expectedType) {
9
7
  if (typeof schema !== "object" || schema === null) return false;
@@ -123,6 +121,8 @@ function getRecordValueType(schema) {
123
121
  const def = readDef(schema);
124
122
  return def?.valueType;
125
123
  }
124
+ const getMapKeyType = getRecordKeyType;
125
+ const getMapValueType = getRecordValueType;
126
126
  function getTupleItems(schema) {
127
127
  const def = readDef(schema);
128
128
  return def?.items ?? [];
@@ -163,6 +163,10 @@ function unwrapEffectsSource(schema) {
163
163
  const def = readDef(schema);
164
164
  return def?.schema;
165
165
  }
166
+ function getEffect(schema) {
167
+ const def = readDef(schema);
168
+ return def?.effect;
169
+ }
166
170
  function getEffectsKind(schema) {
167
171
  const def = readDef(schema);
168
172
  const type = def?.effect?.type;
@@ -188,6 +192,9 @@ function isAsyncEffect(schema) {
188
192
  function containsAsyncRefine(schema, seen) {
189
193
  return walkForTarget(schema, "refinement", seen ?? /* @__PURE__ */ new WeakSet());
190
194
  }
195
+ function containsMapOrSet(schema, seen) {
196
+ return walkForTarget(schema, "map-or-set", /* @__PURE__ */ new WeakSet());
197
+ }
191
198
  function containsDiscriminatedUnion(schema, seen) {
192
199
  return walkForTarget(schema, "discriminated-union", seen ?? /* @__PURE__ */ new WeakSet());
193
200
  }
@@ -267,9 +274,17 @@ function walkForTarget(schema, target, visited) {
267
274
  return false;
268
275
  }
269
276
  if (isZodSchemaType(schema, "ZodSet")) {
277
+ if (target === "map-or-set") return true;
270
278
  const elem = getSetValueType(schema);
271
279
  return elem !== void 0 && walkForTarget(elem, target, visited);
272
280
  }
281
+ if (isZodSchemaType(schema, "ZodMap")) {
282
+ if (target === "map-or-set") return true;
283
+ const keyType = getMapKeyType(schema);
284
+ if (keyType !== void 0 && walkForTarget(keyType, target, visited)) return true;
285
+ const valueType = getMapValueType(schema);
286
+ return valueType !== void 0 && walkForTarget(valueType, target, visited);
287
+ }
273
288
  return false;
274
289
  }
275
290
  function unwrapPipeIn(schema) {
@@ -284,11 +299,7 @@ function unwrapLazy(schema) {
284
299
  const def = readDef(schema);
285
300
  const getter = def?.getter;
286
301
  if (typeof getter !== "function") return void 0;
287
- try {
288
- return getter();
289
- } catch {
290
- return void 0;
291
- }
302
+ return callConsumerSchemaFn(() => getter(), void 0);
292
303
  }
293
304
  function getLazyGetter(schema) {
294
305
  const def = readDef(schema);
@@ -309,21 +320,15 @@ function getDefaultValue(schema) {
309
320
  const def = readDef(schema);
310
321
  const thunk = def?.defaultValue;
311
322
  if (typeof thunk !== "function") return void 0;
312
- try {
313
- return thunk();
314
- } catch {
315
- return void 0;
316
- }
323
+ return callConsumerSchemaFn(() => thunk(), void 0);
317
324
  }
318
325
  function getCatchDefault(schema) {
319
326
  const def = readDef(schema);
320
327
  const cv = def?.catchValue;
321
328
  if (typeof cv !== "function") return void 0;
322
- try {
323
- return cv({ error: null, input: void 0 });
324
- } catch {
325
- return void 0;
326
- }
329
+ return callConsumerSchemaFn(
330
+ () => cv({ error: null, input: void 0 }),
331
+ void 0);
327
332
  }
328
333
  function hasCatchValue(schema) {
329
334
  const def = readDef(schema);
@@ -424,95 +429,6 @@ function isContainerAfterWrapperPeel(schema) {
424
429
  return isZodSchemaType(cur, "ZodObject") || isZodSchemaType(cur, "ZodArray") || isZodSchemaType(cur, "ZodTuple") || isZodSchemaType(cur, "ZodIntersection") || isZodSchemaType(cur, "ZodUnion") || isZodSchemaType(cur, "ZodDiscriminatedUnion") || isZodSchemaType(cur, "ZodRecord") || isZodSchemaType(cur, "ZodSet");
425
430
  }
426
431
 
427
- const UNSUPPORTED_TYPE_NAMES = /* @__PURE__ */ new Set(["ZodPromise", "ZodFunction", "ZodMap", "ZodSymbol"]);
428
- function labelPath(path) {
429
- return path.length === 0 ? "<root>" : path.join(".");
430
- }
431
- function assertSupportedKinds(schema, path = [], lazyGetters = []) {
432
- const typeName = getTypeName(schema);
433
- if (typeName !== void 0 && UNSUPPORTED_TYPE_NAMES.has(typeName)) {
434
- throw new UnsupportedSchemaError(
435
- `[attaform] AF02 attaform.dev/e/af02 '${typeName}' at '${labelPath(path)}'`
436
- );
437
- }
438
- if (isZodSchemaType(schema, "ZodObject")) {
439
- for (const [key, sub] of Object.entries(getObjectShape(schema))) {
440
- assertSupportedKinds(sub, [...path, key], lazyGetters);
441
- }
442
- return;
443
- }
444
- if (isZodSchemaType(schema, "ZodArray")) {
445
- const inner = getArrayElement(schema);
446
- if (inner) assertSupportedKinds(inner, [...path, "*"], lazyGetters);
447
- return;
448
- }
449
- if (isZodSchemaType(schema, "ZodSet")) {
450
- const inner = getSetValueType(schema);
451
- if (inner) assertSupportedKinds(inner, [...path, "*"], lazyGetters);
452
- return;
453
- }
454
- if (isZodSchemaType(schema, "ZodRecord")) {
455
- const inner = getRecordValueType(schema);
456
- if (inner) assertSupportedKinds(inner, [...path, "*"], lazyGetters);
457
- return;
458
- }
459
- if (isZodSchemaType(schema, "ZodTuple")) {
460
- const items = getTupleItems(schema);
461
- items.forEach((item, i) => assertSupportedKinds(item, [...path, String(i)], lazyGetters));
462
- return;
463
- }
464
- if (isZodSchemaType(schema, "ZodUnion")) {
465
- const options = getUnionOptions(schema);
466
- options.forEach((opt, i) => assertSupportedKinds(opt, [...path, `|${i}`], lazyGetters));
467
- return;
468
- }
469
- if (isZodSchemaType(schema, "ZodDiscriminatedUnion")) {
470
- const options = getDiscriminatedOptions(schema);
471
- options.forEach((opt, i) => assertSupportedKinds(opt, [...path, `|${i}`], lazyGetters));
472
- return;
473
- }
474
- if (isZodSchemaType(schema, "ZodIntersection")) {
475
- const left = getIntersectionLeft(schema);
476
- if (left) assertSupportedKinds(left, [...path, "left"], lazyGetters);
477
- const right = getIntersectionRight(schema);
478
- if (right) assertSupportedKinds(right, [...path, "right"], lazyGetters);
479
- return;
480
- }
481
- if (isZodSchemaType(schema, "ZodOptional") || isZodSchemaType(schema, "ZodNullable") || isZodSchemaType(schema, "ZodDefault") || isZodSchemaType(schema, "ZodReadonly") || isZodSchemaType(schema, "ZodCatch")) {
482
- const inner = unwrapInner(schema);
483
- if (inner) assertSupportedKinds(inner, path, lazyGetters);
484
- return;
485
- }
486
- if (isZodSchemaType(schema, "ZodBranded")) {
487
- const inner = unwrapBranded(schema);
488
- if (inner) assertSupportedKinds(inner, path, lazyGetters);
489
- return;
490
- }
491
- if (isZodSchemaType(schema, "ZodEffects")) {
492
- const inner = unwrapEffectsSource(schema);
493
- if (inner) assertSupportedKinds(inner, path, lazyGetters);
494
- return;
495
- }
496
- if (isZodSchemaType(schema, "ZodPipeline")) {
497
- const inner = unwrapPipeIn(schema);
498
- if (inner) assertSupportedKinds(inner, path, lazyGetters);
499
- return;
500
- }
501
- if (isZodSchemaType(schema, "ZodLazy")) {
502
- const getter = getLazyGetter(schema);
503
- if (getter !== void 0 && lazyGetters.includes(getter)) return;
504
- const inner = getter?.();
505
- if (inner !== void 0) {
506
- assertSupportedKinds(
507
- inner,
508
- path,
509
- getter === void 0 ? lazyGetters : [...lazyGetters, getter]
510
- );
511
- }
512
- return;
513
- }
514
- }
515
-
516
432
  const V3_INTROSPECTOR = {
517
433
  kindOf: (schema) => kindOf(schema),
518
434
  getObjectShape,
@@ -531,6 +447,8 @@ const V3_INTROSPECTOR = {
531
447
  getSetValueType,
532
448
  getRecordKeyType,
533
449
  getRecordValueType,
450
+ getMapKeyType: (schema) => kindOf(schema) === "map" ? getMapKeyType(schema) : void 0,
451
+ getMapValueType: (schema) => kindOf(schema) === "map" ? getMapValueType(schema) : void 0,
534
452
  getUnionOptions,
535
453
  getIntersectionLeft,
536
454
  getIntersectionRight,
@@ -551,14 +469,84 @@ const V3_INTROSPECTOR = {
551
469
  hasCatchValue
552
470
  };
553
471
 
472
+ function mapKeyAt(value, index) {
473
+ if (!(value instanceof Map)) return void 0;
474
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) return void 0;
475
+ let i = 0;
476
+ for (const key of value.keys()) {
477
+ if (i === index) return key;
478
+ i += 1;
479
+ }
480
+ return void 0;
481
+ }
482
+ function stepSchema(schema, segment, maxRecursionDepth) {
483
+ if (schema === void 0) return void 0;
484
+ return walkPathSegments(schema, [String(segment)], V3_INTROSPECTOR, maxRecursionDepth, 0)[0];
485
+ }
486
+ function normalizeIssuePaths(issues, rootSchema, data, maxRecursionDepth, peel) {
487
+ let out = null;
488
+ for (let i = 0; i < issues.length; i++) {
489
+ const issue = issues[i];
490
+ const next = normalizeOne(issue.path, rootSchema, data, maxRecursionDepth, peel);
491
+ if (next === null) continue;
492
+ out ?? (out = [...issues]);
493
+ out[i] = { ...issue, path: next };
494
+ }
495
+ return out ?? issues;
496
+ }
497
+ function normalizeOne(path, rootSchema, data, maxRecursionDepth, peel) {
498
+ const out = [];
499
+ let schema = rootSchema;
500
+ let value = data;
501
+ let changed = false;
502
+ let i = 0;
503
+ while (i < path.length) {
504
+ const peeled = schema === void 0 ? void 0 : peel(schema);
505
+ const kind = peeled === void 0 ? void 0 : V3_INTROSPECTOR.kindOf(peeled);
506
+ if (kind === "set") {
507
+ changed = true;
508
+ break;
509
+ }
510
+ if (kind === "map") {
511
+ const key = mapKeyAt(value, path[i]);
512
+ if (key === void 0) {
513
+ changed = true;
514
+ break;
515
+ }
516
+ if (typeof key !== "string" && typeof key !== "number") {
517
+ changed = true;
518
+ break;
519
+ }
520
+ out.push(key);
521
+ value = value.get(key);
522
+ schema = V3_INTROSPECTOR.getMapValueType(peeled);
523
+ changed = true;
524
+ const half = path[i + 1];
525
+ i += half === "key" || half === "value" ? 2 : 1;
526
+ continue;
527
+ }
528
+ const segment = path[i];
529
+ out.push(segment);
530
+ value = getAtPath(value, [segment]);
531
+ schema = stepSchema(schema, segment, maxRecursionDepth);
532
+ i += 1;
533
+ }
534
+ return changed ? out : null;
535
+ }
536
+
554
537
  function slimPrimitivesV3(schema) {
555
538
  return new Set(slimPrimitivesWalk(schema, V3_INTROSPECTOR, 64));
556
539
  }
557
540
 
558
541
  function rebuildWithDef(original, defPatch) {
559
- const node = Object.create(Object.getPrototypeOf(original));
560
- node._def = { ...original._def, ...defPatch };
561
- return node;
542
+ return Object.create(Object.getPrototypeOf(original), {
543
+ _def: {
544
+ value: { ...original._def, ...defPatch },
545
+ writable: true,
546
+ enumerable: true,
547
+ configurable: true
548
+ }
549
+ });
562
550
  }
563
551
  function rebuildObject(original, shape) {
564
552
  const node = rebuildWithDef(original, { shape: () => shape, unknownKeys: "strip" });
@@ -613,8 +601,20 @@ function rebuildDiscriminatedUnion(original, options) {
613
601
  }
614
602
  return rebuildWithDef(original, { options, optionsMap });
615
603
  }
604
+ function rebuildEffects(original, source, effect) {
605
+ return rebuildWithDef(original, { schema: source, effect });
606
+ }
616
607
 
617
608
  function stripAsyncChecks(schema) {
609
+ return walkEffects(schema, "strip");
610
+ }
611
+ function wrapAsyncSafeRefinements(schema) {
612
+ return walkEffects(schema, "wrap");
613
+ }
614
+ function isThenable(value) {
615
+ return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
616
+ }
617
+ function walkEffects(schema, policy) {
618
618
  const seen = /* @__PURE__ */ new WeakSet();
619
619
  function recurse(s) {
620
620
  const candidate = s;
@@ -623,7 +623,26 @@ function stripAsyncChecks(schema) {
623
623
  seen.add(candidate);
624
624
  if (isZodSchemaType(s, "ZodEffects")) {
625
625
  const inner = unwrapEffectsSource(s);
626
- return inner === void 0 ? s : recurse(inner);
626
+ if (inner === void 0) return s;
627
+ if (policy === "strip") return recurse(inner);
628
+ const effect = getEffect(s);
629
+ const rebuiltInner = recurse(inner);
630
+ const original = effect?.["refinement"];
631
+ if (effect === void 0 || typeof original !== "function") {
632
+ return rebuildEffects(s, rebuiltInner, effect);
633
+ }
634
+ const refine = original;
635
+ const safeEffect = {
636
+ ...effect,
637
+ refinement: (value, ctx) => {
638
+ const result = refine(value, ctx);
639
+ if (isThenable(result)) {
640
+ void result.catch(() => void 0);
641
+ }
642
+ return result;
643
+ }
644
+ };
645
+ return rebuildEffects(s, rebuiltInner, safeEffect);
627
646
  }
628
647
  if (isZodSchemaType(s, "ZodOptional")) {
629
648
  const inner = unwrapInner(s);
@@ -771,18 +790,17 @@ function carryObjectChecks(rebuilt, original) {
771
790
 
772
791
  const MAX_UNWRAP_STEPS = 64;
773
792
  function zodAdapter(zodSchema) {
774
- assertSupportedKinds(zodSchema);
775
793
  const peeledRoot = peelAllV3Wrappers(zodSchema);
776
794
  if (!isZodSchemaType(peeledRoot, "ZodObject") && !isZodSchemaType(peeledRoot, "ZodRecord") && !isZodSchemaType(peeledRoot, "ZodDiscriminatedUnion")) {
777
- const name = getTypeName(peeledRoot);
778
- throw new Error(
779
- `Attaform: useForm schema root must be a ZodObject, ZodRecord, or ZodDiscriminatedUnion (got ${name}). Wrap other shapes under a key.`
795
+ const name = getTypeName(peeledRoot) ?? "unknown";
796
+ throw new AttaformError(
797
+ `[attaform] AF15 attaform.dev/e/af15 '${name}'`
780
798
  );
781
799
  }
782
800
  return (formKey, options) => createAbstractSchema(
783
801
  zodSchema,
784
802
  V3_INTROSPECTOR,
785
- buildV3Services(),
803
+ buildV3Services(options.maxRecursionDepth),
786
804
  formKey,
787
805
  options
788
806
  );
@@ -791,10 +809,28 @@ async function lazyFingerprint(schema) {
791
809
  const { fingerprintZodSchema } = await import('../chunks/fingerprint.mjs');
792
810
  return fingerprintZodSchema(schema);
793
811
  }
794
- function buildV3Services() {
812
+ const syncSafeCache = /* @__PURE__ */ new WeakMap();
813
+ function syncSafe(schema) {
814
+ const cached = syncSafeCache.get(schema);
815
+ if (cached !== void 0) return cached;
816
+ const wrapped = wrapAsyncSafeRefinements(schema);
817
+ syncSafeCache.set(schema, wrapped);
818
+ return wrapped;
819
+ }
820
+ function buildV3Services(maxRecursionDepth) {
821
+ const mapOrSetBySchema = /* @__PURE__ */ new WeakMap();
822
+ const refileIssues = (schema, data, issues) => {
823
+ let hasMapOrSet = mapOrSetBySchema.get(schema);
824
+ if (hasMapOrSet === void 0) {
825
+ hasMapOrSet = containsMapOrSet(schema);
826
+ mapOrSetBySchema.set(schema, hasMapOrSet);
827
+ }
828
+ if (!hasMapOrSet) return issues;
829
+ return normalizeIssuePaths(issues, schema, data, maxRecursionDepth, peelAllV3Wrappers);
830
+ };
795
831
  return {
796
832
  fingerprint: (schema) => lazyFingerprint(schema),
797
- getNestedSchemasAtPath: (schema, path, maxRecursionDepth) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth),
833
+ getNestedSchemasAtPath: (schema, path, maxRecursionDepth2) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth2),
798
834
  // v3 pre-strips refinements / defaults / wrappers off the root for
799
835
  // slim-mode walks — `getSlimPrimitiveTypesAtPath` and
800
836
  // `getSchemasAtPath` both consume this variant so the yielded
@@ -802,34 +838,34 @@ function buildV3Services() {
802
838
  // The slim-root projection is gone (size-teardown P7): the shared
803
839
  // path walker peels wrappers / effects inline, so the slim and
804
840
  // unstripped walks coincide — same aliasing v4 always had.
805
- getNestedSchemasInSlimMode: (schema, path, maxRecursionDepth) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth),
841
+ getNestedSchemasInSlimMode: (schema, path, maxRecursionDepth2) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth2),
806
842
  slimPrimitivesOf: (schema, _maxRecursionDepth) => slimPrimitivesV3(schema),
807
843
  deriveDefault: (schema, useDefault, _maxRecursionDepth, formKey) => getDefaultValuesFromZodSchema(schema, useDefault, formKey),
808
- runStrictGetDefaults: (schema, config, formKey, maxRecursionDepth) => runStrictGetDefaultsV3(schema, config, formKey, maxRecursionDepth),
844
+ runStrictGetDefaults: (schema, config, formKey, maxRecursionDepth2) => runStrictGetDefaultsV3(schema, config, formKey, maxRecursionDepth2),
809
845
  unwrapStructuralWrappers: (schema) => unwrapStructuralLeafV3(schema),
810
846
  unwrapToDiscriminatedUnion: (schema) => unwrapToDiscriminatedUnion(schema),
811
847
  peelAllWrappers: (schema) => peelAllV3Wrappers(schema),
812
848
  isLeafRequired: (schema) => isLeafRequiredV3(schema),
813
- resolveFieldMetaAtPath: (schema, path, maxRecursionDepth) => resolveFieldMetaAtPathV3(schema, path, maxRecursionDepth),
849
+ resolveFieldMetaAtPath: (schema, path, maxRecursionDepth2) => resolveFieldMetaAtPathV3(schema, path, maxRecursionDepth2),
814
850
  issuesToValidationErrors: (issues) => zodIssuesToValidationErrors(issues),
815
851
  safeParseSync: (schema, data) => {
816
- const result = schema.safeParse(data);
817
- return result.success ? { success: true, data: result.data } : { success: false, issues: result.error.issues };
852
+ const result = syncSafe(schema).safeParse(data);
853
+ return result.success ? { success: true, data: result.data } : { success: false, issues: refileIssues(schema, data, result.error.issues) };
818
854
  },
819
855
  safeParseAsync: async (schema, data) => {
820
856
  const result = await schema.safeParseAsync(data);
821
- return result.success ? { success: true, data: result.data } : { success: false, issues: result.error.issues };
857
+ return result.success ? { success: true, data: result.data } : { success: false, issues: refileIssues(schema, data, result.error.issues) };
822
858
  },
823
859
  // v3 returns the full recursive AbstractSchema for sub-schemas (the
824
860
  // historical shape) — `getSchemasAtPath` consumers may probe any
825
861
  // method on the result. The factory call rebuilds the full surface
826
862
  // against the sub-schema with its own per-form caches.
827
- makeSubSchema: (sub, formKey, maxRecursionDepth) => createAbstractSchema(
863
+ makeSubSchema: (sub, formKey, maxRecursionDepth2) => createAbstractSchema(
828
864
  sub,
829
865
  V3_INTROSPECTOR,
830
- buildV3Services(),
866
+ buildV3Services(maxRecursionDepth2),
831
867
  formKey,
832
- { maxRecursionDepth }
868
+ { maxRecursionDepth: maxRecursionDepth2 }
833
869
  )
834
870
  };
835
871
  }
@@ -1098,7 +1134,7 @@ function runStrictGetDefaultsV3(rootSchema, config, formKey, maxRecursionDepth)
1098
1134
  };
1099
1135
  }
1100
1136
  try {
1101
- const strictResult = rootSchema.safeParse(rawDefaultValues);
1137
+ const strictResult = syncSafe(rootSchema).safeParse(rawDefaultValues);
1102
1138
  if (strictResult.success) {
1103
1139
  return {
1104
1140
  data: rawDefaultValues,
@@ -1117,7 +1153,7 @@ function runStrictGetDefaultsV3(rootSchema, config, formKey, maxRecursionDepth)
1117
1153
  const isAsyncDetect = err instanceof Error && err.message.includes("Async refinement encountered");
1118
1154
  if (isAsyncDetect) {
1119
1155
  try {
1120
- const strippedResult = stripAsyncChecks(rootSchema).safeParse(rawDefaultValues);
1156
+ const strippedResult = syncSafe(stripAsyncChecks(rootSchema)).safeParse(rawDefaultValues);
1121
1157
  if (strippedResult.success) {
1122
1158
  return {
1123
1159
  data: rawDefaultValues,
@@ -1,4 +1,4 @@
1
- import { G as GenericForm, F as FormKey, a as UseFormReturnType, a5 as RegisterValue, V as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, aa as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, au as FieldRecord, av as ErrorCell, ag as ValidationError, aw as OriginalsRecord, A as AbstractSchema, T as Path, ak as WriteMeta, h as DeepPartial, al as WriteShape, ax as AttaformDomBinding, ay as TransformAbortHolder, ae as ValidateOn, d as AttaformDefaults } from './attaform.Cgu-hdSb.mjs';
1
+ import { G as GenericForm, F as FormKey, a as UseFormReturnType, a7 as RegisterValue, V as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, ac as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, aw as FieldRecord, ax as ErrorCell, ai as ValidationError, ay as OriginalsRecord, A as AbstractSchema, T as Path, am as WriteMeta, h as DeepPartial, an as WriteShape, az as AttaformDomBinding, aA as TransformAbortHolder, ag as ValidateOn, d as AttaformDefaults } from './attaform.CsoeRi3P.mjs';
2
2
  import { Ref, ComputedRef, ShallowRef, App, InjectionKey, Plugin } from 'vue';
3
3
 
4
4
  /**
@@ -121,6 +121,22 @@ declare function injectForm<Form extends GenericForm, GetValueFormType extends G
121
121
  * composable's `onMounted` warn fires once per instance to surface
122
122
  * the misuse case at runtime.
123
123
  *
124
+ * A wrapper meant to work both ways says so with
125
+ * `useRegister({ optional: true })`, which silences the unbound
126
+ * diagnostic and changes nothing else. Without it the diagnostic still
127
+ * earns its keep: a single-mode wrapper whose parent forgot
128
+ * `v-register` renders a field that looks fine and stores nothing.
129
+ *
130
+ * `rv?.isBound` is the test for "did a parent bind me", and it is the
131
+ * one to reach for: script-setup code holds the Proxy, which is an
132
+ * object and so always truthy, while a template holds the unwrapped
133
+ * ref, which is `undefined` when unbound. Those two disagree, and
134
+ * `isBound` reads the same on both. A dual-mode wrapper (one used
135
+ * both `v-register`-bound and plain `v-model`-bound) branches on it,
136
+ * and does not need to duplicate its element to do so: the
137
+ * compile-time value injection keeps an author-written `:value` /
138
+ * `:checked` as its unbound leg, so one element serves both (#620).
139
+ *
124
140
  * Diagnostic: in dev mode, a single `console.warn` fires per instance
125
141
  * at `onMounted` if the captured value is still `undefined` — by then
126
142
  * the parent has had its full mount lifecycle to bind, so a missing
@@ -149,8 +165,48 @@ declare function injectForm<Form extends GenericForm, GetValueFormType extends G
149
165
  * the hybrid's only `.value`. Older code that read `rv.value?.path`
150
166
  * keeps working; new code can write `rv.path` directly.
151
167
  */
152
- type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined>;
153
- declare function useRegister<V = unknown>(): UseRegisterReturn<V> | undefined;
168
+ type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined> & {
169
+ /**
170
+ * Whether a parent has actually bound this wrapper. `false` until a
171
+ * `RegisterValue` lands, `true` once one has, and reactive in both
172
+ * directions: a parent that binds on a later render flips it, and
173
+ * so does one that stops binding.
174
+ *
175
+ * This is the test a dual-mode wrapper reaches for, and the reason
176
+ * it exists as a field rather than a truthiness check on the
177
+ * return: the composable cannot answer "is this bound" at setup
178
+ * time. A parent is free to bind on a later render
179
+ * (`v-register="ready ? form.register('x') : undefined"`), and
180
+ * `useRegister` has to keep serving that, so it can never return a
181
+ * plain `undefined` for "unbound right now". Script-setup code
182
+ * reading `rv` directly sees the hybrid Proxy, which is an object
183
+ * and therefore always truthy; a template sees the unwrapped ref,
184
+ * which IS `undefined` when unbound. `rv?.isBound` reads the same
185
+ * on both sides (#620).
186
+ */
187
+ readonly isBound: boolean;
188
+ };
189
+ /**
190
+ * Options for `useRegister`.
191
+ */
192
+ type UseRegisterOptions = {
193
+ /**
194
+ * Declares that this wrapper is used both with and without a form, so
195
+ * a render with no parent binding is intended rather than a mistake.
196
+ * Silences the unbound diagnostic; nothing else changes.
197
+ *
198
+ * The diagnostic exists because a single-mode wrapper whose parent
199
+ * forgot `v-register` renders a field that looks fine and stores
200
+ * nothing, which is an expensive afternoon. Once a wrapper is
201
+ * deliberately dual-mode, the same signal fires on correct code and
202
+ * tells the author to do something wrong, so this is the one fact
203
+ * that separates the two cases and only the author has it (#620).
204
+ *
205
+ * Pair it with `rv?.isBound` to branch on what actually happened.
206
+ */
207
+ readonly optional?: boolean;
208
+ };
209
+ declare function useRegister<V = unknown>(options?: UseRegisterOptions): UseRegisterReturn<V> | undefined;
154
210
 
155
211
  /**
156
212
  * Per-form display engine: owns the clock and the timers that the pure
@@ -466,10 +522,22 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
466
522
  * Resolves after `hydrating` flips back to `false`; consumers can
467
523
  * `await form.rehydrate()` to gate UI on the fresh load.
468
524
  *
469
- * Does NOT touch dirty / touched / submit state — chain
470
- * `form.reset()` if you want a clean baseline.
525
+ * Does NOT touch touched / submit state; chain `form.reset()` if you
526
+ * want a clean surface. It DOES re-seat the defaults (see
527
+ * `adoptResolvedDefaults`), so dirty afterwards means "differs from
528
+ * what the factory just returned", and `form.reset()` lands on the
529
+ * fresh values rather than throwing them away.
471
530
  */
472
531
  rehydrate(): Promise<void>;
532
+ /**
533
+ * Adopt a resolved async-defaults value as the form's durable
534
+ * defaults and re-seed the dirty baseline from it. Called by the
535
+ * activate / rehydrate orchestrator once its factory settles.
536
+ * Exposed on the store (rather than imported) because
537
+ * `form-activation` is imported BY this module; a value import back
538
+ * the other way would close a cycle.
539
+ */
540
+ adoptResolvedDefaults(value: unknown): void;
473
541
  /**
474
542
  * Incremented by every `reset()` call. The submit wrapper captures
475
543
  * this at entry and skips writing `submitError` from a catch that
@@ -1486,6 +1554,12 @@ type WizardForms<S> = FormsRecordOf<S> & Readonly<Record<FormKey, AnyForm>>;
1486
1554
  * `back()` does not pop; the trail is the audit
1487
1555
  * log, not the back-stack.
1488
1556
  * - `next/back/goTo` — pure navigation. Refuses while `submitting`.
1557
+ * The one exception is `next()` on an UNCLEARED
1558
+ * `gate()` step, which behaves as `tryNext()` so
1559
+ * wiring Next straight to it can never skip the
1560
+ * gate's confirmation. Once that gate clears, `next()`
1561
+ * is plain navigation again and does not re-submit to
1562
+ * re-confirm.
1489
1563
  * - `tryNext()` — submit the active step, and once that submit
1490
1564
  * resolves clean, advance; invalid input keeps the pin
1491
1565
  * put under the form's standard error reveal (first
@@ -1495,6 +1569,13 @@ type WizardForms<S> = FormsRecordOf<S> & Readonly<Record<FormKey, AnyForm>>;
1495
1569
  * (`@click="wizard.tryNext()"`); resolves to whether
1496
1570
  * the pin moved. No-ops to `false` on a degenerate or
1497
1571
  * final-step wizard (finish via `handleSubmit`).
1572
+ * Called while the active step's form already has a
1573
+ * submit in flight (the usual case being from inside
1574
+ * that form's own callback), it rides that submit
1575
+ * rather than starting a second one, so one action
1576
+ * costs one submission. The pin has not moved by the
1577
+ * time it resolves, so the answer is `false`; the
1578
+ * advance lands with the in-flight submit.
1498
1579
  * - `handleSubmit(onSubmit, onError?)` — always validates the entire
1499
1580
  * step list, from any step, and never advances the
1500
1581
  * pin: on success it latches `done`; on any error it
@@ -2380,5 +2461,5 @@ declare function makeDefaultDisplayState({ showDelay, minVisible, }: DisplayTimi
2380
2461
  */
2381
2462
  declare const defaultDisplayState: GetDisplayState;
2382
2463
 
2383
- export { renderAttaformState as $, createAttaform as B, DEFAULT_TIMINGS as D, createRegistry as E, defaultCoercionRules as H, defaultDisplayState as J, defineCoercion as K, escapeForInlineScript as M, gate as N, OutsideSetupError as O, getRegistryFromApp as P, hydrateAttaformState as Q, RegistryNotInstalledError as R, injectForm as T, injectWizard as V, isRegisterValue as X, kAttaformRegistry as Y, lazy as Z, makeDefaultDisplayState as _, useRegister as a0, useRegistry as a1, useWizard as a2, AttaformError as c, AttaformErrorCode as d, DEVTOOLS_WINDOW_KEY as f, InvalidPathError as i, InvalidUseFormConfigError as j, ReservedFormKeyError as k, SubmitErrorHandlerError as n, assignKey as z };
2384
- export type { AttaformRegistry as A, CompiledStep as C, FormStatus as F, GateMarker as G, InjectWizardInput as I, LazyMarker as L, SerializedAttaformState as S, UseRegisterReturn as U, WizardAggregateError as W, AnyForm as a, AttaformDevtoolsBridge as b, AttaformPluginOptions as e, DisplayTimings as g, FormStatusSeed as h, SerializedFormData as l, StepSlot as m, UseWizardReturnType as o, WizardCtx as p, WizardCtxForm as q, WizardOnError as r, WizardOnSubmit as s, WizardOptions as t, WizardPersistFn as u, WizardRestoreFn as v, WizardRestoreState as w, WizardStatusesProxy as x, WizardSubmitContext as y };
2464
+ export { makeDefaultDisplayState as $, assignKey as B, DEFAULT_TIMINGS as D, createAttaform as E, createRegistry as H, defaultCoercionRules as J, defaultDisplayState as K, defineCoercion as M, escapeForInlineScript as N, OutsideSetupError as O, gate as P, getRegistryFromApp as Q, RegistryNotInstalledError as R, hydrateAttaformState as T, injectForm as V, injectWizard as X, isRegisterValue as Y, kAttaformRegistry as Z, lazy as _, renderAttaformState as a0, useRegister as a1, useRegistry as a2, useWizard as a3, AttaformError as c, AttaformErrorCode as d, DEVTOOLS_WINDOW_KEY as f, InvalidPathError as i, InvalidUseFormConfigError as j, ReservedFormKeyError as k, SubmitErrorHandlerError as n };
2465
+ export type { AttaformRegistry as A, CompiledStep as C, FormStatus as F, GateMarker as G, InjectWizardInput as I, LazyMarker as L, SerializedAttaformState as S, UseRegisterOptions as U, WizardAggregateError as W, AnyForm as a, AttaformDevtoolsBridge as b, AttaformPluginOptions as e, DisplayTimings as g, FormStatusSeed as h, SerializedFormData as l, StepSlot as m, UseRegisterReturn as o, UseWizardReturnType as p, WizardCtx as q, WizardCtxForm as r, WizardOnError as s, WizardOnSubmit as t, WizardOptions as u, WizardPersistFn as v, WizardRestoreFn as w, WizardRestoreState as x, WizardStatusesProxy as y, WizardSubmitContext as z };
@@ -1,6 +1,6 @@
1
1
  import { reactive, markRaw, effectScope, watch, nextTick } from 'vue';
2
2
  import { i as isRegisterValue, a as isTransforming, S as SSR_COMPONENT_HOST_MODIFIER, R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
3
- import { e as canonicalizePath } from './attaform.wfY8XFvm.mjs';
3
+ import { e as canonicalizePath } from './attaform.CZYt_5N_.mjs';
4
4
 
5
5
  const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
6
6
 
@@ -1107,6 +1107,14 @@ function setSelected(el, value) {
1107
1107
  const externalValue = value.innerRef.value;
1108
1108
  const isMultiple = el.multiple;
1109
1109
  const isArrayValue = isArray(externalValue);
1110
+ const isUnset = externalValue === null || externalValue === void 0;
1111
+ if (isMultiple && isUnset) {
1112
+ for (let i = 0, l = el.options.length; i < l; i++) {
1113
+ const option = el.options[i];
1114
+ if (option !== void 0) option.selected = false;
1115
+ }
1116
+ return;
1117
+ }
1110
1118
  if (isMultiple && !isArrayValue && !isSet(externalValue)) {
1111
1119
  return;
1112
1120
  }
@@ -1134,10 +1142,11 @@ function setSelected(el, value) {
1134
1142
  }
1135
1143
  return;
1136
1144
  }
1145
+ const target = value.displayValue.value === "" ? "" : externalValue;
1137
1146
  for (let i = 0, l = el.options.length; i < l; i++) {
1138
1147
  const option = el.options[i];
1139
1148
  if (!option) continue;
1140
- if (looseEqual(applyCoerce(getValue(option), value), externalValue)) {
1149
+ if (looseEqual(applyCoerce(getValue(option), value), target)) {
1141
1150
  if (el.selectedIndex !== i) el.selectedIndex = i;
1142
1151
  return;
1143
1152
  }
@@ -1221,8 +1230,7 @@ function observeForLateHostControl(el, rv) {
1221
1230
  hostHealObservers.set(el, observer);
1222
1231
  }
1223
1232
  function activateComponentHost(el, rv) {
1224
- if (rv.hasRegisteredDescendant(el))
1225
- return;
1233
+ if (rv.hasRegisteredDescendant(el)) return;
1226
1234
  el[REGISTER_OWNER_MARKER] = true;
1227
1235
  if (!el.tagName.includes("-")) {
1228
1236
  el.removeAttribute("registerValue");
@@ -1,5 +1,5 @@
1
1
  import { App } from 'vue';
2
- import { ar as RegisterModelDynamicCustomDirective } from './attaform.Cgu-hdSb.mjs';
2
+ import { at as RegisterModelDynamicCustomDirective } from './attaform.CsoeRi3P.mjs';
3
3
 
4
4
  /**
5
5
  * The `v-register` directive. Bind a form field to a native input,