feat(durable-event-iterator): implement event iterator with Cloudflare hibernation and more#668
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughA new experimental package, Changes
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
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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_DurableEventIteratorObjectas 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_ClientDurableEventIteratorandexperimental_createClientDurableEventIterator) and aDurableEventIteratorLinkPluginto enable seamless, type-safe asynchronous iteration over events streamed from Durable Objects via WebSockets. - Server-Side Event Subscription & Upgrade: Added
experimental_DurableEventIteratorBuilderfor server-side subscription to event channels, generating authenticated JWTs for clients. Also introducedexperimental_upgradeDurableEventIteratorRequestto 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
ChatRoomDurable 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
-
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 ReportAttention: Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) {
More templates
@orpc/arktype
@orpc/client
@orpc/contract
@orpc/experimental-durable-event-iterator
@orpc/hey-api
@orpc/nest
@orpc/openapi
@orpc/openapi-client
@orpc/react
@orpc/react-query
@orpc/server
@orpc/shared
@orpc/solid-query
@orpc/standard-server
@orpc/standard-server-aws-lambda
@orpc/standard-server-fetch
@orpc/standard-server-node
@orpc/standard-server-peer
@orpc/svelte-query
@orpc/tanstack-query
@orpc/valibot
@orpc/vue-colada
@orpc/vue-query
@orpc/zod
commit: |
|
/gemini review |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
fetchmethod inobject.tsto 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_INFINITYmight not be appropriate for all use cases.
🧹 Nitpick comments (26)
packages/durable-event-iterator/.gitignore (1)
16-22: Minor nit: extend generated-file ignoresWhile 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
.gitignorealready covers them.packages/durable-event-iterator/tsconfig.json (1)
11-18: Nit: keep test exclusions DRYThe 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 changesThe experimental package is consumed via the
nextdist-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 constantAs 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
iteratorstate 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
iatandexp, 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 avoidingas anytype casting.Using
as anybypasses 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 namepublishMessage2.The method name
publishMessage2appears inconsistent. If this is the intended name, consider adding a comment explaining the difference frompublishMessage. 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 unnecessaryasynckeyword from describe block.The
asynckeyword is not needed fordescribeblocks 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 = 1100Alternatively, 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
iattimestamp. 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
StandardRPCJsonSerializerOptionsbut 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 UsageAlso 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: undefinedand then spreads the original value, which means ifattwas present in the input, it will override theundefined. This effectively preserves the originalattvalue 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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 filesThe 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 pathsThe three reference entries assume the sibling packages live exactly one level up.
If these packages are published independently, use their ownpackage.json#name(e.g."path": "../client"→"path": "../client/tsconfig.json") sotsc -bresolves 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 roottsconfig.jsonto surface type errors, this package will be silently skipped.
Either add a project reference ({ "path": "./packages/durable-event-iterator" }) or ensure the package’s owntsc -brun is integrated into the pipeline.packages/shared/src/index.ts (1)
16-16: 👍 ExportingJsonValueimproves cross-package typingRe-export looks correct and matches the existing pattern.
package.json (1)
55-57:pnpm.onlyBuiltDependencies– verify runtime requirementMoving
better-sqlite3to optional or dev deps may also let you drop it fromonlyBuiltDependencies; otherwise CI will still attempt native builds.apps/content/.vitepress/config.ts (1)
156-160: Ensure target doc page exists & build passesThe new sidebar entry looks fine.
Please double-check that/docs/integrations/durable-event-iterator.md(or anindex.mdin 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 | headpackages/durable-event-iterator/src/durable-object/index.ts (1)
1-5: Barrel export LGTMSimple re-exports keep consumer imports tidy; no issues detected.
packages/durable-event-iterator/src/client/index.ts (1)
1-2: Client barrel export LGTMMatches the pattern used in the server side; good for DX.
packages/durable-event-iterator/tsconfig.test.json (1)
4-7: Test tsconfig looks correctDecorator 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
InferDurableEventIteratorObjectRPCcorrectly:
- 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, andvalibotshould 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, theattoption 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, theattoption 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
ClientDurableEventIteratortype 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 updateThe runtime version comment has been appropriately updated to reflect the newer workerd version.
5192-5192: LGTM: Workflow retention duration type addedThe new
WorkflowRetentionDurationtype export appropriately extends the workflow type hierarchy for retention policies.
5323-5323: LGTM: Enhanced tail stream onset interfaceThe addition of the
executionModelproperty to theTailStream.Onsetinterface provides valuable execution context information.
5366-5366: LGTM: Improved type specificity for return infoRestricting the
infoproperty in theTailStream.Returninterface toFetchResponseInfoprovides better type safety and clarity.
5718-5725: LGTM: Workflow retention configuration addedThe addition of the
retentionproperty toWorkflowInstanceCreateOptionsappropriately 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
thenmethod is required by thePromiseLikeinterface. The static analysis warning about not addingthento 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
neverto 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 tsapps/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
nextandreturnmethods.
46-51: ```shell
#!/bin/bashDisplay 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 -->
There was a problem hiding this comment.
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 returnAlso 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
📒 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/bashPrint 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 -->
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/content/docs/integrations/durable-event-iterator.md (2)
44-50: Standard-English-heading fixesHeadings 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 UsageAlso applies to: 102-104, 143-147, 227-231, 250-254
154-155: Preferwss://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
📒 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 examplesPlacing
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 thatstub.publishMessageis actually exposed onDurableObjectStubCloudflare stubs normally only expose
fetch(). IfpublishMessageis 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(...)).
Closes: https://github.com/unnoq/orpc/issues/107
Summary by CodeRabbit
New Features
Documentation
Refactor
Chores
Tests
End-users gain reliable, persistent event streaming with seamless reconnection and recovery, enhanced by improved documentation and practical examples.