@livestore/livestore 0.0.23 → 0.0.25

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 (108) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/QueryCache.d.ts +2 -2
  3. package/dist/QueryCache.d.ts.map +1 -1
  4. package/dist/QueryCache.js.map +1 -1
  5. package/dist/__tests__/react/fixture.d.ts +2 -2
  6. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  7. package/dist/__tests__/react/fixture.js +2 -2
  8. package/dist/__tests__/react/fixture.js.map +1 -1
  9. package/dist/__tests__/react/useComponentState.test.js +78 -10
  10. package/dist/__tests__/react/useComponentState.test.js.map +1 -1
  11. package/dist/__tests__/react/useQuery.test.js +35 -10
  12. package/dist/__tests__/react/useQuery.test.js.map +1 -1
  13. package/dist/__tests__/reactive.test.js +51 -0
  14. package/dist/__tests__/reactive.test.js.map +1 -1
  15. package/dist/__tests__/reactiveQueries/sql.test.js +2 -9
  16. package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -1
  17. package/dist/effect/LiveStore.js +1 -1
  18. package/dist/effect/LiveStore.js.map +1 -1
  19. package/dist/inMemoryDatabase.d.ts +3 -3
  20. package/dist/inMemoryDatabase.d.ts.map +1 -1
  21. package/dist/inMemoryDatabase.js +3 -3
  22. package/dist/inMemoryDatabase.js.map +1 -1
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/migrations.d.ts.map +1 -1
  28. package/dist/migrations.js +7 -2
  29. package/dist/migrations.js.map +1 -1
  30. package/dist/react/useComponentState.d.ts +3 -3
  31. package/dist/react/useComponentState.d.ts.map +1 -1
  32. package/dist/react/useComponentState.js +43 -57
  33. package/dist/react/useComponentState.js.map +1 -1
  34. package/dist/react/useQuery.js +2 -2
  35. package/dist/react/useQuery.js.map +1 -1
  36. package/dist/react/utils/stack-info.d.ts.map +1 -1
  37. package/dist/react/utils/stack-info.js +0 -1
  38. package/dist/react/utils/stack-info.js.map +1 -1
  39. package/dist/reactive.d.ts +37 -28
  40. package/dist/reactive.d.ts.map +1 -1
  41. package/dist/reactive.js +44 -19
  42. package/dist/reactive.js.map +1 -1
  43. package/dist/reactiveQueries/base-class.d.ts +4 -4
  44. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  45. package/dist/reactiveQueries/base-class.js +2 -1
  46. package/dist/reactiveQueries/base-class.js.map +1 -1
  47. package/dist/reactiveQueries/js.d.ts +6 -1
  48. package/dist/reactiveQueries/js.d.ts.map +1 -1
  49. package/dist/reactiveQueries/js.js.map +1 -1
  50. package/dist/reactiveQueries/sql.d.ts +2 -2
  51. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  52. package/dist/reactiveQueries/sql.js +1 -1
  53. package/dist/reactiveQueries/sql.js.map +1 -1
  54. package/dist/store.d.ts +2 -11
  55. package/dist/store.d.ts.map +1 -1
  56. package/dist/store.js +6 -11
  57. package/dist/store.js.map +1 -1
  58. package/package.json +16 -13
  59. package/src/QueryCache.ts +2 -2
  60. package/src/__tests__/react/fixture.tsx +3 -3
  61. package/src/__tests__/react/useComponentState.test.tsx +116 -10
  62. package/src/__tests__/react/useQuery.test.tsx +54 -12
  63. package/src/__tests__/reactive.test.ts +71 -0
  64. package/src/__tests__/reactiveQueries/sql.test.ts +2 -9
  65. package/src/effect/LiveStore.ts +1 -1
  66. package/src/inMemoryDatabase.ts +8 -8
  67. package/src/index.ts +4 -12
  68. package/src/migrations.ts +13 -7
  69. package/src/react/useComponentState.ts +53 -72
  70. package/src/react/useQuery.ts +2 -2
  71. package/src/react/utils/stack-info.ts +0 -1
  72. package/src/reactive.ts +80 -64
  73. package/src/reactiveQueries/base-class.ts +6 -8
  74. package/src/reactiveQueries/js.ts +6 -1
  75. package/src/reactiveQueries/sql.ts +3 -3
  76. package/src/store.ts +12 -24
  77. package/dist/__tests__/react/useLQuery.test.d.ts +0 -2
  78. package/dist/__tests__/react/useLQuery.test.d.ts.map +0 -1
  79. package/dist/__tests__/react/useLQuery.test.js +0 -38
  80. package/dist/__tests__/react/useLQuery.test.js.map +0 -1
  81. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts +0 -2
  82. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +0 -1
  83. package/dist/__tests__/react/useLiveStoreComponent.test.js +0 -73
  84. package/dist/__tests__/react/useLiveStoreComponent.test.js.map +0 -1
  85. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +0 -2
  86. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +0 -1
  87. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +0 -38
  88. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +0 -1
  89. package/dist/react/useGlobalQuery.d.ts +0 -3
  90. package/dist/react/useGlobalQuery.d.ts.map +0 -1
  91. package/dist/react/useGlobalQuery.js +0 -26
  92. package/dist/react/useGlobalQuery.js.map +0 -1
  93. package/dist/react/useGraphQL.d.ts +0 -13
  94. package/dist/react/useGraphQL.d.ts.map +0 -1
  95. package/dist/react/useGraphQL.js +0 -87
  96. package/dist/react/useGraphQL.js.map +0 -1
  97. package/dist/react/useLiveStoreComponent.d.ts +0 -75
  98. package/dist/react/useLiveStoreComponent.d.ts.map +0 -1
  99. package/dist/react/useLiveStoreComponent.js +0 -361
  100. package/dist/react/useLiveStoreComponent.js.map +0 -1
  101. package/dist/react/utils/extractNamesFromStackTrace.d.ts +0 -3
  102. package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +0 -1
  103. package/dist/react/utils/extractNamesFromStackTrace.js +0 -40
  104. package/dist/react/utils/extractNamesFromStackTrace.js.map +0 -1
  105. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +0 -7
  106. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +0 -1
  107. package/dist/react/utils/extractStackInfoFromStackTrace.js +0 -40
  108. package/dist/react/utils/extractStackInfoFromStackTrace.js.map +0 -1
@@ -9,7 +9,7 @@ import { InMemoryStorage } from '../../storage/in-memory/index.js'
9
9
 
10
10
  export type Todo = {
11
11
  id: string
12
- text: string | null
12
+ text: string
13
13
  completed: boolean
14
14
  }
15
15
 
@@ -59,7 +59,7 @@ export const makeTodoMvc = async ({
59
59
  otelTracer?: otel.Tracer
60
60
  otelContext?: otel.Context
61
61
  } = {}) => {
62
- const AppSchema = LiveStore.defineComponentStateSchema('UserInfo', {
62
+ const AppComponentSchema = LiveStore.defineComponentStateSchema('UserInfo', {
63
63
  username: LiveStore.DbSchema.text({ default: '' }),
64
64
  })
65
65
 
@@ -83,5 +83,5 @@ export const makeTodoMvc = async ({
83
83
  <LiveStoreReact.LiveStoreContext.Provider value={storeContext}>{children}</LiveStoreReact.LiveStoreContext.Provider>
84
84
  )
85
85
 
86
- return { wrapper, AppSchema, store }
86
+ return { wrapper, AppComponentSchema, store }
87
87
  }
@@ -1,22 +1,24 @@
1
- import { act, renderHook } from '@testing-library/react'
1
+ import { act, render, renderHook } from '@testing-library/react'
2
+ import React from 'react'
2
3
  import { describe, expect, it } from 'vitest'
3
4
 
4
- import { sql } from '../../index.js'
5
+ import * as LiveStore from '../../index.js'
5
6
  import * as LiveStoreReact from '../../react/index.js'
7
+ import type { Todo } from './fixture.js'
6
8
  import { makeTodoMvc } from './fixture.js'
7
9
 
8
10
  describe('useComponentState', () => {
9
11
  it('should update the data based on component key', async () => {
10
12
  let renderCount = 0
11
13
 
12
- const { wrapper, AppSchema, store } = await makeTodoMvc()
14
+ const { wrapper, AppComponentSchema, store } = await makeTodoMvc()
13
15
 
14
16
  const { result, rerender } = renderHook(
15
17
  (userId: string) => {
16
18
  renderCount++
17
19
 
18
20
  return LiveStoreReact.useComponentState({
19
- schema: AppSchema,
21
+ schema: AppComponentSchema,
20
22
  componentKey: { name: 'UserInfo', id: userId },
21
23
  })
22
24
  },
@@ -28,7 +30,7 @@ describe('useComponentState', () => {
28
30
  expect(renderCount).toBe(1)
29
31
 
30
32
  act(() => {
31
- void store.execute(sql`INSERT INTO components__UserInfo (id, username) VALUES ('u2', 'username_u2');`)
33
+ void store.execute(LiveStore.sql`INSERT INTO components__UserInfo (id, username) VALUES ('u2', 'username_u2');`)
32
34
  })
33
35
 
34
36
  rerender('u2')
@@ -41,14 +43,14 @@ describe('useComponentState', () => {
41
43
  it('should update the data reactively - via setState', async () => {
42
44
  let renderCount = 0
43
45
 
44
- const { wrapper, AppSchema } = await makeTodoMvc()
46
+ const { wrapper, AppComponentSchema } = await makeTodoMvc()
45
47
 
46
48
  const { result } = renderHook(
47
49
  (userId: string) => {
48
50
  renderCount++
49
51
 
50
52
  return LiveStoreReact.useComponentState({
51
- schema: AppSchema,
53
+ schema: AppComponentSchema,
52
54
  componentKey: { name: 'UserInfo', id: userId },
53
55
  })
54
56
  },
@@ -69,14 +71,14 @@ describe('useComponentState', () => {
69
71
  it('should update the data reactively - via raw store update', async () => {
70
72
  let renderCount = 0
71
73
 
72
- const { wrapper, AppSchema, store } = await makeTodoMvc()
74
+ const { wrapper, AppComponentSchema, store } = await makeTodoMvc()
73
75
 
74
76
  const { result } = renderHook(
75
77
  (userId: string) => {
76
78
  renderCount++
77
79
 
78
80
  return LiveStoreReact.useComponentState({
79
- schema: AppSchema,
81
+ schema: AppComponentSchema,
80
82
  componentKey: { name: 'UserInfo', id: userId },
81
83
  })
82
84
  },
@@ -90,11 +92,115 @@ describe('useComponentState', () => {
90
92
  act(() => result.current.setState.username('username_u1_hello'))
91
93
 
92
94
  act(() => {
93
- void store.execute(sql`UPDATE components__UserInfo SET username = 'username_u1_hello' WHERE id = 'u1';`)
95
+ void store.execute(LiveStore.sql`UPDATE components__UserInfo SET username = 'username_u1_hello' WHERE id = 'u1';`)
94
96
  })
95
97
 
96
98
  expect(result.current.state.id).toBe('u1')
97
99
  expect(result.current.state.username).toBe('username_u1_hello')
98
100
  expect(renderCount).toBe(2)
99
101
  })
102
+
103
+ it('should work for a larger app', async () => {
104
+ const allTodos$ = LiveStore.querySQL<Todo>(`select * from todos`, { label: 'allTodos' })
105
+
106
+ const { wrapper, store } = await makeTodoMvc()
107
+
108
+ const AppRouterSchema = LiveStore.defineComponentStateSchema('AppRouter', {
109
+ currentTaskId: LiveStore.DbSchema.text({ default: null, nullable: true }),
110
+ })
111
+
112
+ const componentKey = { name: 'AppRouter', id: 'static' }
113
+
114
+ let appRouterRenderCount = 0
115
+ let globalSetState: LiveStoreReact.Setters<LiveStoreReact.GetStateTypeEncoded<typeof AppRouterSchema>> | undefined
116
+ const AppRouter: React.FC = () => {
117
+ appRouterRenderCount++
118
+
119
+ const { state, setState } = LiveStoreReact.useComponentState({ schema: AppRouterSchema, componentKey })
120
+
121
+ globalSetState = setState
122
+
123
+ return (
124
+ <div>
125
+ <TasksList setTaskId={setState.currentTaskId} />
126
+ <div role="current-id">Current Task Id: {state.currentTaskId ?? '-'}</div>
127
+ {state.currentTaskId ? <TaskDetails id={state.currentTaskId} /> : <div>Click on a task to see details</div>}
128
+ </div>
129
+ )
130
+ }
131
+
132
+ const TasksList: React.FC<{ setTaskId: (_: string) => void }> = ({ setTaskId }) => {
133
+ const allTodos = LiveStoreReact.useQuery(allTodos$)
134
+
135
+ return (
136
+ <div>
137
+ {allTodos.map((_) => (
138
+ <div key={_.id} onClick={() => setTaskId(_.id)}>
139
+ {_.id}
140
+ </div>
141
+ ))}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ const TaskDetails: React.FC<{ id: string }> = ({ id }) => {
147
+ const todo = LiveStoreReact.useTemporaryQuery(() =>
148
+ LiveStore.querySQL<Todo>(`select * from todos where id = '${id}' limit 1`).getFirstRow(),
149
+ )
150
+ return <div role="content">{JSON.stringify(todo)}</div>
151
+ }
152
+
153
+ const renderResult = render(<AppRouter />, { wrapper })
154
+
155
+ expect(appRouterRenderCount).toBe(1)
156
+
157
+ act(() =>
158
+ store.applyEvent('RawSql', {
159
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
160
+ writeTables: ['todos'],
161
+ }),
162
+ )
163
+
164
+ expect(appRouterRenderCount).toBe(1)
165
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: -"')
166
+
167
+ act(() => globalSetState!.currentTaskId('t1'))
168
+
169
+ expect(appRouterRenderCount).toBe(2)
170
+ expect(renderResult.getByRole('content').innerHTML).toMatchInlineSnapshot(
171
+ '"{\\"id\\":\\"t1\\",\\"text\\":\\"buy milk\\",\\"completed\\":0}"',
172
+ )
173
+
174
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t1"')
175
+
176
+ act(() =>
177
+ store.applyEvents([
178
+ {
179
+ eventType: 'RawSql',
180
+ args: {
181
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0);`,
182
+ writeTables: ['todos'],
183
+ },
184
+ },
185
+ {
186
+ eventType: 'updateComponentState',
187
+ args: {
188
+ componentKey: { _tag: 'custom', componentName: 'AppRouter', id: 'static' },
189
+ columnNames: ['currentTaskId'],
190
+ currentTaskId: 't2',
191
+ },
192
+ },
193
+ {
194
+ eventType: 'RawSql',
195
+ args: {
196
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t3', 'buy bread', 0);`,
197
+ writeTables: ['todos'],
198
+ },
199
+ },
200
+ ]),
201
+ )
202
+
203
+ expect(appRouterRenderCount).toBe(3)
204
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t2"')
205
+ })
100
206
  })
@@ -1,29 +1,26 @@
1
1
  import { act, renderHook } from '@testing-library/react'
2
+ import React from 'react'
2
3
  import { describe, expect, it } from 'vitest'
3
4
 
4
5
  import * as LiveStoreReact from '../../react/index.js'
5
- import { LiveStoreSQLQuery } from '../../reactiveQueries/sql.js'
6
+ import { querySQL } from '../../reactiveQueries/sql.js'
6
7
  import { sql } from '../../util.js'
7
8
  import type { Todo } from './fixture.js'
8
9
  import { makeTodoMvc } from './fixture.js'
9
10
 
10
- const query = new LiveStoreSQLQuery<Todo>({
11
- label: 'todo',
12
- genQueryString: `select * from todos`,
13
- // queriedTables: ['todos'],
14
- })
15
-
16
11
  describe('useQuery', () => {
17
12
  it('simple', async () => {
18
13
  let renderCount = 0
19
14
 
20
15
  const { wrapper, store } = await makeTodoMvc()
21
16
 
17
+ const allTodos$ = querySQL<Todo>(`select * from todos`)
18
+
22
19
  const { result } = renderHook(
23
20
  () => {
24
21
  renderCount++
25
22
 
26
- return LiveStoreReact.useQuery(query)
23
+ return LiveStoreReact.useQuery(allTodos$)
27
24
  },
28
25
  { wrapper },
29
26
  )
@@ -33,8 +30,7 @@ describe('useQuery', () => {
33
30
 
34
31
  act(() =>
35
32
  store.applyEvent('RawSql', {
36
- sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
37
- bindValues: {},
33
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
38
34
  writeTables: ['todos'],
39
35
  }),
40
36
  )
@@ -43,6 +39,52 @@ describe('useQuery', () => {
43
39
  expect(result.current[0]!.text).toBe('buy milk')
44
40
  expect(renderCount).toBe(2)
45
41
  })
46
- })
47
42
 
48
- // TODO write tests that use the same query in multiple components at the same time with different bind values
43
+ it('same `useQuery` hook invoked with different queries', async () => {
44
+ let renderCount = 0
45
+
46
+ const { wrapper, store } = await makeTodoMvc()
47
+
48
+ const todo1$ = querySQL<Todo>(`select * from todos where id = 't1'`, { label: 'libraryTracksView1' })
49
+ const todo2$ = querySQL<Todo>(`select * from todos where id = 't2'`, { label: 'libraryTracksView2' })
50
+
51
+ store.applyEvent('RawSql', {
52
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
53
+ writeTables: ['todos'],
54
+ })
55
+
56
+ store.applyEvent('RawSql', {
57
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0)`,
58
+ writeTables: ['todos'],
59
+ })
60
+
61
+ const { result, rerender } = renderHook(
62
+ (todoId: string) => {
63
+ renderCount++
64
+
65
+ const query$ = React.useMemo(() => (todoId === 't1' ? todo1$ : todo2$), [todoId])
66
+
67
+ return LiveStoreReact.useQuery(query$)[0]!.text
68
+ },
69
+ { wrapper, initialProps: 't1' },
70
+ )
71
+
72
+ expect(result.current).toBe('buy milk')
73
+ expect(renderCount).toBe(1)
74
+
75
+ act(() =>
76
+ store.applyEvent('RawSql', {
77
+ sql: sql`UPDATE todos SET text = 'buy soy milk' WHERE id = 't1'`,
78
+ writeTables: ['todos'],
79
+ }),
80
+ )
81
+
82
+ expect(result.current).toBe('buy soy milk')
83
+ expect(renderCount).toBe(2)
84
+
85
+ rerender('t2')
86
+
87
+ expect(result.current).toBe('buy eggs')
88
+ expect(renderCount).toBe(3)
89
+ })
90
+ })
@@ -159,6 +159,77 @@ describe('a trivial graph', () => {
159
159
  graph.setRef(a, 2)
160
160
  // expect(numberOfCallsToC).toBe(2) // TODO comp caching
161
161
  })
162
+
163
+ describe('skip refresh', () => {
164
+ it(`defers effect execution until manual run`, () => {
165
+ const { graph, a, c, d, numberOfRunsForC } = makeGraph()
166
+
167
+ // using here both to track number oe effect runs and to "update the effect behavior"
168
+ let numberOfEffectRuns = 0
169
+ const effect = graph.makeEffect((get) => {
170
+ expect(get(c)).toBe(numberOfEffectRuns === 0 ? 3 : 4)
171
+ numberOfEffectRuns++
172
+ })
173
+
174
+ effect.doEffect()
175
+
176
+ expect(numberOfEffectRuns).toBe(1)
177
+ expect(numberOfRunsForC.runs).toBe(1)
178
+
179
+ graph.setRef(a, 2, { skipRefresh: true })
180
+
181
+ expect(numberOfEffectRuns).toBe(1)
182
+ expect(numberOfRunsForC.runs).toBe(1)
183
+
184
+ // Even setting a unrelated ref should not trigger a refresh
185
+ graph.setRef(d, 0)
186
+
187
+ expect(numberOfEffectRuns).toBe(1)
188
+ expect(numberOfRunsForC.runs).toBe(1)
189
+
190
+ graph.runDeferredEffects()
191
+
192
+ expect(numberOfEffectRuns).toBe(2)
193
+ expect(numberOfRunsForC.runs).toBe(2)
194
+ })
195
+
196
+ it(`doesn't run deferred effects which have been destroyed already`, () => {
197
+ const { graph, a, c, numberOfRunsForC } = makeGraph()
198
+
199
+ let numberOfEffect1Runs = 0
200
+ const effect1 = graph.makeEffect((get) => {
201
+ expect(get(c)).toBe(numberOfEffect1Runs === 0 ? 3 : 4)
202
+ numberOfEffect1Runs++
203
+ })
204
+
205
+ let numberOfEffect2Runs = 0
206
+ const effect2 = graph.makeEffect((get) => {
207
+ expect(get(c)).toBe(numberOfEffect2Runs === 0 ? 3 : 4)
208
+ numberOfEffect2Runs++
209
+ })
210
+
211
+ effect1.doEffect()
212
+ effect2.doEffect()
213
+
214
+ expect(numberOfEffect1Runs).toBe(1)
215
+ expect(numberOfEffect2Runs).toBe(1)
216
+ expect(numberOfRunsForC.runs).toBe(1)
217
+
218
+ graph.setRef(a, 2, { skipRefresh: true })
219
+
220
+ expect(numberOfEffect1Runs).toBe(1)
221
+ expect(numberOfEffect2Runs).toBe(1)
222
+ expect(numberOfRunsForC.runs).toBe(1)
223
+
224
+ graph.destroy(effect1)
225
+
226
+ graph.runDeferredEffects()
227
+
228
+ expect(numberOfEffect1Runs).toBe(1)
229
+ expect(numberOfEffect2Runs).toBe(2)
230
+ expect(numberOfRunsForC.runs).toBe(2)
231
+ })
232
+ })
162
233
  })
163
234
  })
164
235
 
@@ -30,12 +30,11 @@ describe('otel', () => {
30
30
  it('otel', async () => {
31
31
  const { store, exporter, span } = await makeQuery()
32
32
 
33
- const query = querySQL(`select * from todos`, { queriedTables: ['todos'] })
33
+ const query = querySQL(`select * from todos`, { queriedTables: new Set(['todos']) })
34
34
  expect(query.run()).toMatchInlineSnapshot('[]')
35
35
 
36
36
  store.applyEvent('RawSql', {
37
37
  sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
38
- bindValues: {},
39
38
  writeTables: ['todos'],
40
39
  })
41
40
 
@@ -96,7 +95,6 @@ describe('otel', () => {
96
95
  "livestore.actionType": "RawSql",
97
96
  "livestore.args": "{
98
97
  \\"sql\\": \\"INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);\\",
99
- \\"bindValues\\": {},
100
98
  \\"writeTables\\": [
101
99
  \\"todos\\"
102
100
  ]
@@ -165,10 +163,7 @@ describe('otel', () => {
165
163
  const defaultTodo = { id: '', text: '', completed: 0 }
166
164
 
167
165
  const filter = queryJS(() => `where completed = 0`, { label: 'where-filter' })
168
- const query = querySQL((get) => `select * from todos ${get(filter)}`, {
169
- // queriedTables: ['todos'],
170
- label: 'all todos',
171
- }).getFirstRow({
166
+ const query = querySQL((get) => `select * from todos ${get(filter)}`, { label: 'all todos' }).getFirstRow({
172
167
  defaultValue: defaultTodo,
173
168
  })
174
169
 
@@ -182,7 +177,6 @@ describe('otel', () => {
182
177
 
183
178
  store.applyEvent('RawSql', {
184
179
  sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
185
- bindValues: {},
186
180
  writeTables: ['todos'],
187
181
  })
188
182
 
@@ -241,7 +235,6 @@ describe('otel', () => {
241
235
  "livestore.actionType": "RawSql",
242
236
  "livestore.args": "{
243
237
  \\"sql\\": \\"INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);\\",
244
- \\"bindValues\\": {},
245
238
  \\"writeTables\\": [
246
239
  \\"todos\\"
247
240
  ]
@@ -78,7 +78,7 @@ export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>(
78
78
 
79
79
  const otelRootSpanContext = otel.context.active()
80
80
 
81
- const otelTracer = yield* $(OtelTracer.OtelTracer)
81
+ const otelTracer = yield* $(OtelTracer.Tracer)
82
82
 
83
83
  const graphQLOptions = yield* $(
84
84
  graphQLOptions_
@@ -25,7 +25,7 @@ export type SlowQueryInfo = [
25
25
  bindValues: PreparedBindValues | undefined,
26
26
  durationMs: number,
27
27
  rowsCount: number | undefined,
28
- queriedTables: ReadonlyArray<string>,
28
+ queriedTables: Set<string>,
29
29
  startTimePerfNow: DOMHighResTimeStamp,
30
30
  ]
31
31
 
@@ -39,7 +39,7 @@ export const emptyDebugInfo = (): DebugInfo => ({
39
39
  export class InMemoryDatabase {
40
40
  // TODO: how many unique active statements are expected?
41
41
  private cachedStmts = new BoundMap<string, Sqlite.PreparedStatement>(200)
42
- private tablesUsedCache = new BoundMap<string, string[]>(200)
42
+ private tablesUsedCache = new BoundMap<string, Set<string>>(200)
43
43
  private resultCache = new QueryCache()
44
44
  private tablesUsedStmt
45
45
  public debugInfo: DebugInfo = emptyDebugInfo()
@@ -119,17 +119,17 @@ export class InMemoryDatabase {
119
119
  return cached
120
120
  }
121
121
  const stmt = this.tablesUsedStmt
122
- const tablesUsed = []
122
+ const tablesUsed = new Set<string>()
123
123
  try {
124
124
  stmt.bind([query])
125
125
  while (stmt.step()) {
126
- tablesUsed.push(stmt.get(0))
126
+ tablesUsed.add(stmt.get(0))
127
127
  }
128
128
  } finally {
129
129
  stmt.reset()
130
130
  }
131
- this.tablesUsedCache.set(query, tablesUsed as string[])
132
- return tablesUsed as string[]
131
+ this.tablesUsedCache.set(query, tablesUsed)
132
+ return tablesUsed
133
133
  }
134
134
 
135
135
  execute(
@@ -192,7 +192,7 @@ export class InMemoryDatabase {
192
192
  bindValues,
193
193
  durationMs,
194
194
  undefined,
195
- [],
195
+ new Set(),
196
196
  getStartTimeHighResFromSpan(span),
197
197
  ])
198
198
  }
@@ -205,7 +205,7 @@ export class InMemoryDatabase {
205
205
  select<T = any>(
206
206
  query: string,
207
207
  options?: {
208
- queriedTables?: ReadonlyArray<string>
208
+ queriedTables?: Set<string>
209
209
  bindValues?: PreparedBindValues
210
210
  skipCache?: boolean
211
211
  otelContext?: otel.Context
package/src/index.ts CHANGED
@@ -13,18 +13,10 @@ export {
13
13
  } from './schema.js'
14
14
  export { InMemoryDatabase, type DebugInfo, emptyDebugInfo } from './inMemoryDatabase.js'
15
15
  export type { Storage, StorageType, StorageInit } from './storage/index.js'
16
- export type {
17
- GetAtom,
18
- AtomDebugInfo,
19
- RefreshDebugInfo,
20
- RefreshReasonWithGenericReasons,
21
- SerializedAtom,
22
- SerializedEffect,
23
- Atom,
24
- } from './reactive.js'
25
- export { type LiveStoreJSQuery, queryJS } from './reactiveQueries/js.js'
26
- export { type LiveStoreSQLQuery, querySQL } from './reactiveQueries/sql.js'
27
- export { type LiveStoreGraphQLQuery, queryGraphQL } from './reactiveQueries/graphql.js'
16
+ export type { GetAtom, AtomDebugInfo, RefreshDebugInfo, SerializedAtom, Atom } from './reactive.js'
17
+ export { LiveStoreJSQuery, queryJS } from './reactiveQueries/js.js'
18
+ export { LiveStoreSQLQuery, querySQL } from './reactiveQueries/sql.js'
19
+ export { LiveStoreGraphQLQuery, queryGraphQL } from './reactiveQueries/graphql.js'
28
20
  export { type GetAtomResult } from './reactiveQueries/base-class.js'
29
21
  export { dbGraph } from './reactiveQueries/graph.js'
30
22
 
package/src/migrations.ts CHANGED
@@ -45,11 +45,17 @@ export const migrateDb = ({
45
45
  const dbSchemaHash = dbSchemaHashByTable[tableName]
46
46
  const schemaHash = SqliteAst.hash(tableDef)
47
47
  if (schemaHash !== dbSchemaHash) {
48
- console.log(
49
- `Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
50
- )
51
-
52
- migrateTable({ db, tableDef, otelContext, schemaHash })
48
+ if (import.meta.env.VITE_LIVESTORE_SKIP_MIGRATIONS) {
49
+ console.log(
50
+ `Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), skipping migration...`,
51
+ )
52
+ } else {
53
+ console.log(
54
+ `Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
55
+ )
56
+
57
+ migrateTable({ db, tableDef, otelContext, schemaHash })
58
+ }
53
59
  }
54
60
  }
55
61
  }
@@ -112,8 +118,8 @@ const toSqliteColumnSpec = (column: SqliteAst.Column) => {
112
118
  column.default === undefined
113
119
  ? ''
114
120
  : columnType === 'text'
115
- ? `default '${column.default}'`
116
- : `default ${column.default}`
121
+ ? `default '${column.default}'`
122
+ : `default ${column.default}`
117
123
 
118
124
  return `${column.name} ${columnType} ${nullable} ${defaultValue}`
119
125
  }