@livestore/livestore 0.4.0-dev.21 → 0.4.0-dev.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.js +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/SqliteDbWrapper.d.ts +5 -5
- package/dist/SqliteDbWrapper.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.js +8 -8
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/SqliteDbWrapper.test.js +2 -2
- package/dist/SqliteDbWrapper.test.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +130 -2
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +185 -6
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/LiveStore.test.d.ts +2 -0
- package/dist/effect/LiveStore.test.d.ts.map +1 -0
- package/dist/effect/LiveStore.test.js +42 -0
- package/dist/effect/LiveStore.test.js.map +1 -0
- 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/live-queries/base-class.d.ts +3 -3
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +2 -2
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/client-document-get-query.d.ts +1 -1
- package/dist/live-queries/client-document-get-query.d.ts.map +1 -1
- package/dist/live-queries/client-document-get-query.js +1 -1
- package/dist/live-queries/client-document-get-query.js.map +1 -1
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +2 -2
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.js +14 -14
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +2 -2
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/live-queries/signal.test.js +2 -2
- package/dist/live-queries/signal.test.js.map +1 -1
- package/dist/mod.d.ts +2 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -0
- package/dist/mod.js.map +1 -1
- package/dist/reactive.d.ts +9 -9
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +9 -26
- package/dist/reactive.js.map +1 -1
- package/dist/reactive.test.js +2 -2
- package/dist/reactive.test.js.map +1 -1
- package/dist/store/StoreRegistry.d.ts +215 -0
- package/dist/store/StoreRegistry.d.ts.map +1 -0
- package/dist/store/StoreRegistry.js +267 -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 +381 -0
- package/dist/store/StoreRegistry.test.js.map +1 -0
- package/dist/store/create-store.d.ts +56 -6
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +32 -7
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +1 -1
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +16 -3
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-eventstream.test.js +2 -2
- package/dist/store/store-eventstream.test.js.map +1 -1
- package/dist/store/store-types.d.ts +59 -9
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store-types.test.js +1 -1
- package/dist/store/store-types.test.js.map +1 -1
- package/dist/store/store.d.ts +102 -6
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +148 -47
- package/dist/store/store.js.map +1 -1
- package/dist/utils/dev.js.map +1 -1
- package/dist/utils/stack-info.js +2 -2
- package/dist/utils/stack-info.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +1 -1
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js.map +1 -1
- package/dist/utils/tests/otel.d.ts.map +1 -1
- package/dist/utils/tests/otel.js +5 -5
- package/dist/utils/tests/otel.js.map +1 -1
- package/package.json +59 -18
- package/src/QueryCache.ts +1 -1
- package/src/SqliteDbWrapper.test.ts +4 -2
- package/src/SqliteDbWrapper.ts +12 -11
- package/src/ambient.d.ts +0 -7
- package/src/effect/LiveStore.test.ts +61 -0
- package/src/effect/LiveStore.ts +381 -8
- package/src/effect/mod.ts +13 -1
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +336 -231
- package/src/live-queries/base-class.ts +7 -6
- package/src/live-queries/client-document-get-query.ts +4 -2
- package/src/live-queries/computed.ts +3 -2
- package/src/live-queries/db-query.test.ts +3 -2
- package/src/live-queries/db-query.ts +15 -15
- package/src/live-queries/signal.test.ts +3 -2
- package/src/mod.ts +2 -0
- package/src/reactive.test.ts +3 -2
- package/src/reactive.ts +22 -23
- package/src/store/StoreRegistry.test.ts +540 -0
- package/src/store/StoreRegistry.ts +418 -0
- package/src/store/create-store.ts +76 -15
- package/src/store/devtools.ts +20 -6
- package/src/store/store-eventstream.test.ts +4 -2
- package/src/store/store-types.test.ts +3 -1
- package/src/store/store-types.ts +64 -13
- package/src/store/store.ts +197 -60
- package/src/utils/dev.ts +2 -2
- package/src/utils/stack-info.ts +2 -2
- package/src/utils/tests/fixture.ts +2 -1
- package/src/utils/tests/otel.ts +8 -7
- package/docs/api/index.md +0 -3
- package/docs/building-with-livestore/complex-ui-state/index.md +0 -5
- package/docs/building-with-livestore/crud/index.md +0 -5
- package/docs/building-with-livestore/data-modeling/index.md +0 -1
- package/docs/building-with-livestore/debugging/index.md +0 -17
- package/docs/building-with-livestore/devtools/index.md +0 -79
- package/docs/building-with-livestore/events/index.md +0 -355
- package/docs/building-with-livestore/examples/ai-agent/index.md +0 -5
- package/docs/building-with-livestore/examples/index.md +0 -30
- package/docs/building-with-livestore/examples/todo-workspaces/index.md +0 -891
- package/docs/building-with-livestore/examples/turnbased-game/index.md +0 -7
- package/docs/building-with-livestore/opentelemetry/index.md +0 -208
- package/docs/building-with-livestore/production-checklist/index.md +0 -5
- package/docs/building-with-livestore/reactivity-system/index.md +0 -202
- package/docs/building-with-livestore/rules-for-ai-agents/index.md +0 -9
- package/docs/building-with-livestore/state/materializers/index.md +0 -300
- package/docs/building-with-livestore/state/sql-queries/index.md +0 -72
- package/docs/building-with-livestore/state/sqlite/index.md +0 -45
- package/docs/building-with-livestore/state/sqlite-schema/index.md +0 -306
- package/docs/building-with-livestore/state/sqlite-schema-effect/index.md +0 -300
- package/docs/building-with-livestore/store/index.md +0 -281
- package/docs/building-with-livestore/syncing/index.md +0 -136
- package/docs/building-with-livestore/tools/cli/index.md +0 -177
- package/docs/building-with-livestore/tools/mcp/index.md +0 -187
- package/docs/examples/cloudflare-adapter/index.md +0 -44
- package/docs/examples/expo-adapter/index.md +0 -44
- package/docs/examples/index.md +0 -55
- package/docs/examples/node-adapter/index.md +0 -44
- package/docs/examples/web-adapter/index.md +0 -52
- package/docs/framework-integrations/custom-elements/index.md +0 -142
- package/docs/framework-integrations/react-integration/index.md +0 -918
- package/docs/framework-integrations/solid-integration/index.md +0 -293
- package/docs/framework-integrations/svelte-integration/index.md +0 -42
- package/docs/framework-integrations/vue-integration/index.md +0 -294
- package/docs/getting-started/expo/index.md +0 -736
- package/docs/getting-started/node/index.md +0 -115
- package/docs/getting-started/react-web/index.md +0 -573
- package/docs/getting-started/solid/index.md +0 -3
- package/docs/getting-started/vue/index.md +0 -471
- package/docs/index.md +0 -209
- package/docs/llms.txt +0 -147
- package/docs/misc/CODE_OF_CONDUCT/index.md +0 -133
- package/docs/misc/FAQ/index.md +0 -37
- package/docs/misc/community/index.md +0 -88
- package/docs/misc/credits/index.md +0 -14
- package/docs/misc/design-partners/index.md +0 -13
- package/docs/misc/package-management/index.md +0 -21
- package/docs/misc/performance/index.md +0 -25
- package/docs/misc/resources/index.md +0 -46
- package/docs/misc/state-of-the-project/index.md +0 -37
- package/docs/misc/troubleshooting/index.md +0 -82
- package/docs/overview/concepts/index.md +0 -78
- package/docs/overview/how-livestore-works/index.md +0 -56
- package/docs/overview/introduction/index.md +0 -5
- package/docs/overview/technology-comparison/index.md +0 -40
- package/docs/overview/when-livestore/index.md +0 -81
- package/docs/overview/why-livestore/index.md +0 -5
- package/docs/patterns/ai/index.md +0 -15
- package/docs/patterns/anonymous-user-transition/index.md +0 -10
- package/docs/patterns/app-evolution/index.md +0 -72
- package/docs/patterns/auth/index.md +0 -226
- package/docs/patterns/effect/index.md +0 -1495
- package/docs/patterns/encryption/index.md +0 -6
- package/docs/patterns/external-data/index.md +0 -5
- package/docs/patterns/file-management/index.md +0 -11
- package/docs/patterns/file-structure/index.md +0 -14
- package/docs/patterns/list-ordering/index.md +0 -369
- package/docs/patterns/offline/index.md +0 -32
- package/docs/patterns/orm/index.md +0 -18
- package/docs/patterns/presence/index.md +0 -11
- package/docs/patterns/rich-text-editing/index.md +0 -11
- package/docs/patterns/server-side-clients/index.md +0 -97
- package/docs/patterns/side-effects/index.md +0 -11
- package/docs/patterns/state-machines/index.md +0 -11
- package/docs/patterns/storybook/index.md +0 -192
- package/docs/patterns/undo-redo/index.md +0 -9
- package/docs/patterns/version-control/index.md +0 -8
- package/docs/platform-adapters/cloudflare-durable-object-adapter/index.md +0 -453
- package/docs/platform-adapters/electron-adapter/index.md +0 -15
- package/docs/platform-adapters/expo-adapter/index.md +0 -245
- package/docs/platform-adapters/node-adapter/index.md +0 -160
- package/docs/platform-adapters/tauri-adapter/index.md +0 -15
- package/docs/platform-adapters/web-adapter/index.md +0 -218
- package/docs/sustainable-open-source/contributing/docs/index.md +0 -94
- package/docs/sustainable-open-source/contributing/info/index.md +0 -63
- package/docs/sustainable-open-source/contributing/monorepo/index.md +0 -195
- package/docs/sustainable-open-source/sponsoring/index.md +0 -104
- package/docs/sync-providers/cloudflare/index.md +0 -773
- package/docs/sync-providers/custom/index.md +0 -65
- package/docs/sync-providers/electricsql/index.md +0 -159
- package/docs/sync-providers/s2/index.md +0 -230
- package/docs/tutorial/0-welcome/index.md +0 -48
- package/docs/tutorial/1-setup-starter-project/index.md +0 -105
- package/docs/tutorial/2-deploy-to-cloudflare/index.md +0 -195
- package/docs/tutorial/3-read-and-write-todos-via-livestore/index.md +0 -511
- package/docs/tutorial/4-sync-data-via-cloudflare/index.md +0 -210
- package/docs/tutorial/5-expand-business-logic/index.md +0 -174
- package/docs/tutorial/6-persist-ui-state/index.md +0 -453
- package/docs/tutorial/7-next-steps/index.md +0 -22
- package/docs/understanding-livestore/design-decisions/index.md +0 -33
- package/docs/understanding-livestore/event-sourcing/index.md +0 -40
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
# Store
|
|
2
|
-
|
|
3
|
-
The `Store` is the most common way to interact with LiveStore from your application code. It provides a way to query data, commit events, and subscribe to data changes.
|
|
4
|
-
|
|
5
|
-
## Creating a store
|
|
6
|
-
|
|
7
|
-
For how to create a store in React, see the [React integration docs](/framework-integrations/react-integration). The following example shows how to create a store manually:
|
|
8
|
-
|
|
9
|
-
## `reference/store/create-store.ts`
|
|
10
|
-
|
|
11
|
-
```ts filename="reference/store/create-store.ts"
|
|
12
|
-
|
|
13
|
-
const adapter = makeAdapter({
|
|
14
|
-
storage: { type: 'fs' },
|
|
15
|
-
// sync: { backend: makeWsSync({ url: '...' }) },
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
export const bootstrap = async () => {
|
|
19
|
-
const store = await createStorePromise({
|
|
20
|
-
schema,
|
|
21
|
-
adapter,
|
|
22
|
-
storeId: 'some-store-id',
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
return store
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### `reference/store/schema.ts`
|
|
30
|
-
|
|
31
|
-
```ts filename="reference/store/schema.ts"
|
|
32
|
-
|
|
33
|
-
const tables = {
|
|
34
|
-
todos: State.SQLite.table({
|
|
35
|
-
name: 'todos',
|
|
36
|
-
columns: {
|
|
37
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
38
|
-
text: State.SQLite.text(),
|
|
39
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
40
|
-
},
|
|
41
|
-
}),
|
|
42
|
-
} as const
|
|
43
|
-
|
|
44
|
-
const events = {
|
|
45
|
-
todoCreated: Events.synced({
|
|
46
|
-
name: 'v1.TodoCreated',
|
|
47
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
48
|
-
}),
|
|
49
|
-
} as const
|
|
50
|
-
|
|
51
|
-
const materializers = State.SQLite.materializers(events, {
|
|
52
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
53
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
54
|
-
),
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
58
|
-
|
|
59
|
-
export const schema = makeSchema({ events, state })
|
|
60
|
-
export const storeTables = tables
|
|
61
|
-
export const storeEvents = events
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Using a store
|
|
65
|
-
|
|
66
|
-
### Querying data
|
|
67
|
-
|
|
68
|
-
## `reference/store/query-data.ts`
|
|
69
|
-
|
|
70
|
-
```ts filename="reference/store/query-data.ts"
|
|
71
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet shows query result */
|
|
72
|
-
// ---cut---
|
|
73
|
-
|
|
74
|
-
declare const store: Store
|
|
75
|
-
|
|
76
|
-
const todos = store.query(storeTables.todos)
|
|
77
|
-
console.log(todos)
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### `reference/store/schema.ts`
|
|
81
|
-
|
|
82
|
-
```ts filename="reference/store/schema.ts"
|
|
83
|
-
|
|
84
|
-
const tables = {
|
|
85
|
-
todos: State.SQLite.table({
|
|
86
|
-
name: 'todos',
|
|
87
|
-
columns: {
|
|
88
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
89
|
-
text: State.SQLite.text(),
|
|
90
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
91
|
-
},
|
|
92
|
-
}),
|
|
93
|
-
} as const
|
|
94
|
-
|
|
95
|
-
const events = {
|
|
96
|
-
todoCreated: Events.synced({
|
|
97
|
-
name: 'v1.TodoCreated',
|
|
98
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
99
|
-
}),
|
|
100
|
-
} as const
|
|
101
|
-
|
|
102
|
-
const materializers = State.SQLite.materializers(events, {
|
|
103
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
104
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
105
|
-
),
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
109
|
-
|
|
110
|
-
export const schema = makeSchema({ events, state })
|
|
111
|
-
export const storeTables = tables
|
|
112
|
-
export const storeEvents = events
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Subscribing to data
|
|
116
|
-
|
|
117
|
-
## `reference/store/subscribe.ts`
|
|
118
|
-
|
|
119
|
-
```ts filename="reference/store/subscribe.ts"
|
|
120
|
-
|
|
121
|
-
declare const store: Store
|
|
122
|
-
|
|
123
|
-
const unsubscribe = store.subscribe(storeTables.todos, (todos) => {
|
|
124
|
-
console.log(todos)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
unsubscribe()
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### `reference/store/schema.ts`
|
|
131
|
-
|
|
132
|
-
```ts filename="reference/store/schema.ts"
|
|
133
|
-
|
|
134
|
-
const tables = {
|
|
135
|
-
todos: State.SQLite.table({
|
|
136
|
-
name: 'todos',
|
|
137
|
-
columns: {
|
|
138
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
139
|
-
text: State.SQLite.text(),
|
|
140
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
141
|
-
},
|
|
142
|
-
}),
|
|
143
|
-
} as const
|
|
144
|
-
|
|
145
|
-
const events = {
|
|
146
|
-
todoCreated: Events.synced({
|
|
147
|
-
name: 'v1.TodoCreated',
|
|
148
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
149
|
-
}),
|
|
150
|
-
} as const
|
|
151
|
-
|
|
152
|
-
const materializers = State.SQLite.materializers(events, {
|
|
153
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
154
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
155
|
-
),
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
159
|
-
|
|
160
|
-
export const schema = makeSchema({ events, state })
|
|
161
|
-
export const storeTables = tables
|
|
162
|
-
export const storeEvents = events
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Committing events
|
|
166
|
-
|
|
167
|
-
## `reference/store/commit-event.ts`
|
|
168
|
-
|
|
169
|
-
```ts filename="reference/store/commit-event.ts"
|
|
170
|
-
|
|
171
|
-
declare const store: Store
|
|
172
|
-
|
|
173
|
-
store.commit(storeEvents.todoCreated({ id: '1', text: 'Buy milk' }))
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### `reference/store/schema.ts`
|
|
177
|
-
|
|
178
|
-
```ts filename="reference/store/schema.ts"
|
|
179
|
-
|
|
180
|
-
const tables = {
|
|
181
|
-
todos: State.SQLite.table({
|
|
182
|
-
name: 'todos',
|
|
183
|
-
columns: {
|
|
184
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
185
|
-
text: State.SQLite.text(),
|
|
186
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
187
|
-
},
|
|
188
|
-
}),
|
|
189
|
-
} as const
|
|
190
|
-
|
|
191
|
-
const events = {
|
|
192
|
-
todoCreated: Events.synced({
|
|
193
|
-
name: 'v1.TodoCreated',
|
|
194
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
195
|
-
}),
|
|
196
|
-
} as const
|
|
197
|
-
|
|
198
|
-
const materializers = State.SQLite.materializers(events, {
|
|
199
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
200
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
201
|
-
),
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
205
|
-
|
|
206
|
-
export const schema = makeSchema({ events, state })
|
|
207
|
-
export const storeTables = tables
|
|
208
|
-
export const storeEvents = events
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Streaming events
|
|
212
|
-
|
|
213
|
-
Currently only events confirmed by the sync backend are supported.
|
|
214
|
-
|
|
215
|
-
## `reference/store/stream-events.ts`
|
|
216
|
-
|
|
217
|
-
```ts filename="reference/store/stream-events.ts"
|
|
218
|
-
|
|
219
|
-
declare const store: Store
|
|
220
|
-
|
|
221
|
-
// Run once
|
|
222
|
-
for await (const event of store.events()) {
|
|
223
|
-
console.log('event from leader', event)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Continuos stream
|
|
227
|
-
const iterator = store.events()[Symbol.asyncIterator]()
|
|
228
|
-
try {
|
|
229
|
-
while (true) {
|
|
230
|
-
const { value, done } = await iterator.next()
|
|
231
|
-
if (done) break
|
|
232
|
-
console.log('event from stream:', value)
|
|
233
|
-
}
|
|
234
|
-
} finally {
|
|
235
|
-
await iterator.return?.()
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Shutting down a store
|
|
240
|
-
|
|
241
|
-
LiveStore provides two APIs for shutting down a store:
|
|
242
|
-
|
|
243
|
-
## `reference/store/shutdown.ts`
|
|
244
|
-
|
|
245
|
-
```ts filename="reference/store/shutdown.ts"
|
|
246
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet demonstrates shutdown helpers */
|
|
247
|
-
// ---cut---
|
|
248
|
-
|
|
249
|
-
declare const store: Store
|
|
250
|
-
|
|
251
|
-
const effectShutdown = Effect.gen(function* () {
|
|
252
|
-
yield* Effect.log('Shutting down store')
|
|
253
|
-
yield* store.shutdown()
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
const shutdownWithPromise = async () => {
|
|
257
|
-
await store.shutdownPromise()
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Multiple stores
|
|
262
|
-
|
|
263
|
-
You can create and use multiple stores in the same app. This can be useful when breaking up your data model into smaller pieces.
|
|
264
|
-
|
|
265
|
-
## Development/debugging helpers
|
|
266
|
-
|
|
267
|
-
A store instance also exposes a `_dev` property that contains some helpful methods for development. For convenience you can access a store on `globalThis`/`window` like via `__debugLiveStore.default._dev` (`default` is the store id):
|
|
268
|
-
|
|
269
|
-
```ts
|
|
270
|
-
// Download the SQLite database
|
|
271
|
-
__debugLiveStore.default._dev.downloadDb()
|
|
272
|
-
|
|
273
|
-
// Download the eventlog database
|
|
274
|
-
__debugLiveStore.default._dev.downloadEventlogDb()
|
|
275
|
-
|
|
276
|
-
// Reset the store
|
|
277
|
-
__debugLiveStore.default._dev.hardReset()
|
|
278
|
-
|
|
279
|
-
// See the current sync state
|
|
280
|
-
__debugLiveStore.default._dev.syncStates()
|
|
281
|
-
```
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
# Syncing
|
|
2
|
-
|
|
3
|
-
## How it works
|
|
4
|
-
|
|
5
|
-
LiveStore is based on [the idea of event-sourcing](/understanding-livestore/event-sourcing) which means it syncs events across clients (via a central sync backend) and then materializes the events in the local SQLite database. This means LiveStore isn't syncing the SQLite database itself directly but only the events that are used to materialize the database making sure it's kept in sync across clients.
|
|
6
|
-
|
|
7
|
-
The syncing mechanism is similar to how Git works in that regard that it's based on a "push/pull" model. Upstream events always need to be pulled before a client can push its own events to preserve a [global total order of events](https://medium.com/baseds/ordering-distributed-events-29c1dd9d1eff). Local pending events which haven't been pushed yet need to be rebased on top of the latest upstream events before they can be pushed.
|
|
8
|
-
|
|
9
|
-
<SyncingDiagram />
|
|
10
|
-
|
|
11
|
-
## Events
|
|
12
|
-
|
|
13
|
-
A LiveStore event consists of the following data:
|
|
14
|
-
- `seqNum`: event sequence number
|
|
15
|
-
- `parentSeqNum`: parent event sequence number
|
|
16
|
-
- `name`: event name (refers to a event definition in the schema)
|
|
17
|
-
- `args`: event arguments (encoded using the event's schema definition, usually JSON)
|
|
18
|
-
|
|
19
|
-
### Event sequence numbers
|
|
20
|
-
|
|
21
|
-
- Event sequence numbers: monotonically increasing integers
|
|
22
|
-
- client event sequence number to sync across client sessions (never exposed to the sync backend)
|
|
23
|
-
|
|
24
|
-
### Sync heads
|
|
25
|
-
|
|
26
|
-
- The latest event in a eventlog is referred to as the "head" (similar to how Git refers to the latest commit as the "head").
|
|
27
|
-
- Given that LiveStore does hierarchical syncing between the client session, the client leader and the sync backend, there are three heads (i.e. the client session head, the client leader head, and the sync backend head).
|
|
28
|
-
|
|
29
|
-
## Sync backend
|
|
30
|
-
|
|
31
|
-
The sync backend acts as the global authority and determines the total order of events ("causality"). It's responsible for storing and querying events and for notifying clients when new events are available.
|
|
32
|
-
|
|
33
|
-
### Requirements for sync backend
|
|
34
|
-
|
|
35
|
-
- Needs to provide an efficient way to query an ordered list of events given a starting event ID (often referred to as cursor).
|
|
36
|
-
- Ideally provides a "reactivity" mechanism to notify clients when new events are available (e.g. via WebSocket, HTTP long-polling, etc).
|
|
37
|
-
- Alternatively, the client can periodically query for new events which is less efficient.
|
|
38
|
-
|
|
39
|
-
## Clients
|
|
40
|
-
|
|
41
|
-
- Each client initially chooses a random `clientId` as its globally unique ID
|
|
42
|
-
- LiveStore uses a 6-char nanoid
|
|
43
|
-
- In the unlikely event of a collision which is detected by the sync backend the first time a client tries to push, the client chooses a new random `clientId`, patches the local events with the new `clientId`, and tries again.
|
|
44
|
-
|
|
45
|
-
### Client sessions
|
|
46
|
-
|
|
47
|
-
- Each client has at least one client session
|
|
48
|
-
- Client sessions within the same client share local data
|
|
49
|
-
- In web adapters: multiple tabs/windows can be different sessions within the same client
|
|
50
|
-
- Sessions are identified by a `sessionId` which can persist (e.g., across tab reloads in web)
|
|
51
|
-
- For adapters which support multiple client sessions (e.g. web), LiveStore also supports local syncing across client sessions (e.g. across browser tabs or worker threads)
|
|
52
|
-
- Client session events are not synced to the sync backend
|
|
53
|
-
|
|
54
|
-
## Auth (authentication & authorization)
|
|
55
|
-
|
|
56
|
-
- TODO
|
|
57
|
-
- Provide basic example
|
|
58
|
-
- Encryption
|
|
59
|
-
|
|
60
|
-
## Advanced
|
|
61
|
-
|
|
62
|
-
### Sequence diagrams
|
|
63
|
-
|
|
64
|
-
#### Pulling events (without unpushed events)
|
|
65
|
-
|
|
66
|
-
```d2
|
|
67
|
-
...@../../../../src/content/base.d2
|
|
68
|
-
|
|
69
|
-
shape: sequence_diagram
|
|
70
|
-
|
|
71
|
-
Client: {
|
|
72
|
-
label: "Client"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
SyncBackend: {
|
|
76
|
-
label: "Sync Backend"
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
Client -> SyncBackend: "`pull` request\n(head_cursor)"
|
|
80
|
-
SyncBackend -> SyncBackend: "Get new events\n(since head_cursor)"
|
|
81
|
-
SyncBackend -> Client: "New events"
|
|
82
|
-
Client -> Client: "Client is in sync"
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
#### Pushing events
|
|
86
|
-
|
|
87
|
-
```d2
|
|
88
|
-
...@../../../../src/content/base.d2
|
|
89
|
-
|
|
90
|
-
shape: sequence_diagram
|
|
91
|
-
|
|
92
|
-
Client: {
|
|
93
|
-
label: "Client"
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
SyncBackend: {
|
|
97
|
-
label: "Sync Backend"
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
Client -> Client: "Commit events"
|
|
101
|
-
Client -> SyncBackend: "`push` request\n(new_local_events)"
|
|
102
|
-
SyncBackend -> SyncBackend: "Validate & persist"
|
|
103
|
-
SyncBackend -> Client: "Push success"
|
|
104
|
-
Client -> Client: "Client is in sync"
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Rebasing
|
|
108
|
-
|
|
109
|
-
### Merge conflicts
|
|
110
|
-
|
|
111
|
-
- Merge conflict handling isn't implemented yet (see [this issue](https://github.com/livestorejs/livestore/issues/253)).
|
|
112
|
-
- Merge conflict detection and resolution will be based on the upcoming [facts system functionality](https://github.com/livestorejs/livestore/issues/254).
|
|
113
|
-
|
|
114
|
-
### Compaction
|
|
115
|
-
|
|
116
|
-
- Compaction isn't implemented yet (see [this issue](https://github.com/livestorejs/livestore/issues/136))
|
|
117
|
-
- Compaction will be based on the upcoming [facts system functionality](https://github.com/livestorejs/livestore/issues/254).
|
|
118
|
-
|
|
119
|
-
### Partitioning
|
|
120
|
-
|
|
121
|
-
- Currently LiveStore assumes a 1:1 mapping between an eventlog and a SQLite database.
|
|
122
|
-
- In the future, LiveStore aims to support multiple eventlogs (see [this issue](https://github.com/livestorejs/livestore/issues/255)).
|
|
123
|
-
|
|
124
|
-
## Design decisions / trade-offs
|
|
125
|
-
|
|
126
|
-
- Require a central sync backend to enforce a global total order of events.
|
|
127
|
-
- This means LiveStore can't be used in a fully decentralized/P2P manner.
|
|
128
|
-
- Do rebasing on the client side (instead of on the sync backend). This allows the user to have more control over the rebase process.
|
|
129
|
-
|
|
130
|
-
## Notes
|
|
131
|
-
|
|
132
|
-
- Rich text data is best handled via CRDTs (see [#263](https://github.com/livestorejs/livestore/issues/263))
|
|
133
|
-
|
|
134
|
-
## Further reading
|
|
135
|
-
|
|
136
|
-
- Distributed Systems lecture series by Martin Kleppmann: [YouTube playlist](https://www.youtube.com/playlist?list=PLeKd45zvjcDFUEv_ohr_HdUFe97RItdiB) / [lecture notes](https://www.cl.cam.ac.uk/teaching/2122/ConcDisSys/dist-sys-notes.pdf)
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# LiveStore CLI
|
|
2
|
-
|
|
3
|
-
The LiveStore CLI provides tools for creating new projects and integrating with AI assistants through MCP (Model Context Protocol).
|
|
4
|
-
|
|
5
|
-
:::caution[Experimental - Not Production Ready]
|
|
6
|
-
The LiveStore CLI is an experimental preview and not ready for production use. APIs, commands, and functionality may change significantly. Use for development and evaluation purposes only.
|
|
7
|
-
:::
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
You can use the LiveStore CLI in several ways:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# Recommended: Use bunx (no installation needed)
|
|
15
|
-
bunx @livestore/cli --help
|
|
16
|
-
|
|
17
|
-
# Alternative options:
|
|
18
|
-
npm install -g @livestore/cli # Global install
|
|
19
|
-
npm install -D @livestore/cli # Project install
|
|
20
|
-
npx @livestore/cli --help # Use with npx
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Commands
|
|
24
|
-
|
|
25
|
-
### `livestore new-project`
|
|
26
|
-
Create a new LiveStore project from available examples.
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
# Interactive selection
|
|
30
|
-
livestore new-project
|
|
31
|
-
|
|
32
|
-
# Specify example and path
|
|
33
|
-
livestore new-project --example web-todomvc my-project
|
|
34
|
-
|
|
35
|
-
# Use specific branch
|
|
36
|
-
livestore new-project --branch dev
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### `livestore mcp`
|
|
40
|
-
MCP server tools for AI assistant integration. See [MCP Integration](/building-with-livestore/tools/mcp) for details.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
# Start MCP server
|
|
44
|
-
livestore mcp
|
|
45
|
-
|
|
46
|
-
# Available subcommands
|
|
47
|
-
livestore mcp coach # AI coaching assistant (requires API key env var)
|
|
48
|
-
livestore mcp tools # Development tools server
|
|
49
|
-
|
|
50
|
-
# Coach command requires API key - check implementation for specific variable name
|
|
51
|
-
# Example: OPENAI_API_KEY=your_key livestore mcp coach
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### `livestore sync`
|
|
55
|
-
|
|
56
|
-
Import and export events from the sync backend. Useful for backup, migration, and debugging.
|
|
57
|
-
|
|
58
|
-
#### Export
|
|
59
|
-
|
|
60
|
-
Export all events from the sync backend to a JSON file:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
livestore sync export \
|
|
64
|
-
--config livestore-cli.config.ts \
|
|
65
|
-
--store-id my-store \
|
|
66
|
-
events.json
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Example output:**
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
Exporting events from LiveStore...
|
|
73
|
-
Config: livestore-cli.config.ts
|
|
74
|
-
Store ID: my-store
|
|
75
|
-
Output: events.json
|
|
76
|
-
|
|
77
|
-
Connecting to sync backend...
|
|
78
|
-
✓ Connected to sync backend: @livestore/cf-sync
|
|
79
|
-
Pulling events from sync backend...
|
|
80
|
-
Pulled 127 events
|
|
81
|
-
Exported 127 events to /path/to/events.json
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Options:**
|
|
85
|
-
- `--config, -c` (required) - Path to a config module that exports `schema` and `syncBackend`
|
|
86
|
-
- `--store-id, -i` (required) - Store identifier
|
|
87
|
-
- `--client-id` - Client identifier for the sync connection (default: `cli-export`)
|
|
88
|
-
- Large exports load data in memory; for very large stores run this on a machine with sufficient RAM.
|
|
89
|
-
|
|
90
|
-
#### Import
|
|
91
|
-
|
|
92
|
-
Import events from a JSON file to the sync backend:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
livestore sync import \
|
|
96
|
-
--config livestore-cli.config.ts \
|
|
97
|
-
--store-id my-store \
|
|
98
|
-
events.json
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Example output:**
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
Importing events to LiveStore...
|
|
105
|
-
Config: livestore-cli.config.ts
|
|
106
|
-
Store ID: my-store
|
|
107
|
-
Input: events.json
|
|
108
|
-
|
|
109
|
-
Reading import file...
|
|
110
|
-
Found 127 events in export file
|
|
111
|
-
Checking for existing events...
|
|
112
|
-
Connecting to sync backend...
|
|
113
|
-
✓ Connected to sync backend: @livestore/cf-sync
|
|
114
|
-
Pushing events to sync backend...
|
|
115
|
-
Pushed 100/127 events
|
|
116
|
-
Pushed 127/127 events
|
|
117
|
-
Successfully imported 127 events
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
**Options:**
|
|
121
|
-
- `--config, -c` (required) - Path to a config module that exports `schema` and `syncBackend`
|
|
122
|
-
- `--store-id, -i` (required) - Store identifier
|
|
123
|
-
- `--client-id` - Client identifier for the sync connection (default: `cli-import`)
|
|
124
|
-
- `--force, -f` - Force import even if store ID in the file doesn't match
|
|
125
|
-
- `--dry-run` - Validate the import file without actually importing
|
|
126
|
-
- Large imports are memory-intensive because the JSON is loaded fully before validation/push.
|
|
127
|
-
|
|
128
|
-
**Note:** The sync backend must be empty when importing. The import will fail if events already exist.
|
|
129
|
-
|
|
130
|
-
### Config file
|
|
131
|
-
|
|
132
|
-
Both MCP and sync commands require a config file (conventionally named `livestore-cli.config.ts`) that exports:
|
|
133
|
-
|
|
134
|
-
## `reference/cli/config.ts`
|
|
135
|
-
|
|
136
|
-
```ts filename="reference/cli/config.ts"
|
|
137
|
-
|
|
138
|
-
// Re-export your app's schema (adjust path to your project)
|
|
139
|
-
export { schema } from './schema.ts'
|
|
140
|
-
|
|
141
|
-
// Provide a sync backend constructor
|
|
142
|
-
export const syncBackend = makeWsSync({
|
|
143
|
-
url: process.env.LIVESTORE_SYNC_URL ?? 'ws://localhost:8787',
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// Optionally, pass an auth payload (must be JSON-serializable)
|
|
147
|
-
export const syncPayload = {
|
|
148
|
-
authToken: process.env.LIVESTORE_SYNC_AUTH_TOKEN,
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### `reference/cli/schema.ts`
|
|
153
|
-
|
|
154
|
-
```ts filename="reference/cli/schema.ts"
|
|
155
|
-
|
|
156
|
-
const events = {}
|
|
157
|
-
|
|
158
|
-
const tables = {
|
|
159
|
-
todos: State.SQLite.table({
|
|
160
|
-
name: 'todos',
|
|
161
|
-
columns: {
|
|
162
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
163
|
-
text: State.SQLite.text(),
|
|
164
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
165
|
-
},
|
|
166
|
-
}),
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const state = State.SQLite.makeState({ tables, materializers: {} })
|
|
170
|
-
|
|
171
|
-
export const schema = makeSchema({ events, state })
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Global options
|
|
175
|
-
|
|
176
|
-
- `--verbose` - Enable verbose logging
|
|
177
|
-
- `--help` - Show command help
|