Skip to content

feat(durable-event-iterator): implement event iterator with Cloudflare hibernation and more#668

Merged
dinwwwh merged 44 commits into
mainfrom
feat/durable-event-iterator/integration
Jun 24, 2025
Merged

feat(durable-event-iterator): implement event iterator with Cloudflare hibernation and more#668
dinwwwh merged 44 commits into
mainfrom
feat/durable-event-iterator/integration

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented Jun 19, 2025

  • durable event iterator
  • plugins - first working
  • jwt attachment
  • recovery mode
  • RPC
  • Testing
  • docs

Closes: https://github.com/unnoq/orpc/issues/107

Summary by CodeRabbit

  • New Features

    • Introduced Durable Event Iterator integration for durable event streams with automatic reconnection and event recovery, leveraging Cloudflare Durable Objects.
    • Released a new Durable Event Iterator package with full server and client support, including plugins, token-based authentication, and RPC extensions.
    • Added a chat room example in the Cloudflare Worker playground using the Durable Event Iterator for real-time messaging.
  • Documentation

    • Provided detailed documentation covering installation, usage, RPC extensions, token handling, plugin integration, and contract-first development for the Durable Event Iterator.
  • Refactor

    • Simplified Cloudflare Worker and chat room code to utilize the Durable Event Iterator, removing manual WebSocket and RPC management.
  • Chores

    • Added dependencies, build configurations, type definitions, and package metadata to support the Durable Event Iterator package and its development.
  • Tests

    • Added comprehensive tests for event storage, WebSocket management, request upgrades, plugin behavior, RPC handlers, and type correctness related to the Durable Event Iterator.

End-users gain reliable, persistent event streaming with seamless reconnection and recovery, enhanced by improved documentation and practical examples.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 19, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
orpc ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 24, 2025 11:02am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 19, 2025

Walkthrough

A new experimental package, @orpc/experimental-durable-event-iterator, is introduced to enable durable, reconnectable event streaming (such as WebSocket-based streaming) for serverless and edge runtimes, with initial support for Cloudflare Durable Objects. The implementation includes core library code, schema definitions, plugins, comprehensive tests, documentation, and integration into the Cloudflare Worker playground. The playground and example chat room are refactored to use this new system, replacing manual WebSocket and RPC management with the new durable event iterator abstraction.

Changes

Files/Groups Change Summary
packages/durable-event-iterator/** (new package) Implements experimental durable event iterator: core logic, client/server plugins, handler, storage, websocket manager, schemas, and extensive tests.
apps/content/docs/integrations/durable-event-iterator.md, .vitepress/config.ts Adds documentation for the new integration and updates sidebar navigation.
playgrounds/cloudflare-worker/worker/index.ts, worker/dos/chat-room.ts Refactors chat room and worker to use DurableEventIteratorObject and new RPC-based event streaming.
playgrounds/cloudflare-worker/src/components/chat-room.tsx Updates chat room component to consume new iterator-based event stream and message sending.
playgrounds/cloudflare-worker/src/lib/chat-room.ts, src/lib/orpc.ts Removes legacy client setup, adds new ORPC client with durable event iterator plugin.
playgrounds/cloudflare-worker/package.json Adds experimental durable event iterator package as a devDependency.
packages/shared/src/index.ts Re-exports JsonValue type for use in new package.
package.json, tsconfig.json Adds dependencies and updates TypeScript config to exclude new package from root build.
worker-configuration.d.ts Updates Cloudflare Worker types for compatibility with new features.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Worker
    participant DurableObject

    Client->>Worker: HTTP request to /chat-room
    Worker->>Worker: upgradeDurableEventIteratorRequest()
    Worker->>DurableObject: Forward upgraded request with token payload
    DurableObject->>Client: Upgrade to WebSocket (101 Switching Protocols)
    Client->>DurableObject: Subscribe via WebSocket (with token)
    DurableObject->>Client: Stream events (with automatic reconnection support)
    Client->>DurableObject: Call allowed RPC methods via WebSocket
    DurableObject->>Client: Respond with results/events
Loading

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Add support for streaming/WebSocket in serverless/edge runtimes (#107)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Add better-sqlite3 and types to root package.json devDependencies This is only used for testing the new package; while not directly mentioned, it supports in-scope test infra.
Update worker-configuration.d.ts with new Cloudflare types This is a compatibility update to support the new feature, not an unrelated change.

Poem

In the warren, code streams anew,
With webs that last and tokens true.
Durable events hop edge to edge,
No more lost messages—rabbits pledge!
Streaming flows, connections mend,
Serverless dreams, with bunnies to send.
🐇✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @unnoq, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant new capability to the oRPC ecosystem: a dedicated package for building and consuming event iterators backed by Cloudflare Durable Objects. This enables robust, scalable, and efficient real-time event streaming, particularly benefiting applications deployed on Cloudflare Workers. The changes abstract away much of the complexity of managing WebSocket connections and Durable Object state for event-driven architectures.

Highlights

  • New Package: @orpc/durable-event-iterator: Introduced a new package designed to facilitate event streaming and iteration using Cloudflare Durable Objects, leveraging Cloudflare's hibernation feature for efficient WebSocket connections.
  • Core Durable Object Integration: Implemented experimental_DurableEventIteratorObject as a base class for Durable Objects, providing built-in capabilities for managing WebSocket connections, handling RPC messages, and publishing events to connected clients with support for Cloudflare's hibernation.
  • Client-Side Event Iteration: Developed client-side utilities (experimental_ClientDurableEventIterator and experimental_createClientDurableEventIterator) and a DurableEventIteratorLinkPlugin to enable seamless, type-safe asynchronous iteration over events streamed from Durable Objects via WebSockets.
  • Server-Side Event Subscription & Upgrade: Added experimental_DurableEventIteratorBuilder for server-side subscription to event channels, generating authenticated JWTs for clients. Also introduced experimental_upgradeDurableEventIteratorRequest to handle WebSocket upgrade requests to Durable Objects, including JWT verification.
  • Playground Example Refactor: The Cloudflare Worker playground was updated to demonstrate the new Durable Event Iterator, refactoring the ChatRoom Durable Object to extend the new base class and updating the main worker and client-side components to utilize the new event streaming capabilities.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 19, 2025

Codecov Report

Attention: Patch coverage is 98.08795% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...durable-event-iterator/src/durable-object/index.ts 0.00% 4 Missing and 1 partial ⚠️
packages/durable-event-iterator/src/index.ts 0.00% 4 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the @orpc/durable-event-iterator package, enabling type-safe event streams with Cloudflare Durable Objects and hibernation. Key feedback points focus on enhancing type safety, improving robustness in request handling, and correcting a typo. Addressing these will further solidify the new package.

Comment thread playgrounds/cloudflare-worker/worker/index.ts Outdated
Comment thread packages/durable-event-iterator/src/client/plugin.ts Outdated
Comment thread packages/durable-event-iterator/src/object.ts Outdated
Comment thread packages/durable-event-iterator/src/client/event-iterator.ts
Comment thread packages/durable-event-iterator/src/client/plugin.ts Outdated
Comment thread packages/durable-event-iterator/src/client/plugin.ts
Comment thread packages/durable-event-iterator/src/object.ts Outdated
@dinwwwh dinwwwh requested a review from Copilot June 24, 2025 08:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request implements a durable event iterator integration with Cloudflare Durable Objects, offering new RPC plugins, hibernation support, and comprehensive testing and documentation. Key changes include:

  • Updating the Cloudflare Worker to integrate the new Durable Event Iterator API.
  • Refactoring the Durable Object implementation for chat rooms with enhanced event publishing.
  • Adding a new package with tests, type definitions, and documentation for durable event iterator support.

Reviewed Changes

Copilot reviewed 46 out of 49 changed files in this pull request and generated no comments.

Show a summary per file
File Description
playgrounds/cloudflare-worker/worker/index.ts Adapts the worker endpoint to use the new DurableEventIterator and RPCHandler plugins.
playgrounds/cloudflare-worker/worker/dos/chat-room.ts Refactors the ChatRoom durable object; introduces RPC methods, including a new publishMessage method and a second variant (publishMessage2) for direct event publishing.
playgrounds/cloudflare-worker/src/lib/orpc.ts Configures the ORPC client to include a DurableEventIteratorLinkPlugin.
playgrounds/cloudflare-worker/src/components/chat-room.tsx Updates the ChatRoom component to use the new client APIs for receiving and sending messages.
packages/durable-event-iterator/* Introduces a new package with the durable event iterator’s core logic, plugins, tests, schema definitions, and integration documentation.
Comments suppressed due to low confidence (1)

playgrounds/cloudflare-worker/worker/dos/chat-room.ts:15

  • [nitpick] The method name 'publishMessage2' is ambiguous; consider renaming it to something more descriptive (e.g. 'publishDirectMessage') to clarify its purpose and distinguish it from 'publishMessage'.
  publishMessage2(message: string) {

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 24, 2025

More templates

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@668

@orpc/client

npm i https://pkg.pr.new/@orpc/client@668

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@668

@orpc/experimental-durable-event-iterator

npm i https://pkg.pr.new/@orpc/experimental-durable-event-iterator@668

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@668

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@668

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@668

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@668

@orpc/react

npm i https://pkg.pr.new/@orpc/react@668

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@668

@orpc/server

npm i https://pkg.pr.new/@orpc/server@668

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@668

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@668

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@668

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@668

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@668

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@668

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@668

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@668

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@668

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@668

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@668

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@668

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@668

commit: 866cc1e

@dinwwwh
Copy link
Copy Markdown
Member Author

dinwwwh commented Jun 24, 2025

/gemini review

@dinwwwh dinwwwh marked this pull request as ready for review June 24, 2025 08:37
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jun 24, 2025
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request implements a durable event iterator with Cloudflare hibernation and more, including features like plugins, JWT attachment, recovery mode, RPC, testing, and documentation. The changes include adding a new integration documentation, installing better-sqlite3, creating a durable event iterator package, and modifying the playground to use the new durable event iterator. The code appears well-structured and includes necessary error handling and validation. However, some improvements can be made to enhance error handling and code clarity.

Comment thread packages/durable-event-iterator/src/durable-object/object.ts
Comment thread packages/durable-event-iterator/src/durable-object/event-storage.ts
Comment thread packages/durable-event-iterator/src/durable-object/handler.ts
Comment thread packages/durable-event-iterator/src/durable-object/object.ts
Comment thread packages/durable-event-iterator/src/durable-object/object.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (4)
packages/durable-event-iterator/src/durable-object/upgrade.ts (1)

59-63: Avoid passing JWT payload through URL parameters for security.

Passing the JWT payload as a URL parameter poses security risks as URLs may be logged and have size limitations. Consider using request headers or body instead.

-      const upgradeUrl = new URL(url.origin + url.pathname)
-
-      upgradeUrl.searchParams.set(DURABLE_EVENT_ITERATOR_TOKEN_PAYLOAD_KEY, stringifyJSON(payload))
-
-      return stub.fetch(upgradeUrl, request)
+      const upgradeUrl = new URL(url.origin + url.pathname)
+      
+      // Pass payload securely through request headers
+      const headers = new Headers(request.headers)
+      headers.set('X-DEI-Token-Payload', stringifyJSON(payload))
+      
+      return stub.fetch(upgradeUrl, {
+        ...request,
+        headers,
+      })

Note: This change would also require updating the fetch method in object.ts to read from headers instead of URL parameters, addressing the non-null assertion issue mentioned in previous reviews.

packages/durable-event-iterator/src/client/event-iterator.ts (1)

68-68: Use a more specific type assertion.

Consider using a more specific type assertion instead of as any.

packages/durable-event-iterator/src/client/plugin.ts (2)

69-69: Add type check before casting to string.

The output should be validated before casting to ensure type safety.


90-90: Consider the implications of infinite retry.

Setting retry: Number.POSITIVE_INFINITY might not be appropriate for all use cases.

🧹 Nitpick comments (26)
packages/durable-event-iterator/.gitignore (1)

16-22: Minor nit: extend generated-file ignores

While optional, you could include a few other usual suspects to keep the workspace clean:

+*.tgz
+yarn-error.log
+package-lock.json
+pnpm-lock.yaml
+*.sw?

Feel free to ignore if the repo-level .gitignore already covers them.

packages/durable-event-iterator/tsconfig.json (1)

11-18: Nit: keep test exclusions DRY

The same glob patterns appear in many tsconfigs across the repo. Consider moving them to the base config and re-using "exclude": ["**/*.test.*", …] globally.

playgrounds/cloudflare-worker/package.json (1)

17-18: Using "next" tag—be ready for breaking changes

The experimental package is consumed via the next dist-tag. Locking to an exact version (e.g. 0.0.0-<sha>) makes the playground reproducible; otherwise every install may pull newer, potentially incompatible builds.

packages/durable-event-iterator/build.config.ts (1)

3-7: Consider collecting externals in a constant

As the package grows you may add more Cloudflare-specific externals (cloudflare:html-rebuilder, etc.).
A small refactor keeps this tidy:

-externals: [
-  'cloudflare:workers',
-],
+const cloudflareExternals = ['cloudflare:workers']
+
+externals: cloudflareExternals,

Not critical, but eases maintenance.

packages/durable-event-iterator/README.md (1)

69-71: Add alt text for accessibility compliance.

The sponsors image is missing alt text, which affects accessibility.

  <a href="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg">
-    <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
+    <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg' alt='Sponsors'/>
  </a>
playgrounds/cloudflare-worker/src/components/chat-room.tsx (1)

6-6: Consider removing unused iterator state or use it for cleanup.

The iterator state is set but never used afterward. If it's intended for cleanup, consider using it in the cleanup function.

-const [iterator, setIterator] = useState<Awaited<ReturnType<typeof client.onMessage>> | null>(null)
packages/durable-event-iterator/src/durable-object/upgrade.test.ts (2)

73-84: Consider using more realistic timestamp values for JWT testing.

The test uses the same timestamp for iat and exp, which creates a JWT that expires immediately. Consider using a future expiration time for more realistic testing.

-const date = 1244444444444
+const now = Date.now()
+const expiry = now + 1000 * 60 * 60 // 1 hour from now

const jwt = await new SignJWT({
  chn: 'test-channel',
  rpc: ['someMethod'],
  att: { some: 'attachment' },
})
  .setProtectedHeader({ alg: 'HS256' })
-  .setIssuedAt(date)
-  .setExpirationTime(date)
+  .setIssuedAt(now)
+  .setExpirationTime(expiry)
  .sign(new TextEncoder().encode('test-sign'))

65-113: Consider adding test for expired token scenario.

The test suite covers invalid tokens but doesn't test expired tokens, which is a common real-world scenario.

Add a test case like:

it('rejects expired token', async () => {
  const jwt = await new SignJWT({
    chn: 'test-channel',
    rpc: ['someMethod'],
    att: { some: 'attachment' },
  })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt(Date.now() - 2000)
    .setExpirationTime(Date.now() - 1000) // expired 1 second ago
    .sign(new TextEncoder().encode('test-sign'))
    
  const response = await upgradeDurableEventIteratorRequest(
    new Request(`https://example.com?token=${jwt}`, {
      headers: { upgrade: 'websocket' },
    }),
    {
      namespace: {} as any,
      signingKey: 'test-sign',
    },
  )

  expect(response.status).toBe(401)
})
packages/durable-event-iterator/src/event-iterator.test.ts (1)

16-16: Consider avoiding as any type casting.

Using as any bypasses TypeScript's type checking and could hide potential type issues. Consider using proper typing or type assertions.

If the types are correct but TypeScript can't infer them properly, consider using more specific type assertions:

-    const iterator = new DurableEventIterator(testChannel, options) as any
+    const iterator = new DurableEventIterator(testChannel, options)

Also applies to: 31-31, 55-55

packages/durable-event-iterator/tests/shared.ts (1)

9-11: Improve SQL method detection for robustness.

The current approach of detecting SQL method based on query string content is fragile and could fail with complex queries, comments, or edge cases.

Consider a more robust approach:

-        const method = query.includes('SELECT') || query.includes('RETURNING') ? 'all' : 'run'
+        const normalizedQuery = query.trim().toUpperCase()
+        const method = normalizedQuery.startsWith('SELECT') || normalizedQuery.includes('RETURNING') ? 'all' : 'run'

Or even better, use a proper SQL parser or more comprehensive pattern matching if this becomes a bottleneck in testing.

packages/durable-event-iterator/src/durable-object/object.test.ts (2)

6-40: Complex but necessary global mocking setup.

The global mocking of Cloudflare-specific APIs (DurableObject, WebSocketPair, Response) is necessary for testing but creates potential fragility. Consider isolating these mocks more to prevent test interference.

Consider wrapping the global mocks in a test utility function or using more isolated mocking strategies if this becomes problematic across test suites.


100-105: Basic WebSocket close handling test.

While the test covers WebSocket close functionality, it might benefit from more assertions about cleanup behavior or state management during closure.

Consider adding assertions to verify that WebSocket closure properly cleans up resources or updates internal state as expected.

packages/durable-event-iterator/src/durable-object/event-storage.ts (1)

78-80: Consider batch deletion of expired events to improve performance.

Deleting expired events on every insert could impact performance under high load. Consider implementing a periodic cleanup strategy or batching deletions.

-    this.ctx.storage.sql.exec(`
-      DELETE FROM "dei:events" WHERE time < ?
-    `, now - this.eventRetentionSeconds)
+    // Consider implementing periodic cleanup instead of deleting on every insert
+    // For now, only delete if necessary (e.g., every N inserts or based on row count)
+    const shouldCleanup = Math.random() < 0.1 // 10% chance, or use a counter
+    if (shouldCleanup) {
+      this.ctx.storage.sql.exec(`
+        DELETE FROM "dei:events" WHERE time < ?
+      `, now - this.eventRetentionSeconds)
+    }
playgrounds/cloudflare-worker/worker/index.ts (1)

26-26: Clarify the method name publishMessage2.

The method name publishMessage2 appears inconsistent. If this is the intended name, consider adding a comment explaining the difference from publishMessage. Otherwise, consider renaming for consistency.

-      await stub.publishMessage2(input.message)
+      await stub.publishMessage(input.message)
packages/durable-event-iterator/src/client/plugin.test.ts (2)

25-25: Remove unnecessary async keyword from describe block.

The async keyword is not needed for describe blocks as they don't handle promises.

-describe('durableEventIteratorLinkPlugin', async () => {
+describe('durableEventIteratorLinkPlugin', () => {

73-123: Consider extracting WebSocket message simulation into test utilities.

The test contains complex WebSocket message simulation logic that could be extracted into reusable test utilities. This would improve maintainability and reduce duplication if similar tests are needed elsewhere.

Would you like me to create utility functions for WebSocket message simulation to make these tests more maintainable?

packages/durable-event-iterator/src/plugin.ts (2)

16-16: Consider using a more maintainable plugin ordering approach.

The hardcoded order value of 2,100,000 seems arbitrary. Consider defining order constants or calculating the order relative to the batch plugin's order.

-  order = 2_100_000 // make sure execute after the batch plugin
+  // Ensure execution after batch plugin (typically order ~1000)
+  order = 1100

Alternatively, consider creating an enum or constants file for plugin ordering to make dependencies clearer.


40-48: Optimize header assignment to avoid setting undefined values.

Instead of conditionally setting the header value to undefined, consider only including the header when needed.

       return {
         ...result,
         response: {
           ...result.response,
           headers: {
             ...result.response.headers,
-            [DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_KEY]: pluginContext.isClientDurableEventIteratorOutput
-              ? DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_VALUE
-              : undefined,
+            ...(pluginContext.isClientDurableEventIteratorOutput && {
+              [DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_KEY]: DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_VALUE,
+            }),
           },
         },
       }
packages/durable-event-iterator/src/durable-object/handler.ts (1)

38-38: Clarify the purpose of subtracting 1 second from token issued time.

The calculation subtracts 1 second from the JWT iat timestamp. Consider using a named constant or adding a comment to explain this offset.

+      // Subtract 1 second to account for potential clock skew
+      const CLOCK_SKEW_SECONDS = 1
       context.websocketManager.sendEventsAfter(
         context.currentWebsocket,
         hibernationId,
-        lastEventId !== undefined ? lastEventId : new Date((payload.iat - 1) * 1000),
+        lastEventId !== undefined ? lastEventId : new Date((payload.iat - CLOCK_SKEW_SECONDS) * 1000),
       )
packages/durable-event-iterator/src/durable-object/websocket-manager.ts (1)

8-9: Consider adding specific options or documentation.

The interface extends StandardRPCJsonSerializerOptions but doesn't add any specific options. Consider either adding WebSocket-specific options or documenting the intended use case for future extensibility.

 export interface DurableEventIteratorObjectWebsocketManagerOptions extends StandardRPCJsonSerializerOptions {
+  // Add WebSocket-specific options here in the future if needed
 }
apps/content/docs/integrations/durable-event-iterator.md (4)

44-44: Fix grammar: Use imperative form for section heading.

The section heading should use an imperative verb form for better clarity and consistency with documentation standards.

-## Setup your Durable Object
+## Set Up Your Durable Object

102-102: Add hyphens to compound adjectives.

Multiple instances of compound adjectives modifying nouns should be hyphenated for better readability and grammatical correctness.

-## Server Side Usage
+## Server-Side Usage

-## Client Side Usage  
+## Client-Side Usage

-### Server Side Usage
+### Server-Side Usage

-### Client Side Usage
+### Client-Side Usage

Also applies to: 143-143, 227-227, 250-250


103-103: Add missing comma for clarity.

The sentence structure would benefit from a comma after the introductory phrase.

-Here we define two procedures: one for listening to messages in the chat room, and another for sending messages to all connected clients by invoking the `publishMessage` method on the Durable Object.
+Here we define two procedures: one for listening to messages in the chat room, and another for sending messages to all connected clients by invoking the `publishMessage` method on the Durable Object.

179-179: Fix subject-verb agreement.

The verb should agree with the plural subject "methods."

-To use it, you need to define methods that accepts a `WebSocket` instance as the first argument and returns an [oRPC Router](/docs/server/router).
+To use it, you need to define methods that accept a `WebSocket` instance as the first argument and return an [oRPC Router](/docs/server/router).
packages/durable-event-iterator/src/schemas.ts (1)

13-16: Document the attachment field transformation behavior.

The transformation sets att: undefined and then spreads the original value, which means if att was present in the input, it will override the undefined. This effectively preserves the original att value while ensuring the field exists in the output type. Consider adding a comment explaining this intentional behavior.

  v.transform(value => ({
+   // Ensure att field exists in output type while preserving original value
    att: undefined,
    ...value,
  })),
packages/durable-event-iterator/src/durable-object/event-storage.test.ts (1)

51-66: Consider making the SQLite row limit test more maintainable.

The test uses a hard-coded value '9223372036854775806' for the SQLite row limit test. Consider extracting this as a constant or making the test more resilient to changes in the actual SQLite integer limits.

    // Simulate a full disk (1 row remaining) by exceeding the max number of rows
+   const SQLITE_MAX_INTEGER = 9223372036854775807n;
+   const NEAR_MAX_ID = (SQLITE_MAX_INTEGER - 1n).toString();
    ctx.storage.sql.exec(`
      INSERT INTO "dei:events" (id, event, time) VALUES (?, ?, ?) 
      RETURNING CAST(id AS TEXT) as id
-   `, '9223372036854775806', '{}', Math.floor(Date.now() / 1000))
+   `, NEAR_MAX_ID, '{}', Math.floor(Date.now() / 1000))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32e1281 and 5de832d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (48)
  • apps/content/.vitepress/config.ts (1 hunks)
  • apps/content/docs/integrations/durable-event-iterator.md (1 hunks)
  • package.json (2 hunks)
  • packages/durable-event-iterator/.gitignore (1 hunks)
  • packages/durable-event-iterator/README.md (1 hunks)
  • packages/durable-event-iterator/build.config.ts (1 hunks)
  • packages/durable-event-iterator/package.json (1 hunks)
  • packages/durable-event-iterator/src/client/contract.ts (1 hunks)
  • packages/durable-event-iterator/src/client/event-iterator.test-d.ts (1 hunks)
  • packages/durable-event-iterator/src/client/event-iterator.test.ts (1 hunks)
  • packages/durable-event-iterator/src/client/event-iterator.ts (1 hunks)
  • packages/durable-event-iterator/src/client/index.ts (1 hunks)
  • packages/durable-event-iterator/src/client/plugin.test.ts (1 hunks)
  • packages/durable-event-iterator/src/client/plugin.ts (1 hunks)
  • packages/durable-event-iterator/src/consts.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/consts.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/event-storage.test.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/event-storage.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/handler.test.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/handler.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/index.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/object.test.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/object.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/upgrade.test.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/upgrade.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/websocket-manager.test.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/websocket-manager.ts (1 hunks)
  • packages/durable-event-iterator/src/event-iterator.test-d.ts (1 hunks)
  • packages/durable-event-iterator/src/event-iterator.test.ts (1 hunks)
  • packages/durable-event-iterator/src/event-iterator.ts (1 hunks)
  • packages/durable-event-iterator/src/index.ts (1 hunks)
  • packages/durable-event-iterator/src/object.test-d.ts (1 hunks)
  • packages/durable-event-iterator/src/object.ts (1 hunks)
  • packages/durable-event-iterator/src/plugin.test.ts (1 hunks)
  • packages/durable-event-iterator/src/plugin.ts (1 hunks)
  • packages/durable-event-iterator/src/schemas.ts (1 hunks)
  • packages/durable-event-iterator/tests/shared.ts (1 hunks)
  • packages/durable-event-iterator/tsconfig.json (1 hunks)
  • packages/durable-event-iterator/tsconfig.test.json (1 hunks)
  • packages/shared/src/index.ts (1 hunks)
  • playgrounds/cloudflare-worker/package.json (1 hunks)
  • playgrounds/cloudflare-worker/src/components/chat-room.tsx (2 hunks)
  • playgrounds/cloudflare-worker/src/lib/chat-room.ts (0 hunks)
  • playgrounds/cloudflare-worker/src/lib/orpc.ts (1 hunks)
  • playgrounds/cloudflare-worker/worker-configuration.d.ts (6 hunks)
  • playgrounds/cloudflare-worker/worker/dos/chat-room.ts (1 hunks)
  • playgrounds/cloudflare-worker/worker/index.ts (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • playgrounds/cloudflare-worker/src/lib/chat-room.ts
🧰 Additional context used
🧬 Code Graph Analysis (10)
packages/durable-event-iterator/src/plugin.test.ts (5)
packages/durable-event-iterator/src/event-iterator.ts (1)
  • DurableEventIterator (50-108)
packages/server/src/adapters/standard/rpc-handler.ts (1)
  • StandardRPCHandler (13-22)
packages/durable-event-iterator/src/plugin.ts (1)
  • DurableEventIteratorHandlerPlugin (13-70)
packages/durable-event-iterator/src/consts.ts (2)
  • DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_KEY (2-2)
  • DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_VALUE (3-3)
packages/durable-event-iterator/src/client/event-iterator.ts (1)
  • getClientDurableEventIteratorToken (74-80)
packages/durable-event-iterator/src/object.test-d.ts (2)
packages/client/src/types.ts (1)
  • Client (26-28)
packages/durable-event-iterator/src/object.ts (1)
  • InferDurableEventIteratorObjectRPC (18-24)
packages/durable-event-iterator/src/durable-object/upgrade.test.ts (2)
packages/durable-event-iterator/src/durable-object/upgrade.ts (1)
  • upgradeDurableEventIteratorRequest (20-66)
packages/durable-event-iterator/src/durable-object/consts.ts (1)
  • DURABLE_EVENT_ITERATOR_TOKEN_PAYLOAD_KEY (2-2)
packages/durable-event-iterator/src/client/event-iterator.test.ts (3)
packages/durable-event-iterator/src/schemas.ts (1)
  • DurableEventIteratorTokenPayload (3-3)
packages/shared/src/iterator.ts (3)
  • next (58-60)
  • AsyncIteratorClass (23-96)
  • isAsyncIteratorObject (4-10)
packages/durable-event-iterator/src/client/event-iterator.ts (1)
  • createClientDurableEventIterator (37-69)
packages/durable-event-iterator/src/event-iterator.test.ts (2)
packages/durable-event-iterator/src/event-iterator.ts (1)
  • DurableEventIterator (50-108)
packages/durable-event-iterator/src/client/event-iterator.ts (1)
  • getClientDurableEventIteratorToken (74-80)
packages/durable-event-iterator/src/durable-object/upgrade.ts (7)
packages/shared/src/interceptor.ts (1)
  • Interceptor (13-16)
packages/durable-event-iterator/src/schemas.ts (2)
  • DurableEventIteratorTokenPayload (3-3)
  • DurableEventIteratorTokenPayloadSchema (5-17)
packages/durable-event-iterator/src/consts.ts (1)
  • DURABLE_EVENT_ITERATOR_TOKEN_PARAM (1-1)
packages/shared/src/array.ts (1)
  • toArray (1-3)
packages/durable-event-iterator/src/durable-object/object.ts (1)
  • DurableEventIteratorObject (28-104)
packages/durable-event-iterator/src/durable-object/consts.ts (1)
  • DURABLE_EVENT_ITERATOR_TOKEN_PAYLOAD_KEY (2-2)
packages/shared/src/json.ts (1)
  • stringifyJSON (9-12)
packages/durable-event-iterator/src/plugin.ts (5)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/standard/plugin.ts (1)
  • StandardHandlerPlugin (5-8)
packages/server/src/adapters/standard/handler.ts (1)
  • StandardHandlerOptions (27-48)
packages/durable-event-iterator/src/consts.ts (2)
  • DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_KEY (2-2)
  • DURABLE_EVENT_ITERATOR_PLUGIN_HEADER_VALUE (3-3)
packages/durable-event-iterator/src/client/event-iterator.ts (1)
  • getClientDurableEventIteratorToken (74-80)
packages/durable-event-iterator/src/durable-object/websocket-manager.ts (5)
packages/client/src/adapters/standard/rpc-json-serializer.ts (1)
  • StandardRPCJsonSerializerOptions (25-27)
packages/durable-event-iterator/src/durable-object/consts.ts (2)
  • DURABLE_EVENT_ITERATOR_HIBERNATION_ID_KEY (1-1)
  • DURABLE_EVENT_ITERATOR_TOKEN_PAYLOAD_KEY (2-2)
packages/durable-event-iterator/src/schemas.ts (1)
  • DurableEventIteratorTokenPayload (3-3)
packages/durable-event-iterator/src/object.ts (1)
  • TokenAttachment (6-6)
packages/durable-event-iterator/src/durable-object/event-storage.ts (1)
  • DurableEventIteratorObjectEventStorage (16-136)
packages/durable-event-iterator/src/event-iterator.ts (6)
packages/durable-event-iterator/src/object.ts (2)
  • DurableEventIteratorObject (8-16)
  • InferDurableEventIteratorObjectRPC (18-24)
packages/durable-event-iterator/src/client/event-iterator.ts (2)
  • ClientDurableEventIterator (24-31)
  • createClientDurableEventIterator (37-69)
packages/durable-event-iterator/src/schemas.ts (1)
  • DurableEventIteratorTokenPayload (3-3)
packages/shared/src/array.ts (1)
  • toArray (1-3)
packages/shared/src/iterator.ts (1)
  • AsyncIteratorClass (23-96)
packages/client/src/types.ts (1)
  • ClientLink (36-38)
packages/durable-event-iterator/src/durable-object/object.test.ts (3)
packages/durable-event-iterator/tests/shared.ts (2)
  • createCloudflareWebsocket (26-34)
  • createDurableObjectState (3-24)
packages/durable-event-iterator/src/durable-object/consts.ts (1)
  • DURABLE_EVENT_ITERATOR_TOKEN_PAYLOAD_KEY (2-2)
packages/standard-server-peer/src/codec.ts (1)
  • encodeRequestMessage (92-136)
🪛 GitHub Check: codecov/patch
packages/durable-event-iterator/src/durable-object/index.ts

[warning] 2-5: packages/durable-event-iterator/src/durable-object/index.ts#L2-L5
Added lines #L2 - L5 were not covered by tests

packages/durable-event-iterator/src/index.ts

[warning] 2-5: packages/durable-event-iterator/src/index.ts#L2-L5
Added lines #L2 - L5 were not covered by tests

🪛 LanguageTool
packages/durable-event-iterator/README.md

[misspelling] ~33-~33: Possible spelling mistakes found.
Context: ...y (React, Vue, Solid, Svelte, Angular), Pinia Colada, and more. - 🚀 Server Actions: Ful...

(EN_MULTITOKEN_SPELLING_TWO)


[style] ~51-~51: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... your API or implement API contract. - [@orpc/client](https://www.npmjs.com/package/@...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~52-~52: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... API on the client with type-safety. - [@orpc/openapi](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~53-~53: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...I specs and handle OpenAPI requests. - [@orpc/nest](https://www.npmjs.com/package/@or...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~54-~54: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... with NestJS. - [@orpc/react](https://www.npmjs.com/package/@o...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~55-~55: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...with React and React Server Actions. - [@orpc/tanstack-query](https://www.npmjs.com/p...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~56-~56: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...stack.com/query/latest) integration. - [@orpc/vue-colada](https://www.npmjs.com/packa...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[misspelling] ~56-~56: Possible spelling mistakes found.
Context: ...ge/@orpc/vue-colada): Integration with Pinia Colada. - [@or...

(EN_MULTITOKEN_SPELLING_TWO)


[style] ~57-~57: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ada](https://pinia-colada.esm.dev/). - [@orpc/hey-api](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~58-~58: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...I](https://heyapi.dev/) integration. - [@orpc/zod](https://www.npmjs.com/package/@orp...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~59-~59: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tps://zod.dev/) doesn't support yet. - [@orpc/valibot](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~60-~60: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...rom Valibot. - [@orpc/arktype](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

apps/content/docs/integrations/durable-event-iterator.md

[grammar] ~44-~44: This sentence should probably be started with a verb instead of the noun ‘Setup’. If not, consider inserting a comma for better clarity.
Context: ...t and may change in the future. ::: ## Setup your Durable Object ::: warning This s...

(SENT_START_NN_DT)


[uncategorized] ~102-~102: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ndler export { ChatRoom } ``` ## Server Side Usage Here we define two procedures: o...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~103-~103: Possible missing comma found.
Context: ...{ ChatRoom } ``` ## Server Side Usage Here we define two procedures: one for liste...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~143-~143: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...teratorHandlerPlugin(), ], }) ``` ## Client Side Usage On the client side, you only nee...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[grammar] ~179-~179: Possible subject-verb agreement error.
Context: ...use it, you need to define methods that accepts a WebSocket instance as the first arg...

(NNS_THAT_AGREEMENT)


[uncategorized] ~227-~227: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... .callable(), } } } ### Server Side Usage ts import { DurableEventItera...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~250-~250: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... like user IDs or preferences. ::: ### Client Side Usage You can invoke methods defined i...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.17.2)
packages/durable-event-iterator/README.md

70-70: Images should have alternate text (alt text)
null

(MD045, no-alt-text)

🪛 Biome (1.9.4)
packages/durable-event-iterator/src/event-iterator.ts

[error] 70-70: Do not add then to a class.

(lint/suspicious/noThenProperty)

🔇 Additional comments (58)
packages/durable-event-iterator/.gitignore (1)

2-4: Over-broad .* pattern may unintentionally hide useful config files

The catch-all .* ignores all dotfiles/directories under this package (e.g. .eslintrc.cjs, .editorconfig, .npmrc, .github/*).
If you ever need per-package tooling configs, they’ll silently disappear from Git tracking.
Consider narrowing the rule or adding more negations (e.g. !.eslintrc.*, !.editorconfig) to avoid future head-scratching.

packages/durable-event-iterator/tsconfig.json (1)

6-10: Consider pin-ning exact reference paths

The three reference entries assume the sibling packages live exactly one level up.
If these packages are published independently, use their own package.json#name (e.g. "path": "../client""path": "../client/tsconfig.json") so tsc -b resolves them even when the directory structure changes.

tsconfig.json (1)

40-42: Excluding the new package hides type errors from the root build

packages/durable-event-iterator/** is now excluded.
If CI/build scripts rely on the root tsconfig.json to surface type errors, this package will be silently skipped.
Either add a project reference ({ "path": "./packages/durable-event-iterator" }) or ensure the package’s own tsc -b run is integrated into the pipeline.

packages/shared/src/index.ts (1)

16-16: 👍 Exporting JsonValue improves cross-package typing

Re-export looks correct and matches the existing pattern.

package.json (1)

55-57: pnpm.onlyBuiltDependencies – verify runtime requirement

Moving better-sqlite3 to optional or dev deps may also let you drop it from onlyBuiltDependencies; otherwise CI will still attempt native builds.

apps/content/.vitepress/config.ts (1)

156-160: Ensure target doc page exists & build passes

The new sidebar entry looks fine.
Please double-check that /docs/integrations/durable-event-iterator.md (or an index.md in that folder) is committed; otherwise the docs build will break with a 404.

#!/bin/bash
# Verify the page exists in the repo
fd durable-event-iterator apps/content/docs | head
packages/durable-event-iterator/src/durable-object/index.ts (1)

1-5: Barrel export LGTM

Simple re-exports keep consumer imports tidy; no issues detected.

packages/durable-event-iterator/src/client/index.ts (1)

1-2: Client barrel export LGTM

Matches the pattern used in the server side; good for DX.

packages/durable-event-iterator/tsconfig.test.json (1)

4-7: Test tsconfig looks correct

Decorator flag & type roots are appropriate for Workers + Vitest. No concerns.

packages/durable-event-iterator/src/index.ts (1)

1-5: LGTM! Clean barrel module implementation.

This follows the standard barrel module pattern for aggregating package exports. The wildcard exports provide a clean, centralized API for consumers.

playgrounds/cloudflare-worker/src/lib/orpc.ts (1)

1-16: Well-structured ORPC client setup with proper type safety.

The implementation correctly:

  • Uses dynamic origin URL for flexibility across environments
  • Configures the durable event iterator plugin with appropriate endpoint
  • Maintains end-to-end type safety with the router type
  • Follows clean import patterns
packages/durable-event-iterator/src/durable-object/consts.ts (1)

1-2: Well-designed constants with efficient naming.

The constants use:

  • Efficient short prefixes (dei:) for storage optimization
  • Consistent naming convention
  • Proper TypeScript const assertions for literal types
  • Clear, descriptive names that indicate their purpose
packages/durable-event-iterator/src/object.test-d.ts (1)

5-19: Comprehensive type test coverage for RPC method inference.

The test effectively validates that InferDurableEventIteratorObjectRPC correctly:

  • Includes valid RPC methods (singleClient, nestedClient)
  • Excludes methods with required context (requiredContext)
  • Excludes AsyncIterator methods (next)
  • Excludes non-function properties (notAFunction)

This ensures type safety in the durable event iterator's RPC method filtering.

packages/durable-event-iterator/README.md (1)

1-76: Well-structured and comprehensive README documentation.

The README provides excellent coverage of the oRPC ecosystem and the durable event iterator package. The formatting, badges, and content organization are professional and informative.

packages/durable-event-iterator/src/client/contract.ts (1)

5-13: Well-structured contract definition.

The contract properly defines the expected structure for durable event iterator operations with appropriate typing and validation.

packages/durable-event-iterator/src/client/event-iterator.test-d.ts (1)

6-18: Comprehensive type-level testing.

The type tests properly verify that the ClientDurableEventIterator maintains both async iterator capabilities and proper RPC method typing.

packages/durable-event-iterator/src/consts.ts (1)

1-3: Well-defined constants with clear naming.

The constants follow a consistent naming pattern and use proper TypeScript literal typing with as const.

packages/durable-event-iterator/src/event-iterator.test.ts (1)

24-46: Test coverage looks comprehensive for core functionality.

The test effectively validates:

  • JWT token creation and signing
  • Token payload structure and content
  • Error throwing on direct iteration/cleanup
  • Client iterator token extraction
packages/durable-event-iterator/package.json (2)

16-34: Well-structured export configuration.

The dual export configuration properly supports both development (source TypeScript files) and production (compiled dist files) environments, which is essential for a workspace package.


54-56: Verify external dependency versions for security and compatibility.

The external dependencies jose, partysocket, and valibot should be verified to ensure they are using secure, up-to-date versions without known vulnerabilities.

#!/bin/bash
# Check for security advisories and latest versions of external dependencies

echo "Checking jose package..."
curl -s https://pypi.org/pypi/jose/json 2>/dev/null || echo "Not a Python package"
npm view jose versions --json | tail -10

echo -e "\nChecking partysocket package..."
npm view partysocket versions --json | tail -10

echo -e "\nChecking valibot package..." 
npm view valibot versions --json | tail -10

# Check for security advisories
echo -e "\nChecking for security advisories..."
npm audit --audit-level=moderate --json 2>/dev/null | jq '.vulnerabilities // {}'
packages/durable-event-iterator/tests/shared.ts (1)

26-34: Clean WebSocket mock implementation.

The WebSocket mock with attachment serialization/deserialization is well-designed for testing Cloudflare-specific functionality.

packages/durable-event-iterator/src/durable-object/handler.test.ts (4)

9-18: Well-structured test data setup.

The token payload structure with attachment, timestamps, channel, and RPC permissions provides good test coverage for the authentication and authorization flow.


21-54: Comprehensive hibernation callback testing.

The test properly validates that the hibernation callback serializes the hibernation ID and triggers event sending with the correct parameters based on token timestamp.


94-115: Proper RPC authorization testing.

The test correctly validates that unauthorized RPC methods are rejected with appropriate error messages, ensuring security boundaries are enforced.


149-181: Thorough nested client testing.

The test validates both single-level and nested RPC method calls with proper argument passing including signal and lastEventId, which is essential for the hierarchical RPC structure.

packages/durable-event-iterator/src/event-iterator.test-d.ts (4)

6-24: Excellent type constraint testing for optional attachments.

The test properly validates that when the attachment type includes undefined, the att option becomes optional, while maintaining type safety for the attachment structure.


26-44: Proper validation of required attachment types.

The test ensures that when attachment types don't include undefined, the att option is required and type-checked correctly.


46-60: Robust RPC method validation.

The type test ensures only valid RPC methods can be passed to the .rpc() method, preventing runtime errors from invalid method names.


62-81: Comprehensive type resolution testing.

The tests validate that the resolved ClientDurableEventIterator type correctly reflects whether RPC methods are specified, ensuring type safety throughout the API.

packages/durable-event-iterator/src/durable-object/object.test.ts (2)

52-80: Comprehensive WebSocket upgrade testing.

The test properly validates the WebSocket upgrade flow including token payload attachment, proper response status (101), and WebSocket pair management.


82-98: Proper message handling validation.

The test validates that encoded request messages are processed and responses are sent correctly through the WebSocket connection.

packages/durable-event-iterator/src/durable-object/websocket-manager.test.ts (1)

1-151: Well-structured test suite with comprehensive coverage!

The tests effectively validate the WebsocketManager's core functionality including attachment serialization with internal key protection, event publishing with hibernation ID filtering, and replay of stored events.

packages/durable-event-iterator/src/object.ts (1)

1-24: Clean and well-structured type definitions!

The use of a unique symbol for type branding and the type utility for inferring RPC methods are well-designed. The exclusion of AsyncIterator methods in the type inference is particularly thoughtful.

playgrounds/cloudflare-worker/worker-configuration.d.ts (5)

3-3: LGTM: Runtime version update

The runtime version comment has been appropriately updated to reflect the newer workerd version.


5192-5192: LGTM: Workflow retention duration type added

The new WorkflowRetentionDuration type export appropriately extends the workflow type hierarchy for retention policies.


5323-5323: LGTM: Enhanced tail stream onset interface

The addition of the executionModel property to the TailStream.Onset interface provides valuable execution context information.


5366-5366: LGTM: Improved type specificity for return info

Restricting the info property in the TailStream.Return interface to FetchResponseInfo provides better type safety and clarity.


5718-5725: LGTM: Workflow retention configuration added

The addition of the retention property to WorkflowInstanceCreateOptions appropriately supports the new workflow retention features with sensible defaults.

playgrounds/cloudflare-worker/worker/dos/chat-room.ts (1)

1-18: Clean refactor to use DurableEventIteratorObject!

The refactoring successfully simplifies the chat room implementation by leveraging the new durable event iterator abstraction. The removal of manual WebSocket handling and hibernation logic makes the code much cleaner and more maintainable.

packages/durable-event-iterator/src/event-iterator.ts (1)

70-107: Correct implementation of PromiseLike interface.

The then method is required by the PromiseLike interface. The static analysis warning about not adding then to a class can be safely ignored in this case as it's a false positive.

packages/durable-event-iterator/src/durable-object/websocket-manager.ts (6)

27-31: Clever type exclusion pattern.

The use of mapped types with never to exclude internal attachment keys is a sophisticated approach that prevents users from accidentally overriding internal data while maintaining type safety.


44-58: Event publishing logic is well-implemented.

The method correctly:

  • Stores the event first to get the ID
  • Filters out WebSockets without hibernation IDs (unfinished subscriptions)
  • Sends encoded events to valid connections

The defensive programming approach of skipping connections without hibernation IDs is good practice.


60-78: Event replay functionality looks correct.

The method efficiently retrieves and sends historical events using the event storage abstraction. The hibernation ID parameter passing is consistent with the publishing method.


80-88: Attachment merging preserves internal state correctly.

The method safely merges user-provided attachments while preserving internal hibernation and token data. This prevents users from accidentally corrupting the internal state.


90-98: Internal attachment management is properly isolated.

The method allows updating internal attachment data while preserving existing values with proper fallbacks. The nullish coalescing operators ensure robust handling of undefined values.


100-102: Consider adding error handling for attachment deserialization.

While the current implementation delegates to the WebSocket's built-in method, consider whether additional error handling or validation is needed for corrupted attachment data.

#!/bin/bash
# Search for error handling patterns in WebSocket attachment deserialization across the codebase
rg -A 5 -B 5 "deserializeAttachment" --type ts
apps/content/docs/integrations/durable-event-iterator.md (5)

54-71: Code example demonstrates proper class extension.

The ChatRoom class example correctly shows:

  • Proper inheritance from DurableEventIteratorObject
  • Type parameters for event payload
  • Constructor with optional configuration
  • Public method for event publishing

81-100: WebSocket upgrade example is complete and secure.

The upgrade handler example demonstrates:

  • Proper URL routing
  • Secure token validation with signing key
  • Correct namespace binding
  • Error handling with 404 response

111-141: Server-side RPC integration is well-documented.

The router example effectively shows:

  • Event iterator creation with proper configuration
  • RPC method invocation on Durable Object stubs
  • Plugin integration for handler setup

147-171: Client-side usage example is clear and practical.

The client example demonstrates:

  • Plugin configuration with WebSocket URL
  • Event iteration pattern
  • RPC method calls
  • Proper async/await usage

172-176: Recovery feature explanation is valuable.

The automatic event persistence and recovery capability is well-explained with configuration options. This addresses a key concern for production systems.

packages/durable-event-iterator/src/client/event-iterator.test.ts (2)

31-39: Comprehensive async iterator testing.

The test correctly verifies that the returned object satisfies the async iterator interface and properly delegates to the underlying iterator's next and return methods.


46-51: ```shell
#!/bin/bash

Display the implementation of createORPCClient to verify nested method support

sed -n '1,200p' packages/client/src/client.ts


</details>
<details>
<summary>packages/durable-event-iterator/src/schemas.ts (1)</summary>

`5-12`: **Well-structured token payload schema.**

The schema correctly defines all necessary JWT fields and custom fields for the durable event iterator token, with appropriate types and validation.

</details>
<details>
<summary>packages/durable-event-iterator/src/plugin.test.ts (2)</summary>

`31-47`: **Thorough testing of plugin header behavior.**

The test correctly verifies that the plugin adds the appropriate header when the response is a client durable event iterator and validates both the header presence and the token extraction.

---

`79-93`: **Good error handling test for corrupted plugin context.**

The test properly simulates plugin context corruption by modifying the interceptor to pass an empty context, ensuring the plugin throws an appropriate error when its internal state is compromised.

</details>
<details>
<summary>packages/durable-event-iterator/src/durable-object/event-storage.test.ts (2)</summary>

`7-25`: **Comprehensive event storage and metadata testing.**

The test correctly verifies that events are stored with auto-incrementing IDs, existing metadata is preserved while ID is overridden, and the underlying SQL storage is properly updated.

---

`87-114`: **Proper time-based event retrieval testing.**

The test correctly uses fake timers to simulate time progression and validates that events can be retrieved based on both ID and timestamp criteria, with proper metadata preservation.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread packages/durable-event-iterator/tsconfig.json
Comment thread package.json
Comment thread playgrounds/cloudflare-worker/src/components/chat-room.tsx
Comment thread packages/durable-event-iterator/src/event-iterator.test.ts
Comment thread packages/durable-event-iterator/src/durable-object/event-storage.ts
Comment thread playgrounds/cloudflare-worker/worker/index.ts
Comment thread packages/durable-event-iterator/src/durable-object/handler.ts
Comment thread playgrounds/cloudflare-worker/worker-configuration.d.ts
Comment thread packages/durable-event-iterator/src/client/event-iterator.ts
Comment thread packages/durable-event-iterator/src/durable-object/object.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/durable-event-iterator/src/durable-object/event-storage.ts (1)

108-120: Reconsider the aggressive schema reset on storage errors.

As previously noted, resetting the entire schema on any storage error could lead to significant data loss and break event continuity for connected clients. Consider implementing more targeted error recovery strategies that preserve existing data.

packages/durable-event-iterator/src/client/plugin.ts (1)

69-72: Add type validation before string cast.

The output is directly cast to string without validation. If the response is not a string, this could cause runtime errors when constructing the URL.

Apply this diff to add type validation:

-      const token = output as string
+      if (typeof output !== 'string') {
+        throw new TypeError('[DurableEventIteratorLinkPlugin] Expected JWT string for durable event iterator response')
+      }
+      const token = output
       const url = new URL(await value(this.url))
       url.searchParams.append(DURABLE_EVENT_ITERATOR_TOKEN_PARAM, token)
🧹 Nitpick comments (3)
packages/durable-event-iterator/src/client/plugin.ts (1)

88-92: Consider making retry configuration customizable.

While infinite retries may be appropriate for durable event subscriptions, consider allowing users to configure this behavior through plugin options for different use cases.

Add a retry option to the plugin:

export interface DurableEventIteratorLinkPluginOptions extends Omit<RPCLinkOptions<object>, 'websocket'> {
  /**
   * The WebSocket URL to connect to the Durable Event Iterator Object.
   */
  url: Value<Promisable<string | URL>>

  /**
   * Polyfill for WebSocket construction.
   */
  WebSocket?: typeof WebSocket
+
+  /**
+   * Number of retry attempts for the durable subscription.
+   * @default Number.POSITIVE_INFINITY
+   */
+  subscriptionRetries?: number
}

Then use it in the subscription:

       const iterator = await durableClient.subscribe(undefined, {
         context: {
-          retry: Number.POSITIVE_INFINITY,
+          retry: this.linkOptions.subscriptionRetries ?? Number.POSITIVE_INFINITY,
         },
       })
apps/content/docs/integrations/durable-event-iterator.md (2)

44-44: Fix grammar and formatting issues.

Several grammar improvements needed for better clarity:

Apply these grammar fixes:

-## Setup your Durable Object
+## Set up your Durable Object
-## Server Side Usage
+## Server-Side Usage
-## Client Side Usage  
+## Client-Side Usage
-You can use `eventRetentionSeconds` option to change
+You can use the `eventRetentionSeconds` option to change
-you need to define methods that accepts a `WebSocket` instance as the first argument and returns
+you need to define methods that accept a `WebSocket` instance as the first argument and return

Also applies to: 102-102, 143-143, 175-175, 179-179, 227-227, 250-250


10-12: Clarify the current support status.

The current wording is somewhat contradictory about support for other platforms.

Consider rephrasing for clarity:

::: info
-This feature is not limited to [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/), but it is approachable and currently the only supported option.
+This feature is designed to support multiple platforms in the future. Currently, only [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/) is supported.
:::
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5de832d and 3c68152.

📒 Files selected for processing (5)
  • apps/content/docs/integrations/durable-event-iterator.md (1 hunks)
  • packages/durable-event-iterator/src/client/plugin.ts (1 hunks)
  • packages/durable-event-iterator/src/durable-object/event-storage.ts (1 hunks)
  • playgrounds/cloudflare-worker/worker/dos/chat-room.ts (1 hunks)
  • playgrounds/cloudflare-worker/worker/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • playgrounds/cloudflare-worker/worker/index.ts
🧰 Additional context used
🪛 LanguageTool
apps/content/docs/integrations/durable-event-iterator.md

[grammar] ~44-~44: This sentence should probably be started with a verb instead of the noun ‘Setup’. If not, consider inserting a comma for better clarity.
Context: ...t and may change in the future. ::: ## Setup your Durable Object ::: warning This s...

(SENT_START_NN_DT)


[uncategorized] ~102-~102: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ndler export { ChatRoom } ``` ## Server Side Usage Here we define two procedures: o...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~103-~103: Possible missing comma found.
Context: ...{ ChatRoom } ``` ## Server Side Usage Here we define two procedures: one for liste...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~143-~143: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...teratorHandlerPlugin(), ], }) ``` ## Client Side Usage On the client side, you only nee...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~175-~175: You might be missing the article “the” here.
Context: ...ring network interruptions. You can use eventRetentionSeconds option to change ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[grammar] ~179-~179: Possible subject-verb agreement error.
Context: ...use it, you need to define methods that accepts a WebSocket instance as the first arg...

(NNS_THAT_AGREEMENT)


[uncategorized] ~179-~179: This verb does not appear to agree with the subject. Consider using a different form.
Context: ...ket` instance as the first argument and returns an [oRPC Client](/docs/client/server-si...

(AI_EN_LECTOR_REPLACEMENT_VERB_AGREEMENT)


[uncategorized] ~227-~227: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... .callable(), } } } ### Server Side Usage ts import { DurableEventItera...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~250-~250: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... like user IDs or preferences. ::: ### Client Side Usage You can invoke methods defined i...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: lint
🔇 Additional comments (2)
playgrounds/cloudflare-worker/worker/dos/chat-room.ts (1)

1-18: Clean refactor to use DurableEventIteratorObject!

The simplified implementation correctly leverages the new durable event iterator abstraction, removing manual WebSocket management complexity.

apps/content/docs/integrations/durable-event-iterator.md (1)

59-65: ```shell
#!/bin/bash

Print DurableEventIteratorObjectOptions interface definition and its fields

rg -n -A 20 "export interface DurableEventIteratorObjectOptions" -t ts packages/durable-event-iterator/src/durable-object/object.ts


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread packages/durable-event-iterator/src/durable-object/event-storage.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/content/docs/integrations/durable-event-iterator.md (2)

44-50: Standard-English-heading fixes

Headings should use the verb-phrase “Set up” (two words) and hyphenate compound adjectives (“Server-Side”, “Client-Side”) for correctness and consistency.

-## Setup your Durable Object
+## Set up Your Durable Object

-## Server Side Usage
+## Server-Side Usage

-### Server Side Usage
+### Server-Side Usage

-## Client Side Usage
+## Client-Side Usage

Also applies to: 102-104, 143-147, 227-231, 250-254


154-155: Prefer wss:// in production docs

ws:// will be rejected on HTTPS pages and is unsafe over the public internet. Consider:

-      url: 'ws://localhost:3000/chat-room',
+      url: 'wss://example.com/chat-room', // use wss in production
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17b3b1a and 866cc1e.

📒 Files selected for processing (1)
  • apps/content/docs/integrations/durable-event-iterator.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
apps/content/docs/integrations/durable-event-iterator.md

[grammar] ~44-~44: This sentence should probably be started with a verb instead of the noun ‘Setup’. If not, consider inserting a comma for better clarity.
Context: ...t and may change in the future. ::: ## Setup your Durable Object ::: warning This s...

(SENT_START_NN_DT)


[uncategorized] ~102-~102: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ndler export { ChatRoom } ``` ## Server Side Usage Here we define two procedures: o...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~103-~103: Possible missing comma found.
Context: ...{ ChatRoom } ``` ## Server Side Usage Here we define two procedures: one for liste...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~143-~143: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...teratorHandlerPlugin(), ], }) ``` ## Client Side Usage On the client side, you only nee...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~227-~227: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... .callable(), } } } ### Server Side Usage ts import { DurableEventItera...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~250-~250: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... like user IDs or preferences. ::: ### Client Side Usage You can invoke methods defined i...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: lint
  • GitHub Check: publish-commit
  • GitHub Check: test
🔇 Additional comments (2)
apps/content/docs/integrations/durable-event-iterator.md (2)

90-93: Avoid hard-coded secrets in examples

Placing signingKey: 'secret-key' directly in the sample may encourage insecure copy-pasting. Prefer an environment variable placeholder.

-        signingKey: 'secret-key', // Replace with your actual signing key
+        // NEVER commit real secrets.  Prefer an env var or secrets manager:
+        signingKey: env.SIGNING_KEY,

123-127: Verify that stub.publishMessage is actually exposed on DurableObjectStub

Cloudflare stubs normally only expose fetch(). If publishMessage is added via the oRPC layer you introduced, great—otherwise this call will explode at runtime. Please confirm generation/augmentation of the stub or update the example (e.g. await stub.fetch(...)).

Comment thread apps/content/docs/integrations/durable-event-iterator.md
@dinwwwh dinwwwh merged commit ce8559f into main Jun 24, 2025
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Streaming/Websocket for serverless/edge runtime?

2 participants