@livestore/livestore 0.0.24 → 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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.d.ts +2 -2
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +2 -2
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +2 -2
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useComponentState.test.js +78 -10
- package/dist/__tests__/react/useComponentState.test.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.js +35 -10
- package/dist/__tests__/react/useQuery.test.js.map +1 -1
- package/dist/__tests__/reactive.test.js +51 -0
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.js +2 -9
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -1
- package/dist/effect/LiveStore.js +1 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/inMemoryDatabase.d.ts +3 -3
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/inMemoryDatabase.js +3 -3
- package/dist/inMemoryDatabase.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations.js.map +1 -1
- package/dist/react/useComponentState.d.ts +3 -3
- package/dist/react/useComponentState.d.ts.map +1 -1
- package/dist/react/useComponentState.js +43 -57
- package/dist/react/useComponentState.js.map +1 -1
- package/dist/react/useQuery.js +2 -2
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/utils/stack-info.d.ts.map +1 -1
- package/dist/react/utils/stack-info.js +0 -1
- package/dist/react/utils/stack-info.js.map +1 -1
- package/dist/reactive.d.ts +37 -28
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +44 -19
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +4 -4
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +2 -1
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +6 -1
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +2 -2
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +1 -1
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/store.d.ts +2 -11
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +6 -11
- package/dist/store.js.map +1 -1
- package/package.json +16 -13
- package/src/QueryCache.ts +2 -2
- package/src/__tests__/react/fixture.tsx +3 -3
- package/src/__tests__/react/useComponentState.test.tsx +116 -10
- package/src/__tests__/react/useQuery.test.tsx +54 -12
- package/src/__tests__/reactive.test.ts +71 -0
- package/src/__tests__/reactiveQueries/sql.test.ts +2 -9
- package/src/effect/LiveStore.ts +1 -1
- package/src/inMemoryDatabase.ts +8 -8
- package/src/index.ts +4 -12
- package/src/migrations.ts +2 -2
- package/src/react/useComponentState.ts +53 -72
- package/src/react/useQuery.ts +2 -2
- package/src/react/utils/stack-info.ts +0 -1
- package/src/reactive.ts +80 -64
- package/src/reactiveQueries/base-class.ts +6 -8
- package/src/reactiveQueries/js.ts +6 -1
- package/src/reactiveQueries/sql.ts +3 -3
- package/src/store.ts +12 -24
- package/dist/__tests__/react/useLQuery.test.d.ts +0 -2
- package/dist/__tests__/react/useLQuery.test.d.ts.map +0 -1
- package/dist/__tests__/react/useLQuery.test.js +0 -38
- package/dist/__tests__/react/useLQuery.test.js.map +0 -1
- package/dist/__tests__/react/useLiveStoreComponent.test.d.ts +0 -2
- package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +0 -1
- package/dist/__tests__/react/useLiveStoreComponent.test.js +0 -73
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +0 -1
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +0 -2
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +0 -1
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +0 -38
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +0 -1
- package/dist/react/useGlobalQuery.d.ts +0 -3
- package/dist/react/useGlobalQuery.d.ts.map +0 -1
- package/dist/react/useGlobalQuery.js +0 -26
- package/dist/react/useGlobalQuery.js.map +0 -1
- package/dist/react/useGraphQL.d.ts +0 -13
- package/dist/react/useGraphQL.d.ts.map +0 -1
- package/dist/react/useGraphQL.js +0 -87
- package/dist/react/useGraphQL.js.map +0 -1
- package/dist/react/useLiveStoreComponent.d.ts +0 -75
- package/dist/react/useLiveStoreComponent.d.ts.map +0 -1
- package/dist/react/useLiveStoreComponent.js +0 -361
- package/dist/react/useLiveStoreComponent.js.map +0 -1
- package/dist/react/utils/extractNamesFromStackTrace.d.ts +0 -3
- package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +0 -1
- package/dist/react/utils/extractNamesFromStackTrace.js +0 -40
- package/dist/react/utils/extractNamesFromStackTrace.js.map +0 -1
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +0 -7
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +0 -1
- package/dist/react/utils/extractStackInfoFromStackTrace.js +0 -40
- 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
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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:
|
|
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,
|
|
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:
|
|
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,
|
|
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:
|
|
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 {
|
|
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(
|
|
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
|
-
|
|
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
|
]
|
package/src/effect/LiveStore.ts
CHANGED
|
@@ -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.
|
|
81
|
+
const otelTracer = yield* $(OtelTracer.Tracer)
|
|
82
82
|
|
|
83
83
|
const graphQLOptions = yield* $(
|
|
84
84
|
graphQLOptions_
|
package/src/inMemoryDatabase.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type SlowQueryInfo = [
|
|
|
25
25
|
bindValues: PreparedBindValues | undefined,
|
|
26
26
|
durationMs: number,
|
|
27
27
|
rowsCount: number | undefined,
|
|
28
|
-
queriedTables:
|
|
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
|
|
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.
|
|
126
|
+
tablesUsed.add(stmt.get(0))
|
|
127
127
|
}
|
|
128
128
|
} finally {
|
|
129
129
|
stmt.reset()
|
|
130
130
|
}
|
|
131
|
-
this.tablesUsedCache.set(query, tablesUsed
|
|
132
|
-
return tablesUsed
|
|
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?:
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
@@ -118,8 +118,8 @@ const toSqliteColumnSpec = (column: SqliteAst.Column) => {
|
|
|
118
118
|
column.default === undefined
|
|
119
119
|
? ''
|
|
120
120
|
: columnType === 'text'
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
? `default '${column.default}'`
|
|
122
|
+
: `default ${column.default}`
|
|
123
123
|
|
|
124
124
|
return `${column.name} ${columnType} ${nullable} ${defaultValue}`
|
|
125
125
|
}
|