@pluv/platform-pluv 4.0.0 → 4.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/platform-pluv@4.0.0 build /home/runner/work/pluv/pluv/packages/platform-pluv
2
+ > @pluv/platform-pluv@4.0.1 build /home/runner/work/pluv/pluv/packages/platform-pluv
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.20.0-beta.4 powered by rolldown v1.0.0-beta.60
@@ -8,11 +8,11 @@
8
8
  ℹ target: esnext
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
- ℹ dist/index.mjs 13.63 kB │ gzip: 3.43 kB
12
- ℹ dist/index.mjs.map 33.64 kB │ gzip: 7.39 kB
11
+ [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
12
+ ℹ dist/index.mjs 13.74 kB │ gzip: 3.46 kB
13
+ ℹ dist/index.mjs.map 33.93 kB │ gzip: 7.44 kB
13
14
  ℹ dist/index.d.mts.map  1.86 kB │ gzip: 0.69 kB
14
15
  ℹ dist/index.d.mts  4.21 kB │ gzip: 1.29 kB
15
- ℹ 4 files, total: 53.34 kB
16
- [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
16
+ ℹ 4 files, total: 53.74 kB
17
17
 
18
- ✔ Build complete in 4200ms
18
+ ✔ Build complete in 3774ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @pluv/platform-pluv
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 982e068: Added http header for webhook events (`x-pluv-event`).
8
+ - @pluv/crdt@4.0.1
9
+ - @pluv/io@4.0.1
10
+ - @pluv/types@4.0.1
11
+
3
12
  ## 4.0.0
4
13
 
5
14
  ### Major Changes
package/dist/index.mjs CHANGED
@@ -90,11 +90,11 @@ const ZodEventResponse = z.discriminatedUnion("event", [
90
90
 
91
91
  //#endregion
92
92
  //#region src/shared/createErrorResponse.ts
93
- const createErrorResponse = (c, error, status) => {
93
+ const createErrorResponse = (c, error, status, event = "unknown") => {
94
94
  return c.json({
95
95
  ok: false,
96
96
  error: { message: error.message }
97
- }, status);
97
+ }, status, { "x-pluv-event": event });
98
98
  };
99
99
 
100
100
  //#endregion
@@ -131,7 +131,7 @@ const createSuccessResponse = (c, data, status = 200) => {
131
131
  return c.json({
132
132
  ok: true,
133
133
  data: ZodEventResponse.parse(data)
134
- }, status);
134
+ }, status, { "x-pluv-event": data.event });
135
135
  };
136
136
 
137
137
  //#endregion
@@ -431,7 +431,7 @@ var PluvPlatform = class extends AbstractPlatform {
431
431
  throw error;
432
432
  }
433
433
  }
434
- default: throw new HttpError("Unknown event", 400);
434
+ default: return createErrorResponse(c, { message: "Unknown event" }, 400, event);
435
435
  }
436
436
  } catch (error) {
437
437
  const message = error instanceof Error ? error.message : "Unexpected error";
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/schemas.ts","../src/shared/createErrorResponse.ts","../src/shared/getCrypto.ts","../src/shared/createHmac.ts","../src/shared/createSuccessResponse.ts","../src/shared/HttpError.ts","../src/shared/timingSafeEqual.ts","../src/shared/verifyWebhook.ts","../src/PluvPlatform.ts","../src/platformPluv.ts"],"sourcesContent":["export const SIGNATURE_ALGORITHM = \"sha256\";\nexport const SIGNATURE_HEADER = \"x-pluv-signature-256\";\n","import { z } from \"zod\";\n\nexport const ZodEventKind = z.union([\n z.literal(\"initial-storage\"),\n z.literal(\"room-destroyed\"),\n z.literal(\"storage-destroyed\"),\n z.literal(\"user-connected\"),\n z.literal(\"user-disconnected\"),\n]);\n\nexport const ZodEventInitialStorage = z.object({\n event: z.literal(\"initial-storage\"),\n data: z.object({\n room: z.string(),\n }),\n});\n\nexport const ZodEventRoomDestroyed = z.object({\n event: z.literal(\"room-destroyed\"),\n data: z.object({\n room: z.string(),\n }),\n});\n\nexport const ZodEventStorageDestroyed = z.object({\n event: z.literal(\"storage-destroyed\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n }),\n});\n\nexport const ZodEventUserConnected = z.object({\n event: z.literal(\"user-connected\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n user: z\n .object({\n id: z.string(),\n })\n .passthrough(),\n }),\n});\n\nexport const ZodEventUserDisconnected = z.object({\n event: z.literal(\"user-disconnected\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n user: z\n .object({\n id: z.string(),\n })\n .passthrough(),\n }),\n});\n\nexport const ZodEvent = z.discriminatedUnion(\"event\", [\n ZodEventInitialStorage,\n ZodEventRoomDestroyed,\n ZodEventStorageDestroyed,\n ZodEventUserConnected,\n ZodEventUserDisconnected,\n]);\n\nexport const ZodInitialStorageResponse = z.object({\n event: z.literal(\"initial-storage\"),\n room: z.string(),\n storage: z.string().nullable(),\n});\n\nexport const ZodRoomDestroyedResponse = z.object({\n event: z.literal(\"room-destroyed\"),\n room: z.string(),\n});\n\nexport const ZodStorageDestroyedResponse = z.object({\n event: z.literal(\"storage-destroyed\"),\n room: z.string(),\n});\n\nexport const ZodUserConnectedResponse = z.object({\n event: z.literal(\"user-connected\"),\n room: z.string(),\n});\n\nexport const ZodUserDisconnectedResponse = z.object({\n event: z.literal(\"user-disconnected\"),\n room: z.string(),\n});\n\nexport const ZodEventResponse = z.discriminatedUnion(\"event\", [\n ZodInitialStorageResponse,\n ZodRoomDestroyedResponse,\n ZodStorageDestroyedResponse,\n ZodUserConnectedResponse,\n ZodUserDisconnectedResponse,\n]);\n","import type { Context } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\n\nexport const createErrorResponse = <TStatus extends ContentfulStatusCode>(\n c: Context<BlankEnv, \"/\", BlankInput>,\n error: { message: string },\n status: TStatus,\n) => {\n return c.json({ ok: false, error: { message: error.message } }, status);\n};\n","import type { webcrypto } from \"crypto\";\n\nexport const getCrypto = (): webcrypto.Crypto => {\n if (typeof crypto !== \"undefined\") {\n // In a browser or Web Worker (including Cloudflare Workers)\n return crypto;\n }\n\n if (typeof require === \"function\") {\n // In Node.js\n // Node 15+ supports `crypto.webcrypto`\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require(\"node:crypto\").webcrypto as webcrypto.Crypto;\n }\n\n throw new Error(\"Missing crypto module\");\n};\n","import type { webcrypto } from \"crypto\";\nimport { getCrypto } from \"./getCrypto\";\n\nexport interface CreateHmacParams {\n payload: string;\n secret: string;\n}\n\nexport type CreateHmacResult = {\n algorithm: \"sha256\";\n hmac: string;\n};\n\nexport const createHmac = async (params: CreateHmacParams): Promise<CreateHmacResult> => {\n const { payload, secret } = params;\n\n if (!payload || !secret) throw new Error(\"Secret and payload are required to sign payload\");\n\n const encoder = new TextEncoder();\n const keyBytes = encoder.encode(secret);\n\n const crypto = getCrypto();\n\n const algorithm: webcrypto.HmacImportParams = { name: \"HMAC\", hash: { name: \"SHA-256\" } };\n const extractable = false;\n\n const key = await crypto.subtle.importKey(\"raw\", keyBytes, algorithm, extractable, [\n \"sign\",\n \"verify\",\n ]);\n const payloadBytes = encoder.encode(payload);\n\n const signature = await crypto.subtle.sign(\"HMAC\", key, payloadBytes);\n\n // Convert the signature to a hex string\n const hmac = Array.from(new Uint8Array(signature))\n .map((b) => (\"0\" + b.toString(16)).slice(-2))\n .join(\"\");\n\n return { algorithm: \"sha256\", hmac };\n};\n","import type { Context } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport { ZodEventResponse } from \"../schemas\";\nimport type { EventResponse } from \"../types\";\n\nexport interface CreateSuccessResponseParams<TStatus extends ContentfulStatusCode> {\n data: EventResponse;\n status?: TStatus;\n}\n\nexport const createSuccessResponse = <TStatus extends ContentfulStatusCode = 200>(\n c: Context<BlankEnv, \"/\", BlankInput>,\n data: EventResponse,\n status: TStatus = 200 as TStatus,\n) => {\n return c.json(\n {\n ok: true,\n data: ZodEventResponse.parse(data),\n },\n status,\n );\n};\n","import type { ContentfulStatusCode } from \"hono/utils/http-status\";\n\nexport class HttpError extends Error {\n readonly status: ContentfulStatusCode;\n\n constructor(message: string, status: ContentfulStatusCode) {\n super(message);\n\n this.status = status;\n }\n}\n","export const timingSafeEqual = (a: Uint8Array, b: Uint8Array): boolean => {\n if (a.length !== b.length) return false; // Lengths are different\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a[i] ^ b[i]; // XOR each byte\n }\n\n return result === 0; // If all bytes are equal, result will be 0\n};\n","import { createHmac } from \"./createHmac\";\nimport { timingSafeEqual } from \"./timingSafeEqual\";\n\nexport interface VerifyWebhookParams {\n payload: string;\n secret: string;\n signature: string;\n}\n\nexport const verifyWebhook = async (params: VerifyWebhookParams): Promise<boolean> => {\n const { payload, secret, signature } = params;\n\n if (!secret || !payload || !signature) {\n throw new Error(\"Secret, payload and signature are required to verify payload\");\n }\n\n const { hmac } = await createHmac({ payload, secret });\n\n if (hmac.length !== signature.length) return false;\n\n const encoder = new TextEncoder();\n const verificationBytes = encoder.encode(hmac);\n const signatureBytes = encoder.encode(signature);\n\n if (verificationBytes.length !== signatureBytes.length) return false;\n\n return timingSafeEqual(verificationBytes, signatureBytes);\n};\n","import type {\n AbstractPlatformConfig,\n AbstractWebSocket,\n BaseUser,\n ConvertWebSocketConfig,\n GetInitialStorageFn,\n JWTEncodeParams,\n PluvContext,\n WebSocketSerializedState,\n} from \"@pluv/io\";\nimport { AbstractPlatform } from \"@pluv/io\";\nimport type { MaybePromise } from \"@pluv/types\";\nimport stringify from \"fast-json-stable-stringify\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport { SIGNATURE_ALGORITHM, SIGNATURE_HEADER } from \"./constants\";\nimport { ZodEvent } from \"./schemas\";\nimport { createErrorResponse, createSuccessResponse, HttpError, verifyWebhook } from \"./shared\";\nimport type { PluvIOEndpoints, PluvIOListeners } from \"./types\";\n\nexport type PublicKey = string | (() => MaybePromise<string>);\nexport type SecretKey = string | (() => MaybePromise<string>);\nexport type WebhookSecret = string | (() => MaybePromise<string>);\n\nexport interface PluvPlatformConfig<TContext extends Record<string, any> = {}> {\n /**\n * @ignore\n * @readonly\n * @deprecated Internal use only. Changes to this will never be marked as breaking.\n */\n _defs?: {\n debug?: boolean;\n endpoints?: PluvIOEndpoints | (() => MaybePromise<PluvIOEndpoints>);\n };\n basePath: string;\n context?: PluvContext<any, TContext>;\n publicKey: PublicKey;\n secretKey: SecretKey;\n webhookSecret?: WebhookSecret;\n}\n\nexport class PluvPlatform<\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n> extends AbstractPlatform<\n any,\n {},\n {},\n {\n authorize: {\n secret: false;\n };\n handleMode: \"fetch\";\n registrationMode: \"attached\";\n requireAuth: true;\n listeners: {\n onRoomDestroyed: true;\n onRoomMessage: false;\n onStorageDestroyed: true;\n onStorageUpdated: false;\n onUserConnected: true;\n onUserDisconnected: true;\n };\n router: false;\n }\n> {\n public readonly id = Math.random().toString();\n public readonly _config = {\n authorize: {\n secret: false as const,\n },\n handleMode: \"fetch\" as const,\n registrationMode: \"attached\" as const,\n requireAuth: true as const,\n listeners: {\n onRoomDestroyed: true as const,\n onRoomMessage: false as const,\n onStorageDestroyed: true as const,\n onStorageUpdated: false as const,\n onUserConnected: true as const,\n onUserDisconnected: true as const,\n },\n router: false as const,\n };\n public readonly _name = \"platformPluv\";\n\n private readonly _app: Hono;\n private readonly _basePath: string;\n private readonly _context: PluvContext<this, TContext>;\n private readonly _debug: boolean;\n private readonly _endpoints: PluvIOEndpoints | (() => MaybePromise<PluvIOEndpoints>);\n private _getInitialStorage?: GetInitialStorageFn<{}>;\n private _listeners?: PluvIOListeners;\n private readonly _publicKey: PublicKey;\n private readonly _secretKey: SecretKey;\n private readonly _webhookSecret?: WebhookSecret;\n\n public _createToken = async (params: JWTEncodeParams<any, any>): Promise<string> => {\n const parsed = params.authorize.user.parse(params.user);\n\n const [endpoints, publicKey, secretKey] = await Promise.all([\n typeof this._endpoints === \"object\" ? this._endpoints : this._endpoints(),\n typeof this._publicKey === \"string\" ? this._publicKey : this._publicKey(),\n typeof this._secretKey === \"string\" ? this._secretKey : this._secretKey(),\n ]);\n\n this._logDebug({ endpoints, publicKey, secretKey });\n\n const res = await fetch(endpoints.createToken, {\n headers: { \"content-type\": \"application/json\" },\n method: \"post\",\n body: JSON.stringify({\n maxAge: params.maxAge ?? null,\n publicKey,\n room: params.room,\n secretKey,\n user: parsed,\n }),\n }).catch((error) => {\n this._logDebug(error);\n\n return null;\n });\n\n this._logDebug({ response: { status: res?.status ?? null } });\n\n if (!res || !res.ok || res.status !== 200) {\n throw new Error(\"Authorization failed\");\n }\n\n const token = await res.text().catch(() => null);\n\n this._logDebug({ token });\n\n if (typeof token !== \"string\") throw new Error(\"Authorization failed\");\n\n return token;\n };\n\n constructor(params: PluvPlatformConfig) {\n super();\n\n const { _defs, basePath, context, publicKey, secretKey, webhookSecret } = params;\n\n this._basePath = basePath;\n this._context = (context ?? {}) as TContext;\n this._debug = _defs?.debug ?? false;\n this._endpoints = _defs?.endpoints ?? {\n createToken: \"https://rooms.pluv.io/api/room/token\",\n };\n this._publicKey = publicKey;\n this._secretKey = secretKey;\n this._webhookSecret = webhookSecret;\n\n this._app = new Hono().basePath(this._basePath).route(\"/\", this._webhooksRouter);\n this._fetch = this._app.fetch as (req: any) => Promise<any>;\n }\n\n public acceptWebSocket(webSocket: AbstractWebSocket): Promise<void> {\n throw new Error(\"Not implemented\");\n }\n\n public convertWebSocket(webSocket: any, config: ConvertWebSocketConfig): AbstractWebSocket {\n throw new Error(\"Not implemented\");\n }\n\n public getLastPing(webSocket: AbstractWebSocket): number | null {\n throw new Error(\"Not implemented\");\n }\n\n public getSerializedState(webSocket: any): WebSocketSerializedState | null {\n throw new Error(\"Not implemented\");\n }\n\n public getSessionId(webSocket: any): string | null {\n throw new Error(\"Not implemented\");\n }\n\n public getWebSockets(): readonly any[] {\n throw new Error(\"Not implemented\");\n }\n\n public initialize(config: AbstractPlatformConfig<{}>): this {\n throw new Error(\"Not implemented\");\n }\n\n public parseData(data: string | ArrayBuffer): Record<string, any> {\n throw new Error(\"Not implemented\");\n }\n\n public randomUUID(): string {\n throw new Error(\"Not implemented\");\n }\n\n public setSerializedState(\n webSocket: AbstractWebSocket,\n state: WebSocketSerializedState,\n ): WebSocketSerializedState {\n throw new Error(\"Not implemented\");\n }\n\n public validateConfig(config: any): void {\n this._logDebug(\"validating config with properties:\", Object.keys(config ?? {}));\n\n if (!config.authorize) {\n this._logDebug(\"Config `authorize` must be provided to `platformPluv`\");\n throw new Error(\"Config `authorize` must be provided to `platformPluv`\");\n }\n if (!!config.onRoomMessage) {\n this._logDebug(\"Config `onRoomMessage` is not supported on `platformPluv`\");\n throw new Error(\"Config `onRoomMessage` is not supported on `platformPluv`\");\n }\n if (!!config.onStorageUpdated) {\n this._logDebug(\"Config `onStorageUpdated` is not supported on `platformPluv`\");\n throw new Error(\"Config `onStorageUpdated` is not supported on `platformPluv`\");\n }\n\n this._getInitialStorage = config.getInitialStorage;\n this._listeners = {\n onRoomDestroyed: (event) => config.onRoomDestroyed?.(event),\n onStorageDestroyed: (event) => config.onStorageDestroyed?.(event),\n onUserConnected: (event) => config.onUserConnected?.(event),\n onUserDisconnected: (event) => config.onUserDisconnected?.(event),\n };\n }\n\n private _webhooksRouter = new Hono()\n .basePath(\"/\")\n .post(\"/\", async (c: Context<BlankEnv, \"/\", BlankInput>) => {\n const [algorithm, signature] = c.req.header(SIGNATURE_HEADER)?.split(\"=\") ?? [];\n\n try {\n if (!this._webhookSecret) {\n this._logDebug(\"Missing webhook secret\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n if (algorithm !== SIGNATURE_ALGORITHM) {\n this._logDebug(\n `Verification algorithm is not ${SIGNATURE_ALGORITHM}. Found: `,\n algorithm,\n );\n throw new HttpError(\"Unauthorized\", 401);\n }\n if (!signature) {\n this._logDebug(\"Missing webhook signature\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n\n const [payload, webhookSecret] = await Promise.all([\n c.req.json(),\n typeof this._webhookSecret === \"string\"\n ? this._webhookSecret\n : await this._webhookSecret(),\n ]).catch((error) => {\n this._logDebug(\n \"Could not derive webhook secret: \",\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n throw error;\n });\n\n const verified = await verifyWebhook({\n payload: stringify(payload),\n signature,\n secret: webhookSecret,\n }).catch((error) => {\n this._logDebug(\n \"Error while verifying webhook: \",\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n\n return false;\n });\n\n if (!verified) {\n this._logDebug(\"Failed to verify webhook\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n\n const parsed = ZodEvent.safeParse(payload);\n\n if (!parsed.success) {\n this._logDebug(\n \"Failed to validate event payload:\",\n JSON.stringify(parsed.data ?? {}, null, 4),\n );\n throw new HttpError(\"Invalid request\", 400);\n }\n\n const { event, data } = parsed.data;\n const context = await this._getContext();\n\n switch (event) {\n case \"initial-storage\": {\n const room = data.room;\n const storage =\n typeof room === \"string\"\n ? ((await this._getInitialStorage?.({ context, room })) ?? null)\n : null;\n\n try {\n return createSuccessResponse(c, { event, room, storage });\n } catch (error) {\n this._logDebug(\"Could not create getInitialStorage response\");\n throw error;\n }\n }\n case \"room-destroyed\": {\n const room = data.room;\n\n await Promise.resolve(\n this._listeners?.onRoomDestroyed({\n context,\n platform: this,\n room,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onRoomDestroyed response\");\n throw error;\n }\n }\n case \"storage-destroyed\": {\n const room = data.room;\n const encodedState = data.storage;\n\n await Promise.resolve(\n this._listeners?.onStorageDestroyed({\n context,\n encodedState,\n platform: this,\n room,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onStorageDestroyed response\");\n throw error;\n }\n }\n case \"user-connected\": {\n const room = data.room;\n const encodedState = data.storage;\n const user = data.user as any;\n\n await Promise.resolve(\n this._listeners?.onUserConnected({\n context,\n encodedState,\n platform: this,\n room,\n user,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onUserConnected response\");\n throw error;\n }\n }\n case \"user-disconnected\": {\n const room = data.room;\n const encodedState = data.storage;\n const user = data.user as any;\n\n await Promise.resolve(\n this._listeners?.onUserDisconnected({\n context,\n encodedState,\n platform: this,\n room,\n user,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onUserDisconnected response\");\n throw error;\n }\n }\n default: {\n throw new HttpError(\"Unknown event\", 400);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unexpected error\";\n const status = error instanceof HttpError ? error.status : 500;\n\n this._logDebug(\"Uncaught error: \", message);\n\n return createErrorResponse(c, { message }, status);\n }\n });\n\n private async _getContext(): Promise<TContext> {\n return typeof this._context === \"function\"\n ? await Promise.resolve(this._context(this._roomContext as any))\n : await Promise.resolve(this._context);\n }\n\n private _logDebug(...args: any[]): void {\n if (!this._debug) return;\n\n console.log(\"[PLATFORM PLUV]\", ...args);\n }\n}\n","import type { CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport type { CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize } from \"@pluv/io\";\nimport type { BaseUser, Id } from \"@pluv/types\";\nimport type { PluvPlatformConfig } from \"./PluvPlatform\";\nimport { PluvPlatform } from \"./PluvPlatform\";\n\nexport type PlatformPluvCreateIOParams<\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n> = Id<\n PluvPlatformConfig &\n Omit<\n CreateIOParams<PluvPlatform, TContext, TUser, TCrdt>,\n \"authorize\" | \"context\" | \"limits\" | \"platform\"\n > & {\n authorize: PluvIOAuthorize<PluvPlatform, TUser, InferInitContextType<PluvPlatform>>;\n context?: PluvContext<PluvPlatform, TContext>;\n }\n>;\n\nexport const platformPluv = <\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n>(\n config: PlatformPluvCreateIOParams<TContext, TUser, TCrdt>,\n): CreateIOParams<PluvPlatform<TContext, TUser>, TContext, TUser, TCrdt> => {\n const { authorize, context, crdt, debug } = config;\n\n return {\n authorize,\n context,\n crdt,\n debug,\n platform: () => new PluvPlatform<TContext, TUser>(config),\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/schemas.ts","../src/shared/createErrorResponse.ts","../src/shared/getCrypto.ts","../src/shared/createHmac.ts","../src/shared/createSuccessResponse.ts","../src/shared/HttpError.ts","../src/shared/timingSafeEqual.ts","../src/shared/verifyWebhook.ts","../src/PluvPlatform.ts","../src/platformPluv.ts"],"sourcesContent":["export const SIGNATURE_ALGORITHM = \"sha256\";\nexport const SIGNATURE_HEADER = \"x-pluv-signature-256\";\n","import { z } from \"zod\";\n\nexport const ZodEventKind = z.union([\n z.literal(\"initial-storage\"),\n z.literal(\"room-destroyed\"),\n z.literal(\"storage-destroyed\"),\n z.literal(\"user-connected\"),\n z.literal(\"user-disconnected\"),\n]);\n\nexport const ZodEventInitialStorage = z.object({\n event: z.literal(\"initial-storage\"),\n data: z.object({\n room: z.string(),\n }),\n});\n\nexport const ZodEventRoomDestroyed = z.object({\n event: z.literal(\"room-destroyed\"),\n data: z.object({\n room: z.string(),\n }),\n});\n\nexport const ZodEventStorageDestroyed = z.object({\n event: z.literal(\"storage-destroyed\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n }),\n});\n\nexport const ZodEventUserConnected = z.object({\n event: z.literal(\"user-connected\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n user: z\n .object({\n id: z.string(),\n })\n .passthrough(),\n }),\n});\n\nexport const ZodEventUserDisconnected = z.object({\n event: z.literal(\"user-disconnected\"),\n data: z.object({\n room: z.string(),\n storage: z.string().nullable(),\n user: z\n .object({\n id: z.string(),\n })\n .passthrough(),\n }),\n});\n\nexport const ZodEvent = z.discriminatedUnion(\"event\", [\n ZodEventInitialStorage,\n ZodEventRoomDestroyed,\n ZodEventStorageDestroyed,\n ZodEventUserConnected,\n ZodEventUserDisconnected,\n]);\n\nexport const ZodInitialStorageResponse = z.object({\n event: z.literal(\"initial-storage\"),\n room: z.string(),\n storage: z.string().nullable(),\n});\n\nexport const ZodRoomDestroyedResponse = z.object({\n event: z.literal(\"room-destroyed\"),\n room: z.string(),\n});\n\nexport const ZodStorageDestroyedResponse = z.object({\n event: z.literal(\"storage-destroyed\"),\n room: z.string(),\n});\n\nexport const ZodUserConnectedResponse = z.object({\n event: z.literal(\"user-connected\"),\n room: z.string(),\n});\n\nexport const ZodUserDisconnectedResponse = z.object({\n event: z.literal(\"user-disconnected\"),\n room: z.string(),\n});\n\nexport const ZodEventResponse = z.discriminatedUnion(\"event\", [\n ZodInitialStorageResponse,\n ZodRoomDestroyedResponse,\n ZodStorageDestroyedResponse,\n ZodUserConnectedResponse,\n ZodUserDisconnectedResponse,\n]);\n","import type { Context } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\n\nexport const createErrorResponse = <TStatus extends ContentfulStatusCode>(\n c: Context<BlankEnv, \"/\", BlankInput>,\n error: { message: string },\n status: TStatus,\n event: string = \"unknown\",\n) => {\n return c.json(\n {\n ok: false,\n error: { message: error.message },\n },\n status,\n { \"x-pluv-event\": event },\n );\n};\n","import type { webcrypto } from \"crypto\";\n\nexport const getCrypto = (): webcrypto.Crypto => {\n if (typeof crypto !== \"undefined\") {\n // In a browser or Web Worker (including Cloudflare Workers)\n return crypto;\n }\n\n if (typeof require === \"function\") {\n // In Node.js\n // Node 15+ supports `crypto.webcrypto`\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require(\"node:crypto\").webcrypto as webcrypto.Crypto;\n }\n\n throw new Error(\"Missing crypto module\");\n};\n","import type { webcrypto } from \"crypto\";\nimport { getCrypto } from \"./getCrypto\";\n\nexport interface CreateHmacParams {\n payload: string;\n secret: string;\n}\n\nexport type CreateHmacResult = {\n algorithm: \"sha256\";\n hmac: string;\n};\n\nexport const createHmac = async (params: CreateHmacParams): Promise<CreateHmacResult> => {\n const { payload, secret } = params;\n\n if (!payload || !secret) throw new Error(\"Secret and payload are required to sign payload\");\n\n const encoder = new TextEncoder();\n const keyBytes = encoder.encode(secret);\n\n const crypto = getCrypto();\n\n const algorithm: webcrypto.HmacImportParams = { name: \"HMAC\", hash: { name: \"SHA-256\" } };\n const extractable = false;\n\n const key = await crypto.subtle.importKey(\"raw\", keyBytes, algorithm, extractable, [\n \"sign\",\n \"verify\",\n ]);\n const payloadBytes = encoder.encode(payload);\n\n const signature = await crypto.subtle.sign(\"HMAC\", key, payloadBytes);\n\n // Convert the signature to a hex string\n const hmac = Array.from(new Uint8Array(signature))\n .map((b) => (\"0\" + b.toString(16)).slice(-2))\n .join(\"\");\n\n return { algorithm: \"sha256\", hmac };\n};\n","import type { Context } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport { ZodEventResponse } from \"../schemas\";\nimport type { EventResponse } from \"../types\";\n\nexport interface CreateSuccessResponseParams<TStatus extends ContentfulStatusCode> {\n data: EventResponse;\n status?: TStatus;\n}\n\nexport const createSuccessResponse = <TStatus extends ContentfulStatusCode = 200>(\n c: Context<BlankEnv, \"/\", BlankInput>,\n data: EventResponse,\n status: TStatus = 200 as TStatus,\n) => {\n return c.json(\n {\n ok: true,\n data: ZodEventResponse.parse(data),\n },\n status,\n { \"x-pluv-event\": data.event },\n );\n};\n","import type { ContentfulStatusCode } from \"hono/utils/http-status\";\n\nexport class HttpError extends Error {\n readonly status: ContentfulStatusCode;\n\n constructor(message: string, status: ContentfulStatusCode) {\n super(message);\n\n this.status = status;\n }\n}\n","export const timingSafeEqual = (a: Uint8Array, b: Uint8Array): boolean => {\n if (a.length !== b.length) return false; // Lengths are different\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a[i] ^ b[i]; // XOR each byte\n }\n\n return result === 0; // If all bytes are equal, result will be 0\n};\n","import { createHmac } from \"./createHmac\";\nimport { timingSafeEqual } from \"./timingSafeEqual\";\n\nexport interface VerifyWebhookParams {\n payload: string;\n secret: string;\n signature: string;\n}\n\nexport const verifyWebhook = async (params: VerifyWebhookParams): Promise<boolean> => {\n const { payload, secret, signature } = params;\n\n if (!secret || !payload || !signature) {\n throw new Error(\"Secret, payload and signature are required to verify payload\");\n }\n\n const { hmac } = await createHmac({ payload, secret });\n\n if (hmac.length !== signature.length) return false;\n\n const encoder = new TextEncoder();\n const verificationBytes = encoder.encode(hmac);\n const signatureBytes = encoder.encode(signature);\n\n if (verificationBytes.length !== signatureBytes.length) return false;\n\n return timingSafeEqual(verificationBytes, signatureBytes);\n};\n","import type {\n AbstractPlatformConfig,\n AbstractWebSocket,\n BaseUser,\n ConvertWebSocketConfig,\n GetInitialStorageFn,\n JWTEncodeParams,\n PluvContext,\n WebSocketSerializedState,\n} from \"@pluv/io\";\nimport { AbstractPlatform } from \"@pluv/io\";\nimport type { MaybePromise } from \"@pluv/types\";\nimport stringify from \"fast-json-stable-stringify\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport type { BlankEnv, BlankInput } from \"hono/types\";\nimport { SIGNATURE_ALGORITHM, SIGNATURE_HEADER } from \"./constants\";\nimport { ZodEvent } from \"./schemas\";\nimport { createErrorResponse, createSuccessResponse, HttpError, verifyWebhook } from \"./shared\";\nimport type { PluvIOEndpoints, PluvIOListeners } from \"./types\";\n\nexport type PublicKey = string | (() => MaybePromise<string>);\nexport type SecretKey = string | (() => MaybePromise<string>);\nexport type WebhookSecret = string | (() => MaybePromise<string>);\n\nexport interface PluvPlatformConfig<TContext extends Record<string, any> = {}> {\n /**\n * @ignore\n * @readonly\n * @deprecated Internal use only. Changes to this will never be marked as breaking.\n */\n _defs?: {\n debug?: boolean;\n endpoints?: PluvIOEndpoints | (() => MaybePromise<PluvIOEndpoints>);\n };\n basePath: string;\n context?: PluvContext<any, TContext>;\n publicKey: PublicKey;\n secretKey: SecretKey;\n webhookSecret?: WebhookSecret;\n}\n\nexport class PluvPlatform<\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n> extends AbstractPlatform<\n any,\n {},\n {},\n {\n authorize: {\n secret: false;\n };\n handleMode: \"fetch\";\n registrationMode: \"attached\";\n requireAuth: true;\n listeners: {\n onRoomDestroyed: true;\n onRoomMessage: false;\n onStorageDestroyed: true;\n onStorageUpdated: false;\n onUserConnected: true;\n onUserDisconnected: true;\n };\n router: false;\n }\n> {\n public readonly id = Math.random().toString();\n public readonly _config = {\n authorize: {\n secret: false as const,\n },\n handleMode: \"fetch\" as const,\n registrationMode: \"attached\" as const,\n requireAuth: true as const,\n listeners: {\n onRoomDestroyed: true as const,\n onRoomMessage: false as const,\n onStorageDestroyed: true as const,\n onStorageUpdated: false as const,\n onUserConnected: true as const,\n onUserDisconnected: true as const,\n },\n router: false as const,\n };\n public readonly _name = \"platformPluv\";\n\n private readonly _app: Hono;\n private readonly _basePath: string;\n private readonly _context: PluvContext<this, TContext>;\n private readonly _debug: boolean;\n private readonly _endpoints: PluvIOEndpoints | (() => MaybePromise<PluvIOEndpoints>);\n private _getInitialStorage?: GetInitialStorageFn<{}>;\n private _listeners?: PluvIOListeners;\n private readonly _publicKey: PublicKey;\n private readonly _secretKey: SecretKey;\n private readonly _webhookSecret?: WebhookSecret;\n\n public _createToken = async (params: JWTEncodeParams<any, any>): Promise<string> => {\n const parsed = params.authorize.user.parse(params.user);\n\n const [endpoints, publicKey, secretKey] = await Promise.all([\n typeof this._endpoints === \"object\" ? this._endpoints : this._endpoints(),\n typeof this._publicKey === \"string\" ? this._publicKey : this._publicKey(),\n typeof this._secretKey === \"string\" ? this._secretKey : this._secretKey(),\n ]);\n\n this._logDebug({ endpoints, publicKey, secretKey });\n\n const res = await fetch(endpoints.createToken, {\n headers: { \"content-type\": \"application/json\" },\n method: \"post\",\n body: JSON.stringify({\n maxAge: params.maxAge ?? null,\n publicKey,\n room: params.room,\n secretKey,\n user: parsed,\n }),\n }).catch((error) => {\n this._logDebug(error);\n\n return null;\n });\n\n this._logDebug({ response: { status: res?.status ?? null } });\n\n if (!res || !res.ok || res.status !== 200) {\n throw new Error(\"Authorization failed\");\n }\n\n const token = await res.text().catch(() => null);\n\n this._logDebug({ token });\n\n if (typeof token !== \"string\") throw new Error(\"Authorization failed\");\n\n return token;\n };\n\n constructor(params: PluvPlatformConfig) {\n super();\n\n const { _defs, basePath, context, publicKey, secretKey, webhookSecret } = params;\n\n this._basePath = basePath;\n this._context = (context ?? {}) as TContext;\n this._debug = _defs?.debug ?? false;\n this._endpoints = _defs?.endpoints ?? {\n createToken: \"https://rooms.pluv.io/api/room/token\",\n };\n this._publicKey = publicKey;\n this._secretKey = secretKey;\n this._webhookSecret = webhookSecret;\n\n this._app = new Hono().basePath(this._basePath).route(\"/\", this._webhooksRouter);\n this._fetch = this._app.fetch as (req: any) => Promise<any>;\n }\n\n public acceptWebSocket(webSocket: AbstractWebSocket): Promise<void> {\n throw new Error(\"Not implemented\");\n }\n\n public convertWebSocket(webSocket: any, config: ConvertWebSocketConfig): AbstractWebSocket {\n throw new Error(\"Not implemented\");\n }\n\n public getLastPing(webSocket: AbstractWebSocket): number | null {\n throw new Error(\"Not implemented\");\n }\n\n public getSerializedState(webSocket: any): WebSocketSerializedState | null {\n throw new Error(\"Not implemented\");\n }\n\n public getSessionId(webSocket: any): string | null {\n throw new Error(\"Not implemented\");\n }\n\n public getWebSockets(): readonly any[] {\n throw new Error(\"Not implemented\");\n }\n\n public initialize(config: AbstractPlatformConfig<{}>): this {\n throw new Error(\"Not implemented\");\n }\n\n public parseData(data: string | ArrayBuffer): Record<string, any> {\n throw new Error(\"Not implemented\");\n }\n\n public randomUUID(): string {\n throw new Error(\"Not implemented\");\n }\n\n public setSerializedState(\n webSocket: AbstractWebSocket,\n state: WebSocketSerializedState,\n ): WebSocketSerializedState {\n throw new Error(\"Not implemented\");\n }\n\n public validateConfig(config: any): void {\n this._logDebug(\"validating config with properties:\", Object.keys(config ?? {}));\n\n if (!config.authorize) {\n this._logDebug(\"Config `authorize` must be provided to `platformPluv`\");\n throw new Error(\"Config `authorize` must be provided to `platformPluv`\");\n }\n if (!!config.onRoomMessage) {\n this._logDebug(\"Config `onRoomMessage` is not supported on `platformPluv`\");\n throw new Error(\"Config `onRoomMessage` is not supported on `platformPluv`\");\n }\n if (!!config.onStorageUpdated) {\n this._logDebug(\"Config `onStorageUpdated` is not supported on `platformPluv`\");\n throw new Error(\"Config `onStorageUpdated` is not supported on `platformPluv`\");\n }\n\n this._getInitialStorage = config.getInitialStorage;\n this._listeners = {\n onRoomDestroyed: (event) => config.onRoomDestroyed?.(event),\n onStorageDestroyed: (event) => config.onStorageDestroyed?.(event),\n onUserConnected: (event) => config.onUserConnected?.(event),\n onUserDisconnected: (event) => config.onUserDisconnected?.(event),\n };\n }\n\n private _webhooksRouter = new Hono()\n .basePath(\"/\")\n .post(\"/\", async (c: Context<BlankEnv, \"/\", BlankInput>) => {\n const [algorithm, signature] = c.req.header(SIGNATURE_HEADER)?.split(\"=\") ?? [];\n\n try {\n if (!this._webhookSecret) {\n this._logDebug(\"Missing webhook secret\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n if (algorithm !== SIGNATURE_ALGORITHM) {\n this._logDebug(\n `Verification algorithm is not ${SIGNATURE_ALGORITHM}. Found: `,\n algorithm,\n );\n throw new HttpError(\"Unauthorized\", 401);\n }\n if (!signature) {\n this._logDebug(\"Missing webhook signature\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n\n const [payload, webhookSecret] = await Promise.all([\n c.req.json(),\n typeof this._webhookSecret === \"string\"\n ? this._webhookSecret\n : await this._webhookSecret(),\n ]).catch((error) => {\n this._logDebug(\n \"Could not derive webhook secret: \",\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n throw error;\n });\n\n const verified = await verifyWebhook({\n payload: stringify(payload),\n signature,\n secret: webhookSecret,\n }).catch((error) => {\n this._logDebug(\n \"Error while verifying webhook: \",\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n\n return false;\n });\n\n if (!verified) {\n this._logDebug(\"Failed to verify webhook\");\n throw new HttpError(\"Unauthorized\", 401);\n }\n\n const parsed = ZodEvent.safeParse(payload);\n\n if (!parsed.success) {\n this._logDebug(\n \"Failed to validate event payload:\",\n JSON.stringify(parsed.data ?? {}, null, 4),\n );\n throw new HttpError(\"Invalid request\", 400);\n }\n\n const { event, data } = parsed.data;\n const context = await this._getContext();\n\n switch (event) {\n case \"initial-storage\": {\n const room = data.room;\n const storage =\n typeof room === \"string\"\n ? ((await this._getInitialStorage?.({ context, room })) ?? null)\n : null;\n\n try {\n return createSuccessResponse(c, { event, room, storage });\n } catch (error) {\n this._logDebug(\"Could not create getInitialStorage response\");\n throw error;\n }\n }\n case \"room-destroyed\": {\n const room = data.room;\n\n await Promise.resolve(\n this._listeners?.onRoomDestroyed({\n context,\n platform: this,\n room,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onRoomDestroyed response\");\n throw error;\n }\n }\n case \"storage-destroyed\": {\n const room = data.room;\n const encodedState = data.storage;\n\n await Promise.resolve(\n this._listeners?.onStorageDestroyed({\n context,\n encodedState,\n platform: this,\n room,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onStorageDestroyed response\");\n throw error;\n }\n }\n case \"user-connected\": {\n const room = data.room;\n const encodedState = data.storage;\n const user = data.user as any;\n\n await Promise.resolve(\n this._listeners?.onUserConnected({\n context,\n encodedState,\n platform: this,\n room,\n user,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onUserConnected response\");\n throw error;\n }\n }\n case \"user-disconnected\": {\n const room = data.room;\n const encodedState = data.storage;\n const user = data.user as any;\n\n await Promise.resolve(\n this._listeners?.onUserDisconnected({\n context,\n encodedState,\n platform: this,\n room,\n user,\n }),\n );\n\n try {\n return createSuccessResponse(c, { event, room });\n } catch (error) {\n this._logDebug(\"Could not create onUserDisconnected response\");\n throw error;\n }\n }\n default: {\n return createErrorResponse(c, { message: \"Unknown event\" }, 400, event);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unexpected error\";\n const status = error instanceof HttpError ? error.status : 500;\n\n this._logDebug(\"Uncaught error: \", message);\n\n return createErrorResponse(c, { message }, status);\n }\n });\n\n private async _getContext(): Promise<TContext> {\n return typeof this._context === \"function\"\n ? await Promise.resolve(this._context(this._roomContext as any))\n : await Promise.resolve(this._context);\n }\n\n private _logDebug(...args: any[]): void {\n if (!this._debug) return;\n\n console.log(\"[PLATFORM PLUV]\", ...args);\n }\n}\n","import type { CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport type { CreateIOParams, InferInitContextType, PluvContext, PluvIOAuthorize } from \"@pluv/io\";\nimport type { BaseUser, Id } from \"@pluv/types\";\nimport type { PluvPlatformConfig } from \"./PluvPlatform\";\nimport { PluvPlatform } from \"./PluvPlatform\";\n\nexport type PlatformPluvCreateIOParams<\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n> = Id<\n PluvPlatformConfig &\n Omit<\n CreateIOParams<PluvPlatform, TContext, TUser, TCrdt>,\n \"authorize\" | \"context\" | \"limits\" | \"platform\"\n > & {\n authorize: PluvIOAuthorize<PluvPlatform, TUser, InferInitContextType<PluvPlatform>>;\n context?: PluvContext<PluvPlatform, TContext>;\n }\n>;\n\nexport const platformPluv = <\n TContext extends Record<string, any> = {},\n TUser extends BaseUser = BaseUser,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n>(\n config: PlatformPluvCreateIOParams<TContext, TUser, TCrdt>,\n): CreateIOParams<PluvPlatform<TContext, TUser>, TContext, TUser, TCrdt> => {\n const { authorize, context, crdt, debug } = config;\n\n return {\n authorize,\n context,\n crdt,\n debug,\n platform: () => new PluvPlatform<TContext, TUser>(config),\n };\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/platform-pluv",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "@pluv/io adapter for pluv.io",
5
5
  "author": {
6
6
  "email": "david@pluv.io",
@@ -24,16 +24,16 @@
24
24
  "fast-json-stable-stringify": "^2.1.0",
25
25
  "hono": "^4.11.4",
26
26
  "zod": "^4.3.5",
27
- "@pluv/crdt": "^4.0.0",
28
- "@pluv/io": "^4.0.0",
29
- "@pluv/types": "^4.0.0"
27
+ "@pluv/crdt": "^4.0.1",
28
+ "@pluv/io": "^4.0.1",
29
+ "@pluv/types": "^4.0.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "eslint": "^9.39.2",
33
33
  "tsdown": "0.20.0-beta.4",
34
34
  "typescript": "^5.9.3",
35
- "@pluv/tsconfig": "^4.0.0",
36
- "eslint-config-pluv": "^4.0.0"
35
+ "@pluv/tsconfig": "^4.0.1",
36
+ "eslint-config-pluv": "^4.0.1"
37
37
  },
38
38
  "exports": {
39
39
  ".": "./dist/index.mjs",
@@ -389,7 +389,7 @@ export class PluvPlatform<
389
389
  }
390
390
  }
391
391
  default: {
392
- throw new HttpError("Unknown event", 400);
392
+ return createErrorResponse(c, { message: "Unknown event" }, 400, event);
393
393
  }
394
394
  }
395
395
  } catch (error) {
@@ -6,6 +6,14 @@ export const createErrorResponse = <TStatus extends ContentfulStatusCode>(
6
6
  c: Context<BlankEnv, "/", BlankInput>,
7
7
  error: { message: string },
8
8
  status: TStatus,
9
+ event: string = "unknown",
9
10
  ) => {
10
- return c.json({ ok: false, error: { message: error.message } }, status);
11
+ return c.json(
12
+ {
13
+ ok: false,
14
+ error: { message: error.message },
15
+ },
16
+ status,
17
+ { "x-pluv-event": event },
18
+ );
11
19
  };
@@ -20,5 +20,6 @@ export const createSuccessResponse = <TStatus extends ContentfulStatusCode = 200
20
20
  data: ZodEventResponse.parse(data),
21
21
  },
22
22
  status,
23
+ { "x-pluv-event": data.event },
23
24
  );
24
25
  };