attaform 0.27.2 → 0.27.4
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.
- package/dist/abstract.cjs +1 -1
- package/dist/abstract.d.cts +2 -2
- package/dist/abstract.d.mts +2 -2
- package/dist/abstract.d.ts +2 -2
- package/dist/abstract.mjs +1 -1
- package/dist/chunks/fingerprint2.cjs +1 -1
- package/dist/chunks/fingerprint2.mjs +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/shared/{attaform.BF1mYHru.cjs → attaform.B3spfqIA.cjs} +3 -3
- package/dist/shared/{attaform.BF1mYHru.cjs.map → attaform.B3spfqIA.cjs.map} +1 -1
- package/dist/shared/{attaform.gyed1fGh.mjs → attaform.BrVzB14N.mjs} +3 -3
- package/dist/shared/{attaform.gyed1fGh.mjs.map → attaform.BrVzB14N.mjs.map} +1 -1
- package/dist/shared/{attaform.C1SJy524.cjs → attaform.C9arOC5p.cjs} +4 -4
- package/dist/shared/{attaform.C1SJy524.cjs.map → attaform.C9arOC5p.cjs.map} +1 -1
- package/dist/shared/{attaform.6IZBjA1Q.mjs → attaform.CC4KCx_q.mjs} +4 -4
- package/dist/shared/{attaform.6IZBjA1Q.mjs.map → attaform.CC4KCx_q.mjs.map} +1 -1
- package/dist/shared/{attaform.CIPwCQe8.cjs → attaform.CX2NHAFs.cjs} +2 -2
- package/dist/shared/{attaform.CIPwCQe8.cjs.map → attaform.CX2NHAFs.cjs.map} +1 -1
- package/dist/shared/{attaform.DIJTp_Kv.d.cts → attaform.Cjh6b6xU.d.cts} +64 -16
- package/dist/shared/{attaform.DGCijpZT.mjs → attaform.CuUx4jWI.mjs} +53 -30
- package/dist/shared/{attaform.CXHebpij.cjs.map → attaform.CuUx4jWI.mjs.map} +1 -1
- package/dist/shared/{attaform.B2d0FszH.d.ts → attaform.DKtXMaJB.d.ts} +64 -16
- package/dist/shared/{attaform.CvaPYOZu.cjs → attaform.DXwJVUNS.cjs} +3 -3
- package/dist/shared/{attaform.CvaPYOZu.cjs.map → attaform.DXwJVUNS.cjs.map} +1 -1
- package/dist/shared/{attaform.CXHebpij.cjs → attaform.Ib2seKUE.cjs} +53 -30
- package/dist/shared/{attaform.DGCijpZT.mjs.map → attaform.Ib2seKUE.cjs.map} +1 -1
- package/dist/shared/{attaform.BAlfXoAx.mjs → attaform.a1GnhjVk.mjs} +2 -2
- package/dist/shared/{attaform.BAlfXoAx.mjs.map → attaform.a1GnhjVk.mjs.map} +1 -1
- package/dist/shared/{attaform.Dcj-OS43.d.mts → attaform.gj8dtOlx.d.mts} +64 -16
- package/dist/shared/{attaform.yOE3udL1.mjs → attaform.shkNCMEz.mjs} +3 -3
- package/dist/shared/{attaform.yOE3udL1.mjs.map → attaform.shkNCMEz.mjs.map} +1 -1
- package/dist/zod-v3.cjs +2 -2
- package/dist/zod-v3.d.cts +1 -1
- package/dist/zod-v3.d.mts +1 -1
- package/dist/zod-v3.d.ts +1 -1
- package/dist/zod-v3.mjs +2 -2
- package/dist/zod-v4.cjs +2 -2
- package/dist/zod-v4.d.cts +2 -2
- package/dist/zod-v4.d.mts +2 -2
- package/dist/zod-v4.d.ts +2 -2
- package/dist/zod-v4.mjs +2 -2
- package/dist/zod.cjs +2 -2
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.mts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.mjs +2 -2
- package/package.json +1 -1
|
@@ -1026,13 +1026,24 @@ type AnyForm = {
|
|
|
1026
1026
|
* `submissionAttempts > 0` is the "user has tried" signal.
|
|
1027
1027
|
* - `errorCount` — `form.meta.errorCount`. Count of active validation
|
|
1028
1028
|
* errors (zero when valid).
|
|
1029
|
-
* - `locked`: `true` when this step sits after an uncleared
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1029
|
+
* - `locked`: `true` when this step sits after an uncleared gate. Its
|
|
1030
|
+
* member form is frozen (data writes no-op) and navigation to it is
|
|
1031
|
+
* refused, so a stepper can render it as an unreachable step. `false`
|
|
1032
|
+
* when no gate seals it. Orthogonal to `gate`: the first uncleared
|
|
1033
|
+
* gate reads `locked: false` (you must reach it to clear it), while a
|
|
1034
|
+
* later gate sealed behind it reads both `gate: 'uncleared'` and
|
|
1035
|
+
* `locked: true`.
|
|
1036
|
+
* - `gate`: this step's own role as a hard prerequisite. `null` when the
|
|
1037
|
+
* step is not a `gate()`; `'uncleared'` while its member form is
|
|
1038
|
+
* unconfirmed (so it seals every later step); `'cleared'` once
|
|
1039
|
+
* confirmed by a clean member submit or seeded cleared via
|
|
1040
|
+
* `defaultStatuses`. Reflects the live compiled shape, so a conditional
|
|
1041
|
+
* `gate()` that a function slot drops resolves to `null`.
|
|
1033
1042
|
*
|
|
1034
1043
|
* Noop forms generated for string slots surface as default-valid
|
|
1035
|
-
* (`{ valid: true, dirty: false, submitted: false, errorCount: 0, locked: false }`)
|
|
1044
|
+
* (`{ valid: true, dirty: false, submitted: false, errorCount: 0, locked: false, gate: null }`),
|
|
1045
|
+
* unless the string slot is a `gate('...')`, whose `gate` tracks the
|
|
1046
|
+
* in-session acknowledgment.
|
|
1036
1047
|
*/
|
|
1037
1048
|
type FormStatus = {
|
|
1038
1049
|
readonly valid: boolean;
|
|
@@ -1040,14 +1051,30 @@ type FormStatus = {
|
|
|
1040
1051
|
readonly submitted: boolean;
|
|
1041
1052
|
readonly errorCount: number;
|
|
1042
1053
|
readonly locked: boolean;
|
|
1054
|
+
readonly gate: 'cleared' | 'uncleared' | null;
|
|
1043
1055
|
};
|
|
1044
1056
|
/**
|
|
1045
|
-
* Seed shape accepted by `useWizard({ defaultStatuses })`.
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
|
|
1050
|
-
|
|
1057
|
+
* Seed shape accepted by `useWizard({ defaultStatuses })`. Every field is
|
|
1058
|
+
* optional; an omitted meta field falls back to the pending sentinel on
|
|
1059
|
+
* read.
|
|
1060
|
+
*
|
|
1061
|
+
* `locked` is never accepted: it is purely live-derived (sealed behind an
|
|
1062
|
+
* earlier uncleared gate) and overlaid on every status read, so a seeded
|
|
1063
|
+
* value would be ignored.
|
|
1064
|
+
*
|
|
1065
|
+
* `gate` is a WRITE-only seed, not a status field echoed back on read. A
|
|
1066
|
+
* seed of `'cleared'` latches the gate's cleared state once at construction
|
|
1067
|
+
* (an SSR restore of a confirmed prerequisite); the live `gate` overlay
|
|
1068
|
+
* then owns the read. `'uncleared'` is the default and seeds nothing. Only
|
|
1069
|
+
* a step that compiles to a `gate()` honors it — other keys ignore it.
|
|
1070
|
+
*/
|
|
1071
|
+
type FormStatusSeed = {
|
|
1072
|
+
readonly valid?: boolean;
|
|
1073
|
+
readonly dirty?: boolean;
|
|
1074
|
+
readonly submitted?: boolean;
|
|
1075
|
+
readonly errorCount?: number;
|
|
1076
|
+
readonly gate?: 'cleared' | 'uncleared';
|
|
1077
|
+
};
|
|
1051
1078
|
/**
|
|
1052
1079
|
* Flat error shape returned per form by `wizard.allErrors[key]`. Each
|
|
1053
1080
|
* entry carries the formKey + path tuple so consumers can route to the
|
|
@@ -1302,12 +1329,20 @@ type WizardOptions = {
|
|
|
1302
1329
|
* Status resolution priority per form:
|
|
1303
1330
|
* 1. `store.defaultsResolved === true` → derive from `form.meta`
|
|
1304
1331
|
* 2. else noop form → built-in always-valid status
|
|
1305
|
-
* 3. else seed value for this key →
|
|
1332
|
+
* 3. else seed value for this key → seed over the pending sentinel
|
|
1306
1333
|
* 4. else → pending sentinel
|
|
1307
1334
|
*
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1335
|
+
* `locked` is never seeded: it is computed live from the wizard's gates
|
|
1336
|
+
* and overlaid on every status read. `gate: 'cleared'` IS honored, but as
|
|
1337
|
+
* a one-shot seed of the gate's cleared latch at construction (an SSR
|
|
1338
|
+
* restore of a confirmed prerequisite), not as a status field echoed on
|
|
1339
|
+
* read — the live `gate` overlay owns the read. Only a step that compiles
|
|
1340
|
+
* to a `gate()` honors it. See `FormStatusSeed`.
|
|
1341
|
+
*
|
|
1342
|
+
* SSR note: the async factory resolves after hydration (client-only), so
|
|
1343
|
+
* a gate seeded through it clears post-hydration. For a gate that must
|
|
1344
|
+
* render open on the first server byte, use the plain or sync-factory
|
|
1345
|
+
* form (resolve server truth in the page's `setup` await).
|
|
1311
1346
|
*
|
|
1312
1347
|
* Unknown keys in the seed object dev-warn so a stale resume payload
|
|
1313
1348
|
* surfaces at construction.
|
|
@@ -1547,7 +1582,19 @@ type WizardForms<S> = FormsRecordOf<S> & Readonly<Record<FormKey, AnyForm>>;
|
|
|
1547
1582
|
* - `reset()` — zeros wizard lifecycle (`submissionAttempts`,
|
|
1548
1583
|
* `visited`), resets every form, returns
|
|
1549
1584
|
* `currentStep` to `steps[0].key`, and invokes
|
|
1550
|
-
* `persist` with the cleared state.
|
|
1585
|
+
* `persist` with the cleared state. Re-applies the
|
|
1586
|
+
* `defaultStatuses` gate seed, so a reboot returns to
|
|
1587
|
+
* the seeded initial clearance, not fully sealed.
|
|
1588
|
+
* - `relock(key, commit)` — re-seal a cleared gate, contingent on
|
|
1589
|
+
* `commit`. Awaits `commit` (a server-side revoke) and
|
|
1590
|
+
* re-seals only if it resolves clean, so the gate reflects
|
|
1591
|
+
* server-confirmed truth the same way a clearing submit
|
|
1592
|
+
* does; a thrown `commit` leaves it as-is. Seal-only (never
|
|
1593
|
+
* opens a gate), so it cannot become the leading-signal
|
|
1594
|
+
* foot-gun `gate()` exists to prevent. `commit` is required
|
|
1595
|
+
* (pass `() => {}` for a deliberate client-only re-seal).
|
|
1596
|
+
* Resolves whether the gate ended up sealed; never rejects.
|
|
1597
|
+
* Resolves `false` (dev-warn) on a non-gate key.
|
|
1551
1598
|
*/
|
|
1552
1599
|
type UseWizardReturnType<S extends ReadonlyArray<StepSlot> = ReadonlyArray<StepSlot>> = {
|
|
1553
1600
|
readonly key: string;
|
|
@@ -1576,6 +1623,7 @@ type UseWizardReturnType<S extends ReadonlyArray<StepSlot> = ReadonlyArray<StepS
|
|
|
1576
1623
|
readonly tryNext: () => Promise<boolean>;
|
|
1577
1624
|
readonly handleSubmit: (onSubmit: WizardOnSubmit, onError?: WizardOnError) => (event?: Event) => Promise<void>;
|
|
1578
1625
|
readonly reset: () => void;
|
|
1626
|
+
readonly relock: (key: FormKey, commit: () => void | Promise<void>) => Promise<boolean>;
|
|
1579
1627
|
};
|
|
1580
1628
|
|
|
1581
1629
|
/**
|
|
@@ -6346,14 +6346,16 @@ const PENDING_STATUS = {
|
|
|
6346
6346
|
dirty: false,
|
|
6347
6347
|
submitted: false,
|
|
6348
6348
|
errorCount: 0,
|
|
6349
|
-
locked: false
|
|
6349
|
+
locked: false,
|
|
6350
|
+
gate: null
|
|
6350
6351
|
};
|
|
6351
6352
|
const NOOP_VALID_STATUS = {
|
|
6352
6353
|
valid: true,
|
|
6353
6354
|
dirty: false,
|
|
6354
6355
|
submitted: false,
|
|
6355
6356
|
errorCount: 0,
|
|
6356
|
-
locked: false
|
|
6357
|
+
locked: false,
|
|
6358
|
+
gate: null
|
|
6357
6359
|
};
|
|
6358
6360
|
const EMPTY_LOCK_SET = /* @__PURE__ */ new Set();
|
|
6359
6361
|
function asStatusSource(form) {
|
|
@@ -6685,24 +6687,6 @@ function useWizard(options) {
|
|
|
6685
6687
|
(slot) => isGateMarker(slot) || isLazyMarker(slot) || typeof slot === "function"
|
|
6686
6688
|
);
|
|
6687
6689
|
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
6690
|
const gateSubs = /* @__PURE__ */ new Map();
|
|
6707
6691
|
function reconcileGates() {
|
|
6708
6692
|
const gateKeys = /* @__PURE__ */ new Set();
|
|
@@ -6719,7 +6703,6 @@ function useWizard(options) {
|
|
|
6719
6703
|
);
|
|
6720
6704
|
}
|
|
6721
6705
|
}
|
|
6722
|
-
sampleSeededGate(key);
|
|
6723
6706
|
}
|
|
6724
6707
|
for (const [key, unsub] of gateSubs) {
|
|
6725
6708
|
if (!gateKeys.has(key)) {
|
|
@@ -6777,8 +6760,7 @@ function useWizard(options) {
|
|
|
6777
6760
|
for (const step of compiledSteps.value) {
|
|
6778
6761
|
if (!step.isGate) continue;
|
|
6779
6762
|
const store = registry.forms.get(step.key);
|
|
6780
|
-
|
|
6781
|
-
parts.push(`${step.key}:${store !== void 0 ? 1 : 0}:${settled ? 1 : 0}`);
|
|
6763
|
+
parts.push(`${step.key}:${store !== void 0 ? 1 : 0}`);
|
|
6782
6764
|
}
|
|
6783
6765
|
return parts.join("|");
|
|
6784
6766
|
},
|
|
@@ -6811,20 +6793,30 @@ function useWizard(options) {
|
|
|
6811
6793
|
});
|
|
6812
6794
|
}
|
|
6813
6795
|
}
|
|
6796
|
+
function applyGateSeed(map) {
|
|
6797
|
+
if (map === void 0 || !mightGate) return;
|
|
6798
|
+
const gates = gatePositions.value;
|
|
6799
|
+
for (const key of Object.keys(map)) {
|
|
6800
|
+
if (map[key]?.gate === "cleared" && gates.includes(key)) clearedGates.add(key);
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6814
6803
|
const seedRef = ref(void 0);
|
|
6815
6804
|
const seedInput = options.defaultStatuses;
|
|
6816
6805
|
if (seedInput !== void 0) {
|
|
6817
6806
|
const resolved = resolveTrichotomy(seedInput);
|
|
6818
6807
|
if (resolved.kind === "sync") {
|
|
6819
6808
|
seedRef.value = resolved.value;
|
|
6809
|
+
applyGateSeed(resolved.value);
|
|
6820
6810
|
} else {
|
|
6821
6811
|
const eager = resolved.factory();
|
|
6822
6812
|
if (eager instanceof Promise) {
|
|
6823
6813
|
void eager.then((value) => {
|
|
6824
6814
|
seedRef.value = value;
|
|
6815
|
+
applyGateSeed(value);
|
|
6825
6816
|
});
|
|
6826
6817
|
} else {
|
|
6827
6818
|
seedRef.value = eager;
|
|
6819
|
+
applyGateSeed(eager);
|
|
6828
6820
|
}
|
|
6829
6821
|
}
|
|
6830
6822
|
}
|
|
@@ -6835,6 +6827,10 @@ function useWizard(options) {
|
|
|
6835
6827
|
const source = asStatusSource(form);
|
|
6836
6828
|
const computedStatus = computed(() => {
|
|
6837
6829
|
const locked = navLockSet.value.has(form.key);
|
|
6830
|
+
let gate = null;
|
|
6831
|
+
if (gatePositions.value.includes(form.key)) {
|
|
6832
|
+
gate = clearedGates.has(form.key) ? "cleared" : "uncleared";
|
|
6833
|
+
}
|
|
6838
6834
|
if (isFormReady(form.key)) {
|
|
6839
6835
|
const meta = source.meta;
|
|
6840
6836
|
if (meta !== void 0 && meta !== null) {
|
|
@@ -6843,16 +6839,17 @@ function useWizard(options) {
|
|
|
6843
6839
|
dirty: meta.dirty,
|
|
6844
6840
|
submitted: meta.submitted,
|
|
6845
6841
|
errorCount: meta.errorCount,
|
|
6846
|
-
locked
|
|
6842
|
+
locked,
|
|
6843
|
+
gate
|
|
6847
6844
|
};
|
|
6848
6845
|
}
|
|
6849
6846
|
}
|
|
6850
6847
|
if (noopForms.has(form.key)) {
|
|
6851
|
-
return locked ? { ...NOOP_VALID_STATUS, locked
|
|
6848
|
+
return locked || gate !== null ? { ...NOOP_VALID_STATUS, locked, gate } : NOOP_VALID_STATUS;
|
|
6852
6849
|
}
|
|
6853
6850
|
const seed = seedRef.value?.[form.key];
|
|
6854
|
-
if (seed !== void 0) return { ...seed, locked };
|
|
6855
|
-
return locked ? { ...PENDING_STATUS, locked
|
|
6851
|
+
if (seed !== void 0) return { ...PENDING_STATUS, ...seed, locked, gate };
|
|
6852
|
+
return locked || gate !== null ? { ...PENDING_STATUS, locked, gate } : PENDING_STATUS;
|
|
6856
6853
|
});
|
|
6857
6854
|
statusCache.set(form.key, computedStatus);
|
|
6858
6855
|
return computedStatus;
|
|
@@ -7343,14 +7340,16 @@ function useWizard(options) {
|
|
|
7343
7340
|
submitError.value = null;
|
|
7344
7341
|
if (mightGate) {
|
|
7345
7342
|
clearedGates.clear();
|
|
7346
|
-
seededSampled.clear();
|
|
7347
7343
|
}
|
|
7348
7344
|
lazyEpoch.value += 1;
|
|
7349
7345
|
for (const step of compiledSteps.value) {
|
|
7350
7346
|
const full = asSubmissionSource(step.form);
|
|
7351
7347
|
if (typeof full.reset === "function") full.reset();
|
|
7352
7348
|
}
|
|
7353
|
-
if (mightGate)
|
|
7349
|
+
if (mightGate) {
|
|
7350
|
+
reconcileGates();
|
|
7351
|
+
applyGateSeed(seedRef.value);
|
|
7352
|
+
}
|
|
7354
7353
|
const firstStep = compiledSteps.value[0];
|
|
7355
7354
|
if (firstStep !== void 0) {
|
|
7356
7355
|
const landing = commitActiveKey(firstStep.key);
|
|
@@ -7361,6 +7360,29 @@ function useWizard(options) {
|
|
|
7361
7360
|
}
|
|
7362
7361
|
}
|
|
7363
7362
|
}
|
|
7363
|
+
async function relock(key, commit) {
|
|
7364
|
+
if (!mightGate || !gatePositions.value.includes(key)) {
|
|
7365
|
+
if (__DEV__) {
|
|
7366
|
+
console.warn(
|
|
7367
|
+
`[attaform] useWizard.relock(${JSON.stringify(key)}): no gate at that key; nothing to re-lock.`
|
|
7368
|
+
);
|
|
7369
|
+
}
|
|
7370
|
+
return false;
|
|
7371
|
+
}
|
|
7372
|
+
try {
|
|
7373
|
+
await commit();
|
|
7374
|
+
} catch (err) {
|
|
7375
|
+
if (__DEV__) {
|
|
7376
|
+
console.warn(
|
|
7377
|
+
`[attaform] useWizard.relock(${JSON.stringify(key)}): commit threw, so the gate was not re-sealed.`,
|
|
7378
|
+
err
|
|
7379
|
+
);
|
|
7380
|
+
}
|
|
7381
|
+
return false;
|
|
7382
|
+
}
|
|
7383
|
+
clearedGates.delete(key);
|
|
7384
|
+
return true;
|
|
7385
|
+
}
|
|
7364
7386
|
if (mightGate) {
|
|
7365
7387
|
watchEffect(() => {
|
|
7366
7388
|
const key = activeKey.value;
|
|
@@ -7386,6 +7408,7 @@ function useWizard(options) {
|
|
|
7386
7408
|
tryNext,
|
|
7387
7409
|
handleSubmit,
|
|
7388
7410
|
reset,
|
|
7411
|
+
relock,
|
|
7389
7412
|
get currentStep() {
|
|
7390
7413
|
return currentStep.value;
|
|
7391
7414
|
},
|
|
@@ -7566,4 +7589,4 @@ function escapeForInlineScript(json) {
|
|
|
7566
7589
|
}
|
|
7567
7590
|
|
|
7568
7591
|
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.
|
|
7592
|
+
//# sourceMappingURL=attaform.CuUx4jWI.mjs.map
|