UNPKG

@rivetkit/cloudflare-workers

Version:

Cloudflare Workers adapter for RivetKit actors

1,027 lines (986 loc) 31.1 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;// src/actor-handler-do.ts var _cloudflareworkers = require('cloudflare:workers'); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _rivetkit = require('rivetkit'); var _driverhelpers = require('rivetkit/driver-helpers'); // src/actor-driver.ts var _utils = require('rivetkit/utils'); // src/actor-id.ts function buildActorId(doId, generation) { return `${doId}:${generation}`; } function parseActorId(actorId) { const parts = actorId.split(":"); if (parts.length !== 2) { throw new Error(`Invalid actor ID format: ${actorId}`); } const [doId, generationStr] = parts; const generation = parseInt(generationStr, 10); if (Number.isNaN(generation)) { throw new Error(`Invalid generation number in actor ID: ${actorId}`); } return [doId, generation]; } // src/actor-kv.ts function kvGet(sql, key) { const cursor = sql.exec( "SELECT value FROM _rivetkit_kv_storage WHERE key = ?", key ); const result = cursor.raw().next(); if (!result.done && result.value) { return toUint8Array(result.value[0]); } return null; } function kvPut(sql, key, value) { sql.exec( "INSERT OR REPLACE INTO _rivetkit_kv_storage (key, value) VALUES (?, ?)", key, value ); } function kvDelete(sql, key) { sql.exec("DELETE FROM _rivetkit_kv_storage WHERE key = ?", key); } function kvListPrefix(sql, prefix) { const cursor = sql.exec("SELECT key, value FROM _rivetkit_kv_storage"); const entries = []; for (const row of cursor.raw()) { const key = toUint8Array(row[0]); const value = toUint8Array(row[1]); if (hasPrefix(key, prefix)) { entries.push([key, value]); } } return entries; } function toUint8Array(value) { var _a; if (value instanceof Uint8Array) { return value; } if (value instanceof ArrayBuffer) { return new Uint8Array(value); } throw new Error( `Unexpected SQL value type: ${typeof value} (${(_a = value == null ? void 0 : value.constructor) == null ? void 0 : _a.name})` ); } function hasPrefix(arr, prefix) { if (prefix.length > arr.length) return false; for (let i = 0; i < prefix.length; i++) { if (arr[i] !== prefix[i]) return false; } return true; } // src/global-kv.ts var GLOBAL_KV_KEYS = { actorMetadata: (actorId) => { return `actor:${actorId}:metadata`; } }; // src/handler.ts // src/config.ts var _zod = require('zod'); var ConfigSchemaBase = _driverhelpers.RunConfigSchema.removeDefault().omit({ driver: true, getUpgradeWebSocket: true }).extend({ /** Path that the Rivet manager API will be mounted. */ managerPath: _zod.z.string().optional().default("/rivet"), fetch: _zod.z.custom().optional() }); var ConfigSchema = ConfigSchemaBase.default( () => ConfigSchemaBase.parse({}) ); // src/manager-driver.ts var _errors = require('rivetkit/errors'); // src/log.ts var _log = require('rivetkit/log'); function logger() { return _log.getLogger.call(void 0, "driver-cloudflare-workers"); } // src/util.ts var EMPTY_KEY = "(none)"; var KEY_SEPARATOR = ","; function serializeNameAndKey(name, key) { const escapedName = name.replace(/:/g, "\\:"); if (key.length === 0) { return `${escapedName}:${EMPTY_KEY}`; } const serializedKey = serializeKey(key); return `${escapedName}:${serializedKey}`; } function serializeKey(key) { if (key.length === 0) { return EMPTY_KEY; } const escapedParts = key.map((part) => { if (part === EMPTY_KEY) { return `\\${EMPTY_KEY}`; } let escaped = part.replace(/\\/g, "\\\\"); escaped = escaped.replace(/,/g, "\\,"); return escaped; }); return escapedParts.join(KEY_SEPARATOR); } // src/manager-driver.ts var STANDARD_WEBSOCKET_HEADERS = [ "connection", "upgrade", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions" ]; var CloudflareActorsManagerDriver = class { async sendRequest(actorId, actorRequest) { const env3 = getCloudflareAmbientEnv(); const [doId] = parseActorId(actorId); logger().debug({ msg: "sending request to durable object", actorId, doId, method: actorRequest.method, url: actorRequest.url }); const id = env3.ACTOR_DO.idFromString(doId); const stub = env3.ACTOR_DO.get(id); return await stub.fetch(actorRequest); } async openWebSocket(path, actorId, encoding, params) { const env3 = getCloudflareAmbientEnv(); const [doId] = parseActorId(actorId); logger().debug({ msg: "opening websocket to durable object", actorId, doId, path }); const id = env3.ACTOR_DO.idFromString(doId); const stub = env3.ACTOR_DO.get(id); const protocols = []; protocols.push(_driverhelpers.WS_PROTOCOL_STANDARD); protocols.push(`${_driverhelpers.WS_PROTOCOL_TARGET}actor`); protocols.push(`${_driverhelpers.WS_PROTOCOL_ACTOR}${encodeURIComponent(actorId)}`); protocols.push(`${_driverhelpers.WS_PROTOCOL_ENCODING}${encoding}`); if (params) { protocols.push( `${_driverhelpers.WS_PROTOCOL_CONN_PARAMS}${encodeURIComponent(JSON.stringify(params))}` ); } const headers = { Upgrade: "websocket", Connection: "Upgrade", "sec-websocket-protocol": protocols.join(", ") }; const normalizedPath = path.startsWith("/") ? path : `/${path}`; const url = `http://actor${normalizedPath}`; logger().debug({ msg: "rewriting websocket url", from: path, to: url }); const response = await stub.fetch(url, { headers }); const webSocket = response.webSocket; if (!webSocket) { throw new (0, _errors.InternalError)( `missing websocket connection in response from DO Status: ${response.status} Response: ${await response.text()}` ); } logger().debug({ msg: "durable object websocket connection open", actorId }); webSocket.accept(); setTimeout(() => { var _a; const event = new Event("open"); (_a = webSocket.onopen) == null ? void 0 : _a.call(webSocket, event); webSocket.dispatchEvent(event); }, 0); return webSocket; } async proxyRequest(c, actorRequest, actorId) { const [doId] = parseActorId(actorId); logger().debug({ msg: "forwarding request to durable object", actorId, doId, method: actorRequest.method, url: actorRequest.url }); const id = c.env.ACTOR_DO.idFromString(doId); const stub = c.env.ACTOR_DO.get(id); return await stub.fetch(actorRequest); } async proxyWebSocket(c, path, actorId, encoding, params) { logger().debug({ msg: "forwarding websocket to durable object", actorId, path }); const upgradeHeader = c.req.header("Upgrade"); if (!upgradeHeader || upgradeHeader !== "websocket") { return new Response("Expected Upgrade: websocket", { status: 426 }); } const newUrl = new URL(`http://actor${path}`); const actorRequest = new Request(newUrl, c.req.raw); logger().debug({ msg: "rewriting websocket url", from: c.req.url, to: actorRequest.url }); const headerKeys = []; actorRequest.headers.forEach((v, k) => { headerKeys.push(k); }); for (const k of headerKeys) { if (!STANDARD_WEBSOCKET_HEADERS.includes(k)) { actorRequest.headers.delete(k); } } const protocols = []; protocols.push(_driverhelpers.WS_PROTOCOL_STANDARD); protocols.push(`${_driverhelpers.WS_PROTOCOL_TARGET}actor`); protocols.push(`${_driverhelpers.WS_PROTOCOL_ACTOR}${encodeURIComponent(actorId)}`); protocols.push(`${_driverhelpers.WS_PROTOCOL_ENCODING}${encoding}`); if (params) { protocols.push( `${_driverhelpers.WS_PROTOCOL_CONN_PARAMS}${encodeURIComponent(JSON.stringify(params))}` ); } actorRequest.headers.set( "sec-websocket-protocol", protocols.join(", ") ); const [doId] = parseActorId(actorId); const id = c.env.ACTOR_DO.idFromString(doId); const stub = c.env.ACTOR_DO.get(id); return await stub.fetch(actorRequest); } async getForId({ c, actorId }) { const env3 = getCloudflareAmbientEnv(); const [doId, expectedGeneration] = parseActorId(actorId); const id = env3.ACTOR_DO.idFromString(doId); const stub = env3.ACTOR_DO.get(id); const result = await stub.getMetadata(); if (!result) { logger().debug({ msg: "getForId: actor not found", actorId }); return void 0; } if (result.actorId !== actorId) { logger().debug({ msg: "getForId: generation mismatch", requestedActorId: actorId, actualActorId: result.actorId }); return void 0; } if (result.destroying) { throw new (0, _errors.ActorNotFound)(actorId); } return { actorId: result.actorId, name: result.name, key: result.key }; } async getWithKey({ c, name, key }) { const env3 = getCloudflareAmbientEnv(); logger().debug({ msg: "getWithKey: searching for actor", name, key }); const nameKeyString = serializeNameAndKey(name, key); const doId = env3.ACTOR_DO.idFromName(nameKeyString).toString(); const id = env3.ACTOR_DO.idFromString(doId); const stub = env3.ACTOR_DO.get(id); const result = await stub.getMetadata(); if (result) { logger().debug({ msg: "getWithKey: found actor with matching name and key", actorId: result.actorId, name: result.name, key: result.key }); return { actorId: result.actorId, name: result.name, key: result.key }; } else { logger().debug({ msg: "getWithKey: no actor found with matching name and key", name, key, doId }); return void 0; } } async getOrCreateWithKey({ c, name, key, input }) { const env3 = getCloudflareAmbientEnv(); const nameKeyString = serializeNameAndKey(name, key); const doId = env3.ACTOR_DO.idFromName(nameKeyString); const actor = env3.ACTOR_DO.get(doId); const result = await actor.create({ name, key, input, allowExisting: true }); if ("success" in result) { const { actorId, created } = result.success; logger().debug({ msg: "getOrCreateWithKey result", actorId, name, key, created }); return { actorId, name, key }; } else if ("error" in result) { throw new Error(`Error: ${JSON.stringify(result.error)}`); } else { _utils.assertUnreachable.call(void 0, result); } } async createActor({ c, name, key, input }) { const env3 = getCloudflareAmbientEnv(); const nameKeyString = serializeNameAndKey(name, key); const doId = env3.ACTOR_DO.idFromName(nameKeyString); const actor = env3.ACTOR_DO.get(doId); const result = await actor.create({ name, key, input, allowExisting: false }); if ("success" in result) { const { actorId } = result.success; return { actorId, name, key }; } else if ("error" in result) { if (result.error.actorAlreadyExists) { throw new (0, _errors.ActorDuplicateKey)(name, key); } throw new (0, _errors.InternalError)( `Unknown error creating actor: ${JSON.stringify(result.error)}` ); } else { _utils.assertUnreachable.call(void 0, result); } } async listActors({ c, name }) { logger().warn({ msg: "listActors not fully implemented for Cloudflare Workers", name }); return []; } displayInformation() { return { name: "Cloudflare Workers", properties: {} }; } getOrCreateInspectorAccessToken() { return _driverhelpers.generateRandomString.call(void 0, ); } }; // src/websocket.ts var _ws = require('hono/ws'); var upgradeWebSocket = _ws.defineWebSocketHelper.call(void 0, async (c, events) => { var _a, _b; const upgradeHeader = c.req.header("Upgrade"); if (upgradeHeader !== "websocket") { return; } const webSocketPair = new WebSocketPair(); const client = webSocketPair[0]; const server = webSocketPair[1]; const wsContext = new (0, _ws.WSContext)({ close: (code, reason) => server.close(code, reason), get protocol() { return server.protocol; }, raw: server, get readyState() { return server.readyState; }, url: server.url ? new URL(server.url) : null, send: (source) => server.send(source) }); if (events.onClose) { server.addEventListener( "close", (evt) => { var _a2; return (_a2 = events.onClose) == null ? void 0 : _a2.call(events, evt, wsContext); } ); } if (events.onMessage) { server.addEventListener( "message", (evt) => { var _a2; return (_a2 = events.onMessage) == null ? void 0 : _a2.call(events, evt, wsContext); } ); } if (events.onError) { server.addEventListener( "error", (evt) => { var _a2; return (_a2 = events.onError) == null ? void 0 : _a2.call(events, evt, wsContext); } ); } (_a = server.accept) == null ? void 0 : _a.call(server); (_b = events.onOpen) == null ? void 0 : _b.call(events, new Event("open"), wsContext); const headers = {}; const protocols = c.req.header("Sec-WebSocket-Protocol"); if (typeof protocols === "string" && protocols.split(",").map((x) => x.trim()).includes(_driverhelpers.WS_PROTOCOL_STANDARD)) { headers["Sec-WebSocket-Protocol"] = _driverhelpers.WS_PROTOCOL_STANDARD; } return new Response(null, { status: 101, headers, webSocket: client }); }); // src/handler.ts function getCloudflareAmbientEnv() { return _cloudflareworkers.env; } function createInlineClient(registry, inputConfig) { inputConfig = { ...inputConfig, runnerKey: "" }; const config = ConfigSchema.parse(inputConfig); const runConfig = { ...config, driver: { name: "cloudflare-workers", manager: () => new CloudflareActorsManagerDriver(), // HACK: We can't build the actor driver until we're inside the Durable Object actor: void 0 }, getUpgradeWebSocket: () => upgradeWebSocket }; const ActorHandler = createActorDurableObject(registry, runConfig); const { client, fetch } = registry.start(runConfig); return { client, fetch, config, ActorHandler }; } function createHandler(registry, inputConfig) { const { client, fetch, config, ActorHandler } = createInlineClient( registry, inputConfig ); const handler = { fetch: async (request, cfEnv, ctx) => { const url = new URL(request.url); const env3 = Object.assign({ RIVET: client }, cfEnv); if (url.pathname.startsWith(config.managerPath)) { const strippedPath = url.pathname.substring( config.managerPath.length ); url.pathname = strippedPath; const modifiedRequest = new Request(url.toString(), request); return fetch(modifiedRequest, env3, ctx); } if (config.fetch) { return config.fetch(request, env3, ctx); } else { return new Response( "This is a RivetKit server.\n\nLearn more at https://rivetkit.org\n", { status: 200 } ); } } }; return { handler, ActorHandler }; } // src/actor-driver.ts var CloudflareDurableObjectGlobalState = class { // Map of actor ID -> DO state #dos = /* @__PURE__ */ new Map(); // WeakMap of DO state -> ActorGlobalState for proper GC #actors = /* @__PURE__ */ new WeakMap(); getDOState(doId) { const state = this.#dos.get(doId); _invariant2.default.call(void 0, state !== void 0, "durable object state not in global state" ); return state; } setDOState(doId, state) { this.#dos.set(doId, state); } getActorState(ctx) { return this.#actors.get(ctx); } setActorState(ctx, actorState) { this.#actors.set(ctx, actorState); } }; var ActorGlobalState = (_class = class {constructor() { _class.prototype.__init.call(this); } // Initialization state // Loaded actor state /** * Indicates if `startDestroy` has been called. * * This is stored in memory instead of SQLite since the destroy may be cancelled. * * See the corresponding `destroyed` property in SQLite metadata. */ __init() {this.destroying = false} reset() { this.initialized = void 0; this.actor = void 0; this.actorInstance = void 0; this.actorPromise = void 0; this.destroying = false; } }, _class); var CloudflareActorsActorDriver = class { #registryConfig; #runConfig; #managerDriver; #inlineClient; #globalState; constructor(registryConfig, runConfig, managerDriver, inlineClient, globalState) { this.#registryConfig = registryConfig; this.#runConfig = runConfig; this.#managerDriver = managerDriver; this.#inlineClient = inlineClient; this.#globalState = globalState; } #getDOCtx(actorId) { const [doId] = parseActorId(actorId); return this.#globalState.getDOState(doId).ctx; } async loadActor(actorId) { var _a; const [doId, expectedGeneration] = parseActorId(actorId); const doState = this.#globalState.getDOState(doId); let actorState = this.#globalState.getActorState(doState.ctx); if (actorState == null ? void 0 : actorState.actorInstance) { return actorState.actorInstance; } if (!actorState) { actorState = new ActorGlobalState(); actorState.actorPromise = _utils.promiseWithResolvers.call(void 0, ); this.#globalState.setActorState(doState.ctx, actorState); } else if (actorState.actorPromise) { await actorState.actorPromise.promise; if (!actorState.actorInstance) { throw new Error( `Actor ${actorId} failed to load in concurrent request` ); } return actorState.actorInstance; } const sql = doState.ctx.storage.sql; const cursor = sql.exec( "SELECT name, key, destroyed, generation FROM _rivetkit_metadata LIMIT 1" ); const result = cursor.raw().next(); if (result.done || !result.value) { throw new Error( `Actor ${actorId} is not initialized - missing metadata` ); } const name = result.value[0]; const key = JSON.parse(result.value[1]); const destroyed = result.value[2]; const generation = result.value[3]; if (destroyed) { throw new Error(`Actor ${actorId} is destroyed`); } if (generation !== expectedGeneration) { throw new Error( `Actor ${actorId} generation mismatch - expected ${expectedGeneration}, got ${generation}` ); } const definition = _rivetkit.lookupInRegistry.call(void 0, this.#registryConfig, name); actorState.actorInstance = definition.instantiate(); await actorState.actorInstance.start( this, this.#inlineClient, actorId, name, key, "unknown" // TODO: Support regions in Cloudflare ); (_a = actorState.actorPromise) == null ? void 0 : _a.resolve(); actorState.actorPromise = void 0; return actorState.actorInstance; } getContext(actorId) { const [doId] = parseActorId(actorId); const state = this.#globalState.getDOState(doId); return { state: state.ctx }; } async setAlarm(actor, timestamp) { await this.#getDOCtx(actor.id).storage.setAlarm(timestamp); } async getDatabase(actorId) { return this.#getDOCtx(actorId).storage.sql; } // Batch KV operations async kvBatchPut(actorId, entries) { const sql = this.#getDOCtx(actorId).storage.sql; for (const [key, value] of entries) { kvPut(sql, key, value); } } async kvBatchGet(actorId, keys) { const sql = this.#getDOCtx(actorId).storage.sql; const results = []; for (const key of keys) { results.push(kvGet(sql, key)); } return results; } async kvBatchDelete(actorId, keys) { const sql = this.#getDOCtx(actorId).storage.sql; for (const key of keys) { kvDelete(sql, key); } } async kvListPrefix(actorId, prefix) { const sql = this.#getDOCtx(actorId).storage.sql; return kvListPrefix(sql, prefix); } startDestroy(actorId) { const [doId, generation] = parseActorId(actorId); const doState = this.#globalState.getDOState(doId); const actorState = this.#globalState.getActorState(doState.ctx); if (!(actorState == null ? void 0 : actorState.actorInstance)) { return; } if (actorState.destroying) { return; } actorState.destroying = true; this.#callOnStopAsync(actorId, doId, actorState.actorInstance); } async #callOnStopAsync(actorId, doId, actor) { await actor.onStop("destroy"); const doState = this.#globalState.getDOState(doId); const sql = doState.ctx.storage.sql; sql.exec("UPDATE _rivetkit_metadata SET destroyed = 1 WHERE 1=1"); sql.exec("DELETE FROM _rivetkit_kv_storage"); await doState.ctx.storage.deleteAlarm(); const env3 = getCloudflareAmbientEnv(); doState.ctx.waitUntil( env3.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId)) ); const actorHandle = this.#globalState.getActorState(doState.ctx); actorHandle == null ? void 0 : actorHandle.reset(); } }; function createCloudflareActorsActorDriverBuilder(globalState) { return (registryConfig, runConfig, managerDriver, inlineClient) => { return new CloudflareActorsActorDriver( registryConfig, runConfig, managerDriver, inlineClient, globalState ); }; } // src/actor-handler-do.ts function createActorDurableObject(registry, rootRunConfig) { const globalState = new CloudflareDurableObjectGlobalState(); const runConfig = Object.assign({}, rootRunConfig, { role: "runner" }); return class ActorHandler extends _cloudflareworkers.DurableObject { /** * This holds a strong reference to ActorGlobalState. * CloudflareDurableObjectGlobalState holds a weak reference so we can * access it elsewhere. **/ #state; constructor(...args) { super(...args); this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS _rivetkit_kv_storage( key BLOB PRIMARY KEY, value BLOB ); `); this.ctx.storage.sql.exec(` CREATE TABLE IF NOT EXISTS _rivetkit_metadata( id INTEGER PRIMARY KEY CHECK (id = 1), name TEXT NOT NULL, key TEXT NOT NULL, destroyed INTEGER DEFAULT 0, generation INTEGER DEFAULT 0 ); `); const state = globalState.getActorState(this.ctx); if (state) { this.#state = state; } else { this.#state = new ActorGlobalState(); globalState.setActorState(this.ctx, this.#state); } } async #loadActor() { var _a; _invariant2.default.call(void 0, this.#state, "State should be initialized"); if (!this.#state.initialized) { const cursor = this.ctx.storage.sql.exec( "SELECT name, key, destroyed, generation FROM _rivetkit_metadata WHERE id = 1" ); const result = cursor.raw().next(); if (!result.done && result.value) { const name = result.value[0]; const key = JSON.parse( result.value[1] ); const destroyed = result.value[2]; const generation = result.value[3]; if (!destroyed) { logger().debug({ msg: "already initialized", name, key, generation }); this.#state.initialized = { name, key, generation }; } else { logger().debug("actor is destroyed, cannot load"); throw new Error("Actor is destroyed"); } } else { logger().debug("not initialized"); throw new Error("Actor is not initialized"); } } if (this.#state.actor) { _invariant2.default.call(void 0, !this.#state.initialized || this.#state.actor.generation === this.#state.initialized.generation, `Stale actor cached: actor generation ${this.#state.actor.generation} != initialized generation ${(_a = this.#state.initialized) == null ? void 0 : _a.generation}. This should not happen.` ); return this.#state.actor; } if (!this.#state.initialized) throw new Error("Not initialized"); const actorId = this.ctx.id.toString(); globalState.setDOState(actorId, { ctx: this.ctx, env: _cloudflareworkers.env }); _invariant2.default.call(void 0, runConfig.driver, "runConfig.driver"); runConfig.driver.actor = createCloudflareActorsActorDriverBuilder(globalState); const managerDriver = runConfig.driver.manager( registry.config, runConfig ); const inlineClient = _rivetkit.createClientWithDriver.call(void 0, managerDriver, runConfig ); const actorDriver = runConfig.driver.actor( registry.config, runConfig, managerDriver, inlineClient ); const actorRouter = _rivetkit.createActorRouter.call(void 0, runConfig, actorDriver, false ); this.#state.actor = { actorRouter, actorDriver, generation: this.#state.initialized.generation }; const actorIdWithGen = buildActorId( actorId, this.#state.initialized.generation ); await actorDriver.loadActor(actorIdWithGen); return this.#state.actor; } /** RPC called to get actor metadata without creating it */ async getMetadata() { var _a; const cursor = this.ctx.storage.sql.exec( "SELECT name, key, destroyed, generation FROM _rivetkit_metadata WHERE id = 1" ); const result = cursor.raw().next(); if (!result.done && result.value) { const name = result.value[0]; const key = JSON.parse(result.value[1]); const destroyed = result.value[2]; const generation = result.value[3]; if (destroyed) { logger().debug({ msg: "getMetadata: actor is destroyed", name, key, generation }); return void 0; } const doId = this.ctx.id.toString(); const actorId = buildActorId(doId, generation); const destroying = _nullishCoalesce(((_a = globalState.getActorState(this.ctx)) == null ? void 0 : _a.destroying), () => ( false)); logger().debug({ msg: "getMetadata: found actor metadata", actorId, name, key, generation, destroying }); return { actorId, name, key, destroying }; } logger().debug({ msg: "getMetadata: no metadata found" }); return void 0; } /** RPC called by the manager to create a DO. Can optionally allow existing actors. */ async create(req) { const checkCursor = this.ctx.storage.sql.exec( "SELECT destroyed, generation FROM _rivetkit_metadata WHERE id = 1" ); const checkResult = checkCursor.raw().next(); let created = false; let generation = 0; if (!checkResult.done && checkResult.value) { const destroyed = checkResult.value[0]; generation = checkResult.value[1]; if (!destroyed) { if (!req.allowExisting) { logger().debug({ msg: "create failed: actor already exists", name: req.name, key: req.key, generation }); return { error: { actorAlreadyExists: true } }; } logger().debug({ msg: "actor already exists", key: req.key, generation }); const doId2 = this.ctx.id.toString(); const actorId2 = buildActorId(doId2, generation); return { success: { actorId: actorId2, created: false } }; } generation = generation + 1; created = true; if (this.#state) { this.#state.actor = void 0; } logger().debug({ msg: "resurrecting destroyed actor", key: req.key, oldGeneration: generation - 1, newGeneration: generation }); } else { generation = 0; created = true; logger().debug({ msg: "creating new actor", key: req.key, generation }); } this.ctx.storage.sql.exec( `INSERT INTO _rivetkit_metadata (id, name, key, destroyed, generation) VALUES (1, ?, ?, 0, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, key = excluded.key, destroyed = 0, generation = excluded.generation`, req.name, JSON.stringify(req.key), generation ); this.#state.initialized = { name: req.name, key: req.key, generation }; const doId = this.ctx.id.toString(); const actorId = buildActorId(doId, generation); if (created) { initializeActorKvStorage(this.ctx.storage.sql, req.input); const env3 = getCloudflareAmbientEnv(); const actorData = { name: req.name, key: req.key, generation }; this.ctx.waitUntil( env3.ACTOR_KV.put( GLOBAL_KV_KEYS.actorMetadata(actorId), JSON.stringify(actorData) ) ); } await this.#loadActor(); logger().debug({ msg: created ? "actor created/resurrected" : "returning existing actor", actorId, created, generation }); return { success: { actorId, created } }; } async fetch(request) { const { actorRouter, generation } = await this.#loadActor(); const doId = this.ctx.id.toString(); const actorId = buildActorId(doId, generation); return await actorRouter.fetch(request, { actorId }); } async alarm() { const { actorDriver, generation } = await this.#loadActor(); const doId = this.ctx.id.toString(); const actorId = buildActorId(doId, generation); const actor = await actorDriver.loadActor(actorId); await actor.onAlarm(); } }; } function initializeActorKvStorage(sql, input) { const initialKvState = _driverhelpers.getInitialActorKvState.call(void 0, input); for (const [key, value] of initialKvState) { kvPut(sql, key, value); } } exports.createActorDurableObject = createActorDurableObject; exports.createHandler = createHandler; exports.createInlineClient = createInlineClient; //# sourceMappingURL=mod.cjs.map