@livestore/livestore 0.0.54-dev.26 → 0.0.54-dev.27
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__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +2 -2
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +14 -8
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +15 -16
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +1 -1
- package/dist/effect/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/LiveStoreContext.d.ts +5 -2
- package/dist/react/LiveStoreContext.d.ts.map +1 -1
- package/dist/react/LiveStoreContext.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +3 -2
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +63 -39
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/LiveStoreProvider.test.js +28 -9
- package/dist/react/LiveStoreProvider.test.js.map +1 -1
- package/dist/react/useRow.test.js +1 -1
- package/dist/react/useRow.test.js.map +1 -1
- package/dist/reactiveQueries/sql.test.js +6 -6
- package/dist/reactiveQueries/sql.test.js.map +1 -1
- package/dist/store.d.ts +11 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +52 -120
- package/dist/store.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/react/fixture.tsx +2 -2
- package/src/effect/LiveStore.ts +48 -41
- package/src/effect/index.ts +2 -1
- package/src/index.ts +6 -2
- package/src/react/LiveStoreContext.ts +3 -2
- package/src/react/LiveStoreProvider.test.tsx +47 -10
- package/src/react/LiveStoreProvider.tsx +95 -38
- package/src/react/useRow.test.tsx +1 -1
- package/src/reactiveQueries/sql.test.ts +6 -6
- package/src/store.ts +234 -284
package/src/effect/LiveStore.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { BootDb, StoreAdapterFactory } from '@livestore/common'
|
|
1
|
+
import type { BootDb, StoreAdapterFactory, UnexpectedError } from '@livestore/common'
|
|
2
2
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
3
|
-
import
|
|
4
|
-
import { Context, Deferred, Duration, Effect, Layer, OtelTracer, pipe, Runtime } from '@livestore/utils/effect'
|
|
3
|
+
import { Context, Deferred, Duration, Effect, Layer, OtelTracer, pipe, Runtime, Scope } from '@livestore/utils/effect'
|
|
5
4
|
import * as otel from '@opentelemetry/api'
|
|
6
5
|
import type { GraphQLSchema } from 'graphql'
|
|
7
6
|
|
|
@@ -11,7 +10,17 @@ import type { BaseGraphQLContext, GraphQLOptions, OtelOptions, Store } from '../
|
|
|
11
10
|
import { createStore } from '../store.js'
|
|
12
11
|
|
|
13
12
|
// TODO get rid of `LiveStoreContext` wrapper and only expose the `Store` directly
|
|
14
|
-
export type LiveStoreContext =
|
|
13
|
+
export type LiveStoreContext =
|
|
14
|
+
| LiveStoreContextRunning
|
|
15
|
+
| {
|
|
16
|
+
stage: 'error'
|
|
17
|
+
error: UnexpectedError | unknown
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
stage: 'shutdown'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type LiveStoreContextRunning = {
|
|
15
24
|
stage: 'running'
|
|
16
25
|
store: Store
|
|
17
26
|
}
|
|
@@ -26,13 +35,16 @@ export type LiveStoreCreateStoreOptions<GraphQLContext extends BaseGraphQLContex
|
|
|
26
35
|
adapter: StoreAdapterFactory
|
|
27
36
|
batchUpdates?: (run: () => void) => void
|
|
28
37
|
disableDevtools?: boolean
|
|
38
|
+
signal?: AbortSignal
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
export const
|
|
41
|
+
export const LiveStoreContextRunning = Context.GenericTag<LiveStoreContextRunning>(
|
|
42
|
+
'@livestore/livestore/effect/LiveStoreContextRunning',
|
|
43
|
+
)
|
|
32
44
|
|
|
33
|
-
export type DeferredStoreContext = Deferred.Deferred<
|
|
45
|
+
export type DeferredStoreContext = Deferred.Deferred<LiveStoreContextRunning>
|
|
34
46
|
export const DeferredStoreContext = Context.GenericTag<DeferredStoreContext>(
|
|
35
|
-
'@livestore/livestore/DeferredStoreContext',
|
|
47
|
+
'@livestore/livestore/effect/DeferredStoreContext',
|
|
36
48
|
)
|
|
37
49
|
|
|
38
50
|
// export const DeferredStoreContext = Effect.cached(Effect.flatMap(StoreContext, (_) => Effect.succeed(_)))
|
|
@@ -50,13 +62,13 @@ export type LiveStoreContextProps<GraphQLContext extends BaseGraphQLContext> = {
|
|
|
50
62
|
|
|
51
63
|
export const LiveStoreContextLayer = <GraphQLContext extends BaseGraphQLContext>(
|
|
52
64
|
props: LiveStoreContextProps<GraphQLContext>,
|
|
53
|
-
): Layer.Layer<
|
|
54
|
-
Layer.scoped(
|
|
65
|
+
): Layer.Layer<LiveStoreContextRunning, never, otel.Tracer> =>
|
|
66
|
+
Layer.scoped(LiveStoreContextRunning, makeLiveStoreContext(props)).pipe(
|
|
55
67
|
Layer.withSpan('LiveStore'),
|
|
56
68
|
Layer.provide(LiveStoreContextDeferred),
|
|
57
69
|
)
|
|
58
70
|
|
|
59
|
-
export const LiveStoreContextDeferred = Layer.effect(DeferredStoreContext, Deferred.make<
|
|
71
|
+
export const LiveStoreContextDeferred = Layer.effect(DeferredStoreContext, Deferred.make<LiveStoreContextRunning>())
|
|
60
72
|
|
|
61
73
|
export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>({
|
|
62
74
|
schema,
|
|
@@ -65,54 +77,49 @@ export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>(
|
|
|
65
77
|
adapter,
|
|
66
78
|
disableDevtools,
|
|
67
79
|
}: LiveStoreContextProps<GraphQLContext>): Effect.Effect<
|
|
68
|
-
|
|
80
|
+
LiveStoreContextRunning,
|
|
69
81
|
never,
|
|
70
82
|
DeferredStoreContext | Scope.Scope | otel.Tracer
|
|
71
83
|
> =>
|
|
72
84
|
pipe(
|
|
73
|
-
Effect.gen(function* (
|
|
74
|
-
const runtime = yield*
|
|
85
|
+
Effect.gen(function* () {
|
|
86
|
+
const runtime = yield* Effect.runtime<never>()
|
|
75
87
|
|
|
76
88
|
const otelRootSpanContext = otel.context.active()
|
|
77
89
|
|
|
78
|
-
const
|
|
90
|
+
const storeScope = yield* Scope.make()
|
|
91
|
+
|
|
92
|
+
yield* Effect.addFinalizer((ex) => Scope.close(storeScope, ex))
|
|
79
93
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
const otelTracer = yield* OtelTracer.Tracer
|
|
95
|
+
|
|
96
|
+
const graphQLOptions = yield* graphQLOptions_
|
|
97
|
+
? Effect.all({ schema: graphQLOptions_.schema, makeContext: Effect.succeed(graphQLOptions_.makeContext) })
|
|
98
|
+
: Effect.succeed(undefined)
|
|
85
99
|
|
|
86
100
|
const boot = boot_
|
|
87
101
|
? (db: BootDb) => boot_(db).pipe(Effect.withSpan('boot'), Effect.tapCauseLogPretty, Runtime.runPromise(runtime))
|
|
88
102
|
: undefined
|
|
89
103
|
|
|
90
|
-
const store = yield*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}),
|
|
103
|
-
),
|
|
104
|
-
Effect.acquireRelease((store) => Effect.sync(() => store.destroy())),
|
|
105
|
-
)
|
|
104
|
+
const store = yield* createStore({
|
|
105
|
+
schema,
|
|
106
|
+
graphQLOptions,
|
|
107
|
+
otelOptions: {
|
|
108
|
+
tracer: otelTracer,
|
|
109
|
+
rootSpanContext: otelRootSpanContext,
|
|
110
|
+
},
|
|
111
|
+
boot,
|
|
112
|
+
adapter,
|
|
113
|
+
disableDevtools,
|
|
114
|
+
storeScope,
|
|
115
|
+
})
|
|
106
116
|
|
|
107
117
|
window.__debugLiveStore = store
|
|
108
118
|
|
|
109
|
-
return { stage: 'running', store } satisfies
|
|
119
|
+
return { stage: 'running', store } satisfies LiveStoreContextRunning
|
|
110
120
|
}),
|
|
111
121
|
Effect.tap((storeCtx) => Effect.flatMap(DeferredStoreContext, (def) => Deferred.succeed(def, storeCtx))),
|
|
112
|
-
Effect.
|
|
113
|
-
|
|
114
|
-
onTimeout: () => new Error('Timed out while creating LiveStore store after 60sec'),
|
|
115
|
-
duration: Duration.seconds(60),
|
|
116
|
-
}),
|
|
122
|
+
Effect.timeout(Duration.seconds(60)),
|
|
123
|
+
Effect.withSpan('@livestore/livestore/effect:makeLiveStoreContext'),
|
|
117
124
|
Effect.orDie,
|
|
118
125
|
)
|
package/src/effect/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
export { Store, createStore } from './store.js'
|
|
1
|
+
export { Store, createStorePromise, createStore } from './store.js'
|
|
2
2
|
export type { BaseGraphQLContext, QueryDebugInfo, RefreshReason } from './store.js'
|
|
3
3
|
|
|
4
|
-
export type {
|
|
4
|
+
export type {
|
|
5
|
+
QueryDefinition,
|
|
6
|
+
LiveStoreCreateStoreOptions,
|
|
7
|
+
LiveStoreContextRunning as LiveStoreContext,
|
|
8
|
+
} from './effect/LiveStore.js'
|
|
5
9
|
|
|
6
10
|
export { MainDatabaseWrapper, emptyDebugInfo } from './MainDatabaseWrapper.js'
|
|
7
11
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { LiveStoreContextRunning as LiveStoreContext_ } from '../effect/LiveStore.js'
|
|
4
|
+
import type { Store } from '../store.js'
|
|
4
5
|
|
|
5
6
|
export const LiveStoreContext = React.createContext<LiveStoreContext_ | undefined>(undefined)
|
|
6
7
|
|
|
7
|
-
export const useStore = ():
|
|
8
|
+
export const useStore = (): { store: Store } => {
|
|
8
9
|
const storeContext = useContext(LiveStoreContext)
|
|
9
10
|
|
|
10
11
|
if (storeContext === undefined) {
|
|
@@ -7,27 +7,25 @@ import { describe, expect, it } from 'vitest'
|
|
|
7
7
|
|
|
8
8
|
import { parseTodos, schema } from '../__tests__/react/fixture.js'
|
|
9
9
|
import { querySQL } from '../reactiveQueries/sql.js'
|
|
10
|
-
import type { Store } from '../store.js'
|
|
11
10
|
import * as LiveStoreReact from './index.js'
|
|
12
11
|
import { LiveStoreProvider } from './LiveStoreProvider.js'
|
|
13
12
|
|
|
14
13
|
describe('LiveStoreProvider', () => {
|
|
15
14
|
it('simple', async () => {
|
|
16
|
-
let
|
|
15
|
+
let appRenderCount = 0
|
|
17
16
|
|
|
18
17
|
const allTodos$ = querySQL(`select * from todos`, { map: parseTodos })
|
|
19
|
-
let latestStoreCtx: { store: Store } | undefined = undefined
|
|
20
18
|
|
|
21
19
|
const App = () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
latestStoreCtx = LiveStoreReact.useStore()
|
|
20
|
+
appRenderCount++
|
|
25
21
|
|
|
26
22
|
const todos = LiveStoreReact.useQuery(allTodos$)
|
|
27
23
|
|
|
28
24
|
return <div>{JSON.stringify(todos)}</div>
|
|
29
25
|
}
|
|
30
26
|
|
|
27
|
+
const abortController = new AbortController()
|
|
28
|
+
|
|
31
29
|
const Root = ({ forceUpdate }: { forceUpdate: number }) => {
|
|
32
30
|
const bootCb = React.useCallback(
|
|
33
31
|
(db: BootDb) =>
|
|
@@ -42,6 +40,7 @@ describe('LiveStoreProvider', () => {
|
|
|
42
40
|
renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
|
|
43
41
|
adapter={adapterMemo}
|
|
44
42
|
boot={bootCb}
|
|
43
|
+
signal={abortController.signal}
|
|
45
44
|
>
|
|
46
45
|
<App />
|
|
47
46
|
</LiveStoreProvider>
|
|
@@ -50,19 +49,57 @@ describe('LiveStoreProvider', () => {
|
|
|
50
49
|
|
|
51
50
|
const { rerender } = render(<Root forceUpdate={1} />)
|
|
52
51
|
|
|
53
|
-
expect(
|
|
52
|
+
expect(appRenderCount).toBe(0)
|
|
54
53
|
|
|
55
54
|
await waitForElementToBeRemoved(() => screen.getByText((_) => _.startsWith('Loading LiveStore')))
|
|
56
55
|
|
|
57
|
-
expect(
|
|
56
|
+
expect(appRenderCount).toBe(1)
|
|
58
57
|
|
|
59
58
|
rerender(<Root forceUpdate={2} />)
|
|
60
59
|
|
|
61
60
|
await waitFor(() => screen.getByText('Loading LiveStore: loading'))
|
|
62
61
|
await waitForElementToBeRemoved(() => screen.getByText((_) => _.startsWith('Loading LiveStore')))
|
|
63
62
|
|
|
64
|
-
expect(
|
|
63
|
+
expect(appRenderCount).toBe(2)
|
|
64
|
+
|
|
65
|
+
abortController.abort()
|
|
66
|
+
|
|
67
|
+
await waitFor(() => screen.getByText('LiveStore Shutdown'))
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('error during boot', async () => {
|
|
71
|
+
let appRenderCount = 0
|
|
72
|
+
|
|
73
|
+
const App = () => {
|
|
74
|
+
appRenderCount++
|
|
75
|
+
|
|
76
|
+
return <div>hello world</div>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const Root = ({ forceUpdate }: { forceUpdate: number }) => {
|
|
80
|
+
const bootCb = React.useCallback(
|
|
81
|
+
(db: BootDb) =>
|
|
82
|
+
db.execute(sql`INSERT INTO todos_mising_table (id, text, completed) VALUES ('t1', 'buy milk', 0);`),
|
|
83
|
+
[],
|
|
84
|
+
)
|
|
85
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
+
const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate])
|
|
87
|
+
return (
|
|
88
|
+
<LiveStoreProvider
|
|
89
|
+
schema={schema}
|
|
90
|
+
renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
|
|
91
|
+
adapter={adapterMemo}
|
|
92
|
+
boot={bootCb}
|
|
93
|
+
>
|
|
94
|
+
<App />
|
|
95
|
+
</LiveStoreProvider>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render(<Root forceUpdate={1} />)
|
|
100
|
+
|
|
101
|
+
expect(appRenderCount).toBe(0)
|
|
65
102
|
|
|
66
|
-
await
|
|
103
|
+
await waitFor(() => screen.getByText((_) => _.startsWith('LiveStore.UnexpectedError')))
|
|
67
104
|
})
|
|
68
105
|
})
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BootDb, type BootStatus, type StoreAdapterFactory, UnexpectedError } from '@livestore/common'
|
|
2
2
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
3
|
-
import {
|
|
3
|
+
import { errorToString } from '@livestore/utils'
|
|
4
|
+
import { Effect, Exit, Logger, LogLevel, Schema, Scope } from '@livestore/utils/effect'
|
|
4
5
|
import type * as otel from '@opentelemetry/api'
|
|
5
6
|
import type { ReactElement, ReactNode } from 'react'
|
|
6
7
|
import React from 'react'
|
|
7
8
|
|
|
8
9
|
// TODO refactor so the `react` module doesn't depend on `effect` module
|
|
9
10
|
import type { LiveStoreContext as StoreContext_, LiveStoreCreateStoreOptions } from '../effect/LiveStore.js'
|
|
10
|
-
import type { BaseGraphQLContext, GraphQLOptions, OtelOptions
|
|
11
|
+
import type { BaseGraphQLContext, GraphQLOptions, OtelOptions } from '../store.js'
|
|
11
12
|
import { createStore } from '../store.js'
|
|
12
13
|
import { LiveStoreContext } from './LiveStoreContext.js'
|
|
13
14
|
|
|
@@ -20,6 +21,7 @@ interface LiveStoreProviderProps<GraphQLContext> {
|
|
|
20
21
|
adapter: StoreAdapterFactory
|
|
21
22
|
batchUpdates?: (run: () => void) => void
|
|
22
23
|
disableDevtools?: boolean
|
|
24
|
+
signal?: AbortSignal
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
@@ -32,6 +34,7 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
32
34
|
adapter,
|
|
33
35
|
batchUpdates,
|
|
34
36
|
disableDevtools,
|
|
37
|
+
signal,
|
|
35
38
|
}: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): JSX.Element => {
|
|
36
39
|
const storeCtx = useCreateStore({
|
|
37
40
|
schema,
|
|
@@ -41,8 +44,21 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
41
44
|
adapter,
|
|
42
45
|
batchUpdates,
|
|
43
46
|
disableDevtools,
|
|
47
|
+
signal,
|
|
44
48
|
})
|
|
45
49
|
|
|
50
|
+
if (storeCtx.stage === 'error') {
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
{Schema.is(UnexpectedError)(storeCtx.error) ? storeCtx.error.toString() : errorToString(storeCtx.error)}
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (storeCtx.stage === 'shutdown') {
|
|
59
|
+
return <div>LiveStore Shutdown</div>
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
if (storeCtx.stage !== 'running') {
|
|
47
63
|
return <div>{renderLoading(storeCtx)}</div>
|
|
48
64
|
}
|
|
@@ -60,9 +76,21 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
60
76
|
adapter,
|
|
61
77
|
batchUpdates,
|
|
62
78
|
disableDevtools,
|
|
79
|
+
signal,
|
|
63
80
|
}: LiveStoreCreateStoreOptions<GraphQLContext>) => {
|
|
64
81
|
const [_, rerender] = React.useState(0)
|
|
65
|
-
const ctxValueRef = React.useRef<
|
|
82
|
+
const ctxValueRef = React.useRef<{
|
|
83
|
+
value: StoreContext_ | BootStatus
|
|
84
|
+
scope: Scope.CloseableScope | undefined
|
|
85
|
+
counter: number
|
|
86
|
+
}>({
|
|
87
|
+
value: { stage: 'loading' },
|
|
88
|
+
scope: undefined,
|
|
89
|
+
counter: 0,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// console.debug(`useCreateStore (${ctxValueRef.current.counter})`, ctxValueRef.current.value.stage)
|
|
93
|
+
|
|
66
94
|
const inputPropsCacheRef = React.useRef({
|
|
67
95
|
schema,
|
|
68
96
|
graphQLOptions,
|
|
@@ -70,8 +98,9 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
70
98
|
boot,
|
|
71
99
|
adapter,
|
|
72
100
|
batchUpdates,
|
|
101
|
+
disableDevtools,
|
|
102
|
+
signal,
|
|
73
103
|
})
|
|
74
|
-
const oldStoreAlreadyDestroyedRef = React.useRef(false)
|
|
75
104
|
|
|
76
105
|
if (
|
|
77
106
|
inputPropsCacheRef.current.schema !== schema ||
|
|
@@ -79,7 +108,9 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
79
108
|
inputPropsCacheRef.current.otelOptions !== otelOptions ||
|
|
80
109
|
inputPropsCacheRef.current.boot !== boot ||
|
|
81
110
|
inputPropsCacheRef.current.adapter !== adapter ||
|
|
82
|
-
inputPropsCacheRef.current.batchUpdates !== batchUpdates
|
|
111
|
+
inputPropsCacheRef.current.batchUpdates !== batchUpdates ||
|
|
112
|
+
inputPropsCacheRef.current.disableDevtools !== disableDevtools ||
|
|
113
|
+
inputPropsCacheRef.current.signal !== signal
|
|
83
114
|
) {
|
|
84
115
|
inputPropsCacheRef.current = {
|
|
85
116
|
schema,
|
|
@@ -88,47 +119,73 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
88
119
|
boot,
|
|
89
120
|
adapter,
|
|
90
121
|
batchUpdates,
|
|
122
|
+
disableDevtools,
|
|
123
|
+
signal,
|
|
91
124
|
}
|
|
92
|
-
if (ctxValueRef.current.
|
|
93
|
-
ctxValueRef.current.
|
|
94
|
-
oldStoreAlreadyDestroyedRef.current = true
|
|
95
|
-
ctxValueRef.current = { stage: 'loading' }
|
|
125
|
+
if (ctxValueRef.current.scope !== undefined) {
|
|
126
|
+
Scope.close(ctxValueRef.current.scope, Exit.void).pipe(Effect.tapCauseLogPretty, Effect.runFork)
|
|
96
127
|
}
|
|
128
|
+
ctxValueRef.current = { value: { stage: 'loading' }, scope: undefined, counter: ctxValueRef.current.counter + 1 }
|
|
97
129
|
}
|
|
98
130
|
|
|
99
131
|
React.useEffect(() => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} catch (e) {
|
|
122
|
-
shouldNeverHappen(`Error creating LiveStore store: ${e}`)
|
|
132
|
+
const storeScope = Scope.make().pipe(Effect.runSync)
|
|
133
|
+
|
|
134
|
+
const counter = ctxValueRef.current.counter
|
|
135
|
+
|
|
136
|
+
const setContextValue = (value: StoreContext_ | BootStatus) => {
|
|
137
|
+
if (ctxValueRef.current.counter !== counter) return
|
|
138
|
+
ctxValueRef.current.value = value
|
|
139
|
+
rerender((c) => c + 1)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
Scope.addFinalizer(
|
|
143
|
+
storeScope,
|
|
144
|
+
Effect.sync(() => setContextValue({ stage: 'shutdown' })),
|
|
145
|
+
).pipe(Effect.runSync)
|
|
146
|
+
|
|
147
|
+
ctxValueRef.current.scope = storeScope
|
|
148
|
+
|
|
149
|
+
signal?.addEventListener('abort', () => {
|
|
150
|
+
if (ctxValueRef.current.scope !== undefined && ctxValueRef.current.counter === counter) {
|
|
151
|
+
Scope.close(ctxValueRef.current.scope, Exit.void).pipe(Effect.tapCauseLogPretty, Effect.runFork)
|
|
152
|
+
ctxValueRef.current.scope = undefined
|
|
123
153
|
}
|
|
124
|
-
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
createStore({
|
|
157
|
+
storeScope,
|
|
158
|
+
schema,
|
|
159
|
+
graphQLOptions,
|
|
160
|
+
otelOptions,
|
|
161
|
+
boot,
|
|
162
|
+
adapter,
|
|
163
|
+
batchUpdates,
|
|
164
|
+
disableDevtools,
|
|
165
|
+
onBootStatus: (status) => {
|
|
166
|
+
if (ctxValueRef.current.value.stage === 'running' || ctxValueRef.current.value.stage === 'error') return
|
|
167
|
+
setContextValue(status)
|
|
168
|
+
},
|
|
169
|
+
}).pipe(
|
|
170
|
+
Effect.tapSync((store) => setContextValue({ stage: 'running', store })),
|
|
171
|
+
Effect.tapError((error) => Effect.sync(() => setContextValue({ stage: 'error', error }))),
|
|
172
|
+
Effect.tapDefect((defect) => Effect.sync(() => setContextValue({ stage: 'error', error: defect }))),
|
|
173
|
+
Scope.extend(storeScope),
|
|
174
|
+
Effect.forkIn(storeScope),
|
|
175
|
+
Effect.tapCauseLogPretty,
|
|
176
|
+
Effect.annotateLogs({ thread: 'window' }),
|
|
177
|
+
Effect.provide(Logger.pretty),
|
|
178
|
+
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
179
|
+
Effect.runFork,
|
|
180
|
+
)
|
|
125
181
|
|
|
126
182
|
return () => {
|
|
127
|
-
if (
|
|
128
|
-
|
|
183
|
+
if (ctxValueRef.current.scope !== undefined) {
|
|
184
|
+
Scope.close(ctxValueRef.current.scope, Exit.void).pipe(Effect.tapCauseLogPretty, Effect.runFork)
|
|
185
|
+
ctxValueRef.current.scope = undefined
|
|
129
186
|
}
|
|
130
187
|
}
|
|
131
|
-
}, [schema, graphQLOptions, otelOptions, boot, adapter, batchUpdates, disableDevtools])
|
|
188
|
+
}, [schema, graphQLOptions, otelOptions, boot, adapter, batchUpdates, disableDevtools, signal])
|
|
132
189
|
|
|
133
|
-
return ctxValueRef.current
|
|
190
|
+
return ctxValueRef.current.value
|
|
134
191
|
}
|
|
@@ -24,17 +24,17 @@ describe('otel', () => {
|
|
|
24
24
|
provider.addSpanProcessor(new SimpleSpanProcessor(exporter))
|
|
25
25
|
provider.register()
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const otelTracer = otel.trace.getTracer('test')
|
|
28
28
|
|
|
29
|
-
const span =
|
|
29
|
+
const span = otelTracer.startSpan('test')
|
|
30
30
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
31
31
|
|
|
32
|
-
const { store } = await makeTodoMvc({ otelTracer
|
|
32
|
+
const { store } = await makeTodoMvc({ otelTracer, otelContext })
|
|
33
33
|
|
|
34
34
|
return {
|
|
35
35
|
[Symbol.dispose]: () => store.destroy(),
|
|
36
36
|
store,
|
|
37
|
-
|
|
37
|
+
otelTracer,
|
|
38
38
|
exporter,
|
|
39
39
|
span,
|
|
40
40
|
provider,
|
|
@@ -60,7 +60,7 @@ describe('otel', () => {
|
|
|
60
60
|
]
|
|
61
61
|
`)
|
|
62
62
|
|
|
63
|
-
store.destroy()
|
|
63
|
+
await store.destroy()
|
|
64
64
|
query.destroy()
|
|
65
65
|
span.end()
|
|
66
66
|
|
|
@@ -193,7 +193,7 @@ describe('otel', () => {
|
|
|
193
193
|
}
|
|
194
194
|
`)
|
|
195
195
|
|
|
196
|
-
store.destroy()
|
|
196
|
+
await store.destroy()
|
|
197
197
|
query.destroy()
|
|
198
198
|
span.end()
|
|
199
199
|
|