@pluv/platform-pluv 3.2.2 → 4.0.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.
package/dist/index.mjs CHANGED
@@ -1,469 +1,467 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
- var __async = (__this, __arguments, generator) => {
8
- return new Promise((resolve, reject) => {
9
- var fulfilled = (value) => {
10
- try {
11
- step(generator.next(value));
12
- } catch (e) {
13
- reject(e);
14
- }
15
- };
16
- var rejected = (value) => {
17
- try {
18
- step(generator.throw(value));
19
- } catch (e) {
20
- reject(e);
21
- }
22
- };
23
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
24
- step((generator = generator.apply(__this, __arguments)).next());
25
- });
26
- };
27
-
28
- // src/PluvPlatform.ts
1
+ import { createRequire } from "node:module";
29
2
  import { AbstractPlatform } from "@pluv/io";
30
3
  import stringify from "fast-json-stable-stringify";
31
4
  import { Hono } from "hono";
5
+ import { z } from "zod";
32
6
 
33
- // src/constants.ts
34
- var SIGNATURE_ALGORITHM = "sha256";
35
- var SIGNATURE_HEADER = "x-pluv-signature-256";
7
+ //#region rolldown:runtime
8
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
36
9
 
37
- // src/schemas.ts
38
- import { z } from "zod";
39
- var ZodEventKind = z.union([
40
- z.literal("initial-storage"),
41
- z.literal("room-deleted"),
42
- z.literal("user-connected"),
43
- z.literal("user-disconnected")
10
+ //#endregion
11
+ //#region src/constants.ts
12
+ const SIGNATURE_ALGORITHM = "sha256";
13
+ const SIGNATURE_HEADER = "x-pluv-signature-256";
14
+
15
+ //#endregion
16
+ //#region src/schemas.ts
17
+ const ZodEventKind = z.union([
18
+ z.literal("initial-storage"),
19
+ z.literal("room-destroyed"),
20
+ z.literal("storage-destroyed"),
21
+ z.literal("user-connected"),
22
+ z.literal("user-disconnected")
44
23
  ]);
45
- var ZodEventInitialStorage = z.object({
46
- event: z.literal("initial-storage"),
47
- data: z.object({
48
- room: z.string()
49
- })
24
+ const ZodEventInitialStorage = z.object({
25
+ event: z.literal("initial-storage"),
26
+ data: z.object({ room: z.string() })
27
+ });
28
+ const ZodEventRoomDestroyed = z.object({
29
+ event: z.literal("room-destroyed"),
30
+ data: z.object({ room: z.string() })
50
31
  });
51
- var ZodEventRoomDeleted = z.object({
52
- event: z.literal("room-deleted"),
53
- data: z.object({
54
- room: z.string(),
55
- storage: z.string().nullable()
56
- })
32
+ const ZodEventStorageDestroyed = z.object({
33
+ event: z.literal("storage-destroyed"),
34
+ data: z.object({
35
+ room: z.string(),
36
+ storage: z.string().nullable()
37
+ })
57
38
  });
58
- var ZodEventUserConnected = z.object({
59
- event: z.literal("user-connected"),
60
- data: z.object({
61
- room: z.string(),
62
- storage: z.string().nullable(),
63
- user: z.object({
64
- id: z.string()
65
- }).passthrough()
66
- })
39
+ const ZodEventUserConnected = z.object({
40
+ event: z.literal("user-connected"),
41
+ data: z.object({
42
+ room: z.string(),
43
+ storage: z.string().nullable(),
44
+ user: z.object({ id: z.string() }).passthrough()
45
+ })
67
46
  });
68
- var ZodEventUserDisconnected = z.object({
69
- event: z.literal("user-disconnected"),
70
- data: z.object({
71
- room: z.string(),
72
- storage: z.string().nullable(),
73
- user: z.object({
74
- id: z.string()
75
- }).passthrough()
76
- })
47
+ const ZodEventUserDisconnected = z.object({
48
+ event: z.literal("user-disconnected"),
49
+ data: z.object({
50
+ room: z.string(),
51
+ storage: z.string().nullable(),
52
+ user: z.object({ id: z.string() }).passthrough()
53
+ })
77
54
  });
78
- var ZodEvent = z.discriminatedUnion("event", [
79
- ZodEventInitialStorage,
80
- ZodEventRoomDeleted,
81
- ZodEventUserConnected,
82
- ZodEventUserDisconnected
55
+ const ZodEvent = z.discriminatedUnion("event", [
56
+ ZodEventInitialStorage,
57
+ ZodEventRoomDestroyed,
58
+ ZodEventStorageDestroyed,
59
+ ZodEventUserConnected,
60
+ ZodEventUserDisconnected
83
61
  ]);
84
- var ZodInitialStorageResponse = z.object({
85
- event: z.literal("initial-storage"),
86
- room: z.string(),
87
- storage: z.string().nullable()
62
+ const ZodInitialStorageResponse = z.object({
63
+ event: z.literal("initial-storage"),
64
+ room: z.string(),
65
+ storage: z.string().nullable()
88
66
  });
89
- var ZodRoomDeletedResponse = z.object({
90
- event: z.literal("room-deleted"),
91
- room: z.string()
67
+ const ZodRoomDestroyedResponse = z.object({
68
+ event: z.literal("room-destroyed"),
69
+ room: z.string()
92
70
  });
93
- var ZodUserConnectedResponse = z.object({
94
- event: z.literal("user-connected"),
95
- room: z.string()
71
+ const ZodStorageDestroyedResponse = z.object({
72
+ event: z.literal("storage-destroyed"),
73
+ room: z.string()
96
74
  });
97
- var ZodUserDisconnectedResponse = z.object({
98
- event: z.literal("user-disconnected"),
99
- room: z.string()
75
+ const ZodUserConnectedResponse = z.object({
76
+ event: z.literal("user-connected"),
77
+ room: z.string()
100
78
  });
101
- var ZodEventResponse = z.discriminatedUnion("event", [
102
- ZodInitialStorageResponse,
103
- ZodRoomDeletedResponse,
104
- ZodUserConnectedResponse,
105
- ZodUserDisconnectedResponse
79
+ const ZodUserDisconnectedResponse = z.object({
80
+ event: z.literal("user-disconnected"),
81
+ room: z.string()
82
+ });
83
+ const ZodEventResponse = z.discriminatedUnion("event", [
84
+ ZodInitialStorageResponse,
85
+ ZodRoomDestroyedResponse,
86
+ ZodStorageDestroyedResponse,
87
+ ZodUserConnectedResponse,
88
+ ZodUserDisconnectedResponse
106
89
  ]);
107
90
 
108
- // src/shared/createErrorResponse.ts
109
- var createErrorResponse = (c, error, status) => {
110
- return c.json({ ok: false, error: { message: error.message } }, status);
91
+ //#endregion
92
+ //#region src/shared/createErrorResponse.ts
93
+ const createErrorResponse = (c, error, status) => {
94
+ return c.json({
95
+ ok: false,
96
+ error: { message: error.message }
97
+ }, status);
111
98
  };
112
99
 
113
- // src/shared/getCrypto.ts
114
- var getCrypto = () => {
115
- if (typeof crypto !== "undefined") {
116
- return crypto;
117
- }
118
- if (typeof __require === "function") {
119
- return __require("crypto").webcrypto;
120
- }
121
- throw new Error("Missing crypto module");
100
+ //#endregion
101
+ //#region src/shared/getCrypto.ts
102
+ const getCrypto = () => {
103
+ if (typeof crypto !== "undefined") return crypto;
104
+ if (typeof __require === "function") return __require("node:crypto").webcrypto;
105
+ throw new Error("Missing crypto module");
122
106
  };
123
107
 
124
- // src/shared/createHmac.ts
125
- var createHmac = (params) => __async(null, null, function* () {
126
- const { payload, secret } = params;
127
- if (!payload || !secret) throw new Error("Secret and payload are required to sign payload");
128
- const encoder = new TextEncoder();
129
- const keyBytes = encoder.encode(secret);
130
- const crypto2 = getCrypto();
131
- const algorithm = { name: "HMAC", hash: { name: "SHA-256" } };
132
- const extractable = false;
133
- const key = yield crypto2.subtle.importKey("raw", keyBytes, algorithm, extractable, [
134
- "sign",
135
- "verify"
136
- ]);
137
- const payloadBytes = encoder.encode(payload);
138
- const signature = yield crypto2.subtle.sign("HMAC", key, payloadBytes);
139
- const hmac = Array.from(new Uint8Array(signature)).map((b) => ("0" + b.toString(16)).slice(-2)).join("");
140
- return { algorithm: "sha256", hmac };
141
- });
108
+ //#endregion
109
+ //#region src/shared/createHmac.ts
110
+ const createHmac = async (params) => {
111
+ const { payload, secret } = params;
112
+ if (!payload || !secret) throw new Error("Secret and payload are required to sign payload");
113
+ const encoder = new TextEncoder();
114
+ const keyBytes = encoder.encode(secret);
115
+ const crypto = getCrypto();
116
+ const key = await crypto.subtle.importKey("raw", keyBytes, {
117
+ name: "HMAC",
118
+ hash: { name: "SHA-256" }
119
+ }, false, ["sign", "verify"]);
120
+ const payloadBytes = encoder.encode(payload);
121
+ const signature = await crypto.subtle.sign("HMAC", key, payloadBytes);
122
+ return {
123
+ algorithm: "sha256",
124
+ hmac: Array.from(new Uint8Array(signature)).map((b) => ("0" + b.toString(16)).slice(-2)).join("")
125
+ };
126
+ };
142
127
 
143
- // src/shared/createSuccessResponse.ts
144
- var createSuccessResponse = (c, data, status = 200) => {
145
- return c.json(
146
- {
147
- ok: true,
148
- data: ZodEventResponse.parse(data)
149
- },
150
- status
151
- );
128
+ //#endregion
129
+ //#region src/shared/createSuccessResponse.ts
130
+ const createSuccessResponse = (c, data, status = 200) => {
131
+ return c.json({
132
+ ok: true,
133
+ data: ZodEventResponse.parse(data)
134
+ }, status);
152
135
  };
153
136
 
154
- // src/shared/HttpError.ts
137
+ //#endregion
138
+ //#region src/shared/HttpError.ts
155
139
  var HttpError = class extends Error {
156
- constructor(message, status) {
157
- super(message);
158
- this.status = status;
159
- }
140
+ status;
141
+ constructor(message, status) {
142
+ super(message);
143
+ this.status = status;
144
+ }
160
145
  };
161
146
 
162
- // src/shared/timingSafeEqual.ts
163
- var timingSafeEqual = (a, b) => {
164
- if (a.length !== b.length) return false;
165
- let result = 0;
166
- for (let i = 0; i < a.length; i++) {
167
- result |= a[i] ^ b[i];
168
- }
169
- return result === 0;
147
+ //#endregion
148
+ //#region src/shared/timingSafeEqual.ts
149
+ const timingSafeEqual = (a, b) => {
150
+ if (a.length !== b.length) return false;
151
+ let result = 0;
152
+ for (let i = 0; i < a.length; i++) result |= a[i] ^ b[i];
153
+ return result === 0;
170
154
  };
171
155
 
172
- // src/shared/verifyWebhook.ts
173
- var verifyWebhook = (params) => __async(null, null, function* () {
174
- const { payload, secret, signature } = params;
175
- if (!secret || !payload || !signature) {
176
- throw new Error("Secret, payload and signature are required to verify payload");
177
- }
178
- const { hmac } = yield createHmac({ payload, secret });
179
- if (hmac.length !== signature.length) return false;
180
- const encoder = new TextEncoder();
181
- const verificationBytes = encoder.encode(hmac);
182
- const signatureBytes = encoder.encode(signature);
183
- if (verificationBytes.length !== signatureBytes.length) return false;
184
- return timingSafeEqual(verificationBytes, signatureBytes);
185
- });
156
+ //#endregion
157
+ //#region src/shared/verifyWebhook.ts
158
+ const verifyWebhook = async (params) => {
159
+ const { payload, secret, signature } = params;
160
+ if (!secret || !payload || !signature) throw new Error("Secret, payload and signature are required to verify payload");
161
+ const { hmac } = await createHmac({
162
+ payload,
163
+ secret
164
+ });
165
+ if (hmac.length !== signature.length) return false;
166
+ const encoder = new TextEncoder();
167
+ const verificationBytes = encoder.encode(hmac);
168
+ const signatureBytes = encoder.encode(signature);
169
+ if (verificationBytes.length !== signatureBytes.length) return false;
170
+ return timingSafeEqual(verificationBytes, signatureBytes);
171
+ };
186
172
 
187
- // src/PluvPlatform.ts
173
+ //#endregion
174
+ //#region src/PluvPlatform.ts
188
175
  var PluvPlatform = class extends AbstractPlatform {
189
- constructor(params) {
190
- var _a, _b;
191
- super();
192
- this.id = Math.random().toString();
193
- this._config = {
194
- authorize: {
195
- secret: false
196
- },
197
- handleMode: "fetch",
198
- registrationMode: "attached",
199
- requireAuth: true,
200
- listeners: {
201
- onRoomDeleted: true,
202
- onRoomMessage: false,
203
- onStorageUpdated: false,
204
- onUserConnected: true,
205
- onUserDisconnected: true
206
- },
207
- router: false
208
- };
209
- this._name = "platformPluv";
210
- this._createToken = (params) => __async(this, null, function* () {
211
- var _a, _b;
212
- const parsed = params.authorize.user.parse(params.user);
213
- const [endpoints, publicKey, secretKey] = yield Promise.all([
214
- typeof this._endpoints === "object" ? this._endpoints : this._endpoints(),
215
- typeof this._publicKey === "string" ? this._publicKey : this._publicKey(),
216
- typeof this._secretKey === "string" ? this._secretKey : this._secretKey()
217
- ]);
218
- this._logDebug({ endpoints, publicKey, secretKey });
219
- const res = yield fetch(endpoints.createToken, {
220
- headers: { "content-type": "application/json" },
221
- method: "post",
222
- body: JSON.stringify({
223
- maxAge: (_a = params.maxAge) != null ? _a : null,
224
- publicKey,
225
- room: params.room,
226
- secretKey,
227
- user: parsed
228
- })
229
- }).catch((error) => {
230
- this._logDebug(error);
231
- return null;
232
- });
233
- this._logDebug({ response: { status: (_b = res == null ? void 0 : res.status) != null ? _b : null } });
234
- if (!res || !res.ok || res.status !== 200) {
235
- throw new Error("Authorization failed");
236
- }
237
- const token = yield res.text().catch(() => null);
238
- this._logDebug({ token });
239
- if (typeof token !== "string") throw new Error("Authorization failed");
240
- return token;
241
- });
242
- this._webhooksRouter = new Hono().basePath("/").post("/", (c) => __async(this, null, function* () {
243
- var _a, _b, _c, _d, _e, _f, _g, _h;
244
- const [algorithm, signature] = (_b = (_a = c.req.header(SIGNATURE_HEADER)) == null ? void 0 : _a.split("=")) != null ? _b : [];
245
- try {
246
- if (!this._webhookSecret) {
247
- this._logDebug("Missing webhook secret");
248
- throw new HttpError("Unauthorized", 401);
249
- }
250
- if (algorithm !== SIGNATURE_ALGORITHM) {
251
- this._logDebug(
252
- `Verification algorithm is not ${SIGNATURE_ALGORITHM}. Found: `,
253
- algorithm
254
- );
255
- throw new HttpError("Unauthorized", 401);
256
- }
257
- if (!signature) {
258
- this._logDebug("Missing webhook signature");
259
- throw new HttpError("Unauthorized", 401);
260
- }
261
- const [payload, webhookSecret] = yield Promise.all([
262
- c.req.json(),
263
- typeof this._webhookSecret === "string" ? this._webhookSecret : yield this._webhookSecret()
264
- ]).catch((error) => {
265
- this._logDebug(
266
- "Could not derive webhook secret: ",
267
- error instanceof Error ? error.message : "Unexpected error"
268
- );
269
- throw error;
270
- });
271
- const verified = yield verifyWebhook({
272
- payload: stringify(payload),
273
- signature,
274
- secret: webhookSecret
275
- }).catch((error) => {
276
- this._logDebug(
277
- "Error while verifying webhook: ",
278
- error instanceof Error ? error.message : "Unexpected error"
279
- );
280
- return false;
281
- });
282
- if (!verified) {
283
- this._logDebug("Failed to verify webhook");
284
- throw new HttpError("Unauthorized", 401);
285
- }
286
- const parsed = ZodEvent.safeParse(payload);
287
- if (!parsed.success) {
288
- this._logDebug(
289
- "Failed to validate event payload:",
290
- JSON.stringify((_c = parsed.data) != null ? _c : {}, null, 4)
291
- );
292
- throw new HttpError("Invalid request", 400);
293
- }
294
- const { event, data } = parsed.data;
295
- const context = yield this._getContext();
296
- switch (event) {
297
- case "initial-storage": {
298
- const room = data.room;
299
- const storage = typeof room === "string" ? (_e = yield (_d = this._getInitialStorage) == null ? void 0 : _d.call(this, { context, room })) != null ? _e : null : null;
300
- try {
301
- return createSuccessResponse(c, { event, room, storage });
302
- } catch (error) {
303
- this._logDebug("Could not create getInitialStorage response");
304
- throw error;
305
- }
306
- }
307
- case "room-deleted": {
308
- const room = data.room;
309
- const encodedState = data.storage;
310
- yield Promise.resolve(
311
- (_f = this._listeners) == null ? void 0 : _f.onRoomDeleted({ context, encodedState, room })
312
- );
313
- try {
314
- return createSuccessResponse(c, { event, room });
315
- } catch (error) {
316
- this._logDebug("Could not create onRoomDeleted response");
317
- throw error;
318
- }
319
- }
320
- case "user-connected": {
321
- const room = data.room;
322
- const encodedState = data.storage;
323
- const user = data.user;
324
- yield Promise.resolve(
325
- (_g = this._listeners) == null ? void 0 : _g.onUserConnected({
326
- context,
327
- encodedState,
328
- platform: this,
329
- room,
330
- user
331
- })
332
- );
333
- try {
334
- return createSuccessResponse(c, { event, room });
335
- } catch (error) {
336
- this._logDebug("Could not create onUserConnected response");
337
- throw error;
338
- }
339
- }
340
- case "user-disconnected": {
341
- const room = data.room;
342
- const encodedState = data.storage;
343
- const user = data.user;
344
- yield Promise.resolve(
345
- (_h = this._listeners) == null ? void 0 : _h.onUserDisconnected({
346
- context,
347
- encodedState,
348
- platform: this,
349
- room,
350
- user
351
- })
352
- );
353
- try {
354
- return createSuccessResponse(c, { event, room });
355
- } catch (error) {
356
- this._logDebug("Could not create onUserDisconnected response");
357
- throw error;
358
- }
359
- }
360
- default: {
361
- throw new HttpError("Unknown event", 400);
362
- }
363
- }
364
- } catch (error) {
365
- const message = error instanceof Error ? error.message : "Unexpected error";
366
- const status = error instanceof HttpError ? error.status : 500;
367
- this._logDebug("Uncaught error: ", message);
368
- return createErrorResponse(c, { message }, status);
369
- }
370
- }));
371
- const { _defs, basePath, context, publicKey, secretKey, webhookSecret } = params;
372
- this._basePath = basePath;
373
- this._context = context != null ? context : {};
374
- this._debug = (_a = _defs == null ? void 0 : _defs.debug) != null ? _a : false;
375
- this._endpoints = (_b = _defs == null ? void 0 : _defs.endpoints) != null ? _b : {
376
- createToken: "https://rooms.pluv.io/api/room/token"
377
- };
378
- this._publicKey = publicKey;
379
- this._secretKey = secretKey;
380
- this._webhookSecret = webhookSecret;
381
- this._app = new Hono().basePath(this._basePath).route("/", this._webhooksRouter);
382
- this._fetch = this._app.fetch;
383
- }
384
- acceptWebSocket(webSocket) {
385
- throw new Error("Not implemented");
386
- }
387
- convertWebSocket(webSocket, config) {
388
- throw new Error("Not implemented");
389
- }
390
- getLastPing(webSocket) {
391
- throw new Error("Not implemented");
392
- }
393
- getSerializedState(webSocket) {
394
- throw new Error("Not implemented");
395
- }
396
- getSessionId(webSocket) {
397
- throw new Error("Not implemented");
398
- }
399
- getWebSockets() {
400
- throw new Error("Not implemented");
401
- }
402
- initialize(config) {
403
- throw new Error("Not implemented");
404
- }
405
- parseData(data) {
406
- throw new Error("Not implemented");
407
- }
408
- randomUUID() {
409
- throw new Error("Not implemented");
410
- }
411
- setSerializedState(webSocket, state) {
412
- throw new Error("Not implemented");
413
- }
414
- validateConfig(config) {
415
- this._logDebug("validating config with properties:", Object.keys(config != null ? config : {}));
416
- if (!config.authorize) {
417
- this._logDebug("Config `authorize` must be provided to `platformPluv`");
418
- throw new Error("Config `authorize` must be provided to `platformPluv`");
419
- }
420
- if (!!config.onRoomMessage) {
421
- this._logDebug("Config `onRoomMessage` is not supported on `platformPluv`");
422
- throw new Error("Config `onRoomMessage` is not supported on `platformPluv`");
423
- }
424
- if (!!config.onStorageUpdated) {
425
- this._logDebug("Config `onStorageUpdated` is not supported on `platformPluv`");
426
- throw new Error("Config `onStorageUpdated` is not supported on `platformPluv`");
427
- }
428
- this._getInitialStorage = config.getInitialStorage;
429
- this._listeners = {
430
- onRoomDeleted: (event) => {
431
- var _a;
432
- return (_a = config.onRoomDeleted) == null ? void 0 : _a.call(config, event);
433
- },
434
- onUserConnected: (event) => {
435
- var _a;
436
- return (_a = config.onUserConnected) == null ? void 0 : _a.call(config, event);
437
- },
438
- onUserDisconnected: (event) => {
439
- var _a;
440
- return (_a = config.onUserDisconnected) == null ? void 0 : _a.call(config, event);
441
- }
442
- };
443
- }
444
- _getContext() {
445
- return __async(this, null, function* () {
446
- return typeof this._context === "function" ? yield Promise.resolve(this._context(this._roomContext)) : yield Promise.resolve(this._context);
447
- });
448
- }
449
- _logDebug(...args) {
450
- if (!this._debug) return;
451
- console.log("[PLATFORM PLUV]", ...args);
452
- }
176
+ id = Math.random().toString();
177
+ _config = {
178
+ authorize: { secret: false },
179
+ handleMode: "fetch",
180
+ registrationMode: "attached",
181
+ requireAuth: true,
182
+ listeners: {
183
+ onRoomDestroyed: true,
184
+ onRoomMessage: false,
185
+ onStorageDestroyed: true,
186
+ onStorageUpdated: false,
187
+ onUserConnected: true,
188
+ onUserDisconnected: true
189
+ },
190
+ router: false
191
+ };
192
+ _name = "platformPluv";
193
+ _app;
194
+ _basePath;
195
+ _context;
196
+ _debug;
197
+ _endpoints;
198
+ _getInitialStorage;
199
+ _listeners;
200
+ _publicKey;
201
+ _secretKey;
202
+ _webhookSecret;
203
+ _createToken = async (params) => {
204
+ const parsed = params.authorize.user.parse(params.user);
205
+ const [endpoints, publicKey, secretKey] = await Promise.all([
206
+ typeof this._endpoints === "object" ? this._endpoints : this._endpoints(),
207
+ typeof this._publicKey === "string" ? this._publicKey : this._publicKey(),
208
+ typeof this._secretKey === "string" ? this._secretKey : this._secretKey()
209
+ ]);
210
+ this._logDebug({
211
+ endpoints,
212
+ publicKey,
213
+ secretKey
214
+ });
215
+ const res = await fetch(endpoints.createToken, {
216
+ headers: { "content-type": "application/json" },
217
+ method: "post",
218
+ body: JSON.stringify({
219
+ maxAge: params.maxAge ?? null,
220
+ publicKey,
221
+ room: params.room,
222
+ secretKey,
223
+ user: parsed
224
+ })
225
+ }).catch((error) => {
226
+ this._logDebug(error);
227
+ return null;
228
+ });
229
+ this._logDebug({ response: { status: res?.status ?? null } });
230
+ if (!res || !res.ok || res.status !== 200) throw new Error("Authorization failed");
231
+ const token = await res.text().catch(() => null);
232
+ this._logDebug({ token });
233
+ if (typeof token !== "string") throw new Error("Authorization failed");
234
+ return token;
235
+ };
236
+ constructor(params) {
237
+ super();
238
+ const { _defs, basePath, context, publicKey, secretKey, webhookSecret } = params;
239
+ this._basePath = basePath;
240
+ this._context = context ?? {};
241
+ this._debug = _defs?.debug ?? false;
242
+ this._endpoints = _defs?.endpoints ?? { createToken: "https://rooms.pluv.io/api/room/token" };
243
+ this._publicKey = publicKey;
244
+ this._secretKey = secretKey;
245
+ this._webhookSecret = webhookSecret;
246
+ this._app = new Hono().basePath(this._basePath).route("/", this._webhooksRouter);
247
+ this._fetch = this._app.fetch;
248
+ }
249
+ acceptWebSocket(webSocket) {
250
+ throw new Error("Not implemented");
251
+ }
252
+ convertWebSocket(webSocket, config) {
253
+ throw new Error("Not implemented");
254
+ }
255
+ getLastPing(webSocket) {
256
+ throw new Error("Not implemented");
257
+ }
258
+ getSerializedState(webSocket) {
259
+ throw new Error("Not implemented");
260
+ }
261
+ getSessionId(webSocket) {
262
+ throw new Error("Not implemented");
263
+ }
264
+ getWebSockets() {
265
+ throw new Error("Not implemented");
266
+ }
267
+ initialize(config) {
268
+ throw new Error("Not implemented");
269
+ }
270
+ parseData(data) {
271
+ throw new Error("Not implemented");
272
+ }
273
+ randomUUID() {
274
+ throw new Error("Not implemented");
275
+ }
276
+ setSerializedState(webSocket, state) {
277
+ throw new Error("Not implemented");
278
+ }
279
+ validateConfig(config) {
280
+ this._logDebug("validating config with properties:", Object.keys(config ?? {}));
281
+ if (!config.authorize) {
282
+ this._logDebug("Config `authorize` must be provided to `platformPluv`");
283
+ throw new Error("Config `authorize` must be provided to `platformPluv`");
284
+ }
285
+ if (!!config.onRoomMessage) {
286
+ this._logDebug("Config `onRoomMessage` is not supported on `platformPluv`");
287
+ throw new Error("Config `onRoomMessage` is not supported on `platformPluv`");
288
+ }
289
+ if (!!config.onStorageUpdated) {
290
+ this._logDebug("Config `onStorageUpdated` is not supported on `platformPluv`");
291
+ throw new Error("Config `onStorageUpdated` is not supported on `platformPluv`");
292
+ }
293
+ this._getInitialStorage = config.getInitialStorage;
294
+ this._listeners = {
295
+ onRoomDestroyed: (event) => config.onRoomDestroyed?.(event),
296
+ onStorageDestroyed: (event) => config.onStorageDestroyed?.(event),
297
+ onUserConnected: (event) => config.onUserConnected?.(event),
298
+ onUserDisconnected: (event) => config.onUserDisconnected?.(event)
299
+ };
300
+ }
301
+ _webhooksRouter = new Hono().basePath("/").post("/", async (c) => {
302
+ const [algorithm, signature] = c.req.header(SIGNATURE_HEADER)?.split("=") ?? [];
303
+ try {
304
+ if (!this._webhookSecret) {
305
+ this._logDebug("Missing webhook secret");
306
+ throw new HttpError("Unauthorized", 401);
307
+ }
308
+ if (algorithm !== SIGNATURE_ALGORITHM) {
309
+ this._logDebug(`Verification algorithm is not ${SIGNATURE_ALGORITHM}. Found: `, algorithm);
310
+ throw new HttpError("Unauthorized", 401);
311
+ }
312
+ if (!signature) {
313
+ this._logDebug("Missing webhook signature");
314
+ throw new HttpError("Unauthorized", 401);
315
+ }
316
+ const [payload, webhookSecret] = await Promise.all([c.req.json(), typeof this._webhookSecret === "string" ? this._webhookSecret : await this._webhookSecret()]).catch((error) => {
317
+ this._logDebug("Could not derive webhook secret: ", error instanceof Error ? error.message : "Unexpected error");
318
+ throw error;
319
+ });
320
+ if (!await verifyWebhook({
321
+ payload: stringify(payload),
322
+ signature,
323
+ secret: webhookSecret
324
+ }).catch((error) => {
325
+ this._logDebug("Error while verifying webhook: ", error instanceof Error ? error.message : "Unexpected error");
326
+ return false;
327
+ })) {
328
+ this._logDebug("Failed to verify webhook");
329
+ throw new HttpError("Unauthorized", 401);
330
+ }
331
+ const parsed = ZodEvent.safeParse(payload);
332
+ if (!parsed.success) {
333
+ this._logDebug("Failed to validate event payload:", JSON.stringify(parsed.data ?? {}, null, 4));
334
+ throw new HttpError("Invalid request", 400);
335
+ }
336
+ const { event, data } = parsed.data;
337
+ const context = await this._getContext();
338
+ switch (event) {
339
+ case "initial-storage": {
340
+ const room = data.room;
341
+ const storage = typeof room === "string" ? await this._getInitialStorage?.({
342
+ context,
343
+ room
344
+ }) ?? null : null;
345
+ try {
346
+ return createSuccessResponse(c, {
347
+ event,
348
+ room,
349
+ storage
350
+ });
351
+ } catch (error) {
352
+ this._logDebug("Could not create getInitialStorage response");
353
+ throw error;
354
+ }
355
+ }
356
+ case "room-destroyed": {
357
+ const room = data.room;
358
+ await Promise.resolve(this._listeners?.onRoomDestroyed({
359
+ context,
360
+ platform: this,
361
+ room
362
+ }));
363
+ try {
364
+ return createSuccessResponse(c, {
365
+ event,
366
+ room
367
+ });
368
+ } catch (error) {
369
+ this._logDebug("Could not create onRoomDestroyed response");
370
+ throw error;
371
+ }
372
+ }
373
+ case "storage-destroyed": {
374
+ const room = data.room;
375
+ const encodedState = data.storage;
376
+ await Promise.resolve(this._listeners?.onStorageDestroyed({
377
+ context,
378
+ encodedState,
379
+ platform: this,
380
+ room
381
+ }));
382
+ try {
383
+ return createSuccessResponse(c, {
384
+ event,
385
+ room
386
+ });
387
+ } catch (error) {
388
+ this._logDebug("Could not create onStorageDestroyed response");
389
+ throw error;
390
+ }
391
+ }
392
+ case "user-connected": {
393
+ const room = data.room;
394
+ const encodedState = data.storage;
395
+ const user = data.user;
396
+ await Promise.resolve(this._listeners?.onUserConnected({
397
+ context,
398
+ encodedState,
399
+ platform: this,
400
+ room,
401
+ user
402
+ }));
403
+ try {
404
+ return createSuccessResponse(c, {
405
+ event,
406
+ room
407
+ });
408
+ } catch (error) {
409
+ this._logDebug("Could not create onUserConnected response");
410
+ throw error;
411
+ }
412
+ }
413
+ case "user-disconnected": {
414
+ const room = data.room;
415
+ const encodedState = data.storage;
416
+ const user = data.user;
417
+ await Promise.resolve(this._listeners?.onUserDisconnected({
418
+ context,
419
+ encodedState,
420
+ platform: this,
421
+ room,
422
+ user
423
+ }));
424
+ try {
425
+ return createSuccessResponse(c, {
426
+ event,
427
+ room
428
+ });
429
+ } catch (error) {
430
+ this._logDebug("Could not create onUserDisconnected response");
431
+ throw error;
432
+ }
433
+ }
434
+ default: throw new HttpError("Unknown event", 400);
435
+ }
436
+ } catch (error) {
437
+ const message = error instanceof Error ? error.message : "Unexpected error";
438
+ const status = error instanceof HttpError ? error.status : 500;
439
+ this._logDebug("Uncaught error: ", message);
440
+ return createErrorResponse(c, { message }, status);
441
+ }
442
+ });
443
+ async _getContext() {
444
+ return typeof this._context === "function" ? await Promise.resolve(this._context(this._roomContext)) : await Promise.resolve(this._context);
445
+ }
446
+ _logDebug(...args) {
447
+ if (!this._debug) return;
448
+ console.log("[PLATFORM PLUV]", ...args);
449
+ }
453
450
  };
454
451
 
455
- // src/platformPluv.ts
456
- var platformPluv = (config) => {
457
- const { authorize, context, crdt, debug } = config;
458
- return {
459
- authorize,
460
- context,
461
- crdt,
462
- debug,
463
- platform: () => new PluvPlatform(config)
464
- };
465
- };
466
- export {
467
- PluvPlatform,
468
- platformPluv
452
+ //#endregion
453
+ //#region src/platformPluv.ts
454
+ const platformPluv = (config) => {
455
+ const { authorize, context, crdt, debug } = config;
456
+ return {
457
+ authorize,
458
+ context,
459
+ crdt,
460
+ debug,
461
+ platform: () => new PluvPlatform(config)
462
+ };
469
463
  };
464
+
465
+ //#endregion
466
+ export { PluvPlatform, platformPluv };
467
+ //# sourceMappingURL=index.mjs.map