@livestore/react 0.2.0 → 0.3.0-dev.11

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 (87) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/LiveStoreContext.d.ts +5 -3
  3. package/dist/LiveStoreContext.d.ts.map +1 -1
  4. package/dist/LiveStoreContext.js +7 -3
  5. package/dist/LiveStoreContext.js.map +1 -1
  6. package/dist/LiveStoreProvider.d.ts +6 -4
  7. package/dist/LiveStoreProvider.d.ts.map +1 -1
  8. package/dist/LiveStoreProvider.js +47 -45
  9. package/dist/LiveStoreProvider.js.map +1 -1
  10. package/dist/LiveStoreProvider.test.js +8 -2
  11. package/dist/LiveStoreProvider.test.js.map +1 -1
  12. package/dist/__tests__/fixture.d.ts +7 -10
  13. package/dist/__tests__/fixture.d.ts.map +1 -1
  14. package/dist/__tests__/fixture.js +10 -15
  15. package/dist/__tests__/fixture.js.map +1 -1
  16. package/dist/experimental/components/LiveList.d.ts +2 -2
  17. package/dist/experimental/components/LiveList.d.ts.map +1 -1
  18. package/dist/experimental/components/LiveList.js +5 -4
  19. package/dist/experimental/components/LiveList.js.map +1 -1
  20. package/dist/mod.d.ts +0 -1
  21. package/dist/mod.d.ts.map +1 -1
  22. package/dist/mod.js +0 -1
  23. package/dist/mod.js.map +1 -1
  24. package/dist/useAtom.d.ts +4 -2
  25. package/dist/useAtom.d.ts.map +1 -1
  26. package/dist/useAtom.js +32 -28
  27. package/dist/useAtom.js.map +1 -1
  28. package/dist/useQuery.d.ts +26 -3
  29. package/dist/useQuery.d.ts.map +1 -1
  30. package/dist/useQuery.js +60 -45
  31. package/dist/useQuery.js.map +1 -1
  32. package/dist/useQuery.test.js +70 -16
  33. package/dist/useQuery.test.js.map +1 -1
  34. package/dist/useRcRef.d.ts +72 -0
  35. package/dist/useRcRef.d.ts.map +1 -0
  36. package/dist/useRcRef.js +146 -0
  37. package/dist/useRcRef.js.map +1 -0
  38. package/dist/useRcRef.test.d.ts +2 -0
  39. package/dist/useRcRef.test.d.ts.map +1 -0
  40. package/dist/useRcRef.test.js +128 -0
  41. package/dist/useRcRef.test.js.map +1 -0
  42. package/dist/useRcResource.d.ts +76 -0
  43. package/dist/useRcResource.d.ts.map +1 -0
  44. package/dist/useRcResource.js +150 -0
  45. package/dist/useRcResource.js.map +1 -0
  46. package/dist/useRcResource.test.d.ts +2 -0
  47. package/dist/useRcResource.test.d.ts.map +1 -0
  48. package/dist/useRcResource.test.js +122 -0
  49. package/dist/useRcResource.test.js.map +1 -0
  50. package/dist/useRow.d.ts +10 -7
  51. package/dist/useRow.d.ts.map +1 -1
  52. package/dist/useRow.js +16 -19
  53. package/dist/useRow.js.map +1 -1
  54. package/dist/useRow.test.js +74 -97
  55. package/dist/useRow.test.js.map +1 -1
  56. package/dist/useScopedQuery.d.ts +10 -4
  57. package/dist/useScopedQuery.d.ts.map +1 -1
  58. package/dist/useScopedQuery.js +97 -52
  59. package/dist/useScopedQuery.js.map +1 -1
  60. package/dist/useScopedQuery.test.js +13 -12
  61. package/dist/useScopedQuery.test.js.map +1 -1
  62. package/dist/utils/useStateRefWithReactiveInput.d.ts +1 -1
  63. package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
  64. package/dist/utils/useStateRefWithReactiveInput.js.map +1 -1
  65. package/package.json +18 -17
  66. package/src/LiveStoreContext.ts +10 -6
  67. package/src/LiveStoreProvider.test.tsx +13 -2
  68. package/src/LiveStoreProvider.tsx +69 -53
  69. package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
  70. package/src/__snapshots__/useRow.test.tsx.snap +347 -151
  71. package/src/__tests__/fixture.tsx +11 -19
  72. package/src/experimental/components/LiveList.tsx +8 -7
  73. package/src/mod.ts +0 -1
  74. package/src/useAtom.ts +22 -11
  75. package/src/useQuery.test.tsx +165 -67
  76. package/src/useQuery.ts +84 -54
  77. package/src/useRcResource.test.tsx +167 -0
  78. package/src/useRcResource.ts +180 -0
  79. package/src/useRow.test.tsx +130 -164
  80. package/src/useRow.ts +32 -35
  81. package/src/utils/useStateRefWithReactiveInput.ts +1 -1
  82. package/dist/useTemporaryQuery.d.ts +0 -22
  83. package/dist/useTemporaryQuery.d.ts.map +0 -1
  84. package/dist/useTemporaryQuery.js +0 -75
  85. package/dist/useTemporaryQuery.js.map +0 -1
  86. package/src/useScopedQuery.test.tsx +0 -96
  87. package/src/useScopedQuery.ts +0 -142
@@ -1,5 +1,5 @@
1
1
  import type { Adapter, BootStatus, IntentionalShutdownCause } from '@livestore/common'
2
- import { UnexpectedError } from '@livestore/common'
2
+ import { provideOtel, UnexpectedError } from '@livestore/common'
3
3
  import type { LiveStoreSchema } from '@livestore/common/schema'
4
4
  import type {
5
5
  BaseGraphQLContext,
@@ -7,11 +7,13 @@ import type {
7
7
  GraphQLOptions,
8
8
  LiveStoreContext as StoreContext_,
9
9
  OtelOptions,
10
+ ShutdownDeferred,
10
11
  Store,
11
12
  } from '@livestore/livestore'
12
13
  import { createStore, StoreAbort, StoreInterrupted } from '@livestore/livestore'
13
- import { errorToString } from '@livestore/utils'
14
- import { Effect, FiberSet, Logger, LogLevel, Schema } from '@livestore/utils/effect'
14
+ import { errorToString, LS_DEV } from '@livestore/utils'
15
+ import type { OtelTracer } from '@livestore/utils/effect'
16
+ import { Deferred, Effect, Exit, identity, Logger, LogLevel, Schema, Scope, TaskTracing } from '@livestore/utils/effect'
15
17
  import type * as otel from '@opentelemetry/api'
16
18
  import type { ReactElement, ReactNode } from 'react'
17
19
  import React from 'react'
@@ -26,7 +28,7 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
26
28
  *
27
29
  * The `storeId` is also used for persistence.
28
30
  *
29
- * Make sure to also provide `storeId` to `mountDevtools` in `_devtools.html`.
31
+ * Make sure to also configure `storeId` in LiveStore Devtools (e.g. in Vite plugin).
30
32
  *
31
33
  * @default 'default'
32
34
  */
@@ -34,9 +36,9 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
34
36
  boot?: (
35
37
  store: Store<GraphQLContext, LiveStoreSchema>,
36
38
  parentSpan: otel.Span,
37
- ) => void | Promise<void> | Effect.Effect<void, unknown, otel.Tracer>
39
+ ) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>
38
40
  graphQLOptions?: GraphQLOptions<GraphQLContext>
39
- otelOptions?: OtelOptions
41
+ otelOptions?: Partial<OtelOptions>
40
42
  renderLoading: (status: BootStatus) => ReactElement
41
43
  renderError?: (error: UnexpectedError | unknown) => ReactElement
42
44
  renderShutdown?: (cause: IntentionalShutdownCause | StoreAbort) => ReactElement
@@ -69,7 +71,9 @@ const defaultRenderShutdown = (cause: IntentionalShutdownCause | StoreAbort) =>
69
71
  ? 'devtools import'
70
72
  : cause.reason === 'devtools-reset'
71
73
  ? 'devtools reset'
72
- : 'unknown reason'
74
+ : cause.reason === 'manual'
75
+ ? 'manual shutdown'
76
+ : 'unknown reason'
73
77
 
74
78
  return <>LiveStore Shutdown due to {reason}</>
75
79
  }
@@ -88,7 +92,7 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
88
92
  batchUpdates,
89
93
  disableDevtools,
90
94
  signal,
91
- }: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): JSX.Element => {
95
+ }: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): React.ReactElement => {
92
96
  const storeCtx = useCreateStore({
93
97
  storeId,
94
98
  schema,
@@ -114,6 +118,9 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
114
118
  }
115
119
 
116
120
  globalThis.__debugLiveStore ??= {}
121
+ if (Object.keys(globalThis.__debugLiveStore).length === 0) {
122
+ globalThis.__debugLiveStore['_'] = storeCtx.store
123
+ }
117
124
  globalThis.__debugLiveStore[storeId] = storeCtx.store
118
125
 
119
126
  return <LiveStoreContext.Provider value={storeCtx}>{children}</LiveStoreContext.Provider>
@@ -140,17 +147,21 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
140
147
  adapter,
141
148
  batchUpdates,
142
149
  disableDevtools,
143
- reactivityGraph,
144
150
  signal,
145
- }: CreateStoreOptions<GraphQLContext, LiveStoreSchema> & { signal?: AbortSignal }) => {
151
+ }: CreateStoreOptions<GraphQLContext, LiveStoreSchema> & {
152
+ signal?: AbortSignal
153
+ otelOptions?: Partial<OtelOptions>
154
+ }) => {
146
155
  const [_, rerender] = React.useState(0)
147
156
  const ctxValueRef = React.useRef<{
148
157
  value: StoreContext_ | BootStatus
149
- fiberSet: FiberSet.FiberSet | undefined
158
+ componentScope: Scope.CloseableScope | undefined
159
+ shutdownDeferred: ShutdownDeferred | undefined
150
160
  counter: number
151
161
  }>({
152
162
  value: { stage: 'loading' },
153
- fiberSet: undefined,
163
+ componentScope: undefined,
164
+ shutdownDeferred: undefined,
154
165
  counter: 0,
155
166
  })
156
167
 
@@ -164,16 +175,20 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
164
175
  adapter,
165
176
  batchUpdates,
166
177
  disableDevtools,
167
- reactivityGraph,
168
178
  signal,
169
179
  })
170
180
 
171
- const interrupt = (fiberSet: FiberSet.FiberSet, error: StoreAbort | StoreInterrupted) =>
181
+ const interrupt = (
182
+ componentScope: Scope.CloseableScope,
183
+ shutdownDeferred: ShutdownDeferred,
184
+ error: StoreAbort | StoreInterrupted,
185
+ ) =>
172
186
  Effect.gen(function* () {
173
- yield* FiberSet.clear(fiberSet)
174
- yield* FiberSet.run(fiberSet, Effect.fail(error))
187
+ // console.log('[@livestore/livestore/react] interupting', error)
188
+ yield* Scope.close(componentScope, Exit.fail(error))
189
+ yield* Deferred.fail(shutdownDeferred, error)
175
190
  }).pipe(
176
- Effect.tapErrorCause((cause) => Effect.logDebug(`[@livestore/livestore/react] interupting`, cause)),
191
+ Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] interupting', cause)),
177
192
  Effect.runFork,
178
193
  )
179
194
 
@@ -185,7 +200,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
185
200
  inputPropsCacheRef.current.adapter !== adapter ||
186
201
  inputPropsCacheRef.current.batchUpdates !== batchUpdates ||
187
202
  inputPropsCacheRef.current.disableDevtools !== disableDevtools ||
188
- inputPropsCacheRef.current.reactivityGraph !== reactivityGraph ||
189
203
  inputPropsCacheRef.current.signal !== signal
190
204
  ) {
191
205
  inputPropsCacheRef.current = {
@@ -196,14 +210,19 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
196
210
  adapter,
197
211
  batchUpdates,
198
212
  disableDevtools,
199
- reactivityGraph,
200
213
  signal,
201
214
  }
202
- if (ctxValueRef.current.fiberSet !== undefined) {
203
- interrupt(ctxValueRef.current.fiberSet, new StoreInterrupted())
204
- ctxValueRef.current.fiberSet = undefined
215
+ if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
216
+ interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted())
217
+ ctxValueRef.current.componentScope = undefined
218
+ ctxValueRef.current.shutdownDeferred = undefined
219
+ }
220
+ ctxValueRef.current = {
221
+ value: { stage: 'loading' },
222
+ componentScope: undefined,
223
+ shutdownDeferred: undefined,
224
+ counter: ctxValueRef.current.counter + 1,
205
225
  }
206
- ctxValueRef.current = { value: { stage: 'loading' }, fiberSet: undefined, counter: ctxValueRef.current.counter + 1 }
207
226
  }
208
227
 
209
228
  React.useEffect(() => {
@@ -216,47 +235,51 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
216
235
  }
217
236
 
218
237
  signal?.addEventListener('abort', () => {
219
- if (ctxValueRef.current.fiberSet !== undefined && ctxValueRef.current.counter === counter) {
220
- interrupt(ctxValueRef.current.fiberSet, new StoreAbort())
221
- ctxValueRef.current.fiberSet = undefined
238
+ if (
239
+ ctxValueRef.current.componentScope !== undefined &&
240
+ ctxValueRef.current.shutdownDeferred !== undefined &&
241
+ ctxValueRef.current.counter === counter
242
+ ) {
243
+ interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreAbort())
244
+ ctxValueRef.current.componentScope = undefined
245
+ ctxValueRef.current.shutdownDeferred = undefined
222
246
  }
223
247
  })
224
248
 
225
249
  Effect.gen(function* () {
226
- const fiberSet = yield* FiberSet.make<
227
- unknown,
250
+ const componentScope = yield* Scope.make()
251
+ const shutdownDeferred = yield* Deferred.make<
252
+ void,
228
253
  UnexpectedError | IntentionalShutdownCause | StoreAbort | StoreInterrupted
229
254
  >()
230
255
 
231
- ctxValueRef.current.fiberSet = fiberSet
256
+ ctxValueRef.current.componentScope = componentScope
257
+ ctxValueRef.current.shutdownDeferred = shutdownDeferred
232
258
 
233
259
  yield* Effect.gen(function* () {
234
260
  const store = yield* createStore({
235
- fiberSet,
236
261
  schema,
237
262
  storeId,
238
263
  graphQLOptions,
239
- otelOptions,
240
264
  boot,
241
265
  adapter,
242
- reactivityGraph,
243
266
  batchUpdates,
244
267
  disableDevtools,
268
+ shutdownDeferred,
245
269
  onBootStatus: (status) => {
246
270
  if (ctxValueRef.current.value.stage === 'running' || ctxValueRef.current.value.stage === 'error') return
247
271
  setContextValue(status)
248
272
  },
249
- })
273
+ }).pipe(Effect.tapErrorCause((cause) => Deferred.failCause(shutdownDeferred, cause)))
250
274
 
251
275
  setContextValue({ stage: 'running', store })
252
-
253
- yield* Effect.never
254
- }).pipe(Effect.scoped, FiberSet.run(fiberSet))
276
+ }).pipe(Scope.extend(componentScope), Effect.forkIn(componentScope))
255
277
 
256
278
  const shutdownContext = (cause: IntentionalShutdownCause | StoreAbort) =>
257
279
  Effect.sync(() => setContextValue({ stage: 'shutdown', cause }))
258
280
 
259
- yield* FiberSet.join(fiberSet).pipe(
281
+ yield* Deferred.await(shutdownDeferred).pipe(
282
+ Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', cause)),
260
283
  Effect.catchTag('LiveStore.IntentionalShutdownCause', (cause) => shutdownContext(cause)),
261
284
  Effect.catchTag('LiveStore.StoreAbort', (cause) => shutdownContext(cause)),
262
285
  Effect.tapError((error) => Effect.sync(() => setContextValue({ stage: 'error', error }))),
@@ -269,31 +292,24 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
269
292
  // shutdown before a new one is created - especially when shutdown logic is async. You can't trust `React.useEffect`.
270
293
  // Thank you to Mattia Manzati for this idea.
271
294
  withSemaphore(storeId),
295
+ Effect.withSpan('@livestore/react:useCreateStore'),
296
+ LS_DEV ? TaskTracing.withAsyncTaggingTracing((name: string) => (console as any).createTask(name)) : identity,
297
+ provideOtel({ parentSpanContext: otelOptions?.rootSpanContext, otelTracer: otelOptions?.tracer }),
272
298
  Effect.tapCauseLogPretty,
273
299
  Effect.annotateLogs({ thread: 'window' }),
274
- Effect.provide(Logger.pretty),
300
+ Effect.provide(Logger.prettyWithThread('window')),
275
301
  Logger.withMinimumLogLevel(LogLevel.Debug),
276
302
  Effect.runFork,
277
303
  )
278
304
 
279
305
  return () => {
280
- if (ctxValueRef.current.fiberSet !== undefined) {
281
- interrupt(ctxValueRef.current.fiberSet, new StoreInterrupted())
282
- ctxValueRef.current.fiberSet = undefined
306
+ if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
307
+ interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted())
308
+ ctxValueRef.current.componentScope = undefined
309
+ ctxValueRef.current.shutdownDeferred = undefined
283
310
  }
284
311
  }
285
- }, [
286
- schema,
287
- graphQLOptions,
288
- otelOptions,
289
- boot,
290
- adapter,
291
- batchUpdates,
292
- disableDevtools,
293
- signal,
294
- reactivityGraph,
295
- storeId,
296
- ])
312
+ }, [schema, graphQLOptions, otelOptions, boot, adapter, batchUpdates, disableDevtools, signal, storeId])
297
313
 
298
314
  return ctxValueRef.current.value
299
315
  }