@pyreon/reactivity 0.11.5 → 0.11.7

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.
@@ -1,18 +1,18 @@
1
- import { effect } from "../effect"
2
- import { EffectScope, effectScope, getCurrentScope, setCurrentScope } from "../scope"
3
- import { signal } from "../signal"
1
+ import { effect } from '../effect'
2
+ import { EffectScope, effectScope, getCurrentScope, setCurrentScope } from '../scope'
3
+ import { signal } from '../signal'
4
4
 
5
- describe("effectScope", () => {
6
- test("creates an EffectScope instance", () => {
5
+ describe('effectScope', () => {
6
+ test('creates an EffectScope instance', () => {
7
7
  const scope = effectScope()
8
8
  expect(scope).toBeInstanceOf(EffectScope)
9
9
  })
10
10
 
11
- test("getCurrentScope returns null by default", () => {
11
+ test('getCurrentScope returns null by default', () => {
12
12
  expect(getCurrentScope()).toBeNull()
13
13
  })
14
14
 
15
- test("setCurrentScope sets and clears the current scope", () => {
15
+ test('setCurrentScope sets and clears the current scope', () => {
16
16
  const scope = effectScope()
17
17
  setCurrentScope(scope)
18
18
  expect(getCurrentScope()).toBe(scope)
@@ -20,7 +20,7 @@ describe("effectScope", () => {
20
20
  expect(getCurrentScope()).toBeNull()
21
21
  })
22
22
 
23
- test("effects created within a scope are disposed on stop", () => {
23
+ test('effects created within a scope are disposed on stop', () => {
24
24
  const scope = effectScope()
25
25
  setCurrentScope(scope)
26
26
 
@@ -42,7 +42,7 @@ describe("effectScope", () => {
42
42
  expect(count).toBe(2) // effect disposed, no re-run
43
43
  })
44
44
 
45
- test("stop is idempotent — second call does nothing", () => {
45
+ test('stop is idempotent — second call does nothing', () => {
46
46
  const scope = effectScope()
47
47
  setCurrentScope(scope)
48
48
 
@@ -60,14 +60,14 @@ describe("effectScope", () => {
60
60
  expect(count).toBe(1)
61
61
  })
62
62
 
63
- test("add is ignored after scope is stopped", () => {
63
+ test('add is ignored after scope is stopped', () => {
64
64
  const scope = effectScope()
65
65
  scope.stop()
66
66
  // Should not throw — add is silently ignored
67
67
  scope.add({ dispose() {} })
68
68
  })
69
69
 
70
- test("runInScope temporarily re-activates the scope", () => {
70
+ test('runInScope temporarily re-activates the scope', () => {
71
71
  const scope = effectScope()
72
72
  setCurrentScope(null)
73
73
 
@@ -91,7 +91,7 @@ describe("effectScope", () => {
91
91
  expect(count).toBe(2) // disposed via scope
92
92
  })
93
93
 
94
- test("runInScope restores previous scope even on error", () => {
94
+ test('runInScope restores previous scope even on error', () => {
95
95
  const scope = effectScope()
96
96
  const prevScope = effectScope()
97
97
  setCurrentScope(prevScope)
@@ -99,7 +99,7 @@ describe("effectScope", () => {
99
99
  try {
100
100
  scope.runInScope(() => {
101
101
  expect(getCurrentScope()).toBe(scope)
102
- throw new Error("test")
102
+ throw new Error('test')
103
103
  })
104
104
  } catch {
105
105
  // expected
@@ -115,7 +115,7 @@ describe("effectScope", () => {
115
115
  expect(result).toBe(42)
116
116
  })
117
117
 
118
- test("addUpdateHook + notifyEffectRan fires hooks via microtask", async () => {
118
+ test('addUpdateHook + notifyEffectRan fires hooks via microtask', async () => {
119
119
  const scope = effectScope()
120
120
  let hookCalled = 0
121
121
 
@@ -130,14 +130,14 @@ describe("effectScope", () => {
130
130
  expect(hookCalled).toBe(1)
131
131
  })
132
132
 
133
- test("notifyEffectRan does nothing when no update hooks", async () => {
133
+ test('notifyEffectRan does nothing when no update hooks', async () => {
134
134
  const scope = effectScope()
135
135
  // Should not throw — early return when _updateHooks is empty
136
136
  scope.notifyEffectRan()
137
137
  await new Promise((r) => setTimeout(r, 10))
138
138
  })
139
139
 
140
- test("notifyEffectRan does nothing after scope is stopped", async () => {
140
+ test('notifyEffectRan does nothing after scope is stopped', async () => {
141
141
  const scope = effectScope()
142
142
  let hookCalled = 0
143
143
 
@@ -152,7 +152,7 @@ describe("effectScope", () => {
152
152
  expect(hookCalled).toBe(0)
153
153
  })
154
154
 
155
- test("notifyEffectRan deduplicates — only one microtask while pending", async () => {
155
+ test('notifyEffectRan deduplicates — only one microtask while pending', async () => {
156
156
  const scope = effectScope()
157
157
  let hookCalled = 0
158
158
 
@@ -168,7 +168,7 @@ describe("effectScope", () => {
168
168
  expect(hookCalled).toBe(1) // only fired once
169
169
  })
170
170
 
171
- test("notifyEffectRan skips hooks if scope stopped before microtask fires", async () => {
171
+ test('notifyEffectRan skips hooks if scope stopped before microtask fires', async () => {
172
172
  const scope = effectScope()
173
173
  let hookCalled = 0
174
174
 
@@ -183,14 +183,14 @@ describe("effectScope", () => {
183
183
  expect(hookCalled).toBe(0)
184
184
  })
185
185
 
186
- test("onUpdate hook errors are caught and logged", async () => {
186
+ test('onUpdate hook errors are caught and logged', async () => {
187
187
  const scope = effectScope()
188
188
  const errors: unknown[] = []
189
189
  const origError = console.error
190
190
  console.error = (...args: unknown[]) => errors.push(args)
191
191
 
192
192
  scope.addUpdateHook(() => {
193
- throw new Error("hook error")
193
+ throw new Error('hook error')
194
194
  })
195
195
 
196
196
  scope.notifyEffectRan()
@@ -1,26 +1,26 @@
1
- import { batch } from "../batch"
2
- import { effect } from "../effect"
3
- import { signal } from "../signal"
1
+ import { batch } from '../batch'
2
+ import { effect } from '../effect'
3
+ import { signal } from '../signal'
4
4
 
5
- describe("signal", () => {
6
- test("reads initial value", () => {
5
+ describe('signal', () => {
6
+ test('reads initial value', () => {
7
7
  const s = signal(42)
8
8
  expect(s()).toBe(42)
9
9
  })
10
10
 
11
- test("set updates value", () => {
11
+ test('set updates value', () => {
12
12
  const s = signal(0)
13
13
  s.set(10)
14
14
  expect(s()).toBe(10)
15
15
  })
16
16
 
17
- test("update transforms value", () => {
17
+ test('update transforms value', () => {
18
18
  const s = signal(5)
19
19
  s.update((n) => n * 2)
20
20
  expect(s()).toBe(10)
21
21
  })
22
22
 
23
- test("set with same value does not notify", () => {
23
+ test('set with same value does not notify', () => {
24
24
  const s = signal(1)
25
25
  let calls = 0
26
26
  effect(() => {
@@ -34,20 +34,20 @@ describe("signal", () => {
34
34
  expect(calls).toBe(2)
35
35
  })
36
36
 
37
- test("works with objects", () => {
37
+ test('works with objects', () => {
38
38
  const s = signal({ x: 1 })
39
39
  s.update((o) => ({ ...o, x: 2 }))
40
40
  expect(s().x).toBe(2)
41
41
  })
42
42
 
43
- test("works with null and undefined", () => {
43
+ test('works with null and undefined', () => {
44
44
  const s = signal<string | null>(null)
45
45
  expect(s()).toBeNull()
46
- s.set("hello")
47
- expect(s()).toBe("hello")
46
+ s.set('hello')
47
+ expect(s()).toBe('hello')
48
48
  })
49
49
 
50
- test("peek reads value without tracking", () => {
50
+ test('peek reads value without tracking', () => {
51
51
  const s = signal(42)
52
52
  let count = 0
53
53
  effect(() => {
@@ -60,7 +60,7 @@ describe("signal", () => {
60
60
  expect(s.peek()).toBe(100)
61
61
  })
62
62
 
63
- test("subscribe adds a static listener", () => {
63
+ test('subscribe adds a static listener', () => {
64
64
  const s = signal(0)
65
65
  let notified = 0
66
66
  const unsub = s.subscribe(() => {
@@ -77,34 +77,34 @@ describe("signal", () => {
77
77
  expect(notified).toBe(2) // unsubscribed
78
78
  })
79
79
 
80
- test("subscribe disposer is safe to call multiple times", () => {
80
+ test('subscribe disposer is safe to call multiple times', () => {
81
81
  const s = signal(0)
82
82
  const unsub = s.subscribe(() => {})
83
83
  unsub()
84
84
  unsub() // should not throw
85
85
  })
86
86
 
87
- test("label getter returns name from options", () => {
88
- const s = signal(0, { name: "counter" })
89
- expect(s.label).toBe("counter")
87
+ test('label getter returns name from options', () => {
88
+ const s = signal(0, { name: 'counter' })
89
+ expect(s.label).toBe('counter')
90
90
  })
91
91
 
92
- test("label setter updates the name", () => {
92
+ test('label setter updates the name', () => {
93
93
  const s = signal(0)
94
94
  expect(s.label).toBeUndefined()
95
- s.label = "renamed"
96
- expect(s.label).toBe("renamed")
95
+ s.label = 'renamed'
96
+ expect(s.label).toBe('renamed')
97
97
  })
98
98
 
99
- test("debug() returns signal info", () => {
100
- const s = signal(42, { name: "test" })
99
+ test('debug() returns signal info', () => {
100
+ const s = signal(42, { name: 'test' })
101
101
  const info = s.debug()
102
- expect(info.name).toBe("test")
102
+ expect(info.name).toBe('test')
103
103
  expect(info.value).toBe(42)
104
104
  expect(info.subscriberCount).toBe(0)
105
105
  })
106
106
 
107
- test("debug() reports subscriber count", () => {
107
+ test('debug() reports subscriber count', () => {
108
108
  const s = signal(0)
109
109
  s.subscribe(() => {})
110
110
  s.subscribe(() => {})
@@ -112,15 +112,15 @@ describe("signal", () => {
112
112
  expect(info.subscriberCount).toBe(2)
113
113
  })
114
114
 
115
- test("signal without options has undefined name", () => {
115
+ test('signal without options has undefined name', () => {
116
116
  const s = signal(0)
117
117
  expect(s.label).toBeUndefined()
118
118
  const info = s.debug()
119
119
  expect(info.name).toBeUndefined()
120
120
  })
121
121
 
122
- describe("direct updater disposal", () => {
123
- test("disposed direct updater is not called on subsequent updates", () => {
122
+ describe('direct updater disposal', () => {
123
+ test('disposed direct updater is not called on subsequent updates', () => {
124
124
  const s = signal(0)
125
125
  let called = 0
126
126
  const dispose = s.direct(() => {
@@ -135,7 +135,7 @@ describe("signal", () => {
135
135
  expect(called).toBe(1) // not called after disposal
136
136
  })
137
137
 
138
- test("multiple direct updaters, dispose one, others still fire", () => {
138
+ test('multiple direct updaters, dispose one, others still fire', () => {
139
139
  const s = signal(0)
140
140
  let calls1 = 0
141
141
  let calls2 = 0
@@ -163,22 +163,22 @@ describe("signal", () => {
163
163
  expect(calls3).toBe(2) // still active
164
164
  })
165
165
 
166
- test("direct updater slot is null after disposal", () => {
166
+ test('direct updater slot is null after disposal', () => {
167
167
  const s = signal(0)
168
168
  const dispose = s.direct(() => {})
169
169
 
170
170
  // Access internal _d array via cast
171
171
  const internal = s as unknown as { _d: ((() => void) | null)[] | null }
172
172
  expect(internal._d).not.toBeNull()
173
- expect(internal._d![0]).toBeTypeOf("function")
173
+ expect(internal._d![0]).toBeTypeOf('function')
174
174
 
175
175
  dispose()
176
176
  expect(internal._d![0]).toBeNull()
177
177
  })
178
178
  })
179
179
 
180
- describe("signal.direct() for template binding", () => {
181
- test("direct updater is called synchronously on signal change", () => {
180
+ describe('signal.direct() for template binding', () => {
181
+ test('direct updater is called synchronously on signal change', () => {
182
182
  const s = signal(0)
183
183
  const values: number[] = []
184
184
  s.direct(() => {
@@ -191,7 +191,7 @@ describe("signal", () => {
191
191
  expect(values).toEqual([1, 2])
192
192
  })
193
193
 
194
- test("direct updaters are batch-aware", () => {
194
+ test('direct updaters are batch-aware', () => {
195
195
  const s = signal(0)
196
196
  let calls = 0
197
197
  s.direct(() => {
@@ -207,7 +207,7 @@ describe("signal", () => {
207
207
  expect(calls).toBe(1)
208
208
  })
209
209
 
210
- test("direct updater with no prior direct array initializes lazily", () => {
210
+ test('direct updater with no prior direct array initializes lazily', () => {
211
211
  const s = signal(0)
212
212
  const internal = s as unknown as { _d: ((() => void) | null)[] | null }
213
213
  expect(internal._d).toBeNull()
@@ -217,14 +217,14 @@ describe("signal", () => {
217
217
  })
218
218
  })
219
219
 
220
- describe("signal misuse warning in dev", () => {
221
- test("warns when signal is called with arguments", () => {
222
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
220
+ describe('signal misuse warning in dev', () => {
221
+ test('warns when signal is called with arguments', () => {
222
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
223
223
  const s = signal(42)
224
224
  // Call signal with an argument (common mistake — trying to set via call)
225
225
  ;(s as unknown as (v: number) => number)(99)
226
226
  expect(warnSpy).toHaveBeenCalledWith(
227
- expect.stringContaining("signal() was called with an argument"),
227
+ expect.stringContaining('signal() was called with an argument'),
228
228
  )
229
229
  // Value should not change — the argument is ignored
230
230
  expect(s()).toBe(42)
@@ -1,9 +1,9 @@
1
- import { effect } from "../effect"
2
- import { reconcile } from "../reconcile"
3
- import { createStore, isStore } from "../store"
1
+ import { effect } from '../effect'
2
+ import { reconcile } from '../reconcile'
3
+ import { createStore, isStore } from '../store'
4
4
 
5
- describe("createStore", () => {
6
- test("reads primitive properties reactively", () => {
5
+ describe('createStore', () => {
6
+ test('reads primitive properties reactively', () => {
7
7
  const state = createStore({ count: 0 })
8
8
  const calls: number[] = []
9
9
  effect(() => {
@@ -16,19 +16,19 @@ describe("createStore", () => {
16
16
  expect(calls).toEqual([0, 1])
17
17
  })
18
18
 
19
- test("deep reactive — nested object", () => {
20
- const state = createStore({ user: { name: "Alice", age: 30 } })
19
+ test('deep reactive — nested object', () => {
20
+ const state = createStore({ user: { name: 'Alice', age: 30 } })
21
21
  const names: string[] = []
22
22
  effect(() => {
23
23
  names.push(state.user.name)
24
24
  })
25
- expect(names).toEqual(["Alice"])
26
- state.user.name = "Bob"
27
- expect(names).toEqual(["Alice", "Bob"])
25
+ expect(names).toEqual(['Alice'])
26
+ state.user.name = 'Bob'
27
+ expect(names).toEqual(['Alice', 'Bob'])
28
28
  })
29
29
 
30
- test("deep reactive — nested change does NOT re-run parent-only effects", () => {
31
- const state = createStore({ user: { name: "Alice", age: 30 } })
30
+ test('deep reactive — nested change does NOT re-run parent-only effects', () => {
31
+ const state = createStore({ user: { name: 'Alice', age: 30 } })
32
32
  const userCalls: number[] = []
33
33
  const nameCalls: string[] = []
34
34
  effect(() => {
@@ -41,10 +41,10 @@ describe("createStore", () => {
41
41
  expect(userCalls.length).toBe(1)
42
42
  state.user.age = 31
43
43
  // Only the age signal fires — user object didn't change, name didn't change
44
- expect(nameCalls).toEqual(["Alice"]) // name effect didn't re-run
44
+ expect(nameCalls).toEqual(['Alice']) // name effect didn't re-run
45
45
  })
46
46
 
47
- test("array — tracks length on push", () => {
47
+ test('array — tracks length on push', () => {
48
48
  const state = createStore({ items: [1, 2, 3] })
49
49
  const lengths: number[] = []
50
50
  effect(() => {
@@ -55,20 +55,20 @@ describe("createStore", () => {
55
55
  expect(lengths).toEqual([3, 4])
56
56
  })
57
57
 
58
- test("array — tracks index access", () => {
59
- const state = createStore({ items: ["a", "b"] })
58
+ test('array — tracks index access', () => {
59
+ const state = createStore({ items: ['a', 'b'] })
60
60
  const values: string[] = []
61
61
  effect(() => {
62
62
  values.push(state.items[0] as string)
63
63
  })
64
- expect(values).toEqual(["a"])
65
- state.items[0] = "x"
66
- expect(values).toEqual(["a", "x"])
67
- state.items[1] = "y" // different index — should not re-run this effect
68
- expect(values).toEqual(["a", "x"])
64
+ expect(values).toEqual(['a'])
65
+ state.items[0] = 'x'
66
+ expect(values).toEqual(['a', 'x'])
67
+ state.items[1] = 'y' // different index — should not re-run this effect
68
+ expect(values).toEqual(['a', 'x'])
69
69
  })
70
70
 
71
- test("isStore identifies proxy", () => {
71
+ test('isStore identifies proxy', () => {
72
72
  const raw = { x: 1 }
73
73
  const store = createStore(raw)
74
74
  expect(isStore(store)).toBe(true)
@@ -77,14 +77,14 @@ describe("createStore", () => {
77
77
  expect(isStore(42)).toBe(false)
78
78
  })
79
79
 
80
- test("same raw object returns same proxy", () => {
80
+ test('same raw object returns same proxy', () => {
81
81
  const raw = { a: 1 }
82
82
  const s1 = createStore(raw)
83
83
  const s2 = createStore(raw)
84
84
  expect(s1).toBe(s2)
85
85
  })
86
86
 
87
- test("deep nested object mutations trigger fine-grained updates", () => {
87
+ test('deep nested object mutations trigger fine-grained updates', () => {
88
88
  const state = createStore({
89
89
  a: { b: { c: { d: 1 } } },
90
90
  })
@@ -97,20 +97,20 @@ describe("createStore", () => {
97
97
  expect(dValues).toEqual([1, 2])
98
98
  })
99
99
 
100
- test("replacing a nested object triggers dependent effects", () => {
101
- const state = createStore({ user: { name: "Alice", address: { city: "NYC" } } })
100
+ test('replacing a nested object triggers dependent effects', () => {
101
+ const state = createStore({ user: { name: 'Alice', address: { city: 'NYC' } } })
102
102
  const cities: string[] = []
103
103
  effect(() => {
104
104
  cities.push(state.user.address.city)
105
105
  })
106
- expect(cities).toEqual(["NYC"])
106
+ expect(cities).toEqual(['NYC'])
107
107
  // Replace the address object entirely
108
- state.user.address = { city: "LA" }
109
- expect(cities).toEqual(["NYC", "LA"])
108
+ state.user.address = { city: 'LA' }
109
+ expect(cities).toEqual(['NYC', 'LA'])
110
110
  })
111
111
 
112
- test("array splice triggers length and index updates", () => {
113
- const state = createStore({ items: ["a", "b", "c", "d"] })
112
+ test('array splice triggers length and index updates', () => {
113
+ const state = createStore({ items: ['a', 'b', 'c', 'd'] })
114
114
  const lengths: number[] = []
115
115
  effect(() => {
116
116
  lengths.push(state.items.length)
@@ -118,10 +118,10 @@ describe("createStore", () => {
118
118
  expect(lengths).toEqual([4])
119
119
  state.items.splice(1, 2) // removes "b", "c"
120
120
  expect(lengths).toEqual([4, 2])
121
- expect([...state.items]).toEqual(["a", "d"])
121
+ expect([...state.items]).toEqual(['a', 'd'])
122
122
  })
123
123
 
124
- test("array pop triggers length update", () => {
124
+ test('array pop triggers length update', () => {
125
125
  const state = createStore({ items: [1, 2, 3] })
126
126
  const lengths: number[] = []
127
127
  effect(() => {
@@ -132,7 +132,7 @@ describe("createStore", () => {
132
132
  expect(lengths).toEqual([3, 2])
133
133
  })
134
134
 
135
- test("delete property triggers reactivity", () => {
135
+ test('delete property triggers reactivity', () => {
136
136
  const state = createStore({ a: 1, b: 2 } as Record<string, number | undefined>)
137
137
  const bValues: (number | undefined)[] = []
138
138
  effect(() => {
@@ -143,7 +143,7 @@ describe("createStore", () => {
143
143
  expect(bValues).toEqual([2, undefined])
144
144
  })
145
145
 
146
- test("setting array length directly triggers reactivity", () => {
146
+ test('setting array length directly triggers reactivity', () => {
147
147
  const state = createStore({ items: [1, 2, 3, 4, 5] })
148
148
  const lengths: number[] = []
149
149
  effect(() => {
@@ -154,36 +154,36 @@ describe("createStore", () => {
154
154
  expect(lengths).toEqual([5, 2])
155
155
  })
156
156
 
157
- test("symbol property access does not trigger tracking", () => {
158
- const sym = Symbol("test")
157
+ test('symbol property access does not trigger tracking', () => {
158
+ const sym = Symbol('test')
159
159
  const raw = { x: 1 } as Record<string | symbol, unknown>
160
- raw[sym] = "hidden"
160
+ raw[sym] = 'hidden'
161
161
  const state = createStore(raw)
162
- expect(state[sym]).toBe("hidden")
162
+ expect(state[sym]).toBe('hidden')
163
163
  })
164
164
 
165
- test("symbol property set goes through to target", () => {
166
- const sym = Symbol("test")
165
+ test('symbol property set goes through to target', () => {
166
+ const sym = Symbol('test')
167
167
  const state = createStore({} as Record<string | symbol, unknown>)
168
- state[sym] = "value"
169
- expect(state[sym]).toBe("value")
168
+ state[sym] = 'value'
169
+ expect(state[sym]).toBe('value')
170
170
  })
171
171
 
172
- test("has trap works with in operator", () => {
172
+ test('has trap works with in operator', () => {
173
173
  const state = createStore({ a: 1 })
174
- expect("a" in state).toBe(true)
175
- expect("b" in state).toBe(false)
174
+ expect('a' in state).toBe(true)
175
+ expect('b' in state).toBe(false)
176
176
  })
177
177
 
178
- test("ownKeys returns correct keys", () => {
178
+ test('ownKeys returns correct keys', () => {
179
179
  const state = createStore({ a: 1, b: 2 })
180
- expect(Object.keys(state)).toEqual(["a", "b"])
180
+ expect(Object.keys(state)).toEqual(['a', 'b'])
181
181
  })
182
182
  })
183
183
 
184
- describe("reconcile", () => {
185
- test("updates only changed scalar properties", () => {
186
- const state = createStore({ name: "Alice", age: 30 })
184
+ describe('reconcile', () => {
185
+ test('updates only changed scalar properties', () => {
186
+ const state = createStore({ name: 'Alice', age: 30 })
187
187
  const nameCalls: string[] = []
188
188
  const ageCalls: number[] = []
189
189
  effect(() => {
@@ -192,41 +192,41 @@ describe("reconcile", () => {
192
192
  effect(() => {
193
193
  ageCalls.push(state.age)
194
194
  })
195
- reconcile({ name: "Alice", age: 31 }, state)
196
- expect(nameCalls).toEqual(["Alice"]) // unchanged — no re-run
195
+ reconcile({ name: 'Alice', age: 31 }, state)
196
+ expect(nameCalls).toEqual(['Alice']) // unchanged — no re-run
197
197
  expect(ageCalls).toEqual([30, 31]) // changed — re-ran
198
198
  })
199
199
 
200
- test("reconciles nested objects recursively", () => {
201
- const state = createStore({ user: { name: "Alice", age: 30 } })
200
+ test('reconciles nested objects recursively', () => {
201
+ const state = createStore({ user: { name: 'Alice', age: 30 } })
202
202
  const nameCalls: string[] = []
203
203
  effect(() => {
204
204
  nameCalls.push(state.user.name)
205
205
  })
206
- reconcile({ user: { name: "Bob", age: 30 } }, state)
207
- expect(nameCalls).toEqual(["Alice", "Bob"])
206
+ reconcile({ user: { name: 'Bob', age: 30 } }, state)
207
+ expect(nameCalls).toEqual(['Alice', 'Bob'])
208
208
  })
209
209
 
210
- test("reconciles arrays by index", () => {
211
- const state = createStore({ items: ["a", "b", "c"] })
210
+ test('reconciles arrays by index', () => {
211
+ const state = createStore({ items: ['a', 'b', 'c'] })
212
212
  const calls: string[][] = []
213
213
  effect(() => {
214
214
  calls.push([...state.items])
215
215
  })
216
- reconcile({ items: ["a", "X", "c"] }, state)
217
- expect(state.items[1]).toBe("X")
216
+ reconcile({ items: ['a', 'X', 'c'] }, state)
217
+ expect(state.items[1]).toBe('X')
218
218
  expect(calls.length).toBe(2) // initial + after reconcile
219
219
  })
220
220
 
221
- test("trims excess array elements", () => {
221
+ test('trims excess array elements', () => {
222
222
  const state = createStore({ items: [1, 2, 3, 4, 5] })
223
223
  reconcile({ items: [1, 2] }, state)
224
224
  expect(state.items.length).toBe(2)
225
225
  })
226
226
 
227
- test("removes deleted keys", () => {
227
+ test('removes deleted keys', () => {
228
228
  const state = createStore({ a: 1, b: 2, c: 3 } as Record<string, number>)
229
229
  reconcile({ a: 1, b: 2 }, state)
230
- expect("c" in state).toBe(false)
230
+ expect('c' in state).toBe(false)
231
231
  })
232
232
  })