@livestore/adapter-cloudflare 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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/WebSocket.d.ts +5 -5
- package/dist/WebSocket.d.ts.map +1 -1
- package/dist/WebSocket.js +18 -15
- package/dist/WebSocket.js.map +1 -1
- package/dist/create-store-do.d.ts +115 -0
- package/dist/create-store-do.d.ts.map +1 -0
- package/dist/create-store-do.js +97 -0
- package/dist/create-store-do.js.map +1 -0
- package/dist/make-adapter.d.ts +2 -2
- package/dist/make-adapter.d.ts.map +1 -1
- package/dist/make-adapter.js +58 -45
- package/dist/make-adapter.js.map +1 -1
- package/dist/make-sqlite-db.d.ts +4 -4
- package/dist/make-sqlite-db.d.ts.map +1 -1
- package/dist/make-sqlite-db.js +41 -12
- package/dist/make-sqlite-db.js.map +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -1
- package/dist/mod.js.map +1 -1
- package/package.json +60 -17
- package/src/WebSocket.ts +23 -18
- package/src/create-store-do.ts +173 -0
- package/src/make-adapter.ts +76 -53
- package/src/make-sqlite-db.ts +52 -21
- package/src/mod.ts +1 -8
- package/dist/cf-types.d.ts +0 -2
- package/dist/cf-types.d.ts.map +0 -1
- package/dist/cf-types.js +0 -2
- package/dist/cf-types.js.map +0 -1
- package/dist/make-client-durable-object.d.ts +0 -48
- package/dist/make-client-durable-object.d.ts.map +0 -1
- package/dist/make-client-durable-object.js +0 -26
- package/dist/make-client-durable-object.js.map +0 -1
- package/src/cf-types.ts +0 -20
- package/src/make-client-durable-object.ts +0 -107
package/dist/make-sqlite-db.js
CHANGED
|
@@ -11,13 +11,15 @@ class CloudflarePreparedStatement {
|
|
|
11
11
|
}
|
|
12
12
|
execute = (bindValues, options) => {
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
if (isTransactionControlStatement(this.sql) === true)
|
|
15
|
+
return;
|
|
16
|
+
const cursor = this.sqlStorage.exec(this.sql, ...(bindValues !== undefined ? Object.values(bindValues) : []));
|
|
15
17
|
// Count affected rows by iterating through cursor
|
|
16
18
|
let changedCount = 0;
|
|
17
19
|
for (const _row of cursor) {
|
|
18
20
|
changedCount++;
|
|
19
21
|
}
|
|
20
|
-
if (options?.onRowsChanged) {
|
|
22
|
+
if (options?.onRowsChanged !== undefined) {
|
|
21
23
|
options.onRowsChanged(changedCount);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -31,7 +33,7 @@ class CloudflarePreparedStatement {
|
|
|
31
33
|
};
|
|
32
34
|
select = (bindValues) => {
|
|
33
35
|
try {
|
|
34
|
-
const cursor = this.sqlStorage.exec(this.sql, ...(bindValues ? Object.values(bindValues) : []));
|
|
36
|
+
const cursor = this.sqlStorage.exec(this.sql, ...(bindValues !== undefined ? Object.values(bindValues) : []));
|
|
35
37
|
const results = [];
|
|
36
38
|
for (const row of cursor) {
|
|
37
39
|
results.push(row);
|
|
@@ -51,14 +53,12 @@ class CloudflarePreparedStatement {
|
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
export const makeSqliteDb = (input) => Effect.gen(function* () {
|
|
54
|
-
// console.log('makeSqliteDb', input)
|
|
55
56
|
if (input._tag === 'in-memory') {
|
|
56
57
|
return makeSqliteDb_({
|
|
57
58
|
sqlStorage: input.db,
|
|
58
59
|
metadata: {
|
|
59
60
|
_tag: 'file',
|
|
60
61
|
dbPointer: 0,
|
|
61
|
-
// persistenceInfo: { fileName: ':memory:' },
|
|
62
62
|
persistenceInfo: { fileName: 'cf' },
|
|
63
63
|
input,
|
|
64
64
|
configureDb: input.configureDb,
|
|
@@ -71,7 +71,6 @@ export const makeSqliteDb = (input) => Effect.gen(function* () {
|
|
|
71
71
|
metadata: {
|
|
72
72
|
_tag: 'file',
|
|
73
73
|
dbPointer: 0,
|
|
74
|
-
// persistenceInfo: { fileName: `${input.directory}/${input.databaseName}` },
|
|
75
74
|
persistenceInfo: { fileName: 'cf' },
|
|
76
75
|
input,
|
|
77
76
|
configureDb: input.configureDb,
|
|
@@ -87,7 +86,7 @@ export const makeSqliteDb_ = ({ sqlStorage, metadata, }) => {
|
|
|
87
86
|
metadata,
|
|
88
87
|
debug: {
|
|
89
88
|
// Setting initially to root but will be set to correct value shortly after
|
|
90
|
-
head: EventSequenceNumber.ROOT,
|
|
89
|
+
head: EventSequenceNumber.Client.ROOT,
|
|
91
90
|
},
|
|
92
91
|
prepare: (queryStr) => {
|
|
93
92
|
try {
|
|
@@ -126,17 +125,13 @@ export const makeSqliteDb_ = ({ sqlStorage, metadata, }) => {
|
|
|
126
125
|
}),
|
|
127
126
|
destroy: () => {
|
|
128
127
|
sqliteDb.close();
|
|
129
|
-
// metadata.deleteDb()
|
|
130
128
|
throw new SqliteError({
|
|
131
129
|
code: -1,
|
|
132
130
|
cause: 'Database destroy not supported with public SqlStorage API',
|
|
133
131
|
});
|
|
134
|
-
// if (metadata._tag === 'opfs') {
|
|
135
|
-
// metadata.vfs.resetAccessHandle(metadata.fileName)
|
|
136
|
-
// }
|
|
137
132
|
},
|
|
138
133
|
close: () => {
|
|
139
|
-
if (isClosed) {
|
|
134
|
+
if (isClosed === true) {
|
|
140
135
|
return;
|
|
141
136
|
}
|
|
142
137
|
for (const stmt of preparedStmts) {
|
|
@@ -191,4 +186,38 @@ export const makeSqliteDb_ = ({ sqlStorage, metadata, }) => {
|
|
|
191
186
|
metadata.configureDb(sqliteDb);
|
|
192
187
|
return sqliteDb;
|
|
193
188
|
};
|
|
189
|
+
/**
|
|
190
|
+
* CF DO SQLite rejects SQL-level transaction control and requires `storage.transactionSync()` instead.
|
|
191
|
+
* The current adapter only detects and suppresses those SQL statements. It does not yet translate the
|
|
192
|
+
* caller's transaction intent into a shared Durable Object storage transaction.
|
|
193
|
+
*
|
|
194
|
+
* ## Consistency implications
|
|
195
|
+
*
|
|
196
|
+
* `LeaderSyncProcessor.materializeEventsBatch()` wraps both `dbState` and `dbEventlog` in
|
|
197
|
+
* `BEGIN`/`COMMIT` to keep them consistent. Because this adapter drops those statements,
|
|
198
|
+
* eventlog INSERTs are auto-committed individually while `dbState` (VFS-backed) still has
|
|
199
|
+
* real transaction boundaries. If a batch partially fails, earlier eventlog rows survive
|
|
200
|
+
* while `dbState` rolls back.
|
|
201
|
+
*
|
|
202
|
+
* This is safe because:
|
|
203
|
+
* - The eventlog is append-only and idempotent — replaying already-inserted events is a no-op.
|
|
204
|
+
* - State is always rebuildable from the eventlog on cold start (`recreateDb`).
|
|
205
|
+
* - A Durable Object is single-threaded, so no concurrent reader can observe the
|
|
206
|
+
* intermediate inconsistency.
|
|
207
|
+
*
|
|
208
|
+
* Uses prefix matching to cover all SQLite variants:
|
|
209
|
+
* - `BEGIN [DEFERRED | IMMEDIATE | EXCLUSIVE] [TRANSACTION]`
|
|
210
|
+
* - `COMMIT [TRANSACTION]` / `END [TRANSACTION]`
|
|
211
|
+
* - `ROLLBACK [TRANSACTION] [TO [SAVEPOINT] name]`
|
|
212
|
+
* - `SAVEPOINT name` / `RELEASE [SAVEPOINT] name`
|
|
213
|
+
*/
|
|
214
|
+
const isTransactionControlStatement = (sql) => {
|
|
215
|
+
const upper = sql.trim().toUpperCase();
|
|
216
|
+
return (upper.startsWith('BEGIN') === true ||
|
|
217
|
+
upper.startsWith('COMMIT') === true ||
|
|
218
|
+
upper.startsWith('END') === true ||
|
|
219
|
+
upper.startsWith('ROLLBACK') === true ||
|
|
220
|
+
upper.startsWith('SAVEPOINT') === true ||
|
|
221
|
+
upper.startsWith('RELEASE') === true);
|
|
222
|
+
};
|
|
194
223
|
//# sourceMappingURL=make-sqlite-db.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make-sqlite-db.js","sourceRoot":"","sources":["../src/make-sqlite-db.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"make-sqlite-db.js","sourceRoot":"","sources":["../src/make-sqlite-db.ts"],"names":[],"mappings":"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"}
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import './polyfill.ts';
|
|
2
2
|
export type { ClientDoWithRpcCallback } from '@livestore/common-cf';
|
|
3
|
+
export { type CreateStoreDoOptions, createStoreDo, createStoreDoPromise, type Env } from './create-store-do.ts';
|
|
3
4
|
export { makeAdapter } from './make-adapter.ts';
|
|
4
|
-
export { type CreateStoreDoOptions, createStoreDo, createStoreDoPromise, type Env, type MakeDurableObjectClass, type MakeDurableObjectClassOptions, } from './make-client-durable-object.ts';
|
|
5
5
|
//# sourceMappingURL=mod.d.ts.map
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AACnE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AACnE,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,oBAAoB,EAAE,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC/G,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./polyfill.js";
|
|
2
|
+
export { createStoreDo, createStoreDoPromise } from "./create-store-do.js";
|
|
2
3
|
export { makeAdapter } from "./make-adapter.js";
|
|
3
|
-
export { createStoreDo, createStoreDoPromise, } from "./make-client-durable-object.js";
|
|
4
4
|
//# sourceMappingURL=mod.js.map
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAGtB,OAAO,
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAGtB,OAAO,EAA6B,aAAa,EAAE,oBAAoB,EAAY,MAAM,sBAAsB,CAAA;AAC/G,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestore/adapter-cloudflare",
|
|
3
|
-
"version": "0.4.0
|
|
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": [
|
|
6
16
|
"./src/polyfill.ts",
|
|
@@ -10,26 +20,59 @@
|
|
|
10
20
|
".": "./dist/mod.js",
|
|
11
21
|
"./polyfill": "./dist/polyfill.js"
|
|
12
22
|
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
13
26
|
"dependencies": {
|
|
14
|
-
"@cloudflare/workers-types": "4.
|
|
15
|
-
"@livestore/common": "0.4.0
|
|
16
|
-
"@livestore/common-cf": "0.4.0
|
|
17
|
-
"@livestore/
|
|
18
|
-
"@livestore/sqlite-wasm": "0.4.0
|
|
19
|
-
"@livestore/
|
|
20
|
-
"@livestore/utils": "0.4.0
|
|
27
|
+
"@cloudflare/workers-types": "4.20251118.0",
|
|
28
|
+
"@livestore/common": "^0.4.0",
|
|
29
|
+
"@livestore/common-cf": "^0.4.0",
|
|
30
|
+
"@livestore/livestore": "^0.4.0",
|
|
31
|
+
"@livestore/sqlite-wasm": "^0.4.0",
|
|
32
|
+
"@livestore/sync-cf": "^0.4.0",
|
|
33
|
+
"@livestore/utils": "^0.4.0"
|
|
21
34
|
},
|
|
22
35
|
"devDependencies": {
|
|
23
|
-
"wrangler": "4.
|
|
36
|
+
"wrangler": "4.42.2"
|
|
24
37
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
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-cloudflare",
|
|
64
|
+
"packages/@livestore/adapter-web",
|
|
65
|
+
"packages/@livestore/common",
|
|
66
|
+
"packages/@livestore/common-cf",
|
|
67
|
+
"packages/@livestore/devtools-web-common",
|
|
68
|
+
"packages/@livestore/livestore",
|
|
69
|
+
"packages/@livestore/sqlite-wasm",
|
|
70
|
+
"packages/@livestore/sync-cf",
|
|
71
|
+
"packages/@livestore/utils",
|
|
72
|
+
"packages/@livestore/utils-dev",
|
|
73
|
+
"packages/@livestore/wa-sqlite",
|
|
74
|
+
"packages/@livestore/webmesh"
|
|
75
|
+
]
|
|
33
76
|
},
|
|
34
77
|
"scripts": {
|
|
35
78
|
"test": "echo No tests yet"
|
package/src/WebSocket.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { Effect, Exit, identity,
|
|
3
|
-
import
|
|
1
|
+
import type { CfTypes } from '@livestore/common-cf'
|
|
2
|
+
import { Effect, Exit, identity, type Schedule, type Scope } from '@livestore/utils/effect'
|
|
3
|
+
import { WebSocket } from '@livestore/utils/effect/browser'
|
|
4
4
|
|
|
5
5
|
// TODO refactor using Effect socket implementation
|
|
6
6
|
// https://github.com/Effect-TS/effect/blob/main/packages%2Fexperimental%2Fsrc%2FDevTools%2FClient.ts#L113
|
|
@@ -18,23 +18,27 @@ export const makeWebSocket = ({
|
|
|
18
18
|
durableObject,
|
|
19
19
|
}: {
|
|
20
20
|
/** CF Sync Backend DO with `/sync` endpoint */
|
|
21
|
-
durableObject:
|
|
21
|
+
durableObject: CfTypes.DurableObjectStub
|
|
22
22
|
url: URL
|
|
23
23
|
reconnect?: Schedule.Schedule<unknown> | false
|
|
24
|
-
}): Effect.Effect<
|
|
24
|
+
}): Effect.Effect<CfTypes.WebSocket, WebSocket.WebSocketError, Scope.Scope> =>
|
|
25
25
|
Effect.gen(function* () {
|
|
26
26
|
// yield* validateUrl(url)
|
|
27
27
|
|
|
28
28
|
const socket = yield* Effect.tryPromise({
|
|
29
29
|
try: () =>
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// NOTE: .toString() required due to type mismatch between standard URL and CF's URL types
|
|
31
|
+
durableObject.fetch(url.toString(), { headers: { Upgrade: 'websocket' } }).then((res: any) => {
|
|
32
|
+
if (res.webSocket == null) {
|
|
32
33
|
throw new Error('WebSocket upgrade failed')
|
|
33
34
|
}
|
|
34
|
-
return res.webSocket as
|
|
35
|
+
return res.webSocket as CfTypes.WebSocket
|
|
35
36
|
}),
|
|
36
37
|
catch: (cause) => new WebSocket.WebSocketError({ cause }),
|
|
37
|
-
}).pipe(
|
|
38
|
+
}).pipe(
|
|
39
|
+
reconnect !== undefined && reconnect !== false ? Effect.retry(reconnect) : identity,
|
|
40
|
+
Effect.withSpan('make-websocket-durable-object'),
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
socket.accept()
|
|
40
44
|
|
|
@@ -53,15 +57,16 @@ export const makeWebSocket = ({
|
|
|
53
57
|
*/
|
|
54
58
|
yield* Effect.addFinalizer(
|
|
55
59
|
Effect.fn(function* (exit) {
|
|
56
|
-
try
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
yield* Effect.try({
|
|
61
|
+
try: () => {
|
|
62
|
+
if (Exit.isFailure(exit) === true) {
|
|
63
|
+
socket.close(3000)
|
|
64
|
+
} else {
|
|
65
|
+
socket.close(1000)
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
catch: (error) => new WebSocket.WebSocketError({ cause: error }),
|
|
69
|
+
}).pipe(Effect.orDie)
|
|
65
70
|
}),
|
|
66
71
|
)
|
|
67
72
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { LogConfig, type SyncOptions } from '@livestore/common'
|
|
2
|
+
import type { CfTypes, HelperTypes } from '@livestore/common-cf'
|
|
3
|
+
import { createStore, type LiveStoreSchema, provideOtel } from '@livestore/livestore'
|
|
4
|
+
import type * as CfSyncBackend from '@livestore/sync-cf/cf-worker'
|
|
5
|
+
import { makeDoRpcSync } from '@livestore/sync-cf/client'
|
|
6
|
+
import { Effect, Logger, Scope } from '@livestore/utils/effect'
|
|
7
|
+
|
|
8
|
+
import { makeAdapter } from './make-adapter.ts'
|
|
9
|
+
|
|
10
|
+
export type Env = {
|
|
11
|
+
SYNC_BACKEND_DO: CfTypes.DurableObjectNamespace<CfSyncBackend.SyncBackendRpcInterface>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type CreateStoreDoOptions<TSchema extends LiveStoreSchema, TEnv, TState> = {
|
|
15
|
+
/** LiveStore schema that defines state, migrations, and validators. */
|
|
16
|
+
schema: TSchema
|
|
17
|
+
/** Logical identifier for the store instance persisted inside the Durable Object. */
|
|
18
|
+
storeId: string
|
|
19
|
+
/** Unique identifier for the client that owns the Durable Object instance. */
|
|
20
|
+
clientId: string
|
|
21
|
+
/** Identifier for the LiveStore session running inside the Durable Object. */
|
|
22
|
+
sessionId: string
|
|
23
|
+
/** Runtime details about the Durable Object this store runs inside. Needed for sync backend to call back to this instance. */
|
|
24
|
+
durableObject: {
|
|
25
|
+
/** Durable Object state handle (e.g. `this.ctx`). */
|
|
26
|
+
ctx: TState
|
|
27
|
+
/** Environment bindings associated with the Durable Object. */
|
|
28
|
+
env: TEnv
|
|
29
|
+
/** Binding name Cloudflare uses to reach this Durable Object from other workers. */
|
|
30
|
+
bindingName: HelperTypes.ExtractDurableObjectKeys<NoInfer<TEnv>>
|
|
31
|
+
}
|
|
32
|
+
/** RPC stub pointing at the sync backend Durable Object used for replication. */
|
|
33
|
+
syncBackendStub: CfTypes.DurableObjectStub<CfSyncBackend.SyncBackendRpcInterface>
|
|
34
|
+
/**
|
|
35
|
+
* Enables live pull mode to receive sync updates via Durable Object RPC callbacks.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
livePull?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* Clears existing Durable Object persistence before bootstrapping the store.
|
|
42
|
+
*
|
|
43
|
+
* Note: Only use this for development purposes.
|
|
44
|
+
*/
|
|
45
|
+
resetPersistence?: boolean
|
|
46
|
+
/**
|
|
47
|
+
* Controls how initial sync behaves when the store boots.
|
|
48
|
+
*
|
|
49
|
+
* @default { _tag: 'Blocking', timeout: 500 }
|
|
50
|
+
*/
|
|
51
|
+
initialSyncOptions?: SyncOptions['initialSyncOptions']
|
|
52
|
+
} & LogConfig.WithLoggerOptions
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a LiveStore instance inside a Cloudflare Durable Object.
|
|
56
|
+
*
|
|
57
|
+
* This function bootstraps a full LiveStore client within a Durable Object, enabling
|
|
58
|
+
* persistent state management with automatic sync to a sync backend. Use this when you
|
|
59
|
+
* need server-side LiveStore instances that can communicate with other clients.
|
|
60
|
+
*
|
|
61
|
+
* Returns an Effect that resolves to a Store instance. For a Promise-based API,
|
|
62
|
+
* use `createStoreDoPromise` instead.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { createStoreDo } from '@livestore/adapter-cloudflare'
|
|
67
|
+
* import { schema } from './schema'
|
|
68
|
+
*
|
|
69
|
+
* export class MyDurableObject extends DurableObject {
|
|
70
|
+
* store: Store | undefined
|
|
71
|
+
*
|
|
72
|
+
* async fetch(request: Request) {
|
|
73
|
+
* if (!this.store) {
|
|
74
|
+
* this.store = await createStoreDo({
|
|
75
|
+
* schema,
|
|
76
|
+
* storeId: 'my-store',
|
|
77
|
+
* clientId: this.ctx.id.toString(),
|
|
78
|
+
* sessionId: 'do-session',
|
|
79
|
+
* durableObject: {
|
|
80
|
+
* ctx: this.ctx,
|
|
81
|
+
* env: this.env,
|
|
82
|
+
* bindingName: 'MY_DO',
|
|
83
|
+
* },
|
|
84
|
+
* syncBackendStub: this.env.SYNC_BACKEND_DO.get(syncBackendId),
|
|
85
|
+
* }).pipe(Effect.runPromise)
|
|
86
|
+
* }
|
|
87
|
+
* // Use this.store...
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see https://livestore.dev/docs/reference/adapters/cloudflare for setup guide
|
|
93
|
+
*/
|
|
94
|
+
// TODO Also support in Cloudflare workers outside of a durable object context.
|
|
95
|
+
export const createStoreDo = <
|
|
96
|
+
TSchema extends LiveStoreSchema,
|
|
97
|
+
TEnv,
|
|
98
|
+
TState extends CfTypes.DurableObjectState = CfTypes.DurableObjectState,
|
|
99
|
+
>({
|
|
100
|
+
schema,
|
|
101
|
+
storeId,
|
|
102
|
+
clientId,
|
|
103
|
+
sessionId,
|
|
104
|
+
durableObject,
|
|
105
|
+
syncBackendStub,
|
|
106
|
+
livePull = false,
|
|
107
|
+
resetPersistence = false,
|
|
108
|
+
initialSyncOptions = { _tag: 'Blocking', timeout: 500 },
|
|
109
|
+
}: CreateStoreDoOptions<TSchema, TEnv, TState>) =>
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
const { ctx, bindingName } = durableObject
|
|
112
|
+
const storage = ctx.storage
|
|
113
|
+
const durableObjectId = ctx.id.toString()
|
|
114
|
+
const scope = yield* Scope.make()
|
|
115
|
+
|
|
116
|
+
const adapter = makeAdapter({
|
|
117
|
+
clientId,
|
|
118
|
+
sessionId,
|
|
119
|
+
storage,
|
|
120
|
+
resetPersistence,
|
|
121
|
+
syncOptions: {
|
|
122
|
+
backend: makeDoRpcSync({
|
|
123
|
+
syncBackendStub,
|
|
124
|
+
durableObjectContext: { bindingName, durableObjectId },
|
|
125
|
+
}),
|
|
126
|
+
livePull, // Uses DO RPC callbacks for reactive pull
|
|
127
|
+
initialSyncOptions,
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
return yield* createStore({ schema, adapter, storeId }).pipe(Scope.extend(scope), provideOtel({}))
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Promise-based wrapper around `createStoreDo` for simpler async/await usage.
|
|
136
|
+
*
|
|
137
|
+
* Equivalent to calling `createStoreDo(options).pipe(Effect.runPromise)` with
|
|
138
|
+
* logging configured automatically.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { createStoreDoPromise } from '@livestore/adapter-cloudflare'
|
|
143
|
+
*
|
|
144
|
+
* export class MyDurableObject extends DurableObject {
|
|
145
|
+
* async fetch(request: Request) {
|
|
146
|
+
* const store = await createStoreDoPromise({
|
|
147
|
+
* schema,
|
|
148
|
+
* storeId: 'my-store',
|
|
149
|
+
* clientId: this.ctx.id.toString(),
|
|
150
|
+
* sessionId: 'do-session',
|
|
151
|
+
* durableObject: { ctx: this.ctx, env: this.env, bindingName: 'MY_DO' },
|
|
152
|
+
* syncBackendStub: this.env.SYNC_BACKEND_DO.get(syncBackendId),
|
|
153
|
+
* })
|
|
154
|
+
* // Use store...
|
|
155
|
+
* }
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export const createStoreDoPromise = <
|
|
160
|
+
TSchema extends LiveStoreSchema,
|
|
161
|
+
TEnv,
|
|
162
|
+
TState extends CfTypes.DurableObjectState = CfTypes.DurableObjectState,
|
|
163
|
+
>(
|
|
164
|
+
options: CreateStoreDoOptions<TSchema, TEnv, TState>,
|
|
165
|
+
) =>
|
|
166
|
+
createStoreDo(options).pipe(
|
|
167
|
+
LogConfig.withLoggerConfig(options, {
|
|
168
|
+
threadName: 'DoClient',
|
|
169
|
+
defaultLogger: Logger.consoleWithThread('DoClient'),
|
|
170
|
+
}),
|
|
171
|
+
Effect.tapCauseLogPretty,
|
|
172
|
+
Effect.runPromise,
|
|
173
|
+
)
|