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,228 @@
|
|
|
1
|
+
import { type LogLevel, LogLevels } from "./log-levels";
|
|
2
|
+
|
|
3
|
+
export type LogEntry = [string, LogValue];
|
|
4
|
+
export type LogValue = string | number | bigint | boolean | null | undefined;
|
|
5
|
+
|
|
6
|
+
const LOG_LEVEL_COLORS: Record<number, string> = {
|
|
7
|
+
[LogLevels.CRITICAL]: "\x1b[31m", // Red
|
|
8
|
+
[LogLevels.ERROR]: "\x1b[31m", // Red
|
|
9
|
+
[LogLevels.WARN]: "\x1b[33m", // Yellow
|
|
10
|
+
[LogLevels.INFO]: "\x1b[32m", // Green
|
|
11
|
+
[LogLevels.DEBUG]: "\x1b[36m", // Cyan
|
|
12
|
+
[LogLevels.TRACE]: "\x1b[36m", // Cyan
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const RESET_COLOR = "\x1b[0m";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Serializes logfmt line using orderer parameters.
|
|
19
|
+
*
|
|
20
|
+
* We use varargs because it's ordered & it has less overhead than an object.
|
|
21
|
+
*
|
|
22
|
+
* ## Styling Methodology
|
|
23
|
+
*
|
|
24
|
+
* The three things you need to know for every log line is the level, the
|
|
25
|
+
* message, and who called it. These properties are highlighted in different colros
|
|
26
|
+
* and sorted in th eorder that you usually read them.
|
|
27
|
+
*
|
|
28
|
+
* Once you've found a log line you care about, then you want to find the
|
|
29
|
+
* property you need to see. The property names are bolded and the default color
|
|
30
|
+
* while the rest of the data is dim. This lets you scan to find the property
|
|
31
|
+
* name quickly then look closer to read the data associated with the
|
|
32
|
+
* property.
|
|
33
|
+
*/
|
|
34
|
+
export function stringify(...data: LogEntry[]) {
|
|
35
|
+
let line = "";
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < data.length; i++) {
|
|
38
|
+
const [key, valueRaw] = data[i];
|
|
39
|
+
|
|
40
|
+
let isNull = false;
|
|
41
|
+
let valueString: string;
|
|
42
|
+
if (valueRaw == null) {
|
|
43
|
+
isNull = true;
|
|
44
|
+
valueString = "";
|
|
45
|
+
} else {
|
|
46
|
+
valueString = valueRaw.toString();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Clip value unless specifically the error message
|
|
50
|
+
if (valueString.length > 512 && key !== "msg" && key !== "error")
|
|
51
|
+
valueString = `${valueString.slice(0, 512)}...`;
|
|
52
|
+
|
|
53
|
+
const needsQuoting =
|
|
54
|
+
valueString.indexOf(" ") > -1 || valueString.indexOf("=") > -1;
|
|
55
|
+
const needsEscaping =
|
|
56
|
+
valueString.indexOf('"') > -1 || valueString.indexOf("\\") > -1;
|
|
57
|
+
|
|
58
|
+
valueString = valueString.replace(/\n/g, "\\n");
|
|
59
|
+
if (needsEscaping) valueString = valueString.replace(/["\\]/g, "\\$&");
|
|
60
|
+
if (needsQuoting || needsEscaping) valueString = `"${valueString}"`;
|
|
61
|
+
if (valueString === "" && !isNull) valueString = '""';
|
|
62
|
+
|
|
63
|
+
if (LOGGER_CONFIG.enableColor) {
|
|
64
|
+
// With color
|
|
65
|
+
|
|
66
|
+
// Special message colors
|
|
67
|
+
let color = "\x1b[2m";
|
|
68
|
+
if (key === "level") {
|
|
69
|
+
const level = LogLevels[valueString as LogLevel];
|
|
70
|
+
const levelColor = LOG_LEVEL_COLORS[level];
|
|
71
|
+
if (levelColor) {
|
|
72
|
+
color = levelColor;
|
|
73
|
+
}
|
|
74
|
+
} else if (key === "msg") {
|
|
75
|
+
color = "\x1b[32m";
|
|
76
|
+
} else if (key === "trace") {
|
|
77
|
+
color = "\x1b[34m";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Format line
|
|
81
|
+
line += `\x1b[0m\x1b[1m${key}\x1b[0m\x1b[2m=\x1b[0m${color}${valueString}${RESET_COLOR}`;
|
|
82
|
+
} else {
|
|
83
|
+
// No color
|
|
84
|
+
line += `${key}=${valueString}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (i !== data.length - 1) {
|
|
88
|
+
line += " ";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return line;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function formatTimestamp(date: Date): string {
|
|
96
|
+
const year = date.getUTCFullYear();
|
|
97
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
98
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
99
|
+
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
100
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
101
|
+
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
|
102
|
+
const milliseconds = String(date.getUTCMilliseconds()).padStart(3, "0");
|
|
103
|
+
|
|
104
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}Z`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function castToLogValue(v: unknown): LogValue {
|
|
108
|
+
if (
|
|
109
|
+
typeof v === "string" ||
|
|
110
|
+
typeof v === "number" ||
|
|
111
|
+
typeof v === "bigint" ||
|
|
112
|
+
typeof v === "boolean" ||
|
|
113
|
+
v === null ||
|
|
114
|
+
v === undefined
|
|
115
|
+
) {
|
|
116
|
+
return v;
|
|
117
|
+
}
|
|
118
|
+
if (v instanceof Error) {
|
|
119
|
+
//args.push(...errorToLogEntries(k, v));
|
|
120
|
+
return String(v);
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
return JSON.stringify(v);
|
|
124
|
+
} catch {
|
|
125
|
+
return "[cannot stringify]";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// MARK: Config
|
|
130
|
+
interface GlobalLoggerConfig {
|
|
131
|
+
enableColor: boolean;
|
|
132
|
+
enableSpreadObject: boolean;
|
|
133
|
+
enableErrorStack: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const LOGGER_CONFIG: GlobalLoggerConfig = {
|
|
137
|
+
enableColor: false,
|
|
138
|
+
enableSpreadObject: false,
|
|
139
|
+
enableErrorStack: false,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// MARK: Utils
|
|
143
|
+
/**
|
|
144
|
+
* Converts an object in to an easier to read KV of entries.
|
|
145
|
+
*/
|
|
146
|
+
export function spreadObjectToLogEntries(
|
|
147
|
+
base: string,
|
|
148
|
+
data: unknown,
|
|
149
|
+
): LogEntry[] {
|
|
150
|
+
if (
|
|
151
|
+
LOGGER_CONFIG.enableSpreadObject &&
|
|
152
|
+
typeof data === "object" &&
|
|
153
|
+
!Array.isArray(data) &&
|
|
154
|
+
data !== null &&
|
|
155
|
+
Object.keys(data).length !== 0 &&
|
|
156
|
+
Object.keys(data).length < 16
|
|
157
|
+
) {
|
|
158
|
+
const logData: LogEntry[] = [];
|
|
159
|
+
for (const key in data) {
|
|
160
|
+
// logData.push([`${base}.${key}`, JSON.stringify((data as any)[key])]);
|
|
161
|
+
|
|
162
|
+
logData.push(
|
|
163
|
+
...spreadObjectToLogEntries(
|
|
164
|
+
`${base}.${key}`,
|
|
165
|
+
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
|
166
|
+
(data as any)[key],
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return logData;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return [[base, JSON.stringify(data)]];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function errorToLogEntries(base: string, error: unknown): LogEntry[] {
|
|
177
|
+
if (error instanceof Error) {
|
|
178
|
+
return [
|
|
179
|
+
//[`${base}.name`, error.name],
|
|
180
|
+
[`${base}.message`, error.message],
|
|
181
|
+
...(LOGGER_CONFIG.enableErrorStack && error.stack
|
|
182
|
+
? [[`${base}.stack`, formatStackTrace(error.stack)] as LogEntry]
|
|
183
|
+
: []),
|
|
184
|
+
...(error.cause ? errorToLogEntries(`${base}.cause`, error.cause) : []),
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
return [[base, `${error}`]];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// export function errorToLogEntries(base: string, error: unknown): LogEntry[] {
|
|
191
|
+
// if (error instanceof RuntimeError) {
|
|
192
|
+
// return [
|
|
193
|
+
// [`${base}.code`, error.code],
|
|
194
|
+
// [`${base}.description`, error.errorConfig?.description],
|
|
195
|
+
// [`${base}.module`, error.moduleName],
|
|
196
|
+
// ...(error.trace ? [[`${base}.trace`, stringifyTrace(error.trace)] as LogEntry] : []),
|
|
197
|
+
// ...(LOGGER_CONFIG.enableErrorStack && error.stack
|
|
198
|
+
// ? [[`${base}.stack`, formatStackTrace(error.stack)] as LogEntry]
|
|
199
|
+
// : []),
|
|
200
|
+
// ...(error.meta ? [[`${base}.meta`, JSON.stringify(error.meta)] as LogEntry] : []),
|
|
201
|
+
// ...(error.cause ? errorToLogEntries(`${base}.cause`, error.cause) : []),
|
|
202
|
+
// ];
|
|
203
|
+
// } else if (error instanceof Error) {
|
|
204
|
+
// return [
|
|
205
|
+
// [`${base}.name`, error.name],
|
|
206
|
+
// [`${base}.message`, error.message],
|
|
207
|
+
// ...(LOGGER_CONFIG.enableErrorStack && error.stack
|
|
208
|
+
// ? [[`${base}.stack`, formatStackTrace(error.stack)] as LogEntry]
|
|
209
|
+
// : []),
|
|
210
|
+
// ...(error.cause ? errorToLogEntries(`${base}.cause`, error.cause) : []),
|
|
211
|
+
// ];
|
|
212
|
+
// } else {
|
|
213
|
+
// return [
|
|
214
|
+
// [base, `${error}`],
|
|
215
|
+
// ];
|
|
216
|
+
// }
|
|
217
|
+
// }
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Formats a JS stack trace in to a legible one-liner.
|
|
221
|
+
*/
|
|
222
|
+
function formatStackTrace(stackTrace: string): string {
|
|
223
|
+
const regex = /at (.+?)$/gm;
|
|
224
|
+
const matches = [...stackTrace.matchAll(regex)];
|
|
225
|
+
// Reverse array since the stack goes from top level -> bottom level
|
|
226
|
+
matches.reverse();
|
|
227
|
+
return matches.map((match) => match[1].trim()).join(" > ");
|
|
228
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as cbor from "cbor-x";
|
|
2
|
+
import type { Context as HonoContext, Next } from "hono";
|
|
3
|
+
import type { Encoding } from "@/actor/protocol/serde";
|
|
4
|
+
import {
|
|
5
|
+
getRequestEncoding,
|
|
6
|
+
getRequestExposeInternalError,
|
|
7
|
+
} from "@/actor/router-endpoints";
|
|
8
|
+
import { HttpResponseError } from "@/schemas/client-protocol/mod";
|
|
9
|
+
import { HTTP_RESPONSE_ERROR_VERSIONED } from "@/schemas/client-protocol/versioned";
|
|
10
|
+
import { serializeWithEncoding } from "@/serde";
|
|
11
|
+
import { bufferToArrayBuffer } from "@/utils";
|
|
12
|
+
import { getLogger, type Logger } from "./log";
|
|
13
|
+
import { deconstructError, stringifyError } from "./utils";
|
|
14
|
+
|
|
15
|
+
export function logger() {
|
|
16
|
+
return getLogger("router");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function loggerMiddleware(logger: Logger) {
|
|
20
|
+
return async (c: HonoContext, next: Next) => {
|
|
21
|
+
const method = c.req.method;
|
|
22
|
+
const path = c.req.path;
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
|
|
25
|
+
await next();
|
|
26
|
+
|
|
27
|
+
const duration = Date.now() - startTime;
|
|
28
|
+
logger.debug("http request", {
|
|
29
|
+
method,
|
|
30
|
+
path,
|
|
31
|
+
status: c.res.status,
|
|
32
|
+
dt: `${duration}ms`,
|
|
33
|
+
reqSize: c.req.header("content-length"),
|
|
34
|
+
resSize: c.res.headers.get("content-length"),
|
|
35
|
+
userAgent: c.req.header("user-agent"),
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function handleRouteNotFound(c: HonoContext) {
|
|
41
|
+
return c.text("Not Found (RivetKit)", 404);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface HandleRouterErrorOpts {
|
|
45
|
+
enableExposeInternalError?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function handleRouteError(
|
|
49
|
+
opts: HandleRouterErrorOpts,
|
|
50
|
+
error: unknown,
|
|
51
|
+
c: HonoContext,
|
|
52
|
+
) {
|
|
53
|
+
const exposeInternalError =
|
|
54
|
+
opts.enableExposeInternalError && getRequestExposeInternalError(c.req.raw);
|
|
55
|
+
|
|
56
|
+
const { statusCode, code, message, metadata } = deconstructError(
|
|
57
|
+
error,
|
|
58
|
+
logger(),
|
|
59
|
+
{
|
|
60
|
+
method: c.req.method,
|
|
61
|
+
path: c.req.path,
|
|
62
|
+
},
|
|
63
|
+
exposeInternalError,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
let encoding: Encoding;
|
|
67
|
+
try {
|
|
68
|
+
encoding = getRequestEncoding(c.req);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
logger().debug("failed to extract encoding", {
|
|
71
|
+
error: stringifyError(err),
|
|
72
|
+
});
|
|
73
|
+
encoding = "json";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const output = serializeWithEncoding(
|
|
77
|
+
encoding,
|
|
78
|
+
{
|
|
79
|
+
code,
|
|
80
|
+
message,
|
|
81
|
+
metadata: bufferToArrayBuffer(cbor.encode(metadata)),
|
|
82
|
+
},
|
|
83
|
+
HTTP_RESPONSE_ERROR_VERSIONED,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return c.body(output, { status: statusCode });
|
|
87
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import type { Next } from "hono";
|
|
2
|
+
import type { ContentfulStatusCode } from "hono/utils/http-status";
|
|
3
|
+
import * as errors from "@/actor/errors";
|
|
4
|
+
import { getEnvUniversal } from "@/utils";
|
|
5
|
+
import type { Logger } from "./log";
|
|
6
|
+
|
|
7
|
+
export function assertUnreachable(x: never): never {
|
|
8
|
+
throw new Error(`Unreachable case: ${x}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Safely stringifies an object, ensuring that the stringified object is under a certain size.
|
|
13
|
+
* @param obj any object to stringify
|
|
14
|
+
* @param maxSize maximum size of the stringified object in bytes
|
|
15
|
+
* @returns stringified object
|
|
16
|
+
*/
|
|
17
|
+
export function safeStringify(obj: unknown, maxSize: number) {
|
|
18
|
+
let size = 0;
|
|
19
|
+
|
|
20
|
+
function replacer(key: string, value: unknown) {
|
|
21
|
+
if (value === null || value === undefined) return value;
|
|
22
|
+
const valueSize =
|
|
23
|
+
typeof value === "string" ? value.length : JSON.stringify(value).length;
|
|
24
|
+
size += key.length + valueSize;
|
|
25
|
+
|
|
26
|
+
if (size > maxSize) {
|
|
27
|
+
throw new Error(`JSON object exceeds size limit of ${maxSize} bytes.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return JSON.stringify(obj, replacer);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// TODO: Instead of doing this, use a temp var for state and attempt to write
|
|
37
|
+
// it. Roll back state if fails to serialize.
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if a value is CBOR serializable.
|
|
41
|
+
* Optionally pass an onInvalid callback to receive the path to invalid values.
|
|
42
|
+
*
|
|
43
|
+
* For a complete list of supported CBOR tags, see:
|
|
44
|
+
* https://github.com/kriszyp/cbor-x/blob/cc1cf9df8ba72288c7842af1dd374d73e34cdbc1/README.md#list-of-supported-tags-for-decoding
|
|
45
|
+
*/
|
|
46
|
+
export function isCborSerializable(
|
|
47
|
+
value: unknown,
|
|
48
|
+
onInvalid?: (path: string) => void,
|
|
49
|
+
currentPath = "",
|
|
50
|
+
): boolean {
|
|
51
|
+
// Handle primitive types directly
|
|
52
|
+
if (value === null || value === undefined) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (typeof value === "number") {
|
|
57
|
+
if (!Number.isFinite(value)) {
|
|
58
|
+
onInvalid?.(currentPath);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof value === "boolean" || typeof value === "string") {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle BigInt (CBOR tags 2 and 3)
|
|
69
|
+
if (typeof value === "bigint") {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle Date objects (CBOR tags 0 and 1)
|
|
74
|
+
if (value instanceof Date) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle typed arrays (CBOR tags 64-82)
|
|
79
|
+
if (
|
|
80
|
+
value instanceof Uint8Array ||
|
|
81
|
+
value instanceof Uint8ClampedArray ||
|
|
82
|
+
value instanceof Uint16Array ||
|
|
83
|
+
value instanceof Uint32Array ||
|
|
84
|
+
value instanceof BigUint64Array ||
|
|
85
|
+
value instanceof Int8Array ||
|
|
86
|
+
value instanceof Int16Array ||
|
|
87
|
+
value instanceof Int32Array ||
|
|
88
|
+
value instanceof BigInt64Array ||
|
|
89
|
+
value instanceof Float32Array ||
|
|
90
|
+
value instanceof Float64Array
|
|
91
|
+
) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Handle Map (CBOR tag 259)
|
|
96
|
+
if (value instanceof Map) {
|
|
97
|
+
for (const [key, val] of value.entries()) {
|
|
98
|
+
const keyPath = currentPath
|
|
99
|
+
? `${currentPath}.key(${String(key)})`
|
|
100
|
+
: `key(${String(key)})`;
|
|
101
|
+
const valPath = currentPath
|
|
102
|
+
? `${currentPath}.value(${String(key)})`
|
|
103
|
+
: `value(${String(key)})`;
|
|
104
|
+
if (
|
|
105
|
+
!isCborSerializable(key, onInvalid, keyPath) ||
|
|
106
|
+
!isCborSerializable(val, onInvalid, valPath)
|
|
107
|
+
) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Handle Set (CBOR tag 258)
|
|
115
|
+
if (value instanceof Set) {
|
|
116
|
+
let index = 0;
|
|
117
|
+
for (const item of value.values()) {
|
|
118
|
+
const itemPath = currentPath
|
|
119
|
+
? `${currentPath}.set[${index}]`
|
|
120
|
+
: `set[${index}]`;
|
|
121
|
+
if (!isCborSerializable(item, onInvalid, itemPath)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
index++;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Handle RegExp (CBOR tag 27)
|
|
130
|
+
if (value instanceof RegExp) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle Error objects (CBOR tag 27)
|
|
135
|
+
if (value instanceof Error) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Handle arrays
|
|
140
|
+
if (Array.isArray(value)) {
|
|
141
|
+
for (let i = 0; i < value.length; i++) {
|
|
142
|
+
const itemPath = currentPath ? `${currentPath}[${i}]` : `[${i}]`;
|
|
143
|
+
if (!isCborSerializable(value[i], onInvalid, itemPath)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Handle plain objects and records (CBOR tags 105, 51, 57344-57599)
|
|
151
|
+
if (typeof value === "object") {
|
|
152
|
+
// Allow plain objects and objects with prototypes (for records and named objects)
|
|
153
|
+
const proto = Object.getPrototypeOf(value);
|
|
154
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
155
|
+
// Check if it's a known serializable object type
|
|
156
|
+
const protoConstructor = value.constructor;
|
|
157
|
+
if (protoConstructor && typeof protoConstructor.name === "string") {
|
|
158
|
+
// Allow objects with named constructors (records, named objects)
|
|
159
|
+
// This includes user-defined classes and built-in objects
|
|
160
|
+
// that CBOR can serialize with tag 27 or record tags
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check all properties recursively
|
|
165
|
+
for (const key in value) {
|
|
166
|
+
const propPath = currentPath ? `${currentPath}.${key}` : key;
|
|
167
|
+
if (
|
|
168
|
+
!isCborSerializable(
|
|
169
|
+
value[key as keyof typeof value],
|
|
170
|
+
onInvalid,
|
|
171
|
+
propPath,
|
|
172
|
+
)
|
|
173
|
+
) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Not serializable
|
|
181
|
+
onInvalid?.(currentPath);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface DeconstructedError {
|
|
186
|
+
__type: "ActorError";
|
|
187
|
+
statusCode: ContentfulStatusCode;
|
|
188
|
+
public: boolean;
|
|
189
|
+
code: string;
|
|
190
|
+
message: string;
|
|
191
|
+
metadata?: unknown;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Deconstructs error in to components that are used to build responses. */
|
|
195
|
+
export function deconstructError(
|
|
196
|
+
error: unknown,
|
|
197
|
+
logger: Logger,
|
|
198
|
+
extraLog: Record<string, unknown>,
|
|
199
|
+
exposeInternalError = false,
|
|
200
|
+
): DeconstructedError {
|
|
201
|
+
// Build response error information. Only return errors if flagged as public in order to prevent leaking internal behavior.
|
|
202
|
+
//
|
|
203
|
+
// We log the error here instead of after generating the code & message because we need to log the original error, not the masked internal error.
|
|
204
|
+
let statusCode: ContentfulStatusCode;
|
|
205
|
+
let public_: boolean;
|
|
206
|
+
let code: string;
|
|
207
|
+
let message: string;
|
|
208
|
+
let metadata: unknown;
|
|
209
|
+
if (errors.ActorError.isActorError(error) && error.public) {
|
|
210
|
+
// Check if error has statusCode (could be ActorError instance or DeconstructedError)
|
|
211
|
+
statusCode = (
|
|
212
|
+
"statusCode" in error && error.statusCode ? error.statusCode : 400
|
|
213
|
+
) as ContentfulStatusCode;
|
|
214
|
+
public_ = true;
|
|
215
|
+
code = error.code;
|
|
216
|
+
message = getErrorMessage(error);
|
|
217
|
+
metadata = error.metadata;
|
|
218
|
+
|
|
219
|
+
logger.info("public error", {
|
|
220
|
+
code,
|
|
221
|
+
message,
|
|
222
|
+
issues: "https://github.com/rivet-gg/rivetkit/issues",
|
|
223
|
+
support: "https://rivet.gg/discord",
|
|
224
|
+
...extraLog,
|
|
225
|
+
});
|
|
226
|
+
} else if (exposeInternalError) {
|
|
227
|
+
if (errors.ActorError.isActorError(error)) {
|
|
228
|
+
statusCode = 500;
|
|
229
|
+
public_ = false;
|
|
230
|
+
code = error.code;
|
|
231
|
+
message = getErrorMessage(error);
|
|
232
|
+
metadata = error.metadata;
|
|
233
|
+
|
|
234
|
+
logger.info("internal error", {
|
|
235
|
+
code,
|
|
236
|
+
message,
|
|
237
|
+
issues: "https://github.com/rivet-gg/rivetkit/issues",
|
|
238
|
+
support: "https://rivet.gg/discord",
|
|
239
|
+
...extraLog,
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
statusCode = 500;
|
|
243
|
+
public_ = false;
|
|
244
|
+
code = errors.INTERNAL_ERROR_CODE;
|
|
245
|
+
message = getErrorMessage(error);
|
|
246
|
+
|
|
247
|
+
logger.info("internal error", {
|
|
248
|
+
code,
|
|
249
|
+
message,
|
|
250
|
+
issues: "https://github.com/rivet-gg/rivetkit/issues",
|
|
251
|
+
support: "https://rivet.gg/discord",
|
|
252
|
+
...extraLog,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
statusCode = 500;
|
|
257
|
+
public_ = false;
|
|
258
|
+
code = errors.INTERNAL_ERROR_CODE;
|
|
259
|
+
message = errors.INTERNAL_ERROR_DESCRIPTION;
|
|
260
|
+
metadata = {
|
|
261
|
+
//url: `https://hub.rivet.gg/projects/${actorMetadata.project.slug}/environments/${actorMetadata.environment.slug}/actors?actorId=${actorMetadata.actor.id}`,
|
|
262
|
+
} satisfies errors.InternalErrorMetadata;
|
|
263
|
+
|
|
264
|
+
logger.warn("internal error", {
|
|
265
|
+
error: getErrorMessage(error),
|
|
266
|
+
stack: (error as Error)?.stack,
|
|
267
|
+
issues: "https://github.com/rivet-gg/rivetkit/issues",
|
|
268
|
+
support: "https://rivet.gg/discord",
|
|
269
|
+
...extraLog,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
__type: "ActorError",
|
|
275
|
+
statusCode,
|
|
276
|
+
public: public_,
|
|
277
|
+
code,
|
|
278
|
+
message,
|
|
279
|
+
metadata,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function stringifyError(error: unknown): string {
|
|
284
|
+
if (error instanceof Error) {
|
|
285
|
+
if (
|
|
286
|
+
typeof process !== "undefined" &&
|
|
287
|
+
getEnvUniversal("_RIVETKIT_ERROR_STACK") === "1"
|
|
288
|
+
) {
|
|
289
|
+
return `${error.name}: ${error.message}${error.stack ? `\n${error.stack}` : ""}`;
|
|
290
|
+
} else {
|
|
291
|
+
return `${error.name}: ${error.message}`;
|
|
292
|
+
}
|
|
293
|
+
} else if (typeof error === "string") {
|
|
294
|
+
return error;
|
|
295
|
+
} else if (typeof error === "object" && error !== null) {
|
|
296
|
+
try {
|
|
297
|
+
return `${JSON.stringify(error)}`;
|
|
298
|
+
} catch {
|
|
299
|
+
return "[cannot stringify error]";
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
return `Unknown error: ${getErrorMessage(error)}`;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function getErrorMessage(err: unknown): string {
|
|
307
|
+
if (
|
|
308
|
+
err &&
|
|
309
|
+
typeof err === "object" &&
|
|
310
|
+
"message" in err &&
|
|
311
|
+
typeof err.message === "string"
|
|
312
|
+
) {
|
|
313
|
+
return err.message;
|
|
314
|
+
} else {
|
|
315
|
+
return String(err);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Generates a `Next` handler to pass to middleware in order to be able to call arbitrary middleware. */
|
|
320
|
+
export function noopNext(): Next {
|
|
321
|
+
return async () => {};
|
|
322
|
+
}
|