@pluv/platform-cloudflare 0.0.0-canary-20261027022227

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Shoubhit Dash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ ## `@pluv/platform-cloudflare`
2
+
3
+ > Enables [@pluv/io](https://www.npmjs.com/package/@pluv/io) to run on [Cloudflare Workers](https://workers.cloudflare.com/).
4
+
5
+ **👉 See full documentation on [docs.pluv.io](https://docs.pluv.io/docs). 👈**
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # npm
11
+ npm install @pluv/platform-cloudflare
12
+
13
+ # yarn
14
+ yarn add @pluv/platform-cloudflare
15
+
16
+ # pnpm
17
+ pnpm add @pluv/platform-cloudflare
18
+ ```
@@ -0,0 +1,60 @@
1
+ import { AbstractEventMap, AbstractListener, AbstractPlatform, AbstractPlatformConfig, AbstractWebSocket, AbstractWebSocketConfig, ConvertWebSocketConfig, WebSocketSerializedState, WebSocketSession } from "@pluv/io";
2
+ import { BaseUser, Id, Json, JsonObject } from "@pluv/types";
3
+ type CloudflareWebSocketConfig = AbstractWebSocketConfig;
4
+ declare class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
5
+ set presence(presence: JsonObject | null);
6
+ get readyState(): 0 | 1 | 2 | 3;
7
+ get session(): WebSocketSession<any>;
8
+ get sessionId(): string;
9
+ get state(): WebSocketSerializedState;
10
+ set state(state: WebSocketSerializedState);
11
+ get user(): BaseUser | null;
12
+ set user(user: BaseUser);
13
+ constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig);
14
+ addEventListener<TType extends keyof AbstractEventMap>(type: TType, handler: AbstractListener<TType>): void;
15
+ close(code?: number, reason?: string): void;
16
+ send(message: string | ArrayBuffer | ArrayBufferView): void;
17
+ terminate(code?: number): void;
18
+ }
19
+ type CloudflarePlatformRoomContext<TEnv extends Record<string, any>, TMeta extends Record<string, Json>> = {
20
+ env: TEnv;
21
+ state: DurableObjectState;
22
+ } & (keyof TMeta extends never ? {
23
+ meta?: undefined;
24
+ } : {
25
+ meta: TMeta;
26
+ });
27
+ type CloudflarePlatformConfig<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>;
28
+ declare class CloudflarePlatform<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> extends AbstractPlatform<CloudflareWebSocket, {
29
+ env: TEnv;
30
+ request: Request;
31
+ }, CloudflarePlatformRoomContext<TEnv, TMeta>> {
32
+ readonly id: string;
33
+ readonly _name = "platformCloudflare";
34
+ private readonly _persistenceProvided;
35
+ constructor(config: CloudflarePlatformConfig<TEnv, TMeta>);
36
+ acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void>;
37
+ convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket;
38
+ getLastPing(webSocket: CloudflareWebSocket): number | null;
39
+ getSerializedState(webSocket: WebSocket): WebSocketSerializedState | null;
40
+ getSessionId(webSocket: WebSocket): string | null;
41
+ getWebSockets(): readonly WebSocket[];
42
+ initialize(config: AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>): this;
43
+ parseData(data: string | ArrayBuffer): Record<string, any>;
44
+ randomUUID(): string;
45
+ setSerializedState(webSocket: CloudflareWebSocket, state: WebSocketSerializedState): WebSocketSerializedState;
46
+ keepAlive(promise: Promise<unknown>): void;
47
+ private _getDurableObjectState;
48
+ }
49
+ declare const identity: <T extends unknown>(x: T) => T;
50
+ type InferCallback<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = (i: typeof identity) => {
51
+ env?: (io: TEnv) => TEnv;
52
+ meta?: (io: TMeta) => TMeta;
53
+ };
54
+ export declare const infer: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}>(callback: InferCallback<TEnv, TMeta>) => InferCallback<TEnv, TMeta>;
55
+ type PlatformCloudflareParams<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = Id<CloudflarePlatformConfig<TEnv, TMeta> & {
56
+ types?: InferCallback<TEnv, TMeta>;
57
+ }>;
58
+ export declare const platformCloudflare: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}>(config?: PlatformCloudflareParams<TEnv, TMeta>) => (() => CloudflarePlatform<TEnv, TMeta>);
59
+ export type { CloudflarePlatform, CloudflarePlatformConfig, InferCallback, PlatformCloudflareParams };
60
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/CloudflareWebSocket.ts","../src/CloudflarePlatform.ts","../src/utils/identity.ts","../src/infer.ts","../src/platformCloudflare.ts"],"mappings":";;KAUY,4BAA4B;cAE3B,4BAA4B,kBAAkB;MAC5C,SAAS,UAAU;MAUnB;MAIA,WAAW;MAkBX;MAWA,SAAS;MAoBT,MAAM,OAAO;MAMb,QAAQ;MAOR,KAAK,MAAM;EAMtB,YAAY,WAAW,WAAW,QAAQ;EAuBnC,iBAAiB,oBAAoB,kBACxC,MAAM,OACN,SAAS,iBAAiB;EAOvB,MAAM,eAAe;EAUrB,KAAK,kBAAkB,cAAc;EAMrC,UAAU;;KCrIT,8BACR,aAAa,qBACb,cAAc,eAAe;EAE7B,KAAK;EACL,OAAO;WACA;EAAwB;;EAAuB,MAAM;;KAEpD,yBACR,aAAa,0BACb,cAAc,eAAe,cAC7B,uBAAuB,8BAA8B,MAAM;cAElD,mBACT,aAAa,0BACb,cAAc,eAAe,oBACvB,iBACN;EACE,KAAK;EAAM,SAAS;GACtB,8BAA8B,MAAM;WAEpB;WACA;mBAEC;EAEjB,YAAY,QAAQ,yBAAyB,MAAM;EAyBtC,gBAAgB,WAAW,sBAAsB;EAYvD,iBACH,WAAW,WACX,QAAQ,yBACT;EAUI,YAAY,WAAW;EAUvB,mBAAmB,WAAW,YAAY;EAM1C,aAAa,WAAW;EASxB,0BAA0B;EAU1B,WACH,QAAQ,uBAAuB,8BAA8B,MAAM;EAyBhE,UAAU,eAAe,cAAc;EAQvC;EAIA,mBACH,WAAW,qBACX,OAAO,2BACR;EAQa,UAAU,SAAS;UAK3B;;cC/KC,WAAY,mBAAiB,GAAK,MAAI;KCGvC,cACR,aAAa,0BACb,cAAc,eAAe,eAC5B,UAAU;EACX,OAAO,IAAI,SAAS;EACpB,QAAQ,IAAI,UAAU;;qBAGb,QACT,aAAa,0BACb,cAAc,eAAe,YAAU,UAE7B,cAAc,MAAM,WAAM,cAAA,MAAA;KCV5B,yBACR,aAAa,0BACb,cAAc,eAAe,cAC7B,GACA,yBAAyB,MAAM;EAC3B,QAAQ,cAAc,MAAM;;qBAIvB,qBACT,aAAa,0BACb,cAAc,eAAe,YAAU,SAE/B,yBAAyB,MAAM,kBACjC,mBAAmB,MAAM"}
package/dist/index.mjs ADDED
@@ -0,0 +1,200 @@
1
+ import { AbstractPlatform, AbstractWebSocket } from "@pluv/io";
2
+ import { PersistenceCloudflareTransactionalStorage } from "@pluv/persistence-cloudflare-transactional-storage";
3
+ // src/infer.ts
4
+ const infer = (callback) => callback;
5
+ // src/CloudflareWebSocket.ts
6
+ var CloudflareWebSocket = class extends AbstractWebSocket {
7
+ set presence(presence) {
8
+ const state = this.webSocket.deserializeAttachment().state;
9
+ this.webSocket.serializeAttachment({
10
+ ...this.webSocket.deserializeAttachment(),
11
+ state: {
12
+ ...state,
13
+ presence
14
+ }
15
+ });
16
+ }
17
+ get readyState() {
18
+ return this.webSocket.readyState;
19
+ }
20
+ get session() {
21
+ const sessionId = this.webSocket.deserializeAttachment().sessionId;
22
+ const state = this.state;
23
+ const user = this.user;
24
+ if (!user) throw new Error("WebSocket is not authorized");
25
+ return {
26
+ ...state,
27
+ id: sessionId,
28
+ user,
29
+ webSocket: this
30
+ };
31
+ }
32
+ get sessionId() {
33
+ const deserialized = this.webSocket.deserializeAttachment() ?? {};
34
+ const sessionId = deserialized.sessionId ?? `p_${crypto.randomUUID()}`;
35
+ if (typeof deserialized.sessionId !== "string") this.webSocket.serializeAttachment({
36
+ ...deserialized,
37
+ sessionId
38
+ });
39
+ return sessionId;
40
+ }
41
+ get state() {
42
+ const state = this.webSocket.deserializeAttachment()?.state ?? null;
43
+ if (!state) throw new Error("Could not get websocket state");
44
+ const currentPing = state.timers.ping;
45
+ const lastPing = this._platform.getLastPing(this);
46
+ if (!lastPing) return state;
47
+ if (currentPing >= lastPing) return state;
48
+ const newState = {
49
+ ...state,
50
+ timers: {
51
+ ...state.timers,
52
+ ping: lastPing
53
+ }
54
+ };
55
+ return this._platform.setSerializedState(this, newState);
56
+ }
57
+ set state(state) {
58
+ const deserialized = this.webSocket.deserializeAttachment();
59
+ this.webSocket.serializeAttachment({
60
+ ...deserialized,
61
+ state
62
+ });
63
+ }
64
+ get user() {
65
+ return this.webSocket.deserializeAttachment()?.user ?? null;
66
+ }
67
+ set user(user) {
68
+ const deserialized = this.webSocket.deserializeAttachment();
69
+ this.webSocket.serializeAttachment({
70
+ ...deserialized,
71
+ user
72
+ });
73
+ }
74
+ constructor(webSocket, config) {
75
+ const { room } = config;
76
+ super(webSocket, config);
77
+ const state = {
78
+ kind: "user",
79
+ operator: null,
80
+ presence: null,
81
+ quit: false,
82
+ room,
83
+ seq: { presence: null },
84
+ timers: { ping: (/* @__PURE__ */ new Date()).getTime() }
85
+ };
86
+ webSocket.serializeAttachment({
87
+ state,
88
+ ...webSocket.deserializeAttachment()
89
+ });
90
+ }
91
+ addEventListener(type, handler) {
92
+ throw new Error("Cloudflare WebSockets use hibernation handlers (webSocketMessage/Close/Error), not addEventListener");
93
+ }
94
+ close(code, reason) {
95
+ if (![this.CONNECTING, this.OPEN].some((readyState) => readyState === this.readyState)) return;
96
+ this.webSocket.close(code, reason);
97
+ }
98
+ send(message) {
99
+ if (this.readyState !== this.OPEN) return;
100
+ this.webSocket.send(message);
101
+ }
102
+ terminate(code = 1011) {
103
+ return this.webSocket.close(code, "Terminated");
104
+ }
105
+ };
106
+ // src/CloudflarePlatform.ts
107
+ var CloudflarePlatform = class CloudflarePlatform extends AbstractPlatform {
108
+ id = crypto.randomUUID();
109
+ _name = "platformCloudflare";
110
+ _persistenceProvided;
111
+ constructor(config) {
112
+ super({
113
+ ...config,
114
+ ...config.roomContext ? { persistence: config.persistence ?? new PersistenceCloudflareTransactionalStorage() } : {}
115
+ });
116
+ this._persistenceProvided = !!config.persistence;
117
+ const state = this._getDurableObjectState();
118
+ if (!state) return;
119
+ state.setWebSocketAutoResponse(new WebSocketRequestResponsePair("{\"type\":\"$ping\",\"data\":{}}", JSON.stringify({
120
+ type: "$pong",
121
+ data: {}
122
+ })));
123
+ }
124
+ async acceptWebSocket(webSocket) {
125
+ const state = this._getDurableObjectState();
126
+ if (!state) throw new Error("Cloudflare platform requires DurableObjectState for WebSocket hibernation");
127
+ state.acceptWebSocket(webSocket.webSocket);
128
+ }
129
+ convertWebSocket(webSocket, config) {
130
+ const { room } = config;
131
+ return new CloudflareWebSocket(webSocket, {
132
+ persistence: this.persistence,
133
+ platform: this,
134
+ room
135
+ });
136
+ }
137
+ getLastPing(webSocket) {
138
+ const state = this._getDurableObjectState();
139
+ if (!state) return null;
140
+ return state.getWebSocketAutoResponseTimestamp(webSocket.webSocket)?.getTime() ?? null;
141
+ }
142
+ getSerializedState(webSocket) {
143
+ return webSocket.deserializeAttachment()?.state ?? null;
144
+ }
145
+ getSessionId(webSocket) {
146
+ const sessionId = (webSocket.deserializeAttachment() ?? {}).sessionId;
147
+ if (typeof sessionId !== "string") return null;
148
+ return sessionId;
149
+ }
150
+ getWebSockets() {
151
+ const state = this._getDurableObjectState();
152
+ if (!state) return [];
153
+ return state.getWebSockets() ?? [];
154
+ }
155
+ initialize(config) {
156
+ const ctx = config.roomContext ?? { ...this._roomContext };
157
+ if (!ctx.env || !ctx.state) throw new Error("Could not derive platform roomContext");
158
+ const roomContext = {
159
+ env: ctx.env,
160
+ meta: ctx.meta,
161
+ state: ctx.state
162
+ };
163
+ const persistence = (this._persistenceProvided ? this.persistence : new PersistenceCloudflareTransactionalStorage()).initialize(roomContext);
164
+ return new CloudflarePlatform({
165
+ roomContext,
166
+ persistence,
167
+ pubSub: this.pubSub
168
+ })._initialize();
169
+ }
170
+ parseData(data) {
171
+ if (typeof data === "string") return JSON.parse(data);
172
+ const decoder = new TextDecoder("utf8");
173
+ return JSON.parse(decoder.decode(data));
174
+ }
175
+ randomUUID() {
176
+ return crypto.randomUUID();
177
+ }
178
+ setSerializedState(webSocket, state) {
179
+ const deserialized = webSocket.webSocket.deserializeAttachment() ?? {};
180
+ webSocket.webSocket.serializeAttachment({
181
+ ...deserialized,
182
+ state
183
+ });
184
+ return state;
185
+ }
186
+ keepAlive(promise) {
187
+ super.keepAlive(promise);
188
+ this._getDurableObjectState()?.waitUntil(promise);
189
+ }
190
+ _getDurableObjectState() {
191
+ return this._roomContext?.state ?? null;
192
+ }
193
+ };
194
+ // src/platformCloudflare.ts
195
+ const platformCloudflare = (config = {}) => {
196
+ return () => new CloudflarePlatform(config);
197
+ };
198
+ export { infer, platformCloudflare };
199
+
200
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/infer.ts","../src/CloudflareWebSocket.ts","../src/CloudflarePlatform.ts","../src/platformCloudflare.ts"],"sourcesContent":["import type { Json } from \"@pluv/types\";\nimport type { identity } from \"./utils\";\n\nexport type InferCallback<\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n> = (i: typeof identity) => {\n env?: (io: TEnv) => TEnv;\n meta?: (io: TMeta) => TMeta;\n};\n\nexport const infer = <\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n>(\n callback: InferCallback<TEnv, TMeta>,\n) => callback;\n","import type {\n AbstractEventMap,\n AbstractListener,\n AbstractWebSocketConfig,\n WebSocketSerializedState,\n WebSocketSession,\n} from \"@pluv/io\";\nimport { AbstractWebSocket } from \"@pluv/io\";\nimport type { BaseUser, JsonObject } from \"@pluv/types\";\n\nexport type CloudflareWebSocketConfig = AbstractWebSocketConfig;\n\nexport class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {\n public set presence(presence: JsonObject | null) {\n const deserialized = this.webSocket.deserializeAttachment();\n const state = deserialized.state;\n\n this.webSocket.serializeAttachment({\n ...this.webSocket.deserializeAttachment(),\n state: { ...state, presence },\n });\n }\n\n public get readyState(): 0 | 1 | 2 | 3 {\n return this.webSocket.readyState as 0 | 1 | 2 | 3;\n }\n\n public get session(): WebSocketSession<any> {\n const deserialized = this.webSocket.deserializeAttachment();\n const sessionId = deserialized.sessionId as string;\n const state = this.state;\n const user = this.user;\n\n if (!user) {\n throw new Error(\"WebSocket is not authorized\");\n }\n\n return {\n ...state,\n id: sessionId,\n user,\n webSocket: this,\n };\n }\n\n public get sessionId(): string {\n const deserialized = this.webSocket.deserializeAttachment() ?? {};\n const sessionId = deserialized.sessionId ?? `p_${crypto.randomUUID()}`;\n\n if (typeof deserialized.sessionId !== \"string\") {\n this.webSocket.serializeAttachment({ ...deserialized, sessionId });\n }\n\n return sessionId;\n }\n\n public get state(): WebSocketSerializedState {\n const deserialized = this.webSocket.deserializeAttachment();\n const state = deserialized?.state ?? null;\n\n if (!state) throw new Error(\"Could not get websocket state\");\n\n const currentPing = state.timers.ping;\n const lastPing = this._platform.getLastPing(this);\n\n if (!lastPing) return state;\n if (currentPing >= lastPing) return state;\n\n const newState: WebSocketSerializedState = {\n ...state,\n timers: { ...state.timers, ping: lastPing },\n };\n\n return this._platform.setSerializedState(this, newState);\n }\n\n public set state(state: WebSocketSerializedState) {\n const deserialized = this.webSocket.deserializeAttachment();\n\n this.webSocket.serializeAttachment({ ...deserialized, state });\n }\n\n public get user(): BaseUser | null {\n const deserialized = this.webSocket.deserializeAttachment();\n const user = deserialized?.user as BaseUser | null | undefined;\n\n return user ?? null;\n }\n\n public set user(user: BaseUser) {\n const deserialized = this.webSocket.deserializeAttachment();\n\n this.webSocket.serializeAttachment({ ...deserialized, user });\n }\n\n constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig) {\n const { room } = config;\n\n super(webSocket, config);\n\n const state: WebSocketSerializedState = {\n kind: \"user\",\n operator: null,\n presence: null,\n quit: false,\n room,\n seq: { presence: null },\n timers: {\n ping: new Date().getTime(),\n },\n };\n\n webSocket.serializeAttachment({\n state,\n ...webSocket.deserializeAttachment(),\n });\n }\n\n public addEventListener<TType extends keyof AbstractEventMap>(\n type: TType,\n handler: AbstractListener<TType>,\n ): void {\n throw new Error(\n \"Cloudflare WebSockets use hibernation handlers (webSocketMessage/Close/Error), not addEventListener\",\n );\n }\n\n public close(code?: number, reason?: string): void {\n const canClose = [this.CONNECTING, this.OPEN].some(\n (readyState) => readyState === this.readyState,\n );\n\n if (!canClose) return;\n\n this.webSocket.close(code, reason);\n }\n\n public send(message: string | ArrayBuffer | ArrayBufferView): void {\n if (this.readyState !== this.OPEN) return;\n\n this.webSocket.send(message);\n }\n\n public terminate(code: number = 1011): void {\n return this.webSocket.close(code, \"Terminated\");\n }\n}\n","import type {\n AbstractPlatformConfig,\n ConvertWebSocketConfig,\n WebSocketSerializedState,\n} from \"@pluv/io\";\nimport { AbstractPlatform } from \"@pluv/io\";\nimport { PersistenceCloudflareTransactionalStorage } from \"@pluv/persistence-cloudflare-transactional-storage\";\nimport type { Json } from \"@pluv/types\";\nimport { CloudflareWebSocket } from \"./CloudflareWebSocket\";\n\nexport type CloudflarePlatformRoomContext<\n TEnv extends Record<string, any>,\n TMeta extends Record<string, Json>,\n> = {\n env: TEnv;\n state: DurableObjectState;\n} & (keyof TMeta extends never ? { meta?: undefined } : { meta: TMeta });\n\nexport type CloudflarePlatformConfig<\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n> = AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>;\n\nexport class CloudflarePlatform<\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n> extends AbstractPlatform<\n CloudflareWebSocket,\n { env: TEnv; request: Request },\n CloudflarePlatformRoomContext<TEnv, TMeta>\n> {\n public readonly id = crypto.randomUUID();\n public readonly _name = \"platformCloudflare\";\n\n private readonly _persistenceProvided: boolean;\n\n constructor(config: CloudflarePlatformConfig<TEnv, TMeta>) {\n super({\n ...config,\n ...(config.roomContext\n ? {\n persistence:\n config.persistence ?? new PersistenceCloudflareTransactionalStorage(),\n }\n : {}),\n });\n\n this._persistenceProvided = !!config.persistence;\n\n const state = this._getDurableObjectState();\n\n if (!state) return;\n\n state.setWebSocketAutoResponse(\n new WebSocketRequestResponsePair(\n '{\"type\":\"$ping\",\"data\":{}}',\n JSON.stringify({ type: \"$pong\", data: {} }),\n ),\n );\n }\n\n public async acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void> {\n const state = this._getDurableObjectState();\n\n if (!state) {\n throw new Error(\n \"Cloudflare platform requires DurableObjectState for WebSocket hibernation\",\n );\n }\n\n state.acceptWebSocket(webSocket.webSocket);\n }\n\n public convertWebSocket(\n webSocket: WebSocket,\n config: ConvertWebSocketConfig,\n ): CloudflareWebSocket {\n const { room } = config;\n\n return new CloudflareWebSocket(webSocket, {\n persistence: this.persistence,\n platform: this,\n room,\n });\n }\n\n public getLastPing(webSocket: CloudflareWebSocket): number | null {\n const state = this._getDurableObjectState();\n\n if (!state) return null;\n\n const timestamp = state.getWebSocketAutoResponseTimestamp(webSocket.webSocket);\n\n return timestamp?.getTime() ?? null;\n }\n\n public getSerializedState(webSocket: WebSocket): WebSocketSerializedState | null {\n const deserialized = webSocket.deserializeAttachment();\n\n return deserialized?.state ?? null;\n }\n\n public getSessionId(webSocket: WebSocket): string | null {\n const deserialized = webSocket.deserializeAttachment() ?? {};\n const sessionId = deserialized.sessionId;\n\n if (typeof sessionId !== \"string\") return null;\n\n return sessionId;\n }\n\n public getWebSockets(): readonly WebSocket[] {\n const state = this._getDurableObjectState();\n\n if (!state) return [];\n\n const webSockets = state.getWebSockets() ?? [];\n\n return webSockets;\n }\n\n public initialize(\n config: AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>,\n ): this {\n const ctx = config.roomContext ?? { ...this._roomContext };\n\n if (!ctx.env || !ctx.state) throw new Error(\"Could not derive platform roomContext\");\n\n const roomContext = {\n env: ctx.env,\n meta: ctx.meta,\n state: ctx.state,\n } as CloudflarePlatformRoomContext<TEnv, TMeta>;\n\n const persistence = (\n this._persistenceProvided\n ? this.persistence\n : new PersistenceCloudflareTransactionalStorage()\n ).initialize(roomContext);\n\n return new CloudflarePlatform<TEnv, TMeta>({\n roomContext,\n persistence,\n pubSub: this.pubSub,\n })._initialize() as this;\n }\n\n public parseData(data: string | ArrayBuffer): Record<string, any> {\n if (typeof data === \"string\") return JSON.parse(data);\n\n const decoder = new TextDecoder(\"utf8\");\n\n return JSON.parse(decoder.decode(data));\n }\n\n public randomUUID(): string {\n return crypto.randomUUID();\n }\n\n public setSerializedState(\n webSocket: CloudflareWebSocket,\n state: WebSocketSerializedState,\n ): WebSocketSerializedState {\n const deserialized = webSocket.webSocket.deserializeAttachment() ?? {};\n\n webSocket.webSocket.serializeAttachment({ ...deserialized, state });\n\n return state;\n }\n\n public override keepAlive(promise: Promise<unknown>): void {\n super.keepAlive(promise);\n this._getDurableObjectState()?.waitUntil(promise);\n }\n\n private _getDurableObjectState(): DurableObjectState | null {\n return this._roomContext?.state ?? null;\n }\n}\n","import type { Id, Json } from \"@pluv/types\";\nimport type { CloudflarePlatformConfig } from \"./CloudflarePlatform\";\nimport { CloudflarePlatform } from \"./CloudflarePlatform\";\nimport type { InferCallback } from \"./infer\";\n\nexport type PlatformCloudflareParams<\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n> = Id<\n CloudflarePlatformConfig<TEnv, TMeta> & {\n types?: InferCallback<TEnv, TMeta>;\n }\n>;\n\nexport const platformCloudflare = <\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n>(\n config: PlatformCloudflareParams<TEnv, TMeta> = {} as PlatformCloudflareParams<TEnv, TMeta>,\n): (() => CloudflarePlatform<TEnv, TMeta>) => {\n return () => new CloudflarePlatform<TEnv, TMeta>(config);\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@pluv/platform-cloudflare",
3
+ "version": "0.0.0-canary-20261027022227",
4
+ "description": "@pluv/io adapter for cloudflare workers",
5
+ "author": {
6
+ "email": "david@pluv.io",
7
+ "name": "i3dly",
8
+ "url": "https://github.com/i3dly"
9
+ },
10
+ "license": "MIT",
11
+ "homepage": "https://github.com/pluv-io/pluv",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/pluv-io/pluv.git",
15
+ "directory": "packages/platform-cloudflare"
16
+ },
17
+ "type": "module",
18
+ "types": "./dist/index.d.mts",
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "dependencies": {
26
+ "@pluv/crdt": "^0.0.0-canary-20261027022227",
27
+ "@pluv/io": "^0.0.0-canary-20261027022227",
28
+ "@pluv/persistence-cloudflare-transactional-storage": "^0.0.0-canary-20261027022227",
29
+ "@pluv/types": "^0.0.0-canary-20261027022227",
30
+ "path-to-regexp": "^9.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@cloudflare/workers-types": "^5.20261025.1",
34
+ "@pluv/tsconfig": "^0.0.0-canary-20261027022227",
35
+ "tsdown": "^0.23.2",
36
+ "typescript": "7.1.0-beta"
37
+ },
38
+ "exports": {
39
+ ".": "./dist/index.mjs",
40
+ "./*": "./*"
41
+ },
42
+ "scripts": {
43
+ "build": "tsdown",
44
+ "dev": "tsdown --watch",
45
+ "typecheck": "tsc --noEmit",
46
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
47
+ }
48
+ }