@bitstillery/mithril 3.4.0 → 3.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitstillery/mithril",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Mithril + Signals, Store and SSR",
5
5
  "license": "MIT",
6
6
  "author": "Bitstillery",
@@ -100,8 +100,10 @@ export function serializeStore(state: State<any>): any {
100
100
  continue
101
101
  }
102
102
 
103
- // For nested states, only serialize keys that belong to this state (not parent keys)
104
- if (nestedOriginalKeys && !nestedOriginalKeys.has(key)) {
103
+ // For nested states, only serialize keys that belong to this state (not parent keys).
104
+ // When originalKeys is empty (e.g. lookup: {}), include all keys so dynamically
105
+ // added entries (e.g. $s.lookup[path] = data) are serialized.
106
+ if (nestedOriginalKeys && nestedOriginalKeys.size > 0 && !nestedOriginalKeys.has(key)) {
105
107
  continue
106
108
  }
107
109
 
package/store.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import {state, State, updateStateRegistry} from './state'
2
2
  import {serializeStore, deserializeStore} from './render/ssrState'
3
- import {ComputedSignal} from './signal'
4
3
 
5
4
  // Helper function to restore computed properties (same as in ssrState.ts)
6
5
  function restoreComputedProperties(state: State<any>, initial: any): void {
@@ -374,22 +373,26 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
374
373
  return
375
374
  }
376
375
 
377
- // Default to saving saved/tab when no options; session is opt-in (off by default)
378
- const saveSaved = options?.saved ?? options === undefined
379
- const saveTab = options?.tab ?? options === undefined
380
- const saveSession = options?.session === true
376
+ // Default: write to localStorage and sessionStorage when no options; session is opt-in
377
+ const writeLocalStorage = options?.saved ?? options === undefined
378
+ const writeSessionStorage = options?.tab ?? options === undefined
379
+ const writeSessionApi = options?.session === true
381
380
 
382
- // Use SSR serialization which properly handles State objects and skips ComputedSignal properties
383
- // This is the same mechanism used for SSR, ensuring consistency
384
381
  const statePlain = serializeStore(this.stateInstance)
385
382
 
386
- // Save to localStorage (saved state)
387
- if (saveSaved && this.templates.saved) {
383
+ // Write to localStorage (persistent across browser restarts)
384
+ if (writeLocalStorage && this.templates.saved) {
388
385
  this.set(this.storageKey, this.blueprint(statePlain, copy_object(this.templates.saved)))
389
386
  }
390
387
 
391
- // Save to sessionStorage (tab state)
392
- if (saveTab && this.templates.tab) {
388
+ // Lookup is always persisted to localStorage when present, regardless of writeLocalStorage.
389
+ // This ensures cached values (e.g. filter/sort state) are never lost when save() is called.
390
+ if ((statePlain as any).lookup) {
391
+ this.persist_lookup_to_local_storage(statePlain)
392
+ }
393
+
394
+ // Write to sessionStorage (tab-scoped, cleared when tab closes)
395
+ if (writeSessionStorage && this.templates.tab) {
393
396
  const tabState = (this.stateInstance as any).tab
394
397
  if (tabState) {
395
398
  // Get the tab template - unwrap if it's nested under a 'tab' key
@@ -420,7 +423,7 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
420
423
  // Save to session API (session state) - async by nature
421
424
  // Only save session on client side (not during SSR)
422
425
  if (
423
- saveSession &&
426
+ writeSessionApi &&
424
427
  this.templates.session &&
425
428
  Object.keys(this.templates.session).length > 0 &&
426
429
  typeof window !== 'undefined'
@@ -441,6 +444,27 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
441
444
  }
442
445
  }
443
446
 
447
+ /**
448
+ * Merge lookup from state into localStorage and write. Ensures lookup is always persisted
449
+ * whenever save() is called and state contains lookup.
450
+ */
451
+ private persist_lookup_to_local_storage(statePlain: Record<string, any>): void {
452
+ if (typeof window === 'undefined') return
453
+ try {
454
+ const existing = this.get(this.storageKey)
455
+ let storeData: Record<string, any> = {}
456
+ try {
457
+ storeData = JSON.parse(existing) || {}
458
+ } catch {
459
+ storeData = {}
460
+ }
461
+ storeData.lookup = copy_object(statePlain.lookup || {})
462
+ window.localStorage.setItem(this.storageKey, JSON.stringify(storeData))
463
+ } catch (err) {
464
+ console.error('Cannot persist lookup to Local Storage; continue without.', err)
465
+ }
466
+ }
467
+
444
468
  set(key: string, item: object): void {
445
469
  if (typeof window === 'undefined') return
446
470
  try {