@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.
Files changed (216) hide show
  1. package/README.md +0 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/QueryCache.js +1 -1
  4. package/dist/QueryCache.js.map +1 -1
  5. package/dist/SqliteDbWrapper.d.ts +5 -5
  6. package/dist/SqliteDbWrapper.d.ts.map +1 -1
  7. package/dist/SqliteDbWrapper.js +8 -8
  8. package/dist/SqliteDbWrapper.js.map +1 -1
  9. package/dist/SqliteDbWrapper.test.js +2 -2
  10. package/dist/SqliteDbWrapper.test.js.map +1 -1
  11. package/dist/effect/LiveStore.d.ts +130 -2
  12. package/dist/effect/LiveStore.d.ts.map +1 -1
  13. package/dist/effect/LiveStore.js +185 -6
  14. package/dist/effect/LiveStore.js.map +1 -1
  15. package/dist/effect/LiveStore.test.d.ts +2 -0
  16. package/dist/effect/LiveStore.test.d.ts.map +1 -0
  17. package/dist/effect/LiveStore.test.js +42 -0
  18. package/dist/effect/LiveStore.test.js.map +1 -0
  19. package/dist/effect/mod.d.ts +1 -1
  20. package/dist/effect/mod.d.ts.map +1 -1
  21. package/dist/effect/mod.js +3 -1
  22. package/dist/effect/mod.js.map +1 -1
  23. package/dist/live-queries/base-class.d.ts +3 -3
  24. package/dist/live-queries/base-class.d.ts.map +1 -1
  25. package/dist/live-queries/base-class.js +2 -2
  26. package/dist/live-queries/base-class.js.map +1 -1
  27. package/dist/live-queries/client-document-get-query.d.ts +1 -1
  28. package/dist/live-queries/client-document-get-query.d.ts.map +1 -1
  29. package/dist/live-queries/client-document-get-query.js +1 -1
  30. package/dist/live-queries/client-document-get-query.js.map +1 -1
  31. package/dist/live-queries/computed.d.ts.map +1 -1
  32. package/dist/live-queries/computed.js +2 -2
  33. package/dist/live-queries/computed.js.map +1 -1
  34. package/dist/live-queries/db-query.js +14 -14
  35. package/dist/live-queries/db-query.js.map +1 -1
  36. package/dist/live-queries/db-query.test.js +2 -2
  37. package/dist/live-queries/db-query.test.js.map +1 -1
  38. package/dist/live-queries/signal.test.js +2 -2
  39. package/dist/live-queries/signal.test.js.map +1 -1
  40. package/dist/mod.d.ts +2 -1
  41. package/dist/mod.d.ts.map +1 -1
  42. package/dist/mod.js +1 -0
  43. package/dist/mod.js.map +1 -1
  44. package/dist/reactive.d.ts +9 -9
  45. package/dist/reactive.d.ts.map +1 -1
  46. package/dist/reactive.js +9 -26
  47. package/dist/reactive.js.map +1 -1
  48. package/dist/reactive.test.js +2 -2
  49. package/dist/reactive.test.js.map +1 -1
  50. package/dist/store/StoreRegistry.d.ts +215 -0
  51. package/dist/store/StoreRegistry.d.ts.map +1 -0
  52. package/dist/store/StoreRegistry.js +267 -0
  53. package/dist/store/StoreRegistry.js.map +1 -0
  54. package/dist/store/StoreRegistry.test.d.ts +2 -0
  55. package/dist/store/StoreRegistry.test.d.ts.map +1 -0
  56. package/dist/store/StoreRegistry.test.js +381 -0
  57. package/dist/store/StoreRegistry.test.js.map +1 -0
  58. package/dist/store/create-store.d.ts +56 -6
  59. package/dist/store/create-store.d.ts.map +1 -1
  60. package/dist/store/create-store.js +32 -7
  61. package/dist/store/create-store.js.map +1 -1
  62. package/dist/store/devtools.d.ts +1 -1
  63. package/dist/store/devtools.d.ts.map +1 -1
  64. package/dist/store/devtools.js +16 -3
  65. package/dist/store/devtools.js.map +1 -1
  66. package/dist/store/store-eventstream.test.js +2 -2
  67. package/dist/store/store-eventstream.test.js.map +1 -1
  68. package/dist/store/store-types.d.ts +59 -9
  69. package/dist/store/store-types.d.ts.map +1 -1
  70. package/dist/store/store-types.js.map +1 -1
  71. package/dist/store/store-types.test.js +1 -1
  72. package/dist/store/store-types.test.js.map +1 -1
  73. package/dist/store/store.d.ts +102 -6
  74. package/dist/store/store.d.ts.map +1 -1
  75. package/dist/store/store.js +148 -47
  76. package/dist/store/store.js.map +1 -1
  77. package/dist/utils/dev.js.map +1 -1
  78. package/dist/utils/stack-info.js +2 -2
  79. package/dist/utils/stack-info.js.map +1 -1
  80. package/dist/utils/tests/fixture.d.ts +1 -1
  81. package/dist/utils/tests/fixture.d.ts.map +1 -1
  82. package/dist/utils/tests/fixture.js.map +1 -1
  83. package/dist/utils/tests/otel.d.ts.map +1 -1
  84. package/dist/utils/tests/otel.js +5 -5
  85. package/dist/utils/tests/otel.js.map +1 -1
  86. package/package.json +59 -18
  87. package/src/QueryCache.ts +1 -1
  88. package/src/SqliteDbWrapper.test.ts +4 -2
  89. package/src/SqliteDbWrapper.ts +12 -11
  90. package/src/ambient.d.ts +0 -7
  91. package/src/effect/LiveStore.test.ts +61 -0
  92. package/src/effect/LiveStore.ts +381 -8
  93. package/src/effect/mod.ts +13 -1
  94. package/src/live-queries/__snapshots__/db-query.test.ts.snap +336 -231
  95. package/src/live-queries/base-class.ts +7 -6
  96. package/src/live-queries/client-document-get-query.ts +4 -2
  97. package/src/live-queries/computed.ts +3 -2
  98. package/src/live-queries/db-query.test.ts +3 -2
  99. package/src/live-queries/db-query.ts +15 -15
  100. package/src/live-queries/signal.test.ts +3 -2
  101. package/src/mod.ts +2 -0
  102. package/src/reactive.test.ts +3 -2
  103. package/src/reactive.ts +22 -23
  104. package/src/store/StoreRegistry.test.ts +540 -0
  105. package/src/store/StoreRegistry.ts +418 -0
  106. package/src/store/create-store.ts +76 -15
  107. package/src/store/devtools.ts +20 -6
  108. package/src/store/store-eventstream.test.ts +4 -2
  109. package/src/store/store-types.test.ts +3 -1
  110. package/src/store/store-types.ts +64 -13
  111. package/src/store/store.ts +197 -60
  112. package/src/utils/dev.ts +2 -2
  113. package/src/utils/stack-info.ts +2 -2
  114. package/src/utils/tests/fixture.ts +2 -1
  115. package/src/utils/tests/otel.ts +8 -7
  116. package/docs/api/index.md +0 -3
  117. package/docs/building-with-livestore/complex-ui-state/index.md +0 -5
  118. package/docs/building-with-livestore/crud/index.md +0 -5
  119. package/docs/building-with-livestore/data-modeling/index.md +0 -1
  120. package/docs/building-with-livestore/debugging/index.md +0 -17
  121. package/docs/building-with-livestore/devtools/index.md +0 -79
  122. package/docs/building-with-livestore/events/index.md +0 -355
  123. package/docs/building-with-livestore/examples/ai-agent/index.md +0 -5
  124. package/docs/building-with-livestore/examples/index.md +0 -30
  125. package/docs/building-with-livestore/examples/todo-workspaces/index.md +0 -891
  126. package/docs/building-with-livestore/examples/turnbased-game/index.md +0 -7
  127. package/docs/building-with-livestore/opentelemetry/index.md +0 -208
  128. package/docs/building-with-livestore/production-checklist/index.md +0 -5
  129. package/docs/building-with-livestore/reactivity-system/index.md +0 -202
  130. package/docs/building-with-livestore/rules-for-ai-agents/index.md +0 -9
  131. package/docs/building-with-livestore/state/materializers/index.md +0 -300
  132. package/docs/building-with-livestore/state/sql-queries/index.md +0 -72
  133. package/docs/building-with-livestore/state/sqlite/index.md +0 -45
  134. package/docs/building-with-livestore/state/sqlite-schema/index.md +0 -306
  135. package/docs/building-with-livestore/state/sqlite-schema-effect/index.md +0 -300
  136. package/docs/building-with-livestore/store/index.md +0 -281
  137. package/docs/building-with-livestore/syncing/index.md +0 -136
  138. package/docs/building-with-livestore/tools/cli/index.md +0 -177
  139. package/docs/building-with-livestore/tools/mcp/index.md +0 -187
  140. package/docs/examples/cloudflare-adapter/index.md +0 -44
  141. package/docs/examples/expo-adapter/index.md +0 -44
  142. package/docs/examples/index.md +0 -55
  143. package/docs/examples/node-adapter/index.md +0 -44
  144. package/docs/examples/web-adapter/index.md +0 -52
  145. package/docs/framework-integrations/custom-elements/index.md +0 -142
  146. package/docs/framework-integrations/react-integration/index.md +0 -918
  147. package/docs/framework-integrations/solid-integration/index.md +0 -293
  148. package/docs/framework-integrations/svelte-integration/index.md +0 -42
  149. package/docs/framework-integrations/vue-integration/index.md +0 -294
  150. package/docs/getting-started/expo/index.md +0 -736
  151. package/docs/getting-started/node/index.md +0 -115
  152. package/docs/getting-started/react-web/index.md +0 -573
  153. package/docs/getting-started/solid/index.md +0 -3
  154. package/docs/getting-started/vue/index.md +0 -471
  155. package/docs/index.md +0 -209
  156. package/docs/llms.txt +0 -147
  157. package/docs/misc/CODE_OF_CONDUCT/index.md +0 -133
  158. package/docs/misc/FAQ/index.md +0 -37
  159. package/docs/misc/community/index.md +0 -88
  160. package/docs/misc/credits/index.md +0 -14
  161. package/docs/misc/design-partners/index.md +0 -13
  162. package/docs/misc/package-management/index.md +0 -21
  163. package/docs/misc/performance/index.md +0 -25
  164. package/docs/misc/resources/index.md +0 -46
  165. package/docs/misc/state-of-the-project/index.md +0 -37
  166. package/docs/misc/troubleshooting/index.md +0 -82
  167. package/docs/overview/concepts/index.md +0 -78
  168. package/docs/overview/how-livestore-works/index.md +0 -56
  169. package/docs/overview/introduction/index.md +0 -5
  170. package/docs/overview/technology-comparison/index.md +0 -40
  171. package/docs/overview/when-livestore/index.md +0 -81
  172. package/docs/overview/why-livestore/index.md +0 -5
  173. package/docs/patterns/ai/index.md +0 -15
  174. package/docs/patterns/anonymous-user-transition/index.md +0 -10
  175. package/docs/patterns/app-evolution/index.md +0 -72
  176. package/docs/patterns/auth/index.md +0 -226
  177. package/docs/patterns/effect/index.md +0 -1495
  178. package/docs/patterns/encryption/index.md +0 -6
  179. package/docs/patterns/external-data/index.md +0 -5
  180. package/docs/patterns/file-management/index.md +0 -11
  181. package/docs/patterns/file-structure/index.md +0 -14
  182. package/docs/patterns/list-ordering/index.md +0 -369
  183. package/docs/patterns/offline/index.md +0 -32
  184. package/docs/patterns/orm/index.md +0 -18
  185. package/docs/patterns/presence/index.md +0 -11
  186. package/docs/patterns/rich-text-editing/index.md +0 -11
  187. package/docs/patterns/server-side-clients/index.md +0 -97
  188. package/docs/patterns/side-effects/index.md +0 -11
  189. package/docs/patterns/state-machines/index.md +0 -11
  190. package/docs/patterns/storybook/index.md +0 -192
  191. package/docs/patterns/undo-redo/index.md +0 -9
  192. package/docs/patterns/version-control/index.md +0 -8
  193. package/docs/platform-adapters/cloudflare-durable-object-adapter/index.md +0 -453
  194. package/docs/platform-adapters/electron-adapter/index.md +0 -15
  195. package/docs/platform-adapters/expo-adapter/index.md +0 -245
  196. package/docs/platform-adapters/node-adapter/index.md +0 -160
  197. package/docs/platform-adapters/tauri-adapter/index.md +0 -15
  198. package/docs/platform-adapters/web-adapter/index.md +0 -218
  199. package/docs/sustainable-open-source/contributing/docs/index.md +0 -94
  200. package/docs/sustainable-open-source/contributing/info/index.md +0 -63
  201. package/docs/sustainable-open-source/contributing/monorepo/index.md +0 -195
  202. package/docs/sustainable-open-source/sponsoring/index.md +0 -104
  203. package/docs/sync-providers/cloudflare/index.md +0 -773
  204. package/docs/sync-providers/custom/index.md +0 -65
  205. package/docs/sync-providers/electricsql/index.md +0 -159
  206. package/docs/sync-providers/s2/index.md +0 -230
  207. package/docs/tutorial/0-welcome/index.md +0 -48
  208. package/docs/tutorial/1-setup-starter-project/index.md +0 -105
  209. package/docs/tutorial/2-deploy-to-cloudflare/index.md +0 -195
  210. package/docs/tutorial/3-read-and-write-todos-via-livestore/index.md +0 -511
  211. package/docs/tutorial/4-sync-data-via-cloudflare/index.md +0 -210
  212. package/docs/tutorial/5-expand-business-logic/index.md +0 -174
  213. package/docs/tutorial/6-persist-ui-state/index.md +0 -453
  214. package/docs/tutorial/7-next-steps/index.md +0 -22
  215. package/docs/understanding-livestore/design-decisions/index.md +0 -33
  216. package/docs/understanding-livestore/event-sourcing/index.md +0 -40
@@ -1,79 +0,0 @@
1
- # Devtools
2
-
3
- NOTE: Once LiveStore is open source, the devtools will be a [sponsor-only benefit](/sustainable-open-source/sponsoring).
4
-
5
- ## Features
6
-
7
- - Real-time data browser with 2-way sync
8
- ![](https://share.cleanshot.com/F79hpTCY+)
9
- - Query inspector
10
- ![](https://share.cleanshot.com/pkr2jqgb+)
11
- - Eventlog browser
12
- ![](https://share.cleanshot.com/PTgXpcPm+)
13
- - Sync status
14
- ![](https://share.cleanshot.com/VsKY3KnR+)
15
- - Export/import
16
- ![](https://share.cleanshot.com/LQKYX6rq+)
17
- - Reactivity graph / signals inspector
18
- ![](https://share.cleanshot.com/M26FHD6j+)
19
- - SQLite playground
20
- ![](https://share.cleanshot.com/BcWmLmn2+)
21
-
22
- ## Adapters
23
-
24
- ### `@livestore/adapter-web`:
25
-
26
- Requires the `@livestore/devtools-vite` package to be installed and configured in your Vite config:
27
-
28
- ## `reference/devtools/vite-config.ts`
29
-
30
- ```ts filename="reference/devtools/vite-config.ts"
31
-
32
- export default defineConfig({
33
- // ...
34
- plugins: [livestoreDevtoolsPlugin({ schemaPath: './src/livestore/schema.ts' })],
35
- })
36
- ```
37
-
38
- The devtools can be opened in a separate tab (via e.g. `localhost:3000/_livestore/web). You should see the Devtools URL logged in the browser console when running the app.
39
-
40
- #### Chrome extension
41
-
42
- You can also use the Devtools Chrome extension.
43
-
44
- ![](https://share.cleanshot.com/wlM4ybFn+)
45
-
46
- Please make sure to manually install the extension version matching the LiveStore version you are using by downloading the appropriate version from the [GitHub releases page](https://github.com/livestorejs/livestore/releases) and installing it manually via `chrome://extensions/`.
47
-
48
- To install the extension:
49
-
50
- 1. **Unpack the ZIP file** (e.g. `livestore-devtools-chrome-0.3.0.zip`) into a folder on your computer.
51
- 2. Navigate to `chrome://extensions/` and enable **Developer mode** (toggle in the top-right corner).
52
- 3. Click **"Load unpacked"** and select the unpacked folder or drag and drop the folder onto the page.
53
-
54
- ### `@livestore/adapter-expo`:
55
-
56
- Requires the `@livestore/devtools-expo` package to be installed and configured in your metro config:
57
-
58
- ## `reference/devtools/metro-config.ts`
59
-
60
- ```ts filename="reference/devtools/metro-config.ts"
61
- // @noErrors
62
- // metro.config.js
63
- const { getDefaultConfig } = require('expo/metro-config')
64
- const { addLiveStoreDevtoolsMiddleware } = require('@livestore/devtools-expo')
65
-
66
- const config = getDefaultConfig(__dirname)
67
-
68
- addLiveStoreDevtoolsMiddleware(config, { schemaPath: './src/livestore/schema.ts' })
69
-
70
- module.exports = config
71
- ```
72
-
73
- You can open the devtools by pressing `Shift+m` in the Expo CLI process and then selecting `@livestore/devtools-expo` which will open the devtools in a new tab.
74
-
75
- ### `@livestore/adapter-node`:
76
-
77
- Devtools are configured out of the box for the `makePersistedAdapter` variant (note currently not supported for the `makeInMemoryAdapter` variant).
78
-
79
- You should see the Devtools URL logged when running the app.
@@ -1,355 +0,0 @@
1
- # Events
2
-
3
- ## Event definitions
4
-
5
- There are two types of events:
6
-
7
- - `synced`: Events that are synced across clients
8
- - `clientOnly`: Events that are only processed locally on the client (but still synced across client sessions e.g. across browser tabs/windows)
9
-
10
- An event definition consists of a unique name of the event and a schema for the event arguments. It's recommended to version event definitions to make it easier to evolve them over time.
11
-
12
- Events will be synced across clients and materialized into state (i.e. SQLite tables) via [materializers](/building-with-livestore/state/materializers).
13
-
14
- ### Example
15
-
16
- ## `reference/events/livestore-schema.ts`
17
-
18
- ```ts filename="reference/events/livestore-schema.ts"
19
- // livestore/schema.ts
20
-
21
- export const events = {
22
- todoCreated: Events.synced({
23
- name: 'v1.TodoCreated',
24
- schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
25
- }),
26
- todoCompleted: Events.synced({
27
- name: 'v1.TodoCompleted',
28
- schema: Schema.Struct({ id: Schema.String }),
29
- }),
30
- } as const
31
- ```
32
-
33
- ### Commiting events
34
-
35
- ## `reference/events/commit.ts`
36
-
37
- ```ts filename="reference/events/commit.ts"
38
- // somewhere in your app
39
-
40
- declare const store: Store
41
-
42
- store.commit(events.todoCreated({ id: '1', text: 'Buy milk' }))
43
- ```
44
-
45
- ### `reference/events/livestore-schema.ts`
46
-
47
- ```ts filename="reference/events/livestore-schema.ts"
48
- // livestore/schema.ts
49
-
50
- export const events = {
51
- todoCreated: Events.synced({
52
- name: 'v1.TodoCreated',
53
- schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
54
- }),
55
- todoCompleted: Events.synced({
56
- name: 'v1.TodoCompleted',
57
- schema: Schema.Struct({ id: Schema.String }),
58
- }),
59
- } as const
60
- ```
61
-
62
- ### Streaming events
63
-
64
- Currently only events confirmed by the sync backend are supported.
65
-
66
- ## `reference/events/stream.ts`
67
-
68
- ```ts filename="reference/events/stream.ts"
69
-
70
- declare const store: Store
71
-
72
- for await (const event of store.events()) {
73
- console.log('event from leader', event)
74
- }
75
- ```
76
-
77
- ### Best practices
78
-
79
- - It's strongly recommended to use past-tense event names (e.g. `todoCreated`/`createdTodo` instead of `todoCreate`/`createTodo`) to indicate something already occurred.
80
- - When generating IDs for events (e.g. for the todo in the example above), it's recommended to use a globally unique ID generator (e.g. UUID, nanoid, etc.) to avoid conflicts. For convenience, `@livestore/livestore` re-exports the `nanoid` function.
81
- - TODO: write down more best practices
82
- - TODO: mention AI linting (either manually or via a CI step)
83
- - core idea: feed list of best practices to AI and check if events adhere to them + get suggestions if not
84
- - It's recommended to avoid `DELETE` events and instead use soft-deletes (e.g. add a `deleted` date/boolean column with a default value of `null`). This helps avoid some common concurrency issues.
85
-
86
- ## Nodes in the LiveStore system
87
-
88
- <EventNodesDiagram class="my-8" />
89
-
90
- #### Client Session
91
- - `SyncState`: in-memory for pending events only
92
- - `dbState`: Materialized state that matches the schema (SQLite database)
93
-
94
- #### Client Leader
95
- - `dbEventLog`: Database that stores the durable event log and tracks global sequence of events which the backend has acknowledged
96
- - `dbState`: Materialized state that matches the schema (SQLite database) so leader can materialize events and handle rollbacks
97
-
98
- #### Sync backend
99
- - `EventLog`: Any storage solution that supports pushing and pulling events. Append only storage.
100
-
101
- ### Happy event path
102
-
103
- 1. Client session commits an event
104
- - Client session merges the new event `e3` into its local `SyncState` as pending
105
- - Client session pushes the pending event to the client leader thread; the leader still shows the previous head until it persists the event
106
- <EventsVisualizer
107
- client={["e1", "e2", "A:e3'{todoCreated}"]}
108
- leader={["e1", "e2"]}
109
- backend={["e1", "e2"]} />
110
-
111
- 2. Client leader persists the event
112
- - Client leader materializes the event and writes it to `EventLog`
113
- - Event `e3` remains unconfirmed from the leader's perspective because the backend has not acknowledged it yet
114
- <EventsVisualizer
115
- client={["e1", "e2", "A:e3'{todoCreated}"]}
116
- leader={["e1", "e2", "A:e3'{todoCreated}"]}
117
- backend={["e1", "e2"]} />
118
-
119
- 3. Leader thread emits signal back to subscribed clients
120
- - Client session merges the authoritative event from the leader
121
- - Event transitions from pending to confirmed on the client while the leader still waits for backend confirmation
122
- <EventsVisualizer
123
- client={["e1", "e2", "A:e3{todoCreated}"]}
124
- leader={["e1", "e2", "A:e3'{todoCreated}"]}
125
- backend={["e1", "e2"]} />
126
-
127
- 4. Leader thread pushes the event to the sync backend
128
- - Leader pushes the pending event upstream; it stays marked as unconfirmed in the client leader's eventlog until the backend acknowledges receipt
129
- <EventsVisualizer
130
- client={["e1", "e2", "A:e3{todoCreated}"]}
131
- leader={["e1", "e2", "A:e3'{todoCreated}"]}
132
- backend={["e1", "e2"]} />
133
-
134
- 5. Sync backend pulls the event from the client leader
135
- - Sync backend acknowledges the event and advances its head
136
- - Client leader receives the acknowledgement and marks the event as confirmed
137
- - All heads align on the confirmed sequence
138
- <EventsVisualizer
139
- client={["e1", "e2", "A:e3{todoCreated}"]}
140
- leader={["e1", "e2", "A:e3{todoCreated}"]}
141
- backend={["e1", "e2", "A:e3{todoCreated}"]} />
142
-
143
- ### Conflict resolution
144
-
145
- This example shows how a client session rebases its pending events when new authoritative events arrive from upstream. Client `A` owns the local work that gets rebased, while client `B` introduces the authoritative change. Colors follow the client IDs so lineage remains visible, and origin notation tracks the rebased event.
146
-
147
- 1. Client session has local pending work while upstream advances
148
- - Client session holds pending event `A:e3'{todoRenamed}` built on top of shared history `e1 → e2`
149
- - Sync backend publishes authoritative event `B:e3{todoRenamed}` that replaces the client's local change
150
- <EventsVisualizer
151
- client={["e1", "e2", "A:e3{todoRenamed}"]}
152
- leader={["e1", "e2", "A:e3'{todoRenamed}"]}
153
- backend={["e1", "e2", "B:e3{todoRenamed}"]} />
154
-
155
- 2. Client leader pulls authoritative events from the sync backend
156
- - Client compares its pending chain with upstream events and spots the divergence at `e2`
157
- - Client rolls back events and state to the point of divergence
158
- <EventsVisualizer
159
- client={["e1", "e2"]}
160
- leader={["e1", "e2"]}
161
- backend={["e1", "e2", "B:e3{todoRenamed}"]} />
162
-
163
- 3. Client applies authoritative upstream events
164
- - Client session and leader apply the authoritative upstream events and advances their heads to `e3`
165
- <EventsVisualizer
166
- client={["e1", "e2", "B:e3{todoRenamed}"]}
167
- leader={["e1", "e2", "B:e3{todoRenamed}"]}
168
- backend={["e1", "e2", "B:e3{todoRenamed}"]} />
169
-
170
- 4. Client replays its local pending events on top of the new head
171
- - Stored original events keep their payload but their sequence number gets updated to follow upstream head
172
- - Each newly numbered event is re-appplied and materialized to state in both client session and client leader
173
- <EventsVisualizer
174
- client={["e1", "e2", "B:e3{todoRenamed}", "A:e4{todoRenamed}/e3"]}
175
- leader={["e1", "e2", "B:e3{todoRenamed}", "A:e4'{todoRenamed}/e3"]}
176
- backend={["e1", "e2", "B:e3{todoRenamed}"]} />
177
-
178
- 5. Client pushes its local pending events to sync backend
179
- - Upon receipt local pending events are marked as confirmed and the client leader advances its head to `e4`
180
- <EventsVisualizer
181
- client={["e1", "e2", "B:e3{todoRenamed}", "A:e4{todoRenamed}/e3"]}
182
- leader={["e1", "e2", "B:e3{todoRenamed}", "A:e4{todoRenamed}/e3"]}
183
- backend={["e1", "e2", "B:e3{todoRenamed}", "A:e4{todoRenamed}/e3"]} />
184
-
185
- ## Unknown events
186
-
187
- Older clients might receive events that were introduced in newer app versions. Configure the behaviour centrally via `unknownEventHandling` when constructing the schema:
188
-
189
- ## `reference/events/unknown-event-handling.ts`
190
-
191
- ```ts filename="reference/events/unknown-event-handling.ts"
192
-
193
- const tables = {
194
- todos: State.SQLite.table({
195
- name: 'todos',
196
- columns: {
197
- id: State.SQLite.text({ primaryKey: true }),
198
- text: State.SQLite.text(),
199
- },
200
- }),
201
- } as const
202
-
203
- const events = {
204
- todoCreated: Events.synced({
205
- name: 'v1.TodoCreated',
206
- schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
207
- }),
208
- } as const
209
-
210
- const materializers = State.SQLite.materializers(events, {
211
- [events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
212
- tables.todos.insert({ id, text }),
213
- ),
214
- })
215
-
216
- const state = State.SQLite.makeState({ tables, materializers })
217
-
218
- // ---cut---
219
-
220
- const _schema = makeSchema({
221
- events,
222
- state,
223
- unknownEventHandling: {
224
- strategy: 'callback',
225
- onUnknownEvent: (event, error) => {
226
- console.warn('LiveStore saw an unknown event', { event, reason: error.reason })
227
- },
228
- },
229
- })
230
- ```
231
-
232
- Pick `'warn'` (default) to log every occurrence, `'ignore'` to silently drop new events until the client updates, `'fail'` to halt immediately, or `'callback'` to delegate to custom logging/telemetry while continuing to process the log.
233
-
234
- ## Schema evolution \{#schema-evolution\}
235
-
236
- - Event definitions can't be removed after they were added to your app.
237
- - Event schema definitions can be evolved as long as the changes are forward-compatible.
238
- - That means data encoded with the old schema can be decoded with the new schema.
239
- - In practice, this means ...
240
- - for structs ...
241
- - you can add new fields if they have default values or are optional
242
- - you can remove fields
243
-
244
- ## Event format
245
-
246
- Each event has the following structure:
247
-
248
- | Field | Description |
249
- |-------|-------------|
250
- | `name` | Event name matching the event definition |
251
- | `args` | Event arguments as defined by the event schema |
252
- | `seqNum` | Sequence number identifying this event |
253
- | `parentSeqNum` | Parent event's sequence number (for causal ordering) |
254
- | `clientId` | Identifier of the client that created the event |
255
- | `sessionId` | Identifier of the session |
256
-
257
- ### Encoded vs decoded events
258
-
259
- Events exist in two formats:
260
-
261
- **Decoded** - Native TypeScript types used in application code:
262
-
263
- ```json
264
- {
265
- "name": "todoCreated-v1",
266
- "args": { "id": "abc123", "text": "Buy milk", "createdAt": Date },
267
- "seqNum": 5,
268
- "parentSeqNum": 4,
269
- "clientId": "client-xyz",
270
- "sessionId": "session-123"
271
- }
272
- ```
273
-
274
- **Encoded** - Serialized format for storage and sync:
275
-
276
- ```json
277
- {
278
- "name": "todoCreated-v1",
279
- "args": { "id": "abc123", "text": "Buy milk", "createdAt": "2024-01-15T10:30:00.000Z" },
280
- "seqNum": 5,
281
- "parentSeqNum": 4,
282
- "clientId": "client-xyz",
283
- "sessionId": "session-123"
284
- }
285
- ```
286
-
287
- The `args` field is encoded according to the event's schema (e.g., `Date` objects become ISO strings, binary data becomes base64). LiveStore handles encoding/decoding automatically.
288
-
289
- ### Client-side sequence numbers
290
-
291
- On the client, sequence numbers are expanded to track additional information for local events:
292
-
293
- ```json
294
- {
295
- "seqNum": { "global": 5, "client": 1, "rebaseGeneration": 0 },
296
- "parentSeqNum": { "global": 5, "client": 0, "rebaseGeneration": 0 }
297
- }
298
- ```
299
-
300
- - **global**: Globally unique integer assigned by the sync backend (`EventSequenceNumber.Global`)
301
- - **client**: Client-local counter (0 for synced events, increments for client-only events) (`EventSequenceNumber.Client`)
302
- - **rebaseGeneration**: Increments when the client rebases unconfirmed events
303
-
304
- Events can be represented as strings like `e5` (global event 5), `e5.1` (client-local event), or `e5r1` (after a rebase).
305
-
306
- For the full type definitions, see [`LiveStoreEvent`](https://github.com/livestorejs/livestore/tree/dev/packages/@livestore/common/src/schema/LiveStoreEvent) and [`EventSequenceNumber`](https://github.com/livestorejs/livestore/tree/dev/packages/@livestore/common/src/schema/EventSequenceNumber).
307
-
308
- ### Event type namespaces
309
-
310
- LiveStore organizes event types into namespaces based on their usage context:
311
-
312
- | Namespace | Description | Sequence Number Format |
313
- |-----------|-------------|----------------------|
314
- | `LiveStoreEvent.Input` | Events without sequence numbers (for committing) | None |
315
- | `LiveStoreEvent.Global` | Sync backend format | Integer (`seqNum: number`) |
316
- | `LiveStoreEvent.Client` | Client-side format with full metadata | Struct (`seqNum: { global, client, rebaseGeneration }`) |
317
-
318
- ## `reference/events/event-type-namespaces.ts`
319
-
320
- ```ts filename="reference/events/event-type-namespaces.ts"
321
-
322
- // Input events (no sequence numbers) - used when committing
323
- const _input: LiveStoreEvent.Input.Decoded = {
324
- name: 'todoCreated-v1',
325
- args: { id: 'abc123', text: 'Buy milk' },
326
- }
327
-
328
- // Global events (sync backend format) - integer sequence numbers
329
- const _global: LiveStoreEvent.Global.Encoded = {
330
- name: 'todoCreated-v1',
331
- args: { id: 'abc123', text: 'Buy milk' },
332
- seqNum: EventSequenceNumber.Global.make(5),
333
- parentSeqNum: EventSequenceNumber.Global.make(4),
334
- clientId: 'client-xyz',
335
- sessionId: 'session-123',
336
- }
337
-
338
- // Client events (local format) - composite sequence numbers
339
- const _client: LiveStoreEvent.Client.Encoded = {
340
- name: 'todoCreated-v1',
341
- args: { id: 'abc123', text: 'Buy milk' },
342
- seqNum: EventSequenceNumber.Client.Composite.make({ global: 5, client: 0, rebaseGeneration: 0 }),
343
- parentSeqNum: EventSequenceNumber.Client.Composite.make({ global: 4, client: 0, rebaseGeneration: 0 }),
344
- clientId: 'client-xyz',
345
- sessionId: 'session-123',
346
- }
347
- ```
348
-
349
- ## Eventlog
350
-
351
- The history of all events that have been committed is stored forms the "eventlog". It is persisted in the client as well as in the sync backend.
352
-
353
- Example `eventlog.db`:
354
-
355
- ![](https://share.cleanshot.com/R6ny879w+)
@@ -1,5 +0,0 @@
1
- # AI agent
2
-
3
- LiveStore is a great fit for building AI agents.
4
-
5
- TODO: actually write this section
@@ -1,30 +0,0 @@
1
- # Data modeling
2
-
3
- ## Core idea
4
-
5
- - Data modeling is probably the most important part of any app and needs to be done carefully.
6
- - The core idea is to model the read and write model separately.
7
- - Depending on the use case, you might also want to split up the read/write model into separate "containers" (e.g. for data-sharing/scalability/access control reasons).
8
- - There is no transactional consistency between containers.
9
- - Caveat: Event sourcing is not ideal for all use cases - some apps might be better off with another approach (e.g. use CRDTs for rich text editing).
10
-
11
- ## Considerations for data modeling
12
-
13
- - How much data do you expect to have and what is the shape of the data?
14
- - Some kind of data needs special handling (e.g. blobs or rich text)
15
- - Access patterns (performance, ...)
16
- - Access control
17
- - Data integrity / consistency
18
- - Sharing / collaboration
19
- - Regulatory requirements (e.g. GDPR, audit logs, ...)
20
-
21
- ## TODO
22
-
23
- - TODO: actually write this section
24
- - questions to answer
25
- - When to split things into separate containers?
26
- - How do migrations work?
27
- - Read model migrations
28
- - Write model migrations
29
- - How to create new write models based on existing ones
30
- - Example: An app has multiple workspaces and you now want to introduce the concept of "projects" inside a workspace. You might want to pre-populate a "default workspace project" for each workspace.