Skip to content
Open
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
12 changes: 12 additions & 0 deletions rivetkit-typescript/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ DEBUG perf user: dbMigrateMs durationMs=...

The log name matches the key in `ActorMetrics.startup`. Internal phases use `perf internal:`, user-code callbacks use `perf user:`. This convention keeps startup logs greppable and makes it easy to separate framework overhead from user-code time. When adding a new startup phase, always add a corresponding log with the appropriate prefix and update the `#userStartupKeys` set in `ActorInstance` if the phase runs user code.

## Drizzle Compatibility Testing

To test rivetkit's drizzle integration against multiple drizzle-orm versions:

```bash
cd rivetkit-typescript/packages/rivetkit
./scripts/test-drizzle-compat.sh # test all default versions
./scripts/test-drizzle-compat.sh 0.44.2 0.45.1 # test specific versions
```

The script installs each drizzle-orm version, runs the drizzle driver tests, and reports pass/fail per version. It restores the original package.json and lockfile on exit. Update the `DEFAULT_VERSIONS` array in the script and `SUPPORTED_DRIZZLE_RANGE` in `packages/rivetkit/src/db/drizzle/mod.ts` when adding support for new drizzle releases.

## Workflow Context Actor Access Guards

In `ActorWorkflowContext` (`packages/rivetkit/src/workflow/context.ts`), all side-effectful `#runCtx` access must be guarded by `#ensureActorAccess` so that side effects only run inside workflow steps and are not replayed outside of them. Read-only properties (e.g., `actorId`, `log`) do not need guards. When adding new methods or properties to the workflow context that delegate to `#runCtx`, apply the guard if the operation has side effects.
4 changes: 0 additions & 4 deletions rivetkit-typescript/packages/rivetkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@
"@e2b/code-interpreter": "^2.3.3",
"dockerode": "^4.0.9",
"drizzle-kit": "^0.31.2",
"drizzle-orm": "^0.44.2",
"eventsource": "^4.0.0",
"ws": "^8.0.0"
},
Expand All @@ -383,9 +382,6 @@
"drizzle-kit": {
"optional": true
},
"drizzle-orm": {
"optional": true
},
"eventsource": {
"optional": true
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
#
# Tests rivetkit's drizzle integration against multiple drizzle-orm versions.
#
# Usage:
# ./scripts/test-drizzle-compat.sh # test all versions
# ./scripts/test-drizzle-compat.sh 0.44.2 0.45.1 # test specific versions
#
# Run from rivetkit-typescript/packages/rivetkit/.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PKG_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
ROOT_DIR="$(cd "$PKG_DIR/../../.." && pwd)"

# Default versions to test. Uses the latest patch of each minor release.
# Add new minor versions here as drizzle releases them.
DEFAULT_VERSIONS=("0.44" "0.45")

if [[ $# -gt 0 ]]; then
VERSIONS=("$@")
else
VERSIONS=("${DEFAULT_VERSIONS[@]}")
fi

cd "$ROOT_DIR"

# Back up files that pnpm add will modify.
cp "$PKG_DIR/package.json" "$PKG_DIR/package.json.drizzle-compat-bak"
cp "$ROOT_DIR/pnpm-lock.yaml" "$ROOT_DIR/pnpm-lock.yaml.drizzle-compat-bak"

cleanup() {
echo ""
echo "Restoring original package.json and lockfile..."
mv "$PKG_DIR/package.json.drizzle-compat-bak" "$PKG_DIR/package.json"
mv "$ROOT_DIR/pnpm-lock.yaml.drizzle-compat-bak" "$ROOT_DIR/pnpm-lock.yaml"
cd "$ROOT_DIR" && pnpm install --frozen-lockfile 2>/dev/null || pnpm install
}
trap cleanup EXIT

declare -A RESULTS

for version in "${VERSIONS[@]}"; do
echo ""
echo "=========================================="
echo " Testing drizzle-orm@$version"
echo "=========================================="

# Install the target version into the rivetkit package.
pnpm --filter rivetkit add -D "drizzle-orm@$version" 2>&1 | tail -3

# Run only the drizzle variant of the DB tests.
TEST_LOG="/tmp/drizzle-compat-${version}.log"
if cd "$PKG_DIR" && pnpm test driver-file-system -t "Actor Database \(drizzle\)" > "$TEST_LOG" 2>&1; then
RESULTS["$version"]="PASS"
echo " -> PASS"
else
RESULTS["$version"]="FAIL"
echo " -> FAIL (see $TEST_LOG)"
fi
cd "$ROOT_DIR"
done

echo ""
echo "=========================================="
echo " Drizzle Compatibility Results"
echo "=========================================="
for version in "${VERSIONS[@]}"; do
printf " drizzle-orm@%-10s %s\n" "$version" "${RESULTS[$version]}"
done
56 changes: 52 additions & 4 deletions rivetkit-typescript/packages/rivetkit/src/db/drizzle/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createRequire } from "node:module";
import type { IDatabase } from "@rivetkit/sqlite-vfs";
import {
drizzle as proxyDrizzle,
Expand All @@ -10,6 +11,54 @@ export * from "./sqlite-core";

import { type Config, defineConfig as originalDefineConfig } from "drizzle-kit";

/**
* Supported drizzle-orm version bounds. Update these when testing confirms
* compatibility with new releases. Run scripts/test-drizzle-compat.sh to
* validate.
*/
const DRIZZLE_MIN = [0, 44, 0];
const DRIZZLE_MAX = [0, 46, 0]; // exclusive

let drizzleVersionChecked = false;

function compareVersions(a: number[], b: number[]): number {
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const diff = (a[i] ?? 0) - (b[i] ?? 0);
if (diff !== 0) return diff;
}
return 0;
}

function isSupported(version: string): boolean {
// Strip prerelease suffix (e.g. "0.45.1-a7a15d0" -> "0.45.1")
const v = version.replace(/-.*$/, "").split(".").map(Number);
return (
compareVersions(v, DRIZZLE_MIN) >= 0 &&
compareVersions(v, DRIZZLE_MAX) < 0
);
}

function checkDrizzleVersion() {
if (drizzleVersionChecked) return;
drizzleVersionChecked = true;

try {
const require = createRequire(import.meta.url);
const { version } = require("drizzle-orm/package.json") as {
version: string;
};
if (!isSupported(version)) {
console.warn(
`[rivetkit] drizzle-orm@${version} has not been tested with this version of rivetkit. ` +
`Supported: >= ${DRIZZLE_MIN.join(".")} and < ${DRIZZLE_MAX.join(".")}. ` +
`Things may still work, but please report issues at https://github.com/rivet-dev/rivet/issues`,
);
}
} catch {
// Cannot determine version, skip check.
}
}

export function defineConfig(
config: Partial<Config & { driver: "durable-sqlite" }>,
): Config {
Expand Down Expand Up @@ -141,6 +190,8 @@ export function db<
>(
config?: DatabaseFactoryConfig<TSchema>,
): DatabaseProvider<SqliteRemoteDatabase<TSchema> & RawAccess> {
checkDrizzleVersion();

// Map from drizzle client to the underlying @rivetkit/sqlite Database.
// Uses WeakMap so entries are garbage-collected when the client is.
// This replaces the previous shared `waDbInstance` variable which caused
Expand Down Expand Up @@ -271,10 +322,7 @@ export function db<
clientToKvStore.get(client as object)?.clearPreload();
const waDb = clientToRawDb.get(client as object);
if (config?.migrations && waDb) {
await runInlineMigrations(
waDb,
config.migrations,
);
await runInlineMigrations(waDb, config.migrations);
}
},
onDestroy: async (client) => {
Expand Down
Loading