@pluv/platform-cloudflare 5.0.0 → 5.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @pluv/platform-cloudflare
2
2
 
3
+ ## 5.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @pluv/crdt@5.0.1
8
+ - @pluv/io@5.0.1
9
+ - @pluv/persistence-cloudflare-transactional-storage@5.0.1
10
+ - @pluv/types@5.0.1
11
+
3
12
  ## 5.0.0
4
13
 
5
14
  ### Patch Changes
@@ -1507,9 +1516,7 @@
1507
1516
  import { createClient } from "@pluv/react";
1508
1517
  import type { io } from "../backend/io";
1509
1518
 
1510
- const client = createClient<typeof io>({
1511
- /* ... */
1512
- });
1519
+ const client = createClient<typeof io>({/* ... */});
1513
1520
  ```
1514
1521
 
1515
1522
  ### Now:
@@ -1551,9 +1558,7 @@
1551
1558
  import type { ioServer } from "../backend/io";
1552
1559
 
1553
1560
  // This users the PluvServer type now
1554
- const client = createClient<typeof ioServer>({
1555
- /* ... */
1556
- });
1561
+ const client = createClient<typeof ioServer>({/* ... */});
1557
1562
  ```
1558
1563
 
1559
1564
  - `PluvRouter` instances can also be merged via the `mergeRouters` method, which effectively performs an `Object.assign` of the events object and returns a new `PluvRouter` with the correct types:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/platform-cloudflare",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "@pluv/io adapter for cloudflare workers",
5
5
  "author": {
6
6
  "email": "david@pluv.io",
@@ -20,19 +20,19 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "path-to-regexp": "^8.4.2",
24
- "@pluv/crdt": "^5.0.0",
25
- "@pluv/io": "^5.0.0",
26
- "@pluv/persistence-cloudflare-transactional-storage": "^5.0.0",
27
- "@pluv/types": "^5.0.0"
23
+ "@pluv/crdt": "^5.0.1",
24
+ "@pluv/io": "^5.0.1",
25
+ "@pluv/persistence-cloudflare-transactional-storage": "^5.0.1",
26
+ "@pluv/types": "^5.0.1",
27
+ "path-to-regexp": "^8.4.2"
28
28
  },
29
29
  "devDependencies": {
30
- "@cloudflare/workers-types": "^4.20260613.1",
31
- "eslint": "^10.5.0",
32
- "tsdown": "0.22.3",
33
- "typescript": "^5.9.3",
34
- "@pluv/tsconfig": "^5.0.0",
35
- "eslint-config-pluv": "^5.0.0"
30
+ "@cloudflare/workers-types": "^5.20260823.1",
31
+ "@pluv/tsconfig": "^5.0.1",
32
+ "eslint": "^10.9.0",
33
+ "eslint-config-pluv": "^5.0.1",
34
+ "tsdown": "0.22.14",
35
+ "typescript": "^5.9.3"
36
36
  },
37
37
  "exports": {
38
38
  ".": "./dist/index.mjs",
@@ -1,16 +0,0 @@
1
- $ tsdown
2
- ℹ tsdown v0.22.3 powered by rolldown v1.1.2
3
- ℹ config file: /home/runner/work/pluv/pluv/packages/platform-cloudflare/tsdown.config.ts
4
- ℹ entry: src/index.ts
5
- ℹ target: esnext
6
- ℹ tsconfig: tsconfig.json
7
-
8
-  WARN  `external` is deprecated. Use `deps.neverBundle` instead.
9
-
10
- ℹ Build start
11
- ℹ dist/index.mjs  6.19 kB │ gzip: 1.77 kB
12
- ℹ dist/index.mjs.map 18.45 kB │ gzip: 4.52 kB
13
- ℹ dist/index.d.mts.map  3.07 kB │ gzip: 0.99 kB
14
- ℹ dist/index.d.mts  5.79 kB │ gzip: 1.47 kB
15
- ℹ 4 files, total: 33.50 kB
16
- ✔ Build complete in 759ms
package/dist/index.d.mts DELETED
@@ -1,106 +0,0 @@
1
- import { AbstractEventMap, AbstractListener, AbstractPlatform, AbstractPlatformConfig, AbstractWebSocket, AbstractWebSocketConfig, ConvertWebSocketConfig, CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize, WebSocketRegistrationMode, WebSocketSerializedState, WebSocketSession } from "@pluv/io";
2
- import { BaseUser, IOAuthorize, Id, InferIOAuthorizeUser, Json, JsonObject } from "@pluv/types";
3
- import { CrdtLibraryType, NoopCrdtDocFactory } from "@pluv/crdt";
4
-
5
- //#region src/CloudflareWebSocket.d.ts
6
- type CloudflareWebSocketConfig = AbstractWebSocketConfig;
7
- declare class CloudflareWebSocket<TAuthorize extends IOAuthorize<any, any> | null = null> extends AbstractWebSocket<WebSocket, TAuthorize> {
8
- set presence(presence: JsonObject | null);
9
- get readyState(): 0 | 1 | 2 | 3;
10
- get session(): WebSocketSession<TAuthorize>;
11
- get sessionId(): string;
12
- get state(): WebSocketSerializedState;
13
- set state(state: WebSocketSerializedState);
14
- set user(user: InferIOAuthorizeUser<TAuthorize>);
15
- constructor(webSocket: WebSocket, config: CloudflareWebSocketConfig);
16
- addEventListener<TType extends keyof AbstractEventMap>(type: TType, handler: AbstractListener<TType>): void;
17
- close(code?: number | undefined, reason?: string | undefined): void;
18
- send(message: string | ArrayBuffer | ArrayBufferView): void;
19
- terminate(code?: number): void;
20
- }
21
- //#endregion
22
- //#region src/CloudflarePlatform.d.ts
23
- type CloudflarePlatformRoomContext<TEnv extends Record<string, any>, TMeta extends Record<string, Json>> = {
24
- env: TEnv;
25
- state: DurableObjectState;
26
- } & (keyof TMeta extends never ? {
27
- meta?: undefined;
28
- } : {
29
- meta: TMeta;
30
- });
31
- type CloudflarePlatformConfig<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>> & {
32
- mode?: WebSocketRegistrationMode;
33
- };
34
- declare class CloudflarePlatform<TAuthorize extends IOAuthorize<any, any> | null = null, TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> extends AbstractPlatform<CloudflareWebSocket<TAuthorize>, {
35
- env: TEnv;
36
- request: Request;
37
- }, CloudflarePlatformRoomContext<TEnv, TMeta>, {
38
- authorize: {
39
- secret: true;
40
- };
41
- handleMode: "io";
42
- requireAuth: false;
43
- registrationMode: WebSocketRegistrationMode;
44
- listeners: {
45
- onRoomDestroyed: true;
46
- onRoomMessage: true;
47
- onStorageDestroyed: true;
48
- onStorageUpdated: true;
49
- onUserConnected: true;
50
- onUserDisconnected: true;
51
- };
52
- router: true;
53
- }> {
54
- readonly id: string;
55
- readonly _config: {
56
- authorize: {
57
- secret: true;
58
- };
59
- handleMode: "io";
60
- registrationMode: WebSocketRegistrationMode;
61
- requireAuth: false;
62
- listeners: {
63
- onRoomDestroyed: true;
64
- onRoomMessage: true;
65
- onStorageDestroyed: true;
66
- onStorageUpdated: true;
67
- onUserConnected: true;
68
- onUserDisconnected: true;
69
- };
70
- router: true;
71
- };
72
- readonly _name = "platformCloudflare";
73
- constructor(config: CloudflarePlatformConfig<TEnv, TMeta>);
74
- acceptWebSocket(webSocket: CloudflareWebSocket<TAuthorize>): Promise<void>;
75
- convertWebSocket(webSocket: WebSocket, config: ConvertWebSocketConfig): CloudflareWebSocket<TAuthorize>;
76
- getLastPing(webSocket: CloudflareWebSocket<TAuthorize>): number | null;
77
- getSerializedState(webSocket: WebSocket): WebSocketSerializedState | null;
78
- getSessionId(webSocket: WebSocket): string | null;
79
- getWebSockets(): readonly WebSocket[];
80
- initialize(config: AbstractPlatformConfig<CloudflarePlatformRoomContext<TEnv, TMeta>>): this;
81
- parseData(data: string | ArrayBuffer): Record<string, any>;
82
- randomUUID(): string;
83
- setSerializedState(webSocket: CloudflareWebSocket<TAuthorize>, state: WebSocketSerializedState): WebSocketSerializedState;
84
- private _getDetachedState;
85
- }
86
- //#endregion
87
- //#region src/utils/identity.d.ts
88
- declare const identity: <T extends unknown>(x: T) => T;
89
- //#endregion
90
- //#region src/infer.d.ts
91
- type InferCallback<TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}> = (i: typeof identity) => {
92
- env?: (io: TEnv) => TEnv;
93
- meta?: (io: TMeta) => TMeta;
94
- };
95
- declare const infer: <TEnv extends Record<string, any> = {}, TMeta extends Record<string, Json> = {}>(callback: InferCallback<TEnv, TMeta>) => InferCallback<TEnv, TMeta>;
96
- //#endregion
97
- //#region src/platformCloudflare.d.ts
98
- 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"> & {
99
- authorize?: PluvIOAuthorize<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>, TUser, InferInitContextType<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>>>;
100
- context?: PluvContext<CloudflarePlatform<IOAuthorize<TUser, any>, TEnv, TMeta>, TContext>;
101
- types?: InferCallback<TEnv, TMeta>;
102
- }>;
103
- 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>;
104
- //#endregion
105
- export { type CloudflarePlatform, type CloudflarePlatformConfig, type InferCallback, type PlatformCloudflareCreateIOParams, infer, platformCloudflare };
106
- //# sourceMappingURL=index.d.mts.map
@@ -1 +0,0 @@
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":";;;;;KAiBY,yBAAA,GAA4B,uBAAuB;AAAA,cAElD,mBAAA,oBACU,WAAA,kCACb,iBAAA,CAAkB,SAAA,EAAW,UAAA;EAAA,IACxB,QAAA,CAAS,QAAA,EAAU,UAAA;EAAA,IAUnB,UAAA;EAAA,IAIA,OAAA,IAAW,gBAAA,CAAiB,UAAA;EAAA,IAc5B,SAAA;EAAA,IAWA,KAAA,IAAS,wBAAA;EAAA,IAoBT,KAAA,CAAM,KAAA,EAAO,wBAAA;EAAA,IAMb,IAAA,CAAK,IAAA,EAAM,oBAAA,CAAqB,UAAA;cAM/B,SAAA,EAAW,SAAA,EAAW,MAAA,EAAQ,yBAAA;EAqBnC,gBAAA,qBAAqC,gBAAA,EACxC,IAAA,EAAM,KAAA,EACN,OAAA,EAAS,gBAAA,CAAiB,KAAA;EAKvB,KAAA,CAAM,IAAA,uBAA2B,MAAA;EAUjC,IAAA,CAAK,OAAA,WAAkB,WAAA,GAAc,eAAA;EAMrC,SAAA,CAAU,IAAA;AAAA;;;KC7HT,6BAAA,cACK,MAAA,6BACC,MAAA,SAAe,IAAA;EAE7B,GAAA,EAAK,IAAA;EACL,KAAA,EAAO,kBAAA;AAAA,WACA,KAAA;EAAwB,IAAA;AAAA;EAAuB,IAAA,EAAM,KAAA;AAAA;AAAA,KAEpD,wBAAA,cACK,MAAA,kCACC,MAAA,SAAe,IAAA,UAC7B,sBAAA,CAAuB,6BAAA,CAA8B,IAAA,EAAM,KAAA;EAC3D,IAAA,GAAO,yBAAA;AAAA;AAAA,cAGE,kBAAA,oBACU,WAAA,uCACN,MAAA,kCACC,MAAA,SAAe,IAAA,gBACvB,gBAAA,CACN,mBAAA,CAAoB,UAAA;EAClB,GAAA,EAAK,IAAA;EAAM,OAAA,EAAS,OAAA;AAAA,GACtB,6BAAA,CAA8B,IAAA,EAAM,KAAA;EAEhC,SAAA;IACI,MAAA;EAAA;EAEJ,UAAA;EACA,WAAA;EACA,gBAAA,EAAkB,yBAAA;EAClB,SAAA;IACI,eAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;EAAA;EAEJ,MAAA;AAAA;EAAA,SAGY,EAAA;EAAA,SACA,OAAA;;;;;;;;;;;;;;;;;WACA,KAAA;cAEJ,MAAA,EAAQ,wBAAA,CAAyB,IAAA,EAAM,KAAA;EA0CtC,eAAA,CAAgB,SAAA,EAAW,mBAAA,CAAoB,UAAA,IAAc,OAAA;EAYnE,gBAAA,CACH,SAAA,EAAW,SAAA,EACX,MAAA,EAAQ,sBAAA,GACT,mBAAA,CAAoB,UAAA;EAUhB,WAAA,CAAY,SAAA,EAAW,mBAAA,CAAoB,UAAA;EAU3C,kBAAA,CAAmB,SAAA,EAAW,SAAA,GAAY,wBAAA;EAM1C,YAAA,CAAa,SAAA,EAAW,SAAA;EASxB,aAAA,aAA0B,SAAA;EAU1B,UAAA,CACH,MAAA,EAAQ,sBAAA,CAAuB,6BAAA,CAA8B,IAAA,EAAM,KAAA;EAoBhE,SAAA,CAAU,IAAA,WAAe,WAAA,GAAc,MAAA;EAQvC,UAAA;EAIA,kBAAA,CACH,SAAA,EAAW,mBAAA,CAAoB,UAAA,GAC/B,KAAA,EAAO,wBAAA,GACR,wBAAA;EAAA,QAQK,iBAAA;AAAA;;;cC3MC,QAAA,sBAA+B,CAAA,EAAG,CAAA,KAAI,CAAM;;;KCG7C,aAAA,cACK,MAAA,kCACC,MAAA,SAAe,IAAA,WAC5B,CAAA,SAAU,QAAA;EACX,GAAA,IAAO,EAAA,EAAI,IAAA,KAAS,IAAA;EACpB,IAAA,IAAQ,EAAA,EAAI,KAAA,KAAU,KAAA;AAAA;AAAA,cAGb,KAAA,gBACI,MAAA,kCACC,MAAA,SAAe,IAAA,QAE7B,QAAA,EAAU,aAAA,CAAc,IAAA,EAAM,KAAA,MAAM,aAAA,CAAA,IAAA,EAAA,KAAA;;;KCR5B,gCAAA,cACK,MAAA,kCACC,MAAA,SAAe,IAAA,yBACZ,MAAA,kCACH,QAAA,8BACA,eAAA,QAAuB,eAAA,CAAgB,kBAAA,KACrD,EAAA,CACA,wBAAA,CAAyB,IAAA,EAAM,KAAA,IAC3B,IAAA,CACI,cAAA,CACI,kBAAA,CAAmB,WAAA,CAAY,KAAA,EAAO,QAAA,GAAW,IAAA,EAAM,KAAA,GACvD,QAAA,EACA,KAAA,EACA,KAAA;EAIJ,SAAA,GAAY,eAAA,CACR,kBAAA,CAAmB,WAAA,CAAY,KAAA,EAAO,QAAA,GAAW,IAAA,EAAM,KAAA,GACvD,KAAA,EACA,oBAAA,CAAqB,kBAAA,CAAmB,WAAA,CAAY,KAAA,EAAO,QAAA,GAAW,IAAA,EAAM,KAAA;EAEhF,OAAA,GAAU,WAAA,CACN,kBAAA,CAAmB,WAAA,CAAY,KAAA,QAAa,IAAA,EAAM,KAAA,GAClD,QAAA;EAEJ,KAAA,GAAQ,aAAA,CAAc,IAAA,EAAM,KAAA;AAAA;AAAA,cAI3B,kBAAA,gBACI,MAAA,kCACC,MAAA,SAAe,IAAA,yBACZ,MAAA,kCACH,QAAA,8BACA,eAAA,QAAuB,eAAA,CAAgB,kBAAA,GAErD,MAAA,GAAQ,gCAAA,CAAiC,IAAA,EAAM,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,KAAA,MACxE,cAAA,CACC,kBAAA,CAAmB,WAAA,CAAY,KAAA,EAAO,QAAA,GAAW,IAAA,EAAM,KAAA,GACvD,QAAA,EACA,KAAA,EACA,KAAA"}
package/dist/index.mjs DELETED
@@ -1,223 +0,0 @@
1
- import { AbstractPlatform, AbstractWebSocket } from "@pluv/io";
2
- import { PersistenceCloudflareTransactionalStorage } from "@pluv/persistence-cloudflare-transactional-storage";
3
- //#region src/infer.ts
4
- const infer = (callback) => callback;
5
- //#endregion
6
- //#region src/CloudflareWebSocket.ts
7
- var CloudflareWebSocket = class extends AbstractWebSocket {
8
- set presence(presence) {
9
- const state = this.webSocket.deserializeAttachment().state;
10
- this.webSocket.serializeAttachment({
11
- ...this.webSocket.deserializeAttachment(),
12
- state: {
13
- ...state,
14
- presence
15
- }
16
- });
17
- }
18
- get readyState() {
19
- return this.webSocket.readyState;
20
- }
21
- get session() {
22
- const deserialized = this.webSocket.deserializeAttachment();
23
- const sessionId = deserialized.sessionId;
24
- const state = this.state;
25
- const user = deserialized.user ?? null;
26
- return {
27
- ...state,
28
- id: sessionId,
29
- user,
30
- webSocket: this
31
- };
32
- }
33
- get sessionId() {
34
- const deserialized = this.webSocket.deserializeAttachment() ?? {};
35
- const sessionId = deserialized.sessionId ?? `p_${crypto.randomUUID()}`;
36
- if (typeof deserialized.sessionId !== "string") this.webSocket.serializeAttachment({
37
- ...deserialized,
38
- sessionId
39
- });
40
- return sessionId;
41
- }
42
- get state() {
43
- const state = this.webSocket.deserializeAttachment()?.state ?? null;
44
- if (!state) throw new Error("Could not get websocket state");
45
- const currentPing = state.timers.ping;
46
- const lastPing = this._platform.getLastPing(this);
47
- if (!lastPing) return state;
48
- if (currentPing >= lastPing) return state;
49
- const newState = {
50
- ...state,
51
- timers: {
52
- ...state.timers,
53
- ping: lastPing
54
- }
55
- };
56
- return this._platform.setSerializedState(this, newState);
57
- }
58
- set state(state) {
59
- const deserialized = this.webSocket.deserializeAttachment();
60
- this.webSocket.serializeAttachment({
61
- ...deserialized,
62
- state
63
- });
64
- }
65
- set user(user) {
66
- const deserialized = this.webSocket.deserializeAttachment();
67
- this.webSocket.serializeAttachment({
68
- ...deserialized,
69
- user
70
- });
71
- }
72
- constructor(webSocket, config) {
73
- const { room } = config;
74
- super(webSocket, config);
75
- const state = {
76
- presence: null,
77
- quit: false,
78
- room,
79
- timers: {
80
- ping: (/* @__PURE__ */ new Date()).getTime(),
81
- presence: null
82
- }
83
- };
84
- webSocket.serializeAttachment({
85
- state,
86
- ...webSocket.deserializeAttachment()
87
- });
88
- }
89
- addEventListener(type, handler) {
90
- this.webSocket.addEventListener(type, handler);
91
- }
92
- close(code, reason) {
93
- if (![this.CONNECTING, this.OPEN].some((readyState) => readyState === this.readyState)) return;
94
- this.webSocket.close(code, reason);
95
- }
96
- send(message) {
97
- if (this.readyState !== this.OPEN) return;
98
- this.webSocket.send(message);
99
- }
100
- terminate(code = 1011) {
101
- return this.webSocket.close(code, "Terminated");
102
- }
103
- };
104
- //#endregion
105
- //#region src/CloudflarePlatform.ts
106
- var CloudflarePlatform = class CloudflarePlatform extends AbstractPlatform {
107
- id = crypto.randomUUID();
108
- _config;
109
- _name = "platformCloudflare";
110
- constructor(config) {
111
- super({
112
- ...config,
113
- ...config.roomContext && config.mode === "detached" ? { persistence: config.persistence ?? new PersistenceCloudflareTransactionalStorage({ mode: "sqlite" }) } : {}
114
- });
115
- this._config = {
116
- authorize: { secret: true },
117
- handleMode: "io",
118
- registrationMode: config.mode ?? "detached",
119
- requireAuth: false,
120
- listeners: {
121
- onRoomDestroyed: true,
122
- onRoomMessage: true,
123
- onStorageDestroyed: true,
124
- onStorageUpdated: true,
125
- onUserConnected: true,
126
- onUserDisconnected: true
127
- },
128
- router: true
129
- };
130
- const detachedState = this._getDetachedState();
131
- if (!detachedState) return;
132
- detachedState.setWebSocketAutoResponse(new WebSocketRequestResponsePair("{\"type\":\"$ping\",\"data\":{}}", JSON.stringify({
133
- type: "$pong",
134
- data: {}
135
- })));
136
- }
137
- async acceptWebSocket(webSocket) {
138
- const detachedState = this._getDetachedState();
139
- if (!detachedState) {
140
- webSocket.webSocket.accept();
141
- return;
142
- }
143
- detachedState.acceptWebSocket(webSocket.webSocket);
144
- }
145
- convertWebSocket(webSocket, config) {
146
- const { room } = config;
147
- return new CloudflareWebSocket(webSocket, {
148
- persistence: this.persistence,
149
- platform: this,
150
- room
151
- });
152
- }
153
- getLastPing(webSocket) {
154
- const detachedState = this._getDetachedState();
155
- if (!detachedState) return null;
156
- return detachedState.getWebSocketAutoResponseTimestamp(webSocket.webSocket)?.getTime() ?? null;
157
- }
158
- getSerializedState(webSocket) {
159
- return webSocket.deserializeAttachment()?.state ?? null;
160
- }
161
- getSessionId(webSocket) {
162
- const sessionId = (webSocket.deserializeAttachment() ?? {}).sessionId;
163
- if (typeof sessionId !== "string") return null;
164
- return sessionId;
165
- }
166
- getWebSockets() {
167
- const detachedState = this._getDetachedState();
168
- if (!detachedState) return [];
169
- return detachedState.getWebSockets() ?? [];
170
- }
171
- initialize(config) {
172
- const ctx = config.roomContext ?? { ...this._roomContext };
173
- if (!ctx.env || !ctx.state) throw new Error("Could not derive platform roomContext");
174
- const roomContext = {
175
- env: ctx.env,
176
- meta: ctx.meta,
177
- state: ctx.state
178
- };
179
- return new CloudflarePlatform({
180
- roomContext,
181
- mode: this._config.registrationMode,
182
- persistence: this.persistence.initialize(roomContext),
183
- pubSub: this.pubSub
184
- })._initialize();
185
- }
186
- parseData(data) {
187
- if (typeof data === "string") return JSON.parse(data);
188
- const decoder = new TextDecoder("utf8");
189
- return JSON.parse(decoder.decode(data));
190
- }
191
- randomUUID() {
192
- return crypto.randomUUID();
193
- }
194
- setSerializedState(webSocket, state) {
195
- const deserialized = webSocket.webSocket.deserializeAttachment() ?? {};
196
- webSocket.webSocket.serializeAttachment({
197
- ...deserialized,
198
- state
199
- });
200
- return state;
201
- }
202
- _getDetachedState() {
203
- if (this._config.registrationMode !== "detached") return null;
204
- return this._roomContext?.state ?? null;
205
- }
206
- };
207
- //#endregion
208
- //#region src/platformCloudflare.ts
209
- const platformCloudflare = (config = {}) => {
210
- const { authorize, context, crdt, debug, limits } = config;
211
- return {
212
- authorize,
213
- context,
214
- crdt,
215
- debug,
216
- limits,
217
- platform: () => new CloudflarePlatform(config)
218
- };
219
- };
220
- //#endregion
221
- export { infer, platformCloudflare };
222
-
223
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/infer.ts","../src/CloudflareWebSocket.ts","../src/constants.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 { InferIOAuthorizeUser, IOAuthorize, JsonObject } from \"@pluv/types\";\n\nexport interface CloudflareWebSocketEventMap {\n close: CloseEvent;\n message: MessageEvent;\n open: Event;\n error: ErrorEvent;\n}\n\nexport type CloudflareWebSocketConfig = AbstractWebSocketConfig;\n\nexport class CloudflareWebSocket<\n TAuthorize extends IOAuthorize<any, any> | null = null,\n> extends AbstractWebSocket<WebSocket, TAuthorize> {\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<TAuthorize> {\n const deserialized = this.webSocket.deserializeAttachment();\n const sessionId = deserialized.sessionId as string;\n const state = this.state;\n const user = (deserialized.user ?? null) as InferIOAuthorizeUser<TAuthorize>;\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 set user(user: InferIOAuthorizeUser<TAuthorize>) {\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 presence: null,\n quit: false,\n room,\n timers: {\n ping: new Date().getTime(),\n presence: null,\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 ) {\n this.webSocket.addEventListener(type, handler as any);\n }\n\n public close(code?: number | undefined, reason?: string | undefined): 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 { WebSocketRegistrationMode } from \"@pluv/io\";\n\nexport const DEFAULT_REGISTRATION_MODE: WebSocketRegistrationMode = \"detached\";\nexport const GARBAGE_COLLECT_INTERVAL_MS = 60_000;\n","import type {\n AbstractPlatformConfig,\n ConvertWebSocketConfig,\n WebSocketRegistrationMode,\n WebSocketSerializedState,\n} from \"@pluv/io\";\nimport { AbstractPlatform } from \"@pluv/io\";\nimport { PersistenceCloudflareTransactionalStorage } from \"@pluv/persistence-cloudflare-transactional-storage\";\nimport type { IOAuthorize, Json } from \"@pluv/types\";\nimport { CloudflareWebSocket } from \"./CloudflareWebSocket\";\nimport { DEFAULT_REGISTRATION_MODE } from \"./constants\";\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 mode?: WebSocketRegistrationMode;\n};\n\nexport class CloudflarePlatform<\n TAuthorize extends IOAuthorize<any, any> | null = null,\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n> extends AbstractPlatform<\n CloudflareWebSocket<TAuthorize>,\n { env: TEnv; request: Request },\n CloudflarePlatformRoomContext<TEnv, TMeta>,\n {\n authorize: {\n secret: true;\n };\n handleMode: \"io\";\n requireAuth: false;\n registrationMode: WebSocketRegistrationMode;\n listeners: {\n onRoomDestroyed: true;\n onRoomMessage: true;\n onStorageDestroyed: true;\n onStorageUpdated: true;\n onUserConnected: true;\n onUserDisconnected: true;\n };\n router: true;\n }\n> {\n public readonly id = crypto.randomUUID();\n public readonly _config;\n public readonly _name = \"platformCloudflare\";\n\n constructor(config: CloudflarePlatformConfig<TEnv, TMeta>) {\n super({\n ...config,\n ...(config.roomContext && config.mode === \"detached\"\n ? {\n persistence:\n config.persistence ??\n new PersistenceCloudflareTransactionalStorage({ mode: \"sqlite\" }),\n }\n : {}),\n });\n\n this._config = {\n authorize: {\n secret: true as const,\n },\n handleMode: \"io\" as const,\n registrationMode: config.mode ?? DEFAULT_REGISTRATION_MODE,\n requireAuth: false as const,\n listeners: {\n onRoomDestroyed: true as const,\n onRoomMessage: true as const,\n onStorageDestroyed: true as const,\n onStorageUpdated: true as const,\n onUserConnected: true as const,\n onUserDisconnected: true as const,\n },\n router: true as const,\n };\n\n const detachedState = this._getDetachedState();\n\n if (!detachedState) return;\n\n detachedState.setWebSocketAutoResponse(\n new WebSocketRequestResponsePair(\n '{\"type\":\"$ping\",\"data\":{}}',\n JSON.stringify({ type: \"$pong\", data: {} }),\n ),\n );\n }\n\n public async acceptWebSocket(webSocket: CloudflareWebSocket<TAuthorize>): Promise<void> {\n const detachedState = this._getDetachedState();\n\n if (!detachedState) {\n webSocket.webSocket.accept();\n\n return;\n }\n\n detachedState.acceptWebSocket(webSocket.webSocket);\n }\n\n public convertWebSocket(\n webSocket: WebSocket,\n config: ConvertWebSocketConfig,\n ): CloudflareWebSocket<TAuthorize> {\n const { room } = config;\n\n return new CloudflareWebSocket<TAuthorize>(webSocket, {\n persistence: this.persistence,\n platform: this,\n room,\n });\n }\n\n public getLastPing(webSocket: CloudflareWebSocket<TAuthorize>): number | null {\n const detachedState = this._getDetachedState();\n\n if (!detachedState) return null;\n\n const timestamp = detachedState.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 detachedState = this._getDetachedState();\n\n if (!detachedState) return [];\n\n const webSockets = detachedState.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 return new CloudflarePlatform<TAuthorize, TEnv, TMeta>({\n roomContext,\n mode: this._config.registrationMode,\n persistence: this.persistence.initialize(roomContext),\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<TAuthorize>,\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 private _getDetachedState(): DurableObjectState | null {\n if (this._config.registrationMode !== \"detached\") return null;\n\n const detachedState = this._roomContext?.state ?? null;\n\n return detachedState;\n }\n}\n","import type { CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport type { CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize } from \"@pluv/io\";\nimport type { BaseUser, Id, IOAuthorize, Json } from \"@pluv/types\";\nimport type { CloudflarePlatformConfig } from \"./CloudflarePlatform\";\nimport { CloudflarePlatform } from \"./CloudflarePlatform\";\nimport type { InferCallback } from \"./infer\";\n\nexport type PlatformCloudflareCreateIOParams<\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n TContext extends Record<string, any> = {},\n TUser extends BaseUser | null = null,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n> = Id<\n CloudflarePlatformConfig<TEnv, TMeta> &\n Omit<\n CreateIOParams<\n CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,\n TContext,\n TUser,\n TCrdt\n >,\n \"authorize\" | \"context\" | \"platform\"\n > & {\n authorize?: PluvIOAuthorize<\n CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,\n TUser,\n InferInitContextType<CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>>\n >;\n context?: PluvContext<\n CloudflarePlatform<IOAuthorize<TUser, any>, TEnv, TMeta>,\n TContext\n >;\n types?: InferCallback<TEnv, TMeta>;\n }\n>;\n\nexport const platformCloudflare = <\n TEnv extends Record<string, any> = {},\n TMeta extends Record<string, Json> = {},\n TContext extends Record<string, any> = {},\n TUser extends BaseUser | null = null,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n>(\n config: PlatformCloudflareCreateIOParams<TEnv, TMeta, TContext, TUser, TCrdt> = {},\n): CreateIOParams<\n CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>,\n TContext,\n TUser,\n TCrdt\n> => {\n const { authorize, context, crdt, debug, limits } = config;\n\n return {\n authorize,\n context,\n crdt,\n debug,\n limits,\n platform: () => new CloudflarePlatform<IOAuthorize<TUser, TContext>, TEnv, TMeta>(config),\n };\n};\n"],"mappings":"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"}