Skip to content
This repository was archived by the owner on Oct 22, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,3 @@ Always include a README.md. The `README.md` should always follow this structure:
## Test Notes

- Using setTimeout in tests & test actors will not work unless you call `await waitFor(driverTestConfig, <ts>)`
- Do not use setTimeout in tests or in actors used in tests unless you explictily use `await vi.advanceTimersByTimeAsync(time)`
8 changes: 7 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
{
"$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
"files": {
"includes": ["**/*.json", "**/*.ts", "**/*.js", "!examples/snippets/**"],
"includes": [
"**/*.json",
"**/*.ts",
"**/*.js",
"!examples/snippets/**",
"!clients/openapi/openapi.json"
],
"ignoreUnknown": true
},
"vcs": {
Expand Down
6 changes: 4 additions & 2 deletions clients/openapi/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"example": "actor-123"
}
},
"required": ["i"]
"required": [
"i"
]
},
"ResolveQuery": {
"type": "object",
Expand Down Expand Up @@ -676,4 +678,4 @@
}
}
}
}
}
12 changes: 3 additions & 9 deletions packages/core/fixtures/driver-test-suite/action-timeout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ export const shortTimeoutActor = actor({
onAuth: () => {},
state: { value: 0 },
options: {
action: {
timeout: 50, // 50ms timeout
},
actionTimeout: 50, // 50ms timeout
},
actions: {
quickAction: async (c) => {
Expand All @@ -26,9 +24,7 @@ export const longTimeoutActor = actor({
onAuth: () => {},
state: { value: 0 },
options: {
action: {
timeout: 200, // 200ms timeout
},
actionTimeout: 200, // 200ms timeout
},
actions: {
delayedAction: async (c) => {
Expand Down Expand Up @@ -56,9 +52,7 @@ export const syncTimeoutActor = actor({
onAuth: () => {},
state: { value: 0 },
options: {
action: {
timeout: 50, // 50ms timeout
},
actionTimeout: 50, // 50ms timeout
},
actions: {
syncAction: (c) => {
Expand Down
6 changes: 2 additions & 4 deletions packages/core/fixtures/driver-test-suite/conn-liveness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export const connLivenessActor = actor({
acceptingConnections: true,
},
options: {
lifecycle: {
connectionLivenessInterval: 5_000,
connectionLivenessTimeout: 2_500,
},
connectionLivenessInterval: 5_000,
connectionLivenessTimeout: 2_500,
},
onConnect: (c, conn) => {
if (!c.state.acceptingConnections) {
Expand Down
9 changes: 2 additions & 7 deletions packages/core/fixtures/driver-test-suite/error-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ export const errorHandlingActor = actor({
},
},
options: {
// Set a short timeout for this actor's actions
action: {
timeout: 500, // 500ms timeout for actions
},
actionTimeout: 500, // 500ms timeout for actions
},
});

Expand All @@ -90,8 +87,6 @@ export const customTimeoutActor = actor({
},
},
options: {
action: {
timeout: 200, // 200ms timeout
},
actionTimeout: 200, // 200ms timeout
},
});
13 changes: 13 additions & 0 deletions packages/core/fixtures/driver-test-suite/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ import {
import { requestAccessActor } from "./request-access";
import { requestAccessAuthActor } from "./request-access-auth";
import { scheduled } from "./scheduled";
import {
sleep,
sleepWithLongRpc,
sleepWithNoSleepOption,
sleepWithRawHttp,
sleepWithRawWebSocket,
} from "./sleep";
import {
driverCtxActor,
dynamicVarActor,
Expand All @@ -68,6 +75,12 @@ export const registry = setup({
counterWithLifecycle,
// From scheduled.ts
scheduled,
// From sleep.ts
sleep,
sleepWithLongRpc,
sleepWithRawHttp,
sleepWithRawWebSocket,
sleepWithNoSleepOption,
// From error-handling.ts
errorHandlingActor,
customTimeoutActor,
Expand Down
186 changes: 186 additions & 0 deletions packages/core/fixtures/driver-test-suite/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { actor, type UniversalWebSocket } from "@rivetkit/core";

export const SLEEP_TIMEOUT = 500;

export const sleep = actor({
onAuth: () => {},
state: { startCount: 0, sleepCount: 0 },
onStart: (c) => {
c.state.startCount += 1;
},
onStop: (c) => {
c.state.sleepCount += 1;
},
actions: {
triggerSleep: (c) => {
c.sleep();
},
getCounts: (c) => {
return { startCount: c.state.startCount, sleepCount: c.state.sleepCount };
},
setAlarm: async (c, duration: number) => {
await c.schedule.after(duration, "onAlarm");
},
onAlarm: (c) => {
c.log.info("alarm called");
},
},
options: {
sleepTimeout: SLEEP_TIMEOUT,
},
});

export const sleepWithLongRpc = actor({
onAuth: () => {},
state: { startCount: 0, sleepCount: 0 },
createVars: () => ({}) as { longRunningResolve: PromiseWithResolvers<void> },
onStart: (c) => {
c.state.startCount += 1;
},
onStop: (c) => {
c.state.sleepCount += 1;
},
actions: {
getCounts: (c) => {
return { startCount: c.state.startCount, sleepCount: c.state.sleepCount };
},
longRunningRpc: async (c) => {
c.log.info("starting long running rpc");
c.vars.longRunningResolve = Promise.withResolvers();
c.broadcast("waiting");
await c.vars.longRunningResolve.promise;
c.log.info("finished long running rpc");
},
finishLongRunningRpc: (c) => c.vars.longRunningResolve?.resolve(),
},
options: {
sleepTimeout: SLEEP_TIMEOUT,
},
});

export const sleepWithRawHttp = actor({
onAuth: () => {},
state: { startCount: 0, sleepCount: 0, requestCount: 0 },
onStart: (c) => {
c.state.startCount += 1;
},
onStop: (c) => {
c.state.sleepCount += 1;
},
onFetch: async (c, request) => {
c.state.requestCount += 1;
const url = new URL(request.url);

if (url.pathname === "/long-request") {
const duration = parseInt(url.searchParams.get("duration") || "1000");
c.log.info("starting long fetch request", { duration });
await new Promise((resolve) => setTimeout(resolve, duration));
c.log.info("finished long fetch request");
return new Response(JSON.stringify({ completed: true }), {
headers: { "Content-Type": "application/json" },
});
}

return new Response("Not Found", { status: 404 });
},
actions: {
getCounts: (c) => {
return {
startCount: c.state.startCount,
sleepCount: c.state.sleepCount,
requestCount: c.state.requestCount,
};
},
},
options: {
sleepTimeout: SLEEP_TIMEOUT,
},
});

export const sleepWithRawWebSocket = actor({
onAuth: () => {},
state: { startCount: 0, sleepCount: 0, connectionCount: 0 },
onStart: (c) => {
c.state.startCount += 1;
},
onStop: (c) => {
c.state.sleepCount += 1;
},
onWebSocket: (c, websocket: UniversalWebSocket, opts) => {
c.state.connectionCount += 1;
c.log.info("websocket connected", {
connectionCount: c.state.connectionCount,
});

websocket.send(
JSON.stringify({
type: "connected",
connectionCount: c.state.connectionCount,
}),
);

websocket.addEventListener("message", (event: any) => {
const data = event.data;
if (typeof data === "string") {
try {
const parsed = JSON.parse(data);
if (parsed.type === "getCounts") {
websocket.send(
JSON.stringify({
type: "counts",
startCount: c.state.startCount,
sleepCount: c.state.sleepCount,
connectionCount: c.state.connectionCount,
}),
);
} else if (parsed.type === "keepAlive") {
// Just acknowledge to keep connection alive
websocket.send(JSON.stringify({ type: "ack" }));
}
} catch {
// Echo non-JSON messages
websocket.send(data);
}
}
});

websocket.addEventListener("close", () => {
c.state.connectionCount -= 1;
c.log.info("websocket disconnected", {
connectionCount: c.state.connectionCount,
});
});
},
actions: {
getCounts: (c) => {
return {
startCount: c.state.startCount,
sleepCount: c.state.sleepCount,
connectionCount: c.state.connectionCount,
};
},
},
options: {
sleepTimeout: SLEEP_TIMEOUT,
},
});

export const sleepWithNoSleepOption = actor({
onAuth: () => {},
state: { startCount: 0, sleepCount: 0 },
onStart: (c) => {
c.state.startCount += 1;
},
onStop: (c) => {
c.state.sleepCount += 1;
},
actions: {
getCounts: (c) => {
return { startCount: c.state.startCount, sleepCount: c.state.sleepCount };
},
},
options: {
sleepTimeout: SLEEP_TIMEOUT,
noSleep: true,
},
});
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
"on-change": "^5.0.1",
"p-retry": "^6.2.1",
"zod": "^3.25.76",
"@rivetkit/engine-runner": "https://pkg.pr.new/rivet-gg/rivet/@rivetkit/engine-runner@f1c054d"
"@rivetkit/engine-runner": "https://pkg.pr.new/rivet-gg/engine/@rivetkit/engine-runner@664a377"
},
"devDependencies": {
"@hono/node-server": "^1.18.2",
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/actor/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,15 @@ export class ActionContext<
runInBackground(promise: Promise<void>): void {
this.#actorContext.runInBackground(promise);
}

/**
* Forces the actor to sleep.
*
* Not supported on all drivers.
*
* @experimental
*/
sleep() {
this.#actorContext.sleep();
}
}
Loading
Loading