@livestore/adapter-cloudflare 0.4.0-dev.9 → 0.5.0-dev.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.
@@ -5,14 +5,24 @@ import {
5
5
  liveStoreStorageFormatVersion,
6
6
  makeClientSession,
7
7
  type SyncOptions,
8
- UnexpectedError,
8
+ UnknownError,
9
+ StateHead,
9
10
  } from '@livestore/common'
10
- import { type DevtoolsOptions, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
11
+ import type { CfTypes } from '@livestore/common-cf'
12
+ import {
13
+ type DevtoolsOptions,
14
+ Eventlog,
15
+ LeaderThreadCtx,
16
+ makeLeaderThreadLayer,
17
+ streamEventsWithSyncState,
18
+ } from '@livestore/common/leader-thread'
19
+ import { getStateDbBaseName } from '@livestore/common/schema'
11
20
  import { LiveStoreEvent } from '@livestore/livestore'
12
- import { sqliteDbFactory } from '@livestore/sqlite-wasm/cf'
21
+ import { CF_SQL_VFS_REQUIRED_PRAGMAS, sqliteDbFactory } from '@livestore/sqlite-wasm/cf'
13
22
  import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
14
- import { Effect, FetchHttpClient, Layer, Schedule, SubscriptionRef, WebChannel } from '@livestore/utils/effect'
15
- import type * as CfWorker from './cf-types.ts'
23
+ import { Effect, FetchHttpClient, Layer, Queue, Schedule, SubscriptionRef, WebChannel } from '@livestore/utils/effect'
24
+
25
+ import { makeSqliteDb as makeDoSqliteDb } from './make-sqlite-db.ts'
16
26
 
17
27
  export const makeAdapter =
18
28
  ({
@@ -22,7 +32,7 @@ export const makeAdapter =
22
32
  sessionId,
23
33
  resetPersistence = false,
24
34
  }: {
25
- storage: CfWorker.DurableObjectStorage
35
+ storage: CfTypes.DurableObjectStorage
26
36
  clientId: string
27
37
  syncOptions: SyncOptions
28
38
  sessionId: string
@@ -30,7 +40,13 @@ export const makeAdapter =
30
40
  }): Adapter =>
31
41
  (adapterArgs) =>
32
42
  Effect.gen(function* () {
33
- const { storeId, /* devtoolsEnabled, shutdown, bootStatusQueue, */ syncPayload, schema } = adapterArgs
43
+ const {
44
+ storeId,
45
+ /* devtoolsEnabled, shutdown, bootStatusQueue, */
46
+ syncPayloadEncoded,
47
+ syncPayloadSchema,
48
+ schema,
49
+ } = adapterArgs
34
50
 
35
51
  const devtoolsOptions = { enabled: false } as DevtoolsOptions
36
52
 
@@ -39,41 +55,32 @@ export const makeAdapter =
39
55
  const makeSqliteDb = sqliteDbFactory({ sqlite3 })
40
56
 
41
57
  const syncInMemoryDb = yield* makeSqliteDb({ _tag: 'in-memory', storage, configureDb: () => {} }).pipe(
42
- UnexpectedError.mapToUnexpectedError,
58
+ UnknownError.mapToUnknownError,
43
59
  )
44
60
 
45
- const schemaHashSuffix =
46
- schema.state.sqlite.migrations.strategy === 'manual' ? 'fixed' : schema.state.sqlite.hash.toString()
47
-
48
- const stateDbFileName = getStateDbFileName(schemaHashSuffix)
49
- const eventlogDbFileName = getEventlogDbFileName()
50
-
51
61
  if (resetPersistence === true) {
52
- yield* resetDurableObjectPersistence({
53
- storage,
54
- storeId,
55
- dbFileNames: [stateDbFileName, eventlogDbFileName],
56
- })
62
+ yield* resetDurableObjectPersistence({ storage, storeId })
57
63
  }
58
64
 
59
65
  const dbState = yield* makeSqliteDb({
60
66
  _tag: 'storage',
61
67
  storage,
62
- fileName: stateDbFileName,
63
- configureDb: () => {},
64
- }).pipe(UnexpectedError.mapToUnexpectedError)
65
-
66
- const dbEventlog = yield* makeSqliteDb({
67
- _tag: 'storage',
68
- storage,
69
- fileName: eventlogDbFileName,
68
+ fileName: `${getStateDbBaseName(schema)}@${liveStoreStorageFormatVersion}.db`,
69
+ configureDb: (db) =>
70
+ db.execute([...CF_SQL_VFS_REQUIRED_PRAGMAS, 'cache_size=-8000'].map((p) => `PRAGMA ${p}`).join(';\n')),
71
+ }).pipe(UnknownError.mapToUnknownError)
72
+
73
+ // dbEventlog runs on DO SQLite directly (not through the VFS). SQL-level transaction
74
+ // control (BEGIN/COMMIT/ROLLBACK) is silently dropped — see isTransactionControlStatement
75
+ // in make-sqlite-db.ts for details on why this is safe.
76
+ const dbEventlog = yield* makeDoSqliteDb({
77
+ _tag: 'file',
78
+ db: storage.sql,
70
79
  configureDb: () => {},
71
- }).pipe(UnexpectedError.mapToUnexpectedError)
80
+ }).pipe(UnknownError.mapToUnknownError)
72
81
 
73
82
  const shutdownChannel = yield* WebChannel.noopChannel<any, any>()
74
83
 
75
- // Use Durable Object sync backend if no backend is specified
76
-
77
84
  const layer = yield* Layer.build(
78
85
  makeLeaderThreadLayer({
79
86
  schema,
@@ -85,12 +92,14 @@ export const makeAdapter =
85
92
  dbEventlog,
86
93
  devtoolsOptions,
87
94
  shutdownChannel,
88
- syncPayload,
89
- }),
95
+ syncPayloadEncoded,
96
+ syncPayloadSchema,
97
+ }).pipe(Layer.provide(StateHead.layer({ dbState }))),
90
98
  )
91
99
 
92
100
  const { leaderThread, initialSnapshot } = yield* Effect.gen(function* () {
93
- const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState } = yield* LeaderThreadCtx
101
+ const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState, networkStatus } =
102
+ yield* LeaderThreadCtx
94
103
 
95
104
  const initialLeaderHead = Eventlog.getClientHeadFromDb(dbEventlog)
96
105
  // const initialLeaderHead = EventSequenceNumber.ROOT
@@ -99,17 +108,24 @@ export const makeAdapter =
99
108
  {
100
109
  events: {
101
110
  pull: ({ cursor }) => syncProcessor.pull({ cursor }),
102
- push: (batch) =>
103
- syncProcessor.push(
104
- batch.map((item) => new LiveStoreEvent.EncodedWithMeta(item)),
105
- { waitForProcessing: true },
106
- ),
111
+ push: (batch) => syncProcessor.push(batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item))),
112
+ stream: (options) =>
113
+ streamEventsWithSyncState({
114
+ dbEventlog,
115
+ syncState: syncProcessor.syncState,
116
+ options,
117
+ }),
118
+ },
119
+ initialState: {
120
+ leaderHead: initialLeaderHead,
121
+ migrationsReport: initialState.migrationsReport,
122
+ storageMode: 'persisted',
107
123
  },
108
- initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
109
124
  export: Effect.sync(() => dbState.export()),
110
125
  getEventlogData: Effect.sync(() => dbEventlog.export()),
111
- getSyncState: syncProcessor.syncState,
112
- sendDevtoolsMessage: (message) => extraIncomingMessagesQueue.offer(message),
126
+ syncState: syncProcessor.syncState,
127
+ sendDevtoolsMessage: (message) => Queue.offer(extraIncomingMessagesQueue, message),
128
+ networkStatus,
113
129
  },
114
130
  {
115
131
  // overrides: testing?.overrides?.clientSession?.leaderThreadProxy
@@ -130,14 +146,14 @@ export const makeAdapter =
130
146
  sqliteDb: syncInMemoryDb,
131
147
  webmeshMode: 'proxy',
132
148
  connectWebmeshNode: Effect.fnUntraced(function* ({ webmeshNode }) {
133
- console.log('connectWebmeshNode', { webmeshNode })
134
- // if (devtoolsOptions.enabled) {
135
- // yield* Webmesh.connectViaWebSocket({
136
- // node: webmeshNode,
137
- // url: `ws://${devtoolsOptions.host}:${devtoolsOptions.port}`,
138
- // openTimeout: 500,
139
- // }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
140
- // }
149
+ if (devtoolsOptions.enabled === true) {
150
+ console.log('connectWebmeshNode', { webmeshNode })
151
+ // yield* Webmesh.connectViaWebSocket({
152
+ // node: webmeshNode,
153
+ // url: `ws://${devtoolsOptions.host}:${devtoolsOptions.port}`,
154
+ // openTimeout: 500,
155
+ // }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
156
+ }
141
157
  }),
142
158
  leaderThread,
143
159
  lockStatus,
@@ -146,6 +162,7 @@ export const makeAdapter =
146
162
  isLeader: true,
147
163
  // Not really applicable for node as there is no "reload the app" concept
148
164
  registerBeforeUnload: (_onBeforeUnload) => () => {},
165
+ origin: undefined,
149
166
  })
150
167
 
151
168
  return clientSession
@@ -154,30 +171,26 @@ export const makeAdapter =
154
171
  Effect.provide(FetchHttpClient.layer),
155
172
  )
156
173
 
157
- const getStateDbFileName = (suffix: string) => `state${suffix}@${liveStoreStorageFormatVersion}.db`
158
-
159
- const getEventlogDbFileName = () => `eventlog@${liveStoreStorageFormatVersion}.db`
160
-
161
174
  const resetDurableObjectPersistence = ({
162
175
  storage,
163
176
  storeId,
164
- dbFileNames,
165
177
  }: {
166
- storage: CfWorker.DurableObjectStorage
178
+ storage: CfTypes.DurableObjectStorage
167
179
  storeId: string
168
- dbFileNames: ReadonlyArray<string>
169
180
  }) =>
170
181
  Effect.try({
171
182
  try: () =>
183
+ // All three tables live in the DO's single storage.sql database but are
184
+ // owned by different layers during normal operation:
185
+ // - vfs_pages: written by the wa-sqlite VFS layer (backs dbState)
186
+ // - eventlog, __livestore_sync_status: written directly by dbEventlog via storage.sql
172
187
  storage.transactionSync(() => {
173
- for (const baseName of dbFileNames) {
174
- const likePattern = `${baseName}%`
175
- safeSqlExec(storage, 'DELETE FROM vfs_blocks WHERE file_path LIKE ?', likePattern)
176
- safeSqlExec(storage, 'DELETE FROM vfs_files WHERE file_path LIKE ?', likePattern)
177
- }
188
+ safeSqlExec(storage, 'DELETE FROM vfs_pages')
189
+ safeSqlExec(storage, 'DELETE FROM eventlog')
190
+ safeSqlExec(storage, 'DELETE FROM __livestore_sync_status')
178
191
  }),
179
192
  catch: (cause) =>
180
- new UnexpectedError({
193
+ new UnknownError({
181
194
  cause,
182
195
  note: `@livestore/adapter-cloudflare: Failed to reset persistence for store ${storeId}`,
183
196
  }),
@@ -186,11 +199,11 @@ const resetDurableObjectPersistence = ({
186
199
  Effect.withSpan('@livestore/adapter-cloudflare:resetPersistence', { attributes: { storeId } }),
187
200
  )
188
201
 
189
- const safeSqlExec = (storage: CfWorker.DurableObjectStorage, query: string, binding: string) => {
202
+ const safeSqlExec = (storage: CfTypes.DurableObjectStorage, query: string, binding?: string) => {
190
203
  try {
191
- storage.sql.exec(query, binding)
204
+ binding !== undefined ? storage.sql.exec(query, binding) : storage.sql.exec(query)
192
205
  } catch (error) {
193
- if (isMissingVfsTableError(error)) {
206
+ if (isMissingTableError(error) === true) {
194
207
  return
195
208
  }
196
209
 
@@ -198,5 +211,5 @@ const safeSqlExec = (storage: CfWorker.DurableObjectStorage, query: string, bind
198
211
  }
199
212
  }
200
213
 
201
- const isMissingVfsTableError = (error: unknown): boolean =>
214
+ const isMissingTableError = (error: unknown): boolean =>
202
215
  error instanceof Error && error.message.toLowerCase().includes('no such table')
@@ -8,23 +8,25 @@ import type {
8
8
  SqliteDbSession,
9
9
  } from '@livestore/common'
10
10
  import { SqliteDbHelper, SqliteError } from '@livestore/common'
11
+ import type { CfTypes } from '@livestore/common-cf'
11
12
  import { EventSequenceNumber } from '@livestore/common/schema'
12
13
  import { Effect } from '@livestore/utils/effect'
13
- import type * as CfWorker from './cf-types.ts'
14
14
 
15
15
  // Simplified prepared statement implementation using only public API
16
16
  class CloudflarePreparedStatement implements PreparedStatement {
17
- private sqlStorage: CfWorker.SqlStorage
17
+ private sqlStorage: CfTypes.SqlStorage
18
18
  public readonly sql: string
19
19
 
20
- constructor(sqlStorage: CfWorker.SqlStorage, sql: string) {
20
+ constructor(sqlStorage: CfTypes.SqlStorage, sql: string) {
21
21
  this.sqlStorage = sqlStorage
22
22
  this.sql = sql
23
23
  }
24
24
 
25
25
  execute = (bindValues?: PreparedBindValues, options?: { onRowsChanged?: (count: number) => void }) => {
26
26
  try {
27
- const cursor = this.sqlStorage.exec(this.sql, ...(bindValues ? Object.values(bindValues) : []))
27
+ if (isTransactionControlStatement(this.sql) === true) return
28
+
29
+ const cursor = this.sqlStorage.exec(this.sql, ...(bindValues !== undefined ? Object.values(bindValues) : []))
28
30
 
29
31
  // Count affected rows by iterating through cursor
30
32
  let changedCount = 0
@@ -32,7 +34,7 @@ class CloudflarePreparedStatement implements PreparedStatement {
32
34
  changedCount++
33
35
  }
34
36
 
35
- if (options?.onRowsChanged) {
37
+ if (options?.onRowsChanged !== undefined) {
36
38
  options.onRowsChanged(changedCount)
37
39
  }
38
40
  } catch (e) {
@@ -46,9 +48,9 @@ class CloudflarePreparedStatement implements PreparedStatement {
46
48
 
47
49
  select = <T>(bindValues?: PreparedBindValues): readonly T[] => {
48
50
  try {
49
- const cursor = this.sqlStorage.exec<Record<string, CfWorker.SqlStorageValue>>(
51
+ const cursor = this.sqlStorage.exec<Record<string, CfTypes.SqlStorageValue>>(
50
52
  this.sql,
51
- ...(bindValues ? Object.values(bindValues) : []),
53
+ ...(bindValues !== undefined ? Object.values(bindValues) : []),
52
54
  )
53
55
  const results: T[] = []
54
56
 
@@ -84,12 +86,12 @@ type CloudflareDatabaseInput =
84
86
  _tag: 'file'
85
87
  // databaseName: string
86
88
  // directory: string
87
- db: CfWorker.SqlStorage
89
+ db: CfTypes.SqlStorage
88
90
  configureDb: (db: SqliteDb) => void
89
91
  }
90
92
  | {
91
93
  _tag: 'in-memory'
92
- db: CfWorker.SqlStorage
94
+ db: CfTypes.SqlStorage
93
95
  configureDb: (db: SqliteDb) => void
94
96
  }
95
97
 
@@ -97,14 +99,12 @@ export type MakeCloudflareSqliteDb = MakeSqliteDb<Metadata, CloudflareDatabaseIn
97
99
 
98
100
  export const makeSqliteDb: MakeCloudflareSqliteDb = (input: CloudflareDatabaseInput) =>
99
101
  Effect.gen(function* () {
100
- // console.log('makeSqliteDb', input)
101
102
  if (input._tag === 'in-memory') {
102
103
  return makeSqliteDb_<Metadata>({
103
104
  sqlStorage: input.db,
104
105
  metadata: {
105
106
  _tag: 'file' as const,
106
107
  dbPointer: 0,
107
- // persistenceInfo: { fileName: ':memory:' },
108
108
  persistenceInfo: { fileName: 'cf' },
109
109
  input,
110
110
  configureDb: input.configureDb,
@@ -118,7 +118,6 @@ export const makeSqliteDb: MakeCloudflareSqliteDb = (input: CloudflareDatabaseIn
118
118
  metadata: {
119
119
  _tag: 'file' as const,
120
120
  dbPointer: 0,
121
- // persistenceInfo: { fileName: `${input.directory}/${input.databaseName}` },
122
121
  persistenceInfo: { fileName: 'cf' },
123
122
  input,
124
123
  configureDb: input.configureDb,
@@ -130,14 +129,13 @@ export const makeSqliteDb: MakeCloudflareSqliteDb = (input: CloudflareDatabaseIn
130
129
  export const makeSqliteDb_ = <
131
130
  TMetadata extends {
132
131
  persistenceInfo: PersistenceInfo
133
- // deleteDb: () => void
134
132
  configureDb: (db: SqliteDb<TMetadata>) => void
135
133
  },
136
134
  >({
137
135
  sqlStorage,
138
136
  metadata,
139
137
  }: {
140
- sqlStorage: CfWorker.SqlStorage
138
+ sqlStorage: CfTypes.SqlStorage
141
139
  metadata: TMetadata
142
140
  }): SqliteDb<TMetadata> => {
143
141
  const preparedStmts: PreparedStatement[] = []
@@ -149,7 +147,7 @@ export const makeSqliteDb_ = <
149
147
  metadata,
150
148
  debug: {
151
149
  // Setting initially to root but will be set to correct value shortly after
152
- head: EventSequenceNumber.ROOT,
150
+ head: EventSequenceNumber.Client.ROOT,
153
151
  },
154
152
  prepare: (queryStr) => {
155
153
  try {
@@ -188,18 +186,13 @@ export const makeSqliteDb_ = <
188
186
  destroy: () => {
189
187
  sqliteDb.close()
190
188
 
191
- // metadata.deleteDb()
192
189
  throw new SqliteError({
193
190
  code: -1,
194
191
  cause: 'Database destroy not supported with public SqlStorage API',
195
192
  })
196
-
197
- // if (metadata._tag === 'opfs') {
198
- // metadata.vfs.resetAccessHandle(metadata.fileName)
199
- // }
200
193
  },
201
194
  close: () => {
202
- if (isClosed) {
195
+ if (isClosed === true) {
203
196
  return
204
197
  }
205
198
 
@@ -259,3 +252,40 @@ export const makeSqliteDb_ = <
259
252
 
260
253
  return sqliteDb
261
254
  }
255
+
256
+ /**
257
+ * CF DO SQLite rejects SQL-level transaction control and requires `storage.transactionSync()` instead.
258
+ * The current adapter only detects and suppresses those SQL statements. It does not yet translate the
259
+ * caller's transaction intent into a shared Durable Object storage transaction.
260
+ *
261
+ * ## Consistency implications
262
+ *
263
+ * `LeaderSyncProcessor.materializeEventsBatch()` wraps both `dbState` and `dbEventlog` in
264
+ * `BEGIN`/`COMMIT` to keep them consistent. Because this adapter drops those statements,
265
+ * eventlog INSERTs are auto-committed individually while `dbState` (VFS-backed) still has
266
+ * real transaction boundaries. If a batch partially fails, earlier eventlog rows survive
267
+ * while `dbState` rolls back.
268
+ *
269
+ * This is safe because:
270
+ * - The eventlog is append-only and idempotent — replaying already-inserted events is a no-op.
271
+ * - State is always rebuildable from the eventlog on cold start (`recreateDb`).
272
+ * - A Durable Object is single-threaded, so no concurrent reader can observe the
273
+ * intermediate inconsistency.
274
+ *
275
+ * Uses prefix matching to cover all SQLite variants:
276
+ * - `BEGIN [DEFERRED | IMMEDIATE | EXCLUSIVE] [TRANSACTION]`
277
+ * - `COMMIT [TRANSACTION]` / `END [TRANSACTION]`
278
+ * - `ROLLBACK [TRANSACTION] [TO [SAVEPOINT] name]`
279
+ * - `SAVEPOINT name` / `RELEASE [SAVEPOINT] name`
280
+ */
281
+ const isTransactionControlStatement = (sql: string): boolean => {
282
+ const upper = sql.trim().toUpperCase()
283
+ return (
284
+ upper.startsWith('BEGIN') === true ||
285
+ upper.startsWith('COMMIT') === true ||
286
+ upper.startsWith('END') === true ||
287
+ upper.startsWith('ROLLBACK') === true ||
288
+ upper.startsWith('SAVEPOINT') === true ||
289
+ upper.startsWith('RELEASE') === true
290
+ )
291
+ }
package/src/mod.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  import './polyfill.ts'
2
2
 
3
3
  export type { ClientDoWithRpcCallback } from '@livestore/common-cf'
4
- export {
5
- type CreateStoreDoOptions,
6
- createStoreDo,
7
- createStoreDoPromise,
8
- type Env,
9
- } from './create-store-do.ts'
4
+ export { type CreateStoreDoOptions, createStoreDo, createStoreDoPromise, type Env } from './create-store-do.ts'
10
5
  export { makeAdapter } from './make-adapter.ts'
@@ -1,2 +0,0 @@
1
- export { type D1Database, type D1Result, type DurableObject, type DurableObjectNamespace, type DurableObjectState, type DurableObjectStorage, type DurableObjectStub, type MessageEvent, Request, Response, Rpc, type SqlStorage, SqlStorageCursor, SqlStorageStatement, type SqlStorageValue, WebSocket, WebSocketPair, WebSocketRequestResponsePair, } from '@cloudflare/workers-types';
2
- //# sourceMappingURL=cf-types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cf-types.d.ts","sourceRoot":"","sources":["../src/cf-types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,OAAO,EACP,QAAQ,EACR,GAAG,EACH,KAAK,UAAU,EACf,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,aAAa,EACb,4BAA4B,GAC7B,MAAM,2BAA2B,CAAA"}
package/dist/cf-types.js DELETED
@@ -1,2 +0,0 @@
1
- export { Request, Response, Rpc, SqlStorageCursor, SqlStorageStatement, WebSocket, WebSocketPair, WebSocketRequestResponsePair, } from '@cloudflare/workers-types';
2
- //# sourceMappingURL=cf-types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cf-types.js","sourceRoot":"","sources":["../src/cf-types.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,OAAO,EACP,QAAQ,EACR,GAAG,EAEH,gBAAgB,EAChB,mBAAmB,EAEnB,SAAS,EACT,aAAa,EACb,4BAA4B,GAC7B,MAAM,2BAA2B,CAAA"}
@@ -1,48 +0,0 @@
1
- import type { UnexpectedError } from '@livestore/common';
2
- import { type LiveStoreSchema, type Store, type Unsubscribe } from '@livestore/livestore';
3
- import type * as CfSyncBackend from '@livestore/sync-cf/cf-worker';
4
- import { Effect } from '@livestore/utils/effect';
5
- import type * as CfWorker from './cf-types.ts';
6
- export type MakeDurableObjectClassOptions<TSchema extends LiveStoreSchema = LiveStoreSchema.Any> = {
7
- schema: TSchema;
8
- clientId: string;
9
- sessionId: string;
10
- onStoreReady?: (store: Store<TSchema>) => Effect.SyncOrPromiseOrEffect<void, UnexpectedError>;
11
- registerQueries?: (store: Store<TSchema>) => Effect.SyncOrPromiseOrEffect<ReadonlyArray<Unsubscribe>>;
12
- syncBackendUrl?: string;
13
- handleCustomRequest?: (request: CfWorker.Request, ensureStore: Effect.Effect<Store<TSchema>, UnexpectedError, never>) => Effect.SyncOrPromiseOrEffect<CfWorker.Response | undefined, UnexpectedError>;
14
- };
15
- export type Env = {
16
- SYNC_BACKEND_DO: CfWorker.DurableObjectNamespace;
17
- };
18
- export type MakeDurableObjectClass = <TSchema extends LiveStoreSchema = LiveStoreSchema.Any>(options: MakeDurableObjectClassOptions<TSchema>) => {
19
- new (ctx: CfWorker.DurableObjectState, env: Env): CfWorker.DurableObject & CfWorker.Rpc.DurableObjectBranded;
20
- };
21
- /**
22
- * Options used to initialize the LiveStore Durable Object runtime.
23
- */
24
- export type CreateStoreDoOptions<TSchema extends LiveStoreSchema = LiveStoreSchema.Any> = {
25
- /** LiveStore schema that defines state, migrations, and validators. */
26
- schema: TSchema;
27
- /** Logical identifier for the store instance persisted inside the Durable Object. */
28
- storeId: string;
29
- /** Unique identifier for the client that owns the Durable Object instance. */
30
- clientId: string;
31
- /** Identifier for the LiveStore session running inside the Durable Object. */
32
- sessionId: string;
33
- /** Cloudflare Durable Object storage binding backing the local SQLite files. */
34
- storage: CfWorker.DurableObjectStorage;
35
- /** RPC stub pointing at the sync backend Durable Object used for replication. */
36
- syncBackendDurableObject: CfWorker.DurableObjectStub<CfSyncBackend.SyncBackendRpcInterface>;
37
- /** Durable Object identifier for the current instance, forwarded to the sync backend. */
38
- durableObjectId: string;
39
- /** Binding name Cloudflare uses to reach this Durable Object from other workers. */
40
- bindingName: string;
41
- /** Enables live pull mode to receive sync updates via Durable Object RPC callbacks. */
42
- livePull?: boolean;
43
- /** Clears existing Durable Object persistence before bootstrapping the store. */
44
- resetPersistence?: boolean;
45
- };
46
- export declare const createStoreDo: <TSchema extends LiveStoreSchema = LiveStoreSchema.Any>({ schema, storeId, clientId, sessionId, storage, syncBackendDurableObject, durableObjectId, bindingName, livePull, resetPersistence, }: CreateStoreDoOptions<TSchema>) => Effect.Effect<Store<TSchema, {}>, UnexpectedError, never>;
47
- export declare const createStoreDoPromise: <TSchema extends LiveStoreSchema = LiveStoreSchema.Any>(options: CreateStoreDoOptions<TSchema>) => Promise<Store<TSchema, {}>>;
48
- //# sourceMappingURL=make-client-durable-object.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"make-client-durable-object.d.ts","sourceRoot":"","sources":["../src/make-client-durable-object.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAe,KAAK,eAAe,EAAe,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACnH,OAAO,KAAK,KAAK,aAAa,MAAM,8BAA8B,CAAA;AAElE,OAAO,EAAE,MAAM,EAA2B,MAAM,yBAAyB,CAAA;AACzE,OAAO,KAAK,KAAK,QAAQ,MAAM,eAAe,CAAA;AAK9C,MAAM,MAAM,6BAA6B,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,CAAC,GAAG,IAAI;IACjG,MAAM,EAAE,OAAO,CAAA;IAEf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;IAG7F,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,qBAAqB,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAA;IACrG,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB,mBAAmB,CAAC,EAAE,CACpB,OAAO,EAAE,QAAQ,CAAC,OAAO,EACzB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,KAC/D,MAAM,CAAC,qBAAqB,CAAC,QAAQ,CAAC,QAAQ,GAAG,SAAS,EAAE,eAAe,CAAC,CAAA;CAClF,CAAA;AAED,MAAM,MAAM,GAAG,GAAG;IAChB,eAAe,EAAE,QAAQ,CAAC,sBAAsB,CAAA;CACjD,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,CAAC,GAAG,EACzF,OAAO,EAAE,6BAA6B,CAAC,OAAO,CAAC,KAC5C;IACH,KAAK,GAAG,EAAE,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAA;CAC7G,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,CAAC,GAAG,IAAI;IACxF,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAA;IAChB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAA;IACjB,gFAAgF;IAChF,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAA;IACtC,iFAAiF;IACjF,wBAAwB,EAAE,QAAQ,CAAC,iBAAiB,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAA;IAC3F,yFAAyF;IACzF,eAAe,EAAE,MAAM,CAAA;IACvB,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAA;IACnB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,OAAO,SAAS,eAAe,GAAG,eAAe,CAAC,GAAG,EAAE,wIAWlF,oBAAoB,CAAC,OAAO,CAAC,8DAsB5B,CAAA;AAEJ,eAAO,MAAM,oBAAoB,GAAI,OAAO,SAAS,eAAe,GAAG,eAAe,CAAC,GAAG,EACxF,SAAS,oBAAoB,CAAC,OAAO,CAAC,gCAOrC,CAAA"}
@@ -1,26 +0,0 @@
1
- import { createStore, provideOtel } from '@livestore/livestore';
2
- import { makeDoRpcSync } from '@livestore/sync-cf/client';
3
- import { Effect, Logger, LogLevel, Scope } from '@livestore/utils/effect';
4
- import { makeAdapter } from "./make-adapter.js";
5
- export const createStoreDo = ({ schema, storeId, clientId, sessionId, storage, syncBackendDurableObject, durableObjectId, bindingName, livePull = false, resetPersistence = false, }) => Effect.gen(function* () {
6
- const scope = yield* Scope.make();
7
- const adapter = makeAdapter({
8
- clientId,
9
- sessionId,
10
- storage,
11
- resetPersistence,
12
- syncOptions: {
13
- backend: makeDoRpcSync({
14
- syncBackendStub: syncBackendDurableObject,
15
- durableObjectContext: { bindingName, durableObjectId },
16
- }),
17
- livePull, // Uses DO RPC callbacks for reactive pull
18
- // backend: makeHttpSync({ url: `http://localhost:8787`, livePull: { pollInterval: 500 } }),
19
- initialSyncOptions: { _tag: 'Blocking', timeout: 500 },
20
- // backend: makeWsSyncProviderClient({ durableObject: syncBackendDurableObject }),
21
- },
22
- });
23
- return yield* createStore({ schema, adapter, storeId }).pipe(Scope.extend(scope), provideOtel({}));
24
- });
25
- export const createStoreDoPromise = (options) => createStoreDo(options).pipe(Logger.withMinimumLogLevel(LogLevel.Debug), Effect.provide(Logger.consoleWithThread('DoClient')), Effect.tapCauseLogPretty, Effect.runPromise);
26
- //# sourceMappingURL=make-client-durable-object.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"make-client-durable-object.js","sourceRoot":"","sources":["../src/make-client-durable-object.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAwB,WAAW,EAAgC,MAAM,sBAAsB,CAAA;AAEnH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAyD/C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAwD,EACnF,MAAM,EACN,OAAO,EACP,QAAQ,EACR,SAAS,EACT,OAAO,EACP,wBAAwB,EACxB,eAAe,EACf,WAAW,EACX,QAAQ,GAAG,KAAK,EAChB,gBAAgB,GAAG,KAAK,GACM,EAAE,EAAE,CAClC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IAEjC,MAAM,OAAO,GAAG,WAAW,CAAC;QAC1B,QAAQ;QACR,SAAS;QACT,OAAO;QACP,gBAAgB;QAChB,WAAW,EAAE;YACX,OAAO,EAAE,aAAa,CAAC;gBACrB,eAAe,EAAE,wBAAwB;gBACzC,oBAAoB,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE;aACvD,CAAC;YACF,QAAQ,EAAE,0CAA0C;YACpD,4FAA4F;YAC5F,kBAAkB,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE;YACtD,kFAAkF;SACnF;KACF,CAAC,CAAA;IAEF,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAA;AACpG,CAAC,CAAC,CAAA;AAEJ,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,OAAsC,EACtC,EAAE,CACF,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,CACzB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC1C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,EACpD,MAAM,CAAC,iBAAiB,EACxB,MAAM,CAAC,UAAU,CAClB,CAAA"}
package/src/cf-types.ts DELETED
@@ -1,20 +0,0 @@
1
- export {
2
- type D1Database,
3
- type D1Result,
4
- type DurableObject,
5
- type DurableObjectNamespace,
6
- type DurableObjectState,
7
- type DurableObjectStorage,
8
- type DurableObjectStub,
9
- type MessageEvent,
10
- Request,
11
- Response,
12
- Rpc,
13
- type SqlStorage,
14
- SqlStorageCursor,
15
- SqlStorageStatement,
16
- type SqlStorageValue,
17
- WebSocket,
18
- WebSocketPair,
19
- WebSocketRequestResponsePair,
20
- } from '@cloudflare/workers-types'