@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.
package/dist/durable-object.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/durable-object.js
CHANGED
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAU3D;;;GAGG;AACH,MAAM,UAAU,GAAG,IAAI,OAAO,EAA2B,CAAC;AAE1D,SAAS,eAAe,CAAC,EAAa,EAAwB;IAC5D,OAAO,IAAI,SAAS,CAAY;QAC9B,IAAI,CAAC,IAAI,EAAE;YACT,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAA,CACf;QACD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE;YAClB,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAAA,CACxB;QACD,GAAG,EAAE,EAAE;QACP,IAAI,UAAU,GAAG;YACf,OAAO,EAAE,CAAC,UAA2B,CAAC;QAAA,CACvC;KACF,CAAC,CAAC;AAAA,CACJ;AAED;;;;;;GAMG;AACH,SAAS,wBAAwB,CAC/B,OAA2B,EACE;IAC7B,OAAO,qBAAqB,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;QAEvE,4DAA4D;QAC5D,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAEhC,6EAA6E;QAC7E,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAA6B,EAAE,CAAC,CAAC;QAElE,yDAAyD;QACzD,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAE1C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAAA,CAC/D,CAAC,CAAC;AAAA,CACJ;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,OAAgB,iBAAiB;IAG3B,GAAG,CAAqB;IACxB,GAAG,CAAI;IAET,GAAG,GAAiC,IAAI,CAAC;IACzC,WAAW,GAAyB,IAAI,CAAC;IACzC,kBAAkB,CAA8B;IAExD,YAAY,GAAuB,EAAE,GAAM,EAAE;QAC3C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAAA,
|
|
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
|
@@ -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
|
+
});
|
package/src/durable-object.ts
CHANGED
|
@@ -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)
|
|
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)
|
|
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)
|
|
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');
|