@zeix/cause-effect 0.16.0 → 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.
- package/.ai-context.md +71 -21
- package/.cursorrules +3 -2
- package/.github/copilot-instructions.md +59 -13
- package/CLAUDE.md +170 -24
- package/LICENSE +1 -1
- package/README.md +156 -52
- package/archive/benchmark.ts +688 -0
- package/archive/collection.ts +312 -0
- package/{src → archive}/computed.ts +33 -34
- package/archive/list.ts +551 -0
- package/archive/memo.ts +138 -0
- package/archive/state.ts +89 -0
- package/archive/store.ts +368 -0
- package/archive/task.ts +194 -0
- package/eslint.config.js +1 -0
- package/index.dev.js +902 -501
- package/index.js +1 -1
- package/index.ts +42 -22
- package/package.json +1 -1
- package/src/classes/collection.ts +272 -0
- package/src/classes/composite.ts +176 -0
- package/src/classes/computed.ts +333 -0
- package/src/classes/list.ts +304 -0
- package/src/classes/state.ts +98 -0
- package/src/classes/store.ts +210 -0
- package/src/diff.ts +28 -52
- package/src/effect.ts +9 -9
- package/src/errors.ts +50 -25
- package/src/signal.ts +58 -41
- package/src/system.ts +79 -42
- package/src/util.ts +16 -34
- package/test/batch.test.ts +15 -17
- package/test/benchmark.test.ts +4 -4
- package/test/collection.test.ts +796 -0
- package/test/computed.test.ts +138 -130
- package/test/diff.test.ts +2 -2
- package/test/effect.test.ts +36 -35
- package/test/list.test.ts +754 -0
- package/test/match.test.ts +25 -25
- package/test/resolve.test.ts +17 -19
- package/test/signal.test.ts +72 -121
- package/test/state.test.ts +44 -44
- package/test/store.test.ts +344 -1663
- package/types/index.d.ts +11 -9
- package/types/src/classes/collection.d.ts +32 -0
- package/types/src/classes/composite.d.ts +15 -0
- package/types/src/classes/computed.d.ts +97 -0
- package/types/src/classes/list.d.ts +41 -0
- package/types/src/classes/state.d.ts +52 -0
- package/types/src/classes/store.d.ts +51 -0
- package/types/src/diff.d.ts +8 -12
- package/types/src/errors.d.ts +12 -11
- package/types/src/signal.d.ts +27 -14
- package/types/src/system.d.ts +41 -20
- package/types/src/util.d.ts +6 -3
- package/src/state.ts +0 -98
- package/src/store.ts +0 -525
- package/types/src/collection.d.ts +0 -26
- package/types/src/computed.d.ts +0 -33
- package/types/src/scheduler.d.ts +0 -55
- package/types/src/state.d.ts +0 -24
- package/types/src/store.d.ts +0 -66
package/src/store.ts
DELETED
|
@@ -1,525 +0,0 @@
|
|
|
1
|
-
import { isComputed } from './computed'
|
|
2
|
-
import {
|
|
3
|
-
type ArrayToRecord,
|
|
4
|
-
diff,
|
|
5
|
-
type UnknownArray,
|
|
6
|
-
type UnknownRecord,
|
|
7
|
-
type UnknownRecordOrArray,
|
|
8
|
-
} from './diff'
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
InvalidSignalValueError,
|
|
12
|
-
NullishSignalValueError,
|
|
13
|
-
StoreKeyExistsError,
|
|
14
|
-
StoreKeyRangeError,
|
|
15
|
-
StoreKeyReadonlyError,
|
|
16
|
-
} from './errors'
|
|
17
|
-
import { isMutableSignal, type Signal } from './signal'
|
|
18
|
-
import { createState, isState, type State } from './state'
|
|
19
|
-
import {
|
|
20
|
-
batch,
|
|
21
|
-
type Cleanup,
|
|
22
|
-
createWatcher,
|
|
23
|
-
notify,
|
|
24
|
-
observe,
|
|
25
|
-
subscribe,
|
|
26
|
-
type Watcher,
|
|
27
|
-
} from './system'
|
|
28
|
-
import {
|
|
29
|
-
isFunction,
|
|
30
|
-
isObjectOfType,
|
|
31
|
-
isRecord,
|
|
32
|
-
isSymbol,
|
|
33
|
-
recordToArray,
|
|
34
|
-
UNSET,
|
|
35
|
-
valueString,
|
|
36
|
-
} from './util'
|
|
37
|
-
|
|
38
|
-
/* === Types === */
|
|
39
|
-
|
|
40
|
-
type ArrayItem<T> = T extends readonly (infer U extends {})[] ? U : never
|
|
41
|
-
|
|
42
|
-
type StoreChanges<T> = {
|
|
43
|
-
add: Partial<T>
|
|
44
|
-
change: Partial<T>
|
|
45
|
-
remove: Partial<T>
|
|
46
|
-
sort: string[]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type StoreListeners<T> = {
|
|
50
|
-
[K in keyof StoreChanges<T>]: Set<(change: StoreChanges<T>[K]) => void>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface BaseStore {
|
|
54
|
-
readonly [Symbol.toStringTag]: 'Store'
|
|
55
|
-
readonly size: State<number>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type RecordStore<T extends UnknownRecord> = BaseStore & {
|
|
59
|
-
[K in keyof T]: T[K] extends readonly unknown[] | Record<string, unknown>
|
|
60
|
-
? Store<T[K]>
|
|
61
|
-
: State<T[K]>
|
|
62
|
-
} & {
|
|
63
|
-
[Symbol.iterator](): IterableIterator<
|
|
64
|
-
[
|
|
65
|
-
Extract<keyof T, string>,
|
|
66
|
-
T[Extract<keyof T, string>] extends
|
|
67
|
-
| readonly unknown[]
|
|
68
|
-
| Record<string, unknown>
|
|
69
|
-
? Store<T[Extract<keyof T, string>]>
|
|
70
|
-
: State<T[Extract<keyof T, string>]>,
|
|
71
|
-
]
|
|
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
|
|
85
|
-
}
|
|
86
|
-
|
|
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
|
|
94
|
-
[n: number]: ArrayItem<T> extends
|
|
95
|
-
| readonly unknown[]
|
|
96
|
-
| Record<string, unknown>
|
|
97
|
-
? Store<ArrayItem<T>>
|
|
98
|
-
: State<ArrayItem<T>>
|
|
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
|
|
108
|
-
remove(index: number): void
|
|
109
|
-
readonly length: number
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
type Store<T extends UnknownRecord | UnknownArray> = T extends UnknownRecord
|
|
113
|
-
? RecordStore<T>
|
|
114
|
-
: T extends UnknownArray
|
|
115
|
-
? ArrayStore<T>
|
|
116
|
-
: never
|
|
117
|
-
|
|
118
|
-
/* === Constants === */
|
|
119
|
-
|
|
120
|
-
const TYPE_STORE = 'Store'
|
|
121
|
-
|
|
122
|
-
/* === Functions === */
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Create a new store with deeply nested reactive properties
|
|
126
|
-
*
|
|
127
|
-
* Supports both objects and arrays as initial values. Arrays are converted
|
|
128
|
-
* to records internally for storage but maintain their array type through
|
|
129
|
-
* the .get() method, which automatically converts objects with consecutive
|
|
130
|
-
* numeric keys back to arrays.
|
|
131
|
-
*
|
|
132
|
-
* @since 0.15.0
|
|
133
|
-
* @param {T} initialValue - initial object or array value of the store
|
|
134
|
-
* @returns {Store<T>} - new store with reactive properties that preserves the original type T
|
|
135
|
-
*/
|
|
136
|
-
const createStore = <T extends UnknownRecord | UnknownArray>(
|
|
137
|
-
initialValue: T,
|
|
138
|
-
): Store<T> => {
|
|
139
|
-
if (initialValue == null) throw new NullishSignalValueError('store')
|
|
140
|
-
|
|
141
|
-
const watchers = new Set<Watcher>()
|
|
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
|
-
}
|
|
148
|
-
const signals = new Map<string, Signal<T[Extract<keyof T, string>] & {}>>()
|
|
149
|
-
const signalWatchers = new Map<string, Watcher>()
|
|
150
|
-
|
|
151
|
-
// Determine if this is an array-like store at creation time
|
|
152
|
-
const isArrayLike = Array.isArray(initialValue)
|
|
153
|
-
|
|
154
|
-
// Internal state
|
|
155
|
-
const size = createState(0)
|
|
156
|
-
|
|
157
|
-
// Get current record
|
|
158
|
-
const current = () => {
|
|
159
|
-
const record: Record<string, unknown> = {}
|
|
160
|
-
for (const [key, signal] of signals) {
|
|
161
|
-
record[key] = signal.get()
|
|
162
|
-
}
|
|
163
|
-
return record
|
|
164
|
-
}
|
|
165
|
-
|
|
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
|
-
}
|
|
176
|
-
|
|
177
|
-
// Get sorted indexes
|
|
178
|
-
const getSortedIndexes = () =>
|
|
179
|
-
Array.from(signals.keys())
|
|
180
|
-
.map(k => Number(k))
|
|
181
|
-
.filter(n => Number.isInteger(n))
|
|
182
|
-
.sort((a, b) => a - b)
|
|
183
|
-
|
|
184
|
-
// Validate input
|
|
185
|
-
const isValidValue = <T>(
|
|
186
|
-
key: string,
|
|
187
|
-
value: T,
|
|
188
|
-
): value is NonNullable<T> => {
|
|
189
|
-
if (value == null)
|
|
190
|
-
throw new NullishSignalValueError(`store for key "${key}"`)
|
|
191
|
-
if (value === UNSET) return true
|
|
192
|
-
if (isSymbol(value) || isFunction(value) || isComputed(value))
|
|
193
|
-
throw new InvalidSignalValueError(
|
|
194
|
-
`store for key "${key}"`,
|
|
195
|
-
valueString(value),
|
|
196
|
-
)
|
|
197
|
-
return true
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Add nested signal and effect
|
|
201
|
-
const addProperty = <K extends Extract<keyof T, string>>(
|
|
202
|
-
key: K,
|
|
203
|
-
value: T[K] | ArrayItem<T>,
|
|
204
|
-
single = false,
|
|
205
|
-
): boolean => {
|
|
206
|
-
if (!isValidValue(key, value)) return false
|
|
207
|
-
const signal =
|
|
208
|
-
isState(value) || isStore(value)
|
|
209
|
-
? value
|
|
210
|
-
: isRecord(value) || Array.isArray(value)
|
|
211
|
-
? createStore(value)
|
|
212
|
-
: createState(value)
|
|
213
|
-
// @ts-expect-error non-matching signal types
|
|
214
|
-
signals.set(key, signal)
|
|
215
|
-
const watcher = createWatcher(() =>
|
|
216
|
-
observe(() => {
|
|
217
|
-
emit('change', {
|
|
218
|
-
[key]: signal.get(),
|
|
219
|
-
} as unknown as Partial<T>)
|
|
220
|
-
}, watcher),
|
|
221
|
-
)
|
|
222
|
-
watcher()
|
|
223
|
-
signalWatchers.set(key, watcher)
|
|
224
|
-
|
|
225
|
-
if (single) {
|
|
226
|
-
size.set(signals.size)
|
|
227
|
-
notify(watchers)
|
|
228
|
-
emit('add', {
|
|
229
|
-
[key]: value,
|
|
230
|
-
} as unknown as Partial<T>)
|
|
231
|
-
}
|
|
232
|
-
return true
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Remove nested signal and effect
|
|
236
|
-
const removeProperty = <K extends Extract<keyof T, string>>(
|
|
237
|
-
key: K,
|
|
238
|
-
single = false,
|
|
239
|
-
) => {
|
|
240
|
-
const ok = signals.delete(key)
|
|
241
|
-
if (ok) {
|
|
242
|
-
const watcher = signalWatchers.get(key)
|
|
243
|
-
if (watcher) watcher.cleanup()
|
|
244
|
-
signalWatchers.delete(key)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (single) {
|
|
248
|
-
size.set(signals.size)
|
|
249
|
-
notify(watchers)
|
|
250
|
-
emit('remove', {
|
|
251
|
-
[key]: UNSET,
|
|
252
|
-
} as unknown as Partial<T>)
|
|
253
|
-
}
|
|
254
|
-
return ok
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Reconcile data and dispatch events
|
|
258
|
-
const reconcile = (
|
|
259
|
-
oldValue: T,
|
|
260
|
-
newValue: T,
|
|
261
|
-
initialRun?: boolean,
|
|
262
|
-
): boolean => {
|
|
263
|
-
const changes = diff(
|
|
264
|
-
oldValue as T extends UnknownArray ? ArrayToRecord<T> : T,
|
|
265
|
-
newValue as T extends UnknownArray ? ArrayToRecord<T> : T,
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
batch(() => {
|
|
269
|
-
// Additions
|
|
270
|
-
if (Object.keys(changes.add).length) {
|
|
271
|
-
for (const key in changes.add) {
|
|
272
|
-
const value = changes.add[key] ?? UNSET
|
|
273
|
-
addProperty(
|
|
274
|
-
key as Extract<keyof T, string>,
|
|
275
|
-
value as T[Extract<keyof T, string>] & {},
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Queue initial additions event to allow listeners to be added first
|
|
280
|
-
if (initialRun) {
|
|
281
|
-
setTimeout(() => {
|
|
282
|
-
emit('add', changes.add as Partial<T>)
|
|
283
|
-
}, 0)
|
|
284
|
-
} else {
|
|
285
|
-
emit('add', changes.add as Partial<T>)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Changes
|
|
290
|
-
if (Object.keys(changes.change).length) {
|
|
291
|
-
for (const key in changes.change) {
|
|
292
|
-
const value = changes.change[key]
|
|
293
|
-
if (!isValidValue(key, value)) continue
|
|
294
|
-
const signal = signals.get(key as Extract<keyof T, string>)
|
|
295
|
-
if (isMutableSignal(signal))
|
|
296
|
-
signal.set(value as T[Extract<keyof T, string>] & {})
|
|
297
|
-
else
|
|
298
|
-
throw new StoreKeyReadonlyError(key, valueString(value))
|
|
299
|
-
}
|
|
300
|
-
emit('change', changes.change as Partial<T>)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Removals
|
|
304
|
-
if (Object.keys(changes.remove).length) {
|
|
305
|
-
for (const key in changes.remove)
|
|
306
|
-
removeProperty(key as Extract<keyof T, string>)
|
|
307
|
-
emit('remove', changes.remove as Partial<T>)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
size.set(signals.size)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
return changes.changed
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Initialize data - convert arrays to records for internal storage
|
|
317
|
-
reconcile({} as T, initialValue, true)
|
|
318
|
-
|
|
319
|
-
// Methods and Properties
|
|
320
|
-
const store: Record<string, unknown> = {
|
|
321
|
-
add: isArrayLike
|
|
322
|
-
? (v: ArrayItem<T>): void => {
|
|
323
|
-
const nextIndex = signals.size
|
|
324
|
-
const key = String(nextIndex) as Extract<keyof T, string>
|
|
325
|
-
addProperty(key, v, true)
|
|
326
|
-
}
|
|
327
|
-
: <K extends Extract<keyof T, string>>(k: K, v: T[K]): void => {
|
|
328
|
-
if (!signals.has(k)) addProperty(k, v, true)
|
|
329
|
-
else throw new StoreKeyExistsError(k, valueString(v))
|
|
330
|
-
},
|
|
331
|
-
get: (): T => {
|
|
332
|
-
subscribe(watchers)
|
|
333
|
-
return recordToArray(current()) as T
|
|
334
|
-
},
|
|
335
|
-
remove: isArrayLike
|
|
336
|
-
? (index: number): void => {
|
|
337
|
-
const currentArray = recordToArray(current()) as T
|
|
338
|
-
const currentLength = signals.size
|
|
339
|
-
if (
|
|
340
|
-
!Array.isArray(currentArray) ||
|
|
341
|
-
index <= -currentLength ||
|
|
342
|
-
index >= currentLength
|
|
343
|
-
)
|
|
344
|
-
throw new StoreKeyRangeError(index)
|
|
345
|
-
const newArray = [...currentArray]
|
|
346
|
-
newArray.splice(index, 1)
|
|
347
|
-
|
|
348
|
-
if (reconcile(currentArray, newArray as unknown as T))
|
|
349
|
-
notify(watchers)
|
|
350
|
-
}
|
|
351
|
-
: <K extends Extract<keyof T, string>>(k: K): void => {
|
|
352
|
-
if (signals.has(k)) removeProperty(k, true)
|
|
353
|
-
},
|
|
354
|
-
set: (v: T): void => {
|
|
355
|
-
if (reconcile(current() as T, v)) {
|
|
356
|
-
notify(watchers)
|
|
357
|
-
if (UNSET === v) watchers.clear()
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
update: (fn: (v: T) => T): void => {
|
|
361
|
-
const oldValue = current()
|
|
362
|
-
const newValue = fn(recordToArray(oldValue) as T)
|
|
363
|
-
if (reconcile(oldValue as T, newValue)) {
|
|
364
|
-
notify(watchers)
|
|
365
|
-
if (UNSET === newValue) watchers.clear()
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
sort: (
|
|
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(
|
|
381
|
-
([key, signal]) =>
|
|
382
|
-
[key, signal.get()] as [
|
|
383
|
-
string,
|
|
384
|
-
T[Extract<keyof T, string>],
|
|
385
|
-
],
|
|
386
|
-
)
|
|
387
|
-
.sort(
|
|
388
|
-
compareFn
|
|
389
|
-
? (a, b) => compareFn(a[1], b[1])
|
|
390
|
-
: (a, b) => String(a[1]).localeCompare(String(b[1])),
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
// Create array of original keys in their new sorted order
|
|
394
|
-
const newOrder: string[] = entries.map(([key]) => String(key))
|
|
395
|
-
const newSignals = new Map<
|
|
396
|
-
string,
|
|
397
|
-
Signal<T[Extract<keyof T, string>] & {}>
|
|
398
|
-
>()
|
|
399
|
-
|
|
400
|
-
entries.forEach(([key], newIndex) => {
|
|
401
|
-
const oldKey = String(key)
|
|
402
|
-
const newKey = isArrayLike ? String(newIndex) : String(key)
|
|
403
|
-
|
|
404
|
-
const signal = signals.get(oldKey)
|
|
405
|
-
if (signal) newSignals.set(newKey, signal)
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
// Replace signals map
|
|
409
|
-
signals.clear()
|
|
410
|
-
newSignals.forEach((signal, key) => signals.set(key, signal))
|
|
411
|
-
notify(watchers)
|
|
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)
|
|
420
|
-
},
|
|
421
|
-
size,
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Return proxy directly with integrated signal methods
|
|
425
|
-
return new Proxy({} as Store<T>, {
|
|
426
|
-
get(_target, prop) {
|
|
427
|
-
// Symbols
|
|
428
|
-
if (prop === Symbol.toStringTag) return TYPE_STORE
|
|
429
|
-
if (prop === Symbol.isConcatSpreadable) return isArrayLike
|
|
430
|
-
if (prop === Symbol.iterator)
|
|
431
|
-
return isArrayLike
|
|
432
|
-
? function* () {
|
|
433
|
-
const indexes = getSortedIndexes()
|
|
434
|
-
for (const index of indexes) {
|
|
435
|
-
const signal = signals.get(
|
|
436
|
-
String(index) as Extract<keyof T, string>,
|
|
437
|
-
)
|
|
438
|
-
if (signal) yield signal
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
: function* () {
|
|
442
|
-
for (const [key, signal] of signals)
|
|
443
|
-
yield [key, signal]
|
|
444
|
-
}
|
|
445
|
-
if (isSymbol(prop)) return undefined
|
|
446
|
-
|
|
447
|
-
// Methods and Properties
|
|
448
|
-
if (prop in store) return store[prop]
|
|
449
|
-
if (prop === 'length' && isArrayLike) {
|
|
450
|
-
subscribe(watchers)
|
|
451
|
-
return size.get()
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Signals
|
|
455
|
-
return signals.get(prop as Extract<keyof T, string>)
|
|
456
|
-
},
|
|
457
|
-
has(_target, prop) {
|
|
458
|
-
const stringProp = String(prop)
|
|
459
|
-
return (
|
|
460
|
-
(stringProp &&
|
|
461
|
-
signals.has(stringProp as Extract<keyof T, string>)) ||
|
|
462
|
-
Object.keys(store).includes(stringProp) ||
|
|
463
|
-
prop === Symbol.toStringTag ||
|
|
464
|
-
prop === Symbol.iterator ||
|
|
465
|
-
prop === Symbol.isConcatSpreadable ||
|
|
466
|
-
(prop === 'length' && isArrayLike)
|
|
467
|
-
)
|
|
468
|
-
},
|
|
469
|
-
ownKeys() {
|
|
470
|
-
return isArrayLike
|
|
471
|
-
? getSortedIndexes()
|
|
472
|
-
.map(key => String(key))
|
|
473
|
-
.concat(['length'])
|
|
474
|
-
: Array.from(signals.keys()).map(key => String(key))
|
|
475
|
-
},
|
|
476
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
477
|
-
const nonEnumerable = <T>(value: T) => ({
|
|
478
|
-
enumerable: false,
|
|
479
|
-
configurable: true,
|
|
480
|
-
writable: false,
|
|
481
|
-
value,
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
if (prop === 'length' && isArrayLike)
|
|
485
|
-
return {
|
|
486
|
-
enumerable: true,
|
|
487
|
-
configurable: true,
|
|
488
|
-
writable: false,
|
|
489
|
-
value: size.get(),
|
|
490
|
-
}
|
|
491
|
-
if (prop === Symbol.isConcatSpreadable)
|
|
492
|
-
return nonEnumerable(isArrayLike)
|
|
493
|
-
if (prop === Symbol.toStringTag) return nonEnumerable(TYPE_STORE)
|
|
494
|
-
if (isSymbol(prop)) return undefined
|
|
495
|
-
|
|
496
|
-
if (Object.keys(store).includes(prop))
|
|
497
|
-
return nonEnumerable(store[prop])
|
|
498
|
-
|
|
499
|
-
const signal = signals.get(prop as Extract<keyof T, string>)
|
|
500
|
-
return signal
|
|
501
|
-
? {
|
|
502
|
-
enumerable: true,
|
|
503
|
-
configurable: true,
|
|
504
|
-
writable: true,
|
|
505
|
-
value: signal,
|
|
506
|
-
}
|
|
507
|
-
: undefined
|
|
508
|
-
},
|
|
509
|
-
})
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Check if the provided value is a Store instance
|
|
514
|
-
*
|
|
515
|
-
* @since 0.15.0
|
|
516
|
-
* @param {unknown} value - value to check
|
|
517
|
-
* @returns {boolean} - true if the value is a Store instance, false otherwise
|
|
518
|
-
*/
|
|
519
|
-
const isStore = <T extends UnknownRecordOrArray>(
|
|
520
|
-
value: unknown,
|
|
521
|
-
): value is Store<T> => isObjectOfType(value, TYPE_STORE)
|
|
522
|
-
|
|
523
|
-
/* === Exports === */
|
|
524
|
-
|
|
525
|
-
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 };
|
package/types/src/computed.d.ts
DELETED
|
@@ -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, };
|
package/types/src/scheduler.d.ts
DELETED
|
@@ -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, };
|
package/types/src/state.d.ts
DELETED
|
@@ -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 };
|