@base44/functions-compiler 0.1.0

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.
Files changed (81) hide show
  1. package/README.md +137 -0
  2. package/lib/dist/activation-shim.mjs +237 -0
  3. package/lib/dist/actor.mjs +1129 -0
  4. package/lib/dist/deno-shim.mjs +2347 -0
  5. package/lib/src/actor-compat.d.ts +24 -0
  6. package/lib/src/actor-compat.js +302 -0
  7. package/lib/src/actor-compat.js.map +1 -0
  8. package/lib/src/bundler.d.ts +51 -0
  9. package/lib/src/bundler.js +321 -0
  10. package/lib/src/bundler.js.map +1 -0
  11. package/lib/src/contracts.d.ts +108 -0
  12. package/lib/src/contracts.js +39 -0
  13. package/lib/src/contracts.js.map +1 -0
  14. package/lib/src/deno-bundle.d.ts +30 -0
  15. package/lib/src/deno-bundle.js +146 -0
  16. package/lib/src/deno-bundle.js.map +1 -0
  17. package/lib/src/errors.d.ts +16 -0
  18. package/lib/src/errors.js +11 -0
  19. package/lib/src/errors.js.map +1 -0
  20. package/lib/src/esbuild/deno-resolver.d.ts +26 -0
  21. package/lib/src/esbuild/deno-resolver.js +436 -0
  22. package/lib/src/esbuild/deno-resolver.js.map +1 -0
  23. package/lib/src/esbuild/node-builtin-require.d.ts +2 -0
  24. package/lib/src/esbuild/node-builtin-require.js +33 -0
  25. package/lib/src/esbuild/node-builtin-require.js.map +1 -0
  26. package/lib/src/esbuild/private-data-sources-virtual.d.ts +3 -0
  27. package/lib/src/esbuild/private-data-sources-virtual.js +127 -0
  28. package/lib/src/esbuild/private-data-sources-virtual.js.map +1 -0
  29. package/lib/src/esbuild/runtime-context-virtual.d.ts +3 -0
  30. package/lib/src/esbuild/runtime-context-virtual.js +42 -0
  31. package/lib/src/esbuild/runtime-context-virtual.js.map +1 -0
  32. package/lib/src/esbuild/runtime-virtual.d.ts +2 -0
  33. package/lib/src/esbuild/runtime-virtual.js +52 -0
  34. package/lib/src/esbuild/runtime-virtual.js.map +1 -0
  35. package/lib/src/esbuild/user-files.d.ts +15 -0
  36. package/lib/src/esbuild/user-files.js +73 -0
  37. package/lib/src/esbuild/user-files.js.map +1 -0
  38. package/lib/src/fetch-guard.d.ts +26 -0
  39. package/lib/src/fetch-guard.js +67 -0
  40. package/lib/src/fetch-guard.js.map +1 -0
  41. package/lib/src/index.d.ts +12 -0
  42. package/lib/src/index.js +11 -0
  43. package/lib/src/index.js.map +1 -0
  44. package/lib/src/log.d.ts +7 -0
  45. package/lib/src/log.js +39 -0
  46. package/lib/src/log.js.map +1 -0
  47. package/lib/src/private-data-sources/build-http.ts +51 -0
  48. package/lib/src/private-data-sources/elasticsearch.ts +8 -0
  49. package/lib/src/private-data-sources/http-url.ts +23 -0
  50. package/lib/src/private-data-sources/http.ts +6 -0
  51. package/lib/src/private-data-sources/hyperdrive.ts +48 -0
  52. package/lib/src/private-data-sources/ioredis-adapter.ts +229 -0
  53. package/lib/src/private-data-sources/manifest.ts +150 -0
  54. package/lib/src/private-data-sources/mariadb.ts +8 -0
  55. package/lib/src/private-data-sources/mongodb.ts +70 -0
  56. package/lib/src/private-data-sources/mysql.ts +8 -0
  57. package/lib/src/private-data-sources/node-net-adapter.ts +244 -0
  58. package/lib/src/private-data-sources/postgres.ts +8 -0
  59. package/lib/src/private-data-sources/redis-auth.ts +83 -0
  60. package/lib/src/private-data-sources/redis.ts +47 -0
  61. package/lib/src/private-data-sources/runtime-environment.ts +4 -0
  62. package/lib/src/private-data-sources/runtime-manifest-store.ts +32 -0
  63. package/lib/src/private-data-sources/sqlserver.ts +22 -0
  64. package/lib/src/private-data-sources/tcp.ts +86 -0
  65. package/lib/src/private-data-sources/tedious-adapter.ts +67 -0
  66. package/lib/src/private-data-sources/types.ts +29 -0
  67. package/lib/src/runtime/index.ts +30 -0
  68. package/lib/src/runtime-context.ts +29 -0
  69. package/lib/src/static-egress-marker.d.ts +1 -0
  70. package/lib/src/static-egress-marker.js +7 -0
  71. package/lib/src/static-egress-marker.js.map +1 -0
  72. package/lib/src/telemetry.d.ts +32 -0
  73. package/lib/src/telemetry.js +84 -0
  74. package/lib/src/telemetry.js.map +1 -0
  75. package/lib/src/tracing.d.ts +9 -0
  76. package/lib/src/tracing.js +21 -0
  77. package/lib/src/tracing.js.map +1 -0
  78. package/lib/src/worker-entry.d.ts +36 -0
  79. package/lib/src/worker-entry.js +320 -0
  80. package/lib/src/worker-entry.js.map +1 -0
  81. package/package.json +43 -0
@@ -0,0 +1,1129 @@
1
+ // GENERATED by scripts/build-shim.ts from src/shim/actor.ts — do not edit.
2
+ // Regenerate with `npm run build:shim`.
3
+
4
+ // ../../node_modules/partyserver/dist/index.js
5
+ import { DurableObject } from "cloudflare:workers";
6
+
7
+ // ../../node_modules/nanoid/url-alphabet/index.js
8
+ var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
9
+
10
+ // ../../node_modules/nanoid/index.browser.js
11
+ var nanoid = (size = 21) => {
12
+ let id = "";
13
+ let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
14
+ while (size--) {
15
+ id += urlAlphabet[bytes[size] & 63];
16
+ }
17
+ return id;
18
+ };
19
+
20
+ // ../../node_modules/partyserver/dist/index.js
21
+ if (!("OPEN" in WebSocket)) {
22
+ const WebSocketStatus = {
23
+ CONNECTING: WebSocket.READY_STATE_CONNECTING,
24
+ OPEN: WebSocket.READY_STATE_OPEN,
25
+ CLOSING: WebSocket.READY_STATE_CLOSING,
26
+ CLOSED: WebSocket.READY_STATE_CLOSED
27
+ };
28
+ Object.assign(WebSocket, WebSocketStatus);
29
+ Object.assign(WebSocket.prototype, WebSocketStatus);
30
+ }
31
+ var AttachmentCache = class {
32
+ #cache = /* @__PURE__ */ new WeakMap();
33
+ get(ws) {
34
+ let attachment = this.#cache.get(ws);
35
+ if (!attachment) {
36
+ attachment = WebSocket.prototype.deserializeAttachment.call(
37
+ ws
38
+ );
39
+ if (attachment !== void 0) {
40
+ this.#cache.set(ws, attachment);
41
+ } else {
42
+ throw new Error(
43
+ "Missing websocket attachment. This is most likely an issue in PartyServer, please open an issue at https://github.com/threepointone/partyserver/issues"
44
+ );
45
+ }
46
+ }
47
+ return attachment;
48
+ }
49
+ set(ws, attachment) {
50
+ this.#cache.set(ws, attachment);
51
+ WebSocket.prototype.serializeAttachment.call(ws, attachment);
52
+ }
53
+ };
54
+ var attachments = new AttachmentCache();
55
+ var connections = /* @__PURE__ */ new WeakSet();
56
+ var isWrapped = (ws) => {
57
+ return connections.has(ws);
58
+ };
59
+ var createLazyConnection = (ws) => {
60
+ if (isWrapped(ws)) {
61
+ return ws;
62
+ }
63
+ let initialState = void 0;
64
+ if ("state" in ws) {
65
+ initialState = ws.state;
66
+ delete ws.state;
67
+ }
68
+ const connection = Object.defineProperties(ws, {
69
+ id: {
70
+ get() {
71
+ return attachments.get(ws).__pk.id;
72
+ }
73
+ },
74
+ server: {
75
+ get() {
76
+ return attachments.get(ws).__pk.server;
77
+ }
78
+ },
79
+ socket: {
80
+ get() {
81
+ return ws;
82
+ }
83
+ },
84
+ state: {
85
+ get() {
86
+ return ws.deserializeAttachment();
87
+ }
88
+ },
89
+ setState: {
90
+ value: function setState(setState) {
91
+ let state;
92
+ if (setState instanceof Function) {
93
+ state = setState(this.state);
94
+ } else {
95
+ state = setState;
96
+ }
97
+ ws.serializeAttachment(state);
98
+ return state;
99
+ }
100
+ },
101
+ deserializeAttachment: {
102
+ value: function deserializeAttachment() {
103
+ const attachment = attachments.get(ws);
104
+ return attachment.__user ?? null;
105
+ }
106
+ },
107
+ serializeAttachment: {
108
+ value: function serializeAttachment(attachment) {
109
+ const setting = {
110
+ ...attachments.get(ws),
111
+ __user: attachment ?? null
112
+ };
113
+ attachments.set(ws, setting);
114
+ }
115
+ }
116
+ });
117
+ if (initialState) {
118
+ connection.setState(initialState);
119
+ }
120
+ connections.add(connection);
121
+ return connection;
122
+ };
123
+ var HibernatingConnectionIterator = class {
124
+ constructor(state, tag) {
125
+ this.state = state;
126
+ this.tag = tag;
127
+ }
128
+ index = 0;
129
+ sockets;
130
+ [Symbol.iterator]() {
131
+ return this;
132
+ }
133
+ next() {
134
+ const sockets = this.sockets ?? (this.sockets = this.state.getWebSockets(this.tag));
135
+ let socket;
136
+ while (socket = sockets[this.index++]) {
137
+ if (socket.readyState === WebSocket.READY_STATE_OPEN) {
138
+ const value = createLazyConnection(socket);
139
+ return { done: false, value };
140
+ }
141
+ }
142
+ return { done: true, value: void 0 };
143
+ }
144
+ };
145
+ var InMemoryConnectionManager = class {
146
+ #connections = /* @__PURE__ */ new Map();
147
+ tags = /* @__PURE__ */ new WeakMap();
148
+ getCount() {
149
+ return this.#connections.size;
150
+ }
151
+ getConnection(id) {
152
+ return this.#connections.get(id);
153
+ }
154
+ *getConnections(tag) {
155
+ if (!tag) {
156
+ yield* this.#connections.values();
157
+ return;
158
+ }
159
+ for (const connection of this.#connections.values()) {
160
+ const connectionTags = this.tags.get(connection) ?? [];
161
+ if (connectionTags.includes(tag)) {
162
+ yield connection;
163
+ }
164
+ }
165
+ }
166
+ accept(connection, options) {
167
+ connection.accept();
168
+ this.#connections.set(connection.id, connection);
169
+ this.tags.set(connection, [
170
+ // make sure we have id tag
171
+ connection.id,
172
+ ...options.tags.filter((t) => t !== connection.id)
173
+ ]);
174
+ const removeConnection = () => {
175
+ this.#connections.delete(connection.id);
176
+ connection.removeEventListener("close", removeConnection);
177
+ connection.removeEventListener("error", removeConnection);
178
+ };
179
+ connection.addEventListener("close", removeConnection);
180
+ connection.addEventListener("error", removeConnection);
181
+ return connection;
182
+ }
183
+ };
184
+ var HibernatingConnectionManager = class {
185
+ constructor(controller) {
186
+ this.controller = controller;
187
+ }
188
+ getCount() {
189
+ return Number(this.controller.getWebSockets().length);
190
+ }
191
+ getConnection(id) {
192
+ const sockets = this.controller.getWebSockets(id);
193
+ if (sockets.length === 0) return void 0;
194
+ if (sockets.length === 1)
195
+ return createLazyConnection(sockets[0]);
196
+ throw new Error(
197
+ `More than one connection found for id ${id}. Did you mean to use getConnections(tag) instead?`
198
+ );
199
+ }
200
+ getConnections(tag) {
201
+ return new HibernatingConnectionIterator(this.controller, tag);
202
+ }
203
+ accept(connection, options) {
204
+ const tags = [
205
+ connection.id,
206
+ ...options.tags.filter((t) => t !== connection.id)
207
+ ];
208
+ if (tags.length > 10) {
209
+ throw new Error(
210
+ "A connection can only have 10 tags, including the default id tag."
211
+ );
212
+ }
213
+ for (const tag of tags) {
214
+ if (typeof tag !== "string") {
215
+ throw new Error(`A connection tag must be a string. Received: ${tag}`);
216
+ }
217
+ if (tag === "") {
218
+ throw new Error(`A connection tag must not be an empty string.`);
219
+ }
220
+ if (tag.length > 256) {
221
+ throw new Error(`A connection tag must not exceed 256 characters`);
222
+ }
223
+ }
224
+ this.controller.acceptWebSocket(connection, tags);
225
+ connection.serializeAttachment({
226
+ __pk: {
227
+ id: connection.id,
228
+ server: options.server
229
+ },
230
+ __user: null
231
+ });
232
+ return createLazyConnection(connection);
233
+ }
234
+ };
235
+ var serverMapCache = /* @__PURE__ */ new WeakMap();
236
+ function camelCaseToKebabCase(str) {
237
+ let kebabified = str.replace(
238
+ /[A-Z]/g,
239
+ (letter) => `-${letter.toLowerCase()}`
240
+ );
241
+ kebabified = kebabified.startsWith("-") ? kebabified.slice(1) : kebabified;
242
+ return kebabified.replace(/-$/, "");
243
+ }
244
+ async function routePartykitRequest(req, env, options) {
245
+ if (!serverMapCache.has(env)) {
246
+ serverMapCache.set(
247
+ env,
248
+ Object.entries(env).reduce((acc, [k, v]) => {
249
+ if (v && typeof v === "object" && "idFromName" in v && typeof v.idFromName === "function") {
250
+ return { ...acc, [camelCaseToKebabCase(k)]: v };
251
+ }
252
+ return acc;
253
+ }, {})
254
+ );
255
+ }
256
+ const map = serverMapCache.get(env);
257
+ const prefix = options?.prefix || "parties";
258
+ const url = new URL(req.url);
259
+ const parts = url.pathname.split("/");
260
+ if (parts[1] === prefix && parts.length < 4) {
261
+ return null;
262
+ }
263
+ const name = parts[3], namespace = parts[2];
264
+ if (parts[1] === prefix && name && namespace) {
265
+ if (!map[namespace]) {
266
+ if (namespace === "main") {
267
+ console.warn(
268
+ `You appear to be migrating a PartyKit project to PartyServer.`
269
+ );
270
+ console.warn(`PartyServer doesn't have a "main" party by default. Try adding this to your PartySocket client:
271
+
272
+ party: "${camelCaseToKebabCase(Object.keys(map)[0])}"`);
273
+ } else {
274
+ console.error(`The url ${req.url} does not match any server namespace.
275
+ Did you forget to add a durable object binding to the class in your wrangler.toml?`);
276
+ }
277
+ }
278
+ let doNamespace = map[namespace];
279
+ if (options?.jurisdiction) {
280
+ doNamespace = doNamespace.jurisdiction(options.jurisdiction);
281
+ }
282
+ const id = doNamespace.idFromName(name);
283
+ const stub = doNamespace.get(id, options);
284
+ req = new Request(req);
285
+ req.headers.set("x-partykit-room", name);
286
+ req.headers.set("x-partykit-namespace", namespace);
287
+ if (options?.jurisdiction) {
288
+ req.headers.set("x-partykit-jurisdiction", options.jurisdiction);
289
+ }
290
+ return stub.fetch(req);
291
+ } else {
292
+ return null;
293
+ }
294
+ }
295
+ var Server = class extends DurableObject {
296
+ static options = {
297
+ hibernate: false
298
+ };
299
+ #status = "zero";
300
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
301
+ #ParentClass = Object.getPrototypeOf(this).constructor;
302
+ #connectionManager = this.#ParentClass.options.hibernate ? new HibernatingConnectionManager(this.ctx) : new InMemoryConnectionManager();
303
+ constructor(ctx, env) {
304
+ super(ctx, env);
305
+ }
306
+ /**
307
+ * Handle incoming requests to the server.
308
+ */
309
+ async fetch(request) {
310
+ if (!this.#_name) {
311
+ const room = request.headers.get("x-partykit-room");
312
+ if (
313
+ // !namespace ||
314
+ !room
315
+ ) {
316
+ throw new Error(`Missing namespace or room headers when connecting to ${this.#ParentClass.name}.
317
+ Did you try connecting directly to this Durable Object? Try using getServerByName(namespace, id) instead.`);
318
+ }
319
+ await this.setName(room);
320
+ }
321
+ try {
322
+ const url = new URL(request.url);
323
+ if (url.pathname === "/cdn-cgi/partyserver/set-name/") {
324
+ return Response.json({ ok: true });
325
+ }
326
+ if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") {
327
+ return await this.onRequest(request);
328
+ } else {
329
+ const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair();
330
+ let connectionId = url.searchParams.get("_pk");
331
+ if (!connectionId) {
332
+ connectionId = nanoid();
333
+ }
334
+ let connection = Object.assign(serverWebSocket, {
335
+ id: connectionId,
336
+ server: this.name,
337
+ state: null,
338
+ setState(setState) {
339
+ let state;
340
+ if (setState instanceof Function) {
341
+ state = setState(this.state);
342
+ } else {
343
+ state = setState;
344
+ }
345
+ this.state = state;
346
+ return this.state;
347
+ }
348
+ });
349
+ const ctx = { request };
350
+ const tags = await this.getConnectionTags(connection, ctx);
351
+ connection = this.#connectionManager.accept(connection, {
352
+ tags,
353
+ server: this.name
354
+ });
355
+ if (!this.#ParentClass.options.hibernate) {
356
+ this.#attachSocketEventHandlers(connection);
357
+ }
358
+ await this.onConnect(connection, ctx);
359
+ return new Response(null, { status: 101, webSocket: clientWebSocket });
360
+ }
361
+ } catch (err) {
362
+ console.error(
363
+ `Error in ${this.#ParentClass.name}:${this.name} fetch:`,
364
+ err
365
+ );
366
+ if (!(err instanceof Error)) throw err;
367
+ if (request.headers.get("Upgrade") == "websocket") {
368
+ const pair = new WebSocketPair();
369
+ pair[1].accept();
370
+ pair[1].send(JSON.stringify({ error: err.stack }));
371
+ pair[1].close(1011, "Uncaught exception during session setup");
372
+ return new Response(null, { status: 101, webSocket: pair[0] });
373
+ } else {
374
+ return new Response(err.stack, { status: 500 });
375
+ }
376
+ }
377
+ }
378
+ async webSocketMessage(ws, message) {
379
+ const connection = createLazyConnection(ws);
380
+ await this.setName(connection.server);
381
+ if (this.#status !== "started") {
382
+ await this.#initialize();
383
+ }
384
+ return this.onMessage(connection, message);
385
+ }
386
+ async webSocketClose(ws, code, reason, wasClean) {
387
+ const connection = createLazyConnection(ws);
388
+ await this.setName(connection.server);
389
+ if (this.#status !== "started") {
390
+ await this.#initialize();
391
+ }
392
+ return this.onClose(connection, code, reason, wasClean);
393
+ }
394
+ async webSocketError(ws, error) {
395
+ const connection = createLazyConnection(ws);
396
+ await this.setName(connection.server);
397
+ if (this.#status !== "started") {
398
+ await this.#initialize();
399
+ }
400
+ return this.onError(connection, error);
401
+ }
402
+ async #initialize() {
403
+ await this.ctx.blockConcurrencyWhile(async () => {
404
+ this.#status = "starting";
405
+ await this.onStart();
406
+ this.#status = "started";
407
+ });
408
+ }
409
+ #attachSocketEventHandlers(connection) {
410
+ const handleMessageFromClient = (event) => {
411
+ this.onMessage(connection, event.data)?.catch((e) => {
412
+ console.error("onMessage error:", e);
413
+ });
414
+ };
415
+ const handleCloseFromClient = (event) => {
416
+ connection.removeEventListener("message", handleMessageFromClient);
417
+ connection.removeEventListener("close", handleCloseFromClient);
418
+ this.onClose(connection, event.code, event.reason, event.wasClean)?.catch(
419
+ (e) => {
420
+ console.error("onClose error:", e);
421
+ }
422
+ );
423
+ };
424
+ const handleErrorFromClient = (e) => {
425
+ connection.removeEventListener("message", handleMessageFromClient);
426
+ connection.removeEventListener("error", handleErrorFromClient);
427
+ this.onError(connection, e.error)?.catch((e2) => {
428
+ console.error("onError error:", e2);
429
+ });
430
+ };
431
+ connection.addEventListener("close", handleCloseFromClient);
432
+ connection.addEventListener("error", handleErrorFromClient);
433
+ connection.addEventListener("message", handleMessageFromClient);
434
+ }
435
+ // Public API
436
+ #_name;
437
+ #_longErrorAboutNameThrown = false;
438
+ /**
439
+ * The name for this server. Write-once-only.
440
+ */
441
+ get name() {
442
+ if (!this.#_name) {
443
+ if (!this.#_longErrorAboutNameThrown) {
444
+ this.#_longErrorAboutNameThrown = true;
445
+ throw new Error(
446
+ `Attempting to read .name on ${this.#ParentClass.name} before it was set. The name can be set by explicitly calling .setName(name) on the stub, or by using routePartyKitRequest(). This is a known issue and will be fixed soon. Follow https://github.com/cloudflare/workerd/issues/2240 for more updates.`
447
+ );
448
+ } else {
449
+ throw new Error(
450
+ `Attempting to read .name on ${this.#ParentClass.name} before it was set.`
451
+ );
452
+ }
453
+ }
454
+ return this.#_name;
455
+ }
456
+ // We won't have an await inside this function
457
+ // but it will be called remotely,
458
+ // so we need to mark it as async
459
+ async setName(name) {
460
+ if (!name) {
461
+ throw new Error("A name is required.");
462
+ }
463
+ if (this.#_name && this.#_name !== name) {
464
+ throw new Error("This server already has a name.");
465
+ }
466
+ this.#_name = name;
467
+ if (this.#status !== "started") {
468
+ await this.ctx.blockConcurrencyWhile(async () => {
469
+ await this.#initialize();
470
+ });
471
+ }
472
+ }
473
+ #sendMessageToConnection(connection, message) {
474
+ try {
475
+ connection.send(message);
476
+ } catch (_e) {
477
+ connection.close(1011, "Unexpected error");
478
+ }
479
+ }
480
+ /** Send a message to all connected clients, except connection ids listed in `without` */
481
+ broadcast(msg, without) {
482
+ for (const connection of this.#connectionManager.getConnections()) {
483
+ if (!without || !without.includes(connection.id)) {
484
+ this.#sendMessageToConnection(connection, msg);
485
+ }
486
+ }
487
+ }
488
+ /** Get a connection by connection id */
489
+ getConnection(id) {
490
+ return this.#connectionManager.getConnection(id);
491
+ }
492
+ /**
493
+ * Get all connections. Optionally, you can provide a tag to filter returned connections.
494
+ * Use `Server#getConnectionTags` to tag the connection on connect.
495
+ */
496
+ getConnections(tag) {
497
+ return this.#connectionManager.getConnections(tag);
498
+ }
499
+ /**
500
+ * You can tag a connection to filter them in Server#getConnections.
501
+ * Each connection supports up to 9 tags, each tag max length is 256 characters.
502
+ */
503
+ getConnectionTags(connection, context) {
504
+ return [];
505
+ }
506
+ // Implemented by the user
507
+ /**
508
+ * Called when the server is started for the first time.
509
+ */
510
+ onStart() {
511
+ }
512
+ /**
513
+ * Called when a new connection is made to the server.
514
+ */
515
+ onConnect(connection, ctx) {
516
+ console.log(
517
+ `Connection ${connection.id} connected to ${this.#ParentClass.name}:${this.name}`
518
+ );
519
+ }
520
+ /**
521
+ * Called when a message is received from a connection.
522
+ */
523
+ onMessage(connection, message) {
524
+ console.log(
525
+ `Received message on connection ${this.#ParentClass.name}:${connection.id}`
526
+ );
527
+ console.info(
528
+ `Implement onMessage on ${this.#ParentClass.name} to handle this message.`
529
+ );
530
+ }
531
+ /**
532
+ * Called when a connection is closed.
533
+ */
534
+ onClose(connection, code, reason, wasClean) {
535
+ }
536
+ /**
537
+ * Called when an error occurs on a connection.
538
+ */
539
+ onError(connection, error) {
540
+ console.error(
541
+ `Error on connection ${connection.id} in ${this.#ParentClass.name}:${this.name}:`,
542
+ error
543
+ );
544
+ console.info(
545
+ `Implement onError on ${this.#ParentClass.name} to handle this error.`
546
+ );
547
+ }
548
+ /**
549
+ * Called when a request is made to the server.
550
+ */
551
+ onRequest(request) {
552
+ console.warn(
553
+ `onRequest hasn't been implemented on ${this.#ParentClass.name}:${this.name} responding to ${request.url}`
554
+ );
555
+ return new Response(`Not implemented`, { status: 404 });
556
+ }
557
+ onAlarm() {
558
+ console.log(
559
+ `Implement onAlarm on ${this.#ParentClass.name} to handle alarms.`
560
+ );
561
+ }
562
+ async alarm() {
563
+ if (this.#status !== "started") {
564
+ await this.#initialize();
565
+ }
566
+ await this.onAlarm();
567
+ }
568
+ };
569
+
570
+ // src/shim/actor.ts
571
+ import { createClient } from "npm:@base44/sdk@0.8.41";
572
+
573
+ // src/shim/tick-loop.ts
574
+ var TickLoop = class {
575
+ constructor(onTick, maxCatchup = 3) {
576
+ this.onTick = onTick;
577
+ this.maxCatchup = maxCatchup;
578
+ }
579
+ onTick;
580
+ maxCatchup;
581
+ timer = null;
582
+ nextAt = 0;
583
+ running = false;
584
+ intervalMs = 0;
585
+ // A run() resuming after stop()+start() must not arm a second, untracked
586
+ // timer — each run() acts only for its own generation.
587
+ gen = 0;
588
+ get isRunning() {
589
+ return this.running;
590
+ }
591
+ start(intervalMs) {
592
+ if (this.running) {
593
+ if (intervalMs === this.intervalMs) return;
594
+ this.intervalMs = intervalMs;
595
+ this.nextAt = Date.now() + intervalMs;
596
+ if (this.timer !== null) {
597
+ clearTimeout(this.timer);
598
+ this.timer = null;
599
+ this.schedule();
600
+ }
601
+ return;
602
+ }
603
+ this.running = true;
604
+ this.intervalMs = intervalMs;
605
+ this.nextAt = Date.now() + intervalMs;
606
+ this.schedule();
607
+ }
608
+ stop() {
609
+ this.running = false;
610
+ this.gen++;
611
+ if (this.timer !== null) {
612
+ clearTimeout(this.timer);
613
+ this.timer = null;
614
+ }
615
+ }
616
+ schedule() {
617
+ this.timer = setTimeout(() => {
618
+ void this.run();
619
+ }, Math.max(0, this.nextAt - Date.now()));
620
+ }
621
+ async run() {
622
+ const gen = this.gen;
623
+ this.timer = null;
624
+ let steps = 0;
625
+ while (this.running && this.gen === gen && Date.now() >= this.nextAt && steps < this.maxCatchup) {
626
+ steps++;
627
+ this.nextAt += this.intervalMs;
628
+ let keep = true;
629
+ try {
630
+ keep = await this.onTick();
631
+ } catch (err) {
632
+ console.error("TickLoop tick threw:", err);
633
+ }
634
+ if (this.gen !== gen) return;
635
+ if (!keep) {
636
+ this.stop();
637
+ return;
638
+ }
639
+ }
640
+ if (!this.running || this.gen !== gen) return;
641
+ if (Date.now() >= this.nextAt) this.nextAt = Date.now() + this.intervalMs;
642
+ this.schedule();
643
+ }
644
+ };
645
+
646
+ // src/shim/actor.ts
647
+ var SUPERSEDED_PREFIX = "__superseded__:";
648
+ function buildConn(ws, identity) {
649
+ return {
650
+ id: ws.id,
651
+ // partyserver's per-connection id (survives hibernation)
652
+ ...identity ? { identity } : {},
653
+ send(data) {
654
+ ws.send(JSON.stringify(data));
655
+ },
656
+ reject(code, reason) {
657
+ ws.close(code, reason);
658
+ }
659
+ };
660
+ }
661
+ var Actor = class _Actor extends Server {
662
+ // WebSocket Hibernation: idle rooms are evicted from memory (no duration
663
+ // billing) with sockets kept open; onStart re-runs on wake to rehydrate. A
664
+ // ticking room stays resident (its setTimeout loop blocks hibernation), so this
665
+ // only affects non-ticking occupied rooms. Statics inherit, so a handler opts
666
+ // out with `static options = { hibernate: false }`.
667
+ static options = { hibernate: true };
668
+ // conn.id is client-chosen and partyserver silently REPLACES a same-id socket
669
+ // on accept, so a duplicate must be resolved before super.fetch(). Liveness
670
+ // (SDK pings every 1s) distinguishes a real conflict from a reconnect.
671
+ lastSeen = /* @__PURE__ */ new Map();
672
+ identities = /* @__PURE__ */ new Map();
673
+ static LIVE_MS = 3e3;
674
+ static IDENTITY_HEADER = "X-Base44-Actor-Identity";
675
+ // Captured at construction so `client` reads this DO's own env, not the shared
676
+ // globalThis.Base44 bridge (which the last-constructed DO in the isolate wins).
677
+ _b44Env;
678
+ _b44Client;
679
+ get client() {
680
+ return this._b44Client ??= new Proxy(
681
+ createClient({
682
+ appId: this._b44Env.BASE44_APP_ID,
683
+ serverUrl: this._b44Env.BASE44_API_URL,
684
+ // "prod" on the published script, "preview" on the draft — set at deploy.
685
+ functionsVersion: this._b44Env.BASE44_FUNCTIONS_VERSION
686
+ }),
687
+ {
688
+ // The SDK bakes serviceToken into axios defaults at createClient time and
689
+ // its own asServiceRole getter throws without one, so that one property is
690
+ // answered by the lazy exchange below instead.
691
+ get: (target, prop) => prop === "asServiceRole" ? this._b44ServiceRolePath([]) : Reflect.get(target, prop, target)
692
+ }
693
+ );
694
+ }
695
+ // ── Service role ──────────────────────────────────────────────────────
696
+ // The token is fetched on the first privileged call by trading an assertion
697
+ // signed with the actor's private key, cached in memory, and re-fetched
698
+ // after expiry. Hibernation wiping the cache just means the next call
699
+ // exchanges again — nothing durable is stored.
700
+ _b44Service = null;
701
+ _b44ServicePending = null;
702
+ async _b44SignServiceAssertion(privateKey, publicKey) {
703
+ const b64url = (bin) => btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
704
+ const now = Math.floor(Date.now() / 1e3);
705
+ const claims = {
706
+ iss: "base44-actor",
707
+ aud: "base44-actor-service-token",
708
+ purpose: "actor-service-token",
709
+ v: 1,
710
+ iat: now,
711
+ exp: now + 60,
712
+ app_id: this._b44Env.BASE44_APP_ID,
713
+ actor_name: this.constructor._b44ActorName
714
+ };
715
+ const input = `${b64url(JSON.stringify({ alg: "EdDSA", typ: "JWT" }))}.${b64url(JSON.stringify(claims))}`;
716
+ const key = await crypto.subtle.importKey(
717
+ "jwk",
718
+ { kty: "OKP", crv: "Ed25519", d: privateKey, x: publicKey },
719
+ { name: "Ed25519" },
720
+ false,
721
+ ["sign"]
722
+ );
723
+ const signature = new Uint8Array(
724
+ await crypto.subtle.sign({ name: "Ed25519" }, key, new TextEncoder().encode(input))
725
+ );
726
+ return `${input}.${b64url(String.fromCharCode(...signature))}`;
727
+ }
728
+ async _b44FetchServiceClient() {
729
+ const privateKey = this._b44Env.BASE44_ACTOR_PRIVATE_KEY;
730
+ const publicKey = this._b44Env.BASE44_ACTOR_PUBLIC_KEY;
731
+ if (typeof privateKey !== "string" || !privateKey || typeof publicKey !== "string" || !publicKey) {
732
+ throw new Error(
733
+ "asServiceRole is unavailable on this actor: it predates direct connections. Delete the actor and deploy it again (room storage is not preserved)."
734
+ );
735
+ }
736
+ const response = await fetch(
737
+ `${this._b44Env.BASE44_API_URL}/api/apps/${this._b44Env.BASE44_APP_ID}/actors/service-token`,
738
+ {
739
+ method: "POST",
740
+ headers: { "Content-Type": "application/json" },
741
+ body: JSON.stringify({ assertion: await this._b44SignServiceAssertion(privateKey, publicKey) })
742
+ }
743
+ );
744
+ if (!response.ok) {
745
+ throw new Error(`Actor service token exchange failed (${response.status})`);
746
+ }
747
+ const { access_token, expires_in } = await response.json();
748
+ const client = createClient({
749
+ appId: this._b44Env.BASE44_APP_ID,
750
+ serverUrl: this._b44Env.BASE44_API_URL,
751
+ functionsVersion: this._b44Env.BASE44_FUNCTIONS_VERSION,
752
+ serviceToken: access_token
753
+ });
754
+ this._b44Service = { client, expiresAtMs: Date.now() + (expires_in - 60) * 1e3 };
755
+ return client;
756
+ }
757
+ _b44ServiceClient() {
758
+ if (this._b44Service && Date.now() < this._b44Service.expiresAtMs) {
759
+ return Promise.resolve(this._b44Service.client);
760
+ }
761
+ return this._b44ServicePending ??= this._b44FetchServiceClient().finally(() => {
762
+ this._b44ServicePending = null;
763
+ });
764
+ }
765
+ // asServiceRole is a sync property chain ending in an async call, so proxies
766
+ // record the chain and the exchange awaits inside the final call — actor code
767
+ // reads identically to backend functions.
768
+ _b44ServiceRolePath(path) {
769
+ const self = this;
770
+ return new Proxy(function() {
771
+ }, {
772
+ get(_target, prop) {
773
+ if (typeof prop === "symbol" || prop === "then") return void 0;
774
+ return self._b44ServiceRolePath([...path, prop]);
775
+ },
776
+ async apply(_target, _thisArg, args) {
777
+ const serviceClient = await self._b44ServiceClient();
778
+ let parent = serviceClient.asServiceRole;
779
+ for (const prop of path.slice(0, -1)) parent = parent[prop];
780
+ return parent[path[path.length - 1]](...args);
781
+ }
782
+ });
783
+ }
784
+ // Reserved platform keys — the private-data-sources manifest carries
785
+ // plaintext VPC DB credentials. Keep in sync with worker-entry.ts.
786
+ static RESERVED_SECRETS = /* @__PURE__ */ new Set([
787
+ "BASE44_ACTOR_PRIVATE_KEY",
788
+ "BASE44_ACTOR_PUBLIC_KEY",
789
+ "BASE44_ACTOR_SCRIPT_ID",
790
+ "BASE44_PRIVATE_DATA_SOURCES"
791
+ ]);
792
+ constructor(ctx, env) {
793
+ super(ctx, env);
794
+ this._b44Env = env;
795
+ globalThis.Base44 = Object.assign(
796
+ globalThis.Base44 ?? {},
797
+ {
798
+ // DOs have no request-scoped waitUntil (they stay alive while
799
+ // connections exist) — absorb so shared function code doesn't crash.
800
+ waitUntil: (p) => {
801
+ Promise.resolve(p).catch(() => {
802
+ });
803
+ },
804
+ secrets: {
805
+ // String(n) BEFORE the reserved check: a boxed String fails Set.has
806
+ // yet coerces back to the reserved key on the env lookup.
807
+ get: (n) => {
808
+ const key = String(n);
809
+ if (_Actor.RESERVED_SECRETS.has(key)) return void 0;
810
+ const v = env[key];
811
+ return typeof v === "string" ? v : void 0;
812
+ }
813
+ }
814
+ }
815
+ );
816
+ }
817
+ async onStart() {
818
+ try {
819
+ this.ctx.setWebSocketAutoResponse(
820
+ new WebSocketRequestResponsePair(
821
+ JSON.stringify({ type: "__ping" }),
822
+ JSON.stringify({ type: "__pong" })
823
+ )
824
+ );
825
+ } catch {
826
+ }
827
+ await this.handleStart();
828
+ await this.maintainTicker();
829
+ }
830
+ livenessMs(ws) {
831
+ let autoResp = 0;
832
+ try {
833
+ autoResp = this.ctx.getWebSocketAutoResponseTimestamp(ws)?.getTime() ?? 0;
834
+ } catch {
835
+ }
836
+ return Math.max(this.lastSeen.get(ws.id) ?? 0, autoResp);
837
+ }
838
+ static parseIdentity(value) {
839
+ if (!value || value.length > 512) return void 0;
840
+ try {
841
+ const identity = JSON.parse(value);
842
+ if (identity.type === "authenticated" && typeof identity.userId === "string" && identity.userId.length > 0 && identity.userId.length <= 128) {
843
+ return Object.freeze({ type: "authenticated", userId: identity.userId });
844
+ }
845
+ if (identity.type === "anonymous" && typeof identity.anonymousId === "string" && identity.anonymousId.length > 0 && identity.anonymousId.length <= 128) {
846
+ return Object.freeze({ type: "anonymous", anonymousId: identity.anonymousId });
847
+ }
848
+ } catch {
849
+ return void 0;
850
+ }
851
+ return void 0;
852
+ }
853
+ identityFor(ws) {
854
+ if (this.identities.has(ws.id)) return this.identities.get(ws.id);
855
+ let identity;
856
+ try {
857
+ identity = ws.deserializeAttachment()?.__base44Identity;
858
+ if (identity) Object.freeze(identity);
859
+ } catch {
860
+ }
861
+ this.identities.set(ws.id, identity);
862
+ return identity;
863
+ }
864
+ // partyserver's fetch() catch re-throws "Network connection lost." when the
865
+ // client already went away; swallow it so it isn't logged as an exception.
866
+ async fetch(request) {
867
+ if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") {
868
+ const pk = new URL(request.url).searchParams.get("_pk");
869
+ if (pk) {
870
+ const existing = [...super.getConnections()].find((c) => c.id === pk);
871
+ if (existing !== void 0 && Date.now() - this.livenessMs(existing) < _Actor.LIVE_MS) {
872
+ return new Response("Connection id already in use", { status: 409 });
873
+ }
874
+ if (existing !== void 0) {
875
+ try {
876
+ const att = existing.deserializeAttachment() ?? {};
877
+ existing.serializeAttachment({ ...att, __superseded: true });
878
+ } catch {
879
+ }
880
+ try {
881
+ existing.id = `${SUPERSEDED_PREFIX}${pk}`;
882
+ } catch {
883
+ }
884
+ try {
885
+ existing.close(4408, "Superseded by reconnect");
886
+ } catch {
887
+ }
888
+ }
889
+ }
890
+ }
891
+ try {
892
+ return await super.fetch(request);
893
+ } catch (err) {
894
+ if (err instanceof Error && err.message.includes("Network connection lost")) {
895
+ return new Response("Connection lost", { status: 503 });
896
+ }
897
+ throw err;
898
+ }
899
+ }
900
+ async onConnect(ws, ctx) {
901
+ const identity = _Actor.parseIdentity(
902
+ ctx.request.headers.get(_Actor.IDENTITY_HEADER)
903
+ );
904
+ this.identities.set(ws.id, identity);
905
+ if (identity) {
906
+ try {
907
+ const attachment = ws.deserializeAttachment() ?? {};
908
+ ws.serializeAttachment({ ...attachment, __base44Identity: identity });
909
+ } catch {
910
+ }
911
+ }
912
+ this.lastSeen.set(ws.id, Date.now());
913
+ await this.handleConnect(buildConn(ws, identity));
914
+ await this.maintainTicker();
915
+ }
916
+ async onMessage(ws, raw) {
917
+ this.lastSeen.set(ws.id, Date.now());
918
+ let msg;
919
+ try {
920
+ msg = JSON.parse(raw);
921
+ } catch {
922
+ return;
923
+ }
924
+ if (msg?.type === "__ping") {
925
+ ws.send(JSON.stringify({ type: "__pong" }));
926
+ await this.maintainTicker();
927
+ return;
928
+ }
929
+ await this.handleMessage(buildConn(ws, this.identityFor(ws)), msg);
930
+ await this.maintainTicker();
931
+ }
932
+ async onClose(ws) {
933
+ let superseded = ws.id.startsWith(SUPERSEDED_PREFIX);
934
+ if (!superseded) {
935
+ try {
936
+ superseded = !!ws.deserializeAttachment()?.__superseded;
937
+ } catch {
938
+ }
939
+ }
940
+ if (superseded) return;
941
+ const identity = this.identityFor(ws);
942
+ this.lastSeen.delete(ws.id);
943
+ this.identities.delete(ws.id);
944
+ await this.handleClose(buildConn(ws, identity));
945
+ await this.maintainTicker();
946
+ }
947
+ // WATCHDOG, not the metronome (see tick-loop.ts): resumes the loop after
948
+ // eviction / self-heals a dead one. Never runs handleTick itself.
949
+ async onAlarm() {
950
+ const gen = this.tickerGen;
951
+ const now = Date.now();
952
+ const due = await this.mutateSchedules((m) => {
953
+ const d = Object.keys(m).filter((k) => m[k] <= now);
954
+ for (const k of d) delete m[k];
955
+ return d;
956
+ });
957
+ for (const key of due) {
958
+ try {
959
+ await this.handleWake(key);
960
+ } catch (err) {
961
+ console.error("Actor.handleWake threw:", err);
962
+ }
963
+ }
964
+ const ms = this.loopMs ?? await this.ctx.storage.get("__loop_ms");
965
+ if (gen !== this.tickerGen) return;
966
+ if (ms) {
967
+ this.loopMs = ms;
968
+ if (typeof this.shouldTick === "function" && !([...super.getConnections()].length > 0 && this.shouldTick())) {
969
+ this.ticking = false;
970
+ await this.stopLoop();
971
+ return;
972
+ }
973
+ if (!this.loop.isRunning) this.loop.start(ms);
974
+ }
975
+ await this.rearmAlarm();
976
+ }
977
+ handleWake(_key) {
978
+ }
979
+ async schedule(key, at) {
980
+ const when = typeof at === "number" ? at : at.getTime();
981
+ await this.mutateSchedules((m) => {
982
+ m[key] = when;
983
+ });
984
+ await this.rearmAlarm();
985
+ }
986
+ async cancelSchedule(key) {
987
+ await this.mutateSchedules((m) => {
988
+ delete m[key];
989
+ });
990
+ await this.rearmAlarm();
991
+ }
992
+ // Concurrent read-modify-writes of the map would drop each other's keys
993
+ // (two schedule() calls both reading the prior map); chain them instead.
994
+ schedulesChain = Promise.resolve();
995
+ mutateSchedules(fn) {
996
+ const run = this.schedulesChain.then(async () => {
997
+ const m = await this.ctx.storage.get("__schedules") ?? {};
998
+ const out = fn(m);
999
+ await this.ctx.storage.put("__schedules", m);
1000
+ return out;
1001
+ });
1002
+ this.schedulesChain = run.catch(() => {
1003
+ });
1004
+ return run;
1005
+ }
1006
+ // The single DO alarm serves the ticker watchdog AND scheduled wakes: arm to
1007
+ // whichever is earliest; clear only when neither needs it. Chained on the
1008
+ // schedules queue so a rearm that read the map before a queued mutation can
1009
+ // never act after that mutation's own rearm and clobber it.
1010
+ rearmAlarm() {
1011
+ const run = this.schedulesChain.then(async () => {
1012
+ const wakes = Object.values(
1013
+ await this.ctx.storage.get("__schedules") ?? {}
1014
+ );
1015
+ const nextWake = wakes.length ? Math.min(...wakes) : null;
1016
+ const watchdog = this.loop.isRunning ? Date.now() + _Actor.WATCHDOG_MS : null;
1017
+ const next = nextWake === null ? watchdog : watchdog === null ? nextWake : Math.min(nextWake, watchdog);
1018
+ if (next === null) await this.ctx.storage.deleteAlarm();
1019
+ else await this.ctx.storage.setAlarm(next);
1020
+ });
1021
+ this.schedulesChain = run.catch(() => {
1022
+ });
1023
+ return run;
1024
+ }
1025
+ // Optional wake hook: once per instance start, before any connection.
1026
+ handleStart() {
1027
+ }
1028
+ // ─── Managed ticker (opt-in) ──────────────────────────────────────────────
1029
+ // Override shouldTick() and the platform runs handleTick() on a timer while it
1030
+ // returns true, stopping (the DO can idle out) when false. shouldTick()
1031
+ // must be cheap and pure: it runs after every lifecycle event and tick.
1032
+ tickIntervalMs = 100;
1033
+ ticking = false;
1034
+ loopMs = null;
1035
+ // in-memory mirror of __loop_ms
1036
+ loop = new TickLoop(() => this.runOneTick());
1037
+ static WATCHDOG_MS = 3e4;
1038
+ // One tick: a throw skips the tick, never kills the loop.
1039
+ async runOneTick() {
1040
+ const gen = this.tickerGen;
1041
+ try {
1042
+ await this.handleTick();
1043
+ } catch (err) {
1044
+ console.error("Actor.handleTick threw:", err);
1045
+ }
1046
+ if (gen !== this.tickerGen) return false;
1047
+ if (typeof this.shouldTick === "function") {
1048
+ const keep = [...super.getConnections()].length > 0 && this.shouldTick();
1049
+ this.ticking = keep;
1050
+ if (!keep) {
1051
+ await this.stopLoop();
1052
+ return false;
1053
+ }
1054
+ return true;
1055
+ }
1056
+ return this.loopMs != null;
1057
+ }
1058
+ broadcast(data) {
1059
+ super.broadcast(JSON.stringify(data));
1060
+ }
1061
+ getConnections() {
1062
+ return [...super.getConnections()].map((ws) => buildConn(ws, this.identityFor(ws)));
1063
+ }
1064
+ // The newest start/stop owns the persisted state: a stale transition
1065
+ // resuming after its await must not erase a fresh __loop_ms/watchdog.
1066
+ tickerGen = 0;
1067
+ async startLoop(ms) {
1068
+ const gen = ++this.tickerGen;
1069
+ this.loopMs = ms;
1070
+ await this.ctx.storage.put("__loop_ms", ms);
1071
+ if (gen !== this.tickerGen) return;
1072
+ this.loop.start(ms);
1073
+ await this.rearmAlarm();
1074
+ }
1075
+ async stopLoop() {
1076
+ const gen = ++this.tickerGen;
1077
+ this.loop.stop();
1078
+ this.loopMs = null;
1079
+ this.ticking = false;
1080
+ await this.ctx.storage.delete("__loop_ms");
1081
+ if (gen !== this.tickerGen) return;
1082
+ await this.rearmAlarm();
1083
+ }
1084
+ // Restart a dead in-memory loop (eviction / isolate reload).
1085
+ async ensureLoop() {
1086
+ if (this.loopMs == null) {
1087
+ const gen = this.tickerGen;
1088
+ const ms = await this.ctx.storage.get("__loop_ms");
1089
+ if (gen !== this.tickerGen) return;
1090
+ if (!ms) return;
1091
+ this.loopMs = ms;
1092
+ }
1093
+ if (!this.loop.isRunning) this.loop.start(this.loopMs);
1094
+ }
1095
+ // Managed-ticker reconcile: run after every lifecycle event. The ticker is
1096
+ // opt-in via shouldTick() — a handler that doesn't define it never ticks.
1097
+ // When opted in, start/stop the loop to match, self-healing a dead loop
1098
+ // while still wanted.
1099
+ async maintainTicker() {
1100
+ if (typeof this.shouldTick !== "function") return;
1101
+ const want = [...super.getConnections()].length > 0 && this.shouldTick();
1102
+ if (want !== this.ticking) {
1103
+ this.ticking = want;
1104
+ if (want) await this.startLoop(this.tickIntervalMs);
1105
+ else await this.stopLoop();
1106
+ } else if (want) {
1107
+ await this.ensureLoop();
1108
+ }
1109
+ }
1110
+ get instanceId() {
1111
+ return this.name;
1112
+ }
1113
+ get storage() {
1114
+ const store = this.ctx.storage;
1115
+ return {
1116
+ get: (key) => store.get(key),
1117
+ put: (key, value) => store.put(key, value),
1118
+ delete: (key) => store.delete(key),
1119
+ deleteAll: async () => {
1120
+ await this.stopLoop();
1121
+ await store.deleteAll();
1122
+ }
1123
+ };
1124
+ }
1125
+ };
1126
+ export {
1127
+ Actor,
1128
+ routePartykitRequest
1129
+ };