@zeix/cause-effect 0.15.2 → 0.16.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/src/store.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  type UnknownRecord,
7
7
  type UnknownRecordOrArray,
8
8
  } from './diff'
9
- import { effect } from './effect'
9
+
10
10
  import {
11
11
  InvalidSignalValueError,
12
12
  NullishSignalValueError,
@@ -14,15 +14,17 @@ import {
14
14
  StoreKeyRangeError,
15
15
  StoreKeyReadonlyError,
16
16
  } from './errors'
17
+ import { isMutableSignal, type Signal } from './signal'
18
+ import { createState, isState, type State } from './state'
17
19
  import {
18
20
  batch,
19
21
  type Cleanup,
22
+ createWatcher,
20
23
  notify,
24
+ observe,
21
25
  subscribe,
22
26
  type Watcher,
23
- } from './scheduler'
24
- import { isMutableSignal, type Signal } from './signal'
25
- import { isState, type State, state } from './state'
27
+ } from './system'
26
28
  import {
27
29
  isFunction,
28
30
  isObjectOfType,
@@ -37,51 +39,27 @@ import {
37
39
 
38
40
  type ArrayItem<T> = T extends readonly (infer U extends {})[] ? U : never
39
41
 
40
- type StoreEventMap<T extends UnknownRecord | UnknownArray> = {
41
- 'store-add': StoreAddEvent<T>
42
- 'store-change': StoreChangeEvent<T>
43
- 'store-remove': StoreRemoveEvent<T>
44
- 'store-sort': StoreSortEvent
42
+ type StoreChanges<T> = {
43
+ add: Partial<T>
44
+ change: Partial<T>
45
+ remove: Partial<T>
46
+ sort: string[]
45
47
  }
46
48
 
47
- interface StoreEventTarget<T extends UnknownRecord | UnknownArray>
48
- extends EventTarget {
49
- addEventListener<K extends keyof StoreEventMap<T>>(
50
- type: K,
51
- listener: (event: StoreEventMap<T>[K]) => void,
52
- options?: boolean | AddEventListenerOptions,
53
- ): void
54
-
55
- removeEventListener<K extends keyof StoreEventMap<T>>(
56
- type: K,
57
- listener: (event: StoreEventMap<T>[K]) => void,
58
- options?: boolean | EventListenerOptions,
59
- ): void
60
-
61
- dispatchEvent(event: Event): boolean
49
+ type StoreListeners<T> = {
50
+ [K in keyof StoreChanges<T>]: Set<(change: StoreChanges<T>[K]) => void>
62
51
  }
63
52
 
64
- interface BaseStore<T extends UnknownRecord | UnknownArray>
65
- extends StoreEventTarget<T> {
53
+ interface BaseStore {
66
54
  readonly [Symbol.toStringTag]: 'Store'
67
- get(): T
68
- set(value: T): void
69
- update(fn: (value: T) => T): void
70
- sort<
71
- U = T extends UnknownArray ? ArrayItem<T> : T[Extract<keyof T, string>],
72
- >(
73
- compareFn?: (a: U, b: U) => number,
74
- ): void
75
55
  readonly size: State<number>
76
56
  }
77
57
 
78
- type RecordStore<T extends UnknownRecord> = BaseStore<T> & {
58
+ type RecordStore<T extends UnknownRecord> = BaseStore & {
79
59
  [K in keyof T]: T[K] extends readonly unknown[] | Record<string, unknown>
80
60
  ? Store<T[K]>
81
61
  : State<T[K]>
82
62
  } & {
83
- add<K extends Extract<keyof T, string>>(key: K, value: T[K]): void
84
- remove<K extends Extract<keyof T, string>>(key: K): void
85
63
  [Symbol.iterator](): IterableIterator<
86
64
  [
87
65
  Extract<keyof T, string>,
@@ -92,49 +70,46 @@ type RecordStore<T extends UnknownRecord> = BaseStore<T> & {
92
70
  : State<T[Extract<keyof T, string>]>,
93
71
  ]
94
72
  >
73
+ add<K extends Extract<keyof T, string>>(key: K, value: T[K]): void
74
+ get(): T
75
+ set(value: T): void
76
+ update(fn: (value: T) => T): void
77
+ sort<U = T[Extract<keyof T, string>]>(
78
+ compareFn?: (a: U, b: U) => number,
79
+ ): void
80
+ on<K extends keyof StoreChanges<T>>(
81
+ type: K,
82
+ listener: (change: StoreChanges<T>[K]) => void,
83
+ ): Cleanup
84
+ remove<K extends Extract<keyof T, string>>(key: K): void
95
85
  }
96
86
 
97
- type ArrayStore<T extends UnknownArray> = BaseStore<T> & {
98
- readonly length: number
87
+ type ArrayStore<T extends UnknownArray> = BaseStore & {
88
+ [Symbol.iterator](): IterableIterator<
89
+ ArrayItem<T> extends readonly unknown[] | Record<string, unknown>
90
+ ? Store<ArrayItem<T>>
91
+ : State<ArrayItem<T>>
92
+ >
93
+ readonly [Symbol.isConcatSpreadable]: boolean
99
94
  [n: number]: ArrayItem<T> extends
100
95
  | readonly unknown[]
101
96
  | Record<string, unknown>
102
97
  ? Store<ArrayItem<T>>
103
98
  : State<ArrayItem<T>>
104
99
  add(value: ArrayItem<T>): void
100
+ get(): T
101
+ set(value: T): void
102
+ update(fn: (value: T) => T): void
103
+ sort<U = ArrayItem<T>>(compareFn?: (a: U, b: U) => number): void
104
+ on<K extends keyof StoreChanges<T>>(
105
+ type: K,
106
+ listener: (change: StoreChanges<T>[K]) => void,
107
+ ): Cleanup
105
108
  remove(index: number): void
106
- [Symbol.iterator](): IterableIterator<
107
- ArrayItem<T> extends readonly unknown[] | Record<string, unknown>
108
- ? Store<ArrayItem<T>>
109
- : State<ArrayItem<T>>
110
- >
111
- readonly [Symbol.isConcatSpreadable]: boolean
112
- }
113
-
114
- interface StoreAddEvent<T extends UnknownRecord | UnknownArray>
115
- extends CustomEvent {
116
- type: 'store-add'
117
- detail: Partial<T>
118
- }
119
-
120
- interface StoreChangeEvent<T extends UnknownRecord | UnknownArray>
121
- extends CustomEvent {
122
- type: 'store-change'
123
- detail: Partial<T>
124
- }
125
-
126
- interface StoreRemoveEvent<T extends UnknownRecord | UnknownArray>
127
- extends CustomEvent {
128
- type: 'store-remove'
129
- detail: Partial<T>
130
- }
131
-
132
- interface StoreSortEvent extends CustomEvent {
133
- type: 'store-sort'
134
- detail: string[]
109
+ readonly length: number
135
110
  }
136
111
 
137
- type Store<T> = T extends UnknownRecord
112
+ type Store<T extends UnknownRecord | UnknownArray> = T extends UnknownRecord
138
113
  ? RecordStore<T>
139
114
  : T extends UnknownArray
140
115
  ? ArrayStore<T>
@@ -144,11 +119,6 @@ type Store<T> = T extends UnknownRecord
144
119
 
145
120
  const TYPE_STORE = 'Store'
146
121
 
147
- const STORE_EVENT_ADD = 'store-add'
148
- const STORE_EVENT_CHANGE = 'store-change'
149
- const STORE_EVENT_REMOVE = 'store-remove'
150
- const STORE_EVENT_SORT = 'store-sort'
151
-
152
122
  /* === Functions === */
153
123
 
154
124
  /**
@@ -163,19 +133,26 @@ const STORE_EVENT_SORT = 'store-sort'
163
133
  * @param {T} initialValue - initial object or array value of the store
164
134
  * @returns {Store<T>} - new store with reactive properties that preserves the original type T
165
135
  */
166
- const store = <T extends UnknownRecord | UnknownArray>(
136
+ const createStore = <T extends UnknownRecord | UnknownArray>(
167
137
  initialValue: T,
168
138
  ): Store<T> => {
139
+ if (initialValue == null) throw new NullishSignalValueError('store')
140
+
169
141
  const watchers = new Set<Watcher>()
170
- const eventTarget = new EventTarget()
142
+ const listeners: StoreListeners<T> = {
143
+ add: new Set<(change: Partial<T>) => void>(),
144
+ change: new Set<(change: Partial<T>) => void>(),
145
+ remove: new Set<(change: Partial<T>) => void>(),
146
+ sort: new Set<(change: string[]) => void>(),
147
+ }
171
148
  const signals = new Map<string, Signal<T[Extract<keyof T, string>] & {}>>()
172
- const cleanups = new Map<string, Cleanup>()
149
+ const signalWatchers = new Map<string, Watcher>()
173
150
 
174
151
  // Determine if this is an array-like store at creation time
175
152
  const isArrayLike = Array.isArray(initialValue)
176
153
 
177
154
  // Internal state
178
- const size = state(0)
155
+ const size = createState(0)
179
156
 
180
157
  // Get current record
181
158
  const current = () => {
@@ -186,9 +163,16 @@ const store = <T extends UnknownRecord | UnknownArray>(
186
163
  return record
187
164
  }
188
165
 
189
- // Emit event
190
- const emit = <R>(type: keyof StoreEventMap<T>, detail: R) =>
191
- eventTarget.dispatchEvent(new CustomEvent(type, { detail }))
166
+ // Emit change notifications
167
+ const emit = <K extends keyof StoreChanges<T>>(
168
+ key: K,
169
+ changes: StoreChanges<T>[K],
170
+ ) => {
171
+ Object.freeze(changes)
172
+ for (const listener of listeners[key]) {
173
+ listener(changes)
174
+ }
175
+ }
192
176
 
193
177
  // Get sorted indexes
194
178
  const getSortedIndexes = () =>
@@ -223,26 +207,25 @@ const store = <T extends UnknownRecord | UnknownArray>(
223
207
  const signal =
224
208
  isState(value) || isStore(value)
225
209
  ? value
226
- : isRecord(value)
227
- ? store(value)
228
- : Array.isArray(value)
229
- ? store(value)
230
- : state(value)
210
+ : isRecord(value) || Array.isArray(value)
211
+ ? createStore(value)
212
+ : createState(value)
231
213
  // @ts-expect-error non-matching signal types
232
214
  signals.set(key, signal)
233
- const cleanup = effect(() => {
234
- const currentValue = signal.get()
235
- if (currentValue != null)
236
- emit(STORE_EVENT_CHANGE, {
237
- [key]: currentValue,
215
+ const watcher = createWatcher(() =>
216
+ observe(() => {
217
+ emit('change', {
218
+ [key]: signal.get(),
238
219
  } as unknown as Partial<T>)
239
- })
240
- cleanups.set(key, cleanup)
220
+ }, watcher),
221
+ )
222
+ watcher()
223
+ signalWatchers.set(key, watcher)
241
224
 
242
225
  if (single) {
243
226
  size.set(signals.size)
244
227
  notify(watchers)
245
- emit(STORE_EVENT_ADD, {
228
+ emit('add', {
246
229
  [key]: value,
247
230
  } as unknown as Partial<T>)
248
231
  }
@@ -256,15 +239,15 @@ const store = <T extends UnknownRecord | UnknownArray>(
256
239
  ) => {
257
240
  const ok = signals.delete(key)
258
241
  if (ok) {
259
- const cleanup = cleanups.get(key)
260
- if (cleanup) cleanup()
261
- cleanups.delete(key)
242
+ const watcher = signalWatchers.get(key)
243
+ if (watcher) watcher.cleanup()
244
+ signalWatchers.delete(key)
262
245
  }
263
246
 
264
247
  if (single) {
265
248
  size.set(signals.size)
266
249
  notify(watchers)
267
- emit(STORE_EVENT_REMOVE, {
250
+ emit('remove', {
268
251
  [key]: UNSET,
269
252
  } as unknown as Partial<T>)
270
253
  }
@@ -296,10 +279,10 @@ const store = <T extends UnknownRecord | UnknownArray>(
296
279
  // Queue initial additions event to allow listeners to be added first
297
280
  if (initialRun) {
298
281
  setTimeout(() => {
299
- emit(STORE_EVENT_ADD, changes.add as Partial<T>)
282
+ emit('add', changes.add as Partial<T>)
300
283
  }, 0)
301
284
  } else {
302
- emit<Partial<T>>(STORE_EVENT_ADD, changes.add as Partial<T>)
285
+ emit('add', changes.add as Partial<T>)
303
286
  }
304
287
  }
305
288
 
@@ -314,14 +297,14 @@ const store = <T extends UnknownRecord | UnknownArray>(
314
297
  else
315
298
  throw new StoreKeyReadonlyError(key, valueString(value))
316
299
  }
317
- emit(STORE_EVENT_CHANGE, changes.change as Partial<T>)
300
+ emit('change', changes.change as Partial<T>)
318
301
  }
319
302
 
320
303
  // Removals
321
304
  if (Object.keys(changes.remove).length) {
322
305
  for (const key in changes.remove)
323
306
  removeProperty(key as Extract<keyof T, string>)
324
- emit(STORE_EVENT_REMOVE, changes.remove as Partial<T>)
307
+ emit('remove', changes.remove as Partial<T>)
325
308
  }
326
309
 
327
310
  size.set(signals.size)
@@ -334,7 +317,7 @@ const store = <T extends UnknownRecord | UnknownArray>(
334
317
  reconcile({} as T, initialValue, true)
335
318
 
336
319
  // Methods and Properties
337
- const s: Record<string, unknown> = {
320
+ const store: Record<string, unknown> = {
338
321
  add: isArrayLike
339
322
  ? (v: ArrayItem<T>): void => {
340
323
  const nextIndex = signals.size
@@ -426,11 +409,15 @@ const store = <T extends UnknownRecord | UnknownArray>(
426
409
  signals.clear()
427
410
  newSignals.forEach((signal, key) => signals.set(key, signal))
428
411
  notify(watchers)
429
- emit(STORE_EVENT_SORT, newOrder)
412
+ emit('sort', newOrder)
413
+ },
414
+ on: <K extends keyof StoreChanges<T>>(
415
+ type: K,
416
+ listener: (change: StoreChanges<T>[K]) => void,
417
+ ): Cleanup => {
418
+ listeners[type].add(listener)
419
+ return () => listeners[type].delete(listener)
430
420
  },
431
- addEventListener: eventTarget.addEventListener.bind(eventTarget),
432
- removeEventListener: eventTarget.removeEventListener.bind(eventTarget),
433
- dispatchEvent: eventTarget.dispatchEvent.bind(eventTarget),
434
421
  size,
435
422
  }
436
423
 
@@ -458,7 +445,7 @@ const store = <T extends UnknownRecord | UnknownArray>(
458
445
  if (isSymbol(prop)) return undefined
459
446
 
460
447
  // Methods and Properties
461
- if (prop in s) return s[prop]
448
+ if (prop in store) return store[prop]
462
449
  if (prop === 'length' && isArrayLike) {
463
450
  subscribe(watchers)
464
451
  return size.get()
@@ -472,7 +459,7 @@ const store = <T extends UnknownRecord | UnknownArray>(
472
459
  return (
473
460
  (stringProp &&
474
461
  signals.has(stringProp as Extract<keyof T, string>)) ||
475
- Object.keys(s).includes(stringProp) ||
462
+ Object.keys(store).includes(stringProp) ||
476
463
  prop === Symbol.toStringTag ||
477
464
  prop === Symbol.iterator ||
478
465
  prop === Symbol.isConcatSpreadable ||
@@ -506,7 +493,8 @@ const store = <T extends UnknownRecord | UnknownArray>(
506
493
  if (prop === Symbol.toStringTag) return nonEnumerable(TYPE_STORE)
507
494
  if (isSymbol(prop)) return undefined
508
495
 
509
- if (Object.keys(s).includes(prop)) return nonEnumerable(s[prop])
496
+ if (Object.keys(store).includes(prop))
497
+ return nonEnumerable(store[prop])
510
498
 
511
499
  const signal = signals.get(prop as Extract<keyof T, string>)
512
500
  return signal
@@ -534,14 +522,4 @@ const isStore = <T extends UnknownRecordOrArray>(
534
522
 
535
523
  /* === Exports === */
536
524
 
537
- export {
538
- TYPE_STORE,
539
- isStore,
540
- store,
541
- type Store,
542
- type StoreAddEvent,
543
- type StoreChangeEvent,
544
- type StoreRemoveEvent,
545
- type StoreSortEvent,
546
- type StoreEventMap,
547
- }
525
+ export { TYPE_STORE, isStore, createStore, type Store, type StoreChanges }
package/src/system.ts ADDED
@@ -0,0 +1,122 @@
1
+ /* === Types === */
2
+
3
+ type Cleanup = () => void
4
+
5
+ type Watcher = {
6
+ (): void
7
+ unwatch(cleanup: Cleanup): void
8
+ cleanup(): void
9
+ }
10
+
11
+ /* === Internal === */
12
+
13
+ // Currently active watcher
14
+ let activeWatcher: Watcher | undefined
15
+
16
+ // Pending queue for batched change notifications
17
+ const pendingWatchers = new Set<Watcher>()
18
+ let batchDepth = 0
19
+
20
+ /* === Functions === */
21
+
22
+ /**
23
+ * Create a watcher that can be used to observe changes to a signal
24
+ *
25
+ * @since 0.14.1
26
+ * @param {() => void} watch - Function to be called when the state changes
27
+ * @returns {Watcher} - Watcher object with off and cleanup methods
28
+ */
29
+ const createWatcher = (watch: () => void): Watcher => {
30
+ const cleanups = new Set<Cleanup>()
31
+ const w = watch as Partial<Watcher>
32
+ w.unwatch = (cleanup: Cleanup) => {
33
+ cleanups.add(cleanup)
34
+ }
35
+ w.cleanup = () => {
36
+ for (const cleanup of cleanups) cleanup()
37
+ cleanups.clear()
38
+ }
39
+ return w as Watcher
40
+ }
41
+
42
+ /**
43
+ * Add active watcher to the Set of watchers
44
+ *
45
+ * @param {Set<Watcher>} watchers - watchers of the signal
46
+ */
47
+ const subscribe = (watchers: Set<Watcher>) => {
48
+ if (activeWatcher && !watchers.has(activeWatcher)) {
49
+ const watcher = activeWatcher
50
+ watcher.unwatch(() => {
51
+ watchers.delete(watcher)
52
+ })
53
+ watchers.add(watcher)
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Add watchers to the pending set of change notifications
59
+ *
60
+ * @param {Set<Watcher>} watchers - watchers of the signal
61
+ */
62
+ const notify = (watchers: Set<Watcher>) => {
63
+ for (const watcher of watchers) {
64
+ if (batchDepth) pendingWatchers.add(watcher)
65
+ else watcher()
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Flush all pending changes to notify watchers
71
+ */
72
+ const flush = () => {
73
+ while (pendingWatchers.size) {
74
+ const watchers = Array.from(pendingWatchers)
75
+ pendingWatchers.clear()
76
+ for (const watcher of watchers) watcher()
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Batch multiple changes in a single signal graph and DOM update cycle
82
+ *
83
+ * @param {() => void} fn - function with multiple signal writes to be batched
84
+ */
85
+ const batch = (fn: () => void) => {
86
+ batchDepth++
87
+ try {
88
+ fn()
89
+ } finally {
90
+ flush()
91
+ batchDepth--
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Run a function in a reactive context
97
+ *
98
+ * @param {() => void} run - function to run the computation or effect
99
+ * @param {Watcher} watcher - function to be called when the state changes or undefined for temporary unwatching while inserting auto-hydrating DOM nodes that might read signals (e.g., web components)
100
+ */
101
+ const observe = (run: () => void, watcher?: Watcher): void => {
102
+ const prev = activeWatcher
103
+ activeWatcher = watcher
104
+ try {
105
+ run()
106
+ } finally {
107
+ activeWatcher = prev
108
+ }
109
+ }
110
+
111
+ /* === Exports === */
112
+
113
+ export {
114
+ type Cleanup,
115
+ type Watcher,
116
+ subscribe,
117
+ notify,
118
+ flush,
119
+ batch,
120
+ createWatcher,
121
+ observe,
122
+ }
package/src/util.ts CHANGED
@@ -23,10 +23,6 @@ const isAsyncFunction = /*#__PURE__*/ <T>(
23
23
  ): fn is (...args: unknown[]) => Promise<T> =>
24
24
  isFunction(fn) && fn.constructor.name === 'AsyncFunction'
25
25
 
26
- const isDefinedObject = /*#__PURE__*/ (
27
- value: unknown,
28
- ): value is Record<string, unknown> => !!value && typeof value === 'object'
29
-
30
26
  const isObjectOfType = /*#__PURE__*/ <T>(
31
27
  value: unknown,
32
28
  type: string,
@@ -92,7 +88,7 @@ const recordToArray = /*#__PURE__*/ <T>(
92
88
  const valueString = /*#__PURE__*/ (value: unknown): string =>
93
89
  isString(value)
94
90
  ? `"${value}"`
95
- : isDefinedObject(value)
91
+ : !!value && typeof value === 'object'
96
92
  ? JSON.stringify(value)
97
93
  : String(value)
98
94
 
@@ -105,7 +101,6 @@ export {
105
101
  isSymbol,
106
102
  isFunction,
107
103
  isAsyncFunction,
108
- isDefinedObject,
109
104
  isObjectOfType,
110
105
  isRecord,
111
106
  isRecordOrArray,
@@ -1,14 +1,21 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { batch, computed, effect, match, resolve, state } from '../'
2
+ import {
3
+ batch,
4
+ createComputed,
5
+ createEffect,
6
+ createState,
7
+ match,
8
+ resolve,
9
+ } from '../'
3
10
 
4
11
  /* === Tests === */
5
12
 
6
13
  describe('Batch', () => {
7
14
  test('should be triggered only once after repeated state change', () => {
8
- const cause = state(0)
15
+ const cause = createState(0)
9
16
  let result = 0
10
17
  let count = 0
11
- effect((): undefined => {
18
+ createEffect((): undefined => {
12
19
  result = cause.get()
13
20
  count++
14
21
  })
@@ -22,13 +29,13 @@ describe('Batch', () => {
22
29
  })
23
30
 
24
31
  test('should be triggered only once when multiple signals are set', () => {
25
- const a = state(3)
26
- const b = state(4)
27
- const c = state(5)
28
- const sum = computed(() => a.get() + b.get() + c.get())
32
+ const a = createState(3)
33
+ const b = createState(4)
34
+ const c = createState(5)
35
+ const sum = createComputed(() => a.get() + b.get() + c.get())
29
36
  let result = 0
30
37
  let count = 0
31
- effect(() => {
38
+ createEffect(() => {
32
39
  const resolved = resolve({ sum })
33
40
  match(resolved, {
34
41
  ok: ({ sum: res }) => {
@@ -49,10 +56,10 @@ describe('Batch', () => {
49
56
 
50
57
  test('should prove example from README works', () => {
51
58
  // State: define an array of Signal<number>
52
- const signals = [state(2), state(3), state(5)]
59
+ const signals = [createState(2), createState(3), createState(5)]
53
60
 
54
61
  // Computed: derive a calculation ...
55
- const sum = computed(() => {
62
+ const sum = createComputed(() => {
56
63
  const v = signals.reduce((total, v) => total + v.get(), 0)
57
64
  if (!Number.isFinite(v)) throw new Error('Invalid value')
58
65
  return v
@@ -63,7 +70,7 @@ describe('Batch', () => {
63
70
  let errCount = 0
64
71
 
65
72
  // Effect: switch cases for the result
66
- effect(() => {
73
+ createEffect(() => {
67
74
  const resolved = resolve({ sum })
68
75
  match(resolved, {
69
76
  ok: ({ sum: v }) => {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, mock, test } from 'bun:test'
2
- import { batch, computed, effect, state } from '../'
2
+ import { batch, createComputed, createEffect, createState } from '../'
3
3
  import { Counter, makeGraph, runGraph } from './util/dependency-graph'
4
4
  import type { Computed, ReactiveFramework } from './util/reactive-framework'
5
5
 
@@ -15,19 +15,19 @@ const busy = () => {
15
15
  const framework = {
16
16
  name: 'Cause & Effect',
17
17
  signal: <T extends {}>(initialValue: T) => {
18
- const s = state<T>(initialValue)
18
+ const s = createState<T>(initialValue)
19
19
  return {
20
20
  write: (v: T) => s.set(v),
21
21
  read: () => s.get(),
22
22
  }
23
23
  },
24
24
  computed: <T extends {}>(fn: () => T) => {
25
- const c = computed(fn)
25
+ const c = createComputed(fn)
26
26
  return {
27
27
  read: () => c.get(),
28
28
  }
29
29
  },
30
- effect: (fn: () => undefined) => effect(fn),
30
+ effect: (fn: () => undefined) => createEffect(fn),
31
31
  withBatch: (fn: () => undefined) => batch(fn),
32
32
  withBuild: <T>(fn: () => T) => fn(),
33
33
  }