Skip to content

Conversation

@NathanFlurry
Copy link
Member

No description provided.

@vercel
Copy link

vercel bot commented Nov 10, 2025

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

Project Deployment Preview Comments Updated (UTC)
rivetkit-serverless Ready Ready Preview Comment Nov 10, 2025 3:58am
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
rivet-cloud Ignored Ignored Nov 10, 2025 3:58am
rivet-inspector Ignored Ignored Nov 10, 2025 3:58am
rivet-site Ignored Ignored Nov 10, 2025 3:58am

Copy link
Member Author

NathanFlurry commented Nov 10, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


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 10, 2025

Code Review: JSON Protocol Support

This PR adds JSON protocol support to RivetKit by introducing a flexible serialization system that can handle JSON, CBOR, and BARE encodings. Overall, this is a well-structured change with good architectural decisions. Here's my detailed feedback:


Strengths

  1. Clean Abstraction: The CachedSerializer refactor is excellent. The new generic signature <TBare, TJson, T = TBare> with conversion functions toJson and toBare provides clear separation of concerns.

  2. Type Safety: The use of Zod schemas (src/schemas/client-protocol-zod/mod.ts) for JSON validation ensures runtime type safety while maintaining TypeScript compile-time types.

  3. Performance Optimization: Caching serialized data per encoding in CachedSerializer prevents redundant serialization - smart optimization.

  4. Consistent Pattern: The conversion pattern is applied consistently across all message types (Init, Error, ActionResponse, Event, etc.) - good maintainability.

  5. Backward Compatibility: BARE/CBOR encoding still works with ArrayBuffer for binary data, while JSON uses native JavaScript types.


Issues & Concerns ⚠️

1. Overly Permissive Type Usage (High Priority)

Location: Multiple files

  • src/actor/conn/driver.ts:35 - CachedSerializer<any, any, any>
  • src/actor/conn/drivers/websocket.ts:26 - CachedSerializer<any, any, any>
  • src/actor/protocol/old.ts:90 (in parseMessage return type) - any

Issue: Using any weakens type safety and defeats the purpose of TypeScript. The driver interface should specify proper generic constraints.

Recommendation:

// Instead of:
sendMessage?(actor: AnyActorInstance, conn: AnyConn, message: CachedSerializer<any, any, any>): void;

// Consider:
sendMessage?<TBare, TJson, T = TBare>(
  actor: AnyActorInstance,
  conn: AnyConn,
  message: CachedSerializer<TBare, TJson, T>
): void;

2. Type Coercion Without Validation (Medium Priority)

Location: src/actor/protocol/old.ts:183-189

const output = await handler.onExecuteAction(ctx, name, args as unknown[]);

Issue: The args are cast to unknown[] without validation. For JSON encoding, args come directly from user input and should be validated.

Recommendation: Add runtime validation or at least document the assumption that args are arrays.

3. Inconsistent Error Handling (Medium Priority)

Location: src/client/actor-conn.ts:811-864

Issue: The #parseMessage function has complex CBOR decoding logic that could fail, but errors aren't explicitly handled. If cbor.decode() throws, the error might be cryptic.

Recommendation: Wrap CBOR decoding in try-catch blocks with descriptive error messages:

try {
  return cbor.decode(new Uint8Array(msg.body.val.output));
} catch (error) {
  throw new errors.MalformedMessage(\`Failed to decode action output: \${error}\`);
}

4. Missing Documentation (Low Priority)

Issue: The new conversion functions (toJson, toBare, fromJson, fromBare) lack documentation explaining when each is used and what transformations they perform.

Recommendation: Add JSDoc comments to CachedSerializer and serializeWithEncoding/deserializeWithEncoding explaining the data flow.

5. Type Annotations Clarity (Low Priority)

Location: src/actor/instance/mod.ts:514-540

const initData = { actorId: this.id, connectionId: conn.id };

Issue: The initData type is inferred. While TypeScript can figure it out, explicit typing would make the code more readable.

Recommendation:

const initData: { actorId: string; connectionId: string } = { actorId: this.id, connectionId: conn.id };

Security Considerations 🔒

  1. Input Validation: The JSON path now accepts arbitrary JavaScript objects. Ensure all action handlers properly validate their inputs.

  2. CBOR Bomb Protection: CBOR can encode deeply nested structures. Consider adding size/depth limits to prevent denial-of-service attacks.

  3. Metadata Encoding: Error metadata is user-controlled and gets serialized. Verify that jsonStringifyCompat properly handles malicious inputs (e.g., circular references, large objects).


Test Coverage 🧪

Missing Tests:

  • Unit tests for CachedSerializer with all three encodings
  • Integration tests for JSON vs CBOR vs BARE message round-trips
  • Error cases: malformed JSON, invalid CBOR, oversized messages
  • Edge cases: BigInt serialization, ArrayBuffer handling, null metadata

Recommendation: Add tests to verify:

// Example test structure
describe('CachedSerializer', () => {
  it('should correctly serialize events for JSON encoding', () => {
    const eventData = { name: 'test', args: [1, 'foo', true] };
    const serializer = new CachedSerializer(/* ... */);
    const json = serializer.serialize('json');
    // Assert json structure
  });

  it('should handle CBOR encoding for binary data', () => {
    // Test ArrayBuffer serialization
  });

  it('should cache serialization results', () => {
    // Verify cache hits
  });
});

Performance Considerations

  1. Serialization Overhead: For JSON encoding, data goes through toJson → Zod validation → jsonStringifyCompat. This is more expensive than raw JSON.stringify. Consider benchmarking for high-throughput scenarios.

  2. Memory Usage: CachedSerializer stores multiple encoded versions in memory. For large messages, this could increase memory footprint significantly.

Recommendation: Document the trade-offs and consider adding a max cache size or TTL.


Minor Issues 🔧

  1. Commented Code: src/actor/protocol/serde.ts:70-111 contains large commented-out blocks. Remove dead code or add a TODO if it's needed for reference.

  2. TODO Comment: src/common/router.ts:99 - "TODO: Remove any" - this should be addressed or tracked in an issue.

  3. Truncated Import: src/client/utils.ts:66 line appears incomplete in the diff.


Recommendations Summary

High Priority:

  • Replace any types with proper generics
  • Add input validation for action args

Medium Priority:

  • Add error handling around CBOR decode operations
  • Add comprehensive tests for all three encoding formats

Low Priority:

  • Add documentation for conversion functions
  • Clean up commented code and TODOs
  • Add explicit type annotations where types are inferred

Conclusion

This is a solid implementation that successfully adds JSON protocol support while maintaining backward compatibility. The architecture is clean and extensible. The main concerns are around type safety (using any) and test coverage. Once those are addressed, this should be good to merge!

Overall Assessment: ✅ Approve with suggestions

Great work on maintaining a consistent pattern throughout the codebase! 🚀

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 10, 2025

More templates

@rivetkit/actor

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/actor@3432

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@3432

@rivetkit/core

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/core@3432

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/db@3432

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@3432

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@3432

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@3432

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@3432

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@3432

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@3432

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@3432

commit: acb0447

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.

2 participants