@livestore/livestore 0.0.56-dev.2 → 0.0.56-dev.3

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 (58) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/SynchronousDatabaseWrapper.d.ts.map +1 -1
  3. package/dist/SynchronousDatabaseWrapper.js +9 -18
  4. package/dist/SynchronousDatabaseWrapper.js.map +1 -1
  5. package/dist/__tests__/react/fixture.d.ts +3 -4
  6. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  7. package/dist/__tests__/react/fixture.js +7 -5
  8. package/dist/__tests__/react/fixture.js.map +1 -1
  9. package/dist/effect/LiveStore.d.ts +4 -24
  10. package/dist/effect/LiveStore.d.ts.map +1 -1
  11. package/dist/effect/LiveStore.js +2 -1
  12. package/dist/effect/LiveStore.js.map +1 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/react/LiveStoreContext.d.ts +1 -2
  17. package/dist/react/LiveStoreContext.d.ts.map +1 -1
  18. package/dist/react/LiveStoreProvider.d.ts +5 -13
  19. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  20. package/dist/react/LiveStoreProvider.js +25 -12
  21. package/dist/react/LiveStoreProvider.js.map +1 -1
  22. package/dist/react/LiveStoreProvider.test.js +1 -1
  23. package/dist/react/LiveStoreProvider.test.js.map +1 -1
  24. package/dist/react/useQuery.test.js +7 -7
  25. package/dist/react/useQuery.test.js.map +1 -1
  26. package/dist/react/useRow.test.js +52 -46
  27. package/dist/react/useRow.test.js.map +1 -1
  28. package/dist/react/useTemporaryQuery.test.js +4 -4
  29. package/dist/react/useTemporaryQuery.test.js.map +1 -1
  30. package/dist/reactiveQueries/sql.test.js +34 -33
  31. package/dist/reactiveQueries/sql.test.js.map +1 -1
  32. package/dist/store-context.d.ts +26 -0
  33. package/dist/store-context.d.ts.map +1 -0
  34. package/dist/store-context.js +6 -0
  35. package/dist/store-context.js.map +1 -0
  36. package/dist/store-devtools.d.ts +1 -1
  37. package/dist/store-devtools.d.ts.map +1 -1
  38. package/dist/store-devtools.js +5 -0
  39. package/dist/store-devtools.js.map +1 -1
  40. package/dist/store.d.ts +8 -23
  41. package/dist/store.d.ts.map +1 -1
  42. package/dist/store.js +30 -35
  43. package/dist/store.js.map +1 -1
  44. package/package.json +5 -5
  45. package/src/SynchronousDatabaseWrapper.ts +10 -18
  46. package/src/__tests__/react/fixture.tsx +53 -50
  47. package/src/effect/LiveStore.ts +5 -34
  48. package/src/index.ts +2 -10
  49. package/src/react/LiveStoreProvider.test.tsx +1 -1
  50. package/src/react/LiveStoreProvider.tsx +37 -19
  51. package/src/react/useQuery.test.tsx +53 -51
  52. package/src/react/useRow.test.tsx +237 -224
  53. package/src/react/useTemporaryQuery.test.tsx +46 -45
  54. package/src/reactiveQueries/sql.test.ts +36 -33
  55. package/src/store-context.ts +23 -0
  56. package/src/store-devtools.ts +8 -0
  57. package/src/store.ts +55 -59
  58. package/vitest.config.js +1 -1
@@ -1,4 +1,4 @@
1
- import { ReadonlyRecord, Schema } from '@livestore/utils/effect'
1
+ import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
2
2
  import * as otel from '@opentelemetry/api'
3
3
  import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
4
4
  import { render, renderHook } from '@testing-library/react'
@@ -13,242 +13,252 @@ import type { StackInfo } from './utils/stack-info.js'
13
13
 
14
14
  // NOTE running tests concurrently doesn't work with the default global db graph
15
15
  describe.concurrent('useRow', () => {
16
- it('should update the data based on component key', async () => {
17
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false })
16
+ it('should update the data based on component key', () =>
17
+ Effect.gen(function* () {
18
+ const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount } = yield* makeTodoMvc({
19
+ useGlobalReactivityGraph: false,
20
+ })
18
21
 
19
- const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount } = inputs
20
-
21
- const renderCount = makeRenderCount()
22
+ const renderCount = makeRenderCount()
22
23
 
23
- const { result, rerender } = renderHook(
24
- (userId: string) => {
25
- renderCount.inc()
24
+ const { result, rerender } = renderHook(
25
+ (userId: string) => {
26
+ renderCount.inc()
26
27
 
27
- const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
28
- return { state, setState }
29
- },
30
- { wrapper, initialProps: 'u1' },
31
- )
28
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
29
+ return { state, setState }
30
+ },
31
+ { wrapper, initialProps: 'u1' },
32
+ )
32
33
 
33
- expect(result.current.state.id).toBe('u1')
34
- expect(result.current.state.username).toBe('')
35
- expect(renderCount.val).toBe(1)
34
+ expect(result.current.state.id).toBe('u1')
35
+ expect(result.current.state.username).toBe('')
36
+ expect(renderCount.val).toBe(1)
36
37
 
37
- React.act(() =>
38
- store.mutate(
39
- LiveStore.rawSqlMutation({
40
- sql: LiveStore.sql`INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')`,
41
- }),
42
- ),
43
- )
38
+ React.act(() =>
39
+ store.mutate(
40
+ LiveStore.rawSqlMutation({
41
+ sql: LiveStore.sql`INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')`,
42
+ }),
43
+ ),
44
+ )
44
45
 
45
- rerender('u2')
46
+ rerender('u2')
46
47
 
47
- expect(result.current.state.id).toBe('u2')
48
- expect(result.current.state.username).toBe('username_u2')
49
- expect(renderCount.val).toBe(2)
50
- })
48
+ expect(result.current.state.id).toBe('u2')
49
+ expect(result.current.state.username).toBe('username_u2')
50
+ expect(renderCount.val).toBe(2)
51
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
51
52
 
52
53
  // TODO add a test that makes sure React doesn't re-render when a setter is used to set the same value
53
54
 
54
- it('should update the data reactively - via setState', async () => {
55
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false })
56
-
57
- const { wrapper, AppComponentSchema, reactivityGraph, makeRenderCount } = inputs
55
+ it('should update the data reactively - via setState', () =>
56
+ Effect.gen(function* () {
57
+ const { wrapper, AppComponentSchema, reactivityGraph, makeRenderCount } = yield* makeTodoMvc({
58
+ useGlobalReactivityGraph: false,
59
+ })
58
60
 
59
- const renderCount = makeRenderCount()
60
-
61
- const { result } = renderHook(
62
- (userId: string) => {
63
- renderCount.inc()
61
+ const renderCount = makeRenderCount()
64
62
 
65
- const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
66
- return { state, setState }
67
- },
68
- { wrapper, initialProps: 'u1' },
69
- )
63
+ const { result } = renderHook(
64
+ (userId: string) => {
65
+ renderCount.inc()
70
66
 
71
- expect(result.current.state.id).toBe('u1')
72
- expect(result.current.state.username).toBe('')
73
- expect(renderCount.val).toBe(1)
67
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
68
+ return { state, setState }
69
+ },
70
+ { wrapper, initialProps: 'u1' },
71
+ )
74
72
 
75
- React.act(() => result.current.setState.username('username_u1_hello'))
73
+ expect(result.current.state.id).toBe('u1')
74
+ expect(result.current.state.username).toBe('')
75
+ expect(renderCount.val).toBe(1)
76
76
 
77
- expect(result.current.state.id).toBe('u1')
78
- expect(result.current.state.username).toBe('username_u1_hello')
79
- expect(renderCount.val).toBe(2)
80
- })
77
+ React.act(() => result.current.setState.username('username_u1_hello'))
81
78
 
82
- it('should update the data reactively - via raw store mutation', async () => {
83
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false })
79
+ expect(result.current.state.id).toBe('u1')
80
+ expect(result.current.state.username).toBe('username_u1_hello')
81
+ expect(renderCount.val).toBe(2)
82
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
84
83
 
85
- const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount } = inputs
84
+ it('should update the data reactively - via raw store mutation', () =>
85
+ Effect.gen(function* () {
86
+ const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount } = yield* makeTodoMvc({
87
+ useGlobalReactivityGraph: false,
88
+ })
86
89
 
87
- const renderCount = makeRenderCount()
90
+ const renderCount = makeRenderCount()
88
91
 
89
- const { result } = renderHook(
90
- (userId: string) => {
91
- renderCount.inc()
92
+ const { result } = renderHook(
93
+ (userId: string) => {
94
+ renderCount.inc()
92
95
 
93
- const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
94
- return { state, setState }
95
- },
96
- { wrapper, initialProps: 'u1' },
97
- )
96
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
97
+ return { state, setState }
98
+ },
99
+ { wrapper, initialProps: 'u1' },
100
+ )
98
101
 
99
- expect(result.current.state.id).toBe('u1')
100
- expect(result.current.state.username).toBe('')
101
- expect(renderCount.val).toBe(1)
102
+ expect(result.current.state.id).toBe('u1')
103
+ expect(result.current.state.username).toBe('')
104
+ expect(renderCount.val).toBe(1)
102
105
 
103
- React.act(() =>
104
- store.mutate(
105
- LiveStore.rawSqlMutation({
106
- sql: LiveStore.sql`UPDATE UserInfo SET username = 'username_u1_hello' WHERE id = 'u1';`,
107
- }),
108
- ),
109
- )
110
-
111
- expect(result.current.state.id).toBe('u1')
112
- expect(result.current.state.username).toBe('username_u1_hello')
113
- expect(renderCount.val).toBe(2)
114
- })
106
+ React.act(() =>
107
+ store.mutate(
108
+ LiveStore.rawSqlMutation({
109
+ sql: LiveStore.sql`UPDATE UserInfo SET username = 'username_u1_hello' WHERE id = 'u1';`,
110
+ }),
111
+ ),
112
+ )
115
113
 
116
- it('should work for a larger app', async () => {
117
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false })
118
- const { wrapper, store, reactivityGraph, makeRenderCount, AppRouterSchema } = inputs
114
+ expect(result.current.state.id).toBe('u1')
115
+ expect(result.current.state.username).toBe('username_u1_hello')
116
+ expect(renderCount.val).toBe(2)
117
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
118
+
119
+ it('should work for a larger app', () =>
120
+ Effect.gen(function* () {
121
+ const { wrapper, store, reactivityGraph, makeRenderCount, AppRouterSchema } = yield* makeTodoMvc({
122
+ useGlobalReactivityGraph: false,
123
+ })
124
+
125
+ const allTodos$ = LiveStore.querySQL(`select * from todos`, {
126
+ label: 'allTodos',
127
+ schema: Schema.Array(tables.todos.schema),
128
+ reactivityGraph,
129
+ })
130
+
131
+ const appRouterRenderCount = makeRenderCount()
132
+ let globalSetState: LiveStoreReact.StateSetters<typeof AppRouterSchema> | undefined
133
+ const AppRouter: React.FC = () => {
134
+ appRouterRenderCount.inc()
135
+
136
+ const [state, setState] = LiveStoreReact.useRow(AppRouterSchema, { reactivityGraph })
137
+
138
+ globalSetState = setState
139
+
140
+ return (
141
+ <div>
142
+ <TasksList setTaskId={setState.currentTaskId} />
143
+ <div role="current-id">Current Task Id: {state.currentTaskId ?? '-'}</div>
144
+ {state.currentTaskId ? <TaskDetails id={state.currentTaskId} /> : <div>Click on a task to see details</div>}
145
+ </div>
146
+ )
147
+ }
119
148
 
120
- const allTodos$ = LiveStore.querySQL(`select * from todos`, {
121
- label: 'allTodos',
122
- schema: Schema.Array(tables.todos.schema),
123
- reactivityGraph,
124
- })
149
+ const TasksList: React.FC<{ setTaskId: (_: string) => void }> = ({ setTaskId }) => {
150
+ const allTodos = LiveStoreReact.useQuery(allTodos$)
151
+
152
+ return (
153
+ <div>
154
+ {allTodos.map((_) => (
155
+ <div key={_.id} onClick={() => setTaskId(_.id)}>
156
+ {_.id}
157
+ </div>
158
+ ))}
159
+ </div>
160
+ )
161
+ }
125
162
 
126
- const appRouterRenderCount = makeRenderCount()
127
- let globalSetState: LiveStoreReact.StateSetters<typeof AppRouterSchema> | undefined
128
- const AppRouter: React.FC = () => {
129
- appRouterRenderCount.inc()
163
+ const TaskDetails: React.FC<{ id: string }> = ({ id }) => {
164
+ const [todo] = LiveStoreReact.useRow(todos, id, { reactivityGraph })
165
+ return <div role="content">{JSON.stringify(todo)}</div>
166
+ }
130
167
 
131
- const [state, setState] = LiveStoreReact.useRow(AppRouterSchema, { reactivityGraph })
168
+ const renderResult = render(<AppRouter />, { wrapper })
132
169
 
133
- globalSetState = setState
170
+ expect(appRouterRenderCount.val).toBe(1)
134
171
 
135
- return (
136
- <div>
137
- <TasksList setTaskId={setState.currentTaskId} />
138
- <div role="current-id">Current Task Id: {state.currentTaskId ?? '-'}</div>
139
- {state.currentTaskId ? <TaskDetails id={state.currentTaskId} /> : <div>Click on a task to see details</div>}
140
- </div>
141
- )
142
- }
143
-
144
- const TasksList: React.FC<{ setTaskId: (_: string) => void }> = ({ setTaskId }) => {
145
- const allTodos = LiveStoreReact.useQuery(allTodos$)
146
-
147
- return (
148
- <div>
149
- {allTodos.map((_) => (
150
- <div key={_.id} onClick={() => setTaskId(_.id)}>
151
- {_.id}
152
- </div>
153
- ))}
154
- </div>
172
+ React.act(() =>
173
+ store.mutate(
174
+ LiveStore.rawSqlMutation({
175
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
176
+ }),
177
+ ),
155
178
  )
156
- }
157
-
158
- const TaskDetails: React.FC<{ id: string }> = ({ id }) => {
159
- const [todo] = LiveStoreReact.useRow(todos, id, { reactivityGraph })
160
- return <div role="content">{JSON.stringify(todo)}</div>
161
- }
162
179
 
163
- const renderResult = render(<AppRouter />, { wrapper })
180
+ expect(appRouterRenderCount.val).toBe(1)
181
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: -"')
164
182
 
165
- expect(appRouterRenderCount.val).toBe(1)
183
+ React.act(() => globalSetState!.currentTaskId('t1'))
166
184
 
167
- React.act(() =>
168
- store.mutate(
169
- LiveStore.rawSqlMutation({
170
- sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
171
- }),
172
- ),
173
- )
185
+ expect(appRouterRenderCount.val).toBe(2)
186
+ expect(renderResult.getByRole('content').innerHTML).toMatchInlineSnapshot(
187
+ `"{"completed":false,"id":"t1","text":"buy milk"}"`,
188
+ )
174
189
 
175
- expect(appRouterRenderCount.val).toBe(1)
176
- expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: -"')
190
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t1"')
177
191
 
178
- React.act(() => globalSetState!.currentTaskId('t1'))
192
+ React.act(() =>
193
+ store.mutate(
194
+ LiveStore.rawSqlMutation({
195
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0)`,
196
+ }),
197
+ AppRouterSchema.update({ where: { id: 'singleton' }, values: { currentTaskId: 't2' } }),
198
+ LiveStore.rawSqlMutation({
199
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t3', 'buy bread', 0)`,
200
+ }),
201
+ ),
202
+ )
179
203
 
180
- expect(appRouterRenderCount.val).toBe(2)
181
- expect(renderResult.getByRole('content').innerHTML).toMatchInlineSnapshot(
182
- `"{"completed":false,"id":"t1","text":"buy milk"}"`,
183
- )
204
+ expect(appRouterRenderCount.val).toBe(3)
205
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t2"')
206
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
184
207
 
185
- expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t1"')
208
+ it('should work for a useRow query chained with a useTemporary query', () =>
209
+ Effect.gen(function* () {
210
+ const { store, wrapper, AppComponentSchema, reactivityGraph, makeRenderCount } = yield* makeTodoMvc({
211
+ useGlobalReactivityGraph: false,
212
+ })
213
+ const renderCount = makeRenderCount()
186
214
 
187
- React.act(() =>
188
215
  store.mutate(
189
- LiveStore.rawSqlMutation({
190
- sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0)`,
191
- }),
192
- AppRouterSchema.update({ where: { id: 'singleton' }, values: { currentTaskId: 't2' } }),
193
- LiveStore.rawSqlMutation({
194
- sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t3', 'buy bread', 0)`,
195
- }),
196
- ),
197
- )
198
-
199
- expect(appRouterRenderCount.val).toBe(3)
200
- expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t2"')
201
- })
216
+ todos.insert({ id: 't1', text: 'buy milk', completed: false }),
217
+ todos.insert({ id: 't2', text: 'buy bread', completed: false }),
218
+ )
202
219
 
203
- it('should work for a useRow query chained with a useTemporary query', async () => {
204
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false })
205
- const { store, wrapper, AppComponentSchema, reactivityGraph, makeRenderCount } = inputs
206
- const renderCount = makeRenderCount()
207
-
208
- store.mutate(
209
- todos.insert({ id: 't1', text: 'buy milk', completed: false }),
210
- todos.insert({ id: 't2', text: 'buy bread', completed: false }),
211
- )
212
-
213
- const { result, unmount, rerender } = renderHook(
214
- (userId: string) => {
215
- renderCount.inc()
216
-
217
- const [_row, _setRow, rowState$] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
218
- const todos = LiveStoreReact.useTemporaryQuery(
219
- () =>
220
- LiveStore.querySQL((get) => LiveStore.sql`select * from todos where text like '%${get(rowState$).text}%'`, {
221
- schema: Schema.Array(tables.todos.schema),
222
- reactivityGraph,
223
- label: 'todosFiltered',
224
- }),
225
- userId,
226
- )
220
+ const { result, unmount, rerender } = renderHook(
221
+ (userId: string) => {
222
+ renderCount.inc()
227
223
 
228
- return { todos }
229
- },
230
- { wrapper, initialProps: 'u1' },
231
- )
224
+ const [_row, _setRow, rowState$] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
225
+ const todos = LiveStoreReact.useTemporaryQuery(
226
+ () =>
227
+ LiveStore.querySQL(
228
+ (get) => LiveStore.sql`select * from todos where text like '%${get(rowState$).text}%'`,
229
+ {
230
+ schema: Schema.Array(tables.todos.schema),
231
+ reactivityGraph,
232
+ label: 'todosFiltered',
233
+ },
234
+ ),
235
+ userId,
236
+ )
232
237
 
233
- React.act(() =>
234
- store.mutate(
235
- LiveStore.rawSqlMutation({
236
- sql: LiveStore.sql`INSERT INTO UserInfo (id, username, text) VALUES ('u2', 'username_u2', 'milk')`,
237
- }),
238
- ),
239
- )
238
+ return { todos }
239
+ },
240
+ { wrapper, initialProps: 'u1' },
241
+ )
242
+
243
+ React.act(() =>
244
+ store.mutate(
245
+ LiveStore.rawSqlMutation({
246
+ sql: LiveStore.sql`INSERT INTO UserInfo (id, username, text) VALUES ('u2', 'username_u2', 'milk')`,
247
+ }),
248
+ ),
249
+ )
240
250
 
241
- expect(result.current.todos.length).toBe(2)
242
- // expect(result.current.state.username).toBe('')
243
- expect(renderCount.val).toBe(1)
251
+ expect(result.current.todos.length).toBe(2)
252
+ // expect(result.current.state.username).toBe('')
253
+ expect(renderCount.val).toBe(1)
244
254
 
245
- rerender('u2')
255
+ rerender('u2')
246
256
 
247
- expect(result.current.todos.length).toBe(1)
248
- expect(renderCount.val).toBe(2)
257
+ expect(result.current.todos.length).toBe(1)
258
+ expect(renderCount.val).toBe(2)
249
259
 
250
- unmount()
251
- })
260
+ unmount()
261
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
252
262
 
253
263
  let cachedProvider: BasicTracerProvider | undefined
254
264
 
@@ -266,43 +276,46 @@ describe.concurrent('useRow', () => {
266
276
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
267
277
 
268
278
  it('should update the data based on component key', async () => {
269
- using inputs = await makeTodoMvc({ useGlobalReactivityGraph: false, otelContext, otelTracer })
279
+ const { strictMode } = await Effect.gen(function* () {
280
+ const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount, strictMode } = yield* makeTodoMvc(
281
+ { useGlobalReactivityGraph: false, otelContext, otelTracer },
282
+ )
270
283
 
271
- const { wrapper, AppComponentSchema, store, reactivityGraph, makeRenderCount, strictMode } = inputs
284
+ const renderCount = makeRenderCount()
272
285
 
273
- const renderCount = makeRenderCount()
286
+ const { result, rerender, unmount } = renderHook(
287
+ (userId: string) => {
288
+ renderCount.inc()
274
289
 
275
- const { result, rerender, unmount } = renderHook(
276
- (userId: string) => {
277
- renderCount.inc()
290
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
291
+ return { state, setState }
292
+ },
293
+ { wrapper, initialProps: 'u1' },
294
+ )
278
295
 
279
- const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId, { reactivityGraph })
280
- return { state, setState }
281
- },
282
- { wrapper, initialProps: 'u1' },
283
- )
296
+ expect(result.current.state.id).toBe('u1')
297
+ expect(result.current.state.username).toBe('')
298
+ expect(renderCount.val).toBe(1)
284
299
 
285
- expect(result.current.state.id).toBe('u1')
286
- expect(result.current.state.username).toBe('')
287
- expect(renderCount.val).toBe(1)
300
+ React.act(() =>
301
+ store.mutate(
302
+ LiveStore.rawSqlMutation({
303
+ sql: LiveStore.sql`INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')`,
304
+ }),
305
+ ),
306
+ )
288
307
 
289
- React.act(() =>
290
- store.mutate(
291
- LiveStore.rawSqlMutation({
292
- sql: LiveStore.sql`INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')`,
293
- }),
294
- ),
295
- )
308
+ rerender('u2')
296
309
 
297
- rerender('u2')
310
+ expect(result.current.state.id).toBe('u2')
311
+ expect(result.current.state.username).toBe('username_u2')
312
+ expect(renderCount.val).toBe(2)
298
313
 
299
- expect(result.current.state.id).toBe('u2')
300
- expect(result.current.state.username).toBe('username_u2')
301
- expect(renderCount.val).toBe(2)
314
+ unmount()
315
+ span.end()
302
316
 
303
- unmount()
304
- await store.destroy()
305
- span.end()
317
+ return { strictMode }
318
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
306
319
 
307
320
  const mapAttributes = (attributes: otel.Attributes) => {
308
321
  return ReadonlyRecord.map(attributes, (val, key) => {
@@ -371,7 +384,7 @@ describe.concurrent('useRow', () => {
371
384
  },
372
385
  "children": [
373
386
  {
374
- "_name": "LiveStore:mutatetWithoutRefresh",
387
+ "_name": "LiveStore:mutateWithoutRefresh",
375
388
  "attributes": {
376
389
  "livestore.args": "{
377
390
  "sql": "INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')"
@@ -420,7 +433,7 @@ describe.concurrent('useRow', () => {
420
433
  },
421
434
  "children": [
422
435
  {
423
- "_name": "LiveStore:mutatetWithoutRefresh",
436
+ "_name": "LiveStore:mutateWithoutRefresh",
424
437
  "attributes": {
425
438
  "livestore.args": "{
426
439
  "id": "u1"
@@ -534,7 +547,7 @@ describe.concurrent('useRow', () => {
534
547
  },
535
548
  "children": [
536
549
  {
537
- "_name": "LiveStore:mutatetWithoutRefresh",
550
+ "_name": "LiveStore:mutateWithoutRefresh",
538
551
  "attributes": {
539
552
  "livestore.args": "{
540
553
  "sql": "INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2')"
@@ -583,7 +596,7 @@ describe.concurrent('useRow', () => {
583
596
  },
584
597
  "children": [
585
598
  {
586
- "_name": "LiveStore:mutatetWithoutRefresh",
599
+ "_name": "LiveStore:mutateWithoutRefresh",
587
600
  "attributes": {
588
601
  "livestore.args": "{
589
602
  "id": "u1"
@@ -1,4 +1,4 @@
1
- import { Schema } from '@livestore/utils/effect'
1
+ import { Effect, Schema } from '@livestore/utils/effect'
2
2
  import { renderHook } from '@testing-library/react'
3
3
  import { describe, expect, it } from 'vitest'
4
4
 
@@ -8,48 +8,49 @@ import { querySQL } from '../reactiveQueries/sql.js'
8
8
  import * as LiveStoreReact from './index.js'
9
9
 
10
10
  describe('useTemporaryQuery', () => {
11
- it('simple', async () => {
12
- const { wrapper, store, makeRenderCount } = await makeTodoMvc()
13
-
14
- const renderCount = makeRenderCount()
15
-
16
- store.mutate(
17
- todos.insert({ id: 't1', text: 'buy milk', completed: false }),
18
- todos.insert({ id: 't2', text: 'buy bread', completed: false }),
19
- )
20
-
21
- const queryMap = new Map<string, LiveStore.LiveQuery<any>>()
22
-
23
- const { rerender, result, unmount } = renderHook(
24
- (id: string) => {
25
- renderCount.inc()
26
-
27
- return LiveStoreReact.useTemporaryQuery(() => {
28
- const query$ = querySQL(`select * from todos where id = '${id}'`, {
29
- schema: Schema.Array(tables.todos.schema),
30
- })
31
- queryMap.set(id, query$)
32
- return query$
33
- }, id)
34
- },
35
- { wrapper, initialProps: 't1' },
36
- )
37
-
38
- expect(result.current.length).toBe(1)
39
- expect(result.current[0]!.text).toBe('buy milk')
40
- expect(renderCount.val).toBe(1)
41
- expect(queryMap.get('t1')!.runs).toBe(1)
42
-
43
- rerender('t2')
44
-
45
- expect(result.current.length).toBe(1)
46
- expect(result.current[0]!.text).toBe('buy bread')
47
- expect(renderCount.val).toBe(2)
48
- expect(queryMap.get('t1')!.runs).toBe(1)
49
- expect(queryMap.get('t2')!.runs).toBe(1)
50
-
51
- unmount()
52
-
53
- expect(queryMap.get('t2')!.runs).toBe(1)
54
- })
11
+ it('simple', () =>
12
+ Effect.gen(function* () {
13
+ const { wrapper, store, makeRenderCount } = yield* makeTodoMvc()
14
+
15
+ const renderCount = makeRenderCount()
16
+
17
+ store.mutate(
18
+ todos.insert({ id: 't1', text: 'buy milk', completed: false }),
19
+ todos.insert({ id: 't2', text: 'buy bread', completed: false }),
20
+ )
21
+
22
+ const queryMap = new Map<string, LiveStore.LiveQuery<any>>()
23
+
24
+ const { rerender, result, unmount } = renderHook(
25
+ (id: string) => {
26
+ renderCount.inc()
27
+
28
+ return LiveStoreReact.useTemporaryQuery(() => {
29
+ const query$ = querySQL(`select * from todos where id = '${id}'`, {
30
+ schema: Schema.Array(tables.todos.schema),
31
+ })
32
+ queryMap.set(id, query$)
33
+ return query$
34
+ }, id)
35
+ },
36
+ { wrapper, initialProps: 't1' },
37
+ )
38
+
39
+ expect(result.current.length).toBe(1)
40
+ expect(result.current[0]!.text).toBe('buy milk')
41
+ expect(renderCount.val).toBe(1)
42
+ expect(queryMap.get('t1')!.runs).toBe(1)
43
+
44
+ rerender('t2')
45
+
46
+ expect(result.current.length).toBe(1)
47
+ expect(result.current[0]!.text).toBe('buy bread')
48
+ expect(renderCount.val).toBe(2)
49
+ expect(queryMap.get('t1')!.runs).toBe(1)
50
+ expect(queryMap.get('t2')!.runs).toBe(1)
51
+
52
+ unmount()
53
+
54
+ expect(queryMap.get('t2')!.runs).toBe(1)
55
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
55
56
  })