@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/react",
3
- "version": "0.4.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/common": "^0.4.0",
26
- "@livestore/framework-toolkit": "^0.4.0",
27
- "@livestore/livestore": "^0.4.0",
28
- "@livestore/utils": "^0.4.0"
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": "5.9.3",
41
+ "typescript": "6.0.3",
42
42
  "vite": "7.3.1",
43
- "vitest": "3.2.4",
44
- "@livestore/utils-dev": "^0.4.0",
45
- "@livestore/adapter-web": "^0.4.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/ai": "^0.35.0",
49
- "@effect/cli": "^0.75.1",
50
- "@effect/cluster": "^0.58.2",
51
- "@effect/experimental": "^0.60.0",
52
- "@effect/opentelemetry": "^0.63.0",
53
- "@effect/platform": "^0.96.1",
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": "^3.21.2",
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": "@livestore/common:LeaderSyncProcessor:push",
30
+ "_name": "client-session-sync-processor:pull",
31
31
  "attributes": {
32
- "batch": "undefined",
33
- "batchSize": 1,
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": "@livestore/common:LeaderSyncProcessor:push",
330
+ "_name": "client-session-sync-processor:pull",
311
331
  "attributes": {
312
- "batch": "undefined",
313
- "batchSize": 1,
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,6 +1,7 @@
1
+ import React from 'react'
2
+
1
3
  import type { LiveQueryDef, Store } from '@livestore/livestore'
2
4
  import { computed } from '@livestore/livestore'
3
- import React from 'react'
4
5
 
5
6
  import { useQuery } from '../../useQuery.ts'
6
7
 
@@ -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.scopedLive('should update the data based on component key', () =>
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.scopedLive('should update the data reactively - via setState', () =>
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.scopedLive('should update the data reactively - via raw store commit', () =>
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.scopedLive('should work for a larger app', () =>
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 ? <TaskDetails id={state.currentTaskId} /> : <div>Click on a task to see details</div>}
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.scopedLive('should work for a useClientDocument query chained with a useTemporary query', () =>
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.scopedLive('kv client document overwrites value (Schema.Any, no partial merge)', () =>
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
- { strictMode: true },
269
- { strictMode: false },
270
- ])('should update the data based on component key strictMode=%s', async ({ strictMode }) => {
271
- const exporter = new InMemorySpanExporter()
272
-
273
- const provider = new BasicTracerProvider({
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 { result, rerender, unmount } = ReactTesting.renderHook(
290
- (userId: string) => {
291
- renderCount.inc()
280
+ const otelTracer = provider.getTracer(`testing-${strictMode !== undefined ? 'strict' : 'non-strict'}`)
292
281
 
293
- const [state, setState, id] = store.useClientDocument(tables.userInfo, userId)
294
- return { state, setState, id }
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
- expect(result.current.id).toBe('u1')
300
- expect(result.current.state.username).toBe('')
301
- expect(renderCount.val).toBe(1)
302
-
303
- // For u2 we'll make sure that the row already exists,
304
- // so the lazy `insert` will be skipped
305
- ReactTesting.act(() => store.commit(events.UserInfoSet({ username: 'username_u2' }, 'u2')))
306
-
307
- rerender('u2')
308
-
309
- expect(result.current.id).toBe('u2')
310
- expect(result.current.state.username).toBe('username_u2')
311
- expect(renderCount.val).toBe(2)
312
-
313
- unmount()
314
- span.end()
315
- }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
316
-
317
- await provider.forceFlush()
318
-
319
- const mapAttributes = (attributes: otel.Attributes) => {
320
- return ReadonlyRecord.map(attributes, (val, key) => {
321
- if (key === 'code.stacktrace') {
322
- return '<STACKTRACE>'
323
- } else if (key === 'firstStackInfo') {
324
- const stackInfo = JSON.parse(val as string) as LiveStore.StackInfo
325
- // stackInfo.frames.shift() // Removes `renderHook.wrapper` from the stack
326
- stackInfo.frames.forEach((_) => {
327
- if (_.name.includes('renderHook.wrapper') === true) {
328
- _.name = 'renderHook.wrapper'
329
- }
330
- _.filePath = '__REPLACED_FOR_SNAPSHOT__'
331
- })
332
- return JSON.stringify(stackInfo)
333
- }
334
- return val
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
- expect(getSimplifiedRootSpan(exporter, 'createStore', mapAttributes)).toMatchSnapshot()
339
- expect(getAllSimplifiedRootSpans(exporter, 'LiveStore:commit', mapAttributes)).toMatchSnapshot()
341
+ expect(getSimplifiedRootSpan(exporter, 'createStore', mapAttributes)).toMatchSnapshot()
342
+ expect(getAllSimplifiedRootSpans(exporter, 'LiveStore:commit', mapAttributes)).toMatchSnapshot()
340
343
 
341
- await provider.shutdown()
342
- })
344
+ await provider.shutdown()
345
+ },
346
+ )
343
347
  })
344
348
  })
@@ -1,20 +1,21 @@
1
- /** biome-ignore-all lint/a11y: test */
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 * as ReactTesting from '@testing-library/react'
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.scopedLive('simple', () =>
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.scopedLive('same `useQuery` hook invoked with different queries', () =>
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.scopedLive('filtered dependency query', () =>
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.scopedLive('should work for a list with react-window', () =>
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.scopedLive('should work with signal', () =>
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.scopedLive('supports query builders directly', () =>
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.scopedLive('derives a fresh LiveQuery per Store instance', () =>
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.scopedLive('union of different result types with useQuery', () =>
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()
@@ -87,7 +87,7 @@ export const useRcResource = <T>(
87
87
  createRef.current = create
88
88
  disposeRef.current = dispose
89
89
 
90
- // biome-ignore lint/correctness/useExhaustiveDependencies: Dependencies are deliberately limited to `scope` and `key` to avoid unintended re-creations.
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
- // biome-ignore lint/correctness/useExhaustiveDependencies: We assume the `dispose` function is stable and won't change across renders
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) {
@@ -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<any> = typeof Schema.JsonValue,
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 => {