@pluv/platform-cloudflare 0.40.2 → 0.41.1
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +25 -0
- package/dist/index.d.mts +16 -14
- package/dist/index.mjs +35 -6
- package/package.json +9 -9
- package/src/CloudflarePlatform.ts +9 -8
- package/src/CloudflareWebSocket.ts +37 -6
- package/src/constants.ts +1 -0
- package/src/createPluvHandler.ts +13 -4
- package/src/platformCloudflare.ts +10 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/platform-cloudflare@0.
|
|
2
|
+
> @pluv/platform-cloudflare@0.41.1 build /home/runner/work/pluv/pluv/packages/platform-cloudflare
|
|
3
3
|
> tsup src/index.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
[34mCLI[39m Using tsup config: /home/runner/work/pluv/pluv/packages/platform-cloudflare/tsup.config.ts
|
|
9
9
|
[34mCLI[39m Target: es6
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
12
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m13.90 KB[39m
|
|
12
|
+
[32mESM[39m ⚡️ Build success in 52ms
|
|
13
13
|
[34mDTS[39m Build start
|
|
14
|
-
[32mDTS[39m ⚡️ Build success in
|
|
15
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
14
|
+
[32mDTS[39m ⚡️ Build success in 1140ms
|
|
15
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m7.18 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @pluv/platform-cloudflare
|
|
2
2
|
|
|
3
|
+
## 0.41.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9e61e10: Fix inferred context type.
|
|
8
|
+
- @pluv/io@0.41.1
|
|
9
|
+
- @pluv/persistence-cloudflare-transactional-storage@0.41.1
|
|
10
|
+
- @pluv/types@0.41.1
|
|
11
|
+
|
|
12
|
+
## 0.41.0
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- d07cf2b: Update `createPluvHandler` to automatically garbage collect the dead websockets in the `IORoom` every 60 seconds.
|
|
17
|
+
- Updated dependencies [73adf21]
|
|
18
|
+
- Updated dependencies [f02a430]
|
|
19
|
+
- Updated dependencies [0b908b3]
|
|
20
|
+
- Updated dependencies [6422f3b]
|
|
21
|
+
- Updated dependencies [555b88d]
|
|
22
|
+
- Updated dependencies [a663c65]
|
|
23
|
+
- Updated dependencies [555b88d]
|
|
24
|
+
- @pluv/io@0.41.0
|
|
25
|
+
- @pluv/persistence-cloudflare-transactional-storage@0.41.0
|
|
26
|
+
- @pluv/types@0.41.0
|
|
27
|
+
|
|
3
28
|
## 0.40.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { AbstractWebSocket, WebSocketSerializedState, AbstractWebSocketConfig, AbstractEventMap, AbstractListener, AbstractPlatform, WebSocketRegistrationMode, AbstractPlatformConfig, ConvertWebSocketConfig, PluvServer, InferInitContextType, CreateIOParams, PluvIOAuthorize, PluvContext } from '@pluv/io';
|
|
2
|
-
import { JsonObject, Json, MaybePromise,
|
|
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
3
|
import { DurableObject } from 'cloudflare:workers';
|
|
4
4
|
|
|
5
5
|
type CloudflareWebSocketConfig = AbstractWebSocketConfig;
|
|
6
|
-
declare class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
|
|
6
|
+
declare class CloudflareWebSocket<TAuthorize extends IOAuthorize<any, any> | null = null> extends AbstractWebSocket<WebSocket, TAuthorize> {
|
|
7
7
|
set presence(presence: JsonObject | null);
|
|
8
8
|
get readyState(): 0 | 1 | 2 | 3;
|
|
9
|
+
get session(): WebSocketSession<TAuthorize>;
|
|
9
10
|
get sessionId(): string;
|
|
10
11
|
get state(): WebSocketSerializedState;
|
|
11
12
|
set state(state: WebSocketSerializedState);
|
|
13
|
+
set user(user: InferIOAuthorizeUser<TAuthorize>);
|
|
12
14
|
constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig);
|
|
13
15
|
addEventListener<TType extends keyof AbstractEventMap>(type: TType, handler: AbstractListener<TType>): void;
|
|
14
16
|
close(code?: number | undefined, reason?: string | undefined): void;
|
|
@@ -27,7 +29,7 @@ type CloudflarePlatformRoomContext<TEnv extends Record<string, any>, TMeta exten
|
|
|
27
29
|
type CloudflarePlatformConfig<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>> & {
|
|
28
30
|
mode?: WebSocketRegistrationMode;
|
|
29
31
|
};
|
|
30
|
-
declare class CloudflarePlatform<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> extends AbstractPlatform<CloudflareWebSocket
|
|
32
|
+
declare class CloudflarePlatform<TAuthorize extends IOAuthorize<any, any> | null = null, TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> extends AbstractPlatform<CloudflareWebSocket<TAuthorize>, {
|
|
31
33
|
env: TEnv;
|
|
32
34
|
request: Request;
|
|
33
35
|
}, CloudflarePlatformRoomContext<TEnv, TMeta>, {
|
|
@@ -64,16 +66,16 @@ declare class CloudflarePlatform<TEnv extends Record<string, any> = {}, TMeta ex
|
|
|
64
66
|
};
|
|
65
67
|
readonly _name = "platformCloudflare";
|
|
66
68
|
constructor(config: CloudflarePlatformConfig<TEnv, TMeta>);
|
|
67
|
-
acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void>;
|
|
68
|
-
convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket
|
|
69
|
-
getLastPing(webSocket: CloudflareWebSocket): number | null;
|
|
69
|
+
acceptWebSocket(webSocket: CloudflareWebSocket<TAuthorize>): Promise<void>;
|
|
70
|
+
convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket<TAuthorize>;
|
|
71
|
+
getLastPing(webSocket: CloudflareWebSocket<TAuthorize>): number | null;
|
|
70
72
|
getSerializedState(webSocket: WebSocket): WebSocketSerializedState | null;
|
|
71
73
|
getSessionId(webSocket: WebSocket): string | null;
|
|
72
74
|
getWebSockets(): readonly WebSocket[];
|
|
73
75
|
initialize(config: AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>): this;
|
|
74
76
|
parseData(data: string | ArrayBuffer): Record<string, any>;
|
|
75
77
|
randomUUID(): string;
|
|
76
|
-
setSerializedState(webSocket: CloudflareWebSocket
|
|
78
|
+
setSerializedState(webSocket: CloudflareWebSocket<TAuthorize>, state: WebSocketSerializedState): WebSocketSerializedState;
|
|
77
79
|
private _getDetachedState;
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -99,8 +101,8 @@ interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}> {
|
|
|
99
101
|
fetch: PluvHandlerFetch<TEnv>;
|
|
100
102
|
handler: ExportedHandler<TEnv>;
|
|
101
103
|
}
|
|
102
|
-
type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>> = TPluvServer extends PluvServer<CloudflarePlatform<infer IEnv>, any, any, any> ? IEnv : {};
|
|
103
|
-
declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
|
|
104
|
+
type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>> = TPluvServer extends PluvServer<CloudflarePlatform<any, infer IEnv>, any, any, any> ? IEnv : {};
|
|
105
|
+
declare const createPluvHandler: <TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>>(config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>) => CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>;
|
|
104
106
|
|
|
105
107
|
declare const identity: <T extends unknown>(x: T) => T;
|
|
106
108
|
|
|
@@ -110,11 +112,11 @@ type InferCallback<TEnv extends Record<string, any> = {}, TMeta extends Record<s
|
|
|
110
112
|
};
|
|
111
113
|
declare const infer: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}>(callback: InferCallback<TEnv, TMeta>) => InferCallback<TEnv, TMeta>;
|
|
112
114
|
|
|
113
|
-
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<TEnv, TMeta>, TContext, TUser>, "authorize" | "context" | "platform"> & {
|
|
114
|
-
authorize?: PluvIOAuthorize<CloudflarePlatform<TEnv, TMeta>, TUser, InferInitContextType<CloudflarePlatform<TEnv, TMeta>>>;
|
|
115
|
-
context?: PluvContext<CloudflarePlatform<TEnv, TMeta>, TContext>;
|
|
115
|
+
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"> & {
|
|
116
|
+
authorize?: PluvIOAuthorize<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TUser, InferInitContextType<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>>>;
|
|
117
|
+
context?: PluvContext<CloudflarePlatform<IOAuthorize<TUser, any>, TEnv, TMeta>, TContext>;
|
|
116
118
|
types?: InferCallback<TEnv, TMeta>;
|
|
117
119
|
}>;
|
|
118
|
-
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<TEnv, TMeta>, TContext, TUser>;
|
|
120
|
+
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>;
|
|
119
121
|
|
|
120
122
|
export { type AuthorizeFunction, type AuthorizeFunctionContext, CloudflarePlatform, type CloudflarePlatformConfig, type CreatePluvHandlerConfig, type CreatePluvHandlerResult, type InferCallback, type PlatformCloudflareCreateIOParams, type PluvHandlerFetch, createPluvHandler, infer, platformCloudflare };
|
package/dist/index.mjs
CHANGED
|
@@ -41,6 +41,12 @@ var __async = (__this, __arguments, generator) => {
|
|
|
41
41
|
// src/createPluvHandler.ts
|
|
42
42
|
import { DurableObject as BaseDurableObject } from "cloudflare:workers";
|
|
43
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
|
|
44
50
|
var createPluvHandler = (config) => {
|
|
45
51
|
const { authorize, binding, endpoint = "/api/pluv", modify, io } = config;
|
|
46
52
|
const DurableObject = class extends BaseDurableObject {
|
|
@@ -76,10 +82,18 @@ var createPluvHandler = (config) => {
|
|
|
76
82
|
if (!isWSRequest) return new Response("Expected websocket", { status: 400 });
|
|
77
83
|
const { 0: client, 1: server } = new WebSocketPair();
|
|
78
84
|
const token = new URL(request.url).searchParams.get("token");
|
|
85
|
+
const alarm = yield this.ctx.storage.getAlarm();
|
|
86
|
+
if (alarm !== null) yield this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
|
|
79
87
|
yield this._room.register(server, { env: this.env, request, token });
|
|
80
88
|
return new Response(null, { status: 101, webSocket: client });
|
|
81
89
|
});
|
|
82
90
|
}
|
|
91
|
+
alarm(alarmInfo) {
|
|
92
|
+
return __async(this, null, function* () {
|
|
93
|
+
yield this._room.garbageCollect();
|
|
94
|
+
yield this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
83
97
|
};
|
|
84
98
|
const getDurableObjectNamespace = (env) => {
|
|
85
99
|
const namespace = env[binding];
|
|
@@ -180,10 +194,22 @@ var CloudflareWebSocket = class extends AbstractWebSocket {
|
|
|
180
194
|
get readyState() {
|
|
181
195
|
return this.webSocket.readyState;
|
|
182
196
|
}
|
|
197
|
+
get session() {
|
|
198
|
+
var _a;
|
|
199
|
+
const deserialized = this.webSocket.deserializeAttachment();
|
|
200
|
+
const sessionId = deserialized.sessionId;
|
|
201
|
+
const state = this.state;
|
|
202
|
+
const user = (_a = deserialized.user) != null ? _a : null;
|
|
203
|
+
return __spreadProps(__spreadValues({}, state), {
|
|
204
|
+
id: sessionId,
|
|
205
|
+
user,
|
|
206
|
+
webSocket: this
|
|
207
|
+
});
|
|
208
|
+
}
|
|
183
209
|
get sessionId() {
|
|
184
210
|
var _a, _b;
|
|
185
211
|
const deserialized = (_a = this.webSocket.deserializeAttachment()) != null ? _a : {};
|
|
186
|
-
const sessionId = (_b = deserialized.sessionId) != null ? _b : crypto.randomUUID()
|
|
212
|
+
const sessionId = (_b = deserialized.sessionId) != null ? _b : `p_${crypto.randomUUID()}`;
|
|
187
213
|
if (typeof deserialized.sessionId !== "string") {
|
|
188
214
|
this.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, deserialized), { sessionId }));
|
|
189
215
|
}
|
|
@@ -207,6 +233,10 @@ var CloudflareWebSocket = class extends AbstractWebSocket {
|
|
|
207
233
|
const deserialized = this.webSocket.deserializeAttachment();
|
|
208
234
|
this.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, deserialized), { state }));
|
|
209
235
|
}
|
|
236
|
+
set user(user) {
|
|
237
|
+
const deserialized = this.webSocket.deserializeAttachment();
|
|
238
|
+
this.webSocket.serializeAttachment(__spreadProps(__spreadValues({}, deserialized), { user }));
|
|
239
|
+
}
|
|
210
240
|
constructor(webSocket, config) {
|
|
211
241
|
const { room } = config;
|
|
212
242
|
super(webSocket, config);
|
|
@@ -214,10 +244,12 @@ var CloudflareWebSocket = class extends AbstractWebSocket {
|
|
|
214
244
|
presence: null,
|
|
215
245
|
quit: false,
|
|
216
246
|
room,
|
|
217
|
-
timers: {
|
|
247
|
+
timers: {
|
|
248
|
+
ping: (/* @__PURE__ */ new Date()).getTime(),
|
|
249
|
+
presence: null
|
|
250
|
+
}
|
|
218
251
|
};
|
|
219
252
|
webSocket.serializeAttachment(__spreadValues({
|
|
220
|
-
sessionId: this.sessionId,
|
|
221
253
|
state
|
|
222
254
|
}, webSocket.deserializeAttachment()));
|
|
223
255
|
}
|
|
@@ -238,9 +270,6 @@ var CloudflareWebSocket = class extends AbstractWebSocket {
|
|
|
238
270
|
}
|
|
239
271
|
};
|
|
240
272
|
|
|
241
|
-
// src/constants.ts
|
|
242
|
-
var DEFAULT_REGISTRATION_MODE = "detached";
|
|
243
|
-
|
|
244
273
|
// src/CloudflarePlatform.ts
|
|
245
274
|
var CloudflarePlatform = class _CloudflarePlatform extends AbstractPlatform {
|
|
246
275
|
constructor(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/platform-cloudflare",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "@pluv/io adapter for cloudflare workers",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,22 +17,22 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"path-to-regexp": "^8.2.0",
|
|
20
|
-
"@pluv/io": "^0.
|
|
21
|
-
"@pluv/persistence-cloudflare-transactional-storage": "^0.
|
|
22
|
-
"@pluv/types": "^0.
|
|
20
|
+
"@pluv/io": "^0.41.1",
|
|
21
|
+
"@pluv/persistence-cloudflare-transactional-storage": "^0.41.1",
|
|
22
|
+
"@pluv/types": "^0.41.1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@cloudflare/workers-types": "^4.
|
|
26
|
-
"eslint": "^9.
|
|
25
|
+
"@cloudflare/workers-types": "^4.20250418.0",
|
|
26
|
+
"eslint": "^9.25.0",
|
|
27
27
|
"tsup": "^8.4.0",
|
|
28
28
|
"typescript": "^5.8.3",
|
|
29
|
-
"@pluv/tsconfig": "^0.
|
|
30
|
-
"eslint-config-pluv": "^0.
|
|
29
|
+
"@pluv/tsconfig": "^0.41.1",
|
|
30
|
+
"eslint-config-pluv": "^0.41.1"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsup src/index.ts",
|
|
34
34
|
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
35
|
-
"lint": "eslint src/**/*.ts
|
|
35
|
+
"lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
|
|
36
36
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from "@pluv/io";
|
|
7
7
|
import { AbstractPlatform } from "@pluv/io";
|
|
8
8
|
import { PersistenceCloudflareTransactionalStorage } from "@pluv/persistence-cloudflare-transactional-storage";
|
|
9
|
-
import type { Json } from "@pluv/types";
|
|
9
|
+
import type { IOAuthorize, Json } from "@pluv/types";
|
|
10
10
|
import { CloudflareWebSocket } from "./CloudflareWebSocket";
|
|
11
11
|
import { DEFAULT_REGISTRATION_MODE } from "./constants";
|
|
12
12
|
|
|
@@ -21,10 +21,11 @@ export type CloudflarePlatformConfig<
|
|
|
21
21
|
> = AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>> & { mode?: WebSocketRegistrationMode };
|
|
22
22
|
|
|
23
23
|
export class CloudflarePlatform<
|
|
24
|
+
TAuthorize extends IOAuthorize<any, any> | null = null,
|
|
24
25
|
TEnv extends Record<string, any> = {},
|
|
25
26
|
TMeta extends Record<string, Json> = {},
|
|
26
27
|
> extends AbstractPlatform<
|
|
27
|
-
CloudflareWebSocket
|
|
28
|
+
CloudflareWebSocket<TAuthorize>,
|
|
28
29
|
{ env: TEnv; request: Request },
|
|
29
30
|
CloudflarePlatformRoomContext<TEnv, TMeta>,
|
|
30
31
|
{
|
|
@@ -81,7 +82,7 @@ export class CloudflarePlatform<
|
|
|
81
82
|
);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
public async acceptWebSocket(webSocket: CloudflareWebSocket): Promise<void> {
|
|
85
|
+
public async acceptWebSocket(webSocket: CloudflareWebSocket<TAuthorize>): Promise<void> {
|
|
85
86
|
const detachedState = this._getDetachedState();
|
|
86
87
|
|
|
87
88
|
if (!detachedState) {
|
|
@@ -93,13 +94,13 @@ export class CloudflarePlatform<
|
|
|
93
94
|
detachedState.acceptWebSocket(webSocket.webSocket);
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
public convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket {
|
|
97
|
+
public convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket<TAuthorize> {
|
|
97
98
|
const { room } = config;
|
|
98
99
|
|
|
99
|
-
return new CloudflareWebSocket(webSocket, { persistence: this.persistence, platform: this, room });
|
|
100
|
+
return new CloudflareWebSocket<TAuthorize>(webSocket, { persistence: this.persistence, platform: this, room });
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
public getLastPing(webSocket: CloudflareWebSocket): number | null {
|
|
103
|
+
public getLastPing(webSocket: CloudflareWebSocket<TAuthorize>): number | null {
|
|
103
104
|
const detachedState = this._getDetachedState();
|
|
104
105
|
|
|
105
106
|
if (!detachedState) return null;
|
|
@@ -139,7 +140,7 @@ export class CloudflarePlatform<
|
|
|
139
140
|
|
|
140
141
|
if (!roomContext.env || !roomContext.state) throw new Error("Could not derive platform roomContext");
|
|
141
142
|
|
|
142
|
-
return new CloudflarePlatform<TEnv, TMeta>({
|
|
143
|
+
return new CloudflarePlatform<TAuthorize, TEnv, TMeta>({
|
|
143
144
|
roomContext: {
|
|
144
145
|
env: roomContext.env,
|
|
145
146
|
meta: roomContext.meta,
|
|
@@ -164,7 +165,7 @@ export class CloudflarePlatform<
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
public setSerializedState(
|
|
167
|
-
webSocket: CloudflareWebSocket
|
|
168
|
+
webSocket: CloudflareWebSocket<TAuthorize>,
|
|
168
169
|
state: WebSocketSerializedState,
|
|
169
170
|
): WebSocketSerializedState {
|
|
170
171
|
const deserialized = webSocket.webSocket.deserializeAttachment() ?? {};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AbstractEventMap,
|
|
3
|
+
AbstractListener,
|
|
4
|
+
AbstractWebSocketConfig,
|
|
5
|
+
WebSocketSerializedState,
|
|
6
|
+
WebSocketSession,
|
|
7
|
+
} from "@pluv/io";
|
|
2
8
|
import { AbstractWebSocket } from "@pluv/io";
|
|
3
|
-
import type { JsonObject } from "@pluv/types";
|
|
9
|
+
import type { InferIOAuthorizeUser, IOAuthorize, JsonObject } from "@pluv/types";
|
|
4
10
|
|
|
5
11
|
export interface CloudflareWebSocketEventMap {
|
|
6
12
|
close: CloseEvent;
|
|
@@ -11,7 +17,10 @@ export interface CloudflareWebSocketEventMap {
|
|
|
11
17
|
|
|
12
18
|
export type CloudflareWebSocketConfig = AbstractWebSocketConfig;
|
|
13
19
|
|
|
14
|
-
export class CloudflareWebSocket extends
|
|
20
|
+
export class CloudflareWebSocket<TAuthorize extends IOAuthorize<any, any> | null = null> extends AbstractWebSocket<
|
|
21
|
+
WebSocket,
|
|
22
|
+
TAuthorize
|
|
23
|
+
> {
|
|
15
24
|
public set presence(presence: JsonObject | null) {
|
|
16
25
|
const deserialized = this.webSocket.deserializeAttachment();
|
|
17
26
|
const state = deserialized.state;
|
|
@@ -26,9 +35,23 @@ export class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
|
|
|
26
35
|
return this.webSocket.readyState as 0 | 1 | 2 | 3;
|
|
27
36
|
}
|
|
28
37
|
|
|
38
|
+
public get session(): WebSocketSession<TAuthorize> {
|
|
39
|
+
const deserialized = this.webSocket.deserializeAttachment();
|
|
40
|
+
const sessionId = deserialized.sessionId as string;
|
|
41
|
+
const state = this.state;
|
|
42
|
+
const user = (deserialized.user ?? null) as InferIOAuthorizeUser<TAuthorize>;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
...state,
|
|
46
|
+
id: sessionId,
|
|
47
|
+
user,
|
|
48
|
+
webSocket: this,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
29
52
|
public get sessionId(): string {
|
|
30
53
|
const deserialized = this.webSocket.deserializeAttachment() ?? {};
|
|
31
|
-
const sessionId = deserialized.sessionId ?? crypto.randomUUID()
|
|
54
|
+
const sessionId = deserialized.sessionId ?? `p_${crypto.randomUUID()}`;
|
|
32
55
|
|
|
33
56
|
if (typeof deserialized.sessionId !== "string") {
|
|
34
57
|
this.webSocket.serializeAttachment({ ...deserialized, sessionId });
|
|
@@ -63,6 +86,12 @@ export class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
|
|
|
63
86
|
this.webSocket.serializeAttachment({ ...deserialized, state });
|
|
64
87
|
}
|
|
65
88
|
|
|
89
|
+
public set user(user: InferIOAuthorizeUser<TAuthorize>) {
|
|
90
|
+
const deserialized = this.webSocket.deserializeAttachment();
|
|
91
|
+
|
|
92
|
+
this.webSocket.serializeAttachment({ ...deserialized, user });
|
|
93
|
+
}
|
|
94
|
+
|
|
66
95
|
constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig) {
|
|
67
96
|
const { room } = config;
|
|
68
97
|
|
|
@@ -72,11 +101,13 @@ export class CloudflareWebSocket extends AbstractWebSocket<WebSocket> {
|
|
|
72
101
|
presence: null,
|
|
73
102
|
quit: false,
|
|
74
103
|
room,
|
|
75
|
-
timers: {
|
|
104
|
+
timers: {
|
|
105
|
+
ping: new Date().getTime(),
|
|
106
|
+
presence: null,
|
|
107
|
+
},
|
|
76
108
|
};
|
|
77
109
|
|
|
78
110
|
webSocket.serializeAttachment({
|
|
79
|
-
sessionId: this.sessionId,
|
|
80
111
|
state,
|
|
81
112
|
...webSocket.deserializeAttachment(),
|
|
82
113
|
});
|
package/src/constants.ts
CHANGED
package/src/createPluvHandler.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { BaseUser, Id, InferIOAuthorize, InferIOAuthorizeUser, Maybe, Maybe
|
|
|
3
3
|
import { DurableObject as BaseDurableObject } from "cloudflare:workers";
|
|
4
4
|
import { match } from "path-to-regexp";
|
|
5
5
|
import { CloudflarePlatform } from "./CloudflarePlatform";
|
|
6
|
+
import { GARBAGE_COLLECT_INTERVAL_MS } from "./constants";
|
|
6
7
|
|
|
7
8
|
export type AuthorizeFunctionContext<TPluvServer extends PluvServer<any, any, any, any>> = {
|
|
8
9
|
room: string;
|
|
@@ -36,16 +37,16 @@ export interface CreatePluvHandlerResult<TEnv extends Record<string, any> = {}>
|
|
|
36
37
|
handler: ExportedHandler<TEnv>;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>> =
|
|
40
|
-
TPluvServer extends PluvServer<CloudflarePlatform<infer IEnv>, any, any, any> ? IEnv : {};
|
|
40
|
+
type InferCloudflarePluvHandlerEnv<TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>> =
|
|
41
|
+
TPluvServer extends PluvServer<CloudflarePlatform<any, infer IEnv>, any, any, any> ? IEnv : {};
|
|
41
42
|
|
|
42
|
-
export const createPluvHandler = <TPluvServer extends PluvServer<CloudflarePlatform, any, any, any>>(
|
|
43
|
+
export const createPluvHandler = <TPluvServer extends PluvServer<CloudflarePlatform<any, any>, any, any, any>>(
|
|
43
44
|
config: CreatePluvHandlerConfig<TPluvServer, Id<InferCloudflarePluvHandlerEnv<TPluvServer>>>,
|
|
44
45
|
): CreatePluvHandlerResult<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> => {
|
|
45
46
|
const { authorize, binding, endpoint = "/api/pluv", modify, io } = config;
|
|
46
47
|
|
|
47
48
|
const DurableObject = class extends BaseDurableObject<Id<InferCloudflarePluvHandlerEnv<TPluvServer>>> {
|
|
48
|
-
private _room: IORoom<CloudflarePlatform
|
|
49
|
+
private _room: IORoom<CloudflarePlatform<any, any>>;
|
|
49
50
|
|
|
50
51
|
constructor(state: DurableObjectState, env: Id<InferCloudflarePluvHandlerEnv<TPluvServer>>) {
|
|
51
52
|
super(state, env);
|
|
@@ -86,10 +87,18 @@ export const createPluvHandler = <TPluvServer extends PluvServer<CloudflarePlatf
|
|
|
86
87
|
const { 0: client, 1: server } = new WebSocketPair();
|
|
87
88
|
const token = new URL(request.url).searchParams.get("token");
|
|
88
89
|
|
|
90
|
+
const alarm = await this.ctx.storage.getAlarm();
|
|
91
|
+
if (alarm !== null) await this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
|
|
92
|
+
|
|
89
93
|
await this._room.register(server, { env: this.env, request, token });
|
|
90
94
|
|
|
91
95
|
return new Response(null, { status: 101, webSocket: client });
|
|
92
96
|
}
|
|
97
|
+
|
|
98
|
+
public async alarm(alarmInfo?: AlarmInvocationInfo): Promise<void> {
|
|
99
|
+
await this._room.garbageCollect();
|
|
100
|
+
await this.ctx.storage.setAlarm(Date.now() + GARBAGE_COLLECT_INTERVAL_MS);
|
|
101
|
+
}
|
|
93
102
|
};
|
|
94
103
|
|
|
95
104
|
const getDurableObjectNamespace = (env: Id<InferCloudflarePluvHandlerEnv<TPluvServer>>): DurableObjectNamespace => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize } from "@pluv/io";
|
|
2
|
-
import type { BaseUser, Id, Json } from "@pluv/types";
|
|
2
|
+
import type { BaseUser, Id, IOAuthorize, Json } from "@pluv/types";
|
|
3
3
|
import type { CloudflarePlatformConfig } from "./CloudflarePlatform";
|
|
4
4
|
import { CloudflarePlatform } from "./CloudflarePlatform";
|
|
5
5
|
import type { InferCallback } from "./infer";
|
|
@@ -11,13 +11,16 @@ export type PlatformCloudflareCreateIOParams<
|
|
|
11
11
|
TUser extends BaseUser | null = null,
|
|
12
12
|
> = Id<
|
|
13
13
|
CloudflarePlatformConfig<TEnv, TMeta> &
|
|
14
|
-
Omit<
|
|
14
|
+
Omit<
|
|
15
|
+
CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser>,
|
|
16
|
+
"authorize" | "context" | "platform"
|
|
17
|
+
> & {
|
|
15
18
|
authorize?: PluvIOAuthorize<
|
|
16
|
-
CloudflarePlatform<TEnv, TMeta>,
|
|
19
|
+
CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,
|
|
17
20
|
TUser,
|
|
18
|
-
InferInitContextType<CloudflarePlatform<TEnv, TMeta>>
|
|
21
|
+
InferInitContextType<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>>
|
|
19
22
|
>;
|
|
20
|
-
context?: PluvContext<CloudflarePlatform<TEnv, TMeta>, TContext>;
|
|
23
|
+
context?: PluvContext<CloudflarePlatform<IOAuthorize<TUser, any>, TEnv, TMeta>, TContext>;
|
|
21
24
|
types?: InferCallback<TEnv, TMeta>;
|
|
22
25
|
}
|
|
23
26
|
>;
|
|
@@ -29,7 +32,7 @@ export const platformCloudflare = <
|
|
|
29
32
|
TUser extends BaseUser | null = null,
|
|
30
33
|
>(
|
|
31
34
|
config: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser> = {},
|
|
32
|
-
): CreateIOParams<CloudflarePlatform<TEnv, TMeta>, TContext, TUser> => {
|
|
35
|
+
): CreateIOParams<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TContext, TUser> => {
|
|
33
36
|
const { authorize, context, crdt, debug } = config;
|
|
34
37
|
|
|
35
38
|
return {
|
|
@@ -37,6 +40,6 @@ export const platformCloudflare = <
|
|
|
37
40
|
context,
|
|
38
41
|
crdt,
|
|
39
42
|
debug,
|
|
40
|
-
platform: new CloudflarePlatform<TEnv, TMeta>(config),
|
|
43
|
+
platform: new CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>(config),
|
|
41
44
|
};
|
|
42
45
|
};
|