@livestore/livestore 0.0.54-dev.22 → 0.0.54-dev.24

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 (43) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/react/fixture.d.ts +0 -8
  3. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/react/fixture.js +1 -1
  5. package/dist/__tests__/react/fixture.js.map +1 -1
  6. package/dist/effect/LiveStore.d.ts +1 -0
  7. package/dist/effect/LiveStore.d.ts.map +1 -1
  8. package/dist/effect/LiveStore.js +1 -1
  9. package/dist/effect/LiveStore.js.map +1 -1
  10. package/dist/global-state.d.ts +0 -2
  11. package/dist/global-state.d.ts.map +1 -1
  12. package/dist/global-state.js +0 -1
  13. package/dist/global-state.js.map +1 -1
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/react/LiveStoreContext.d.ts.map +1 -1
  19. package/dist/react/LiveStoreContext.js +3 -0
  20. package/dist/react/LiveStoreContext.js.map +1 -1
  21. package/dist/react/LiveStoreProvider.d.ts +3 -3
  22. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  23. package/dist/react/LiveStoreProvider.js +16 -8
  24. package/dist/react/LiveStoreProvider.js.map +1 -1
  25. package/dist/react/LiveStoreProvider.test.js +6 -4
  26. package/dist/react/LiveStoreProvider.test.js.map +1 -1
  27. package/dist/row-query.d.ts.map +1 -1
  28. package/dist/row-query.js +4 -38
  29. package/dist/row-query.js.map +1 -1
  30. package/dist/store.d.ts +7 -4
  31. package/dist/store.d.ts.map +1 -1
  32. package/dist/store.js +231 -96
  33. package/dist/store.js.map +1 -1
  34. package/package.json +5 -5
  35. package/src/__tests__/react/fixture.tsx +1 -1
  36. package/src/effect/LiveStore.ts +2 -1
  37. package/src/global-state.ts +0 -4
  38. package/src/index.ts +2 -1
  39. package/src/react/LiveStoreContext.ts +4 -0
  40. package/src/react/LiveStoreProvider.test.tsx +9 -4
  41. package/src/react/LiveStoreProvider.tsx +17 -10
  42. package/src/row-query.ts +5 -51
  43. package/src/store.ts +315 -128
@@ -11,10 +11,6 @@
11
11
  *
12
12
  */
13
13
 
14
- import type { DbSchema } from '@livestore/common/schema'
15
-
16
14
  import { makeReactivityGraph } from './reactiveQueries/base-class.js'
17
15
 
18
16
  export const globalReactivityGraph = makeReactivityGraph()
19
-
20
- export const dynamicallyRegisteredTables: Map<string, DbSchema.TableDef> = new Map()
package/src/index.ts CHANGED
@@ -27,7 +27,7 @@ export {
27
27
  type LiveQuery,
28
28
  } from './reactiveQueries/base-class.js'
29
29
 
30
- export { globalReactivityGraph, dynamicallyRegisteredTables } from './global-state.js'
30
+ export { globalReactivityGraph } from './global-state.js'
31
31
 
32
32
  export { type RowResult, type RowResultEncoded, rowQuery, deriveColQuery } from './row-query.js'
33
33
 
@@ -35,6 +35,7 @@ export * from '@livestore/common/schema'
35
35
  export {
36
36
  sql,
37
37
  type BootDb,
38
+ type BootStatus,
38
39
  type InMemoryDatabase,
39
40
  type DebugInfo,
40
41
  type MutableDebugInfo,
@@ -11,5 +11,9 @@ export const useStore = (): LiveStoreContext_ => {
11
11
  throw new Error(`useStore can only be used inside StoreContext.Provider`)
12
12
  }
13
13
 
14
+ if (storeContext.stage !== 'running') {
15
+ throw new Error(`useStore can only be used after the store is running`)
16
+ }
17
+
14
18
  return storeContext
15
19
  }
@@ -37,7 +37,12 @@ describe('LiveStoreProvider', () => {
37
37
  // eslint-disable-next-line react-hooks/exhaustive-deps
38
38
  const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate])
39
39
  return (
40
- <LiveStoreProvider schema={schema} fallback={<div>Loading LiveStore</div>} adapter={adapterMemo} boot={bootCb}>
40
+ <LiveStoreProvider
41
+ schema={schema}
42
+ renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
43
+ adapter={adapterMemo}
44
+ boot={bootCb}
45
+ >
41
46
  <App />
42
47
  </LiveStoreProvider>
43
48
  )
@@ -47,14 +52,14 @@ describe('LiveStoreProvider', () => {
47
52
 
48
53
  expect(renderCount).toBe(0)
49
54
 
50
- await waitForElementToBeRemoved(() => screen.getByText('Loading LiveStore'))
55
+ await waitForElementToBeRemoved(() => screen.getByText((_) => _.startsWith('Loading LiveStore')))
51
56
 
52
57
  expect(renderCount).toBe(1)
53
58
 
54
59
  rerender(<Root forceUpdate={2} />)
55
60
 
56
- await waitFor(() => screen.getByText('Loading LiveStore'))
57
- await waitForElementToBeRemoved(() => screen.getByText('Loading LiveStore'))
61
+ await waitFor(() => screen.getByText('Loading LiveStore: loading'))
62
+ await waitForElementToBeRemoved(() => screen.getByText((_) => _.startsWith('Loading LiveStore')))
58
63
 
59
64
  expect(renderCount).toBe(2)
60
65
 
@@ -1,4 +1,4 @@
1
- import type { BootDb, StoreAdapterFactory } from '@livestore/common'
1
+ import type { BootDb, BootStatus, StoreAdapterFactory } from '@livestore/common'
2
2
  import type { LiveStoreSchema } from '@livestore/common/schema'
3
3
  import { shouldNeverHappen } from '@livestore/utils'
4
4
  import type * as otel from '@opentelemetry/api'
@@ -16,14 +16,14 @@ interface LiveStoreProviderProps<GraphQLContext> {
16
16
  boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
17
17
  graphQLOptions?: GraphQLOptions<GraphQLContext>
18
18
  otelOptions?: OtelOptions
19
- fallback: ReactElement
19
+ renderLoading: (status: BootStatus) => ReactElement
20
20
  adapter: StoreAdapterFactory
21
21
  batchUpdates?: (run: () => void) => void
22
22
  disableDevtools?: boolean
23
23
  }
24
24
 
25
25
  export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
26
- fallback,
26
+ renderLoading,
27
27
  graphQLOptions,
28
28
  otelOptions,
29
29
  children,
@@ -43,8 +43,8 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
43
43
  disableDevtools,
44
44
  })
45
45
 
46
- if (storeCtx === undefined) {
47
- return fallback
46
+ if (storeCtx.stage !== 'running') {
47
+ return <div>{renderLoading(storeCtx)}</div>
48
48
  }
49
49
 
50
50
  window.__debugLiveStore = storeCtx.store
@@ -62,7 +62,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
62
62
  disableDevtools,
63
63
  }: LiveStoreCreateStoreOptions<GraphQLContext>) => {
64
64
  const [_, rerender] = React.useState(0)
65
- const ctxValueRef = React.useRef<StoreContext_ | undefined>(undefined)
65
+ const ctxValueRef = React.useRef<StoreContext_ | BootStatus>({ stage: 'loading' })
66
66
  const inputPropsCacheRef = React.useRef({
67
67
  schema,
68
68
  graphQLOptions,
@@ -89,9 +89,11 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
89
89
  adapter,
90
90
  batchUpdates,
91
91
  }
92
- ctxValueRef.current?.store.destroy()
93
- oldStoreAlreadyDestroyedRef.current = true
94
- ctxValueRef.current = undefined
92
+ if (ctxValueRef.current.stage === 'running') {
93
+ ctxValueRef.current.store.destroy()
94
+ oldStoreAlreadyDestroyedRef.current = true
95
+ ctxValueRef.current = { stage: 'loading' }
96
+ }
95
97
  }
96
98
 
97
99
  React.useEffect(() => {
@@ -107,8 +109,13 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
107
109
  adapter,
108
110
  batchUpdates,
109
111
  disableDevtools,
112
+ onBootStatus: (status) => {
113
+ if (ctxValueRef.current.stage === 'running') return
114
+ ctxValueRef.current = status
115
+ rerender((c) => c + 1)
116
+ },
110
117
  })
111
- ctxValueRef.current = { store }
118
+ ctxValueRef.current = { stage: 'running', store }
112
119
  oldStoreAlreadyDestroyedRef.current = false
113
120
  rerender((c) => c + 1)
114
121
  } catch (e) {
package/src/row-query.ts CHANGED
@@ -1,18 +1,16 @@
1
- import type { InMemoryDatabase, QueryInfoCol, QueryInfoNone, QueryInfoRow } from '@livestore/common'
2
- import { migrateTable, sql } from '@livestore/common'
3
- import { DbSchema, SCHEMA_META_TABLE } from '@livestore/common/schema'
1
+ import type { QueryInfoCol, QueryInfoNone, QueryInfoRow } from '@livestore/common'
2
+ import { sql } from '@livestore/common'
3
+ import { DbSchema } from '@livestore/common/schema'
4
4
  import type { GetValForKey } from '@livestore/utils'
5
5
  import { shouldNeverHappen } from '@livestore/utils'
6
- import { Effect, Schema, TreeFormatter } from '@livestore/utils/effect'
6
+ import { Schema, TreeFormatter } from '@livestore/utils/effect'
7
7
  import type * as otel from '@opentelemetry/api'
8
8
  import type { SqliteDsl } from 'effect-db-schema'
9
- import { SqliteAst } from 'effect-db-schema'
10
9
 
11
- import type { Ref } from './reactive.js'
12
10
  import type { LiveQuery, LiveQueryAny, QueryContext, ReactivityGraph } from './reactiveQueries/base-class.js'
13
11
  import { computed } from './reactiveQueries/js.js'
14
12
  import { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
15
- import type { RefreshReason, Store } from './store.js'
13
+ import type { Store } from './store.js'
16
14
 
17
15
  export type RowQueryOptions = {
18
16
  otelContext?: otel.Context
@@ -81,7 +79,6 @@ export const rowQuery: MakeRowQuery = <TTableDef extends DbSchema.TableDef>(
81
79
  execBeforeFirstRun: makeExecBeforeFirstRun({
82
80
  otelContext: options?.otelContext,
83
81
  table,
84
- tableName,
85
82
  defaultValues,
86
83
  id,
87
84
  skipInsertDefaultRow: options?.skipInsertDefaultRow,
@@ -136,59 +133,16 @@ const makeExecBeforeFirstRun =
136
133
  skipInsertDefaultRow,
137
134
  otelContext: otelContext_,
138
135
  table,
139
- tableName,
140
136
  }: {
141
137
  id?: string
142
138
  defaultValues?: any
143
139
  skipInsertDefaultRow?: boolean
144
140
  otelContext?: otel.Context
145
- tableName: string
146
141
  table: DbSchema.TableDef
147
142
  }) =>
148
143
  ({ store }: QueryContext) => {
149
144
  const otelContext = otelContext_ ?? store.otel.queriesSpanContext
150
145
 
151
- // TODO we can remove this codepath again when Devtools v2 has landed
152
- if (store.tableRefs[tableName] === undefined) {
153
- const schemaHash = SqliteAst.hash(table.sqliteDef.ast)
154
- const res = store.mainDbWrapper.select<{ schemaHash: number }>(
155
- sql`SELECT schemaHash FROM ${SCHEMA_META_TABLE} WHERE tableName = '${tableName}'`,
156
- )
157
- if (res.length === 0 || res[0]!.schemaHash !== schemaHash) {
158
- const db = {
159
- ...store.adapter.mainDb,
160
- prepare: (query) => {
161
- const mainStmt = store.adapter.mainDb.prepare(query)
162
- return {
163
- ...mainStmt,
164
- execute: (bindValues) => {
165
- const getRowsChanged = mainStmt.execute(bindValues)
166
- store.adapter.coordinator.execute(query, bindValues).pipe(Effect.tapCauseLogPretty, Effect.runFork)
167
- return getRowsChanged
168
- },
169
- }
170
- },
171
- } satisfies InMemoryDatabase
172
-
173
- migrateTable({
174
- db,
175
- tableAst: table.sqliteDef.ast,
176
- otelContext,
177
- schemaHash,
178
- behaviour: 'create-if-not-exists',
179
- })
180
- }
181
-
182
- const label = `tableRef:${tableName}`
183
-
184
- // TODO find a better implementation for this
185
- const existingTableRefFromGraph = Array.from(store.reactivityGraph.atoms.values()).find(
186
- (_) => _._tag === 'ref' && _.label === label,
187
- ) as Ref<null, QueryContext, RefreshReason> | undefined
188
-
189
- store.tableRefs[tableName] = existingTableRefFromGraph ?? store.makeTableRef(tableName)
190
- }
191
-
192
146
  if (skipInsertDefaultRow !== true && table.options.isSingleton === false) {
193
147
  insertRowWithDefaultValuesOrIgnore({
194
148
  store,