attaform 0.27.0 → 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 (90) hide show
  1. package/bin/attaform.mjs +55 -8
  2. package/dist/abstract.cjs +3 -2
  3. package/dist/abstract.cjs.map +1 -1
  4. package/dist/abstract.d.cts +4 -4
  5. package/dist/abstract.d.mts +4 -4
  6. package/dist/abstract.d.ts +4 -4
  7. package/dist/abstract.mjs +2 -2
  8. package/dist/chunks/devtools.cjs +1 -1
  9. package/dist/chunks/devtools.mjs +1 -1
  10. package/dist/chunks/fingerprint2.cjs +1 -1
  11. package/dist/chunks/fingerprint2.mjs +1 -1
  12. package/dist/index.cjs +4 -3
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +5 -5
  15. package/dist/index.d.mts +5 -5
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.mjs +3 -3
  18. package/dist/nuxt.d.cts +1 -1
  19. package/dist/nuxt.d.mts +1 -1
  20. package/dist/nuxt.d.ts +1 -1
  21. package/dist/runtime/plugins/attaform.cjs +1 -1
  22. package/dist/runtime/plugins/attaform.mjs +1 -1
  23. package/dist/shared/{attaform.DkD0DR7R.mjs → attaform.6IZBjA1Q.mjs} +5 -5
  24. package/dist/shared/{attaform.DkD0DR7R.mjs.map → attaform.6IZBjA1Q.mjs.map} +1 -1
  25. package/dist/shared/{attaform.D99pd7JG.d.cts → attaform.B2d0FszH.d.ts} +169 -24
  26. package/dist/shared/{attaform.DDAmSFnl.mjs → attaform.BAlfXoAx.mjs} +3 -3
  27. package/dist/shared/{attaform.DDAmSFnl.mjs.map → attaform.BAlfXoAx.mjs.map} +1 -1
  28. package/dist/shared/{attaform.Co5LNRnv.cjs → attaform.BBaMFa-k.cjs} +31 -5
  29. package/dist/shared/attaform.BBaMFa-k.cjs.map +1 -0
  30. package/dist/shared/{attaform.DhVgG2jh.cjs → attaform.BF1mYHru.cjs} +4 -4
  31. package/dist/shared/{attaform.DhVgG2jh.cjs.map → attaform.BF1mYHru.cjs.map} +1 -1
  32. package/dist/shared/{attaform.C3WnCDXz.d.cts → attaform.BIPSHY5U.d.cts} +55 -1
  33. package/dist/shared/{attaform.C3WnCDXz.d.mts → attaform.BIPSHY5U.d.mts} +55 -1
  34. package/dist/shared/{attaform.C3WnCDXz.d.ts → attaform.BIPSHY5U.d.ts} +55 -1
  35. package/dist/shared/{attaform.8tFZu1Ub.cjs → attaform.Bn0IzhD_.cjs} +50 -2
  36. package/dist/shared/attaform.Bn0IzhD_.cjs.map +1 -0
  37. package/dist/shared/{attaform.K6Thc6By.cjs → attaform.C1SJy524.cjs} +5 -5
  38. package/dist/shared/{attaform.K6Thc6By.cjs.map → attaform.C1SJy524.cjs.map} +1 -1
  39. package/dist/shared/{attaform.CC_y1_Ej.d.ts → attaform.C8zVmank.d.ts} +1 -1
  40. package/dist/shared/{attaform.CpXHK60W.d.cts → attaform.C925L0WA.d.mts} +1 -1
  41. package/dist/shared/{attaform.Drsi43GG.cjs → attaform.CIPwCQe8.cjs} +3 -3
  42. package/dist/shared/{attaform.Drsi43GG.cjs.map → attaform.CIPwCQe8.cjs.map} +1 -1
  43. package/dist/shared/{attaform.DQT7_-FO.cjs → attaform.CXHebpij.cjs} +324 -82
  44. package/dist/shared/attaform.CXHebpij.cjs.map +1 -0
  45. package/dist/shared/{attaform.Da8EdI2t.mjs → attaform.CgsQZdy1.mjs} +31 -5
  46. package/dist/shared/attaform.CgsQZdy1.mjs.map +1 -0
  47. package/dist/shared/{attaform.CvVYOvZF.d.mts → attaform.CnvbLiIO.d.cts} +1 -1
  48. package/dist/shared/{attaform.BERt5YMU.mjs → attaform.Cs2Rbdlo.mjs} +50 -2
  49. package/dist/shared/attaform.Cs2Rbdlo.mjs.map +1 -0
  50. package/dist/shared/{attaform.C__F01CE.cjs → attaform.CvaPYOZu.cjs} +4 -4
  51. package/dist/shared/{attaform.C__F01CE.cjs.map → attaform.CvaPYOZu.cjs.map} +1 -1
  52. package/dist/shared/{attaform.CyQxTOdX.mjs → attaform.DGCijpZT.mjs} +325 -84
  53. package/dist/shared/attaform.DGCijpZT.mjs.map +1 -0
  54. package/dist/shared/{attaform.kBkYlKP_.d.mts → attaform.DIJTp_Kv.d.cts} +169 -24
  55. package/dist/shared/{attaform.CEXUlUkc.d.ts → attaform.Dcj-OS43.d.mts} +169 -24
  56. package/dist/shared/{attaform.DlSu08F5.mjs → attaform.gyed1fGh.mjs} +4 -4
  57. package/dist/shared/{attaform.DlSu08F5.mjs.map → attaform.gyed1fGh.mjs.map} +1 -1
  58. package/dist/shared/{attaform.BceJrhQ7.mjs → attaform.yOE3udL1.mjs} +4 -4
  59. package/dist/shared/{attaform.BceJrhQ7.mjs.map → attaform.yOE3udL1.mjs.map} +1 -1
  60. package/dist/transforms.cjs +1 -1
  61. package/dist/transforms.mjs +1 -1
  62. package/dist/vite.cjs +3 -2
  63. package/dist/vite.cjs.map +1 -1
  64. package/dist/vite.mjs +3 -2
  65. package/dist/vite.mjs.map +1 -1
  66. package/dist/zod-v3.cjs +4 -3
  67. package/dist/zod-v3.cjs.map +1 -1
  68. package/dist/zod-v3.d.cts +3 -3
  69. package/dist/zod-v3.d.mts +3 -3
  70. package/dist/zod-v3.d.ts +3 -3
  71. package/dist/zod-v3.mjs +3 -3
  72. package/dist/zod-v4.cjs +4 -3
  73. package/dist/zod-v4.cjs.map +1 -1
  74. package/dist/zod-v4.d.cts +6 -6
  75. package/dist/zod-v4.d.mts +6 -6
  76. package/dist/zod-v4.d.ts +6 -6
  77. package/dist/zod-v4.mjs +3 -3
  78. package/dist/zod.cjs +4 -3
  79. package/dist/zod.cjs.map +1 -1
  80. package/dist/zod.d.cts +3 -3
  81. package/dist/zod.d.mts +3 -3
  82. package/dist/zod.d.ts +3 -3
  83. package/dist/zod.mjs +3 -3
  84. package/package.json +3 -2
  85. package/dist/shared/attaform.8tFZu1Ub.cjs.map +0 -1
  86. package/dist/shared/attaform.BERt5YMU.mjs.map +0 -1
  87. package/dist/shared/attaform.Co5LNRnv.cjs.map +0 -1
  88. package/dist/shared/attaform.CyQxTOdX.mjs.map +0 -1
  89. package/dist/shared/attaform.DQT7_-FO.cjs.map +0 -1
  90. package/dist/shared/attaform.Da8EdI2t.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const devtoolsShared = require('./attaform.Co5LNRnv.cjs');
4
+ const devtoolsShared = require('./attaform.BBaMFa-k.cjs');
5
5
  const registerProtocol = require('./attaform.yUrz7C2z.cjs');
6
6
 
7
7
  function safeAssign(target, key, value) {
@@ -595,7 +595,17 @@ const AttaformErrorCode = {
595
595
  * keeps them; a bare `Error` or a non-Error throw defaults here at the
596
596
  * form-level path `[]`.
597
597
  */
598
- SubmitError: "atta:submit-error"
598
+ SubmitError: "atta:submit-error",
599
+ /**
600
+ * Synthesized by `wizard.handleSubmit` when the whole-wizard submit
601
+ * would otherwise succeed but a `gate()` step is still uncleared. A
602
+ * gate clears only on its member form's clean submit (confirmation), so
603
+ * a downstream step that happens to be valid-by-default must not let the
604
+ * wizard finish past an unconfirmed prerequisite. Emitted at the gate
605
+ * form's key (path `[]`) so the aggregate error pipeline routes focus to
606
+ * the gate, and `done` never latches.
607
+ */
608
+ GateNotCleared: "atta:gate-not-cleared"
599
609
  };
600
610
  function makeBlankRequiredError(segments, formKey) {
601
611
  return {
@@ -610,6 +620,7 @@ function isGateOpen(field, formMeta) {
610
620
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
611
621
  }
612
622
  function computeVerdict(field, formMeta) {
623
+ if (field.disabled === true) return "idle";
613
624
  if (!isGateOpen(field, formMeta)) return "idle";
614
625
  const hasOwnError = field.errors.some(
615
626
  (e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
@@ -817,6 +828,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
817
828
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
818
829
  key: state.arrayElementKey(segments),
819
830
  blank: state.blankPaths.has(key),
831
+ disabled: state.effectiveDisabled.value,
820
832
  label,
821
833
  description: resolved.description,
822
834
  placeholder: resolved.placeholder,
@@ -1004,6 +1016,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
1004
1016
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
1005
1017
  key: state.arrayElementKey(segments),
1006
1018
  blank,
1019
+ disabled: state.effectiveDisabled.value,
1007
1020
  label,
1008
1021
  description: resolved.description,
1009
1022
  placeholder: resolved.placeholder,
@@ -1058,7 +1071,8 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
1058
1071
  rollupApplies = true;
1059
1072
  }
1060
1073
  const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
1061
- const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
1074
+ const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
1075
+ const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
1062
1076
  return {
1063
1077
  ...base,
1064
1078
  displayState,
@@ -1646,6 +1660,7 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1646
1660
  "aria",
1647
1661
  "key",
1648
1662
  "blank",
1663
+ "disabled",
1649
1664
  "label",
1650
1665
  "description",
1651
1666
  "placeholder",
@@ -2415,6 +2430,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2415
2430
  return Object.prototype.toString.call(raw);
2416
2431
  }
2417
2432
  });
2433
+ const hostModelValue = vue.computed(
2434
+ () => state.blankPaths.has(pathKey) ? void 0 : innerRef.value
2435
+ );
2418
2436
  const slimDefault = state.schema.getDefaultAtPath(segments);
2419
2437
  const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
2420
2438
  const acceptsUndefined = slimTypes.has("undefined");
@@ -2434,19 +2452,18 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2434
2452
  const isRequired = state.schema.isRequiredAtPath(segments);
2435
2453
  const ariaEnabled = options?.autoAria ?? formAutoAria;
2436
2454
  const ariaDisplayState = getDisplayStateAt !== void 0 ? vue.computed(() => getDisplayStateAt(segments)) : void 0;
2455
+ const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
2437
2456
  const internalRv = {
2438
2457
  innerRef,
2439
2458
  displayValue,
2459
+ hostModelValue,
2460
+ // Live form-freeze flag for the compile-time transforms' `:disabled`
2461
+ // bind (native attribute + component-host prop) and custom
2462
+ // `useRegister` integrations. Reads the form-level effective freeze,
2463
+ // so every binding on a disabled form reports `true`.
2464
+ disabled: vue.computed(() => state.effectiveDisabled.value),
2440
2465
  lastTypedForm,
2441
- markBlank: () => {
2442
- return state.setValueAtPath(
2443
- segments,
2444
- slimDefault,
2445
- withInstanceMeta({
2446
- blank: true
2447
- })
2448
- );
2449
- },
2466
+ markBlank,
2450
2467
  markInteracted: () => {
2451
2468
  state.markInteracted(segments);
2452
2469
  },
@@ -2464,7 +2481,8 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2464
2481
  },
2465
2482
  setValueFromHost: (value) => {
2466
2483
  state.markInteracted(segments);
2467
- const accepted = state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2484
+ const isBlankSignal = value === "" && !acceptsString || value === null && !slimTypes.has("null") || value === void 0 && !acceptsUndefined;
2485
+ const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2468
2486
  if (devtoolsShared.__DEV__) {
2469
2487
  const dedupeKey = `${formInstanceId}:${pathKey}`;
2470
2488
  const isWired = () => (state.elements.get(pathKey)?.elements.size ?? 0) > 0 || state.getFieldRecord(segments)?.connected === true;
@@ -2911,6 +2929,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2911
2929
  get blank() {
2912
2930
  return rootBase().blank;
2913
2931
  },
2932
+ get disabled() {
2933
+ return rootBase().disabled;
2934
+ },
2914
2935
  get label() {
2915
2936
  return rootBase().label;
2916
2937
  },
@@ -3229,6 +3250,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
3229
3250
  get blank() {
3230
3251
  return rootFieldState.value.blank;
3231
3252
  },
3253
+ get disabled() {
3254
+ return rootFieldState.value.disabled;
3255
+ },
3232
3256
  get label() {
3233
3257
  return rootFieldState.value.label;
3234
3258
  },
@@ -4271,6 +4295,24 @@ function createFormStore(options) {
4271
4295
  const submitted = vue.ref(false);
4272
4296
  const submitError = vue.ref(null);
4273
4297
  const departAttempts = vue.ref(0);
4298
+ const externalLock = vue.ref(false);
4299
+ let warnedDisabledThrow = false;
4300
+ let warnedDisabledWrite = false;
4301
+ const effectiveDisabled = vue.computed(() => {
4302
+ let own = false;
4303
+ try {
4304
+ own = Boolean(vue.toValue(options.disabled));
4305
+ } catch (err) {
4306
+ if (devtoolsShared.__DEV__ && !warnedDisabledThrow) {
4307
+ warnedDisabledThrow = true;
4308
+ console.warn(
4309
+ `[attaform] useForm({ disabled }) getter threw for form "${String(formKey)}"; treating its config as enabled. Fix the getter to clear this warning.`,
4310
+ err
4311
+ );
4312
+ }
4313
+ }
4314
+ return own || externalLock.value;
4315
+ });
4274
4316
  const submissionGeneration = vue.ref(0);
4275
4317
  const activeValidations = vue.ref(0);
4276
4318
  const pathSnapshots = /* @__PURE__ */ new Map();
@@ -4564,6 +4606,15 @@ function createFormStore(options) {
4564
4606
  commitWritePatches(patches, meta);
4565
4607
  }
4566
4608
  function setValueAtPath(path, value, meta) {
4609
+ if (effectiveDisabled.value) {
4610
+ if (devtoolsShared.__DEV__ && !warnedDisabledWrite) {
4611
+ warnedDisabledWrite = true;
4612
+ console.warn(
4613
+ `[attaform] Ignored a write to a disabled form ("${String(formKey)}"). Value writes no-op while \`disabled\` resolves truthy. This warning fires once.`
4614
+ );
4615
+ }
4616
+ return false;
4617
+ }
4567
4618
  if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4568
4619
  for (const idx of freshElementIndices(meta.arrayOp)) {
4569
4620
  value[idx] = stripSymbolsDeep(value[idx]);
@@ -5127,6 +5178,7 @@ function createFormStore(options) {
5127
5178
  }
5128
5179
  }
5129
5180
  function markFocused(path, focused, meta) {
5181
+ if (effectiveDisabled.value) return;
5130
5182
  const { key } = devtoolsShared.canonicalizePath(path);
5131
5183
  const current = fields.get(key);
5132
5184
  touchFieldRecord(key, path, {
@@ -5174,6 +5226,7 @@ function createFormStore(options) {
5174
5226
  }
5175
5227
  }
5176
5228
  function markInteracted(path) {
5229
+ if (effectiveDisabled.value) return;
5177
5230
  const { key } = devtoolsShared.canonicalizePath(path);
5178
5231
  if (fields.get(key)?.interacted === true) return;
5179
5232
  touchFieldRecord(key, path, { interacted: true });
@@ -5490,6 +5543,8 @@ function createFormStore(options) {
5490
5543
  submitted,
5491
5544
  submitError,
5492
5545
  departAttempts,
5546
+ effectiveDisabled,
5547
+ externalLock,
5493
5548
  hydrating,
5494
5549
  hydrateError,
5495
5550
  defaultValuesFactory,
@@ -5877,6 +5932,7 @@ function mergeWithDefaults(defaults, configuration) {
5877
5932
  const onInvalidSubmit = configuration.onInvalidSubmit ?? defaults.onInvalidSubmit;
5878
5933
  const history = configuration.history ?? defaults.history;
5879
5934
  const rememberVariants = configuration.rememberVariants ?? defaults.rememberVariants;
5935
+ const disabled = configuration.disabled ?? defaults.disabled;
5880
5936
  const coerce = configuration.coerce ?? defaults.coerce;
5881
5937
  const validateOn = configuration.validateOn ?? defaults.validateOn;
5882
5938
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
@@ -5889,6 +5945,7 @@ function mergeWithDefaults(defaults, configuration) {
5889
5945
  ...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
5890
5946
  ...history === void 0 ? {} : { history },
5891
5947
  ...rememberVariants === void 0 ? {} : { rememberVariants },
5948
+ ...disabled === void 0 ? {} : { disabled },
5892
5949
  ...coerce === void 0 ? {} : { coerce },
5893
5950
  ...validateOn === void 0 ? {} : { validateOn },
5894
5951
  ...debounceMs === void 0 ? {} : { debounceMs },
@@ -5934,6 +5991,7 @@ function buildFreshState(key, schema, configuration, registry) {
5934
5991
  }
5935
5992
  } : {},
5936
5993
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
5994
+ ...configuration.disabled !== void 0 ? { disabled: configuration.disabled } : {},
5937
5995
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
5938
5996
  ...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
5939
5997
  ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
@@ -6158,6 +6216,14 @@ function isLazyMarker(value) {
6158
6216
  return typeof value === "object" && value !== null && LAZY_BRAND in value;
6159
6217
  }
6160
6218
 
6219
+ const GATE_BRAND = /* @__PURE__ */ Symbol.for("attaform/wizard-gate");
6220
+ function gate(step) {
6221
+ return { [GATE_BRAND]: true, inner: step };
6222
+ }
6223
+ function isGateMarker(value) {
6224
+ return typeof value === "object" && value !== null && GATE_BRAND in value;
6225
+ }
6226
+
6161
6227
  const NOOP_WIZARD_HISTORY = {
6162
6228
  push() {
6163
6229
  },
@@ -6281,14 +6347,17 @@ const PENDING_STATUS = {
6281
6347
  valid: false,
6282
6348
  dirty: false,
6283
6349
  submitted: false,
6284
- errorCount: 0
6350
+ errorCount: 0,
6351
+ locked: false
6285
6352
  };
6286
6353
  const NOOP_VALID_STATUS = {
6287
6354
  valid: true,
6288
6355
  dirty: false,
6289
6356
  submitted: false,
6290
- errorCount: 0
6357
+ errorCount: 0,
6358
+ locked: false
6291
6359
  };
6360
+ const EMPTY_LOCK_SET = /* @__PURE__ */ new Set();
6292
6361
  function asStatusSource(form) {
6293
6362
  return form;
6294
6363
  }
@@ -6355,7 +6424,7 @@ function useWizard(options) {
6355
6424
  }
6356
6425
  }
6357
6426
  const lazyEpoch = vue.ref(0);
6358
- const lazyComputeds = /* @__PURE__ */ new Map();
6427
+ const lazyComputeds = /* @__PURE__ */ new WeakMap();
6359
6428
  const activeKey = vue.ref("");
6360
6429
  const formsAccumulator = /* @__PURE__ */ new Map();
6361
6430
  for (const slot of rawSteps) {
@@ -6391,50 +6460,44 @@ function useWizard(options) {
6391
6460
  return activeKey.value === "" ? void 0 : activeKey.value;
6392
6461
  }
6393
6462
  };
6394
- function resolveSlot(slot, index, ctx) {
6395
- if (slot === void 0 || slot === null) return void 0;
6396
- if (typeof slot === "string") {
6397
- return getOrBuildNoop(slot);
6398
- }
6399
- if (isLazyMarker(slot)) {
6400
- const c = lazyComputeds.get(index);
6401
- 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 = vue.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 (devtoolsShared.__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;
6402
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);
6403
6487
  if (typeof slot === "function") {
6404
6488
  const result = slot(ctx);
6405
- return resolveSlotResult(result);
6489
+ return normalizeSlot(result, ctx, gated, depth + 1);
6406
6490
  }
6407
- if (isAnyForm(slot)) return slot;
6491
+ if (isAnyForm(slot)) return { form: slot, gated };
6408
6492
  return void 0;
6409
6493
  }
6410
- function resolveSlotResult(result) {
6411
- if (result === void 0 || result === null) return void 0;
6412
- if (typeof result === "string") {
6413
- return getOrBuildNoop(result);
6414
- }
6415
- return result;
6416
- }
6417
- for (let i = 0; i < rawSteps.length; i++) {
6418
- const slot = rawSteps[i];
6419
- if (isLazyMarker(slot)) {
6420
- const idx = i;
6421
- const marker = slot;
6422
- lazyComputeds.set(
6423
- idx,
6424
- vue.computed(() => {
6425
- void lazyEpoch.value;
6426
- return marker.resolve(slotCtx);
6427
- })
6428
- );
6429
- }
6430
- }
6431
6494
  const compiledSteps = vue.computed(() => {
6432
6495
  const out = [];
6433
6496
  const seen = /* @__PURE__ */ new Set();
6434
6497
  for (let i = 0; i < rawSteps.length; i++) {
6435
- const slot = rawSteps[i];
6436
- const form = resolveSlot(slot, i, slotCtx);
6437
- 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;
6438
6501
  if (seen.has(form.key)) {
6439
6502
  if (devtoolsShared.__DEV__) {
6440
6503
  console.warn(
@@ -6445,7 +6508,7 @@ function useWizard(options) {
6445
6508
  }
6446
6509
  seen.add(form.key);
6447
6510
  trackOnce(form);
6448
- out.push({ key: form.key, form });
6511
+ out.push({ key: form.key, form, isGate: gated });
6449
6512
  }
6450
6513
  return out;
6451
6514
  });
@@ -6481,12 +6544,12 @@ function useWizard(options) {
6481
6544
  if (key === "") return;
6482
6545
  if (list.some((step) => step.key === key)) return;
6483
6546
  if (list.length === 0) {
6484
- activeKey.value = "";
6547
+ commitActiveKey("");
6485
6548
  return;
6486
6549
  }
6487
6550
  const oldIndex = prevList.findIndex((step) => step.key === key);
6488
6551
  const slid = list[oldIndex < 0 ? 0 : Math.min(oldIndex, list.length - 1)];
6489
- if (slid !== void 0) activeKey.value = slid.key;
6552
+ if (slid !== void 0) commitActiveKey(slid.key);
6490
6553
  });
6491
6554
  const activeFormFacade = new Proxy({}, {
6492
6555
  get(_target, prop) {
@@ -6620,6 +6683,136 @@ function useWizard(options) {
6620
6683
  };
6621
6684
  }
6622
6685
  });
6686
+ const mightGate = rawSteps.some(
6687
+ (slot) => isGateMarker(slot) || isLazyMarker(slot) || typeof slot === "function"
6688
+ );
6689
+ const clearedGates = vue.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 = vue.computed(
6734
+ () => compiledSteps.value.filter((step) => step.isGate).map((step) => step.key)
6735
+ );
6736
+ const navLockSet = vue.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 = vue.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
+ vue.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
+ vue.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 (vue.getCurrentScope() !== void 0) {
6806
+ vue.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
+ }
6623
6816
  const seedRef = vue.ref(void 0);
6624
6817
  const seedInput = options.defaultStatuses;
6625
6818
  if (seedInput !== void 0) {
@@ -6643,6 +6836,7 @@ function useWizard(options) {
6643
6836
  if (cached !== void 0) return cached;
6644
6837
  const source = asStatusSource(form);
6645
6838
  const computedStatus = vue.computed(() => {
6839
+ const locked = navLockSet.value.has(form.key);
6646
6840
  if (isFormReady(form.key)) {
6647
6841
  const meta = source.meta;
6648
6842
  if (meta !== void 0 && meta !== null) {
@@ -6650,16 +6844,17 @@ function useWizard(options) {
6650
6844
  valid: meta.valid,
6651
6845
  dirty: meta.dirty,
6652
6846
  submitted: meta.submitted,
6653
- errorCount: meta.errorCount
6847
+ errorCount: meta.errorCount,
6848
+ locked
6654
6849
  };
6655
6850
  }
6656
6851
  }
6657
- if (noopForms.has(form.key)) return NOOP_VALID_STATUS;
6658
- const seedMap = seedRef.value;
6659
- if (seedMap !== void 0 && Object.hasOwn(seedMap, form.key)) {
6660
- return seedMap[form.key];
6852
+ if (noopForms.has(form.key)) {
6853
+ return locked ? { ...NOOP_VALID_STATUS, locked: true } : NOOP_VALID_STATUS;
6661
6854
  }
6662
- return PENDING_STATUS;
6855
+ const seed = seedRef.value?.[form.key];
6856
+ if (seed !== void 0) return { ...seed, locked };
6857
+ return locked ? { ...PENDING_STATUS, locked: true } : PENDING_STATUS;
6663
6858
  });
6664
6859
  statusCache.set(form.key, computedStatus);
6665
6860
  return computedStatus;
@@ -6775,8 +6970,8 @@ function useWizard(options) {
6775
6970
  initialKey = firstKey();
6776
6971
  }
6777
6972
  if (initialKey !== void 0) {
6778
- activeKey.value = initialKey;
6779
- visited.value = [initialKey];
6973
+ const landing = commitActiveKey(initialKey);
6974
+ visited.value = [landing];
6780
6975
  }
6781
6976
  if (registry.ssr) {
6782
6977
  for (const step of compiledSteps.value) {
@@ -6808,8 +7003,8 @@ function useWizard(options) {
6808
7003
  return;
6809
7004
  }
6810
7005
  if (step === activeKey.value) return;
6811
- activeKey.value = step;
6812
- if (!visited.value.includes(step)) visited.value.push(step);
7006
+ const landing = commitActiveKey(step);
7007
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6813
7008
  }
6814
7009
  );
6815
7010
  }
@@ -6841,11 +7036,11 @@ function useWizard(options) {
6841
7036
  }
6842
7037
  function moveTo(key, options2) {
6843
7038
  if (activeKey.value === key) return;
6844
- activeKey.value = key;
6845
- if (!visited.value.includes(key)) visited.value.push(key);
7039
+ const landing = commitActiveKey(key);
7040
+ if (!visited.value.includes(landing)) visited.value.push(landing);
6846
7041
  const list = compiledSteps.value;
6847
7042
  for (const step of list) {
6848
- if (step.key === key) {
7043
+ if (step.key === landing) {
6849
7044
  activateForm(step.form);
6850
7045
  return;
6851
7046
  }
@@ -6855,6 +7050,14 @@ function useWizard(options) {
6855
7050
  const store = registry.forms.get(key);
6856
7051
  if (store !== void 0) store.departAttempts.value += 1;
6857
7052
  }
7053
+ function advanceOne() {
7054
+ const list = compiledSteps.value;
7055
+ const idx = activeIndex.value;
7056
+ if (idx < 0 || idx >= list.length - 1) return;
7057
+ recordDeparture(activeKey.value);
7058
+ const target = list[idx + 1];
7059
+ moveTo(target.key);
7060
+ }
6858
7061
  async function next() {
6859
7062
  if (submitting.value) {
6860
7063
  if (devtoolsShared.__DEV__) {
@@ -6880,9 +7083,12 @@ function useWizard(options) {
6880
7083
  }
6881
7084
  return;
6882
7085
  }
6883
- recordDeparture(activeKey.value);
6884
- const target = list[idx + 1];
6885
- moveTo(target.key);
7086
+ const active = list[idx];
7087
+ if (active !== void 0 && active.isGate) {
7088
+ await tryNext();
7089
+ return;
7090
+ }
7091
+ advanceOne();
6886
7092
  }
6887
7093
  async function tryNext() {
6888
7094
  if (submitting.value) {
@@ -6909,13 +7115,13 @@ function useWizard(options) {
6909
7115
  }
6910
7116
  const form = activeForm.value;
6911
7117
  if (form === void 0) return false;
6912
- let advanced = false;
7118
+ const before = activeKey.value;
7119
+ let ranClean = false;
6913
7120
  await asHandleSubmitSource(form).handleSubmit(() => {
6914
- const before = activeKey.value;
6915
- next();
6916
- advanced = activeKey.value !== before;
7121
+ ranClean = true;
6917
7122
  })();
6918
- return advanced;
7123
+ if (ranClean) advanceOne();
7124
+ return activeKey.value !== before;
6919
7125
  }
6920
7126
  function back() {
6921
7127
  if (submitting.value) {
@@ -6955,6 +7161,11 @@ function useWizard(options) {
6955
7161
  }
6956
7162
  return;
6957
7163
  }
7164
+ if (devtoolsShared.__DEV__ && navLockSet.value.has(key)) {
7165
+ console.warn(
7166
+ `[attaform] wizard.goTo("${key}"): that step sits behind an uncleared gate(); the navigation was refused. Clear the gate before navigating here.`
7167
+ );
7168
+ }
6958
7169
  if (key !== activeKey.value) recordDeparture(activeKey.value);
6959
7170
  moveTo(key);
6960
7171
  }
@@ -7014,6 +7225,20 @@ function useWizard(options) {
7014
7225
  }
7015
7226
  return out;
7016
7227
  }
7228
+ function collectUnclearedGateErrors() {
7229
+ const out = [];
7230
+ for (const step of compiledSteps.value) {
7231
+ if (step.isGate && !clearedGates.has(step.key)) {
7232
+ out.push({
7233
+ formKey: step.key,
7234
+ path: [],
7235
+ message: `Step "${step.key}" is a gate that has not been cleared. Submit it to continue.`,
7236
+ code: AttaformErrorCode.GateNotCleared
7237
+ });
7238
+ }
7239
+ }
7240
+ return out;
7241
+ }
7017
7242
  async function focusFirstWizardError(errors) {
7018
7243
  if (options.focusFirstError === false) return;
7019
7244
  const firstFailedKey = errors[0]?.formKey;
@@ -7070,7 +7295,9 @@ function useWizard(options) {
7070
7295
  }
7071
7296
  submissionAttempts.value += 1;
7072
7297
  const errors = collectErrors(results);
7073
- if (errors.length === 0) {
7298
+ const gateErrors = mightGate ? collectUnclearedGateErrors() : [];
7299
+ const blocking = gateErrors.length > 0 ? [...errors, ...gateErrors] : errors;
7300
+ if (blocking.length === 0) {
7074
7301
  const valuesMap = {};
7075
7302
  for (const step of list) {
7076
7303
  const processed = results.get(step.key);
@@ -7096,10 +7323,10 @@ function useWizard(options) {
7096
7323
  }
7097
7324
  done.value = true;
7098
7325
  } else {
7099
- await focusFirstWizardError(errors);
7326
+ await focusFirstWizardError(blocking);
7100
7327
  if (onError !== void 0) {
7101
7328
  try {
7102
- await onError(errors);
7329
+ await onError(blocking);
7103
7330
  } catch (cause) {
7104
7331
  throw new devtoolsShared.SubmitErrorHandlerError("User-provided onError threw", { cause });
7105
7332
  }
@@ -7116,21 +7343,35 @@ function useWizard(options) {
7116
7343
  submissionAttempts.value = 0;
7117
7344
  done.value = false;
7118
7345
  submitError.value = null;
7346
+ if (mightGate) {
7347
+ clearedGates.clear();
7348
+ seededSampled.clear();
7349
+ }
7119
7350
  lazyEpoch.value += 1;
7120
7351
  for (const step of compiledSteps.value) {
7121
7352
  const full = asSubmissionSource(step.form);
7122
7353
  if (typeof full.reset === "function") full.reset();
7123
7354
  }
7355
+ if (mightGate) reconcileGates();
7124
7356
  const firstStep = compiledSteps.value[0];
7125
7357
  if (firstStep !== void 0) {
7126
- activeKey.value = firstStep.key;
7127
- visited.value = [firstStep.key];
7358
+ const landing = commitActiveKey(firstStep.key);
7359
+ visited.value = [landing];
7128
7360
  if (persistCallback !== void 0) {
7129
- lastPersisted = firstStep.key;
7130
- persistCallback({ step: firstStep.key });
7361
+ lastPersisted = landing;
7362
+ persistCallback({ step: landing });
7131
7363
  }
7132
7364
  }
7133
7365
  }
7366
+ if (mightGate) {
7367
+ vue.watchEffect(() => {
7368
+ const key = activeKey.value;
7369
+ if (key === "" || !navLockSet.value.has(key)) return;
7370
+ const redirect = lastReachableKey();
7371
+ if (redirect === void 0 || redirect === key || !isFormReady(redirect)) return;
7372
+ moveTo(redirect);
7373
+ });
7374
+ }
7134
7375
  if (vue.getCurrentScope() !== void 0) {
7135
7376
  vue.onScopeDispose(() => {
7136
7377
  historyHandle.dispose();
@@ -7332,6 +7573,7 @@ exports.defaultCoercionRules = defaultCoercionRules;
7332
7573
  exports.defaultDisplayState = defaultDisplayState;
7333
7574
  exports.defineCoercion = defineCoercion;
7334
7575
  exports.escapeForInlineScript = escapeForInlineScript;
7576
+ exports.gate = gate;
7335
7577
  exports.getAtPath = getAtPath;
7336
7578
  exports.humanize = humanize;
7337
7579
  exports.injectForm = injectForm;
@@ -7348,4 +7590,4 @@ exports.unset = unset;
7348
7590
  exports.useAbstractForm = useAbstractForm;
7349
7591
  exports.useRegister = useRegister;
7350
7592
  exports.useWizard = useWizard;
7351
- //# sourceMappingURL=attaform.DQT7_-FO.cjs.map
7593
+ //# sourceMappingURL=attaform.CXHebpij.cjs.map