@bitstillery/mithril 3.2.0 → 3.3.0

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/api/router.ts CHANGED
@@ -105,7 +105,7 @@ export default function router($window: any, mountRedraw: MountRedraw) {
105
105
  // (e.g. Stock <-> TBO) forces full remount. Key must change when path changes.
106
106
  // Always wrap when we have a path so structure is consistent; key drives remount.
107
107
  const fragmentKey = remountPath ?? currentPath ?? undefined
108
- if (fragmentKey != null) {
108
+ if (remountPath && fragmentKey != null) {
109
109
  return hyperscript.fragment({key: fragmentKey}, result)
110
110
  }
111
111
  return result
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitstillery/mithril",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Mithril + Signals, Store and SSR",
5
5
  "license": "MIT",
6
6
  "author": "Bitstillery",
@@ -56,6 +56,7 @@
56
56
  "test": "bun test",
57
57
  "prepublishOnly": "bun run lint:ts",
58
58
  "pack:dry-run": "npm pack --dry-run",
59
+ "publish": "./scripts/release.sh",
59
60
  "release": "./scripts/release.sh",
60
61
  "release:patch": "./scripts/release.sh --release-as patch",
61
62
  "release:minor": "./scripts/release.sh --release-as minor",
package/signal.ts CHANGED
@@ -100,32 +100,40 @@ export class Signal<T> {
100
100
  set value(newValue: T) {
101
101
  if (this._value !== newValue) {
102
102
  this._value = newValue
103
- // Ensure _subscribers is initialized (defensive check)
104
- if (!this._subscribers) {
105
- this._subscribers = new Set()
106
- }
107
- // Notify all subscribers
108
- const context = getSSRContext()
109
- this._subscribers.forEach((fn) => {
110
- try {
111
- // Always run watchers - wrap in SSR context if available
112
- if (context) {
113
- // Run watcher inside SSR context, similar to events
114
- runWithContext(context, () => {
115
- fn()
116
- })
117
- } else {
103
+ this.trigger()
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Notify subscribers and trigger redraws without changing the value.
109
+ * Use when the value is an object/array that was mutated in place (e.g. keys added/removed).
110
+ */
111
+ trigger(): void {
112
+ // Ensure _subscribers is initialized (defensive check)
113
+ if (!this._subscribers) {
114
+ this._subscribers = new Set()
115
+ }
116
+ // Notify all subscribers
117
+ const context = getSSRContext()
118
+ this._subscribers.forEach((fn) => {
119
+ try {
120
+ // Always run watchers - wrap in SSR context if available
121
+ if (context) {
122
+ // Run watcher inside SSR context, similar to events
123
+ runWithContext(context, () => {
118
124
  fn()
119
- }
120
- } catch (e) {
121
- console.error('Error in signal subscriber:', e)
125
+ })
126
+ } else {
127
+ fn()
122
128
  }
123
- })
124
- // Trigger component redraws for affected components
125
- // This is set up in index.ts after m.redraw is created
126
- if ((signal as any).__redrawCallback) {
127
- ;(signal as any).__redrawCallback(this)
129
+ } catch (e) {
130
+ console.error('Error in signal subscriber:', e)
128
131
  }
132
+ })
133
+ // Trigger component redraws for affected components
134
+ // This is set up in index.ts after m.redraw is created
135
+ if ((signal as any).__redrawCallback) {
136
+ ;(signal as any).__redrawCallback(this)
129
137
  }
130
138
  }
131
139
 
package/state.ts CHANGED
@@ -211,6 +211,9 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
211
211
  ;(value as any)._parentSignal = sig as Signal<any>
212
212
  } else if (Array.isArray(value)) {
213
213
  arrayParentSignalMap.set(value, sig as Signal<any>)
214
+ } else if ((value as any).__isState === true) {
215
+ // Nested object proxies: notify parent when keys are added/removed
216
+ arrayParentSignalMap.set(value, sig as Signal<any>)
214
217
  }
215
218
  }
216
219
 
@@ -738,8 +741,13 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
738
741
  nestedSignalMap.set(key, createPropertySignal(value))
739
742
  }
740
743
  } else {
741
- // Create new signal
744
+ // Create new signal (new key added to object)
742
745
  nestedSignalMap.set(key, createPropertySignal(value))
746
+ // Notify parent so subscribers see the key addition
747
+ const parentSignal = arrayParentSignalMap.get(wrapped) || (wrapped as any)._parentSignal
748
+ if (parentSignal && typeof (parentSignal as any).trigger === 'function') {
749
+ ;(parentSignal as Signal<any>).trigger()
750
+ }
743
751
  }
744
752
 
745
753
  return true
@@ -794,6 +802,11 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
794
802
  }
795
803
  // Remove from the signal map
796
804
  nestedSignalMap.delete(key)
805
+ // Notify parent so subscribers see the key removal
806
+ const parentSignal = arrayParentSignalMap.get(wrapped) || (wrapped as any)._parentSignal
807
+ if (parentSignal && typeof (parentSignal as any).trigger === 'function') {
808
+ ;(parentSignal as Signal<any>).trigger()
809
+ }
797
810
  }
798
811
 
799
812
  // Delete from target