zastro-websockets-cloudflare 0.0.0-729d313

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.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Cloudflare WebSocket server entrypoint
3
+ */
4
+ import type { App } from 'astro/app';
5
+ declare global {
6
+ var WebSocketPair: {
7
+ new (): {
8
+ 0: CloudflareWebSocket;
9
+ 1: CloudflareWebSocket;
10
+ };
11
+ };
12
+ interface CloudflareWebSocket {
13
+ send(data: string | ArrayBufferLike | ArrayBufferView): void;
14
+ close(code?: number, reason?: string): void;
15
+ accept(): void;
16
+ addEventListener(type: 'message', listener: (event: {
17
+ data: any;
18
+ }) => void): void;
19
+ addEventListener(type: 'close', listener: (event: {
20
+ code: number;
21
+ reason: string;
22
+ wasClean: boolean;
23
+ }) => void): void;
24
+ addEventListener(type: 'error', listener: (event: any) => void): void;
25
+ addEventListener(type: 'open', listener: (event: any) => void): void;
26
+ removeEventListener(type: string, listener: any): void;
27
+ readonly readyState: number;
28
+ readonly url: string;
29
+ }
30
+ }
31
+ export type CloudflareApp = App;
32
+ export declare function createWebSocketHandler(app: CloudflareApp): (request: Request, env: any, ctx: any) => Promise<Response>;
33
+ export { WebSocket, attach } from './websocket.js';
@@ -0,0 +1,57 @@
1
+ import { WebSocket, attach } from "./websocket.js";
2
+ function createWebSocketHandler(app) {
3
+ return async function handleWebSocket(request, env, ctx) {
4
+ const upgradeHeader = request.headers.get("upgrade");
5
+ const connectionHeader = request.headers.get("connection");
6
+ if (upgradeHeader !== "websocket" || !connectionHeader?.toLowerCase().includes("upgrade")) {
7
+ return app.render(request, { locals: { isUpgradeRequest: false } });
8
+ }
9
+ const webSocketPair = new WebSocketPair();
10
+ const [client, server] = [webSocketPair[0], webSocketPair[1]];
11
+ const response = await app.render(request, {
12
+ locals: {
13
+ isUpgradeRequest: true,
14
+ upgradeWebSocket() {
15
+ const socket = new WebSocket(request.url);
16
+ attach(socket, server);
17
+ const upgradeResponse = new Response(null, {
18
+ status: 101,
19
+ statusText: "Switching Protocols",
20
+ headers: {
21
+ "Upgrade": "websocket",
22
+ "Connection": "Upgrade"
23
+ },
24
+ webSocket: client
25
+ });
26
+ return { socket, response: upgradeResponse };
27
+ },
28
+ runtime: {
29
+ env,
30
+ cf: request.cf,
31
+ ctx,
32
+ caches: globalThis.caches,
33
+ waitUntil: (promise) => ctx.waitUntil(promise)
34
+ }
35
+ }
36
+ });
37
+ if (response.status === 101 && response.headers.get("upgrade") === "websocket") {
38
+ server.accept();
39
+ return new Response(null, {
40
+ status: 101,
41
+ statusText: "Switching Protocols",
42
+ headers: {
43
+ "Upgrade": "websocket",
44
+ "Connection": "Upgrade"
45
+ },
46
+ webSocket: client
47
+ });
48
+ }
49
+ return response;
50
+ };
51
+ }
52
+ import { WebSocket as WebSocket2, attach as attach2 } from "./websocket.js";
53
+ export {
54
+ WebSocket2 as WebSocket,
55
+ attach2 as attach,
56
+ createWebSocketHandler
57
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Cloudflare WebSocket implementation
3
+ */
4
+ export interface WebSocketUpgrade {
5
+ socket: WebSocket;
6
+ response: Response;
7
+ }
8
+ export declare class WebSocket extends EventTarget {
9
+ private _ws;
10
+ private _readyState;
11
+ private _binaryType;
12
+ private _url;
13
+ private _protocol;
14
+ private _extensions;
15
+ private _bufferedAmount;
16
+ static readonly CONNECTING: 0;
17
+ static readonly OPEN: 1;
18
+ static readonly CLOSING: 2;
19
+ static readonly CLOSED: 3;
20
+ readonly CONNECTING: 0;
21
+ readonly OPEN: 1;
22
+ readonly CLOSING: 2;
23
+ readonly CLOSED: 3;
24
+ onopen: ((event: Event) => void) | null;
25
+ onerror: ((event: Event) => void) | null;
26
+ onclose: ((event: CloseEvent) => void) | null;
27
+ onmessage: ((event: MessageEvent) => void) | null;
28
+ constructor(url?: string);
29
+ get url(): string;
30
+ get readyState(): number;
31
+ get bufferedAmount(): number;
32
+ get extensions(): string;
33
+ get protocol(): string;
34
+ get binaryType(): "blob" | "arraybuffer";
35
+ set binaryType(value: 'blob' | 'arraybuffer');
36
+ close(code?: number, reason?: string): void;
37
+ send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;
38
+ }
39
+ export declare function attach(standard: WebSocket, cfWebSocket: CloudflareWebSocket): void;
40
+ export declare class ErrorEvent extends Event {
41
+ constructor(type: string, init?: {
42
+ message?: string;
43
+ });
44
+ readonly message: string;
45
+ }
46
+ export declare class CloseEvent extends Event implements globalThis.CloseEvent {
47
+ readonly code: number;
48
+ readonly reason: string;
49
+ readonly wasClean: boolean;
50
+ constructor(type: string, eventInitDict?: CloseEventInit);
51
+ }
52
+ interface CloseEventInit extends EventInit {
53
+ code?: number;
54
+ reason?: string;
55
+ wasClean?: boolean;
56
+ }
57
+ interface CloudflareWebSocket {
58
+ send(data: string | ArrayBufferLike | ArrayBufferView): void;
59
+ close(code?: number, reason?: string): void;
60
+ addEventListener(type: 'message', listener: (event: {
61
+ data: any;
62
+ }) => void): void;
63
+ addEventListener(type: 'close', listener: (event: {
64
+ code: number;
65
+ reason: string;
66
+ wasClean: boolean;
67
+ }) => void): void;
68
+ addEventListener(type: 'error', listener: (event: any) => void): void;
69
+ addEventListener(type: 'open', listener: (event: any) => void): void;
70
+ removeEventListener(type: string, listener: any): void;
71
+ readonly readyState: number;
72
+ readonly url: string;
73
+ accept(): void;
74
+ }
75
+ export {};
@@ -0,0 +1,131 @@
1
+ class WebSocket extends EventTarget {
2
+ _ws;
3
+ _readyState = 0;
4
+ // Use numeric literal instead of static property
5
+ _binaryType = "blob";
6
+ _url = "";
7
+ _protocol = "";
8
+ _extensions = "";
9
+ _bufferedAmount = 0;
10
+ static CONNECTING = 0;
11
+ static OPEN = 1;
12
+ static CLOSING = 2;
13
+ static CLOSED = 3;
14
+ // Instance constants
15
+ CONNECTING = 0;
16
+ OPEN = 1;
17
+ CLOSING = 2;
18
+ CLOSED = 3;
19
+ // Event handlers
20
+ onopen = null;
21
+ onerror = null;
22
+ onclose = null;
23
+ onmessage = null;
24
+ constructor(url) {
25
+ super();
26
+ this._url = url || "";
27
+ }
28
+ get url() {
29
+ return this._url;
30
+ }
31
+ get readyState() {
32
+ return this._readyState;
33
+ }
34
+ get bufferedAmount() {
35
+ return this._bufferedAmount;
36
+ }
37
+ get extensions() {
38
+ return this._extensions;
39
+ }
40
+ get protocol() {
41
+ return this._protocol;
42
+ }
43
+ get binaryType() {
44
+ return this._binaryType;
45
+ }
46
+ set binaryType(value) {
47
+ this._binaryType = value;
48
+ }
49
+ close(code, reason) {
50
+ if (this._readyState === WebSocket.CLOSED || this._readyState === WebSocket.CLOSING) {
51
+ return;
52
+ }
53
+ this._readyState = WebSocket.CLOSING;
54
+ if (this._ws) {
55
+ this._ws.close(code, reason);
56
+ } else {
57
+ this._readyState = WebSocket.CLOSED;
58
+ const event = new CloseEvent("close", { code: code || 1e3, reason: reason || "", wasClean: true });
59
+ this.onclose?.(event);
60
+ this.dispatchEvent(event);
61
+ }
62
+ }
63
+ send(data) {
64
+ if (this._readyState !== WebSocket.OPEN) {
65
+ throw new Error("WebSocket is not open");
66
+ }
67
+ if (this._ws) {
68
+ if (data instanceof Blob) {
69
+ data.arrayBuffer().then((buffer) => this._ws.send(buffer));
70
+ } else {
71
+ this._ws.send(data);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ const wsMap = /* @__PURE__ */ new WeakMap();
77
+ function attach(standard, cfWebSocket) {
78
+ if (wsMap.has(standard)) {
79
+ throw new Error("WebSocket already attached");
80
+ }
81
+ wsMap.set(standard, cfWebSocket);
82
+ Object.defineProperty(standard, "_ws", { value: cfWebSocket, writable: true });
83
+ Object.defineProperty(standard, "_readyState", { value: WebSocket.OPEN, writable: true });
84
+ cfWebSocket.addEventListener("message", (event) => {
85
+ const messageEvent = new MessageEvent("message", { data: event.data });
86
+ standard.onmessage?.(messageEvent);
87
+ standard.dispatchEvent(messageEvent);
88
+ });
89
+ cfWebSocket.addEventListener("close", (event) => {
90
+ Object.assign(standard, { _readyState: WebSocket.CLOSED });
91
+ const closeEvent = new CloseEvent("close", {
92
+ code: event.code,
93
+ reason: event.reason,
94
+ wasClean: event.wasClean
95
+ });
96
+ standard.onclose?.(closeEvent);
97
+ standard.dispatchEvent(closeEvent);
98
+ });
99
+ cfWebSocket.addEventListener("error", (_event) => {
100
+ const errorEvent = new ErrorEvent("error", { message: "WebSocket error" });
101
+ standard.onerror?.(errorEvent);
102
+ standard.dispatchEvent(errorEvent);
103
+ });
104
+ const openEvent = new Event("open");
105
+ standard.onopen?.(openEvent);
106
+ standard.dispatchEvent(openEvent);
107
+ }
108
+ class ErrorEvent extends Event {
109
+ constructor(type, init) {
110
+ super(type);
111
+ this.message = init?.message || "";
112
+ }
113
+ message;
114
+ }
115
+ class CloseEvent extends Event {
116
+ code;
117
+ reason;
118
+ wasClean;
119
+ constructor(type, eventInitDict) {
120
+ super(type, eventInitDict);
121
+ this.code = eventInitDict?.code ?? 0;
122
+ this.reason = eventInitDict?.reason ?? "";
123
+ this.wasClean = eventInitDict?.wasClean ?? false;
124
+ }
125
+ }
126
+ export {
127
+ CloseEvent,
128
+ ErrorEvent,
129
+ WebSocket,
130
+ attach
131
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "zastro-websockets-cloudflare",
3
+ "description": "Deploy your site to Cloudflare Workers/Pages with WebSocket support",
4
+ "version": "0.0.0-729d313",
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "author": "Zach Handley <zach@zachhandley.com>",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/zachhandley/ZAstroWebsockets.git"
12
+ },
13
+ "keywords": [
14
+ "withastro",
15
+ "astro-adapter",
16
+ "websockets"
17
+ ],
18
+ "exports": {
19
+ ".": "./dist/index.js",
20
+ "./entrypoints/server.js": "./dist/entrypoints/server.js",
21
+ "./entrypoints/middleware.js": "./dist/entrypoints/middleware.js",
22
+ "./image-service": "./dist/entrypoints/image-service.js",
23
+ "./image-endpoint": "./dist/entrypoints/image-endpoint.js",
24
+ "./handler": "./dist/utils/handler.js",
25
+ "./package.json": "./package.json",
26
+ "./websocket": "./dist/websocket/index.js"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "dependencies": {
32
+ "@astrojs/internal-helpers": "^0.6.1",
33
+ "@astrojs/underscore-redirects": "^1.0.0",
34
+ "@cloudflare/workers-types": "^4.20250507.0",
35
+ "tinyglobby": "^0.2.13",
36
+ "vite": "^6.3.5",
37
+ "wrangler": "^4.14.1"
38
+ },
39
+ "peerDependencies": {
40
+ "astro": "^5.0.0"
41
+ }
42
+ }