@livestore/sync-cf 0.3.2-dev.9 → 0.4.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.
@@ -1,24 +1,30 @@
1
1
  import type * as CfWorker from '@cloudflare/workers-types';
2
2
  import type { Schema } from '@livestore/utils/effect';
3
3
  import type { Env } from './durable-object.ts';
4
- /**
5
- * Helper type to extract DurableObject keys from Env to give consumer type safety.
6
- *
7
- * @example
8
- * ```ts
9
- * type PlatformEnv = {
10
- * DB: D1Database
11
- * ADMIN_TOKEN: string
12
- * WEBSOCKET_SERVER: DurableObjectNamespace<WebSocketServer>
13
- * }
14
- * export default makeWorker<PlatformEnv>({
15
- * durableObject: { name: "WEBSOCKET_SERVER" },
16
- * // ^ (property) name?: "WEBSOCKET_SERVER" | undefined
17
- * });
18
- */
19
- type ExtractDurableObjectKeys<TEnv = Env> = TEnv extends Env ? [keyof TEnv] extends [keyof Env] ? string : keyof {
20
- [K in keyof TEnv as K extends keyof Env ? never : TEnv[K] extends CfWorker.DurableObjectNamespace<any> ? K : never]: TEnv[K];
21
- } : never;
4
+ export declare namespace HelperTypes {
5
+ type AnyDON = CfWorker.DurableObjectNamespace<undefined>;
6
+ type DOKeys<T> = {
7
+ [K in keyof T]-?: T[K] extends AnyDON ? K : never;
8
+ }[keyof T];
9
+ type NonBuiltins<T> = Omit<T, keyof Env>;
10
+ /**
11
+ * Helper type to extract DurableObject keys from Env to give consumer type safety.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * type PlatformEnv = {
16
+ * DB: D1Database
17
+ * ADMIN_TOKEN: string
18
+ * WEBSOCKET_SERVER: DurableObjectNamespace<WebSocketServer>
19
+ * }
20
+ * export default makeWorker<PlatformEnv>({
21
+ * durableObject: { name: "WEBSOCKET_SERVER" },
22
+ * // ^ (property) name?: "WEBSOCKET_SERVER" | undefined
23
+ * });
24
+ */
25
+ export type ExtractDurableObjectKeys<TEnv = Env> = DOKeys<NonBuiltins<TEnv>> extends never ? string : DOKeys<NonBuiltins<TEnv>>;
26
+ export {};
27
+ }
22
28
  export type CFWorker<TEnv extends Env = Env, _T extends CfWorker.Rpc.DurableObjectBranded | undefined = undefined> = {
23
29
  fetch: <CFHostMetada = unknown>(request: CfWorker.Request<CFHostMetada>, env: TEnv, ctx: CfWorker.ExecutionContext) => Promise<CfWorker.Response>;
24
30
  };
@@ -39,7 +45,7 @@ export type MakeWorkerOptions<TEnv extends Env = Env> = {
39
45
  *
40
46
  * @default 'WEBSOCKET_SERVER'
41
47
  */
42
- name?: ExtractDurableObjectKeys<TEnv>;
48
+ name?: HelperTypes.ExtractDurableObjectKeys<TEnv>;
43
49
  };
44
50
  };
45
51
  export declare const makeWorker: <TEnv extends Env = Env, TDurableObjectRpc extends CfWorker.Rpc.DurableObjectBranded | undefined = undefined>(options?: MakeWorkerOptions<TEnv>) => CFWorker<TEnv, TDurableObjectRpc>;
@@ -61,19 +67,19 @@ export declare const makeWorker: <TEnv extends Env = Env, TDurableObjectRpc exte
61
67
  * return handleWebSocket(request, env, ctx, { headers: {}, validatePayload })
62
68
  * }
63
69
  *
64
- * return new Response('Invalid path', { status: 400, headers: corsHeaders })
70
+ * return new Response('Invalid path', { status: 400 })
71
+ * return new Response('Invalid path', { status: 400 })
65
72
  * }
66
73
  * }
67
74
  * ```
68
75
  *
69
76
  * @throws {UnexpectedError} If the payload is invalid
70
77
  */
71
- export declare const handleWebSocket: <TEnv extends Env = Env, TDurableObjectRpc extends CfWorker.Rpc.DurableObjectBranded | undefined = undefined, CFHostMetada = unknown>(request: CfWorker.Request<CFHostMetada>, env: TEnv, _ctx: CfWorker.ExecutionContext, options: {
78
+ export declare const handleWebSocket: <TEnv extends Env = Env, TDurableObjectRpc extends CfWorker.Rpc.DurableObjectBranded | undefined = undefined, CFHostMetada = unknown>(request: CfWorker.Request<CFHostMetada>, env: TEnv, _ctx: CfWorker.ExecutionContext, options?: {
72
79
  headers?: CfWorker.HeadersInit;
73
80
  durableObject?: MakeWorkerOptions<TEnv>["durableObject"];
74
81
  validatePayload?: (payload: Schema.JsonValue | undefined, context: {
75
82
  storeId: string;
76
83
  }) => void | Promise<void>;
77
84
  }) => Promise<CfWorker.Response>;
78
- export {};
79
85
  //# sourceMappingURL=worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/cf-worker/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,QAAQ,MAAM,2BAA2B,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAIrD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AAK9C;;;;;;;;;;;;;;GAcG;AACH,KAAK,wBAAwB,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,SAAS,GAAG,GACxD,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAC9B,MAAM,GACN,MAAM;KACH,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,SAAS,MAAM,GAAG,GACnC,KAAK,GACL,IAAI,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAClD,CAAC,GACD,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;CACtB,GACH,KAAK,CAAA;AAGT,MAAM,MAAM,QAAQ,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,EAAE,EAAE,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,IAAI;IACnH,KAAK,EAAE,CAAC,YAAY,GAAG,OAAO,EAC5B,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,EACvC,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,QAAQ,CAAC,gBAAgB,KAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,IAAI;IACtD;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/G,qBAAqB;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE;QACd;;;;WAIG;QACH,IAAI,CAAC,EAAE,wBAAwB,CAAC,IAAI,CAAC,CAAA;KACtC,CAAA;CACF,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,IAAI,SAAS,GAAG,GAAG,GAAG,EACtB,iBAAiB,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,EAEnF,UAAS,iBAAiB,CAAC,IAAI,CAAM,KACpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAiDlC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,eAAe,GAC1B,IAAI,SAAS,GAAG,GAAG,GAAG,EACtB,iBAAiB,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,EACnF,YAAY,GAAG,OAAO,EAEtB,SAAS,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,EACvC,KAAK,IAAI,EACT,MAAM,QAAQ,CAAC,gBAAgB,EAC/B,SAAS;IACP,OAAO,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAA;IAC9B,aAAa,CAAC,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAA;IACxD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAChH,KACA,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAqD0B,CAAA"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/cf-worker/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,QAAQ,MAAM,2BAA2B,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAKrD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AAK9C,yBAAiB,WAAW,CAAC;IAC3B,KAAK,MAAM,GAAG,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAA;IAExD,KAAK,MAAM,CAAC,CAAC,IAAI;SACd,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK;KAClD,CAAC,MAAM,CAAC,CAAC,CAAA;IAEV,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;IAExC;;;;;;;;;;;;;;OAcG;IACH,MAAM,MAAM,wBAAwB,CAAC,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,SAAS,KAAK,GACtF,MAAM,GACN,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;;CAC9B;AAGD,MAAM,MAAM,QAAQ,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,EAAE,EAAE,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,IAAI;IACnH,KAAK,EAAE,CAAC,YAAY,GAAG,OAAO,EAC5B,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,EACvC,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,QAAQ,CAAC,gBAAgB,KAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,IAAI;IACtD;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/G,qBAAqB;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE;QACd;;;;WAIG;QACH,IAAI,CAAC,EAAE,WAAW,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAA;KAClD,CAAA;CACF,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,IAAI,SAAS,GAAG,GAAG,GAAG,EACtB,iBAAiB,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,EAEnF,UAAS,iBAAiB,CAAC,IAAI,CAAM,KACpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAiDlC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,eAAe,GAC1B,IAAI,SAAS,GAAG,GAAG,GAAG,EACtB,iBAAiB,SAAS,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,SAAS,GAAG,SAAS,EACnF,YAAY,GAAG,OAAO,EAEtB,SAAS,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,EACvC,KAAK,IAAI,EACT,MAAM,QAAQ,CAAC,gBAAgB,EAC/B,UAAS;IACP,OAAO,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAA;IAC9B,aAAa,CAAC,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAA;IACxD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3G,KACL,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAwD0B,CAAA"}
@@ -62,14 +62,15 @@ export const makeWorker = (options = {}) => {
62
62
  * return handleWebSocket(request, env, ctx, { headers: {}, validatePayload })
63
63
  * }
64
64
  *
65
- * return new Response('Invalid path', { status: 400, headers: corsHeaders })
65
+ * return new Response('Invalid path', { status: 400 })
66
+ * return new Response('Invalid path', { status: 400 })
66
67
  * }
67
68
  * }
68
69
  * ```
69
70
  *
70
71
  * @throws {UnexpectedError} If the payload is invalid
71
72
  */
72
- export const handleWebSocket = (request, env, _ctx, options) => Effect.gen(function* () {
73
+ export const handleWebSocket = (request, env, _ctx, options = {}) => Effect.gen(function* () {
73
74
  const url = new URL(request.url);
74
75
  const urlParams = UrlParams.fromInput(url.searchParams);
75
76
  const paramsResult = yield* UrlParams.schemaStruct(SearchParamsSchema)(urlParams).pipe(Effect.either);
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/cf-worker/worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/cf-worker/worker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,19 @@
1
1
  {
2
2
  "name": "@livestore/sync-cf",
3
- "version": "0.3.2-dev.9",
3
+ "version": "0.4.0-dev.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
7
7
  ".": "./dist/sync-impl/mod.js",
8
+ "./common": "./dist/common/mod.js",
8
9
  "./cf-worker": "./dist/cf-worker/mod.js",
9
10
  "./cf-worker/durable-object": "./dist/cf-worker/durable-object.js",
10
11
  "./cf-worker/worker": "./dist/cf-worker/worker.js"
11
12
  },
12
13
  "dependencies": {
13
- "@livestore/common": "0.3.2-dev.9",
14
- "@livestore/utils": "0.3.2-dev.9"
15
- },
16
- "devDependencies": {
17
- "@cloudflare/workers-types": "^4.20250702.0"
14
+ "@cloudflare/workers-types": "4.20250807.0",
15
+ "@livestore/utils": "0.4.0-dev.0",
16
+ "@livestore/common": "0.4.0-dev.0"
18
17
  },
19
18
  "files": [
20
19
  "dist",
@@ -0,0 +1,12 @@
1
+ export {
2
+ type D1Database,
3
+ type D1Result,
4
+ type DurableObject,
5
+ type DurableObjectState,
6
+ Request,
7
+ Response,
8
+ Rpc,
9
+ WebSocket,
10
+ WebSocketPair,
11
+ WebSocketRequestResponsePair,
12
+ } from '@cloudflare/workers-types'
@@ -1,22 +1,23 @@
1
- import { DurableObject } from 'cloudflare:workers'
2
- import { makeColumnSpec, UnexpectedError } from '@livestore/common'
1
+ import { UnexpectedError } from '@livestore/common'
3
2
  import { EventSequenceNumber, type LiveStoreEvent, State } from '@livestore/common/schema'
4
3
  import { shouldNeverHappen } from '@livestore/utils'
5
4
  import { Effect, Logger, LogLevel, Option, Schema, UrlParams } from '@livestore/utils/effect'
6
-
7
5
  import { SearchParamsSchema, WSMessage } from '../common/mod.ts'
8
6
  import type { SyncMetadata } from '../common/ws-message-types.ts'
7
+ import type * as CfWorker from './cf-types.ts'
8
+
9
+ // NOTE We need to redeclare runtime types here to avoid type conflicts with the lib.dom Response type.
10
+ declare class Response extends CfWorker.Response {}
11
+ declare class WebSocketPair extends CfWorker.WebSocketPair {}
12
+ declare class WebSocketRequestResponsePair extends CfWorker.WebSocketRequestResponsePair {}
9
13
 
10
14
  export interface Env {
11
- DB: D1Database
15
+ DB: CfWorker.D1Database
12
16
  ADMIN_SECRET: string
13
17
  }
14
18
 
15
- type WebSocketClient = WebSocket
16
-
17
19
  const encodeOutgoingMessage = Schema.encodeSync(Schema.parseJson(WSMessage.BackendToClientMessage))
18
20
  const encodeIncomingMessage = Schema.encodeSync(Schema.parseJson(WSMessage.ClientToBackendMessage))
19
- const decodeIncomingMessage = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.ClientToBackendMessage))
20
21
 
21
22
  export const eventlogTable = State.SQLite.table({
22
23
  // NOTE actual table name is determined at runtime
@@ -63,17 +64,50 @@ export type MakeDurableObjectClassOptions = {
63
64
  }
64
65
 
65
66
  export type MakeDurableObjectClass = (options?: MakeDurableObjectClassOptions) => {
66
- new (ctx: DurableObjectState, env: Env): DurableObject<Env>
67
+ new (ctx: CfWorker.DurableObjectState, env: Env): CfWorker.DurableObject
67
68
  }
68
69
 
70
+ /**
71
+ * Creates a Durable Object class for handling WebSocket-based sync.
72
+ *
73
+ * Example:
74
+ * ```ts
75
+ * // In your Cloudflare Worker file
76
+ * import { makeDurableObject } from '@livestore/sync-cf/cf-worker'
77
+ *
78
+ * export class WebSocketServer extends makeDurableObject({
79
+ * onPush: async (message) => {
80
+ * console.log('onPush', message.batch)
81
+ * },
82
+ * onPull: async (message) => {
83
+ * console.log('onPull', message)
84
+ * },
85
+ * }) {}
86
+ * ```
87
+ *
88
+ * ```toml
89
+ * # wrangler.toml
90
+ * [new_classes]
91
+ * WebSocketServer = "src/websocket-server.ts"
92
+ * ```
93
+ */
69
94
  export const makeDurableObject: MakeDurableObjectClass = (options) => {
70
- return class WebSocketServerBase extends DurableObject<Env> {
95
+ return class WebSocketServerBase implements CfWorker.DurableObject, CfWorker.Rpc.DurableObjectBranded {
96
+ __DURABLE_OBJECT_BRAND = 'WebSocketServerBase' as never
97
+ ctx: CfWorker.DurableObjectState
98
+ env: Env
99
+
100
+ constructor(ctx: CfWorker.DurableObjectState, env: Env) {
101
+ this.ctx = ctx
102
+ this.env = env
103
+ }
104
+
71
105
  /** Needed to prevent concurrent pushes */
72
106
  private pushSemaphore = Effect.makeSemaphore(1).pipe(Effect.runSync)
73
107
 
74
108
  private currentHead: EventSequenceNumber.GlobalEventSequenceNumber | 'uninitialized' = 'uninitialized'
75
109
 
76
- fetch = async (request: Request) =>
110
+ fetch = async (request: CfWorker.Request): Promise<CfWorker.Response> =>
77
111
  Effect.sync(() => {
78
112
  const { storeId, payload } = getRequestSearchParams(request)
79
113
  const storage = makeStorage(this.ctx, this.env, storeId)
@@ -94,7 +128,7 @@ export const makeDurableObject: MakeDurableObjectClass = (options) => {
94
128
  ),
95
129
  )
96
130
 
97
- const colSpec = makeColumnSpec(eventlogTable.sqliteDef.ast)
131
+ const colSpec = State.SQLite.makeColumnSpec(eventlogTable.sqliteDef.ast)
98
132
  this.env.DB.exec(`CREATE TABLE IF NOT EXISTS ${storage.dbName} (${colSpec}) strict`)
99
133
 
100
134
  return new Response(null, {
@@ -103,190 +137,201 @@ export const makeDurableObject: MakeDurableObjectClass = (options) => {
103
137
  })
104
138
  }).pipe(Effect.tapCauseLogPretty, Effect.runPromise)
105
139
 
106
- webSocketMessage = (ws: WebSocketClient, message: ArrayBuffer | string) => {
107
- console.log('webSocketMessage', message)
108
- const decodedMessageRes = decodeIncomingMessage(message)
140
+ webSocketMessage = (ws: CfWorker.WebSocket, message: ArrayBuffer | string): Promise<void> | undefined => {
141
+ const decodedMessageRes = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.ClientToBackendMessage))(message)
109
142
 
110
143
  if (decodedMessageRes._tag === 'Left') {
111
- console.error('Invalid message received', decodedMessageRes.left)
144
+ Effect.logError('Invalid message received', { message }).pipe(
145
+ Effect.provide(Logger.prettyWithThread('durable-object')),
146
+ Effect.runSync,
147
+ )
112
148
  return
113
149
  }
114
150
 
115
151
  const decodedMessage = decodedMessageRes.right
152
+
116
153
  const requestId = decodedMessage.requestId
117
154
 
118
155
  return Effect.gen(this, function* () {
119
156
  const { storeId, payload } = yield* Schema.decode(WebSocketAttachmentSchema)(ws.deserializeAttachment())
120
157
  const storage = makeStorage(this.ctx, this.env, storeId)
121
158
 
122
- try {
123
- switch (decodedMessage._tag) {
124
- // TODO allow pulling concurrently to not block incoming push requests
125
- case 'WSMessage.PullReq': {
126
- if (options?.onPull) {
127
- yield* Effect.tryAll(() => options.onPull!(decodedMessage, { storeId, payload }))
128
- }
159
+ switch (decodedMessage._tag) {
160
+ // TODO allow pulling concurrently to not block incoming push requests
161
+ case 'WSMessage.PullReq': {
162
+ if (options?.onPull) {
163
+ yield* Effect.tryAll(() => options.onPull!(decodedMessage, { storeId, payload }))
164
+ }
129
165
 
130
- const respond = (message: WSMessage.PullRes) =>
131
- Effect.gen(function* () {
132
- if (options?.onPullRes) {
133
- yield* Effect.tryAll(() => options.onPullRes!(message))
134
- }
135
- ws.send(encodeOutgoingMessage(message))
136
- })
166
+ const respond = (message: WSMessage.PullRes) =>
167
+ Effect.gen(function* () {
168
+ if (options?.onPullRes) {
169
+ yield* Effect.tryAll(() => options.onPullRes!(message))
170
+ }
137
171
 
138
- const cursor = decodedMessage.cursor
172
+ if (ws.readyState !== WebSocket.OPEN) {
173
+ yield* Effect.logWarning('WebSocket not open, skipping send', {
174
+ readyState: ws.readyState,
175
+ message,
176
+ })
177
+ return
178
+ }
139
179
 
140
- // TODO use streaming
141
- const remainingEvents = yield* storage.getEvents(cursor)
180
+ yield* Effect.try({
181
+ try: () => ws.send(encodeOutgoingMessage(message)),
182
+ catch: (cause) =>
183
+ new UnexpectedError({ cause, note: 'Failed to send pull response', payload: { message } }),
184
+ })
185
+ })
142
186
 
143
- // Send at least one response, even if there are no events
144
- const batches =
145
- remainingEvents.length === 0
146
- ? [[]]
147
- : Array.from({ length: Math.ceil(remainingEvents.length / PULL_CHUNK_SIZE) }, (_, i) =>
148
- remainingEvents.slice(i * PULL_CHUNK_SIZE, (i + 1) * PULL_CHUNK_SIZE),
149
- )
187
+ const cursor = decodedMessage.cursor
150
188
 
151
- for (const [index, batch] of batches.entries()) {
152
- const remaining = Math.max(0, remainingEvents.length - (index + 1) * PULL_CHUNK_SIZE)
153
- yield* respond(WSMessage.PullRes.make({ batch, remaining, requestId: { context: 'pull', requestId } }))
154
- }
189
+ // TODO use streaming
190
+ const remainingEvents = yield* storage.getEvents(cursor)
155
191
 
156
- break
192
+ // Send at least one response, even if there are no events
193
+ const batches =
194
+ remainingEvents.length === 0
195
+ ? [[]]
196
+ : Array.from({ length: Math.ceil(remainingEvents.length / PULL_CHUNK_SIZE) }, (_, i) =>
197
+ remainingEvents.slice(i * PULL_CHUNK_SIZE, (i + 1) * PULL_CHUNK_SIZE),
198
+ )
199
+
200
+ for (const [index, batch] of batches.entries()) {
201
+ const remaining = Math.max(0, remainingEvents.length - (index + 1) * PULL_CHUNK_SIZE)
202
+ yield* respond(WSMessage.PullRes.make({ batch, remaining, requestId: { context: 'pull', requestId } }))
157
203
  }
158
- case 'WSMessage.PushReq': {
159
- const respond = (message: WSMessage.PushAck | WSMessage.Error) =>
160
- Effect.gen(function* () {
161
- if (options?.onPushRes) {
162
- yield* Effect.tryAll(() => options.onPushRes!(message))
163
- }
164
- ws.send(encodeOutgoingMessage(message))
165
- })
166
204
 
167
- if (decodedMessage.batch.length === 0) {
168
- yield* respond(WSMessage.PushAck.make({ requestId }))
169
- return
170
- }
205
+ break
206
+ }
207
+ case 'WSMessage.PushReq': {
208
+ const respond = (message: WSMessage.PushAck | WSMessage.Error) =>
209
+ Effect.gen(function* () {
210
+ if (options?.onPushRes) {
211
+ yield* Effect.tryAll(() => options.onPushRes!(message))
212
+ }
213
+ ws.send(encodeOutgoingMessage(message))
214
+ })
215
+
216
+ if (decodedMessage.batch.length === 0) {
217
+ yield* respond(WSMessage.PushAck.make({ requestId }))
218
+ return
219
+ }
171
220
 
172
- yield* this.pushSemaphore.take(1)
221
+ yield* this.pushSemaphore.take(1)
173
222
 
174
- if (options?.onPush) {
175
- yield* Effect.tryAll(() => options.onPush!(decodedMessage, { storeId, payload }))
176
- }
223
+ if (options?.onPush) {
224
+ yield* Effect.tryAll(() => options.onPush!(decodedMessage, { storeId, payload }))
225
+ }
177
226
 
178
- // TODO check whether we could use the Durable Object storage for this to speed up the lookup
179
- // const expectedParentNum = yield* storage.getHead
180
-
181
- let currentHead: EventSequenceNumber.GlobalEventSequenceNumber
182
- if (this.currentHead === 'uninitialized') {
183
- const currentHeadFromStorage = yield* Effect.promise(() => this.ctx.storage.get('currentHead'))
184
- // console.log('currentHeadFromStorage', currentHeadFromStorage)
185
- if (currentHeadFromStorage === undefined) {
186
- // console.log('currentHeadFromStorage is null, getting from D1')
187
- // currentHead = yield* storage.getHead
188
- // console.log('currentHeadFromStorage is null, using root')
189
- currentHead = EventSequenceNumber.ROOT.global
190
- } else {
191
- currentHead = currentHeadFromStorage as EventSequenceNumber.GlobalEventSequenceNumber
192
- }
227
+ // TODO check whether we could use the Durable Object storage for this to speed up the lookup
228
+ // const expectedParentNum = yield* storage.getHead
229
+ let currentHead: EventSequenceNumber.GlobalEventSequenceNumber
230
+ if (this.currentHead === 'uninitialized') {
231
+ const currentHeadFromStorage = yield* Effect.promise(() => this.ctx.storage.get('currentHead'))
232
+ // console.log('currentHeadFromStorage', currentHeadFromStorage)
233
+ if (currentHeadFromStorage === undefined) {
234
+ // console.log('currentHeadFromStorage is null, getting from D1')
235
+ // currentHead = yield* storage.getHead
236
+ // console.log('currentHeadFromStorage is null, using root')
237
+ currentHead = EventSequenceNumber.ROOT.global
193
238
  } else {
194
- // console.log('currentHead is already initialized', this.currentHead)
195
- currentHead = this.currentHead
239
+ currentHead = currentHeadFromStorage as EventSequenceNumber.GlobalEventSequenceNumber
196
240
  }
241
+ } else {
242
+ // console.log('currentHead is already initialized', this.currentHead)
243
+ currentHead = this.currentHead
244
+ }
197
245
 
198
- // TODO handle clientId unique conflict
199
- // Validate the batch
200
- const firstEvent = decodedMessage.batch[0]!
201
- if (firstEvent.parentSeqNum !== currentHead) {
202
- const err = WSMessage.Error.make({
203
- message: `Invalid parent event number. Received e${firstEvent.parentSeqNum} but expected e${currentHead}`,
204
- requestId,
205
- })
246
+ // TODO handle clientId unique conflict
247
+ // Validate the batch
248
+ const firstEvent = decodedMessage.batch[0]!
249
+ if (firstEvent.parentSeqNum !== currentHead) {
250
+ const err = WSMessage.Error.make({
251
+ message: `Invalid parent event number. Received e${firstEvent.parentSeqNum} but expected e${currentHead}`,
252
+ requestId,
253
+ })
206
254
 
207
- yield* Effect.logError(err)
255
+ yield* Effect.logError(err)
208
256
 
209
- yield* respond(err)
210
- yield* this.pushSemaphore.release(1)
211
- return
212
- }
257
+ yield* respond(err)
258
+ yield* this.pushSemaphore.release(1)
259
+ return
260
+ }
213
261
 
214
- yield* respond(WSMessage.PushAck.make({ requestId }))
262
+ yield* respond(WSMessage.PushAck.make({ requestId }))
215
263
 
216
- const createdAt = new Date().toISOString()
264
+ const createdAt = new Date().toISOString()
217
265
 
218
- // NOTE we're not waiting for this to complete yet to allow the broadcast to happen right away
219
- // while letting the async storage write happen in the background
220
- const storeFiber = yield* storage.appendEvents(decodedMessage.batch, createdAt).pipe(Effect.fork)
266
+ // NOTE we're not waiting for this to complete yet to allow the broadcast to happen right away
267
+ // while letting the async storage write happen in the background
268
+ const storeFiber = yield* storage.appendEvents(decodedMessage.batch, createdAt).pipe(Effect.fork)
221
269
 
222
- this.currentHead = decodedMessage.batch.at(-1)!.seqNum
223
- yield* Effect.promise(() => this.ctx.storage.put('currentHead', this.currentHead))
270
+ this.currentHead = decodedMessage.batch.at(-1)!.seqNum
271
+ yield* Effect.promise(() => this.ctx.storage.put('currentHead', this.currentHead))
224
272
 
225
- yield* this.pushSemaphore.release(1)
273
+ yield* this.pushSemaphore.release(1)
226
274
 
227
- const connectedClients = this.ctx.getWebSockets()
228
-
229
- // console.debug(`Broadcasting push batch to ${this.subscribedWebSockets.size} clients`)
230
- if (connectedClients.length > 0) {
231
- // TODO refactor to batch api
232
- const pullRes = WSMessage.PullRes.make({
233
- batch: decodedMessage.batch.map((eventEncoded) => ({
234
- eventEncoded,
235
- metadata: Option.some({ createdAt }),
236
- })),
237
- remaining: 0,
238
- requestId: { context: 'push', requestId },
239
- })
240
- const pullResEnc = encodeOutgoingMessage(pullRes)
275
+ const connectedClients = this.ctx.getWebSockets()
241
276
 
242
- // Only calling once for now.
243
- if (options?.onPullRes) {
244
- yield* Effect.tryAll(() => options.onPullRes!(pullRes))
245
- }
277
+ // console.debug(`Broadcasting push batch to ${this.subscribedWebSockets.size} clients`)
278
+ if (connectedClients.length > 0) {
279
+ // TODO refactor to batch api
280
+ const pullRes = WSMessage.PullRes.make({
281
+ batch: decodedMessage.batch.map((eventEncoded) => ({
282
+ eventEncoded,
283
+ metadata: Option.some({ createdAt }),
284
+ })),
285
+ remaining: 0,
286
+ requestId: { context: 'push', requestId },
287
+ })
288
+ const pullResEnc = encodeOutgoingMessage(pullRes)
246
289
 
247
- // NOTE we're also sending the pullRes to the pushing ws client as a confirmation
248
- for (const conn of connectedClients) {
249
- conn.send(pullResEnc)
250
- }
290
+ // Only calling once for now.
291
+ if (options?.onPullRes) {
292
+ yield* Effect.tryAll(() => options.onPullRes!(pullRes))
251
293
  }
252
294
 
253
- // Wait for the storage write to complete before finishing this request
254
- yield* storeFiber
255
-
256
- break
257
- }
258
- case 'WSMessage.AdminResetRoomReq': {
259
- if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
260
- ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
261
- return
295
+ // NOTE we're also sending the pullRes to the pushing ws client as a confirmation
296
+ for (const conn of connectedClients) {
297
+ conn.send(pullResEnc)
262
298
  }
299
+ }
263
300
 
264
- yield* storage.resetStore
265
- ws.send(encodeOutgoingMessage(WSMessage.AdminResetRoomRes.make({ requestId })))
301
+ // Wait for the storage write to complete before finishing this request
302
+ yield* storeFiber
266
303
 
267
- break
304
+ break
305
+ }
306
+ case 'WSMessage.AdminResetRoomReq': {
307
+ if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
308
+ ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
309
+ return
268
310
  }
269
- case 'WSMessage.AdminInfoReq': {
270
- if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
271
- ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
272
- return
273
- }
274
311
 
275
- ws.send(
276
- encodeOutgoingMessage(
277
- WSMessage.AdminInfoRes.make({ requestId, info: { durableObjectId: this.ctx.id.toString() } }),
278
- ),
279
- )
312
+ yield* storage.resetStore
313
+ ws.send(encodeOutgoingMessage(WSMessage.AdminResetRoomRes.make({ requestId })))
280
314
 
281
- break
282
- }
283
- default: {
284
- console.error('unsupported message', decodedMessage)
285
- return shouldNeverHappen()
315
+ break
316
+ }
317
+ case 'WSMessage.AdminInfoReq': {
318
+ if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
319
+ ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
320
+ return
286
321
  }
322
+
323
+ ws.send(
324
+ encodeOutgoingMessage(
325
+ WSMessage.AdminInfoRes.make({ requestId, info: { durableObjectId: this.ctx.id.toString() } }),
326
+ ),
327
+ )
328
+
329
+ break
330
+ }
331
+ default: {
332
+ console.error('unsupported message', decodedMessage)
333
+ return shouldNeverHappen()
287
334
  }
288
- } catch (error: any) {
289
- ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: error.message, requestId })))
290
335
  }
291
336
  }).pipe(
292
337
  Effect.withSpan(`@livestore/sync-cf:durable-object:webSocketMessage:${decodedMessage._tag}`, {
@@ -304,7 +349,12 @@ export const makeDurableObject: MakeDurableObjectClass = (options) => {
304
349
  )
305
350
  }
306
351
 
307
- webSocketClose = async (ws: WebSocketClient, code: number, _reason: string, _wasClean: boolean) => {
352
+ webSocketClose = async (
353
+ ws: CfWorker.WebSocket,
354
+ code: number,
355
+ _reason: string,
356
+ _wasClean: boolean,
357
+ ): Promise<void> => {
308
358
  // If the client closes the connection, the runtime will invoke the webSocketClose() handler.
309
359
  ws.close(code, 'Durable Object is closing WebSocket')
310
360
  }
@@ -327,10 +377,10 @@ type SyncStorage = {
327
377
  resetStore: Effect.Effect<void, UnexpectedError>
328
378
  }
329
379
 
330
- const makeStorage = (ctx: DurableObjectState, env: Env, storeId: string): SyncStorage => {
380
+ const makeStorage = (ctx: any, env: Env, storeId: string): SyncStorage => {
331
381
  const dbName = `eventlog_${PERSISTENCE_FORMAT_VERSION}_${toValidTableName(storeId)}`
332
382
 
333
- const execDb = <T>(cb: (db: D1Database) => Promise<D1Result<T>>) =>
383
+ const execDb = <T>(cb: (db: CfWorker.D1Database) => Promise<CfWorker.D1Result<T>>) =>
334
384
  Effect.tryPromise({
335
385
  try: () => cb(env.DB),
336
386
  catch: (error) => new UnexpectedError({ cause: error, payload: { dbName } }),
@@ -418,7 +468,7 @@ const makeStorage = (ctx: DurableObjectState, env: Env, storeId: string): SyncSt
418
468
  }
419
469
  }
420
470
 
421
- const getRequestSearchParams = (request: Request) => {
471
+ const getRequestSearchParams = (request: CfWorker.Request) => {
422
472
  const url = new URL(request.url)
423
473
  const urlParams = UrlParams.fromInput(url.searchParams)
424
474
  const paramsResult = UrlParams.schemaStruct(SearchParamsSchema)(urlParams).pipe(Effect.runSync)
@@ -1,2 +1,3 @@
1
+ export * from './cf-types.ts'
1
2
  export * from './durable-object.ts'
2
3
  export * from './worker.ts'