attaform 0.27.1 → 0.27.2

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 (89) hide show
  1. package/dist/abstract.cjs +3 -2
  2. package/dist/abstract.cjs.map +1 -1
  3. package/dist/abstract.d.cts +4 -4
  4. package/dist/abstract.d.mts +4 -4
  5. package/dist/abstract.d.ts +4 -4
  6. package/dist/abstract.mjs +2 -2
  7. package/dist/chunks/devtools.cjs +1 -1
  8. package/dist/chunks/devtools.mjs +1 -1
  9. package/dist/chunks/fingerprint2.cjs +1 -1
  10. package/dist/chunks/fingerprint2.mjs +1 -1
  11. package/dist/index.cjs +4 -3
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +5 -5
  14. package/dist/index.d.mts +5 -5
  15. package/dist/index.d.ts +5 -5
  16. package/dist/index.mjs +3 -3
  17. package/dist/nuxt.d.cts +1 -1
  18. package/dist/nuxt.d.mts +1 -1
  19. package/dist/nuxt.d.ts +1 -1
  20. package/dist/runtime/plugins/attaform.cjs +1 -1
  21. package/dist/runtime/plugins/attaform.mjs +1 -1
  22. package/dist/shared/{attaform.DkD0DR7R.mjs → attaform.6IZBjA1Q.mjs} +5 -5
  23. package/dist/shared/{attaform.DkD0DR7R.mjs.map → attaform.6IZBjA1Q.mjs.map} +1 -1
  24. package/dist/shared/{attaform.D99pd7JG.d.cts → attaform.B2d0FszH.d.ts} +169 -24
  25. package/dist/shared/{attaform.DDAmSFnl.mjs → attaform.BAlfXoAx.mjs} +3 -3
  26. package/dist/shared/{attaform.DDAmSFnl.mjs.map → attaform.BAlfXoAx.mjs.map} +1 -1
  27. package/dist/shared/{attaform.Co5LNRnv.cjs → attaform.BBaMFa-k.cjs} +31 -5
  28. package/dist/shared/attaform.BBaMFa-k.cjs.map +1 -0
  29. package/dist/shared/{attaform.DhVgG2jh.cjs → attaform.BF1mYHru.cjs} +4 -4
  30. package/dist/shared/{attaform.DhVgG2jh.cjs.map → attaform.BF1mYHru.cjs.map} +1 -1
  31. package/dist/shared/{attaform.C3WnCDXz.d.cts → attaform.BIPSHY5U.d.cts} +55 -1
  32. package/dist/shared/{attaform.C3WnCDXz.d.mts → attaform.BIPSHY5U.d.mts} +55 -1
  33. package/dist/shared/{attaform.C3WnCDXz.d.ts → attaform.BIPSHY5U.d.ts} +55 -1
  34. package/dist/shared/{attaform.8tFZu1Ub.cjs → attaform.Bn0IzhD_.cjs} +50 -2
  35. package/dist/shared/attaform.Bn0IzhD_.cjs.map +1 -0
  36. package/dist/shared/{attaform.K6Thc6By.cjs → attaform.C1SJy524.cjs} +5 -5
  37. package/dist/shared/{attaform.K6Thc6By.cjs.map → attaform.C1SJy524.cjs.map} +1 -1
  38. package/dist/shared/{attaform.CC_y1_Ej.d.ts → attaform.C8zVmank.d.ts} +1 -1
  39. package/dist/shared/{attaform.CpXHK60W.d.cts → attaform.C925L0WA.d.mts} +1 -1
  40. package/dist/shared/{attaform.Drsi43GG.cjs → attaform.CIPwCQe8.cjs} +3 -3
  41. package/dist/shared/{attaform.Drsi43GG.cjs.map → attaform.CIPwCQe8.cjs.map} +1 -1
  42. package/dist/shared/{attaform.DQT7_-FO.cjs → attaform.CXHebpij.cjs} +324 -82
  43. package/dist/shared/attaform.CXHebpij.cjs.map +1 -0
  44. package/dist/shared/{attaform.Da8EdI2t.mjs → attaform.CgsQZdy1.mjs} +31 -5
  45. package/dist/shared/attaform.CgsQZdy1.mjs.map +1 -0
  46. package/dist/shared/{attaform.CvVYOvZF.d.mts → attaform.CnvbLiIO.d.cts} +1 -1
  47. package/dist/shared/{attaform.BERt5YMU.mjs → attaform.Cs2Rbdlo.mjs} +50 -2
  48. package/dist/shared/attaform.Cs2Rbdlo.mjs.map +1 -0
  49. package/dist/shared/{attaform.C__F01CE.cjs → attaform.CvaPYOZu.cjs} +4 -4
  50. package/dist/shared/{attaform.C__F01CE.cjs.map → attaform.CvaPYOZu.cjs.map} +1 -1
  51. package/dist/shared/{attaform.CyQxTOdX.mjs → attaform.DGCijpZT.mjs} +325 -84
  52. package/dist/shared/attaform.DGCijpZT.mjs.map +1 -0
  53. package/dist/shared/{attaform.kBkYlKP_.d.mts → attaform.DIJTp_Kv.d.cts} +169 -24
  54. package/dist/shared/{attaform.CEXUlUkc.d.ts → attaform.Dcj-OS43.d.mts} +169 -24
  55. package/dist/shared/{attaform.DlSu08F5.mjs → attaform.gyed1fGh.mjs} +4 -4
  56. package/dist/shared/{attaform.DlSu08F5.mjs.map → attaform.gyed1fGh.mjs.map} +1 -1
  57. package/dist/shared/{attaform.BceJrhQ7.mjs → attaform.yOE3udL1.mjs} +4 -4
  58. package/dist/shared/{attaform.BceJrhQ7.mjs.map → attaform.yOE3udL1.mjs.map} +1 -1
  59. package/dist/transforms.cjs +1 -1
  60. package/dist/transforms.mjs +1 -1
  61. package/dist/vite.cjs +3 -2
  62. package/dist/vite.cjs.map +1 -1
  63. package/dist/vite.mjs +3 -2
  64. package/dist/vite.mjs.map +1 -1
  65. package/dist/zod-v3.cjs +4 -3
  66. package/dist/zod-v3.cjs.map +1 -1
  67. package/dist/zod-v3.d.cts +3 -3
  68. package/dist/zod-v3.d.mts +3 -3
  69. package/dist/zod-v3.d.ts +3 -3
  70. package/dist/zod-v3.mjs +3 -3
  71. package/dist/zod-v4.cjs +4 -3
  72. package/dist/zod-v4.cjs.map +1 -1
  73. package/dist/zod-v4.d.cts +6 -6
  74. package/dist/zod-v4.d.mts +6 -6
  75. package/dist/zod-v4.d.ts +6 -6
  76. package/dist/zod-v4.mjs +3 -3
  77. package/dist/zod.cjs +4 -3
  78. package/dist/zod.cjs.map +1 -1
  79. package/dist/zod.d.cts +3 -3
  80. package/dist/zod.d.mts +3 -3
  81. package/dist/zod.d.ts +3 -3
  82. package/dist/zod.mjs +3 -3
  83. package/package.json +3 -2
  84. package/dist/shared/attaform.8tFZu1Ub.cjs.map +0 -1
  85. package/dist/shared/attaform.BERt5YMU.mjs.map +0 -1
  86. package/dist/shared/attaform.Co5LNRnv.cjs.map +0 -1
  87. package/dist/shared/attaform.CyQxTOdX.mjs.map +0 -1
  88. package/dist/shared/attaform.DQT7_-FO.cjs.map +0 -1
  89. package/dist/shared/attaform.Da8EdI2t.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, readonly, toRaw, reactive, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
- import { n as pathsEqual, k as isPathPrefix, _ as __DEV__, f as canonicalizePath, s as segmentsForPathKey, o as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, q as normalizeErrorInput, w as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, x as normalizeErrorInputs, y as coerceToPathKey, a as InvalidUseFormConfigError, z as ensureAttaformInstalled, u as useRegistry, B as kFormContext, C as kFormInstanceId, d as ReservedFormKeyError, m as kAttaformWizardActiveStepResolver, E as kAttaformAncestorWizard } from './attaform.Da8EdI2t.mjs';
1
+ import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, readonly, toRaw, reactive, toValue, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
+ import { n as pathsEqual, k as isPathPrefix, _ as __DEV__, f as canonicalizePath, s as segmentsForPathKey, o as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, q as normalizeErrorInput, w as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, x as normalizeErrorInputs, y as coerceToPathKey, a as InvalidUseFormConfigError, z as ensureAttaformInstalled, u as useRegistry, B as kFormContext, C as kFormInstanceId, d as ReservedFormKeyError, m as kAttaformWizardActiveStepResolver, E as kAttaformAncestorWizard } from './attaform.CgsQZdy1.mjs';
3
3
  import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
4
4
 
5
5
  function safeAssign(target, key, value) {
@@ -593,7 +593,17 @@ const AttaformErrorCode = {
593
593
  * keeps them; a bare `Error` or a non-Error throw defaults here at the
594
594
  * form-level path `[]`.
595
595
  */
596
- SubmitError: "atta:submit-error"
596
+ SubmitError: "atta:submit-error",
597
+ /**
598
+ * Synthesized by `wizard.handleSubmit` when the whole-wizard submit
599
+ * would otherwise succeed but a `gate()` step is still uncleared. A
600
+ * gate clears only on its member form's clean submit (confirmation), so
601
+ * a downstream step that happens to be valid-by-default must not let the
602
+ * wizard finish past an unconfirmed prerequisite. Emitted at the gate
603
+ * form's key (path `[]`) so the aggregate error pipeline routes focus to
604
+ * the gate, and `done` never latches.
605
+ */
606
+ GateNotCleared: "atta:gate-not-cleared"
597
607
  };
598
608
  function makeBlankRequiredError(segments, formKey) {
599
609
  return {
@@ -608,6 +618,7 @@ function isGateOpen(field, formMeta) {
608
618
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
609
619
  }
610
620
  function computeVerdict(field, formMeta) {
621
+ if (field.disabled === true) return "idle";
611
622
  if (!isGateOpen(field, formMeta)) return "idle";
612
623
  const hasOwnError = field.errors.some(
613
624
  (e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
@@ -815,6 +826,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
815
826
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
816
827
  key: state.arrayElementKey(segments),
817
828
  blank: state.blankPaths.has(key),
829
+ disabled: state.effectiveDisabled.value,
818
830
  label,
819
831
  description: resolved.description,
820
832
  placeholder: resolved.placeholder,
@@ -1002,6 +1014,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
1002
1014
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
1003
1015
  key: state.arrayElementKey(segments),
1004
1016
  blank,
1017
+ disabled: state.effectiveDisabled.value,
1005
1018
  label,
1006
1019
  description: resolved.description,
1007
1020
  placeholder: resolved.placeholder,
@@ -1056,7 +1069,8 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
1056
1069
  rollupApplies = true;
1057
1070
  }
1058
1071
  const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
1059
- const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
1072
+ const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
1073
+ const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
1060
1074
  return {
1061
1075
  ...base,
1062
1076
  displayState,
@@ -1644,6 +1658,7 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1644
1658
  "aria",
1645
1659
  "key",
1646
1660
  "blank",
1661
+ "disabled",
1647
1662
  "label",
1648
1663
  "description",
1649
1664
  "placeholder",
@@ -2413,6 +2428,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2413
2428
  return Object.prototype.toString.call(raw);
2414
2429
  }
2415
2430
  });
2431
+ const hostModelValue = computed(
2432
+ () => state.blankPaths.has(pathKey) ? void 0 : innerRef.value
2433
+ );
2416
2434
  const slimDefault = state.schema.getDefaultAtPath(segments);
2417
2435
  const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
2418
2436
  const acceptsUndefined = slimTypes.has("undefined");
@@ -2432,19 +2450,18 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2432
2450
  const isRequired = state.schema.isRequiredAtPath(segments);
2433
2451
  const ariaEnabled = options?.autoAria ?? formAutoAria;
2434
2452
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2453
+ const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
2435
2454
  const internalRv = {
2436
2455
  innerRef,
2437
2456
  displayValue,
2457
+ hostModelValue,
2458
+ // Live form-freeze flag for the compile-time transforms' `:disabled`
2459
+ // bind (native attribute + component-host prop) and custom
2460
+ // `useRegister` integrations. Reads the form-level effective freeze,
2461
+ // so every binding on a disabled form reports `true`.
2462
+ disabled: computed(() => state.effectiveDisabled.value),
2438
2463
  lastTypedForm,
2439
- markBlank: () => {
2440
- return state.setValueAtPath(
2441
- segments,
2442
- slimDefault,
2443
- withInstanceMeta({
2444
- blank: true
2445
- })
2446
- );
2447
- },
2464
+ markBlank,
2448
2465
  markInteracted: () => {
2449
2466
  state.markInteracted(segments);
2450
2467
  },
@@ -2462,7 +2479,8 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2462
2479
  },
2463
2480
  setValueFromHost: (value) => {
2464
2481
  state.markInteracted(segments);
2465
- const accepted = state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2482
+ const isBlankSignal = value === "" && !acceptsString || value === null && !slimTypes.has("null") || value === void 0 && !acceptsUndefined;
2483
+ const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2466
2484
  if (__DEV__) {
2467
2485
  const dedupeKey = `${formInstanceId}:${pathKey}`;
2468
2486
  const isWired = () => (state.elements.get(pathKey)?.elements.size ?? 0) > 0 || state.getFieldRecord(segments)?.connected === true;
@@ -2909,6 +2927,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2909
2927
  get blank() {
2910
2928
  return rootBase().blank;
2911
2929
  },
2930
+ get disabled() {
2931
+ return rootBase().disabled;
2932
+ },
2912
2933
  get label() {
2913
2934
  return rootBase().label;
2914
2935
  },
@@ -3227,6 +3248,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
3227
3248
  get blank() {
3228
3249
  return rootFieldState.value.blank;
3229
3250
  },
3251
+ get disabled() {
3252
+ return rootFieldState.value.disabled;
3253
+ },
3230
3254
  get label() {
3231
3255
  return rootFieldState.value.label;
3232
3256
  },
@@ -4269,6 +4293,24 @@ function createFormStore(options) {
4269
4293
  const submitted = ref(false);
4270
4294
  const submitError = ref(null);
4271
4295
  const departAttempts = ref(0);
4296
+ const externalLock = ref(false);
4297
+ let warnedDisabledThrow = false;
4298
+ let warnedDisabledWrite = false;
4299
+ const effectiveDisabled = computed(() => {
4300
+ let own = false;
4301
+ try {
4302
+ own = Boolean(toValue(options.disabled));
4303
+ } catch (err) {
4304
+ if (__DEV__ && !warnedDisabledThrow) {
4305
+ warnedDisabledThrow = true;
4306
+ console.warn(
4307
+ `[attaform] useForm({ disabled }) getter threw for form "${String(formKey)}"; treating its config as enabled. Fix the getter to clear this warning.`,
4308
+ err
4309
+ );
4310
+ }
4311
+ }
4312
+ return own || externalLock.value;
4313
+ });
4272
4314
  const submissionGeneration = ref(0);
4273
4315
  const activeValidations = ref(0);
4274
4316
  const pathSnapshots = /* @__PURE__ */ new Map();
@@ -4562,6 +4604,15 @@ function createFormStore(options) {
4562
4604
  commitWritePatches(patches, meta);
4563
4605
  }
4564
4606
  function setValueAtPath(path, value, meta) {
4607
+ if (effectiveDisabled.value) {
4608
+ if (__DEV__ && !warnedDisabledWrite) {
4609
+ warnedDisabledWrite = true;
4610
+ console.warn(
4611
+ `[attaform] Ignored a write to a disabled form ("${String(formKey)}"). Value writes no-op while \`disabled\` resolves truthy. This warning fires once.`
4612
+ );
4613
+ }
4614
+ return false;
4615
+ }
4565
4616
  if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4566
4617
  for (const idx of freshElementIndices(meta.arrayOp)) {
4567
4618
  value[idx] = stripSymbolsDeep(value[idx]);
@@ -5125,6 +5176,7 @@ function createFormStore(options) {
5125
5176
  }
5126
5177
  }
5127
5178
  function markFocused(path, focused, meta) {
5179
+ if (effectiveDisabled.value) return;
5128
5180
  const { key } = canonicalizePath(path);
5129
5181
  const current = fields.get(key);
5130
5182
  touchFieldRecord(key, path, {
@@ -5172,6 +5224,7 @@ function createFormStore(options) {
5172
5224
  }
5173
5225
  }
5174
5226
  function markInteracted(path) {
5227
+ if (effectiveDisabled.value) return;
5175
5228
  const { key } = canonicalizePath(path);
5176
5229
  if (fields.get(key)?.interacted === true) return;
5177
5230
  touchFieldRecord(key, path, { interacted: true });
@@ -5488,6 +5541,8 @@ function createFormStore(options) {
5488
5541
  submitted,
5489
5542
  submitError,
5490
5543
  departAttempts,
5544
+ effectiveDisabled,
5545
+ externalLock,
5491
5546
  hydrating,
5492
5547
  hydrateError,
5493
5548
  defaultValuesFactory,
@@ -5875,6 +5930,7 @@ function mergeWithDefaults(defaults, configuration) {
5875
5930
  const onInvalidSubmit = configuration.onInvalidSubmit ?? defaults.onInvalidSubmit;
5876
5931
  const history = configuration.history ?? defaults.history;
5877
5932
  const rememberVariants = configuration.rememberVariants ?? defaults.rememberVariants;
5933
+ const disabled = configuration.disabled ?? defaults.disabled;
5878
5934
  const coerce = configuration.coerce ?? defaults.coerce;
5879
5935
  const validateOn = configuration.validateOn ?? defaults.validateOn;
5880
5936
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
@@ -5887,6 +5943,7 @@ function mergeWithDefaults(defaults, configuration) {
5887
5943
  ...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
5888
5944
  ...history === void 0 ? {} : { history },
5889
5945
  ...rememberVariants === void 0 ? {} : { rememberVariants },
5946
+ ...disabled === void 0 ? {} : { disabled },
5890
5947
  ...coerce === void 0 ? {} : { coerce },
5891
5948
  ...validateOn === void 0 ? {} : { validateOn },
5892
5949
  ...debounceMs === void 0 ? {} : { debounceMs },
@@ -5932,6 +5989,7 @@ function buildFreshState(key, schema, configuration, registry) {
5932
5989
  }
5933
5990
  } : {},
5934
5991
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
5992
+ ...configuration.disabled !== void 0 ? { disabled: configuration.disabled } : {},
5935
5993
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
5936
5994
  ...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
5937
5995
  ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
@@ -6156,6 +6214,14 @@ function isLazyMarker(value) {
6156
6214
  return typeof value === "object" && value !== null && LAZY_BRAND in value;
6157
6215
  }
6158
6216
 
6217
+ const GATE_BRAND = /* @__PURE__ */ Symbol.for("attaform/wizard-gate");
6218
+ function gate(step) {
6219
+ return { [GATE_BRAND]: true, inner: step };
6220
+ }
6221
+ function isGateMarker(value) {
6222
+ return typeof value === "object" && value !== null && GATE_BRAND in value;
6223
+ }
6224
+
6159
6225
  const NOOP_WIZARD_HISTORY = {
6160
6226
  push() {
6161
6227
  },
@@ -6279,14 +6345,17 @@ const PENDING_STATUS = {
6279
6345
  valid: false,
6280
6346
  dirty: false,
6281
6347
  submitted: false,
6282
- errorCount: 0
6348
+ errorCount: 0,
6349
+ locked: false
6283
6350
  };
6284
6351
  const NOOP_VALID_STATUS = {
6285
6352
  valid: true,
6286
6353
  dirty: false,
6287
6354
  submitted: false,
6288
- errorCount: 0
6355
+ errorCount: 0,
6356
+ locked: false
6289
6357
  };
6358
+ const EMPTY_LOCK_SET = /* @__PURE__ */ new Set();
6290
6359
  function asStatusSource(form) {
6291
6360
  return form;
6292
6361
  }
@@ -6353,7 +6422,7 @@ function useWizard(options) {
6353
6422
  }
6354
6423
  }
6355
6424
  const lazyEpoch = ref(0);
6356
- const lazyComputeds = /* @__PURE__ */ new Map();
6425
+ const lazyComputeds = /* @__PURE__ */ new WeakMap();
6357
6426
  const activeKey = ref("");
6358
6427
  const formsAccumulator = /* @__PURE__ */ new Map();
6359
6428
  for (const slot of rawSteps) {
@@ -6389,50 +6458,44 @@ function useWizard(options) {
6389
6458
  return activeKey.value === "" ? void 0 : activeKey.value;
6390
6459
  }
6391
6460
  };
6392
- function resolveSlot(slot, index, ctx) {
6393
- if (slot === void 0 || slot === null) return void 0;
6394
- if (typeof slot === "string") {
6395
- return getOrBuildNoop(slot);
6396
- }
6397
- if (isLazyMarker(slot)) {
6398
- const c = lazyComputeds.get(index);
6399
- return c === void 0 ? void 0 : resolveSlotResult(c.value);
6461
+ function lazyComputedFor(marker) {
6462
+ const cached = lazyComputeds.get(marker);
6463
+ if (cached !== void 0) return cached;
6464
+ const c = computed(() => {
6465
+ void lazyEpoch.value;
6466
+ return marker.resolve(slotCtx);
6467
+ });
6468
+ lazyComputeds.set(marker, c);
6469
+ return c;
6470
+ }
6471
+ const MAX_SLOT_DEPTH = 32;
6472
+ function normalizeSlot(slot, ctx, gated, depth) {
6473
+ if (depth > MAX_SLOT_DEPTH) {
6474
+ if (__DEV__) {
6475
+ console.warn(
6476
+ `[attaform] useWizard: a step slot nested past ${MAX_SLOT_DEPTH} levels (gate / lazy / function); dropping it. Check for a resolver that returns itself.`
6477
+ );
6478
+ }
6479
+ return void 0;
6400
6480
  }
6481
+ if (slot === void 0 || slot === null) return void 0;
6482
+ if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
6483
+ if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
6484
+ if (isLazyMarker(slot)) return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
6401
6485
  if (typeof slot === "function") {
6402
6486
  const result = slot(ctx);
6403
- return resolveSlotResult(result);
6487
+ return normalizeSlot(result, ctx, gated, depth + 1);
6404
6488
  }
6405
- if (isAnyForm(slot)) return slot;
6489
+ if (isAnyForm(slot)) return { form: slot, gated };
6406
6490
  return void 0;
6407
6491
  }
6408
- function resolveSlotResult(result) {
6409
- if (result === void 0 || result === null) return void 0;
6410
- if (typeof result === "string") {
6411
- return getOrBuildNoop(result);
6412
- }
6413
- return result;
6414
- }
6415
- for (let i = 0; i < rawSteps.length; i++) {
6416
- const slot = rawSteps[i];
6417
- if (isLazyMarker(slot)) {
6418
- const idx = i;
6419
- const marker = slot;
6420
- lazyComputeds.set(
6421
- idx,
6422
- computed(() => {
6423
- void lazyEpoch.value;
6424
- return marker.resolve(slotCtx);
6425
- })
6426
- );
6427
- }
6428
- }
6429
6492
  const compiledSteps = computed(() => {
6430
6493
  const out = [];
6431
6494
  const seen = /* @__PURE__ */ new Set();
6432
6495
  for (let i = 0; i < rawSteps.length; i++) {
6433
- const slot = rawSteps[i];
6434
- const form = resolveSlot(slot, i, slotCtx);
6435
- if (form === void 0) continue;
6496
+ const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0);
6497
+ if (norm === void 0) continue;
6498
+ const { form, gated } = norm;
6436
6499
  if (seen.has(form.key)) {
6437
6500
  if (__DEV__) {
6438
6501
  console.warn(
@@ -6443,7 +6506,7 @@ function useWizard(options) {
6443
6506
  }
6444
6507
  seen.add(form.key);
6445
6508
  trackOnce(form);
6446
- out.push({ key: form.key, form });
6509
+ out.push({ key: form.key, form, isGate: gated });
6447
6510
  }
6448
6511
  return out;
6449
6512
  });
@@ -6479,12 +6542,12 @@ function useWizard(options) {
6479
6542
  if (key === "") return;
6480
6543
  if (list.some((step) => step.key === key)) return;
6481
6544
  if (list.length === 0) {
6482
- activeKey.value = "";
6545
+ commitActiveKey("");
6483
6546
  return;
6484
6547
  }
6485
6548
  const oldIndex = prevList.findIndex((step) => step.key === key);
6486
6549
  const slid = list[oldIndex < 0 ? 0 : Math.min(oldIndex, list.length - 1)];
6487
- if (slid !== void 0) activeKey.value = slid.key;
6550
+ if (slid !== void 0) commitActiveKey(slid.key);
6488
6551
  });
6489
6552
  const activeFormFacade = new Proxy({}, {
6490
6553
  get(_target, prop) {
@@ -6618,6 +6681,136 @@ function useWizard(options) {
6618
6681
  };
6619
6682
  }
6620
6683
  });
6684
+ const mightGate = rawSteps.some(
6685
+ (slot) => isGateMarker(slot) || isLazyMarker(slot) || typeof slot === "function"
6686
+ );
6687
+ const clearedGates = reactive(/* @__PURE__ */ new Set());
6688
+ const seededSampled = /* @__PURE__ */ new Set();
6689
+ function isGateVerdictSettled(key) {
6690
+ const store = registry.forms.get(key);
6691
+ if (store === void 0) return false;
6692
+ return store.defaultsResolved.value === true && store.firstValidationDone.value === true && store.activeValidations.value === 0;
6693
+ }
6694
+ function isGateFormValid(key) {
6695
+ const form = formsRecord.value[key] ?? formsAccumulator.get(key);
6696
+ if (form === void 0) return false;
6697
+ return asStatusSource(form).meta?.valid ?? false;
6698
+ }
6699
+ function sampleSeededGate(key) {
6700
+ if (seededSampled.has(key)) return;
6701
+ if (noopForms.has(key)) return;
6702
+ if (!isGateVerdictSettled(key)) return;
6703
+ seededSampled.add(key);
6704
+ if (isGateFormValid(key)) clearedGates.add(key);
6705
+ }
6706
+ const gateSubs = /* @__PURE__ */ new Map();
6707
+ function reconcileGates() {
6708
+ const gateKeys = /* @__PURE__ */ new Set();
6709
+ for (const step of compiledSteps.value) if (step.isGate) gateKeys.add(step.key);
6710
+ for (const key of gateKeys) {
6711
+ if (!gateSubs.has(key)) {
6712
+ const store = registry.forms.get(key);
6713
+ if (store !== void 0) {
6714
+ gateSubs.set(
6715
+ key,
6716
+ store.onSubmitSuccess(() => {
6717
+ clearedGates.add(key);
6718
+ })
6719
+ );
6720
+ }
6721
+ }
6722
+ sampleSeededGate(key);
6723
+ }
6724
+ for (const [key, unsub] of gateSubs) {
6725
+ if (!gateKeys.has(key)) {
6726
+ unsub();
6727
+ gateSubs.delete(key);
6728
+ }
6729
+ }
6730
+ }
6731
+ const gatePositions = computed(
6732
+ () => compiledSteps.value.filter((step) => step.isGate).map((step) => step.key)
6733
+ );
6734
+ const navLockSet = computed(() => {
6735
+ const out = /* @__PURE__ */ new Set();
6736
+ let sealed = false;
6737
+ for (const step of compiledSteps.value) {
6738
+ if (sealed) {
6739
+ out.add(step.key);
6740
+ continue;
6741
+ }
6742
+ if (step.isGate && !clearedGates.has(step.key)) sealed = true;
6743
+ }
6744
+ return out.size === 0 ? EMPTY_LOCK_SET : out;
6745
+ });
6746
+ const freezeSet = computed(() => {
6747
+ const out = new Set(navLockSet.value);
6748
+ for (const key of gatePositions.value) if (clearedGates.has(key)) out.add(key);
6749
+ return out.size === 0 ? EMPTY_LOCK_SET : out;
6750
+ });
6751
+ function lastReachableKey() {
6752
+ const list = compiledSteps.value;
6753
+ const locked = navLockSet.value;
6754
+ let last;
6755
+ for (const step of list) {
6756
+ if (locked.has(step.key)) break;
6757
+ last = step.key;
6758
+ }
6759
+ return last;
6760
+ }
6761
+ function resolveLandingKey(target) {
6762
+ if (!navLockSet.value.has(target)) return target;
6763
+ const redirect = lastReachableKey();
6764
+ if (redirect === void 0) return target;
6765
+ if (!isFormReady(redirect)) return target;
6766
+ return redirect;
6767
+ }
6768
+ function commitActiveKey(target) {
6769
+ const landing = target === "" ? "" : resolveLandingKey(target);
6770
+ if (activeKey.value !== landing) activeKey.value = landing;
6771
+ return landing;
6772
+ }
6773
+ if (mightGate) {
6774
+ watch(
6775
+ () => {
6776
+ const parts = [];
6777
+ for (const step of compiledSteps.value) {
6778
+ if (!step.isGate) continue;
6779
+ const store = registry.forms.get(step.key);
6780
+ const settled = store !== void 0 && isGateVerdictSettled(step.key);
6781
+ parts.push(`${step.key}:${store !== void 0 ? 1 : 0}:${settled ? 1 : 0}`);
6782
+ }
6783
+ return parts.join("|");
6784
+ },
6785
+ reconcileGates,
6786
+ { immediate: true }
6787
+ );
6788
+ let previouslyFrozen = [];
6789
+ watchEffect(() => {
6790
+ const frozen = freezeSet.value;
6791
+ for (const key of previouslyFrozen) {
6792
+ if (!frozen.has(key)) {
6793
+ const store = registry.forms.get(key);
6794
+ if (store !== void 0) store.externalLock.value = false;
6795
+ }
6796
+ }
6797
+ for (const key of frozen) {
6798
+ const store = registry.forms.get(key);
6799
+ if (store !== void 0) store.externalLock.value = true;
6800
+ }
6801
+ previouslyFrozen = [...frozen];
6802
+ });
6803
+ if (getCurrentScope() !== void 0) {
6804
+ onScopeDispose(() => {
6805
+ for (const key of previouslyFrozen) {
6806
+ const store = registry.forms.get(key);
6807
+ if (store !== void 0) store.externalLock.value = false;
6808
+ }
6809
+ for (const unsub of gateSubs.values()) unsub();
6810
+ gateSubs.clear();
6811
+ });
6812
+ }
6813
+ }
6621
6814
  const seedRef = ref(void 0);
6622
6815
  const seedInput = options.defaultStatuses;
6623
6816
  if (seedInput !== void 0) {
@@ -6641,6 +6834,7 @@ function useWizard(options) {
6641
6834
  if (cached !== void 0) return cached;
6642
6835
  const source = asStatusSource(form);
6643
6836
  const computedStatus = computed(() => {
6837
+ const locked = navLockSet.value.has(form.key);
6644
6838
  if (isFormReady(form.key)) {
6645
6839
  const meta = source.meta;
6646
6840
  if (meta !== void 0 && meta !== null) {
@@ -6648,16 +6842,17 @@ function useWizard(options) {
6648
6842
  valid: meta.valid,
6649
6843
  dirty: meta.dirty,
6650
6844
  submitted: meta.submitted,
6651
- errorCount: meta.errorCount
6845
+ errorCount: meta.errorCount,
6846
+ locked
6652
6847
  };
6653
6848
  }
6654
6849
  }
6655
- if (noopForms.has(form.key)) return NOOP_VALID_STATUS;
6656
- const seedMap = seedRef.value;
6657
- if (seedMap !== void 0 && Object.hasOwn(seedMap, form.key)) {
6658
- return seedMap[form.key];
6850
+ if (noopForms.has(form.key)) {
6851
+ return locked ? { ...NOOP_VALID_STATUS, locked: true } : NOOP_VALID_STATUS;
6659
6852
  }
6660
- return PENDING_STATUS;
6853
+ const seed = seedRef.value?.[form.key];
6854
+ if (seed !== void 0) return { ...seed, locked };
6855
+ return locked ? { ...PENDING_STATUS, locked: true } : PENDING_STATUS;
6661
6856
  });
6662
6857
  statusCache.set(form.key, computedStatus);
6663
6858
  return computedStatus;
@@ -6773,8 +6968,8 @@ function useWizard(options) {
6773
6968
  initialKey = firstKey();
6774
6969
  }
6775
6970
  if (initialKey !== void 0) {
6776
- activeKey.value = initialKey;
6777
- visited.value = [initialKey];
6971
+ const landing = commitActiveKey(initialKey);
6972
+ visited.value = [landing];
6778
6973
  }
6779
6974
  if (registry.ssr) {
6780
6975
  for (const step of compiledSteps.value) {
@@ -6806,8 +7001,8 @@ function useWizard(options) {
6806
7001
  return;
6807
7002
  }
6808
7003
  if (step === activeKey.value) return;
6809
- activeKey.value = step;
6810
- if (!visited.value.includes(step)) visited.value.push(step);
7004
+ const landing = commitActiveKey(step);
7005
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6811
7006
  }
6812
7007
  );
6813
7008
  }
@@ -6839,11 +7034,11 @@ function useWizard(options) {
6839
7034
  }
6840
7035
  function moveTo(key, options2) {
6841
7036
  if (activeKey.value === key) return;
6842
- activeKey.value = key;
6843
- if (!visited.value.includes(key)) visited.value.push(key);
7037
+ const landing = commitActiveKey(key);
7038
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6844
7039
  const list = compiledSteps.value;
6845
7040
  for (const step of list) {
6846
- if (step.key === key) {
7041
+ if (step.key === landing) {
6847
7042
  activateForm(step.form);
6848
7043
  return;
6849
7044
  }
@@ -6853,6 +7048,14 @@ function useWizard(options) {
6853
7048
  const store = registry.forms.get(key);
6854
7049
  if (store !== void 0) store.departAttempts.value += 1;
6855
7050
  }
7051
+ function advanceOne() {
7052
+ const list = compiledSteps.value;
7053
+ const idx = activeIndex.value;
7054
+ if (idx < 0 || idx >= list.length - 1) return;
7055
+ recordDeparture(activeKey.value);
7056
+ const target = list[idx + 1];
7057
+ moveTo(target.key);
7058
+ }
6856
7059
  async function next() {
6857
7060
  if (submitting.value) {
6858
7061
  if (__DEV__) {
@@ -6878,9 +7081,12 @@ function useWizard(options) {
6878
7081
  }
6879
7082
  return;
6880
7083
  }
6881
- recordDeparture(activeKey.value);
6882
- const target = list[idx + 1];
6883
- moveTo(target.key);
7084
+ const active = list[idx];
7085
+ if (active !== void 0 && active.isGate) {
7086
+ await tryNext();
7087
+ return;
7088
+ }
7089
+ advanceOne();
6884
7090
  }
6885
7091
  async function tryNext() {
6886
7092
  if (submitting.value) {
@@ -6907,13 +7113,13 @@ function useWizard(options) {
6907
7113
  }
6908
7114
  const form = activeForm.value;
6909
7115
  if (form === void 0) return false;
6910
- let advanced = false;
7116
+ const before = activeKey.value;
7117
+ let ranClean = false;
6911
7118
  await asHandleSubmitSource(form).handleSubmit(() => {
6912
- const before = activeKey.value;
6913
- next();
6914
- advanced = activeKey.value !== before;
7119
+ ranClean = true;
6915
7120
  })();
6916
- return advanced;
7121
+ if (ranClean) advanceOne();
7122
+ return activeKey.value !== before;
6917
7123
  }
6918
7124
  function back() {
6919
7125
  if (submitting.value) {
@@ -6953,6 +7159,11 @@ function useWizard(options) {
6953
7159
  }
6954
7160
  return;
6955
7161
  }
7162
+ if (__DEV__ && navLockSet.value.has(key)) {
7163
+ console.warn(
7164
+ `[attaform] wizard.goTo("${key}"): that step sits behind an uncleared gate(); the navigation was refused. Clear the gate before navigating here.`
7165
+ );
7166
+ }
6956
7167
  if (key !== activeKey.value) recordDeparture(activeKey.value);
6957
7168
  moveTo(key);
6958
7169
  }
@@ -7012,6 +7223,20 @@ function useWizard(options) {
7012
7223
  }
7013
7224
  return out;
7014
7225
  }
7226
+ function collectUnclearedGateErrors() {
7227
+ const out = [];
7228
+ for (const step of compiledSteps.value) {
7229
+ if (step.isGate && !clearedGates.has(step.key)) {
7230
+ out.push({
7231
+ formKey: step.key,
7232
+ path: [],
7233
+ message: `Step "${step.key}" is a gate that has not been cleared. Submit it to continue.`,
7234
+ code: AttaformErrorCode.GateNotCleared
7235
+ });
7236
+ }
7237
+ }
7238
+ return out;
7239
+ }
7015
7240
  async function focusFirstWizardError(errors) {
7016
7241
  if (options.focusFirstError === false) return;
7017
7242
  const firstFailedKey = errors[0]?.formKey;
@@ -7068,7 +7293,9 @@ function useWizard(options) {
7068
7293
  }
7069
7294
  submissionAttempts.value += 1;
7070
7295
  const errors = collectErrors(results);
7071
- if (errors.length === 0) {
7296
+ const gateErrors = mightGate ? collectUnclearedGateErrors() : [];
7297
+ const blocking = gateErrors.length > 0 ? [...errors, ...gateErrors] : errors;
7298
+ if (blocking.length === 0) {
7072
7299
  const valuesMap = {};
7073
7300
  for (const step of list) {
7074
7301
  const processed = results.get(step.key);
@@ -7094,10 +7321,10 @@ function useWizard(options) {
7094
7321
  }
7095
7322
  done.value = true;
7096
7323
  } else {
7097
- await focusFirstWizardError(errors);
7324
+ await focusFirstWizardError(blocking);
7098
7325
  if (onError !== void 0) {
7099
7326
  try {
7100
- await onError(errors);
7327
+ await onError(blocking);
7101
7328
  } catch (cause) {
7102
7329
  throw new SubmitErrorHandlerError("User-provided onError threw", { cause });
7103
7330
  }
@@ -7114,21 +7341,35 @@ function useWizard(options) {
7114
7341
  submissionAttempts.value = 0;
7115
7342
  done.value = false;
7116
7343
  submitError.value = null;
7344
+ if (mightGate) {
7345
+ clearedGates.clear();
7346
+ seededSampled.clear();
7347
+ }
7117
7348
  lazyEpoch.value += 1;
7118
7349
  for (const step of compiledSteps.value) {
7119
7350
  const full = asSubmissionSource(step.form);
7120
7351
  if (typeof full.reset === "function") full.reset();
7121
7352
  }
7353
+ if (mightGate) reconcileGates();
7122
7354
  const firstStep = compiledSteps.value[0];
7123
7355
  if (firstStep !== void 0) {
7124
- activeKey.value = firstStep.key;
7125
- visited.value = [firstStep.key];
7356
+ const landing = commitActiveKey(firstStep.key);
7357
+ visited.value = [landing];
7126
7358
  if (persistCallback !== void 0) {
7127
- lastPersisted = firstStep.key;
7128
- persistCallback({ step: firstStep.key });
7359
+ lastPersisted = landing;
7360
+ persistCallback({ step: landing });
7129
7361
  }
7130
7362
  }
7131
7363
  }
7364
+ if (mightGate) {
7365
+ watchEffect(() => {
7366
+ const key = activeKey.value;
7367
+ if (key === "" || !navLockSet.value.has(key)) return;
7368
+ const redirect = lastReachableKey();
7369
+ if (redirect === void 0 || redirect === key || !isFormReady(redirect)) return;
7370
+ moveTo(redirect);
7371
+ });
7372
+ }
7132
7373
  if (getCurrentScope() !== void 0) {
7133
7374
  onScopeDispose(() => {
7134
7375
  historyHandle.dispose();
@@ -7324,5 +7565,5 @@ function escapeForInlineScript(json) {
7324
7565
  });
7325
7566
  }
7326
7567
 
7327
- export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, escapeForInlineScript as e, isUnset as f, useAbstractForm as g, useRegister as h, injectForm as i, useWizard as j, isPlainRecord as k, lazy as l, makeDefaultDisplayState as m, safeAssign as n, slimKindOf as o, humanize as p, getAtPath as q, setAtPath as r, safeOwnRead as s, unset as u };
7328
- //# sourceMappingURL=attaform.CyQxTOdX.mjs.map
7568
+ export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, escapeForInlineScript as e, isUnset as f, gate as g, useAbstractForm as h, injectForm as i, useRegister as j, useWizard as k, lazy as l, makeDefaultDisplayState as m, isPlainRecord as n, safeAssign as o, slimKindOf as p, humanize as q, getAtPath as r, safeOwnRead as s, setAtPath as t, unset as u };
7569
+ //# sourceMappingURL=attaform.DGCijpZT.mjs.map