diff --git a/engine/sdks/typescript/runner/src/tunnel.ts b/engine/sdks/typescript/runner/src/tunnel.ts index 5c93445392..c99a0d8e72 100644 --- a/engine/sdks/typescript/runner/src/tunnel.ts +++ b/engine/sdks/typescript/runner/src/tunnel.ts @@ -1,6 +1,6 @@ import type * as protocol from "@rivetkit/engine-runner-protocol"; import type { MessageId, RequestId } from "@rivetkit/engine-runner-protocol"; -import { v4 as uuidv4, stringify as uuidstringify } from "uuid"; +import { stringify as uuidstringify, v4 as uuidv4 } from "uuid"; import { logger } from "./log"; import type { ActorInstance, Runner } from "./mod"; import { unreachable } from "./utils"; @@ -77,12 +77,20 @@ export class Tunnel { // Build message const messageId = generateUuidBuffer(); - const requestIdStr = bufferToString(requestId); - this.#pendingTunnelMessages.set(bufferToString(messageId), { + const requestIdStr = idToStr(requestId); + const messageIdStr = idToStr(messageId); + this.#pendingTunnelMessages.set(messageIdStr, { sentAt: Date.now(), requestIdStr, }); + logger()?.debug({ + msg: "send tunnel msg", + requestId: requestIdStr, + messageId: messageIdStr, + message: messageKind, + }); + // Send message const message: protocol.ToServer = { tag: "ToServerTunnelMessage", @@ -111,8 +119,8 @@ export class Tunnel { logger()?.debug({ msg: "ack tunnel msg", - requestId: uuidstringify(new Uint8Array(requestId)), - messageId: uuidstringify(new Uint8Array(messageId)), + requestId: idToStr(requestId), + messageId: idToStr(messageId), }); this.#runner.__sendToServer(message); @@ -163,7 +171,10 @@ export class Tunnel { const webSocket = this.#actorWebSockets.get(requestIdStr); if (webSocket) { // Close the WebSocket connection - webSocket.__closeWithRetry(1000, "Message acknowledgment timeout"); + webSocket.__closeWithRetry( + 1000, + "Message acknowledgment timeout", + ); // Clean up from actorWebSockets map this.#actorWebSockets.delete(requestIdStr); @@ -207,7 +218,11 @@ export class Tunnel { actor.webSockets.clear(); } - async #fetch(actorId: string, requestId: protocol.RequestId, request: Request): Promise { + async #fetch( + actorId: string, + requestId: protocol.RequestId, + request: Request, + ): Promise { // Validate actor exists if (!this.#runner.hasActor(actorId)) { logger()?.warn({ @@ -219,7 +234,10 @@ export class Tunnel { // // See should_retry_request_inner // https://github.com/rivet-dev/rivet/blob/222dae87e3efccaffa2b503de40ecf8afd4e31eb/engine/packages/guard-core/src/proxy_service.rs#L2458 - return new Response("Actor not found", { status: 503, headers: { "x-rivet-error": "runner.actor_not_found" } }); + return new Response("Actor not found", { + status: 503, + headers: { "x-rivet-error": "runner.actor_not_found" }, + }); } const fetchHandler = this.#runner.config.fetch( @@ -237,19 +255,28 @@ export class Tunnel { } async handleTunnelMessage(message: protocol.ToClientTunnelMessage) { + const requestIdStr = idToStr(message.requestId); + const messageIdStr = idToStr(message.messageId); logger()?.debug({ - msg: "tunnel msg", - requestId: uuidstringify(new Uint8Array(message.requestId)), - messageId: uuidstringify(new Uint8Array(message.messageId)), + msg: "receive tunnel msg", + requestId: requestIdStr, + messageId: messageIdStr, message: message.messageKind, }); if (message.messageKind.tag === "TunnelAck") { // Mark pending message as acknowledged and remove it - const msgIdStr = bufferToString(message.messageId); - const pending = this.#pendingTunnelMessages.get(msgIdStr); + const pending = this.#pendingTunnelMessages.get(messageIdStr); if (pending) { - this.#pendingTunnelMessages.delete(msgIdStr); + const didDelete = + this.#pendingTunnelMessages.delete(messageIdStr); + if (!didDelete) { + logger()?.warn({ + msg: "received tunnel ack for nonexistent message", + requestId: requestIdStr, + messageId: messageIdStr, + }); + } } } else { switch (message.messageKind.tag) { @@ -282,14 +309,15 @@ export class Tunnel { message.messageKind.val, ); break; - case "ToClientWebSocketMessage": + case "ToClientWebSocketMessage": { this.#sendAck(message.requestId, message.messageId); - let _unhandled = await this.#handleWebSocketMessage( + const _unhandled = await this.#handleWebSocketMessage( message.requestId, message.messageKind.val, ); break; + } case "ToClientWebSocketClose": this.#sendAck(message.requestId, message.messageId); @@ -309,7 +337,7 @@ export class Tunnel { req: protocol.ToClientRequestStart, ) { // Track this request for the actor - const requestIdStr = bufferToString(requestId); + const requestIdStr = idToStr(requestId); const actor = this.#runner.getActor(req.actorId); if (actor) { actor.requests.add(requestIdStr); @@ -342,8 +370,8 @@ export class Tunnel { existing.actorId = req.actorId; } else { this.#actorPendingRequests.set(requestIdStr, { - resolve: () => { }, - reject: () => { }, + resolve: () => {}, + reject: () => {}, streamController: controller, actorId: req.actorId, }); @@ -366,7 +394,11 @@ export class Tunnel { await this.#sendResponse(requestId, response); } else { // Non-streaming request - const response = await this.#fetch(req.actorId, requestId, request); + const response = await this.#fetch( + req.actorId, + requestId, + request, + ); await this.#sendResponse(requestId, response); } } catch (error) { @@ -385,7 +417,7 @@ export class Tunnel { requestId: ArrayBuffer, chunk: protocol.ToClientRequestChunk, ) { - const requestIdStr = bufferToString(requestId); + const requestIdStr = idToStr(requestId); const pending = this.#actorPendingRequests.get(requestIdStr); if (pending?.streamController) { pending.streamController.enqueue(new Uint8Array(chunk.body)); @@ -397,7 +429,7 @@ export class Tunnel { } async #handleRequestAbort(requestId: ArrayBuffer) { - const requestIdStr = bufferToString(requestId); + const requestIdStr = idToStr(requestId); const pending = this.#actorPendingRequests.get(requestIdStr); if (pending?.streamController) { pending.streamController.error(new Error("Request aborted")); @@ -461,7 +493,7 @@ export class Tunnel { requestId: protocol.RequestId, open: protocol.ToClientWebSocketOpen, ) { - const webSocketId = bufferToString(requestId); + const webSocketId = idToStr(requestId); // Validate actor exists const actor = this.#runner.getActor(open.actorId); if (!actor) { @@ -518,7 +550,7 @@ export class Tunnel { const dataBuffer = typeof data === "string" ? (new TextEncoder().encode(data) - .buffer as ArrayBuffer) + .buffer as ArrayBuffer) : data; this.#sendMessage(requestId, { @@ -575,7 +607,12 @@ export class Tunnel { }); // Send open confirmation - let hibernationConfig = this.#runner.config.getActorHibernationConfig(actor.actorId, requestId, request); + const hibernationConfig = + this.#runner.config.getActorHibernationConfig( + actor.actorId, + requestId, + request, + ); this.#sendMessage(requestId, { tag: "ToServerWebSocketOpen", val: { @@ -587,8 +624,6 @@ export class Tunnel { // Notify adapter that connection is open adapter._handleOpen(requestId); - - // Call websocket handler await websocketHandler( this.#runner, @@ -623,14 +658,19 @@ export class Tunnel { requestId: ArrayBuffer, msg: protocol.ToClientWebSocketMessage, ): Promise { - const webSocketId = bufferToString(requestId); + const webSocketId = idToStr(requestId); const adapter = this.#actorWebSockets.get(webSocketId); if (adapter) { const data = msg.binary ? new Uint8Array(msg.data) : new TextDecoder().decode(new Uint8Array(msg.data)); - return adapter._handleMessage(requestId, data, msg.index, msg.binary); + return adapter._handleMessage( + requestId, + data, + msg.index, + msg.binary, + ); } else { return true; } @@ -639,11 +679,12 @@ export class Tunnel { __ackWebsocketMessage(requestId: ArrayBuffer, index: number) { logger()?.debug({ msg: "ack ws msg", - requestId: uuidstringify(new Uint8Array(requestId)), + requestId: idToStr(requestId), index, }); - if (index < 0 || index > 65535) throw new Error("invalid websocket ack index"); + if (index < 0 || index > 65535) + throw new Error("invalid websocket ack index"); // Send the ack message this.#sendMessage(requestId, { @@ -658,27 +699,26 @@ export class Tunnel { requestId: ArrayBuffer, close: protocol.ToClientWebSocketClose, ) { - const webSocketId = bufferToString(requestId); - const adapter = this.#actorWebSockets.get(webSocketId); + const requestIdStr = idToStr(requestId); + const adapter = this.#actorWebSockets.get(requestIdStr); if (adapter) { adapter._handleClose( requestId, close.code || undefined, close.reason || undefined, ); - this.#actorWebSockets.delete(webSocketId); + this.#actorWebSockets.delete(requestIdStr); } } } -/** Converts a buffer to a string. Used for storing strings in a lookup map. */ -function bufferToString(buffer: ArrayBuffer): string { - return Buffer.from(buffer).toString("base64"); -} - /** Generates a UUID as bytes. */ function generateUuidBuffer(): ArrayBuffer { const buffer = new Uint8Array(16); uuidv4(undefined, buffer); return buffer.buffer; } + +function idToStr(id: ArrayBuffer): string { + return uuidstringify(new Uint8Array(id)); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c1c2f8641..be0ce7bfd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1977,7 +1977,7 @@ importers: version: 3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@uiw/codemirror-extensions-basic-setup': specifier: ^4.25.1 - version: 4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) + version: 4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) '@uiw/codemirror-theme-github': specifier: ^4.25.1 version: 4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) @@ -2333,6 +2333,9 @@ importers: pino: specifier: ^9.5.0 version: 9.9.5 + uuid: + specifier: ^12.0.0 + version: 12.0.0 zod: specifier: ^3.25.76 version: 3.25.76 @@ -2448,16 +2451,16 @@ importers: version: 6.0.1 '@mdx-js/loader': specifier: ^3.1.1 - version: 3.1.1(webpack@5.101.3(esbuild@0.25.9)) + version: 3.1.1(webpack@5.101.3) '@mdx-js/react': specifier: ^3.1.1 version: 3.1.1(@types/react@19.2.2)(react@19.1.1) '@next/mdx': specifier: ^15.5.2 - version: 15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1)) + version: 15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1)) '@next/third-parties': specifier: latest - version: 16.0.0(next@15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1) + version: 16.0.1(next@15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1) '@rivet-gg/api': specifier: 0.0.1-rc8 version: 0.0.1-rc8 @@ -2644,7 +2647,7 @@ importers: version: 13.0.2(eslint@8.26.0)(typescript@5.9.2) file-loader: specifier: ^6.2.0 - version: 6.2.0(webpack@5.101.3(esbuild@0.25.9)) + version: 6.2.0(webpack@5.101.3) prettier: specifier: ^2.8.8 version: 2.8.8 @@ -5127,8 +5130,8 @@ packages: cpu: [x64] os: [win32] - '@next/third-parties@16.0.0': - resolution: {integrity: sha512-bSFRHDBj7VdfBVSJBigpeJYMGQgi/GuvWAQfxUEBNFTOxk6buLbfdAPBFCKwU1Tx3QWrGER6o82L3m5M2/jUaQ==} + '@next/third-parties@16.0.1': + resolution: {integrity: sha512-OFgku/XgeZPrPaI33qGqwoLkKW5cN5pY0VKETH2J+cUxY3iboJuYK5gRdcx+crjg/bEo0M38BhPrXHUxBUljwQ==} peerDependencies: next: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0 react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 @@ -16063,12 +16066,12 @@ snapshots: '@marijn/find-cluster-break@1.0.2': {} - '@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9))': + '@mdx-js/loader@3.1.1(webpack@5.101.3)': dependencies: '@mdx-js/mdx': 3.1.1 source-map: 0.7.6 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.101.3 transitivePeerDependencies: - supports-color @@ -16246,11 +16249,11 @@ snapshots: dependencies: glob: 7.1.7 - '@next/mdx@15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1))': + '@next/mdx@15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1))': dependencies: source-map: 0.7.6 optionalDependencies: - '@mdx-js/loader': 3.1.1(webpack@5.101.3(esbuild@0.25.9)) + '@mdx-js/loader': 3.1.1(webpack@5.101.3) '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.1.1) '@next/swc-darwin-arm64@15.4.5': @@ -16301,7 +16304,7 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.2': optional: true - '@next/third-parties@16.0.0(next@15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1)': + '@next/third-parties@16.0.1(next@15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1)': dependencies: next: 15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2) react: 19.1.1 @@ -18457,16 +18460,6 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.2 - '@uiw/codemirror-extensions-basic-setup@4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)': - dependencies: - '@codemirror/autocomplete': 6.19.0 - '@codemirror/commands': 6.9.0 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.0 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.2 - '@uiw/codemirror-theme-github@4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)': dependencies: '@uiw/codemirror-themes': 4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) @@ -20522,7 +20515,7 @@ snapshots: eslint: 8.26.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.26.0) eslint-plugin-react: 7.37.5(eslint@8.26.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.26.0) @@ -20544,7 +20537,7 @@ snapshots: dependencies: debug: 4.4.1 eslint: 8.26.0 - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.10 @@ -20563,7 +20556,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -21062,11 +21055,11 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.101.3(esbuild@0.25.9)): + file-loader@6.2.0(webpack@5.101.3): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.101.3 file-saver@2.0.5: {} @@ -25578,6 +25571,16 @@ snapshots: webpack: 5.101.3(esbuild@0.25.9) optionalDependencies: esbuild: 0.25.9 + optional: true + + terser-webpack-plugin@5.3.14(webpack@5.101.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.101.3 terser@5.44.0: dependencies: @@ -26690,6 +26693,38 @@ snapshots: webpack-virtual-modules@0.6.2: {} + webpack@5.101.3: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.26.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.101.3) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.101.3(esbuild@0.25.9): dependencies: '@types/eslint-scope': 3.7.7 @@ -26721,6 +26756,7 @@ snapshots: - '@swc/core' - esbuild - uglify-js + optional: true whatwg-fetch@3.6.20: {} diff --git a/rivetkit-typescript/packages/rivetkit/package.json b/rivetkit-typescript/packages/rivetkit/package.json index dd3cf5676b..5401f33cf3 100644 --- a/rivetkit-typescript/packages/rivetkit/package.json +++ b/rivetkit-typescript/packages/rivetkit/package.json @@ -172,6 +172,7 @@ "on-change": "npm:@rivetkit/on-change@^6.0.2-rc.1", "p-retry": "^6.2.1", "pino": "^9.5.0", + "uuid": "^12.0.0", "zod": "^3.25.76" }, "devDependencies": { diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts index 6cfb556d9a..6cea7cf22d 100644 --- a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts @@ -29,7 +29,11 @@ import { } from "@/common/actor-router-consts"; import type { UpgradeWebSocketArgs } from "@/common/inline-websocket-adapter2"; import { getLogger } from "@/common/log"; -import type { UniversalWebSocket } from "@/common/websocket-interface"; +import type { + RivetEvent, + RivetMessageEvent, + UniversalWebSocket, +} from "@/common/websocket-interface"; import { type ActorDriver, type AnyActorInstance, @@ -41,7 +45,7 @@ import type { RunnerConfig } from "@/registry/run-config"; import { getEndpoint } from "@/remote-manager-driver/api-utils"; import { arrayBuffersEqual, - bufferToString, + idToStr, type LongTimeoutHandle, promiseWithResolvers, setLongTimeout, @@ -465,7 +469,7 @@ export class EngineActorDriver implements ActorDriver { request: Request, ): Promise { const websocket = websocketRaw as UniversalWebSocket; - const requestId = bufferToString(requestIdBuf); + const requestId = idToStr(requestIdBuf); logger().debug({ msg: "runner websocket", actorId, url: request.url }); diff --git a/rivetkit-typescript/packages/rivetkit/src/utils.ts b/rivetkit-typescript/packages/rivetkit/src/utils.ts index cee74f86e7..9c63ba86fd 100644 --- a/rivetkit-typescript/packages/rivetkit/src/utils.ts +++ b/rivetkit-typescript/packages/rivetkit/src/utils.ts @@ -2,6 +2,7 @@ export { stringifyError } from "@/common/utils"; export { assertUnreachable } from "./common/utils"; import type { Context as HonoContext, Handler as HonoHandler } from "hono"; +import { stringify as uuidstringify } from "uuid"; import pkgJson from "../package.json" with { type: "json" }; @@ -270,7 +271,6 @@ export const EXTRA_ERROR_LOG = { version: VERSION, }; -/** Converts a buffer to a string. Used for storing strings in a lookup map. */ -export function bufferToString(buffer: ArrayBuffer): string { - return Buffer.from(buffer).toString("base64"); +export function idToStr(id: ArrayBuffer): string { + return uuidstringify(new Uint8Array(id)); }