@livestore/react 0.4.0-dev.21 → 0.4.0-dev.22

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 (97) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/StoreRegistryContext.d.ts +56 -0
  3. package/dist/StoreRegistryContext.d.ts.map +1 -0
  4. package/dist/StoreRegistryContext.js +61 -0
  5. package/dist/StoreRegistryContext.js.map +1 -0
  6. package/dist/__tests__/fixture.d.ts.map +1 -1
  7. package/dist/__tests__/fixture.js +1 -6
  8. package/dist/__tests__/fixture.js.map +1 -1
  9. package/dist/experimental/components/LiveList.d.ts +4 -2
  10. package/dist/experimental/components/LiveList.d.ts.map +1 -1
  11. package/dist/experimental/components/LiveList.js +6 -5
  12. package/dist/experimental/components/LiveList.js.map +1 -1
  13. package/dist/experimental/mod.d.ts +0 -1
  14. package/dist/experimental/mod.d.ts.map +1 -1
  15. package/dist/experimental/mod.js +0 -1
  16. package/dist/experimental/mod.js.map +1 -1
  17. package/dist/mod.d.ts +4 -3
  18. package/dist/mod.d.ts.map +1 -1
  19. package/dist/mod.js +3 -2
  20. package/dist/mod.js.map +1 -1
  21. package/dist/useClientDocument.d.ts.map +1 -1
  22. package/dist/useClientDocument.js +1 -4
  23. package/dist/useClientDocument.js.map +1 -1
  24. package/dist/useQuery.d.ts +1 -1
  25. package/dist/useQuery.d.ts.map +1 -1
  26. package/dist/useQuery.js +2 -5
  27. package/dist/useQuery.js.map +1 -1
  28. package/dist/useStore.d.ts +50 -46
  29. package/dist/useStore.d.ts.map +1 -1
  30. package/dist/useStore.js +66 -59
  31. package/dist/useStore.js.map +1 -1
  32. package/dist/useStore.test.d.ts.map +1 -0
  33. package/dist/{experimental/multi-store/useStore.test.js → useStore.test.js} +20 -22
  34. package/dist/useStore.test.js.map +1 -0
  35. package/package.json +7 -7
  36. package/src/StoreRegistryContext.tsx +69 -0
  37. package/src/__tests__/fixture.tsx +1 -13
  38. package/src/experimental/components/LiveList.tsx +13 -4
  39. package/src/experimental/mod.ts +0 -1
  40. package/src/mod.ts +4 -3
  41. package/src/useClientDocument.ts +1 -5
  42. package/src/useQuery.ts +2 -6
  43. package/src/{experimental/multi-store/useStore.test.tsx → useStore.test.tsx} +32 -30
  44. package/src/useStore.ts +94 -66
  45. package/dist/LiveStoreContext.d.ts +0 -40
  46. package/dist/LiveStoreContext.d.ts.map +0 -1
  47. package/dist/LiveStoreContext.js +0 -21
  48. package/dist/LiveStoreContext.js.map +0 -1
  49. package/dist/LiveStoreProvider.d.ts +0 -73
  50. package/dist/LiveStoreProvider.d.ts.map +0 -1
  51. package/dist/LiveStoreProvider.js +0 -233
  52. package/dist/LiveStoreProvider.js.map +0 -1
  53. package/dist/LiveStoreProvider.test.d.ts +0 -2
  54. package/dist/LiveStoreProvider.test.d.ts.map +0 -1
  55. package/dist/LiveStoreProvider.test.js +0 -117
  56. package/dist/LiveStoreProvider.test.js.map +0 -1
  57. package/dist/experimental/multi-store/StoreRegistry.d.ts +0 -105
  58. package/dist/experimental/multi-store/StoreRegistry.d.ts.map +0 -1
  59. package/dist/experimental/multi-store/StoreRegistry.js +0 -184
  60. package/dist/experimental/multi-store/StoreRegistry.js.map +0 -1
  61. package/dist/experimental/multi-store/StoreRegistry.test.d.ts +0 -2
  62. package/dist/experimental/multi-store/StoreRegistry.test.d.ts.map +0 -1
  63. package/dist/experimental/multi-store/StoreRegistry.test.js +0 -381
  64. package/dist/experimental/multi-store/StoreRegistry.test.js.map +0 -1
  65. package/dist/experimental/multi-store/StoreRegistryContext.d.ts +0 -10
  66. package/dist/experimental/multi-store/StoreRegistryContext.d.ts.map +0 -1
  67. package/dist/experimental/multi-store/StoreRegistryContext.js +0 -15
  68. package/dist/experimental/multi-store/StoreRegistryContext.js.map +0 -1
  69. package/dist/experimental/multi-store/mod.d.ts +0 -6
  70. package/dist/experimental/multi-store/mod.d.ts.map +0 -1
  71. package/dist/experimental/multi-store/mod.js +0 -6
  72. package/dist/experimental/multi-store/mod.js.map +0 -1
  73. package/dist/experimental/multi-store/storeOptions.d.ts +0 -4
  74. package/dist/experimental/multi-store/storeOptions.d.ts.map +0 -1
  75. package/dist/experimental/multi-store/storeOptions.js +0 -4
  76. package/dist/experimental/multi-store/storeOptions.js.map +0 -1
  77. package/dist/experimental/multi-store/types.d.ts +0 -25
  78. package/dist/experimental/multi-store/types.d.ts.map +0 -1
  79. package/dist/experimental/multi-store/types.js +0 -2
  80. package/dist/experimental/multi-store/types.js.map +0 -1
  81. package/dist/experimental/multi-store/useStore.d.ts +0 -11
  82. package/dist/experimental/multi-store/useStore.d.ts.map +0 -1
  83. package/dist/experimental/multi-store/useStore.js +0 -16
  84. package/dist/experimental/multi-store/useStore.js.map +0 -1
  85. package/dist/experimental/multi-store/useStore.test.d.ts.map +0 -1
  86. package/dist/experimental/multi-store/useStore.test.js.map +0 -1
  87. package/src/LiveStoreContext.ts +0 -41
  88. package/src/LiveStoreProvider.test.tsx +0 -248
  89. package/src/LiveStoreProvider.tsx +0 -430
  90. package/src/experimental/multi-store/StoreRegistry.test.ts +0 -518
  91. package/src/experimental/multi-store/StoreRegistry.ts +0 -253
  92. package/src/experimental/multi-store/StoreRegistryContext.tsx +0 -23
  93. package/src/experimental/multi-store/mod.ts +0 -5
  94. package/src/experimental/multi-store/storeOptions.ts +0 -8
  95. package/src/experimental/multi-store/types.ts +0 -37
  96. package/src/experimental/multi-store/useStore.ts +0 -26
  97. /package/dist/{experimental/multi-store/useStore.test.d.ts → useStore.test.d.ts} +0 -0
@@ -1,25 +1,27 @@
1
1
  import { makeInMemoryAdapter } from '@livestore/adapter-web'
2
- import type { Store } from '@livestore/livestore'
3
- import { StoreInternalsSymbol } from '@livestore/livestore'
2
+ import {
3
+ type RegistryStoreOptions,
4
+ type Store,
5
+ StoreInternalsSymbol,
6
+ StoreRegistry,
7
+ storeOptions,
8
+ } from '@livestore/livestore'
4
9
  import { shouldNeverHappen } from '@livestore/utils'
5
10
  import { act, type RenderHookResult, type RenderResult, render, renderHook, waitFor } from '@testing-library/react'
6
11
  import * as React from 'react'
7
12
  import { describe, expect, it } from 'vitest'
8
- import { schema } from '../../__tests__/fixture.tsx'
9
- import { StoreRegistry } from './StoreRegistry.ts'
13
+ import { schema } from './__tests__/fixture.tsx'
10
14
  import { StoreRegistryProvider } from './StoreRegistryContext.tsx'
11
- import { storeOptions } from './storeOptions.ts'
12
- import type { CachedStoreOptions } from './types.ts'
13
15
  import { useStore } from './useStore.ts'
14
16
 
15
17
  describe('experimental useStore', () => {
16
18
  it('should return the same promise instance for concurrent getOrLoadStore calls', async () => {
17
- const registry = new StoreRegistry()
19
+ const storeRegistry = new StoreRegistry()
18
20
  const options = testStoreOptions()
19
21
 
20
22
  // Make two concurrent calls during loading
21
- const firstStore = registry.getOrLoadPromise(options)
22
- const secondStore = registry.getOrLoadPromise(options)
23
+ const firstStore = storeRegistry.getOrLoadPromise(options)
24
+ const secondStore = storeRegistry.getOrLoadPromise(options)
23
25
 
24
26
  // Both should be promises (store is loading)
25
27
  expect(firstStore).toBeInstanceOf(Promise)
@@ -35,13 +37,13 @@ describe('experimental useStore', () => {
35
37
  })
36
38
 
37
39
  it('works with Suspense boundary', async () => {
38
- const registry = new StoreRegistry()
40
+ const storeRegistry = new StoreRegistry()
39
41
  const options = testStoreOptions()
40
42
 
41
43
  let view: RenderResult | undefined
42
44
  await act(async () => {
43
45
  view = render(
44
- <StoreRegistryProvider storeRegistry={registry}>
46
+ <StoreRegistryProvider storeRegistry={storeRegistry}>
45
47
  <React.Suspense fallback={<div data-testid="fallback" />}>
46
48
  <StoreConsumer options={options} />
47
49
  </React.Suspense>
@@ -58,11 +60,11 @@ describe('experimental useStore', () => {
58
60
  })
59
61
 
60
62
  it('does not re-suspend on subsequent renders when store is already loaded', async () => {
61
- const registry = new StoreRegistry()
63
+ const storeRegistry = new StoreRegistry()
62
64
  const options = testStoreOptions()
63
65
 
64
- const Wrapper = ({ opts }: { opts: CachedStoreOptions<typeof schema> }) => (
65
- <StoreRegistryProvider storeRegistry={registry}>
66
+ const Wrapper = ({ opts }: { opts: RegistryStoreOptions<typeof schema> }) => (
67
+ <StoreRegistryProvider storeRegistry={storeRegistry}>
66
68
  <React.Suspense fallback={<div data-testid="fallback" />}>
67
69
  <StoreConsumer options={opts} />
68
70
  </React.Suspense>
@@ -92,19 +94,19 @@ describe('experimental useStore', () => {
92
94
  })
93
95
 
94
96
  it('throws when store loading fails', async () => {
95
- const registry = new StoreRegistry()
97
+ const storeRegistry = new StoreRegistry()
96
98
  const badOptions = testStoreOptions({
97
99
  // @ts-expect-error - intentionally passing invalid adapter to trigger error
98
100
  adapter: null,
99
101
  })
100
102
 
101
103
  // Pre-load the store to cache the error (error happens synchronously)
102
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow()
104
+ expect(() => storeRegistry.getOrLoadPromise(badOptions)).toThrow()
103
105
 
104
106
  // Now when useStore tries to get it, it should throw synchronously
105
107
  expect(() =>
106
108
  renderHook(() => useStore(badOptions), {
107
- wrapper: makeProvider(registry),
109
+ wrapper: makeProvider(storeRegistry),
108
110
  }),
109
111
  ).toThrow()
110
112
  })
@@ -113,13 +115,13 @@ describe('experimental useStore', () => {
113
115
  { label: 'non-strict mode', strictMode: false },
114
116
  { label: 'strict mode', strictMode: true },
115
117
  ])('works in $label', async ({ strictMode }) => {
116
- const registry = new StoreRegistry()
118
+ const storeRegistry = new StoreRegistry()
117
119
  const options = testStoreOptions()
118
120
 
119
- let hook: RenderHookResult<Store<typeof schema>, CachedStoreOptions<typeof schema>> | undefined
121
+ let hook: RenderHookResult<Store<typeof schema>, RegistryStoreOptions<typeof schema>> | undefined
120
122
  await act(async () => {
121
123
  hook = renderHook(() => useStore(options), {
122
- wrapper: makeProvider(registry, { suspense: true }),
124
+ wrapper: makeProvider(storeRegistry, { suspense: true }),
123
125
  reactStrictMode: strictMode,
124
126
  })
125
127
  })
@@ -133,16 +135,16 @@ describe('experimental useStore', () => {
133
135
  })
134
136
 
135
137
  it('handles switching between different storeId values', async () => {
136
- const registry = new StoreRegistry()
138
+ const storeRegistry = new StoreRegistry()
137
139
 
138
140
  const optionsA = testStoreOptions({ storeId: 'store-a' })
139
141
  const optionsB = testStoreOptions({ storeId: 'store-b' })
140
142
 
141
- let hook: RenderHookResult<Store<typeof schema>, CachedStoreOptions<typeof schema>> | undefined
143
+ let hook: RenderHookResult<Store<typeof schema>, RegistryStoreOptions<typeof schema>> | undefined
142
144
  await act(async () => {
143
145
  hook = renderHook((opts) => useStore(opts), {
144
146
  initialProps: optionsA,
145
- wrapper: makeProvider(registry, { suspense: true }),
147
+ wrapper: makeProvider(storeRegistry, { suspense: true }),
146
148
  })
147
149
  })
148
150
  const { result, rerender, unmount } = hook ?? shouldNeverHappen('renderHook failed')
@@ -173,10 +175,10 @@ describe('experimental useStore', () => {
173
175
  // useStore doesn't handle unusedCacheTime=0 correctly because retain is called in useEffect (after render)
174
176
  // See https://github.com/livestorejs/livestore/issues/916
175
177
  it.skip('should load store with unusedCacheTime set to 0', async () => {
176
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime: 0 } })
178
+ const storeRegistry = new StoreRegistry({ defaultOptions: { unusedCacheTime: 0 } })
177
179
  const options = testStoreOptions({ unusedCacheTime: 0 })
178
180
 
179
- const StoreConsumerWithVerification = ({ opts }: { opts: CachedStoreOptions<typeof schema> }) => {
181
+ const StoreConsumerWithVerification = ({ opts }: { opts: RegistryStoreOptions<typeof schema> }) => {
180
182
  const store = useStore(opts)
181
183
  // Verify store is usable - access internals to confirm it's not disposed
182
184
  const clientSession = store[StoreInternalsSymbol].clientSession
@@ -186,7 +188,7 @@ describe('experimental useStore', () => {
186
188
  let view: RenderResult | undefined
187
189
  await act(async () => {
188
190
  view = render(
189
- <StoreRegistryProvider storeRegistry={registry}>
191
+ <StoreRegistryProvider storeRegistry={storeRegistry}>
190
192
  <React.Suspense fallback={<div data-testid="fallback" />}>
191
193
  <StoreConsumerWithVerification opts={options} />
192
194
  </React.Suspense>
@@ -211,15 +213,15 @@ describe('experimental useStore', () => {
211
213
  })
212
214
  })
213
215
 
214
- const StoreConsumer = ({ options }: { options: CachedStoreOptions<any> }) => {
216
+ const StoreConsumer = ({ options }: { options: RegistryStoreOptions<any> }) => {
215
217
  useStore(options)
216
218
  return <div data-testid="ready" />
217
219
  }
218
220
 
219
221
  const makeProvider =
220
- (registry: StoreRegistry, { suspense = false }: { suspense?: boolean } = {}) =>
222
+ (storeRegistry: StoreRegistry, { suspense = false }: { suspense?: boolean } = {}) =>
221
223
  ({ children }: { children: React.ReactNode }) => {
222
- let content = <StoreRegistryProvider storeRegistry={registry}>{children}</StoreRegistryProvider>
224
+ let content = <StoreRegistryProvider storeRegistry={storeRegistry}>{children}</StoreRegistryProvider>
223
225
 
224
226
  if (suspense) {
225
227
  content = <React.Suspense fallback={null}>{content}</React.Suspense>
@@ -230,7 +232,7 @@ const makeProvider =
230
232
 
231
233
  let testStoreCounter = 0
232
234
 
233
- const testStoreOptions = (overrides: Partial<CachedStoreOptions<typeof schema>> = {}) =>
235
+ const testStoreOptions = (overrides: Partial<RegistryStoreOptions<typeof schema>> = {}) =>
234
236
  storeOptions({
235
237
  storeId: overrides.storeId ?? `test-store-${testStoreCounter++}`,
236
238
  schema,
package/src/useStore.ts CHANGED
@@ -1,88 +1,116 @@
1
1
  import type { LiveStoreSchema } from '@livestore/common/schema'
2
- import type { Store } from '@livestore/livestore'
2
+ import type { RegistryStoreOptions, Store } from '@livestore/livestore'
3
+ import type { Schema } from '@livestore/utils/effect'
3
4
  import React from 'react'
4
-
5
- import type { ReactApi } from './LiveStoreContext.ts'
6
- import { LiveStoreContext } from './LiveStoreContext.ts'
5
+ import { useStoreRegistry } from './StoreRegistryContext.tsx'
7
6
  import { useClientDocument } from './useClientDocument.ts'
8
7
  import { useQuery } from './useQuery.ts'
9
8
 
10
9
  /**
11
- * Augments a Store instance with React-specific methods (`useQuery`, `useClientDocument`).
12
- *
13
- * This is called automatically by `useStore()` and `LiveStoreProvider`. You typically
14
- * don't need to call it directly unless you're building custom integrations.
10
+ * Returns a store instance augmented with hooks (`store.useQuery()` and `store.useClientDocument()`) for reactive queries.
15
11
  *
16
12
  * @example
17
- * ```ts
18
- * // Usually not needed—useStore() does this automatically
19
- * const store = withReactApi(myStore)
20
- * const todos = store.useQuery(tables.todos.all())
21
- * ```
22
- */
23
- export const withReactApi = <TSchema extends LiveStoreSchema>(store: Store<TSchema>): Store<TSchema> & ReactApi => {
24
- // @ts-expect-error TODO properly implement this
25
-
26
- store.useQuery = (queryable) => useQuery(queryable, { store })
27
- // @ts-expect-error TODO properly implement this
28
-
29
- store.useClientDocument = (table, idOrOptions, options) => useClientDocument(table, idOrOptions, options, { store })
30
- return store as Store<TSchema> & ReactApi
31
- }
32
-
33
- /**
34
- * Returns the current Store instance from React context, augmented with React-specific methods.
35
- *
36
- * Use this hook when you need direct access to the Store for operations like
37
- * `store.commit()`, `store.subscribe()`, or accessing `store.sessionId`.
38
- *
39
- * For reactive queries, prefer `useQuery()` or `useClientDocument()` which handle
40
- * subscriptions and re-renders automatically.
13
+ * ```tsx
14
+ * function Issue() {
15
+ * // Suspends until loaded or returns immediately if already loaded
16
+ * const issueStore = useStore(issueStoreOptions('abc123'))
17
+ * const [issue] = issueStore.useQuery(queryDb(tables.issue.select()))
41
18
  *
42
- * @example
43
- * ```ts
44
- * const MyComponent = () => {
45
- * const { store } = useStore()
19
+ * const toggleStatus = () =>
20
+ * issueStore.commit(
21
+ * issueEvents.issueStatusChanged({
22
+ * id: issue.id,
23
+ * status: issue.status === 'done' ? 'todo' : 'done',
24
+ * }),
25
+ * )
46
26
  *
47
- * const handleClick = () => {
48
- * store.commit(events.todoCreated({ id: nanoid(), text: 'New todo' }))
49
- * }
27
+ * const preloadParentIssue = (issueId: string) =>
28
+ * storeRegistry.preload({
29
+ * ...issueStoreOptions(issueId),
30
+ * unusedCacheTime: 10_000,
31
+ * })
50
32
  *
51
- * return <button onClick={handleClick}>Add Todo</button>
33
+ * return (
34
+ * <>
35
+ * <h2>{issue.title}</h2>
36
+ * <button onClick={() => toggleStatus()}>Toggle Status</button>
37
+ * <button onMouseEnter={() => preloadParentIssue(issue.parentIssueId)}>Open Parent Issue</button>
38
+ * </>
39
+ * )
52
40
  * }
53
41
  * ```
54
42
  *
55
- * @example
56
- * ```ts
57
- * // Access store metadata
58
- * const { store } = useStore()
59
- * console.log('Session ID:', store.sessionId)
60
- * console.log('Client ID:', store.clientId)
61
- * ```
62
- *
63
- * @example
64
- * ```ts
65
- * // Use with an explicit store instance (bypasses context)
66
- * const { store } = useStore({ store: myExternalStore })
67
- * ```
43
+ * @remarks
44
+ * - Suspends until the store is loaded.
45
+ * - Store is cached by its `storeId` in the `StoreRegistry`. Multiple calls with the same `storeId` return the same store instance.
46
+ * - Store is cached as long as it's being used, and after `unusedCacheTime` expires (default `60_000` ms in browser, `Infinity` in non-browser)
47
+ * - Default store options can be configured in `StoreRegistry` constructor.
48
+ * - Store options are only applied when the store is loaded. Subsequent calls with different options will not affect the store if it's already loaded and cached in the registry.
68
49
  *
69
- * @throws Error if called outside of `<LiveStoreProvider>` or before the store is running
50
+ * @typeParam TSchema - The schema type for the store
51
+ * @returns The loaded store instance augmented with React hooks
52
+ * @throws unknown - store loading error or if called outside `<StoreRegistryProvider>`
70
53
  */
71
- export const useStore = (options?: { store?: Store }): { store: Store & ReactApi } => {
72
- if (options?.store !== undefined) {
73
- return { store: withReactApi(options.store) }
74
- }
54
+ export const useStore = <
55
+ TSchema extends LiveStoreSchema,
56
+ TContext = {},
57
+ TSyncPayloadSchema extends Schema.Schema<any> = typeof Schema.JsonValue,
58
+ >(
59
+ options: RegistryStoreOptions<TSchema, TContext, TSyncPayloadSchema>,
60
+ ): Store<TSchema, TContext> & ReactApi => {
61
+ const storeRegistry = useStoreRegistry()
75
62
 
76
- // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
77
- const storeContext = React.useContext(LiveStoreContext)
63
+ // NOTE: retain() is called in useEffect (after render), while getOrLoadPromise() is called
64
+ // in useMemo (during render). This creates a timing gap where with very short unusedCacheTime
65
+ // values (e.g., 0), the store could theoretically be disposed before the effect fires.
66
+ // In practice, this is not an issue with the default 60s cache time, but it becomes an issue when
67
+ // `unusedCacheTime` is configured to values less than ~100ms.
68
+ // See https://github.com/livestorejs/livestore/issues/916
69
+ React.useEffect(() => storeRegistry.retain(options), [storeRegistry, options])
78
70
 
79
- if (storeContext === undefined) {
80
- throw new Error(`useStore can only be used inside StoreContext.Provider`)
81
- }
71
+ const storeOrPromise = React.useMemo(() => storeRegistry.getOrLoadPromise(options), [storeRegistry, options])
82
72
 
83
- if (storeContext.stage !== 'running') {
84
- throw new Error(`useStore can only be used after the store is running`)
73
+ const store = storeOrPromise instanceof Promise ? React.use(storeOrPromise) : storeOrPromise
74
+
75
+ // Expose store on the global object for browser console debugging.
76
+ globalThis.__debugLiveStore ??= {}
77
+ if (Object.keys(globalThis.__debugLiveStore).length === 0) {
78
+ globalThis.__debugLiveStore._ = store
85
79
  }
80
+ globalThis.__debugLiveStore[options.debug?.instanceId ?? options.storeId] = store
81
+
82
+ return withReactApi(store)
83
+ }
84
+
85
+ /**
86
+ * React-specific methods added to the Store when used via React hooks.
87
+ *
88
+ * These methods are attached by `withReactApi()` and `useStore()`, allowing you
89
+ * to call `store.useQuery()` and `store.useClientDocument()` directly on the
90
+ * Store instance.
91
+ */
92
+ export type ReactApi = {
93
+ /** Hook version of query subscription—re-renders component when query result changes */
94
+ useQuery: typeof useQuery
95
+ /** Hook for reading and writing client-document tables with React state semantics */
96
+ useClientDocument: typeof useClientDocument
97
+ }
86
98
 
87
- return { store: withReactApi(storeContext.store) }
99
+ /**
100
+ * Augments a Store instance with React-specific methods (`useQuery`, `useClientDocument`).
101
+ *
102
+ * This is called automatically by `useStore()`. You typically don't need to call it
103
+ * directly unless you're building custom integrations.
104
+ *
105
+ * @internal
106
+ */
107
+ export const withReactApi = <TSchema extends LiveStoreSchema, TContext = {}>(
108
+ store: Store<TSchema, TContext>,
109
+ ): Store<TSchema, TContext> & ReactApi => {
110
+ // @ts-expect-error TODO properly implement this
111
+ store.useQuery = (queryable) => useQuery(queryable, { store })
112
+
113
+ // @ts-expect-error TODO properly implement this
114
+ store.useClientDocument = (table, idOrOptions, options) => useClientDocument(table, idOrOptions, options, { store })
115
+ return store as Store<TSchema, TContext> & ReactApi
88
116
  }
@@ -1,40 +0,0 @@
1
- import type { LiveStoreContextRunning } from '@livestore/livestore';
2
- import React from 'react';
3
- import type { useClientDocument } from './useClientDocument.ts';
4
- import type { useQuery } from './useQuery.ts';
5
- /**
6
- * React-specific methods added to the Store when used via React hooks.
7
- *
8
- * These methods are attached by `withReactApi()` and `useStore()`, allowing you
9
- * to call `store.useQuery()` and `store.useClientDocument()` directly on the
10
- * Store instance.
11
- */
12
- export type ReactApi = {
13
- /** Hook version of query subscription—re-renders component when query result changes */
14
- useQuery: typeof useQuery;
15
- /** Hook for reading and writing client-document tables with React state semantics */
16
- useClientDocument: typeof useClientDocument;
17
- };
18
- /**
19
- * React context for accessing the LiveStore instance.
20
- *
21
- * This context is provided by `<LiveStoreProvider>` and consumed by hooks like
22
- * `useStore()`, `useQuery()`, and `useClientDocument()`.
23
- *
24
- * The context value is `undefined` until the Store has finished booting,
25
- * then transitions to `{ stage: 'running', store: ... }`.
26
- *
27
- * @example
28
- * ```tsx
29
- * // Typically you don't use this directly—use useStore() instead
30
- * const context = React.useContext(LiveStoreContext)
31
- * if (context?.stage === 'running') {
32
- * console.log('Store ready:', context.store.storeId)
33
- * }
34
- * ```
35
- */
36
- export declare const LiveStoreContext: React.Context<{
37
- stage: "running";
38
- store: LiveStoreContextRunning["store"] & ReactApi;
39
- } | undefined>;
40
- //# sourceMappingURL=LiveStoreContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LiveStoreContext.d.ts","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AACnE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,wFAAwF;IACxF,QAAQ,EAAE,OAAO,QAAQ,CAAA;IACzB,qFAAqF;IACrF,iBAAiB,EAAE,OAAO,iBAAiB,CAAA;CAC5C,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB;WAClB,SAAS;WAAS,uBAAuB,CAAC,OAAO,CAAC,GAAG,QAAQ;cAC5D,CAAA"}
@@ -1,21 +0,0 @@
1
- import React from 'react';
2
- /**
3
- * React context for accessing the LiveStore instance.
4
- *
5
- * This context is provided by `<LiveStoreProvider>` and consumed by hooks like
6
- * `useStore()`, `useQuery()`, and `useClientDocument()`.
7
- *
8
- * The context value is `undefined` until the Store has finished booting,
9
- * then transitions to `{ stage: 'running', store: ... }`.
10
- *
11
- * @example
12
- * ```tsx
13
- * // Typically you don't use this directly—use useStore() instead
14
- * const context = React.useContext(LiveStoreContext)
15
- * if (context?.stage === 'running') {
16
- * console.log('Store ready:', context.store.storeId)
17
- * }
18
- * ```
19
- */
20
- export const LiveStoreContext = React.createContext(undefined);
21
- //# sourceMappingURL=LiveStoreContext.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LiveStoreContext.js","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAmBzB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAEjD,SAAS,CAAC,CAAA"}
@@ -1,73 +0,0 @@
1
- import type { Adapter, BootStatus, IntentionalShutdownCause, MigrationsReport, SyncError } from '@livestore/common';
2
- import { LogConfig, UnknownError } from '@livestore/common';
3
- import type { LiveStoreSchema } from '@livestore/common/schema';
4
- import type { CreateStoreOptions, OtelOptions, Store } from '@livestore/livestore';
5
- import { StoreInterrupted } from '@livestore/livestore';
6
- import type { OtelTracer } from '@livestore/utils/effect';
7
- import { Effect, Schema } from '@livestore/utils/effect';
8
- import type * as otel from '@opentelemetry/api';
9
- import React from 'react';
10
- export interface LiveStoreProviderProps<TSyncPayloadSchema extends Schema.Schema<any> = typeof Schema.JsonValue> extends LogConfig.WithLoggerOptions {
11
- schema: LiveStoreSchema;
12
- /**
13
- * The `storeId` can be used to isolate multiple stores from each other.
14
- * So it can be useful for multi-tenancy scenarios.
15
- *
16
- * The `storeId` is also used for persistence.
17
- *
18
- * Make sure to also configure `storeId` in LiveStore Devtools (e.g. in Vite plugin).
19
- *
20
- * @default 'default'
21
- */
22
- storeId?: string;
23
- boot?: (store: Store<LiveStoreSchema>, ctx: {
24
- migrationsReport: MigrationsReport;
25
- parentSpan: otel.Span;
26
- }) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>;
27
- otelOptions?: Partial<OtelOptions>;
28
- renderLoading?: (status: BootStatus) => React.ReactNode;
29
- renderError?: (error: UnknownError | unknown) => React.ReactNode;
30
- renderShutdown?: (cause: IntentionalShutdownCause | StoreInterrupted | SyncError) => React.ReactNode;
31
- adapter: Adapter;
32
- /**
33
- * In order for LiveStore to apply multiple events in a single render,
34
- * you need to pass the `batchUpdates` function from either `react-dom` or `react-native`.
35
- *
36
- * ```ts
37
- * // With React DOM
38
- * import { unstable_batchedUpdates as batchUpdates } from 'react-dom'
39
- *
40
- * // With React Native
41
- * import { unstable_batchedUpdates as batchUpdates } from 'react-native'
42
- * ```
43
- */
44
- batchUpdates: (run: () => void) => void;
45
- disableDevtools?: boolean;
46
- signal?: AbortSignal;
47
- /**
48
- * Currently only used in the web adapter:
49
- * If true, registers a beforeunload event listener to confirm unsaved changes.
50
- *
51
- * @default true
52
- */
53
- confirmUnsavedChanges?: boolean;
54
- /**
55
- * Advanced store parameters forwarded to `createStore`.
56
- * Currently supports:
57
- * - `leaderPushBatchSize`: max events pushed to the leader per write batch.
58
- * - `eventQueryBatchSize`: chunk size used when the stream replays confirmed events.
59
- */
60
- params?: CreateStoreOptions<LiveStoreSchema>['params'];
61
- /**
62
- * Payload that will be passed to the sync backend when connecting
63
- *
64
- * @default undefined
65
- */
66
- syncPayloadSchema?: TSyncPayloadSchema;
67
- syncPayload?: Schema.Schema.Type<TSyncPayloadSchema>;
68
- debug?: {
69
- instanceId?: string;
70
- };
71
- }
72
- export declare const LiveStoreProvider: <TSyncPayloadSchema extends Schema.Schema<any> = typeof Schema.JsonValue>({ renderLoading, renderError, renderShutdown, otelOptions, children, schema, storeId, boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges, params, syncPayload, syncPayloadSchema, debug, logger, logLevel, }: LiveStoreProviderProps<TSyncPayloadSchema> & React.PropsWithChildren) => React.ReactNode;
73
- //# sourceMappingURL=LiveStoreProvider.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LiveStoreProvider.d.ts","sourceRoot":"","sources":["../src/LiveStoreProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACnH,OAAO,EAAE,SAAS,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAEX,KAAK,EAEN,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAqC,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAmB,MAAM,EAAkB,MAAM,EAAsB,MAAM,yBAAyB,CAAA;AAC7G,OAAO,KAAK,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,sBAAsB,CAAC,kBAAkB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,MAAM,CAAC,SAAS,CAC7G,SAAQ,SAAS,CAAC,iBAAiB;IACnC,MAAM,EAAE,eAAe,CAAA;IACvB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,CACL,KAAK,EAAE,KAAK,CAAC,eAAe,CAAC,EAC7B,GAAG,EAAE;QAAE,gBAAgB,EAAE,gBAAgB,CAAC;QAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAA;KAAE,KAC/D,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAA;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAClC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,SAAS,CAAA;IACvD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,KAAK,KAAK,CAAC,SAAS,CAAA;IAChE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,GAAG,gBAAgB,GAAG,SAAS,KAAK,KAAK,CAAC,SAAS,CAAA;IACpG,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;IACvC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,CAAA;IACtD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAA;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;IACpD,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AA2BD,eAAO,MAAM,iBAAiB,GAAI,kBAAkB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,MAAM,CAAC,SAAS,EAAE,uOAoBxG,sBAAsB,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAAC,iBAAiB,KAAG,KAAK,CAAC,SAwC/E,CAAA"}