mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
fix(migration): normalize legacy theme values under v5 marker
Two related bugs surfaced during end-to-end verification:
1. The 'isAlreadyV5' short-circuit in both the main-side and renderer-side
v4-to-v5 transforms returned the persisted object as-is when a
migration.version=5 marker was present. A previously-shipped v4 build
had written theme: 'ayu-light' (and similar) under the v5 marker;
the transform trusted the marker and passed the invalid theme through,
which then failed the renderer's zod schema on every launch and
reset user settings to defaults.
Fix: in both transforms, when isAlreadyV5 matches, normalize theme
against the v5 enum (light/dark/system) and validate the full result
with the v5 schema before returning. Out-of-range values are replaced
with the v5 default ('system').
2. The renderer's settings-store onRehydrateStorage callback called
useSettingsStore.setState() to reset the store on validation failure.
At that moment the store is still being constructed, and setState
could hit a TDZ ReferenceError (the one we already wrapped in a
try/catch in v5.0.0, which only hid the symptom).
Fix: return the normalized state object from onRehydrateStorage
instead. Zustand's persist middleware applies the returned value
*after* construction completes, so there is no TDZ.
Tests: 334 vitest + 208 jest = 542 passing.
E2E: 12/12 verify-features.mjs steps green; no console errors.
Amit Haridas
This commit is contained in:
@@ -25,19 +25,23 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
const { setSetting, resetToDefaults, ...rest } = state;
|
||||
return rest;
|
||||
},
|
||||
// onRehydrateStorage must NOT call useSettingsStore.setState() — at the
|
||||
// moment the callback runs the store is still being constructed, and
|
||||
// setState can hit a TDZ ReferenceError. Instead, return a normalized
|
||||
// state object from this callback. Zustand's persist middleware will
|
||||
// merge it into the store *after* construction completes.
|
||||
onRehydrateStorage: () => (state) => {
|
||||
if (!state) return;
|
||||
try {
|
||||
const result = settingsSchema.safeParse(state);
|
||||
if (!result.success) {
|
||||
console.warn('[settings-store] invalid persisted state, resetting to defaults', result.error);
|
||||
useSettingsStore.setState({ ...DEFAULTS } as any);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[settings-store] rehydration failed, resetting to defaults', err);
|
||||
useSettingsStore.setState({ ...DEFAULTS } as any);
|
||||
const result = settingsSchema.safeParse(state);
|
||||
if (!result.success) {
|
||||
console.warn(
|
||||
'[settings-store] invalid persisted state, replacing with defaults',
|
||||
result.error.issues.map((i) => i.path.join('.') + ': ' + i.message).join('; '),
|
||||
);
|
||||
return { ...DEFAULTS } as Partial<SettingsState>;
|
||||
}
|
||||
return result.data as Partial<SettingsState>;
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user