@pluv/platform-cloudflare 2.3.1 → 3.1.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,5 +1,5 @@
1
1
 
2
- > @pluv/platform-cloudflare@2.3.1 build /home/runner/work/pluv/pluv/packages/platform-cloudflare
2
+ > @pluv/platform-cloudflare@3.1.0 build /home/runner/work/pluv/pluv/packages/platform-cloudflare
3
3
  > tsup src/index.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,8 +8,8 @@
8
8
  CLI Using tsup config: /home/runner/work/pluv/pluv/packages/platform-cloudflare/tsup.config.ts
9
9
  CLI Target: es6
10
10
  ESM Build start
11
- ESM dist/index.mjs 14.09 KB
12
- ESM ⚡️ Build success in 52ms
11
+ ESM dist/index.mjs 8.92 KB
12
+ ESM ⚡️ Build success in 27ms
13
13
  DTS Build start
14
- DTS ⚡️ Build success in 1224ms
15
- DTS dist/index.d.mts 7.33 KB
14
+ DTS ⚡️ Build success in 1125ms
15
+ DTS dist/index.d.mts 5.50 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @pluv/platform-cloudflare
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [a40f4dc]
8
+ - @pluv/io@3.1.0
9
+ - @pluv/persistence-cloudflare-transactional-storage@3.1.0
10
+ - @pluv/crdt@3.1.0
11
+ - @pluv/types@3.1.0
12
+
13
+ ## 3.0.0
14
+
15
+ ### Major Changes
16
+
17
+ - bc1ae91: **BREAKING** Removed deprecated `createPluvHandler`.
18
+
19
+ ### Patch Changes
20
+
21
+ - @pluv/io@3.0.0
22
+ - @pluv/persistence-cloudflare-transactional-storage@3.0.0
23
+ - @pluv/types@3.0.0
24
+
3
25
  ## 2.3.1
4
26
 
5
27
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { AbstractWebSocket, WebSocketSession, WebSocketSerializedState, AbstractWebSocketConfig, AbstractEventMap, AbstractListener, AbstractPlatform, WebSocketRegistrationMode, AbstractPlatformConfig, ConvertWebSocketConfig, PluvServer, InferInitContextType, CreateIOParams, PluvIOAuthorize, PluvContext } from '@pluv/io';
2
- import { IOAuthorize, JsonObject, InferIOAuthorizeUser, Json, MaybePromise, InferIOAuthorize, BaseUser, Maybe, Id } from '@pluv/types';
3
- import { DurableObject } from 'cloudflare:workers';
1
+ import { AbstractWebSocket, WebSocketSession, WebSocketSerializedState, AbstractWebSocketConfig, AbstractEventMap, AbstractListener, AbstractPlatform, WebSocketRegistrationMode, AbstractPlatformConfig, ConvertWebSocketConfig, CreateIOParams, PluvIOAuthorize, InferInitContextType, PluvContext } from '@pluv/io';
2
+ import { IOAuthorize, JsonObject, InferIOAuthorizeUser, Json, BaseUser, Id } from '@pluv/types';
3
+ import { CrdtLibraryType, NoopCrdtDocFactory } from '@pluv/crdt';
4
4
 
5
5
  type CloudflareWebSocketConfig = AbstractWebSocketConfig;
6
6
  declare class CloudflareWebSocket<TAuthorize extends IOAuthorize<any, any> | null = null> extends AbstractWebSocket<WebSocket, TAuthorize> {
@@ -80,35 +80,6 @@ declare class CloudflarePlatform<TAuthorize extends IOAuthorize<any, any> | null
80
80
  private _getDetachedState;
81
81
  }
82
82
 
83
- type AuthorizeFunctionContext<TPluvServer extends PluvServer<any, any, any, any>> = {
84
- room: string;
85
- } & InferInitContextType<TPluvServer extends PluvServer<infer IPlatform, any, any, any> ? IPlatform : never>;
86
- type AuthorizeFunction<TPluvServer extends PluvServer<any, any, any, any>> = (ctx: AuthorizeFunctionContext<TPluvServer>) => MaybePromise<Maybe<InferIOAuthorizeUser<InferIOAuthorize<TPluvServer>>>>;
87
- type CreatePluvHandlerConfig<TPluvServer extends PluvServer<any, any, any, any>, TEnv extends Record<string, any>> = {
88
- binding: string;
89
- endpoint?: string;
90
- modify?: (request: Request, response: Response, env: TEnv) => MaybePromise<Response>;
91
- io: TPluvServer;
92
- } & (InferIOAuthorizeUser<InferIOAuthorize<TPluvServer>> extends BaseUser ? {
93
- authorize: AuthorizeFunction<TPluvServer>;
94
- } : {
95
- authorize?: undefined;
96
- });
97
- type PluvHandlerFetch<TEnv extends Record<string, any> = {}> = (request: Request, env: TEnv) => Promise<Response | null>;
98
- interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}> {
99
- DurableObject: {
100
- new (state: DurableObjectState, env: TEnv): DurableObject<TEnv>;
101
- };
102
- fetch: PluvHandlerFetch<TEnv>;
103
- handler: ExportedHandler<TEnv>;
104
- }
105
- type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>> = TPluvServer extends PluvServer<CloudflarePlatform<any, infer IEnv>, any, any, any> ? IEnv : {};
106
- /**
107
- * @deprecated Instructions will be provided on https://pluv.io on how to host this yourself instead.
108
- * @date April 27, 2025
109
- */
110
- declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
111
-
112
83
  declare const identity: <T extends unknown>(x: T) => T;
113
84
 
114
85
  type InferCallback<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = (i: typeof identity) => {
@@ -117,11 +88,11 @@ type InferCallback<TEnv extends Record<string, any> = {}, TMeta extends Record<s
117
88
  };
118
89
  declare const infer: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}>(callback: InferCallback<TEnv, TMeta>) => InferCallback<TEnv, TMeta>;
119
90
 
120
- type PlatformCloudflareCreateIOParams<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null> = Id<CloudflarePlatformConfig<TEnv, TMeta> & Omit<CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser>, "authorize" | "context" | "platform"> & {
91
+ type PlatformCloudflareCreateIOParams<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null, TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>> = Id<CloudflarePlatformConfig<TEnv, TMeta> & Omit<CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser, TCrdt>, "authorize" | "context" | "platform"> & {
121
92
  authorize?: PluvIOAuthorize<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TUser, InferInitContextType<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>>>;
122
93
  context?: PluvContext<CloudflarePlatform<IOAuthorize<TUser, any>, TEnv, TMeta>, TContext>;
123
94
  types?: InferCallback<TEnv, TMeta>;
124
95
  }>;
125
- declare const platformCloudflare: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null>(config?: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser>) => CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser>;
96
+ declare const platformCloudflare: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null, TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>>(config?: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser, TCrdt>) => CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser, TCrdt>;
126
97
 
127
- export { type AuthorizeFunction, type AuthorizeFunctionContext, CloudflarePlatform, type CloudflarePlatformConfig, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type InferCallback, type PlatformCloudflareCreateIOParams, type PluvHandlerFetch, createPluvHandler, infer, platformCloudflare };
98
+ export { CloudflarePlatform, type CloudflarePlatformConfig, type InferCallback, type PlatformCloudflareCreateIOParams, infer, platformCloudflare };
package/dist/index.mjs CHANGED
@@ -38,147 +38,6 @@ var __async = (__this, __arguments, generator) => {
38
38
  });
39
39
  };
40
40
 
41
- // src/createPluvHandler.ts
42
- import { DurableObject as BaseDurableObject } from "cloudflare:workers";
43
- import { match } from "path-to-regexp";
44
-
45
- // src/constants.ts
46
- var DEFAULT_REGISTRATION_MODE = "detached";
47
- var GARBAGE_COLLECT_INTERVAL_MS = 6e4;
48
-
49
- // src/createPluvHandler.ts
50
- var createPluvHandler = (config) => {
51
- const { authorize, binding, endpoint = "/api/pluv", modify, io } = config;
52
- const DurableObject = class extends BaseDurableObject {
53
- constructor(state, env) {
54
- super(state, env);
55
- this._room = io.createRoom(state.id.toString(), { env, state });
56
- }
57
- webSocketClose(ws, code, reason, wasClean) {
58
- return __async(this, null, function* () {
59
- if (io._defs.platform._config.registrationMode !== "detached") return;
60
- const onCloseHandler = this._room.onClose(ws);
61
- yield onCloseHandler({ code, reason });
62
- });
63
- }
64
- webSocketError(ws, error) {
65
- return __async(this, null, function* () {
66
- if (io._defs.platform._config.registrationMode !== "detached") return;
67
- const onErrorHandler = this._room.onError(ws);
68
- const eventError = error instanceof Error ? error : new Error("Internal Error");
69
- yield onErrorHandler({ error: eventError, message: eventError.message });
70
- });
71
- }
72
- webSocketMessage(ws, message) {
73
- return __async(this, null, function* () {
74
- if (io._defs.platform._config.registrationMode !== "detached") return;
75
- const onMessageHandler = this._room.onMessage(ws);
76
- yield onMessageHandler({ data: message });
77
- });
78
- }
79
- fetch(request) {
80
- return __async(this, null, function* () {
81
- const isWSRequest = request.headers.get("Upgrade") === "websocket";
82
- if (!isWSRequest) return new Response("Expected websocket", { status: 400 });
83
- const { 0: client, 1: server } = new WebSocketPair();
84
- const token = new URL(request.url).searchParams.get("token");
85
- const alarm = yield this.ctx.storage.getAlarm();
86
- if (alarm !== null)
87
- yield this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
88
- yield this._room.register(server, { env: this.env, request, token });
89
- return new Response(null, { status: 101, webSocket: client });
90
- });
91
- }
92
- alarm(alarmInfo) {
93
- return __async(this, null, function* () {
94
- yield this._room.garbageCollect();
95
- yield this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
96
- });
97
- }
98
- };
99
- const getDurableObjectNamespace = (env) => {
100
- const namespace = env[binding];
101
- if (!namespace) {
102
- throw new Error(`Could not find DurableObject binding: ${binding}`);
103
- }
104
- return namespace;
105
- };
106
- const authHandler = (request, env) => __async(null, null, function* () {
107
- if (!authorize) return null;
108
- const { pathname, searchParams } = new URL(request.url);
109
- const matcher = match(`${endpoint}/authorize`);
110
- const matched = matcher(pathname);
111
- if (!matched) return null;
112
- const room = searchParams.get("room");
113
- if (!room) {
114
- return new Response("Not found", {
115
- headers: { "Content-Type": "text/plain" },
116
- status: 404
117
- });
118
- }
119
- try {
120
- const user = yield authorize({
121
- env,
122
- request,
123
- room
124
- });
125
- if (!user) throw new Error();
126
- const namespace = getDurableObjectNamespace(env);
127
- const durableObjectId = namespace.idFromName(room);
128
- const token = yield io.createToken({
129
- env,
130
- room: durableObjectId.toString(),
131
- user,
132
- request
133
- });
134
- return new Response(token, {
135
- headers: { "Content-Type": "text/plain" },
136
- status: 200
137
- });
138
- } catch (err) {
139
- return new Response(err instanceof Error ? err.message : "Unauthorized", {
140
- headers: { "Content-Type": "text/plain" },
141
- status: 403
142
- });
143
- }
144
- });
145
- const roomHandler = (request, env) => __async(null, null, function* () {
146
- const { pathname } = new URL(request.url);
147
- const matcher = match(`${endpoint}/room/:roomId`);
148
- const matched = matcher(pathname);
149
- if (!matched) return null;
150
- const { roomId } = matched.params;
151
- if (!roomId) {
152
- return new Response("Not found", {
153
- headers: { "Content-Type": "text/plain" },
154
- status: 404
155
- });
156
- }
157
- const namespace = getDurableObjectNamespace(env);
158
- const durableObjectId = namespace.idFromName(roomId);
159
- const room = namespace.get(durableObjectId);
160
- return room.fetch(request);
161
- });
162
- const handlerFetch = (request, env) => __async(null, null, function* () {
163
- return [authHandler, roomHandler].reduce((promise, current) => __async(null, null, function* () {
164
- return yield promise.then((value) => __async(null, null, function* () {
165
- return value != null ? value : yield current(request, env);
166
- }));
167
- }), Promise.resolve(null));
168
- });
169
- const handler = {
170
- fetch: (request, env) => __async(null, null, function* () {
171
- var _a, _b;
172
- const response = (_a = yield handlerFetch(request, env)) != null ? _a : new Response("Not Found", {
173
- headers: { "Content-Type": "text/plain" },
174
- status: 404
175
- });
176
- return (_b = modify == null ? void 0 : modify(request, response, env)) != null ? _b : response;
177
- })
178
- };
179
- return { fetch: handlerFetch, DurableObject, handler };
180
- };
181
-
182
41
  // src/infer.ts
183
42
  var infer = (callback) => callback;
184
43
 
@@ -277,6 +136,9 @@ var CloudflareWebSocket = class extends AbstractWebSocket {
277
136
  }
278
137
  };
279
138
 
139
+ // src/constants.ts
140
+ var DEFAULT_REGISTRATION_MODE = "detached";
141
+
280
142
  // src/CloudflarePlatform.ts
281
143
  var CloudflarePlatform = class _CloudflarePlatform extends AbstractPlatform {
282
144
  constructor(config) {
@@ -406,7 +268,6 @@ var platformCloudflare = (config = {}) => {
406
268
  };
407
269
  };
408
270
  export {
409
- createPluvHandler,
410
271
  infer,
411
272
  platformCloudflare
412
273
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/platform-cloudflare",
3
- "version": "2.3.1",
3
+ "version": "3.1.0",
4
4
  "description": "@pluv/io adapter for cloudflare workers",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -17,17 +17,18 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "path-to-regexp": "^8.2.0",
20
- "@pluv/io": "^2.3.1",
21
- "@pluv/persistence-cloudflare-transactional-storage": "^2.3.1",
22
- "@pluv/types": "^2.3.1"
20
+ "@pluv/crdt": "^3.1.0",
21
+ "@pluv/io": "^3.1.0",
22
+ "@pluv/persistence-cloudflare-transactional-storage": "^3.1.0",
23
+ "@pluv/types": "^3.1.0"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@cloudflare/workers-types": "^4.20250521.0",
26
27
  "eslint": "^9.27.0",
27
28
  "tsup": "^8.5.0",
28
29
  "typescript": "^5.8.3",
29
- "@pluv/tsconfig": "^2.3.1",
30
- "eslint-config-pluv": "^2.3.1"
30
+ "@pluv/tsconfig": "^3.1.0",
31
+ "eslint-config-pluv": "^3.1.0"
31
32
  },
32
33
  "scripts": {
33
34
  "build": "tsup src/index.ts",
package/src/index.ts CHANGED
@@ -1,12 +1,4 @@
1
1
  export type { CloudflarePlatform, CloudflarePlatformConfig } from "./CloudflarePlatform";
2
- export { createPluvHandler } from "./createPluvHandler";
3
- export type {
4
- AuthorizeFunction,
5
- AuthorizeFunctionContext,
6
- CreatePluvHandlerConfig,
7
- CreatePluvHandlerResult,
8
- PluvHandlerFetch,
9
- } from "./createPluvHandler";
10
2
  export { infer } from "./infer";
11
3
  export type { InferCallback } from "./infer";
12
4
  export { platformCloudflare } from "./platformCloudflare";
@@ -1,3 +1,4 @@
1
+ import type { CrdtLibraryType, NoopCrdtDocFactory } from "@pluv/crdt";
1
2
  import type { CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize } from "@pluv/io";
2
3
  import type { BaseUser, Id, IOAuthorize, Json } from "@pluv/types";
3
4
  import type { CloudflarePlatformConfig } from "./CloudflarePlatform";
@@ -9,13 +10,15 @@ export type PlatformCloudflareCreateIOParams<
9
10
  TMeta extends Record<string, Json> = {},
10
11
  TContext extends Record<string, any> = {},
11
12
  TUser extends BaseUser | null = null,
13
+ TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
12
14
  > = Id<
13
15
  CloudflarePlatformConfig<TEnv, TMeta> &
14
16
  Omit<
15
17
  CreateIOParams<
16
18
  CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,
17
19
  TContext,
18
- TUser
20
+ TUser,
21
+ TCrdt
19
22
  >,
20
23
  "authorize" | "context" | "platform"
21
24
  > & {
@@ -37,12 +40,14 @@ export const platformCloudflare = <
37
40
  TMeta extends Record<string, Json> = {},
38
41
  TContext extends Record<string, any> = {},
39
42
  TUser extends BaseUser | null = null,
43
+ TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
40
44
  >(
41
- config: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser> = {},
45
+ config: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser, TCrdt> = {},
42
46
  ): CreateIOParams<
43
47
  CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,
44
48
  TContext,
45
- TUser
49
+ TUser,
50
+ TCrdt
46
51
  > => {
47
52
  const { authorize, context, crdt, debug, limits } = config;
48
53
 
@@ -1,244 +0,0 @@
1
- import type { InferInitContextType, IORoom, PluvServer } from "@pluv/io";
2
- import type {
3
- BaseUser,
4
- Id,
5
- InferIOAuthorize,
6
- InferIOAuthorizeUser,
7
- Maybe,
8
- MaybePromise,
9
- } from "@pluv/types";
10
- import { DurableObject as BaseDurableObject } from "cloudflare:workers";
11
- import { match } from "path-to-regexp";
12
- import { CloudflarePlatform } from "./CloudflarePlatform";
13
- import { GARBAGE_COLLECT_INTERVAL_MS } from "./constants";
14
-
15
- export type AuthorizeFunctionContext<TPluvServer extends PluvServer<any, any, any, any>> = {
16
- room: string;
17
- } & InferInitContextType<
18
- TPluvServer extends PluvServer<infer IPlatform, any, any, any> ? IPlatform : never
19
- >;
20
- export type AuthorizeFunction<TPluvServer extends PluvServer<any, any, any, any>> = (
21
- ctx: AuthorizeFunctionContext<TPluvServer>,
22
- ) => MaybePromise<Maybe<InferIOAuthorizeUser<InferIOAuthorize<TPluvServer>>>>;
23
-
24
- export type CreatePluvHandlerConfig<
25
- TPluvServer extends PluvServer<any, any, any, any>,
26
- TEnv extends Record<string, any>,
27
- > = {
28
- binding: string;
29
- endpoint?: string;
30
- modify?: (request: Request, response: Response, env: TEnv) => MaybePromise<Response>;
31
- io: TPluvServer;
32
- } & (InferIOAuthorizeUser<InferIOAuthorize<TPluvServer>> extends BaseUser
33
- ? { authorize: AuthorizeFunction<TPluvServer> }
34
- : { authorize?: undefined });
35
-
36
- export type PluvHandlerFetch<TEnv extends Record<string, any> = {}> = (
37
- request: Request,
38
- env: TEnv,
39
- ) => Promise<Response | null>;
40
-
41
- export interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}> {
42
- DurableObject: {
43
- new (state: DurableObjectState, env: TEnv): BaseDurableObject<TEnv>;
44
- };
45
- fetch: PluvHandlerFetch<TEnv>;
46
- handler: ExportedHandler<TEnv>;
47
- }
48
-
49
- type InferCloudflarePluvHandlerEnv<
50
- TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>,
51
- > = TPluvServer extends PluvServer<CloudflarePlatform<any, infer IEnv>, any, any, any> ? IEnv : {};
52
-
53
- /**
54
- * @deprecated Instructions will be provided on https://pluv.io on how to host this yourself instead.
55
- * @date April 27, 2025
56
- */
57
- export const createPluvHandler = <
58
- TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>,
59
- >(
60
- config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>,
61
- ): CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> => {
62
- const { authorize, binding, endpoint = "/api/pluv", modify, io } = config;
63
-
64
- const DurableObject = class extends BaseDurableObject<
65
- Id<InferCloudflarePluvHandlerEnv<TPluvServer>>
66
- > {
67
- private _room: IORoom<CloudflarePlatform<any, any>>;
68
-
69
- constructor(
70
- state: DurableObjectState,
71
- env: Id<InferCloudflarePluvHandlerEnv<TPluvServer>>,
72
- ) {
73
- super(state, env);
74
-
75
- this._room = io.createRoom(state.id.toString(), { env, state });
76
- }
77
-
78
- public async webSocketClose(
79
- ws: WebSocket,
80
- code: number,
81
- reason: string,
82
- wasClean: boolean,
83
- ): Promise<void> {
84
- if (io._defs.platform._config.registrationMode !== "detached") return;
85
-
86
- const onCloseHandler = this._room.onClose(ws);
87
-
88
- await onCloseHandler({ code, reason });
89
- }
90
-
91
- public async webSocketError(ws: WebSocket, error: unknown): Promise<void> {
92
- if (io._defs.platform._config.registrationMode !== "detached") return;
93
-
94
- const onErrorHandler = this._room.onError(ws);
95
- const eventError = error instanceof Error ? error : new Error("Internal Error");
96
-
97
- await onErrorHandler({ error: eventError, message: eventError.message });
98
- }
99
-
100
- public async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
101
- if (io._defs.platform._config.registrationMode !== "detached") return;
102
-
103
- const onMessageHandler = this._room.onMessage(ws);
104
-
105
- await onMessageHandler({ data: message });
106
- }
107
-
108
- async fetch(request: Request<any, CfProperties<any>>): Promise<Response> {
109
- const isWSRequest = request.headers.get("Upgrade") === "websocket";
110
-
111
- if (!isWSRequest) return new Response("Expected websocket", { status: 400 });
112
-
113
- const { 0: client, 1: server } = new WebSocketPair();
114
- const token = new URL(request.url).searchParams.get("token");
115
-
116
- const alarm = await this.ctx.storage.getAlarm();
117
- if (alarm !== null)
118
- await this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
119
-
120
- await this._room.register(server, { env: this.env, request, token });
121
-
122
- return new Response(null, { status: 101, webSocket: client });
123
- }
124
-
125
- public async alarm(alarmInfo?: AlarmInvocationInfo): Promise<void> {
126
- await this._room.garbageCollect();
127
- await this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
128
- }
129
- };
130
-
131
- const getDurableObjectNamespace = (
132
- env: Id<InferCloudflarePluvHandlerEnv<TPluvServer>>,
133
- ): DurableObjectNamespace => {
134
- const namespace = env[binding as keyof typeof env] as DurableObjectNamespace;
135
-
136
- if (!namespace) {
137
- throw new Error(`Could not find DurableObject binding: ${binding}`);
138
- }
139
-
140
- return namespace;
141
- };
142
-
143
- const authHandler: PluvHandlerFetch<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> = async (
144
- request,
145
- env,
146
- ) => {
147
- if (!authorize) return null;
148
-
149
- const { pathname, searchParams } = new URL(request.url);
150
- const matcher = match<{}>(`${endpoint}/authorize`);
151
- const matched = matcher(pathname);
152
-
153
- if (!matched) return null;
154
-
155
- const room = searchParams.get("room");
156
-
157
- if (!room) {
158
- return new Response("Not found", {
159
- headers: { "Content-Type": "text/plain" },
160
- status: 404,
161
- });
162
- }
163
-
164
- try {
165
- const user = await authorize({
166
- env,
167
- request,
168
- room,
169
- } as AuthorizeFunctionContext<TPluvServer>);
170
-
171
- if (!user) throw new Error();
172
-
173
- const namespace = getDurableObjectNamespace(env);
174
- const durableObjectId = namespace.idFromName(room);
175
-
176
- const token = await io.createToken({
177
- env,
178
- room: durableObjectId.toString(),
179
- user: user as any,
180
- request,
181
- });
182
-
183
- return new Response(token, {
184
- headers: { "Content-Type": "text/plain" },
185
- status: 200,
186
- });
187
- } catch (err) {
188
- return new Response(err instanceof Error ? err.message : "Unauthorized", {
189
- headers: { "Content-Type": "text/plain" },
190
- status: 403,
191
- });
192
- }
193
- };
194
-
195
- const roomHandler: PluvHandlerFetch<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> = async (
196
- request,
197
- env,
198
- ) => {
199
- const { pathname } = new URL(request.url);
200
- const matcher = match<{ roomId: string }>(`${endpoint}/room/:roomId`);
201
- const matched = matcher(pathname);
202
-
203
- if (!matched) return null;
204
-
205
- const { roomId } = matched.params;
206
-
207
- if (!roomId) {
208
- return new Response("Not found", {
209
- headers: { "Content-Type": "text/plain" },
210
- status: 404,
211
- });
212
- }
213
-
214
- const namespace = getDurableObjectNamespace(env);
215
- const durableObjectId = namespace.idFromName(roomId);
216
- const room = namespace.get(durableObjectId);
217
-
218
- return room.fetch(request);
219
- };
220
-
221
- const handlerFetch: PluvHandlerFetch<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> = async (
222
- request,
223
- env,
224
- ) => {
225
- return [authHandler, roomHandler].reduce(async (promise, current) => {
226
- return await promise.then(async (value) => value ?? (await current(request, env)));
227
- }, Promise.resolve<Response | null>(null));
228
- };
229
-
230
- const handler: ExportedHandler<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> = {
231
- fetch: async (request, env) => {
232
- const response =
233
- (await handlerFetch(request, env)) ??
234
- new Response("Not Found", {
235
- headers: { "Content-Type": "text/plain" },
236
- status: 404,
237
- });
238
-
239
- return modify?.(request, response, env) ?? response;
240
- },
241
- };
242
-
243
- return { fetch: handlerFetch, DurableObject, handler };
244
- };