rivetkit 2.0.20 → 2.0.21
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/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/{chunk-L3YPHXPE.js → chunk-2POQCWMA.js} +481 -100
- package/dist/tsup/chunk-2POQCWMA.js.map +1 -0
- package/dist/tsup/{chunk-5F6X4AFU.js → chunk-3UIGKLZW.js} +6 -6
- package/dist/tsup/chunk-3UIGKLZW.js.map +1 -0
- package/dist/tsup/{chunk-M6LIJ6BK.js → chunk-4OINFQBR.js} +3 -3
- package/dist/tsup/{chunk-ZODINJWN.cjs → chunk-65SAIRRY.cjs} +12 -12
- package/dist/tsup/chunk-65SAIRRY.cjs.map +1 -0
- package/dist/tsup/{chunk-RI4YHZZW.js → chunk-6G76WIWL.js} +2 -2
- package/dist/tsup/{chunk-RI4YHZZW.js.map → chunk-6G76WIWL.js.map} +1 -1
- package/dist/tsup/{chunk-YFFCPYHY.js → chunk-D2LS4X6E.js} +11 -5
- package/dist/tsup/chunk-D2LS4X6E.js.map +1 -0
- package/dist/tsup/{chunk-QRUGCDA5.js → chunk-DYA34FHW.js} +2 -2
- package/dist/tsup/{chunk-KUZWEM23.cjs → chunk-ELDFBXDV.cjs} +8 -4
- package/dist/tsup/chunk-ELDFBXDV.cjs.map +1 -0
- package/dist/tsup/{chunk-FYP3TZXD.cjs → chunk-FDJ3AVNB.cjs} +32 -26
- package/dist/tsup/chunk-FDJ3AVNB.cjs.map +1 -0
- package/dist/tsup/{chunk-ER5OT3SQ.js → chunk-FUX6U6TL.js} +2 -2
- package/dist/tsup/chunk-FUX6U6TL.js.map +1 -0
- package/dist/tsup/{chunk-RJVSNJO7.cjs → chunk-HN7UXCYQ.cjs} +7 -7
- package/dist/tsup/chunk-HN7UXCYQ.cjs.map +1 -0
- package/dist/tsup/{chunk-QMVCFQ37.js → chunk-HUGSRAGL.js} +8 -4
- package/dist/tsup/chunk-HUGSRAGL.js.map +1 -0
- package/dist/tsup/{chunk-PV22ZBDE.cjs → chunk-JKOUXDK6.cjs} +16 -10
- package/dist/tsup/chunk-JKOUXDK6.cjs.map +1 -0
- package/dist/tsup/{chunk-2I6L3VRO.cjs → chunk-JTIBPF7N.cjs} +14 -14
- package/dist/tsup/chunk-JTIBPF7N.cjs.map +1 -0
- package/dist/tsup/chunk-KSRXX3Z4.cjs.map +1 -1
- package/dist/tsup/{chunk-JZD6FEOE.cjs → chunk-LMJHBF26.cjs} +455 -289
- package/dist/tsup/chunk-LMJHBF26.cjs.map +1 -0
- package/dist/tsup/{chunk-KKRR7DSG.cjs → chunk-LWGCMELP.cjs} +3 -3
- package/dist/tsup/chunk-LWGCMELP.cjs.map +1 -0
- package/dist/tsup/{chunk-2S7HJMMY.cjs → chunk-M5BHNJHB.cjs} +630 -249
- package/dist/tsup/chunk-M5BHNJHB.cjs.map +1 -0
- package/dist/tsup/{chunk-G6JGHCG4.cjs → chunk-O4GUKGK4.cjs} +6 -6
- package/dist/tsup/chunk-O4GUKGK4.cjs.map +1 -0
- package/dist/tsup/{chunk-7ACKZS3T.js → chunk-RZZDFDB6.js} +13 -7
- package/dist/tsup/chunk-RZZDFDB6.js.map +1 -0
- package/dist/tsup/{chunk-MGHPBNWB.js → chunk-VLR3TDHT.js} +2 -2
- package/dist/tsup/{chunk-GQ5WTE64.js → chunk-Y2QONT7B.js} +263 -97
- package/dist/tsup/chunk-Y2QONT7B.js.map +1 -0
- package/dist/tsup/{chunk-DUOTOMP7.cjs → chunk-ZNWE3XBT.cjs} +3 -3
- package/dist/tsup/chunk-ZNWE3XBT.cjs.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.cjs.map +1 -1
- package/dist/tsup/client/mod.d.cts +2 -2
- package/dist/tsup/client/mod.d.ts +2 -2
- package/dist/tsup/client/mod.js +8 -8
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.cjs.map +1 -1
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.cjs.map +1 -1
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{conn-CmPcqOCF.d.ts → conn-Clu655RU.d.ts} +72 -71
- package/dist/tsup/{conn-DU5EbfCu.d.cts → conn-lUvFLo_q.d.cts} +72 -71
- package/dist/tsup/driver-helpers/mod.cjs +5 -5
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +1 -1
- package/dist/tsup/driver-helpers/mod.d.ts +1 -1
- package/dist/tsup/driver-helpers/mod.js +4 -4
- package/dist/tsup/driver-test-suite/mod.cjs +603 -294
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +1 -1
- package/dist/tsup/driver-test-suite/mod.d.ts +1 -1
- package/dist/tsup/driver-test-suite/mod.js +574 -265
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.cjs.map +1 -1
- package/dist/tsup/inspector/mod.d.cts +68 -7
- package/dist/tsup/inspector/mod.d.ts +68 -7
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -10
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +2 -2
- package/dist/tsup/mod.d.ts +2 -2
- package/dist/tsup/mod.js +9 -9
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.cjs.map +1 -1
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.js +1 -1
- package/package.json +4 -3
- package/src/actor/config.ts +108 -15
- package/src/actor/conn-drivers.ts +2 -1
- package/src/actor/instance.ts +119 -35
- package/src/actor/keys.test.ts +13 -4
- package/src/actor/protocol/old.ts +10 -3
- package/src/actor/router-endpoints.ts +26 -16
- package/src/actor/router.ts +41 -13
- package/src/actor/unstable-react.ts +1 -1
- package/src/client/actor-common.ts +3 -1
- package/src/client/actor-conn.ts +44 -12
- package/src/client/actor-handle.ts +4 -1
- package/src/client/client.ts +32 -18
- package/src/client/utils.ts +21 -8
- package/src/common/actor-router-consts.ts +2 -0
- package/src/common/inline-websocket-adapter2.ts +24 -6
- package/src/common/log.ts +6 -2
- package/src/common/logfmt.ts +3 -1
- package/src/common/router.ts +3 -1
- package/src/common/utils.ts +6 -2
- package/src/driver-helpers/utils.ts +4 -1
- package/src/driver-test-suite/mod.ts +15 -4
- package/src/driver-test-suite/test-inline-client-driver.ts +35 -13
- package/src/driver-test-suite/tests/action-features.ts +6 -2
- package/src/driver-test-suite/tests/actor-conn-state.ts +18 -8
- package/src/driver-test-suite/tests/actor-conn.ts +35 -13
- package/src/driver-test-suite/tests/actor-handle.ts +35 -15
- package/src/driver-test-suite/tests/actor-inline-client.ts +34 -23
- package/src/driver-test-suite/tests/actor-inspector.ts +241 -131
- package/src/driver-test-suite/tests/actor-reconnect.ts +14 -4
- package/src/driver-test-suite/tests/actor-schedule.ts +12 -3
- package/src/driver-test-suite/tests/actor-sleep.ts +6 -3
- package/src/driver-test-suite/tests/actor-vars.ts +6 -2
- package/src/driver-test-suite/tests/manager-driver.ts +16 -6
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +64 -25
- package/src/driver-test-suite/tests/raw-http.ts +17 -5
- package/src/driver-test-suite/tests/raw-websocket.ts +36 -12
- package/src/driver-test-suite/tests/request-access.ts +18 -8
- package/src/drivers/engine/actor-driver.ts +46 -25
- package/src/drivers/engine/config.ts +2 -1
- package/src/drivers/file-system/global-state.ts +58 -16
- package/src/drivers/file-system/manager.ts +35 -12
- package/src/drivers/file-system/mod.ts +6 -1
- package/src/drivers/file-system/utils.ts +8 -2
- package/src/engine-process/mod.ts +15 -4
- package/src/inspector/actor.ts +63 -23
- package/src/inspector/config.ts +2 -1
- package/src/inspector/manager.ts +10 -3
- package/src/inspector/utils.ts +2 -1
- package/src/manager/driver.ts +4 -1
- package/src/manager/gateway.ts +278 -8
- package/src/manager/hono-websocket-adapter.ts +33 -10
- package/src/manager/router-schema.ts +4 -2
- package/src/manager/router.ts +78 -12
- package/src/manager-api/actors.ts +2 -0
- package/src/registry/mod.ts +31 -9
- package/src/registry/run-config.ts +3 -1
- package/src/remote-manager-driver/api-endpoints.ts +2 -2
- package/src/remote-manager-driver/mod.ts +23 -7
- package/src/remote-manager-driver/ws-proxy.ts +21 -5
- package/src/serde.ts +6 -2
- package/src/test/mod.ts +2 -1
- package/src/utils.ts +6 -2
- package/dist/tsup/chunk-2I6L3VRO.cjs.map +0 -1
- package/dist/tsup/chunk-2S7HJMMY.cjs.map +0 -1
- package/dist/tsup/chunk-5F6X4AFU.js.map +0 -1
- package/dist/tsup/chunk-7ACKZS3T.js.map +0 -1
- package/dist/tsup/chunk-DUOTOMP7.cjs.map +0 -1
- package/dist/tsup/chunk-ER5OT3SQ.js.map +0 -1
- package/dist/tsup/chunk-FYP3TZXD.cjs.map +0 -1
- package/dist/tsup/chunk-G6JGHCG4.cjs.map +0 -1
- package/dist/tsup/chunk-GQ5WTE64.js.map +0 -1
- package/dist/tsup/chunk-JZD6FEOE.cjs.map +0 -1
- package/dist/tsup/chunk-KKRR7DSG.cjs.map +0 -1
- package/dist/tsup/chunk-KUZWEM23.cjs.map +0 -1
- package/dist/tsup/chunk-L3YPHXPE.js.map +0 -1
- package/dist/tsup/chunk-PV22ZBDE.cjs.map +0 -1
- package/dist/tsup/chunk-QMVCFQ37.js.map +0 -1
- package/dist/tsup/chunk-RJVSNJO7.cjs.map +0 -1
- package/dist/tsup/chunk-YFFCPYHY.js.map +0 -1
- package/dist/tsup/chunk-ZODINJWN.cjs.map +0 -1
- /package/dist/tsup/{chunk-M6LIJ6BK.js.map → chunk-4OINFQBR.js.map} +0 -0
- /package/dist/tsup/{chunk-QRUGCDA5.js.map → chunk-DYA34FHW.js.map} +0 -0
- /package/dist/tsup/{chunk-MGHPBNWB.js.map → chunk-VLR3TDHT.js.map} +0 -0
package/src/inspector/actor.ts
CHANGED
|
@@ -50,7 +50,9 @@ export function createActorInspectorRouter() {
|
|
|
50
50
|
"/state",
|
|
51
51
|
sValidator(
|
|
52
52
|
"json",
|
|
53
|
-
z
|
|
53
|
+
z
|
|
54
|
+
.object({ patch: PatchSchema })
|
|
55
|
+
.or(z.object({ replace: z.any() })),
|
|
54
56
|
),
|
|
55
57
|
async (c) => {
|
|
56
58
|
if (!(await c.var.inspector.accessors.isStateEnabled())) {
|
|
@@ -77,7 +79,10 @@ export function createActorInspectorRouter() {
|
|
|
77
79
|
await c.var.inspector.accessors.setState(newState);
|
|
78
80
|
|
|
79
81
|
return c.json(
|
|
80
|
-
{
|
|
82
|
+
{
|
|
83
|
+
enabled: true,
|
|
84
|
+
state: await c.var.inspector.accessors.getState(),
|
|
85
|
+
},
|
|
81
86
|
200,
|
|
82
87
|
);
|
|
83
88
|
},
|
|
@@ -92,13 +97,16 @@ export function createActorInspectorRouter() {
|
|
|
92
97
|
return streamSSE(
|
|
93
98
|
c,
|
|
94
99
|
async (stream) => {
|
|
95
|
-
unsub = c.var.inspector.emitter.on(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
unsub = c.var.inspector.emitter.on(
|
|
101
|
+
"stateUpdated",
|
|
102
|
+
async (state) => {
|
|
103
|
+
stream.writeSSE({
|
|
104
|
+
data: JSON.stringify(state) || "",
|
|
105
|
+
event: "state-update",
|
|
106
|
+
id: String(id++),
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
);
|
|
102
110
|
|
|
103
111
|
const { promise } = promiseWithResolvers<void>();
|
|
104
112
|
|
|
@@ -110,7 +118,8 @@ export function createActorInspectorRouter() {
|
|
|
110
118
|
);
|
|
111
119
|
})
|
|
112
120
|
.get("/connections", async (c) => {
|
|
113
|
-
const connections =
|
|
121
|
+
const connections =
|
|
122
|
+
await c.var.inspector.accessors.getConnections();
|
|
114
123
|
return c.json({ connections }, 200);
|
|
115
124
|
})
|
|
116
125
|
.get("/connections/stream", async (c) => {
|
|
@@ -119,15 +128,18 @@ export function createActorInspectorRouter() {
|
|
|
119
128
|
return streamSSE(
|
|
120
129
|
c,
|
|
121
130
|
async (stream) => {
|
|
122
|
-
unsub = c.var.inspector.emitter.on(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
unsub = c.var.inspector.emitter.on(
|
|
132
|
+
"connectionUpdated",
|
|
133
|
+
async () => {
|
|
134
|
+
stream.writeSSE({
|
|
135
|
+
data: JSON.stringify(
|
|
136
|
+
await c.var.inspector.accessors.getConnections(),
|
|
137
|
+
),
|
|
138
|
+
event: "connection-update",
|
|
139
|
+
id: String(id++),
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
);
|
|
131
143
|
|
|
132
144
|
const { promise } = promiseWithResolvers<void>();
|
|
133
145
|
|
|
@@ -154,7 +166,9 @@ export function createActorInspectorRouter() {
|
|
|
154
166
|
async (stream) => {
|
|
155
167
|
unsub = c.var.inspector.emitter.on("eventFired", () => {
|
|
156
168
|
stream.writeSSE({
|
|
157
|
-
data: JSON.stringify(
|
|
169
|
+
data: JSON.stringify(
|
|
170
|
+
c.var.inspector.lastRealtimeEvents,
|
|
171
|
+
),
|
|
158
172
|
event: "realtime-event",
|
|
159
173
|
id: String(id++),
|
|
160
174
|
});
|
|
@@ -184,11 +198,15 @@ export function createActorInspectorRouter() {
|
|
|
184
198
|
// Get list of tables
|
|
185
199
|
const rows = await db.execute(`PRAGMA table_list`);
|
|
186
200
|
const tables = TablesSchema.parse(rows).filter(
|
|
187
|
-
(table) =>
|
|
201
|
+
(table) =>
|
|
202
|
+
table.schema !== "temp" &&
|
|
203
|
+
!table.name.startsWith("sqlite_"),
|
|
188
204
|
);
|
|
189
205
|
// Get columns for each table
|
|
190
206
|
const tablesInfo = await Promise.all(
|
|
191
|
-
tables.map((table) =>
|
|
207
|
+
tables.map((table) =>
|
|
208
|
+
db.execute(`PRAGMA table_info(${table.name})`),
|
|
209
|
+
),
|
|
192
210
|
);
|
|
193
211
|
const columns = tablesInfo.map((def) => ColumnsSchema.parse(def));
|
|
194
212
|
|
|
@@ -231,7 +249,10 @@ export function createActorInspectorRouter() {
|
|
|
231
249
|
"/db",
|
|
232
250
|
sValidator(
|
|
233
251
|
"json",
|
|
234
|
-
z.object({
|
|
252
|
+
z.object({
|
|
253
|
+
query: z.string(),
|
|
254
|
+
params: z.array(z.any()).optional(),
|
|
255
|
+
}),
|
|
235
256
|
),
|
|
236
257
|
async (c) => {
|
|
237
258
|
if (!(await c.var.inspector.accessors.isDbEnabled())) {
|
|
@@ -250,6 +271,24 @@ export function createActorInspectorRouter() {
|
|
|
250
271
|
return c.json({ error: (error as Error).message }, 500);
|
|
251
272
|
}
|
|
252
273
|
},
|
|
274
|
+
)
|
|
275
|
+
.post(
|
|
276
|
+
"/action",
|
|
277
|
+
sValidator(
|
|
278
|
+
"json",
|
|
279
|
+
z.object({
|
|
280
|
+
name: z.string(),
|
|
281
|
+
params: z.array(z.any()).optional(),
|
|
282
|
+
}),
|
|
283
|
+
),
|
|
284
|
+
async (c) => {
|
|
285
|
+
const { name, params } = c.req.valid("json");
|
|
286
|
+
const result = await c.var.inspector.accessors.executeAction(
|
|
287
|
+
name,
|
|
288
|
+
params,
|
|
289
|
+
);
|
|
290
|
+
return c.json({ result }, 200);
|
|
291
|
+
},
|
|
253
292
|
);
|
|
254
293
|
}
|
|
255
294
|
|
|
@@ -261,6 +300,7 @@ interface ActorInspectorAccessors {
|
|
|
261
300
|
getDb: () => Promise<InferDatabaseClient<AnyDatabaseProvider>>;
|
|
262
301
|
getRpcs: () => Promise<string[]>;
|
|
263
302
|
getConnections: () => Promise<Connection[]>;
|
|
303
|
+
executeAction: (name: string, params?: unknown[]) => Promise<unknown>;
|
|
264
304
|
}
|
|
265
305
|
|
|
266
306
|
interface ActorInspectorEmitterEvents {
|
package/src/inspector/config.ts
CHANGED
|
@@ -35,7 +35,8 @@ const defaultCors: CorsOptions = {
|
|
|
35
35
|
origin: (origin) => {
|
|
36
36
|
if (
|
|
37
37
|
defaultInspectorOrigins.includes(origin) ||
|
|
38
|
-
(origin.startsWith("https://") &&
|
|
38
|
+
(origin.startsWith("https://") &&
|
|
39
|
+
origin.endsWith("rivet-dev.vercel.app"))
|
|
39
40
|
) {
|
|
40
41
|
return origin;
|
|
41
42
|
} else {
|
package/src/inspector/manager.ts
CHANGED
|
@@ -21,7 +21,8 @@ export function createManagerInspectorRouter() {
|
|
|
21
21
|
return c.json({ message: "pong" }, 200);
|
|
22
22
|
})
|
|
23
23
|
.get("/actors", async (c) => {
|
|
24
|
-
const limit =
|
|
24
|
+
const limit =
|
|
25
|
+
Number.parseInt(c.req.query("limit") ?? "") || undefined;
|
|
25
26
|
const cursor = c.req.query("cursor") || undefined;
|
|
26
27
|
|
|
27
28
|
if (!limit || (limit && limit <= 0)) {
|
|
@@ -35,7 +36,10 @@ export function createManagerInspectorRouter() {
|
|
|
35
36
|
});
|
|
36
37
|
return c.json(actors, 200);
|
|
37
38
|
} catch (error) {
|
|
38
|
-
inspectorLogger().error({
|
|
39
|
+
inspectorLogger().error({
|
|
40
|
+
msg: "Failed to fetch actors",
|
|
41
|
+
error,
|
|
42
|
+
});
|
|
39
43
|
return c.json("Failed to fetch actors", 500);
|
|
40
44
|
}
|
|
41
45
|
})
|
|
@@ -67,7 +71,10 @@ export function createManagerInspectorRouter() {
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
interface ManagerInspectorAccessors {
|
|
70
|
-
getAllActors: (param: {
|
|
74
|
+
getAllActors: (param: {
|
|
75
|
+
cursor?: string;
|
|
76
|
+
limit: number;
|
|
77
|
+
}) => Promise<Actor[]>;
|
|
71
78
|
getActorById: (id: string) => Promise<Actor | null>;
|
|
72
79
|
getBuilds: () => Promise<Builds>;
|
|
73
80
|
createActor: (input: CreateInput) => Promise<Actor | null>;
|
package/src/inspector/utils.ts
CHANGED
|
@@ -66,7 +66,8 @@ export function getInspectorUrl(runConfig: RunnerConfigInput | undefined) {
|
|
|
66
66
|
url.searchParams.set("t", accessToken);
|
|
67
67
|
|
|
68
68
|
const overrideDefaultEndpoint =
|
|
69
|
-
runConfig?.inspector?.defaultEndpoint ??
|
|
69
|
+
runConfig?.inspector?.defaultEndpoint ??
|
|
70
|
+
runConfig.overrideServerAddress;
|
|
70
71
|
if (overrideDefaultEndpoint) {
|
|
71
72
|
url.searchParams.set("u", overrideDefaultEndpoint);
|
|
72
73
|
}
|
package/src/manager/driver.ts
CHANGED
|
@@ -43,7 +43,10 @@ export interface ManagerDriver {
|
|
|
43
43
|
|
|
44
44
|
extraStartupLog?: () => Record<string, unknown>;
|
|
45
45
|
|
|
46
|
-
modifyManagerRouter?: (
|
|
46
|
+
modifyManagerRouter?: (
|
|
47
|
+
registryConfig: RegistryConfig,
|
|
48
|
+
router: Hono,
|
|
49
|
+
) => void;
|
|
47
50
|
|
|
48
51
|
/**
|
|
49
52
|
* @internal
|
package/src/manager/gateway.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { MissingActorHeader, WebSocketsNotEnabled } from "@/actor/errors";
|
|
|
4
4
|
import type { Encoding, Transport } from "@/client/mod";
|
|
5
5
|
import {
|
|
6
6
|
HEADER_RIVET_ACTOR,
|
|
7
|
+
HEADER_RIVET_NAMESPACE,
|
|
7
8
|
HEADER_RIVET_TARGET,
|
|
8
9
|
WS_PROTOCOL_ACTOR,
|
|
9
10
|
WS_PROTOCOL_CONN_ID,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
WS_PROTOCOL_CONN_TOKEN,
|
|
12
13
|
WS_PROTOCOL_ENCODING,
|
|
13
14
|
WS_PROTOCOL_TARGET,
|
|
15
|
+
WS_PROTOCOL_TOKEN,
|
|
14
16
|
} from "@/common/actor-router-consts";
|
|
15
17
|
import { deconstructError, noopNext } from "@/common/utils";
|
|
16
18
|
import type { UniversalWebSocket, UpgradeWebSocketArgs } from "@/mod";
|
|
@@ -19,10 +21,123 @@ import { promiseWithResolvers, stringifyError } from "@/utils";
|
|
|
19
21
|
import type { ManagerDriver } from "./driver";
|
|
20
22
|
import { logger } from "./log";
|
|
21
23
|
|
|
24
|
+
interface ActorPathInfo {
|
|
25
|
+
actorId: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
remainingPath: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Handle path-based WebSocket routing
|
|
32
|
+
*/
|
|
33
|
+
async function handleWebSocketGatewayPathBased(
|
|
34
|
+
runConfig: RunnerConfig,
|
|
35
|
+
managerDriver: ManagerDriver,
|
|
36
|
+
c: HonoContext,
|
|
37
|
+
actorPathInfo: ActorPathInfo,
|
|
38
|
+
): Promise<Response> {
|
|
39
|
+
const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
|
|
40
|
+
if (!upgradeWebSocket) {
|
|
41
|
+
throw new WebSocketsNotEnabled();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// NOTE: Token validation implemented in EE
|
|
45
|
+
|
|
46
|
+
// Parse additional configuration from Sec-WebSocket-Protocol header
|
|
47
|
+
const protocols = c.req.header("sec-websocket-protocol");
|
|
48
|
+
let encodingRaw: string | undefined;
|
|
49
|
+
let connParamsRaw: string | undefined;
|
|
50
|
+
let connIdRaw: string | undefined;
|
|
51
|
+
let connTokenRaw: string | undefined;
|
|
52
|
+
|
|
53
|
+
if (protocols) {
|
|
54
|
+
const protocolList = protocols.split(",").map((p) => p.trim());
|
|
55
|
+
for (const protocol of protocolList) {
|
|
56
|
+
if (protocol.startsWith(WS_PROTOCOL_ENCODING)) {
|
|
57
|
+
encodingRaw = protocol.substring(WS_PROTOCOL_ENCODING.length);
|
|
58
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_PARAMS)) {
|
|
59
|
+
connParamsRaw = decodeURIComponent(
|
|
60
|
+
protocol.substring(WS_PROTOCOL_CONN_PARAMS.length),
|
|
61
|
+
);
|
|
62
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_ID)) {
|
|
63
|
+
connIdRaw = protocol.substring(WS_PROTOCOL_CONN_ID.length);
|
|
64
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_TOKEN)) {
|
|
65
|
+
connTokenRaw = protocol.substring(
|
|
66
|
+
WS_PROTOCOL_CONN_TOKEN.length,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger().debug({
|
|
73
|
+
msg: "proxying websocket to actor via path-based routing",
|
|
74
|
+
actorId: actorPathInfo.actorId,
|
|
75
|
+
path: actorPathInfo.remainingPath,
|
|
76
|
+
encoding: encodingRaw,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const encoding = encodingRaw || "json";
|
|
80
|
+
const connParams = connParamsRaw ? JSON.parse(connParamsRaw) : undefined;
|
|
81
|
+
|
|
82
|
+
return await managerDriver.proxyWebSocket(
|
|
83
|
+
c,
|
|
84
|
+
actorPathInfo.remainingPath,
|
|
85
|
+
actorPathInfo.actorId,
|
|
86
|
+
encoding as any, // Will be validated by driver
|
|
87
|
+
connParams,
|
|
88
|
+
connIdRaw,
|
|
89
|
+
connTokenRaw,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Handle path-based HTTP routing
|
|
95
|
+
*/
|
|
96
|
+
async function handleHttpGatewayPathBased(
|
|
97
|
+
managerDriver: ManagerDriver,
|
|
98
|
+
c: HonoContext,
|
|
99
|
+
actorPathInfo: ActorPathInfo,
|
|
100
|
+
): Promise<Response> {
|
|
101
|
+
// NOTE: Token validation implemented in EE
|
|
102
|
+
|
|
103
|
+
logger().debug({
|
|
104
|
+
msg: "proxying request to actor via path-based routing",
|
|
105
|
+
actorId: actorPathInfo.actorId,
|
|
106
|
+
path: actorPathInfo.remainingPath,
|
|
107
|
+
method: c.req.method,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Preserve all headers
|
|
111
|
+
const proxyHeaders = new Headers(c.req.raw.headers);
|
|
112
|
+
|
|
113
|
+
// Build the proxy request with the actor URL format
|
|
114
|
+
const proxyUrl = new URL(`http://actor${actorPathInfo.remainingPath}`);
|
|
115
|
+
|
|
116
|
+
const proxyRequest = new Request(proxyUrl, {
|
|
117
|
+
method: c.req.raw.method,
|
|
118
|
+
headers: proxyHeaders,
|
|
119
|
+
body: c.req.raw.body,
|
|
120
|
+
signal: c.req.raw.signal,
|
|
121
|
+
duplex: "half",
|
|
122
|
+
} as RequestInit);
|
|
123
|
+
|
|
124
|
+
return await managerDriver.proxyRequest(
|
|
125
|
+
c,
|
|
126
|
+
proxyRequest,
|
|
127
|
+
actorPathInfo.actorId,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
22
131
|
/**
|
|
23
132
|
* Provides an endpoint to connect to individual actors.
|
|
24
133
|
*
|
|
25
|
-
* Routes requests based
|
|
134
|
+
* Routes requests using either path-based routing or header-based routing:
|
|
135
|
+
*
|
|
136
|
+
* Path-based routing (checked first):
|
|
137
|
+
* - /gateway/actors/{actor_id}/tokens/{token}/route/{...path}
|
|
138
|
+
* - /gateway/actors/{actor_id}/route/{...path}
|
|
139
|
+
*
|
|
140
|
+
* Header-based routing (fallback):
|
|
26
141
|
* - WebSocket requests: Uses sec-websocket-protocol for routing (target.actor, actor.{id})
|
|
27
142
|
* - HTTP requests: Uses x-rivet-target and x-rivet-actor headers for routing
|
|
28
143
|
*/
|
|
@@ -47,6 +162,40 @@ export async function actorGateway(
|
|
|
47
162
|
}
|
|
48
163
|
}
|
|
49
164
|
|
|
165
|
+
// Include query string if present (needed for parseActorPath to preserve query params)
|
|
166
|
+
const pathWithQuery = c.req.url.includes("?")
|
|
167
|
+
? strippedPath + c.req.url.substring(c.req.url.indexOf("?"))
|
|
168
|
+
: strippedPath;
|
|
169
|
+
|
|
170
|
+
// First, check if this is an actor path-based route
|
|
171
|
+
const actorPathInfo = parseActorPath(pathWithQuery);
|
|
172
|
+
if (actorPathInfo) {
|
|
173
|
+
logger().debug({
|
|
174
|
+
msg: "routing using path-based actor routing",
|
|
175
|
+
actorPathInfo,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Check if this is a WebSocket upgrade request
|
|
179
|
+
const isWebSocket = c.req.header("upgrade") === "websocket";
|
|
180
|
+
|
|
181
|
+
if (isWebSocket) {
|
|
182
|
+
return await handleWebSocketGatewayPathBased(
|
|
183
|
+
runConfig,
|
|
184
|
+
managerDriver,
|
|
185
|
+
c,
|
|
186
|
+
actorPathInfo,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle regular HTTP requests
|
|
191
|
+
return await handleHttpGatewayPathBased(
|
|
192
|
+
managerDriver,
|
|
193
|
+
c,
|
|
194
|
+
actorPathInfo,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Fallback to header-based routing
|
|
50
199
|
// Check if this is a WebSocket upgrade request
|
|
51
200
|
if (c.req.header("upgrade") === "websocket") {
|
|
52
201
|
return await handleWebSocketGateway(
|
|
@@ -100,7 +249,9 @@ async function handleWebSocketGateway(
|
|
|
100
249
|
} else if (protocol.startsWith(WS_PROTOCOL_CONN_ID)) {
|
|
101
250
|
connIdRaw = protocol.substring(WS_PROTOCOL_CONN_ID.length);
|
|
102
251
|
} else if (protocol.startsWith(WS_PROTOCOL_CONN_TOKEN)) {
|
|
103
|
-
connTokenRaw = protocol.substring(
|
|
252
|
+
connTokenRaw = protocol.substring(
|
|
253
|
+
WS_PROTOCOL_CONN_TOKEN.length,
|
|
254
|
+
);
|
|
104
255
|
}
|
|
105
256
|
}
|
|
106
257
|
}
|
|
@@ -186,6 +337,113 @@ async function handleHttpGateway(
|
|
|
186
337
|
return await managerDriver.proxyRequest(c, proxyRequest, actorId);
|
|
187
338
|
}
|
|
188
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Parse actor routing information from path
|
|
342
|
+
* Matches patterns:
|
|
343
|
+
* - /gateway/actors/{actor_id}/tokens/{token}/route/{...path}
|
|
344
|
+
* - /gateway/actors/{actor_id}/route/{...path}
|
|
345
|
+
*/
|
|
346
|
+
export function parseActorPath(path: string): ActorPathInfo | null {
|
|
347
|
+
// Find query string position (everything from ? onwards, but before fragment)
|
|
348
|
+
const queryPos = path.indexOf("?");
|
|
349
|
+
const fragmentPos = path.indexOf("#");
|
|
350
|
+
|
|
351
|
+
// Extract query string (excluding fragment)
|
|
352
|
+
let queryString = "";
|
|
353
|
+
if (queryPos !== -1) {
|
|
354
|
+
if (fragmentPos !== -1 && queryPos < fragmentPos) {
|
|
355
|
+
queryString = path.slice(queryPos, fragmentPos);
|
|
356
|
+
} else {
|
|
357
|
+
queryString = path.slice(queryPos);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Extract base path (before query and fragment)
|
|
362
|
+
let basePath = path;
|
|
363
|
+
if (queryPos !== -1) {
|
|
364
|
+
basePath = path.slice(0, queryPos);
|
|
365
|
+
} else if (fragmentPos !== -1) {
|
|
366
|
+
basePath = path.slice(0, fragmentPos);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Check for double slashes (invalid path)
|
|
370
|
+
if (basePath.includes("//")) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Split the path into segments
|
|
375
|
+
const segments = basePath.split("/").filter((s) => s.length > 0);
|
|
376
|
+
|
|
377
|
+
// Check minimum required segments: gateway, actors, {actor_id}, route
|
|
378
|
+
if (segments.length < 4) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Verify the fixed segments
|
|
383
|
+
if (segments[0] !== "gateway" || segments[1] !== "actors") {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Check for empty actor_id
|
|
388
|
+
if (segments[2].length === 0) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const actorId = segments[2];
|
|
393
|
+
|
|
394
|
+
// Check for token or direct route
|
|
395
|
+
let token: string | undefined;
|
|
396
|
+
let remainingPathStartIdx: number;
|
|
397
|
+
|
|
398
|
+
if (
|
|
399
|
+
segments.length >= 6 &&
|
|
400
|
+
segments[3] === "tokens" &&
|
|
401
|
+
segments[5] === "route"
|
|
402
|
+
) {
|
|
403
|
+
// Pattern with token: /gateway/actors/{actor_id}/tokens/{token}/route/{...path}
|
|
404
|
+
// Check for empty token
|
|
405
|
+
if (segments[4].length === 0) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
token = segments[4];
|
|
409
|
+
remainingPathStartIdx = 6;
|
|
410
|
+
} else if (segments.length >= 4 && segments[3] === "route") {
|
|
411
|
+
// Pattern without token: /gateway/actors/{actor_id}/route/{...path}
|
|
412
|
+
token = undefined;
|
|
413
|
+
remainingPathStartIdx = 4;
|
|
414
|
+
} else {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Calculate the position in the original path where remaining path starts
|
|
419
|
+
let prefixLen = 0;
|
|
420
|
+
for (let i = 0; i < remainingPathStartIdx; i++) {
|
|
421
|
+
prefixLen += 1 + segments[i].length; // +1 for the slash
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Extract the remaining path preserving trailing slashes
|
|
425
|
+
let remainingBase: string;
|
|
426
|
+
if (prefixLen < basePath.length) {
|
|
427
|
+
remainingBase = basePath.slice(prefixLen);
|
|
428
|
+
} else {
|
|
429
|
+
remainingBase = "/";
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Ensure remaining path starts with /
|
|
433
|
+
let remainingPath: string;
|
|
434
|
+
if (remainingBase.length === 0 || !remainingBase.startsWith("/")) {
|
|
435
|
+
remainingPath = `/${remainingBase}${queryString}`;
|
|
436
|
+
} else {
|
|
437
|
+
remainingPath = `${remainingBase}${queryString}`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
actorId,
|
|
442
|
+
token,
|
|
443
|
+
remainingPath,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
189
447
|
/**
|
|
190
448
|
* Creates a WebSocket proxy for test endpoints that forwards messages between server and client WebSockets
|
|
191
449
|
*/
|
|
@@ -212,13 +470,20 @@ export async function createTestWebSocketProxy(
|
|
|
212
470
|
// Wait for ws to open
|
|
213
471
|
await new Promise<void>((resolve, reject) => {
|
|
214
472
|
const onOpen = () => {
|
|
215
|
-
logger().debug({
|
|
473
|
+
logger().debug({
|
|
474
|
+
msg: "test websocket connection to actor opened",
|
|
475
|
+
});
|
|
216
476
|
resolve();
|
|
217
477
|
};
|
|
218
478
|
const onError = (error: any) => {
|
|
219
|
-
logger().error({
|
|
479
|
+
logger().error({
|
|
480
|
+
msg: "test websocket connection failed",
|
|
481
|
+
error,
|
|
482
|
+
});
|
|
220
483
|
reject(
|
|
221
|
-
new Error(
|
|
484
|
+
new Error(
|
|
485
|
+
`Failed to open WebSocket: ${error.message || error}`,
|
|
486
|
+
),
|
|
222
487
|
);
|
|
223
488
|
serverWsReject();
|
|
224
489
|
};
|
|
@@ -327,7 +592,8 @@ export async function createTestWebSocketProxy(
|
|
|
327
592
|
logger().debug({
|
|
328
593
|
msg: "clientWs info",
|
|
329
594
|
constructor: clientWs.constructor.name,
|
|
330
|
-
hasAddEventListener:
|
|
595
|
+
hasAddEventListener:
|
|
596
|
+
typeof clientWs.addEventListener === "function",
|
|
331
597
|
readyState: clientWs.readyState,
|
|
332
598
|
});
|
|
333
599
|
|
|
@@ -341,7 +607,9 @@ export async function createTestWebSocketProxy(
|
|
|
341
607
|
isArrayBuffer: evt.data instanceof ArrayBuffer,
|
|
342
608
|
dataConstructor: evt.data?.constructor?.name,
|
|
343
609
|
dataStr:
|
|
344
|
-
typeof evt.data === "string"
|
|
610
|
+
typeof evt.data === "string"
|
|
611
|
+
? evt.data.substring(0, 100)
|
|
612
|
+
: undefined,
|
|
345
613
|
});
|
|
346
614
|
|
|
347
615
|
// Forward messages from server websocket to client websocket
|
|
@@ -369,7 +637,9 @@ export async function createTestWebSocketProxy(
|
|
|
369
637
|
msg: "sending data directly",
|
|
370
638
|
dataType: typeof evt.data,
|
|
371
639
|
dataLength:
|
|
372
|
-
typeof evt.data === "string"
|
|
640
|
+
typeof evt.data === "string"
|
|
641
|
+
? evt.data.length
|
|
642
|
+
: undefined,
|
|
373
643
|
});
|
|
374
644
|
clientWs.send(evt.data);
|
|
375
645
|
}
|
|
@@ -76,7 +76,9 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
76
76
|
isString: typeof data === "string",
|
|
77
77
|
isArrayBuffer: data instanceof ArrayBuffer,
|
|
78
78
|
dataStr:
|
|
79
|
-
typeof data === "string"
|
|
79
|
+
typeof data === "string"
|
|
80
|
+
? data.substring(0, 100)
|
|
81
|
+
: "<non-string>",
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
if (typeof data === "string") {
|
|
@@ -99,8 +101,7 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
99
101
|
}
|
|
100
102
|
} else if (data instanceof Blob) {
|
|
101
103
|
// Convert Blob to ArrayBuffer
|
|
102
|
-
data
|
|
103
|
-
.arrayBuffer()
|
|
104
|
+
data.arrayBuffer()
|
|
104
105
|
.then((buffer) => {
|
|
105
106
|
(this.#ws as any).send(buffer);
|
|
106
107
|
})
|
|
@@ -109,7 +110,11 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
109
110
|
msg: "failed to convert blob to arraybuffer",
|
|
110
111
|
error,
|
|
111
112
|
});
|
|
112
|
-
this.#fireEvent("error", {
|
|
113
|
+
this.#fireEvent("error", {
|
|
114
|
+
type: "error",
|
|
115
|
+
target: this,
|
|
116
|
+
error,
|
|
117
|
+
});
|
|
113
118
|
});
|
|
114
119
|
} else {
|
|
115
120
|
// Try to convert to string as a fallback
|
|
@@ -128,7 +133,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
close(code = 1000, reason = ""): void {
|
|
131
|
-
if (
|
|
136
|
+
if (
|
|
137
|
+
this.readyState === this.CLOSING ||
|
|
138
|
+
this.readyState === this.CLOSED
|
|
139
|
+
) {
|
|
132
140
|
return;
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -260,7 +268,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
260
268
|
try {
|
|
261
269
|
listener(event);
|
|
262
270
|
} catch (error) {
|
|
263
|
-
logger().error({
|
|
271
|
+
logger().error({
|
|
272
|
+
msg: `error in ${type} event listener`,
|
|
273
|
+
error,
|
|
274
|
+
});
|
|
264
275
|
}
|
|
265
276
|
}
|
|
266
277
|
}
|
|
@@ -272,7 +283,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
272
283
|
try {
|
|
273
284
|
this.#onopen(event);
|
|
274
285
|
} catch (error) {
|
|
275
|
-
logger().error({
|
|
286
|
+
logger().error({
|
|
287
|
+
msg: "error in onopen handler",
|
|
288
|
+
error,
|
|
289
|
+
});
|
|
276
290
|
}
|
|
277
291
|
}
|
|
278
292
|
break;
|
|
@@ -281,7 +295,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
281
295
|
try {
|
|
282
296
|
this.#onclose(event);
|
|
283
297
|
} catch (error) {
|
|
284
|
-
logger().error({
|
|
298
|
+
logger().error({
|
|
299
|
+
msg: "error in onclose handler",
|
|
300
|
+
error,
|
|
301
|
+
});
|
|
285
302
|
}
|
|
286
303
|
}
|
|
287
304
|
break;
|
|
@@ -290,7 +307,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
290
307
|
try {
|
|
291
308
|
this.#onerror(event);
|
|
292
309
|
} catch (error) {
|
|
293
|
-
logger().error({
|
|
310
|
+
logger().error({
|
|
311
|
+
msg: "error in onerror handler",
|
|
312
|
+
error,
|
|
313
|
+
});
|
|
294
314
|
}
|
|
295
315
|
}
|
|
296
316
|
break;
|
|
@@ -299,7 +319,10 @@ export class HonoWebSocketAdapter implements UniversalWebSocket {
|
|
|
299
319
|
try {
|
|
300
320
|
this.#onmessage(event);
|
|
301
321
|
} catch (error) {
|
|
302
|
-
logger().error({
|
|
322
|
+
logger().error({
|
|
323
|
+
msg: "error in onmessage handler",
|
|
324
|
+
error,
|
|
325
|
+
});
|
|
303
326
|
}
|
|
304
327
|
}
|
|
305
328
|
break;
|