rivetkit 2.0.2 → 2.0.3
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/schemas/actor-persist/v1.ts +228 -0
- package/dist/schemas/client-protocol/v1.ts +429 -0
- package/dist/schemas/file-system-driver/v1.ts +102 -0
- package/dist/tsup/actor/errors.cjs +69 -0
- package/dist/tsup/actor/errors.cjs.map +1 -0
- package/dist/tsup/actor/errors.d.cts +143 -0
- package/dist/tsup/actor/errors.d.ts +143 -0
- package/dist/tsup/actor/errors.js +69 -0
- package/dist/tsup/actor/errors.js.map +1 -0
- package/dist/tsup/chunk-2CRLFV6Z.cjs +202 -0
- package/dist/tsup/chunk-2CRLFV6Z.cjs.map +1 -0
- package/dist/tsup/chunk-3H7O2A7I.js +525 -0
- package/dist/tsup/chunk-3H7O2A7I.js.map +1 -0
- package/dist/tsup/chunk-42I3OZ3Q.js +15 -0
- package/dist/tsup/chunk-42I3OZ3Q.js.map +1 -0
- package/dist/tsup/chunk-4NSUQZ2H.js +1790 -0
- package/dist/tsup/chunk-4NSUQZ2H.js.map +1 -0
- package/dist/tsup/chunk-6PDXBYI5.js +132 -0
- package/dist/tsup/chunk-6PDXBYI5.js.map +1 -0
- package/dist/tsup/chunk-6WKQDDUD.cjs +1790 -0
- package/dist/tsup/chunk-6WKQDDUD.cjs.map +1 -0
- package/dist/tsup/chunk-CTBOSFUH.cjs +116 -0
- package/dist/tsup/chunk-CTBOSFUH.cjs.map +1 -0
- package/dist/tsup/chunk-EGVZZFE2.js +2857 -0
- package/dist/tsup/chunk-EGVZZFE2.js.map +1 -0
- package/dist/tsup/chunk-FCCPJNMA.cjs +132 -0
- package/dist/tsup/chunk-FCCPJNMA.cjs.map +1 -0
- package/dist/tsup/chunk-FLMTTN27.js +244 -0
- package/dist/tsup/chunk-FLMTTN27.js.map +1 -0
- package/dist/tsup/chunk-GIR3AFFI.cjs +315 -0
- package/dist/tsup/chunk-GIR3AFFI.cjs.map +1 -0
- package/dist/tsup/chunk-INGJP237.js +315 -0
- package/dist/tsup/chunk-INGJP237.js.map +1 -0
- package/dist/tsup/chunk-KJCJLKRM.js +116 -0
- package/dist/tsup/chunk-KJCJLKRM.js.map +1 -0
- package/dist/tsup/chunk-KUPQZYUQ.cjs +15 -0
- package/dist/tsup/chunk-KUPQZYUQ.cjs.map +1 -0
- package/dist/tsup/chunk-O2MBYIXO.cjs +2857 -0
- package/dist/tsup/chunk-O2MBYIXO.cjs.map +1 -0
- package/dist/tsup/chunk-OGAPU3UG.cjs +525 -0
- package/dist/tsup/chunk-OGAPU3UG.cjs.map +1 -0
- package/dist/tsup/chunk-OV6AYD4S.js +4406 -0
- package/dist/tsup/chunk-OV6AYD4S.js.map +1 -0
- package/dist/tsup/chunk-PO4VLDWA.js +47 -0
- package/dist/tsup/chunk-PO4VLDWA.js.map +1 -0
- package/dist/tsup/chunk-R2OPSKIV.cjs +244 -0
- package/dist/tsup/chunk-R2OPSKIV.cjs.map +1 -0
- package/dist/tsup/chunk-TZJKSBUQ.cjs +47 -0
- package/dist/tsup/chunk-TZJKSBUQ.cjs.map +1 -0
- package/dist/tsup/chunk-UBUC5C3G.cjs +189 -0
- package/dist/tsup/chunk-UBUC5C3G.cjs.map +1 -0
- package/dist/tsup/chunk-UIM22YJL.cjs +4406 -0
- package/dist/tsup/chunk-UIM22YJL.cjs.map +1 -0
- package/dist/tsup/chunk-URVFQMYI.cjs +230 -0
- package/dist/tsup/chunk-URVFQMYI.cjs.map +1 -0
- package/dist/tsup/chunk-UVUPOS46.js +230 -0
- package/dist/tsup/chunk-UVUPOS46.js.map +1 -0
- package/dist/tsup/chunk-VRRHBNJC.js +189 -0
- package/dist/tsup/chunk-VRRHBNJC.js.map +1 -0
- package/dist/tsup/chunk-XFSS33EQ.js +202 -0
- package/dist/tsup/chunk-XFSS33EQ.js.map +1 -0
- package/dist/tsup/client/mod.cjs +32 -0
- package/dist/tsup/client/mod.cjs.map +1 -0
- package/dist/tsup/client/mod.d.cts +26 -0
- package/dist/tsup/client/mod.d.ts +26 -0
- package/dist/tsup/client/mod.js +32 -0
- package/dist/tsup/client/mod.js.map +1 -0
- package/dist/tsup/common/log.cjs +13 -0
- package/dist/tsup/common/log.cjs.map +1 -0
- package/dist/tsup/common/log.d.cts +20 -0
- package/dist/tsup/common/log.d.ts +20 -0
- package/dist/tsup/common/log.js +13 -0
- package/dist/tsup/common/log.js.map +1 -0
- package/dist/tsup/common/websocket.cjs +10 -0
- package/dist/tsup/common/websocket.cjs.map +1 -0
- package/dist/tsup/common/websocket.d.cts +3 -0
- package/dist/tsup/common/websocket.d.ts +3 -0
- package/dist/tsup/common/websocket.js +10 -0
- package/dist/tsup/common/websocket.js.map +1 -0
- package/dist/tsup/common-CpqORuCq.d.cts +218 -0
- package/dist/tsup/common-CpqORuCq.d.ts +218 -0
- package/dist/tsup/connection-BR_Ve4ku.d.cts +2117 -0
- package/dist/tsup/connection-BwUMoe6n.d.ts +2117 -0
- package/dist/tsup/driver-helpers/mod.cjs +33 -0
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -0
- package/dist/tsup/driver-helpers/mod.d.cts +18 -0
- package/dist/tsup/driver-helpers/mod.d.ts +18 -0
- package/dist/tsup/driver-helpers/mod.js +33 -0
- package/dist/tsup/driver-helpers/mod.js.map +1 -0
- package/dist/tsup/driver-test-suite/mod.cjs +4619 -0
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -0
- package/dist/tsup/driver-test-suite/mod.d.cts +57 -0
- package/dist/tsup/driver-test-suite/mod.d.ts +57 -0
- package/dist/tsup/driver-test-suite/mod.js +4619 -0
- package/dist/tsup/driver-test-suite/mod.js.map +1 -0
- package/dist/tsup/inspector/mod.cjs +53 -0
- package/dist/tsup/inspector/mod.cjs.map +1 -0
- package/dist/tsup/inspector/mod.d.cts +408 -0
- package/dist/tsup/inspector/mod.d.ts +408 -0
- package/dist/tsup/inspector/mod.js +53 -0
- package/dist/tsup/inspector/mod.js.map +1 -0
- package/dist/tsup/mod.cjs +73 -0
- package/dist/tsup/mod.cjs.map +1 -0
- package/dist/tsup/mod.d.cts +100 -0
- package/dist/tsup/mod.d.ts +100 -0
- package/dist/tsup/mod.js +73 -0
- package/dist/tsup/mod.js.map +1 -0
- package/dist/tsup/router-endpoints-AYkXG8Tl.d.cts +66 -0
- package/dist/tsup/router-endpoints-DAbqVFx2.d.ts +66 -0
- package/dist/tsup/test/mod.cjs +21 -0
- package/dist/tsup/test/mod.cjs.map +1 -0
- package/dist/tsup/test/mod.d.cts +27 -0
- package/dist/tsup/test/mod.d.ts +27 -0
- package/dist/tsup/test/mod.js +21 -0
- package/dist/tsup/test/mod.js.map +1 -0
- package/dist/tsup/utils-CT0cv4jd.d.cts +17 -0
- package/dist/tsup/utils-CT0cv4jd.d.ts +17 -0
- package/dist/tsup/utils.cjs +26 -0
- package/dist/tsup/utils.cjs.map +1 -0
- package/dist/tsup/utils.d.cts +36 -0
- package/dist/tsup/utils.d.ts +36 -0
- package/dist/tsup/utils.js +26 -0
- package/dist/tsup/utils.js.map +1 -0
- package/package.json +208 -5
- package/src/actor/action.ts +182 -0
- package/src/actor/config.ts +765 -0
- package/src/actor/connection.ts +260 -0
- package/src/actor/context.ts +171 -0
- package/src/actor/database.ts +23 -0
- package/src/actor/definition.ts +86 -0
- package/src/actor/driver.ts +84 -0
- package/src/actor/errors.ts +360 -0
- package/src/actor/generic-conn-driver.ts +234 -0
- package/src/actor/instance.ts +1800 -0
- package/src/actor/log.ts +15 -0
- package/src/actor/mod.ts +113 -0
- package/src/actor/persisted.ts +42 -0
- package/src/actor/protocol/old.ts +281 -0
- package/src/actor/protocol/serde.ts +131 -0
- package/src/actor/router-endpoints.ts +685 -0
- package/src/actor/router.ts +263 -0
- package/src/actor/schedule.ts +17 -0
- package/src/actor/unstable-react.ts +110 -0
- package/src/actor/utils.ts +98 -0
- package/src/client/actor-common.ts +30 -0
- package/src/client/actor-conn.ts +804 -0
- package/src/client/actor-handle.ts +208 -0
- package/src/client/client.ts +623 -0
- package/src/client/errors.ts +41 -0
- package/src/client/http-client-driver.ts +326 -0
- package/src/client/log.ts +7 -0
- package/src/client/mod.ts +56 -0
- package/src/client/raw-utils.ts +92 -0
- package/src/client/test.ts +44 -0
- package/src/client/utils.ts +150 -0
- package/src/common/eventsource-interface.ts +47 -0
- package/src/common/eventsource.ts +80 -0
- package/src/common/fake-event-source.ts +266 -0
- package/src/common/inline-websocket-adapter2.ts +445 -0
- package/src/common/log-levels.ts +27 -0
- package/src/common/log.ts +139 -0
- package/src/common/logfmt.ts +228 -0
- package/src/common/network.ts +2 -0
- package/src/common/router.ts +87 -0
- package/src/common/utils.ts +322 -0
- package/src/common/versioned-data.ts +95 -0
- package/src/common/websocket-interface.ts +49 -0
- package/src/common/websocket.ts +43 -0
- package/src/driver-helpers/mod.ts +22 -0
- package/src/driver-helpers/utils.ts +17 -0
- package/src/driver-test-suite/log.ts +7 -0
- package/src/driver-test-suite/mod.ts +213 -0
- package/src/driver-test-suite/test-inline-client-driver.ts +402 -0
- package/src/driver-test-suite/tests/action-features.ts +136 -0
- package/src/driver-test-suite/tests/actor-auth.ts +591 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +249 -0
- package/src/driver-test-suite/tests/actor-conn.ts +349 -0
- package/src/driver-test-suite/tests/actor-driver.ts +25 -0
- package/src/driver-test-suite/tests/actor-error-handling.ts +158 -0
- package/src/driver-test-suite/tests/actor-handle.ts +259 -0
- package/src/driver-test-suite/tests/actor-inline-client.ts +152 -0
- package/src/driver-test-suite/tests/actor-inspector.ts +570 -0
- package/src/driver-test-suite/tests/actor-metadata.ts +116 -0
- package/src/driver-test-suite/tests/actor-onstatechange.ts +95 -0
- package/src/driver-test-suite/tests/actor-schedule.ts +108 -0
- package/src/driver-test-suite/tests/actor-sleep.ts +413 -0
- package/src/driver-test-suite/tests/actor-state.ts +54 -0
- package/src/driver-test-suite/tests/actor-vars.ts +93 -0
- package/src/driver-test-suite/tests/manager-driver.ts +365 -0
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +226 -0
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +414 -0
- package/src/driver-test-suite/tests/raw-http.ts +347 -0
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +392 -0
- package/src/driver-test-suite/tests/raw-websocket.ts +484 -0
- package/src/driver-test-suite/tests/request-access.ts +244 -0
- package/src/driver-test-suite/utils.ts +68 -0
- package/src/drivers/default.ts +31 -0
- package/src/drivers/engine/actor-driver.ts +360 -0
- package/src/drivers/engine/api-endpoints.ts +128 -0
- package/src/drivers/engine/api-utils.ts +70 -0
- package/src/drivers/engine/config.ts +39 -0
- package/src/drivers/engine/keys.test.ts +266 -0
- package/src/drivers/engine/keys.ts +89 -0
- package/src/drivers/engine/kv.ts +3 -0
- package/src/drivers/engine/log.ts +7 -0
- package/src/drivers/engine/manager-driver.ts +391 -0
- package/src/drivers/engine/mod.ts +36 -0
- package/src/drivers/engine/ws-proxy.ts +170 -0
- package/src/drivers/file-system/actor.ts +91 -0
- package/src/drivers/file-system/global-state.ts +673 -0
- package/src/drivers/file-system/log.ts +7 -0
- package/src/drivers/file-system/manager.ts +306 -0
- package/src/drivers/file-system/mod.ts +48 -0
- package/src/drivers/file-system/utils.ts +109 -0
- package/src/globals.d.ts +6 -0
- package/src/inline-client-driver/log.ts +7 -0
- package/src/inline-client-driver/mod.ts +385 -0
- package/src/inspector/actor.ts +298 -0
- package/src/inspector/config.ts +83 -0
- package/src/inspector/log.ts +5 -0
- package/src/inspector/manager.ts +86 -0
- package/src/inspector/mod.ts +2 -0
- package/src/inspector/protocol/actor.ts +10 -0
- package/src/inspector/protocol/common.ts +196 -0
- package/src/inspector/protocol/manager.ts +10 -0
- package/src/inspector/protocol/mod.ts +2 -0
- package/src/inspector/utils.ts +76 -0
- package/src/manager/auth.ts +121 -0
- package/src/manager/driver.ts +80 -0
- package/src/manager/hono-websocket-adapter.ts +333 -0
- package/src/manager/log.ts +7 -0
- package/src/manager/mod.ts +2 -0
- package/src/manager/protocol/mod.ts +24 -0
- package/src/manager/protocol/query.ts +89 -0
- package/src/manager/router.ts +1792 -0
- package/src/mod.ts +20 -0
- package/src/registry/config.ts +32 -0
- package/src/registry/log.ts +7 -0
- package/src/registry/mod.ts +124 -0
- package/src/registry/run-config.ts +54 -0
- package/src/registry/serve.ts +53 -0
- package/src/schemas/actor-persist/mod.ts +1 -0
- package/src/schemas/actor-persist/versioned.ts +25 -0
- package/src/schemas/client-protocol/mod.ts +1 -0
- package/src/schemas/client-protocol/versioned.ts +63 -0
- package/src/schemas/file-system-driver/mod.ts +1 -0
- package/src/schemas/file-system-driver/versioned.ts +28 -0
- package/src/serde.ts +84 -0
- package/src/test/config.ts +16 -0
- package/src/test/log.ts +7 -0
- package/src/test/mod.ts +153 -0
- package/src/utils.ts +172 -0
- package/README.md +0 -13
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import * as cbor from "cbor-x";
|
|
2
|
+
import type { Context as HonoContext } from "hono";
|
|
3
|
+
import invariant from "invariant";
|
|
4
|
+
import onChange from "on-change";
|
|
5
|
+
import type { WebSocket } from "ws";
|
|
6
|
+
import * as errors from "@/actor/errors";
|
|
7
|
+
import type { Encoding } from "@/actor/protocol/serde";
|
|
8
|
+
import {
|
|
9
|
+
PATH_CONNECT_WEBSOCKET,
|
|
10
|
+
PATH_RAW_WEBSOCKET_PREFIX,
|
|
11
|
+
} from "@/actor/router";
|
|
12
|
+
import {
|
|
13
|
+
HEADER_CONN_ID,
|
|
14
|
+
HEADER_CONN_PARAMS,
|
|
15
|
+
HEADER_CONN_TOKEN,
|
|
16
|
+
HEADER_ENCODING,
|
|
17
|
+
HEADER_EXPOSE_INTERNAL_ERROR,
|
|
18
|
+
} from "@/actor/router-endpoints";
|
|
19
|
+
import { assertUnreachable } from "@/actor/utils";
|
|
20
|
+
import type { ClientDriver } from "@/client/client";
|
|
21
|
+
import { ActorError as ClientActorError } from "@/client/errors";
|
|
22
|
+
import { sendHttpRequest } from "@/client/utils";
|
|
23
|
+
import { importEventSource } from "@/common/eventsource";
|
|
24
|
+
import type { UniversalEventSource } from "@/common/eventsource-interface";
|
|
25
|
+
import { deconstructError } from "@/common/utils";
|
|
26
|
+
import type { ManagerDriver } from "@/manager/driver";
|
|
27
|
+
import type { ActorQuery } from "@/manager/protocol/query";
|
|
28
|
+
import type { RunConfig } from "@/mod";
|
|
29
|
+
import type * as protocol from "@/schemas/client-protocol/mod";
|
|
30
|
+
import {
|
|
31
|
+
HTTP_ACTION_REQUEST_VERSIONED,
|
|
32
|
+
HTTP_ACTION_RESPONSE_VERSIONED,
|
|
33
|
+
TO_CLIENT_VERSIONED,
|
|
34
|
+
TO_SERVER_VERSIONED,
|
|
35
|
+
} from "@/schemas/client-protocol/versioned";
|
|
36
|
+
import { bufferToArrayBuffer, httpUserAgent } from "@/utils";
|
|
37
|
+
import { logger } from "./log";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Client driver that calls the manager driver inline.
|
|
41
|
+
*
|
|
42
|
+
* This is only applicable to standalone & coordinated topologies.
|
|
43
|
+
*
|
|
44
|
+
* This driver can access private resources.
|
|
45
|
+
*
|
|
46
|
+
* This driver serves a double purpose as:
|
|
47
|
+
* - Providing the client for the internal requests
|
|
48
|
+
* - Provide the driver for the manager HTTP router (see manager/router.ts)
|
|
49
|
+
*/
|
|
50
|
+
export function createInlineClientDriver(
|
|
51
|
+
managerDriver: ManagerDriver,
|
|
52
|
+
): ClientDriver {
|
|
53
|
+
const driver: ClientDriver = {
|
|
54
|
+
action: async <Args extends Array<unknown> = unknown[], Response = unknown>(
|
|
55
|
+
c: HonoContext | undefined,
|
|
56
|
+
actorQuery: ActorQuery,
|
|
57
|
+
encoding: Encoding,
|
|
58
|
+
params: unknown,
|
|
59
|
+
actionName: string,
|
|
60
|
+
args: Args,
|
|
61
|
+
opts: { signal?: AbortSignal },
|
|
62
|
+
): Promise<Response> => {
|
|
63
|
+
try {
|
|
64
|
+
// Get the actor ID
|
|
65
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
66
|
+
logger().debug("found actor for action", { actorId });
|
|
67
|
+
invariant(actorId, "Missing actor ID");
|
|
68
|
+
|
|
69
|
+
// Invoke the action
|
|
70
|
+
logger().debug("handling action", { actionName, encoding });
|
|
71
|
+
const responseData = await sendHttpRequest<
|
|
72
|
+
protocol.HttpActionRequest,
|
|
73
|
+
protocol.HttpActionResponse
|
|
74
|
+
>({
|
|
75
|
+
url: `http://actor/action/${encodeURIComponent(actionName)}`,
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: {
|
|
78
|
+
[HEADER_ENCODING]: encoding,
|
|
79
|
+
...(params !== undefined
|
|
80
|
+
? { [HEADER_CONN_PARAMS]: JSON.stringify(params) }
|
|
81
|
+
: {}),
|
|
82
|
+
[HEADER_EXPOSE_INTERNAL_ERROR]: "true",
|
|
83
|
+
},
|
|
84
|
+
body: {
|
|
85
|
+
args: bufferToArrayBuffer(cbor.encode(args)),
|
|
86
|
+
} satisfies protocol.HttpActionRequest,
|
|
87
|
+
encoding: encoding,
|
|
88
|
+
customFetch: managerDriver.sendRequest.bind(managerDriver, actorId),
|
|
89
|
+
signal: opts?.signal,
|
|
90
|
+
requestVersionedDataHandler: HTTP_ACTION_REQUEST_VERSIONED,
|
|
91
|
+
responseVersionedDataHandler: HTTP_ACTION_RESPONSE_VERSIONED,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return cbor.decode(new Uint8Array(responseData.output));
|
|
95
|
+
} catch (err) {
|
|
96
|
+
// Standardize to ClientActorError instead of the native backend error
|
|
97
|
+
const { code, message, metadata } = deconstructError(
|
|
98
|
+
err,
|
|
99
|
+
logger(),
|
|
100
|
+
{},
|
|
101
|
+
true,
|
|
102
|
+
);
|
|
103
|
+
const x = new ClientActorError(code, message, metadata);
|
|
104
|
+
throw new ClientActorError(code, message, metadata);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
resolveActorId: async (
|
|
109
|
+
c: HonoContext | undefined,
|
|
110
|
+
actorQuery: ActorQuery,
|
|
111
|
+
_encodingKind: Encoding,
|
|
112
|
+
): Promise<string> => {
|
|
113
|
+
// Get the actor ID
|
|
114
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
115
|
+
logger().debug("resolved actor", { actorId });
|
|
116
|
+
invariant(actorId, "missing actor ID");
|
|
117
|
+
|
|
118
|
+
return actorId;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
connectWebSocket: async (
|
|
122
|
+
c: HonoContext | undefined,
|
|
123
|
+
actorQuery: ActorQuery,
|
|
124
|
+
encodingKind: Encoding,
|
|
125
|
+
params?: unknown,
|
|
126
|
+
): Promise<WebSocket> => {
|
|
127
|
+
// Get the actor ID
|
|
128
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
129
|
+
logger().debug("found actor for action", { actorId });
|
|
130
|
+
invariant(actorId, "Missing actor ID");
|
|
131
|
+
|
|
132
|
+
// Invoke the action
|
|
133
|
+
logger().debug("opening websocket", { actorId, encoding: encodingKind });
|
|
134
|
+
|
|
135
|
+
// Open WebSocket
|
|
136
|
+
const ws = await managerDriver.openWebSocket(
|
|
137
|
+
PATH_CONNECT_WEBSOCKET,
|
|
138
|
+
actorId,
|
|
139
|
+
encodingKind,
|
|
140
|
+
params,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Node & browser WebSocket types are incompatible
|
|
144
|
+
return ws as any;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
connectSse: async (
|
|
148
|
+
c: HonoContext | undefined,
|
|
149
|
+
actorQuery: ActorQuery,
|
|
150
|
+
encodingKind: Encoding,
|
|
151
|
+
params: unknown,
|
|
152
|
+
): Promise<UniversalEventSource> => {
|
|
153
|
+
// Get the actor ID
|
|
154
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
155
|
+
logger().debug("found actor for sse connection", { actorId });
|
|
156
|
+
invariant(actorId, "Missing actor ID");
|
|
157
|
+
|
|
158
|
+
logger().debug("opening sse connection", {
|
|
159
|
+
actorId,
|
|
160
|
+
encoding: encodingKind,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const EventSourceClass = await importEventSource();
|
|
164
|
+
|
|
165
|
+
const eventSource = new EventSourceClass("http://actor/connect/sse", {
|
|
166
|
+
fetch: (input, init) => {
|
|
167
|
+
return fetch(input, {
|
|
168
|
+
...init,
|
|
169
|
+
headers: {
|
|
170
|
+
...init?.headers,
|
|
171
|
+
"User-Agent": httpUserAgent(),
|
|
172
|
+
[HEADER_ENCODING]: encodingKind,
|
|
173
|
+
...(params !== undefined
|
|
174
|
+
? { [HEADER_CONN_PARAMS]: JSON.stringify(params) }
|
|
175
|
+
: {}),
|
|
176
|
+
[HEADER_EXPOSE_INTERNAL_ERROR]: "true",
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
}) as UniversalEventSource;
|
|
181
|
+
|
|
182
|
+
return eventSource;
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
sendHttpMessage: async (
|
|
186
|
+
c: HonoContext | undefined,
|
|
187
|
+
actorId: string,
|
|
188
|
+
encoding: Encoding,
|
|
189
|
+
connectionId: string,
|
|
190
|
+
connectionToken: string,
|
|
191
|
+
message: protocol.ToServer,
|
|
192
|
+
): Promise<void> => {
|
|
193
|
+
logger().debug("sending http message", { actorId, connectionId });
|
|
194
|
+
|
|
195
|
+
// Send an HTTP request to the connections endpoint
|
|
196
|
+
await sendHttpRequest({
|
|
197
|
+
url: "http://actor/connections/message",
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
[HEADER_ENCODING]: encoding,
|
|
201
|
+
[HEADER_CONN_ID]: connectionId,
|
|
202
|
+
[HEADER_CONN_TOKEN]: connectionToken,
|
|
203
|
+
[HEADER_EXPOSE_INTERNAL_ERROR]: "true",
|
|
204
|
+
},
|
|
205
|
+
body: message,
|
|
206
|
+
encoding,
|
|
207
|
+
skipParseResponse: true,
|
|
208
|
+
customFetch: managerDriver.sendRequest.bind(managerDriver, actorId),
|
|
209
|
+
requestVersionedDataHandler: TO_SERVER_VERSIONED,
|
|
210
|
+
responseVersionedDataHandler: TO_CLIENT_VERSIONED,
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
rawHttpRequest: async (
|
|
215
|
+
c: HonoContext | undefined,
|
|
216
|
+
actorQuery: ActorQuery,
|
|
217
|
+
encoding: Encoding,
|
|
218
|
+
params: unknown,
|
|
219
|
+
path: string,
|
|
220
|
+
init: RequestInit,
|
|
221
|
+
): Promise<Response> => {
|
|
222
|
+
try {
|
|
223
|
+
// Get the actor ID
|
|
224
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
225
|
+
logger().debug("found actor for raw http", { actorId });
|
|
226
|
+
invariant(actorId, "Missing actor ID");
|
|
227
|
+
|
|
228
|
+
// Build the URL with normalized path
|
|
229
|
+
const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
230
|
+
const url = new URL(`http://actor/raw/http/${normalizedPath}`);
|
|
231
|
+
|
|
232
|
+
// Forward conn params if provided
|
|
233
|
+
const proxyRequestHeaders = new Headers(init.headers);
|
|
234
|
+
if (params) {
|
|
235
|
+
proxyRequestHeaders.set(HEADER_CONN_PARAMS, JSON.stringify(params));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Forward the request to the actor
|
|
239
|
+
const proxyRequest = new Request(url, {
|
|
240
|
+
...init,
|
|
241
|
+
headers: proxyRequestHeaders,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return await managerDriver.sendRequest(actorId, proxyRequest);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
// Standardize to ClientActorError instead of the native backend error
|
|
247
|
+
const { code, message, metadata } = deconstructError(
|
|
248
|
+
err,
|
|
249
|
+
logger(),
|
|
250
|
+
{},
|
|
251
|
+
true,
|
|
252
|
+
);
|
|
253
|
+
throw new ClientActorError(code, message, metadata);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
rawWebSocket: async (
|
|
258
|
+
c: HonoContext | undefined,
|
|
259
|
+
actorQuery: ActorQuery,
|
|
260
|
+
encoding: Encoding,
|
|
261
|
+
params: unknown,
|
|
262
|
+
path: string,
|
|
263
|
+
protocols: string | string[] | undefined,
|
|
264
|
+
): Promise<WebSocket> => {
|
|
265
|
+
// Get the actor ID
|
|
266
|
+
const { actorId } = await queryActor(c, actorQuery, managerDriver);
|
|
267
|
+
logger().debug("found actor for action", { actorId });
|
|
268
|
+
invariant(actorId, "Missing actor ID");
|
|
269
|
+
|
|
270
|
+
// Normalize path to match raw HTTP behavior
|
|
271
|
+
const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
272
|
+
logger().debug("opening websocket", {
|
|
273
|
+
actorId,
|
|
274
|
+
encoding,
|
|
275
|
+
path: normalizedPath,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Open WebSocket
|
|
279
|
+
const ws = await managerDriver.openWebSocket(
|
|
280
|
+
`${PATH_RAW_WEBSOCKET_PREFIX}${normalizedPath}`,
|
|
281
|
+
actorId,
|
|
282
|
+
encoding,
|
|
283
|
+
params,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Node & browser WebSocket types are incompatible
|
|
287
|
+
return ws as any;
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
return driver;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Query the manager driver to get or create a actor based on the provided query
|
|
296
|
+
*/
|
|
297
|
+
export async function queryActor(
|
|
298
|
+
c: HonoContext | undefined,
|
|
299
|
+
query: ActorQuery,
|
|
300
|
+
driver: ManagerDriver,
|
|
301
|
+
): Promise<{ actorId: string }> {
|
|
302
|
+
logger().debug("querying actor", { query });
|
|
303
|
+
let actorOutput: { actorId: string };
|
|
304
|
+
if ("getForId" in query) {
|
|
305
|
+
const output = await driver.getForId({
|
|
306
|
+
c,
|
|
307
|
+
name: query.getForId.name,
|
|
308
|
+
actorId: query.getForId.actorId,
|
|
309
|
+
});
|
|
310
|
+
if (!output) throw new errors.ActorNotFound(query.getForId.actorId);
|
|
311
|
+
actorOutput = output;
|
|
312
|
+
} else if ("getForKey" in query) {
|
|
313
|
+
const existingActor = await driver.getWithKey({
|
|
314
|
+
c,
|
|
315
|
+
name: query.getForKey.name,
|
|
316
|
+
key: query.getForKey.key,
|
|
317
|
+
});
|
|
318
|
+
if (!existingActor) {
|
|
319
|
+
throw new errors.ActorNotFound(
|
|
320
|
+
`${query.getForKey.name}:${JSON.stringify(query.getForKey.key)}`,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
actorOutput = existingActor;
|
|
324
|
+
} else if ("getOrCreateForKey" in query) {
|
|
325
|
+
const getOrCreateOutput = await driver.getOrCreateWithKey({
|
|
326
|
+
c,
|
|
327
|
+
name: query.getOrCreateForKey.name,
|
|
328
|
+
key: query.getOrCreateForKey.key,
|
|
329
|
+
input: query.getOrCreateForKey.input,
|
|
330
|
+
region: query.getOrCreateForKey.region,
|
|
331
|
+
});
|
|
332
|
+
actorOutput = {
|
|
333
|
+
actorId: getOrCreateOutput.actorId,
|
|
334
|
+
};
|
|
335
|
+
} else if ("create" in query) {
|
|
336
|
+
const createOutput = await driver.createActor({
|
|
337
|
+
c,
|
|
338
|
+
name: query.create.name,
|
|
339
|
+
key: query.create.key,
|
|
340
|
+
input: query.create.input,
|
|
341
|
+
region: query.create.region,
|
|
342
|
+
});
|
|
343
|
+
actorOutput = {
|
|
344
|
+
actorId: createOutput.actorId,
|
|
345
|
+
};
|
|
346
|
+
} else {
|
|
347
|
+
throw new errors.InvalidRequest("Invalid query format");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
logger().debug("actor query result", {
|
|
351
|
+
actorId: actorOutput.actorId,
|
|
352
|
+
});
|
|
353
|
+
return { actorId: actorOutput.actorId };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Removes the on-change library's proxy recursively from a value so we can clone it with `structuredClone`.
|
|
358
|
+
*/
|
|
359
|
+
function unproxyRecursive<T>(objProxied: T): T {
|
|
360
|
+
const obj = onChange.target<any>(objProxied);
|
|
361
|
+
|
|
362
|
+
// Short circuit if this object was proxied
|
|
363
|
+
//
|
|
364
|
+
// If the reference is different, then this value was proxied and no
|
|
365
|
+
// nested values are proxied
|
|
366
|
+
if (obj !== objProxied) return obj;
|
|
367
|
+
|
|
368
|
+
// Handle null/undefined
|
|
369
|
+
if (!obj || typeof obj !== "object") {
|
|
370
|
+
return obj;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Handle arrays
|
|
374
|
+
if (Array.isArray(obj)) {
|
|
375
|
+
return obj.map((x) => unproxyRecursive<any>(x)) as T;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Handle objects
|
|
379
|
+
const result: any = {};
|
|
380
|
+
for (const key in obj) {
|
|
381
|
+
result[key] = unproxyRecursive<any>(obj[key]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { sValidator } from "@hono/standard-validator";
|
|
2
|
+
import jsonPatch from "@rivetkit/fast-json-patch";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { streamSSE } from "hono/streaming";
|
|
5
|
+
import { createNanoEvents, type Unsubscribe } from "nanoevents";
|
|
6
|
+
import z from "zod/v4";
|
|
7
|
+
import type {
|
|
8
|
+
AnyDatabaseProvider,
|
|
9
|
+
InferDatabaseClient,
|
|
10
|
+
} from "@/actor/database";
|
|
11
|
+
import {
|
|
12
|
+
ColumnsSchema,
|
|
13
|
+
type Connection,
|
|
14
|
+
ForeignKeysSchema,
|
|
15
|
+
PatchSchema,
|
|
16
|
+
type RealtimeEvent,
|
|
17
|
+
type RecordedRealtimeEvent,
|
|
18
|
+
TablesSchema,
|
|
19
|
+
} from "./protocol/common";
|
|
20
|
+
|
|
21
|
+
export type ActorInspectorRouterEnv = {
|
|
22
|
+
Variables: {
|
|
23
|
+
inspector: ActorInspector;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a router for the Actor Inspector.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export function createActorInspectorRouter() {
|
|
32
|
+
return new Hono<ActorInspectorRouterEnv>()
|
|
33
|
+
.get("/ping", (c) => {
|
|
34
|
+
return c.json({ message: "pong" }, 200);
|
|
35
|
+
})
|
|
36
|
+
.get("/state", async (c) => {
|
|
37
|
+
if (await c.var.inspector.accessors.isStateEnabled()) {
|
|
38
|
+
return c.json(
|
|
39
|
+
{
|
|
40
|
+
enabled: true,
|
|
41
|
+
state: await c.var.inspector.accessors.getState(),
|
|
42
|
+
},
|
|
43
|
+
200,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return c.json({ enabled: false, state: null }, 200);
|
|
47
|
+
})
|
|
48
|
+
.patch(
|
|
49
|
+
"/state",
|
|
50
|
+
sValidator(
|
|
51
|
+
"json",
|
|
52
|
+
z.object({ patch: PatchSchema }).or(z.object({ replace: z.any() })),
|
|
53
|
+
),
|
|
54
|
+
async (c) => {
|
|
55
|
+
if (!(await c.var.inspector.accessors.isStateEnabled())) {
|
|
56
|
+
return c.json({ enabled: false }, 200);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const body = c.req.valid("json");
|
|
60
|
+
if ("replace" in body) {
|
|
61
|
+
await c.var.inspector.accessors.setState(body.replace);
|
|
62
|
+
return c.json(
|
|
63
|
+
{
|
|
64
|
+
enabled: true,
|
|
65
|
+
state: await c.var.inspector.accessors.getState(),
|
|
66
|
+
},
|
|
67
|
+
200,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const state = await c.var.inspector.accessors.getState();
|
|
71
|
+
|
|
72
|
+
const { newDocument: newState } = jsonPatch.applyPatch(
|
|
73
|
+
state,
|
|
74
|
+
body.patch,
|
|
75
|
+
);
|
|
76
|
+
await c.var.inspector.accessors.setState(newState);
|
|
77
|
+
|
|
78
|
+
return c.json(
|
|
79
|
+
{ enabled: true, state: await c.var.inspector.accessors.getState() },
|
|
80
|
+
200,
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
.get("/state/stream", async (c) => {
|
|
85
|
+
if (!(await c.var.inspector.accessors.isStateEnabled())) {
|
|
86
|
+
return c.json({ enabled: false }, 200);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let id = 0;
|
|
90
|
+
let unsub: Unsubscribe;
|
|
91
|
+
return streamSSE(
|
|
92
|
+
c,
|
|
93
|
+
async (stream) => {
|
|
94
|
+
unsub = c.var.inspector.emitter.on("stateUpdated", async (state) => {
|
|
95
|
+
stream.writeSSE({
|
|
96
|
+
data: JSON.stringify(state) || "",
|
|
97
|
+
event: "state-update",
|
|
98
|
+
id: String(id++),
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const { promise } = Promise.withResolvers<void>();
|
|
103
|
+
|
|
104
|
+
return promise;
|
|
105
|
+
},
|
|
106
|
+
async () => {
|
|
107
|
+
unsub?.();
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
})
|
|
111
|
+
.get("/connections", async (c) => {
|
|
112
|
+
const connections = await c.var.inspector.accessors.getConnections();
|
|
113
|
+
return c.json({ connections }, 200);
|
|
114
|
+
})
|
|
115
|
+
.get("/connections/stream", async (c) => {
|
|
116
|
+
let id = 0;
|
|
117
|
+
let unsub: Unsubscribe;
|
|
118
|
+
return streamSSE(
|
|
119
|
+
c,
|
|
120
|
+
async (stream) => {
|
|
121
|
+
unsub = c.var.inspector.emitter.on("connectionUpdated", async () => {
|
|
122
|
+
stream.writeSSE({
|
|
123
|
+
data: JSON.stringify(
|
|
124
|
+
await c.var.inspector.accessors.getConnections(),
|
|
125
|
+
),
|
|
126
|
+
event: "connection-update",
|
|
127
|
+
id: String(id++),
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const { promise } = Promise.withResolvers<void>();
|
|
132
|
+
|
|
133
|
+
return promise;
|
|
134
|
+
},
|
|
135
|
+
async () => {
|
|
136
|
+
unsub?.();
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
})
|
|
140
|
+
.get("/events", async (c) => {
|
|
141
|
+
const events = c.var.inspector.lastRealtimeEvents;
|
|
142
|
+
return c.json({ events }, 200);
|
|
143
|
+
})
|
|
144
|
+
.post("/events/clear", async (c) => {
|
|
145
|
+
c.var.inspector.lastRealtimeEvents.length = 0; // Clear the events
|
|
146
|
+
return c.json({ message: "Events cleared" }, 200);
|
|
147
|
+
})
|
|
148
|
+
.get("/events/stream", async (c) => {
|
|
149
|
+
let id = 0;
|
|
150
|
+
let unsub: Unsubscribe;
|
|
151
|
+
return streamSSE(
|
|
152
|
+
c,
|
|
153
|
+
async (stream) => {
|
|
154
|
+
unsub = c.var.inspector.emitter.on("eventFired", () => {
|
|
155
|
+
stream.writeSSE({
|
|
156
|
+
data: JSON.stringify(c.var.inspector.lastRealtimeEvents),
|
|
157
|
+
event: "realtime-event",
|
|
158
|
+
id: String(id++),
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const { promise } = Promise.withResolvers<void>();
|
|
163
|
+
|
|
164
|
+
return promise;
|
|
165
|
+
},
|
|
166
|
+
async () => {
|
|
167
|
+
unsub?.();
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
})
|
|
171
|
+
.get("/rpcs", async (c) => {
|
|
172
|
+
const rpcs = await c.var.inspector.accessors.getRpcs();
|
|
173
|
+
return c.json({ rpcs }, 200);
|
|
174
|
+
})
|
|
175
|
+
.get("/db", async (c) => {
|
|
176
|
+
if (!(await c.var.inspector.accessors.isDbEnabled())) {
|
|
177
|
+
return c.json({ enabled: false, db: null }, 200);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Access the SQLite database
|
|
181
|
+
const db = await c.var.inspector.accessors.getDb();
|
|
182
|
+
|
|
183
|
+
// Get list of tables
|
|
184
|
+
const rows = await db.execute(`PRAGMA table_list`);
|
|
185
|
+
const tables = TablesSchema.parse(rows).filter(
|
|
186
|
+
(table) => table.schema !== "temp" && !table.name.startsWith("sqlite_"),
|
|
187
|
+
);
|
|
188
|
+
// Get columns for each table
|
|
189
|
+
const tablesInfo = await Promise.all(
|
|
190
|
+
tables.map((table) => db.execute(`PRAGMA table_info(${table.name})`)),
|
|
191
|
+
);
|
|
192
|
+
const columns = tablesInfo.map((def) => ColumnsSchema.parse(def));
|
|
193
|
+
|
|
194
|
+
// Get foreign keys for each table
|
|
195
|
+
const foreignKeysList = await Promise.all(
|
|
196
|
+
tables.map((table) =>
|
|
197
|
+
db.execute(`PRAGMA foreign_key_list(${table.name})`),
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
const foreignKeys = foreignKeysList.map((def) =>
|
|
201
|
+
ForeignKeysSchema.parse(def),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Get record counts for each table
|
|
205
|
+
const countInfo = await Promise.all(
|
|
206
|
+
tables.map((table) =>
|
|
207
|
+
db.execute(`SELECT COUNT(*) as count FROM ${table.name}`),
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
const counts = countInfo.map((def) => {
|
|
211
|
+
return def[0].count || 0;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return c.json(
|
|
215
|
+
{
|
|
216
|
+
enabled: true,
|
|
217
|
+
db: tablesInfo.map((_, index) => {
|
|
218
|
+
return {
|
|
219
|
+
table: tables[index],
|
|
220
|
+
columns: columns[index],
|
|
221
|
+
foreignKeys: foreignKeys[index],
|
|
222
|
+
records: counts[index],
|
|
223
|
+
};
|
|
224
|
+
}),
|
|
225
|
+
},
|
|
226
|
+
200,
|
|
227
|
+
);
|
|
228
|
+
})
|
|
229
|
+
.post(
|
|
230
|
+
"/db",
|
|
231
|
+
sValidator(
|
|
232
|
+
"json",
|
|
233
|
+
z.object({ query: z.string(), params: z.array(z.any()).optional() }),
|
|
234
|
+
),
|
|
235
|
+
async (c) => {
|
|
236
|
+
if (!(await c.var.inspector.accessors.isDbEnabled())) {
|
|
237
|
+
return c.json({ enabled: false }, 200);
|
|
238
|
+
}
|
|
239
|
+
const db = await c.var.inspector.accessors.getDb();
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const result = (await db.execute(
|
|
243
|
+
c.req.valid("json").query,
|
|
244
|
+
...(c.req.valid("json").params || []),
|
|
245
|
+
)) as unknown;
|
|
246
|
+
return c.json({ result }, 200);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
c;
|
|
249
|
+
return c.json({ error: (error as Error).message }, 500);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface ActorInspectorAccessors {
|
|
256
|
+
isStateEnabled: () => Promise<boolean>;
|
|
257
|
+
getState: () => Promise<unknown>;
|
|
258
|
+
setState: (state: unknown) => Promise<void>;
|
|
259
|
+
isDbEnabled: () => Promise<boolean>;
|
|
260
|
+
getDb: () => Promise<InferDatabaseClient<AnyDatabaseProvider>>;
|
|
261
|
+
getRpcs: () => Promise<string[]>;
|
|
262
|
+
getConnections: () => Promise<Connection[]>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
interface ActorInspectorEmitterEvents {
|
|
266
|
+
stateUpdated: (state: unknown) => void;
|
|
267
|
+
connectionUpdated: () => void;
|
|
268
|
+
eventFired: (event: RealtimeEvent) => void;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Provides a unified interface for inspecting actor external and internal state.
|
|
273
|
+
*/
|
|
274
|
+
export class ActorInspector {
|
|
275
|
+
public readonly accessors: ActorInspectorAccessors;
|
|
276
|
+
public readonly emitter = createNanoEvents<ActorInspectorEmitterEvents>();
|
|
277
|
+
|
|
278
|
+
#lastRealtimeEvents: RecordedRealtimeEvent[] = [];
|
|
279
|
+
|
|
280
|
+
get lastRealtimeEvents() {
|
|
281
|
+
return this.#lastRealtimeEvents;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
constructor(accessors: () => ActorInspectorAccessors) {
|
|
285
|
+
this.accessors = accessors();
|
|
286
|
+
this.emitter.on("eventFired", (event) => {
|
|
287
|
+
this.#lastRealtimeEvents.push({
|
|
288
|
+
id: crypto.randomUUID(),
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
...event,
|
|
291
|
+
});
|
|
292
|
+
// keep the last 100 events
|
|
293
|
+
if (this.#lastRealtimeEvents.length > 100) {
|
|
294
|
+
this.#lastRealtimeEvents = this.#lastRealtimeEvents.slice(-100);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|