@livestore/livestore 0.4.0-dev.21 → 0.4.0-dev.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/effect/LiveStore.d.ts +123 -2
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +195 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/mod.d.ts +1 -1
- package/dist/effect/mod.d.ts.map +1 -1
- package/dist/effect/mod.js +3 -1
- package/dist/effect/mod.js.map +1 -1
- package/dist/mod.d.ts +1 -0
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -0
- package/dist/mod.js.map +1 -1
- package/dist/store/StoreRegistry.d.ts +190 -0
- package/dist/store/StoreRegistry.d.ts.map +1 -0
- package/dist/store/StoreRegistry.js +244 -0
- package/dist/store/StoreRegistry.js.map +1 -0
- package/dist/store/StoreRegistry.test.d.ts +2 -0
- package/dist/store/StoreRegistry.test.d.ts.map +1 -0
- package/dist/store/StoreRegistry.test.js +380 -0
- package/dist/store/StoreRegistry.test.js.map +1 -0
- package/dist/store/create-store.d.ts +50 -4
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +19 -0
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +13 -0
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +10 -25
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +23 -6
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +20 -2
- package/dist/store/store.js.map +1 -1
- package/docs/building-with-livestore/complex-ui-state/index.md +0 -2
- package/docs/building-with-livestore/crud/index.md +0 -2
- package/docs/building-with-livestore/data-modeling/index.md +29 -0
- package/docs/building-with-livestore/examples/todo-workspaces/index.md +0 -6
- package/docs/building-with-livestore/opentelemetry/index.md +25 -6
- package/docs/building-with-livestore/rules-for-ai-agents/index.md +2 -2
- package/docs/building-with-livestore/state/sql-queries/index.md +22 -0
- package/docs/building-with-livestore/state/sqlite-schema/index.md +2 -2
- package/docs/building-with-livestore/store/index.md +344 -0
- package/docs/framework-integrations/react-integration/index.md +380 -361
- package/docs/framework-integrations/vue-integration/index.md +2 -2
- package/docs/getting-started/expo/index.md +189 -43
- package/docs/getting-started/react-web/index.md +77 -24
- package/docs/getting-started/vue/index.md +3 -3
- package/docs/index.md +1 -2
- package/docs/llms.txt +0 -1
- package/docs/misc/troubleshooting/index.md +3 -3
- package/docs/overview/how-livestore-works/index.md +1 -1
- package/docs/overview/introduction/index.md +409 -1
- package/docs/overview/why-livestore/index.md +108 -2
- package/docs/patterns/auth/index.md +185 -34
- package/docs/patterns/effect/index.md +11 -1
- package/docs/patterns/storybook/index.md +43 -26
- package/docs/platform-adapters/expo-adapter/index.md +36 -19
- package/docs/platform-adapters/web-adapter/index.md +71 -2
- package/docs/tutorial/1-setup-starter-project/index.md +5 -5
- package/docs/tutorial/3-read-and-write-todos-via-livestore/index.md +54 -35
- package/docs/tutorial/5-expand-business-logic/index.md +1 -1
- package/docs/tutorial/6-persist-ui-state/index.md +12 -12
- package/package.json +6 -6
- package/src/effect/LiveStore.ts +385 -3
- package/src/effect/mod.ts +13 -1
- package/src/mod.ts +1 -0
- package/src/store/StoreRegistry.test.ts +516 -0
- package/src/store/StoreRegistry.ts +393 -0
- package/src/store/create-store.ts +50 -4
- package/src/store/devtools.ts +15 -0
- package/src/store/store-types.ts +17 -5
- package/src/store/store.ts +25 -5
- package/docs/building-with-livestore/examples/index.md +0 -30
|
@@ -4,9 +4,9 @@ The [vue-livestore](https://github.com/slashv/vue-livestore) package provides in
|
|
|
4
4
|
|
|
5
5
|
## API
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### `<LiveStoreProvider>`
|
|
8
8
|
|
|
9
|
-
In order to use LiveStore with Vue, you need to wrap your application in a
|
|
9
|
+
In order to use LiveStore with Vue, you need to wrap your application in a `<LiveStoreProvider>`.
|
|
10
10
|
|
|
11
11
|
## `reference/framework-integrations/vue/provider.vue`
|
|
12
12
|
|
|
@@ -242,47 +242,125 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
242
242
|
export const schema = makeSchema({ events, state })
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
##
|
|
245
|
+
## Configure the store
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
Create a `store.ts` file in the `src/livestore` folder. This file configures the store adapter and exports a custom hook that components will use to access the store.
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
The `useStore()` hook accepts store configuration options (schema, adapter, store ID) and returns a store instance. It suspends while the store is loading, so make sure to use a `Suspense` boundary to handle the loading state.
|
|
250
250
|
|
|
251
|
-
## `getting-started/expo/
|
|
251
|
+
## `getting-started/expo/livestore/store.ts`
|
|
252
252
|
|
|
253
|
-
```
|
|
253
|
+
```ts filename="getting-started/expo/livestore/store.ts"
|
|
254
254
|
|
|
255
|
-
const storeId = 'expo-todomvc'
|
|
256
255
|
const syncUrl = 'https://example.org/sync'
|
|
257
256
|
|
|
258
257
|
const adapter = makePersistedAdapter({
|
|
259
258
|
sync: { backend: makeWsSync({ url: syncUrl }) },
|
|
260
259
|
})
|
|
261
260
|
|
|
262
|
-
export const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
261
|
+
export const useAppStore = () =>
|
|
262
|
+
useStore({
|
|
263
|
+
storeId: 'expo-todomvc',
|
|
264
|
+
schema,
|
|
265
|
+
adapter,
|
|
266
|
+
batchUpdates,
|
|
267
|
+
boot: (store) => {
|
|
268
|
+
if (store.query(tables.todos.count()) === 0) {
|
|
269
|
+
store.commit(events.todoCreated({ id: crypto.randomUUID(), text: 'Make coffee' }))
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### `getting-started/expo/livestore/schema.ts`
|
|
276
|
+
|
|
277
|
+
```ts filename="getting-started/expo/livestore/schema.ts"
|
|
278
|
+
|
|
279
|
+
export const tables = {
|
|
280
|
+
todos: State.SQLite.table({
|
|
281
|
+
name: 'todos',
|
|
282
|
+
columns: {
|
|
283
|
+
id: State.SQLite.text({ primaryKey: true }),
|
|
284
|
+
text: State.SQLite.text({ default: '' }),
|
|
285
|
+
completed: State.SQLite.boolean({ default: false }),
|
|
286
|
+
deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
287
|
+
},
|
|
288
|
+
}),
|
|
289
|
+
uiState: State.SQLite.clientDocument({
|
|
290
|
+
name: 'uiState',
|
|
291
|
+
schema: Schema.Struct({ newTodoText: Schema.String, filter: Schema.Literal('all', 'active', 'completed') }),
|
|
292
|
+
default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },
|
|
293
|
+
}),
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export const events = {
|
|
297
|
+
todoCreated: Events.synced({
|
|
298
|
+
name: 'v1.TodoCreated',
|
|
299
|
+
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
300
|
+
}),
|
|
301
|
+
todoCompleted: Events.synced({
|
|
302
|
+
name: 'v1.TodoCompleted',
|
|
303
|
+
schema: Schema.Struct({ id: Schema.String }),
|
|
304
|
+
}),
|
|
305
|
+
todoUncompleted: Events.synced({
|
|
306
|
+
name: 'v1.TodoUncompleted',
|
|
307
|
+
schema: Schema.Struct({ id: Schema.String }),
|
|
308
|
+
}),
|
|
309
|
+
todoDeleted: Events.synced({
|
|
310
|
+
name: 'v1.TodoDeleted',
|
|
311
|
+
schema: Schema.Struct({ id: Schema.String, deletedAt: Schema.Date }),
|
|
312
|
+
}),
|
|
313
|
+
todoClearedCompleted: Events.synced({
|
|
314
|
+
name: 'v1.TodoClearedCompleted',
|
|
315
|
+
schema: Schema.Struct({ deletedAt: Schema.Date }),
|
|
316
|
+
}),
|
|
317
|
+
uiStateSet: tables.uiState.set,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const materializers = State.SQLite.materializers(events, {
|
|
321
|
+
'v1.TodoCreated': ({ id, text }) => tables.todos.insert({ id, text, completed: false }),
|
|
322
|
+
'v1.TodoCompleted': ({ id }) => tables.todos.update({ completed: true }).where({ id }),
|
|
323
|
+
'v1.TodoUncompleted': ({ id }) => tables.todos.update({ completed: false }).where({ id }),
|
|
324
|
+
'v1.TodoDeleted': ({ id, deletedAt }) => tables.todos.update({ deletedAt }).where({ id }),
|
|
325
|
+
'v1.TodoClearedCompleted': ({ deletedAt }) => tables.todos.update({ deletedAt }).where({ completed: true }),
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
const state = State.SQLite.makeState({ tables, materializers })
|
|
329
|
+
|
|
330
|
+
export const schema = makeSchema({ events, state })
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Set up the store registry
|
|
334
|
+
|
|
335
|
+
To enable store management throughout your app, create a `StoreRegistry` and provide it with a `<StoreRegistryProvider>`. The registry manages store instance lifecycles (loading, caching, disposal).
|
|
336
|
+
|
|
337
|
+
Wrap the provider in a `Suspense` boundary to handle the loading state for when the store is loading.
|
|
338
|
+
|
|
339
|
+
## `getting-started/expo/Root.tsx`
|
|
340
|
+
|
|
341
|
+
```tsx filename="getting-started/expo/Root.tsx"
|
|
342
|
+
|
|
343
|
+
const AppContent: FC = () => (
|
|
344
|
+
<View style={{ flex: 1, gap: 24, padding: 24 }}>
|
|
345
|
+
<NewTodo />
|
|
346
|
+
<ListTodos />
|
|
347
|
+
</View>
|
|
285
348
|
)
|
|
349
|
+
|
|
350
|
+
export const Root: FC = () => {
|
|
351
|
+
const [storeRegistry] = useState(() => new StoreRegistry())
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
355
|
+
<Suspense fallback={<Text>Loading LiveStore...</Text>}>
|
|
356
|
+
<StoreRegistryProvider storeRegistry={storeRegistry}>
|
|
357
|
+
<AppContent />
|
|
358
|
+
</StoreRegistryProvider>
|
|
359
|
+
</Suspense>
|
|
360
|
+
<StatusBar style="auto" />
|
|
361
|
+
</SafeAreaView>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
286
364
|
```
|
|
287
365
|
|
|
288
366
|
### `getting-started/expo/components/ListTodos.tsx`
|
|
@@ -290,8 +368,8 @@ export const Root: FC = () => (
|
|
|
290
368
|
```tsx filename="getting-started/expo/components/ListTodos.tsx"
|
|
291
369
|
|
|
292
370
|
export const ListTodos: FC = () => {
|
|
293
|
-
const
|
|
294
|
-
const todos = useQuery(visibleTodos$)
|
|
371
|
+
const store = useAppStore()
|
|
372
|
+
const todos = store.useQuery(visibleTodos$)
|
|
295
373
|
|
|
296
374
|
const toggleTodo = useCallback(
|
|
297
375
|
({ id, completed }: typeof tables.todos.Type) => {
|
|
@@ -339,8 +417,8 @@ export const ListTodos: FC = () => {
|
|
|
339
417
|
```tsx filename="getting-started/expo/components/NewTodo.tsx"
|
|
340
418
|
|
|
341
419
|
export const NewTodo: FC = () => {
|
|
342
|
-
const
|
|
343
|
-
const { newTodoText } = useQuery(uiState$)
|
|
420
|
+
const store = useAppStore()
|
|
421
|
+
const { newTodoText } = store.useQuery(uiState$)
|
|
344
422
|
|
|
345
423
|
const updateText = (text: string) => store.commit(events.uiStateSet({ newTodoText: text }))
|
|
346
424
|
const createTodo = () =>
|
|
@@ -444,19 +522,41 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
444
522
|
export const schema = makeSchema({ events, state })
|
|
445
523
|
```
|
|
446
524
|
|
|
447
|
-
###
|
|
525
|
+
### `getting-started/expo/livestore/store.ts`
|
|
526
|
+
|
|
527
|
+
```ts filename="getting-started/expo/livestore/store.ts"
|
|
528
|
+
|
|
529
|
+
const syncUrl = 'https://example.org/sync'
|
|
530
|
+
|
|
531
|
+
const adapter = makePersistedAdapter({
|
|
532
|
+
sync: { backend: makeWsSync({ url: syncUrl }) },
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
export const useAppStore = () =>
|
|
536
|
+
useStore({
|
|
537
|
+
storeId: 'expo-todomvc',
|
|
538
|
+
schema,
|
|
539
|
+
adapter,
|
|
540
|
+
batchUpdates,
|
|
541
|
+
boot: (store) => {
|
|
542
|
+
if (store.query(tables.todos.count()) === 0) {
|
|
543
|
+
store.commit(events.todoCreated({ id: crypto.randomUUID(), text: 'Make coffee' }))
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
})
|
|
547
|
+
```
|
|
448
548
|
|
|
449
|
-
|
|
549
|
+
## Commit events
|
|
450
550
|
|
|
451
|
-
|
|
551
|
+
After setting up the registry, use the `useAppStore()` hook from any component to access the store and commit events.
|
|
452
552
|
|
|
453
553
|
## `getting-started/expo/components/NewTodo.tsx`
|
|
454
554
|
|
|
455
555
|
```tsx filename="getting-started/expo/components/NewTodo.tsx"
|
|
456
556
|
|
|
457
557
|
export const NewTodo: FC = () => {
|
|
458
|
-
const
|
|
459
|
-
const { newTodoText } = useQuery(uiState$)
|
|
558
|
+
const store = useAppStore()
|
|
559
|
+
const { newTodoText } = store.useQuery(uiState$)
|
|
460
560
|
|
|
461
561
|
const updateText = (text: string) => store.commit(events.uiStateSet({ newTodoText: text }))
|
|
462
562
|
const createTodo = () =>
|
|
@@ -560,21 +660,43 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
560
660
|
export const schema = makeSchema({ events, state })
|
|
561
661
|
```
|
|
562
662
|
|
|
663
|
+
### `getting-started/expo/livestore/store.ts`
|
|
664
|
+
|
|
665
|
+
```ts filename="getting-started/expo/livestore/store.ts"
|
|
666
|
+
|
|
667
|
+
const syncUrl = 'https://example.org/sync'
|
|
668
|
+
|
|
669
|
+
const adapter = makePersistedAdapter({
|
|
670
|
+
sync: { backend: makeWsSync({ url: syncUrl }) },
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
export const useAppStore = () =>
|
|
674
|
+
useStore({
|
|
675
|
+
storeId: 'expo-todomvc',
|
|
676
|
+
schema,
|
|
677
|
+
adapter,
|
|
678
|
+
batchUpdates,
|
|
679
|
+
boot: (store) => {
|
|
680
|
+
if (store.query(tables.todos.count()) === 0) {
|
|
681
|
+
store.commit(events.todoCreated({ id: crypto.randomUUID(), text: 'Make coffee' }))
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
})
|
|
685
|
+
```
|
|
686
|
+
|
|
563
687
|
## Queries
|
|
564
688
|
|
|
565
|
-
To retrieve data from the database,
|
|
689
|
+
To retrieve data from the database, define a query using `queryDb` from `@livestore/livestore`, then execute it with `store.useQuery()`.
|
|
566
690
|
|
|
567
691
|
Consider abstracting queries into a separate file to keep your code organized, though you can also define them directly within components if preferred.
|
|
568
692
|
|
|
569
|
-
Here's an example:
|
|
570
|
-
|
|
571
693
|
## `getting-started/expo/components/ListTodos.tsx`
|
|
572
694
|
|
|
573
695
|
```tsx filename="getting-started/expo/components/ListTodos.tsx"
|
|
574
696
|
|
|
575
697
|
export const ListTodos: FC = () => {
|
|
576
|
-
const
|
|
577
|
-
const todos = useQuery(visibleTodos$)
|
|
698
|
+
const store = useAppStore()
|
|
699
|
+
const todos = store.useQuery(visibleTodos$)
|
|
578
700
|
|
|
579
701
|
const toggleTodo = useCallback(
|
|
580
702
|
({ id, completed }: typeof tables.todos.Type) => {
|
|
@@ -694,6 +816,30 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
694
816
|
export const schema = makeSchema({ events, state })
|
|
695
817
|
```
|
|
696
818
|
|
|
819
|
+
### `getting-started/expo/livestore/store.ts`
|
|
820
|
+
|
|
821
|
+
```ts filename="getting-started/expo/livestore/store.ts"
|
|
822
|
+
|
|
823
|
+
const syncUrl = 'https://example.org/sync'
|
|
824
|
+
|
|
825
|
+
const adapter = makePersistedAdapter({
|
|
826
|
+
sync: { backend: makeWsSync({ url: syncUrl }) },
|
|
827
|
+
})
|
|
828
|
+
|
|
829
|
+
export const useAppStore = () =>
|
|
830
|
+
useStore({
|
|
831
|
+
storeId: 'expo-todomvc',
|
|
832
|
+
schema,
|
|
833
|
+
adapter,
|
|
834
|
+
batchUpdates,
|
|
835
|
+
boot: (store) => {
|
|
836
|
+
if (store.query(tables.todos.count()) === 0) {
|
|
837
|
+
store.commit(events.todoCreated({ id: crypto.randomUUID(), text: 'Make coffee' }))
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
})
|
|
841
|
+
```
|
|
842
|
+
|
|
697
843
|
## Devtools
|
|
698
844
|
|
|
699
845
|
To open the devtools, run the app and from your terminal press `shift + m`, then select LiveStore Devtools and press `Enter`.
|
|
@@ -250,15 +250,15 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
250
250
|
export const schema = makeSchema({ events, state })
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
-
##
|
|
253
|
+
## Configure the store
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
Create a `store.ts` file in the `src` folder. This file configures the store adapter and exports a custom hook that components will use to access the store.
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
The `useStore()` hook accepts store configuration options (schema, adapter, store ID) and returns a store instance. It suspends while the store is loading, so make sure to use a `Suspense` boundary to handle the loading state.
|
|
258
258
|
|
|
259
|
-
## `getting-started/react-web/
|
|
259
|
+
## `getting-started/react-web/store.ts`
|
|
260
260
|
|
|
261
|
-
```
|
|
261
|
+
```ts filename="getting-started/react-web/store.ts"
|
|
262
262
|
|
|
263
263
|
const adapter = makePersistedAdapter({
|
|
264
264
|
storage: { type: 'opfs' },
|
|
@@ -266,17 +266,13 @@ const adapter = makePersistedAdapter({
|
|
|
266
266
|
sharedWorker: LiveStoreSharedWorker,
|
|
267
267
|
})
|
|
268
268
|
|
|
269
|
-
export const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
batchUpdates
|
|
275
|
-
|
|
276
|
-
>
|
|
277
|
-
<div className="todoapp">{/* Your app components go here */}</div>
|
|
278
|
-
</LiveStoreProvider>
|
|
279
|
-
)
|
|
269
|
+
export const useAppStore = () =>
|
|
270
|
+
useStore({
|
|
271
|
+
storeId: 'app-root',
|
|
272
|
+
schema,
|
|
273
|
+
adapter,
|
|
274
|
+
batchUpdates,
|
|
275
|
+
})
|
|
280
276
|
```
|
|
281
277
|
|
|
282
278
|
### `getting-started/react-web/livestore/schema.ts`
|
|
@@ -341,11 +337,32 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
341
337
|
export const schema = makeSchema({ events, state })
|
|
342
338
|
```
|
|
343
339
|
|
|
344
|
-
|
|
340
|
+
## Set up the store registry
|
|
341
|
+
|
|
342
|
+
To enable store management throughout your app, create a `StoreRegistry` and provide it with a `<StoreRegistryProvider>`. The registry manages store instance lifecycles (loading, caching, disposal).
|
|
343
|
+
|
|
344
|
+
Wrap the provider in a `Suspense` boundary to handle the loading state for when the store is loading.
|
|
345
|
+
|
|
346
|
+
## `getting-started/react-web/Root.tsx`
|
|
347
|
+
|
|
348
|
+
```tsx filename="getting-started/react-web/Root.tsx"
|
|
349
|
+
|
|
350
|
+
export const App: React.FC = () => {
|
|
351
|
+
const [storeRegistry] = useState(() => new StoreRegistry())
|
|
345
352
|
|
|
346
|
-
|
|
353
|
+
return (
|
|
354
|
+
<Suspense fallback={<div>Loading app...</div>}>
|
|
355
|
+
<StoreRegistryProvider storeRegistry={storeRegistry}>
|
|
356
|
+
<div className="todoapp">{/* Your app components go here */}</div>
|
|
357
|
+
</StoreRegistryProvider>
|
|
358
|
+
</Suspense>
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
```
|
|
347
362
|
|
|
348
|
-
|
|
363
|
+
## Commit events
|
|
364
|
+
|
|
365
|
+
After setting up the registry, use the `useAppStore()` hook from any component to access the store and commit events.
|
|
349
366
|
|
|
350
367
|
## `getting-started/react-web/Header.tsx`
|
|
351
368
|
|
|
@@ -354,7 +371,7 @@ Here's an example:
|
|
|
354
371
|
const uiState$ = queryDb(tables.uiState.get(), { label: 'uiState' })
|
|
355
372
|
|
|
356
373
|
export const Header: React.FC = () => {
|
|
357
|
-
const
|
|
374
|
+
const store = useAppStore()
|
|
358
375
|
const { newTodoText } = store.useQuery(uiState$)
|
|
359
376
|
|
|
360
377
|
const updateNewTodoText = (text: string) => store.commit(events.uiStateSet({ newTodoText: text }))
|
|
@@ -446,14 +463,31 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
446
463
|
export const schema = makeSchema({ events, state })
|
|
447
464
|
```
|
|
448
465
|
|
|
466
|
+
### `getting-started/react-web/store.ts`
|
|
467
|
+
|
|
468
|
+
```ts filename="getting-started/react-web/store.ts"
|
|
469
|
+
|
|
470
|
+
const adapter = makePersistedAdapter({
|
|
471
|
+
storage: { type: 'opfs' },
|
|
472
|
+
worker: LiveStoreWorker,
|
|
473
|
+
sharedWorker: LiveStoreSharedWorker,
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
export const useAppStore = () =>
|
|
477
|
+
useStore({
|
|
478
|
+
storeId: 'app-root',
|
|
479
|
+
schema,
|
|
480
|
+
adapter,
|
|
481
|
+
batchUpdates,
|
|
482
|
+
})
|
|
483
|
+
```
|
|
484
|
+
|
|
449
485
|
## Queries
|
|
450
486
|
|
|
451
|
-
To retrieve data from the database,
|
|
487
|
+
To retrieve data from the database, define a query using `queryDb` from `@livestore/livestore`, then execute it with `store.useQuery()`.
|
|
452
488
|
|
|
453
489
|
Consider abstracting queries into a separate file to keep your code organized, though you can also define them directly within components if preferred.
|
|
454
490
|
|
|
455
|
-
Here's an example:
|
|
456
|
-
|
|
457
491
|
## `getting-started/react-web/MainSection.tsx`
|
|
458
492
|
|
|
459
493
|
```tsx filename="getting-started/react-web/MainSection.tsx"
|
|
@@ -472,7 +506,7 @@ const visibleTodos$ = queryDb(
|
|
|
472
506
|
)
|
|
473
507
|
|
|
474
508
|
export const MainSection: React.FC = () => {
|
|
475
|
-
const
|
|
509
|
+
const store = useAppStore()
|
|
476
510
|
|
|
477
511
|
const toggleTodo = React.useCallback(
|
|
478
512
|
({ id, completed }: typeof tables.todos.Type) =>
|
|
@@ -571,3 +605,22 @@ const state = State.SQLite.makeState({ tables, materializers })
|
|
|
571
605
|
|
|
572
606
|
export const schema = makeSchema({ events, state })
|
|
573
607
|
```
|
|
608
|
+
|
|
609
|
+
### `getting-started/react-web/store.ts`
|
|
610
|
+
|
|
611
|
+
```ts filename="getting-started/react-web/store.ts"
|
|
612
|
+
|
|
613
|
+
const adapter = makePersistedAdapter({
|
|
614
|
+
storage: { type: 'opfs' },
|
|
615
|
+
worker: LiveStoreWorker,
|
|
616
|
+
sharedWorker: LiveStoreSharedWorker,
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
export const useAppStore = () =>
|
|
620
|
+
useStore({
|
|
621
|
+
storeId: 'app-root',
|
|
622
|
+
schema,
|
|
623
|
+
adapter,
|
|
624
|
+
batchUpdates,
|
|
625
|
+
})
|
|
626
|
+
```
|
|
@@ -198,7 +198,7 @@ export const schema = makeSchema({ events, state })
|
|
|
198
198
|
|
|
199
199
|
### Add the LiveStore provider
|
|
200
200
|
|
|
201
|
-
To make the LiveStore available throughout your app, wrap your app's root component with the
|
|
201
|
+
To make the LiveStore available throughout your app, wrap your app's root component with the `<LiveStoreProvider>` component from `vue-livestore`. This provider manages your app's data store, loading, and error states.
|
|
202
202
|
|
|
203
203
|
Here's an example:
|
|
204
204
|
|
|
@@ -292,7 +292,7 @@ export const schema = makeSchema({ events, state })
|
|
|
292
292
|
|
|
293
293
|
### Commit events
|
|
294
294
|
|
|
295
|
-
After wrapping your app with the
|
|
295
|
+
After wrapping your app with the `<LiveStoreProvider>`, you can use the `useStore()` hook from any component to commit events.
|
|
296
296
|
|
|
297
297
|
Here's an example:
|
|
298
298
|
|
|
@@ -382,7 +382,7 @@ export const schema = makeSchema({ events, state })
|
|
|
382
382
|
|
|
383
383
|
### Queries
|
|
384
384
|
|
|
385
|
-
To retrieve data from the database, first define a query using `queryDb` from `@livestore/livestore`. Then, execute the query with the `useQuery` hook from `vue-livestore`.
|
|
385
|
+
To retrieve data from the database, first define a query using `queryDb` from `@livestore/livestore`. Then, execute the query with the `useQuery()` hook from `vue-livestore`.
|
|
386
386
|
|
|
387
387
|
Consider abstracting queries into a separate file to keep your code organized, though you can also define them directly within components if preferred.
|
|
388
388
|
|
package/docs/index.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
## State of the documentation
|
|
37
37
|
|
|
38
|
-
Please note that the documentation is still work in progress
|
|
38
|
+
Please note that the documentation is still work in progress. Please [leave feedback](https://github.com/livestorejs/livestore/issues) if you didn't find the information you were looking for and [consider contributing](/sustainable-open-source/contributing/docs) yourself.
|
|
39
39
|
|
|
40
40
|
### Docs for LLMs
|
|
41
41
|
|
|
@@ -102,7 +102,6 @@ Currently, we have the following root-level files:
|
|
|
102
102
|
|
|
103
103
|
### Examples
|
|
104
104
|
|
|
105
|
-
- [Data modeling](/building-with-livestore/examples): How to model data in LiveStore.
|
|
106
105
|
- [Todo app with shared workspaces](/building-with-livestore/examples/todo-workspaces): How to model a todo workspace app with shared workspaces in LiveStore.
|
|
107
106
|
- [Turn-based game](/building-with-livestore/examples/turnbased-game): How to model a turn-based game in LiveStore.
|
|
108
107
|
- [AI agent](/building-with-livestore/examples/ai-agent): How to model an AI agent in LiveStore.
|
package/docs/llms.txt
CHANGED
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
|
|
66
66
|
### Examples
|
|
67
67
|
|
|
68
|
-
- [Data modeling](/building-with-livestore/examples): How to model data in LiveStore.
|
|
69
68
|
- [Todo app with shared workspaces](/building-with-livestore/examples/todo-workspaces): How to model a todo workspace app with shared workspaces in LiveStore.
|
|
70
69
|
- [Turn-based game](/building-with-livestore/examples/turnbased-game): How to model a turn-based game in LiveStore.
|
|
71
70
|
- [AI agent](/building-with-livestore/examples/ai-agent): How to model an AI agent in LiveStore.
|
|
@@ -53,17 +53,17 @@ Note on terminology
|
|
|
53
53
|
|
|
54
54
|
### Query doesn't update properly
|
|
55
55
|
|
|
56
|
-
If you notice the result of a `useQuery` hook is not updating properly, you might be missing some dependencies in the query's hash.
|
|
56
|
+
If you notice the result of a `useQuery()` hook is not updating properly, you might be missing some dependencies in the query's hash.
|
|
57
57
|
|
|
58
58
|
For example, the following query:
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
// Don't do this
|
|
62
|
-
const query$ = useQuery(queryDb(tables.issues.query.where({ id: issueId }).first()))
|
|
62
|
+
const query$ = store.useQuery(queryDb(tables.issues.query.where({ id: issueId }).first()))
|
|
63
63
|
// ^^^^^^^ missing in deps
|
|
64
64
|
|
|
65
65
|
// Do this instead
|
|
66
|
-
const query$ = useQuery(queryDb(tables.issues.query.where({ id: issueId }).first(), { deps: [issueId] }))
|
|
66
|
+
const query$ = store.useQuery(queryDb(tables.issues.query.where({ id: issueId }).first(), { deps: [issueId] }))
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
## `node_modules` related issues
|
|
@@ -14,7 +14,7 @@ On the client, LiveStore provides a reactive SQLite database for application sta
|
|
|
14
14
|
|
|
15
15
|
Application state is materialized into a local SQLite database, offering high-performance, offline-capable data access. This SQLite database is reactive: UI components subscribe to data changes and update automatically when the state changes. LiveStore uses in-memory SQLite for sub-millisecond queries and persistent SQLite for durable storage across application sessions.
|
|
16
16
|
|
|
17
|
-
#### Event
|
|
17
|
+
#### Event sourcing
|
|
18
18
|
|
|
19
19
|
Underpinning the reactive state, LiveStore implements the event sourcing pattern. All data modifications are captured as an immutable, ordered sequence of events. This eventlog serves as the canonical history, enabling reliable state reconstruction and providing inherent auditability, which aids in debugging. The reactive SQLite state is a projection of this eventlog.
|
|
20
20
|
|