@livestore/react 0.4.0 → 0.5.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/fixture.d.ts +2 -2
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +1 -1
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/experimental/components/LiveList.d.ts +1 -1
- package/dist/experimental/components/LiveList.d.ts.map +1 -1
- package/dist/experimental/components/LiveList.js +1 -1
- package/dist/experimental/components/LiveList.js.map +1 -1
- package/dist/useClientDocument.test.js +14 -18
- package/dist/useClientDocument.test.js.map +1 -1
- package/dist/useQuery.test.js +14 -14
- package/dist/useQuery.test.js.map +1 -1
- package/dist/useRcResource.js +2 -2
- package/dist/useRcResource.js.map +1 -1
- package/dist/useRcResource.test.js +1 -0
- package/dist/useRcResource.test.js.map +1 -1
- package/dist/useStore.d.ts +1 -1
- package/dist/useStore.d.ts.map +1 -1
- package/dist/useStore.test.js +4 -3
- package/dist/useStore.test.js.map +1 -1
- package/package.json +16 -26
- package/src/__snapshots__/useClientDocument.test.tsx.snap +58 -18
- package/src/__tests__/fixture.tsx +3 -2
- package/src/experimental/components/LiveList.tsx +2 -1
- package/src/useClientDocument.test.tsx +88 -84
- package/src/useQuery.test.tsx +15 -14
- package/src/useRcResource.test.tsx +1 -0
- package/src/useRcResource.ts +2 -2
- package/src/useStore.test.tsx +13 -3
- package/src/useStore.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestore/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-dev.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@opentelemetry/api": "1.9.0",
|
|
25
|
-
"@livestore/
|
|
26
|
-
"@livestore/
|
|
27
|
-
"@livestore/
|
|
28
|
-
"@livestore/
|
|
25
|
+
"@livestore/framework-toolkit": "^0.5.0-dev.0",
|
|
26
|
+
"@livestore/livestore": "^0.5.0-dev.0",
|
|
27
|
+
"@livestore/utils": "^0.5.0-dev.0",
|
|
28
|
+
"@livestore/common": "^0.5.0-dev.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@opentelemetry/sdk-trace-base": "2.2.0",
|
|
@@ -38,32 +38,23 @@
|
|
|
38
38
|
"react": "19.2.3",
|
|
39
39
|
"react-dom": "19.2.3",
|
|
40
40
|
"react-window": "1.8.11",
|
|
41
|
-
"typescript": "
|
|
41
|
+
"typescript": "6.0.3",
|
|
42
42
|
"vite": "7.3.1",
|
|
43
|
-
"vitest": "
|
|
44
|
-
"@livestore/utils-dev": "^0.
|
|
45
|
-
"@livestore/adapter-web": "^0.
|
|
43
|
+
"vitest": "4.1.9",
|
|
44
|
+
"@livestore/utils-dev": "^0.5.0-dev.0",
|
|
45
|
+
"@livestore/adapter-web": "^0.5.0-dev.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@effect/
|
|
49
|
-
"@effect/
|
|
50
|
-
"@effect/
|
|
51
|
-
"@effect/
|
|
52
|
-
"@effect/
|
|
53
|
-
"@effect/
|
|
54
|
-
"@effect/platform-browser": "^0.76.0",
|
|
55
|
-
"@effect/platform-bun": "^0.89.0",
|
|
56
|
-
"@effect/platform-node": "^0.106.0",
|
|
57
|
-
"@effect/printer": "^0.49.0",
|
|
58
|
-
"@effect/printer-ansi": "^0.49.0",
|
|
59
|
-
"@effect/rpc": "^0.75.1",
|
|
60
|
-
"@effect/sql": "^0.51.1",
|
|
61
|
-
"@effect/typeclass": "^0.40.0",
|
|
62
|
-
"@effect/vitest": "^0.29.0",
|
|
48
|
+
"@effect/opentelemetry": "^4.0.0-rc.111",
|
|
49
|
+
"@effect/platform-browser": "^4.0.0-rc.111",
|
|
50
|
+
"@effect/platform-bun": "^4.0.0-rc.111",
|
|
51
|
+
"@effect/platform-node": "^4.0.0-rc.111",
|
|
52
|
+
"@effect/platform-node-shared": "^4.0.0-rc.111",
|
|
53
|
+
"@effect/vitest": "^4.0.0-rc.111",
|
|
63
54
|
"@opentelemetry/api": "^1.9.0",
|
|
64
55
|
"@opentelemetry/resources": "^2.2.0",
|
|
65
56
|
"@standard-schema/spec": "^1.1.0",
|
|
66
|
-
"effect": "^
|
|
57
|
+
"effect": "^4.0.0-rc.111",
|
|
67
58
|
"react": "^19.0.0"
|
|
68
59
|
},
|
|
69
60
|
"$genie": {
|
|
@@ -73,7 +64,6 @@
|
|
|
73
64
|
"packages/@livestore/adapter-web",
|
|
74
65
|
"packages/@livestore/common",
|
|
75
66
|
"packages/@livestore/common-cf",
|
|
76
|
-
"packages/@livestore/devtools-web-common",
|
|
77
67
|
"packages/@livestore/framework-toolkit",
|
|
78
68
|
"packages/@livestore/livestore",
|
|
79
69
|
"packages/@livestore/react",
|
|
@@ -27,10 +27,10 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
27
27
|
"_name": "client-session-sync-processor:boot",
|
|
28
28
|
"children": [
|
|
29
29
|
{
|
|
30
|
-
"_name": "
|
|
30
|
+
"_name": "client-session-sync-processor:pull",
|
|
31
31
|
"attributes": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"span.label": "⚠︎ Interrupted",
|
|
33
|
+
"status.interrupted": true,
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
{
|
|
@@ -38,12 +38,6 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
38
38
|
"attributes": {
|
|
39
39
|
"batch": "undefined",
|
|
40
40
|
"batchSize": 1,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"_name": "client-session-sync-processor:pull",
|
|
45
|
-
"attributes": {
|
|
46
|
-
"code.stacktrace": "<STACKTRACE>",
|
|
47
41
|
"span.label": "⚠︎ Interrupted",
|
|
48
42
|
"status.interrupted": true,
|
|
49
43
|
},
|
|
@@ -217,6 +211,19 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
217
211
|
",
|
|
218
212
|
},
|
|
219
213
|
},
|
|
214
|
+
{
|
|
215
|
+
"_name": "@livestore/common:execSql",
|
|
216
|
+
"attributes": {
|
|
217
|
+
"bindValueKeys": "[
|
|
218
|
+
"$id",
|
|
219
|
+
"$seqNumGlobal",
|
|
220
|
+
"$seqNumClient",
|
|
221
|
+
"$seqNumRebaseGeneration"
|
|
222
|
+
]",
|
|
223
|
+
"span.label": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
224
|
+
"sql": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
225
|
+
},
|
|
226
|
+
},
|
|
220
227
|
],
|
|
221
228
|
},
|
|
222
229
|
],
|
|
@@ -261,6 +268,19 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
261
268
|
",
|
|
262
269
|
},
|
|
263
270
|
},
|
|
271
|
+
{
|
|
272
|
+
"_name": "@livestore/common:execSql",
|
|
273
|
+
"attributes": {
|
|
274
|
+
"bindValueKeys": "[
|
|
275
|
+
"$id",
|
|
276
|
+
"$seqNumGlobal",
|
|
277
|
+
"$seqNumClient",
|
|
278
|
+
"$seqNumRebaseGeneration"
|
|
279
|
+
]",
|
|
280
|
+
"span.label": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
281
|
+
"sql": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
264
284
|
],
|
|
265
285
|
},
|
|
266
286
|
],
|
|
@@ -307,10 +327,10 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
307
327
|
"_name": "client-session-sync-processor:boot",
|
|
308
328
|
"children": [
|
|
309
329
|
{
|
|
310
|
-
"_name": "
|
|
330
|
+
"_name": "client-session-sync-processor:pull",
|
|
311
331
|
"attributes": {
|
|
312
|
-
"
|
|
313
|
-
"
|
|
332
|
+
"span.label": "⚠︎ Interrupted",
|
|
333
|
+
"status.interrupted": true,
|
|
314
334
|
},
|
|
315
335
|
},
|
|
316
336
|
{
|
|
@@ -318,12 +338,6 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
318
338
|
"attributes": {
|
|
319
339
|
"batch": "undefined",
|
|
320
340
|
"batchSize": 1,
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
"_name": "client-session-sync-processor:pull",
|
|
325
|
-
"attributes": {
|
|
326
|
-
"code.stacktrace": "<STACKTRACE>",
|
|
327
341
|
"span.label": "⚠︎ Interrupted",
|
|
328
342
|
"status.interrupted": true,
|
|
329
343
|
},
|
|
@@ -435,6 +449,19 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
435
449
|
",
|
|
436
450
|
},
|
|
437
451
|
},
|
|
452
|
+
{
|
|
453
|
+
"_name": "@livestore/common:execSql",
|
|
454
|
+
"attributes": {
|
|
455
|
+
"bindValueKeys": "[
|
|
456
|
+
"$id",
|
|
457
|
+
"$seqNumGlobal",
|
|
458
|
+
"$seqNumClient",
|
|
459
|
+
"$seqNumRebaseGeneration"
|
|
460
|
+
]",
|
|
461
|
+
"span.label": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
462
|
+
"sql": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
463
|
+
},
|
|
464
|
+
},
|
|
438
465
|
],
|
|
439
466
|
},
|
|
440
467
|
],
|
|
@@ -479,6 +506,19 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
|
479
506
|
",
|
|
480
507
|
},
|
|
481
508
|
},
|
|
509
|
+
{
|
|
510
|
+
"_name": "@livestore/common:execSql",
|
|
511
|
+
"attributes": {
|
|
512
|
+
"bindValueKeys": "[
|
|
513
|
+
"$id",
|
|
514
|
+
"$seqNumGlobal",
|
|
515
|
+
"$seqNumClient",
|
|
516
|
+
"$seqNumRebaseGeneration"
|
|
517
|
+
]",
|
|
518
|
+
"span.label": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
519
|
+
"sql": "INSERT OR REPLACE INTO __livestore_state_head (id, seqNumGlobal, seqNumClient, seqNumRebaseGeneration) VALUES ($id, $seqNumGlobal, $seqNumClient, $seqNumRebaseGeneration)",
|
|
520
|
+
},
|
|
521
|
+
},
|
|
482
522
|
],
|
|
483
523
|
},
|
|
484
524
|
],
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
1
3
|
import type { UnknownError } from '@livestore/common'
|
|
2
4
|
import {
|
|
3
5
|
type AppState,
|
|
@@ -11,13 +13,12 @@ import {
|
|
|
11
13
|
} from '@livestore/framework-toolkit/testing'
|
|
12
14
|
import type { Store } from '@livestore/livestore'
|
|
13
15
|
import { Effect, type Scope } from '@livestore/utils/effect'
|
|
14
|
-
import React from 'react'
|
|
15
16
|
|
|
16
17
|
import * as LiveStoreReact from '../mod.ts'
|
|
17
18
|
|
|
19
|
+
export type { AppState, Filter, Todo }
|
|
18
20
|
// Re-export shared types and schema
|
|
19
21
|
export { events, schema, tables }
|
|
20
|
-
export type { AppState, Filter, Todo }
|
|
21
22
|
|
|
22
23
|
export type MakeTodoMvcReactOptions = CreateTodoMvcStoreOptions & {
|
|
23
24
|
strictMode?: boolean | undefined
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
/** biome-ignore-all lint/a11y/useValidAriaRole: not needed for testing */
|
|
2
|
-
/** biome-ignore-all lint/a11y/noStaticElementInteractions: not needed for testing */
|
|
3
|
-
import * as LiveStore from '@livestore/livestore'
|
|
4
|
-
import { StoreInternalsSymbol } from '@livestore/livestore'
|
|
5
|
-
import { getAllSimplifiedRootSpans, getSimplifiedRootSpan } from '@livestore/livestore/internal/testing-utils'
|
|
6
|
-
import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
7
|
-
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
8
1
|
import * as otel from '@opentelemetry/api'
|
|
9
2
|
import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
|
|
10
3
|
import * as ReactTesting from '@testing-library/react'
|
|
11
4
|
import * as React from 'react'
|
|
12
5
|
import { beforeEach, expect, it } from 'vitest'
|
|
13
6
|
|
|
7
|
+
import * as LiveStore from '@livestore/livestore'
|
|
8
|
+
import { StoreInternalsSymbol } from '@livestore/livestore'
|
|
9
|
+
import { getAllSimplifiedRootSpans, getSimplifiedRootSpan } from '@livestore/livestore/internal/testing-utils'
|
|
10
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
11
|
+
import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
12
|
+
|
|
14
13
|
import { events, makeTodoMvcReact, tables } from './__tests__/fixture.tsx'
|
|
15
14
|
import type * as LiveStoreReact from './mod.ts'
|
|
16
15
|
import { __resetUseRcResourceCache } from './useRcResource.ts'
|
|
@@ -18,12 +17,13 @@ import { __resetUseRcResourceCache } from './useRcResource.ts'
|
|
|
18
17
|
// const strictMode = process.env.REACT_STRICT_MODE !== undefined
|
|
19
18
|
|
|
20
19
|
// NOTE running tests concurrently doesn't work with the default global db graph
|
|
20
|
+
/** Verifies: LS.SYS.INT.REACT-R01, LS.SYS.STATE.SQLITE-R07 */
|
|
21
21
|
Vitest.describe('useClientDocument', () => {
|
|
22
22
|
beforeEach(() => {
|
|
23
23
|
__resetUseRcResourceCache()
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
Vitest.
|
|
26
|
+
Vitest.live('should update the data based on component key', () =>
|
|
27
27
|
Effect.gen(function* () {
|
|
28
28
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
|
|
29
29
|
|
|
@@ -54,7 +54,7 @@ Vitest.describe('useClientDocument', () => {
|
|
|
54
54
|
|
|
55
55
|
// TODO add a test that makes sure React doesn't re-render when a setter is used to set the same value
|
|
56
56
|
|
|
57
|
-
Vitest.
|
|
57
|
+
Vitest.live('should update the data reactively - via setState', () =>
|
|
58
58
|
Effect.gen(function* () {
|
|
59
59
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
|
|
60
60
|
|
|
@@ -80,7 +80,7 @@ Vitest.describe('useClientDocument', () => {
|
|
|
80
80
|
}),
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
-
Vitest.
|
|
83
|
+
Vitest.live('should update the data reactively - via raw store commit', () =>
|
|
84
84
|
Effect.gen(function* () {
|
|
85
85
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
|
|
86
86
|
|
|
@@ -106,7 +106,7 @@ Vitest.describe('useClientDocument', () => {
|
|
|
106
106
|
}),
|
|
107
107
|
)
|
|
108
108
|
|
|
109
|
-
Vitest.
|
|
109
|
+
Vitest.live('should work for a larger app', () =>
|
|
110
110
|
Effect.gen(function* () {
|
|
111
111
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
|
|
112
112
|
|
|
@@ -128,7 +128,11 @@ Vitest.describe('useClientDocument', () => {
|
|
|
128
128
|
<div>
|
|
129
129
|
<TasksList setTaskId={setCurrentTaskId} />
|
|
130
130
|
<div role="current-id">Current Task Id: {state.currentTaskId ?? '-'}</div>
|
|
131
|
-
{state.currentTaskId !== null ?
|
|
131
|
+
{state.currentTaskId !== null ? (
|
|
132
|
+
<TaskDetails id={state.currentTaskId} />
|
|
133
|
+
) : (
|
|
134
|
+
<div>Click on a task to see details</div>
|
|
135
|
+
)}
|
|
132
136
|
</div>
|
|
133
137
|
)
|
|
134
138
|
}
|
|
@@ -192,7 +196,7 @@ Vitest.describe('useClientDocument', () => {
|
|
|
192
196
|
}),
|
|
193
197
|
)
|
|
194
198
|
|
|
195
|
-
Vitest.
|
|
199
|
+
Vitest.live('should work for a useClientDocument query chained with a useTemporary query', () =>
|
|
196
200
|
Effect.gen(function* () {
|
|
197
201
|
const { store, wrapper, renderCount } = yield* makeTodoMvcReact({})
|
|
198
202
|
|
|
@@ -235,7 +239,7 @@ Vitest.describe('useClientDocument', () => {
|
|
|
235
239
|
}),
|
|
236
240
|
)
|
|
237
241
|
|
|
238
|
-
Vitest.
|
|
242
|
+
Vitest.live('kv client document overwrites value (Schema.Any, no partial merge)', () =>
|
|
239
243
|
Effect.gen(function* () {
|
|
240
244
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
|
|
241
245
|
|
|
@@ -264,81 +268,81 @@ Vitest.describe('useClientDocument', () => {
|
|
|
264
268
|
)
|
|
265
269
|
|
|
266
270
|
Vitest.describe('otel', () => {
|
|
267
|
-
it.each([
|
|
268
|
-
|
|
269
|
-
{ strictMode
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
spanProcessors: [new SimpleSpanProcessor(exporter)],
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const otelTracer = provider.getTracer(`testing-${strictMode !== undefined ? 'strict' : 'non-strict'}`)
|
|
278
|
-
|
|
279
|
-
const span = otelTracer.startSpan('test-root')
|
|
280
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
281
|
-
|
|
282
|
-
await Effect.gen(function* () {
|
|
283
|
-
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({
|
|
284
|
-
otelContext,
|
|
285
|
-
otelTracer,
|
|
286
|
-
strictMode,
|
|
271
|
+
it.each([{ strictMode: true }, { strictMode: false }])(
|
|
272
|
+
'should update the data based on component key strictMode=%s',
|
|
273
|
+
async ({ strictMode }) => {
|
|
274
|
+
const exporter = new InMemorySpanExporter()
|
|
275
|
+
|
|
276
|
+
const provider = new BasicTracerProvider({
|
|
277
|
+
spanProcessors: [new SimpleSpanProcessor(exporter)],
|
|
287
278
|
})
|
|
288
279
|
|
|
289
|
-
const {
|
|
290
|
-
(userId: string) => {
|
|
291
|
-
renderCount.inc()
|
|
280
|
+
const otelTracer = provider.getTracer(`testing-${strictMode !== undefined ? 'strict' : 'non-strict'}`)
|
|
292
281
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
},
|
|
296
|
-
{ wrapper, initialProps: 'u1' },
|
|
297
|
-
)
|
|
282
|
+
const span = otelTracer.startSpan('test-root')
|
|
283
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
298
284
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
285
|
+
await Effect.gen(function* () {
|
|
286
|
+
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({
|
|
287
|
+
otelContext,
|
|
288
|
+
otelTracer,
|
|
289
|
+
strictMode,
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
const { result, rerender, unmount } = ReactTesting.renderHook(
|
|
293
|
+
(userId: string) => {
|
|
294
|
+
renderCount.inc()
|
|
295
|
+
|
|
296
|
+
const [state, setState, id] = store.useClientDocument(tables.userInfo, userId)
|
|
297
|
+
return { state, setState, id }
|
|
298
|
+
},
|
|
299
|
+
{ wrapper, initialProps: 'u1' },
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
expect(result.current.id).toBe('u1')
|
|
303
|
+
expect(result.current.state.username).toBe('')
|
|
304
|
+
expect(renderCount.val).toBe(1)
|
|
305
|
+
|
|
306
|
+
// For u2 we'll make sure that the row already exists,
|
|
307
|
+
// so the lazy `insert` will be skipped
|
|
308
|
+
ReactTesting.act(() => store.commit(events.UserInfoSet({ username: 'username_u2' }, 'u2')))
|
|
309
|
+
|
|
310
|
+
rerender('u2')
|
|
311
|
+
|
|
312
|
+
expect(result.current.id).toBe('u2')
|
|
313
|
+
expect(result.current.state.username).toBe('username_u2')
|
|
314
|
+
expect(renderCount.val).toBe(2)
|
|
315
|
+
|
|
316
|
+
unmount()
|
|
317
|
+
span.end()
|
|
318
|
+
}).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
|
|
319
|
+
|
|
320
|
+
await provider.forceFlush()
|
|
321
|
+
|
|
322
|
+
const mapAttributes = (attributes: otel.Attributes) => {
|
|
323
|
+
return ReadonlyRecord.map(attributes, (val, key) => {
|
|
324
|
+
if (key === 'code.stacktrace') {
|
|
325
|
+
return '<STACKTRACE>'
|
|
326
|
+
} else if (key === 'firstStackInfo') {
|
|
327
|
+
const stackInfo = JSON.parse(val as string) as LiveStore.StackInfo
|
|
328
|
+
// stackInfo.frames.shift() // Removes `renderHook.wrapper` from the stack
|
|
329
|
+
stackInfo.frames.forEach((_) => {
|
|
330
|
+
if (_.name.includes('renderHook.wrapper') === true) {
|
|
331
|
+
_.name = 'renderHook.wrapper'
|
|
332
|
+
}
|
|
333
|
+
_.filePath = '__REPLACED_FOR_SNAPSHOT__'
|
|
334
|
+
})
|
|
335
|
+
return JSON.stringify(stackInfo)
|
|
336
|
+
}
|
|
337
|
+
return val
|
|
338
|
+
})
|
|
339
|
+
}
|
|
337
340
|
|
|
338
|
-
|
|
339
|
-
|
|
341
|
+
expect(getSimplifiedRootSpan(exporter, 'createStore', mapAttributes)).toMatchSnapshot()
|
|
342
|
+
expect(getAllSimplifiedRootSpans(exporter, 'LiveStore:commit', mapAttributes)).toMatchSnapshot()
|
|
340
343
|
|
|
341
|
-
|
|
342
|
-
|
|
344
|
+
await provider.shutdown()
|
|
345
|
+
},
|
|
346
|
+
)
|
|
343
347
|
})
|
|
344
348
|
})
|
package/src/useQuery.test.tsx
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import * as ReactTesting from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import * as ReactWindow from 'react-window'
|
|
4
|
+
import { expect } from 'vitest'
|
|
5
|
+
|
|
2
6
|
import { makeInMemoryAdapter } from '@livestore/adapter-web'
|
|
3
7
|
import { provideOtel } from '@livestore/common'
|
|
4
8
|
import * as LiveStore from '@livestore/livestore'
|
|
5
9
|
import { createStore, queryDb, StoreInternalsSymbol, signal } from '@livestore/livestore'
|
|
6
10
|
import { RG } from '@livestore/livestore/internal/testing-utils'
|
|
7
|
-
import { Effect, Schema } from '@livestore/utils/effect'
|
|
8
11
|
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
9
|
-
import
|
|
10
|
-
import React from 'react'
|
|
11
|
-
import * as ReactWindow from 'react-window'
|
|
12
|
-
import { expect } from 'vitest'
|
|
12
|
+
import { Effect, Schema } from '@livestore/utils/effect'
|
|
13
13
|
|
|
14
14
|
import { events, makeTodoMvcReact, schema, tables } from './__tests__/fixture.tsx'
|
|
15
15
|
import { __resetUseRcResourceCache } from './useRcResource.ts'
|
|
16
16
|
import { withReactApi } from './useStore.ts'
|
|
17
17
|
|
|
18
|
+
/** Verifies: LS.SYS.INT.REACT-R01, LS.SYS.INT.REACT-R04, LS.SYS.INT.REACT-R05 */
|
|
18
19
|
Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
19
20
|
'useQuery (strictMode=%s)',
|
|
20
21
|
({ strictMode }) => {
|
|
@@ -23,7 +24,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
23
24
|
__resetUseRcResourceCache()
|
|
24
25
|
})
|
|
25
26
|
|
|
26
|
-
Vitest.
|
|
27
|
+
Vitest.live('simple', () =>
|
|
27
28
|
Effect.gen(function* () {
|
|
28
29
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({ strictMode })
|
|
29
30
|
|
|
@@ -51,7 +52,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
51
52
|
}),
|
|
52
53
|
)
|
|
53
54
|
|
|
54
|
-
Vitest.
|
|
55
|
+
Vitest.live('same `useQuery` hook invoked with different queries', () =>
|
|
55
56
|
Effect.gen(function* () {
|
|
56
57
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({ strictMode })
|
|
57
58
|
|
|
@@ -104,7 +105,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
104
105
|
}),
|
|
105
106
|
)
|
|
106
107
|
|
|
107
|
-
Vitest.
|
|
108
|
+
Vitest.live('filtered dependency query', () =>
|
|
108
109
|
Effect.gen(function* () {
|
|
109
110
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({ strictMode })
|
|
110
111
|
|
|
@@ -147,7 +148,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
147
148
|
// NOTE this test covers some special react lifecyle paths which I couldn't easily reproduce without react-window
|
|
148
149
|
// it basically causes a "query swap" in the `useMemo` and both a `useEffect` cleanup call.
|
|
149
150
|
// To handle this properly we introduced the `_tag: 'destroyed'` state in the `spanAlreadyStartedCache`.
|
|
150
|
-
Vitest.
|
|
151
|
+
Vitest.live('should work for a list with react-window', () =>
|
|
151
152
|
Effect.gen(function* () {
|
|
152
153
|
const { wrapper, store } = yield* makeTodoMvcReact({ strictMode })
|
|
153
154
|
|
|
@@ -181,7 +182,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
181
182
|
}),
|
|
182
183
|
)
|
|
183
184
|
|
|
184
|
-
Vitest.
|
|
185
|
+
Vitest.live('should work with signal', () =>
|
|
185
186
|
Effect.gen(function* () {
|
|
186
187
|
const { wrapper, store } = yield* makeTodoMvcReact({ strictMode })
|
|
187
188
|
const num$ = signal(0)
|
|
@@ -196,7 +197,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
196
197
|
}),
|
|
197
198
|
)
|
|
198
199
|
|
|
199
|
-
Vitest.
|
|
200
|
+
Vitest.live('supports query builders directly', () =>
|
|
200
201
|
Effect.gen(function* () {
|
|
201
202
|
const { wrapper, store } = yield* makeTodoMvcReact({ strictMode })
|
|
202
203
|
|
|
@@ -215,7 +216,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
215
216
|
}),
|
|
216
217
|
)
|
|
217
218
|
|
|
218
|
-
Vitest.
|
|
219
|
+
Vitest.live('derives a fresh LiveQuery per Store instance', () =>
|
|
219
220
|
Effect.gen(function* () {
|
|
220
221
|
const makeStore = () =>
|
|
221
222
|
createStore({
|
|
@@ -248,7 +249,7 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
|
248
249
|
}),
|
|
249
250
|
)
|
|
250
251
|
|
|
251
|
-
Vitest.
|
|
252
|
+
Vitest.live('union of different result types with useQuery', () =>
|
|
252
253
|
Effect.gen(function* () {
|
|
253
254
|
const { wrapper, store, renderCount } = yield* makeTodoMvcReact({ strictMode })
|
|
254
255
|
|
|
@@ -4,6 +4,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
4
4
|
|
|
5
5
|
import { __resetUseRcResourceCache, useRcResource } from './useRcResource.ts'
|
|
6
6
|
|
|
7
|
+
/** Verifies: LS.SYS.INT.REACT-R03, LS.SYS.INT.REACT-R05 */
|
|
7
8
|
describe.each([{ strictMode: true }, { strictMode: false }])('useRcResource (strictMode=%s)', ({ strictMode }) => {
|
|
8
9
|
beforeEach(() => {
|
|
9
10
|
__resetUseRcResourceCache()
|
package/src/useRcResource.ts
CHANGED
|
@@ -87,7 +87,7 @@ export const useRcResource = <T>(
|
|
|
87
87
|
createRef.current = create
|
|
88
88
|
disposeRef.current = dispose
|
|
89
89
|
|
|
90
|
-
//
|
|
90
|
+
// oxlint-disable-next-line react/exhaustive-deps -- dependencies are deliberately limited to `scope` and `key` to avoid unintended re-creations
|
|
91
91
|
const resource = React.useMemo(() => {
|
|
92
92
|
const bucket = getBucket(scope)
|
|
93
93
|
|
|
@@ -130,7 +130,7 @@ export const useRcResource = <T>(
|
|
|
130
130
|
return resource
|
|
131
131
|
}, [scope, key])
|
|
132
132
|
|
|
133
|
-
//
|
|
133
|
+
// oxlint-disable-next-line react/exhaustive-deps -- `disposeRef` keeps the latest dispose function without re-running cleanup
|
|
134
134
|
React.useEffect(() => {
|
|
135
135
|
return () => {
|
|
136
136
|
if (didDisposeInMemo.current === true) {
|
package/src/useStore.test.tsx
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
act,
|
|
3
|
+
fireEvent,
|
|
4
|
+
type RenderHookResult,
|
|
5
|
+
type RenderResult,
|
|
6
|
+
render,
|
|
7
|
+
renderHook,
|
|
8
|
+
waitFor,
|
|
9
|
+
} from '@testing-library/react'
|
|
10
|
+
import * as React from 'react'
|
|
11
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
12
|
+
|
|
1
13
|
import { makeInMemoryAdapter } from '@livestore/adapter-web'
|
|
2
14
|
import {
|
|
3
15
|
type RegistryStoreOptions,
|
|
@@ -7,14 +19,12 @@ import {
|
|
|
7
19
|
storeOptions,
|
|
8
20
|
} from '@livestore/livestore'
|
|
9
21
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
10
|
-
import { act, type RenderHookResult, type RenderResult, fireEvent, render, renderHook, waitFor } from '@testing-library/react'
|
|
11
|
-
import * as React from 'react'
|
|
12
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
13
22
|
|
|
14
23
|
import { schema } from './__tests__/fixture.tsx'
|
|
15
24
|
import { StoreRegistryProvider } from './StoreRegistryContext.tsx'
|
|
16
25
|
import { useStore } from './useStore.ts'
|
|
17
26
|
|
|
27
|
+
/** Verifies: LS.SYS.STORE-R06, LS.SYS.INT.REACT-R01 */
|
|
18
28
|
describe('experimental useStore', () => {
|
|
19
29
|
it('should return the same promise instance for concurrent getOrLoadStore calls', async () => {
|
|
20
30
|
const storeRegistry = new StoreRegistry()
|
package/src/useStore.ts
CHANGED
|
@@ -57,7 +57,7 @@ import { useSyncStatus } from './useSyncStatus.ts'
|
|
|
57
57
|
export const useStore = <
|
|
58
58
|
TSchema extends LiveStoreSchema,
|
|
59
59
|
TContext = {},
|
|
60
|
-
TSyncPayloadSchema extends Schema.Schema
|
|
60
|
+
TSyncPayloadSchema extends Schema.Codec<Schema.Json, Schema.Json> = typeof Schema.Json,
|
|
61
61
|
>(
|
|
62
62
|
options: RegistryStoreOptions<TSchema, TContext, TSyncPayloadSchema>,
|
|
63
63
|
): Store<TSchema, TContext> & ReactApi => {
|