@livestore/livestore 0.0.58-dev.8 → 0.1.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/README.md +1 -117
- package/dist/.tsbuildinfo +1 -1
- package/dist/effect/LiveStore.d.ts +6 -6
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +1 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/global-state.d.ts.map +1 -1
- package/dist/global-state.js +2 -1
- package/dist/global-state.js.map +1 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +5 -4
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/computed.d.ts +35 -0
- package/dist/reactiveQueries/computed.d.ts.map +1 -0
- package/dist/reactiveQueries/computed.js +57 -0
- package/dist/reactiveQueries/computed.js.map +1 -0
- package/dist/reactiveQueries/graphql.d.ts +2 -1
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +2 -2
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +3 -3
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/reactiveQueries/sql.test.js +2 -2
- package/dist/reactiveQueries/sql.test.js.map +1 -1
- package/dist/row-query.d.ts +3 -2
- package/dist/row-query.d.ts.map +1 -1
- package/dist/row-query.js +14 -10
- package/dist/row-query.js.map +1 -1
- package/dist/store/create-store.d.ts +28 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/create-store.js +85 -0
- package/dist/store/create-store.js.map +1 -0
- package/dist/store/devtools.d.ts +19 -0
- package/dist/store/devtools.d.ts.map +1 -0
- package/dist/store/devtools.js +141 -0
- package/dist/store/devtools.js.map +1 -0
- package/dist/store/store-context.d.ts +26 -0
- package/dist/store/store-context.d.ts.map +1 -0
- package/dist/store/store-context.js +6 -0
- package/dist/store/store-context.js.map +1 -0
- package/dist/store/store-types.d.ts +98 -0
- package/dist/store/store-types.d.ts.map +1 -0
- package/dist/store/store-types.js +6 -0
- package/dist/store/store-types.js.map +1 -0
- package/dist/store/store.d.ts +88 -0
- package/dist/store/store.d.ts.map +1 -0
- package/dist/store/store.js +367 -0
- package/dist/store/store.js.map +1 -0
- package/dist/store-devtools.d.ts +2 -2
- package/dist/store-devtools.d.ts.map +1 -1
- package/dist/store-devtools.js +2 -2
- package/dist/store-devtools.js.map +1 -1
- package/dist/store.d.ts +8 -8
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +54 -54
- package/dist/store.js.map +1 -1
- package/dist/utils/dev.d.ts +1 -0
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +5 -0
- package/dist/utils/dev.js.map +1 -1
- package/dist/{react/utils → utils}/stack-info.d.ts +1 -2
- package/dist/utils/stack-info.d.ts.map +1 -0
- package/dist/{react/utils → utils}/stack-info.js +1 -9
- package/dist/utils/stack-info.js.map +1 -0
- package/dist/utils/stack-info.test.d.ts.map +1 -0
- package/dist/{__tests__/react/utils → utils}/stack-info.test.js +1 -1
- package/dist/utils/stack-info.test.js.map +1 -0
- package/dist/utils/tests/fixture.d.ts +259 -0
- package/dist/utils/tests/fixture.d.ts.map +1 -0
- package/dist/utils/tests/fixture.js +32 -0
- package/dist/utils/tests/fixture.js.map +1 -0
- package/dist/utils/tests/mod.d.ts +3 -0
- package/dist/utils/tests/mod.d.ts.map +1 -0
- package/dist/utils/tests/mod.js +3 -0
- package/dist/utils/tests/mod.js.map +1 -0
- package/dist/utils/tests/otel.d.ts.map +1 -0
- package/dist/{__tests__/react/utils → utils/tests}/otel.js +3 -3
- package/dist/utils/tests/otel.js.map +1 -0
- package/package.json +13 -20
- package/src/ambient.d.ts +3 -1
- package/src/effect/LiveStore.ts +7 -7
- package/src/global-state.ts +5 -1
- package/src/index.ts +19 -7
- package/src/reactiveQueries/base-class.ts +5 -4
- package/src/reactiveQueries/{js.ts → computed.ts} +3 -3
- package/src/reactiveQueries/graphql.ts +2 -1
- package/src/reactiveQueries/sql.test.ts +2 -2
- package/src/reactiveQueries/sql.ts +5 -5
- package/src/row-query.ts +33 -17
- package/src/store/create-store.ts +214 -0
- package/src/{store-devtools.ts → store/devtools.ts} +9 -9
- package/src/store/store-types.ts +110 -0
- package/src/{store.ts → store/store.ts} +56 -415
- package/src/utils/dev.ts +6 -0
- package/src/{__tests__/react/utils → utils}/stack-info.test.ts +1 -1
- package/src/{react/utils → utils}/stack-info.ts +2 -12
- package/src/utils/tests/fixture.ts +73 -0
- package/src/utils/tests/mod.ts +2 -0
- package/src/{__tests__/react/utils → utils/tests}/otel.ts +4 -4
- package/tsconfig.json +1 -2
- package/vitest.config.js +0 -8
- package/dist/__tests__/react/fixture.d.ts +0 -461
- package/dist/__tests__/react/fixture.d.ts.map +0 -1
- package/dist/__tests__/react/fixture.js +0 -68
- package/dist/__tests__/react/fixture.js.map +0 -1
- package/dist/__tests__/react/utils/otel.d.ts.map +0 -1
- package/dist/__tests__/react/utils/otel.js.map +0 -1
- package/dist/__tests__/react/utils/stack-info.test.d.ts.map +0 -1
- package/dist/__tests__/react/utils/stack-info.test.js.map +0 -1
- package/dist/react/LiveStoreContext.d.ts +0 -7
- package/dist/react/LiveStoreContext.d.ts.map +0 -1
- package/dist/react/LiveStoreContext.js +0 -13
- package/dist/react/LiveStoreContext.js.map +0 -1
- package/dist/react/LiveStoreProvider.d.ts +0 -49
- package/dist/react/LiveStoreProvider.d.ts.map +0 -1
- package/dist/react/LiveStoreProvider.js +0 -169
- package/dist/react/LiveStoreProvider.js.map +0 -1
- package/dist/react/LiveStoreProvider.test.d.ts +0 -2
- package/dist/react/LiveStoreProvider.test.d.ts.map +0 -1
- package/dist/react/LiveStoreProvider.test.js +0 -62
- package/dist/react/LiveStoreProvider.test.js.map +0 -1
- package/dist/react/components/LiveList.d.ts +0 -21
- package/dist/react/components/LiveList.d.ts.map +0 -1
- package/dist/react/components/LiveList.js +0 -31
- package/dist/react/components/LiveList.js.map +0 -1
- package/dist/react/index.d.ts +0 -11
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -10
- package/dist/react/index.js.map +0 -1
- package/dist/react/useAtom.d.ts +0 -10
- package/dist/react/useAtom.d.ts.map +0 -1
- package/dist/react/useAtom.js +0 -37
- package/dist/react/useAtom.js.map +0 -1
- package/dist/react/useLocalId.d.ts +0 -10
- package/dist/react/useLocalId.d.ts.map +0 -1
- package/dist/react/useLocalId.js +0 -22
- package/dist/react/useLocalId.js.map +0 -1
- package/dist/react/useQuery.d.ts +0 -9
- package/dist/react/useQuery.d.ts.map +0 -1
- package/dist/react/useQuery.js +0 -70
- package/dist/react/useQuery.js.map +0 -1
- package/dist/react/useQuery.test.d.ts +0 -2
- package/dist/react/useQuery.test.d.ts.map +0 -1
- package/dist/react/useQuery.test.js +0 -51
- package/dist/react/useQuery.test.js.map +0 -1
- package/dist/react/useRow.d.ts +0 -46
- package/dist/react/useRow.d.ts.map +0 -1
- package/dist/react/useRow.js +0 -94
- package/dist/react/useRow.js.map +0 -1
- package/dist/react/useRow.test.d.ts +0 -2
- package/dist/react/useRow.test.d.ts.map +0 -1
- package/dist/react/useRow.test.js +0 -208
- package/dist/react/useRow.test.js.map +0 -1
- package/dist/react/useTemporaryQuery.d.ts +0 -22
- package/dist/react/useTemporaryQuery.d.ts.map +0 -1
- package/dist/react/useTemporaryQuery.js +0 -75
- package/dist/react/useTemporaryQuery.js.map +0 -1
- package/dist/react/useTemporaryQuery.test.d.ts +0 -2
- package/dist/react/useTemporaryQuery.test.d.ts.map +0 -1
- package/dist/react/useTemporaryQuery.test.js +0 -59
- package/dist/react/useTemporaryQuery.test.js.map +0 -1
- package/dist/react/utils/stack-info.d.ts.map +0 -1
- package/dist/react/utils/stack-info.js.map +0 -1
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts +0 -13
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +0 -1
- package/dist/react/utils/useStateRefWithReactiveInput.js +0 -38
- package/dist/react/utils/useStateRefWithReactiveInput.js.map +0 -1
- package/src/__tests__/react/fixture.tsx +0 -126
- package/src/react/LiveStoreContext.ts +0 -20
- package/src/react/LiveStoreProvider.test.tsx +0 -109
- package/src/react/LiveStoreProvider.tsx +0 -291
- package/src/react/__snapshots__/useRow.test.tsx.snap +0 -359
- package/src/react/components/LiveList.tsx +0 -84
- package/src/react/index.ts +0 -19
- package/src/react/useAtom.ts +0 -55
- package/src/react/useLocalId.ts +0 -34
- package/src/react/useQuery.test.tsx +0 -82
- package/src/react/useQuery.ts +0 -106
- package/src/react/useRow.test.tsx +0 -345
- package/src/react/useRow.ts +0 -180
- package/src/react/useTemporaryQuery.test.tsx +0 -98
- package/src/react/useTemporaryQuery.ts +0 -131
- package/src/react/utils/useStateRefWithReactiveInput.ts +0 -51
- package/src/store-context.ts +0 -23
- /package/dist/{__tests__/react/utils → utils}/stack-info.test.d.ts +0 -0
- /package/dist/{__tests__/react/utils → utils/tests}/otel.d.ts +0 -0
|
@@ -1,139 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
BootStatus,
|
|
4
|
-
EventId,
|
|
5
|
-
IntentionalShutdownCause,
|
|
6
|
-
ParamsObject,
|
|
7
|
-
PreparedBindValues,
|
|
8
|
-
StoreAdapter,
|
|
9
|
-
StoreAdapterFactory,
|
|
10
|
-
StoreDevtoolsChannel,
|
|
11
|
-
} from '@livestore/common'
|
|
12
|
-
import { getExecArgsFromMutation, prepareBindValues, UnexpectedError } from '@livestore/common'
|
|
1
|
+
import type { ClientSession, ParamsObject } from '@livestore/common'
|
|
2
|
+
import { getExecArgsFromMutation, prepareBindValues, replaceSessionIdSymbol } from '@livestore/common'
|
|
13
3
|
import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
|
|
14
4
|
import {
|
|
5
|
+
isPartialMutationEvent,
|
|
15
6
|
makeMutationEventSchemaMemo,
|
|
16
7
|
SCHEMA_META_TABLE,
|
|
17
8
|
SCHEMA_MUTATIONS_META_TABLE,
|
|
18
9
|
SESSION_CHANGESET_META_TABLE,
|
|
19
10
|
} from '@livestore/common/schema'
|
|
20
|
-
import { assertNever,
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
Data,
|
|
24
|
-
Deferred,
|
|
25
|
-
Duration,
|
|
26
|
-
Effect,
|
|
27
|
-
Exit,
|
|
28
|
-
FiberSet,
|
|
29
|
-
Inspectable,
|
|
30
|
-
Layer,
|
|
31
|
-
Logger,
|
|
32
|
-
LogLevel,
|
|
33
|
-
MutableHashMap,
|
|
34
|
-
OtelTracer,
|
|
35
|
-
Queue,
|
|
36
|
-
Runtime,
|
|
37
|
-
Schema,
|
|
38
|
-
Scope,
|
|
39
|
-
Stream,
|
|
40
|
-
} from '@livestore/utils/effect'
|
|
11
|
+
import { assertNever, shouldNeverHappen } from '@livestore/utils'
|
|
12
|
+
import type { Scope } from '@livestore/utils/effect'
|
|
13
|
+
import { Data, Effect, FiberSet, Inspectable, MutableHashMap, Runtime, Schema, Stream } from '@livestore/utils/effect'
|
|
41
14
|
import * as otel from '@opentelemetry/api'
|
|
42
15
|
import type { GraphQLSchema } from 'graphql'
|
|
43
16
|
|
|
44
|
-
import {
|
|
45
|
-
import type {
|
|
46
|
-
import
|
|
47
|
-
import
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import { downloadBlob } from './utils/dev.js'
|
|
52
|
-
import { getDurationMsFromSpan } from './utils/otel.js'
|
|
53
|
-
|
|
54
|
-
export type BaseGraphQLContext = {
|
|
55
|
-
queriedTables: Set<string>
|
|
56
|
-
/** Needed by Pothos Otel plugin for resolver tracing to work */
|
|
57
|
-
otelContext?: otel.Context
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export type GraphQLOptions<TContext> = {
|
|
61
|
-
schema: GraphQLSchema
|
|
62
|
-
makeContext: (db: SynchronousDatabaseWrapper, tracer: otel.Tracer) => TContext
|
|
63
|
-
}
|
|
17
|
+
import type { Ref } from '../reactive.js'
|
|
18
|
+
import type { LiveQuery, QueryContext, ReactivityGraph } from '../reactiveQueries/base-class.js'
|
|
19
|
+
import { SynchronousDatabaseWrapper } from '../SynchronousDatabaseWrapper.js'
|
|
20
|
+
import { ReferenceCountedSet } from '../utils/data-structures.js'
|
|
21
|
+
import { downloadBlob, exposeDebugUtils } from '../utils/dev.js'
|
|
22
|
+
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
23
|
+
import type { BaseGraphQLContext, RefreshReason, StoreMutateOptions, StoreOptions, StoreOtel } from './store-types.js'
|
|
64
24
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
rootSpanContext: otel.Context
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export type StoreOptions<
|
|
71
|
-
TGraphQLContext extends BaseGraphQLContext,
|
|
72
|
-
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
73
|
-
> = {
|
|
74
|
-
adapter: StoreAdapter
|
|
75
|
-
schema: TSchema
|
|
76
|
-
storeId: string
|
|
77
|
-
// TODO remove graphql-related stuff from store and move to GraphQL query directly
|
|
78
|
-
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
79
|
-
otelOptions: OtelOptions
|
|
80
|
-
reactivityGraph: ReactivityGraph
|
|
81
|
-
disableDevtools?: boolean
|
|
82
|
-
fiberSet: FiberSet.FiberSet
|
|
83
|
-
runtime: Runtime.Runtime<Scope.Scope>
|
|
84
|
-
batchUpdates: (runUpdates: () => void) => void
|
|
85
|
-
currentMutationEventIdRef: { current: EventId }
|
|
86
|
-
unsyncedMutationEvents: MutableHashMap.MutableHashMap<EventId, MutationEvent.ForSchema<TSchema>>
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export type RefreshReason =
|
|
90
|
-
| DebugRefreshReasonBase
|
|
91
|
-
| {
|
|
92
|
-
_tag: 'mutate'
|
|
93
|
-
/** The mutations that were applied */
|
|
94
|
-
mutations: ReadonlyArray<MutationEvent.Any>
|
|
95
|
-
|
|
96
|
-
/** The tables that were written to by the event */
|
|
97
|
-
writeTables: ReadonlyArray<string>
|
|
98
|
-
}
|
|
99
|
-
| {
|
|
100
|
-
_tag: 'react'
|
|
101
|
-
api: string
|
|
102
|
-
label?: string
|
|
103
|
-
stackInfo?: StackInfo
|
|
104
|
-
}
|
|
105
|
-
| { _tag: 'manual'; label?: string }
|
|
106
|
-
|
|
107
|
-
export type QueryDebugInfo = {
|
|
108
|
-
_tag: 'graphql' | 'sql' | 'js' | 'unknown'
|
|
109
|
-
label: string
|
|
110
|
-
query: string
|
|
111
|
-
durationMs: number
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export type StoreOtel = {
|
|
115
|
-
tracer: otel.Tracer
|
|
116
|
-
mutationsSpanContext: otel.Context
|
|
117
|
-
queriesSpanContext: otel.Context
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export type StoreMutateOptions = {
|
|
121
|
-
label?: string
|
|
122
|
-
skipRefresh?: boolean
|
|
123
|
-
wasSyncMessage?: boolean
|
|
124
|
-
/**
|
|
125
|
-
* When set to `false` the mutation won't be persisted in the mutation log and sync server (but still synced).
|
|
126
|
-
* This can be useful e.g. for fine-granular update events (e.g. position updates during drag & drop)
|
|
127
|
-
*
|
|
128
|
-
* @default true
|
|
129
|
-
*/
|
|
130
|
-
persisted?: boolean
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// eslint-disable-next-line unicorn/prefer-global-this
|
|
134
|
-
if (import.meta.env.DEV && typeof window !== 'undefined') {
|
|
135
|
-
// eslint-disable-next-line unicorn/prefer-global-this
|
|
136
|
-
window.__debugDownloadBlob = downloadBlob
|
|
25
|
+
if (import.meta.env.DEV) {
|
|
26
|
+
exposeDebugUtils()
|
|
137
27
|
}
|
|
138
28
|
|
|
139
29
|
export class Store<
|
|
@@ -143,7 +33,7 @@ export class Store<
|
|
|
143
33
|
readonly storeId: string
|
|
144
34
|
reactivityGraph: ReactivityGraph
|
|
145
35
|
syncDbWrapper: SynchronousDatabaseWrapper
|
|
146
|
-
|
|
36
|
+
clientSession: ClientSession
|
|
147
37
|
schema: LiveStoreSchema
|
|
148
38
|
graphQLSchema?: GraphQLSchema
|
|
149
39
|
graphQLContext?: TGraphQLContext
|
|
@@ -162,20 +52,17 @@ export class Store<
|
|
|
162
52
|
|
|
163
53
|
// NOTE this is currently exposed for the Devtools databrowser to emit mutation events
|
|
164
54
|
readonly __mutationEventSchema
|
|
165
|
-
|
|
166
|
-
private currentMutationEventIdRef
|
|
167
55
|
private unsyncedMutationEvents
|
|
168
56
|
|
|
169
57
|
// #region constructor
|
|
170
58
|
private constructor({
|
|
171
|
-
|
|
59
|
+
clientSession,
|
|
172
60
|
schema,
|
|
173
61
|
graphQLOptions,
|
|
174
62
|
reactivityGraph,
|
|
175
63
|
otelOptions,
|
|
176
64
|
disableDevtools,
|
|
177
65
|
batchUpdates,
|
|
178
|
-
currentMutationEventIdRef,
|
|
179
66
|
unsyncedMutationEvents,
|
|
180
67
|
storeId,
|
|
181
68
|
fiberSet,
|
|
@@ -185,11 +72,10 @@ export class Store<
|
|
|
185
72
|
|
|
186
73
|
this.storeId = storeId
|
|
187
74
|
|
|
188
|
-
this.currentMutationEventIdRef = currentMutationEventIdRef
|
|
189
75
|
this.unsyncedMutationEvents = unsyncedMutationEvents
|
|
190
76
|
|
|
191
|
-
this.syncDbWrapper = new SynchronousDatabaseWrapper({ otel: otelOptions, db:
|
|
192
|
-
this.
|
|
77
|
+
this.syncDbWrapper = new SynchronousDatabaseWrapper({ otel: otelOptions, db: clientSession.syncDb })
|
|
78
|
+
this.clientSession = clientSession
|
|
193
79
|
this.schema = schema
|
|
194
80
|
|
|
195
81
|
this.fiberSet = fiberSet
|
|
@@ -247,11 +133,15 @@ export class Store<
|
|
|
247
133
|
|
|
248
134
|
if (graphQLOptions) {
|
|
249
135
|
this.graphQLSchema = graphQLOptions.schema
|
|
250
|
-
this.graphQLContext = graphQLOptions.makeContext(
|
|
136
|
+
this.graphQLContext = graphQLOptions.makeContext(
|
|
137
|
+
this.syncDbWrapper,
|
|
138
|
+
this.otel.tracer,
|
|
139
|
+
clientSession.coordinator.sessionId,
|
|
140
|
+
)
|
|
251
141
|
}
|
|
252
142
|
|
|
253
143
|
Effect.gen(this, function* () {
|
|
254
|
-
yield* this.
|
|
144
|
+
yield* this.clientSession.coordinator.syncMutations.pipe(
|
|
255
145
|
Stream.tapChunk((mutationsEventsDecodedChunk) =>
|
|
256
146
|
Effect.sync(() => {
|
|
257
147
|
this.mutate({ wasSyncMessage: true }, ...mutationsEventsDecodedChunk)
|
|
@@ -265,18 +155,20 @@ export class Store<
|
|
|
265
155
|
|
|
266
156
|
yield* Effect.addFinalizer(() =>
|
|
267
157
|
Effect.sync(() => {
|
|
158
|
+
// Remove all table refs from the reactivity graph
|
|
268
159
|
for (const tableRef of Object.values(this.tableRefs)) {
|
|
269
160
|
for (const superComp of tableRef.super) {
|
|
270
161
|
this.reactivityGraph.removeEdge(superComp, tableRef)
|
|
271
162
|
}
|
|
272
163
|
}
|
|
273
164
|
|
|
165
|
+
// End the otel spans
|
|
274
166
|
otel.trace.getSpan(this.otel.mutationsSpanContext)!.end()
|
|
275
167
|
otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
|
|
276
168
|
}),
|
|
277
169
|
)
|
|
278
170
|
|
|
279
|
-
yield* Effect.never
|
|
171
|
+
yield* Effect.never // to keep the scope alive and bind to the parent scope
|
|
280
172
|
}).pipe(Effect.scoped, Effect.withSpan('LiveStore:constructor'), this.runEffectFork)
|
|
281
173
|
}
|
|
282
174
|
// #endregion constructor
|
|
@@ -295,6 +187,10 @@ export class Store<
|
|
|
295
187
|
})
|
|
296
188
|
}
|
|
297
189
|
|
|
190
|
+
get sessionId(): string {
|
|
191
|
+
return this.clientSession.coordinator.sessionId
|
|
192
|
+
}
|
|
193
|
+
|
|
298
194
|
/**
|
|
299
195
|
* Subscribe to the results of a query
|
|
300
196
|
* Returns a function to cancel the subscription.
|
|
@@ -491,6 +387,7 @@ export class Store<
|
|
|
491
387
|
|
|
492
388
|
return res
|
|
493
389
|
}
|
|
390
|
+
// #endregion mutate
|
|
494
391
|
|
|
495
392
|
/**
|
|
496
393
|
* This can be used in combination with `skipRefresh` when applying mutations.
|
|
@@ -510,6 +407,7 @@ export class Store<
|
|
|
510
407
|
)
|
|
511
408
|
}
|
|
512
409
|
|
|
410
|
+
// #region mutateWithoutRefresh
|
|
513
411
|
/**
|
|
514
412
|
* Apply a mutation to the store.
|
|
515
413
|
* Returns the tables that were affected by the event.
|
|
@@ -520,6 +418,7 @@ export class Store<
|
|
|
520
418
|
mutationEventDecoded_: MutationEvent.ForSchema<TSchema> | MutationEvent.PartialForSchema<TSchema>,
|
|
521
419
|
options: {
|
|
522
420
|
otelContext: otel.Context
|
|
421
|
+
// TODO adjust `skip-persist` with new rebase sync strategy
|
|
523
422
|
coordinatorMode: 'default' | 'skip-coordinator' | 'skip-persist'
|
|
524
423
|
},
|
|
525
424
|
): { writeTables: ReadonlySet<string>; durationMs: number } => {
|
|
@@ -527,12 +426,18 @@ export class Store<
|
|
|
527
426
|
this.schema.mutations.get(mutationEventDecoded_.mutation) ??
|
|
528
427
|
shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded_.mutation}`)
|
|
529
428
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
429
|
+
// Needs to happen only for partial mutation events (thus a function)
|
|
430
|
+
const nextMutationEventId = () => {
|
|
431
|
+
const { id, parentId } = this.clientSession.coordinator
|
|
432
|
+
.nextMutationEventIdPair({ localOnly: mutationDef.options.localOnly })
|
|
433
|
+
.pipe(Effect.runSync)
|
|
434
|
+
|
|
435
|
+
return { id, parentId }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const mutationEventDecoded: MutationEvent.ForSchema<TSchema> = isPartialMutationEvent(mutationEventDecoded_)
|
|
439
|
+
? { ...mutationEventDecoded_, ...nextMutationEventId() }
|
|
440
|
+
: mutationEventDecoded_
|
|
536
441
|
|
|
537
442
|
// NOTE we also need this temporary workaround here since some code-paths use `mutateWithoutRefresh` directly
|
|
538
443
|
// e.g. the row-query functionality
|
|
@@ -560,6 +465,8 @@ export class Store<
|
|
|
560
465
|
const allWriteTables = new Set<string>()
|
|
561
466
|
let durationMsTotal = 0
|
|
562
467
|
|
|
468
|
+
replaceSessionIdSymbol(mutationEventDecoded.args, this.clientSession.coordinator.sessionId)
|
|
469
|
+
|
|
563
470
|
const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
|
|
564
471
|
|
|
565
472
|
for (const {
|
|
@@ -579,7 +486,7 @@ export class Store<
|
|
|
579
486
|
|
|
580
487
|
if (coordinatorMode !== 'skip-coordinator') {
|
|
581
488
|
// Asynchronously apply mutation to a persistent storage (we're not awaiting this promise here)
|
|
582
|
-
this.
|
|
489
|
+
this.clientSession.coordinator
|
|
583
490
|
.mutate(mutationEventEncoded as MutationEvent.AnyEncoded, { persisted: coordinatorMode !== 'skip-persist' })
|
|
584
491
|
.pipe(this.runEffectFork)
|
|
585
492
|
}
|
|
@@ -593,7 +500,7 @@ export class Store<
|
|
|
593
500
|
},
|
|
594
501
|
)
|
|
595
502
|
}
|
|
596
|
-
// #endregion
|
|
503
|
+
// #endregion mutateWithoutRefresh
|
|
597
504
|
|
|
598
505
|
/**
|
|
599
506
|
* Directly execute a SQL query on the Store.
|
|
@@ -608,24 +515,13 @@ export class Store<
|
|
|
608
515
|
) => {
|
|
609
516
|
this.syncDbWrapper.execute(query, prepareBindValues(params, query), writeTables, { otelContext })
|
|
610
517
|
|
|
611
|
-
this.
|
|
518
|
+
this.clientSession.coordinator.execute(query, prepareBindValues(params, query)).pipe(this.runEffectFork)
|
|
612
519
|
}
|
|
613
520
|
|
|
614
521
|
__select = (query: string, params: ParamsObject = {}) => {
|
|
615
522
|
return this.syncDbWrapper.select(query, { bindValues: prepareBindValues(params, query) })
|
|
616
523
|
}
|
|
617
524
|
|
|
618
|
-
private getNextMutationEventId = ({ localOnly }: { localOnly: boolean }): { id: EventId; parentId: EventId } => {
|
|
619
|
-
const id = this.adapter.coordinator.getNextMutationEventId({ localOnly }).pipe(Effect.runSync)
|
|
620
|
-
const parentId = localOnly
|
|
621
|
-
? this.currentMutationEventIdRef.current
|
|
622
|
-
: { global: this.currentMutationEventIdRef.current.global, local: 0 }
|
|
623
|
-
|
|
624
|
-
this.currentMutationEventIdRef.current = id
|
|
625
|
-
|
|
626
|
-
return { id, parentId }
|
|
627
|
-
}
|
|
628
|
-
|
|
629
525
|
private makeTableRef = (tableName: string) =>
|
|
630
526
|
this.reactivityGraph.makeRef(null, {
|
|
631
527
|
equal: () => false,
|
|
@@ -640,14 +536,16 @@ export class Store<
|
|
|
640
536
|
|
|
641
537
|
__devDownloadMutationLogDb = () =>
|
|
642
538
|
Effect.gen(this, function* () {
|
|
643
|
-
const data = yield* this.
|
|
539
|
+
const data = yield* this.clientSession.coordinator.getMutationLogData
|
|
644
540
|
downloadBlob(data, `livestore-mutationlog-${Date.now()}.db`)
|
|
645
541
|
}).pipe(this.runEffectFork)
|
|
646
542
|
|
|
543
|
+
__devCurrentMutationEventId = () => this.clientSession.coordinator.getCurrentMutationEventId.pipe(Effect.runSync)
|
|
544
|
+
|
|
647
545
|
// NOTE This is needed because when booting a Store via Effect it seems to call `toJSON` in the error path
|
|
648
546
|
toJSON = () => {
|
|
649
547
|
return {
|
|
650
|
-
_tag: 'Store',
|
|
548
|
+
_tag: 'livestore.Store',
|
|
651
549
|
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults: true }),
|
|
652
550
|
}
|
|
653
551
|
}
|
|
@@ -655,260 +553,3 @@ export class Store<
|
|
|
655
553
|
private runEffectFork = <A, E>(effect: Effect.Effect<A, E, never>) =>
|
|
656
554
|
effect.pipe(Effect.tapCauseLogPretty, FiberSet.run(this.fiberSet), Runtime.runFork(this.runtime))
|
|
657
555
|
}
|
|
658
|
-
|
|
659
|
-
export type CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema> = {
|
|
660
|
-
schema: TSchema
|
|
661
|
-
adapter: StoreAdapterFactory
|
|
662
|
-
storeId: string
|
|
663
|
-
reactivityGraph?: ReactivityGraph
|
|
664
|
-
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
665
|
-
otelOptions?: Partial<OtelOptions>
|
|
666
|
-
boot?: (db: BootDb, parentSpan: otel.Span) => void | Promise<void> | Effect.Effect<void, unknown, otel.Tracer>
|
|
667
|
-
batchUpdates?: (run: () => void) => void
|
|
668
|
-
disableDevtools?: boolean
|
|
669
|
-
onBootStatus?: (status: BootStatus) => void
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/** Create a new LiveStore Store */
|
|
673
|
-
export const createStorePromise = async <
|
|
674
|
-
TGraphQLContext extends BaseGraphQLContext,
|
|
675
|
-
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
676
|
-
>({
|
|
677
|
-
signal,
|
|
678
|
-
...options
|
|
679
|
-
}: CreateStoreOptions<TGraphQLContext, TSchema> & { signal?: AbortSignal }): Promise<Store<TGraphQLContext, TSchema>> =>
|
|
680
|
-
Effect.gen(function* () {
|
|
681
|
-
const scope = yield* Scope.make()
|
|
682
|
-
const runtime = yield* Effect.runtime()
|
|
683
|
-
|
|
684
|
-
if (signal !== undefined) {
|
|
685
|
-
signal.addEventListener('abort', () => {
|
|
686
|
-
Scope.close(scope, Exit.void).pipe(Effect.tapCauseLogPretty, Runtime.runFork(runtime))
|
|
687
|
-
})
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
return yield* FiberSet.make().pipe(
|
|
691
|
-
Effect.andThen((fiberSet) => createStore({ ...options, fiberSet })),
|
|
692
|
-
Scope.extend(scope),
|
|
693
|
-
)
|
|
694
|
-
}).pipe(
|
|
695
|
-
Effect.withSpan('createStore'),
|
|
696
|
-
Effect.tapCauseLogPretty,
|
|
697
|
-
Effect.annotateLogs({ thread: 'window' }),
|
|
698
|
-
Effect.provide(Logger.pretty),
|
|
699
|
-
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
700
|
-
Effect.runPromise,
|
|
701
|
-
)
|
|
702
|
-
|
|
703
|
-
// #region createStore
|
|
704
|
-
export const createStore = <
|
|
705
|
-
TGraphQLContext extends BaseGraphQLContext,
|
|
706
|
-
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
707
|
-
>({
|
|
708
|
-
schema,
|
|
709
|
-
adapter: adapterFactory,
|
|
710
|
-
storeId,
|
|
711
|
-
graphQLOptions,
|
|
712
|
-
otelOptions,
|
|
713
|
-
boot,
|
|
714
|
-
reactivityGraph = globalReactivityGraph,
|
|
715
|
-
batchUpdates,
|
|
716
|
-
disableDevtools,
|
|
717
|
-
onBootStatus,
|
|
718
|
-
fiberSet,
|
|
719
|
-
}: CreateStoreOptions<TGraphQLContext, TSchema> & { fiberSet: FiberSet.FiberSet }): Effect.Effect<
|
|
720
|
-
Store<TGraphQLContext, TSchema>,
|
|
721
|
-
UnexpectedError,
|
|
722
|
-
Scope.Scope
|
|
723
|
-
> => {
|
|
724
|
-
const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
|
|
725
|
-
const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
|
|
726
|
-
|
|
727
|
-
const TracingLive = Layer.unwrapEffect(Effect.map(OtelTracer.make, Layer.setTracer)).pipe(
|
|
728
|
-
Layer.provide(Layer.sync(OtelTracer.Tracer, () => otelTracer)),
|
|
729
|
-
)
|
|
730
|
-
|
|
731
|
-
return Effect.gen(function* () {
|
|
732
|
-
const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie)
|
|
733
|
-
|
|
734
|
-
const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
|
|
735
|
-
|
|
736
|
-
yield* Queue.take(bootStatusQueue).pipe(
|
|
737
|
-
Effect.tapSync((status) => onBootStatus?.(status)),
|
|
738
|
-
Effect.tap((status) => (status.stage === 'done' ? Queue.shutdown(bootStatusQueue) : Effect.void)),
|
|
739
|
-
Effect.forever,
|
|
740
|
-
Effect.tapCauseLogPretty,
|
|
741
|
-
Effect.forkScoped,
|
|
742
|
-
)
|
|
743
|
-
|
|
744
|
-
const storeDeferred = yield* Deferred.make<Store>()
|
|
745
|
-
|
|
746
|
-
const connectDevtoolsToStore_ = (storeDevtoolsChannel: StoreDevtoolsChannel) =>
|
|
747
|
-
Effect.gen(function* () {
|
|
748
|
-
const store = yield* Deferred.await(storeDeferred)
|
|
749
|
-
yield* connectDevtoolsToStore({ storeDevtoolsChannel, store })
|
|
750
|
-
})
|
|
751
|
-
|
|
752
|
-
const runtime = yield* Effect.runtime<Scope.Scope>()
|
|
753
|
-
|
|
754
|
-
const runEffectFork = (effect: Effect.Effect<any, any, never>) =>
|
|
755
|
-
effect.pipe(Effect.tapCauseLogPretty, FiberSet.run(fiberSet), Runtime.runFork(runtime))
|
|
756
|
-
|
|
757
|
-
// TODO close parent scope? (Needs refactor with Mike A)
|
|
758
|
-
const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) =>
|
|
759
|
-
Effect.gen(function* () {
|
|
760
|
-
// NOTE we're calling `cause.toString()` here to avoid triggering a `console.error` in the grouped log
|
|
761
|
-
const logCause =
|
|
762
|
-
Cause.isFailType(cause) && cause.error._tag === 'LiveStore.IntentionalShutdownCause'
|
|
763
|
-
? cause.toString()
|
|
764
|
-
: cause
|
|
765
|
-
yield* Effect.logDebug(`Shutting down LiveStore`, logCause)
|
|
766
|
-
|
|
767
|
-
FiberSet.clear(fiberSet).pipe(
|
|
768
|
-
Effect.andThen(() => FiberSet.run(fiberSet, Effect.failCause(cause))),
|
|
769
|
-
Effect.timeout(Duration.seconds(1)),
|
|
770
|
-
Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown:clear-fiber-set', duration: 500 }),
|
|
771
|
-
Effect.catchTag('TimeoutException', (err) =>
|
|
772
|
-
Effect.logError('Store shutdown timed out. Forcing shutdown.', err).pipe(
|
|
773
|
-
Effect.andThen(FiberSet.run(fiberSet, Effect.failCause(cause))),
|
|
774
|
-
),
|
|
775
|
-
),
|
|
776
|
-
Runtime.runFork(runtime), // NOTE we need to fork this separately otherwise it will also be interrupted
|
|
777
|
-
)
|
|
778
|
-
}).pipe(Effect.withSpan('livestore:shutdown'))
|
|
779
|
-
|
|
780
|
-
const adapter: StoreAdapter = yield* adapterFactory({
|
|
781
|
-
schema,
|
|
782
|
-
storeId,
|
|
783
|
-
devtoolsEnabled: disableDevtools !== true,
|
|
784
|
-
bootStatusQueue,
|
|
785
|
-
shutdown,
|
|
786
|
-
connectDevtoolsToStore: connectDevtoolsToStore_,
|
|
787
|
-
}).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
|
|
788
|
-
|
|
789
|
-
const mutationEventSchema = makeMutationEventSchemaMemo(schema)
|
|
790
|
-
|
|
791
|
-
// TODO get rid of this
|
|
792
|
-
// const __processedMutationIds = new Set<number>()
|
|
793
|
-
|
|
794
|
-
const currentMutationEventIdRef = { current: yield* adapter.coordinator.getCurrentMutationEventId }
|
|
795
|
-
|
|
796
|
-
// TODO fill up with unsynced mutation events from the coordinator
|
|
797
|
-
const unsyncedMutationEvents = MutableHashMap.empty<EventId, MutationEvent.ForSchema<TSchema>>()
|
|
798
|
-
|
|
799
|
-
// TODO consider moving booting into the storage backend
|
|
800
|
-
if (boot !== undefined) {
|
|
801
|
-
let isInTxn = false
|
|
802
|
-
let txnExecuteStmnts: [string, PreparedBindValues | undefined][] = []
|
|
803
|
-
|
|
804
|
-
const bootDbImpl: BootDb = {
|
|
805
|
-
_tag: 'BootDb',
|
|
806
|
-
execute: (queryStr, bindValues) => {
|
|
807
|
-
const stmt = adapter.syncDb.prepare(queryStr)
|
|
808
|
-
stmt.execute(bindValues)
|
|
809
|
-
|
|
810
|
-
if (isInTxn === true) {
|
|
811
|
-
txnExecuteStmnts.push([queryStr, bindValues])
|
|
812
|
-
} else {
|
|
813
|
-
adapter.coordinator.execute(queryStr, bindValues).pipe(runEffectFork)
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
mutate: (...list) => {
|
|
817
|
-
for (const mutationEventDecoded_ of list) {
|
|
818
|
-
const mutationDef =
|
|
819
|
-
schema.mutations.get(mutationEventDecoded_.mutation) ??
|
|
820
|
-
shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded_.mutation}`)
|
|
821
|
-
|
|
822
|
-
const parentId = mutationDef.options.localOnly
|
|
823
|
-
? currentMutationEventIdRef.current
|
|
824
|
-
: { global: currentMutationEventIdRef.current.global, local: 0 }
|
|
825
|
-
|
|
826
|
-
currentMutationEventIdRef.current = adapter.coordinator
|
|
827
|
-
.getNextMutationEventId({ localOnly: mutationDef.options.localOnly })
|
|
828
|
-
.pipe(Effect.runSync)
|
|
829
|
-
|
|
830
|
-
const mutationEventDecoded = { ...mutationEventDecoded_, id: currentMutationEventIdRef.current, parentId }
|
|
831
|
-
|
|
832
|
-
MutableHashMap.set(unsyncedMutationEvents, Data.struct(mutationEventDecoded.id), mutationEventDecoded)
|
|
833
|
-
|
|
834
|
-
// __processedMutationIds.add(mutationEventDecoded.id.global)
|
|
835
|
-
|
|
836
|
-
const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
|
|
837
|
-
// const { bindValues, statementSql } = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
|
|
838
|
-
|
|
839
|
-
for (const { statementSql, bindValues } of execArgsArr) {
|
|
840
|
-
adapter.syncDb.execute(statementSql, bindValues)
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded)
|
|
844
|
-
|
|
845
|
-
adapter.coordinator
|
|
846
|
-
.mutate(mutationEventEncoded as MutationEvent.AnyEncoded, { persisted: true })
|
|
847
|
-
.pipe(runEffectFork)
|
|
848
|
-
}
|
|
849
|
-
},
|
|
850
|
-
select: (queryStr, bindValues) => {
|
|
851
|
-
const stmt = adapter.syncDb.prepare(queryStr)
|
|
852
|
-
return stmt.select(bindValues)
|
|
853
|
-
},
|
|
854
|
-
txn: (callback) => {
|
|
855
|
-
try {
|
|
856
|
-
isInTxn = true
|
|
857
|
-
// adapter.syncDb.execute('BEGIN TRANSACTION', undefined)
|
|
858
|
-
|
|
859
|
-
callback()
|
|
860
|
-
|
|
861
|
-
// adapter.syncDb.execute('COMMIT', undefined)
|
|
862
|
-
|
|
863
|
-
// adapter.coordinator.execute('BEGIN', undefined, undefined)
|
|
864
|
-
for (const [queryStr, bindValues] of txnExecuteStmnts) {
|
|
865
|
-
adapter.coordinator.execute(queryStr, bindValues).pipe(runEffectFork)
|
|
866
|
-
}
|
|
867
|
-
// adapter.coordinator.execute('COMMIT', undefined, undefined)
|
|
868
|
-
} catch (e: any) {
|
|
869
|
-
// adapter.syncDb.execute('ROLLBACK', undefined)
|
|
870
|
-
throw e
|
|
871
|
-
} finally {
|
|
872
|
-
isInTxn = false
|
|
873
|
-
txnExecuteStmnts = []
|
|
874
|
-
}
|
|
875
|
-
},
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
yield* Effect.tryAll(() => boot(bootDbImpl, span)).pipe(
|
|
879
|
-
UnexpectedError.mapToUnexpectedError,
|
|
880
|
-
Effect.withSpan('createStore:boot'),
|
|
881
|
-
)
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const store = Store.createStore<TGraphQLContext, TSchema>(
|
|
885
|
-
{
|
|
886
|
-
adapter,
|
|
887
|
-
schema,
|
|
888
|
-
graphQLOptions,
|
|
889
|
-
otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
|
|
890
|
-
reactivityGraph,
|
|
891
|
-
disableDevtools,
|
|
892
|
-
currentMutationEventIdRef,
|
|
893
|
-
unsyncedMutationEvents,
|
|
894
|
-
fiberSet,
|
|
895
|
-
runtime,
|
|
896
|
-
batchUpdates: batchUpdates ?? ((run) => run()),
|
|
897
|
-
storeId,
|
|
898
|
-
},
|
|
899
|
-
span,
|
|
900
|
-
)
|
|
901
|
-
|
|
902
|
-
yield* Deferred.succeed(storeDeferred, store as any as Store)
|
|
903
|
-
|
|
904
|
-
return store
|
|
905
|
-
}).pipe(
|
|
906
|
-
Effect.withSpan('createStore', {
|
|
907
|
-
parent: otelOptions?.rootSpanContext
|
|
908
|
-
? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext)!)
|
|
909
|
-
: undefined,
|
|
910
|
-
}),
|
|
911
|
-
Effect.provide(TracingLive),
|
|
912
|
-
)
|
|
913
|
-
}
|
|
914
|
-
// #endregion createStore
|
package/src/utils/dev.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
export const originalStackLimit = Error.stackTraceLimit
|
|
4
|
-
|
|
5
1
|
export type StackInfo = {
|
|
6
2
|
frames: StackFrame[]
|
|
7
3
|
}
|
|
@@ -54,11 +50,5 @@ export const extractStackInfoFromStackTrace = (stackTrace: string): StackInfo =>
|
|
|
54
50
|
return { frames }
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
export const
|
|
58
|
-
|
|
59
|
-
Error.stackTraceLimit = 10
|
|
60
|
-
// eslint-disable-next-line unicorn/error-message
|
|
61
|
-
const stack = new Error().stack!
|
|
62
|
-
Error.stackTraceLimit = originalStackLimit
|
|
63
|
-
return extractStackInfoFromStackTrace(stack)
|
|
64
|
-
}, [])
|
|
53
|
+
export const stackInfoToString = (stackInfo: StackInfo): string =>
|
|
54
|
+
stackInfo.frames.map((f) => `${f.name} (${f.filePath})`).join('\n')
|