@zeix/cause-effect 0.16.1 → 0.17.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.
Files changed (61) hide show
  1. package/.ai-context.md +71 -21
  2. package/.cursorrules +3 -2
  3. package/.github/copilot-instructions.md +59 -13
  4. package/CLAUDE.md +170 -24
  5. package/LICENSE +1 -1
  6. package/README.md +156 -52
  7. package/archive/benchmark.ts +688 -0
  8. package/archive/collection.ts +312 -0
  9. package/{src → archive}/computed.ts +19 -19
  10. package/archive/list.ts +551 -0
  11. package/archive/memo.ts +138 -0
  12. package/{src → archive}/state.ts +13 -11
  13. package/archive/store.ts +368 -0
  14. package/archive/task.ts +194 -0
  15. package/eslint.config.js +1 -0
  16. package/index.dev.js +899 -503
  17. package/index.js +1 -1
  18. package/index.ts +41 -22
  19. package/package.json +1 -1
  20. package/src/classes/collection.ts +272 -0
  21. package/src/classes/composite.ts +176 -0
  22. package/src/classes/computed.ts +333 -0
  23. package/src/classes/list.ts +304 -0
  24. package/src/classes/state.ts +98 -0
  25. package/src/classes/store.ts +210 -0
  26. package/src/diff.ts +26 -53
  27. package/src/effect.ts +9 -9
  28. package/src/errors.ts +50 -25
  29. package/src/signal.ts +58 -41
  30. package/src/system.ts +79 -42
  31. package/src/util.ts +16 -30
  32. package/test/batch.test.ts +15 -17
  33. package/test/benchmark.test.ts +4 -4
  34. package/test/collection.test.ts +796 -0
  35. package/test/computed.test.ts +138 -130
  36. package/test/diff.test.ts +2 -2
  37. package/test/effect.test.ts +36 -35
  38. package/test/list.test.ts +754 -0
  39. package/test/match.test.ts +25 -25
  40. package/test/resolve.test.ts +17 -19
  41. package/test/signal.test.ts +70 -119
  42. package/test/state.test.ts +44 -44
  43. package/test/store.test.ts +253 -929
  44. package/types/index.d.ts +10 -8
  45. package/types/src/classes/collection.d.ts +32 -0
  46. package/types/src/classes/composite.d.ts +15 -0
  47. package/types/src/classes/computed.d.ts +97 -0
  48. package/types/src/classes/list.d.ts +41 -0
  49. package/types/src/classes/state.d.ts +52 -0
  50. package/types/src/classes/store.d.ts +51 -0
  51. package/types/src/diff.d.ts +8 -12
  52. package/types/src/errors.d.ts +12 -11
  53. package/types/src/signal.d.ts +27 -14
  54. package/types/src/system.d.ts +41 -20
  55. package/types/src/util.d.ts +6 -3
  56. package/src/store.ts +0 -474
  57. package/types/src/collection.d.ts +0 -26
  58. package/types/src/computed.d.ts +0 -33
  59. package/types/src/scheduler.d.ts +0 -55
  60. package/types/src/state.d.ts +0 -24
  61. package/types/src/store.d.ts +0 -65
package/src/store.ts DELETED
@@ -1,474 +0,0 @@
1
- import { isComputed } from './computed'
2
- import {
3
- type ArrayToRecord,
4
- diff,
5
- type PartialRecord,
6
- type UnknownArray,
7
- type UnknownRecord,
8
- } from './diff'
9
- import {
10
- InvalidSignalValueError,
11
- NullishSignalValueError,
12
- StoreKeyExistsError,
13
- StoreKeyRangeError,
14
- StoreKeyReadonlyError,
15
- } from './errors'
16
- import { isMutableSignal, type Signal } from './signal'
17
- import { createState, isState, type State } from './state'
18
- import {
19
- batch,
20
- type Cleanup,
21
- createWatcher,
22
- notify,
23
- observe,
24
- subscribe,
25
- type Watcher,
26
- } from './system'
27
- import {
28
- isFunction,
29
- isObjectOfType,
30
- isRecord,
31
- isSymbol,
32
- recordToArray,
33
- UNSET,
34
- valueString,
35
- } from './util'
36
-
37
- /* === Types === */
38
-
39
- type ArrayItem<T> = T extends readonly (infer U extends {})[] ? U : never
40
-
41
- type StoreChanges<T> = {
42
- add: PartialRecord<T>
43
- change: PartialRecord<T>
44
- remove: PartialRecord<T>
45
- sort: string[]
46
- }
47
-
48
- type StoreListeners<T> = {
49
- [K in keyof StoreChanges<T>]: Set<(change: StoreChanges<T>[K]) => void>
50
- }
51
-
52
- interface BaseStore {
53
- readonly [Symbol.toStringTag]: 'Store'
54
- readonly length: number
55
- }
56
-
57
- type RecordStore<T extends UnknownRecord> = BaseStore & {
58
- [K in keyof T]: T[K] extends readonly unknown[] | Record<string, unknown>
59
- ? Store<T[K]>
60
- : State<T[K]>
61
- } & {
62
- [Symbol.iterator](): IterableIterator<
63
- [
64
- Extract<keyof T, string>,
65
- T[Extract<keyof T, string>] extends
66
- | readonly unknown[]
67
- | Record<string, unknown>
68
- ? Store<T[Extract<keyof T, string>]>
69
- : State<T[Extract<keyof T, string>]>,
70
- ]
71
- >
72
- add<K extends Extract<keyof T, string>>(key: K, value: T[K]): void
73
- get(): T
74
- set(value: T): void
75
- update(fn: (value: T) => T): void
76
- sort<U = T[Extract<keyof T, string>]>(
77
- compareFn?: (a: U, b: U) => number,
78
- ): void
79
- on<K extends keyof StoreChanges<T>>(
80
- type: K,
81
- listener: (change: StoreChanges<T>[K]) => void,
82
- ): Cleanup
83
- remove<K extends Extract<keyof T, string>>(key: K): void
84
- }
85
-
86
- type ArrayStore<T extends UnknownArray> = BaseStore & {
87
- [Symbol.iterator](): IterableIterator<
88
- ArrayItem<T> extends readonly unknown[] | Record<string, unknown>
89
- ? Store<ArrayItem<T>>
90
- : State<ArrayItem<T>>
91
- >
92
- readonly [Symbol.isConcatSpreadable]: boolean
93
- [n: number]: ArrayItem<T> extends
94
- | readonly unknown[]
95
- | Record<string, unknown>
96
- ? Store<ArrayItem<T>>
97
- : State<ArrayItem<T>>
98
- add(value: ArrayItem<T>): void
99
- get(): T
100
- set(value: T): void
101
- update(fn: (value: T) => T): void
102
- sort<U = ArrayItem<T>>(compareFn?: (a: U, b: U) => number): void
103
- on<K extends keyof StoreChanges<T>>(
104
- type: K,
105
- listener: (change: StoreChanges<T>[K]) => void,
106
- ): Cleanup
107
- remove(index: number): void
108
- }
109
-
110
- type Store<T extends UnknownRecord | UnknownArray> = T extends UnknownRecord
111
- ? RecordStore<T>
112
- : T extends UnknownArray
113
- ? ArrayStore<T>
114
- : never
115
-
116
- /* === Constants === */
117
-
118
- const TYPE_STORE = 'Store'
119
-
120
- /* === Functions === */
121
-
122
- /**
123
- * Create a new store with deeply nested reactive properties
124
- *
125
- * Supports both objects and arrays as initial values. Arrays are converted
126
- * to records internally for storage but maintain their array type through
127
- * the .get() method, which automatically converts objects with consecutive
128
- * numeric keys back to arrays.
129
- *
130
- * @since 0.15.0
131
- * @param {T} initialValue - initial object or array value of the store
132
- * @returns {Store<T>} - new store with reactive properties that preserves the original type T
133
- */
134
- const createStore = <T extends UnknownRecord | UnknownArray>(
135
- initialValue: T,
136
- ): Store<T> => {
137
- if (initialValue == null) throw new NullishSignalValueError('store')
138
-
139
- const watchers = new Set<Watcher>()
140
- const listeners: StoreListeners<T> = {
141
- add: new Set<(change: PartialRecord<T>) => void>(),
142
- change: new Set<(change: PartialRecord<T>) => void>(),
143
- remove: new Set<(change: PartialRecord<T>) => void>(),
144
- sort: new Set<(change: string[]) => void>(),
145
- }
146
- const signals = new Map<string, Signal<T[Extract<keyof T, string>] & {}>>()
147
- const signalWatchers = new Map<string, Watcher>()
148
-
149
- // Determine if this is an array-like store at creation time
150
- const isArrayLike = Array.isArray(initialValue)
151
-
152
- // Get current record
153
- const current = () => {
154
- const record: Record<string, unknown> = {}
155
- for (const [key, signal] of signals) record[key] = signal.get()
156
- return record
157
- }
158
-
159
- // Emit change notifications
160
- const emit = <K extends keyof StoreChanges<T>>(
161
- key: K,
162
- changes: StoreChanges<T>[K],
163
- ) => {
164
- Object.freeze(changes)
165
- for (const listener of listeners[key]) listener(changes)
166
- }
167
-
168
- // Get sorted indexes
169
- const getSortedIndexes = () =>
170
- Array.from(signals.keys())
171
- .map(k => Number(k))
172
- .filter(n => Number.isInteger(n))
173
- .sort((a, b) => a - b)
174
-
175
- // Validate input
176
- const isValidValue = <T>(
177
- key: string,
178
- value: T,
179
- ): value is NonNullable<T> => {
180
- if (value == null)
181
- throw new NullishSignalValueError(`store for key "${key}"`)
182
- if (value === UNSET) return true
183
- if (isSymbol(value) || isFunction(value) || isComputed(value))
184
- throw new InvalidSignalValueError(
185
- `store for key "${key}"`,
186
- valueString(value),
187
- )
188
- return true
189
- }
190
-
191
- // Add nested signal and effect
192
- const addProperty = (
193
- key: string,
194
- value: ArrayItem<T> | T[keyof T],
195
- single = false,
196
- ): boolean => {
197
- if (!isValidValue(key, value)) return false
198
- const signal =
199
- isState(value) || isStore(value)
200
- ? value
201
- : isRecord(value) || Array.isArray(value)
202
- ? createStore(value)
203
- : createState(value)
204
- // @ts-expect-error non-matching signal types
205
- signals.set(key, signal)
206
- const watcher = createWatcher(() =>
207
- observe(() => {
208
- emit('change', { [key]: signal.get() } as PartialRecord<T>)
209
- }, watcher),
210
- )
211
- watcher()
212
- signalWatchers.set(key, watcher)
213
-
214
- if (single) {
215
- notify(watchers)
216
- emit('add', { [key]: value } as PartialRecord<T>)
217
- }
218
- return true
219
- }
220
-
221
- // Remove nested signal and effect
222
- const removeProperty = (key: string, single = false) => {
223
- const ok = signals.delete(key)
224
- if (ok) {
225
- const watcher = signalWatchers.get(key)
226
- if (watcher) watcher.cleanup()
227
- signalWatchers.delete(key)
228
- }
229
-
230
- if (single) {
231
- notify(watchers)
232
- emit('remove', { [key]: UNSET } as PartialRecord<T>)
233
- }
234
- return ok
235
- }
236
-
237
- // Reconcile data and dispatch events
238
- const reconcile = (
239
- oldValue: T,
240
- newValue: T,
241
- initialRun?: boolean,
242
- ): boolean => {
243
- const changes = diff(
244
- oldValue as T extends UnknownArray ? ArrayToRecord<T> : T,
245
- newValue as T extends UnknownArray ? ArrayToRecord<T> : T,
246
- )
247
-
248
- batch(() => {
249
- // Additions
250
- if (Object.keys(changes.add).length) {
251
- for (const key in changes.add)
252
- addProperty(key, changes.add[key] ?? UNSET)
253
-
254
- // Queue initial additions event to allow listeners to be added first
255
- if (initialRun) {
256
- setTimeout(() => {
257
- emit('add', changes.add)
258
- }, 0)
259
- } else {
260
- emit('add', changes.add)
261
- }
262
- }
263
-
264
- // Changes
265
- if (Object.keys(changes.change).length) {
266
- for (const key in changes.change) {
267
- const value = changes.change[key]
268
- if (!isValidValue(key, value)) continue
269
- const signal = signals.get(key as Extract<keyof T, string>)
270
- if (isMutableSignal(signal)) signal.set(value)
271
- else
272
- throw new StoreKeyReadonlyError(key, valueString(value))
273
- }
274
- emit('change', changes.change)
275
- }
276
-
277
- // Removals
278
- if (Object.keys(changes.remove).length) {
279
- for (const key in changes.remove) removeProperty(key)
280
- emit('remove', changes.remove)
281
- }
282
- })
283
-
284
- return changes.changed
285
- }
286
-
287
- // Initialize data
288
- reconcile({} as T, initialValue, true)
289
-
290
- // Methods and Properties
291
- const store: Record<PropertyKey, unknown> = {}
292
- Object.defineProperties(store, {
293
- [Symbol.toStringTag]: {
294
- value: TYPE_STORE,
295
- },
296
- [Symbol.isConcatSpreadable]: {
297
- value: isArrayLike,
298
- },
299
- [Symbol.iterator]: {
300
- value: isArrayLike
301
- ? function* () {
302
- const indexes = getSortedIndexes()
303
- for (const index of indexes) {
304
- const signal = signals.get(String(index))
305
- if (signal) yield signal
306
- }
307
- }
308
- : function* () {
309
- for (const [key, signal] of signals) yield [key, signal]
310
- },
311
- },
312
- add: {
313
- value: isArrayLike
314
- ? (v: ArrayItem<T>): void => {
315
- addProperty(String(signals.size), v, true)
316
- }
317
- : <K extends Extract<keyof T, string>>(k: K, v: T[K]): void => {
318
- if (!signals.has(k)) addProperty(k, v, true)
319
- else throw new StoreKeyExistsError(k, valueString(v))
320
- },
321
- },
322
- get: {
323
- value: (): T => {
324
- subscribe(watchers)
325
- return recordToArray(current()) as T
326
- },
327
- },
328
- remove: {
329
- value: isArrayLike
330
- ? (index: number): void => {
331
- const currentArray = recordToArray(current()) as T
332
- const currentLength = signals.size
333
- if (
334
- !Array.isArray(currentArray) ||
335
- index <= -currentLength ||
336
- index >= currentLength
337
- )
338
- throw new StoreKeyRangeError(index)
339
- const newArray = [...currentArray]
340
- newArray.splice(index, 1)
341
-
342
- if (reconcile(currentArray, newArray as unknown as T))
343
- notify(watchers)
344
- }
345
- : (k: string): void => {
346
- if (signals.has(k)) removeProperty(k, true)
347
- },
348
- },
349
- set: {
350
- value: (v: T): void => {
351
- if (reconcile(current() as T, v)) {
352
- notify(watchers)
353
- if (UNSET === v) watchers.clear()
354
- }
355
- },
356
- },
357
- update: {
358
- value: (fn: (v: T) => T): void => {
359
- const oldValue = current()
360
- const newValue = fn(recordToArray(oldValue) as T)
361
- if (reconcile(oldValue as T, newValue)) {
362
- notify(watchers)
363
- if (UNSET === newValue) watchers.clear()
364
- }
365
- },
366
- },
367
- sort: {
368
- value: (
369
- compareFn?: <
370
- U = T extends UnknownArray
371
- ? ArrayItem<T>
372
- : T[Extract<keyof T, string>],
373
- >(
374
- a: U,
375
- b: U,
376
- ) => number,
377
- ): void => {
378
- // Get all entries as [key, value] pairs
379
- const entries = Array.from(signals.entries())
380
- .map(([key, signal]) => [key, signal.get()])
381
- .sort(
382
- compareFn
383
- ? (a, b) => compareFn(a[1], b[1])
384
- : (a, b) =>
385
- String(a[1]).localeCompare(String(b[1])),
386
- )
387
-
388
- // Create array of original keys in their new sorted order
389
- const newOrder: string[] = entries.map(([key]) => String(key))
390
- const newSignals = new Map<
391
- string,
392
- Signal<T[Extract<keyof T, string>] & {}>
393
- >()
394
-
395
- entries.forEach(([key], newIndex) => {
396
- const oldKey = String(key)
397
- const newKey = isArrayLike ? String(newIndex) : String(key)
398
- const signal = signals.get(oldKey)
399
- if (signal) newSignals.set(newKey, signal)
400
- })
401
-
402
- // Replace signals map
403
- signals.clear()
404
- newSignals.forEach((signal, key) => signals.set(key, signal))
405
- notify(watchers)
406
- emit('sort', newOrder)
407
- },
408
- },
409
- on: {
410
- value: <K extends keyof StoreChanges<T>>(
411
- type: K,
412
- listener: (change: StoreChanges<T>[K]) => void,
413
- ): Cleanup => {
414
- listeners[type].add(listener)
415
- return () => listeners[type].delete(listener)
416
- },
417
- },
418
- length: {
419
- get(): number {
420
- subscribe(watchers)
421
- return signals.size
422
- },
423
- },
424
- })
425
-
426
- // Return proxy directly with integrated signal methods
427
- return new Proxy(store as Store<T>, {
428
- get(target, prop) {
429
- if (prop in target) return Reflect.get(target, prop)
430
- if (isSymbol(prop)) return undefined
431
- return signals.get(prop)
432
- },
433
- has(target, prop) {
434
- if (prop in target) return true
435
- return signals.has(String(prop))
436
- },
437
- ownKeys(target) {
438
- const staticKeys = Reflect.ownKeys(target)
439
- const signalKeys = isArrayLike
440
- ? getSortedIndexes().map(key => String(key))
441
- : Array.from(signals.keys())
442
- return [...new Set([...signalKeys, ...staticKeys])]
443
- },
444
- getOwnPropertyDescriptor(target, prop) {
445
- if (prop in target)
446
- return Reflect.getOwnPropertyDescriptor(target, prop)
447
-
448
- const signal = signals.get(String(prop))
449
- return signal
450
- ? {
451
- enumerable: true,
452
- configurable: true,
453
- writable: true,
454
- value: signal,
455
- }
456
- : undefined
457
- },
458
- })
459
- }
460
-
461
- /**
462
- * Check if the provided value is a Store instance
463
- *
464
- * @since 0.15.0
465
- * @param {unknown} value - value to check
466
- * @returns {boolean} - true if the value is a Store instance, false otherwise
467
- */
468
- const isStore = <T extends UnknownRecord | UnknownArray>(
469
- value: unknown,
470
- ): value is Store<T> => isObjectOfType(value, TYPE_STORE)
471
-
472
- /* === Exports === */
473
-
474
- export { TYPE_STORE, isStore, createStore, type Store, type StoreChanges }
@@ -1,26 +0,0 @@
1
- import type { UnknownArray } from './diff';
2
- import { type Store, type StoreChanges } from './store';
3
- import { type Cleanup } from './system';
4
- type Collection<T extends UnknownArray> = {
5
- readonly [Symbol.toStringTag]: 'Collection';
6
- get(): T;
7
- on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
8
- };
9
- type CollectionCallback<T extends UnknownArray> = (store: Store<T>) => T;
10
- declare const TYPE_COLLECTION = "Collection";
11
- /**
12
- * Create a collection signal
13
- *
14
- * @param {CollectionCallback<T>} fn - callback function to create the collection
15
- * @returns {Collection<T>} - collection signal
16
- */
17
- declare const createCollection: <T extends UnknownArray>(fn: CollectionCallback<T>) => Collection<T>;
18
- /**
19
- * Check if a value is a collection signal
20
- *
21
- * @since 0.16.0
22
- * @param {unknown} value - value to check
23
- * @returns {boolean} - true if value is a computed state, false otherwise
24
- */
25
- declare const isCollection: <T extends UnknownArray>(value: unknown) => value is Collection<T>;
26
- export { TYPE_COLLECTION, createCollection, isCollection, type Collection };
@@ -1,33 +0,0 @@
1
- type Computed<T extends {}> = {
2
- readonly [Symbol.toStringTag]: 'Computed';
3
- get(): T;
4
- };
5
- type ComputedCallback<T extends {} & {
6
- then?: undefined;
7
- }> = ((oldValue: T, abort: AbortSignal) => Promise<T>) | ((oldValue: T) => T);
8
- declare const TYPE_COMPUTED = "Computed";
9
- /**
10
- * Create a derived signal from existing signals
11
- *
12
- * @since 0.9.0
13
- * @param {ComputedCallback<T>} callback - Computation callback function
14
- * @returns {Computed<T>} - Computed signal
15
- */
16
- declare const createComputed: <T extends {}>(callback: ComputedCallback<T>, initialValue?: T) => Computed<T>;
17
- /**
18
- * Check if a value is a computed signal
19
- *
20
- * @since 0.9.0
21
- * @param {unknown} value - Value to check
22
- * @returns {boolean} - true if value is a computed signal, false otherwise
23
- */
24
- declare const isComputed: <T extends {}>(value: unknown) => value is Computed<T>;
25
- /**
26
- * Check if the provided value is a callback that may be used as input for toSignal() to derive a computed state
27
- *
28
- * @since 0.12.0
29
- * @param {unknown} value - Value to check
30
- * @returns {boolean} - true if value is a callback or callbacks object, false otherwise
31
- */
32
- declare const isComputedCallback: <T extends {}>(value: unknown) => value is ComputedCallback<T>;
33
- export { TYPE_COMPUTED, createComputed, isComputed, isComputedCallback, type Computed, type ComputedCallback, };
@@ -1,55 +0,0 @@
1
- type Cleanup = () => void;
2
- type Watcher = {
3
- (): void;
4
- off(cleanup: Cleanup): void;
5
- cleanup(): void;
6
- };
7
- type Updater = <T>() => T | boolean | undefined;
8
- /**
9
- * Create a watcher that can be used to observe changes to a signal
10
- *
11
- * @since 0.14.1
12
- * @param {() => void} notice - function to be called when the state changes
13
- * @returns {Watcher} - watcher object with off and cleanup methods
14
- */
15
- declare const watch: (notice: () => void) => Watcher;
16
- /**
17
- * Add active watcher to the Set of watchers
18
- *
19
- * @param {Set<Watcher>} watchers - watchers of the signal
20
- */
21
- declare const subscribe: (watchers: Set<Watcher>) => void;
22
- /**
23
- * Add watchers to the pending set of change notifications
24
- *
25
- * @param {Set<Watcher>} watchers - watchers of the signal
26
- */
27
- declare const notify: (watchers: Set<Watcher>) => void;
28
- /**
29
- * Flush all pending changes to notify watchers
30
- */
31
- declare const flush: () => void;
32
- /**
33
- * Batch multiple changes in a single signal graph and DOM update cycle
34
- *
35
- * @param {() => void} fn - function with multiple signal writes to be batched
36
- */
37
- declare const batch: (fn: () => void) => void;
38
- /**
39
- * Run a function in a reactive context
40
- *
41
- * @param {() => void} run - function to run the computation or effect
42
- * @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)
43
- */
44
- declare const observe: (run: () => void, watcher?: Watcher) => void;
45
- /**
46
- * Enqueue a function to be executed on the next animation frame
47
- *
48
- * If the same Symbol is provided for multiple calls before the next animation frame,
49
- * only the latest call will be executed (deduplication).
50
- *
51
- * @param {Updater} fn - function to be executed on the next animation frame; can return updated value <T>, success <boolean> or void
52
- * @param {symbol} dedupe - Symbol for deduplication; if not provided, a unique Symbol is created ensuring the update is always executed
53
- */
54
- declare const enqueue: <T>(fn: Updater, dedupe?: symbol) => Promise<boolean | T | undefined>;
55
- export { type Cleanup, type Watcher, type Updater, subscribe, notify, flush, batch, watch, observe, enqueue, };
@@ -1,24 +0,0 @@
1
- type State<T extends {}> = {
2
- readonly [Symbol.toStringTag]: 'State';
3
- get(): T;
4
- set(newValue: T): void;
5
- update(updater: (oldValue: T) => T): void;
6
- };
7
- declare const TYPE_STATE = "State";
8
- /**
9
- * Create a new state signal
10
- *
11
- * @since 0.9.0
12
- * @param {T} initialValue - initial value of the state
13
- * @returns {State<T>} - new state signal
14
- */
15
- declare const createState: <T extends {}>(initialValue: T) => State<T>;
16
- /**
17
- * Check if the provided value is a State instance
18
- *
19
- * @since 0.9.0
20
- * @param {unknown} value - value to check
21
- * @returns {boolean} - true if the value is a State instance, false otherwise
22
- */
23
- declare const isState: <T extends {}>(value: unknown) => value is State<T>;
24
- export { TYPE_STATE, isState, createState, type State };
@@ -1,65 +0,0 @@
1
- import { type PartialRecord, type UnknownArray, type UnknownRecord } from './diff';
2
- import { type State } from './state';
3
- import { type Cleanup } from './system';
4
- type ArrayItem<T> = T extends readonly (infer U extends {})[] ? U : never;
5
- type StoreChanges<T> = {
6
- add: PartialRecord<T>;
7
- change: PartialRecord<T>;
8
- remove: PartialRecord<T>;
9
- sort: string[];
10
- };
11
- interface BaseStore {
12
- readonly [Symbol.toStringTag]: 'Store';
13
- readonly length: number;
14
- }
15
- type RecordStore<T extends UnknownRecord> = BaseStore & {
16
- [K in keyof T]: T[K] extends readonly unknown[] | Record<string, unknown> ? Store<T[K]> : State<T[K]>;
17
- } & {
18
- [Symbol.iterator](): IterableIterator<[
19
- Extract<keyof T, string>,
20
- T[Extract<keyof T, string>] extends readonly unknown[] | Record<string, unknown> ? Store<T[Extract<keyof T, string>]> : State<T[Extract<keyof T, string>]>
21
- ]>;
22
- add<K extends Extract<keyof T, string>>(key: K, value: T[K]): void;
23
- get(): T;
24
- set(value: T): void;
25
- update(fn: (value: T) => T): void;
26
- sort<U = T[Extract<keyof T, string>]>(compareFn?: (a: U, b: U) => number): void;
27
- on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
28
- remove<K extends Extract<keyof T, string>>(key: K): void;
29
- };
30
- type ArrayStore<T extends UnknownArray> = BaseStore & {
31
- [Symbol.iterator](): IterableIterator<ArrayItem<T> extends readonly unknown[] | Record<string, unknown> ? Store<ArrayItem<T>> : State<ArrayItem<T>>>;
32
- readonly [Symbol.isConcatSpreadable]: boolean;
33
- [n: number]: ArrayItem<T> extends readonly unknown[] | Record<string, unknown> ? Store<ArrayItem<T>> : State<ArrayItem<T>>;
34
- add(value: ArrayItem<T>): void;
35
- get(): T;
36
- set(value: T): void;
37
- update(fn: (value: T) => T): void;
38
- sort<U = ArrayItem<T>>(compareFn?: (a: U, b: U) => number): void;
39
- on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
40
- remove(index: number): void;
41
- };
42
- type Store<T extends UnknownRecord | UnknownArray> = T extends UnknownRecord ? RecordStore<T> : T extends UnknownArray ? ArrayStore<T> : never;
43
- declare const TYPE_STORE = "Store";
44
- /**
45
- * Create a new store with deeply nested reactive properties
46
- *
47
- * Supports both objects and arrays as initial values. Arrays are converted
48
- * to records internally for storage but maintain their array type through
49
- * the .get() method, which automatically converts objects with consecutive
50
- * numeric keys back to arrays.
51
- *
52
- * @since 0.15.0
53
- * @param {T} initialValue - initial object or array value of the store
54
- * @returns {Store<T>} - new store with reactive properties that preserves the original type T
55
- */
56
- declare const createStore: <T extends UnknownRecord | UnknownArray>(initialValue: T) => Store<T>;
57
- /**
58
- * Check if the provided value is a Store instance
59
- *
60
- * @since 0.15.0
61
- * @param {unknown} value - value to check
62
- * @returns {boolean} - true if the value is a Store instance, false otherwise
63
- */
64
- declare const isStore: <T extends UnknownRecord | UnknownArray>(value: unknown) => value is Store<T>;
65
- export { TYPE_STORE, isStore, createStore, type Store, type StoreChanges };