@pluv/platform-cloudflare 0.19.0 → 0.21.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,18 +1,18 @@
1
1
 
2
- > @pluv/platform-cloudflare@0.19.0 build /home/runner/work/pluv/pluv/packages/platform-cloudflare
2
+ > @pluv/platform-cloudflare@0.21.0 build /home/runner/work/pluv/pluv/packages/platform-cloudflare
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.1.0
8
- CLI Target: es6
9
- ESM Build start
10
- CJS Build start
11
- CJS dist/index.js 6.86 KB
12
- CJS ⚡️ Build success in 91ms
13
- ESM dist/index.mjs 5.73 KB
14
- ESM ⚡️ Build success in 92ms
15
- DTS Build start
16
- DTS ⚡️ Build success in 2530ms
17
- DTS dist/index.d.mts 3.02 KB
18
- DTS dist/index.d.ts 3.02 KB
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.2.4
8
+ CLI Target: es6
9
+ ESM Build start
10
+ CJS Build start
11
+ CJS dist/index.js 15.00 KB
12
+ CJS ⚡️ Build success in 80ms
13
+ ESM dist/index.mjs 13.96 KB
14
+ ESM ⚡️ Build success in 85ms
15
+ DTS Build start
16
+ DTS ⚡️ Build success in 2845ms
17
+ DTS dist/index.d.mts 4.11 KB
18
+ DTS dist/index.d.ts 4.11 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,90 @@
1
1
  # @pluv/platform-cloudflare
2
2
 
3
+ ## 0.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 307bd44: `@pluv/platform-cloudflare` now supports Cloudflare Worker's WebSocket Hibernation API, and usees it by default.
8
+
9
+ To switch back to not using the WebSocket Hibernation API, specify a `mode` of `attached`.
10
+
11
+ ```ts
12
+ // With event-listeners directly attached to the websocket on registration (i.e. non-hibernation)
13
+ createIO({
14
+ platform: platformCloudflare({
15
+ mode: "attached",
16
+ }),
17
+ });
18
+
19
+ // With event listeners unattached to the websocket during registration (i.e. hibernation)
20
+ createIO({
21
+ platform: platformCloudflare({
22
+ mode: "detached",
23
+ }),
24
+ });
25
+ ```
26
+
27
+ - 41b15e4: **BREAKING** - Updated `sessions` type in the procedure context from `Map<string, WebSocketSession>` to `readonly WebSocketSession[]`.
28
+ - f570c8a: **BREAKING**: The original request object is no longer available in the context of any event resolvers.
29
+
30
+ Previously, the request object that was passed into `PluvServer.getRoom` would be made available on the context object of each of the resolvers. This is no-longer a part of the event context, and therefore needs to be omitted from calls to `PluvServer.getRoom`.
31
+
32
+ ```ts
33
+ // Before
34
+
35
+ // With platform-node
36
+ ioServer.getRoom(websocket, { req, token });
37
+
38
+ // With platform-cloudflare
39
+ ioServer.getRoom(websocket, { env, req, token });
40
+ ```
41
+
42
+ ```ts
43
+ // Now
44
+
45
+ // With platform-node
46
+ ioServer.getRoom(websocket, { req });
47
+
48
+ // With platform-cloudflare
49
+ ioServer.getRoom(websocket, { env, req });
50
+ ```
51
+
52
+ - b98ab6b: Internal updates to platforms (i.e. `@pluv/platform-cloudflare` and `@pluv/platform-node`) to be able to support Cloudflare Worker Websocket Hibernation APIs.
53
+ - 4c2228d: **BREAKING**: Require `DurableObjectState` in `ioServer.getRoom`.
54
+
55
+ ```ts
56
+ // Before
57
+
58
+ // With platform-cloudflare
59
+ ioServer.getRoom(websocket, { env, req });
60
+ ```
61
+
62
+ ```ts
63
+ // Now
64
+
65
+ // With platform-cloudflare
66
+ ioServer.getRoom(websocket, { env, req, state });
67
+ ```
68
+
69
+ ### Patch Changes
70
+
71
+ - cc2613e: Moved `sessionId` from being derived in `IORoom` to being derived as a getter in `AbstractWebsocket`.
72
+ - Updated dependencies [307bd44]
73
+ - Updated dependencies [41b15e4]
74
+ - Updated dependencies [f570c8a]
75
+ - Updated dependencies [b98ab6b]
76
+ - Updated dependencies [4c2228d]
77
+ - Updated dependencies [cc2613e]
78
+ - @pluv/io@0.21.0
79
+ - @pluv/types@0.21.0
80
+
81
+ ## 0.20.0
82
+
83
+ ### Patch Changes
84
+
85
+ - @pluv/io@0.20.0
86
+ - @pluv/types@0.20.0
87
+
3
88
  ## 0.19.0
4
89
 
5
90
  ### Patch Changes
package/README.md CHANGED
@@ -1,30 +1,3 @@
1
- > **Disclaimer:**
2
- > This package is currently in preview and may have breaking changes between versions. Please wait for a `v1.0.0` stable release before using this in production.
3
-
4
- <h1 align="center">
5
- <br />
6
- <img src="https://github.com/pluv-io/pluv/blob/master/assets/pluv-icon-192x192.png?raw=true" alt="Pluv.IO" width="180" style="border-radius:16px" />
7
- <br />
8
- <a href="https://pluv.io/docs/introduction">Pluv.IO (preview)</a>
9
- <br />
10
- </h1>
11
-
12
- <h3 align="center">Multi-platform, E2E type-safe realtime packages</h3>
13
- <h4 align="center">💕 Inspired by <a href="https://trpc.io">trpc</a> 💕 <a href="https://docs.yjs.dev/">yjs</a> 💕 and <a href="https://developers.cloudflare.com/">Cloudflare</a> 💕 </h4>
14
-
15
- <p align="center">
16
- <a href="https://www.npmjs.com/package/@pluv/platform-cloudflare">
17
- <img src="https://img.shields.io/npm/v/@pluv/platform-cloudflare" alt="npm @pluv/platform-cloudflare" />
18
- </a>
19
- <a href="https://github.com/pluv-io/pluv/blob/master/LICENSE">
20
- <img alt="GitHub" src="https://img.shields.io/github/license/pluv-io/pluv" alt="License MIT" />
21
- </a>
22
- <a href="https://commitizen.github.io/cz-cli/">
23
- <img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen friendly" />
24
- </a>
25
- <img src="https://badgen.net/badge/-/TypeScript?icon=typescript&label&labelColor=blue&color=555555" alt="TypeScript" />
26
- </p>
27
-
28
1
  ## `@pluv/platform-cloudflare`
29
2
 
30
3
  > Enables [@pluv/io](https://www.npmjs.com/package/@pluv/io) to run on [Cloudflare Workers](https://workers.cloudflare.com/).
@@ -43,26 +16,3 @@ yarn add @pluv/platform-cloudflare
43
16
  # pnpm
44
17
  pnpm add @pluv/platform-cloudflare
45
18
  ```
46
-
47
- ## Basic Example
48
-
49
- ```ts
50
- import { createIO } from "@pluv/io";
51
- import { platformCloudflare } from "@pluv/platform-cloudflare";
52
-
53
- export const io = createIO({ platform: platformCloudflare() });
54
- export const ioServer = io.server();
55
-
56
- /* Somewhere in a Cloudflare worker durable object */
57
- const { 0: client, 1: server } = new WebSocketPair();
58
-
59
- const room = ioServer.getRoom(state.id.toString(), { env });
60
-
61
- await room.register(server);
62
-
63
- return new Response(null, { status: 101, webSocket: client });
64
- ```
65
-
66
- ## Reference
67
-
68
- Check us out on [GitHub](https://github.com/pluv-io/pluv) for more information on how to use `@pluv/io`.
package/dist/index.d.mts CHANGED
@@ -1,26 +1,48 @@
1
- import { AbstractWebSocket, AbstractEventMap, AbstractListener, AbstractWebSocketConfig, AbstractPlatform, ConvertWebSocketConfig, PluvServer } from '@pluv/io';
2
- import { MaybePromise, Maybe, InferIOAuthorizeUser, InferIOAuthorize, InferIOAuthorizeRequired, Id } from '@pluv/types';
1
+ import { AbstractWebSocket, WebSocketSerializedState, AbstractEventMap, AbstractListener, AbstractWebSocketConfig, AbstractPlatformConfig, WebSocketRegistrationMode, AbstractPlatform, ConvertWebSocketConfig, PluvServer } from '@pluv/io';
2
+ import { JsonObject, MaybePromise, Maybe, InferIOAuthorizeUser, InferIOAuthorize, InferIOAuthorizeRequired, Id } from '@pluv/types';
3
3
 
4
4
  type CloudflareWebSocketConfig = AbstractWebSocketConfig;
5
- declare class CloudflareWebSocket extends AbstractWebSocket {
6
- webSocket: WebSocket;
5
+ declare class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
6
+ set presence(presence: JsonObject | null);
7
7
  get readyState(): 0 | 1 | 2 | 3;
8
+ get sessionId(): string;
9
+ get state(): WebSocketSerializedState;
8
10
  constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig);
9
11
  addEventListener<TType extends keyof AbstractEventMap>(type: TType, handler: AbstractListener<TType>): void;
10
12
  close(code?: number | undefined, reason?: string | undefined): void;
11
- initialize(): Promise<() => undefined>;
12
13
  send(message: string | ArrayBuffer | ArrayBufferView): void;
13
14
  terminate(): void;
14
15
  }
15
16
 
16
- declare class CloudflarePlatform<TEnv extends Record<string, any> = {}> extends AbstractPlatform<WebSocket, {
17
+ type CloudflarePlatformConfig<TEnv extends Record<string, any> = {}> = AbstractPlatformConfig<{
17
18
  env: TEnv;
18
19
  }, {
19
- request: Request;
20
+ state: DurableObjectState;
21
+ }> & {
22
+ mode?: WebSocketRegistrationMode;
23
+ };
24
+ declare class CloudflarePlatform<TEnv extends Record<string, any> = {}> extends AbstractPlatform<CloudflareWebSocket, {
25
+ env: TEnv;
26
+ }, {
27
+ state: DurableObjectState;
20
28
  }> {
29
+ readonly _registrationMode: WebSocketRegistrationMode;
30
+ constructor(config?: CloudflarePlatformConfig<TEnv>);
31
+ acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void>;
21
32
  convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket;
33
+ getLastPing(webSocket: CloudflareWebSocket): number | null;
34
+ getSerializedState(webSocket: CloudflareWebSocket): WebSocketSerializedState | null;
35
+ getSessionId(webSocket: WebSocket): string | null;
36
+ getWebSockets(): readonly WebSocket[];
37
+ initialize(config: AbstractPlatformConfig<{
38
+ env: TEnv;
39
+ }, {
40
+ state: DurableObjectState;
41
+ }>): this;
22
42
  parseData(data: string | ArrayBuffer): Record<string, any>;
23
43
  randomUUID(): string;
44
+ setSerializedState(webSocket: CloudflareWebSocket, state: WebSocketSerializedState): void;
45
+ private _getDetachedState;
24
46
  }
25
47
 
26
48
  interface AuthorizeFunctionContext {
@@ -47,8 +69,8 @@ interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}> {
47
69
  handler: ExportedHandler<TEnv>;
48
70
  }
49
71
  type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>> = TPluvServer extends PluvServer<CloudflarePlatform<infer IEnv>, any, any, any> ? IEnv : {};
50
- declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform<{}>, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
72
+ declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
51
73
 
52
- declare const platformCloudflare: <TEnv extends Record<string, any> = {}>() => CloudflarePlatform<TEnv>;
74
+ declare const platformCloudflare: <TEnv extends Record<string, any> = {}>(config?: CloudflarePlatformConfig<TEnv>) => CloudflarePlatform<TEnv>;
53
75
 
54
- export { type AuthorizeFunction, type AuthorizeFunctionContext, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type PluvHandlerFetch, createPluvHandler, platformCloudflare };
76
+ export { type AuthorizeFunction, type AuthorizeFunctionContext, type CloudflarePlatformConfig, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type PluvHandlerFetch, createPluvHandler, platformCloudflare };
package/dist/index.d.ts CHANGED
@@ -1,26 +1,48 @@
1
- import { AbstractWebSocket, AbstractEventMap, AbstractListener, AbstractWebSocketConfig, AbstractPlatform, ConvertWebSocketConfig, PluvServer } from '@pluv/io';
2
- import { MaybePromise, Maybe, InferIOAuthorizeUser, InferIOAuthorize, InferIOAuthorizeRequired, Id } from '@pluv/types';
1
+ import { AbstractWebSocket, WebSocketSerializedState, AbstractEventMap, AbstractListener, AbstractWebSocketConfig, AbstractPlatformConfig, WebSocketRegistrationMode, AbstractPlatform, ConvertWebSocketConfig, PluvServer } from '@pluv/io';
2
+ import { JsonObject, MaybePromise, Maybe, InferIOAuthorizeUser, InferIOAuthorize, InferIOAuthorizeRequired, Id } from '@pluv/types';
3
3
 
4
4
  type CloudflareWebSocketConfig = AbstractWebSocketConfig;
5
- declare class CloudflareWebSocket extends AbstractWebSocket {
6
- webSocket: WebSocket;
5
+ declare class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
6
+ set presence(presence: JsonObject | null);
7
7
  get readyState(): 0 | 1 | 2 | 3;
8
+ get sessionId(): string;
9
+ get state(): WebSocketSerializedState;
8
10
  constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig);
9
11
  addEventListener<TType extends keyof AbstractEventMap>(type: TType, handler: AbstractListener<TType>): void;
10
12
  close(code?: number | undefined, reason?: string | undefined): void;
11
- initialize(): Promise<() => undefined>;
12
13
  send(message: string | ArrayBuffer | ArrayBufferView): void;
13
14
  terminate(): void;
14
15
  }
15
16
 
16
- declare class CloudflarePlatform<TEnv extends Record<string, any> = {}> extends AbstractPlatform<WebSocket, {
17
+ type CloudflarePlatformConfig<TEnv extends Record<string, any> = {}> = AbstractPlatformConfig<{
17
18
  env: TEnv;
18
19
  }, {
19
- request: Request;
20
+ state: DurableObjectState;
21
+ }> & {
22
+ mode?: WebSocketRegistrationMode;
23
+ };
24
+ declare class CloudflarePlatform<TEnv extends Record<string, any> = {}> extends AbstractPlatform<CloudflareWebSocket, {
25
+ env: TEnv;
26
+ }, {
27
+ state: DurableObjectState;
20
28
  }> {
29
+ readonly _registrationMode: WebSocketRegistrationMode;
30
+ constructor(config?: CloudflarePlatformConfig<TEnv>);
31
+ acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void>;
21
32
  convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket;
33
+ getLastPing(webSocket: CloudflareWebSocket): number | null;
34
+ getSerializedState(webSocket: CloudflareWebSocket): WebSocketSerializedState | null;
35
+ getSessionId(webSocket: WebSocket): string | null;
36
+ getWebSockets(): readonly WebSocket[];
37
+ initialize(config: AbstractPlatformConfig<{
38
+ env: TEnv;
39
+ }, {
40
+ state: DurableObjectState;
41
+ }>): this;
22
42
  parseData(data: string | ArrayBuffer): Record<string, any>;
23
43
  randomUUID(): string;
44
+ setSerializedState(webSocket: CloudflareWebSocket, state: WebSocketSerializedState): void;
45
+ private _getDetachedState;
24
46
  }
25
47
 
26
48
  interface AuthorizeFunctionContext {
@@ -47,8 +69,8 @@ interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}> {
47
69
  handler: ExportedHandler<TEnv>;
48
70
  }
49
71
  type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>> = TPluvServer extends PluvServer<CloudflarePlatform<infer IEnv>, any, any, any> ? IEnv : {};
50
- declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform<{}>, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
72
+ declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
51
73
 
52
- declare const platformCloudflare: <TEnv extends Record<string, any> = {}>() => CloudflarePlatform<TEnv>;
74
+ declare const platformCloudflare: <TEnv extends Record<string, any> = {}>(config?: CloudflarePlatformConfig<TEnv>) => CloudflarePlatform<TEnv>;
53
75
 
54
- export { type AuthorizeFunction, type AuthorizeFunctionContext, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type PluvHandlerFetch, createPluvHandler, platformCloudflare };
76
+ export { type AuthorizeFunction, type AuthorizeFunctionContext, type CloudflarePlatformConfig, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type PluvHandlerFetch, createPluvHandler, platformCloudflare };
package/dist/index.js CHANGED
@@ -1,8 +1,25 @@
1
1
  "use strict";
2
2
  var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
6
23
  var __export = (target, all) => {
7
24
  for (var name in all)
8
25
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -51,14 +68,23 @@ var createPluvHandler = (config) => {
51
68
  const { authorize, binding, endpoint = "/api/pluv", modify, io } = config;
52
69
  const DurableObject = class {
53
70
  constructor(state, env) {
54
- this._env = env;
55
- this._io = io.getRoom(state.id.toString(), { env });
71
+ this._room = io.getRoom(state.id.toString(), { env, state });
56
72
  }
57
73
  webSocketClose(ws, code, reason, wasClean) {
74
+ if (io._registrationMode !== "detached") return;
75
+ const handler2 = this._room.onClose(ws);
76
+ handler2({ code, reason });
58
77
  }
59
78
  webSocketError(ws, error) {
79
+ if (io._registrationMode !== "detached") return;
80
+ const handler2 = this._room.onError(ws);
81
+ const eventError = error instanceof Error ? error : new Error("Internal Error");
82
+ handler2({ error: eventError, message: eventError.message });
60
83
  }
61
84
  webSocketMessage(ws, message) {
85
+ if (io._registrationMode !== "detached") return;
86
+ const handler2 = this._room.onMessage(ws);
87
+ handler2({ data: message });
62
88
  }
63
89
  fetch(request) {
64
90
  return __async(this, null, function* () {
@@ -68,11 +94,7 @@ var createPluvHandler = (config) => {
68
94
  }
69
95
  const { 0: client, 1: server } = new WebSocketPair();
70
96
  const token = new URL(request.url).searchParams.get("token");
71
- yield this._io.register(server, {
72
- env: this._env,
73
- request,
74
- token
75
- });
97
+ yield this._room.register(server, { token });
76
98
  return new Response(null, { status: 101, webSocket: client });
77
99
  });
78
100
  }
@@ -154,18 +176,50 @@ var createPluvHandler = (config) => {
154
176
  };
155
177
 
156
178
  // src/CloudflarePlatform.ts
157
- var import_io2 = require("@pluv/io");
179
+ var import_io3 = require("@pluv/io");
158
180
 
159
181
  // src/CloudflareWebSocket.ts
160
182
  var import_io = require("@pluv/io");
161
183
  var CloudflareWebSocket = class extends import_io.AbstractWebSocket {
184
+ set presence(presence) {
185
+ const deserialized = this.webSocket.deserializeAttachment();
186
+ const state = deserialized.state;
187
+ this.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, this.webSocket.deserializeAttachment()), {
188
+ state: __spreadProps(__spreadValues({}, state), { presence })
189
+ }));
190
+ }
162
191
  get readyState() {
163
192
  return this.webSocket.readyState;
164
193
  }
194
+ get sessionId() {
195
+ var _a, _b;
196
+ const deserialized = (_a = this.webSocket.deserializeAttachment()) != null ? _a : {};
197
+ const sessionId = (_b = deserialized.sessionId) != null ? _b : crypto.randomUUID();
198
+ if (typeof deserialized.sessionId !== "string") {
199
+ this.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, deserialized), { sessionId }));
200
+ }
201
+ return sessionId;
202
+ }
203
+ get state() {
204
+ var _a;
205
+ const deserialized = this.webSocket.deserializeAttachment();
206
+ const state = (_a = deserialized.state) != null ? _a : null;
207
+ if (!state) throw new Error("Could not get websocket state");
208
+ return state;
209
+ }
165
210
  constructor(webSocket, config) {
166
- const { room, sessionId, userId } = config;
167
- super({ room, sessionId, userId });
168
- this.webSocket = webSocket;
211
+ const { room } = config;
212
+ super(webSocket, config);
213
+ const state = {
214
+ presence: null,
215
+ quit: false,
216
+ room,
217
+ timers: { ping: (/* @__PURE__ */ new Date()).getTime() }
218
+ };
219
+ webSocket.serializeAttachment(__spreadValues({
220
+ sessionId: this.sessionId,
221
+ state
222
+ }, webSocket.deserializeAttachment()));
169
223
  }
170
224
  addEventListener(type, handler) {
171
225
  this.webSocket.addEventListener(type, handler);
@@ -175,10 +229,6 @@ var CloudflareWebSocket = class extends import_io.AbstractWebSocket {
175
229
  if (!canClose) return;
176
230
  this.webSocket.close(code, reason);
177
231
  }
178
- initialize() {
179
- this.webSocket.accept();
180
- return Promise.resolve(() => void 0);
181
- }
182
232
  send(message) {
183
233
  if (this.readyState !== this.OPEN) return;
184
234
  this.webSocket.send(message);
@@ -188,10 +238,169 @@ var CloudflareWebSocket = class extends import_io.AbstractWebSocket {
188
238
  }
189
239
  };
190
240
 
241
+ // src/PersistanceCloudflare.ts
242
+ var import_io2 = require("@pluv/io");
243
+
244
+ // src/utils/partitionByLength.ts
245
+ var partitionByLength = (arr, length) => {
246
+ if (!arr.length) return [];
247
+ const head = arr.slice(0, length);
248
+ const tail = arr.slice(length);
249
+ return [head, ...partitionByLength(tail, length)];
250
+ };
251
+
252
+ // src/PersistanceCloudflare.ts
253
+ var CLOUDFLARE_DELETE_BATCH_LIMIT = 128;
254
+ var STORAGE_PREFIX = "$PLUV_STORAGE;";
255
+ var USER_PREFIX = "$PLUV_USER";
256
+ var PersistanceCloudflare = class extends import_io2.AbstractPersistance {
257
+ constructor(config) {
258
+ super();
259
+ const { state } = config;
260
+ this._state = state;
261
+ }
262
+ addUser(room, connectionId, user) {
263
+ return __async(this, null, function* () {
264
+ yield this._state.storage.put(this._getUserKey(room, connectionId), user);
265
+ });
266
+ }
267
+ deleteStorageState(room) {
268
+ return __async(this, null, function* () {
269
+ yield this._state.storage.delete(this._getStorageKey(room));
270
+ });
271
+ }
272
+ deleteUser(room, connectionId) {
273
+ return __async(this, null, function* () {
274
+ yield this._state.storage.delete(this._getUserKey(room, connectionId));
275
+ });
276
+ }
277
+ deleteUsers(room) {
278
+ return __async(this, null, function* () {
279
+ const map = yield this._state.storage.list({
280
+ allowConcurrency: true,
281
+ noCache: true,
282
+ prefix: USER_PREFIX
283
+ });
284
+ const partitions = partitionByLength(Array.from(map.keys()), CLOUDFLARE_DELETE_BATCH_LIMIT);
285
+ yield this._state.storage.transaction((tx) => __async(this, null, function* () {
286
+ yield partitions.reduce((promise, partition) => __async(this, null, function* () {
287
+ return yield promise.then(() => __async(this, null, function* () {
288
+ yield tx.delete(partition);
289
+ }));
290
+ }), Promise.resolve());
291
+ }));
292
+ });
293
+ }
294
+ getSize(room) {
295
+ return __async(this, null, function* () {
296
+ const storage = yield this._state.storage.list({ prefix: USER_PREFIX });
297
+ return storage.size;
298
+ });
299
+ }
300
+ getStorageState(room) {
301
+ return __async(this, null, function* () {
302
+ const storage = yield this._state.storage.get(this._getStorageKey(room));
303
+ return storage != null ? storage : null;
304
+ });
305
+ }
306
+ getUser(room, connectionId) {
307
+ return __async(this, null, function* () {
308
+ const user = yield this._state.storage.get(this._getUserKey(room, connectionId));
309
+ return user != null ? user : null;
310
+ });
311
+ }
312
+ getUsers(room) {
313
+ return __async(this, null, function* () {
314
+ const storage = yield this._state.storage.list({
315
+ prefix: USER_PREFIX
316
+ });
317
+ return Array.from(storage.values());
318
+ });
319
+ }
320
+ setStorageState(room, state) {
321
+ return __async(this, null, function* () {
322
+ yield this._state.storage.put(this._getStorageKey(room), state, {
323
+ allowConcurrency: true
324
+ });
325
+ });
326
+ }
327
+ _getStorageKey(room) {
328
+ return `${STORAGE_PREFIX}::${room}`;
329
+ }
330
+ _getUserKey(room, connectionId) {
331
+ return `${USER_PREFIX}::${room}::${connectionId}`;
332
+ }
333
+ };
334
+
335
+ // src/constants.ts
336
+ var DEFAULT_REGISTRATION_MODE = "detached";
337
+
191
338
  // src/CloudflarePlatform.ts
192
- var CloudflarePlatform = class extends import_io2.AbstractPlatform {
339
+ var CloudflarePlatform = class _CloudflarePlatform extends import_io3.AbstractPlatform {
340
+ constructor(config = {}) {
341
+ var _a;
342
+ super(__spreadValues(__spreadValues({}, config), config.context && config.mode === "detached" ? { persistance: new PersistanceCloudflare(config.context) } : {}));
343
+ this._registrationMode = (_a = config.mode) != null ? _a : DEFAULT_REGISTRATION_MODE;
344
+ const detachedState = this._getDetachedState();
345
+ if (!detachedState) return;
346
+ detachedState.setWebSocketAutoResponse(
347
+ new WebSocketRequestResponsePair('{"type":"$PING","data":{}}', JSON.stringify({ type: "$PONG", data: {} }))
348
+ );
349
+ }
350
+ acceptWebSocket(webSocket) {
351
+ return __async(this, null, function* () {
352
+ const detachedState = this._getDetachedState();
353
+ if (!detachedState) {
354
+ webSocket.webSocket.accept();
355
+ return;
356
+ }
357
+ detachedState.acceptWebSocket(webSocket.webSocket);
358
+ });
359
+ }
193
360
  convertWebSocket(webSocket, config) {
194
- return new CloudflareWebSocket(webSocket, config);
361
+ const { room } = config;
362
+ return new CloudflareWebSocket(webSocket, { persistance: this.persistance, room });
363
+ }
364
+ getLastPing(webSocket) {
365
+ var _a;
366
+ const detachedState = this._getDetachedState();
367
+ if (!detachedState) return null;
368
+ const timestamp = detachedState.getWebSocketAutoResponseTimestamp(webSocket.webSocket);
369
+ return (_a = timestamp == null ? void 0 : timestamp.getTime()) != null ? _a : null;
370
+ }
371
+ getSerializedState(webSocket) {
372
+ var _a;
373
+ const deserialized = webSocket.webSocket.deserializeAttachment();
374
+ return (_a = deserialized == null ? void 0 : deserialized.state) != null ? _a : null;
375
+ }
376
+ getSessionId(webSocket) {
377
+ var _a;
378
+ const deserialized = (_a = webSocket.deserializeAttachment()) != null ? _a : {};
379
+ const sessionId = deserialized.sessionId;
380
+ if (typeof sessionId !== "string") {
381
+ throw new Error("This websocket was not registered");
382
+ }
383
+ return sessionId;
384
+ }
385
+ getWebSockets() {
386
+ var _a;
387
+ const detachedState = this._getDetachedState();
388
+ if (!detachedState) return [];
389
+ return (_a = detachedState.getWebSockets()) != null ? _a : [];
390
+ }
391
+ initialize(config) {
392
+ var _a;
393
+ const context = (_a = config.context) != null ? _a : __spreadValues(__spreadValues({}, this._ioContext), this._roomContext);
394
+ if (!context.env || !context.state) {
395
+ throw new Error("Could not derive platform context");
396
+ }
397
+ return new _CloudflarePlatform(__spreadProps(__spreadValues({
398
+ mode: this._registrationMode,
399
+ persistance: this.persistance,
400
+ pubSub: this.pubSub
401
+ }, config), {
402
+ context: { env: context.env, state: context.state }
403
+ }))._initialize();
195
404
  }
196
405
  parseData(data) {
197
406
  if (typeof data === "string") return JSON.parse(data);
@@ -201,11 +410,21 @@ var CloudflarePlatform = class extends import_io2.AbstractPlatform {
201
410
  randomUUID() {
202
411
  return crypto.randomUUID();
203
412
  }
413
+ setSerializedState(webSocket, state) {
414
+ var _a;
415
+ const deserialized = (_a = webSocket.webSocket.deserializeAttachment()) != null ? _a : {};
416
+ webSocket.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, deserialized), { state }));
417
+ }
418
+ _getDetachedState() {
419
+ var _a, _b;
420
+ if (this._registrationMode !== "detached") return null;
421
+ return (_b = (_a = this._roomContext) == null ? void 0 : _a.state) != null ? _b : null;
422
+ }
204
423
  };
205
424
 
206
425
  // src/platformCloudflare.ts
207
- var platformCloudflare = () => {
208
- return new CloudflarePlatform();
426
+ var platformCloudflare = (config = {}) => {
427
+ return new CloudflarePlatform(config);
209
428
  };
210
429
  // Annotate the CommonJS export names for ESM import in node:
211
430
  0 && (module.exports = {