Skip to content

Conversation

@NathanFlurry
Copy link
Member

No description provided.

@vercel
Copy link

vercel bot commented Nov 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
rivetkit-serverless Error Error Nov 13, 2025 10:28pm
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
rivet-cloud Ignored Ignored Nov 13, 2025 10:28pm
rivet-inspector Ignored Ignored Preview Nov 13, 2025 10:28pm
rivet-site Ignored Ignored Preview Nov 13, 2025 10:28pm

Copy link
Member Author

NathanFlurry commented Nov 13, 2025


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link

claude bot commented Nov 13, 2025

PR Review: chore(rivetkit-typescript): remove dependency on node modules

Overview

This PR refactors the RivetKit TypeScript package to use dynamic imports for Node.js modules instead of static imports, enabling better compatibility with non-Node.js environments (like edge runtimes). The approach centralizes all Node.js module access through a new @/utils/node module with lazy loading.


Strengths ✅

  1. Excellent Architecture Pattern: The centralized utils/node.ts module with lazy loading is a clean solution for conditional Node.js dependencies. This enables the package to work in edge/browser environments.

  2. Comprehensive Linting Rules: The biome.json configuration enforces the new pattern with detailed error messages, preventing future violations. This is great for maintaining consistency.

  3. Idempotent Import System: The singleton importPromise pattern ensures imports only happen once, preventing race conditions and duplicate work.

  4. Proper Error Handling: The getter functions throw clear errors if modules aren't loaded, making debugging easier.

  5. Minimal API Surface Changes: Making the driver creation functions async is the right call and maintains backward compatibility reasonably well.


Issues Found 🔴

1. Critical: Potential Race Condition in Global State

In src/drivers/file-system/global-state.ts:96-107, the constructor accesses getNodePath() and getNodeFsSync() immediately:

constructor(persist: boolean = true, customPath?: string) {
    this.#persist = persist;
    this.#storagePath = persist ? getStoragePath(customPath) : "/tmp";
    const path = getNodePath();  // ⚠️ Called in constructor
    this.#stateDir = path.join(this.#storagePath, "state");
    // ...
}

However, in src/drivers/file-system/mod.ts:12-17, importNodeDependencies() is called after creating the global state:

export async function createFileSystemOrMemoryDriver(...) {
    // Import Node.js dependencies before creating the state
    await importNodeDependencies();
    
    const state = new FileSystemGlobalState(persist, customPath);  // ⚠️ Constructor runs AFTER import
}

Problem: The FileSystemGlobalState constructor is synchronous and immediately calls getNodePath() which might throw if modules aren't loaded yet. The current code happens to work because getStoragePath() (called before the constructor) internally calls getNodePath() first, but this is fragile.

Solution: Ensure getStoragePath() is only called after imports complete, or make the initialization lazy.

2. Inconsistent Error Message

In utils/node.ts:54, the error handling uses console.warn instead of the project's logging system:

console.warn(
    "Node.js modules not available, file system driver will not work",
    err,
);

Recommendation: Use the structured logging pattern from CLAUDE.md. Should be:

logger().warn({ err, msg: "node.js modules not available, file system driver will not work" });

However, this creates a circular dependency since logging might need to be set up first. Consider whether this error case should be silent (just throw) or use a different approach.

3. Missing Type Safety in Runner Protocol

In engine/sdks/typescript/runner-protocol/src/index.ts:1911-1913, the custom assert function lacks proper type narrowing:

function assert(condition: boolean, message?: string): asserts condition {
    if (!condition) throw new Error(message ?? "Assertion failed")
}

This is technically correct, but the implementation could be more robust. The original node:assert has additional features.

Minor issue: Not critical, but worth noting the behavioral difference.

4. Breaking Change Not Documented

The change from synchronous to asynchronous driver creation (createFileSystemDriver, createMemoryDriver) is a breaking change for consumers. Any existing code that doesn't await these functions will break:

// Before (synchronous)
const driver = createFileSystemDriver();

// After (async - REQUIRED)
const driver = await createFileSystemDriver();

Recommendation:

  • Add a note in the PR description about this breaking change
  • Consider bumping the package version to indicate breaking changes
  • Update any documentation/examples

Potential Concerns ⚠️

1. Performance Impact

Dynamic imports add a small async overhead on first use. For server startup, this is negligible, but worth noting that every driver creation now requires an await.

Question: Have you measured the performance impact? Is there any noticeable difference in cold start times?

2. Test Coverage

The tests were updated to handle async drivers, but I don't see tests specifically for:

  • The new importNodeDependencies() function
  • Error cases when Node.js modules are unavailable
  • The lazy loading behavior

Recommendation: Add unit tests for the utils/node.ts module covering:

// Test idempotency
await importNodeDependencies();
await importNodeDependencies(); // Should not import twice

// Test getters throw before import
expect(() => getNodePath()).toThrow();

// Test getters work after import
await importNodeDependencies();
expect(getNodePath()).toBeDefined();

3. Webpack Comment Placement

The /* webpackIgnore: true */ comments in utils/node.ts:33-39 are correct for preventing webpack from bundling Node.js modules, but this might not work with all bundlers (e.g., esbuild, rollup).

Question: Have you tested this with the actual build pipeline used for RivetKit? Does tsup respect these comments?

4. dump-openapi.ts Simplification

The scripts/dump-openapi.ts file was significantly simplified (removed 136 lines), including the removal of injectActorRouter() function.

Question: Was this dead code, or is there a separate PR that handles actor routes differently now? This seems like a significant functional change beyond just async imports.


Code Style Issues 🎨

1. Extra Blank Lines

In engine/sdks/typescript/runner-protocol/src/index.ts:1909-1914, there are extra blank lines:

}


function assert(condition: boolean, message?: string): asserts condition {

Should be single blank line per typical TS conventions.

2. Inconsistent Import Ordering

Some files moved from importing Node.js modules at the top to importing @/utils/node getters. Ensure consistency in import ordering according to project style.


Security Considerations 🔒

1. Path Traversal (Existing, not introduced)

The getStoragePath() function in utils.ts uses path.join() with user-provided customPath. While it hashes the path, if an attacker can control customPath, they might influence where data is stored.

Recommendation: Consider validating customPath is within expected bounds, though this may be low priority if the API is not exposed to untrusted input.


Recommendations Summary

Must Fix Before Merge:

  1. ✅ Verify the FileSystemGlobalState constructor doesn't race with module imports
  2. ✅ Document the breaking API change (async driver functions)

Should Fix:

  1. Replace console.warn with proper logging or silent throw
  2. Add unit tests for utils/node.ts module
  3. Verify bundler compatibility with webpackIgnore comments
  4. Clarify the dump-openapi.ts changes

Nice to Have:

  1. Remove extra blank lines in runner-protocol
  2. Performance testing for cold start impact

Conclusion

This is a well-architected refactoring that achieves the goal of removing direct Node.js dependencies. The centralized lazy-loading pattern is solid. The main concerns are around the race condition in FileSystemGlobalState construction and the lack of test coverage for the new import system.

Recommendation: Request changes to address the race condition and add tests. The breaking API change should be clearly communicated to users.

Great work on the biome.json linting rules - that's excellent proactive maintenance! 👏

@claude
Copy link

claude bot commented Nov 13, 2025

Code Review

This PR refactors the rivetkit-typescript package to remove direct dependencies on Node.js modules by introducing a centralized abstraction layer. Overall, the changes are well-structured and follow good architectural patterns.

✅ Strengths

1. Excellent Abstraction Pattern

  • The new src/utils/node.ts module provides a clean centralization point for all Node.js dependencies
  • Proper singleton pattern with importPromise ensuring imports happen only once (line 11-19)
  • Good JSDoc comments explaining usage
  • areNodeDependenciesAvailable() helper for checking module availability
  • Clear separation of concerns between Node.js-specific code and platform-agnostic code

2. Strong Linting Enforcement

  • The biome.json rules excellently enforce the abstraction boundaries
  • Comprehensive coverage of all Node.js modules (both node:* and unprefixed imports)
  • Exception for src/utils/node.ts allows proper encapsulation
  • Helpful error messages in linter rules directing developers to use the abstraction layer

3. Consistent Error Handling

  • Proper cleanup of temp files in error cases (global-state.ts:395, 445)
  • Good use of crypto.randomUUID() for unique temp file names to prevent conflicts
  • Helpful error messages in getter functions indicating importNodeDependencies() should be called

4. Code Quality

  • Consistent use of the new getter functions throughout
  • Proper async/await handling where needed
  • Good preservation of existing error messages
  • Webpack ignore comments to prevent bundling Node modules (node.ts:33-39)

⚠️ Issues & Concerns

1. Breaking API Changes (CRITICAL)

The following functions changed from synchronous to async without deprecation:

  • createFileSystemOrMemoryDriver() (drivers/file-system/mod.ts:12)
  • createFileSystemDriver() (drivers/file-system/mod.ts:51)
  • createMemoryDriver() (drivers/file-system/mod.ts:57)
  • chooseDefaultDriver() (drivers/default.ts:10)

Impact: This is a breaking change for any consumers. They'll need to add await or handle the returned Promise.

Recommendation:

  • Document this as a breaking change in the PR description
  • Add a migration section explaining the changes
  • Consider bumping major version

2. Removed Development Environment Variables

The engine process spawning code removes critical development environment variables (engine-process/mod.ts:94-117):

RIVET__PEGBOARD__RETRY_RESET_DURATION: "100",
RIVET__PEGBOARD__BASE_RETRY_TIMEOUT: "100",
RIVET__PEGBOARD__RESCHEDULE_BACKOFF_MAX_EXPONENT: "1",
RIVET__PEGBOARD__RUNNER_ELIGIBLE_THRESHOLD: "5000",
RIVET__PEGBOARD__RUNNER_LOST_THRESHOLD: "7000",

The original comments indicated these were important for development:

"In development, runners can be terminated without a graceful shutdown... This is problematic in development since this will cause actors to become unresponsive if frequently killing your dev server."

Impact: Could significantly degrade development experience with:

  • Slower iteration cycles
  • Actors becoming unresponsive after frequent dev server restarts
  • Longer backoff periods in development

Recommendation:

  • Restore these env vars OR
  • Explain in PR description why they're no longer needed
  • If intentionally removed, document the alternative solution

3. Incomplete Migration in runner-protocol

In engine/sdks/typescript/runner-protocol/src/index.ts:1911, a custom assert function replaces node:assert:

function assert(condition: boolean, message?: string): asserts condition {
    if (!condition) throw new Error(message ?? "Assertion failed")
}

Questions:

  • Is this file intended to be platform-independent?
  • Should this use a shared utility assert or a proper assertion library?
  • Why wasn't it abstracted like other Node.js modules?

Recommendation: Document the reasoning in a comment or PR description

🔍 Security Considerations

File System Operations (all good!)

  • ✅ Proper use of atomic writes with temp files and rename
  • ✅ UUID-based temp file names prevent race conditions
  • ✅ Temp file cleanup in error cases
  • ✅ Good use of path.normalize() and path.join() to prevent path traversal
  • ✅ Hash-based storage paths are deterministic and safe

📊 Test Coverage

The test files show minimal changes (just async function calls), which is appropriate. However:

Recommendations:

  • Add unit tests for utils/node.ts module:
    • Test singleton behavior (importPromise only created once)
    • Test error handling when modules not loaded
    • Test areNodeDependenciesAvailable() function
  • Add integration test for graceful fallback to memory driver when Node.js unavailable

🎯 Best Practices

Follows CLAUDE.md guidelines:

  • ✅ Proper error handling patterns
  • ✅ Structured logging with tracing
  • ✅ Async/await usage
  • ✅ Proper cleanup in error cases
  • ✅ Good use of JSDoc comments

📝 Documentation

Missing from PR Description:

  • Why this change was needed (platform independence? bundling?)
  • What problems it solves
  • Migration guide for breaking changes
  • Any performance implications

dump-openapi.ts cleanup:
The removal of 136 lines of manual OpenAPI injection code looks clean. Verify that:

  • OpenAPI output is still correct and complete
  • All necessary routes are documented
  • No functionality was lost

Summary

This is a well-designed refactoring with solid architecture. The abstraction layer is implemented correctly with:

  • Proper singleton pattern
  • Good error messages
  • Strong linting enforcement
  • Clean separation of concerns

Critical items to address:

  1. ⚠️ Document breaking API changes and provide migration guide
  2. ⚠️ Restore development env vars OR explain why removed
  3. ⚠️ Add PR description explaining motivation and changes
  4. Document runner-protocol assert function reasoning

Nice to have:

  • Unit tests for new utils/node.ts module
  • Integration test for non-Node.js environments

Great work on the abstraction layer design! Once the critical items are addressed, this will be ready to merge.


Review generated with Claude Code

@graphite-app
Copy link
Contributor

graphite-app bot commented Nov 13, 2025

Merge activity

@claude
Copy link

claude bot commented Nov 13, 2025

PR Review: Remove dependency on Node modules

Overview

This PR introduces a layer of abstraction for Node.js modules in RivetKit TypeScript, allowing the library to work in non-Node environments (edge runtimes, browsers, etc.) by dynamically importing Node.js dependencies only when needed.

✅ Strengths

1. Well-designed abstraction pattern

  • The utils/node.ts module provides a clean singleton pattern for lazy-loading Node.js dependencies
  • Idempotent importNodeDependencies() function prevents duplicate imports
  • Clear error messages guide developers when modules aren't loaded

2. Comprehensive linter enforcement

  • Biome configuration (biome.json) restricts direct Node.js imports across the codebase
  • Enforces centralized Node.js module access through getter functions
  • Exempts only src/utils/node.ts itself from the restriction

3. Consistent migration

  • All 20 files updated follow the same pattern: call getNode*() instead of direct imports
  • Functions that create drivers now properly await importNodeDependencies()
  • Test files updated to use async driver creation

4. Removal of runner protocol dependency

  • Removed unnecessary node:assert dependency in runner-protocol/src/index.ts
  • Implemented custom assert function inline (lines 1911-1913)

🔍 Potential Issues

1. Critical Bug: Module assignment in importNodeDependencies()

In src/utils/node.ts lines 42-50, the destructuring assignment is incorrect:

[
    nodeCrypto,
    nodeFsSync,
    nodeFs,
    nodePath,
    nodeOs,
    nodeChildProcess,
    nodeStream,
] = modules;

This attempts to assign to the local variables, but since they're declared with let at module scope, this creates new local variables in the function scope rather than assigning to the module-level variables. The correct approach is:

nodeCrypto = modules[0];
nodeFsSync = modules[1];
nodeFs = modules[2];
nodePath = modules[3];
nodeOs = modules[4];
nodeChildProcess = modules[5];
nodeStream = modules[6];

Or use object destructuring on the returned modules.

Impact: All getter functions will throw errors because the module-level variables remain undefined.

2. Removed environment variables in engine-process/mod.ts

Lines 98-100 show the env object now only spreads process.env, but the previous version (visible in diff) included important development-mode configuration:

  • RIVET__PEGBOARD__RETRY_RESET_DURATION
  • RIVET__PEGBOARD__BASE_RETRY_TIMEOUT
  • RIVET__PEGBOARD__RESCHEDULE_BACKOFF_MAX_EXPONENT
  • RIVET__PEGBOARD__RUNNER_ELIGIBLE_THRESHOLD
  • RIVET__PEGBOARD__RUNNER_LOST_THRESHOLD

Impact: Development iteration may be slower without these optimizations. Was this intentional or an oversight?

3. Error handling could be more specific

In src/utils/node.ts lines 51-58, the catch block logs a warning but doesn't distinguish between:

  • Node.js not available (expected in browser/edge)
  • Import failure due to other reasons (e.g., bundler misconfiguration)

Consider adding more context or environment detection.

4. Missing null check in engine-process/mod.ts

At line 94, getNodeChildProcess() is called without first ensuring importNodeDependencies() completed successfully (though it's called at line 28). If that import fails, this will throw. Consider adding a fallback or clearer error path.

🎯 Recommendations

High Priority:

  1. Fix the module assignment bug in src/utils/node.ts:42-50 - this is critical
  2. Clarify whether removing the Pegboard environment variables was intentional
  3. Add integration tests to verify dynamic imports work correctly

Medium Priority:
4. Consider adding JSDoc examples showing the correct usage pattern
5. Add runtime checks in key driver functions to ensure Node dependencies are available
6. Document the supported environments and fallback behavior

Low Priority:
7. Consider adding a preloadNodeDependencies() helper for eager loading in Node environments
8. Add telemetry/logging when falling back to memory driver due to missing Node.js

📊 Code Quality Metrics

  • Lines added: 389
  • Lines removed: 260
  • Net change: +129 (mostly linter config)
  • Files changed: 20
  • Test coverage: Existing tests updated but no new tests for dynamic import functionality

✨ Overall Assessment

This is a well-architected refactoring that properly isolates platform dependencies. The linter enforcement is excellent. However, there's a critical bug in the module assignment that needs immediate attention before merging. Once fixed, this will significantly improve RivetKit's portability.

Recommendation: Request changes to fix the destructuring assignment bug and clarify the environment variable removal.

@claude
Copy link

claude bot commented Nov 13, 2025

Pull Request Review: Remove dependency on node modules

Summary

This PR introduces a layer of indirection for Node.js module imports to prevent bundlers from including Node.js modules in bundled code. This is a good architectural improvement for making RivetKit more portable across different JavaScript runtimes.

Code Quality & Best Practices

✅ Strengths:

  1. Excellent linting enforcement - The biome.json configuration is well-structured with clear error messages directing developers to the abstraction layer
  2. Clean abstraction layer - The utils/node.ts module provides a clear, type-safe API with good documentation
  3. Proper singleton pattern - Using a singleton promise for importNodeDependencies() ensures initialization happens exactly once
  4. Comprehensive coverage - All Node.js imports have been migrated to use the new abstraction layer
  5. Good error messages - Clear error messages when modules aren't loaded help with debugging

⚠️ Areas for improvement:

Potential Issues

1. Critical: Sync initialization of modules before use

In global-state.ts constructor and other locations, Node.js modules are accessed synchronously without ensuring they're loaded:

constructor(persist: boolean = true, customPath?: string) {
    this.#persist = persist;
    this.#storagePath = persist ? getStoragePath(customPath) : "/tmp";
    const path = getNodePath();  // ❌ Called before importNodeDependencies()
    this.#stateDir = path.join(this.#storagePath, "state");

The getNodePath() call will throw an error because importNodeDependencies() hasn't been called yet. This same issue exists in:

  • global-state.ts:109 - getNodeFsSync() in constructor
  • utils.ts:17 - getNodeCrypto() in generateActorId()
  • Several other locations using getters in constructors

Recommendation: Either:

  • Make these classes/functions async and require initialization, OR
  • Load modules synchronously using require() in the getter functions as a fallback, OR
  • Document that importNodeDependencies() must be called before instantiating these classes

2. Inconsistent async patterns

Functions like createFileSystemOrMemoryDriver() are now async, but some callers may not be awaiting them. Check all call sites to ensure they handle the Promise correctly.

3. Missing initialization in some code paths

While ensureEngineProcess() calls importNodeDependencies(), other entry points that create file system drivers may not. For example:

  • dump-openapi.ts now awaits the driver creation (✅)
  • But direct uses of FileSystemGlobalState constructor will fail

Security Concerns

✅ No security issues identified. The dynamic imports with webpackIgnore comments are appropriate for preventing bundling.

Performance Considerations

Minor concern: The dynamic imports add a small async overhead on first use, but the singleton pattern ensures this only happens once. This is acceptable for the benefits gained.

Suggestion: Consider lazy-loading modules individually rather than all at once. For example, if only path is needed, you don't need to load crypto or child_process.

Test Coverage

⚠️ Missing tests:

  • No tests for the new utils/node.ts module
  • No tests verifying proper initialization order
  • No tests for error handling when Node.js modules aren't available

Recommendations:

  1. Add unit tests for utils/node.ts covering:
    • Successful initialization
    • Error cases when modules unavailable
    • Getter functions throw when not initialized
    • Singleton behavior
  2. Add integration tests ensuring initialization happens before use in real scenarios

Additional Observations

  1. Runner protocol change (engine/sdks/typescript/runner-protocol/src/index.ts):

    • Adding a custom assert() function is good - removes Node.js dependency
    • ✅ Well implemented
  2. OpenAPI script simplification (dump-openapi.ts):

    • ❌ Significant functionality was removed (actor router injection)
    • Was this intentional? The removed code handled health checks, actions, and request paths
    • If intentional, ensure OpenAPI spec is still complete
  3. Engine process environment variables removed:

    • Several RIVET configuration env vars were removed from engine-process/mod.ts
    • Verify these aren't needed for development environments

Recommendations Summary

High Priority:

  1. ✅ Fix synchronous usage of Node.js modules before initialization
  2. Add tests for the new abstraction layer
  3. Verify OpenAPI generation is still complete

Medium Priority:
4. Consider lazy-loading individual modules
5. Document initialization requirements clearly
6. Audit all call sites for proper async handling

Low Priority:
7. Add JSDoc examples showing proper usage patterns

Verdict

This is a valuable architectural improvement, but there are critical initialization issues that need to be addressed before merging. The synchronous usage of getters before importNodeDependencies() is called will cause runtime errors.

Once the initialization issues are fixed and tests are added, this will be a solid improvement to the codebase.


🤖 Generated with Claude Code

Base automatically changed from 11-10-feat_site_add_typedoc_docs to main November 13, 2025 22:25
@NathanFlurry NathanFlurry force-pushed the rivetkit_remove_dependency_node_modules branch from 981bd59 to ec6a9cb Compare November 13, 2025 22:27
@NathanFlurry NathanFlurry merged commit 0c3df6e into main Nov 13, 2025
7 of 11 checks passed
@NathanFlurry NathanFlurry deleted the rivetkit_remove_dependency_node_modules branch November 13, 2025 22:29
@claude
Copy link

claude bot commented Nov 13, 2025

Code Review

This PR refactors RivetKit TypeScript to centralize Node.js module imports through dynamic imports, enabling better support for edge/browser environments. Overall, the approach is solid and well-executed. Here are my findings:

✅ Strengths

  1. Clean Abstraction Pattern: The src/utils/node.ts module provides a clean, centralized way to manage Node.js dependencies with clear error messages when modules aren't available.

  2. Idempotent Import Logic: The singleton pattern (importPromise) ensures Node.js modules are only imported once, preventing redundant dynamic imports.

  3. Comprehensive Linting Rules: The biome.json override configuration effectively enforces the new pattern by restricting direct Node.js imports outside of utils/node.ts.

  4. Consistent Migration: All Node.js module usage has been systematically replaced throughout the codebase (file-system driver, engine-process, registry, etc.).

  5. Proper Async Propagation: Functions that now need to import Node.js dependencies correctly return Promise and are awaited by callers (e.g., createFileSystemOrMemoryDriver, chooseDefaultDriver).

🔍 Issues & Concerns

1. Error Handling in Dynamic Imports (Medium Priority)

Location: rivetkit-typescript/packages/rivetkit/src/utils/node.ts:51-58

The catch block logs a warning but doesn't differentiate between "Node.js not available" (expected in edge environments) vs. "import failed due to bundler/runtime issue" (unexpected error).

} catch (err) {
    // Node.js not available - will use memory driver fallback
    console.warn(
        "Node.js modules not available, file system driver will not work",
        err,
    );
    throw err;
}

Recommendation: Consider checking err.code or message to distinguish between expected vs. unexpected failures. This will help debugging in production environments.

2. Test Module Still Uses Direct Node Import (High Priority)

Location: rivetkit-typescript/packages/rivetkit/src/test/mod.ts:1

import { createServer } from "node:net";

The test module directly imports node:net, which violates the new pattern. Since test utilities are excluded from the biome lint rules (!rivetkit-typescript/packages/rivetkit/src/test/**/*), this wasn't caught.

Recommendation: Either:

  • Add getNodeNet() to utils/node.ts and migrate this usage
  • Document why test utilities are exempt from this pattern
  • Consider if test utilities should follow the same pattern for consistency

3. Missing Initialization Guard in getNode* Functions (Low Priority)

Location: rivetkit-typescript/packages/rivetkit/src/utils/node.ts:68-153

All getNode* functions throw an error if modules aren't loaded, but callers might forget to call importNodeDependencies() first. While the error messages are helpful, there's no programmatic way to check readiness before calling.

Recommendation: The existing areNodeDependenciesAvailable() function (line 159) addresses this, but it's not used anywhere. Consider documenting when developers should use it, or add examples in the function JSDoc.

4. No Rollback for Failed Node Module Initialization (Low Priority)

Location: rivetkit-typescript/packages/rivetkit/src/utils/node.ts:18-62

If importNodeDependencies() throws, the importPromise is set but subsequent calls will return the rejected promise. This could cause confusing behavior where the first call shows one error, but retries show a different cached error.

Recommendation: Consider resetting importPromise = undefined in the catch block to allow retries, or document that initialization failures are fatal and require process restart.

5. Removed Environment Variables for Development (Medium Priority)

Location: rivetkit-typescript/packages/rivetkit/src/engine-process/mod.ts:93-106

The PR removes several environment variables that were specifically designed for faster development iteration:

  • RIVET__PEGBOARD__RETRY_RESET_DURATION
  • RIVET__PEGBOARD__BASE_RETRY_TIMEOUT
  • RIVET__PEGBOARD__RESCHEDULE_BACKOFF_MAX_EXPONENT
  • RIVET__PEGBOARD__RUNNER_ELIGIBLE_THRESHOLD
  • RIVET__PEGBOARD__RUNNER_LOST_THRESHOLD

Recommendation: These seem unrelated to the Node.js dependency refactoring. Was this intentional, or should they be preserved? The original comment indicated these were important for DX when killing dev servers frequently.

6. Simplified OpenAPI Script Loses Functionality (High Priority)

Location: rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts

The script now removes the injectActorRouter() function that manually added actor router paths to the OpenAPI spec. The PR removes 136 lines including:

  • /gateway/{actorId}/health
  • /gateway/{actorId}/action/{action}
  • /gateway/{actorId}/request/{path}

Recommendation: Verify that these routes are now automatically included via the main router, or if this is intentional cleanup. If intentional, the OpenAPI spec will be incomplete for actor routes.

7. Changed Package Privacy (Low Priority)

Location: engine/package.json:2

The engine package changed from "private": true to having a "version": "1.0.0" with license, suggesting it might be published. This seems unrelated to the PR's goal.

Recommendation: Clarify if this is intentional or should be reverted.

🧪 Test Coverage

The PR updates test files to use await createFileSystemOrMemoryDriver():

  • driver-file-system.test.ts
  • driver-memory.test.ts

However, there don't appear to be new tests for:

  • ❌ Error handling when Node.js modules aren't available
  • ❌ Behavior of areNodeDependenciesAvailable()
  • ❌ Retry/recovery scenarios for failed imports

Recommendation: Add tests to verify the new error paths, especially for edge/browser environments where Node.js modules should gracefully fail.

🚀 Performance Considerations

Dynamic imports add minimal overhead since:

  1. They're cached via the singleton pattern
  2. They're done once at initialization
  3. All imports happen in parallel (Promise.all)

No performance concerns identified.

🔒 Security

No security concerns identified. The dynamic imports use standard Node.js built-in modules with no external dependencies.

📋 Minor Suggestions

  1. Consistency: The dump-openapi.ts script still has direct Node.js imports at the top (lines 1-2), which is fine since it's a script, but consider documenting why scripts don't need the abstraction.

  2. Error Messages: The EXTRA_ERROR_LOG constant was replaced with inline objects (issues, support fields). Consider creating a shared constant in utils.ts to maintain consistency.

  3. Type Safety: Consider exporting the module types from utils/node.ts for better IDE support:

    export type NodeCrypto = typeof import("node:crypto");
    export type NodeFs = typeof import("node:fs/promises");
    // etc.

Summary

This is a well-structured refactoring that successfully achieves its goal of removing direct Node.js dependencies. The main concerns are:

  1. ⚠️ High Priority: Verify the OpenAPI script changes don't lose important route documentation
  2. ⚠️ High Priority: Fix direct node:net import in test utilities
  3. ⚠️ Medium Priority: Clarify if removed environment variables and engine package.json changes were intentional

The pattern itself is sound and should work well for edge/browser compatibility. Once the above issues are addressed, this should be ready to merge.


Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants