@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,245 +0,0 @@
|
|
|
1
|
-
# Expo Adapter
|
|
2
|
-
|
|
3
|
-
The Expo adapter enables LiveStore in React Native applications built with [Expo](https://expo.dev). It uses native SQLite via `expo-sqlite` for high-performance local persistence on iOS and Android devices.
|
|
4
|
-
|
|
5
|
-
## Key Features
|
|
6
|
-
|
|
7
|
-
- **Signals-based reactivity** — High-performance state management with fine-grained updates
|
|
8
|
-
- **iOS and Android support** — Works seamlessly on both platforms with native performance
|
|
9
|
-
- **Native SQLite storage** — Uses `expo-sqlite` for fast, reliable persistence directly on the device
|
|
10
|
-
- **Offline-first** — Full functionality without network connectivity; syncs when connected
|
|
11
|
-
- **Real-time sync** — Optional sync backend integration for multi-device data synchronization
|
|
12
|
-
- **Integrated devtools** — Debug and inspect your store via the [LiveStore Devtools](/building-with-livestore/devtools)
|
|
13
|
-
|
|
14
|
-
## Requirements
|
|
15
|
-
|
|
16
|
-
- [Expo New Architecture](https://docs.expo.dev/guides/new-architecture/) (Fabric) must be enabled
|
|
17
|
-
- `expo-sqlite` ^16.0.0
|
|
18
|
-
- `expo-application` ^7.0.0
|
|
19
|
-
|
|
20
|
-
:::note
|
|
21
|
-
Expo Web is not currently supported. See [#130](https://github.com/livestorejs/livestore/issues/130) for progress.
|
|
22
|
-
:::
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm install @livestore/adapter-expo @livestore/livestore @livestore/react expo-sqlite expo-application
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
For a complete setup including sync and devtools, see the [Expo getting started guide](/getting-started/expo).
|
|
31
|
-
|
|
32
|
-
## Basic Usage
|
|
33
|
-
|
|
34
|
-
Create an adapter and wrap your app with the `LiveStoreProvider`:
|
|
35
|
-
|
|
36
|
-
## `reference/platform-adapters/expo-adapter/usage.tsx`
|
|
37
|
-
|
|
38
|
-
```tsx filename="reference/platform-adapters/expo-adapter/usage.tsx"
|
|
39
|
-
|
|
40
|
-
const adapter = makePersistedAdapter()
|
|
41
|
-
|
|
42
|
-
export const App = () => (
|
|
43
|
-
<SafeAreaView style={{ flex: 1 }}>
|
|
44
|
-
<LiveStoreProvider
|
|
45
|
-
schema={schema}
|
|
46
|
-
adapter={adapter}
|
|
47
|
-
storeId="my-app"
|
|
48
|
-
batchUpdates={batchUpdates}
|
|
49
|
-
renderLoading={(status) => <Text>Loading ({status.stage})...</Text>}
|
|
50
|
-
renderError={(error) => <Text>Error: {String(error)}</Text>}
|
|
51
|
-
>
|
|
52
|
-
{/* Your app content */}
|
|
53
|
-
</LiveStoreProvider>
|
|
54
|
-
</SafeAreaView>
|
|
55
|
-
)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### `reference/platform-adapters/expo-adapter/schema.ts`
|
|
59
|
-
|
|
60
|
-
```ts filename="reference/platform-adapters/expo-adapter/schema.ts"
|
|
61
|
-
|
|
62
|
-
const tables = {
|
|
63
|
-
todos: State.SQLite.table({
|
|
64
|
-
name: 'todos',
|
|
65
|
-
columns: {
|
|
66
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
67
|
-
text: State.SQLite.text(),
|
|
68
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
69
|
-
},
|
|
70
|
-
}),
|
|
71
|
-
} as const
|
|
72
|
-
|
|
73
|
-
const events = {
|
|
74
|
-
todoCreated: Events.synced({
|
|
75
|
-
name: 'v1.TodoCreated',
|
|
76
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
77
|
-
}),
|
|
78
|
-
} as const
|
|
79
|
-
|
|
80
|
-
const materializers = State.SQLite.materializers(events, {
|
|
81
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
82
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
83
|
-
),
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
87
|
-
|
|
88
|
-
export const schema = makeSchema({ events, state })
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
The adapter requires minimal configuration. For more details on the provider props, see the [React integration guide](/framework-integrations/react-integration).
|
|
92
|
-
|
|
93
|
-
## Configuration Options
|
|
94
|
-
|
|
95
|
-
## `reference/platform-adapters/expo-adapter/adapter.ts`
|
|
96
|
-
|
|
97
|
-
```ts filename="reference/platform-adapters/expo-adapter/adapter.ts"
|
|
98
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
99
|
-
// ---cut---
|
|
100
|
-
|
|
101
|
-
const adapter = makePersistedAdapter({
|
|
102
|
-
storage: { subDirectory: 'my-app' },
|
|
103
|
-
})
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Available Options
|
|
107
|
-
|
|
108
|
-
| Option | Type | Description |
|
|
109
|
-
|--------|------|-------------|
|
|
110
|
-
| `storage.subDirectory` | `string` | Subdirectory within the default SQLite location for organizing databases |
|
|
111
|
-
| `sync` | `SyncOptions` | Sync backend configuration (see [Syncing](/building-with-livestore/syncing)) |
|
|
112
|
-
| `clientId` | `string` | Custom client identifier (defaults to device ID) |
|
|
113
|
-
| `sessionId` | `string` | Session identifier (defaults to `'static'`) |
|
|
114
|
-
| `resetPersistence` | `boolean` | Clear local databases on startup (development only) |
|
|
115
|
-
|
|
116
|
-
## Adding a Sync Backend
|
|
117
|
-
|
|
118
|
-
Connect to a sync backend for multi-device synchronization:
|
|
119
|
-
|
|
120
|
-
## `reference/platform-adapters/expo-adapter/sync-backend.ts`
|
|
121
|
-
|
|
122
|
-
```ts filename="reference/platform-adapters/expo-adapter/sync-backend.ts"
|
|
123
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
124
|
-
// ---cut---
|
|
125
|
-
|
|
126
|
-
const adapter = makePersistedAdapter({
|
|
127
|
-
sync: { backend: makeWsSync({ url: 'wss://your-sync-backend.com' }) },
|
|
128
|
-
})
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
See the [Syncing documentation](/building-with-livestore/syncing) for available sync providers and configuration options.
|
|
132
|
-
|
|
133
|
-
## Platform Notes
|
|
134
|
-
|
|
135
|
-
### Android
|
|
136
|
-
|
|
137
|
-
Android requires HTTPS for network connections by default. During development with a local sync backend using `http://` or `ws://`, add `expo-build-properties` to allow cleartext traffic:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
npx expo install expo-build-properties
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Then configure `app.json`:
|
|
144
|
-
|
|
145
|
-
```json
|
|
146
|
-
{
|
|
147
|
-
"expo": {
|
|
148
|
-
"plugins": [
|
|
149
|
-
[
|
|
150
|
-
"expo-build-properties",
|
|
151
|
-
{
|
|
152
|
-
"android": {
|
|
153
|
-
"usesCleartextTraffic": true
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
]
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
See [Expo build properties documentation](https://docs.expo.dev/versions/latest/sdk/build-properties/#pluginconfigtypeandroid) for more details.
|
|
163
|
-
|
|
164
|
-
### iOS
|
|
165
|
-
|
|
166
|
-
No special configuration required. The adapter automatically retrieves the iOS vendor ID for client identification.
|
|
167
|
-
|
|
168
|
-
## Devtools
|
|
169
|
-
|
|
170
|
-
LiveStore provides integrated devtools for debugging your store. In development, press `shift + m` in the Expo CLI terminal, then select "LiveStore Devtools" to open the browser-based inspector.
|
|
171
|
-
|
|
172
|
-
See the [Devtools reference](/building-with-livestore/devtools) for full documentation.
|
|
173
|
-
|
|
174
|
-
## Storage & Persistence
|
|
175
|
-
|
|
176
|
-
### Database Location
|
|
177
|
-
|
|
178
|
-
Databases are stored in the device's SQLite directory. The exact path depends on your setup:
|
|
179
|
-
|
|
180
|
-
**Expo Go:**
|
|
181
|
-
```bash
|
|
182
|
-
open $(find $(xcrun simctl get_app_container booted host.exp.Exponent data) -path "*/Documents/ExponentExperienceData/*livestore*" -print -quit)/SQLite
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Development builds:**
|
|
186
|
-
```bash
|
|
187
|
-
open $(xcrun simctl get_app_container booted [APP_BUNDLE_ID] data)/Documents/SQLite
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Replace `[APP_BUNDLE_ID]` with your app's bundle identifier (e.g., `dev.livestore.myapp`).
|
|
191
|
-
|
|
192
|
-
### Resetting Local Persistence
|
|
193
|
-
|
|
194
|
-
During development, you can clear local databases on startup:
|
|
195
|
-
|
|
196
|
-
## `reference/platform-adapters/expo-adapter/reset-persistence.ts`
|
|
197
|
-
|
|
198
|
-
```ts filename="reference/platform-adapters/expo-adapter/reset-persistence.ts"
|
|
199
|
-
|
|
200
|
-
const resetPersistence = process.env.EXPO_PUBLIC_LIVESTORE_RESET === 'true'
|
|
201
|
-
|
|
202
|
-
const _adapter = makePersistedAdapter({
|
|
203
|
-
storage: { subDirectory: 'dev' },
|
|
204
|
-
resetPersistence,
|
|
205
|
-
})
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
:::caution
|
|
209
|
-
This deletes all local LiveStore data for the configured store. It only clears on-device data and does not affect any connected sync backend. Ensure this flag is disabled in production builds.
|
|
210
|
-
:::
|
|
211
|
-
|
|
212
|
-
## Architecture
|
|
213
|
-
|
|
214
|
-
The Expo adapter runs LiveStore directly in the main JavaScript thread, using native SQLite bindings provided by `expo-sqlite`. This differs from the [web adapter](/platform-adapters/web-adapter), which uses web workers and WASM-based SQLite.
|
|
215
|
-
|
|
216
|
-
```
|
|
217
|
-
┌─────────────────────────────────────────┐
|
|
218
|
-
│ React Native App │
|
|
219
|
-
│ ┌───────────────────────────────────┐ │
|
|
220
|
-
│ │ LiveStore Client │ │
|
|
221
|
-
│ │ ┌─────────────┐ ┌─────────────┐ │ │
|
|
222
|
-
│ │ │ State DB │ │ Eventlog DB │ │ │
|
|
223
|
-
│ │ │(expo-sqlite)│ │(expo-sqlite)│ │ │
|
|
224
|
-
│ │ └─────────────┘ └─────────────┘ │ │
|
|
225
|
-
│ └───────────────────────────────────┘ │
|
|
226
|
-
└─────────────────────────────────────────┘
|
|
227
|
-
│
|
|
228
|
-
▼ WebSocket
|
|
229
|
-
┌───────────────┐
|
|
230
|
-
│ Sync Backend │
|
|
231
|
-
│ (optional) │
|
|
232
|
-
└───────────────┘
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Future Improvements
|
|
236
|
-
|
|
237
|
-
We're exploring moving database operations to a background thread to further improve UI responsiveness during intensive writes. Follow [livestore/livestore](https://github.com/livestorejs/livestore) for updates.
|
|
238
|
-
|
|
239
|
-
## See Also
|
|
240
|
-
|
|
241
|
-
- [Expo Getting Started Guide](/getting-started/expo) — Complete setup tutorial
|
|
242
|
-
- [Expo Adapter Examples](/examples/expo-adapter) — Example applications
|
|
243
|
-
- [React Integration](/framework-integrations/react-integration) — Provider and hooks documentation
|
|
244
|
-
- [Syncing](/building-with-livestore/syncing) — Multi-device synchronization
|
|
245
|
-
- [Devtools](/building-with-livestore/devtools) — Debugging and inspection tools
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Node adapter
|
|
2
|
-
|
|
3
|
-
Works with Node.js, Bun and Deno.
|
|
4
|
-
|
|
5
|
-
## Example
|
|
6
|
-
|
|
7
|
-
## `reference/platform-adapters/node-adapter/adapter.ts`
|
|
8
|
-
|
|
9
|
-
```ts filename="reference/platform-adapters/node-adapter/adapter.ts"
|
|
10
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
11
|
-
// ---cut---
|
|
12
|
-
|
|
13
|
-
const adapter = makeAdapter({
|
|
14
|
-
storage: { type: 'fs' },
|
|
15
|
-
// or in-memory:
|
|
16
|
-
// storage: { type: 'in-memory' },
|
|
17
|
-
sync: { backend: makeWsSync({ url: 'ws://localhost:8787' }) },
|
|
18
|
-
// To enable devtools:
|
|
19
|
-
// devtools: { schemaPath: new URL('./schema.ts', import.meta.url) },
|
|
20
|
-
})
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Resetting local persistence
|
|
24
|
-
|
|
25
|
-
During development you can instruct the adapter to wipe the locally persisted state and eventlog databases on startup:
|
|
26
|
-
|
|
27
|
-
## `reference/platform-adapters/node-adapter/reset-persistence.ts`
|
|
28
|
-
|
|
29
|
-
```ts filename="reference/platform-adapters/node-adapter/reset-persistence.ts"
|
|
30
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
31
|
-
// ---cut---
|
|
32
|
-
|
|
33
|
-
const resetPersistence = process.env.NODE_ENV !== 'production' && Boolean(process.env.RESET_LIVESTORE)
|
|
34
|
-
|
|
35
|
-
const adapter = makeAdapter({
|
|
36
|
-
storage: { type: 'fs' },
|
|
37
|
-
resetPersistence,
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
:::caution
|
|
42
|
-
This will delete all local data for the given `storeId` and `clientId`. It only clears local persistence and does not reset any connected sync backend. Only enable it for debugging scenarios.
|
|
43
|
-
:::
|
|
44
|
-
|
|
45
|
-
### Worker adapter
|
|
46
|
-
|
|
47
|
-
The worker adapter can be used for more advanced scenarios where it's preferable to reduce the load of the main thread and run persistence/syncing in a worker thread.
|
|
48
|
-
|
|
49
|
-
## `reference/platform-adapters/node-adapter/worker-main.ts`
|
|
50
|
-
|
|
51
|
-
```ts filename="reference/platform-adapters/node-adapter/worker-main.ts"
|
|
52
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: snippet keeps adapter inline for docs */
|
|
53
|
-
// ---cut---
|
|
54
|
-
|
|
55
|
-
const adapter = makeWorkerAdapter({
|
|
56
|
-
storage: { type: 'fs' },
|
|
57
|
-
workerUrl: new URL('./livestore.worker.js', import.meta.url),
|
|
58
|
-
})
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## `reference/platform-adapters/node-adapter/worker-worker.ts`
|
|
62
|
-
|
|
63
|
-
```ts filename="reference/platform-adapters/node-adapter/worker-worker.ts"
|
|
64
|
-
|
|
65
|
-
makeWorker({
|
|
66
|
-
schema,
|
|
67
|
-
sync: { backend: makeWsSync({ url: 'ws://localhost:8787' }) },
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### `reference/platform-adapters/node-adapter/schema.ts`
|
|
72
|
-
|
|
73
|
-
```ts filename="reference/platform-adapters/node-adapter/schema.ts"
|
|
74
|
-
|
|
75
|
-
const tables = {
|
|
76
|
-
todos: State.SQLite.table({
|
|
77
|
-
name: 'todos',
|
|
78
|
-
columns: {
|
|
79
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
80
|
-
text: State.SQLite.text(),
|
|
81
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
82
|
-
},
|
|
83
|
-
}),
|
|
84
|
-
} as const
|
|
85
|
-
|
|
86
|
-
const events = {
|
|
87
|
-
todoCreated: Events.synced({
|
|
88
|
-
name: 'v1.TodoCreated',
|
|
89
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
90
|
-
}),
|
|
91
|
-
} as const
|
|
92
|
-
|
|
93
|
-
const materializers = State.SQLite.materializers(events, {
|
|
94
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
95
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
96
|
-
),
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
100
|
-
|
|
101
|
-
export const schema = makeSchema({ events, state })
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### Logging
|
|
105
|
-
|
|
106
|
-
You can control what the Node worker logs and how it's formatted.
|
|
107
|
-
Pass two optional options to `makeWorker`:
|
|
108
|
-
|
|
109
|
-
- `logger` — where/format of logs (e.g. pretty console output)
|
|
110
|
-
- `logLevel` — how verbose logs are (`LogLevel.None` silences logs)
|
|
111
|
-
|
|
112
|
-
## `reference/platform-adapters/node-adapter/worker-logging.ts`
|
|
113
|
-
|
|
114
|
-
```ts filename="reference/platform-adapters/node-adapter/worker-logging.ts"
|
|
115
|
-
|
|
116
|
-
makeWorker({
|
|
117
|
-
schema,
|
|
118
|
-
// readable console output by thread name
|
|
119
|
-
logger: Logger.prettyWithThread('livestore-node-leader-thread'),
|
|
120
|
-
// choose verbosity: None | Error | Warning | Info | Debug
|
|
121
|
-
logLevel: LogLevel.Info,
|
|
122
|
-
})
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### `reference/platform-adapters/node-adapter/schema.ts`
|
|
126
|
-
|
|
127
|
-
```ts filename="reference/platform-adapters/node-adapter/schema.ts"
|
|
128
|
-
|
|
129
|
-
const tables = {
|
|
130
|
-
todos: State.SQLite.table({
|
|
131
|
-
name: 'todos',
|
|
132
|
-
columns: {
|
|
133
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
134
|
-
text: State.SQLite.text(),
|
|
135
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
136
|
-
},
|
|
137
|
-
}),
|
|
138
|
-
} as const
|
|
139
|
-
|
|
140
|
-
const events = {
|
|
141
|
-
todoCreated: Events.synced({
|
|
142
|
-
name: 'v1.TodoCreated',
|
|
143
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
144
|
-
}),
|
|
145
|
-
} as const
|
|
146
|
-
|
|
147
|
-
const materializers = State.SQLite.materializers(events, {
|
|
148
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
149
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
150
|
-
),
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
154
|
-
|
|
155
|
-
export const schema = makeSchema({ events, state })
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Tips:
|
|
159
|
-
- Use `LogLevel.None` to keep test output quiet.
|
|
160
|
-
- Keep the default (Debug) when diagnosing issues.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Tauri adapter
|
|
2
|
-
|
|
3
|
-
## Native Tauri adapter
|
|
4
|
-
|
|
5
|
-
While LiveStore doesn't yet support a native Tauri adapter (see [this issue](https://github.com/livestorejs/livestore/issues/125) for more details), you can already use the [web adapter](./web-adapter.md) with Tauri.
|
|
6
|
-
|
|
7
|
-
The goal of the native Tauri adapter is for LiveStore to leverage native platform APIs and capabilities including:
|
|
8
|
-
|
|
9
|
-
- Native file system access (instead of going through the browser abstraction layer)
|
|
10
|
-
- Background sync capabilities
|
|
11
|
-
- ...
|
|
12
|
-
|
|
13
|
-
## Example using the web adapter
|
|
14
|
-
|
|
15
|
-
See this example of a Tauri app using the web adapter: [tauri-todomvc-sync-cf](https://github.com/bohdanbirdie/tauri-todomvc-sync-cf)
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
# Web adapter
|
|
2
|
-
|
|
3
|
-
## Installation
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm install @livestore/adapter-web @livestore/wa-sqlite
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Example
|
|
10
|
-
|
|
11
|
-
## `reference/platform-adapters/web-adapter/main.ts`
|
|
12
|
-
|
|
13
|
-
```ts filename="reference/platform-adapters/web-adapter/main.ts"
|
|
14
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
15
|
-
// ---cut---
|
|
16
|
-
|
|
17
|
-
const adapter = makePersistedAdapter({
|
|
18
|
-
storage: { type: 'opfs' },
|
|
19
|
-
worker: LiveStoreWorker,
|
|
20
|
-
sharedWorker: LiveStoreSharedWorker,
|
|
21
|
-
})
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## `reference/platform-adapters/web-adapter/livestore.worker.ts`
|
|
25
|
-
|
|
26
|
-
```ts filename="reference/platform-adapters/web-adapter/livestore.worker.ts"
|
|
27
|
-
|
|
28
|
-
makeWorker({ schema })
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### `reference/platform-adapters/web-adapter/schema/index.ts`
|
|
32
|
-
|
|
33
|
-
```ts filename="reference/platform-adapters/web-adapter/schema/index.ts"
|
|
34
|
-
|
|
35
|
-
const tables = {
|
|
36
|
-
todos: State.SQLite.table({
|
|
37
|
-
name: 'todos',
|
|
38
|
-
columns: {
|
|
39
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
40
|
-
text: State.SQLite.text(),
|
|
41
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
42
|
-
},
|
|
43
|
-
}),
|
|
44
|
-
} as const
|
|
45
|
-
|
|
46
|
-
const events = {
|
|
47
|
-
todoCreated: Events.synced({
|
|
48
|
-
name: 'v1.TodoCreated',
|
|
49
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
50
|
-
}),
|
|
51
|
-
} as const
|
|
52
|
-
|
|
53
|
-
const materializers = State.SQLite.materializers(events, {
|
|
54
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
55
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
56
|
-
),
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
60
|
-
|
|
61
|
-
export const schema = makeSchema({ events, state })
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Adding a sync backend
|
|
65
|
-
|
|
66
|
-
## `reference/platform-adapters/web-adapter/sync-backend.ts`
|
|
67
|
-
|
|
68
|
-
```ts filename="reference/platform-adapters/web-adapter/sync-backend.ts"
|
|
69
|
-
|
|
70
|
-
makeWorker({ schema, sync: { backend: makeWsSync({ url: 'ws://localhost:8787' }) } })
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### `reference/platform-adapters/web-adapter/schema/index.ts`
|
|
74
|
-
|
|
75
|
-
```ts filename="reference/platform-adapters/web-adapter/schema/index.ts"
|
|
76
|
-
|
|
77
|
-
const tables = {
|
|
78
|
-
todos: State.SQLite.table({
|
|
79
|
-
name: 'todos',
|
|
80
|
-
columns: {
|
|
81
|
-
id: State.SQLite.text({ primaryKey: true }),
|
|
82
|
-
text: State.SQLite.text(),
|
|
83
|
-
completed: State.SQLite.boolean({ default: false }),
|
|
84
|
-
},
|
|
85
|
-
}),
|
|
86
|
-
} as const
|
|
87
|
-
|
|
88
|
-
const events = {
|
|
89
|
-
todoCreated: Events.synced({
|
|
90
|
-
name: 'v1.TodoCreated',
|
|
91
|
-
schema: Schema.Struct({ id: Schema.String, text: Schema.String }),
|
|
92
|
-
}),
|
|
93
|
-
} as const
|
|
94
|
-
|
|
95
|
-
const materializers = State.SQLite.materializers(events, {
|
|
96
|
-
[events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text }) =>
|
|
97
|
-
tables.todos.insert({ id, text, completed: false }),
|
|
98
|
-
),
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const state = State.SQLite.makeState({ tables, materializers })
|
|
102
|
-
|
|
103
|
-
export const schema = makeSchema({ events, state })
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## In-memory adapter
|
|
107
|
-
|
|
108
|
-
You can also use the in-memory adapter which can be useful in certain scenarios (e.g. testing).
|
|
109
|
-
|
|
110
|
-
## `reference/platform-adapters/web-adapter/in-memory.ts`
|
|
111
|
-
|
|
112
|
-
```ts filename="reference/platform-adapters/web-adapter/in-memory.ts"
|
|
113
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
114
|
-
// ---cut---
|
|
115
|
-
|
|
116
|
-
const adapter = makeInMemoryAdapter()
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Web worker
|
|
120
|
-
|
|
121
|
-
- Make sure your schema doesn't depend on any code which needs to run in the main thread (e.g. avoid importing from files using React)
|
|
122
|
-
- Unfortunately this constraints you from co-locating your table definitions in component files.
|
|
123
|
-
- You might be able to still work around this by using the following import in your worker:
|
|
124
|
-
```ts
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Why is there a dedicated web worker and a shared worker?
|
|
129
|
-
|
|
130
|
-
- Shared worker:
|
|
131
|
-
- Needed to allow tabs to communicate with each other using a binary message channel.
|
|
132
|
-
- The shared worker mostly acts as a proxy to the dedicated web worker.
|
|
133
|
-
- Dedicated web worker (also called "leader worker" via leader election mechanism using web locks):
|
|
134
|
-
- Acts as the leader/single writer for the storage.
|
|
135
|
-
- Also handles connection to sync backend.
|
|
136
|
-
- Currently needed for synchronous OPFS API which isn't supported in a shared worker. (Hopefully won't be needed in the future anymore.)
|
|
137
|
-
|
|
138
|
-
### Why not use a service worker?
|
|
139
|
-
|
|
140
|
-
- While service workers seem similar to shared workers (i.e. only a single instance across all tabs), they serve different purposes and have different trade-offs.
|
|
141
|
-
- Service workers are meant to be used to intercept network requests and tend to "shut down" when there are no requests for some period of time making them unsuitable for our use case.
|
|
142
|
-
- Also note that service workers don't support some needed APIs such as OPFS.
|
|
143
|
-
|
|
144
|
-
## Storage
|
|
145
|
-
|
|
146
|
-
LiveStore currently only support OPFS to locally persist its data. In the future we might add support for other storage types (e.g. IndexedDB).
|
|
147
|
-
|
|
148
|
-
During development (`NODE_ENV !== 'production'`), LiveStore automatically copies older state database files into `archive/` inside the OPFS directory for the store (e.g. `livestore-<storeId>@<version>/archive/`). The three most recent copies are retained so you can inspect pre-migration data; older archives are pruned. In production, we delete outdated state databases immediately.
|
|
149
|
-
|
|
150
|
-
LiveStore also uses `window.sessionStorage` to retain the identity of a client session (e.g. tab/window) across reloads.
|
|
151
|
-
|
|
152
|
-
### Resetting local persistence
|
|
153
|
-
|
|
154
|
-
Resetting local persistence only clears data stored in the browser and does not affect any connected sync backend.
|
|
155
|
-
|
|
156
|
-
In case you want to reset the local persistence of a client, you can provide the `resetPersistence` option to the adapter.
|
|
157
|
-
|
|
158
|
-
## `reference/platform-adapters/web-adapter/reset-persistence.ts`
|
|
159
|
-
|
|
160
|
-
```ts filename="reference/platform-adapters/web-adapter/reset-persistence.ts"
|
|
161
|
-
/** biome-ignore-all lint/correctness/noUnusedVariables: docs snippet keeps inline adapter */
|
|
162
|
-
// ---cut---
|
|
163
|
-
|
|
164
|
-
const resetPersistence = import.meta.env.DEV && new URLSearchParams(window.location.search).get('reset') !== null
|
|
165
|
-
|
|
166
|
-
if (resetPersistence) {
|
|
167
|
-
const searchParams = new URLSearchParams(window.location.search)
|
|
168
|
-
searchParams.delete('reset')
|
|
169
|
-
window.history.replaceState(null, '', `${window.location.pathname}?${searchParams.toString()}`)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const adapter = makePersistedAdapter({
|
|
173
|
-
storage: { type: 'opfs' },
|
|
174
|
-
worker: LiveStoreWorker,
|
|
175
|
-
sharedWorker: LiveStoreSharedWorker,
|
|
176
|
-
resetPersistence,
|
|
177
|
-
})
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
If you want to reset persistence manually, you can:
|
|
181
|
-
|
|
182
|
-
1. **Clear site data** in Chrome DevTools (Application tab > Storage > Clear site data)
|
|
183
|
-
2. **Use console command** if the above doesn't work due to a Chrome OPFS bug:
|
|
184
|
-
|
|
185
|
-
```javascript
|
|
186
|
-
const opfsRoot = await navigator.storage.getDirectory();
|
|
187
|
-
await opfsRoot.remove();
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Note: Only use this during development while the app is running.
|
|
191
|
-
|
|
192
|
-
## Architecture diagram
|
|
193
|
-
|
|
194
|
-
Assuming the web adapter in a multi-client, multi-tab browser application, a diagram looks like this:
|
|
195
|
-
|
|
196
|
-

|
|
197
|
-
|
|
198
|
-
## Other notes
|
|
199
|
-
|
|
200
|
-
- The web adapter is using some browser APIs that might require a HTTPS connection (e.g. `navigator.locks`). It's recommended to even use HTTPS during local development (e.g. via [Caddy](https://caddyserver.com/docs/automatic-https)).
|
|
201
|
-
|
|
202
|
-
## Browser support
|
|
203
|
-
|
|
204
|
-
- Notable required browser APIs: OPFS, SharedWorker, `navigator.locks`, WASM
|
|
205
|
-
- The web adapter of LiveStore currently doesn't work on Android browsers as they don't support the `SharedWorker` API (see [Chromium bug](https://issues.chromium.org/issues/40290702)).
|
|
206
|
-
|
|
207
|
-
## Best practices
|
|
208
|
-
|
|
209
|
-
- It's recommended to develop in an incognito window to avoid issues with persistent storage (e.g. OPFS).
|
|
210
|
-
|
|
211
|
-
## FAQ
|
|
212
|
-
|
|
213
|
-
### What's the bundle size of the web adapter?
|
|
214
|
-
|
|
215
|
-
LiveStore with the web adapter adds two parts to your application bundle:
|
|
216
|
-
|
|
217
|
-
- The LiveStore JavaScript bundle (~180KB gzipped)
|
|
218
|
-
- SQLite WASM (~300KB gzipped)
|