Skip to content

Commit 173de9d

Browse files
committed
feat: serverless
1 parent 49ed6fb commit 173de9d

File tree

14 files changed

+178
-51
lines changed

14 files changed

+178
-51
lines changed

examples/next-js/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"private": true,
55
"scripts": {
66
"dev": "next dev",
7+
"build": "next build",
78
"start": "next start",
89
"lint": "next lint"
910
},
@@ -20,4 +21,4 @@
2021
"@types/react": "^19",
2122
"@types/react-dom": "^19"
2223
}
23-
}
24+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { toNextHandler } from "@rivetkit/next-js";
22
import { registry } from "@/rivet/registry";
33

4-
export const { GET, POST, HEAD, PATCH, OPTIONS } = toNextHandler(registry);
4+
export const { GET, POST, PUT, PATCH, HEAD, OPTIONS } = toNextHandler(registry);

packages/next-js/src/mod.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,22 @@ export const toNextHandler = (
99

1010
const { fetch } = registry.start(inputConfig);
1111

12+
const fetchWrapper = async (request: Request, { params }: { params: Promise<{ all: string[] }> }) => {
13+
const { all } = await params;
14+
15+
const newUrl = new URL(request.url);
16+
newUrl.pathname = all.join('/');
17+
const newReq = new Request(newUrl, request);
18+
19+
return await fetch(newReq);
20+
};
21+
1222
return {
13-
GET: fetch,
14-
POST: fetch,
15-
PATCH: fetch,
16-
HEAD: fetch,
17-
OPTIONS: fetch,
23+
GET: fetchWrapper,
24+
POST: fetchWrapper,
25+
PUT: fetchWrapper,
26+
PATCH: fetchWrapper,
27+
HEAD: fetchWrapper,
28+
OPTIONS: fetchWrapper,
1829
};
1930
};

packages/rivetkit/src/actor/driver.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type * as protocol from "@/schemas/client-protocol/mod";
77
import type { AnyConn, ConnectionDriver } from "./connection";
88
import type { GenericConnGlobalState } from "./generic-conn-driver";
99
import type { AnyActorInstance } from "./instance";
10+
import { Hono, MiddlewareHandler } from "hono";
1011

1112
export type ConnectionDriversMap = Record<ConnectionDriver, ConnDriver>;
1213

@@ -45,6 +46,8 @@ export interface ActorDriver {
4546
sleep?(actorId: string): Promise<void>;
4647

4748
shutdown?(immediate: boolean): Promise<void>;
49+
50+
modifyManagerRouter?: (router: Hono, cors: MiddlewareHandler) => void;
4851
}
4952

5053
export enum ConnectionReadyState {

packages/rivetkit/src/common/versioned-data.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface VersionedDataConfig<T> {
1313
}
1414

1515
export class VersionedDataHandler<T> {
16-
constructor(private config: VersionedDataConfig<T>) {}
16+
constructor(private config: VersionedDataConfig<T>) { }
1717

1818
serializeWithEmbeddedVersion(data: T): Uint8Array {
1919
const versioned: VersionedData<Uint8Array> = {
@@ -63,8 +63,8 @@ export class VersionedDataHandler<T> {
6363
}
6464

6565
private embedVersion(data: VersionedData<Uint8Array>): Uint8Array {
66-
const versionBytes = new Uint8Array(4);
67-
new DataView(versionBytes.buffer).setUint32(0, data.version, true);
66+
const versionBytes = new Uint8Array(2);
67+
new DataView(versionBytes.buffer).setUint16(0, data.version, true);
6868

6969
const result = new Uint8Array(versionBytes.length + data.data.length);
7070
result.set(versionBytes);
@@ -74,15 +74,15 @@ export class VersionedDataHandler<T> {
7474
}
7575

7676
private extractVersion(bytes: Uint8Array): VersionedData<Uint8Array> {
77-
if (bytes.length < 4) {
77+
if (bytes.length < 2) {
7878
throw new Error("Invalid versioned data: too short");
7979
}
8080

81-
const version = new DataView(bytes.buffer, bytes.byteOffset).getUint32(
81+
const version = new DataView(bytes.buffer, bytes.byteOffset).getUint16(
8282
0,
8383
true,
8484
);
85-
const data = bytes.slice(4);
85+
const data = bytes.slice(2);
8686

8787
return { version, data };
8888
}

packages/rivetkit/src/driver-test-suite/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ export async function createTestRuntime(
197197
registry.config,
198198
config,
199199
managerDriver,
200+
undefined,
200201
false,
201202
);
202203

packages/rivetkit/src/drivers/engine/actor-driver.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import type { RunConfig } from "@/registry/run-config";
4040
import type { Config } from "./config";
4141
import { KEYS } from "./kv";
4242
import { logger } from "./log";
43+
import { Hono, MiddlewareHandler } from "hono";
44+
import { streamSSE } from "hono/streaming";
4345

4446
interface ActorHandler {
4547
actor?: AnyActorInstance;
@@ -61,6 +63,9 @@ export class EngineActorDriver implements ActorDriver {
6163
#actorRouter: ActorRouter;
6264
#version: number = 1; // Version for the runner protocol
6365

66+
#runnerStarted: PromiseWithResolvers<undefined> = Promise.withResolvers();
67+
#runnerStopped: PromiseWithResolvers<undefined> = Promise.withResolvers();
68+
6469
constructor(
6570
registryConfig: RegistryConfig,
6671
runConfig: RunConfig,
@@ -108,6 +113,8 @@ export class EngineActorDriver implements ActorDriver {
108113
runnerName: this.#config.runnerName,
109114
});
110115
}
116+
117+
this.#runnerStarted.resolve(undefined);
111118
},
112119
onDisconnected: () => {
113120
logger().warn({
@@ -117,7 +124,9 @@ export class EngineActorDriver implements ActorDriver {
117124
});
118125
hasDisconnected = true;
119126
},
120-
onShutdown: () => {},
127+
onShutdown: () => {
128+
this.#runnerStopped.resolve(undefined);
129+
},
121130
fetch: this.#runnerFetch.bind(this),
122131
websocket: this.#runnerWebSocket.bind(this),
123132
onActorStart: this.#runnerOnActorStart.bind(this),
@@ -367,4 +376,22 @@ export class EngineActorDriver implements ActorDriver {
367376
logger().info({ msg: "stopping engine actor driver" });
368377
await this.#runner.shutdown(immediate);
369378
}
379+
380+
modifyManagerRouter(router: Hono, cors: MiddlewareHandler) {
381+
// Serverless start endpoint
382+
router.get("/start", cors, async (c) => {
383+
await this.#runnerStarted.promise;
384+
385+
return streamSSE(c, async (stream) => {
386+
// Runner id should be set if the runner started
387+
stream.writeSSE({ data: this.#runner.runnerId! });
388+
389+
await this.#runnerStopped.promise;
390+
});
391+
});
392+
393+
router.get("/health", cors, (c) => {
394+
return c.text("ok");
395+
});
396+
}
370397
}

packages/rivetkit/src/drivers/file-system/actor.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type {
99
import type { RegistryConfig, RunConfig } from "@/mod";
1010
import { bufferToArrayBuffer } from "@/utils";
1111
import type { FileSystemGlobalState } from "./global-state";
12+
import { Hono, MiddlewareHandler } from "hono";
13+
import { streamSSE } from "hono/streaming";
1214

1315
export type ActorDriverContext = Record<never, never>;
1416

packages/rivetkit/src/manager/router.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
loggerMiddleware,
2121
} from "@/common/router";
2222
import { deconstructError, noopNext } from "@/common/utils";
23-
import { HEADER_ACTOR_ID } from "@/driver-helpers/mod";
23+
import { ActorDriver, HEADER_ACTOR_ID } from "@/driver-helpers/mod";
2424
import type {
2525
TestInlineDriverCallRequest,
2626
TestInlineDriverCallResponse,
@@ -54,10 +54,10 @@ function buildOpenApiResponses<T>(schema: T, validateBody: boolean) {
5454
description: "Success",
5555
content: validateBody
5656
? {
57-
"application/json": {
58-
schema,
59-
},
60-
}
57+
"application/json": {
58+
schema,
59+
},
60+
}
6161
: {},
6262
},
6363
400: {
@@ -73,6 +73,7 @@ export function createManagerRouter(
7373
registryConfig: RegistryConfig,
7474
runConfig: RunConfig,
7575
managerDriver: ManagerDriver,
76+
actorDriver: ActorDriver | undefined,
7677
validateBody: boolean,
7778
): { router: Hono; openapi: OpenAPIHono } {
7879
const router = new OpenAPIHono({ strict: false }).basePath(
@@ -151,6 +152,7 @@ export function createManagerRouter(
151152
method: c.req.raw.method,
152153
headers: proxyHeaders,
153154
body: c.req.raw.body,
155+
duplex: "half",
154156
signal: c.req.raw.signal,
155157
});
156158

@@ -211,10 +213,10 @@ export function createManagerRouter(
211213
body: {
212214
content: validateBody
213215
? {
214-
"application/json": {
215-
schema: ActorsGetOrCreateByIdRequestSchema,
216-
},
217-
}
216+
"application/json": {
217+
schema: ActorsGetOrCreateByIdRequestSchema,
218+
},
219+
}
218220
: {},
219221
},
220222
},
@@ -323,10 +325,10 @@ export function createManagerRouter(
323325
body: {
324326
content: validateBody
325327
? {
326-
"application/json": {
327-
schema: ActorsCreateRequestSchema,
328-
},
329-
}
328+
"application/json": {
329+
schema: ActorsCreateRequestSchema,
330+
},
331+
}
330332
: {},
331333
},
332334
},
@@ -500,6 +502,7 @@ export function createManagerRouter(
500502
method: c.req.method,
501503
headers: c.req.raw.headers,
502504
body: c.req.raw.body,
505+
duplex: "half",
503506
}),
504507
);
505508

@@ -532,6 +535,8 @@ export function createManagerRouter(
532535
router as unknown as Hono,
533536
);
534537

538+
actorDriver?.modifyManagerRouter?.(router as unknown as Hono, cors);
539+
535540
if (runConfig.inspector?.enabled) {
536541
if (!managerDriver.inspector) {
537542
throw new Unsupported("inspector");
@@ -598,9 +603,9 @@ async function createTestWebSocketProxy(
598603
onOpen: (_evt, serverWs) => {
599604
serverWs.close(1011, "Failed to establish connection");
600605
},
601-
onMessage: () => {},
602-
onError: () => {},
603-
onClose: () => {},
606+
onMessage: () => { },
607+
onError: () => { },
608+
onClose: () => { },
604609
};
605610
}
606611

packages/rivetkit/src/registry/mod.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,6 @@ export class Registry<A extends RegistryActors> {
7676

7777
// Create router
7878
const managerDriver = driver.manager(this.#config, config);
79-
const { router: hono } = createManagerRouter(
80-
this.#config,
81-
config,
82-
managerDriver,
83-
false,
84-
);
8579

8680
// Create client
8781
const client = createClientWithDriver<this>(managerDriver, config);
@@ -115,17 +109,24 @@ export class Registry<A extends RegistryActors> {
115109

116110
// HACK: We need to find a better way to let the driver itself decide when to start the actor driver
117111
// Create runner
118-
//
119-
// Even though we do not use the return value, this is required to start the code that will handle incoming actors
112+
let actorDriver;
120113
if (!config.disableActorDriver) {
121-
const _actorDriver = driver.actor(
114+
actorDriver = driver.actor(
122115
this.#config,
123116
config,
124117
managerDriver,
125118
client,
126119
);
127120
}
128121

122+
const { router: hono } = createManagerRouter(
123+
this.#config,
124+
config,
125+
managerDriver,
126+
actorDriver,
127+
false,
128+
);
129+
129130
// Start server
130131
if (!config.disableServer) {
131132
(async () => {

0 commit comments

Comments
 (0)