@livestore/adapter-web 0.4.0-dev.8 → 0.4.0

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 (74) hide show
  1. package/README.md +5 -5
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/in-memory/in-memory-adapter.d.ts +49 -5
  4. package/dist/in-memory/in-memory-adapter.d.ts.map +1 -1
  5. package/dist/in-memory/in-memory-adapter.js +77 -20
  6. package/dist/in-memory/in-memory-adapter.js.map +1 -1
  7. package/dist/index.d.ts +11 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +11 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/single-tab/mod.d.ts +15 -0
  12. package/dist/single-tab/mod.d.ts.map +1 -0
  13. package/dist/single-tab/mod.js +15 -0
  14. package/dist/single-tab/mod.js.map +1 -0
  15. package/dist/single-tab/single-tab-adapter.d.ts +108 -0
  16. package/dist/single-tab/single-tab-adapter.d.ts.map +1 -0
  17. package/dist/single-tab/single-tab-adapter.js +271 -0
  18. package/dist/single-tab/single-tab-adapter.js.map +1 -0
  19. package/dist/web-worker/client-session/client-session-devtools.d.ts +2 -2
  20. package/dist/web-worker/client-session/client-session-devtools.d.ts.map +1 -1
  21. package/dist/web-worker/client-session/client-session-devtools.js +20 -9
  22. package/dist/web-worker/client-session/client-session-devtools.js.map +1 -1
  23. package/dist/web-worker/client-session/persisted-adapter.d.ts +18 -0
  24. package/dist/web-worker/client-session/persisted-adapter.d.ts.map +1 -1
  25. package/dist/web-worker/client-session/persisted-adapter.js +141 -67
  26. package/dist/web-worker/client-session/persisted-adapter.js.map +1 -1
  27. package/dist/web-worker/client-session/sqlite-loader.d.ts +2 -0
  28. package/dist/web-worker/client-session/sqlite-loader.d.ts.map +1 -0
  29. package/dist/web-worker/client-session/sqlite-loader.js +16 -0
  30. package/dist/web-worker/client-session/sqlite-loader.js.map +1 -0
  31. package/dist/web-worker/common/persisted-sqlite.d.ts +13 -20
  32. package/dist/web-worker/common/persisted-sqlite.d.ts.map +1 -1
  33. package/dist/web-worker/common/persisted-sqlite.js +95 -102
  34. package/dist/web-worker/common/persisted-sqlite.js.map +1 -1
  35. package/dist/web-worker/common/shutdown-channel.d.ts +3 -2
  36. package/dist/web-worker/common/shutdown-channel.d.ts.map +1 -1
  37. package/dist/web-worker/common/shutdown-channel.js +2 -2
  38. package/dist/web-worker/common/shutdown-channel.js.map +1 -1
  39. package/dist/web-worker/common/worker-disconnect-channel.d.ts +2 -6
  40. package/dist/web-worker/common/worker-disconnect-channel.d.ts.map +1 -1
  41. package/dist/web-worker/common/worker-disconnect-channel.js +3 -2
  42. package/dist/web-worker/common/worker-disconnect-channel.js.map +1 -1
  43. package/dist/web-worker/common/worker-schema.d.ts +152 -58
  44. package/dist/web-worker/common/worker-schema.d.ts.map +1 -1
  45. package/dist/web-worker/common/worker-schema.js +55 -37
  46. package/dist/web-worker/common/worker-schema.js.map +1 -1
  47. package/dist/web-worker/leader-worker/make-leader-worker.d.ts +5 -3
  48. package/dist/web-worker/leader-worker/make-leader-worker.d.ts.map +1 -1
  49. package/dist/web-worker/leader-worker/make-leader-worker.js +99 -38
  50. package/dist/web-worker/leader-worker/make-leader-worker.js.map +1 -1
  51. package/dist/web-worker/shared-worker/make-shared-worker.d.ts +2 -1
  52. package/dist/web-worker/shared-worker/make-shared-worker.d.ts.map +1 -1
  53. package/dist/web-worker/shared-worker/make-shared-worker.js +62 -52
  54. package/dist/web-worker/shared-worker/make-shared-worker.js.map +1 -1
  55. package/package.json +56 -18
  56. package/src/in-memory/in-memory-adapter.ts +92 -26
  57. package/src/index.ts +15 -1
  58. package/src/single-tab/mod.ts +15 -0
  59. package/src/single-tab/single-tab-adapter.ts +499 -0
  60. package/src/web-worker/ambient.d.ts +7 -24
  61. package/src/web-worker/client-session/client-session-devtools.ts +32 -18
  62. package/src/web-worker/client-session/persisted-adapter.ts +199 -103
  63. package/src/web-worker/client-session/sqlite-loader.ts +19 -0
  64. package/src/web-worker/common/persisted-sqlite.ts +215 -170
  65. package/src/web-worker/common/shutdown-channel.ts +10 -3
  66. package/src/web-worker/common/worker-disconnect-channel.ts +10 -3
  67. package/src/web-worker/common/worker-schema.ts +78 -38
  68. package/src/web-worker/leader-worker/make-leader-worker.ts +149 -71
  69. package/src/web-worker/shared-worker/make-shared-worker.ts +78 -90
  70. package/dist/opfs-utils.d.ts +0 -5
  71. package/dist/opfs-utils.d.ts.map +0 -1
  72. package/dist/opfs-utils.js +0 -43
  73. package/dist/opfs-utils.js.map +0 -1
  74. package/src/opfs-utils.ts +0 -61
package/package.json CHANGED
@@ -1,37 +1,75 @@
1
1
  {
2
2
  "name": "@livestore/adapter-web",
3
- "version": "0.4.0-dev.8",
3
+ "version": "0.4.0",
4
+ "license": "Apache-2.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/livestorejs/livestore.git"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "package.json",
12
+ "src"
13
+ ],
4
14
  "type": "module",
5
15
  "sideEffects": false,
6
16
  "exports": {
7
17
  ".": "./dist/index.js",
8
18
  "./worker": "./dist/web-worker/leader-worker/make-leader-worker.js",
9
19
  "./worker-vite-dev-polyfill": "./dist/web-worker/vite-dev-polyfill.js",
10
- "./shared-worker": "./dist/web-worker/shared-worker/make-shared-worker.js",
11
- "./sqlite": "./dist/sqlite/index.js",
12
- "./opfs-utils": "./dist/opfs-utils.js"
20
+ "./shared-worker": "./dist/web-worker/shared-worker/make-shared-worker.js"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
13
24
  },
14
25
  "dependencies": {
15
26
  "@opentelemetry/api": "1.9.0",
16
- "@livestore/common": "0.4.0-dev.8",
17
- "@livestore/devtools-web-common": "0.4.0-dev.8",
18
- "@livestore/sqlite-wasm": "0.4.0-dev.8",
19
- "@livestore/utils": "0.4.0-dev.8",
20
- "@livestore/webmesh": "0.4.0-dev.8"
27
+ "@livestore/common": "^0.4.0",
28
+ "@livestore/devtools-web-common": "^0.4.0",
29
+ "@livestore/sqlite-wasm": "^0.4.0",
30
+ "@livestore/utils": "^0.4.0",
31
+ "@livestore/webmesh": "^0.4.0"
21
32
  },
22
33
  "devDependencies": {
23
34
  "@types/chrome": "0.1.4",
24
- "@types/wicg-file-system-access": "^2023.10.6",
35
+ "@types/wicg-file-system-access": "2023.10.6",
25
36
  "vitest": "3.2.4"
26
37
  },
27
- "files": [
28
- "package.json",
29
- "src",
30
- "dist"
31
- ],
32
- "license": "Apache-2.0",
33
- "publishConfig": {
34
- "access": "public"
38
+ "peerDependencies": {
39
+ "@effect/ai": "^0.35.0",
40
+ "@effect/cli": "^0.75.1",
41
+ "@effect/cluster": "^0.58.2",
42
+ "@effect/experimental": "^0.60.0",
43
+ "@effect/opentelemetry": "^0.63.0",
44
+ "@effect/platform": "^0.96.1",
45
+ "@effect/platform-browser": "^0.76.0",
46
+ "@effect/platform-bun": "^0.89.0",
47
+ "@effect/platform-node": "^0.106.0",
48
+ "@effect/printer": "^0.49.0",
49
+ "@effect/printer-ansi": "^0.49.0",
50
+ "@effect/rpc": "^0.75.1",
51
+ "@effect/sql": "^0.51.1",
52
+ "@effect/typeclass": "^0.40.0",
53
+ "@effect/vitest": "^0.29.0",
54
+ "@opentelemetry/api": "^1.9.0",
55
+ "@opentelemetry/resources": "^2.2.0",
56
+ "@standard-schema/spec": "^1.1.0",
57
+ "effect": "^3.21.2"
58
+ },
59
+ "$genie": {
60
+ "source": "package.json.genie.ts",
61
+ "warning": "DO NOT EDIT - changes will be overwritten",
62
+ "workspaceClosureDirs": [
63
+ "packages/@livestore/adapter-web",
64
+ "packages/@livestore/common",
65
+ "packages/@livestore/common-cf",
66
+ "packages/@livestore/devtools-web-common",
67
+ "packages/@livestore/sqlite-wasm",
68
+ "packages/@livestore/utils",
69
+ "packages/@livestore/utils-dev",
70
+ "packages/@livestore/wa-sqlite",
71
+ "packages/@livestore/webmesh"
72
+ ]
35
73
  },
36
74
  "scripts": {
37
75
  "test": "echo No tests yet"
@@ -6,29 +6,33 @@ import {
6
6
  makeClientSession,
7
7
  migrateDb,
8
8
  type SyncOptions,
9
- UnexpectedError,
9
+ UnknownError,
10
10
  } from '@livestore/common'
11
11
  import type { DevtoolsOptions, LeaderSqliteDb } from '@livestore/common/leader-thread'
12
- import { configureConnection, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
12
+ import {
13
+ configureConnection,
14
+ Eventlog,
15
+ LeaderThreadCtx,
16
+ makeLeaderThreadLayer,
17
+ streamEventsWithSyncState,
18
+ } from '@livestore/common/leader-thread'
13
19
  import type { LiveStoreSchema } from '@livestore/common/schema'
14
20
  import { LiveStoreEvent } from '@livestore/common/schema'
15
21
  import * as DevtoolsWeb from '@livestore/devtools-web-common/web-channel'
16
22
  import type * as WebmeshWorker from '@livestore/devtools-web-common/worker'
17
23
  import type { MakeWebSqliteDb } from '@livestore/sqlite-wasm/browser'
18
24
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
19
- import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
20
25
  import { tryAsFunctionAndNew } from '@livestore/utils'
21
- import type { Schema, Scope } from '@livestore/utils/effect'
22
- import { BrowserWorker, Effect, FetchHttpClient, Fiber, Layer, SubscriptionRef, Worker } from '@livestore/utils/effect'
26
+ import type { Scope } from '@livestore/utils/effect'
27
+ import { Effect, FetchHttpClient, Fiber, Layer, type Schema, SubscriptionRef, Worker } from '@livestore/utils/effect'
28
+ import { BrowserWorker } from '@livestore/utils/effect/browser'
23
29
  import { nanoid } from '@livestore/utils/nanoid'
24
30
  import * as Webmesh from '@livestore/webmesh'
25
31
 
26
32
  import { connectWebmeshNodeClientSession } from '../web-worker/client-session/client-session-devtools.ts'
33
+ import { loadSqlite3 } from '../web-worker/client-session/sqlite-loader.ts'
27
34
  import { makeShutdownChannel } from '../web-worker/common/shutdown-channel.ts'
28
35
 
29
- // NOTE we're starting to initialize the sqlite wasm binary here to speed things up
30
- const sqlite3Promise = loadSqlite3Wasm()
31
-
32
36
  export interface InMemoryAdapterOptions {
33
37
  importSnapshot?: Uint8Array<ArrayBuffer>
34
38
  sync?: SyncOptions
@@ -55,32 +59,76 @@ export interface InMemoryAdapterOptions {
55
59
  }
56
60
  }
57
61
 
62
+ /**
63
+ * Creates a web-only in-memory LiveStore adapter.
64
+ *
65
+ * This adapter runs entirely in memory with no persistence. Ideal for:
66
+ * - Unit tests and integration tests
67
+ * - Sandboxes and demos
68
+ * - Ephemeral sessions where persistence isn't needed
69
+ *
70
+ * **Characteristics:**
71
+ * - Fast, zero I/O overhead
72
+ * - Works in all browser contexts: Window, WebWorker, SharedWorker, ServiceWorker
73
+ * - Supports optional sync backends for real-time collaboration
74
+ * - No data persists after page reload
75
+ *
76
+ * For persistent storage, use `makePersistedAdapter` instead.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { makeInMemoryAdapter } from '@livestore/adapter-web'
81
+ *
82
+ * const adapter = makeInMemoryAdapter()
83
+ * ```
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * // With sync backend for real-time collaboration
88
+ * import { makeInMemoryAdapter } from '@livestore/adapter-web'
89
+ * import { makeWsSync } from '@livestore/sync-cf/client'
90
+ *
91
+ * const adapter = makeInMemoryAdapter({
92
+ * sync: {
93
+ * backend: makeWsSync({ url: 'wss://api.example.com/sync' }),
94
+ * },
95
+ * })
96
+ * ```
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * // Pre-populate with existing data
101
+ * const adapter = makeInMemoryAdapter({
102
+ * importSnapshot: existingDbSnapshot,
103
+ * })
104
+ * ```
105
+ */
58
106
  export const makeInMemoryAdapter =
59
107
  (options: InMemoryAdapterOptions = {}): Adapter =>
60
108
  (adapterArgs) =>
61
109
  Effect.gen(function* () {
62
- const { schema, shutdown, syncPayload, storeId, devtoolsEnabled } = adapterArgs
63
- const sqlite3 = yield* Effect.promise(() => sqlite3Promise)
110
+ const { schema, shutdown, syncPayloadEncoded, syncPayloadSchema, storeId, devtoolsEnabled } = adapterArgs
111
+ const sqlite3 = yield* Effect.promise(() => loadSqlite3())
64
112
 
65
113
  const sqliteDb = yield* sqliteDbFactory({ sqlite3 })({ _tag: 'in-memory' })
66
114
 
67
115
  const clientId = options.clientId ?? nanoid(6)
68
116
  const sessionId = options.sessionId ?? nanoid(6)
69
117
 
70
- const sharedWebWorker = options.devtools?.sharedWorker
118
+ const sharedWebWorker = options.devtools?.sharedWorker !== undefined
71
119
  ? tryAsFunctionAndNew(options.devtools.sharedWorker, {
72
120
  name: `livestore-shared-worker-${storeId}`,
73
121
  })
74
122
  : undefined
75
123
 
76
- const sharedWorkerFiber = sharedWebWorker
124
+ const sharedWorkerFiber = sharedWebWorker !== undefined
77
125
  ? yield* Worker.makePoolSerialized<typeof WebmeshWorker.Schema.Request.Type>({
78
126
  size: 1,
79
127
  concurrency: 100,
80
128
  }).pipe(
81
129
  Effect.provide(BrowserWorker.layer(() => sharedWebWorker)),
82
130
  Effect.tapCauseLogPretty,
83
- UnexpectedError.mapToUnexpectedError,
131
+ UnknownError.mapToUnknownError,
84
132
  Effect.forkScoped,
85
133
  )
86
134
  : undefined
@@ -91,7 +139,8 @@ export const makeInMemoryAdapter =
91
139
  clientId,
92
140
  makeSqliteDb: sqliteDbFactory({ sqlite3 }),
93
141
  syncOptions: options.sync,
94
- syncPayload,
142
+ syncPayloadEncoded,
143
+ syncPayloadSchema,
95
144
  importSnapshot: options.importSnapshot,
96
145
  devtoolsEnabled,
97
146
  sharedWorkerFiber,
@@ -111,6 +160,8 @@ export const makeInMemoryAdapter =
111
160
  lockStatus,
112
161
  shutdown,
113
162
  webmeshMode: 'direct',
163
+ // Can be undefined in Node.js
164
+ origin: globalThis.location?.origin,
114
165
  connectWebmeshNode: ({ sessionInfo, webmeshNode }) =>
115
166
  Effect.gen(function* () {
116
167
  if (sharedWorkerFiber === undefined || devtoolsEnabled === false) {
@@ -132,7 +183,7 @@ export const makeInMemoryAdapter =
132
183
  })
133
184
 
134
185
  return clientSession
135
- }).pipe(UnexpectedError.mapToUnexpectedError, Effect.provide(FetchHttpClient.layer))
186
+ }).pipe(UnknownError.mapToUnknownError, Effect.provide(FetchHttpClient.layer))
136
187
 
137
188
  export interface MakeLeaderThreadArgs {
138
189
  schema: LiveStoreSchema
@@ -140,7 +191,8 @@ export interface MakeLeaderThreadArgs {
140
191
  clientId: string
141
192
  makeSqliteDb: MakeWebSqliteDb
142
193
  syncOptions: SyncOptions | undefined
143
- syncPayload: Schema.JsonValue | undefined
194
+ syncPayloadEncoded: Schema.JsonValue | undefined
195
+ syncPayloadSchema: Schema.Schema<any> | undefined
144
196
  importSnapshot: Uint8Array<ArrayBuffer> | undefined
145
197
  devtoolsEnabled: boolean
146
198
  sharedWorkerFiber: SharedWorkerFiber | undefined
@@ -152,13 +204,14 @@ const makeLeaderThread = ({
152
204
  clientId,
153
205
  makeSqliteDb,
154
206
  syncOptions,
155
- syncPayload,
207
+ syncPayloadEncoded,
208
+ syncPayloadSchema,
156
209
  importSnapshot,
157
210
  devtoolsEnabled,
158
211
  sharedWorkerFiber,
159
212
  }: MakeLeaderThreadArgs) =>
160
213
  Effect.gen(function* () {
161
- const runtime = yield* Effect.runtime<never>()
214
+ const runtime = yield* Effect.runtime()
162
215
 
163
216
  const makeDb = (_kind: 'state' | 'eventlog') => {
164
217
  return makeSqliteDb({
@@ -173,7 +226,7 @@ const makeLeaderThread = ({
173
226
  // Might involve some async work, so we're running them concurrently
174
227
  const [dbState, dbEventlog] = yield* Effect.all([makeDb('state'), makeDb('eventlog')], { concurrency: 2 })
175
228
 
176
- if (importSnapshot) {
229
+ if (importSnapshot !== undefined) {
177
230
  dbState.import(importSnapshot)
178
231
 
179
232
  const _migrationsReport = yield* migrateDb({ db: dbState, schema })
@@ -199,12 +252,14 @@ const makeLeaderThread = ({
199
252
  dbEventlog,
200
253
  devtoolsOptions,
201
254
  shutdownChannel,
202
- syncPayload,
255
+ syncPayloadEncoded,
256
+ syncPayloadSchema,
203
257
  }),
204
258
  )
205
259
 
206
260
  return yield* Effect.gen(function* () {
207
- const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState } = yield* LeaderThreadCtx
261
+ const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState, networkStatus } =
262
+ yield* LeaderThreadCtx
208
263
 
209
264
  const initialLeaderHead = Eventlog.getClientHeadFromDb(dbEventlog)
210
265
 
@@ -213,15 +268,26 @@ const makeLeaderThread = ({
213
268
  pull: ({ cursor }) => syncProcessor.pull({ cursor }),
214
269
  push: (batch) =>
215
270
  syncProcessor.push(
216
- batch.map((item) => new LiveStoreEvent.EncodedWithMeta(item)),
271
+ batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item)),
217
272
  { waitForProcessing: true },
218
273
  ),
274
+ stream: (options) =>
275
+ streamEventsWithSyncState({
276
+ dbEventlog,
277
+ syncState: syncProcessor.syncState,
278
+ options,
279
+ }),
280
+ },
281
+ initialState: {
282
+ leaderHead: initialLeaderHead,
283
+ migrationsReport: initialState.migrationsReport,
284
+ storageMode: 'in-memory',
219
285
  },
220
- initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
221
286
  export: Effect.sync(() => dbState.export()),
222
287
  getEventlogData: Effect.sync(() => dbEventlog.export()),
223
- getSyncState: syncProcessor.syncState,
288
+ syncState: syncProcessor.syncState,
224
289
  sendDevtoolsMessage: (message) => extraIncomingMessagesQueue.offer(message),
290
+ networkStatus,
225
291
  })
226
292
 
227
293
  const initialSnapshot = dbState.export()
@@ -232,7 +298,7 @@ const makeLeaderThread = ({
232
298
 
233
299
  type SharedWorkerFiber = Fiber.Fiber<
234
300
  Worker.SerializedWorkerPool<typeof WebmeshWorker.Schema.Request.Type>,
235
- UnexpectedError
301
+ UnknownError
236
302
  >
237
303
 
238
304
  const makeDevtoolsOptions = ({
@@ -249,7 +315,7 @@ const makeDevtoolsOptions = ({
249
315
  dbEventlog: LeaderSqliteDb
250
316
  storeId: string
251
317
  clientId: string
252
- }): Effect.Effect<DevtoolsOptions, UnexpectedError, Scope.Scope> =>
318
+ }): Effect.Effect<DevtoolsOptions, UnknownError, Scope.Scope> =>
253
319
  Effect.gen(function* () {
254
320
  if (devtoolsEnabled === false || sharedWorkerFiber === undefined) {
255
321
  return { enabled: false }
package/src/index.ts CHANGED
@@ -1,3 +1,17 @@
1
1
  export { makeInMemoryAdapter } from './in-memory/in-memory-adapter.ts'
2
- export { makePersistedAdapter, type WebAdapterOptions } from './web-worker/client-session/persisted-adapter.ts'
2
+ /**
3
+ * Single-tab adapter for browsers without SharedWorker support (e.g. Android Chrome).
4
+ *
5
+ * In most cases, you should use `makePersistedAdapter` instead, which automatically
6
+ * falls back to single-tab mode when SharedWorker is unavailable.
7
+ *
8
+ * @see https://github.com/livestorejs/livestore/issues/321
9
+ * @see https://issues.chromium.org/issues/40290702
10
+ */
11
+ export { makeSingleTabAdapter, type SingleTabAdapterOptions } from './single-tab/mod.ts'
12
+ export {
13
+ canUseSharedWorker,
14
+ makePersistedAdapter,
15
+ type WebAdapterOptions,
16
+ } from './web-worker/client-session/persisted-adapter.ts'
3
17
  export * as WorkerSchema from './web-worker/common/worker-schema.ts'
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Single-tab adapter module for browsers without SharedWorker support.
3
+ *
4
+ * **NOTE**: This module exists as a fallback for Android Chrome and similar browsers
5
+ * that don't support SharedWorker. It is intended to be deprecated and removed once
6
+ * SharedWorker support is available in these browsers.
7
+ *
8
+ * Track progress:
9
+ * - LiveStore issue: https://github.com/livestorejs/livestore/issues/321
10
+ * - Chromium bug: https://issues.chromium.org/issues/40290702
11
+ *
12
+ * @module
13
+ */
14
+
15
+ export { makeSingleTabAdapter, type SingleTabAdapterOptions } from './single-tab-adapter.ts'