@liveblocks/node 1.7.1-pre1 → 1.7.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/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -29,9 +29,20 @@ function normalizeStatusCode(statusCode) {
|
|
|
29
29
|
return 403;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
function
|
|
33
|
-
const
|
|
34
|
-
|
|
32
|
+
function toURLSearchParams(params) {
|
|
33
|
+
const result = new URLSearchParams();
|
|
34
|
+
for (const [key, value] of Object.entries(params)) {
|
|
35
|
+
if (value !== void 0 && value !== null) {
|
|
36
|
+
result.set(key, value.toString());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function urljoin(baseUrl, path, params) {
|
|
42
|
+
const url = new URL(path, baseUrl);
|
|
43
|
+
if (params !== void 0) {
|
|
44
|
+
url.search = (params instanceof URLSearchParams ? params : toURLSearchParams(params)).toString();
|
|
45
|
+
}
|
|
35
46
|
return url.toString();
|
|
36
47
|
}
|
|
37
48
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/authorize.ts","../src/Session.ts","../src/client.ts","../src/webhooks.ts"],"names":[],"mappings":"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","sourcesContent":["export const DEFAULT_BASE_URL = \"https://api.liveblocks.io\";\n\nexport async function fetchPolyfill(): Promise<typeof fetch> {\n return typeof globalThis.fetch !== \"undefined\"\n ? globalThis.fetch\n : ((await import(\"node-fetch\")).default as unknown as typeof fetch);\n}\n\nexport function isNonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nexport function assertNonEmpty(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value)) {\n throw new Error(\n `Invalid value for field \"${field}\". Please provide a non-empty string. For more information: https://liveblocks.io/docs/api-reference/liveblocks-node#authorize`\n );\n }\n}\n\nexport function assertSecretKey(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value) || !value.startsWith(\"sk_\")) {\n throw new Error(\n `Invalid value for field \"${field}\". Secret keys must start with \"sk_\". Please provide the secret key from your Liveblocks dashboard at https://liveblocks.io/dashboard/apikeys.`\n );\n }\n}\n\nexport function normalizeStatusCode(statusCode: number): number {\n if (statusCode >= 200 && statusCode < 300) {\n return 200; /* OK */\n } else if (statusCode >= 500) {\n return 503; /* Service Unavailable */\n } else {\n return 403; /* Forbidden */\n }\n}\n\n/**\n * Concatenates a path to a URL.\n */\nexport function urljoin(baseUrl: string | URL, path: string): string {\n const url = new URL(baseUrl);\n url.pathname = path;\n return url.toString();\n}\n","import {\n assertNonEmpty,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeOptions = {\n /**\n * The secret API key for your Liveblocks account. You can find it on\n * https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * The room ID for which to authorize the user. This will authorize the user\n * to enter the Liveblocks room.\n */\n room: string;\n\n /**\n * Associates a user ID to the session that is being authorized. The user ID\n * is typically set to the user ID from your own database.\n *\n * It can also be used to generate a token that gives access to a private\n * room where the userId is configured in the room accesses.\n *\n * This user ID will be used as the unique identifier to compute your\n * Liveblocks account's Monthly Active Users.\n */\n userId: string;\n\n /**\n * Arbitrary metadata associated to this user session.\n *\n * You can use it to store a small amount of static metadata for a user\n * session. It is public information, that will be visible to other users in\n * the same room, like name, avatar URL, etc.\n *\n * It's only suitable for static info that won't change during a session. If\n * you want to store dynamic metadata on a user session, don't keep that in\n * the session token, but use Presence instead.\n *\n * Can't exceed 1KB when serialized as JSON.\n */\n userInfo?: unknown;\n\n /**\n * Tell Liveblocks which group IDs this user belongs to. This will authorize\n * the user session to access private rooms that have at least one of these\n * group IDs listed in their room access configuration.\n *\n * See https://liveblocks.io/docs/guides/managing-rooms-users-permissions#permissions\n * for how to configure your room's permissions to use this feature.\n */\n groupIds?: string[];\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\n/**\n * @deprecated Since 1.2, we’re deprecating single-room tokens in favor of\n * either access tokens or ID tokens. Single-room tokens are still supported,\n * but support for them will be dropped in the future. Please refer to our\n * Upgrade Guide to learn how to adopt the new-style authorization, see\n * https://liveblocks.io/docs/platform/upgrading/1.2\n *\n * Tells Liveblocks that a user should be allowed access to a room, which user\n * this session is for, and what metadata to associate with the user (like\n * name, avatar, etc.)\n *\n * @example\n * export default async function auth(req, res) {\n *\n * // Implement your own security here.\n *\n * const room = req.body.room;\n * const response = await authorize({\n * room,\n * secret,\n * userId: \"123\",\n * userInfo: { // Optional\n * name: \"Ada Lovelace\"\n * },\n * groupIds: [\"group1\"] // Optional\n * });\n * return res.status(response.status).end(response.body);\n * }\n */\nexport async function authorize(\n options: AuthorizeOptions\n): Promise<AuthorizeResponse> {\n let url = null;\n try {\n const { room, secret, userId, userInfo, groupIds } =\n // Ensure we'll validate inputs at runtime\n options as Record<string, unknown>;\n\n assertNonEmpty(secret, \"secret\");\n assertNonEmpty(room, \"room\");\n assertNonEmpty(userId, \"userId\");\n\n url = buildLiveblocksAuthorizeEndpoint(options, room);\n\n const fetch = await fetchPolyfill();\n const resp = await fetch(buildLiveblocksAuthorizeEndpoint(options, room), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${secret}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n userId,\n userInfo,\n groupIds,\n }),\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body:\n (url ? `Call to \"${url}\" failed.` : \"Invalid authorize request.\") +\n ' See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n}\n\nfunction buildLiveblocksAuthorizeEndpoint(\n options: AuthorizeOptions,\n roomId: string\n): string {\n const path = `/v2/rooms/${encodeURIComponent(roomId)}/authorize`;\n return urljoin(options.baseUrl || DEFAULT_BASE_URL, path);\n}\n","import type { AuthResponse } from \"./client\";\nimport { assertNonEmpty, normalizeStatusCode } from \"./utils\";\n\n// As defined in the source of truth in ApiScope in\n// https://github.com/liveblocks/liveblocks-cloudflare/blob/main/src/security.ts\nconst ALL_PERMISSIONS = Object.freeze([\n \"room:write\",\n \"room:read\",\n \"room:presence:write\",\n \"comments:write\",\n \"comments:read\",\n] as const);\n\nexport type Permission = (typeof ALL_PERMISSIONS)[number];\n\nfunction isPermission(value: string): value is Permission {\n return (ALL_PERMISSIONS as readonly unknown[]).includes(value);\n}\n\nconst MAX_PERMS_PER_SET = 10;\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * read permissions to the storage and comments data for this room. (Note that\n * the user will still have permissions to update their own presence.)\n */\nconst READ_ACCESS = Object.freeze([\n \"room:read\",\n \"room:presence:write\",\n \"comments:read\",\n] as const);\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * permissions to read and write to the room's storage and comments.\n */\nconst FULL_ACCESS = Object.freeze([\"room:write\", \"comments:write\"] as const);\n\nconst roomPatternRegex = /^[^*]{1,128}[*]?$/;\n\ntype PostFn = (\n path: `/${string}`,\n json: Record<string, unknown>\n) => Promise<Response>;\n\n/**\n * Class to help you construct the exact permission set to grant a user, used\n * when making `.authorizeUser()` calls.\n *\n * Usage:\n *\n * const session = liveblocks.prepareSession();\n * session.allow(roomId, permissions) // or...\n *\n * For the `permissions` argument, you can pass a list of specific permissions,\n * or use one of our presets:\n *\n * session.allow('my-room', session.FULL_ACCESS) // Read + write access to room storage and comments\n * session.allow('my-room', session.READ_ACCESS) // Read-only access to room storage and comments\n *\n * Rooms can be specified with a prefix match, if the name ends in an asterisk.\n * In that case, access is granted to *all* rooms that start with that prefix:\n *\n * // Read + write access to *all* rooms that start with \"abc:\"\n * session.allow('abc:*', session.FULL_ACCESS)\n *\n * You can define at most 10 room IDs (or patterns) in a single token,\n * otherwise the token would become too large and unwieldy.\n *\n * All permissions granted are additive. You cannot \"remove\" permissions once\n * you grant them. For example:\n *\n * session\n * .allow('abc:*', session.FULL_ACCESS)\n * .allow('abc:123', session.READ_ACCESS)\n *\n * Here, room `abc:123` would have full access. The second .allow() call only\n * _adds_ read permissions, but that has no effect since full access\n * permissions were already added to the set.\n */\nexport class Session {\n public readonly FULL_ACCESS = FULL_ACCESS;\n public readonly READ_ACCESS = READ_ACCESS;\n\n /** @internal */\n private _postFn: PostFn;\n /** @internal */\n private _userId: string;\n /** @internal */\n private _userInfo?: unknown;\n /** @internal */\n private _sealed = false;\n /** @internal */\n private readonly _permissions: Map<string, Set<Permission>> = new Map();\n\n /** @internal */\n constructor(postFn: PostFn, userId: string, userInfo?: unknown) {\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n\n this._postFn = postFn;\n this._userId = userId;\n this._userInfo = userInfo;\n }\n\n /** @internal */\n private getOrCreate(roomId: string): Set<Permission> {\n if (this._sealed) {\n throw new Error(\"You can no longer change these permissions.\");\n }\n\n let perms = this._permissions.get(roomId);\n if (perms) {\n return perms;\n } else {\n if (this._permissions.size >= MAX_PERMS_PER_SET) {\n throw new Error(\n \"You cannot add permissions for more than 10 rooms in a single token\"\n );\n }\n\n perms = new Set<Permission>();\n this._permissions.set(roomId, perms);\n return perms;\n }\n }\n\n public allow(roomIdOrPattern: string, newPerms: readonly Permission[]): this {\n if (!roomPatternRegex.test(roomIdOrPattern)) {\n throw new Error(\"Invalid room name or pattern\");\n }\n\n if (newPerms.length === 0) {\n throw new Error(\"Permission list cannot be empty\");\n }\n\n const existingPerms = this.getOrCreate(roomIdOrPattern);\n for (const perm of newPerms) {\n if (!isPermission(perm as string)) {\n throw new Error(`Not a valid permission: ${perm}`);\n }\n existingPerms.add(perm);\n }\n return this; // To allow chaining multiple allow calls\n }\n\n /** @internal - For unit tests only */\n public hasPermissions(): boolean {\n return this._permissions.size > 0;\n }\n\n /** @internal - For unit tests only */\n public seal(): void {\n if (this._sealed) {\n throw new Error(\n \"You cannot reuse Session instances. Please create a new session every time.\"\n );\n }\n this._sealed = true;\n }\n\n /** @internal - For unit tests only */\n public serializePermissions(): Record<string, unknown> {\n return Object.fromEntries(\n Array.from(this._permissions.entries()).map(([pat, perms]) => [\n pat,\n Array.from(perms),\n ])\n );\n }\n\n /**\n * Call this to authorize the session to access Liveblocks. Note that this\n * will return a Liveblocks \"access token\". Anyone that obtains such access\n * token will have access to the allowed resources.\n */\n public async authorize(): Promise<AuthResponse> {\n this.seal();\n if (!this.hasPermissions()) {\n return {\n status: 403,\n body: \"Forbidden\",\n };\n }\n\n try {\n const resp = await this._postFn(\"/v2/authorize-user\", {\n // Required\n userId: this._userId,\n permissions: this.serializePermissions(),\n\n // Optional metadata\n userInfo: this._userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: 'Call to /v2/authorize-user failed. See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n }\n}\n","/**\n * NOTE: only types should be imported from @liveblocks/core.\n * This is because this package is made to be used in Node.js, and\n * @liveblocks/core has browser-specific code.\n */\nimport type { CommentData, ThreadData } from \"@liveblocks/core\";\n\nimport { Session } from \"./Session\";\nimport {\n assertNonEmpty,\n assertSecretKey,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\nexport type LiveblocksOptions = {\n /**\n * The Liveblocks secret key. Must start with \"sk_\".\n * Get it from https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\nexport type CreateSessionOptions = {\n userInfo: unknown;\n};\n\nexport type AuthResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\ntype Identity = {\n userId: string;\n groupIds: string[];\n};\n\ntype ThreadParticipants = {\n participantIds: string[];\n};\n\n/**\n * Interact with the Liveblocks API from your Node.js backend.\n */\nexport class Liveblocks {\n /** @internal */\n private readonly _secret: string;\n /** @internal */\n private readonly _baseUrl: URL;\n\n /**\n * Interact with the Liveblocks API from your Node.js backend.\n */\n constructor(options: LiveblocksOptions) {\n const options_ = options as Record<string, unknown>;\n const secret = options_.secret;\n assertSecretKey(secret, \"secret\");\n this._secret = secret;\n this._baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);\n }\n\n /** @internal */\n private async post(\n path: `/${string}`,\n json: Record<string, unknown>\n ): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(json),\n });\n }\n\n /** @internal */\n private async get(path: `/${string}`): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, { method: \"GET\", headers });\n }\n\n /**\n * Prepares a new session to authorize a user to access Liveblocks.\n *\n * IMPORTANT:\n * Always make sure that you trust the user making the request to your\n * backend before calling .prepareSession()!\n *\n * @param userId Tell Liveblocks the user ID of the user to authorize. Must\n * uniquely identify the user account in your system. The uniqueness of this\n * value will determine how many MAUs will be counted/billed.\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n *\n */\n prepareSession(userId: string, options?: CreateSessionOptions): Session {\n return new Session(this.post.bind(this), userId, options?.userInfo);\n }\n\n /**\n * Call this to authenticate the user as an actor you want to allow to use\n * Liveblocks.\n *\n * You should use this method only if you want to manage your permissions\n * through the Liveblocks Permissions API. This method is more complicated to\n * set up, but allows for finer-grained specification of permissions.\n *\n * Calling `.identifyUser()` only lets you securely identify a user (and what\n * groups they belong to). What permissions this user will end up having is\n * determined by whatever permissions you assign the user/group in your\n * Liveblocks account, through the Permissions API:\n * https://liveblocks.io/docs/rooms/permissions\n *\n * IMPORTANT:\n * Always verify that you trust the user making the request before calling\n * .identifyUser()!\n *\n * @param identity Tell Liveblocks the user ID of the user to authenticate.\n * Must uniquely identify the user account in your system. The uniqueness of\n * this value will determine how many MAUs will be counted/billed.\n *\n * If you also want to assign which groups this user belongs to, use the\n * object form and specify the `groupIds` property. Those `groupIds` should\n * match the groupIds you assigned permissions to via the Liveblocks\n * Permissions API, see\n * https://liveblocks.io/docs/rooms/permissions#permissions-levels-groups-accesses-example\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n */\n // These fields define the security identity of the user. Whatever you pass in here will define which\n public async identifyUser(\n identity:\n | string // Shorthand for userId\n | Identity,\n options?: {\n userInfo: unknown;\n // ....\n }\n ): Promise<AuthResponse> {\n const path = \"/v2/identify-user\";\n const userId = typeof identity === \"string\" ? identity : identity.userId;\n const groupIds =\n typeof identity === \"string\" ? undefined : identity.groupIds;\n\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n // assertStringArrayOrUndefined(groupsIds, \"groupIds\"); // TODO: Check if this is a legal userId value too\n\n try {\n const resp = await this.post(path, {\n userId,\n groupIds,\n\n // Optional metadata\n userInfo: options?.userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: `Call to ${urljoin(\n this._baseUrl,\n path\n )} failed. See \"error\" for more information.`,\n error: er as Error | undefined,\n };\n }\n }\n\n /**\n * Gets all the threads in a room.\n *\n * @param params.roomId The room ID to get the threads from.\n * @returns A list of threads.\n */\n public async getThreads(params: { roomId: string }): Promise<ThreadData[]> {\n const { roomId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads`\n );\n\n const body = await (resp.json() as Promise<ThreadData[]>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread.\n *\n * @param params.roomId The room ID to get the thread from.\n * @param params.threadId The thread ID.\n * @returns A thread.\n */\n public async getThread(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadData> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}`\n );\n\n const body = await (resp.json() as Promise<ThreadData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's participants.\n *\n * Participants are users who have commented on the thread\n * or users and groups that have been mentioned in a comment.\n *\n * @param params.roomId The room ID to get the thread participants from.\n * @param params.threadId The thread ID to get the participants from.\n * @returns An object containing an array of participant IDs.\n */\n public async getThreadParticipants(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadParticipants> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/participants`\n );\n\n const body = await (resp.json() as Promise<ThreadParticipants>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's comment.\n *\n * @param params.roomId The room ID to get the comment from.\n * @param params.threadId The thread ID to get the comment from.\n * @param params.commentId The comment ID.\n * @returns A comment.\n */\n public async getComment(params: {\n roomId: string;\n threadId: string;\n commentId: string;\n }): Promise<CommentData> {\n const { roomId, threadId, commentId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/comments/${encodeURIComponent(commentId)}`\n );\n\n const body = await (resp.json() as Promise<CommentData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n}\n","import * as base64 from \"@stablelib/base64\";\nimport * as sha256 from \"fast-sha256\";\nimport type { IncomingHttpHeaders } from \"http\";\n\nexport class WebhookHandler {\n private secretBuffer: Buffer;\n private static secretPrefix = \"whsec_\";\n\n constructor(\n /**\n * The signing secret provided on the dashboard's webhooks page\n * @example \"whsec_wPbvQ+u3VtN2e2tRPDKchQ1tBZ3svaHLm\"\n */\n secret: string\n ) {\n if (!secret) throw new Error(\"Secret is required\");\n if (typeof secret !== \"string\") throw new Error(\"Secret must be a string\");\n\n if (secret.startsWith(WebhookHandler.secretPrefix) === false)\n throw new Error(\"Invalid secret, must start with whsec_\");\n\n const secretKey = secret.slice(WebhookHandler.secretPrefix.length);\n this.secretBuffer = Buffer.from(secretKey, \"base64\");\n }\n\n /**\n * Verifies a webhook request and returns the event\n */\n public verifyRequest(request: WebhookRequest): WebhookEvent {\n const { webhookId, timestamp, rawSignatures } = this.verifyHeaders(\n request.headers\n );\n\n this.verifyTimestamp(timestamp);\n\n const signature = this.sign(`${webhookId}.${timestamp}.${request.rawBody}`);\n\n const expectedSignatures = rawSignatures\n .split(\" \")\n .map((rawSignature) => {\n const [, parsedSignature] = rawSignature.split(\",\");\n return parsedSignature;\n })\n .filter(isNotUndefined);\n\n if (expectedSignatures.includes(signature) === false)\n throw new Error(\n `Invalid signature, expected one of ${expectedSignatures.join(\n \", \"\n )}, got ${signature}`\n );\n\n const event: WebhookEvent = JSON.parse(request.rawBody) as WebhookEvent;\n\n this.verifyWebhookEventType(event);\n\n return event;\n }\n\n /**\n * Verifies the headers and returns the webhookId, timestamp and rawSignatures\n */\n private verifyHeaders(headers: IncomingHttpHeaders | Headers) {\n const usingNativeHeaders =\n typeof Headers !== \"undefined\" && headers instanceof Headers;\n const normalizedHeaders = usingNativeHeaders\n ? Object.fromEntries(headers)\n : (headers as IncomingHttpHeaders);\n\n const sanitizedHeaders: IncomingHttpHeaders = {};\n Object.keys(normalizedHeaders).forEach((key) => {\n sanitizedHeaders[key.toLowerCase()] = normalizedHeaders[key];\n });\n\n const webhookId = sanitizedHeaders[\"webhook-id\"];\n if (typeof webhookId !== \"string\")\n throw new Error(\"Invalid webhook-id header\");\n\n const timestamp = sanitizedHeaders[\"webhook-timestamp\"];\n if (typeof timestamp !== \"string\")\n throw new Error(\"Invalid webhook-timestamp header\");\n\n const rawSignatures = sanitizedHeaders[\"webhook-signature\"];\n if (typeof rawSignatures !== \"string\")\n throw new Error(\"Invalid webhook-signature header\");\n\n return { webhookId, timestamp, rawSignatures };\n }\n\n /**\n * Signs the content with the secret\n * @param content\n * @returns `string`\n */\n private sign(content: string): string {\n const encoder = new TextEncoder();\n const toSign = encoder.encode(content);\n return base64.encode(sha256.hmac(this.secretBuffer, toSign));\n }\n\n /**\n * Verifies that the timestamp is not too old or in the future\n */\n private verifyTimestamp(timestampHeader: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(timestampHeader, 10);\n\n if (isNaN(timestamp)) {\n throw new Error(\"Invalid timestamp\");\n }\n\n // Check if timestamp is too old\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp too old\");\n }\n\n // Check if timestamp is in the future\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp in the future\");\n }\n }\n\n /**\n * Ensures that the event is a known event type\n * or throws and prompts the user to upgrade to a higher version of @liveblocks/node\n */\n private verifyWebhookEventType(\n event: WebhookEvent\n ): asserts event is WebhookEvent {\n if (\n event &&\n event.type &&\n [\n \"storageUpdated\",\n \"userEntered\",\n \"userLeft\",\n \"roomCreated\",\n \"roomDeleted\",\n \"commentCreated\",\n \"commentEdited\",\n \"commentDeleted\",\n \"commentReactionAdded\",\n \"commentReactionRemoved\",\n \"threadMetadataUpdated\",\n \"threadCreated\",\n \"ydocUpdated\",\n ].includes(event.type)\n )\n return;\n\n throw new Error(\n \"Unknown event type, please upgrade to a higher version of @liveblocks/node\"\n );\n }\n}\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nconst isNotUndefined = <T>(value: T | undefined): value is T =>\n value !== undefined;\n\ntype WebhookRequest = {\n /**\n * Headers of the request, can be a regular object or a Headers object\n * @example\n * {\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }\n *\n * new Headers({\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }}\n */\n headers: IncomingHttpHeaders | Headers;\n /**\n * Raw body of the request, do not parse it\n * @example '{\"type\":\"storageUpdated\",\"data\":{\"roomId\":\"my-room-id\",\"appId\":\"my-app-id\",\"updatedAt\":\"2021-03-01T12:00:00.000Z\"}}'\n */\n rawBody: string;\n};\n\ntype WebhookEvent =\n | StorageUpdatedEvent\n | UserEnteredEvent\n | UserLeftEvent\n | RoomCreatedEvent\n | RoomDeletedEvent\n | CommentCreatedEvent\n | CommentEditedEvent\n | CommentDeletedEvent\n | CommentReactionAdded\n | CommentReactionRemoved\n | ThreadMetadataUpdatedEvent\n | ThreadCreatedEvent\n | YDocUpdatedEvent;\n\ntype StorageUpdatedEvent = {\n type: \"storageUpdated\";\n data: {\n roomId: string;\n projectId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedAt: string;\n };\n};\n\ntype UserEnteredEvent = {\n type: \"userEntered\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user entered the room.\n */\n enteredAt: string;\n numActiveUsers: number;\n };\n};\n\ntype UserLeftEvent = {\n type: \"userLeft\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user left the room.\n */\n leftAt: string;\n numActiveUsers: number;\n };\n};\n\ntype RoomCreatedEvent = {\n type: \"roomCreated\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n };\n};\n\ntype RoomDeletedEvent = {\n type: \"roomDeleted\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentCreatedEvent = {\n type: \"commentCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\ntype CommentEditedEvent = {\n type: \"commentEdited\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n editedAt: string;\n };\n};\n\ntype CommentDeletedEvent = {\n type: \"commentDeleted\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentReactionAdded = {\n type: \"commentReactionAdded\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n addedAt: string;\n addedBy: string;\n };\n};\n\ntype CommentReactionRemoved = {\n type: \"commentReactionRemoved\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n removedAt: string;\n removedBy: string;\n };\n};\n\ntype YDocUpdatedEvent = {\n type: \"ydocUpdated\";\n data: {\n projectId: string;\n roomId: string;\n };\n};\n\ntype ThreadMetadataUpdatedEvent = {\n type: \"threadMetadataUpdated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n updatedAt: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedBy: string;\n };\n};\n\ntype ThreadCreatedEvent = {\n type: \"threadCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\nexport type {\n CommentCreatedEvent,\n CommentDeletedEvent,\n CommentEditedEvent,\n CommentReactionAdded,\n CommentReactionRemoved,\n RoomCreatedEvent,\n RoomDeletedEvent,\n StorageUpdatedEvent,\n ThreadCreatedEvent,\n ThreadMetadataUpdatedEvent,\n UserEnteredEvent,\n UserLeftEvent,\n WebhookEvent,\n WebhookRequest,\n YDocUpdatedEvent,\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/authorize.ts","../src/Session.ts","../src/client.ts","../src/webhooks.ts"],"names":[],"mappings":"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","sourcesContent":["export const DEFAULT_BASE_URL = \"https://api.liveblocks.io\";\n\nexport async function fetchPolyfill(): Promise<typeof fetch> {\n return typeof globalThis.fetch !== \"undefined\"\n ? globalThis.fetch\n : ((await import(\"node-fetch\")).default as unknown as typeof fetch);\n}\n\nexport function isNonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nexport function assertNonEmpty(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value)) {\n throw new Error(\n `Invalid value for field \"${field}\". Please provide a non-empty string. For more information: https://liveblocks.io/docs/api-reference/liveblocks-node#authorize`\n );\n }\n}\n\nexport function assertSecretKey(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value) || !value.startsWith(\"sk_\")) {\n throw new Error(\n `Invalid value for field \"${field}\". Secret keys must start with \"sk_\". Please provide the secret key from your Liveblocks dashboard at https://liveblocks.io/dashboard/apikeys.`\n );\n }\n}\n\nexport function normalizeStatusCode(statusCode: number): number {\n if (statusCode >= 200 && statusCode < 300) {\n return 200; /* OK */\n } else if (statusCode >= 500) {\n return 503; /* Service Unavailable */\n } else {\n return 403; /* Forbidden */\n }\n}\n\ntype QueryParams =\n | Record<string, string | number | null | undefined>\n | URLSearchParams;\n\n/**\n * Safely but conveniently build a URLSearchParams instance from a given\n * dictionary of values. For example:\n *\n * {\n * \"foo\": \"bar+qux/baz\",\n * \"empty\": \"\",\n * \"n\": 42,\n * \"nope\": undefined,\n * \"alsonope\": null,\n * }\n *\n * Will produce a value that will get serialized as\n * `foo=bar%2Bqux%2Fbaz&empty=&n=42`.\n *\n * Notice how the number is converted to its string representation\n * automatically and the `null`/`undefined` values simply don't end up in the\n * URL.\n */\nfunction toURLSearchParams(\n params: Record<string, string | number | null | undefined>\n): URLSearchParams {\n const result = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n result.set(key, value.toString());\n }\n }\n return result;\n}\n\n/**\n * Concatenates a path to an existing URL.\n */\nexport function urljoin(\n baseUrl: string | URL,\n path: string,\n params?: QueryParams\n): string {\n // First, sanitize by removing user/passwd/search/hash parts from the URL\n const url = new URL(path, baseUrl);\n if (params !== undefined) {\n url.search = (\n params instanceof URLSearchParams ? params : toURLSearchParams(params)\n ).toString();\n }\n return url.toString();\n}\n","import {\n assertNonEmpty,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeOptions = {\n /**\n * The secret API key for your Liveblocks account. You can find it on\n * https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * The room ID for which to authorize the user. This will authorize the user\n * to enter the Liveblocks room.\n */\n room: string;\n\n /**\n * Associates a user ID to the session that is being authorized. The user ID\n * is typically set to the user ID from your own database.\n *\n * It can also be used to generate a token that gives access to a private\n * room where the userId is configured in the room accesses.\n *\n * This user ID will be used as the unique identifier to compute your\n * Liveblocks account's Monthly Active Users.\n */\n userId: string;\n\n /**\n * Arbitrary metadata associated to this user session.\n *\n * You can use it to store a small amount of static metadata for a user\n * session. It is public information, that will be visible to other users in\n * the same room, like name, avatar URL, etc.\n *\n * It's only suitable for static info that won't change during a session. If\n * you want to store dynamic metadata on a user session, don't keep that in\n * the session token, but use Presence instead.\n *\n * Can't exceed 1KB when serialized as JSON.\n */\n userInfo?: unknown;\n\n /**\n * Tell Liveblocks which group IDs this user belongs to. This will authorize\n * the user session to access private rooms that have at least one of these\n * group IDs listed in their room access configuration.\n *\n * See https://liveblocks.io/docs/guides/managing-rooms-users-permissions#permissions\n * for how to configure your room's permissions to use this feature.\n */\n groupIds?: string[];\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\n/**\n * @deprecated Since 1.2, we’re deprecating single-room tokens in favor of\n * either access tokens or ID tokens. Single-room tokens are still supported,\n * but support for them will be dropped in the future. Please refer to our\n * Upgrade Guide to learn how to adopt the new-style authorization, see\n * https://liveblocks.io/docs/platform/upgrading/1.2\n *\n * Tells Liveblocks that a user should be allowed access to a room, which user\n * this session is for, and what metadata to associate with the user (like\n * name, avatar, etc.)\n *\n * @example\n * export default async function auth(req, res) {\n *\n * // Implement your own security here.\n *\n * const room = req.body.room;\n * const response = await authorize({\n * room,\n * secret,\n * userId: \"123\",\n * userInfo: { // Optional\n * name: \"Ada Lovelace\"\n * },\n * groupIds: [\"group1\"] // Optional\n * });\n * return res.status(response.status).end(response.body);\n * }\n */\nexport async function authorize(\n options: AuthorizeOptions\n): Promise<AuthorizeResponse> {\n let url = null;\n try {\n const { room, secret, userId, userInfo, groupIds } =\n // Ensure we'll validate inputs at runtime\n options as Record<string, unknown>;\n\n assertNonEmpty(secret, \"secret\");\n assertNonEmpty(room, \"room\");\n assertNonEmpty(userId, \"userId\");\n\n url = buildLiveblocksAuthorizeEndpoint(options, room);\n\n const fetch = await fetchPolyfill();\n const resp = await fetch(buildLiveblocksAuthorizeEndpoint(options, room), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${secret}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n userId,\n userInfo,\n groupIds,\n }),\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body:\n (url ? `Call to \"${url}\" failed.` : \"Invalid authorize request.\") +\n ' See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n}\n\nfunction buildLiveblocksAuthorizeEndpoint(\n options: AuthorizeOptions,\n roomId: string\n): string {\n const path = `/v2/rooms/${encodeURIComponent(roomId)}/authorize`;\n return urljoin(options.baseUrl || DEFAULT_BASE_URL, path);\n}\n","import type { AuthResponse } from \"./client\";\nimport { assertNonEmpty, normalizeStatusCode } from \"./utils\";\n\n// As defined in the source of truth in ApiScope in\n// https://github.com/liveblocks/liveblocks-cloudflare/blob/main/src/security.ts\nconst ALL_PERMISSIONS = Object.freeze([\n \"room:write\",\n \"room:read\",\n \"room:presence:write\",\n \"comments:write\",\n \"comments:read\",\n] as const);\n\nexport type Permission = (typeof ALL_PERMISSIONS)[number];\n\nfunction isPermission(value: string): value is Permission {\n return (ALL_PERMISSIONS as readonly unknown[]).includes(value);\n}\n\nconst MAX_PERMS_PER_SET = 10;\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * read permissions to the storage and comments data for this room. (Note that\n * the user will still have permissions to update their own presence.)\n */\nconst READ_ACCESS = Object.freeze([\n \"room:read\",\n \"room:presence:write\",\n \"comments:read\",\n] as const);\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * permissions to read and write to the room's storage and comments.\n */\nconst FULL_ACCESS = Object.freeze([\"room:write\", \"comments:write\"] as const);\n\nconst roomPatternRegex = /^[^*]{1,128}[*]?$/;\n\ntype PostFn = (\n path: `/${string}`,\n json: Record<string, unknown>\n) => Promise<Response>;\n\n/**\n * Class to help you construct the exact permission set to grant a user, used\n * when making `.authorizeUser()` calls.\n *\n * Usage:\n *\n * const session = liveblocks.prepareSession();\n * session.allow(roomId, permissions) // or...\n *\n * For the `permissions` argument, you can pass a list of specific permissions,\n * or use one of our presets:\n *\n * session.allow('my-room', session.FULL_ACCESS) // Read + write access to room storage and comments\n * session.allow('my-room', session.READ_ACCESS) // Read-only access to room storage and comments\n *\n * Rooms can be specified with a prefix match, if the name ends in an asterisk.\n * In that case, access is granted to *all* rooms that start with that prefix:\n *\n * // Read + write access to *all* rooms that start with \"abc:\"\n * session.allow('abc:*', session.FULL_ACCESS)\n *\n * You can define at most 10 room IDs (or patterns) in a single token,\n * otherwise the token would become too large and unwieldy.\n *\n * All permissions granted are additive. You cannot \"remove\" permissions once\n * you grant them. For example:\n *\n * session\n * .allow('abc:*', session.FULL_ACCESS)\n * .allow('abc:123', session.READ_ACCESS)\n *\n * Here, room `abc:123` would have full access. The second .allow() call only\n * _adds_ read permissions, but that has no effect since full access\n * permissions were already added to the set.\n */\nexport class Session {\n public readonly FULL_ACCESS = FULL_ACCESS;\n public readonly READ_ACCESS = READ_ACCESS;\n\n /** @internal */\n private _postFn: PostFn;\n /** @internal */\n private _userId: string;\n /** @internal */\n private _userInfo?: unknown;\n /** @internal */\n private _sealed = false;\n /** @internal */\n private readonly _permissions: Map<string, Set<Permission>> = new Map();\n\n /** @internal */\n constructor(postFn: PostFn, userId: string, userInfo?: unknown) {\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n\n this._postFn = postFn;\n this._userId = userId;\n this._userInfo = userInfo;\n }\n\n /** @internal */\n private getOrCreate(roomId: string): Set<Permission> {\n if (this._sealed) {\n throw new Error(\"You can no longer change these permissions.\");\n }\n\n let perms = this._permissions.get(roomId);\n if (perms) {\n return perms;\n } else {\n if (this._permissions.size >= MAX_PERMS_PER_SET) {\n throw new Error(\n \"You cannot add permissions for more than 10 rooms in a single token\"\n );\n }\n\n perms = new Set<Permission>();\n this._permissions.set(roomId, perms);\n return perms;\n }\n }\n\n public allow(roomIdOrPattern: string, newPerms: readonly Permission[]): this {\n if (!roomPatternRegex.test(roomIdOrPattern)) {\n throw new Error(\"Invalid room name or pattern\");\n }\n\n if (newPerms.length === 0) {\n throw new Error(\"Permission list cannot be empty\");\n }\n\n const existingPerms = this.getOrCreate(roomIdOrPattern);\n for (const perm of newPerms) {\n if (!isPermission(perm as string)) {\n throw new Error(`Not a valid permission: ${perm}`);\n }\n existingPerms.add(perm);\n }\n return this; // To allow chaining multiple allow calls\n }\n\n /** @internal - For unit tests only */\n public hasPermissions(): boolean {\n return this._permissions.size > 0;\n }\n\n /** @internal - For unit tests only */\n public seal(): void {\n if (this._sealed) {\n throw new Error(\n \"You cannot reuse Session instances. Please create a new session every time.\"\n );\n }\n this._sealed = true;\n }\n\n /** @internal - For unit tests only */\n public serializePermissions(): Record<string, unknown> {\n return Object.fromEntries(\n Array.from(this._permissions.entries()).map(([pat, perms]) => [\n pat,\n Array.from(perms),\n ])\n );\n }\n\n /**\n * Call this to authorize the session to access Liveblocks. Note that this\n * will return a Liveblocks \"access token\". Anyone that obtains such access\n * token will have access to the allowed resources.\n */\n public async authorize(): Promise<AuthResponse> {\n this.seal();\n if (!this.hasPermissions()) {\n return {\n status: 403,\n body: \"Forbidden\",\n };\n }\n\n try {\n const resp = await this._postFn(\"/v2/authorize-user\", {\n // Required\n userId: this._userId,\n permissions: this.serializePermissions(),\n\n // Optional metadata\n userInfo: this._userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: 'Call to /v2/authorize-user failed. See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n }\n}\n","/**\n * NOTE: only types should be imported from @liveblocks/core.\n * This is because this package is made to be used in Node.js, and\n * @liveblocks/core has browser-specific code.\n */\nimport type { CommentData, ThreadData } from \"@liveblocks/core\";\n\nimport { Session } from \"./Session\";\nimport {\n assertNonEmpty,\n assertSecretKey,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\nexport type LiveblocksOptions = {\n /**\n * The Liveblocks secret key. Must start with \"sk_\".\n * Get it from https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\nexport type CreateSessionOptions = {\n userInfo: unknown;\n};\n\nexport type AuthResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\ntype Identity = {\n userId: string;\n groupIds: string[];\n};\n\ntype ThreadParticipants = {\n participantIds: string[];\n};\n\n/**\n * Interact with the Liveblocks API from your Node.js backend.\n */\nexport class Liveblocks {\n /** @internal */\n private readonly _secret: string;\n /** @internal */\n private readonly _baseUrl: URL;\n\n /**\n * Interact with the Liveblocks API from your Node.js backend.\n */\n constructor(options: LiveblocksOptions) {\n const options_ = options as Record<string, unknown>;\n const secret = options_.secret;\n assertSecretKey(secret, \"secret\");\n this._secret = secret;\n this._baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);\n }\n\n /** @internal */\n private async post(\n path: `/${string}`,\n json: Record<string, unknown>\n ): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(json),\n });\n }\n\n /** @internal */\n private async get(path: `/${string}`): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, { method: \"GET\", headers });\n }\n\n /**\n * Prepares a new session to authorize a user to access Liveblocks.\n *\n * IMPORTANT:\n * Always make sure that you trust the user making the request to your\n * backend before calling .prepareSession()!\n *\n * @param userId Tell Liveblocks the user ID of the user to authorize. Must\n * uniquely identify the user account in your system. The uniqueness of this\n * value will determine how many MAUs will be counted/billed.\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n *\n */\n prepareSession(userId: string, options?: CreateSessionOptions): Session {\n return new Session(this.post.bind(this), userId, options?.userInfo);\n }\n\n /**\n * Call this to authenticate the user as an actor you want to allow to use\n * Liveblocks.\n *\n * You should use this method only if you want to manage your permissions\n * through the Liveblocks Permissions API. This method is more complicated to\n * set up, but allows for finer-grained specification of permissions.\n *\n * Calling `.identifyUser()` only lets you securely identify a user (and what\n * groups they belong to). What permissions this user will end up having is\n * determined by whatever permissions you assign the user/group in your\n * Liveblocks account, through the Permissions API:\n * https://liveblocks.io/docs/rooms/permissions\n *\n * IMPORTANT:\n * Always verify that you trust the user making the request before calling\n * .identifyUser()!\n *\n * @param identity Tell Liveblocks the user ID of the user to authenticate.\n * Must uniquely identify the user account in your system. The uniqueness of\n * this value will determine how many MAUs will be counted/billed.\n *\n * If you also want to assign which groups this user belongs to, use the\n * object form and specify the `groupIds` property. Those `groupIds` should\n * match the groupIds you assigned permissions to via the Liveblocks\n * Permissions API, see\n * https://liveblocks.io/docs/rooms/permissions#permissions-levels-groups-accesses-example\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n */\n // These fields define the security identity of the user. Whatever you pass in here will define which\n public async identifyUser(\n identity:\n | string // Shorthand for userId\n | Identity,\n options?: {\n userInfo: unknown;\n // ....\n }\n ): Promise<AuthResponse> {\n const path = \"/v2/identify-user\";\n const userId = typeof identity === \"string\" ? identity : identity.userId;\n const groupIds =\n typeof identity === \"string\" ? undefined : identity.groupIds;\n\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n // assertStringArrayOrUndefined(groupsIds, \"groupIds\"); // TODO: Check if this is a legal userId value too\n\n try {\n const resp = await this.post(path, {\n userId,\n groupIds,\n\n // Optional metadata\n userInfo: options?.userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: `Call to ${urljoin(\n this._baseUrl,\n path\n )} failed. See \"error\" for more information.`,\n error: er as Error | undefined,\n };\n }\n }\n\n /**\n * Gets all the threads in a room.\n *\n * @param params.roomId The room ID to get the threads from.\n * @returns A list of threads.\n */\n public async getThreads(params: { roomId: string }): Promise<ThreadData[]> {\n const { roomId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads`\n );\n\n const body = await (resp.json() as Promise<ThreadData[]>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread.\n *\n * @param params.roomId The room ID to get the thread from.\n * @param params.threadId The thread ID.\n * @returns A thread.\n */\n public async getThread(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadData> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}`\n );\n\n const body = await (resp.json() as Promise<ThreadData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's participants.\n *\n * Participants are users who have commented on the thread\n * or users and groups that have been mentioned in a comment.\n *\n * @param params.roomId The room ID to get the thread participants from.\n * @param params.threadId The thread ID to get the participants from.\n * @returns An object containing an array of participant IDs.\n */\n public async getThreadParticipants(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadParticipants> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/participants`\n );\n\n const body = await (resp.json() as Promise<ThreadParticipants>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's comment.\n *\n * @param params.roomId The room ID to get the comment from.\n * @param params.threadId The thread ID to get the comment from.\n * @param params.commentId The comment ID.\n * @returns A comment.\n */\n public async getComment(params: {\n roomId: string;\n threadId: string;\n commentId: string;\n }): Promise<CommentData> {\n const { roomId, threadId, commentId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/comments/${encodeURIComponent(commentId)}`\n );\n\n const body = await (resp.json() as Promise<CommentData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n}\n","import * as base64 from \"@stablelib/base64\";\nimport * as sha256 from \"fast-sha256\";\nimport type { IncomingHttpHeaders } from \"http\";\n\nexport class WebhookHandler {\n private secretBuffer: Buffer;\n private static secretPrefix = \"whsec_\";\n\n constructor(\n /**\n * The signing secret provided on the dashboard's webhooks page\n * @example \"whsec_wPbvQ+u3VtN2e2tRPDKchQ1tBZ3svaHLm\"\n */\n secret: string\n ) {\n if (!secret) throw new Error(\"Secret is required\");\n if (typeof secret !== \"string\") throw new Error(\"Secret must be a string\");\n\n if (secret.startsWith(WebhookHandler.secretPrefix) === false)\n throw new Error(\"Invalid secret, must start with whsec_\");\n\n const secretKey = secret.slice(WebhookHandler.secretPrefix.length);\n this.secretBuffer = Buffer.from(secretKey, \"base64\");\n }\n\n /**\n * Verifies a webhook request and returns the event\n */\n public verifyRequest(request: WebhookRequest): WebhookEvent {\n const { webhookId, timestamp, rawSignatures } = this.verifyHeaders(\n request.headers\n );\n\n this.verifyTimestamp(timestamp);\n\n const signature = this.sign(`${webhookId}.${timestamp}.${request.rawBody}`);\n\n const expectedSignatures = rawSignatures\n .split(\" \")\n .map((rawSignature) => {\n const [, parsedSignature] = rawSignature.split(\",\");\n return parsedSignature;\n })\n .filter(isNotUndefined);\n\n if (expectedSignatures.includes(signature) === false)\n throw new Error(\n `Invalid signature, expected one of ${expectedSignatures.join(\n \", \"\n )}, got ${signature}`\n );\n\n const event: WebhookEvent = JSON.parse(request.rawBody) as WebhookEvent;\n\n this.verifyWebhookEventType(event);\n\n return event;\n }\n\n /**\n * Verifies the headers and returns the webhookId, timestamp and rawSignatures\n */\n private verifyHeaders(headers: IncomingHttpHeaders | Headers) {\n const usingNativeHeaders =\n typeof Headers !== \"undefined\" && headers instanceof Headers;\n const normalizedHeaders = usingNativeHeaders\n ? Object.fromEntries(headers)\n : (headers as IncomingHttpHeaders);\n\n const sanitizedHeaders: IncomingHttpHeaders = {};\n Object.keys(normalizedHeaders).forEach((key) => {\n sanitizedHeaders[key.toLowerCase()] = normalizedHeaders[key];\n });\n\n const webhookId = sanitizedHeaders[\"webhook-id\"];\n if (typeof webhookId !== \"string\")\n throw new Error(\"Invalid webhook-id header\");\n\n const timestamp = sanitizedHeaders[\"webhook-timestamp\"];\n if (typeof timestamp !== \"string\")\n throw new Error(\"Invalid webhook-timestamp header\");\n\n const rawSignatures = sanitizedHeaders[\"webhook-signature\"];\n if (typeof rawSignatures !== \"string\")\n throw new Error(\"Invalid webhook-signature header\");\n\n return { webhookId, timestamp, rawSignatures };\n }\n\n /**\n * Signs the content with the secret\n * @param content\n * @returns `string`\n */\n private sign(content: string): string {\n const encoder = new TextEncoder();\n const toSign = encoder.encode(content);\n return base64.encode(sha256.hmac(this.secretBuffer, toSign));\n }\n\n /**\n * Verifies that the timestamp is not too old or in the future\n */\n private verifyTimestamp(timestampHeader: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(timestampHeader, 10);\n\n if (isNaN(timestamp)) {\n throw new Error(\"Invalid timestamp\");\n }\n\n // Check if timestamp is too old\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp too old\");\n }\n\n // Check if timestamp is in the future\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp in the future\");\n }\n }\n\n /**\n * Ensures that the event is a known event type\n * or throws and prompts the user to upgrade to a higher version of @liveblocks/node\n */\n private verifyWebhookEventType(\n event: WebhookEvent\n ): asserts event is WebhookEvent {\n if (\n event &&\n event.type &&\n [\n \"storageUpdated\",\n \"userEntered\",\n \"userLeft\",\n \"roomCreated\",\n \"roomDeleted\",\n \"commentCreated\",\n \"commentEdited\",\n \"commentDeleted\",\n \"commentReactionAdded\",\n \"commentReactionRemoved\",\n \"threadMetadataUpdated\",\n \"threadCreated\",\n \"ydocUpdated\",\n ].includes(event.type)\n )\n return;\n\n throw new Error(\n \"Unknown event type, please upgrade to a higher version of @liveblocks/node\"\n );\n }\n}\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nconst isNotUndefined = <T>(value: T | undefined): value is T =>\n value !== undefined;\n\ntype WebhookRequest = {\n /**\n * Headers of the request, can be a regular object or a Headers object\n * @example\n * {\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }\n *\n * new Headers({\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }}\n */\n headers: IncomingHttpHeaders | Headers;\n /**\n * Raw body of the request, do not parse it\n * @example '{\"type\":\"storageUpdated\",\"data\":{\"roomId\":\"my-room-id\",\"appId\":\"my-app-id\",\"updatedAt\":\"2021-03-01T12:00:00.000Z\"}}'\n */\n rawBody: string;\n};\n\ntype WebhookEvent =\n | StorageUpdatedEvent\n | UserEnteredEvent\n | UserLeftEvent\n | RoomCreatedEvent\n | RoomDeletedEvent\n | CommentCreatedEvent\n | CommentEditedEvent\n | CommentDeletedEvent\n | CommentReactionAdded\n | CommentReactionRemoved\n | ThreadMetadataUpdatedEvent\n | ThreadCreatedEvent\n | YDocUpdatedEvent;\n\ntype StorageUpdatedEvent = {\n type: \"storageUpdated\";\n data: {\n roomId: string;\n projectId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedAt: string;\n };\n};\n\ntype UserEnteredEvent = {\n type: \"userEntered\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user entered the room.\n */\n enteredAt: string;\n numActiveUsers: number;\n };\n};\n\ntype UserLeftEvent = {\n type: \"userLeft\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user left the room.\n */\n leftAt: string;\n numActiveUsers: number;\n };\n};\n\ntype RoomCreatedEvent = {\n type: \"roomCreated\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n };\n};\n\ntype RoomDeletedEvent = {\n type: \"roomDeleted\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentCreatedEvent = {\n type: \"commentCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\ntype CommentEditedEvent = {\n type: \"commentEdited\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n editedAt: string;\n };\n};\n\ntype CommentDeletedEvent = {\n type: \"commentDeleted\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentReactionAdded = {\n type: \"commentReactionAdded\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n addedAt: string;\n addedBy: string;\n };\n};\n\ntype CommentReactionRemoved = {\n type: \"commentReactionRemoved\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n removedAt: string;\n removedBy: string;\n };\n};\n\ntype YDocUpdatedEvent = {\n type: \"ydocUpdated\";\n data: {\n projectId: string;\n roomId: string;\n };\n};\n\ntype ThreadMetadataUpdatedEvent = {\n type: \"threadMetadataUpdated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n updatedAt: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedBy: string;\n };\n};\n\ntype ThreadCreatedEvent = {\n type: \"threadCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\nexport type {\n CommentCreatedEvent,\n CommentDeletedEvent,\n CommentEditedEvent,\n CommentReactionAdded,\n CommentReactionRemoved,\n RoomCreatedEvent,\n RoomDeletedEvent,\n StorageUpdatedEvent,\n ThreadCreatedEvent,\n ThreadMetadataUpdatedEvent,\n UserEnteredEvent,\n UserLeftEvent,\n WebhookEvent,\n WebhookRequest,\n YDocUpdatedEvent,\n};\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -29,9 +29,20 @@ function normalizeStatusCode(statusCode) {
|
|
|
29
29
|
return 403;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
function
|
|
33
|
-
const
|
|
34
|
-
|
|
32
|
+
function toURLSearchParams(params) {
|
|
33
|
+
const result = new URLSearchParams();
|
|
34
|
+
for (const [key, value] of Object.entries(params)) {
|
|
35
|
+
if (value !== void 0 && value !== null) {
|
|
36
|
+
result.set(key, value.toString());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function urljoin(baseUrl, path, params) {
|
|
42
|
+
const url = new URL(path, baseUrl);
|
|
43
|
+
if (params !== void 0) {
|
|
44
|
+
url.search = (params instanceof URLSearchParams ? params : toURLSearchParams(params)).toString();
|
|
45
|
+
}
|
|
35
46
|
return url.toString();
|
|
36
47
|
}
|
|
37
48
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/authorize.ts","../src/Session.ts","../src/client.ts","../src/webhooks.ts"],"sourcesContent":["export const DEFAULT_BASE_URL = \"https://api.liveblocks.io\";\n\nexport async function fetchPolyfill(): Promise<typeof fetch> {\n return typeof globalThis.fetch !== \"undefined\"\n ? globalThis.fetch\n : ((await import(\"node-fetch\")).default as unknown as typeof fetch);\n}\n\nexport function isNonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nexport function assertNonEmpty(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value)) {\n throw new Error(\n `Invalid value for field \"${field}\". Please provide a non-empty string. For more information: https://liveblocks.io/docs/api-reference/liveblocks-node#authorize`\n );\n }\n}\n\nexport function assertSecretKey(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value) || !value.startsWith(\"sk_\")) {\n throw new Error(\n `Invalid value for field \"${field}\". Secret keys must start with \"sk_\". Please provide the secret key from your Liveblocks dashboard at https://liveblocks.io/dashboard/apikeys.`\n );\n }\n}\n\nexport function normalizeStatusCode(statusCode: number): number {\n if (statusCode >= 200 && statusCode < 300) {\n return 200; /* OK */\n } else if (statusCode >= 500) {\n return 503; /* Service Unavailable */\n } else {\n return 403; /* Forbidden */\n }\n}\n\n/**\n * Concatenates a path to a URL.\n */\nexport function urljoin(baseUrl: string | URL, path: string): string {\n const url = new URL(baseUrl);\n url.pathname = path;\n return url.toString();\n}\n","import {\n assertNonEmpty,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeOptions = {\n /**\n * The secret API key for your Liveblocks account. You can find it on\n * https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * The room ID for which to authorize the user. This will authorize the user\n * to enter the Liveblocks room.\n */\n room: string;\n\n /**\n * Associates a user ID to the session that is being authorized. The user ID\n * is typically set to the user ID from your own database.\n *\n * It can also be used to generate a token that gives access to a private\n * room where the userId is configured in the room accesses.\n *\n * This user ID will be used as the unique identifier to compute your\n * Liveblocks account's Monthly Active Users.\n */\n userId: string;\n\n /**\n * Arbitrary metadata associated to this user session.\n *\n * You can use it to store a small amount of static metadata for a user\n * session. It is public information, that will be visible to other users in\n * the same room, like name, avatar URL, etc.\n *\n * It's only suitable for static info that won't change during a session. If\n * you want to store dynamic metadata on a user session, don't keep that in\n * the session token, but use Presence instead.\n *\n * Can't exceed 1KB when serialized as JSON.\n */\n userInfo?: unknown;\n\n /**\n * Tell Liveblocks which group IDs this user belongs to. This will authorize\n * the user session to access private rooms that have at least one of these\n * group IDs listed in their room access configuration.\n *\n * See https://liveblocks.io/docs/guides/managing-rooms-users-permissions#permissions\n * for how to configure your room's permissions to use this feature.\n */\n groupIds?: string[];\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\n/**\n * @deprecated Since 1.2, we’re deprecating single-room tokens in favor of\n * either access tokens or ID tokens. Single-room tokens are still supported,\n * but support for them will be dropped in the future. Please refer to our\n * Upgrade Guide to learn how to adopt the new-style authorization, see\n * https://liveblocks.io/docs/platform/upgrading/1.2\n *\n * Tells Liveblocks that a user should be allowed access to a room, which user\n * this session is for, and what metadata to associate with the user (like\n * name, avatar, etc.)\n *\n * @example\n * export default async function auth(req, res) {\n *\n * // Implement your own security here.\n *\n * const room = req.body.room;\n * const response = await authorize({\n * room,\n * secret,\n * userId: \"123\",\n * userInfo: { // Optional\n * name: \"Ada Lovelace\"\n * },\n * groupIds: [\"group1\"] // Optional\n * });\n * return res.status(response.status).end(response.body);\n * }\n */\nexport async function authorize(\n options: AuthorizeOptions\n): Promise<AuthorizeResponse> {\n let url = null;\n try {\n const { room, secret, userId, userInfo, groupIds } =\n // Ensure we'll validate inputs at runtime\n options as Record<string, unknown>;\n\n assertNonEmpty(secret, \"secret\");\n assertNonEmpty(room, \"room\");\n assertNonEmpty(userId, \"userId\");\n\n url = buildLiveblocksAuthorizeEndpoint(options, room);\n\n const fetch = await fetchPolyfill();\n const resp = await fetch(buildLiveblocksAuthorizeEndpoint(options, room), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${secret}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n userId,\n userInfo,\n groupIds,\n }),\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body:\n (url ? `Call to \"${url}\" failed.` : \"Invalid authorize request.\") +\n ' See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n}\n\nfunction buildLiveblocksAuthorizeEndpoint(\n options: AuthorizeOptions,\n roomId: string\n): string {\n const path = `/v2/rooms/${encodeURIComponent(roomId)}/authorize`;\n return urljoin(options.baseUrl || DEFAULT_BASE_URL, path);\n}\n","import type { AuthResponse } from \"./client\";\nimport { assertNonEmpty, normalizeStatusCode } from \"./utils\";\n\n// As defined in the source of truth in ApiScope in\n// https://github.com/liveblocks/liveblocks-cloudflare/blob/main/src/security.ts\nconst ALL_PERMISSIONS = Object.freeze([\n \"room:write\",\n \"room:read\",\n \"room:presence:write\",\n \"comments:write\",\n \"comments:read\",\n] as const);\n\nexport type Permission = (typeof ALL_PERMISSIONS)[number];\n\nfunction isPermission(value: string): value is Permission {\n return (ALL_PERMISSIONS as readonly unknown[]).includes(value);\n}\n\nconst MAX_PERMS_PER_SET = 10;\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * read permissions to the storage and comments data for this room. (Note that\n * the user will still have permissions to update their own presence.)\n */\nconst READ_ACCESS = Object.freeze([\n \"room:read\",\n \"room:presence:write\",\n \"comments:read\",\n] as const);\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * permissions to read and write to the room's storage and comments.\n */\nconst FULL_ACCESS = Object.freeze([\"room:write\", \"comments:write\"] as const);\n\nconst roomPatternRegex = /^[^*]{1,128}[*]?$/;\n\ntype PostFn = (\n path: `/${string}`,\n json: Record<string, unknown>\n) => Promise<Response>;\n\n/**\n * Class to help you construct the exact permission set to grant a user, used\n * when making `.authorizeUser()` calls.\n *\n * Usage:\n *\n * const session = liveblocks.prepareSession();\n * session.allow(roomId, permissions) // or...\n *\n * For the `permissions` argument, you can pass a list of specific permissions,\n * or use one of our presets:\n *\n * session.allow('my-room', session.FULL_ACCESS) // Read + write access to room storage and comments\n * session.allow('my-room', session.READ_ACCESS) // Read-only access to room storage and comments\n *\n * Rooms can be specified with a prefix match, if the name ends in an asterisk.\n * In that case, access is granted to *all* rooms that start with that prefix:\n *\n * // Read + write access to *all* rooms that start with \"abc:\"\n * session.allow('abc:*', session.FULL_ACCESS)\n *\n * You can define at most 10 room IDs (or patterns) in a single token,\n * otherwise the token would become too large and unwieldy.\n *\n * All permissions granted are additive. You cannot \"remove\" permissions once\n * you grant them. For example:\n *\n * session\n * .allow('abc:*', session.FULL_ACCESS)\n * .allow('abc:123', session.READ_ACCESS)\n *\n * Here, room `abc:123` would have full access. The second .allow() call only\n * _adds_ read permissions, but that has no effect since full access\n * permissions were already added to the set.\n */\nexport class Session {\n public readonly FULL_ACCESS = FULL_ACCESS;\n public readonly READ_ACCESS = READ_ACCESS;\n\n /** @internal */\n private _postFn: PostFn;\n /** @internal */\n private _userId: string;\n /** @internal */\n private _userInfo?: unknown;\n /** @internal */\n private _sealed = false;\n /** @internal */\n private readonly _permissions: Map<string, Set<Permission>> = new Map();\n\n /** @internal */\n constructor(postFn: PostFn, userId: string, userInfo?: unknown) {\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n\n this._postFn = postFn;\n this._userId = userId;\n this._userInfo = userInfo;\n }\n\n /** @internal */\n private getOrCreate(roomId: string): Set<Permission> {\n if (this._sealed) {\n throw new Error(\"You can no longer change these permissions.\");\n }\n\n let perms = this._permissions.get(roomId);\n if (perms) {\n return perms;\n } else {\n if (this._permissions.size >= MAX_PERMS_PER_SET) {\n throw new Error(\n \"You cannot add permissions for more than 10 rooms in a single token\"\n );\n }\n\n perms = new Set<Permission>();\n this._permissions.set(roomId, perms);\n return perms;\n }\n }\n\n public allow(roomIdOrPattern: string, newPerms: readonly Permission[]): this {\n if (!roomPatternRegex.test(roomIdOrPattern)) {\n throw new Error(\"Invalid room name or pattern\");\n }\n\n if (newPerms.length === 0) {\n throw new Error(\"Permission list cannot be empty\");\n }\n\n const existingPerms = this.getOrCreate(roomIdOrPattern);\n for (const perm of newPerms) {\n if (!isPermission(perm as string)) {\n throw new Error(`Not a valid permission: ${perm}`);\n }\n existingPerms.add(perm);\n }\n return this; // To allow chaining multiple allow calls\n }\n\n /** @internal - For unit tests only */\n public hasPermissions(): boolean {\n return this._permissions.size > 0;\n }\n\n /** @internal - For unit tests only */\n public seal(): void {\n if (this._sealed) {\n throw new Error(\n \"You cannot reuse Session instances. Please create a new session every time.\"\n );\n }\n this._sealed = true;\n }\n\n /** @internal - For unit tests only */\n public serializePermissions(): Record<string, unknown> {\n return Object.fromEntries(\n Array.from(this._permissions.entries()).map(([pat, perms]) => [\n pat,\n Array.from(perms),\n ])\n );\n }\n\n /**\n * Call this to authorize the session to access Liveblocks. Note that this\n * will return a Liveblocks \"access token\". Anyone that obtains such access\n * token will have access to the allowed resources.\n */\n public async authorize(): Promise<AuthResponse> {\n this.seal();\n if (!this.hasPermissions()) {\n return {\n status: 403,\n body: \"Forbidden\",\n };\n }\n\n try {\n const resp = await this._postFn(\"/v2/authorize-user\", {\n // Required\n userId: this._userId,\n permissions: this.serializePermissions(),\n\n // Optional metadata\n userInfo: this._userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: 'Call to /v2/authorize-user failed. See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n }\n}\n","/**\n * NOTE: only types should be imported from @liveblocks/core.\n * This is because this package is made to be used in Node.js, and\n * @liveblocks/core has browser-specific code.\n */\nimport type { CommentData, ThreadData } from \"@liveblocks/core\";\n\nimport { Session } from \"./Session\";\nimport {\n assertNonEmpty,\n assertSecretKey,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\nexport type LiveblocksOptions = {\n /**\n * The Liveblocks secret key. Must start with \"sk_\".\n * Get it from https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\nexport type CreateSessionOptions = {\n userInfo: unknown;\n};\n\nexport type AuthResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\ntype Identity = {\n userId: string;\n groupIds: string[];\n};\n\ntype ThreadParticipants = {\n participantIds: string[];\n};\n\n/**\n * Interact with the Liveblocks API from your Node.js backend.\n */\nexport class Liveblocks {\n /** @internal */\n private readonly _secret: string;\n /** @internal */\n private readonly _baseUrl: URL;\n\n /**\n * Interact with the Liveblocks API from your Node.js backend.\n */\n constructor(options: LiveblocksOptions) {\n const options_ = options as Record<string, unknown>;\n const secret = options_.secret;\n assertSecretKey(secret, \"secret\");\n this._secret = secret;\n this._baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);\n }\n\n /** @internal */\n private async post(\n path: `/${string}`,\n json: Record<string, unknown>\n ): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(json),\n });\n }\n\n /** @internal */\n private async get(path: `/${string}`): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, { method: \"GET\", headers });\n }\n\n /**\n * Prepares a new session to authorize a user to access Liveblocks.\n *\n * IMPORTANT:\n * Always make sure that you trust the user making the request to your\n * backend before calling .prepareSession()!\n *\n * @param userId Tell Liveblocks the user ID of the user to authorize. Must\n * uniquely identify the user account in your system. The uniqueness of this\n * value will determine how many MAUs will be counted/billed.\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n *\n */\n prepareSession(userId: string, options?: CreateSessionOptions): Session {\n return new Session(this.post.bind(this), userId, options?.userInfo);\n }\n\n /**\n * Call this to authenticate the user as an actor you want to allow to use\n * Liveblocks.\n *\n * You should use this method only if you want to manage your permissions\n * through the Liveblocks Permissions API. This method is more complicated to\n * set up, but allows for finer-grained specification of permissions.\n *\n * Calling `.identifyUser()` only lets you securely identify a user (and what\n * groups they belong to). What permissions this user will end up having is\n * determined by whatever permissions you assign the user/group in your\n * Liveblocks account, through the Permissions API:\n * https://liveblocks.io/docs/rooms/permissions\n *\n * IMPORTANT:\n * Always verify that you trust the user making the request before calling\n * .identifyUser()!\n *\n * @param identity Tell Liveblocks the user ID of the user to authenticate.\n * Must uniquely identify the user account in your system. The uniqueness of\n * this value will determine how many MAUs will be counted/billed.\n *\n * If you also want to assign which groups this user belongs to, use the\n * object form and specify the `groupIds` property. Those `groupIds` should\n * match the groupIds you assigned permissions to via the Liveblocks\n * Permissions API, see\n * https://liveblocks.io/docs/rooms/permissions#permissions-levels-groups-accesses-example\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n */\n // These fields define the security identity of the user. Whatever you pass in here will define which\n public async identifyUser(\n identity:\n | string // Shorthand for userId\n | Identity,\n options?: {\n userInfo: unknown;\n // ....\n }\n ): Promise<AuthResponse> {\n const path = \"/v2/identify-user\";\n const userId = typeof identity === \"string\" ? identity : identity.userId;\n const groupIds =\n typeof identity === \"string\" ? undefined : identity.groupIds;\n\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n // assertStringArrayOrUndefined(groupsIds, \"groupIds\"); // TODO: Check if this is a legal userId value too\n\n try {\n const resp = await this.post(path, {\n userId,\n groupIds,\n\n // Optional metadata\n userInfo: options?.userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: `Call to ${urljoin(\n this._baseUrl,\n path\n )} failed. See \"error\" for more information.`,\n error: er as Error | undefined,\n };\n }\n }\n\n /**\n * Gets all the threads in a room.\n *\n * @param params.roomId The room ID to get the threads from.\n * @returns A list of threads.\n */\n public async getThreads(params: { roomId: string }): Promise<ThreadData[]> {\n const { roomId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads`\n );\n\n const body = await (resp.json() as Promise<ThreadData[]>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread.\n *\n * @param params.roomId The room ID to get the thread from.\n * @param params.threadId The thread ID.\n * @returns A thread.\n */\n public async getThread(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadData> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}`\n );\n\n const body = await (resp.json() as Promise<ThreadData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's participants.\n *\n * Participants are users who have commented on the thread\n * or users and groups that have been mentioned in a comment.\n *\n * @param params.roomId The room ID to get the thread participants from.\n * @param params.threadId The thread ID to get the participants from.\n * @returns An object containing an array of participant IDs.\n */\n public async getThreadParticipants(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadParticipants> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/participants`\n );\n\n const body = await (resp.json() as Promise<ThreadParticipants>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's comment.\n *\n * @param params.roomId The room ID to get the comment from.\n * @param params.threadId The thread ID to get the comment from.\n * @param params.commentId The comment ID.\n * @returns A comment.\n */\n public async getComment(params: {\n roomId: string;\n threadId: string;\n commentId: string;\n }): Promise<CommentData> {\n const { roomId, threadId, commentId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/comments/${encodeURIComponent(commentId)}`\n );\n\n const body = await (resp.json() as Promise<CommentData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n}\n","import * as base64 from \"@stablelib/base64\";\nimport * as sha256 from \"fast-sha256\";\nimport type { IncomingHttpHeaders } from \"http\";\n\nexport class WebhookHandler {\n private secretBuffer: Buffer;\n private static secretPrefix = \"whsec_\";\n\n constructor(\n /**\n * The signing secret provided on the dashboard's webhooks page\n * @example \"whsec_wPbvQ+u3VtN2e2tRPDKchQ1tBZ3svaHLm\"\n */\n secret: string\n ) {\n if (!secret) throw new Error(\"Secret is required\");\n if (typeof secret !== \"string\") throw new Error(\"Secret must be a string\");\n\n if (secret.startsWith(WebhookHandler.secretPrefix) === false)\n throw new Error(\"Invalid secret, must start with whsec_\");\n\n const secretKey = secret.slice(WebhookHandler.secretPrefix.length);\n this.secretBuffer = Buffer.from(secretKey, \"base64\");\n }\n\n /**\n * Verifies a webhook request and returns the event\n */\n public verifyRequest(request: WebhookRequest): WebhookEvent {\n const { webhookId, timestamp, rawSignatures } = this.verifyHeaders(\n request.headers\n );\n\n this.verifyTimestamp(timestamp);\n\n const signature = this.sign(`${webhookId}.${timestamp}.${request.rawBody}`);\n\n const expectedSignatures = rawSignatures\n .split(\" \")\n .map((rawSignature) => {\n const [, parsedSignature] = rawSignature.split(\",\");\n return parsedSignature;\n })\n .filter(isNotUndefined);\n\n if (expectedSignatures.includes(signature) === false)\n throw new Error(\n `Invalid signature, expected one of ${expectedSignatures.join(\n \", \"\n )}, got ${signature}`\n );\n\n const event: WebhookEvent = JSON.parse(request.rawBody) as WebhookEvent;\n\n this.verifyWebhookEventType(event);\n\n return event;\n }\n\n /**\n * Verifies the headers and returns the webhookId, timestamp and rawSignatures\n */\n private verifyHeaders(headers: IncomingHttpHeaders | Headers) {\n const usingNativeHeaders =\n typeof Headers !== \"undefined\" && headers instanceof Headers;\n const normalizedHeaders = usingNativeHeaders\n ? Object.fromEntries(headers)\n : (headers as IncomingHttpHeaders);\n\n const sanitizedHeaders: IncomingHttpHeaders = {};\n Object.keys(normalizedHeaders).forEach((key) => {\n sanitizedHeaders[key.toLowerCase()] = normalizedHeaders[key];\n });\n\n const webhookId = sanitizedHeaders[\"webhook-id\"];\n if (typeof webhookId !== \"string\")\n throw new Error(\"Invalid webhook-id header\");\n\n const timestamp = sanitizedHeaders[\"webhook-timestamp\"];\n if (typeof timestamp !== \"string\")\n throw new Error(\"Invalid webhook-timestamp header\");\n\n const rawSignatures = sanitizedHeaders[\"webhook-signature\"];\n if (typeof rawSignatures !== \"string\")\n throw new Error(\"Invalid webhook-signature header\");\n\n return { webhookId, timestamp, rawSignatures };\n }\n\n /**\n * Signs the content with the secret\n * @param content\n * @returns `string`\n */\n private sign(content: string): string {\n const encoder = new TextEncoder();\n const toSign = encoder.encode(content);\n return base64.encode(sha256.hmac(this.secretBuffer, toSign));\n }\n\n /**\n * Verifies that the timestamp is not too old or in the future\n */\n private verifyTimestamp(timestampHeader: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(timestampHeader, 10);\n\n if (isNaN(timestamp)) {\n throw new Error(\"Invalid timestamp\");\n }\n\n // Check if timestamp is too old\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp too old\");\n }\n\n // Check if timestamp is in the future\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp in the future\");\n }\n }\n\n /**\n * Ensures that the event is a known event type\n * or throws and prompts the user to upgrade to a higher version of @liveblocks/node\n */\n private verifyWebhookEventType(\n event: WebhookEvent\n ): asserts event is WebhookEvent {\n if (\n event &&\n event.type &&\n [\n \"storageUpdated\",\n \"userEntered\",\n \"userLeft\",\n \"roomCreated\",\n \"roomDeleted\",\n \"commentCreated\",\n \"commentEdited\",\n \"commentDeleted\",\n \"commentReactionAdded\",\n \"commentReactionRemoved\",\n \"threadMetadataUpdated\",\n \"threadCreated\",\n \"ydocUpdated\",\n ].includes(event.type)\n )\n return;\n\n throw new Error(\n \"Unknown event type, please upgrade to a higher version of @liveblocks/node\"\n );\n }\n}\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nconst isNotUndefined = <T>(value: T | undefined): value is T =>\n value !== undefined;\n\ntype WebhookRequest = {\n /**\n * Headers of the request, can be a regular object or a Headers object\n * @example\n * {\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }\n *\n * new Headers({\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }}\n */\n headers: IncomingHttpHeaders | Headers;\n /**\n * Raw body of the request, do not parse it\n * @example '{\"type\":\"storageUpdated\",\"data\":{\"roomId\":\"my-room-id\",\"appId\":\"my-app-id\",\"updatedAt\":\"2021-03-01T12:00:00.000Z\"}}'\n */\n rawBody: string;\n};\n\ntype WebhookEvent =\n | StorageUpdatedEvent\n | UserEnteredEvent\n | UserLeftEvent\n | RoomCreatedEvent\n | RoomDeletedEvent\n | CommentCreatedEvent\n | CommentEditedEvent\n | CommentDeletedEvent\n | CommentReactionAdded\n | CommentReactionRemoved\n | ThreadMetadataUpdatedEvent\n | ThreadCreatedEvent\n | YDocUpdatedEvent;\n\ntype StorageUpdatedEvent = {\n type: \"storageUpdated\";\n data: {\n roomId: string;\n projectId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedAt: string;\n };\n};\n\ntype UserEnteredEvent = {\n type: \"userEntered\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user entered the room.\n */\n enteredAt: string;\n numActiveUsers: number;\n };\n};\n\ntype UserLeftEvent = {\n type: \"userLeft\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user left the room.\n */\n leftAt: string;\n numActiveUsers: number;\n };\n};\n\ntype RoomCreatedEvent = {\n type: \"roomCreated\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n };\n};\n\ntype RoomDeletedEvent = {\n type: \"roomDeleted\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentCreatedEvent = {\n type: \"commentCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\ntype CommentEditedEvent = {\n type: \"commentEdited\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n editedAt: string;\n };\n};\n\ntype CommentDeletedEvent = {\n type: \"commentDeleted\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentReactionAdded = {\n type: \"commentReactionAdded\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n addedAt: string;\n addedBy: string;\n };\n};\n\ntype CommentReactionRemoved = {\n type: \"commentReactionRemoved\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n removedAt: string;\n removedBy: string;\n };\n};\n\ntype YDocUpdatedEvent = {\n type: \"ydocUpdated\";\n data: {\n projectId: string;\n roomId: string;\n };\n};\n\ntype ThreadMetadataUpdatedEvent = {\n type: \"threadMetadataUpdated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n updatedAt: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedBy: string;\n };\n};\n\ntype ThreadCreatedEvent = {\n type: \"threadCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\nexport type {\n CommentCreatedEvent,\n CommentDeletedEvent,\n CommentEditedEvent,\n CommentReactionAdded,\n CommentReactionRemoved,\n RoomCreatedEvent,\n RoomDeletedEvent,\n StorageUpdatedEvent,\n ThreadCreatedEvent,\n ThreadMetadataUpdatedEvent,\n UserEnteredEvent,\n UserLeftEvent,\n WebhookEvent,\n WebhookRequest,\n YDocUpdatedEvent,\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/authorize.ts","../src/Session.ts","../src/client.ts","../src/webhooks.ts"],"sourcesContent":["export const DEFAULT_BASE_URL = \"https://api.liveblocks.io\";\n\nexport async function fetchPolyfill(): Promise<typeof fetch> {\n return typeof globalThis.fetch !== \"undefined\"\n ? globalThis.fetch\n : ((await import(\"node-fetch\")).default as unknown as typeof fetch);\n}\n\nexport function isNonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nexport function assertNonEmpty(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value)) {\n throw new Error(\n `Invalid value for field \"${field}\". Please provide a non-empty string. For more information: https://liveblocks.io/docs/api-reference/liveblocks-node#authorize`\n );\n }\n}\n\nexport function assertSecretKey(\n value: unknown,\n field: string\n): asserts value is string {\n if (!isNonEmpty(value) || !value.startsWith(\"sk_\")) {\n throw new Error(\n `Invalid value for field \"${field}\". Secret keys must start with \"sk_\". Please provide the secret key from your Liveblocks dashboard at https://liveblocks.io/dashboard/apikeys.`\n );\n }\n}\n\nexport function normalizeStatusCode(statusCode: number): number {\n if (statusCode >= 200 && statusCode < 300) {\n return 200; /* OK */\n } else if (statusCode >= 500) {\n return 503; /* Service Unavailable */\n } else {\n return 403; /* Forbidden */\n }\n}\n\ntype QueryParams =\n | Record<string, string | number | null | undefined>\n | URLSearchParams;\n\n/**\n * Safely but conveniently build a URLSearchParams instance from a given\n * dictionary of values. For example:\n *\n * {\n * \"foo\": \"bar+qux/baz\",\n * \"empty\": \"\",\n * \"n\": 42,\n * \"nope\": undefined,\n * \"alsonope\": null,\n * }\n *\n * Will produce a value that will get serialized as\n * `foo=bar%2Bqux%2Fbaz&empty=&n=42`.\n *\n * Notice how the number is converted to its string representation\n * automatically and the `null`/`undefined` values simply don't end up in the\n * URL.\n */\nfunction toURLSearchParams(\n params: Record<string, string | number | null | undefined>\n): URLSearchParams {\n const result = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n result.set(key, value.toString());\n }\n }\n return result;\n}\n\n/**\n * Concatenates a path to an existing URL.\n */\nexport function urljoin(\n baseUrl: string | URL,\n path: string,\n params?: QueryParams\n): string {\n // First, sanitize by removing user/passwd/search/hash parts from the URL\n const url = new URL(path, baseUrl);\n if (params !== undefined) {\n url.search = (\n params instanceof URLSearchParams ? params : toURLSearchParams(params)\n ).toString();\n }\n return url.toString();\n}\n","import {\n assertNonEmpty,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeOptions = {\n /**\n * The secret API key for your Liveblocks account. You can find it on\n * https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * The room ID for which to authorize the user. This will authorize the user\n * to enter the Liveblocks room.\n */\n room: string;\n\n /**\n * Associates a user ID to the session that is being authorized. The user ID\n * is typically set to the user ID from your own database.\n *\n * It can also be used to generate a token that gives access to a private\n * room where the userId is configured in the room accesses.\n *\n * This user ID will be used as the unique identifier to compute your\n * Liveblocks account's Monthly Active Users.\n */\n userId: string;\n\n /**\n * Arbitrary metadata associated to this user session.\n *\n * You can use it to store a small amount of static metadata for a user\n * session. It is public information, that will be visible to other users in\n * the same room, like name, avatar URL, etc.\n *\n * It's only suitable for static info that won't change during a session. If\n * you want to store dynamic metadata on a user session, don't keep that in\n * the session token, but use Presence instead.\n *\n * Can't exceed 1KB when serialized as JSON.\n */\n userInfo?: unknown;\n\n /**\n * Tell Liveblocks which group IDs this user belongs to. This will authorize\n * the user session to access private rooms that have at least one of these\n * group IDs listed in their room access configuration.\n *\n * See https://liveblocks.io/docs/guides/managing-rooms-users-permissions#permissions\n * for how to configure your room's permissions to use this feature.\n */\n groupIds?: string[];\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\n/**\n * TODO Officially mark as DEPRECATED, point to migration guide.\n */\ntype AuthorizeResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\n/**\n * @deprecated Since 1.2, we’re deprecating single-room tokens in favor of\n * either access tokens or ID tokens. Single-room tokens are still supported,\n * but support for them will be dropped in the future. Please refer to our\n * Upgrade Guide to learn how to adopt the new-style authorization, see\n * https://liveblocks.io/docs/platform/upgrading/1.2\n *\n * Tells Liveblocks that a user should be allowed access to a room, which user\n * this session is for, and what metadata to associate with the user (like\n * name, avatar, etc.)\n *\n * @example\n * export default async function auth(req, res) {\n *\n * // Implement your own security here.\n *\n * const room = req.body.room;\n * const response = await authorize({\n * room,\n * secret,\n * userId: \"123\",\n * userInfo: { // Optional\n * name: \"Ada Lovelace\"\n * },\n * groupIds: [\"group1\"] // Optional\n * });\n * return res.status(response.status).end(response.body);\n * }\n */\nexport async function authorize(\n options: AuthorizeOptions\n): Promise<AuthorizeResponse> {\n let url = null;\n try {\n const { room, secret, userId, userInfo, groupIds } =\n // Ensure we'll validate inputs at runtime\n options as Record<string, unknown>;\n\n assertNonEmpty(secret, \"secret\");\n assertNonEmpty(room, \"room\");\n assertNonEmpty(userId, \"userId\");\n\n url = buildLiveblocksAuthorizeEndpoint(options, room);\n\n const fetch = await fetchPolyfill();\n const resp = await fetch(buildLiveblocksAuthorizeEndpoint(options, room), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${secret}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n userId,\n userInfo,\n groupIds,\n }),\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body:\n (url ? `Call to \"${url}\" failed.` : \"Invalid authorize request.\") +\n ' See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n}\n\nfunction buildLiveblocksAuthorizeEndpoint(\n options: AuthorizeOptions,\n roomId: string\n): string {\n const path = `/v2/rooms/${encodeURIComponent(roomId)}/authorize`;\n return urljoin(options.baseUrl || DEFAULT_BASE_URL, path);\n}\n","import type { AuthResponse } from \"./client\";\nimport { assertNonEmpty, normalizeStatusCode } from \"./utils\";\n\n// As defined in the source of truth in ApiScope in\n// https://github.com/liveblocks/liveblocks-cloudflare/blob/main/src/security.ts\nconst ALL_PERMISSIONS = Object.freeze([\n \"room:write\",\n \"room:read\",\n \"room:presence:write\",\n \"comments:write\",\n \"comments:read\",\n] as const);\n\nexport type Permission = (typeof ALL_PERMISSIONS)[number];\n\nfunction isPermission(value: string): value is Permission {\n return (ALL_PERMISSIONS as readonly unknown[]).includes(value);\n}\n\nconst MAX_PERMS_PER_SET = 10;\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * read permissions to the storage and comments data for this room. (Note that\n * the user will still have permissions to update their own presence.)\n */\nconst READ_ACCESS = Object.freeze([\n \"room:read\",\n \"room:presence:write\",\n \"comments:read\",\n] as const);\n\n/**\n * Assign this to a room (or wildcard pattern) if you want to grant the user\n * permissions to read and write to the room's storage and comments.\n */\nconst FULL_ACCESS = Object.freeze([\"room:write\", \"comments:write\"] as const);\n\nconst roomPatternRegex = /^[^*]{1,128}[*]?$/;\n\ntype PostFn = (\n path: `/${string}`,\n json: Record<string, unknown>\n) => Promise<Response>;\n\n/**\n * Class to help you construct the exact permission set to grant a user, used\n * when making `.authorizeUser()` calls.\n *\n * Usage:\n *\n * const session = liveblocks.prepareSession();\n * session.allow(roomId, permissions) // or...\n *\n * For the `permissions` argument, you can pass a list of specific permissions,\n * or use one of our presets:\n *\n * session.allow('my-room', session.FULL_ACCESS) // Read + write access to room storage and comments\n * session.allow('my-room', session.READ_ACCESS) // Read-only access to room storage and comments\n *\n * Rooms can be specified with a prefix match, if the name ends in an asterisk.\n * In that case, access is granted to *all* rooms that start with that prefix:\n *\n * // Read + write access to *all* rooms that start with \"abc:\"\n * session.allow('abc:*', session.FULL_ACCESS)\n *\n * You can define at most 10 room IDs (or patterns) in a single token,\n * otherwise the token would become too large and unwieldy.\n *\n * All permissions granted are additive. You cannot \"remove\" permissions once\n * you grant them. For example:\n *\n * session\n * .allow('abc:*', session.FULL_ACCESS)\n * .allow('abc:123', session.READ_ACCESS)\n *\n * Here, room `abc:123` would have full access. The second .allow() call only\n * _adds_ read permissions, but that has no effect since full access\n * permissions were already added to the set.\n */\nexport class Session {\n public readonly FULL_ACCESS = FULL_ACCESS;\n public readonly READ_ACCESS = READ_ACCESS;\n\n /** @internal */\n private _postFn: PostFn;\n /** @internal */\n private _userId: string;\n /** @internal */\n private _userInfo?: unknown;\n /** @internal */\n private _sealed = false;\n /** @internal */\n private readonly _permissions: Map<string, Set<Permission>> = new Map();\n\n /** @internal */\n constructor(postFn: PostFn, userId: string, userInfo?: unknown) {\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n\n this._postFn = postFn;\n this._userId = userId;\n this._userInfo = userInfo;\n }\n\n /** @internal */\n private getOrCreate(roomId: string): Set<Permission> {\n if (this._sealed) {\n throw new Error(\"You can no longer change these permissions.\");\n }\n\n let perms = this._permissions.get(roomId);\n if (perms) {\n return perms;\n } else {\n if (this._permissions.size >= MAX_PERMS_PER_SET) {\n throw new Error(\n \"You cannot add permissions for more than 10 rooms in a single token\"\n );\n }\n\n perms = new Set<Permission>();\n this._permissions.set(roomId, perms);\n return perms;\n }\n }\n\n public allow(roomIdOrPattern: string, newPerms: readonly Permission[]): this {\n if (!roomPatternRegex.test(roomIdOrPattern)) {\n throw new Error(\"Invalid room name or pattern\");\n }\n\n if (newPerms.length === 0) {\n throw new Error(\"Permission list cannot be empty\");\n }\n\n const existingPerms = this.getOrCreate(roomIdOrPattern);\n for (const perm of newPerms) {\n if (!isPermission(perm as string)) {\n throw new Error(`Not a valid permission: ${perm}`);\n }\n existingPerms.add(perm);\n }\n return this; // To allow chaining multiple allow calls\n }\n\n /** @internal - For unit tests only */\n public hasPermissions(): boolean {\n return this._permissions.size > 0;\n }\n\n /** @internal - For unit tests only */\n public seal(): void {\n if (this._sealed) {\n throw new Error(\n \"You cannot reuse Session instances. Please create a new session every time.\"\n );\n }\n this._sealed = true;\n }\n\n /** @internal - For unit tests only */\n public serializePermissions(): Record<string, unknown> {\n return Object.fromEntries(\n Array.from(this._permissions.entries()).map(([pat, perms]) => [\n pat,\n Array.from(perms),\n ])\n );\n }\n\n /**\n * Call this to authorize the session to access Liveblocks. Note that this\n * will return a Liveblocks \"access token\". Anyone that obtains such access\n * token will have access to the allowed resources.\n */\n public async authorize(): Promise<AuthResponse> {\n this.seal();\n if (!this.hasPermissions()) {\n return {\n status: 403,\n body: \"Forbidden\",\n };\n }\n\n try {\n const resp = await this._postFn(\"/v2/authorize-user\", {\n // Required\n userId: this._userId,\n permissions: this.serializePermissions(),\n\n // Optional metadata\n userInfo: this._userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: 'Call to /v2/authorize-user failed. See \"error\" for more information.',\n error: er as Error | undefined,\n };\n }\n }\n}\n","/**\n * NOTE: only types should be imported from @liveblocks/core.\n * This is because this package is made to be used in Node.js, and\n * @liveblocks/core has browser-specific code.\n */\nimport type { CommentData, ThreadData } from \"@liveblocks/core\";\n\nimport { Session } from \"./Session\";\nimport {\n assertNonEmpty,\n assertSecretKey,\n DEFAULT_BASE_URL,\n fetchPolyfill,\n normalizeStatusCode,\n urljoin,\n} from \"./utils\";\n\nexport type LiveblocksOptions = {\n /**\n * The Liveblocks secret key. Must start with \"sk_\".\n * Get it from https://liveblocks.io/dashboard/apikeys\n */\n secret: string;\n\n /**\n * @internal To point the client to a different Liveblocks server. Only\n * useful for Liveblocks developers. Not for end users.\n */\n baseUrl?: string;\n};\n\nexport type CreateSessionOptions = {\n userInfo: unknown;\n};\n\nexport type AuthResponse = {\n status: number;\n body: string;\n error?: Error;\n};\n\ntype Identity = {\n userId: string;\n groupIds: string[];\n};\n\ntype ThreadParticipants = {\n participantIds: string[];\n};\n\n/**\n * Interact with the Liveblocks API from your Node.js backend.\n */\nexport class Liveblocks {\n /** @internal */\n private readonly _secret: string;\n /** @internal */\n private readonly _baseUrl: URL;\n\n /**\n * Interact with the Liveblocks API from your Node.js backend.\n */\n constructor(options: LiveblocksOptions) {\n const options_ = options as Record<string, unknown>;\n const secret = options_.secret;\n assertSecretKey(secret, \"secret\");\n this._secret = secret;\n this._baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);\n }\n\n /** @internal */\n private async post(\n path: `/${string}`,\n json: Record<string, unknown>\n ): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(json),\n });\n }\n\n /** @internal */\n private async get(path: `/${string}`): Promise<Response> {\n const url = urljoin(this._baseUrl, path);\n const headers = {\n Authorization: `Bearer ${this._secret}`,\n \"Content-Type\": \"application/json\",\n };\n\n const fetch = await fetchPolyfill();\n return fetch(url, { method: \"GET\", headers });\n }\n\n /**\n * Prepares a new session to authorize a user to access Liveblocks.\n *\n * IMPORTANT:\n * Always make sure that you trust the user making the request to your\n * backend before calling .prepareSession()!\n *\n * @param userId Tell Liveblocks the user ID of the user to authorize. Must\n * uniquely identify the user account in your system. The uniqueness of this\n * value will determine how many MAUs will be counted/billed.\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n *\n */\n prepareSession(userId: string, options?: CreateSessionOptions): Session {\n return new Session(this.post.bind(this), userId, options?.userInfo);\n }\n\n /**\n * Call this to authenticate the user as an actor you want to allow to use\n * Liveblocks.\n *\n * You should use this method only if you want to manage your permissions\n * through the Liveblocks Permissions API. This method is more complicated to\n * set up, but allows for finer-grained specification of permissions.\n *\n * Calling `.identifyUser()` only lets you securely identify a user (and what\n * groups they belong to). What permissions this user will end up having is\n * determined by whatever permissions you assign the user/group in your\n * Liveblocks account, through the Permissions API:\n * https://liveblocks.io/docs/rooms/permissions\n *\n * IMPORTANT:\n * Always verify that you trust the user making the request before calling\n * .identifyUser()!\n *\n * @param identity Tell Liveblocks the user ID of the user to authenticate.\n * Must uniquely identify the user account in your system. The uniqueness of\n * this value will determine how many MAUs will be counted/billed.\n *\n * If you also want to assign which groups this user belongs to, use the\n * object form and specify the `groupIds` property. Those `groupIds` should\n * match the groupIds you assigned permissions to via the Liveblocks\n * Permissions API, see\n * https://liveblocks.io/docs/rooms/permissions#permissions-levels-groups-accesses-example\n *\n * @param options.userInfo Custom metadata to attach to this user. Data you\n * add here will be visible to all other clients in the room, through the\n * `other.info` property.\n */\n // These fields define the security identity of the user. Whatever you pass in here will define which\n public async identifyUser(\n identity:\n | string // Shorthand for userId\n | Identity,\n options?: {\n userInfo: unknown;\n // ....\n }\n ): Promise<AuthResponse> {\n const path = \"/v2/identify-user\";\n const userId = typeof identity === \"string\" ? identity : identity.userId;\n const groupIds =\n typeof identity === \"string\" ? undefined : identity.groupIds;\n\n assertNonEmpty(userId, \"userId\"); // TODO: Check if this is a legal userId value too\n // assertStringArrayOrUndefined(groupsIds, \"groupIds\"); // TODO: Check if this is a legal userId value too\n\n try {\n const resp = await this.post(path, {\n userId,\n groupIds,\n\n // Optional metadata\n userInfo: options?.userInfo,\n });\n\n return {\n status: normalizeStatusCode(resp.status),\n body: await resp.text(),\n };\n } catch (er) {\n return {\n status: 503 /* Service Unavailable */,\n body: `Call to ${urljoin(\n this._baseUrl,\n path\n )} failed. See \"error\" for more information.`,\n error: er as Error | undefined,\n };\n }\n }\n\n /**\n * Gets all the threads in a room.\n *\n * @param params.roomId The room ID to get the threads from.\n * @returns A list of threads.\n */\n public async getThreads(params: { roomId: string }): Promise<ThreadData[]> {\n const { roomId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads`\n );\n\n const body = await (resp.json() as Promise<ThreadData[]>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread.\n *\n * @param params.roomId The room ID to get the thread from.\n * @param params.threadId The thread ID.\n * @returns A thread.\n */\n public async getThread(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadData> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}`\n );\n\n const body = await (resp.json() as Promise<ThreadData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's participants.\n *\n * Participants are users who have commented on the thread\n * or users and groups that have been mentioned in a comment.\n *\n * @param params.roomId The room ID to get the thread participants from.\n * @param params.threadId The thread ID to get the participants from.\n * @returns An object containing an array of participant IDs.\n */\n public async getThreadParticipants(params: {\n roomId: string;\n threadId: string;\n }): Promise<ThreadParticipants> {\n const { roomId, threadId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/participants`\n );\n\n const body = await (resp.json() as Promise<ThreadParticipants>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n\n /**\n * Gets a thread's comment.\n *\n * @param params.roomId The room ID to get the comment from.\n * @param params.threadId The thread ID to get the comment from.\n * @param params.commentId The comment ID.\n * @returns A comment.\n */\n public async getComment(params: {\n roomId: string;\n threadId: string;\n commentId: string;\n }): Promise<CommentData> {\n const { roomId, threadId, commentId } = params;\n\n const resp = await this.get(\n `/v2/rooms/${encodeURIComponent(roomId)}/threads/${encodeURIComponent(\n threadId\n )}/comments/${encodeURIComponent(commentId)}`\n );\n\n const body = await (resp.json() as Promise<CommentData>);\n\n if (resp.status !== 200) {\n throw {\n status: resp.status,\n ...body,\n };\n }\n\n return body;\n }\n}\n","import * as base64 from \"@stablelib/base64\";\nimport * as sha256 from \"fast-sha256\";\nimport type { IncomingHttpHeaders } from \"http\";\n\nexport class WebhookHandler {\n private secretBuffer: Buffer;\n private static secretPrefix = \"whsec_\";\n\n constructor(\n /**\n * The signing secret provided on the dashboard's webhooks page\n * @example \"whsec_wPbvQ+u3VtN2e2tRPDKchQ1tBZ3svaHLm\"\n */\n secret: string\n ) {\n if (!secret) throw new Error(\"Secret is required\");\n if (typeof secret !== \"string\") throw new Error(\"Secret must be a string\");\n\n if (secret.startsWith(WebhookHandler.secretPrefix) === false)\n throw new Error(\"Invalid secret, must start with whsec_\");\n\n const secretKey = secret.slice(WebhookHandler.secretPrefix.length);\n this.secretBuffer = Buffer.from(secretKey, \"base64\");\n }\n\n /**\n * Verifies a webhook request and returns the event\n */\n public verifyRequest(request: WebhookRequest): WebhookEvent {\n const { webhookId, timestamp, rawSignatures } = this.verifyHeaders(\n request.headers\n );\n\n this.verifyTimestamp(timestamp);\n\n const signature = this.sign(`${webhookId}.${timestamp}.${request.rawBody}`);\n\n const expectedSignatures = rawSignatures\n .split(\" \")\n .map((rawSignature) => {\n const [, parsedSignature] = rawSignature.split(\",\");\n return parsedSignature;\n })\n .filter(isNotUndefined);\n\n if (expectedSignatures.includes(signature) === false)\n throw new Error(\n `Invalid signature, expected one of ${expectedSignatures.join(\n \", \"\n )}, got ${signature}`\n );\n\n const event: WebhookEvent = JSON.parse(request.rawBody) as WebhookEvent;\n\n this.verifyWebhookEventType(event);\n\n return event;\n }\n\n /**\n * Verifies the headers and returns the webhookId, timestamp and rawSignatures\n */\n private verifyHeaders(headers: IncomingHttpHeaders | Headers) {\n const usingNativeHeaders =\n typeof Headers !== \"undefined\" && headers instanceof Headers;\n const normalizedHeaders = usingNativeHeaders\n ? Object.fromEntries(headers)\n : (headers as IncomingHttpHeaders);\n\n const sanitizedHeaders: IncomingHttpHeaders = {};\n Object.keys(normalizedHeaders).forEach((key) => {\n sanitizedHeaders[key.toLowerCase()] = normalizedHeaders[key];\n });\n\n const webhookId = sanitizedHeaders[\"webhook-id\"];\n if (typeof webhookId !== \"string\")\n throw new Error(\"Invalid webhook-id header\");\n\n const timestamp = sanitizedHeaders[\"webhook-timestamp\"];\n if (typeof timestamp !== \"string\")\n throw new Error(\"Invalid webhook-timestamp header\");\n\n const rawSignatures = sanitizedHeaders[\"webhook-signature\"];\n if (typeof rawSignatures !== \"string\")\n throw new Error(\"Invalid webhook-signature header\");\n\n return { webhookId, timestamp, rawSignatures };\n }\n\n /**\n * Signs the content with the secret\n * @param content\n * @returns `string`\n */\n private sign(content: string): string {\n const encoder = new TextEncoder();\n const toSign = encoder.encode(content);\n return base64.encode(sha256.hmac(this.secretBuffer, toSign));\n }\n\n /**\n * Verifies that the timestamp is not too old or in the future\n */\n private verifyTimestamp(timestampHeader: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = parseInt(timestampHeader, 10);\n\n if (isNaN(timestamp)) {\n throw new Error(\"Invalid timestamp\");\n }\n\n // Check if timestamp is too old\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp too old\");\n }\n\n // Check if timestamp is in the future\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw new Error(\"Timestamp in the future\");\n }\n }\n\n /**\n * Ensures that the event is a known event type\n * or throws and prompts the user to upgrade to a higher version of @liveblocks/node\n */\n private verifyWebhookEventType(\n event: WebhookEvent\n ): asserts event is WebhookEvent {\n if (\n event &&\n event.type &&\n [\n \"storageUpdated\",\n \"userEntered\",\n \"userLeft\",\n \"roomCreated\",\n \"roomDeleted\",\n \"commentCreated\",\n \"commentEdited\",\n \"commentDeleted\",\n \"commentReactionAdded\",\n \"commentReactionRemoved\",\n \"threadMetadataUpdated\",\n \"threadCreated\",\n \"ydocUpdated\",\n ].includes(event.type)\n )\n return;\n\n throw new Error(\n \"Unknown event type, please upgrade to a higher version of @liveblocks/node\"\n );\n }\n}\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nconst isNotUndefined = <T>(value: T | undefined): value is T =>\n value !== undefined;\n\ntype WebhookRequest = {\n /**\n * Headers of the request, can be a regular object or a Headers object\n * @example\n * {\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }\n *\n * new Headers({\n * \"webhook-id\": \"123\",\n * \"webhook-timestamp\": \"1614588800000\",\n * \"webhook-signature\": \"v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\"\n * }}\n */\n headers: IncomingHttpHeaders | Headers;\n /**\n * Raw body of the request, do not parse it\n * @example '{\"type\":\"storageUpdated\",\"data\":{\"roomId\":\"my-room-id\",\"appId\":\"my-app-id\",\"updatedAt\":\"2021-03-01T12:00:00.000Z\"}}'\n */\n rawBody: string;\n};\n\ntype WebhookEvent =\n | StorageUpdatedEvent\n | UserEnteredEvent\n | UserLeftEvent\n | RoomCreatedEvent\n | RoomDeletedEvent\n | CommentCreatedEvent\n | CommentEditedEvent\n | CommentDeletedEvent\n | CommentReactionAdded\n | CommentReactionRemoved\n | ThreadMetadataUpdatedEvent\n | ThreadCreatedEvent\n | YDocUpdatedEvent;\n\ntype StorageUpdatedEvent = {\n type: \"storageUpdated\";\n data: {\n roomId: string;\n projectId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedAt: string;\n };\n};\n\ntype UserEnteredEvent = {\n type: \"userEntered\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user entered the room.\n */\n enteredAt: string;\n numActiveUsers: number;\n };\n};\n\ntype UserLeftEvent = {\n type: \"userLeft\";\n data: {\n projectId: string;\n roomId: string;\n connectionId: number;\n userId: string | null;\n userInfo: Record<string, unknown> | null;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n * @description The time when the user left the room.\n */\n leftAt: string;\n numActiveUsers: number;\n };\n};\n\ntype RoomCreatedEvent = {\n type: \"roomCreated\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n };\n};\n\ntype RoomDeletedEvent = {\n type: \"roomDeleted\";\n data: {\n projectId: string;\n roomId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentCreatedEvent = {\n type: \"commentCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\ntype CommentEditedEvent = {\n type: \"commentEdited\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n editedAt: string;\n };\n};\n\ntype CommentDeletedEvent = {\n type: \"commentDeleted\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n deletedAt: string;\n };\n};\n\ntype CommentReactionAdded = {\n type: \"commentReactionAdded\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n addedAt: string;\n addedBy: string;\n };\n};\n\ntype CommentReactionRemoved = {\n type: \"commentReactionRemoved\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n commentId: string;\n emoji: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n removedAt: string;\n removedBy: string;\n };\n};\n\ntype YDocUpdatedEvent = {\n type: \"ydocUpdated\";\n data: {\n projectId: string;\n roomId: string;\n };\n};\n\ntype ThreadMetadataUpdatedEvent = {\n type: \"threadMetadataUpdated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n updatedAt: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n updatedBy: string;\n };\n};\n\ntype ThreadCreatedEvent = {\n type: \"threadCreated\";\n data: {\n projectId: string;\n roomId: string;\n threadId: string;\n /**\n * ISO 8601 datestring\n * @example \"2021-03-01T12:00:00.000Z\"\n */\n createdAt: string;\n createdBy: string;\n };\n};\n\nexport type {\n CommentCreatedEvent,\n CommentDeletedEvent,\n CommentEditedEvent,\n CommentReactionAdded,\n CommentReactionRemoved,\n RoomCreatedEvent,\n RoomDeletedEvent,\n StorageUpdatedEvent,\n ThreadCreatedEvent,\n ThreadMetadataUpdatedEvent,\n UserEnteredEvent,\n UserLeftEvent,\n WebhookEvent,\n WebhookRequest,\n YDocUpdatedEvent,\n};\n"],"mappings":";AAAO,IAAM,mBAAmB;AAEhC,eAAsB,gBAAuC;AAC3D,SAAO,OAAO,WAAW,UAAU,cAC/B,WAAW,SACT,MAAM,OAAO,YAAY,GAAG;AACpC;AAEO,SAAS,WAAW,OAAiC;AAC1D,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS;AACrD;AAEO,SAAS,eACd,OACA,OACyB;AACzB,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,4BAA4B,KAAK;AAAA,IACnC;AAAA,EACF;AACF;AAEO,SAAS,gBACd,OACA,OACyB;AACzB,MAAI,CAAC,WAAW,KAAK,KAAK,CAAC,MAAM,WAAW,KAAK,GAAG;AAClD,UAAM,IAAI;AAAA,MACR,4BAA4B,KAAK;AAAA,IACnC;AAAA,EACF;AACF;AAEO,SAAS,oBAAoB,YAA4B;AAC9D,MAAI,cAAc,OAAO,aAAa,KAAK;AACzC,WAAO;AAAA,EACT,WAAW,cAAc,KAAK;AAC5B,WAAO;AAAA,EACT,OAAO;AACL,WAAO;AAAA,EACT;AACF;AAyBA,SAAS,kBACP,QACiB;AACjB,QAAM,SAAS,IAAI,gBAAgB;AACnC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,UAAa,UAAU,MAAM;AACzC,aAAO,IAAI,KAAK,MAAM,SAAS,CAAC;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,QACd,SACA,MACA,QACQ;AAER,QAAM,MAAM,IAAI,IAAI,MAAM,OAAO;AACjC,MAAI,WAAW,QAAW;AACxB,QAAI,UACF,kBAAkB,kBAAkB,SAAS,kBAAkB,MAAM,GACrE,SAAS;AAAA,EACb;AACA,SAAO,IAAI,SAAS;AACtB;;;ACWA,eAAsB,UACpB,SAC4B;AAC5B,MAAI,MAAM;AACV,MAAI;AACF,UAAM,EAAE,MAAM,QAAQ,QAAQ,UAAU,SAAS;AAAA;AAAA,MAE/C;AAAA;AAEF,mBAAe,QAAQ,QAAQ;AAC/B,mBAAe,MAAM,MAAM;AAC3B,mBAAe,QAAQ,QAAQ;AAE/B,UAAM,iCAAiC,SAAS,IAAI;AAEpD,UAAM,QAAQ,MAAM,cAAc;AAClC,UAAM,OAAO,MAAM,MAAM,iCAAiC,SAAS,IAAI,GAAG;AAAA,MACxE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,MACL,QAAQ,oBAAoB,KAAK,MAAM;AAAA,MACvC,MAAM,MAAM,KAAK,KAAK;AAAA,IACxB;AAAA,EACF,SAAS,IAAP;AACA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OACG,MAAM,YAAY,GAAG,cAAc,gCACpC;AAAA,MACF,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,iCACP,SACA,QACQ;AACR,QAAM,OAAO,aAAa,mBAAmB,MAAM,CAAC;AACpD,SAAO,QAAQ,QAAQ,WAAW,kBAAkB,IAAI;AAC1D;;;ACvJA,IAAM,kBAAkB,OAAO,OAAO;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAU;AAIV,SAAS,aAAa,OAAoC;AACxD,SAAQ,gBAAuC,SAAS,KAAK;AAC/D;AAEA,IAAM,oBAAoB;AAO1B,IAAM,cAAc,OAAO,OAAO;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AACF,CAAU;AAMV,IAAM,cAAc,OAAO,OAAO,CAAC,cAAc,gBAAgB,CAAU;AAE3E,IAAM,mBAAmB;AA0ClB,IAAM,UAAN,MAAc;AAAA;AAAA,EAgBnB,YAAY,QAAgB,QAAgB,UAAoB;AAfhE,SAAgB,cAAc;AAC9B,SAAgB,cAAc;AAS9B;AAAA,SAAQ,UAAU;AAElB;AAAA,SAAiB,eAA6C,oBAAI,IAAI;AAIpE,mBAAe,QAAQ,QAAQ;AAE/B,SAAK,UAAU;AACf,SAAK,UAAU;AACf,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA,EAGQ,YAAY,QAAiC;AACnD,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAEA,QAAI,QAAQ,KAAK,aAAa,IAAI,MAAM;AACxC,QAAI,OAAO;AACT,aAAO;AAAA,IACT,OAAO;AACL,UAAI,KAAK,aAAa,QAAQ,mBAAmB;AAC/C,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,oBAAI,IAAgB;AAC5B,WAAK,aAAa,IAAI,QAAQ,KAAK;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEO,MAAM,iBAAyB,UAAuC;AAC3E,QAAI,CAAC,iBAAiB,KAAK,eAAe,GAAG;AAC3C,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,UAAM,gBAAgB,KAAK,YAAY,eAAe;AACtD,eAAW,QAAQ,UAAU;AAC3B,UAAI,CAAC,aAAa,IAAc,GAAG;AACjC,cAAM,IAAI,MAAM,2BAA2B,IAAI,EAAE;AAAA,MACnD;AACA,oBAAc,IAAI,IAAI;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGO,iBAA0B;AAC/B,WAAO,KAAK,aAAa,OAAO;AAAA,EAClC;AAAA;AAAA,EAGO,OAAa;AAClB,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGO,uBAAgD;AACrD,WAAO,OAAO;AAAA,MACZ,MAAM,KAAK,KAAK,aAAa,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAAA,QAC5D;AAAA,QACA,MAAM,KAAK,KAAK;AAAA,MAClB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,YAAmC;AAC9C,SAAK,KAAK;AACV,QAAI,CAAC,KAAK,eAAe,GAAG;AAC1B,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,QAAQ,sBAAsB;AAAA;AAAA,QAEpD,QAAQ,KAAK;AAAA,QACb,aAAa,KAAK,qBAAqB;AAAA;AAAA,QAGvC,UAAU,KAAK;AAAA,MACjB,CAAC;AAED,aAAO;AAAA,QACL,QAAQ,oBAAoB,KAAK,MAAM;AAAA,QACvC,MAAM,MAAM,KAAK,KAAK;AAAA,MACxB;AAAA,IACF,SAAS,IAAP;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACzJO,IAAM,aAAN,MAAiB;AAAA;AAAA;AAAA;AAAA,EAStB,YAAY,SAA4B;AACtC,UAAM,WAAW;AACjB,UAAM,SAAS,SAAS;AACxB,oBAAgB,QAAQ,QAAQ;AAChC,SAAK,UAAU;AACf,SAAK,WAAW,IAAI,IAAI,QAAQ,WAAW,gBAAgB;AAAA,EAC7D;AAAA;AAAA,EAGA,MAAc,KACZ,MACA,MACmB;AACnB,UAAM,MAAM,QAAQ,KAAK,UAAU,IAAI;AACvC,UAAM,UAAU;AAAA,MACd,eAAe,UAAU,KAAK,OAAO;AAAA,MACrC,gBAAgB;AAAA,IAClB;AAEA,UAAM,QAAQ,MAAM,cAAc;AAClC,WAAO,MAAM,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAc,IAAI,MAAuC;AACvD,UAAM,MAAM,QAAQ,KAAK,UAAU,IAAI;AACvC,UAAM,UAAU;AAAA,MACd,eAAe,UAAU,KAAK,OAAO;AAAA,MACrC,gBAAgB;AAAA,IAClB;AAEA,UAAM,QAAQ,MAAM,cAAc;AAClC,WAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,QAAQ,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,eAAe,QAAgB,SAAyC;AACtE,WAAO,IAAI,QAAQ,KAAK,KAAK,KAAK,IAAI,GAAG,QAAQ,SAAS,QAAQ;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,MAAa,aACX,UAGA,SAIuB;AACvB,UAAM,OAAO;AACb,UAAM,SAAS,OAAO,aAAa,WAAW,WAAW,SAAS;AAClE,UAAM,WACJ,OAAO,aAAa,WAAW,SAAY,SAAS;AAEtD,mBAAe,QAAQ,QAAQ;AAG/B,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,KAAK,MAAM;AAAA,QACjC;AAAA,QACA;AAAA;AAAA,QAGA,UAAU,SAAS;AAAA,MACrB,CAAC;AAED,aAAO;AAAA,QACL,QAAQ,oBAAoB,KAAK,MAAM;AAAA,QACvC,MAAM,MAAM,KAAK,KAAK;AAAA,MACxB;AAAA,IACF,SAAS,IAAP;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,WAAW;AAAA,UACf,KAAK;AAAA,UACL;AAAA,QACF,CAAC;AAAA,QACD,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,WAAW,QAAmD;AACzE,UAAM,EAAE,OAAO,IAAI;AAEnB,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,aAAa,mBAAmB,MAAM,CAAC;AAAA,IACzC;AAEA,UAAM,OAAO,MAAO,KAAK,KAAK;AAE9B,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,GAAG;AAAA,MACL;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,UAAU,QAGC;AACtB,UAAM,EAAE,QAAQ,SAAS,IAAI;AAE7B,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,aAAa,mBAAmB,MAAM,CAAC,YAAY;AAAA,QACjD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,OAAO,MAAO,KAAK,KAAK;AAE9B,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,GAAG;AAAA,MACL;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAa,sBAAsB,QAGH;AAC9B,UAAM,EAAE,QAAQ,SAAS,IAAI;AAE7B,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,aAAa,mBAAmB,MAAM,CAAC,YAAY;AAAA,QACjD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,OAAO,MAAO,KAAK,KAAK;AAE9B,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,GAAG;AAAA,MACL;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,WAAW,QAIC;AACvB,UAAM,EAAE,QAAQ,UAAU,UAAU,IAAI;AAExC,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,aAAa,mBAAmB,MAAM,CAAC,YAAY;AAAA,QACjD;AAAA,MACF,CAAC,aAAa,mBAAmB,SAAS,CAAC;AAAA,IAC7C;AAEA,UAAM,OAAO,MAAO,KAAK,KAAK;AAE9B,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,GAAG;AAAA,MACL;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AC9TA,YAAY,YAAY;AACxB,YAAY,YAAY;AAGjB,IAAM,kBAAN,MAAM,gBAAe;AAAA,EAI1B,YAKE,QACA;AACA,QAAI,CAAC;AAAQ,YAAM,IAAI,MAAM,oBAAoB;AACjD,QAAI,OAAO,WAAW;AAAU,YAAM,IAAI,MAAM,yBAAyB;AAEzE,QAAI,OAAO,WAAW,gBAAe,YAAY,MAAM;AACrD,YAAM,IAAI,MAAM,wCAAwC;AAE1D,UAAM,YAAY,OAAO,MAAM,gBAAe,aAAa,MAAM;AACjE,SAAK,eAAe,OAAO,KAAK,WAAW,QAAQ;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKO,cAAc,SAAuC;AAC1D,UAAM,EAAE,WAAW,WAAW,cAAc,IAAI,KAAK;AAAA,MACnD,QAAQ;AAAA,IACV;AAEA,SAAK,gBAAgB,SAAS;AAE9B,UAAM,YAAY,KAAK,KAAK,GAAG,SAAS,IAAI,SAAS,IAAI,QAAQ,OAAO,EAAE;AAE1E,UAAM,qBAAqB,cACxB,MAAM,GAAG,EACT,IAAI,CAAC,iBAAiB;AACrB,YAAM,CAAC,EAAE,eAAe,IAAI,aAAa,MAAM,GAAG;AAClD,aAAO;AAAA,IACT,CAAC,EACA,OAAO,cAAc;AAExB,QAAI,mBAAmB,SAAS,SAAS,MAAM;AAC7C,YAAM,IAAI;AAAA,QACR,sCAAsC,mBAAmB;AAAA,UACvD;AAAA,QACF,CAAC,SAAS,SAAS;AAAA,MACrB;AAEF,UAAM,QAAsB,KAAK,MAAM,QAAQ,OAAO;AAEtD,SAAK,uBAAuB,KAAK;AAEjC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAAwC;AAC5D,UAAM,qBACJ,OAAO,YAAY,eAAe,mBAAmB;AACvD,UAAM,oBAAoB,qBACtB,OAAO,YAAY,OAAO,IACzB;AAEL,UAAM,mBAAwC,CAAC;AAC/C,WAAO,KAAK,iBAAiB,EAAE,QAAQ,CAAC,QAAQ;AAC9C,uBAAiB,IAAI,YAAY,CAAC,IAAI,kBAAkB,GAAG;AAAA,IAC7D,CAAC;AAED,UAAM,YAAY,iBAAiB,YAAY;AAC/C,QAAI,OAAO,cAAc;AACvB,YAAM,IAAI,MAAM,2BAA2B;AAE7C,UAAM,YAAY,iBAAiB,mBAAmB;AACtD,QAAI,OAAO,cAAc;AACvB,YAAM,IAAI,MAAM,kCAAkC;AAEpD,UAAM,gBAAgB,iBAAiB,mBAAmB;AAC1D,QAAI,OAAO,kBAAkB;AAC3B,YAAM,IAAI,MAAM,kCAAkC;AAEpD,WAAO,EAAE,WAAW,WAAW,cAAc;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,KAAK,SAAyB;AACpC,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,SAAS,QAAQ,OAAO,OAAO;AACrC,WAAc,cAAc,YAAK,KAAK,cAAc,MAAM,CAAC;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,iBAAyB;AAC/C,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,UAAM,YAAY,SAAS,iBAAiB,EAAE;AAE9C,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,QAAI,YAAY,MAAM,8BAA8B;AAClD,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,QAAI,YAAY,MAAM,8BAA8B;AAClD,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBACN,OAC+B;AAC/B,QACE,SACA,MAAM,QACN;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,SAAS,MAAM,IAAI;AAErB;AAEF,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAtJa,gBAEI,eAAe;AAFzB,IAAM,iBAAN;AAwJP,IAAM,+BAA+B,IAAI;AAEzC,IAAM,iBAAiB,CAAI,UACzB,UAAU;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/node",
|
|
3
|
-
"version": "1.7.1
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "A server-side utility that lets you set up a Liveblocks authentication endpoint. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"node-fetch": "^2.6.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@liveblocks/core": "1.7.1
|
|
40
|
+
"@liveblocks/core": "1.7.1",
|
|
41
41
|
"@liveblocks/eslint-config": "*",
|
|
42
42
|
"@liveblocks/jest-config": "*",
|
|
43
43
|
"@types/node": "^20.7.1",
|