@zeix/cause-effect 0.18.0 → 0.18.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/src/nodes/list.ts CHANGED
@@ -39,7 +39,7 @@ type DiffResult = {
39
39
  remove: UnknownRecord
40
40
  }
41
41
 
42
- type KeyConfig<T> = string | ((item: T) => string)
42
+ type KeyConfig<T> = string | ((item: T) => string | undefined)
43
43
 
44
44
  type ListOptions<T extends {}> = {
45
45
  keyConfig?: KeyConfig<T>
@@ -52,8 +52,8 @@ type List<T extends {}> = {
52
52
  [Symbol.iterator](): IterableIterator<State<T>>
53
53
  readonly length: number
54
54
  get(): T[]
55
- set(newValue: T[]): void
56
- update(fn: (oldValue: T[]) => T[]): void
55
+ set(next: T[]): void
56
+ update(fn: (prev: T[]) => T[]): void
57
57
  at(index: number): State<T> | undefined
58
58
  keys(): IterableIterator<string>
59
59
  byKey(key: string): State<T> | undefined
@@ -77,19 +77,11 @@ type List<T extends {}> = {
77
77
  * Checks if two values are equal with cycle detection
78
78
  *
79
79
  * @since 0.15.0
80
- * @param {T} a - First value to compare
81
- * @param {T} b - Second value to compare
82
- * @param {WeakSet<object>} visited - Set to track visited objects for cycle detection
83
- * @returns {boolean} Whether the two values are equal
80
+ * @param a - First value to compare
81
+ * @param b - Second value to compare
82
+ * @param visited - Set to track visited objects for cycle detection
83
+ * @returns Whether the two values are equal
84
84
  */
85
-
86
- /** Shallow equality check for string arrays */
87
- function keysEqual(a: string[], b: string[]): boolean {
88
- if (a.length !== b.length) return false
89
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false
90
- return true
91
- }
92
-
93
85
  function isEqual<T>(a: T, b: T, visited?: WeakSet<object>): boolean {
94
86
  // Fast paths
95
87
  if (Object.is(a, b)) return true
@@ -117,9 +109,8 @@ function isEqual<T>(a: T, b: T, visited?: WeakSet<object>): boolean {
117
109
  const aa = a as unknown[]
118
110
  const ba = b as unknown[]
119
111
  if (aa.length !== ba.length) return false
120
- for (let i = 0; i < aa.length; i++) {
112
+ for (let i = 0; i < aa.length; i++)
121
113
  if (!isEqual(aa[i], ba[i], visited)) return false
122
- }
123
114
  return true
124
115
  }
125
116
 
@@ -144,23 +135,45 @@ function isEqual<T>(a: T, b: T, visited?: WeakSet<object>): boolean {
144
135
  }
145
136
  }
146
137
 
138
+ /** Shallow equality check for string arrays */
139
+ function keysEqual(a: string[], b: string[]): boolean {
140
+ if (a.length !== b.length) return false
141
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false
142
+ return true
143
+ }
144
+
145
+ function getKeyGenerator<T extends {}>(
146
+ keyConfig?: KeyConfig<T>,
147
+ ): [(item: T) => string, boolean] {
148
+ let keyCounter = 0
149
+ const contentBased = typeof keyConfig === 'function'
150
+ return [
151
+ typeof keyConfig === 'string'
152
+ ? () => `${keyConfig}${keyCounter++}`
153
+ : contentBased
154
+ ? (item: T) => keyConfig(item) || String(keyCounter++)
155
+ : () => String(keyCounter++),
156
+ contentBased,
157
+ ]
158
+ }
159
+
147
160
  /**
148
161
  * Compares two arrays using existing keys and returns differences as a DiffResult.
149
162
  * Avoids object conversion by working directly with arrays and keys.
150
163
  *
151
164
  * @since 0.18.0
152
- * @param {T[]} oldArray - The old array
153
- * @param {T[]} newArray - The new array
154
- * @param {string[]} currentKeys - Current keys array (may be sparse or shorter than oldArray)
155
- * @param {(item: T) => string} generateKey - Function to generate keys for new items
156
- * @param {boolean} contentBased - When true, always use generateKey (content-based keys);
165
+ * @param prev - The old array
166
+ * @param next - The new array
167
+ * @param prevKeys - Current keys array (may be sparse or shorter than oldArray)
168
+ * @param generateKey - Function to generate keys for new items
169
+ * @param contentBased - When true, always use generateKey (content-based keys);
157
170
  * when false, reuse positional keys from currentKeys (synthetic keys)
158
- * @returns {DiffResult & { newKeys: string[] }} The differences in DiffResult format plus updated keys array
171
+ * @returns The differences in DiffResult format plus updated keys array
159
172
  */
160
173
  function diffArrays<T>(
161
- oldArray: T[],
162
- newArray: T[],
163
- currentKeys: string[],
174
+ prev: T[],
175
+ next: T[],
176
+ prevKeys: string[],
164
177
  generateKey: (item: T) => string,
165
178
  contentBased: boolean,
166
179
  ): DiffResult & { newKeys: string[] } {
@@ -168,50 +181,46 @@ function diffArrays<T>(
168
181
  const add = {} as UnknownRecord
169
182
  const change = {} as UnknownRecord
170
183
  const remove = {} as UnknownRecord
171
- const newKeys: string[] = []
184
+ const nextKeys: string[] = []
172
185
  let changed = false
173
186
 
174
187
  // Build a map of old values by key for quick lookup
175
- const oldByKey = new Map<string, T>()
176
- for (let i = 0; i < oldArray.length; i++) {
177
- const key = currentKeys[i]
178
- if (key && oldArray[i]) oldByKey.set(key, oldArray[i])
188
+ const prevByKey = new Map<string, T>()
189
+ for (let i = 0; i < prev.length; i++) {
190
+ const key = prevKeys[i]
191
+ if (key && prev[i]) prevByKey.set(key, prev[i])
179
192
  }
180
193
 
181
194
  // Track which old keys we've seen
182
195
  const seenKeys = new Set<string>()
183
196
 
184
197
  // Process new array and build new keys array
185
- for (let i = 0; i < newArray.length; i++) {
186
- const newValue = newArray[i]
187
- if (newValue === undefined) continue
198
+ for (let i = 0; i < next.length; i++) {
199
+ const val = next[i]
200
+ if (val === undefined) continue
188
201
 
189
202
  // Content-based keys: always derive from item; synthetic keys: reuse by position
190
203
  const key = contentBased
191
- ? generateKey(newValue)
192
- : (currentKeys[i] ?? generateKey(newValue))
204
+ ? generateKey(val)
205
+ : (prevKeys[i] ?? generateKey(val))
193
206
 
194
- if (seenKeys.has(key))
195
- throw new DuplicateKeyError(TYPE_LIST, key, newValue)
207
+ if (seenKeys.has(key)) throw new DuplicateKeyError(TYPE_LIST, key, val)
196
208
 
197
- newKeys.push(key)
209
+ nextKeys.push(key)
198
210
  seenKeys.add(key)
199
211
 
200
212
  // Check if this key existed before
201
- if (!oldByKey.has(key)) {
202
- add[key] = newValue
213
+ if (!prevByKey.has(key)) {
214
+ add[key] = val
215
+ changed = true
216
+ } else if (!isEqual(prevByKey.get(key), val, visited)) {
217
+ change[key] = val
203
218
  changed = true
204
- } else {
205
- const oldValue = oldByKey.get(key)
206
- if (!isEqual(oldValue, newValue, visited)) {
207
- change[key] = newValue
208
- changed = true
209
- }
210
219
  }
211
220
  }
212
221
 
213
222
  // Find removed keys (existed in old but not in new)
214
- for (const [key] of oldByKey) {
223
+ for (const [key] of prevByKey) {
215
224
  if (!seenKeys.has(key)) {
216
225
  remove[key] = null
217
226
  changed = true
@@ -219,37 +228,29 @@ function diffArrays<T>(
219
228
  }
220
229
 
221
230
  // Detect reorder even when no values changed
222
- if (!changed && !keysEqual(currentKeys, newKeys)) changed = true
231
+ if (!changed && !keysEqual(prevKeys, nextKeys)) changed = true
223
232
 
224
- return { add, change, remove, newKeys, changed }
233
+ return { add, change, remove, newKeys: nextKeys, changed }
225
234
  }
226
235
 
227
236
  /**
228
237
  * Creates a reactive list with stable keys and per-item reactivity.
229
238
  *
230
239
  * @since 0.18.0
231
- * @param initialValue - Initial array of items
240
+ * @param value - Initial array of items
232
241
  * @param options - Optional configuration for key generation and watch lifecycle
233
242
  * @returns A List signal
234
243
  */
235
244
  function createList<T extends {}>(
236
- initialValue: T[],
245
+ value: T[],
237
246
  options?: ListOptions<T>,
238
247
  ): List<T> {
239
- validateSignalValue(TYPE_LIST, initialValue, Array.isArray)
248
+ validateSignalValue(TYPE_LIST, value, Array.isArray)
240
249
 
241
250
  const signals = new Map<string, State<T>>()
242
251
  let keys: string[] = []
243
252
 
244
- let keyCounter = 0
245
- const keyConfig = options?.keyConfig
246
- const contentBased = isFunction<string>(keyConfig)
247
- const generateKey: (item: T) => string =
248
- typeof keyConfig === 'string'
249
- ? () => `${keyConfig}${keyCounter++}`
250
- : contentBased
251
- ? (item: T) => keyConfig(item)
252
- : () => String(keyCounter++)
253
+ const [generateKey, contentBased] = getKeyGenerator(options?.keyConfig)
253
254
 
254
255
  // --- Internal helpers ---
255
256
 
@@ -265,7 +266,7 @@ function createList<T extends {}>(
265
266
  // Mutation methods (add/remove/set/splice) null out sources to force re-establishment.
266
267
  const node: MemoNode<T[]> = {
267
268
  fn: buildValue,
268
- value: initialValue,
269
+ value,
269
270
  flags: FLAG_DIRTY,
270
271
  sources: null,
271
272
  sourcesTail: null,
@@ -278,14 +279,14 @@ function createList<T extends {}>(
278
279
  const toRecord = (array: T[]): Record<string, T> => {
279
280
  const record = {} as Record<string, T>
280
281
  for (let i = 0; i < array.length; i++) {
281
- const value = array[i]
282
- if (value === undefined) continue
282
+ const val = array[i]
283
+ if (val === undefined) continue
283
284
  let key = keys[i]
284
285
  if (!key) {
285
- key = generateKey(value)
286
+ key = generateKey(val)
286
287
  keys[i] = key
287
288
  }
288
- record[key] = value
289
+ record[key] = val
289
290
  }
290
291
  return record
291
292
  }
@@ -295,9 +296,9 @@ function createList<T extends {}>(
295
296
 
296
297
  // Additions
297
298
  for (const key in changes.add) {
298
- const value = changes.add[key] as T
299
- validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value)
300
- signals.set(key, createState(value))
299
+ const val = changes.add[key] as T
300
+ validateSignalValue(`${TYPE_LIST} item for key "${key}"`, val)
301
+ signals.set(key, createState(val))
301
302
  structural = true
302
303
  }
303
304
 
@@ -305,13 +306,13 @@ function createList<T extends {}>(
305
306
  if (Object.keys(changes.change).length) {
306
307
  batch(() => {
307
308
  for (const key in changes.change) {
308
- const value = changes.change[key]
309
+ const val = changes.change[key]
309
310
  validateSignalValue(
310
311
  `${TYPE_LIST} item for key "${key}"`,
311
- value,
312
+ val,
312
313
  )
313
314
  const signal = signals.get(key)
314
- if (signal) signal.set(value as T)
315
+ if (signal) signal.set(val as T)
315
316
  }
316
317
  })
317
318
  }
@@ -332,17 +333,29 @@ function createList<T extends {}>(
332
333
  return changes.changed
333
334
  }
334
335
 
336
+ const watched = options?.watched
337
+ const subscribe = watched
338
+ ? () => {
339
+ if (activeSink) {
340
+ if (!node.sinks) node.stop = watched()
341
+ link(node, activeSink)
342
+ }
343
+ }
344
+ : () => {
345
+ if (activeSink) link(node, activeSink)
346
+ }
347
+
335
348
  // --- Initialize ---
336
- const initRecord = toRecord(initialValue)
349
+ const initRecord = toRecord(value)
337
350
  for (const key in initRecord) {
338
- const value = initRecord[key]
339
- validateSignalValue(`${TYPE_LIST} item for key "${key}"`, value)
340
- signals.set(key, createState(value))
351
+ const val = initRecord[key]
352
+ validateSignalValue(`${TYPE_LIST} item for key "${key}"`, val)
353
+ signals.set(key, createState(val))
341
354
  }
342
355
 
343
356
  // Starts clean: mutation methods (add/remove/set/splice) explicitly call
344
357
  // propagate() + invalidate edges, so refresh() on first get() is not needed.
345
- node.value = initialValue
358
+ node.value = value
346
359
  node.flags = 0
347
360
 
348
361
  // --- List object ---
@@ -358,20 +371,12 @@ function createList<T extends {}>(
358
371
  },
359
372
 
360
373
  get length() {
361
- if (activeSink) {
362
- if (!node.sinks && options?.watched)
363
- node.stop = options.watched()
364
- link(node, activeSink)
365
- }
374
+ subscribe()
366
375
  return keys.length
367
376
  },
368
377
 
369
378
  get() {
370
- if (activeSink) {
371
- if (!node.sinks && options?.watched)
372
- node.stop = options.watched()
373
- link(node, activeSink)
374
- }
379
+ subscribe()
375
380
  if (node.sources) {
376
381
  // Fast path: edges already established, rebuild value directly
377
382
  if (node.flags) {
@@ -386,12 +391,11 @@ function createList<T extends {}>(
386
391
  return node.value
387
392
  },
388
393
 
389
- set(newValue: T[]) {
390
- const currentValue =
391
- node.flags & FLAG_DIRTY ? buildValue() : node.value
394
+ set(next: T[]) {
395
+ const prev = node.flags & FLAG_DIRTY ? buildValue() : node.value
392
396
  const changes = diffArrays(
393
- currentValue,
394
- newValue,
397
+ prev,
398
+ next,
395
399
  keys,
396
400
  generateKey,
397
401
  contentBased,
@@ -399,13 +403,13 @@ function createList<T extends {}>(
399
403
  if (changes.changed) {
400
404
  keys = changes.newKeys
401
405
  applyChanges(changes)
402
- propagate(node as unknown as SinkNode)
403
406
  node.flags |= FLAG_DIRTY
407
+ for (let e = node.sinks; e; e = e.nextSink) propagate(e.sink)
404
408
  if (batchDepth === 0) flush()
405
409
  }
406
410
  },
407
411
 
408
- update(fn: (oldValue: T[]) => T[]) {
412
+ update(fn: (prev: T[]) => T[]) {
409
413
  list.set(fn(list.get()))
410
414
  },
411
415
 
@@ -414,11 +418,7 @@ function createList<T extends {}>(
414
418
  },
415
419
 
416
420
  keys() {
417
- if (activeSink) {
418
- if (!node.sinks && options?.watched)
419
- node.stop = options.watched()
420
- link(node, activeSink)
421
- }
421
+ subscribe()
422
422
  return keys.values()
423
423
  },
424
424
 
@@ -443,8 +443,8 @@ function createList<T extends {}>(
443
443
  signals.set(key, createState(value))
444
444
  node.sources = null
445
445
  node.sourcesTail = null
446
- propagate(node as unknown as SinkNode)
447
446
  node.flags |= FLAG_DIRTY
447
+ for (let e = node.sinks; e; e = e.nextSink) propagate(e.sink)
448
448
  if (batchDepth === 0) flush()
449
449
  return key
450
450
  },
@@ -461,8 +461,8 @@ function createList<T extends {}>(
461
461
  if (index >= 0) keys.splice(index, 1)
462
462
  node.sources = null
463
463
  node.sourcesTail = null
464
- propagate(node as unknown as SinkNode)
465
464
  node.flags |= FLAG_DIRTY
465
+ for (let e = node.sinks; e; e = e.nextSink) propagate(e.sink)
466
466
  if (batchDepth === 0) flush()
467
467
  }
468
468
  },
@@ -479,8 +479,8 @@ function createList<T extends {}>(
479
479
 
480
480
  if (!keysEqual(keys, newOrder)) {
481
481
  keys = newOrder
482
- propagate(node as unknown as SinkNode)
483
482
  node.flags |= FLAG_DIRTY
483
+ for (let e = node.sinks; e; e = e.nextSink) propagate(e.sink)
484
484
  if (batchDepth === 0) flush()
485
485
  }
486
486
  },
@@ -538,8 +538,8 @@ function createList<T extends {}>(
538
538
  changed,
539
539
  })
540
540
  keys = newOrder
541
- propagate(node as unknown as SinkNode)
542
541
  node.flags |= FLAG_DIRTY
542
+ for (let e = node.sinks; e; e = e.nextSink) propagate(e.sink)
543
543
  if (batchDepth === 0) flush()
544
544
  }
545
545
 
@@ -583,6 +583,7 @@ export {
583
583
  createList,
584
584
  isEqual,
585
585
  isList,
586
+ getKeyGenerator,
586
587
  keysEqual,
587
588
  TYPE_LIST,
588
589
  }
package/src/nodes/memo.ts CHANGED
@@ -5,12 +5,15 @@ import {
5
5
  } from '../errors'
6
6
  import {
7
7
  activeSink,
8
+ batchDepth,
8
9
  type ComputedOptions,
9
- defaultEquals,
10
+ DEFAULT_EQUALITY,
10
11
  FLAG_DIRTY,
12
+ flush,
11
13
  link,
12
14
  type MemoCallback,
13
15
  type MemoNode,
16
+ propagate,
14
17
  refresh,
15
18
  type SinkNode,
16
19
  TYPE_MEMO,
@@ -49,6 +52,12 @@ type Memo<T extends {}> = {
49
52
  * @template T - The type of value computed by the memo
50
53
  * @param fn - The computation function that receives the previous value
51
54
  * @param options - Optional configuration for the memo
55
+ * @param options.value - Optional initial value for reducer patterns
56
+ * @param options.equals - Optional equality function. Defaults to strict equality (`===`)
57
+ * @param options.guard - Optional type guard to validate values
58
+ * @param options.watched - Optional callback invoked when the memo is first watched by an effect.
59
+ * Receives an `invalidate` function to mark the memo dirty and trigger recomputation.
60
+ * Must return a cleanup function called when no effects are watching.
52
61
  * @returns A Memo object with a get() method
53
62
  *
54
63
  * @example
@@ -90,14 +99,33 @@ function createMemo<T extends {}>(
90
99
  sourcesTail: null,
91
100
  sinks: null,
92
101
  sinksTail: null,
93
- equals: options?.equals ?? defaultEquals,
102
+ equals: options?.equals ?? DEFAULT_EQUALITY,
94
103
  error: undefined,
104
+ stop: undefined,
95
105
  }
96
106
 
107
+ const watched = options?.watched
108
+ const subscribe = watched
109
+ ? () => {
110
+ if (activeSink) {
111
+ if (!node.sinks)
112
+ node.stop = watched(() => {
113
+ node.flags |= FLAG_DIRTY
114
+ for (let e = node.sinks; e; e = e.nextSink)
115
+ propagate(e.sink)
116
+ if (batchDepth === 0) flush()
117
+ })
118
+ link(node, activeSink)
119
+ }
120
+ }
121
+ : () => {
122
+ if (activeSink) link(node, activeSink)
123
+ }
124
+
97
125
  return {
98
126
  [Symbol.toStringTag]: TYPE_MEMO,
99
127
  get() {
100
- if (activeSink) link(node, activeSink)
128
+ subscribe()
101
129
  refresh(node as unknown as SinkNode)
102
130
  if (node.error) throw node.error
103
131
  validateReadValue(TYPE_MEMO, node.value)
@@ -6,9 +6,9 @@ import {
6
6
  import {
7
7
  activeSink,
8
8
  type Cleanup,
9
- type ComputedOptions,
10
- defaultEquals,
9
+ DEFAULT_EQUALITY,
11
10
  link,
11
+ type SignalOptions,
12
12
  type StateNode,
13
13
  setState,
14
14
  TYPE_SENSOR,
@@ -27,7 +27,6 @@ type Sensor<T extends {}> = {
27
27
 
28
28
  /**
29
29
  * Gets the current value of the sensor.
30
- * Updates its state value if the sensor is active.
31
30
  * When called inside another reactive context, creates a dependency.
32
31
  * @returns The sensor value
33
32
  * @throws UnsetSignalValueError If the sensor value is still unset when read.
@@ -42,6 +41,14 @@ type Sensor<T extends {}> = {
42
41
  * @param set - A function to set the observed value
43
42
  * @returns A cleanup function when the sensor stops being watched
44
43
  */
44
+ type SensorOptions<T extends {}> = SignalOptions<T> & {
45
+ /**
46
+ * Optional initial value. Avoids `UnsetSignalValueError` on first read
47
+ * before the watched callback fires.
48
+ */
49
+ value?: T
50
+ }
51
+
45
52
  type SensorCallback<T extends {}> = (set: (next: T) => void) => Cleanup
46
53
 
47
54
  /* === Exported Functions === */
@@ -52,12 +59,12 @@ type SensorCallback<T extends {}> = (set: (next: T) => void) => Cleanup
52
59
  * no longer watched. This lazy activation pattern ensures resources are only consumed when needed.
53
60
  *
54
61
  * @since 0.18.0
55
- * @template T - The type of value stored in the state
56
- * @param start - The callback function that starts the sensor and returns a cleanup function.
57
- * @param options - Optional options for the sensor.
62
+ * @template T - The type of value produced by the sensor
63
+ * @param watched - The callback invoked when the sensor starts being watched, receives a `set` function and returns a cleanup function.
64
+ * @param options - Optional configuration for the sensor.
58
65
  * @param options.value - Optional initial value. Avoids `UnsetSignalValueError` on first read
59
- * before the start callback fires. Essential for the mutable-object observation pattern.
60
- * @param options.equals - Optional equality function. Defaults to `Object.is`. Use `SKIP_EQUALITY`
66
+ * before the watched callback fires. Essential for the mutable-object observation pattern.
67
+ * @param options.equals - Optional equality function. Defaults to strict equality (`===`). Use `SKIP_EQUALITY`
61
68
  * for mutable objects where the reference stays the same but internal state changes.
62
69
  * @param options.guard - Optional type guard to validate values.
63
70
  * @returns A read-only sensor signal.
@@ -87,10 +94,10 @@ type SensorCallback<T extends {}> = (set: (next: T) => void) => Cleanup
87
94
  * ```
88
95
  */
89
96
  function createSensor<T extends {}>(
90
- start: SensorCallback<T>,
91
- options?: ComputedOptions<T>,
97
+ watched: SensorCallback<T>,
98
+ options?: SensorOptions<T>,
92
99
  ): Sensor<T> {
93
- validateCallback(TYPE_SENSOR, start, isSyncFunction)
100
+ validateCallback(TYPE_SENSOR, watched, isSyncFunction)
94
101
  if (options?.value !== undefined)
95
102
  validateSignalValue(TYPE_SENSOR, options.value, options?.guard)
96
103
 
@@ -98,7 +105,7 @@ function createSensor<T extends {}>(
98
105
  value: options?.value as T,
99
106
  sinks: null,
100
107
  sinksTail: null,
101
- equals: options?.equals ?? defaultEquals,
108
+ equals: options?.equals ?? DEFAULT_EQUALITY,
102
109
  guard: options?.guard,
103
110
  stop: undefined,
104
111
  }
@@ -107,11 +114,8 @@ function createSensor<T extends {}>(
107
114
  [Symbol.toStringTag]: TYPE_SENSOR,
108
115
  get(): T {
109
116
  if (activeSink) {
110
- // Start fires before link: synchronous set() inside start updates
111
- // node.value without propagation (no sinks yet). The activating
112
- // effect reads the updated value directly after link.
113
117
  if (!node.sinks)
114
- node.stop = start((next: T): void => {
118
+ node.stop = watched((next: T): void => {
115
119
  validateSignalValue(TYPE_SENSOR, next, node.guard)
116
120
  setState(node, next)
117
121
  })
@@ -136,4 +140,10 @@ function isSensor<T extends {} = unknown & {}>(
136
140
  return isObjectOfType(value, TYPE_SENSOR)
137
141
  }
138
142
 
139
- export { createSensor, isSensor, type Sensor, type SensorCallback }
143
+ export {
144
+ createSensor,
145
+ isSensor,
146
+ type Sensor,
147
+ type SensorCallback,
148
+ type SensorOptions,
149
+ }
@@ -1,7 +1,7 @@
1
1
  import { validateCallback, validateSignalValue } from '../errors'
2
2
  import {
3
3
  activeSink,
4
- defaultEquals,
4
+ DEFAULT_EQUALITY,
5
5
  link,
6
6
  type SignalOptions,
7
7
  type StateNode,
@@ -88,7 +88,7 @@ function createState<T extends {}>(
88
88
  value,
89
89
  sinks: null,
90
90
  sinksTail: null,
91
- equals: options?.equals ?? defaultEquals,
91
+ equals: options?.equals ?? DEFAULT_EQUALITY,
92
92
  guard: options?.guard,
93
93
  }
94
94