@zeix/cause-effect 0.17.3 → 0.18.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 +163 -232
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +166 -116
- package/ARCHITECTURE.md +274 -0
- package/CLAUDE.md +199 -143
- package/COLLECTION_REFACTORING.md +161 -0
- package/GUIDE.md +298 -0
- package/README.md +232 -197
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/index.dev.js +1325 -997
- package/index.js +1 -1
- package/index.ts +58 -74
- package/package.json +4 -1
- package/src/errors.ts +118 -74
- package/src/graph.ts +601 -0
- package/src/nodes/collection.ts +474 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +588 -0
- package/src/nodes/memo.ts +120 -0
- package/src/nodes/sensor.ts +139 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +383 -0
- package/src/nodes/task.ts +146 -0
- package/src/signal.ts +112 -66
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -62
- package/test/benchmark.test.ts +473 -487
- package/test/collection.test.ts +466 -706
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +335 -592
- package/test/memo.test.ts +380 -0
- package/test/regression.test.ts +156 -0
- package/test/scope.test.ts +191 -0
- package/test/sensor.test.ts +454 -0
- package/test/signal.test.ts +220 -213
- package/test/state.test.ts +217 -265
- package/test/store.test.ts +346 -446
- package/test/task.test.ts +395 -0
- package/test/untrack.test.ts +167 -0
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -17
- package/types/src/graph.d.ts +208 -0
- package/types/src/nodes/collection.d.ts +64 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +65 -0
- package/types/src/nodes/memo.d.ts +57 -0
- package/types/src/nodes/sensor.d.ts +75 -0
- package/types/src/nodes/state.d.ts +78 -0
- package/types/src/nodes/store.d.ts +51 -0
- package/types/src/nodes/task.d.ts +73 -0
- package/types/src/signal.d.ts +43 -29
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -683
- package/archive/collection.ts +0 -253
- package/archive/composite.ts +0 -85
- package/archive/computed.ts +0 -195
- package/archive/list.ts +0 -483
- package/archive/memo.ts +0 -139
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -298
- package/archive/task.ts +0 -189
- package/src/classes/collection.ts +0 -245
- package/src/classes/computed.ts +0 -349
- package/src/classes/list.ts +0 -343
- package/src/classes/ref.ts +0 -70
- package/src/classes/state.ts +0 -102
- package/src/classes/store.ts +0 -262
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -93
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -257
- package/test/computed.test.ts +0 -1108
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -353
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -45
- package/types/src/classes/computed.d.ts +0 -94
- package/types/src/classes/list.d.ts +0 -43
- package/types/src/classes/ref.d.ts +0 -35
- package/types/src/classes/state.d.ts +0 -49
- package/types/src/classes/store.d.ts +0 -52
- package/types/src/diff.d.ts +0 -28
- package/types/src/effect.d.ts +0 -15
- package/types/src/match.d.ts +0 -21
- package/types/src/resolve.d.ts +0 -29
- package/types/src/system.d.ts +0 -78
package/test/signal.test.ts
CHANGED
|
@@ -1,274 +1,281 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
createComputed,
|
|
4
|
+
createList,
|
|
5
|
+
createMemo,
|
|
6
|
+
createMutableSignal,
|
|
7
|
+
createScope,
|
|
4
8
|
createSignal,
|
|
9
|
+
createState,
|
|
10
|
+
createStore,
|
|
11
|
+
createTask,
|
|
12
|
+
InvalidSignalValueError,
|
|
5
13
|
isComputed,
|
|
6
14
|
isList,
|
|
15
|
+
isMemo,
|
|
16
|
+
isMutableSignal,
|
|
17
|
+
isSignal,
|
|
7
18
|
isState,
|
|
8
19
|
isStore,
|
|
20
|
+
isTask,
|
|
9
21
|
type List,
|
|
22
|
+
type Memo,
|
|
10
23
|
type Signal,
|
|
11
|
-
State,
|
|
24
|
+
type State,
|
|
12
25
|
type Store,
|
|
13
|
-
type
|
|
26
|
+
type Task,
|
|
14
27
|
} from '../index.ts'
|
|
15
28
|
|
|
16
29
|
/* === Tests === */
|
|
17
30
|
|
|
18
|
-
describe('
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
expect(result.at(0)?.get()).toEqual({ id: 1, name: 'Alice' })
|
|
29
|
-
expect(result.at(1)?.get()).toEqual({ id: 2, name: 'Bob' })
|
|
30
|
-
|
|
31
|
-
// Type inference test - now correctly returns List<{ id: number; name: string }>
|
|
32
|
-
const typedResult: List<{ id: number; name: string }> = result
|
|
33
|
-
expect(typedResult).toBeDefined()
|
|
34
|
-
})
|
|
31
|
+
describe('createComputed', () => {
|
|
32
|
+
test('creates a Memo from a sync callback', () => {
|
|
33
|
+
const count = createState(2)
|
|
34
|
+
const doubled = createComputed(() => count.get() * 2)
|
|
35
|
+
expect(isMemo(doubled)).toBe(true)
|
|
36
|
+
expect(doubled.get()).toBe(4)
|
|
37
|
+
|
|
38
|
+
const typedResult: Memo<number> = doubled
|
|
39
|
+
expect(typedResult).toBeDefined()
|
|
40
|
+
})
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
test('creates a Task from an async callback', () => {
|
|
43
|
+
const cleanup = createScope(() => {
|
|
44
|
+
const result = createComputed(async () => 'hello')
|
|
45
|
+
expect(isTask(result)).toBe(true)
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
expect(
|
|
41
|
-
expect(result.length).toBe(0)
|
|
42
|
-
expect(Object.keys(result).length).toBe(0)
|
|
47
|
+
const typedResult: Task<string> = result
|
|
48
|
+
expect(typedResult).toBeDefined()
|
|
43
49
|
})
|
|
50
|
+
cleanup()
|
|
51
|
+
})
|
|
52
|
+
})
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
describe('createSignal', () => {
|
|
55
|
+
test('converts a primitive to State', () => {
|
|
56
|
+
const result = createSignal(42)
|
|
57
|
+
expect(isState(result)).toBe(true)
|
|
58
|
+
expect(result.get()).toBe(42)
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
expect(result.age.get()).toBe(30)
|
|
60
|
+
const typedResult: State<number> = result
|
|
61
|
+
expect(typedResult).toBeDefined()
|
|
62
|
+
})
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
test('converts a non-plain object to State', () => {
|
|
65
|
+
const date = new Date('2024-01-01')
|
|
66
|
+
const result = createSignal(date)
|
|
67
|
+
expect(isState(result)).toBe(true)
|
|
68
|
+
expect(result.get()).toBe(date)
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
const typedResult: State<Date> = result
|
|
71
|
+
expect(typedResult).toBeDefined()
|
|
72
|
+
})
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
test('converts a record to Store', () => {
|
|
75
|
+
const result = createSignal({ name: 'Alice', age: 30 })
|
|
76
|
+
expect(isStore(result)).toBe(true)
|
|
77
|
+
expect(result.name.get()).toBe('Alice')
|
|
78
|
+
expect(result.age.get()).toBe(30)
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
80
|
+
const typedResult: Store<{ name: string; age: number }> = result
|
|
81
|
+
expect(typedResult).toBeDefined()
|
|
82
|
+
})
|
|
71
83
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
test('converts an array to List', () => {
|
|
85
|
+
const result = createSignal([
|
|
86
|
+
{ id: 1, name: 'Alice' },
|
|
87
|
+
{ id: 2, name: 'Bob' },
|
|
88
|
+
])
|
|
89
|
+
expect(isList(result)).toBe(true)
|
|
90
|
+
expect(result.at(0)?.get()).toEqual({ id: 1, name: 'Alice' })
|
|
91
|
+
expect(result.at(1)?.get()).toEqual({ id: 2, name: 'Bob' })
|
|
92
|
+
|
|
93
|
+
const typedResult: List<{ id: number; name: string }> = result
|
|
94
|
+
expect(typedResult).toBeDefined()
|
|
95
|
+
})
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
test('converts an empty array to List', () => {
|
|
98
|
+
const result = createSignal([])
|
|
99
|
+
expect(isList(result)).toBe(true)
|
|
100
|
+
expect(result.length).toBe(0)
|
|
101
|
+
})
|
|
79
102
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
test('converts a sync function to Memo', () => {
|
|
104
|
+
const result = createSignal(() => Math.random())
|
|
105
|
+
expect(isMemo(result)).toBe(true)
|
|
106
|
+
expect(typeof result.get()).toBe('number')
|
|
84
107
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
const typedResult: Memo<number> = result
|
|
109
|
+
expect(typedResult).toBeDefined()
|
|
110
|
+
})
|
|
88
111
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
112
|
+
test('converts an async function to Task', () => {
|
|
113
|
+
const cleanup = createScope(() => {
|
|
114
|
+
const result = createSignal(async () => 'hello')
|
|
115
|
+
expect(isTask(result)).toBe(true)
|
|
92
116
|
|
|
93
|
-
|
|
94
|
-
const typedResult: State<Date> = result
|
|
117
|
+
const typedResult: Task<string> = result
|
|
95
118
|
expect(typedResult).toBeDefined()
|
|
96
119
|
})
|
|
120
|
+
cleanup()
|
|
97
121
|
})
|
|
98
122
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
[1, 2],
|
|
103
|
-
[3, 4],
|
|
104
|
-
])
|
|
105
|
-
|
|
106
|
-
expect(isList(result)).toBe(true)
|
|
107
|
-
// With the fixed behavior, nested arrays should be recovered as arrays
|
|
108
|
-
const firstElement = result.at(0)?.get()
|
|
109
|
-
const secondElement = result.at(1)?.get()
|
|
110
|
-
|
|
111
|
-
// The expected behavior - nested arrays are recovered as arrays
|
|
112
|
-
expect(firstElement).toEqual([1, 2])
|
|
113
|
-
expect(secondElement).toEqual([3, 4])
|
|
114
|
-
})
|
|
123
|
+
test('passes through an existing signal without wrapping', () => {
|
|
124
|
+
const state = createState(42)
|
|
125
|
+
expect(createSignal(state)).toBe(state)
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const result = createSignal(mixedArr)
|
|
127
|
+
const memo = createMemo(() => 'hello')
|
|
128
|
+
expect(createSignal(memo)).toBe(memo)
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
const store = createStore({ a: 1 })
|
|
131
|
+
expect(createSignal(store)).toBe(store)
|
|
132
|
+
|
|
133
|
+
const list = createList([1, 2, 3])
|
|
134
|
+
expect(createSignal(list)).toBe(list)
|
|
125
135
|
})
|
|
126
|
-
})
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
expect(typeof recordSignal.get).toBe('function')
|
|
139
|
-
expect(typeof primitiveSignal.get).toBe('function')
|
|
140
|
-
expect(typeof functionSignal.get).toBe('function')
|
|
141
|
-
expect(typeof stateSignal.get).toBe('function')
|
|
142
|
-
|
|
143
|
-
// All should be assignable to Signal<T>
|
|
144
|
-
const signals: Signal<unknown & {}>[] = [
|
|
145
|
-
arraySignal,
|
|
146
|
-
recordSignal,
|
|
147
|
-
primitiveSignal,
|
|
148
|
-
functionSignal,
|
|
149
|
-
stateSignal,
|
|
150
|
-
]
|
|
151
|
-
expect(signals.length).toBe(5)
|
|
137
|
+
test('throws InvalidSignalValueError for null', () => {
|
|
138
|
+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
|
|
139
|
+
expect(() => createSignal(null as any)).toThrow(InvalidSignalValueError)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('throws InvalidSignalValueError for undefined', () => {
|
|
143
|
+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
|
|
144
|
+
expect(() => createSignal(undefined as any)).toThrow(
|
|
145
|
+
InvalidSignalValueError,
|
|
146
|
+
)
|
|
152
147
|
})
|
|
153
148
|
})
|
|
154
149
|
|
|
155
|
-
describe('
|
|
156
|
-
test('
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
150
|
+
describe('createMutableSignal', () => {
|
|
151
|
+
test('converts a primitive to State', () => {
|
|
152
|
+
const result = createMutableSignal(42)
|
|
153
|
+
expect(isState(result)).toBe(true)
|
|
154
|
+
expect(result.get()).toBe(42)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('converts a record to Store', () => {
|
|
158
|
+
const result = createMutableSignal({ name: 'Alice' })
|
|
159
|
+
expect(isStore(result)).toBe(true)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('converts an array to List', () => {
|
|
163
|
+
const result = createMutableSignal([1, 2, 3])
|
|
164
|
+
expect(isList(result)).toBe(true)
|
|
165
|
+
})
|
|
160
166
|
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
test('passes through an existing mutable signal without wrapping', () => {
|
|
168
|
+
const state = createState(42)
|
|
169
|
+
expect(createMutableSignal(state)).toBe(state)
|
|
163
170
|
|
|
164
|
-
const
|
|
165
|
-
|
|
171
|
+
const store = createStore({ a: 1 })
|
|
172
|
+
expect(createMutableSignal(store)).toBe(store)
|
|
166
173
|
|
|
167
|
-
|
|
168
|
-
expect(
|
|
174
|
+
const list = createList([1, 2, 3])
|
|
175
|
+
expect(createMutableSignal(list)).toBe(list)
|
|
169
176
|
})
|
|
170
177
|
|
|
171
|
-
test('
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const users: User[] = [
|
|
179
|
-
{ id: 1, name: 'Alice', email: 'alice@example.com' },
|
|
180
|
-
{ id: 2, name: 'Bob', email: 'bob@example.com' },
|
|
181
|
-
]
|
|
182
|
-
|
|
183
|
-
const usersSignal = createSignal(users)
|
|
184
|
-
|
|
185
|
-
// Should maintain User type for each element
|
|
186
|
-
const firstUser = usersSignal.at(0)?.get()
|
|
187
|
-
expect(firstUser?.id).toBe(1)
|
|
188
|
-
expect(firstUser?.name).toBe('Alice')
|
|
189
|
-
expect(firstUser?.email).toBe('alice@example.com')
|
|
178
|
+
test('throws InvalidSignalValueError for null', () => {
|
|
179
|
+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
|
|
180
|
+
expect(() => createMutableSignal(null as any)).toThrow(
|
|
181
|
+
InvalidSignalValueError,
|
|
182
|
+
)
|
|
190
183
|
})
|
|
191
184
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
185
|
+
test('throws InvalidSignalValueError for a function', () => {
|
|
186
|
+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
|
|
187
|
+
expect(() => createMutableSignal((() => 42) as any)).toThrow(
|
|
188
|
+
InvalidSignalValueError,
|
|
189
|
+
)
|
|
190
|
+
})
|
|
195
191
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
test('throws InvalidSignalValueError for a read-only signal', () => {
|
|
193
|
+
const memo = createMemo(() => 42)
|
|
194
|
+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
|
|
195
|
+
expect(() => createMutableSignal(memo as any)).toThrow(
|
|
196
|
+
InvalidSignalValueError,
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
describe('isComputed', () => {
|
|
202
|
+
test('returns true for Memo', () => {
|
|
203
|
+
expect(isComputed(createMemo(() => 42))).toBe(true)
|
|
204
|
+
})
|
|
204
205
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
test('returns true for Task', () => {
|
|
207
|
+
const cleanup = createScope(() => {
|
|
208
|
+
expect(isComputed(createTask(async () => 42))).toBe(true)
|
|
209
|
+
})
|
|
210
|
+
cleanup()
|
|
211
|
+
})
|
|
209
212
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
},
|
|
221
|
-
}
|
|
213
|
+
test('returns false for State', () => {
|
|
214
|
+
expect(isComputed(createState(42))).toBe(false)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test('returns false for non-signals', () => {
|
|
218
|
+
expect(isComputed(42)).toBe(false)
|
|
219
|
+
expect(isComputed('hello')).toBe(false)
|
|
220
|
+
expect(isComputed(null)).toBe(false)
|
|
221
|
+
})
|
|
222
|
+
})
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
describe('isSignal', () => {
|
|
225
|
+
test('returns true for all signal types', () => {
|
|
226
|
+
const cleanup = createScope(() => {
|
|
227
|
+
expect(isSignal(createState(42))).toBe(true)
|
|
228
|
+
expect(isSignal(createMemo(() => 42))).toBe(true)
|
|
229
|
+
expect(isSignal(createTask(async () => 42))).toBe(true)
|
|
230
|
+
expect(isSignal(createStore({ a: 1 }))).toBe(true)
|
|
231
|
+
expect(isSignal(createList([1, 2, 3]))).toBe(true)
|
|
226
232
|
})
|
|
233
|
+
cleanup()
|
|
234
|
+
})
|
|
227
235
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// Runtime behavior works correctly
|
|
238
|
-
expect(isList(signal)).toBe(true)
|
|
239
|
-
expect(firstItemSignal?.get()).toEqual({ id: 1, name: 'Alice' })
|
|
240
|
-
expect(secondItemSignal?.get()).toEqual({ id: 2, name: 'Bob' })
|
|
241
|
-
|
|
242
|
-
// Type inference should now work correctly:
|
|
243
|
-
const properlyTyped: List<{ id: number; name: string }> = signal
|
|
244
|
-
expect(properlyTyped).toBeDefined()
|
|
245
|
-
|
|
246
|
-
// These should work without type errors in external libraries
|
|
247
|
-
// that expect Signal<P[K]> where P[K] is the individual element type
|
|
248
|
-
interface ExternalAPI<P extends Record<string, object>> {
|
|
249
|
-
process<K extends keyof P>(
|
|
250
|
-
key: K,
|
|
251
|
-
signal: Signal<P[K] & object>,
|
|
252
|
-
): P[K]
|
|
253
|
-
}
|
|
236
|
+
test('returns false for non-signals', () => {
|
|
237
|
+
expect(isSignal(42)).toBe(false)
|
|
238
|
+
expect(isSignal('hello')).toBe(false)
|
|
239
|
+
expect(isSignal({ get: () => 42 })).toBe(false)
|
|
240
|
+
expect(isSignal(null)).toBe(false)
|
|
241
|
+
expect(isSignal(undefined)).toBe(false)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
254
244
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
245
|
+
describe('isMutableSignal', () => {
|
|
246
|
+
test('returns true for State, Store, and List', () => {
|
|
247
|
+
expect(isMutableSignal(createState(42))).toBe(true)
|
|
248
|
+
expect(isMutableSignal(createStore({ a: 1 }))).toBe(true)
|
|
249
|
+
expect(isMutableSignal(createList([1, 2, 3]))).toBe(true)
|
|
250
|
+
})
|
|
260
251
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
252
|
+
test('returns false for read-only signals', () => {
|
|
253
|
+
const cleanup = createScope(() => {
|
|
254
|
+
expect(isMutableSignal(createMemo(() => 42))).toBe(false)
|
|
255
|
+
expect(isMutableSignal(createTask(async () => 42))).toBe(false)
|
|
256
|
+
})
|
|
257
|
+
cleanup()
|
|
258
|
+
})
|
|
265
259
|
|
|
266
|
-
|
|
267
|
-
|
|
260
|
+
test('returns false for non-signals', () => {
|
|
261
|
+
expect(isMutableSignal(42)).toBe(false)
|
|
262
|
+
expect(isMutableSignal(null)).toBe(false)
|
|
263
|
+
})
|
|
264
|
+
})
|
|
268
265
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
266
|
+
describe('Signal compatibility', () => {
|
|
267
|
+
test('all signal factory results implement Signal<T>', () => {
|
|
268
|
+
const cleanup = createScope(() => {
|
|
269
|
+
const signals: Signal<unknown & {}>[] = [
|
|
270
|
+
createSignal(42),
|
|
271
|
+
createSignal({ a: 1 }),
|
|
272
|
+
createSignal([1, 2, 3]),
|
|
273
|
+
createSignal(() => 'hello'),
|
|
274
|
+
]
|
|
275
|
+
for (const signal of signals) {
|
|
276
|
+
expect(typeof signal.get).toBe('function')
|
|
277
|
+
}
|
|
272
278
|
})
|
|
279
|
+
cleanup()
|
|
273
280
|
})
|
|
274
281
|
})
|