@livestore/livestore 0.0.58-dev.9 → 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.
Files changed (191) hide show
  1. package/README.md +1 -117
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/effect/LiveStore.d.ts +6 -6
  4. package/dist/effect/LiveStore.d.ts.map +1 -1
  5. package/dist/effect/LiveStore.js +1 -1
  6. package/dist/effect/LiveStore.js.map +1 -1
  7. package/dist/global-state.d.ts.map +1 -1
  8. package/dist/global-state.js +2 -1
  9. package/dist/global-state.js.map +1 -1
  10. package/dist/index.d.ts +11 -8
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/reactiveQueries/base-class.d.ts +5 -4
  15. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  16. package/dist/reactiveQueries/base-class.js.map +1 -1
  17. package/dist/reactiveQueries/computed.d.ts +35 -0
  18. package/dist/reactiveQueries/computed.d.ts.map +1 -0
  19. package/dist/reactiveQueries/computed.js +57 -0
  20. package/dist/reactiveQueries/computed.js.map +1 -0
  21. package/dist/reactiveQueries/graphql.d.ts +2 -1
  22. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  23. package/dist/reactiveQueries/graphql.js.map +1 -1
  24. package/dist/reactiveQueries/sql.d.ts +2 -2
  25. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  26. package/dist/reactiveQueries/sql.js +3 -3
  27. package/dist/reactiveQueries/sql.js.map +1 -1
  28. package/dist/reactiveQueries/sql.test.js +2 -2
  29. package/dist/reactiveQueries/sql.test.js.map +1 -1
  30. package/dist/row-query.d.ts +3 -2
  31. package/dist/row-query.d.ts.map +1 -1
  32. package/dist/row-query.js +14 -10
  33. package/dist/row-query.js.map +1 -1
  34. package/dist/store/create-store.d.ts +28 -0
  35. package/dist/store/create-store.d.ts.map +1 -0
  36. package/dist/store/create-store.js +85 -0
  37. package/dist/store/create-store.js.map +1 -0
  38. package/dist/store/devtools.d.ts +19 -0
  39. package/dist/store/devtools.d.ts.map +1 -0
  40. package/dist/store/devtools.js +141 -0
  41. package/dist/store/devtools.js.map +1 -0
  42. package/dist/store/store-context.d.ts +26 -0
  43. package/dist/store/store-context.d.ts.map +1 -0
  44. package/dist/store/store-context.js +6 -0
  45. package/dist/store/store-context.js.map +1 -0
  46. package/dist/store/store-types.d.ts +98 -0
  47. package/dist/store/store-types.d.ts.map +1 -0
  48. package/dist/store/store-types.js +6 -0
  49. package/dist/store/store-types.js.map +1 -0
  50. package/dist/store/store.d.ts +88 -0
  51. package/dist/store/store.d.ts.map +1 -0
  52. package/dist/store/store.js +367 -0
  53. package/dist/store/store.js.map +1 -0
  54. package/dist/store-devtools.d.ts +2 -2
  55. package/dist/store-devtools.d.ts.map +1 -1
  56. package/dist/store-devtools.js +2 -2
  57. package/dist/store-devtools.js.map +1 -1
  58. package/dist/store.d.ts +8 -8
  59. package/dist/store.d.ts.map +1 -1
  60. package/dist/store.js +54 -54
  61. package/dist/store.js.map +1 -1
  62. package/dist/utils/dev.d.ts +1 -0
  63. package/dist/utils/dev.d.ts.map +1 -1
  64. package/dist/utils/dev.js +5 -0
  65. package/dist/utils/dev.js.map +1 -1
  66. package/dist/{react/utils → utils}/stack-info.d.ts +1 -2
  67. package/dist/utils/stack-info.d.ts.map +1 -0
  68. package/dist/{react/utils → utils}/stack-info.js +1 -9
  69. package/dist/utils/stack-info.js.map +1 -0
  70. package/dist/utils/stack-info.test.d.ts.map +1 -0
  71. package/dist/{__tests__/react/utils → utils}/stack-info.test.js +1 -1
  72. package/dist/utils/stack-info.test.js.map +1 -0
  73. package/dist/utils/tests/fixture.d.ts +259 -0
  74. package/dist/utils/tests/fixture.d.ts.map +1 -0
  75. package/dist/utils/tests/fixture.js +32 -0
  76. package/dist/utils/tests/fixture.js.map +1 -0
  77. package/dist/utils/tests/mod.d.ts +3 -0
  78. package/dist/utils/tests/mod.d.ts.map +1 -0
  79. package/dist/utils/tests/mod.js +3 -0
  80. package/dist/utils/tests/mod.js.map +1 -0
  81. package/dist/utils/tests/otel.d.ts.map +1 -0
  82. package/dist/{__tests__/react/utils → utils/tests}/otel.js +3 -3
  83. package/dist/utils/tests/otel.js.map +1 -0
  84. package/package.json +13 -20
  85. package/src/ambient.d.ts +3 -1
  86. package/src/effect/LiveStore.ts +7 -7
  87. package/src/global-state.ts +5 -1
  88. package/src/index.ts +19 -7
  89. package/src/reactiveQueries/base-class.ts +5 -4
  90. package/src/reactiveQueries/{js.ts → computed.ts} +3 -3
  91. package/src/reactiveQueries/graphql.ts +2 -1
  92. package/src/reactiveQueries/sql.test.ts +2 -2
  93. package/src/reactiveQueries/sql.ts +5 -5
  94. package/src/row-query.ts +33 -17
  95. package/src/store/create-store.ts +214 -0
  96. package/src/{store-devtools.ts → store/devtools.ts} +9 -9
  97. package/src/store/store-types.ts +110 -0
  98. package/src/{store.ts → store/store.ts} +56 -415
  99. package/src/utils/dev.ts +6 -0
  100. package/src/{__tests__/react/utils → utils}/stack-info.test.ts +1 -1
  101. package/src/{react/utils → utils}/stack-info.ts +2 -12
  102. package/src/utils/tests/fixture.ts +73 -0
  103. package/src/utils/tests/mod.ts +2 -0
  104. package/src/{__tests__/react/utils → utils/tests}/otel.ts +4 -4
  105. package/tsconfig.json +1 -2
  106. package/vitest.config.js +0 -8
  107. package/dist/__tests__/react/fixture.d.ts +0 -461
  108. package/dist/__tests__/react/fixture.d.ts.map +0 -1
  109. package/dist/__tests__/react/fixture.js +0 -68
  110. package/dist/__tests__/react/fixture.js.map +0 -1
  111. package/dist/__tests__/react/utils/otel.d.ts.map +0 -1
  112. package/dist/__tests__/react/utils/otel.js.map +0 -1
  113. package/dist/__tests__/react/utils/stack-info.test.d.ts.map +0 -1
  114. package/dist/__tests__/react/utils/stack-info.test.js.map +0 -1
  115. package/dist/react/LiveStoreContext.d.ts +0 -7
  116. package/dist/react/LiveStoreContext.d.ts.map +0 -1
  117. package/dist/react/LiveStoreContext.js +0 -13
  118. package/dist/react/LiveStoreContext.js.map +0 -1
  119. package/dist/react/LiveStoreProvider.d.ts +0 -49
  120. package/dist/react/LiveStoreProvider.d.ts.map +0 -1
  121. package/dist/react/LiveStoreProvider.js +0 -169
  122. package/dist/react/LiveStoreProvider.js.map +0 -1
  123. package/dist/react/LiveStoreProvider.test.d.ts +0 -2
  124. package/dist/react/LiveStoreProvider.test.d.ts.map +0 -1
  125. package/dist/react/LiveStoreProvider.test.js +0 -62
  126. package/dist/react/LiveStoreProvider.test.js.map +0 -1
  127. package/dist/react/components/LiveList.d.ts +0 -21
  128. package/dist/react/components/LiveList.d.ts.map +0 -1
  129. package/dist/react/components/LiveList.js +0 -31
  130. package/dist/react/components/LiveList.js.map +0 -1
  131. package/dist/react/index.d.ts +0 -11
  132. package/dist/react/index.d.ts.map +0 -1
  133. package/dist/react/index.js +0 -10
  134. package/dist/react/index.js.map +0 -1
  135. package/dist/react/useAtom.d.ts +0 -10
  136. package/dist/react/useAtom.d.ts.map +0 -1
  137. package/dist/react/useAtom.js +0 -37
  138. package/dist/react/useAtom.js.map +0 -1
  139. package/dist/react/useLocalId.d.ts +0 -10
  140. package/dist/react/useLocalId.d.ts.map +0 -1
  141. package/dist/react/useLocalId.js +0 -22
  142. package/dist/react/useLocalId.js.map +0 -1
  143. package/dist/react/useQuery.d.ts +0 -9
  144. package/dist/react/useQuery.d.ts.map +0 -1
  145. package/dist/react/useQuery.js +0 -70
  146. package/dist/react/useQuery.js.map +0 -1
  147. package/dist/react/useQuery.test.d.ts +0 -2
  148. package/dist/react/useQuery.test.d.ts.map +0 -1
  149. package/dist/react/useQuery.test.js +0 -51
  150. package/dist/react/useQuery.test.js.map +0 -1
  151. package/dist/react/useRow.d.ts +0 -46
  152. package/dist/react/useRow.d.ts.map +0 -1
  153. package/dist/react/useRow.js +0 -94
  154. package/dist/react/useRow.js.map +0 -1
  155. package/dist/react/useRow.test.d.ts +0 -2
  156. package/dist/react/useRow.test.d.ts.map +0 -1
  157. package/dist/react/useRow.test.js +0 -208
  158. package/dist/react/useRow.test.js.map +0 -1
  159. package/dist/react/useTemporaryQuery.d.ts +0 -22
  160. package/dist/react/useTemporaryQuery.d.ts.map +0 -1
  161. package/dist/react/useTemporaryQuery.js +0 -75
  162. package/dist/react/useTemporaryQuery.js.map +0 -1
  163. package/dist/react/useTemporaryQuery.test.d.ts +0 -2
  164. package/dist/react/useTemporaryQuery.test.d.ts.map +0 -1
  165. package/dist/react/useTemporaryQuery.test.js +0 -59
  166. package/dist/react/useTemporaryQuery.test.js.map +0 -1
  167. package/dist/react/utils/stack-info.d.ts.map +0 -1
  168. package/dist/react/utils/stack-info.js.map +0 -1
  169. package/dist/react/utils/useStateRefWithReactiveInput.d.ts +0 -13
  170. package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +0 -1
  171. package/dist/react/utils/useStateRefWithReactiveInput.js +0 -38
  172. package/dist/react/utils/useStateRefWithReactiveInput.js.map +0 -1
  173. package/src/__tests__/react/fixture.tsx +0 -126
  174. package/src/react/LiveStoreContext.ts +0 -20
  175. package/src/react/LiveStoreProvider.test.tsx +0 -109
  176. package/src/react/LiveStoreProvider.tsx +0 -291
  177. package/src/react/__snapshots__/useRow.test.tsx.snap +0 -359
  178. package/src/react/components/LiveList.tsx +0 -84
  179. package/src/react/index.ts +0 -19
  180. package/src/react/useAtom.ts +0 -55
  181. package/src/react/useLocalId.ts +0 -34
  182. package/src/react/useQuery.test.tsx +0 -82
  183. package/src/react/useQuery.ts +0 -106
  184. package/src/react/useRow.test.tsx +0 -345
  185. package/src/react/useRow.ts +0 -180
  186. package/src/react/useTemporaryQuery.test.tsx +0 -98
  187. package/src/react/useTemporaryQuery.ts +0 -131
  188. package/src/react/utils/useStateRefWithReactiveInput.ts +0 -51
  189. package/src/store-context.ts +0 -23
  190. /package/dist/{__tests__/react/utils → utils}/stack-info.test.d.ts +0 -0
  191. /package/dist/{__tests__/react/utils → utils/tests}/otel.d.ts +0 -0
@@ -6,7 +6,7 @@ import * as otel from '@opentelemetry/api'
6
6
  import { globalReactivityGraph } from '../global-state.js'
7
7
  import type { Thunk } from '../reactive.js'
8
8
  import { NOT_REFRESHED_YET } from '../reactive.js'
9
- import type { RefreshReason } from '../store.js'
9
+ import type { RefreshReason } from '../store/store-types.js'
10
10
  import { getDurationMsFromSpan } from '../utils/otel.js'
11
11
  import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
12
12
  import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
@@ -79,7 +79,7 @@ export class LiveStoreSQLQuery<
79
79
  queryInfo,
80
80
  }: {
81
81
  label?: string
82
- genQueryString: string | ((get: GetAtomResult) => string)
82
+ genQueryString: string | ((get: GetAtomResult, ctx: QueryContext) => string)
83
83
  queriedTables?: Set<string>
84
84
  bindValues?: Bindable
85
85
  reactivityGraph?: ReactivityGraph
@@ -101,11 +101,11 @@ export class LiveStoreSQLQuery<
101
101
  let queryString$OrQueryString: string | Thunk<string, QueryContext, RefreshReason>
102
102
  if (typeof genQueryString === 'function') {
103
103
  queryString$OrQueryString = this.reactivityGraph.makeThunk(
104
- (get, setDebugInfo, { rootOtelContext }, otelContext) => {
104
+ (get, setDebugInfo, ctx, otelContext) => {
105
105
  const startMs = performance.now()
106
- const queryString = genQueryString(makeGetAtomResult(get, otelContext ?? rootOtelContext))
106
+ const queryString = genQueryString(makeGetAtomResult(get, otelContext ?? ctx.rootOtelContext), ctx)
107
107
  const durationMs = performance.now() - startMs
108
- setDebugInfo({ _tag: 'js', label: `${label}:queryString`, query: queryString, durationMs })
108
+ setDebugInfo({ _tag: 'computed', label: `${label}:queryString`, query: queryString, durationMs })
109
109
  return queryString
110
110
  },
111
111
  {
package/src/row-query.ts CHANGED
@@ -1,16 +1,22 @@
1
1
  import type { QueryInfoCol, QueryInfoNone, QueryInfoRow } from '@livestore/common'
2
- import { sql } from '@livestore/common'
2
+ import { SessionIdSymbol, sql } from '@livestore/common'
3
3
  import { DbSchema } from '@livestore/common/schema'
4
+ import type { SqliteDsl } from '@livestore/db-schema'
4
5
  import type { GetValForKey } from '@livestore/utils'
5
6
  import { shouldNeverHappen } from '@livestore/utils'
6
7
  import { Schema } from '@livestore/utils/effect'
7
8
  import type * as otel from '@opentelemetry/api'
8
- import type { SqliteDsl } from 'effect-db-schema'
9
9
 
10
- import type { LiveQuery, LiveQueryAny, QueryContext, ReactivityGraph } from './reactiveQueries/base-class.js'
11
- import { computed } from './reactiveQueries/js.js'
10
+ import type {
11
+ GetAtomResult,
12
+ LiveQuery,
13
+ LiveQueryAny,
14
+ QueryContext,
15
+ ReactivityGraph,
16
+ } from './reactiveQueries/base-class.js'
17
+ import { computed } from './reactiveQueries/computed.js'
12
18
  import { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
13
- import type { Store } from './store.js'
19
+ import type { Store } from './store/store.js'
14
20
 
15
21
  export type RowQueryOptions<TTableDef extends DbSchema.TableDef, TResult = RowResult<TTableDef>> = {
16
22
  otelContext?: otel.Context
@@ -46,7 +52,7 @@ export type MakeRowQuery = {
46
52
  >(
47
53
  table: TTableDef,
48
54
  // TODO adjust so it works with arbitrary primary keys or unique constraints
49
- id: string,
55
+ id: string | SessionIdSymbol,
50
56
  options?: RowQueryOptions<TTableDef, TResult> & RowQueryOptionsDefaulValues<TTableDef>,
51
57
  ): LiveQuery<TResult, QueryInfoRow<TTableDef>>
52
58
  }
@@ -54,15 +60,15 @@ export type MakeRowQuery = {
54
60
  // TODO also allow other where clauses and multiple rows
55
61
  export const rowQuery: MakeRowQuery = <TTableDef extends DbSchema.TableDef>(
56
62
  table: TTableDef,
57
- idOrOptions?: string | RowQueryOptions<TTableDef, any>,
63
+ idOrOptions?: string | SessionIdSymbol | RowQueryOptions<TTableDef, any>,
58
64
  options_?: RowQueryOptions<TTableDef, any> & RowQueryOptionsDefaulValues<TTableDef>,
59
65
  ) => {
60
- const id = typeof idOrOptions === 'string' ? idOrOptions : undefined
61
- const options = typeof idOrOptions === 'string' ? options_ : idOrOptions
66
+ const id = typeof idOrOptions === 'string' || idOrOptions === SessionIdSymbol ? idOrOptions : undefined
67
+ const options = typeof idOrOptions === 'string' || idOrOptions === SessionIdSymbol ? options_ : idOrOptions
62
68
  const defaultValues: Partial<RowResult<TTableDef>> | undefined = (options as any)?.defaultValues ?? {}
63
69
 
64
70
  // Validate query args
65
- if (table.options.isSingleton === true && id !== undefined) {
71
+ if (table.options.isSingleton === true && id !== undefined && id !== SessionIdSymbol) {
66
72
  shouldNeverHappen(`Cannot query state table ${table.sqliteDef.name} with id "${id}" as it is a singleton`)
67
73
  } else if (table.options.isSingleton !== true && id === undefined) {
68
74
  shouldNeverHappen(`Cannot query state table ${table.sqliteDef.name} without id`)
@@ -71,14 +77,21 @@ export const rowQuery: MakeRowQuery = <TTableDef extends DbSchema.TableDef>(
71
77
  const tableSchema = table.sqliteDef
72
78
  const tableName = tableSchema.name
73
79
 
74
- const whereClause = id === undefined ? '' : `where id = '${id}'`
75
- const queryStr = sql`select * from ${tableName} ${whereClause} limit 1`
80
+ const makeQueryString = (id: string | undefined) =>
81
+ sql`select * from ${tableName} ${id === undefined ? '' : `where id = '${id}'`} limit 1`
82
+
83
+ const genQueryString =
84
+ id === SessionIdSymbol
85
+ ? (_: GetAtomResult, ctx: QueryContext) => makeQueryString(ctx.store.sessionId)
86
+ : makeQueryString(id)
76
87
 
77
88
  const rowSchema = table.isSingleColumn === true ? table.schema.pipe(Schema.pluck('value' as any)) : table.schema
78
89
 
79
90
  return new LiveStoreSQLQuery({
80
- label: options?.label ?? `rowQuery:query:${tableSchema.name}${id === undefined ? '' : `:${id}`}`,
81
- genQueryString: queryStr,
91
+ label:
92
+ options?.label ??
93
+ `rowQuery:query:${tableSchema.name}${id === undefined ? '' : id === SessionIdSymbol ? `:sessionId` : `:${id}`}`,
94
+ genQueryString,
82
95
  queriedTables: new Set([tableName]),
83
96
  reactivityGraph: options?.reactivityGraph,
84
97
  // While this code-path is not needed for singleton tables, it's still needed for `useRow` with non-existing rows for a given ID
@@ -130,7 +143,7 @@ const makeExecBeforeFirstRun =
130
143
  otelContext: otelContext_,
131
144
  table,
132
145
  }: {
133
- id?: string
146
+ id?: string | SessionIdSymbol
134
147
  defaultValues?: any
135
148
  skipInsertDefaultRow?: boolean
136
149
  otelContext?: otel.Context
@@ -158,12 +171,15 @@ const insertRowWithDefaultValuesOrIgnore = ({
158
171
  explicitDefaultValues,
159
172
  }: {
160
173
  store: Store
161
- id: string
174
+ id: string | SessionIdSymbol
162
175
  table: DbSchema.TableDef
163
176
  otelContext: otel.Context
164
177
  explicitDefaultValues: Partial<RowResult<DbSchema.TableDef>> | undefined
165
178
  }) => {
166
- const rowExists = store.syncDbWrapper.select(`select 1 from ${table.sqliteDef.name} where id = '${id}'`).length === 1
179
+ const idStr = id === SessionIdSymbol ? store.sessionId : id
180
+ const rowExists =
181
+ store.syncDbWrapper.select(`select 1 from ${table.sqliteDef.name} where id = '${idStr}'`).length === 1
182
+
167
183
  if (rowExists) return
168
184
 
169
185
  // const mutationDef = deriveCreateMutationDef(table)
@@ -0,0 +1,214 @@
1
+ import type {
2
+ Adapter,
3
+ BootStatus,
4
+ ClientSession,
5
+ EventId,
6
+ IntentionalShutdownCause,
7
+ StoreDevtoolsChannel,
8
+ } from '@livestore/common'
9
+ import { UnexpectedError } from '@livestore/common'
10
+ import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
11
+ import { makeNoopTracer } from '@livestore/utils'
12
+ import {
13
+ Cause,
14
+ Deferred,
15
+ Duration,
16
+ Effect,
17
+ Exit,
18
+ FiberSet,
19
+ Layer,
20
+ Logger,
21
+ LogLevel,
22
+ MutableHashMap,
23
+ OtelTracer,
24
+ Queue,
25
+ Runtime,
26
+ Scope,
27
+ } from '@livestore/utils/effect'
28
+ import * as otel from '@opentelemetry/api'
29
+
30
+ import { globalReactivityGraph } from '../global-state.js'
31
+ import type { ReactivityGraph } from '../reactiveQueries/base-class.js'
32
+ import { connectDevtoolsToStore } from './devtools.js'
33
+ import { Store } from './store.js'
34
+ import type { BaseGraphQLContext, GraphQLOptions, OtelOptions } from './store-types.js'
35
+
36
+ export type CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema> = {
37
+ schema: TSchema
38
+ adapter: Adapter
39
+ storeId: string
40
+ reactivityGraph?: ReactivityGraph
41
+ graphQLOptions?: GraphQLOptions<TGraphQLContext>
42
+ otelOptions?: Partial<OtelOptions>
43
+ boot?: (
44
+ store: Store<TGraphQLContext, TSchema>,
45
+ parentSpan: otel.Span,
46
+ ) => void | Promise<void> | Effect.Effect<void, unknown, otel.Tracer>
47
+ batchUpdates?: (run: () => void) => void
48
+ disableDevtools?: boolean
49
+ onBootStatus?: (status: BootStatus) => void
50
+ }
51
+
52
+ /** Create a new LiveStore Store */
53
+ export const createStorePromise = async <
54
+ TGraphQLContext extends BaseGraphQLContext,
55
+ TSchema extends LiveStoreSchema = LiveStoreSchema,
56
+ >({
57
+ signal,
58
+ ...options
59
+ }: CreateStoreOptions<TGraphQLContext, TSchema> & { signal?: AbortSignal }): Promise<Store<TGraphQLContext, TSchema>> =>
60
+ Effect.gen(function* () {
61
+ const scope = yield* Scope.make()
62
+ const runtime = yield* Effect.runtime()
63
+
64
+ if (signal !== undefined) {
65
+ signal.addEventListener('abort', () => {
66
+ Scope.close(scope, Exit.void).pipe(Effect.tapCauseLogPretty, Runtime.runFork(runtime))
67
+ })
68
+ }
69
+
70
+ return yield* FiberSet.make().pipe(
71
+ Effect.andThen((fiberSet) => createStore({ ...options, fiberSet })),
72
+ Scope.extend(scope),
73
+ )
74
+ }).pipe(
75
+ Effect.withSpan('createStore'),
76
+ Effect.tapCauseLogPretty,
77
+ Effect.annotateLogs({ thread: 'window' }),
78
+ Effect.provide(Logger.pretty),
79
+ Logger.withMinimumLogLevel(LogLevel.Debug),
80
+ Effect.runPromise,
81
+ )
82
+
83
+ export const createStore = <
84
+ TGraphQLContext extends BaseGraphQLContext,
85
+ TSchema extends LiveStoreSchema = LiveStoreSchema,
86
+ >({
87
+ schema,
88
+ adapter,
89
+ storeId,
90
+ graphQLOptions,
91
+ otelOptions,
92
+ boot,
93
+ reactivityGraph = globalReactivityGraph,
94
+ batchUpdates,
95
+ disableDevtools,
96
+ onBootStatus,
97
+ fiberSet,
98
+ }: CreateStoreOptions<TGraphQLContext, TSchema> & { fiberSet: FiberSet.FiberSet }): Effect.Effect<
99
+ Store<TGraphQLContext, TSchema>,
100
+ UnexpectedError,
101
+ Scope.Scope
102
+ > => {
103
+ const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
104
+ const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
105
+
106
+ const TracingLive = Layer.unwrapEffect(Effect.map(OtelTracer.make, Layer.setTracer)).pipe(
107
+ Layer.provide(Layer.sync(OtelTracer.Tracer, () => otelTracer)),
108
+ )
109
+
110
+ return Effect.gen(function* () {
111
+ const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie)
112
+
113
+ const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
114
+
115
+ yield* Queue.take(bootStatusQueue).pipe(
116
+ Effect.tapSync((status) => onBootStatus?.(status)),
117
+ Effect.tap((status) => (status.stage === 'done' ? Queue.shutdown(bootStatusQueue) : Effect.void)),
118
+ Effect.forever,
119
+ Effect.tapCauseLogPretty,
120
+ Effect.forkScoped,
121
+ )
122
+
123
+ const storeDeferred = yield* Deferred.make<Store>()
124
+
125
+ const connectDevtoolsToStore_ = (storeDevtoolsChannel: StoreDevtoolsChannel) =>
126
+ Effect.gen(function* () {
127
+ const store = yield* Deferred.await(storeDeferred)
128
+ yield* connectDevtoolsToStore({ storeDevtoolsChannel, store })
129
+ })
130
+
131
+ const runtime = yield* Effect.runtime<Scope.Scope>()
132
+
133
+ // TODO close parent scope? (Needs refactor with Mike Arnaldi)
134
+ const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) =>
135
+ Effect.gen(function* () {
136
+ // NOTE we're calling `cause.toString()` here to avoid triggering a `console.error` in the grouped log
137
+ const logCause =
138
+ Cause.isFailType(cause) && cause.error._tag === 'LiveStore.IntentionalShutdownCause'
139
+ ? cause.toString()
140
+ : cause
141
+ yield* Effect.logDebug(`Shutting down LiveStore`, logCause)
142
+
143
+ FiberSet.clear(fiberSet).pipe(
144
+ Effect.andThen(() => FiberSet.run(fiberSet, Effect.failCause(cause))),
145
+ Effect.timeout(Duration.seconds(1)),
146
+ Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown:clear-fiber-set', duration: 500 }),
147
+ Effect.catchTag('TimeoutException', (err) =>
148
+ Effect.logError('Store shutdown timed out. Forcing shutdown.', err).pipe(
149
+ Effect.andThen(FiberSet.run(fiberSet, Effect.failCause(cause))),
150
+ ),
151
+ ),
152
+ Runtime.runFork(runtime), // NOTE we need to fork this separately otherwise it will also be interrupted
153
+ )
154
+ }).pipe(Effect.withSpan('livestore:shutdown'))
155
+
156
+ const clientSession: ClientSession = yield* adapter({
157
+ schema,
158
+ storeId,
159
+ devtoolsEnabled: disableDevtools !== true,
160
+ bootStatusQueue,
161
+ shutdown,
162
+ connectDevtoolsToStore: connectDevtoolsToStore_,
163
+ }).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
164
+
165
+ // TODO fill up with unsynced mutation events from the client session
166
+ const unsyncedMutationEvents = MutableHashMap.empty<EventId, MutationEvent.ForSchema<TSchema>>()
167
+
168
+ const store = Store.createStore<TGraphQLContext, TSchema>(
169
+ {
170
+ clientSession,
171
+ schema,
172
+ graphQLOptions,
173
+ otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
174
+ reactivityGraph,
175
+ disableDevtools,
176
+ unsyncedMutationEvents,
177
+ fiberSet,
178
+ runtime,
179
+ // NOTE during boot we're not yet executing mutations in a batched context
180
+ // but only set the provided `batchUpdates` function after boot
181
+ batchUpdates: (run) => run(),
182
+ storeId,
183
+ },
184
+ span,
185
+ )
186
+
187
+ if (boot !== undefined) {
188
+ // TODO also incorporate `boot` function progress into `bootStatusQueue`
189
+ yield* Effect.tryAll(() => boot(store, span)).pipe(
190
+ UnexpectedError.mapToUnexpectedError,
191
+ Effect.withSpan('createStore:boot'),
192
+ )
193
+ }
194
+
195
+ // NOTE it's important to yield here to allow the forked Effect in the store constructor to run
196
+ yield* Effect.yieldNow()
197
+
198
+ if (batchUpdates !== undefined) {
199
+ // Replacing the default batchUpdates function with the provided one after boot
200
+ store.reactivityGraph.context!.effectsWrapper = batchUpdates
201
+ }
202
+
203
+ yield* Deferred.succeed(storeDeferred, store as any as Store)
204
+
205
+ return store
206
+ }).pipe(
207
+ Effect.withSpan('createStore', {
208
+ parent: otelOptions?.rootSpanContext
209
+ ? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext)!)
210
+ : undefined,
211
+ }),
212
+ Effect.provide(TracingLive),
213
+ )
214
+ }
@@ -1,17 +1,17 @@
1
- import type { DebugInfo, StoreAdapter } from '@livestore/common'
1
+ import type { ClientSession, DebugInfo } from '@livestore/common'
2
2
  import { Devtools, liveStoreVersion, UnexpectedError } from '@livestore/common'
3
3
  import { throttle } from '@livestore/utils'
4
4
  import type { WebChannel } from '@livestore/utils/effect'
5
5
  import { Effect, Stream } from '@livestore/utils/effect'
6
6
 
7
- import { NOT_REFRESHED_YET } from './reactive.js'
8
- import type { LiveQuery, ReactivityGraph } from './reactiveQueries/base-class.js'
9
- import type { SynchronousDatabaseWrapper } from './SynchronousDatabaseWrapper.js'
10
- import { emptyDebugInfo as makeEmptyDebugInfo } from './SynchronousDatabaseWrapper.js'
11
- import type { ReferenceCountedSet } from './utils/data-structures.js'
7
+ import { NOT_REFRESHED_YET } from '../reactive.js'
8
+ import type { LiveQuery, ReactivityGraph } from '../reactiveQueries/base-class.js'
9
+ import type { SynchronousDatabaseWrapper } from '../SynchronousDatabaseWrapper.js'
10
+ import { emptyDebugInfo as makeEmptyDebugInfo } from '../SynchronousDatabaseWrapper.js'
11
+ import type { ReferenceCountedSet } from '../utils/data-structures.js'
12
12
 
13
13
  type IStore = {
14
- adapter: StoreAdapter
14
+ clientSession: ClientSession
15
15
  reactivityGraph: ReactivityGraph
16
16
  syncDbWrapper: SynchronousDatabaseWrapper
17
17
  activeQueries: ReferenceCountedSet<LiveQuery<any>>
@@ -29,7 +29,7 @@ export const connectDevtoolsToStore = ({
29
29
  store: IStore
30
30
  }) =>
31
31
  Effect.gen(function* () {
32
- const appHostId = store.adapter.coordinator.devtools.appHostId
32
+ const appHostId = store.clientSession.coordinator.devtools.appHostId
33
33
 
34
34
  const reactivityGraphSubcriptions: SubMap = new Map()
35
35
  const liveQueriesSubscriptions: SubMap = new Map()
@@ -49,7 +49,7 @@ export const connectDevtoolsToStore = ({
49
49
  const onMessage = (decodedMessage: typeof Devtools.MessageToAppHostStore.Type) => {
50
50
  // console.log('storeMessagePort message', decodedMessage)
51
51
 
52
- if (decodedMessage.appHostId !== store.adapter.coordinator.devtools.appHostId) {
52
+ if (decodedMessage.appHostId !== store.clientSession.coordinator.devtools.appHostId) {
53
53
  // console.log(`Unknown message`, event)
54
54
  return
55
55
  }
@@ -0,0 +1,110 @@
1
+ import type { ClientSession, EventId, IntentionalShutdownCause, UnexpectedError } from '@livestore/common'
2
+ import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
3
+ import type { FiberSet, MutableHashMap, Runtime, Scope } from '@livestore/utils/effect'
4
+ import { Schema } from '@livestore/utils/effect'
5
+ import type * as otel from '@opentelemetry/api'
6
+ import type { GraphQLSchema } from 'graphql'
7
+
8
+ import type { DebugRefreshReasonBase } from '../reactive.js'
9
+ import type { ReactivityGraph } from '../reactiveQueries/base-class.js'
10
+ import type { SynchronousDatabaseWrapper } from '../SynchronousDatabaseWrapper.js'
11
+ import type { StackInfo } from '../utils/stack-info.js'
12
+ import type { Store } from './store.js'
13
+
14
+ export type LiveStoreContext =
15
+ | LiveStoreContextRunning
16
+ | {
17
+ stage: 'error'
18
+ error: UnexpectedError | unknown
19
+ }
20
+ | {
21
+ stage: 'shutdown'
22
+ cause: IntentionalShutdownCause | StoreAbort
23
+ }
24
+
25
+ export class StoreAbort extends Schema.TaggedError<StoreAbort>()('LiveStore.StoreAbort', {}) {}
26
+ export class StoreInterrupted extends Schema.TaggedError<StoreInterrupted>()('LiveStore.StoreInterrupted', {}) {}
27
+
28
+ export type LiveStoreContextRunning = {
29
+ stage: 'running'
30
+ store: Store
31
+ }
32
+
33
+ export type BaseGraphQLContext = {
34
+ queriedTables: Set<string>
35
+ /** Needed by Pothos Otel plugin for resolver tracing to work */
36
+ otelContext?: otel.Context
37
+ }
38
+
39
+ export type GraphQLOptions<TContext> = {
40
+ schema: GraphQLSchema
41
+ makeContext: (db: SynchronousDatabaseWrapper, tracer: otel.Tracer, sessionId: string) => TContext
42
+ }
43
+
44
+ export type OtelOptions = {
45
+ tracer: otel.Tracer
46
+ rootSpanContext: otel.Context
47
+ }
48
+
49
+ export type StoreOptions<
50
+ TGraphQLContext extends BaseGraphQLContext,
51
+ TSchema extends LiveStoreSchema = LiveStoreSchema,
52
+ > = {
53
+ clientSession: ClientSession
54
+ schema: TSchema
55
+ storeId: string
56
+ // TODO remove graphql-related stuff from store and move to GraphQL query directly
57
+ graphQLOptions?: GraphQLOptions<TGraphQLContext>
58
+ otelOptions: OtelOptions
59
+ reactivityGraph: ReactivityGraph
60
+ disableDevtools?: boolean
61
+ fiberSet: FiberSet.FiberSet
62
+ runtime: Runtime.Runtime<Scope.Scope>
63
+ batchUpdates: (runUpdates: () => void) => void
64
+ unsyncedMutationEvents: MutableHashMap.MutableHashMap<EventId, MutationEvent.ForSchema<TSchema>>
65
+ }
66
+
67
+ export type RefreshReason =
68
+ | DebugRefreshReasonBase
69
+ | {
70
+ _tag: 'mutate'
71
+ /** The mutations that were applied */
72
+ mutations: ReadonlyArray<MutationEvent.Any>
73
+
74
+ /** The tables that were written to by the event */
75
+ writeTables: ReadonlyArray<string>
76
+ }
77
+ | {
78
+ // TODO rename to a more appropriate name which is framework-agnostic
79
+ _tag: 'react'
80
+ api: string
81
+ label?: string
82
+ stackInfo?: StackInfo
83
+ }
84
+ | { _tag: 'manual'; label?: string }
85
+
86
+ export type QueryDebugInfo = {
87
+ _tag: 'graphql' | 'sql' | 'computed' | 'unknown'
88
+ label: string
89
+ query: string
90
+ durationMs: number
91
+ }
92
+
93
+ export type StoreOtel = {
94
+ tracer: otel.Tracer
95
+ mutationsSpanContext: otel.Context
96
+ queriesSpanContext: otel.Context
97
+ }
98
+
99
+ export type StoreMutateOptions = {
100
+ label?: string
101
+ skipRefresh?: boolean
102
+ wasSyncMessage?: boolean
103
+ /**
104
+ * When set to `false` the mutation won't be persisted in the mutation log and sync server (but still synced).
105
+ * This can be useful e.g. for fine-granular update events (e.g. position updates during drag & drop)
106
+ *
107
+ * @default true
108
+ */
109
+ persisted?: boolean
110
+ }