@syncular/server-cloudflare 0.0.1-73 → 0.0.1-83

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.
@@ -58,6 +58,7 @@ export declare abstract class SyncDurableObject<E extends object = Record<string
58
58
  Bindings: E;
59
59
  }>, env: E, upgradeWebSocket: UpgradeWebSocket<WebSocket>): void | Promise<void>;
60
60
  private getApp;
61
+ private closeUntrackedSockets;
61
62
  /** Handle incoming HTTP requests (and WebSocket upgrades). */
62
63
  fetch(request: Request): Promise<Response>;
63
64
  /** Dispatch incoming WebSocket messages to Hono event handlers. */
@@ -1 +1 @@
1
- {"version":3,"file":"durable-object.d.ts","sourceRoot":"","sources":["../src/durable-object.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAY,MAAM,SAAS,CAAC;AAgE1D;;;;GAIG;AACH,8BAAsB,iBAAiB,CACrC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;IAEjB,OAAO,CAAC,GAAG,CAAsC;IACjD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,kBAAkB,CAA8B;IAExD,YAAY,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,EAI1C;IAED;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CACZ,GAAG,EAAE,IAAI,CAAC;QAAE,QAAQ,EAAE,CAAC,CAAA;KAAE,CAAC,EAC1B,GAAG,EAAE,CAAC,EACN,gBAAgB,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAEV,MAAM;IAcpB,8DAA8D;IACxD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAG/C;IAED,mEAAmE;IAC7D,gBAAgB,CACpB,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,MAAM,GAAG,WAAW,GAC5B,OAAO,CAAC,IAAI,CAAC,CAOf;IAED,8DAA8D;IACxD,cAAc,CAClB,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC,CAQf;IAED,8DAA8D;IACxD,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlE;CACF;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACrD,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,EAC7B,OAAO,CAAC,EAAE;IACR;;;OAGG;IACH,SAAS,CAAC,EAAE,CACV,EAAE,EAAE,sBAAsB,EAC1B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,KACH,eAAe,CAAC;CACtB,GACA,eAAe,CAAC,CAAC,CAAC,CAiBpB"}
1
+ {"version":3,"file":"durable-object.d.ts","sourceRoot":"","sources":["../src/durable-object.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAY,MAAM,SAAS,CAAC;AA4E1D;;;;GAIG;AACH,8BAAsB,iBAAiB,CACrC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;IAEjB,OAAO,CAAC,GAAG,CAAsC;IACjD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,kBAAkB,CAA8B;IAExD,YAAY,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,EAK1C;IAED;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CACZ,GAAG,EAAE,IAAI,CAAC;QAAE,QAAQ,EAAE,CAAC,CAAA;KAAE,CAAC,EAC1B,GAAG,EAAE,CAAC,EACN,gBAAgB,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAEV,MAAM;IAcpB,OAAO,CAAC,qBAAqB;IAQ7B,8DAA8D;IACxD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAG/C;IAED,mEAAmE;IAC7D,gBAAgB,CACpB,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,MAAM,GAAG,WAAW,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf;IAED,8DAA8D;IACxD,cAAc,CAClB,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC,CAWf;IAED,8DAA8D;IACxD,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlE;CACF;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACrD,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,EAC7B,OAAO,CAAC,EAAE;IACR;;;OAGG;IACH,SAAS,CAAC,EAAE,CACV,EAAE,EAAE,sBAAsB,EAC1B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,KACH,eAAe,CAAC;CACtB,GACA,eAAe,CAAC,CAAC,CAAC,CAiBpB"}
@@ -36,11 +36,21 @@
36
36
  */
37
37
  import { Hono } from 'hono';
38
38
  import { defineWebSocketHelper, WSContext } from 'hono/ws';
39
+ const STALE_SOCKET_CLOSE_CODE = 1012;
40
+ const STALE_SOCKET_CLOSE_REASON = 'WebSocket session expired; reconnect required';
39
41
  /**
40
42
  * WeakMap from server-side WebSocket → tag with event handlers.
41
43
  * Populated on upgrade, read in webSocketMessage/webSocketClose.
42
44
  */
43
45
  const socketTags = new WeakMap();
46
+ function closeStaleSocket(ws) {
47
+ try {
48
+ ws.close(STALE_SOCKET_CLOSE_CODE, STALE_SOCKET_CLOSE_REASON);
49
+ }
50
+ catch {
51
+ // ignore
52
+ }
53
+ }
44
54
  function createWSContext(ws) {
45
55
  return new WSContext({
46
56
  send(data) {
@@ -94,6 +104,7 @@ export class SyncDurableObject {
94
104
  this.ctx = ctx;
95
105
  this.env = env;
96
106
  this.doUpgradeWebSocket = createDOUpgradeWebSocket(ctx);
107
+ this.closeUntrackedSockets();
97
108
  }
98
109
  async getApp() {
99
110
  if (this.app)
@@ -107,6 +118,14 @@ export class SyncDurableObject {
107
118
  await this.initPromise;
108
119
  return this.app;
109
120
  }
121
+ closeUntrackedSockets() {
122
+ const sockets = this.ctx.getWebSockets();
123
+ for (const ws of sockets) {
124
+ if (socketTags.has(ws))
125
+ continue;
126
+ closeStaleSocket(ws);
127
+ }
128
+ }
110
129
  /** Handle incoming HTTP requests (and WebSocket upgrades). */
111
130
  async fetch(request) {
112
131
  const app = await this.getApp();
@@ -115,8 +134,10 @@ export class SyncDurableObject {
115
134
  /** Dispatch incoming WebSocket messages to Hono event handlers. */
116
135
  async webSocketMessage(ws, message) {
117
136
  const tag = socketTags.get(ws);
118
- if (!tag?.events.onMessage)
137
+ if (!tag?.events.onMessage) {
138
+ closeStaleSocket(ws);
119
139
  return;
140
+ }
120
141
  const wsCtx = createWSContext(ws);
121
142
  const evt = new MessageEvent('message', { data: message });
122
143
  tag.events.onMessage(evt, wsCtx);
@@ -124,8 +145,10 @@ export class SyncDurableObject {
124
145
  /** Dispatch WebSocket close events to Hono event handlers. */
125
146
  async webSocketClose(ws, code, reason, _wasClean) {
126
147
  const tag = socketTags.get(ws);
127
- if (!tag?.events.onClose)
148
+ if (!tag?.events.onClose) {
149
+ socketTags.delete(ws);
128
150
  return;
151
+ }
129
152
  const wsCtx = createWSContext(ws);
130
153
  const evt = new CloseEvent('close', { code, reason });
131
154
  tag.events.onClose(evt, wsCtx);
@@ -134,8 +157,10 @@ export class SyncDurableObject {
134
157
  /** Dispatch WebSocket error events to Hono event handlers. */
135
158
  async webSocketError(ws, _error) {
136
159
  const tag = socketTags.get(ws);
137
- if (!tag?.events.onError)
160
+ if (!tag?.events.onError) {
161
+ closeStaleSocket(ws);
138
162
  return;
163
+ }
139
164
  const wsCtx = createWSContext(ws);
140
165
  const evt = new Event('error');
141
166
  tag.events.onError(evt, wsCtx);
@@ -1 +1 @@
1
- {"version":3,"file":"durable-object.js","sourceRoot":"","sources":["../src/durable-object.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"durable-object.js","sourceRoot":"","sources":["../src/durable-object.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * - D1 + SQLite: `@syncular/dialect-d1` + `@syncular/server-dialect-sqlite`
13
13
  * - Neon + Postgres: `@syncular/dialect-neon` + `@syncular/server-dialect-postgres`
14
14
  */
15
- export * from './durable-object';
16
- export * from './r2';
17
- export * from './worker';
15
+ export * from './durable-object.js';
16
+ export * from './r2.js';
17
+ export * from './worker.js';
18
18
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/server-cloudflare",
3
- "version": "0.0.1-73",
3
+ "version": "0.0.1-83",
4
4
  "description": "Cloudflare Workers adapter for the Syncular server",
5
5
  "license": "MIT",
6
6
  "author": "Benjamin Kniffler",
@@ -0,0 +1,65 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import type { Hono } from 'hono';
3
+ import type { UpgradeWebSocket } from 'hono/ws';
4
+ import { SyncDurableObject } from './durable-object';
5
+
6
+ const staleSocketCloseCode = 1012;
7
+ const staleSocketCloseReason = 'WebSocket session expired; reconnect required';
8
+
9
+ class TestSyncDurableObject extends SyncDurableObject<Record<string, never>> {
10
+ async setup(
11
+ _app: Hono<{ Bindings: Record<string, never> }>,
12
+ _env: Record<string, never>,
13
+ _upgradeWebSocket: UpgradeWebSocket<WebSocket>
14
+ ): Promise<void> {}
15
+ }
16
+
17
+ function createSocketTracker(): {
18
+ socket: WebSocket;
19
+ closes: Array<{ code: number | undefined; reason: string | undefined }>;
20
+ } {
21
+ const closes: Array<{
22
+ code: number | undefined;
23
+ reason: string | undefined;
24
+ }> = [];
25
+ const socket = {
26
+ close(code?: number, reason?: string) {
27
+ closes.push({ code, reason });
28
+ },
29
+ } as WebSocket;
30
+ return { socket, closes };
31
+ }
32
+
33
+ function createState(sockets: WebSocket[]): DurableObjectState {
34
+ return {
35
+ acceptWebSocket() {},
36
+ getWebSockets() {
37
+ return sockets;
38
+ },
39
+ } as DurableObjectState;
40
+ }
41
+
42
+ describe('SyncDurableObject stale websocket handling', () => {
43
+ test('closes untracked sockets on construction (hibernation wake-up path)', () => {
44
+ const tracked = createSocketTracker();
45
+ const state = createState([tracked.socket]);
46
+
47
+ new TestSyncDurableObject(state, {});
48
+
49
+ expect(tracked.closes).toEqual([
50
+ { code: staleSocketCloseCode, reason: staleSocketCloseReason },
51
+ ]);
52
+ });
53
+
54
+ test('closes unknown sockets when receiving websocket messages', async () => {
55
+ const state = createState([]);
56
+ const durableObject = new TestSyncDurableObject(state, {});
57
+ const tracked = createSocketTracker();
58
+
59
+ await durableObject.webSocketMessage(tracked.socket, 'hello');
60
+
61
+ expect(tracked.closes).toEqual([
62
+ { code: staleSocketCloseCode, reason: staleSocketCloseReason },
63
+ ]);
64
+ });
65
+ });
@@ -47,12 +47,24 @@ interface WebSocketTag {
47
47
  events: WSEvents<WebSocket>;
48
48
  }
49
49
 
50
+ const STALE_SOCKET_CLOSE_CODE = 1012;
51
+ const STALE_SOCKET_CLOSE_REASON =
52
+ 'WebSocket session expired; reconnect required';
53
+
50
54
  /**
51
55
  * WeakMap from server-side WebSocket → tag with event handlers.
52
56
  * Populated on upgrade, read in webSocketMessage/webSocketClose.
53
57
  */
54
58
  const socketTags = new WeakMap<WebSocket, WebSocketTag>();
55
59
 
60
+ function closeStaleSocket(ws: WebSocket): void {
61
+ try {
62
+ ws.close(STALE_SOCKET_CLOSE_CODE, STALE_SOCKET_CLOSE_REASON);
63
+ } catch {
64
+ // ignore
65
+ }
66
+ }
67
+
56
68
  function createWSContext(ws: WebSocket): WSContext<WebSocket> {
57
69
  return new WSContext<WebSocket>({
58
70
  send(data) {
@@ -119,6 +131,7 @@ export abstract class SyncDurableObject<
119
131
  this.ctx = ctx;
120
132
  this.env = env;
121
133
  this.doUpgradeWebSocket = createDOUpgradeWebSocket(ctx);
134
+ this.closeUntrackedSockets();
122
135
  }
123
136
 
124
137
  /**
@@ -147,6 +160,14 @@ export abstract class SyncDurableObject<
147
160
  return this.app!;
148
161
  }
149
162
 
163
+ private closeUntrackedSockets(): void {
164
+ const sockets = this.ctx.getWebSockets();
165
+ for (const ws of sockets) {
166
+ if (socketTags.has(ws)) continue;
167
+ closeStaleSocket(ws);
168
+ }
169
+ }
170
+
150
171
  /** Handle incoming HTTP requests (and WebSocket upgrades). */
151
172
  async fetch(request: Request): Promise<Response> {
152
173
  const app = await this.getApp();
@@ -159,7 +180,10 @@ export abstract class SyncDurableObject<
159
180
  message: string | ArrayBuffer
160
181
  ): Promise<void> {
161
182
  const tag = socketTags.get(ws);
162
- if (!tag?.events.onMessage) return;
183
+ if (!tag?.events.onMessage) {
184
+ closeStaleSocket(ws);
185
+ return;
186
+ }
163
187
 
164
188
  const wsCtx = createWSContext(ws);
165
189
  const evt = new MessageEvent('message', { data: message });
@@ -174,7 +198,10 @@ export abstract class SyncDurableObject<
174
198
  _wasClean: boolean
175
199
  ): Promise<void> {
176
200
  const tag = socketTags.get(ws);
177
- if (!tag?.events.onClose) return;
201
+ if (!tag?.events.onClose) {
202
+ socketTags.delete(ws);
203
+ return;
204
+ }
178
205
 
179
206
  const wsCtx = createWSContext(ws);
180
207
  const evt = new CloseEvent('close', { code, reason });
@@ -185,7 +212,10 @@ export abstract class SyncDurableObject<
185
212
  /** Dispatch WebSocket error events to Hono event handlers. */
186
213
  async webSocketError(ws: WebSocket, _error: unknown): Promise<void> {
187
214
  const tag = socketTags.get(ws);
188
- if (!tag?.events.onError) return;
215
+ if (!tag?.events.onError) {
216
+ closeStaleSocket(ws);
217
+ return;
218
+ }
189
219
 
190
220
  const wsCtx = createWSContext(ws);
191
221
  const evt = new Event('error');