@livestore/livestore 0.0.0-snapshot-909cdd1ac2fd591945c2be2b0f53e14d87f3c9d4

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 (131) hide show
  1. package/README.md +1 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/QueryCache.d.ts +20 -0
  4. package/dist/QueryCache.d.ts.map +1 -0
  5. package/dist/QueryCache.js +61 -0
  6. package/dist/QueryCache.js.map +1 -0
  7. package/dist/SynchronousDatabaseWrapper.d.ts +36 -0
  8. package/dist/SynchronousDatabaseWrapper.d.ts.map +1 -0
  9. package/dist/SynchronousDatabaseWrapper.js +176 -0
  10. package/dist/SynchronousDatabaseWrapper.js.map +1 -0
  11. package/dist/effect/LiveStore.d.ts +38 -0
  12. package/dist/effect/LiveStore.d.ts.map +1 -0
  13. package/dist/effect/LiveStore.js +38 -0
  14. package/dist/effect/LiveStore.js.map +1 -0
  15. package/dist/effect/index.d.ts +2 -0
  16. package/dist/effect/index.d.ts.map +1 -0
  17. package/dist/effect/index.js +2 -0
  18. package/dist/effect/index.js.map +1 -0
  19. package/dist/global-state.d.ts +14 -0
  20. package/dist/global-state.d.ts.map +1 -0
  21. package/dist/global-state.js +16 -0
  22. package/dist/global-state.js.map +1 -0
  23. package/dist/index.d.ts +19 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +15 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/reactive.d.ts +163 -0
  28. package/dist/reactive.d.ts.map +1 -0
  29. package/dist/reactive.js +382 -0
  30. package/dist/reactive.js.map +1 -0
  31. package/dist/reactive.test.d.ts +2 -0
  32. package/dist/reactive.test.d.ts.map +1 -0
  33. package/dist/reactive.test.js +345 -0
  34. package/dist/reactive.test.js.map +1 -0
  35. package/dist/reactiveQueries/base-class.d.ts +59 -0
  36. package/dist/reactiveQueries/base-class.d.ts.map +1 -0
  37. package/dist/reactiveQueries/base-class.js +29 -0
  38. package/dist/reactiveQueries/base-class.js.map +1 -0
  39. package/dist/reactiveQueries/graphql.d.ts +52 -0
  40. package/dist/reactiveQueries/graphql.d.ts.map +1 -0
  41. package/dist/reactiveQueries/graphql.js +136 -0
  42. package/dist/reactiveQueries/graphql.js.map +1 -0
  43. package/dist/reactiveQueries/js.d.ts +35 -0
  44. package/dist/reactiveQueries/js.d.ts.map +1 -0
  45. package/dist/reactiveQueries/js.js +57 -0
  46. package/dist/reactiveQueries/js.js.map +1 -0
  47. package/dist/reactiveQueries/sql.d.ts +49 -0
  48. package/dist/reactiveQueries/sql.d.ts.map +1 -0
  49. package/dist/reactiveQueries/sql.js +130 -0
  50. package/dist/reactiveQueries/sql.js.map +1 -0
  51. package/dist/reactiveQueries/sql.test.d.ts +2 -0
  52. package/dist/reactiveQueries/sql.test.d.ts.map +1 -0
  53. package/dist/reactiveQueries/sql.test.js +284 -0
  54. package/dist/reactiveQueries/sql.test.js.map +1 -0
  55. package/dist/row-query.d.ts +33 -0
  56. package/dist/row-query.d.ts.map +1 -0
  57. package/dist/row-query.js +84 -0
  58. package/dist/row-query.js.map +1 -0
  59. package/dist/store-context.d.ts +26 -0
  60. package/dist/store-context.d.ts.map +1 -0
  61. package/dist/store-context.js +6 -0
  62. package/dist/store-context.js.map +1 -0
  63. package/dist/store-devtools.d.ts +19 -0
  64. package/dist/store-devtools.d.ts.map +1 -0
  65. package/dist/store-devtools.js +141 -0
  66. package/dist/store-devtools.js.map +1 -0
  67. package/dist/store.d.ts +175 -0
  68. package/dist/store.d.ts.map +1 -0
  69. package/dist/store.js +507 -0
  70. package/dist/store.js.map +1 -0
  71. package/dist/utils/data-structures.d.ts +10 -0
  72. package/dist/utils/data-structures.d.ts.map +1 -0
  73. package/dist/utils/data-structures.js +32 -0
  74. package/dist/utils/data-structures.js.map +1 -0
  75. package/dist/utils/dev.d.ts +3 -0
  76. package/dist/utils/dev.d.ts.map +1 -0
  77. package/dist/utils/dev.js +17 -0
  78. package/dist/utils/dev.js.map +1 -0
  79. package/dist/utils/otel.d.ts +4 -0
  80. package/dist/utils/otel.d.ts.map +1 -0
  81. package/dist/utils/otel.js +6 -0
  82. package/dist/utils/otel.js.map +1 -0
  83. package/dist/utils/stack-info.d.ts +10 -0
  84. package/dist/utils/stack-info.d.ts.map +1 -0
  85. package/dist/utils/stack-info.js +41 -0
  86. package/dist/utils/stack-info.js.map +1 -0
  87. package/dist/utils/stack-info.test.d.ts +2 -0
  88. package/dist/utils/stack-info.test.d.ts.map +1 -0
  89. package/dist/utils/stack-info.test.js +75 -0
  90. package/dist/utils/stack-info.test.js.map +1 -0
  91. package/dist/utils/tests/fixture.d.ts +259 -0
  92. package/dist/utils/tests/fixture.d.ts.map +1 -0
  93. package/dist/utils/tests/fixture.js +33 -0
  94. package/dist/utils/tests/fixture.js.map +1 -0
  95. package/dist/utils/tests/mod.d.ts +3 -0
  96. package/dist/utils/tests/mod.d.ts.map +1 -0
  97. package/dist/utils/tests/mod.js +3 -0
  98. package/dist/utils/tests/mod.js.map +1 -0
  99. package/dist/utils/tests/otel.d.ts +10 -0
  100. package/dist/utils/tests/otel.d.ts.map +1 -0
  101. package/dist/utils/tests/otel.js +42 -0
  102. package/dist/utils/tests/otel.js.map +1 -0
  103. package/package.json +60 -0
  104. package/src/QueryCache.ts +81 -0
  105. package/src/SynchronousDatabaseWrapper.ts +256 -0
  106. package/src/ambient.d.ts +10 -0
  107. package/src/effect/LiveStore.ts +112 -0
  108. package/src/effect/index.ts +8 -0
  109. package/src/global-state.ts +20 -0
  110. package/src/index.ts +64 -0
  111. package/src/reactive.test.ts +426 -0
  112. package/src/reactive.ts +661 -0
  113. package/src/reactiveQueries/base-class.ts +115 -0
  114. package/src/reactiveQueries/graphql.ts +233 -0
  115. package/src/reactiveQueries/js.ts +108 -0
  116. package/src/reactiveQueries/sql.test.ts +308 -0
  117. package/src/reactiveQueries/sql.ts +226 -0
  118. package/src/row-query.ts +200 -0
  119. package/src/store-context.ts +23 -0
  120. package/src/store-devtools.ts +217 -0
  121. package/src/store.ts +920 -0
  122. package/src/utils/data-structures.ts +36 -0
  123. package/src/utils/dev.ts +24 -0
  124. package/src/utils/otel.ts +9 -0
  125. package/src/utils/stack-info.test.ts +79 -0
  126. package/src/utils/stack-info.ts +54 -0
  127. package/src/utils/tests/fixture.ts +77 -0
  128. package/src/utils/tests/mod.ts +2 -0
  129. package/src/utils/tests/otel.ts +61 -0
  130. package/tsconfig.json +18 -0
  131. package/vitest.config.js +9 -0
@@ -0,0 +1,81 @@
1
+ import type { Bindable } from '@livestore/common'
2
+ import { BoundMap, BoundSet } from '@livestore/common'
3
+
4
+ type Opaque<BaseType, BrandType = unknown> = BaseType & {
5
+ readonly [Symbols.base]: BaseType
6
+ readonly [Symbols.brand]: BrandType
7
+ }
8
+
9
+ namespace Symbols {
10
+ export declare const base: unique symbol
11
+ export declare const brand: unique symbol
12
+ }
13
+
14
+ export type CacheKey = Opaque<string, string>
15
+ type TableName = string
16
+
17
+ const ignore = ['begin', 'rollback', 'commit', 'savepoint', 'release']
18
+
19
+ // TODO: profile to see how big we need this cache to be.
20
+ const cacheSize = 200
21
+ export default class QueryCache {
22
+ #entries = new BoundMap<CacheKey, any>(cacheSize)
23
+ #dependencies = new Map<TableName, BoundSet<CacheKey>>()
24
+
25
+ getKey = (sql: string, bindValues?: Bindable): CacheKey => {
26
+ if (bindValues == null) {
27
+ return sql as CacheKey
28
+ }
29
+
30
+ if (Array.isArray(bindValues)) {
31
+ return (sql + '\n' + bindValues.join('\n')) as CacheKey
32
+ }
33
+
34
+ return (sql + '\n' + Object.values(bindValues).join('\n')) as CacheKey
35
+ }
36
+
37
+ get = (key: CacheKey) => {
38
+ return this.#entries.get(key)
39
+ }
40
+
41
+ set = (queriedTables: Iterable<string>, key: CacheKey, results: any) => {
42
+ this.#entries.set(key, results)
43
+ for (const table of queriedTables) {
44
+ let keys = this.#dependencies.get(table)
45
+ if (keys == null) {
46
+ keys = new BoundSet(cacheSize)
47
+ keys.onEvict = this.#dependencyTrackerEvicted
48
+ this.#dependencies.set(table, keys)
49
+ }
50
+ keys.add(key)
51
+ }
52
+ }
53
+
54
+ #dependencyTrackerEvicted = (key: CacheKey) => {
55
+ this.#entries.delete(key)
56
+ }
57
+
58
+ ignoreQuery = (query: string) => {
59
+ return ignore.some((prefix) => query.startsWith(prefix))
60
+ }
61
+
62
+ // The next simplest step is to create a specific implementation for invalidating
63
+ // the expensive track list queries only when constraints data in a write overlaps with read constraints.
64
+ //
65
+ // As well as either:
66
+ // a. removeing the big view (since we'll have our cache)
67
+ // b. incrementally updating the view on insert by the EventImporter
68
+ //
69
+ // We'll not try to tackle any generalized approach until we have a proof of concept working.
70
+ invalidate = (queriedTables: Iterable<string>) => {
71
+ for (const table of queriedTables) {
72
+ const keys = this.#dependencies.get(table)
73
+ if (keys == null) {
74
+ continue
75
+ }
76
+ for (const k of keys) {
77
+ this.#entries.delete(k)
78
+ }
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,256 @@
1
+ /* eslint-disable prefer-arrow/prefer-arrow-functions */
2
+
3
+ import type {
4
+ DebugInfo,
5
+ MutableDebugInfo,
6
+ PreparedBindValues,
7
+ PreparedStatement,
8
+ SynchronousDatabase,
9
+ } from '@livestore/common'
10
+ import { BoundArray, BoundMap, sql } from '@livestore/common'
11
+ import type * as otel from '@opentelemetry/api'
12
+
13
+ import QueryCache from './QueryCache.js'
14
+ import { getDurationMsFromSpan, getStartTimeHighResFromSpan } from './utils/otel.js'
15
+
16
+ export const emptyDebugInfo = (): DebugInfo => ({
17
+ slowQueries: new BoundArray(200),
18
+ queryFrameDuration: 0,
19
+ queryFrameCount: 0,
20
+ events: new BoundArray(1000),
21
+ })
22
+
23
+ export class SynchronousDatabaseWrapper {
24
+ // TODO: how many unique active statements are expected?
25
+ private cachedStmts = new BoundMap<string, PreparedStatement>(200)
26
+ private tablesUsedCache = new BoundMap<string, Set<string>>(200)
27
+ private resultCache = new QueryCache()
28
+ private db: SynchronousDatabase
29
+ private otelTracer: otel.Tracer
30
+ private otelRootSpanContext: otel.Context
31
+ private tablesUsedStmt
32
+ public debugInfo: MutableDebugInfo = emptyDebugInfo()
33
+
34
+ constructor({
35
+ db,
36
+ otel,
37
+ }: {
38
+ db: SynchronousDatabase
39
+ otel: {
40
+ tracer: otel.Tracer
41
+ rootSpanContext: otel.Context
42
+ }
43
+ }) {
44
+ this.db = db
45
+ this.otelTracer = otel.tracer
46
+ this.otelRootSpanContext = otel.rootSpanContext
47
+
48
+ this.tablesUsedStmt = db.prepare(
49
+ `SELECT tbl_name FROM tables_used(?) AS u JOIN sqlite_master ON sqlite_master.name = u.name WHERE u.schema = 'main';`,
50
+ )
51
+
52
+ this.cachedStmts.onEvict = (_queryStr, stmt) => stmt.finalize()
53
+
54
+ configureSQLite(this)
55
+ }
56
+
57
+ txn<TRes>(callback: () => TRes): TRes {
58
+ this.execute(sql`begin transaction;`)
59
+
60
+ let errored = false
61
+ let result: TRes
62
+
63
+ try {
64
+ result = callback()
65
+ } catch (e) {
66
+ errored = true
67
+ this.execute(sql`rollback;`)
68
+ throw e
69
+ }
70
+
71
+ if (!errored) {
72
+ this.execute(sql`commit;`)
73
+ }
74
+
75
+ return result
76
+ }
77
+
78
+ getTablesUsed(query: string) {
79
+ // It seems that SQLite doesn't properly handle `DELETE FROM SOME_TABLE` queries without a WHERE clause
80
+ // So we need to handle these queries separately
81
+ const tableNameFromPlainDeleteQuery = tryGetTableNameFromPlainDeleteQuery(query)
82
+ if (tableNameFromPlainDeleteQuery !== undefined) {
83
+ return new Set<string>([tableNameFromPlainDeleteQuery])
84
+ }
85
+
86
+ const cached = this.tablesUsedCache.get(query)
87
+ if (cached) {
88
+ return cached
89
+ }
90
+ const stmt = this.tablesUsedStmt
91
+ const tablesUsed = new Set<string>()
92
+ try {
93
+ const results = stmt.select<{ tbl_name: string }>([query] as unknown as PreparedBindValues)
94
+
95
+ for (const row of results) {
96
+ tablesUsed.add(row.tbl_name)
97
+ }
98
+ } catch (e) {
99
+ console.error('Error getting tables used', e, 'for query', query)
100
+ return new Set<string>()
101
+ }
102
+ this.tablesUsedCache.set(query, tablesUsed)
103
+ return tablesUsed
104
+ }
105
+
106
+ execute(
107
+ queryStr: string,
108
+ bindValues?: PreparedBindValues,
109
+ writeTables?: ReadonlySet<string>,
110
+ options?: { hasNoEffects?: boolean; otelContext?: otel.Context },
111
+ ): { durationMs: number } {
112
+ // console.debug('in-memory-db:execute', query, bindValues)
113
+
114
+ return this.otelTracer.startActiveSpan(
115
+ 'livestore.in-memory-db:execute',
116
+ // TODO truncate query string
117
+ { attributes: { 'sql.query': queryStr } },
118
+ options?.otelContext ?? this.otelRootSpanContext,
119
+ (span) => {
120
+ let stmt = this.cachedStmts.get(queryStr)
121
+ if (stmt === undefined) {
122
+ stmt = this.db.prepare(queryStr)
123
+ this.cachedStmts.set(queryStr, stmt)
124
+ }
125
+
126
+ stmt.execute(bindValues)
127
+
128
+ if (options?.hasNoEffects !== true && !this.resultCache.ignoreQuery(queryStr)) {
129
+ // TODO use write tables instead
130
+ // check what queries actually end up here.
131
+ this.resultCache.invalidate(writeTables ?? this.getTablesUsed(queryStr))
132
+ }
133
+
134
+ span.end()
135
+
136
+ const durationMs = getDurationMsFromSpan(span)
137
+
138
+ this.debugInfo.queryFrameDuration += durationMs
139
+ this.debugInfo.queryFrameCount++
140
+
141
+ if (durationMs > 5 && import.meta.env.DEV) {
142
+ this.debugInfo.slowQueries.push({
143
+ queryStr,
144
+ bindValues,
145
+ durationMs,
146
+ rowsCount: undefined,
147
+ queriedTables: new Set(),
148
+ startTimePerfNow: getStartTimeHighResFromSpan(span),
149
+ })
150
+ }
151
+
152
+ return { durationMs }
153
+ },
154
+ )
155
+ }
156
+
157
+ select<T = any>(
158
+ queryStr: string,
159
+ options?: {
160
+ queriedTables?: ReadonlySet<string>
161
+ bindValues?: PreparedBindValues
162
+ skipCache?: boolean
163
+ otelContext?: otel.Context
164
+ },
165
+ ): ReadonlyArray<T> {
166
+ const { queriedTables, bindValues, skipCache = false, otelContext } = options ?? {}
167
+
168
+ // console.debug('in-memory-db:select', query, bindValues)
169
+
170
+ return this.otelTracer.startActiveSpan(
171
+ 'sql-in-memory-select',
172
+ {},
173
+ otelContext ?? this.otelRootSpanContext,
174
+ (span) => {
175
+ try {
176
+ span.setAttribute('sql.query', queryStr)
177
+
178
+ const key = this.resultCache.getKey(queryStr, bindValues)
179
+ const cachedResult = this.resultCache.get(key)
180
+ if (skipCache === false && cachedResult !== undefined) {
181
+ span.setAttribute('sql.rowsCount', cachedResult.length)
182
+ span.setAttribute('sql.cached', true)
183
+ span.end()
184
+ return cachedResult
185
+ }
186
+
187
+ let stmt = this.cachedStmts.get(queryStr)
188
+ if (stmt === undefined) {
189
+ stmt = this.db.prepare(queryStr)
190
+ this.cachedStmts.set(queryStr, stmt)
191
+ }
192
+
193
+ const result = stmt.select<T>(bindValues)
194
+
195
+ span.setAttribute('sql.rowsCount', result.length)
196
+ span.setAttribute('sql.cached', false)
197
+
198
+ const queriedTables_ = queriedTables ?? this.getTablesUsed(queryStr)
199
+ this.resultCache.set(queriedTables_, key, result)
200
+
201
+ span.end()
202
+
203
+ const durationMs = getDurationMsFromSpan(span)
204
+
205
+ this.debugInfo.queryFrameDuration += durationMs
206
+ this.debugInfo.queryFrameCount++
207
+
208
+ // TODO also enable in non-dev mode
209
+ if (durationMs > 5 && import.meta.env.DEV) {
210
+ this.debugInfo.slowQueries.push({
211
+ queryStr,
212
+ bindValues,
213
+ durationMs,
214
+ rowsCount: result.length,
215
+ queriedTables: queriedTables_,
216
+ startTimePerfNow: getStartTimeHighResFromSpan(span),
217
+ })
218
+ }
219
+
220
+ return result
221
+ } finally {
222
+ span.end()
223
+ }
224
+ },
225
+ )
226
+ }
227
+
228
+ export() {
229
+ // Clear statement cache because exporting frees statements
230
+ for (const key of this.cachedStmts.keys()) {
231
+ this.cachedStmts.delete(key)
232
+ }
233
+
234
+ return this.db.export()
235
+ }
236
+ }
237
+
238
+ /** Set up SQLite performance; hasn't been super carefully optimized yet. */
239
+ const configureSQLite = (db: SynchronousDatabaseWrapper) => {
240
+ db.execute(
241
+ // TODO: revisit these tuning parameters for max performance
242
+ sql`
243
+ PRAGMA page_size=32768;
244
+ PRAGMA cache_size=10000;
245
+ PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
246
+ PRAGMA synchronous='OFF';
247
+ PRAGMA temp_store='MEMORY';
248
+ PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
249
+ `,
250
+ )
251
+ }
252
+
253
+ const tryGetTableNameFromPlainDeleteQuery = (query: string) => {
254
+ const [_, tableName] = query.trim().match(/^delete\s+from\s+(\w+)$/i) ?? []
255
+ return tableName
256
+ }
@@ -0,0 +1,10 @@
1
+ interface Window {
2
+ [key: `__debug${string}`]: any
3
+ }
4
+
5
+ // eslint-disable-next-line no-var
6
+ var __debugLiveStore: any
7
+
8
+ interface ImportMeta {
9
+ readonly env: ImportMetaEnv
10
+ }
@@ -0,0 +1,112 @@
1
+ import type { Adapter, BootDb, BootStatus, UnexpectedError } from '@livestore/common'
2
+ import type { LiveStoreSchema } from '@livestore/common/schema'
3
+ import type { Cause, Scope } from '@livestore/utils/effect'
4
+ import { Context, Deferred, Duration, Effect, FiberSet, Layer, OtelTracer, pipe } from '@livestore/utils/effect'
5
+ import * as otel from '@opentelemetry/api'
6
+ import type { GraphQLSchema } from 'graphql'
7
+
8
+ import type { BaseGraphQLContext } from '../store.js'
9
+ import { createStore } from '../store.js'
10
+ import type { LiveStoreContextRunning as LiveStoreContextRunning_ } from '../store-context.js'
11
+ import type { SynchronousDatabaseWrapper } from '../SynchronousDatabaseWrapper.js'
12
+
13
+ export type LiveStoreContextRunning = LiveStoreContextRunning_
14
+ export const LiveStoreContextRunning = Context.GenericTag<LiveStoreContextRunning>(
15
+ '@livestore/livestore/effect/LiveStoreContextRunning',
16
+ )
17
+
18
+ export type DeferredStoreContext = Deferred.Deferred<LiveStoreContextRunning, UnexpectedError>
19
+ export const DeferredStoreContext = Context.GenericTag<DeferredStoreContext>(
20
+ '@livestore/livestore/effect/DeferredStoreContext',
21
+ )
22
+
23
+ export type LiveStoreContextProps<GraphQLContext extends BaseGraphQLContext> = {
24
+ schema: LiveStoreSchema
25
+ /**
26
+ * The `storeId` can be used to isolate multiple stores from each other.
27
+ * So it can be useful for multi-tenancy scenarios.
28
+ *
29
+ * The `storeId` is also used for persistence.
30
+ *
31
+ * @default 'default'
32
+ */
33
+ storeId?: string
34
+ graphQLOptions?: {
35
+ schema: Effect.Effect<GraphQLSchema, never, otel.Tracer>
36
+ makeContext: (db: SynchronousDatabaseWrapper, tracer: otel.Tracer, sessionId: string) => GraphQLContext
37
+ }
38
+ boot?: (db: BootDb) => Effect.Effect<void, unknown, otel.Tracer>
39
+ adapter: Adapter
40
+ disableDevtools?: boolean
41
+ onBootStatus?: (status: BootStatus) => void
42
+ batchUpdates: (run: () => void) => void
43
+ }
44
+
45
+ export const LiveStoreContextLayer = <GraphQLContext extends BaseGraphQLContext>(
46
+ props: LiveStoreContextProps<GraphQLContext>,
47
+ ): Layer.Layer<LiveStoreContextRunning, UnexpectedError | Cause.TimeoutException, otel.Tracer> =>
48
+ Layer.scoped(LiveStoreContextRunning, makeLiveStoreContext(props)).pipe(
49
+ Layer.withSpan('LiveStore'),
50
+ Layer.provide(LiveStoreContextDeferred),
51
+ )
52
+
53
+ export const LiveStoreContextDeferred = Layer.effect(
54
+ DeferredStoreContext,
55
+ Deferred.make<LiveStoreContextRunning, UnexpectedError>(),
56
+ )
57
+
58
+ export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>({
59
+ schema,
60
+ storeId = 'default',
61
+ graphQLOptions: graphQLOptions_,
62
+ boot,
63
+ adapter,
64
+ disableDevtools,
65
+ onBootStatus,
66
+ batchUpdates,
67
+ }: LiveStoreContextProps<GraphQLContext>): Effect.Effect<
68
+ LiveStoreContextRunning,
69
+ UnexpectedError | Cause.TimeoutException,
70
+ DeferredStoreContext | Scope.Scope | otel.Tracer
71
+ > =>
72
+ pipe(
73
+ Effect.gen(function* () {
74
+ const otelRootSpanContext = otel.context.active()
75
+
76
+ const otelTracer = yield* OtelTracer.Tracer
77
+
78
+ const graphQLOptions = yield* graphQLOptions_
79
+ ? Effect.all({ schema: graphQLOptions_.schema, makeContext: Effect.succeed(graphQLOptions_.makeContext) })
80
+ : Effect.succeed(undefined)
81
+
82
+ // TODO join fiber set and close tear down parent scope in case of error (Needs refactor with Mike A)
83
+ const fiberSet = yield* FiberSet.make()
84
+
85
+ const store = yield* createStore({
86
+ schema,
87
+ storeId,
88
+ graphQLOptions,
89
+ otelOptions: {
90
+ tracer: otelTracer,
91
+ rootSpanContext: otelRootSpanContext,
92
+ },
93
+ boot,
94
+ adapter,
95
+ disableDevtools,
96
+ fiberSet,
97
+ onBootStatus,
98
+ batchUpdates,
99
+ })
100
+
101
+ globalThis.__debugLiveStore ??= {}
102
+ // window.__debugLiveStore[schema.key] = store
103
+
104
+ return { stage: 'running', store } satisfies LiveStoreContextRunning
105
+ }),
106
+ Effect.tapErrorCause((cause) => Effect.flatMap(DeferredStoreContext, (def) => Deferred.failCause(def, cause))),
107
+ Effect.tap((storeCtx) => Effect.flatMap(DeferredStoreContext, (def) => Deferred.succeed(def, storeCtx))),
108
+ // This can take quite a while.
109
+ // TODO make this configurable
110
+ Effect.timeout(Duration.minutes(5)),
111
+ Effect.withSpan('@livestore/livestore/effect:makeLiveStoreContext'),
112
+ )
@@ -0,0 +1,8 @@
1
+ export {
2
+ LiveStoreContextLayer,
3
+ LiveStoreContextRunning as LiveStoreContext,
4
+ LiveStoreContextRunning,
5
+ LiveStoreContextDeferred,
6
+ DeferredStoreContext,
7
+ type LiveStoreContextProps,
8
+ } from './LiveStore.js'
@@ -0,0 +1,20 @@
1
+ /**
2
+ *
3
+ * LiveStore currently relies on some global state in order to simplify the end-user API.
4
+ * This however also has the downside that LiveStore can't be used in multiple instances in the same app.
5
+ * It could possibly also lead to some other problems.
6
+ *
7
+ * We should find some better way to do this and ideally remove this global state.
8
+ *
9
+ * Another approach could be to use the global state by default but provide an additional way to let the user
10
+ * explicitly pass instances of state below into the LiveStore constructors.
11
+ *
12
+ */
13
+
14
+ import { GlobalValue } from '@livestore/utils/effect'
15
+
16
+ import { makeReactivityGraph } from './reactiveQueries/base-class.js'
17
+
18
+ export const globalReactivityGraph = GlobalValue.globalValue('livestore-global-reactivityGraph', () =>
19
+ makeReactivityGraph(),
20
+ )
package/src/index.ts ADDED
@@ -0,0 +1,64 @@
1
+ export { Store, createStorePromise, createStore } from './store.js'
2
+ export type {
3
+ BaseGraphQLContext,
4
+ QueryDebugInfo,
5
+ RefreshReason,
6
+ CreateStoreOptions,
7
+ GraphQLOptions,
8
+ OtelOptions,
9
+ } from './store.js'
10
+
11
+ export type { LiveStoreContextRunning } from './effect/LiveStore.js'
12
+ export { StoreAbort, StoreInterrupted, type LiveStoreContext } from './store-context.js'
13
+
14
+ export { SynchronousDatabaseWrapper, emptyDebugInfo } from './SynchronousDatabaseWrapper.js'
15
+
16
+ export type {
17
+ GetAtom,
18
+ AtomDebugInfo,
19
+ RefreshDebugInfo,
20
+ ReactiveGraphSnapshot,
21
+ SerializedAtom,
22
+ SerializedEffect,
23
+ Atom,
24
+ Node,
25
+ Ref,
26
+ Effect,
27
+ } from './reactive.js'
28
+ export { LiveStoreJSQuery, computed } from './reactiveQueries/js.js'
29
+ export { LiveStoreSQLQuery, querySQL } from './reactiveQueries/sql.js'
30
+ export { LiveStoreGraphQLQuery, queryGraphQL } from './reactiveQueries/graphql.js'
31
+ export {
32
+ type GetAtomResult,
33
+ type ReactivityGraph,
34
+ makeReactivityGraph,
35
+ type LiveQuery,
36
+ type GetResult,
37
+ type LiveQueryAny,
38
+ } from './reactiveQueries/base-class.js'
39
+
40
+ export { globalReactivityGraph } from './global-state.js'
41
+
42
+ export { type RowResult, type RowResultEncoded, rowQuery, deriveColQuery } from './row-query.js'
43
+
44
+ export * from '@livestore/common/schema'
45
+ export {
46
+ sql,
47
+ SessionIdSymbol,
48
+ type BootDb,
49
+ type BootStatus,
50
+ type SynchronousDatabase,
51
+ type DebugInfo,
52
+ type MutableDebugInfo,
53
+ prepareBindValues,
54
+ type Bindable,
55
+ type PreparedBindValues,
56
+ } from '@livestore/common'
57
+
58
+ export { SqliteAst, SqliteDsl } from '@livestore/db-schema'
59
+
60
+ export { deepEqual } from '@livestore/utils'
61
+
62
+ export * from './utils/stack-info.js'
63
+
64
+ export type { ClientSession, Adapter, PreparedStatement } from '@livestore/common'