@rivetkit/cloudflare-workers 2.0.24-rc.1 → 2.0.24

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/mod.js CHANGED
@@ -1,235 +1,94 @@
1
- // src/handler.ts
2
- import { env as env2 } from "cloudflare:workers";
3
-
4
1
  // src/actor-handler-do.ts
5
- import { DurableObject, env } from "cloudflare:workers";
2
+ import { DurableObject, env as env2 } from "cloudflare:workers";
6
3
  import invariant2 from "invariant";
7
4
  import { createActorRouter, createClientWithDriver } from "rivetkit";
8
- import {
9
- serializeEmptyPersistData
10
- } from "rivetkit/driver-helpers";
11
- import { promiseWithResolvers as promiseWithResolvers2 } from "rivetkit/utils";
5
+ import { getInitialActorKvState } from "rivetkit/driver-helpers";
12
6
 
13
7
  // src/actor-driver.ts
14
8
  import invariant from "invariant";
15
9
  import { lookupInRegistry } from "rivetkit";
16
10
  import { promiseWithResolvers } from "rivetkit/utils";
17
- var CloudflareDurableObjectGlobalState = class {
18
- // Single map for all actor state
19
- #dos = /* @__PURE__ */ new Map();
20
- getDOState(actorId) {
21
- const state = this.#dos.get(actorId);
22
- invariant(
23
- state !== void 0,
24
- "durable object state not in global state"
25
- );
26
- return state;
27
- }
28
- setDOState(actorId, state) {
29
- this.#dos.set(actorId, state);
11
+
12
+ // src/actor-id.ts
13
+ function buildActorId(doId, generation) {
14
+ return `${doId}:${generation}`;
15
+ }
16
+ function parseActorId(actorId) {
17
+ const parts = actorId.split(":");
18
+ if (parts.length !== 2) {
19
+ throw new Error(`Invalid actor ID format: ${actorId}`);
30
20
  }
31
- };
32
- var ActorHandler = class {
33
- actor;
34
- actorPromise = promiseWithResolvers();
35
- };
36
- var CloudflareActorsActorDriver = class {
37
- #registryConfig;
38
- #runConfig;
39
- #managerDriver;
40
- #inlineClient;
41
- #globalState;
42
- #actors = /* @__PURE__ */ new Map();
43
- constructor(registryConfig, runConfig, managerDriver, inlineClient, globalState) {
44
- this.#registryConfig = registryConfig;
45
- this.#runConfig = runConfig;
46
- this.#managerDriver = managerDriver;
47
- this.#inlineClient = inlineClient;
48
- this.#globalState = globalState;
21
+ const [doId, generationStr] = parts;
22
+ const generation = parseInt(generationStr, 10);
23
+ if (Number.isNaN(generation)) {
24
+ throw new Error(`Invalid generation number in actor ID: ${actorId}`);
49
25
  }
50
- #getDOCtx(actorId) {
51
- return this.#globalState.getDOState(actorId).ctx;
26
+ return [doId, generation];
27
+ }
28
+
29
+ // src/actor-kv.ts
30
+ function kvGet(sql, key) {
31
+ const cursor = sql.exec(
32
+ "SELECT value FROM _rivetkit_kv_storage WHERE key = ?",
33
+ key
34
+ );
35
+ const result = cursor.raw().next();
36
+ if (!result.done && result.value) {
37
+ return toUint8Array(result.value[0]);
52
38
  }
53
- async loadActor(actorId) {
54
- var _a;
55
- let handler = this.#actors.get(actorId);
56
- if (handler) {
57
- if (handler.actorPromise) await handler.actorPromise.promise;
58
- if (!handler.actor) throw new Error("Actor should be loaded");
59
- return handler.actor;
60
- }
61
- handler = new ActorHandler();
62
- this.#actors.set(actorId, handler);
63
- const doState = this.#globalState.getDOState(actorId);
64
- const storage = doState.ctx.storage;
65
- const [name, key] = await Promise.all([
66
- storage.get(KEYS.NAME),
67
- storage.get(KEYS.KEY)
68
- ]);
69
- if (!name) {
70
- throw new Error(
71
- `Actor ${actorId} is not initialized - missing name`
72
- );
73
- }
74
- if (!key) {
75
- throw new Error(
76
- `Actor ${actorId} is not initialized - missing key`
77
- );
39
+ return null;
40
+ }
41
+ function kvPut(sql, key, value) {
42
+ sql.exec(
43
+ "INSERT OR REPLACE INTO _rivetkit_kv_storage (key, value) VALUES (?, ?)",
44
+ key,
45
+ value
46
+ );
47
+ }
48
+ function kvDelete(sql, key) {
49
+ sql.exec("DELETE FROM _rivetkit_kv_storage WHERE key = ?", key);
50
+ }
51
+ function kvListPrefix(sql, prefix) {
52
+ const cursor = sql.exec("SELECT key, value FROM _rivetkit_kv_storage");
53
+ const entries = [];
54
+ for (const row of cursor.raw()) {
55
+ const key = toUint8Array(row[0]);
56
+ const value = toUint8Array(row[1]);
57
+ if (hasPrefix(key, prefix)) {
58
+ entries.push([key, value]);
78
59
  }
79
- const definition = lookupInRegistry(this.#registryConfig, name);
80
- handler.actor = definition.instantiate();
81
- await handler.actor.start(
82
- this,
83
- this.#inlineClient,
84
- actorId,
85
- name,
86
- key,
87
- "unknown"
88
- // TODO: Support regions in Cloudflare
89
- );
90
- (_a = handler.actorPromise) == null ? void 0 : _a.resolve();
91
- handler.actorPromise = void 0;
92
- return handler.actor;
93
- }
94
- getContext(actorId) {
95
- const state = this.#globalState.getDOState(actorId);
96
- return { state: state.ctx };
97
60
  }
98
- async readPersistedData(actorId) {
99
- return await this.#getDOCtx(actorId).storage.get(KEYS.PERSIST_DATA);
100
- }
101
- async writePersistedData(actorId, data) {
102
- await this.#getDOCtx(actorId).storage.put(KEYS.PERSIST_DATA, data);
61
+ return entries;
62
+ }
63
+ function toUint8Array(value) {
64
+ var _a;
65
+ if (value instanceof Uint8Array) {
66
+ return value;
103
67
  }
104
- async setAlarm(actor, timestamp) {
105
- await this.#getDOCtx(actor.id).storage.setAlarm(timestamp);
68
+ if (value instanceof ArrayBuffer) {
69
+ return new Uint8Array(value);
106
70
  }
107
- async getDatabase(actorId) {
108
- return this.#getDOCtx(actorId).storage.sql;
109
- }
110
- };
111
- function createCloudflareActorsActorDriverBuilder(globalState) {
112
- return (registryConfig, runConfig, managerDriver, inlineClient) => {
113
- return new CloudflareActorsActorDriver(
114
- registryConfig,
115
- runConfig,
116
- managerDriver,
117
- inlineClient,
118
- globalState
119
- );
120
- };
71
+ throw new Error(
72
+ `Unexpected SQL value type: ${typeof value} (${(_a = value == null ? void 0 : value.constructor) == null ? void 0 : _a.name})`
73
+ );
121
74
  }
122
-
123
- // src/log.ts
124
- import { getLogger } from "rivetkit/log";
125
- function logger() {
126
- return getLogger("driver-cloudflare-workers");
75
+ function hasPrefix(arr, prefix) {
76
+ if (prefix.length > arr.length) return false;
77
+ for (let i = 0; i < prefix.length; i++) {
78
+ if (arr[i] !== prefix[i]) return false;
79
+ }
80
+ return true;
127
81
  }
128
82
 
129
- // src/actor-handler-do.ts
130
- var KEYS = {
131
- NAME: "rivetkit:name",
132
- KEY: "rivetkit:key",
133
- PERSIST_DATA: "rivetkit:data"
83
+ // src/global-kv.ts
84
+ var GLOBAL_KV_KEYS = {
85
+ actorMetadata: (actorId) => {
86
+ return `actor:${actorId}:metadata`;
87
+ }
134
88
  };
135
- function createActorDurableObject(registry, rootRunConfig) {
136
- const globalState = new CloudflareDurableObjectGlobalState();
137
- const runConfig = Object.assign({}, rootRunConfig, { role: "runner" });
138
- return class ActorHandler extends DurableObject {
139
- #initialized;
140
- #initializedPromise;
141
- #actor;
142
- async #loadActor() {
143
- if (!this.#initialized) {
144
- if (this.#initializedPromise) {
145
- await this.#initializedPromise.promise;
146
- } else {
147
- this.#initializedPromise = promiseWithResolvers2();
148
- const res = await this.ctx.storage.get([
149
- KEYS.NAME,
150
- KEYS.KEY,
151
- KEYS.PERSIST_DATA
152
- ]);
153
- if (res.get(KEYS.PERSIST_DATA)) {
154
- const name = res.get(KEYS.NAME);
155
- if (!name) throw new Error("missing actor name");
156
- const key = res.get(KEYS.KEY);
157
- if (!key) throw new Error("missing actor key");
158
- logger().debug({
159
- msg: "already initialized",
160
- name,
161
- key
162
- });
163
- this.#initialized = { name, key };
164
- this.#initializedPromise.resolve();
165
- } else {
166
- logger().debug("waiting to initialize");
167
- }
168
- }
169
- }
170
- if (this.#actor) {
171
- return this.#actor;
172
- }
173
- if (!this.#initialized) throw new Error("Not initialized");
174
- const actorId = this.ctx.id.toString();
175
- globalState.setDOState(actorId, { ctx: this.ctx, env });
176
- invariant2(runConfig.driver, "runConfig.driver");
177
- runConfig.driver.actor = createCloudflareActorsActorDriverBuilder(globalState);
178
- const managerDriver = runConfig.driver.manager(
179
- registry.config,
180
- runConfig
181
- );
182
- const inlineClient = createClientWithDriver(
183
- managerDriver,
184
- runConfig
185
- );
186
- const actorDriver = runConfig.driver.actor(
187
- registry.config,
188
- runConfig,
189
- managerDriver,
190
- inlineClient
191
- );
192
- const actorRouter = createActorRouter(
193
- runConfig,
194
- actorDriver,
195
- false
196
- );
197
- this.#actor = {
198
- actorRouter,
199
- actorDriver
200
- };
201
- await actorDriver.loadActor(actorId);
202
- return this.#actor;
203
- }
204
- /** RPC called by the service that creates the DO to initialize it. */
205
- async initialize(req) {
206
- await this.ctx.storage.put({
207
- [KEYS.NAME]: req.name,
208
- [KEYS.KEY]: req.key,
209
- [KEYS.PERSIST_DATA]: serializeEmptyPersistData(req.input)
210
- });
211
- this.#initialized = {
212
- name: req.name,
213
- key: req.key
214
- };
215
- logger().debug({ msg: "initialized actor", key: req.key });
216
- await this.#loadActor();
217
- }
218
- async fetch(request) {
219
- const { actorRouter } = await this.#loadActor();
220
- const actorId = this.ctx.id.toString();
221
- return await actorRouter.fetch(request, {
222
- actorId
223
- });
224
- }
225
- async alarm() {
226
- const { actorDriver } = await this.#loadActor();
227
- const actorId = this.ctx.id.toString();
228
- const actor = await actorDriver.loadActor(actorId);
229
- await actor._onAlarm();
230
- }
231
- };
232
- }
89
+
90
+ // src/handler.ts
91
+ import { env } from "cloudflare:workers";
233
92
 
234
93
  // src/config.ts
235
94
  import { RunConfigSchema } from "rivetkit/driver-helpers";
@@ -244,14 +103,23 @@ var ConfigSchema = RunConfigSchema.removeDefault().omit({ driver: true, getUpgra
244
103
  import {
245
104
  generateRandomString,
246
105
  WS_PROTOCOL_ACTOR,
247
- WS_PROTOCOL_CONN_ID,
248
106
  WS_PROTOCOL_CONN_PARAMS,
249
- WS_PROTOCOL_CONN_TOKEN,
250
107
  WS_PROTOCOL_ENCODING,
251
108
  WS_PROTOCOL_STANDARD,
252
109
  WS_PROTOCOL_TARGET
253
110
  } from "rivetkit/driver-helpers";
254
- import { ActorAlreadyExists, InternalError } from "rivetkit/errors";
111
+ import {
112
+ ActorDuplicateKey,
113
+ ActorNotFound,
114
+ InternalError
115
+ } from "rivetkit/errors";
116
+ import { assertUnreachable } from "rivetkit/utils";
117
+
118
+ // src/log.ts
119
+ import { getLogger } from "rivetkit/log";
120
+ function logger() {
121
+ return getLogger("driver-cloudflare-workers");
122
+ }
255
123
 
256
124
  // src/util.ts
257
125
  var EMPTY_KEY = "(none)";
@@ -280,16 +148,6 @@ function serializeKey(key) {
280
148
  }
281
149
 
282
150
  // src/manager-driver.ts
283
- var KEYS2 = {
284
- ACTOR: {
285
- // Combined key for actor metadata (name and key)
286
- metadata: (actorId) => `actor:${actorId}:metadata`,
287
- // Key index function for actor lookup
288
- keyIndex: (name, key = []) => {
289
- return `actor_key:${serializeKey(key)}`;
290
- }
291
- }
292
- };
293
151
  var STANDARD_WEBSOCKET_HEADERS = [
294
152
  "connection",
295
153
  "upgrade",
@@ -301,41 +159,39 @@ var STANDARD_WEBSOCKET_HEADERS = [
301
159
  var CloudflareActorsManagerDriver = class {
302
160
  async sendRequest(actorId, actorRequest) {
303
161
  const env3 = getCloudflareAmbientEnv();
162
+ const [doId] = parseActorId(actorId);
304
163
  logger().debug({
305
164
  msg: "sending request to durable object",
306
165
  actorId,
166
+ doId,
307
167
  method: actorRequest.method,
308
168
  url: actorRequest.url
309
169
  });
310
- const id = env3.ACTOR_DO.idFromString(actorId);
170
+ const id = env3.ACTOR_DO.idFromString(doId);
311
171
  const stub = env3.ACTOR_DO.get(id);
312
172
  return await stub.fetch(actorRequest);
313
173
  }
314
- async openWebSocket(path, actorId, encoding, params, connId, connToken) {
174
+ async openWebSocket(path, actorId, encoding, params) {
315
175
  const env3 = getCloudflareAmbientEnv();
176
+ const [doId] = parseActorId(actorId);
316
177
  logger().debug({
317
178
  msg: "opening websocket to durable object",
318
179
  actorId,
180
+ doId,
319
181
  path
320
182
  });
321
- const id = env3.ACTOR_DO.idFromString(actorId);
183
+ const id = env3.ACTOR_DO.idFromString(doId);
322
184
  const stub = env3.ACTOR_DO.get(id);
323
185
  const protocols = [];
324
186
  protocols.push(WS_PROTOCOL_STANDARD);
325
187
  protocols.push(`${WS_PROTOCOL_TARGET}actor`);
326
- protocols.push(`${WS_PROTOCOL_ACTOR}${actorId}`);
188
+ protocols.push(`${WS_PROTOCOL_ACTOR}${encodeURIComponent(actorId)}`);
327
189
  protocols.push(`${WS_PROTOCOL_ENCODING}${encoding}`);
328
190
  if (params) {
329
191
  protocols.push(
330
192
  `${WS_PROTOCOL_CONN_PARAMS}${encodeURIComponent(JSON.stringify(params))}`
331
193
  );
332
194
  }
333
- if (connId) {
334
- protocols.push(`${WS_PROTOCOL_CONN_ID}${connId}`);
335
- }
336
- if (connToken) {
337
- protocols.push(`${WS_PROTOCOL_CONN_TOKEN}${connToken}`);
338
- }
339
195
  const headers = {
340
196
  Upgrade: "websocket",
341
197
  Connection: "Upgrade",
@@ -370,13 +226,15 @@ Response: ${await response.text()}`
370
226
  return webSocket;
371
227
  }
372
228
  async proxyRequest(c, actorRequest, actorId) {
229
+ const [doId] = parseActorId(actorId);
373
230
  logger().debug({
374
231
  msg: "forwarding request to durable object",
375
232
  actorId,
233
+ doId,
376
234
  method: actorRequest.method,
377
235
  url: actorRequest.url
378
236
  });
379
- const id = c.env.ACTOR_DO.idFromString(actorId);
237
+ const id = c.env.ACTOR_DO.idFromString(doId);
380
238
  const stub = c.env.ACTOR_DO.get(id);
381
239
  return await stub.fetch(actorRequest);
382
240
  }
@@ -411,7 +269,7 @@ Response: ${await response.text()}`
411
269
  const protocols = [];
412
270
  protocols.push(WS_PROTOCOL_STANDARD);
413
271
  protocols.push(`${WS_PROTOCOL_TARGET}actor`);
414
- protocols.push(`${WS_PROTOCOL_ACTOR}${actorId}`);
272
+ protocols.push(`${WS_PROTOCOL_ACTOR}${encodeURIComponent(actorId)}`);
415
273
  protocols.push(`${WS_PROTOCOL_ENCODING}${encoding}`);
416
274
  if (params) {
417
275
  protocols.push(
@@ -422,7 +280,8 @@ Response: ${await response.text()}`
422
280
  "sec-websocket-protocol",
423
281
  protocols.join(", ")
424
282
  );
425
- const id = c.env.ACTOR_DO.idFromString(actorId);
283
+ const [doId] = parseActorId(actorId);
284
+ const id = c.env.ACTOR_DO.idFromString(doId);
426
285
  const stub = c.env.ACTOR_DO.get(id);
427
286
  return await stub.fetch(actorRequest);
428
287
  }
@@ -431,19 +290,32 @@ Response: ${await response.text()}`
431
290
  actorId
432
291
  }) {
433
292
  const env3 = getCloudflareAmbientEnv();
434
- const actorData = await env3.ACTOR_KV.get(
435
- KEYS2.ACTOR.metadata(actorId),
436
- {
437
- type: "json"
438
- }
439
- );
440
- if (!actorData) {
293
+ const [doId, expectedGeneration] = parseActorId(actorId);
294
+ const id = env3.ACTOR_DO.idFromString(doId);
295
+ const stub = env3.ACTOR_DO.get(id);
296
+ const result = await stub.getMetadata();
297
+ if (!result) {
298
+ logger().debug({
299
+ msg: "getForId: actor not found",
300
+ actorId
301
+ });
441
302
  return void 0;
442
303
  }
304
+ if (result.actorId !== actorId) {
305
+ logger().debug({
306
+ msg: "getForId: generation mismatch",
307
+ requestedActorId: actorId,
308
+ actualActorId: result.actorId
309
+ });
310
+ return void 0;
311
+ }
312
+ if (result.destroying) {
313
+ throw new ActorNotFound(actorId);
314
+ }
443
315
  return {
444
- actorId,
445
- name: actorData.name,
446
- key: actorData.key
316
+ actorId: result.actorId,
317
+ name: result.name,
318
+ key: result.key
447
319
  };
448
320
  }
449
321
  async getWithKey({
@@ -454,33 +326,66 @@ Response: ${await response.text()}`
454
326
  const env3 = getCloudflareAmbientEnv();
455
327
  logger().debug({ msg: "getWithKey: searching for actor", name, key });
456
328
  const nameKeyString = serializeNameAndKey(name, key);
457
- const actorId = env3.ACTOR_DO.idFromName(nameKeyString).toString();
458
- const actorData = await env3.ACTOR_KV.get(KEYS2.ACTOR.metadata(actorId), {
459
- type: "json"
460
- });
461
- if (!actorData) {
329
+ const doId = env3.ACTOR_DO.idFromName(nameKeyString).toString();
330
+ const id = env3.ACTOR_DO.idFromString(doId);
331
+ const stub = env3.ACTOR_DO.get(id);
332
+ const result = await stub.getMetadata();
333
+ if (result) {
334
+ logger().debug({
335
+ msg: "getWithKey: found actor with matching name and key",
336
+ actorId: result.actorId,
337
+ name: result.name,
338
+ key: result.key
339
+ });
340
+ return {
341
+ actorId: result.actorId,
342
+ name: result.name,
343
+ key: result.key
344
+ };
345
+ } else {
462
346
  logger().debug({
463
347
  msg: "getWithKey: no actor found with matching name and key",
464
348
  name,
465
349
  key,
466
- actorId
350
+ doId
467
351
  });
468
352
  return void 0;
469
353
  }
470
- logger().debug({
471
- msg: "getWithKey: found actor with matching name and key",
472
- actorId,
354
+ }
355
+ async getOrCreateWithKey({
356
+ c,
357
+ name,
358
+ key,
359
+ input
360
+ }) {
361
+ const env3 = getCloudflareAmbientEnv();
362
+ const nameKeyString = serializeNameAndKey(name, key);
363
+ const doId = env3.ACTOR_DO.idFromName(nameKeyString);
364
+ const actor = env3.ACTOR_DO.get(doId);
365
+ const result = await actor.create({
473
366
  name,
474
- key
367
+ key,
368
+ input,
369
+ allowExisting: true
475
370
  });
476
- return this.#buildActorOutput(c, actorId);
477
- }
478
- async getOrCreateWithKey(input) {
479
- const getOutput = await this.getWithKey(input);
480
- if (getOutput) {
481
- return getOutput;
371
+ if ("success" in result) {
372
+ const { actorId, created } = result.success;
373
+ logger().debug({
374
+ msg: "getOrCreateWithKey result",
375
+ actorId,
376
+ name,
377
+ key,
378
+ created
379
+ });
380
+ return {
381
+ actorId,
382
+ name,
383
+ key
384
+ };
385
+ } else if ("error" in result) {
386
+ throw new Error(`Error: ${JSON.stringify(result.error)}`);
482
387
  } else {
483
- return await this.createActor(input);
388
+ assertUnreachable(result);
484
389
  }
485
390
  }
486
391
  async createActor({
@@ -490,48 +395,39 @@ Response: ${await response.text()}`
490
395
  input
491
396
  }) {
492
397
  const env3 = getCloudflareAmbientEnv();
493
- const existingActor = await this.getWithKey({ c, name, key });
494
- if (existingActor) {
495
- throw new ActorAlreadyExists(name, key);
496
- }
497
398
  const nameKeyString = serializeNameAndKey(name, key);
498
399
  const doId = env3.ACTOR_DO.idFromName(nameKeyString);
499
- const actorId = doId.toString();
500
400
  const actor = env3.ACTOR_DO.get(doId);
501
- await actor.initialize({
401
+ const result = await actor.create({
502
402
  name,
503
403
  key,
504
- input
404
+ input,
405
+ allowExisting: false
505
406
  });
506
- const actorData = { name, key };
507
- await env3.ACTOR_KV.put(
508
- KEYS2.ACTOR.metadata(actorId),
509
- JSON.stringify(actorData)
510
- );
511
- await env3.ACTOR_KV.put(KEYS2.ACTOR.keyIndex(name, key), actorId);
512
- return {
513
- actorId,
514
- name,
515
- key
516
- };
517
- }
518
- // Helper method to build actor output from an ID
519
- async #buildActorOutput(c, actorId) {
520
- const env3 = getCloudflareAmbientEnv();
521
- const actorData = await env3.ACTOR_KV.get(
522
- KEYS2.ACTOR.metadata(actorId),
523
- {
524
- type: "json"
407
+ if ("success" in result) {
408
+ const { actorId } = result.success;
409
+ return {
410
+ actorId,
411
+ name,
412
+ key
413
+ };
414
+ } else if ("error" in result) {
415
+ if (result.error.actorAlreadyExists) {
416
+ throw new ActorDuplicateKey(name, key);
525
417
  }
526
- );
527
- if (!actorData) {
528
- return void 0;
418
+ throw new InternalError(
419
+ `Unknown error creating actor: ${JSON.stringify(result.error)}`
420
+ );
421
+ } else {
422
+ assertUnreachable(result);
529
423
  }
530
- return {
531
- actorId,
532
- name: actorData.name,
533
- key: actorData.key
534
- };
424
+ }
425
+ async listActors({ c, name }) {
426
+ logger().warn({
427
+ msg: "listActors not fully implemented for Cloudflare Workers",
428
+ name
429
+ });
430
+ return [];
535
431
  }
536
432
  displayInformation() {
537
433
  return {
@@ -611,9 +507,9 @@ var upgradeWebSocket = defineWebSocketHelper(async (c, events) => {
611
507
 
612
508
  // src/handler.ts
613
509
  function getCloudflareAmbientEnv() {
614
- return env2;
510
+ return env;
615
511
  }
616
- function createHandler(registry, inputConfig) {
512
+ function createInlineClient(registry, inputConfig) {
617
513
  inputConfig = { ...inputConfig, runnerKey: "" };
618
514
  const config = ConfigSchema.parse(inputConfig);
619
515
  const runConfig = {
@@ -626,19 +522,26 @@ function createHandler(registry, inputConfig) {
626
522
  },
627
523
  getUpgradeWebSocket: () => upgradeWebSocket
628
524
  };
629
- const ActorHandler2 = createActorDurableObject(registry, runConfig);
630
- const serverOutput = registry.start(runConfig);
525
+ const ActorHandler = createActorDurableObject(registry, runConfig);
526
+ const { client, fetch } = registry.start(runConfig);
527
+ return { client, fetch, config, ActorHandler };
528
+ }
529
+ function createHandler(registry, inputConfig) {
530
+ const { client, fetch, config, ActorHandler } = createInlineClient(
531
+ registry,
532
+ inputConfig
533
+ );
631
534
  const handler = {
632
- fetch: (request, cfEnv, ctx) => {
535
+ fetch: async (request, cfEnv, ctx) => {
633
536
  const url = new URL(request.url);
634
- const env3 = Object.assign({ RIVET: serverOutput.client }, cfEnv);
537
+ const env3 = Object.assign({ RIVET: client }, cfEnv);
635
538
  if (url.pathname.startsWith(config.managerPath)) {
636
539
  const strippedPath = url.pathname.substring(
637
540
  config.managerPath.length
638
541
  );
639
542
  url.pathname = strippedPath;
640
543
  const modifiedRequest = new Request(url.toString(), request);
641
- return serverOutput.fetch(modifiedRequest, env3, ctx);
544
+ return fetch(modifiedRequest, env3, ctx);
642
545
  }
643
546
  if (config.fetch) {
644
547
  return config.fetch(request, env3, ctx);
@@ -650,9 +553,472 @@ function createHandler(registry, inputConfig) {
650
553
  }
651
554
  }
652
555
  };
653
- return { handler, ActorHandler: ActorHandler2 };
556
+ return { handler, ActorHandler };
557
+ }
558
+
559
+ // src/actor-driver.ts
560
+ var CloudflareDurableObjectGlobalState = class {
561
+ // Map of actor ID -> DO state
562
+ #dos = /* @__PURE__ */ new Map();
563
+ // WeakMap of DO state -> ActorGlobalState for proper GC
564
+ #actors = /* @__PURE__ */ new WeakMap();
565
+ getDOState(doId) {
566
+ const state = this.#dos.get(doId);
567
+ invariant(
568
+ state !== void 0,
569
+ "durable object state not in global state"
570
+ );
571
+ return state;
572
+ }
573
+ setDOState(doId, state) {
574
+ this.#dos.set(doId, state);
575
+ }
576
+ getActorState(ctx) {
577
+ return this.#actors.get(ctx);
578
+ }
579
+ setActorState(ctx, actorState) {
580
+ this.#actors.set(ctx, actorState);
581
+ }
582
+ };
583
+ var ActorGlobalState = class {
584
+ // Initialization state
585
+ initialized;
586
+ // Loaded actor state
587
+ actor;
588
+ actorInstance;
589
+ actorPromise;
590
+ /**
591
+ * Indicates if `startDestroy` has been called.
592
+ *
593
+ * This is stored in memory instead of SQLite since the destroy may be cancelled.
594
+ *
595
+ * See the corresponding `destroyed` property in SQLite metadata.
596
+ */
597
+ destroying = false;
598
+ reset() {
599
+ this.initialized = void 0;
600
+ this.actor = void 0;
601
+ this.actorInstance = void 0;
602
+ this.actorPromise = void 0;
603
+ this.destroying = false;
604
+ }
605
+ };
606
+ var CloudflareActorsActorDriver = class {
607
+ #registryConfig;
608
+ #runConfig;
609
+ #managerDriver;
610
+ #inlineClient;
611
+ #globalState;
612
+ constructor(registryConfig, runConfig, managerDriver, inlineClient, globalState) {
613
+ this.#registryConfig = registryConfig;
614
+ this.#runConfig = runConfig;
615
+ this.#managerDriver = managerDriver;
616
+ this.#inlineClient = inlineClient;
617
+ this.#globalState = globalState;
618
+ }
619
+ #getDOCtx(actorId) {
620
+ const [doId] = parseActorId(actorId);
621
+ return this.#globalState.getDOState(doId).ctx;
622
+ }
623
+ async loadActor(actorId) {
624
+ var _a;
625
+ const [doId, expectedGeneration] = parseActorId(actorId);
626
+ const doState = this.#globalState.getDOState(doId);
627
+ let actorState = this.#globalState.getActorState(doState.ctx);
628
+ if (actorState == null ? void 0 : actorState.actorInstance) {
629
+ return actorState.actorInstance;
630
+ }
631
+ if (!actorState) {
632
+ actorState = new ActorGlobalState();
633
+ actorState.actorPromise = promiseWithResolvers();
634
+ this.#globalState.setActorState(doState.ctx, actorState);
635
+ } else if (actorState.actorPromise) {
636
+ await actorState.actorPromise.promise;
637
+ if (!actorState.actorInstance) {
638
+ throw new Error(
639
+ `Actor ${actorId} failed to load in concurrent request`
640
+ );
641
+ }
642
+ return actorState.actorInstance;
643
+ }
644
+ const sql = doState.ctx.storage.sql;
645
+ const cursor = sql.exec(
646
+ "SELECT name, key, destroyed, generation FROM _rivetkit_metadata LIMIT 1"
647
+ );
648
+ const result = cursor.raw().next();
649
+ if (result.done || !result.value) {
650
+ throw new Error(
651
+ `Actor ${actorId} is not initialized - missing metadata`
652
+ );
653
+ }
654
+ const name = result.value[0];
655
+ const key = JSON.parse(result.value[1]);
656
+ const destroyed = result.value[2];
657
+ const generation = result.value[3];
658
+ if (destroyed) {
659
+ throw new Error(`Actor ${actorId} is destroyed`);
660
+ }
661
+ if (generation !== expectedGeneration) {
662
+ throw new Error(
663
+ `Actor ${actorId} generation mismatch - expected ${expectedGeneration}, got ${generation}`
664
+ );
665
+ }
666
+ const definition = lookupInRegistry(this.#registryConfig, name);
667
+ actorState.actorInstance = definition.instantiate();
668
+ await actorState.actorInstance.start(
669
+ this,
670
+ this.#inlineClient,
671
+ actorId,
672
+ name,
673
+ key,
674
+ "unknown"
675
+ // TODO: Support regions in Cloudflare
676
+ );
677
+ (_a = actorState.actorPromise) == null ? void 0 : _a.resolve();
678
+ actorState.actorPromise = void 0;
679
+ return actorState.actorInstance;
680
+ }
681
+ getContext(actorId) {
682
+ const [doId] = parseActorId(actorId);
683
+ const state = this.#globalState.getDOState(doId);
684
+ return { state: state.ctx };
685
+ }
686
+ async setAlarm(actor, timestamp) {
687
+ await this.#getDOCtx(actor.id).storage.setAlarm(timestamp);
688
+ }
689
+ async getDatabase(actorId) {
690
+ return this.#getDOCtx(actorId).storage.sql;
691
+ }
692
+ // Batch KV operations
693
+ async kvBatchPut(actorId, entries) {
694
+ const sql = this.#getDOCtx(actorId).storage.sql;
695
+ for (const [key, value] of entries) {
696
+ kvPut(sql, key, value);
697
+ }
698
+ }
699
+ async kvBatchGet(actorId, keys) {
700
+ const sql = this.#getDOCtx(actorId).storage.sql;
701
+ const results = [];
702
+ for (const key of keys) {
703
+ results.push(kvGet(sql, key));
704
+ }
705
+ return results;
706
+ }
707
+ async kvBatchDelete(actorId, keys) {
708
+ const sql = this.#getDOCtx(actorId).storage.sql;
709
+ for (const key of keys) {
710
+ kvDelete(sql, key);
711
+ }
712
+ }
713
+ async kvListPrefix(actorId, prefix) {
714
+ const sql = this.#getDOCtx(actorId).storage.sql;
715
+ return kvListPrefix(sql, prefix);
716
+ }
717
+ startDestroy(actorId) {
718
+ const [doId, generation] = parseActorId(actorId);
719
+ const doState = this.#globalState.getDOState(doId);
720
+ const actorState = this.#globalState.getActorState(doState.ctx);
721
+ if (!(actorState == null ? void 0 : actorState.actorInstance)) {
722
+ return;
723
+ }
724
+ if (actorState.destroying) {
725
+ return;
726
+ }
727
+ actorState.destroying = true;
728
+ this.#callOnStopAsync(actorId, doId, actorState.actorInstance);
729
+ }
730
+ async #callOnStopAsync(actorId, doId, actor) {
731
+ await actor.onStop("destroy");
732
+ const doState = this.#globalState.getDOState(doId);
733
+ const sql = doState.ctx.storage.sql;
734
+ sql.exec("UPDATE _rivetkit_metadata SET destroyed = 1 WHERE 1=1");
735
+ sql.exec("DELETE FROM _rivetkit_kv_storage");
736
+ await doState.ctx.storage.deleteAlarm();
737
+ const env3 = getCloudflareAmbientEnv();
738
+ doState.ctx.waitUntil(
739
+ env3.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId))
740
+ );
741
+ const actorHandle = this.#globalState.getActorState(doState.ctx);
742
+ actorHandle == null ? void 0 : actorHandle.reset();
743
+ }
744
+ };
745
+ function createCloudflareActorsActorDriverBuilder(globalState) {
746
+ return (registryConfig, runConfig, managerDriver, inlineClient) => {
747
+ return new CloudflareActorsActorDriver(
748
+ registryConfig,
749
+ runConfig,
750
+ managerDriver,
751
+ inlineClient,
752
+ globalState
753
+ );
754
+ };
755
+ }
756
+
757
+ // src/actor-handler-do.ts
758
+ function createActorDurableObject(registry, rootRunConfig) {
759
+ const globalState = new CloudflareDurableObjectGlobalState();
760
+ const runConfig = Object.assign({}, rootRunConfig, { role: "runner" });
761
+ return class ActorHandler extends DurableObject {
762
+ /**
763
+ * This holds a strong reference to ActorGlobalState.
764
+ * CloudflareDurableObjectGlobalState holds a weak reference so we can
765
+ * access it elsewhere.
766
+ **/
767
+ #state;
768
+ constructor(...args) {
769
+ super(...args);
770
+ this.ctx.storage.sql.exec(`
771
+ CREATE TABLE IF NOT EXISTS _rivetkit_kv_storage(
772
+ key BLOB PRIMARY KEY,
773
+ value BLOB
774
+ );
775
+ `);
776
+ this.ctx.storage.sql.exec(`
777
+ CREATE TABLE IF NOT EXISTS _rivetkit_metadata(
778
+ id INTEGER PRIMARY KEY CHECK (id = 1),
779
+ name TEXT NOT NULL,
780
+ key TEXT NOT NULL,
781
+ destroyed INTEGER DEFAULT 0,
782
+ generation INTEGER DEFAULT 0
783
+ );
784
+ `);
785
+ const state = globalState.getActorState(this.ctx);
786
+ if (state) {
787
+ this.#state = state;
788
+ } else {
789
+ this.#state = new ActorGlobalState();
790
+ globalState.setActorState(this.ctx, this.#state);
791
+ }
792
+ }
793
+ async #loadActor() {
794
+ var _a;
795
+ invariant2(this.#state, "State should be initialized");
796
+ if (!this.#state.initialized) {
797
+ const cursor = this.ctx.storage.sql.exec(
798
+ "SELECT name, key, destroyed, generation FROM _rivetkit_metadata WHERE id = 1"
799
+ );
800
+ const result = cursor.raw().next();
801
+ if (!result.done && result.value) {
802
+ const name = result.value[0];
803
+ const key = JSON.parse(
804
+ result.value[1]
805
+ );
806
+ const destroyed = result.value[2];
807
+ const generation = result.value[3];
808
+ if (!destroyed) {
809
+ logger().debug({
810
+ msg: "already initialized",
811
+ name,
812
+ key,
813
+ generation
814
+ });
815
+ this.#state.initialized = { name, key, generation };
816
+ } else {
817
+ logger().debug("actor is destroyed, cannot load");
818
+ throw new Error("Actor is destroyed");
819
+ }
820
+ } else {
821
+ logger().debug("not initialized");
822
+ throw new Error("Actor is not initialized");
823
+ }
824
+ }
825
+ if (this.#state.actor) {
826
+ invariant2(
827
+ !this.#state.initialized || this.#state.actor.generation === this.#state.initialized.generation,
828
+ `Stale actor cached: actor generation ${this.#state.actor.generation} != initialized generation ${(_a = this.#state.initialized) == null ? void 0 : _a.generation}. This should not happen.`
829
+ );
830
+ return this.#state.actor;
831
+ }
832
+ if (!this.#state.initialized) throw new Error("Not initialized");
833
+ const actorId = this.ctx.id.toString();
834
+ globalState.setDOState(actorId, { ctx: this.ctx, env: env2 });
835
+ invariant2(runConfig.driver, "runConfig.driver");
836
+ runConfig.driver.actor = createCloudflareActorsActorDriverBuilder(globalState);
837
+ const managerDriver = runConfig.driver.manager(
838
+ registry.config,
839
+ runConfig
840
+ );
841
+ const inlineClient = createClientWithDriver(
842
+ managerDriver,
843
+ runConfig
844
+ );
845
+ const actorDriver = runConfig.driver.actor(
846
+ registry.config,
847
+ runConfig,
848
+ managerDriver,
849
+ inlineClient
850
+ );
851
+ const actorRouter = createActorRouter(
852
+ runConfig,
853
+ actorDriver,
854
+ false
855
+ );
856
+ this.#state.actor = {
857
+ actorRouter,
858
+ actorDriver,
859
+ generation: this.#state.initialized.generation
860
+ };
861
+ const actorIdWithGen = buildActorId(
862
+ actorId,
863
+ this.#state.initialized.generation
864
+ );
865
+ await actorDriver.loadActor(actorIdWithGen);
866
+ return this.#state.actor;
867
+ }
868
+ /** RPC called to get actor metadata without creating it */
869
+ async getMetadata() {
870
+ var _a;
871
+ const cursor = this.ctx.storage.sql.exec(
872
+ "SELECT name, key, destroyed, generation FROM _rivetkit_metadata WHERE id = 1"
873
+ );
874
+ const result = cursor.raw().next();
875
+ if (!result.done && result.value) {
876
+ const name = result.value[0];
877
+ const key = JSON.parse(result.value[1]);
878
+ const destroyed = result.value[2];
879
+ const generation = result.value[3];
880
+ if (destroyed) {
881
+ logger().debug({
882
+ msg: "getMetadata: actor is destroyed",
883
+ name,
884
+ key,
885
+ generation
886
+ });
887
+ return void 0;
888
+ }
889
+ const doId = this.ctx.id.toString();
890
+ const actorId = buildActorId(doId, generation);
891
+ const destroying = ((_a = globalState.getActorState(this.ctx)) == null ? void 0 : _a.destroying) ?? false;
892
+ logger().debug({
893
+ msg: "getMetadata: found actor metadata",
894
+ actorId,
895
+ name,
896
+ key,
897
+ generation,
898
+ destroying
899
+ });
900
+ return { actorId, name, key, destroying };
901
+ }
902
+ logger().debug({
903
+ msg: "getMetadata: no metadata found"
904
+ });
905
+ return void 0;
906
+ }
907
+ /** RPC called by the manager to create a DO. Can optionally allow existing actors. */
908
+ async create(req) {
909
+ const checkCursor = this.ctx.storage.sql.exec(
910
+ "SELECT destroyed, generation FROM _rivetkit_metadata WHERE id = 1"
911
+ );
912
+ const checkResult = checkCursor.raw().next();
913
+ let created = false;
914
+ let generation = 0;
915
+ if (!checkResult.done && checkResult.value) {
916
+ const destroyed = checkResult.value[0];
917
+ generation = checkResult.value[1];
918
+ if (!destroyed) {
919
+ if (!req.allowExisting) {
920
+ logger().debug({
921
+ msg: "create failed: actor already exists",
922
+ name: req.name,
923
+ key: req.key,
924
+ generation
925
+ });
926
+ return { error: { actorAlreadyExists: true } };
927
+ }
928
+ logger().debug({
929
+ msg: "actor already exists",
930
+ key: req.key,
931
+ generation
932
+ });
933
+ const doId2 = this.ctx.id.toString();
934
+ const actorId2 = buildActorId(doId2, generation);
935
+ return { success: { actorId: actorId2, created: false } };
936
+ }
937
+ generation = generation + 1;
938
+ created = true;
939
+ if (this.#state) {
940
+ this.#state.actor = void 0;
941
+ }
942
+ logger().debug({
943
+ msg: "resurrecting destroyed actor",
944
+ key: req.key,
945
+ oldGeneration: generation - 1,
946
+ newGeneration: generation
947
+ });
948
+ } else {
949
+ generation = 0;
950
+ created = true;
951
+ logger().debug({
952
+ msg: "creating new actor",
953
+ key: req.key,
954
+ generation
955
+ });
956
+ }
957
+ this.ctx.storage.sql.exec(
958
+ `INSERT INTO _rivetkit_metadata (id, name, key, destroyed, generation)
959
+ VALUES (1, ?, ?, 0, ?)
960
+ ON CONFLICT(id) DO UPDATE SET
961
+ name = excluded.name,
962
+ key = excluded.key,
963
+ destroyed = 0,
964
+ generation = excluded.generation`,
965
+ req.name,
966
+ JSON.stringify(req.key),
967
+ generation
968
+ );
969
+ this.#state.initialized = {
970
+ name: req.name,
971
+ key: req.key,
972
+ generation
973
+ };
974
+ const doId = this.ctx.id.toString();
975
+ const actorId = buildActorId(doId, generation);
976
+ if (created) {
977
+ initializeActorKvStorage(this.ctx.storage.sql, req.input);
978
+ const env3 = getCloudflareAmbientEnv();
979
+ const actorData = { name: req.name, key: req.key, generation };
980
+ this.ctx.waitUntil(
981
+ env3.ACTOR_KV.put(
982
+ GLOBAL_KV_KEYS.actorMetadata(actorId),
983
+ JSON.stringify(actorData)
984
+ )
985
+ );
986
+ }
987
+ await this.#loadActor();
988
+ logger().debug({
989
+ msg: created ? "actor created/resurrected" : "returning existing actor",
990
+ actorId,
991
+ created,
992
+ generation
993
+ });
994
+ return { success: { actorId, created } };
995
+ }
996
+ async fetch(request) {
997
+ const { actorRouter, generation } = await this.#loadActor();
998
+ const doId = this.ctx.id.toString();
999
+ const actorId = buildActorId(doId, generation);
1000
+ return await actorRouter.fetch(request, {
1001
+ actorId
1002
+ });
1003
+ }
1004
+ async alarm() {
1005
+ const { actorDriver, generation } = await this.#loadActor();
1006
+ const doId = this.ctx.id.toString();
1007
+ const actorId = buildActorId(doId, generation);
1008
+ const actor = await actorDriver.loadActor(actorId);
1009
+ await actor.onAlarm();
1010
+ }
1011
+ };
1012
+ }
1013
+ function initializeActorKvStorage(sql, input) {
1014
+ const initialKvState = getInitialActorKvState(input);
1015
+ for (const [key, value] of initialKvState) {
1016
+ kvPut(sql, key, value);
1017
+ }
654
1018
  }
655
1019
  export {
656
- createHandler
1020
+ createActorDurableObject,
1021
+ createHandler,
1022
+ createInlineClient
657
1023
  };
658
1024
  //# sourceMappingURL=mod.js.map