@bitstillery/mithril 3.9.2 → 3.9.3

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.9.2",
3
+ "version": "3.9.3",
4
4
  "description": "Mithril + Signals, Store and SSR",
5
5
  "license": "MIT",
6
6
  "author": "Bitstillery",
package/signal.ts CHANGED
@@ -188,7 +188,7 @@ export class ComputedSignal<T> extends Signal<T> {
188
188
  this._compute = compute
189
189
  }
190
190
 
191
- get value(): T {
191
+ override get value(): T {
192
192
  // Track access by other computed signals - this enables computed-to-computed dependency chains
193
193
  // When computed B accesses computed A, A should notify B when A's dependencies change
194
194
  if (currentEffect) {
@@ -259,7 +259,7 @@ export class ComputedSignal<T> extends Signal<T> {
259
259
  this._markDirty()
260
260
  }
261
261
 
262
- set value(_newValue: T) {
262
+ override set value(_newValue: T) {
263
263
  throw new Error('Computed signals are read-only')
264
264
  }
265
265
  }
package/state.ts CHANGED
@@ -60,12 +60,37 @@ function isState(value: any): boolean {
60
60
  return value && typeof value === 'object' && (value as any).__isState === true
61
61
  }
62
62
 
63
+ /**
64
+ * Built-ins whose methods need their internal slots. A proxy has none, and the get trap calls a
65
+ * function-valued property with the proxy as `this`, so a proxied Date or Map throws on every method
66
+ * call (and a Map's `get`/`set` read as a get/set descriptor). They are held as a signal's value
67
+ * instead, and replaced wholesale to notify.
68
+ */
69
+ function isOpaqueObject(value: object): boolean {
70
+ return (
71
+ value instanceof Date ||
72
+ value instanceof Map ||
73
+ value instanceof Set ||
74
+ value instanceof WeakMap ||
75
+ value instanceof WeakSet ||
76
+ value instanceof RegExp ||
77
+ value instanceof Promise ||
78
+ value instanceof ArrayBuffer ||
79
+ ArrayBuffer.isView(value)
80
+ )
81
+ }
82
+
63
83
  /**
64
84
  * Check if a value is a get/set descriptor object (like JavaScript property descriptors)
65
85
  * Used to detect computed properties defined as { get: () => T, set?: (value: T) => void }
66
86
  */
67
87
  function isGetSetDescriptor(value: any): boolean {
68
- return value && typeof value === 'object' && (typeof value.get === 'function' || typeof value.set === 'function')
88
+ return (
89
+ value &&
90
+ typeof value === 'object' &&
91
+ !isOpaqueObject(value) &&
92
+ (typeof value.get === 'function' || typeof value.set === 'function')
93
+ )
69
94
  }
70
95
 
71
96
  /**
@@ -203,7 +228,7 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
203
228
  parentSignalMap?: Map<string, Signal<any> | ComputedSignal<any>>,
204
229
  context?: InitContext,
205
230
  ): any {
206
- if (obj === null || typeof obj !== 'object') {
231
+ if (obj === null || typeof obj !== 'object' || isOpaqueObject(obj)) {
207
232
  return obj
208
233
  }
209
234
 
@@ -863,6 +888,48 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
863
888
  return wrapped
864
889
  }
865
890
 
891
+ /** The built-ins `isOpaqueObject` keeps out of the proxy: state holds them as they are. */
892
+ type OpaqueObject =
893
+ | Date
894
+ | Map<any, any>
895
+ | Set<any>
896
+ | WeakMap<any, any>
897
+ | WeakSet<any>
898
+ | RegExp
899
+ | Promise<any>
900
+ | ArrayBuffer
901
+ | ArrayBufferView
902
+
903
+ /**
904
+ * A value as state hands it out: a function reads as its computed result, an object (or array) as its
905
+ * own State. Distributes over a union, so a nullable object (`Foo | null`) is still a State with its
906
+ * `$` signals once it is set.
907
+ */
908
+ type StateValue<V> = V extends (...args: any[]) => infer R
909
+ ? R
910
+ : V extends OpaqueObject
911
+ ? V
912
+ : V extends Record<string, any>
913
+ ? State<V>
914
+ : V
915
+
916
+ /**
917
+ * A property as state hands it out. Unlike an array element it doesn't distribute over a union, so a
918
+ * nullable object (`Foo | null`) stays its plain shape and a plain `Foo` can be written to it, as in 3.9.
919
+ * A `{get, set}` descriptor reads as its computed value.
920
+ */
921
+ type StateProp<V> = [V] extends [never]
922
+ ? V
923
+ : [V] extends [(...args: any[]) => infer R]
924
+ ? R
925
+ : [V] extends [{get: () => infer R; set: (value: never) => void}]
926
+ ? R
927
+ : [V] extends [OpaqueObject]
928
+ ? V
929
+ : [V] extends [Record<string, any>]
930
+ ? State<V>
931
+ : V
932
+
866
933
  /**
867
934
  * Mapped type that adds $prop for each key, returning the Signal for that property.
868
935
  * - Primitives: $prop => Signal<T[K]>
@@ -874,24 +941,19 @@ export type StateSignals<T extends Record<string, any>> = {
874
941
  // lookup by a `string` key would read as `Value | Signal<Value>`.
875
942
  [K in keyof T as K extends string ? (string extends K ? never : `$${K}`) : never]: T[K] extends (...args: any[]) => infer R
876
943
  ? ComputedSignal<R>
877
- : T[K] extends object
878
- ? Signal<State<T[K]>>
879
- : Signal<T[K]>
944
+ : Signal<StateProp<T[K]>>
880
945
  }
881
- type StateElem<E> = E extends Record<string, any> ? State<E> : E
882
946
 
883
- export type StateArray<Elem> = Omit<Array<StateElem<Elem>>, 'fill' | 'push' | 'splice' | 'unshift'> & {
947
+ export type StateArray<Elem> = Omit<Array<StateValue<Elem>>, 'fill' | 'push' | 'splice' | 'unshift'> & {
884
948
  fill(value: Elem, start?: number, end?: number): StateArray<Elem>
885
949
  push(...items: Elem[]): number
886
- splice(start: number, deleteCount?: number, ...items: Elem[]): StateElem<Elem>[]
950
+ splice(start: number, deleteCount?: number, ...items: Elem[]): StateValue<Elem>[]
887
951
  unshift(...items: Elem[]): number
888
952
  }
889
953
 
890
954
  export type State<T extends Record<string, any>> = T extends (infer Elem)[]
891
955
  ? StateArray<Elem>
892
- : {
893
- [K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : T[K] extends Record<string, any> ? State<T[K]> : T[K]
894
- } & StateSignals<T>
956
+ : {[K in keyof T]: StateProp<T[K]>} & StateSignals<T>
895
957
 
896
958
  /**
897
959
  * A partial of a state shape at every depth, for the persistence tiers of a Store: each tier fills in
package/store.ts CHANGED
@@ -1,4 +1,5 @@
1
- import {allowComputed, state, State, updateStateRegistry, type DeepPartial} from './state'
1
+ import {allowComputed, state, updateStateRegistry} from './state'
2
+ import type {State, DeepPartial} from './state'
2
3
  import {serializeStore, deserializeStore} from './render/ssrState'
3
4
 
4
5
  // Helper function to restore computed properties (same as in ssrState.ts)