attaform 0.27.1 → 0.27.3

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.Co5LNRnv.cjs → attaform.BBaMFa-k.cjs} +31 -5
  23. package/dist/shared/attaform.BBaMFa-k.cjs.map +1 -0
  24. package/dist/shared/{attaform.C3WnCDXz.d.cts → attaform.BIPSHY5U.d.cts} +55 -1
  25. package/dist/shared/{attaform.C3WnCDXz.d.mts → attaform.BIPSHY5U.d.mts} +55 -1
  26. package/dist/shared/{attaform.C3WnCDXz.d.ts → attaform.BIPSHY5U.d.ts} +55 -1
  27. package/dist/shared/{attaform.DDAmSFnl.mjs → attaform.BKxpBJVF.mjs} +3 -3
  28. package/dist/shared/{attaform.DDAmSFnl.mjs.map → attaform.BKxpBJVF.mjs.map} +1 -1
  29. package/dist/shared/{attaform.C__F01CE.cjs → attaform.BOIpRdeG.cjs} +4 -4
  30. package/dist/shared/{attaform.C__F01CE.cjs.map → attaform.BOIpRdeG.cjs.map} +1 -1
  31. package/dist/shared/{attaform.D99pd7JG.d.cts → attaform.BjGedLx2.d.cts} +181 -24
  32. package/dist/shared/{attaform.8tFZu1Ub.cjs → attaform.Bn0IzhD_.cjs} +50 -2
  33. package/dist/shared/attaform.Bn0IzhD_.cjs.map +1 -0
  34. package/dist/shared/{attaform.CyQxTOdX.mjs → attaform.BqkFQ2B9.mjs} +332 -84
  35. package/dist/shared/attaform.BqkFQ2B9.mjs.map +1 -0
  36. package/dist/shared/{attaform.CC_y1_Ej.d.ts → attaform.C8zVmank.d.ts} +1 -1
  37. package/dist/shared/{attaform.CpXHK60W.d.cts → attaform.C925L0WA.d.mts} +1 -1
  38. package/dist/shared/{attaform.kBkYlKP_.d.mts → attaform.CEZP3i3I.d.mts} +181 -24
  39. package/dist/shared/{attaform.Da8EdI2t.mjs → attaform.CgsQZdy1.mjs} +31 -5
  40. package/dist/shared/attaform.CgsQZdy1.mjs.map +1 -0
  41. package/dist/shared/{attaform.CvVYOvZF.d.mts → attaform.CnvbLiIO.d.cts} +1 -1
  42. package/dist/shared/{attaform.BERt5YMU.mjs → attaform.Cs2Rbdlo.mjs} +50 -2
  43. package/dist/shared/attaform.Cs2Rbdlo.mjs.map +1 -0
  44. package/dist/shared/{attaform.BceJrhQ7.mjs → attaform.DDIn4fv-.mjs} +4 -4
  45. package/dist/shared/{attaform.BceJrhQ7.mjs.map → attaform.DDIn4fv-.mjs.map} +1 -1
  46. package/dist/shared/{attaform.DkD0DR7R.mjs → attaform.DHQjaGMP.mjs} +5 -5
  47. package/dist/shared/{attaform.DkD0DR7R.mjs.map → attaform.DHQjaGMP.mjs.map} +1 -1
  48. package/dist/shared/{attaform.DhVgG2jh.cjs → attaform.DeHCOlC8.cjs} +4 -4
  49. package/dist/shared/{attaform.DhVgG2jh.cjs.map → attaform.DeHCOlC8.cjs.map} +1 -1
  50. package/dist/shared/{attaform.DlSu08F5.mjs → attaform.DuVCclwv.mjs} +4 -4
  51. package/dist/shared/{attaform.DlSu08F5.mjs.map → attaform.DuVCclwv.mjs.map} +1 -1
  52. package/dist/shared/{attaform.K6Thc6By.cjs → attaform.Mg4g-J0z.cjs} +5 -5
  53. package/dist/shared/{attaform.K6Thc6By.cjs.map → attaform.Mg4g-J0z.cjs.map} +1 -1
  54. package/dist/shared/{attaform.Drsi43GG.cjs → attaform.dX58Auwf.cjs} +3 -3
  55. package/dist/shared/{attaform.Drsi43GG.cjs.map → attaform.dX58Auwf.cjs.map} +1 -1
  56. package/dist/shared/{attaform.CEXUlUkc.d.ts → attaform.gqOCvpSB.d.ts} +181 -24
  57. package/dist/shared/{attaform.DQT7_-FO.cjs → attaform.ij2W7VTw.cjs} +331 -82
  58. package/dist/shared/attaform.ij2W7VTw.cjs.map +1 -0
  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,19 @@ const PENDING_STATUS = {
6279
6345
  valid: false,
6280
6346
  dirty: false,
6281
6347
  submitted: false,
6282
- errorCount: 0
6348
+ errorCount: 0,
6349
+ locked: false,
6350
+ gate: null
6283
6351
  };
6284
6352
  const NOOP_VALID_STATUS = {
6285
6353
  valid: true,
6286
6354
  dirty: false,
6287
6355
  submitted: false,
6288
- errorCount: 0
6356
+ errorCount: 0,
6357
+ locked: false,
6358
+ gate: null
6289
6359
  };
6360
+ const EMPTY_LOCK_SET = /* @__PURE__ */ new Set();
6290
6361
  function asStatusSource(form) {
6291
6362
  return form;
6292
6363
  }
@@ -6353,7 +6424,7 @@ function useWizard(options) {
6353
6424
  }
6354
6425
  }
6355
6426
  const lazyEpoch = ref(0);
6356
- const lazyComputeds = /* @__PURE__ */ new Map();
6427
+ const lazyComputeds = /* @__PURE__ */ new WeakMap();
6357
6428
  const activeKey = ref("");
6358
6429
  const formsAccumulator = /* @__PURE__ */ new Map();
6359
6430
  for (const slot of rawSteps) {
@@ -6389,50 +6460,44 @@ function useWizard(options) {
6389
6460
  return activeKey.value === "" ? void 0 : activeKey.value;
6390
6461
  }
6391
6462
  };
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);
6463
+ function lazyComputedFor(marker) {
6464
+ const cached = lazyComputeds.get(marker);
6465
+ if (cached !== void 0) return cached;
6466
+ const c = computed(() => {
6467
+ void lazyEpoch.value;
6468
+ return marker.resolve(slotCtx);
6469
+ });
6470
+ lazyComputeds.set(marker, c);
6471
+ return c;
6472
+ }
6473
+ const MAX_SLOT_DEPTH = 32;
6474
+ function normalizeSlot(slot, ctx, gated, depth) {
6475
+ if (depth > MAX_SLOT_DEPTH) {
6476
+ if (__DEV__) {
6477
+ console.warn(
6478
+ `[attaform] useWizard: a step slot nested past ${MAX_SLOT_DEPTH} levels (gate / lazy / function); dropping it. Check for a resolver that returns itself.`
6479
+ );
6480
+ }
6481
+ return void 0;
6400
6482
  }
6483
+ if (slot === void 0 || slot === null) return void 0;
6484
+ if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
6485
+ if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
6486
+ if (isLazyMarker(slot)) return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
6401
6487
  if (typeof slot === "function") {
6402
6488
  const result = slot(ctx);
6403
- return resolveSlotResult(result);
6489
+ return normalizeSlot(result, ctx, gated, depth + 1);
6404
6490
  }
6405
- if (isAnyForm(slot)) return slot;
6491
+ if (isAnyForm(slot)) return { form: slot, gated };
6406
6492
  return void 0;
6407
6493
  }
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
6494
  const compiledSteps = computed(() => {
6430
6495
  const out = [];
6431
6496
  const seen = /* @__PURE__ */ new Set();
6432
6497
  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;
6498
+ const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0);
6499
+ if (norm === void 0) continue;
6500
+ const { form, gated } = norm;
6436
6501
  if (seen.has(form.key)) {
6437
6502
  if (__DEV__) {
6438
6503
  console.warn(
@@ -6443,7 +6508,7 @@ function useWizard(options) {
6443
6508
  }
6444
6509
  seen.add(form.key);
6445
6510
  trackOnce(form);
6446
- out.push({ key: form.key, form });
6511
+ out.push({ key: form.key, form, isGate: gated });
6447
6512
  }
6448
6513
  return out;
6449
6514
  });
@@ -6479,12 +6544,12 @@ function useWizard(options) {
6479
6544
  if (key === "") return;
6480
6545
  if (list.some((step) => step.key === key)) return;
6481
6546
  if (list.length === 0) {
6482
- activeKey.value = "";
6547
+ commitActiveKey("");
6483
6548
  return;
6484
6549
  }
6485
6550
  const oldIndex = prevList.findIndex((step) => step.key === key);
6486
6551
  const slid = list[oldIndex < 0 ? 0 : Math.min(oldIndex, list.length - 1)];
6487
- if (slid !== void 0) activeKey.value = slid.key;
6552
+ if (slid !== void 0) commitActiveKey(slid.key);
6488
6553
  });
6489
6554
  const activeFormFacade = new Proxy({}, {
6490
6555
  get(_target, prop) {
@@ -6618,6 +6683,136 @@ function useWizard(options) {
6618
6683
  };
6619
6684
  }
6620
6685
  });
6686
+ const mightGate = rawSteps.some(
6687
+ (slot) => isGateMarker(slot) || isLazyMarker(slot) || typeof slot === "function"
6688
+ );
6689
+ const clearedGates = reactive(/* @__PURE__ */ new Set());
6690
+ const seededSampled = /* @__PURE__ */ new Set();
6691
+ function isGateVerdictSettled(key) {
6692
+ const store = registry.forms.get(key);
6693
+ if (store === void 0) return false;
6694
+ return store.defaultsResolved.value === true && store.firstValidationDone.value === true && store.activeValidations.value === 0;
6695
+ }
6696
+ function isGateFormValid(key) {
6697
+ const form = formsRecord.value[key] ?? formsAccumulator.get(key);
6698
+ if (form === void 0) return false;
6699
+ return asStatusSource(form).meta?.valid ?? false;
6700
+ }
6701
+ function sampleSeededGate(key) {
6702
+ if (seededSampled.has(key)) return;
6703
+ if (noopForms.has(key)) return;
6704
+ if (!isGateVerdictSettled(key)) return;
6705
+ seededSampled.add(key);
6706
+ if (isGateFormValid(key)) clearedGates.add(key);
6707
+ }
6708
+ const gateSubs = /* @__PURE__ */ new Map();
6709
+ function reconcileGates() {
6710
+ const gateKeys = /* @__PURE__ */ new Set();
6711
+ for (const step of compiledSteps.value) if (step.isGate) gateKeys.add(step.key);
6712
+ for (const key of gateKeys) {
6713
+ if (!gateSubs.has(key)) {
6714
+ const store = registry.forms.get(key);
6715
+ if (store !== void 0) {
6716
+ gateSubs.set(
6717
+ key,
6718
+ store.onSubmitSuccess(() => {
6719
+ clearedGates.add(key);
6720
+ })
6721
+ );
6722
+ }
6723
+ }
6724
+ sampleSeededGate(key);
6725
+ }
6726
+ for (const [key, unsub] of gateSubs) {
6727
+ if (!gateKeys.has(key)) {
6728
+ unsub();
6729
+ gateSubs.delete(key);
6730
+ }
6731
+ }
6732
+ }
6733
+ const gatePositions = computed(
6734
+ () => compiledSteps.value.filter((step) => step.isGate).map((step) => step.key)
6735
+ );
6736
+ const navLockSet = computed(() => {
6737
+ const out = /* @__PURE__ */ new Set();
6738
+ let sealed = false;
6739
+ for (const step of compiledSteps.value) {
6740
+ if (sealed) {
6741
+ out.add(step.key);
6742
+ continue;
6743
+ }
6744
+ if (step.isGate && !clearedGates.has(step.key)) sealed = true;
6745
+ }
6746
+ return out.size === 0 ? EMPTY_LOCK_SET : out;
6747
+ });
6748
+ const freezeSet = computed(() => {
6749
+ const out = new Set(navLockSet.value);
6750
+ for (const key of gatePositions.value) if (clearedGates.has(key)) out.add(key);
6751
+ return out.size === 0 ? EMPTY_LOCK_SET : out;
6752
+ });
6753
+ function lastReachableKey() {
6754
+ const list = compiledSteps.value;
6755
+ const locked = navLockSet.value;
6756
+ let last;
6757
+ for (const step of list) {
6758
+ if (locked.has(step.key)) break;
6759
+ last = step.key;
6760
+ }
6761
+ return last;
6762
+ }
6763
+ function resolveLandingKey(target) {
6764
+ if (!navLockSet.value.has(target)) return target;
6765
+ const redirect = lastReachableKey();
6766
+ if (redirect === void 0) return target;
6767
+ if (!isFormReady(redirect)) return target;
6768
+ return redirect;
6769
+ }
6770
+ function commitActiveKey(target) {
6771
+ const landing = target === "" ? "" : resolveLandingKey(target);
6772
+ if (activeKey.value !== landing) activeKey.value = landing;
6773
+ return landing;
6774
+ }
6775
+ if (mightGate) {
6776
+ watch(
6777
+ () => {
6778
+ const parts = [];
6779
+ for (const step of compiledSteps.value) {
6780
+ if (!step.isGate) continue;
6781
+ const store = registry.forms.get(step.key);
6782
+ const settled = store !== void 0 && isGateVerdictSettled(step.key);
6783
+ parts.push(`${step.key}:${store !== void 0 ? 1 : 0}:${settled ? 1 : 0}`);
6784
+ }
6785
+ return parts.join("|");
6786
+ },
6787
+ reconcileGates,
6788
+ { immediate: true }
6789
+ );
6790
+ let previouslyFrozen = [];
6791
+ watchEffect(() => {
6792
+ const frozen = freezeSet.value;
6793
+ for (const key of previouslyFrozen) {
6794
+ if (!frozen.has(key)) {
6795
+ const store = registry.forms.get(key);
6796
+ if (store !== void 0) store.externalLock.value = false;
6797
+ }
6798
+ }
6799
+ for (const key of frozen) {
6800
+ const store = registry.forms.get(key);
6801
+ if (store !== void 0) store.externalLock.value = true;
6802
+ }
6803
+ previouslyFrozen = [...frozen];
6804
+ });
6805
+ if (getCurrentScope() !== void 0) {
6806
+ onScopeDispose(() => {
6807
+ for (const key of previouslyFrozen) {
6808
+ const store = registry.forms.get(key);
6809
+ if (store !== void 0) store.externalLock.value = false;
6810
+ }
6811
+ for (const unsub of gateSubs.values()) unsub();
6812
+ gateSubs.clear();
6813
+ });
6814
+ }
6815
+ }
6621
6816
  const seedRef = ref(void 0);
6622
6817
  const seedInput = options.defaultStatuses;
6623
6818
  if (seedInput !== void 0) {
@@ -6641,6 +6836,11 @@ function useWizard(options) {
6641
6836
  if (cached !== void 0) return cached;
6642
6837
  const source = asStatusSource(form);
6643
6838
  const computedStatus = computed(() => {
6839
+ const locked = navLockSet.value.has(form.key);
6840
+ let gate = null;
6841
+ if (gatePositions.value.includes(form.key)) {
6842
+ gate = clearedGates.has(form.key) ? "cleared" : "uncleared";
6843
+ }
6644
6844
  if (isFormReady(form.key)) {
6645
6845
  const meta = source.meta;
6646
6846
  if (meta !== void 0 && meta !== null) {
@@ -6648,16 +6848,18 @@ function useWizard(options) {
6648
6848
  valid: meta.valid,
6649
6849
  dirty: meta.dirty,
6650
6850
  submitted: meta.submitted,
6651
- errorCount: meta.errorCount
6851
+ errorCount: meta.errorCount,
6852
+ locked,
6853
+ gate
6652
6854
  };
6653
6855
  }
6654
6856
  }
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];
6857
+ if (noopForms.has(form.key)) {
6858
+ return locked || gate !== null ? { ...NOOP_VALID_STATUS, locked, gate } : NOOP_VALID_STATUS;
6659
6859
  }
6660
- return PENDING_STATUS;
6860
+ const seed = seedRef.value?.[form.key];
6861
+ if (seed !== void 0) return { ...seed, locked, gate };
6862
+ return locked || gate !== null ? { ...PENDING_STATUS, locked, gate } : PENDING_STATUS;
6661
6863
  });
6662
6864
  statusCache.set(form.key, computedStatus);
6663
6865
  return computedStatus;
@@ -6773,8 +6975,8 @@ function useWizard(options) {
6773
6975
  initialKey = firstKey();
6774
6976
  }
6775
6977
  if (initialKey !== void 0) {
6776
- activeKey.value = initialKey;
6777
- visited.value = [initialKey];
6978
+ const landing = commitActiveKey(initialKey);
6979
+ visited.value = [landing];
6778
6980
  }
6779
6981
  if (registry.ssr) {
6780
6982
  for (const step of compiledSteps.value) {
@@ -6806,8 +7008,8 @@ function useWizard(options) {
6806
7008
  return;
6807
7009
  }
6808
7010
  if (step === activeKey.value) return;
6809
- activeKey.value = step;
6810
- if (!visited.value.includes(step)) visited.value.push(step);
7011
+ const landing = commitActiveKey(step);
7012
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6811
7013
  }
6812
7014
  );
6813
7015
  }
@@ -6839,11 +7041,11 @@ function useWizard(options) {
6839
7041
  }
6840
7042
  function moveTo(key, options2) {
6841
7043
  if (activeKey.value === key) return;
6842
- activeKey.value = key;
6843
- if (!visited.value.includes(key)) visited.value.push(key);
7044
+ const landing = commitActiveKey(key);
7045
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6844
7046
  const list = compiledSteps.value;
6845
7047
  for (const step of list) {
6846
- if (step.key === key) {
7048
+ if (step.key === landing) {
6847
7049
  activateForm(step.form);
6848
7050
  return;
6849
7051
  }
@@ -6853,6 +7055,14 @@ function useWizard(options) {
6853
7055
  const store = registry.forms.get(key);
6854
7056
  if (store !== void 0) store.departAttempts.value += 1;
6855
7057
  }
7058
+ function advanceOne() {
7059
+ const list = compiledSteps.value;
7060
+ const idx = activeIndex.value;
7061
+ if (idx < 0 || idx >= list.length - 1) return;
7062
+ recordDeparture(activeKey.value);
7063
+ const target = list[idx + 1];
7064
+ moveTo(target.key);
7065
+ }
6856
7066
  async function next() {
6857
7067
  if (submitting.value) {
6858
7068
  if (__DEV__) {
@@ -6878,9 +7088,12 @@ function useWizard(options) {
6878
7088
  }
6879
7089
  return;
6880
7090
  }
6881
- recordDeparture(activeKey.value);
6882
- const target = list[idx + 1];
6883
- moveTo(target.key);
7091
+ const active = list[idx];
7092
+ if (active !== void 0 && active.isGate) {
7093
+ await tryNext();
7094
+ return;
7095
+ }
7096
+ advanceOne();
6884
7097
  }
6885
7098
  async function tryNext() {
6886
7099
  if (submitting.value) {
@@ -6907,13 +7120,13 @@ function useWizard(options) {
6907
7120
  }
6908
7121
  const form = activeForm.value;
6909
7122
  if (form === void 0) return false;
6910
- let advanced = false;
7123
+ const before = activeKey.value;
7124
+ let ranClean = false;
6911
7125
  await asHandleSubmitSource(form).handleSubmit(() => {
6912
- const before = activeKey.value;
6913
- next();
6914
- advanced = activeKey.value !== before;
7126
+ ranClean = true;
6915
7127
  })();
6916
- return advanced;
7128
+ if (ranClean) advanceOne();
7129
+ return activeKey.value !== before;
6917
7130
  }
6918
7131
  function back() {
6919
7132
  if (submitting.value) {
@@ -6953,6 +7166,11 @@ function useWizard(options) {
6953
7166
  }
6954
7167
  return;
6955
7168
  }
7169
+ if (__DEV__ && navLockSet.value.has(key)) {
7170
+ console.warn(
7171
+ `[attaform] wizard.goTo("${key}"): that step sits behind an uncleared gate(); the navigation was refused. Clear the gate before navigating here.`
7172
+ );
7173
+ }
6956
7174
  if (key !== activeKey.value) recordDeparture(activeKey.value);
6957
7175
  moveTo(key);
6958
7176
  }
@@ -7012,6 +7230,20 @@ function useWizard(options) {
7012
7230
  }
7013
7231
  return out;
7014
7232
  }
7233
+ function collectUnclearedGateErrors() {
7234
+ const out = [];
7235
+ for (const step of compiledSteps.value) {
7236
+ if (step.isGate && !clearedGates.has(step.key)) {
7237
+ out.push({
7238
+ formKey: step.key,
7239
+ path: [],
7240
+ message: `Step "${step.key}" is a gate that has not been cleared. Submit it to continue.`,
7241
+ code: AttaformErrorCode.GateNotCleared
7242
+ });
7243
+ }
7244
+ }
7245
+ return out;
7246
+ }
7015
7247
  async function focusFirstWizardError(errors) {
7016
7248
  if (options.focusFirstError === false) return;
7017
7249
  const firstFailedKey = errors[0]?.formKey;
@@ -7068,7 +7300,9 @@ function useWizard(options) {
7068
7300
  }
7069
7301
  submissionAttempts.value += 1;
7070
7302
  const errors = collectErrors(results);
7071
- if (errors.length === 0) {
7303
+ const gateErrors = mightGate ? collectUnclearedGateErrors() : [];
7304
+ const blocking = gateErrors.length > 0 ? [...errors, ...gateErrors] : errors;
7305
+ if (blocking.length === 0) {
7072
7306
  const valuesMap = {};
7073
7307
  for (const step of list) {
7074
7308
  const processed = results.get(step.key);
@@ -7094,10 +7328,10 @@ function useWizard(options) {
7094
7328
  }
7095
7329
  done.value = true;
7096
7330
  } else {
7097
- await focusFirstWizardError(errors);
7331
+ await focusFirstWizardError(blocking);
7098
7332
  if (onError !== void 0) {
7099
7333
  try {
7100
- await onError(errors);
7334
+ await onError(blocking);
7101
7335
  } catch (cause) {
7102
7336
  throw new SubmitErrorHandlerError("User-provided onError threw", { cause });
7103
7337
  }
@@ -7114,21 +7348,35 @@ function useWizard(options) {
7114
7348
  submissionAttempts.value = 0;
7115
7349
  done.value = false;
7116
7350
  submitError.value = null;
7351
+ if (mightGate) {
7352
+ clearedGates.clear();
7353
+ seededSampled.clear();
7354
+ }
7117
7355
  lazyEpoch.value += 1;
7118
7356
  for (const step of compiledSteps.value) {
7119
7357
  const full = asSubmissionSource(step.form);
7120
7358
  if (typeof full.reset === "function") full.reset();
7121
7359
  }
7360
+ if (mightGate) reconcileGates();
7122
7361
  const firstStep = compiledSteps.value[0];
7123
7362
  if (firstStep !== void 0) {
7124
- activeKey.value = firstStep.key;
7125
- visited.value = [firstStep.key];
7363
+ const landing = commitActiveKey(firstStep.key);
7364
+ visited.value = [landing];
7126
7365
  if (persistCallback !== void 0) {
7127
- lastPersisted = firstStep.key;
7128
- persistCallback({ step: firstStep.key });
7366
+ lastPersisted = landing;
7367
+ persistCallback({ step: landing });
7129
7368
  }
7130
7369
  }
7131
7370
  }
7371
+ if (mightGate) {
7372
+ watchEffect(() => {
7373
+ const key = activeKey.value;
7374
+ if (key === "" || !navLockSet.value.has(key)) return;
7375
+ const redirect = lastReachableKey();
7376
+ if (redirect === void 0 || redirect === key || !isFormReady(redirect)) return;
7377
+ moveTo(redirect);
7378
+ });
7379
+ }
7132
7380
  if (getCurrentScope() !== void 0) {
7133
7381
  onScopeDispose(() => {
7134
7382
  historyHandle.dispose();
@@ -7324,5 +7572,5 @@ function escapeForInlineScript(json) {
7324
7572
  });
7325
7573
  }
7326
7574
 
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
7575
+ 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 };
7576
+ //# sourceMappingURL=attaform.BqkFQ2B9.mjs.map