Skip to content

feat(client, server): Message Port Adapter#528

Merged
dinwwwh merged 14 commits intomainfrom
feat/client-server/message-port-adapter
May 22, 2025
Merged

feat(client, server): Message Port Adapter#528
dinwwwh merged 14 commits intomainfrom
feat/client-server/message-port-adapter

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented May 21, 2025

Replaces: https://github.com/unnoq/orpc/pull/500

Summary by CodeRabbit

  • New Features

    • Introduced message port adapters for oRPC enabling type-safe RPC communication across Electron, browser extensions, and Node.js worker threads.
    • Added detailed documentation and integration guides for message port usage in multiple environments.
    • Launched new Electron and Browser Extension playgrounds with example apps demonstrating full-stack oRPC integration.
    • Provided React components and hooks for querying and mutating data using oRPC in both Electron and browser extension UIs.
    • Added authentication, retry, and database middlewares with typed API routes for user and planet management in playgrounds.
    • Implemented server-sent events (SSE) endpoints in playground environments.
  • Bug Fixes

    • Improved ESLint configuration coverage for playground directories.
  • Documentation

    • Expanded documentation with new sections and guides for message port adapters, Electron, browser extensions, and worker threads.
    • Updated playground documentation to include new environments and setup instructions.
  • Tests

    • Added extensive test suites for message port adapters, ensuring correct message handling, error processing, and integration with oRPC clients and servers.
  • Chores

    • Added configuration files and package setups for new playgrounds, including TypeScript, Vite, Electron Builder, and WXT configurations.
    • Added new package.json configurations for client, server, and playground packages supporting message-port adapters and playground builds.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 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 May 22, 2025 1:51am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2025

Walkthrough

This update introduces a comprehensive message port adapter for oRPC, enabling type-safe inter-process communication across Electron, browser extensions, and Node.js worker threads. It adds new client and server adapter implementations, extensive documentation, and integration guides. The change also includes new playgrounds for Electron and browser extensions, with full middleware, routing, and schema validation for sample APIs, and associated test suites.

Changes

File(s) / Group Change Summary
apps/content/.vitepress/config.ts, apps/content/docs/adapters/message-port.md, apps/content/docs/integrations/browser-extension.md, apps/content/docs/integrations/electron.md, apps/content/docs/integrations/worker-threads.md, apps/content/docs/playgrounds.md Updated VitePress config and documentation to add message port adapter and integration guides for Electron, browser extensions, and worker threads. Added playground entries.
eslint.config.js Broadened ESLint rule disabling 'node/prefer-global/process' to all playgrounds.
package.json Added pnpm config with onlyBuiltDependencies for electron and esbuild.
packages/client/package.json, packages/server/package.json Added message-port adapter export entries for both client and server packages.
packages/client/src/adapters/message-port/index.ts, packages/server/src/adapters/message-port/index.ts Added adapter index files re-exporting message port adapter modules.
packages/client/src/adapters/message-port/link-client.ts Introduced experimental_LinkMessagePortClient for message port-based RPC client communication.
packages/client/src/adapters/message-port/message-port.ts Defined message port interfaces and utility functions for message and close event handling.
packages/client/src/adapters/message-port/rpc-link.ts Added experimental_RPCLink class for message port-based RPC link.
packages/server/src/adapters/message-port/handler.ts Added experimental_MessagePortHandler for server-side message port handling.
packages/server/src/adapters/message-port/rpc-handler.ts Introduced experimental_RPCHandler for integrating message port handler with standard RPC logic.
packages/client/src/adapters/message-port/message-port.test.ts, packages/client/src/adapters/message-port/rpc-link.test.ts, packages/server/src/adapters/message-port/rpc-handler.test.ts, packages/server/tests/message-port.test.ts Added test suites covering message port adapter utilities, client/server integration, and supported data types.
playgrounds/browser-extension/** Added a new browser extension playground with full ORPC integration, middleware, authentication, database, routing, React popup UI, and supporting configs and scripts.
playgrounds/electron/** Added a new Electron playground with main, preload, and renderer processes, ORPC integration, middleware, routing, React renderer UI, and supporting configs and scripts.

Changes (continued)

File(s) / Group Change Summary
playgrounds/browser-extension/package.json, playgrounds/electron/package.json Added package manifests for new playgrounds with scripts and dependencies.
playgrounds/browser-extension/tsconfig.json, playgrounds/electron/tsconfig.json, playgrounds/electron/tsconfig.node.json, playgrounds/electron/tsconfig.web.json Added TypeScript configuration files for browser extension and Electron playgrounds.
playgrounds/electron/electron-builder.yml, playgrounds/electron/electron.vite.config.ts Added Electron builder and Vite configurations for Electron playground.
playgrounds/electron/.vscode/settings.json, playgrounds/browser-extension/.gitignore, playgrounds/electron/.gitignore Added editor and gitignore configurations for new playgrounds.

Sequence Diagram(s)

oRPC Message Port Adapter: Client-Server Communication (High-Level)

sequenceDiagram
    participant Client
    participant MessagePort
    participant Server

    Client->>MessagePort: postMessage(request)
    MessagePort->>Server: onMessage(event)
    Server->>MessagePort: postMessage(response)
    MessagePort->>Client: onMessage(event)
Loading

Electron Integration: Renderer to Main Process via Message Port

sequenceDiagram
    participant Renderer
    participant Preload
    participant Main Process

    Renderer->>Preload: window.postMessage(port)
    Preload->>Main Process: ipcRenderer.postMessage('start-orpc-server', port)
    Main Process->>Main Process: handler.upgrade(port)
    Renderer->>Renderer: client = new RPCLink(port)
Loading

Browser Extension Integration: Popup to Background via Message Port

sequenceDiagram
    participant Popup
    participant Background

    Popup->>Background: browser.runtime.connect()
    Background->>Background: handler.upgrade(port)
    Popup->>Popup: client = new RPCLink(port)
Loading

Poem

In the warren of code, new tunnels appear,
Message ports connect far and near.
Electron and browsers now hop in the game,
With playgrounds for all—no two are the same!
Tests and docs sprout up with glee,
oRPC’s garden grows wild and free.
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d291b7b and af63fe1.

📒 Files selected for processing (2)
  • playgrounds/browser-extension/.gitignore (1 hunks)
  • playgrounds/browser-extension/.vscode/settings.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • playgrounds/browser-extension/.vscode/settings.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • playgrounds/browser-extension/.gitignore
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: publish-commit
  • GitHub Check: lint

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 21, 2025

More templates

@orpc/arktype

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

@orpc/client

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

@orpc/nest

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

@orpc/contract

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

@orpc/openapi

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

@orpc/openapi-client

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

@orpc/react

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

@orpc/react-query

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

@orpc/server

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

@orpc/shared

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

@orpc/solid-query

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

@orpc/standard-server

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

@orpc/standard-server-fetch

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

@orpc/standard-server-node

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

@orpc/standard-server-peer

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

@orpc/svelte-query

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

@orpc/valibot

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

@orpc/vue-colada

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

@orpc/vue-query

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

@orpc/zod

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

commit: af63fe1

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: 19

🧹 Nitpick comments (66)
playgrounds/browser-extension/.gitignore (1)

1-27: Consider ignoring additional build artifacts
You’ve covered logs, node_modules, and some build outputs (.output). It may be helpful to also ignore other common generated artifacts like dist/, build/, .cache/, and TypeScript build info (*.tsbuildinfo) to prevent stray files from being committed.

playgrounds/electron/README.md (1)

15-19: Add alt text to sponsor image
Per markdown accessibility guidelines (MD045), the <img> tag should include an alt attribute. For example:

<img src="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg" alt="oRPC Sponsors"/>
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

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

(MD045, no-alt-text)

packages/server/src/adapters/message-port/index.ts (1)

1-3: Aggregator module for message-port adapter
This index file cleanly re-exports from handler and rpc-handler. Consider adding a brief JSDoc comment at the top to document the purpose of this module, improving IDE discoverability and future maintainability.

apps/content/docs/playgrounds.md (1)

13-24: Clarify missing StackBlitz links for new playgrounds.

The "Electron Playground" and "Browser Extension Playground" rows leave the StackBlitz column empty, which may confuse readers. Consider adding placeholder text (e.g., "N/A", "Coming Soon") or a brief note to indicate why these links are unavailable.

playgrounds/browser-extension/README.md (2)

17-18: Add alt text to sponsor image.

Per markdownlint (MD045), images should include alt text for accessibility. For example:

- <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
+ <img alt="unnoq sponsors badge" src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

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

(MD045, no-alt-text)


1-11: Consider adding installation prerequisites.

The README instructs users to run npm run dev, but it may be helpful to include steps for installing dependencies (npm install) and specifying any Node.js version requirements to streamline the getting started experience.

playgrounds/browser-extension/entrypoints/content.ts (2)

1-6: Consider expanding content script functionality.

The content script currently only logs a message to the console. Consider implementing actual functionality to interact with web pages or demonstrate integration with the background script via message ports.

Example functionality could include:

  • DOM manipulation on matched pages
  • Communication with the background script using ports
  • Injecting UI elements that use the RPC capabilities

2-2: Consider more specific URL matching patterns.

The current pattern *://*.google.com/* is very broad. For a playground example, consider using more specific patterns that clearly demonstrate when and where the content script activates.

-  matches: ['*://*.google.com/*'],
+  matches: ['*://*.google.com/search?*'],
playgrounds/browser-extension/entrypoints/popup/App.tsx (1)

4-14: Consistent Component Typing: Other playgrounds (e.g., Electron) annotate the App return type as React.JSX.Element. Consider adding : React.JSX.Element to maintain consistency and leverage TypeScript’s explicit typing.

playgrounds/electron/src/renderer/src/main.tsx (1)

1-14: Modern React setup with proper state management

The React entry point follows best practices:

  • Uses React 18's createRoot API
  • Implements StrictMode for development checks
  • Sets up React Query for server state management
  • Properly structures component hierarchy

Consider adding error handling for the case where the root element isn't found, rather than using the non-null assertion operator (!).

-createRoot(document.getElementById('root')!).render(
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+  console.error('Root element not found');
+  throw new Error('Root element not found');
+}
+createRoot(rootElement).render(
playgrounds/browser-extension/entrypoints/popup/main.tsx (1)

1-14: Standard React entry point for browser extension

The React setup is appropriate for a browser extension popup:

  • Properly imports required dependencies
  • Creates a QueryClient for React Query state management
  • Sets up React 18's createRoot API
  • Wraps the App in StrictMode and QueryClientProvider

As with the Electron renderer, consider adding error handling instead of using the non-null assertion operator (!).

-ReactDOM.createRoot(document.getElementById('root')!).render(
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+  console.error('Root element not found');
+  throw new Error('Root element not found');
+}
+ReactDOM.createRoot(rootElement).render(
playgrounds/electron/src/main/middlewares/auth.ts (1)

4-21: Authentication middleware with clear error handling

The middleware correctly:

  • Defines a typed context with optional session and user
  • Uses nullish coalescing for optimization (as documented)
  • Throws an error for unauthorized users
  • Passes the user to downstream middleware when authenticated

For better error clarity, consider using a custom error class or adding a status code to make error handling more consistent across the app.

-      throw new Error('UNAUTHORIZED')
+      const error = new Error('User not authenticated');
+      error.code = 'UNAUTHORIZED';
+      error.statusCode = 401;
+      throw error;
playgrounds/electron/src/main/schemas/auth.ts (1)

1-10: Authentication schemas look good, but consider enhancing password validation.

The validation schemas for credentials and tokens are well-structured and correctly use Zod for type safety. The email validation is properly implemented with the .email() method.

Consider enhancing the password validation with additional constraints for better security:

 export const CredentialSchema = z.object({
   email: z.string().email(),
-  password: z.string(),
+  password: z.string().min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 
+    { message: "Password must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number" }),
 })
playgrounds/browser-extension/entrypoints/background/schemas/auth.ts (1)

1-10: Consider extracting duplicate auth schemas to a shared package.

The authentication schemas are identical to those in playgrounds/electron/src/main/schemas/auth.ts, suggesting an opportunity to extract these to a shared package to avoid duplication.

As with the Electron version, consider enhancing password validation:

 export const CredentialSchema = z.object({
   email: z.string().email(),
-  password: z.string(),
+  password: z.string().min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 
+    { message: "Password must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number" }),
 })
playgrounds/electron/src/main/middlewares/retry.ts (1)

3-35: Retry middleware works, but could benefit from some enhancements.

The retry middleware implementation correctly follows best practices for middleware deduplication and provides basic retry functionality.

Consider these enhancements:

  1. Add exponential backoff between retry attempts
  2. Add selective retry based on error types
  3. Include optional logging of retry attempts
-export function retry(options: { times: number }) {
+export function retry(options: { 
+  times: number;
+  backoff?: boolean; 
+  delay?: number;
+  shouldRetry?: (error: unknown) => boolean;
+  onRetry?: (error: unknown, attempt: number) => void;
+}) {
   /**
    * Best practices for dedupe-middlewares
    * {@link https://orpc.unnoq.com/docs/best-practices/dedupe-middleware}
    */
   return os
     .$context<{ canRetry?: boolean }>()
     .middleware(({ context, next }) => {
       const canRetry = context.canRetry ?? true

       if (!canRetry) {
         return next()
       }

       let times = 0
       while (true) {
         try {
           return next({
             context: {
               canRetry: false,
             },
           })
         }
         catch (e) {
           if (times >= options.times) {
             throw e
           }
+          
+          // Check if this error should be retried
+          if (options.shouldRetry && !options.shouldRetry(e)) {
+            throw e;
+          }
+          
+          // Call onRetry callback if provided
+          if (options.onRetry) {
+            options.onRetry(e, times + 1);
+          }
           
           times++
+          
+          // Implement delay between retries if configured
+          if (options.delay) {
+            const delayMs = options.backoff 
+              ? options.delay * Math.pow(2, times - 1) 
+              : options.delay;
+            await new Promise(resolve => setTimeout(resolve, delayMs));
+          }
         }
       }
     })
 }
playgrounds/browser-extension/entrypoints/popup/components/orpc-mutation.tsx (2)

31-32: Fix line break in variable assignment

The line break in the description assignment makes the code harder to read. Consider placing this on a single line.

-          const description
-          = (form.get('description') as string | null) ?? undefined
+          const description = (form.get('description') as string | null) ?? undefined

7-19: Add loading state indicator for better user feedback

The mutation implementation doesn't provide any visual feedback during the mutation process. Consider showing a loading state to improve the user experience.

-  const { mutate } = useMutation(
+  const { mutate, isPending } = useMutation(
     orpc.planet.create.mutationOptions({
       onSuccess() {
         queryClient.invalidateQueries({
           queryKey: orpc.planet.key(),
         })
+        // Optional: Reset form after successful submission
+        const form = document.querySelector('form') as HTMLFormElement;
+        form?.reset();
       },
       onError(error) {
         console.error(error)
         alert(error.message)
       },
     }),
   )

Then update the submit button:

-        <button type="submit">Submit</button>
+        <button type="submit" disabled={isPending}>
+          {isPending ? 'Submitting...' : 'Submit'}
+        </button>
packages/server/src/adapters/message-port/rpc-handler.ts (1)

13-17: Consider adding documentation about the experimental status

The class has an experimental_ prefix, but there's no documentation explaining what this means for users or when it might become stable.

 /**
  * RPC Handler for common message port implementations.
  *
+ * @experimental This API is experimental and may change in future releases.
  * @see {@link https://orpc.unnoq.com/docs/rpc-handler RPC Handler Docs}
  * @see {@link https://orpc.unnoq.com/docs/adapters/message-port Message Port Adapter Docs}
  */
playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (2)

31-32: Fix line break in variable assignment

The line break in the description assignment makes the code harder to read. Consider placing this on a single line.

-          const description
-          = (form.get('description') as string | null) ?? undefined
+          const description = (form.get('description') as string | null) ?? undefined

7-19: Add loading state indicator for better user feedback

The mutation implementation doesn't provide any visual feedback during the mutation process. Consider showing a loading state to improve the user experience.

-  const { mutate } = useMutation(
+  const { mutate, isPending } = useMutation(
     orpc.planet.create.mutationOptions({
       onSuccess() {
         queryClient.invalidateQueries({
           queryKey: orpc.planet.key(),
         })
+        // Optional: Reset form after successful submission
+        const form = document.querySelector('form') as HTMLFormElement;
+        form?.reset();
       },
       onError(error) {
         console.error(error)
         alert(error.message)
       },
     }),
   )

Then update the submit button:

-        <button type="submit">Submit</button>
+        <button type="submit" disabled={isPending}>
+          {isPending ? 'Submitting...' : 'Submit'}
+        </button>
playgrounds/electron/src/renderer/src/playground-client.ts (1)

4-7: Consider commenting hardcoded credentials.

While acceptable for a playground demonstration, adding a comment to clarify that hardcoded credentials should never be used in production code would be helpful.

+// NOTE: Hardcoded credentials for demo purposes only. In production, use proper authentication methods.
 const token = await orpc.auth.signin({
   email: 'john@doe.com',
   password: '123456',
 })
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)

9-9: Consider adding error handling for postMessage.

While the implementation is correct, it might be worth adding error handling in case the message doesn't reach the main process.

-window.postMessage('start-orpc-client', '*', [serverPort])
+try {
+  window.postMessage('start-orpc-client', '*', [serverPort])
+} catch (error) {
+  console.error('Failed to initialize oRPC client connection:', error)
+}
apps/content/docs/integrations/worker-threads.md (3)

14-26: Consider highlighting the experimental nature of the API

The code example uses experimental_RPCHandler which suggests this API might change in the future. This should be explicitly mentioned to set proper expectations.

Consider adding a note like:

 import { experimental_RPCHandler as RPCHandler } from '@orpc/server/message-port'
+
+// Note: The API is marked as experimental and may change in future releases

32-43: Consider highlighting the experimental nature of the API

Similar to the main thread example, the experimental nature of experimental_RPCLink should be explicitly noted.

Consider adding a note like:

 import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
+
+// Note: The API is marked as experimental and may change in future releases

45-47: Enhance the info box with specific links

The info box could be more helpful with direct links to the client examples.

Consider expanding it like:

 :::info
-This only shows how to configure the link. For full client examples, see [Client-Side Clients](/docs/client/client-side).
+This only shows how to configure the link. For full client examples, see:
+- [Client-Side Clients](/docs/client/client-side)
+- [React Integration](/docs/client/react) (if applicable)
+- [React Query Integration](/docs/client/react-query) (if applicable)
 :::
playgrounds/electron/package.json (1)

14-16: Inconsistent build script pattern

The build:win script uses pnpm run build && electron-builder --win pattern, while build:mac and build:linux scripts don't include the pnpm run prefix.

For consistency, consider standardizing the pattern across all platform-specific build scripts:

-  "build:mac": "electron-vite build && electron-builder --mac",
-  "build:linux": "electron-vite build && electron-builder --linux"
+  "build:mac": "pnpm run build && electron-builder --mac",
+  "build:linux": "pnpm run build && electron-builder --linux"
playgrounds/browser-extension/package.json (2)

6-6: Update the placeholder description

The description is currently a placeholder ("manifest.json description"). This should be updated with a meaningful description that explains the purpose of this playground.

-  "description": "manifest.json description",
+  "description": "Browser extension playground demonstrating oRPC integration with message port adapters",

17-33: Consider specifying exact versions for consistency

The package mixes versioning styles with exact versions for ORPC packages ("next") and caret ranges for other dependencies ("^5.75.7"). For a controlled playground environment, consider standardizing to exact versions.

This would help ensure reproducible builds and minimize unexpected behavior from dependency updates.

playgrounds/browser-extension/entrypoints/background/middlewares/retry.ts (1)

17-33: Consider adding cancellation support

The retry logic doesn't account for request cancellation, which may be important in a browser extension context where user navigation can interrupt operations.

Consider incorporating an AbortSignal for cancellation:

-return os
-  .$context<{ canRetry?: boolean }>()
+return os
+  .$context<{ canRetry?: boolean; signal?: AbortSignal }>()
   .middleware(({ context, next }) => {
     const canRetry = context.canRetry ?? true
+    const { signal } = context;

     if (!canRetry) {
       return next()
     }

     let times = 0
     while (true) {
+      // Check for cancellation
+      if (signal?.aborted) {
+        throw new Error('Operation was cancelled');
+      }
       try {
playgrounds/electron/src/renderer/src/components/orpc-query.tsx (2)

13-27: Consider enhancing error state feedback

While the loading and error states are handled appropriately, the error message "Something went wrong" is very generic. Consider showing more specific error information or a retry option to improve user experience.

if (status === 'error') {
  return (
    <p>
-      Something went wrong.
+      Failed to load planets. <button onClick={() => refetch()}>Retry</button>
    </p>
  )
}

33-53: Consider adding accessibility attributes to the table

The table implementation looks good, but adding accessibility attributes would improve the experience for users with screen readers.

-<table>
+<table aria-label="List of planets">
  <thead>
    <tr>
-     <th>ID</th>
-     <th>Name</th>
-     <th>Description</th>
-     <th>Image</th>
+     <th scope="col">ID</th>
+     <th scope="col">Name</th>
+     <th scope="col">Description</th>
+     <th scope="col">Image</th>
    </tr>
  </thead>
playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (2)

13-27: Consider enhancing error state with more details

The error handling shows a generic message without providing users with more context or recovery options. Consider improving the error state UI.

if (status === 'error') {
  return (
    <p>
-      Something went wrong.
+      Failed to load planets. <button onClick={() => refetch()}>Retry</button>
    </p>
  )
}

33-53: Add accessibility attributes to table elements

The table implementation should include accessibility attributes to better support screen readers and comply with accessibility standards.

-<table>
+<table aria-label="List of planets">
  <thead>
    <tr>
-     <th>ID</th>
-     <th>Name</th>
-     <th>Description</th>
-     <th>Image</th>
+     <th scope="col">ID</th>
+     <th scope="col">Name</th>
+     <th scope="col">Description</th>
+     <th scope="col">Image</th>
    </tr>
  </thead>
playgrounds/electron/src/preload/index.ts (1)

4-24: Consider enhancing type safety for window extensions.

The code uses @ts-expect-error to bypass TypeScript checks when adding properties to the window object. This approach works but sacrifices type safety.

Consider enhancing type safety by creating a proper declaration file:

// Add to a declaration file (e.g., preload.d.ts):
+interface Window {
+  electron: typeof import('@electron-toolkit/preload').electronAPI;
+  api: Record<string, unknown>;
+}

Then remove the @ts-expect-error comments:

-  // @ts-expect-error (define in dts)
   window.electron = electronAPI
-  // @ts-expect-error (define in dts)
   window.api = api
playgrounds/browser-extension/entrypoints/popup/playground-client.ts (2)

4-7: Be cautious with hardcoded credentials in shared code.

While this is acceptable for a playground demonstration, it's worth noting that hardcoded credentials should be avoided in production code. Consider adding a comment indicating this is for demonstration purposes only.

+// NOTE: Hardcoded credentials for demonstration purposes only.
+// In a real application, use secure authentication methods.
 const token = await orpc.auth.signin({
   email: 'john@doe.com',
   password: '123456',
 })

1-2: Consider adding a descriptive comment or JSDoc for this demo file.

As this file serves as an example of using the oRPC client with error handling, adding a descriptive comment at the top would help readers understand its purpose more quickly.

+/**
+ * Playground client demonstration for browser extension popup.
+ * This file shows how to use the oRPC client with proper error handling
+ * using the `safe` utility.
+ */
 import { client as orpc } from './lib/orpc'
 import { safe } from '@orpc/client'
apps/content/docs/adapters/message-port.md (1)

44-46: Consider adding an example with actual client creation.

While you reference the client-side guide, it might be helpful to include a small complete example showing how to actually create and use the client after setting up the link.

 :::info
 This only shows how to configure the link. For full client examples, see [Client-Side Clients](/docs/client/client-side).
+
+Here's a minimal example of creating a client:
+
+```ts
+import { createClient } from '@orpc/client'
+import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
+
+const client = createClient({
+  links: [
+    new RPCLink({ port: clientPort })
+  ]
+})
+
+// Use the client
+const result = await client.someNamespace.someMethod({ /* input */ })
+```
 :::
packages/client/src/adapters/message-port/link-client.ts (3)

29-32: Consider error handling in the call method.

The call method doesn't include explicit error handling. While errors from this.peer.request() will propagate naturally, consider adding try/catch to provide more context-specific error information.

 async call(request: StandardRequest, _options: ClientOptions<T>, _path: readonly string[], _input: unknown): Promise<StandardLazyResponse> {
-  const response = await this.peer.request(request)
-  return { ...response, body: () => Promise.resolve(response.body) }
+  try {
+    const response = await this.peer.request(request)
+    return { ...response, body: () => Promise.resolve(response.body) }
+  } catch (error) {
+    // Enhance error with context if needed
+    throw error instanceof Error 
+      ? Object.assign(error, { source: 'message-port-client' })
+      : new Error('Failed to call through message port', { cause: error });
+  }
 }

8-10: Consider documenting the options interface.

Adding JSDoc comments to the interface would improve developer experience when using this class.

+/**
+ * Options for creating a LinkMessagePortClient
+ */
 export interface experimental_LinkMessagePortClientOptions {
+  /**
+   * The message port to use for communication
+   */
   port: SupportedMessagePort
 }

12-13: Add class documentation to clarify 'experimental' prefix.

The class is marked as experimental but there's no documentation explaining what this means or when it might be stabilized.

+/**
+ * A client implementation that communicates over a message port.
+ * 
+ * @remarks
+ * This is an experimental API that may change in the future.
+ */
 export class experimental_LinkMessagePortClient<T extends ClientContext> implements StandardLinkClient<T> {
   private readonly peer: ClientPeer
playgrounds/electron/src/main/router/auth.ts (1)

31-32: Add a comment explaining token generation is simplified.

The handler returns a static token, which is fine for a playground but should be documented for clarity.

-  .handler(async () => {
-    return { token: 'token' }
+  .handler(async () => {
+    // Note: In a real application, this would generate a proper JWT or session token
+    return { token: 'token' }
playgrounds/electron/src/main/schemas/user.ts (2)

10-11: Consider adding more validation constraints

The password field has no validation constraints beyond being a string. Consider adding minimum length requirements and potentially complexity validation for better security.

-    password: z.string(),
+    password: z.string().min(8, "Password must be at least 8 characters"),

14-21: Add more descriptive OpenAPI examples

The examples are good but could be enhanced with additional examples showing edge cases or different valid inputs. This would improve API documentation and testing scenarios.

packages/client/src/adapters/message-port/rpc-link.ts (1)

7-8: Consider breaking down complex type extension

The interface definition extends multiple types in a single line, making it harder to read. Consider breaking this down for better readability.

-export interface experimental_RPCLinkOptions<T extends ClientContext>
-  extends Omit<StandardRPCLinkOptions<T>, 'url' | 'headers' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, experimental_LinkMessagePortClientOptions {}
+export interface experimental_RPCLinkOptions<T extends ClientContext>
+  extends Omit<
+    StandardRPCLinkOptions<T>, 
+    'url' | 'headers' | 'method' | 'fallbackMethod' | 'maxUrlLength'
+  >, 
+  experimental_LinkMessagePortClientOptions {}
playgrounds/electron/src/main/index.ts (1)

62-62: Use structured logging instead of console.log

Using console.log for IPC test makes debugging difficult in production. Consider using a structured logging approach.

-  ipcMain.on('ping', () => console.log('pong'))
+  ipcMain.on('ping', () => {
+    // Use structured logging or a dedicated logger
+    console.log('[IPC Test] Received ping event')
+  })
packages/client/src/adapters/message-port/message-port.test.ts (1)

91-93: Improve error testing specificity

The error test verifies that an error is thrown but doesn't check the specific error message. Consider testing the exact error message for better diagnostic value.

-  it('throws if invalid port', () => {
-    expect(() => onMessagePortMessage({} as any, () => {})).toThrowError()
-  })
+  it('throws if invalid port', () => {
+    expect(() => onMessagePortMessage({} as any, () => {}))
+      .toThrowError('Cannot find a addEventListener/on/onMessage method on the port')
+  })
playgrounds/browser-extension/entrypoints/background/router/auth.ts (1)

31-33: Placeholder token in signin handler.

The handler returns a hardcoded token without actual authentication validation. For demonstration purposes this is fine, but it should be clearly indicated as non-production code.

Consider adding a comment indicating this is a simplified implementation for demonstration:

  .handler(async ({ input, context }) => {
+   // NOTE: This is a simplified implementation for demonstration purposes
    return { token: 'token' }
  })
playgrounds/browser-extension/entrypoints/background/middlewares/auth.ts (2)

14-16: Consider using a more descriptive error for better error handling.

The current error message is simple but lacks context that could help with debugging or displaying user-friendly messages. Consider using a structured error object with additional information.

-    if (!session.user) {
-      throw new Error('UNAUTHORIZED')
-    }
+    if (!session.user) {
+      throw new Error('UNAUTHORIZED: User authentication required')
+      // Or consider using a more structured approach:
+      // throw new UnauthorizedError('Authentication required', { code: 'NO_USER' })
+    }

23-35: Good implementation for a playground example, but needs real authentication in production.

The getSession function currently returns a hardcoded user object, which works well for demonstration purposes. In a production environment, you would need to implement actual authentication logic.

I could help implement a more realistic authentication approach if you'd like to move beyond the playground example.

playgrounds/electron/src/main/schemas/planet.ts (1)

15-20: Consider making the UpdatePlanetSchema more flexible with partial fields.

The current UpdatePlanetSchema requires all fields to be provided even though some are optional. For updates, it's often better to allow partial updates where only changed fields are provided.

-export const UpdatePlanetSchema = z.object({
-  id: z.number().int().min(1),
-  name: z.string(),
-  description: z.string().optional(),
-  image: oz.file().type('image/*').optional(),
-})
+export const UpdatePlanetSchema = z.object({
+  id: z.number().int().min(1),
+  name: z.string().optional(),
+  description: z.string().optional(),
+  image: oz.file().type('image/*').optional(),
+}).refine(data => Object.keys(data).length > 1, {
+  message: "At least one field besides 'id' must be provided for update"
+})
playgrounds/electron/src/main/middlewares/db.ts (1)

67-91: The create method could benefit from more defensive programming.

While the implementation works, it could be more robust with additional validation and error handling.

      create: async (newPlanet, creator) => {
        const id = planets.length + 1
-        const imageUrl = newPlanet.image ? `https://example.com/cdn/${newPlanet.image.name}` : undefined
+        // Sanitize filename if present
+        const imageUrl = newPlanet.image 
+          ? `https://example.com/cdn/${newPlanet.image.name.replace(/[^a-zA-Z0-9.-]/g, '_')}` 
+          : undefined

        const planet: Planet = {
          creator,
          id,
          name: newPlanet.name,
          description: newPlanet.description,
          imageUrl,
        }

+        // Optional: Check for duplicate names
+        const duplicate = planets.find(p => p.name.toLowerCase() === planet.name.toLowerCase())
+        if (duplicate) {
+          throw new Error(`Planet with name "${planet.name}" already exists`)
+        }

        planets.push(planet)

        return planet
      },
apps/content/docs/integrations/browser-extension.md (2)

35-51: Consider enriching the client example with more implementation details.

While the client setup is clear, it would be helpful to provide a more complete example showing how to actually use the link to make RPC calls.

 ```ts
 import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
 
 const port = browser.runtime.connect()
 
 const link = new RPCLink({
   port,
 })
+
+// Example of creating a client
+import { createClient } from '@orpc/client'
+import type { Router } from '../path-to-your-router-type'
+
+const client = createClient<Router>({
+  links: [link],
+})
+
+// Example of making a call
+async function fetchData() {
+  try {
+    const result = await client.yourNamespace.yourMethod({ /* params */ })
+    console.log('Result:', result)
+  } catch (error) {
+    console.error('Error:', error)
+  }
+}

8-12: Consider adding security considerations for browser extension messaging.

The documentation covers the technical setup well but would benefit from mentioning security best practices when using message ports in extensions.

Add a section on security considerations:

## Security Considerations

When using message ports in browser extensions:

- Consider validating message origins to prevent malicious websites from sending messages to your extension
- Be careful about exposing sensitive APIs through your RPC interface
- Implement proper authentication for critical operations
- Consider using Content Security Policy (CSP) to restrict which scripts can communicate with your extension
packages/client/src/adapters/message-port/rpc-link.test.ts (2)

70-76: The port closure test could be more robust

The test correctly verifies error handling when the port is closed, but it might be more reliable to ensure the request is actually in flight before closing the port.

- await new Promise(resolve => setTimeout(resolve, 0))
+ await new Promise(resolve => {
+   // Ensure the message has been sent before closing the port
+   const messageHandler = () => {
+     serverPort.removeEventListener('message', messageHandler)
+     resolve(undefined)
+   }
+   serverPort.addEventListener('message', messageHandler)
+ })

1-77: Consider adding cleanup and additional test cases

The test suite is missing an afterEach block to clean up resources and lacks test cases for error responses from the server.

 beforeEach(() => {
   const channel = new MessageChannel()
   clientPort = channel.port1
   serverPort = channel.port2

   clientPort.start()
   serverPort.start()

   sentMessages = []
   serverPort.addEventListener('message', (event: any) => {
     sentMessages.push(event.data)
   })

   orpc = createORPCClient(new RPCLink({
     port: clientPort,
   }))
 })
+
+afterEach(() => {
+  clientPort.close()
+  serverPort.close()
+})

Consider adding a test for error responses:

it('on error response', async () => {
  expect(orpc.ping('input')).rejects.toThrow(/not found/)

  await new Promise(resolve => setTimeout(resolve, 100))

  const [id, , _] = (await decodeRequestMessage(sentMessages[0]))

  serverPort.postMessage(
    await encodeResponseMessage(id, MessageType.RESPONSE, { body: 'Not Found', status: 404, headers: {} }),
  )
})
packages/server/src/adapters/message-port/rpc-handler.test.ts (3)

43-65: Test setup - Consider extracting message creation

The test message creation could be moved into the beforeEach block or helper functions to improve readability and reduce duplication.

 beforeEach(() => {
   signal = undefined
   const channel = new MessageChannel()
   clientPort = channel.port1
   serverPort = channel.port2

   sentMessages = []
   clientPort.addEventListener('message', (event: any) => {
     sentMessages.push(event.data)
   })

   const handler = new RPCHandler({
     ping: os.handler(async ({ signal: _signal }) => {
       signal = _signal!
       await new Promise(resolve => setTimeout(resolve, 10))
       return 'pong'
     }),

     file: os.handler(async ({ signal: _signal }) => {
       signal = _signal!
       await new Promise(resolve => setTimeout(resolve, 10))
       return new Blob(['pong'])
     }),
   })

   handler.upgrade(serverPort)
+
+  // Prepare test messages
+  prepareTestMessages()
 })

+async function prepareTestMessages() {
+  ping_request_message = await encodeRequestMessage(19, MessageType.REQUEST, {
+    url: new URL('orpc:/ping'),
+    body: { json: 'input' },
+    headers: {},
+    method: 'POST',
+  })
+
+  // ... other message preparations
+}

81-98: Buffer data test has inconsistent timeout value

This test uses a 100ms timeout while other tests use 20ms. Consider standardizing the timeout duration.

-  await new Promise(resolve => setTimeout(resolve, 100))
+  await new Promise(resolve => setTimeout(resolve, 20))

Also, avoid using any type casting and use proper type annotations:

-  expect(await (payload as any).body.get('0').text()).toBe('pong')
+  expect(await (payload.body as FormData).get('0')!.text()).toBe('pong')

50-55: TextEncoder usage without import

The TextEncoder is used directly without an import. While it might be available globally in the test environment, it's better to explicitly import it.

+import { TextEncoder } from 'node:util'
 import { decodeResponseMessage, encodeRequestMessage, MessageType } from '@orpc/standard-server-peer'
 import { os } from '../../builder'
 import { experimental_RPCHandler as RPCHandler } from './rpc-handler'
playgrounds/browser-extension/entrypoints/background/schemas/planet.ts (2)

5-7: Type declarations before schema definitions

The types are inferred from schemas that are declared later in the file. While this works due to TypeScript hoisting, it could be confusing to readers.

-export type NewPlanet = z.infer<typeof NewPlanetSchema>
-export type UpdatePlanet = z.infer<typeof UpdatePlanetSchema>
-export type Planet = z.infer<typeof PlanetSchema>

export const NewPlanetSchema = z.object({
  name: z.string(),
  description: z.string().optional(),
  image: oz.file().type('image/*').optional(),
})

export const UpdatePlanetSchema = z.object({
  id: z.number().int().min(1),
  name: z.string(),
  description: z.string().optional(),
  image: oz.file().type('image/*').optional(),
})

export const PlanetSchema = z.object({
  id: z.number().int().min(1),
  name: z.string(),
  description: z.string().optional(),
  imageUrl: z.string().url().optional(),
  creator: UserSchema,
})

+export type NewPlanet = z.infer<typeof NewPlanetSchema>
+export type UpdatePlanet = z.infer<typeof UpdatePlanetSchema>
+export type Planet = z.infer<typeof PlanetSchema>

15-20: Consider partial updates for UpdatePlanetSchema

The UpdatePlanetSchema requires all fields including optionals, which can be restrictive for partial updates.

export const UpdatePlanetSchema = z.object({
  id: z.number().int().min(1),
- name: z.string(),
- description: z.string().optional(),
- image: oz.file().type('image/*').optional(),
+ name: z.string().optional(),
+ description: z.string().optional(),
+ image: oz.file().type('image/*').optional(),
})

This would allow updating only specific fields while leaving others unchanged.

playgrounds/browser-extension/entrypoints/background/middlewares/db.ts (2)

73-75: Pagination implementation assumes numeric index

The pagination logic uses slice(cursor, cursor + limit) which assumes that cursor is a numeric index. Consider clarifying in documentation or by renaming the parameter to emphasize this is an index-based pagination rather than cursor-based pagination.

-      list: async (limit: number, cursor: number) => {
+      list: async (limit: number, startIndex: number) => {
-        return planets.slice(cursor, cursor + limit)
+        return planets.slice(startIndex, startIndex + limit)
       },

92-106: Update method with improved error handling

The update method correctly finds and updates the planet, but consider using a more specific error type for better error handling.

-          throw new Error('Planet not found')
+          throw new Error(`Planet with id ${planet.id} not found`)

For a more robust implementation in production code, you might want to create a custom error class:

class PlanetNotFoundError extends Error {
  constructor(id: number) {
    super(`Planet with id ${id} not found`)
    this.name = 'PlanetNotFoundError'
  }
}

// Then in the update method:
throw new PlanetNotFoundError(planet.id)
playgrounds/browser-extension/entrypoints/background/router/planet.ts (1)

63-98: Consider cleaning up educational comments in updatePlanet

While the implementation demonstrates two approaches to error handling (type-safe and normal approach), having production code with commented alternative implementations and educational comments might confuse maintainers.

Consider either:

  1. Removing the commented "Normal Approach" section and related comments
  2. Moving these educational examples to documentation or developer guides
playgrounds/electron/src/main/router/planet.ts (1)

1-98: Consider sharing code between browser extension and electron implementations

This file is identical to playgrounds/browser-extension/entrypoints/background/router/planet.ts. While the contexts differ (browser extension vs Electron), the functionality is the same.

Consider extracting the common router logic to a shared module that both platforms can import. This would:

  • Reduce code duplication
  • Ensure consistency across platforms
  • Simplify maintenance when API changes are needed

A shared implementation could be achieved by:

// In a shared location (e.g., packages/common/src/routers/planet.ts)
export function createPlanetRouter(pub, authed, schemas, middlewares) {
  return {
    listPlanets: pub
      .use(middlewares.retry({ times: 3 }))
      .route({
        // Common route configuration
      })
      // Rest of implementation
      
    // Other endpoints
  };
}

// In platform-specific files:
import { createPlanetRouter } from '@orpc/common/routers/planet';
import { pub, authed } from '../orpc';
import * as schemas from '../schemas/planet';
import * as middlewares from '../middlewares';

export const { listPlanets, createPlanet, findPlanet, updatePlanet } = 
  createPlanetRouter(pub, authed, schemas, middlewares);
packages/client/src/adapters/message-port/message-port.ts (2)

29-56: Consider enhancing error handling in onMessagePortMessage

While the function handles different port types well, there's room for improvement in error handling.

Consider adding:

  1. Try/catch blocks around the callback invocations to prevent errors in user code from breaking the message handling
  2. Type guards for the data to ensure it matches SupportedMessagePortData
export function onMessagePortMessage(port: SupportedMessagePort, callback: (data: SupportedMessagePortData) => void): void {
  if ('addEventListener' in port) {
    port.addEventListener('message', (event) => {
+     try {
        callback(event.data)
+     } catch (error) {
+       console.error('Error in message port callback:', error)
+     }
    })
  }
  // Similar changes for other port types
}

4-7: Consider strengthening type safety for MessagePortMainLike

The current interface allows any data type for both incoming and outgoing messages, which could lead to runtime errors.

Strengthen the type definition:

export interface MessagePortMainLike {
- on: <T extends string>(event: T, callback: (event?: { data: any }) => void) => void
- postMessage: (data: any) => void
+ on: <T extends string>(event: T, callback: (event?: { data: SupportedMessagePortData }) => void) => void
+ postMessage: (data: SupportedMessagePortData) => void
}

Similar changes should be applied to BrowserPortLike.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between fb0d07c and 467423a.

⛔ Files ignored due to path filters (7)
  • playgrounds/browser-extension/public/icon/128.png is excluded by !**/*.png
  • playgrounds/browser-extension/public/icon/16.png is excluded by !**/*.png
  • playgrounds/browser-extension/public/icon/32.png is excluded by !**/*.png
  • playgrounds/browser-extension/public/icon/48.png is excluded by !**/*.png
  • playgrounds/browser-extension/public/icon/96.png is excluded by !**/*.png
  • playgrounds/browser-extension/public/wxt.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (78)
  • apps/content/.vitepress/config.ts (2 hunks)
  • apps/content/docs/adapters/message-port.md (1 hunks)
  • apps/content/docs/integrations/browser-extension.md (1 hunks)
  • apps/content/docs/integrations/electron.md (1 hunks)
  • apps/content/docs/integrations/worker-threads.md (1 hunks)
  • apps/content/docs/playgrounds.md (2 hunks)
  • eslint.config.js (1 hunks)
  • package.json (1 hunks)
  • packages/client/package.json (2 hunks)
  • packages/client/src/adapters/message-port/index.ts (1 hunks)
  • packages/client/src/adapters/message-port/link-client.ts (1 hunks)
  • packages/client/src/adapters/message-port/message-port.test.ts (1 hunks)
  • packages/client/src/adapters/message-port/message-port.ts (1 hunks)
  • packages/client/src/adapters/message-port/rpc-link.test.ts (1 hunks)
  • packages/client/src/adapters/message-port/rpc-link.ts (1 hunks)
  • packages/server/package.json (2 hunks)
  • packages/server/src/adapters/message-port/handler.ts (1 hunks)
  • packages/server/src/adapters/message-port/index.ts (1 hunks)
  • packages/server/src/adapters/message-port/rpc-handler.test.ts (1 hunks)
  • packages/server/src/adapters/message-port/rpc-handler.ts (1 hunks)
  • packages/server/tests/message-port.test.ts (1 hunks)
  • playgrounds/browser-extension/.gitignore (1 hunks)
  • playgrounds/browser-extension/README.md (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/index.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/middlewares/auth.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/middlewares/db.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/middlewares/retry.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/orpc.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/router/auth.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/router/index.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/router/planet.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/router/sse.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/schemas/auth.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/schemas/planet.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/content.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/App.tsx (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/components/orpc-mutation.tsx (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/index.html (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/main.tsx (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/playground-client.ts (1 hunks)
  • playgrounds/browser-extension/entrypoints/popup/playground-query.ts (1 hunks)
  • playgrounds/browser-extension/package.json (1 hunks)
  • playgrounds/browser-extension/tsconfig.json (1 hunks)
  • playgrounds/browser-extension/wxt.config.ts (1 hunks)
  • playgrounds/electron/.gitignore (1 hunks)
  • playgrounds/electron/.vscode/settings.json (1 hunks)
  • playgrounds/electron/README.md (1 hunks)
  • playgrounds/electron/electron-builder.yml (1 hunks)
  • playgrounds/electron/electron.vite.config.ts (1 hunks)
  • playgrounds/electron/package.json (1 hunks)
  • playgrounds/electron/src/main/index.ts (1 hunks)
  • playgrounds/electron/src/main/middlewares/auth.ts (1 hunks)
  • playgrounds/electron/src/main/middlewares/db.ts (1 hunks)
  • playgrounds/electron/src/main/middlewares/retry.ts (1 hunks)
  • playgrounds/electron/src/main/orpc.ts (1 hunks)
  • playgrounds/electron/src/main/router/auth.ts (1 hunks)
  • playgrounds/electron/src/main/router/index.ts (1 hunks)
  • playgrounds/electron/src/main/router/planet.ts (1 hunks)
  • playgrounds/electron/src/main/router/sse.ts (1 hunks)
  • playgrounds/electron/src/main/schemas/auth.ts (1 hunks)
  • playgrounds/electron/src/main/schemas/planet.ts (1 hunks)
  • playgrounds/electron/src/main/schemas/user.ts (1 hunks)
  • playgrounds/electron/src/preload/index.d.ts (1 hunks)
  • playgrounds/electron/src/preload/index.ts (1 hunks)
  • playgrounds/electron/src/renderer/index.html (1 hunks)
  • playgrounds/electron/src/renderer/src/App.tsx (1 hunks)
  • playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (1 hunks)
  • playgrounds/electron/src/renderer/src/components/orpc-query.tsx (1 hunks)
  • playgrounds/electron/src/renderer/src/lib/orpc.ts (1 hunks)
  • playgrounds/electron/src/renderer/src/main.tsx (1 hunks)
  • playgrounds/electron/src/renderer/src/playground-client.ts (1 hunks)
  • playgrounds/electron/src/renderer/src/playground-query.ts (1 hunks)
  • playgrounds/electron/tsconfig.json (1 hunks)
  • playgrounds/electron/tsconfig.node.json (1 hunks)
  • playgrounds/electron/tsconfig.web.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (31)
playgrounds/browser-extension/entrypoints/popup/App.tsx (2)
playgrounds/browser-extension/entrypoints/popup/components/orpc-mutation.tsx (1)
  • CreatePlanetMutationForm (4-58)
playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (1)
  • ListPlanetsQuery (4-76)
playgrounds/browser-extension/entrypoints/popup/main.tsx (1)
playgrounds/browser-extension/entrypoints/popup/App.tsx (1)
  • App (4-14)
playgrounds/electron/src/renderer/src/lib/orpc.ts (4)
packages/client/src/adapters/fetch/rpc-link.ts (1)
  • RPCLink (16-22)
packages/server/src/router-client.ts (1)
  • RouterClient (15-20)
playgrounds/electron/src/main/router/index.ts (1)
  • router (5-20)
packages/client/src/client.ts (1)
  • createORPCClient (16-42)
playgrounds/electron/src/main/middlewares/auth.ts (2)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/electron/src/main/schemas/user.ts (1)
  • User (5-5)
playgrounds/browser-extension/entrypoints/background/schemas/planet.ts (2)
packages/zod/src/index.ts (1)
  • oz (16-22)
playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1)
  • UserSchema (24-39)
playgrounds/electron/src/main/middlewares/db.ts (4)
playgrounds/electron/src/main/schemas/planet.ts (3)
  • Planet (7-7)
  • NewPlanet (5-5)
  • UpdatePlanet (6-6)
playgrounds/electron/src/main/schemas/user.ts (1)
  • User (5-5)
playgrounds/electron/src/main/router/planet.ts (1)
  • updatePlanet (63-98)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/electron/src/main/router/sse.ts (1)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/browser-extension/entrypoints/background/middlewares/retry.ts (1)
packages/server/src/builder.ts (1)
  • os (336-352)
packages/server/tests/message-port.test.ts (4)
packages/server/src/builder.ts (2)
  • os (336-352)
  • handler (273-280)
packages/server/src/router-client.ts (1)
  • RouterClient (15-20)
packages/client/src/client.ts (1)
  • createORPCClient (16-42)
packages/client/tests/shared.ts (1)
  • supportedDataTypes (57-171)
playgrounds/electron/src/main/middlewares/retry.ts (1)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/electron/src/renderer/src/components/orpc-query.tsx (2)
playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (1)
  • ListPlanetsQuery (4-76)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (19-19)
playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (2)
playgrounds/electron/src/renderer/src/components/orpc-query.tsx (1)
  • ListPlanetsQuery (4-76)
playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (1)
  • orpc (15-15)
playgrounds/electron/src/renderer/src/playground-query.ts (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (19-19)
playgrounds/browser-extension/entrypoints/background/router/sse.ts (1)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/electron/src/main/router/index.ts (1)
playgrounds/electron/src/main/router/planet.ts (4)
  • listPlanets (7-24)
  • createPlanet (26-37)
  • findPlanet (39-61)
  • updatePlanet (63-98)
packages/server/src/adapters/message-port/rpc-handler.ts (3)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/router.ts (1)
  • Router (12-17)
packages/server/src/adapters/standard/rpc-handler.ts (2)
  • StandardRPCHandlerOptions (10-11)
  • StandardRPCHandler (13-22)
playgrounds/electron/src/main/router/auth.ts (3)
playgrounds/electron/src/main/orpc.ts (2)
  • pub (5-6)
  • authed (8-9)
playgrounds/electron/src/main/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/electron/src/main/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
packages/client/src/adapters/message-port/message-port.test.ts (1)
packages/client/src/adapters/message-port/message-port.ts (3)
  • postMessagePortMessage (29-31)
  • onMessagePortMessage (33-56)
  • onMessagePortClose (58-81)
playgrounds/browser-extension/entrypoints/background/middlewares/auth.ts (2)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1)
  • User (5-5)
playgrounds/electron/src/main/schemas/planet.ts (2)
packages/zod/src/index.ts (1)
  • oz (16-22)
playgrounds/electron/src/main/schemas/user.ts (1)
  • UserSchema (24-39)
playgrounds/browser-extension/entrypoints/popup/playground-client.ts (1)
playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (1)
  • orpc (15-15)
playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1)
packages/zod/src/index.ts (1)
  • oz (16-22)
playgrounds/browser-extension/entrypoints/popup/components/orpc-mutation.tsx (1)
playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (1)
  • orpc (15-15)
playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (19-19)
playgrounds/electron/src/main/index.ts (2)
packages/server/src/builder.ts (1)
  • handler (273-280)
playgrounds/electron/src/main/router/index.ts (1)
  • router (5-20)
playgrounds/electron/src/renderer/src/playground-client.ts (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (19-19)
playgrounds/browser-extension/entrypoints/background/router/auth.ts (3)
playgrounds/browser-extension/entrypoints/background/orpc.ts (2)
  • pub (5-6)
  • authed (8-9)
playgrounds/browser-extension/entrypoints/background/schemas/user.ts (2)
  • NewUserSchema (7-22)
  • UserSchema (24-39)
playgrounds/browser-extension/entrypoints/background/schemas/auth.ts (2)
  • CredentialSchema (3-6)
  • TokenSchema (8-10)
playgrounds/browser-extension/entrypoints/popup/playground-query.ts (1)
playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (1)
  • orpc (15-15)
playgrounds/browser-extension/entrypoints/background/middlewares/db.ts (4)
playgrounds/browser-extension/entrypoints/background/schemas/planet.ts (3)
  • Planet (7-7)
  • NewPlanet (5-5)
  • UpdatePlanet (6-6)
playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1)
  • User (5-5)
playgrounds/browser-extension/entrypoints/background/router/planet.ts (1)
  • updatePlanet (63-98)
packages/server/src/builder.ts (1)
  • os (336-352)
playgrounds/browser-extension/entrypoints/background/router/index.ts (1)
playgrounds/browser-extension/entrypoints/background/router/planet.ts (4)
  • listPlanets (7-24)
  • createPlanet (26-37)
  • findPlanet (39-61)
  • updatePlanet (63-98)
playgrounds/electron/src/main/schemas/user.ts (1)
packages/zod/src/index.ts (1)
  • oz (16-22)
🪛 markdownlint-cli2 (0.17.2)
playgrounds/electron/README.md

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

(MD045, no-alt-text)

playgrounds/browser-extension/README.md

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

(MD045, no-alt-text)

🔇 Additional comments (98)
playgrounds/electron/.vscode/settings.json (1)

1-3: Enable format on save for consistent code style
This workspace setting ensures that files in the Electron playground are automatically formatted on save, promoting a unified code style across the team.

eslint.config.js (1)

61-61: Broaden node/prefer-global/process rule scope
The ESLint rule 'node/prefer-global/process' is now disabled for the entire playgrounds/** directory, accommodating the new Electron and browser extension playgrounds. Please confirm this broad scope is intentional and doesn’t suppress necessary warnings where process should remain the preferred global.

apps/content/docs/playgrounds.md (1)

43-44: Local clone commands look good.

The new degit commands for the Electron and Browser Extension playgrounds follow the existing pattern and align with the updated table above.

playgrounds/electron/.gitignore (1)

1-6: Gitignore contents look appropriate.

The .gitignore patterns cover node_modules, build output (dist, out), macOS artifacts, ESLint cache, and log files. No further exclusions are needed at this time.

packages/client/src/adapters/message-port/index.ts (1)

1-4: Barrel export aggregation is correct.

Re-exporting from link-client, message-port, and rpc-link simplifies imports for consumers and aligns with the new message-port adapter structure.

playgrounds/electron/tsconfig.json (1)

1-4:

Details

✅ Verification successful

Verify composite project setup.

Ensure that the referenced tsconfig.node.json and tsconfig.web.json enable project references by setting "composite": true in their compilerOptions.


🏁 Script executed:

#!/bin/bash
# Verify that referenced TypeScript configs enable composite projects
rg '"composite":\s*true' playgrounds/electron/tsconfig.node.json playgrounds/electron/tsconfig.web.json

Length of output: 230


Composite project setup confirmed

Both playgrounds/electron/tsconfig.node.json and playgrounds/electron/tsconfig.web.json have "composite": true in their compilerOptions, so no further changes are needed.

playgrounds/browser-extension/wxt.config.ts (1)

1-6: Configuration looks good!

The WXT configuration file is correctly set up with the React module. This minimal configuration enables React support for the browser extension playground.

This aligns with best practices for WXT configuration files, which should be kept simple and only include necessary modules.

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

90-90: Appropriate new navigation item for Message Port adapter.

The addition of the Message Port adapter documentation link is well-placed in the "Adapters" section, consistent with the existing structure.


109-111: Good documentation structure for new integrations.

The new integration links for Electron, Browser Extension, and Worker Threads are appropriately placed in the "Integrations" section, providing a logical organization of related content.

playgrounds/browser-extension/entrypoints/background/index.ts (2)

4-9: Background script implementation looks correct.

The implementation correctly:

  1. Creates an RPC handler with the imported router
  2. Sets up a listener for runtime connections
  3. Upgrades each connection port for RPC communication

This pattern follows best practices for browser extension background scripts using message ports.


1-1:

Details

✅ Verification successful

Note the use of experimental API.

The code imports an experimental RPCHandler. While this works correctly, be aware that experimental APIs may change in future releases.


🌐 Web query:

What does the "experimental_" prefix typically indicate in JavaScript/TypeScript libraries?

💡 Result:

The "experimental_" prefix in JavaScript and TypeScript libraries typically indicates that a feature or functionality is not yet fully tested, stable, or officially supported. This prefix is often used during the development phase to signal that the feature is still under experimentation and may undergo significant changes before it is officially released or becomes part of a stable version.

  1. Feature Development Phase: Features are initially developed with an "experimental_" prefix to denote that they are being tested and may not remain in their current form or might not be included in future releases[6].
  2. Stability and Testing: The prefix serves as a warning that the feature is subject to change and may not be reliable for production use. It is used to gather feedback and test the feasibility of proposed changes in a controlled environment[6].
  3. Removal of Prefix: Once the feature is deemed stable, the prefix is removed, and the feature becomes part of the main branch or stable release[6].

This approach is similar to how new JavaScript features are introduced and tested before they become part of the official specification[6].

Citations:


Use of Experimental RPCHandler
The import of experimental_RPCHandler signals an unstable API that may be refactored or removed in future releases. Exercise caution when relying on experimental features in production.

• Location: playgrounds/browser-extension/entrypoints/background/index.ts (line 1)

playgrounds/electron/src/preload/index.d.ts (1)

1-8: Type declarations look good.

The TypeScript declaration file correctly extends the global Window interface to include Electron-specific properties, enabling type-safe access to these properties in the renderer process.

This follows standard patterns for Electron preload scripts and TypeScript declaration merging.

playgrounds/electron/tsconfig.node.json (1)

1-12: Configuration Validation: The tsconfig.node.json correctly extends the Electron toolkit base (@electron-toolkit/tsconfig/tsconfig.node.json), enables composite builds, and scopes module resolution to "bundler". Including the electron-vite/node types aligns with the Electron playground setup, and disabling unused checks is reasonable for rapid prototyping in this context.

playgrounds/browser-extension/entrypoints/popup/App.tsx (1)

1-3: Imports Sanity Check: The imports for CreatePlanetMutationForm and ListPlanetsQuery correctly reference the sibling components directory and match the project structure.

playgrounds/browser-extension/tsconfig.json (1)

1-8: Configuration Correctness: The tsconfig.json rightly extends the base ./.wxt/tsconfig.json, enables the React 17+ JSX transform with "react-jsx", and allows importing .ts extensions. This aligns well with the browser extension’s popup setup.

playgrounds/electron/src/renderer/src/App.tsx (1)

1-17: Component Structure Review: The App component cleanly imports and renders the mutation and query components with a header and separators. The explicit return type (React.JSX.Element) follows TypeScript best practices and ensures type safety.

playgrounds/electron/src/renderer/index.html (1)

1-18: HTML Entry Point Review: The HTML scaffold provides a strict CSP, a <div id="root"> mount point, and correctly references the module entry (/src/main.tsx). This setup is appropriate for an Electron renderer served via Vite.

playgrounds/browser-extension/entrypoints/popup/index.html (1)

1-13: Well-structured HTML for browser extension popup

This HTML file provides a clean and minimal setup for a browser extension popup with all necessary elements:

  • Proper HTML5 document structure
  • Responsive viewport configuration
  • Browser extension manifest metadata
  • Root element for React mounting
  • Module script loading the React entry point
playgrounds/electron/electron.vite.config.ts (1)

1-20: Well-structured Electron Vite configuration.

The configuration correctly sets up the three separate Electron processes (main, preload, renderer) with appropriate plugins and settings. Using externalizeDepsPlugin() for main and preload is good practice to prevent bundling Node.js modules.

packages/server/package.json (2)

62-67: Message-port adapter exports added properly.

The message-port adapter exports are correctly added to the publishConfig section, following the established pattern for other adapters.


79-80: Message-port adapter source exports added correctly.

The source exports entry for the message-port adapter is properly added, consistent with other adapter exports.

packages/server/tests/message-port.test.ts (4)

1-7: Well-structured import setup for MessagePort adapter testing

The imports correctly bring in the necessary components from both client and server packages to test the MessagePort adapter, including the experimental RPCLink and RPCHandler classes.


8-21: Well-implemented test setup for bi-directional MessagePort communication

The test setup correctly:

  1. Creates a MessageChannel with two ports
  2. Defines a simple echo procedure
  3. Sets up the server-side handler with port1
  4. Sets up the client-side link with port2
  5. Creates a type-safe client with proper RouterClient typing

This demonstrates the core message port adapter pattern for type-safe IPC communication.


22-24: Comprehensive data type testing approach

Great use of parameterized testing with it.each to validate that all supported data types can be correctly transmitted through the MessagePort adapter.


26-32: Good coverage of nested data structures

This test ensures that the MessagePort adapter correctly handles complex nested objects containing the supported data types, which is essential for real-world usage where data is rarely just primitive types.

playgrounds/electron/src/main/router/sse.ts (4)

1-3: Appropriate imports for SSE implementation

The imports correctly bring in eventIterator and os from @orpc/server along with Zod for schema validation, which are the essential components needed for a type-safe SSE implementation.


4-5: Good practice: Defining constants for configuration values

Using a named constant for the maximum number of events improves code readability and maintainability.


6-12: Well-structured route definition with clear metadata

The route is properly defined with HTTP GET method, explicit path, appropriate tags, and a descriptive summary, which helps with API documentation and discoverability.


13-22: Correctly implemented SSE pattern using async generator

The handler implementation:

  1. Uses eventIterator with proper Zod schema validation for type safety
  2. Correctly implements the async generator pattern for streaming events
  3. Includes appropriate rate limiting (1 second between events)
  4. Has a reasonable upper bound on the number of events (MAX_EVENTS)

This is a good example of how to implement SSE endpoints with the oRPC framework.

playgrounds/browser-extension/entrypoints/background/router/sse.ts (1)

1-22: Consistent SSE implementation across playground environments

This SSE implementation for the browser extension playground matches the one in the Electron playground, demonstrating a consistent approach to SSE across different environments. The implementation is clean, type-safe, and follows best practices with async generators and proper schema validation.

playgrounds/browser-extension/entrypoints/background/orpc.ts (2)

1-4: Clean imports for middleware setup

The imports correctly bring in the base os object and the necessary middleware components for database access and authentication.


5-9: Well-designed middleware layering pattern

The middleware setup follows a clean layering pattern:

  1. pub chain applies database middleware for all routes
  2. authed chain extends pub by adding authentication middleware

This approach creates a clear separation of concerns and enables code reuse between public and authenticated routes.

packages/server/src/adapters/message-port/rpc-handler.ts (1)

1-17: Implementation looks good for a wrapper class

The implementation is clean and follows good practice for extending the MessagePortHandler and utilizing StandardRPCHandler. The class provides a simple wrapper for RPC handling over message ports.

playgrounds/electron/src/renderer/src/playground-client.ts (4)

1-3: Appropriate imports for oRPC client usage.

The imports look good, bringing in the client from the local configuration and the safe wrapper for error handling.


9-9: Good use of the safe wrapper pattern.

The safe wrapper function provides an elegant way to handle potential errors with tuple destructuring. This is a clean pattern for error handling in TypeScript.


11-16: Effective demonstration of type-safe error handling.

The code effectively demonstrates how to handle errors with type safety. The comment on line 14 explicitly highlights the type-safe nature of error.data.id access, which is instructive.


17-21: Appropriate error and success case handling.

The code cleanly separates error and success cases, logging appropriate information to the console in each scenario.

playgrounds/electron/tsconfig.web.json (1)

1-25: Well-structured TypeScript configuration for Electron renderer.

This configuration correctly extends the Electron toolkit's web configuration and sets up appropriate compiler options for React JSX development in the renderer process. The path aliases, module resolution settings, and file includes are all properly configured.

playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts (4)

2-6: Appropriate imports for browser extension message port setup.

The imports correctly bring in the necessary types and functions from oRPC packages, including the experimental message port adapter.


7-7: Browser runtime connection established correctly.

The code properly connects to the browser runtime for communication between the popup and background script.


9-12: Correct setup of the experimental RPCLink with message port.

The message port RPCLink is configured correctly with the browser runtime port.


13-15: Type-safe client export with React Query utilities.

The exports properly type the client against the router and create React Query utilities, enabling type-safe RPC calls from the popup UI.

playgrounds/electron/src/renderer/src/lib/orpc.ts (5)

1-6: Appropriate imports for Electron renderer message port setup.

The imports correctly bring in the necessary router type, client types, and the experimental message port adapter.


7-7: Good use of MessageChannel for IPC.

Creating a MessageChannel and extracting both ports with descriptive variable names follows best practices for Electron IPC communication.


11-13: Correct setup of the experimental RPCLink with message port.

The message port RPCLink is configured correctly with the client port from the MessageChannel.


15-15: Important port activation.

Starting the client port is necessary to begin receiving messages, and is correctly done here.


17-19: Type-safe client export with React Query utilities.

The exports properly type the client against the router and create React Query utilities, enabling type-safe RPC calls from the renderer to the main process.

playgrounds/electron/src/main/orpc.ts (1)

1-9: Well-structured middleware composition!

The code establishes a clear layered middleware pattern with pub providing database access and authed extending it with authentication requirements. This approach follows good API design practices by separating concerns and building up middleware chains incrementally.

playgrounds/electron/package.json (1)

22-25:

Details

✅ Verification successful

Verify ORPC packages versioning strategy

All ORPC packages are using the "next" tag which is appropriate for a playground demonstrating new features, but make sure this is the intended versioning strategy before releasing.


🏁 Script executed:

#!/bin/bash
# Check the current versions of ORPC packages
npm view @orpc/client versions --json | jq 'if type=="array" then .[-5:] else . end'
npm view @orpc/server versions --json | jq 'if type=="array" then .[-5:] else . end'
npm view @orpc/react-query versions --json | jq 'if type=="array" then .[-5:] else . end'

Length of output: 453


We should verify the version history for @orpc/zod as well. Let’s pull its recent versions:


🏁 Script executed:

#!/bin/bash
# Check the latest versions of @orpc/zod
npm view @orpc/zod versions --json | jq 'if type=="array" then .[-5:] else . end'

Length of output: 148


To confirm what the “next” tag actually points to for each package, let’s pull their dist-tags:


🏁 Script executed:

#!/bin/bash
npm view @orpc/client dist-tags --json
npm view @orpc/server dist-tags --json
npm view @orpc/react-query dist-tags --json
npm view @orpc/zod dist-tags --json

Length of output: 628


Confirm ORPC “next” tag vs. stable version

All four ORPC packages in playgrounds/electron/package.json currently pin to the next dist-tag, which resolves to the canary build 0.0.0-next.fb0d07c. If your goal is to exercise the absolute latest—unstable—features in this playground, you can leave them as-is. If you instead want a stable baseline for release, swap each to the latest tag (currently 1.2.0):

• @orpc/client: next → 0.0.0-next.fb0d07c | latest → 1.2.0
• @orpc/server: next → 0.0.0-next.fb0d07c | latest → 1.2.0
• @orpc/react-query: next → 0.0.0-next.fb0d07c | latest → 1.2.0
• @orpc/zod: next → 0.0.0-next.fb0d07c | latest → 1.2.0

No changes required if experimental builds are intended. Otherwise update each dependency to "latest".

playgrounds/electron/src/renderer/src/components/orpc-query.tsx (2)

1-11: Well-structured query setup with Tanstack Query

The component correctly sets up infinite querying with pagination parameters and a cursor-based approach. The implementation of getNextPageParam is particularly good - it intelligently derives the next cursor from the last item ID only when the page is full.


55-72: Good implementation of pagination controls

The pagination controls are well implemented with the "Load more" button correctly disabled when there are no more pages to load, and a refresh button to refetch the current data.

apps/content/docs/integrations/electron.md (4)

1-8: Well-structured introduction with relevant references

The documentation starts with clear frontmatter and effectively references both the Message Port Adapter documentation and Electron's own guide on MessagePorts, providing good context for readers.


10-31: Clear instructions for Main Process implementation

The Main Process section correctly explains how to handle the port from the renderer and shows proper usage of the experimental_RPCHandler. The info note about channel naming being arbitrary is a helpful clarification.


33-45: Concise Preload Process implementation

The Preload Process section correctly demonstrates how to receive and forward the port, acting as the bridge between renderer and main processes. The code is clear and focused on the essential steps.


47-65: Complete Renderer Process implementation with appropriate note

The Renderer section correctly shows how to create the MessageChannel and use one port for the client link. The info note appropriately directs readers to additional documentation for full client examples rather than duplicating that information.

playgrounds/browser-extension/entrypoints/popup/components/orpc-query.tsx (2)

1-11: Well-structured query setup with appropriate pagination parameters

The component correctly implements infinite querying with good cursor-based pagination, limiting to 10 items per page and deriving the next cursor from the last item's ID.


58-64: Good implementation of "Load more" button

The "Load more" button is correctly implemented with the disabled attribute tied to the hasNextPage value, preventing users from attempting to load more data when none exists.

playgrounds/browser-extension/entrypoints/background/router/index.ts (2)

1-4: Clear and organized imports

The imports are well-organized and demonstrate good separation of concerns by importing from separate modules for authentication, planet operations, and server-sent events.


5-20: Well-structured router with logical namespace grouping

The router object is well-organized with clear namespaces that logically group related functionality. The planet namespace effectively maps imported functions to more concise endpoint names.

One thing to note is that this router structure aligns well with how the client is using the endpoints in the UI components (e.g., orpc.planet.list.infiniteOptions in the query components).

playgrounds/electron/src/main/router/index.ts (1)

1-20: Well-structured router organization with clean separation of concerns.

The router organizes API handlers into logical groups (auth, planet) with clear naming, making it easier to maintain and understand the API surface. The renamed exports in the planet group provide a clean, consistent interface.

packages/client/package.json (2)

42-47: Good addition of message-port adapter exports.

The message-port adapter is correctly added to the publishConfig.exports section, following the pattern established by other adapters.


55-56: Properly configured exports for message-port adapter source.

The direct source exports are also properly configured for the message-port adapter, maintaining consistency with the existing pattern for other adapters.

playgrounds/browser-extension/entrypoints/popup/playground-client.ts (1)

9-21: Good use of type-safe error handling.

The implementation demonstrates proper error handling using the safe utility, with good practices around error checking and type-safe access to error data properties. This is a helpful example of the recommended error handling pattern.

apps/content/docs/adapters/message-port.md (1)

1-47: Clear and well-structured documentation.

The documentation provides a good overview of the Message Port adapter, its supported environments, and basic usage examples. The code snippets are well-formatted with appropriate labels, and the table of integration guides is helpful for users to find environment-specific instructions.

packages/client/src/adapters/message-port/link-client.ts (1)

15-18: Good handling of Blob to ArrayBuffer conversion.

The implementation properly handles the conversion from Blob to ArrayBuffer before posting messages, which is important for compatibility across different message port implementations.

playgrounds/electron/src/main/router/auth.ts (2)

1-45: Good API structure with clear route organization.

The authentication router is well-structured with appropriate HTTP methods, paths, input/output validation, and handlers for each endpoint. The use of different middleware pipelines (pub vs authed) for public and authenticated routes is a good practice.


43-44:

Details

❌ Incorrect review comment

Verify user exists in context before returning.

The handler should check if the user exists in the context before returning it to avoid potential undefined errors.

-  .handler(async ({ context }) => {
-    return context.user
+  .handler(async ({ context }) => {
+    if (!context.user) {
+      throw new Error('User not found in context');
+    }
+    return context.user

🏁 Script executed:

#!/bin/bash
# Check if the user is properly validated in the auth middleware

# Look for the auth middleware implementation
fd "requiredAuthMiddleware" --exec cat {}

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for createContext definition
rg "createContext" -n playgrounds/electron/src/main

# Search for Context type or interface
rg "export type Context" -n playgrounds/electron/src/main

# Inspect where `user` is assigned into context
rg "user" -n playgrounds/electron/src/main

Length of output: 1560


🏁 Script executed:

#!/bin/bash
# Display the authentication middleware to verify user assignment and error handling
sed -n '1,200p' playgrounds/electron/src/main/middlewares/auth.ts

echo "-----"

# Display the auth router to see how the "Get current user" handler is wired
sed -n '1,200p' playgrounds/electron/src/main/router/auth.ts

Length of output: 1876


🏁 Script executed:

#!/bin/bash
# Search for authed and pub in the orpc setup
rg "export const authed" -n playgrounds/electron/src/main
rg "export const pub" -n playgrounds/electron/src/main

# If orpc.ts exists, display its contents
fd "orpc.ts" --exec sed -n '1,200p' {}

Length of output: 10950


No additional null check needed in me handler
The requiredAuthMiddleware wired into authed already throws if context.session.user is missing and guarantees that context.user is defined when your handler runs. You can safely return context.user without adding an extra existence check.

Likely an incorrect or invalid review comment.

playgrounds/electron/src/main/schemas/user.ts (1)

1-40: Well-organized schema definitions with good OpenAPI integration

This file effectively defines user schemas with Zod and OpenAPI integration. The validation rules are appropriate, with email validation for proper format checking.

packages/client/src/adapters/message-port/rpc-link.ts (1)

1-22: Clean implementation of message port RPC link

The experimental_RPCLink implementation effectively extends StandardRPCLink while properly omitting HTTP-specific options. Good documentation links provided.

playgrounds/electron/src/main/index.ts (1)

44-49:

Details

✅ Verification successful

Verify resource cleanup on port disconnection

The code starts the server port but doesn't handle cleanup if the port disconnects or the window closes. Consider adding disconnect listeners to properly clean up resources.


🏁 Script executed:

#!/bin/bash
# Check if handler has any disconnect or cleanup methods
rg "class (.+)RPCHandler" -A 30 --include="*/message-port/*.ts"

Length of output: 507


I’m not seeing any RPC handler definitions due to the incorrect rg --include syntax. Let’s retry with the proper glob flag:

This will help confirm whether there’s a built-in disconnect/cleanup method we can hook into.


🏁 Script executed:

#!/bin/bash
# Search for any Handler classes under message-port to see if cleanup methods exist
rg "class .+Handler" -A 30 -g "*/message-port/*.ts"

Length of output: 202


🏁 Script executed:

#!/bin/bash
# Display the top of index.ts to locate handler import/definition
sed -n '1,100p' playgrounds/electron/src/main/index.ts

echo "-----"

# Search all TypeScript files for literal 'upgrade(' to find its definition
rg -F "upgrade(" -A 5 --glob "*.ts"

Length of output: 7191


Let’s inspect the message-port adapter to see if any cleanup hooks exist on the handler or port utility:


🏁 Script executed:

#!/bin/bash
# List adapter files
echo "→ message-port adapter files:"
ls packages/server/src/adapters/message-port

echo ; echo "→ RPCHandler implementation (first 200 lines):"
sed -n '1,200p' packages/server/src/adapters/message-port/handler.ts

echo ; echo "→ Locate onMessagePortMessage definition:"
rg "function onMessagePortMessage" -A 20 packages/server/src/adapters/message-port

echo ; echo "→ Search for dispose/cleanup methods in handler.ts:"
rg -E "dispose|destroy|cleanup|close" -n -A 5 packages/server/src/adapters/message-port/handler.ts

Length of output: 2378


Cleanup on port disconnection already implemented
The experimental_MessagePortHandler.upgrade method registers an onMessagePortClose(port, …) listener that calls peer.close(), so the RPC peer is torn down automatically when the port disconnects. No additional cleanup hooks are needed here.

packages/client/src/adapters/message-port/message-port.test.ts (3)

1-14: Good test coverage for postMessagePortMessage

The test effectively verifies that the function correctly delegates to the port's postMessage method.


16-94: Comprehensive test coverage for onMessagePortMessage

The tests thoroughly cover all supported message event listener patterns, preference order, and error handling.


96-174: Comprehensive test coverage for onMessagePortClose

The tests thoroughly cover all supported close event listener patterns, preference order, and error handling.

playgrounds/browser-extension/entrypoints/background/schemas/user.ts (1)

1-40: Well-structured schema definitions using Zod with OpenAPI metadata.

The schema definitions are properly implemented with appropriate validations (email format) and examples for documentation. The types are correctly exported using Zod's inference.

playgrounds/browser-extension/entrypoints/background/router/auth.ts (2)

14-20: Placeholder implementation in signup handler.

The current implementation returns a hardcoded UUID and doesn't actually store the user. Since this is a playground example, this may be intentional, but it's worth noting that this doesn't represent a production-ready implementation.

Is this intended to be a simplified example for demonstration purposes? If so, consider adding a comment to make it clear this is a mock implementation.


43-45: Clean implementation of the me handler.

The handler correctly retrieves the user from the context, which would have been set by the requiredAuthMiddleware.

playgrounds/electron/electron-builder.yml (1)

41-43: Update placeholder publish URL for production use.

The publish configuration uses an example.com URL which isn't a real update server. This is fine for a playground app but should be updated before any production use.

If this is intended only for the playground example, consider adding a comment to indicate this is a placeholder configuration.

playgrounds/electron/src/main/schemas/planet.ts (3)

1-7: Well-structured schema definitions with clean type exports.

The schema imports and type definitions follow best practices by inferring types from Zod schemas, promoting type safety throughout the application.


9-13: The NewPlanetSchema looks good with appropriate validation.

The schema correctly defines required and optional fields with proper validation for the image file type.


22-28: Note the image field naming difference between schemas.

The PlanetSchema uses imageUrl while UpdatePlanetSchema uses image. This is intentional since they represent different things (a URL string vs. a file object), but ensure your transformation logic correctly handles this difference.

playgrounds/electron/src/main/middlewares/db.ts (2)

5-12: Well-defined DB interface with comprehensive CRUD operations.

The interface provides a clear contract for database operations with appropriate type signatures.


14-29: Good practice using nullish coalescing for middleware optimization.

The implementation correctly uses the nullish coalescing operator to avoid unnecessary database creation, with a helpful comment explaining the optimization pattern.

apps/content/docs/integrations/browser-extension.md (2)

1-13: Clear documentation with important warning about binary data limitations.

The introduction and warning effectively communicate the purpose and limitations of using oRPC with browser extensions.


14-34: Well-structured server-side integration code example.

The server example clearly demonstrates how to set up the RPCHandler and handle connections, with helpful information about browser/chrome compatibility.

packages/client/src/adapters/message-port/rpc-link.test.ts (2)

30-48: Good test implementation for the success case

The test effectively validates the RPCLink functionality by:

  1. Creating an expectation for the promise resolution
  2. Verifying the message structure sent to the server
  3. Simulating the server response

Consider adding assertions to verify that the promise actually resolves to confirm the end-to-end flow is complete.


50-68: Well-structured test for blob handling

This test properly validates that the RPCLink can handle Blob payloads, with appropriate expectations for the FormData body and headers in the outgoing request.

packages/server/src/adapters/message-port/rpc-handler.test.ts (4)

66-79: Success test looks good

The test properly verifies the response structure and data after sending a request message.


100-114: Good test for abort handling

The test properly verifies that abort signals are propagated correctly.


116-129: Good test for port closure handling

The test properly verifies that port closure aborts in-flight requests.


131-144: Good test for 404 handling

The test properly verifies the 404 response for non-existent procedures.

playgrounds/browser-extension/entrypoints/background/schemas/planet.ts (1)

9-28: Schema field discrepancy between create/update and full schema

The NewPlanetSchema and UpdatePlanetSchema have an image field for file upload, while PlanetSchema has an imageUrl string field. This is a logical mapping but could be documented for clarity.

Consider adding comments explaining the transformation between file uploads and stored URLs, especially for maintainers who might be new to the codebase:

export const NewPlanetSchema = z.object({
  name: z.string(),
  description: z.string().optional(),
+ // Uploaded image file that will be converted to imageUrl when stored
  image: oz.file().type('image/*').optional(),
})

// ...

export const PlanetSchema = z.object({
  id: z.number().int().min(1),
  name: z.string(),
  description: z.string().optional(),
+ // URL to the stored image (converted from uploaded file)
  imageUrl: z.string().url().optional(),
  creator: UserSchema,
})
playgrounds/browser-extension/entrypoints/background/middlewares/db.ts (2)

14-29: Well-implemented middleware with lazy instantiation

Great job implementing the lazy instantiation pattern with clear documentation. This pattern avoids creating unnecessary DB instances when the middleware is reused.


76-91: Good implementation of create method

The create method properly generates a new ID and handles the image transformation. For a production application, you would want to add validation for file names, but this is appropriate for a playground.

playgrounds/browser-extension/entrypoints/background/router/planet.ts (4)

1-6: Well-organized imports with good separation of concerns

The file has a clean structure with appropriate imports from the framework and local modules. The separation between schemas, middlewares, and core functionality is well-designed.


7-24: Good use of middleware and input validation for the list endpoint

The listPlanets handler effectively:

  • Applies retry middleware for resilience
  • Implements appropriate pagination with sensible limits
  • Uses Zod for input validation with reasonable defaults
  • Returns a typed array response

26-37: Authentication properly applied to createPlanet endpoint

The endpoint correctly:

  • Uses the authenticated middleware chain
  • Passes the authenticated user to the database operation
  • Validates input against the NewPlanetSchema
  • Returns a properly typed response

39-61: Good error handling in findPlanet endpoint

The implementation properly:

  • Validates the ID parameter
  • Handles the not-found case explicitly
  • Uses retry middleware appropriately for a read operation
  • Returns a properly typed response
packages/client/src/adapters/message-port/message-port.ts (1)

4-27: Well-designed interfaces for cross-platform message ports

The type definitions effectively abstract the differences between message port implementations across platforms (Electron, Browser Extensions, standard Web MessagePort).

The SupportedMessagePortData type appropriately restricts message data to what can be safely cloned.

Comment thread package.json
Comment thread playgrounds/electron/src/main/middlewares/auth.ts
Comment thread playgrounds/browser-extension/entrypoints/popup/components/orpc-mutation.tsx Outdated
Comment thread packages/server/src/adapters/message-port/rpc-handler.ts
Comment thread playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx
Comment thread playgrounds/electron/src/main/index.ts
Comment thread packages/server/src/adapters/message-port/handler.ts
Comment thread playgrounds/electron/src/main/middlewares/db.ts
Comment thread playgrounds/electron/src/main/middlewares/db.ts
Comment thread packages/client/src/adapters/message-port/message-port.ts
@dinwwwh dinwwwh merged commit a689803 into main May 22, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant