Skip to content
Draft
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
53 changes: 52 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",

Check notice on line 2 in biome.json

View workflow job for this annotation

GitHub Actions / quality

deserialize

The configuration schema version does not match the CLI version 2.3.2
"files": {
"includes": [
"**/*.js",
Expand Down Expand Up @@ -41,5 +41,56 @@
"noExplicitAny": "off"
}
}
}
},
"overrides": [
{
"includes": [
"rivetkit-typescript/packages/rivetkit/src/**/*",
"!rivetkit-typescript/packages/rivetkit/src/test/**/*"
],
"linter": {
"rules": {
"style": {
"noRestrictedImports": {
"level": "error",
"options": {
"paths": {
"node:crypto": "Use '@/utils/node' getNodeCrypto() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:fs": "Use '@/utils/node' getNodeFsSync() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:fs/promises": "Use '@/utils/node' getNodeFs() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:path": "Use '@/utils/node' getNodePath() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:os": "Use '@/utils/node' getNodeOs() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:child_process": "Use '@/utils/node' getNodeChildProcess() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:stream": "Use '@/utils/node' getNodeStream() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:net": "Use '@/utils/node' instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"node:url": "Use '@/utils/node' instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"crypto": "Use '@/utils/node' getNodeCrypto() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"fs": "Use '@/utils/node' getNodeFsSync() or getNodeFs() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"fs/promises": "Use '@/utils/node' getNodeFs() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"path": "Use '@/utils/node' getNodePath() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"os": "Use '@/utils/node' getNodeOs() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"child_process": "Use '@/utils/node' getNodeChildProcess() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"stream": "Use '@/utils/node' getNodeStream() instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"net": "Use '@/utils/node' instead. Direct Node.js imports are only allowed in src/utils/node.ts",
"url": "Use '@/utils/node' instead. Direct Node.js imports are only allowed in src/utils/node.ts"
}
}
}
}
}
}
},
{
"includes": [
"rivetkit-typescript/packages/rivetkit/src/utils/node.ts"
],
"linter": {
"rules": {
"style": {
"noRestrictedImports": "off"
}
}
}
}
]
}
27 changes: 5 additions & 22 deletions engine/package.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
{
"name": "@rivetkit/engine",
"private": true,
"version": "1.0.0",
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "[email protected]",
"scripts": {
"start": "npx turbo watch build",
"build": "npx turbo build",
"test": "npx turbo test",
"test:watch": "npx turbo watch test",
"check-types": "npx turbo check-types",
"fmt": "pnpm biome check --write --diagnostic-level=error ."
},
"devDependencies": {
"@bare-ts/tools": "0.15.0",
"@biomejs/biome": "^2.2.3",
"lefthook": "^1.12.4",
"tsup": "^8.5.0",
"turbo": "^2.5.6",
"typescript": "^5.9.2"
},
"dependencies": {
"@sentry/vite-plugin": "^2.23.1"
},
"resolutions": {
"rivetkit": "workspace:*",
"@clerk/shared": "3.27.1"
"@vbare/compiler": "^0.0.3"
}
}
8 changes: 7 additions & 1 deletion engine/sdks/typescript/runner-protocol/src/index.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions engine/sdks/typescript/runner/src/tunnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ export class Tunnel {
existing.actorId = req.actorId;
} else {
this.#actorPendingRequests.set(requestIdStr, {
resolve: () => { },
reject: () => { },
resolve: () => {},
reject: () => {},
streamController: controller,
actorId: req.actorId,
});
Expand Down Expand Up @@ -506,7 +506,7 @@ export class Tunnel {
const dataBuffer =
typeof data === "string"
? (new TextEncoder().encode(data)
.buffer as ArrayBuffer)
.buffer as ArrayBuffer)
: data;

this.#sendMessage(requestId, {
Expand Down
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
packages:
- engine
- engine/docker/template
- engine/sdks/typescript/api-full
- engine/sdks/typescript/runner
Expand Down
5 changes: 5 additions & 0 deletions rivetkit-typescript/packages/rivetkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@
"build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/common/websocket.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/driver-test-suite/mod.ts src/test/mod.ts src/inspector/mod.ts",
"build:schema": "./scripts/compile-bare.ts compile schemas/client-protocol/v1.bare -o dist/schemas/client-protocol/v1.ts && ./scripts/compile-bare.ts compile schemas/file-system-driver/v1.bare -o dist/schemas/file-system-driver/v1.ts && ./scripts/compile-bare.ts compile schemas/actor-persist/v1.bare -o dist/schemas/actor-persist/v1.ts",
"check-types": "tsc --noEmit",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"format": "biome format .",
"format:write": "biome format --write .",
"test": "vitest run",
"test:watch": "vitest",
"dump-openapi": "tsx scripts/dump-openapi.ts"
Expand All @@ -176,6 +180,7 @@
},
"devDependencies": {
"@bare-ts/tools": "^0.13.0",
"@biomejs/biome": "^2.2.3",
"@hono/node-server": "^1.18.2",
"@hono/node-ws": "^1.1.1",
"@types/invariant": "^2",
Expand Down
6 changes: 3 additions & 3 deletions rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import {
import { type RunnerConfig, RunnerConfigSchema } from "@/registry/run-config";
import { VERSION } from "@/utils";

function main() {
async function main() {
const registryConfig: RegistryConfig = RegistryConfigSchema.parse({
use: {},
});
const registry = setup(registryConfig);

const driverConfig: RunnerConfig = RunnerConfigSchema.parse({
driver: createFileSystemOrMemoryDriver(false),
driver: await createFileSystemOrMemoryDriver(false),
getUpgradeWebSocket: () => () => unimplemented(),
inspector: {
enabled: false,
Expand Down Expand Up @@ -70,7 +70,7 @@ function main() {
"rivetkit-openapi",
"openapi.json",
);
fs.writeFile(outputPath, JSON.stringify(openApiDoc, null, 2));
await fs.writeFile(outputPath, JSON.stringify(openApiDoc, null, 2));
console.log("Dumped OpenAPI to", outputPath);
}

Expand Down
6 changes: 4 additions & 2 deletions rivetkit-typescript/packages/rivetkit/src/drivers/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import type { DriverConfig, RunnerConfig } from "@/registry/run-config";
/**
* Chooses the appropriate driver based on the run configuration.
*/
export function chooseDefaultDriver(runConfig: RunnerConfig): DriverConfig {
export async function chooseDefaultDriver(
runConfig: RunnerConfig,
): Promise<DriverConfig> {
if (runConfig.endpoint && runConfig.driver) {
throw new UserError(
"Cannot specify both 'endpoint' and 'driver' in configuration",
Expand All @@ -31,5 +33,5 @@ export function chooseDefaultDriver(runConfig: RunnerConfig): DriverConfig {
}

loggerWithoutContext().debug({ msg: "using default file system driver" });
return createFileSystemOrMemoryDriver(true);
return await createFileSystemOrMemoryDriver(true);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import * as crypto from "node:crypto";
import * as fsSync from "node:fs";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import invariant from "invariant";
import { lookupInRegistry } from "@/actor/definition";
import { ActorAlreadyExists } from "@/actor/errors";
Expand All @@ -27,6 +23,12 @@ import {
setLongTimeout,
stringifyError,
} from "@/utils";
import {
getNodeCrypto,
getNodeFs,
getNodeFsSync,
getNodePath,
} from "@/utils/node";
import { logger } from "./log";
import {
ensureDirectoryExists,
Expand Down Expand Up @@ -93,6 +95,7 @@ export class FileSystemGlobalState {
constructor(persist: boolean = true, customPath?: string) {
this.#persist = persist;
this.#storagePath = persist ? getStoragePath(customPath) : "/tmp";
const path = getNodePath();
this.#stateDir = path.join(this.#storagePath, "state");
this.#dbsDir = path.join(this.#storagePath, "databases");
this.#alarmsDir = path.join(this.#storagePath, "alarms");
Expand All @@ -104,6 +107,7 @@ export class FileSystemGlobalState {
ensureDirectoryExistsSync(this.#alarmsDir);

try {
const fsSync = getNodeFsSync();
const actorIds = fsSync.readdirSync(this.#stateDir);
this.#actorCountOnStartup = actorIds.length;
} catch (error) {
Expand Down Expand Up @@ -131,15 +135,15 @@ export class FileSystemGlobalState {
}

getActorStatePath(actorId: string): string {
return path.join(this.#stateDir, actorId);
return getNodePath().join(this.#stateDir, actorId);
}

getActorDbPath(actorId: string): string {
return path.join(this.#dbsDir, `${actorId}.db`);
return getNodePath().join(this.#dbsDir, `${actorId}.db`);
}

getActorAlarmPath(actorId: string): string {
return path.join(this.#alarmsDir, actorId);
return getNodePath().join(this.#alarmsDir, actorId);
}

async *getActorsIterator(params: {
Expand All @@ -148,6 +152,7 @@ export class FileSystemGlobalState {
let actorIds = Array.from(this.#actors.keys()).sort();

// Check if state directory exists first
const fsSync = getNodeFsSync();
if (fsSync.existsSync(this.#stateDir)) {
actorIds = fsSync
.readdirSync(this.#stateDir)
Expand Down Expand Up @@ -258,6 +263,7 @@ export class FileSystemGlobalState {

// Read & parse file
try {
const fs = getNodeFs();
const stateData = await fs.readFile(stateFilePath);

// Cache the loaded state in handler
Expand Down Expand Up @@ -352,8 +358,10 @@ export class FileSystemGlobalState {
// Persist alarm to disk
if (this.#persist) {
const alarmPath = this.getActorAlarmPath(actorId);
const crypto = getNodeCrypto();
const tempPath = `${alarmPath}.tmp.${crypto.randomUUID()}`;
try {
const path = getNodePath();
await ensureDirectoryExists(path.dirname(alarmPath));
const alarmData: schema.ActorAlarm = {
actorId,
Expand All @@ -363,10 +371,12 @@ export class FileSystemGlobalState {
ACTOR_ALARM_VERSIONED.serializeWithEmbeddedVersion(
alarmData,
);
const fs = getNodeFs();
await fs.writeFile(tempPath, data);
await fs.rename(tempPath, alarmPath);
} catch (error) {
try {
const fs = getNodeFs();
await fs.unlink(tempPath);
} catch {}
logger().error({
Expand All @@ -391,10 +401,12 @@ export class FileSystemGlobalState {
): Promise<void> {
const dataPath = this.getActorStatePath(actorId);
// Generate unique temp filename to prevent any race conditions
const crypto = getNodeCrypto();
const tempPath = `${dataPath}.tmp.${crypto.randomUUID()}`;

try {
// Create directory if needed
const path = getNodePath();
await ensureDirectoryExists(path.dirname(dataPath));

// Convert to BARE types for serialization
Expand All @@ -409,11 +421,13 @@ export class FileSystemGlobalState {
// Perform atomic write
const serializedState =
ACTOR_STATE_VERSIONED.serializeWithEmbeddedVersion(bareState);
const fs = getNodeFs();
await fs.writeFile(tempPath, serializedState);
await fs.rename(tempPath, dataPath);
} catch (error) {
// Cleanup temp file on error
try {
const fs = getNodeFs();
await fs.unlink(tempPath);
} catch {
// Ignore cleanup errors
Expand Down Expand Up @@ -548,12 +562,14 @@ export class FileSystemGlobalState {
*/
#loadAlarmsSync(): void {
try {
const fsSync = getNodeFsSync();
const files = fsSync.existsSync(this.#alarmsDir)
? fsSync.readdirSync(this.#alarmsDir)
: [];
for (const file of files) {
// Skip temp files
if (file.includes(".tmp.")) continue;
const path = getNodePath();
const fullPath = path.join(this.#alarmsDir, file);
try {
const buf = fsSync.readFileSync(fullPath);
Expand Down Expand Up @@ -622,6 +638,7 @@ export class FileSystemGlobalState {
// On trigger: remove persisted alarm file
if (this.#persist) {
try {
const fs = getNodeFs();
await fs.unlink(this.getActorAlarmPath(actorId));
} catch (err: any) {
if (err?.code !== "ENOENT") {
Expand Down Expand Up @@ -668,6 +685,8 @@ export class FileSystemGlobalState {
}

getOrCreateInspectorAccessToken(): string {
const path = getNodePath();
const fsSync = getNodeFsSync();
const tokenPath = path.join(this.#storagePath, "inspector-token");
if (fsSync.existsSync(tokenPath)) {
return fsSync.readFileSync(tokenPath, "utf-8");
Expand All @@ -683,13 +702,15 @@ export class FileSystemGlobalState {
*/
#cleanupTempFilesSync(): void {
try {
const fsSync = getNodeFsSync();
const files = fsSync.readdirSync(this.#stateDir);
const tempFiles = files.filter((f) => f.includes(".tmp."));

const oneHourAgo = Date.now() - 3600000; // 1 hour in ms

for (const tempFile of tempFiles) {
try {
const path = getNodePath();
const fullPath = path.join(this.#stateDir, tempFile);
const stat = fsSync.statSync(fullPath);

Expand Down
Loading
Loading