Skip to content

feat(client, server): electron IPC#500

Closed
dinwwwh wants to merge 17 commits intomainfrom
feat/server/electron-ipc
Closed

feat(client, server): electron IPC#500
dinwwwh wants to merge 17 commits intomainfrom
feat/server/electron-ipc

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented May 14, 2025

Summary by CodeRabbit

  • New Features

    • Introduced full support for Electron IPC integration, enabling oRPC communication between Electron main and renderer processes.
    • Added new Electron IPC adapters and handlers for both client and server, with customizable channel names and context support.
    • Launched an Electron Playground application featuring authentication, CRUD operations, server-sent events, and a React-based UI.
    • Provided middleware for authentication, database access, and retry logic within the Electron Playground.
  • Documentation

    • Added comprehensive guides for IPC adapters, Electron IPC integration, and setup instructions.
    • Updated playground documentation to include the new Electron Playground environment and setup commands.
  • Tests

    • Added test suites for Electron IPC adapters and handlers to ensure correct message handling and integration.
  • Chores

    • Updated configuration files, ESLint rules, and package dependencies to support Electron and optimize build processes.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 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 21, 2025 3:07am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This change introduces a comprehensive Electron IPC adapter for the oRPC framework, enabling seamless RPC communication between Electron's main and renderer processes. It includes new client and server-side adapter modules, supporting TypeScript types, and detailed documentation. Additionally, a fully functional Electron playground application is added, complete with middleware, routing, schemas, React components, and build configurations. Documentation and configuration files are updated to reflect these new features and integrations.

Changes

Files/Paths Change Summary
apps/content/.vitepress/config.ts, apps/content/docs/adapters/ipc.md,
apps/content/docs/integrations/electron-ipc.md, apps/content/docs/playgrounds.md
Updated documentation and navigation to include IPC/Electron IPC adapters, integration guides, and a new Electron playground entry.
eslint.config.js Broadened ESLint rule disabling for node/prefer-global/process to all playground files.
package.json Added pnpm.onlyBuiltDependencies config for Electron and esbuild.
packages/client/package.json, packages/server/package.json Added "electron-ipc" adapter exports, peer/dev dependencies for Electron, and related metadata.
packages/client/src/adapters/electron-ipc/consts.ts Added and exported DEFAULT_ORPC_HANDLER_CHANNEL constant.
packages/client/src/adapters/electron-ipc/index.ts New entry point re-exporting all adapter modules for Electron IPC.
packages/client/src/adapters/electron-ipc/link-client.ts Introduced experimental_LinkElectronIPCClient for IPC communication, with channel config and message serialization.
packages/client/src/adapters/electron-ipc/rpc-link.ts Added experimental_RPCLink class and options interface for Electron IPC transport.
packages/client/src/adapters/electron-ipc/types.ts Defined and exported experimental_ExposedORPCHandlerChannel interface.
packages/client/src/adapters/electron-ipc/rpc-link.test.ts New test suite for Electron IPC RPC link, covering message sending and response handling.
packages/server/src/adapters/electron-ipc/expose.ts Added experimental_exposeORPCHandlerChannel to expose IPC handler to Electron renderer via contextBridge.
packages/server/src/adapters/electron-ipc/handler.ts Introduced experimental_ElectronIPCHandler for handling IPC messages and managing peer lifecycles.
packages/server/src/adapters/electron-ipc/rpc-handler.ts Added experimental_RPCHandler class for Electron IPC, bridging router and handler logic.
packages/server/src/adapters/electron-ipc/index.ts New module re-exporting expose, handler, and rpc-handler for centralized imports.
packages/server/src/adapters/electron-ipc/expose.test.ts,
packages/server/src/adapters/electron-ipc/rpc-handler.test.ts
New test suites for IPC handler channel exposure and RPC handler behavior in Electron IPC context.
playgrounds/electron/.gitignore, .vscode/settings.json, electron-builder.yml,
electron.vite.config.ts, package.json, tsconfig.json, tsconfig.node.json, tsconfig.web.json
Added Electron playground project config, build scripts, VSCode settings, and .gitignore.
playgrounds/electron/README.md New README introducing the Electron playground and setup instructions.
playgrounds/electron/src/main/index.ts Main Electron process script: creates window, sets up IPC, integrates oRPC handler, and manages app lifecycle.
playgrounds/electron/src/main/middlewares/auth.ts,
db.ts,
retry.ts
Added authentication, database provider, and retry middlewares for request context management.
playgrounds/electron/src/main/orpc.ts Established pub and authed middleware chains for RPC context.
playgrounds/electron/src/main/router/auth.ts,
planet.ts,
sse.ts,
index.ts
Defined authentication, planet CRUD, SSE routes, and router aggregation.
playgrounds/electron/src/main/schemas/auth.ts,
planet.ts,
user.ts
Added Zod schemas and types for authentication, planet, and user data validation.
playgrounds/electron/src/preload/index.d.ts,
index.ts
Preload script and types: exposes APIs and sets up IPC handler channel for renderer.
playgrounds/electron/src/renderer/index.html Renderer HTML entry point with strict CSP and root mounting div.
playgrounds/electron/src/renderer/src/App.tsx Main React app component rendering planet mutation form and list.
playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx,
orpc-query.tsx
React components for creating planets and listing planets via oRPC and React Query.
playgrounds/electron/src/renderer/src/lib/orpc.ts Set up typed oRPC client and React Query utilities for renderer.
playgrounds/electron/src/renderer/src/main.tsx React entry point: sets up QueryClientProvider and renders App.
playgrounds/electron/src/renderer/src/playground-client.ts,
playground-query.ts
Example modules demonstrating client-side oRPC usage and query/mutation with error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as Renderer Process
    participant Preload as Preload Script
    participant Main as Main Process
    participant oRPC as oRPC Handler

    Renderer->>Preload: Calls exposed oRPC API (via window.api)
    Preload->>Main: Sends IPC message (request)
    Main->>oRPC: Passes request to oRPC handler
    oRPC-->>Main: Returns response
    Main->>Preload: Sends IPC message (response)
    Preload->>Renderer: Delivers response to client code
Loading

Poem

🐇
In circuits bright and windows wide,
oRPC hops the Electron tide.
With channels linked and handlers new,
Main and renderer now rendezvous.
Docs are fresh, the playground's here—
A rabbit’s leap, with code sincere!
🚀


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

Codecov Report

Attention: Patch coverage is 96.77419% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/server/src/adapters/electron-ipc/index.ts 0.00% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 14, 2025

More templates

@orpc/arktype

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

@orpc/client

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

@orpc/nest

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

@orpc/contract

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

@orpc/openapi

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

@orpc/openapi-client

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

@orpc/react-query

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

@orpc/react

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

@orpc/server

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

@orpc/shared

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

@orpc/solid-query

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

@orpc/standard-server

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

@orpc/standard-server-fetch

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

@orpc/standard-server-node

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

@orpc/standard-server-peer

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

@orpc/svelte-query

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

@orpc/valibot

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

@orpc/vue-colada

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

@orpc/vue-query

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

@orpc/zod

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

commit: 929d646

@dinwwwh dinwwwh marked this pull request as ready for review May 15, 2025 09:32
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: 38

🧹 Nitpick comments (55)
eslint.config.js (1)

57-62: Expand rule disable to cover Electron playground
Moving 'node/prefer-global/process': 'off' into the broader playgrounds/** override is correct—your new Electron IPC code relies on the global process object and this prevents lint errors in all playground variants.

Optional refactor: Consolidate overrides
The playgrounds/** pattern appears in multiple override blocks (lines 33, 41, and 57). Consider merging these into a single block to reduce duplication and improve maintainability.

playgrounds/electron/.gitignore (1)

1-6: Refine the log file ignore pattern
The pattern *.log* may unintentionally ignore files like logic.js. It’s safer to target only log files.

 node_modules
 dist
 out
 .DS_Store
 .eslintcache
-*.log*
+*.log
packages/client/src/adapters/electron-ipc/consts.ts (1)

1-1: Consider adding a TSDoc comment for the constant
Documenting the purpose of DEFAULT_ORPC_HANDLER_CHANNEL will help future maintainers understand its role in the IPC adapter.

+/**
+ * Default channel name used for Electron IPC calls in oRPC client/server adapters.
+ */
 export const DEFAULT_ORPC_HANDLER_CHANNEL = 'orpc:default'
apps/content/docs/playgrounds.md (1)

23-23: Clarify missing StackBlitz link for Electron Playground
The StackBlitz column is empty; using or N/A makes it clear that this playground isn’t available on StackBlitz.

-| Electron Playground       |                                                                                                     | [View Source](https://github.com/unnoq/orpc/tree/main/playgrounds/electron)       |
+| Electron Playground       | —                                                                                                   | [View Source](https://github.com/unnoq/orpc/tree/main/playgrounds/electron)       |
packages/server/src/adapters/electron-ipc/index.ts (1)

1-3: Address test coverage for re-export module
Static analysis flags missing coverage on these lines. Since this file only re-exports logic, consider adding a simple test that imports and asserts the exports (or updating coverage rules to exclude it). Let me know if you’d like help with a test snippet.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 2-3: packages/server/src/adapters/electron-ipc/index.ts#L2-L3
Added lines #L2 - L3 were not covered by tests

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

4-13: Review component structure for accessibility and semantics.
Consider wrapping the header and sections in semantic HTML elements (e.g., <header>, <main>, <section>) or adding ARIA attributes for better accessibility, especially as the playground UI expands.

Optionally, you could update:

- <div>
+ <main>
    <h1>ORPC Playground</h1>
    <hr />
    <CreatePlanetMutationForm />
    <hr />
    <ListPlanetsQuery />
- </div>
+ </main>
playgrounds/electron/src/renderer/src/main.tsx (2)

6-6: Consider customizing QueryClient defaults
You may want to configure default options (e.g., staleTime, cacheTime) on new QueryClient({ defaultOptions: { queries: { ... } } }) to better suit your playground’s data fetching needs.


8-14: Handle potential null root element
The non-null assertion (!) bypasses runtime checks. To improve robustness, explicitly guard against a missing #root element:

-createRoot(document.getElementById('root')!).render(
+const container = document.getElementById('root');
+if (!container) throw new Error('Root container not found');
+createRoot(container).render(
playgrounds/electron/electron.vite.config.ts (1)

13-17: Clarify alias resolution path
For better clarity and portability, consider anchoring the alias to the config file’s directory:

- '@renderer': resolve('src/renderer/src'),
+ '@renderer': resolve(__dirname, 'src/renderer/src'),

This ensures the path remains correct regardless of the working directory.

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

8-10: Enhance token validation
The token field is a plain string. Optionally, you could enforce a JWT format or length check:

token: z.string().regex(/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/, 'Invalid JWT')
playgrounds/electron/tsconfig.web.json (1)

19-24: Optimize include globs
You can simplify your include patterns by combining extensions:

- "src/renderer/src/**/*",
- "src/renderer/src/**/*.tsx",
+ "src/renderer/src/**/*.{ts,tsx}",

This reduces redundancy.

playgrounds/electron/src/preload/index.ts (3)

5-6: Consider enhancing the custom API object with actual functionality.

The api object is currently empty but is exposed to the renderer process. If there are no custom APIs needed at this point, consider adding a comment explaining its purpose or future use, or consider removing it until needed.


11-19: Enhance error handling with specific actions.

The error handling in the try-catch block currently logs the error but doesn't implement any recovery mechanism or specific actions based on the error type. Consider adding more robust error handling, especially for critical preload functionality.

  try {
    contextBridge.exposeInMainWorld('electron', electronAPI)
    contextBridge.exposeInMainWorld('api', api)
  }
  catch (error) {
    console.error(error)
+   // Consider additional error handling or recovery actions
+   // For example: window.alert('Failed to initialize Electron API')
  }

20-25: Replace TypeScript ignore comments with proper type definitions.

Using @ts-expect-error is a temporary solution. Consider adding proper type definitions in a declaration file instead.

else {
-  // @ts-expect-error (define in dts)
  window.electron = electronAPI
-  // @ts-expect-error (define in dts)
  window.api = api
}

You could create a declaration file (e.g., electron-env.d.ts) with:

interface Window {
  electron: typeof import('@electron-toolkit/preload').electronAPI;
  api: {}; // Define the type of your custom API
}
packages/server/src/adapters/electron-ipc/expose.test.ts (2)

18-33: Test description could be more specific.

The current test name 'exposeORPCHandlerChannel' just repeats the function name without describing the behavior being tested. Consider renaming it to describe the expected behavior.

-it('exposeORPCHandlerChannel', () => {
+it('should expose ORPC handler channel with send and receive methods', () => {

Additionally, consider adding a test case for when context isolation is disabled, as this function might behave differently in that scenario.


29-32: Consider testing error handling scenarios.

The test verifies the happy path but doesn't test how the function handles errors. Consider adding test cases for error scenarios, such as:

  1. What happens if contextBridge throws an error?
  2. What happens if ipcRenderer.on throws an error?

This would ensure the function handles error cases gracefully.

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

5-6: Add documentation to clarify the purpose of pub.

Consider adding JSDoc comments to explain the purpose of the pub export and what functionality the dbProviderMiddleware adds to it.

+/**
+ * Public oRPC server object with database provider middleware.
+ * This can be used for endpoints that don't require authentication.
+ */
export const pub = os
  .use(dbProviderMiddleware)

8-9: Add documentation to clarify the purpose of authed.

Consider adding JSDoc comments to explain the purpose of the authed export and emphasize that it builds on top of pub by adding authentication requirements.

+/**
+ * Authenticated oRPC server object. Extends the public server with authentication
+ * requirements. Use this for endpoints that require a logged-in user.
+ */
export const authed = pub
  .use(requiredAuthMiddleware)

1-9: Consider adding explicit type annotations.

While TypeScript can infer the types, explicit type annotations would make the API contract clearer for consumers of these exports.

import { os } from '@orpc/server'
import { dbProviderMiddleware } from './middlewares/db'
import { requiredAuthMiddleware } from './middlewares/auth'

-export const pub = os
+export const pub = os as ReturnType<typeof os.use<typeof dbProviderMiddleware>>
  .use(dbProviderMiddleware)

-export const authed = pub
+export const authed = pub as ReturnType<typeof pub.use<typeof requiredAuthMiddleware>>
  .use(requiredAuthMiddleware)

This would help developers understand exactly what they're working with when using these exports.

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

18-21: Enhance type safety for context.

The middleware adds a user to the context but doesn't explicitly type what it returns. Consider enhancing the type safety:

return next({
-  context: { user: session.user },
+  context: { 
+    session,
+    user: session.user 
+  },
})

This way, downstream middlewares can access both the session and user directly, and the types are more complete.


4-6: Be more explicit about context types.

The type definition for the context is minimal. Consider providing more comprehensive type information:

export const requiredAuthMiddleware = os
-  .$context<{ session?: { user?: User } }>()
+  .$context<{ 
+    session?: { 
+      user?: User;
+      // Add other session properties that might be relevant
+    };
+  }>()

Also consider defining the expected return context type to make the contract clearer.

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

12-17: Consider adding JSDoc comments for better documentation

While the naming is clear, adding JSDoc comments to describe the purpose and parameters of each procedure would improve developer experience, especially for consumers of this API.

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

7-7: Add configuration options to RPCLink constructor

The RPCLink is initialized with zero arguments, but may need configuration for more complex scenarios (like custom channel names, timeouts, etc.). Consider documenting what options are available or configurable.

-const link = new RPCLink()
+// Initialize RPCLink with default configuration
+// Consider adding options for custom IPC channel names, timeouts, etc. if needed
+const link = new RPCLink({
+  // Example: channelName: 'custom-ipc-channel',
+  // Example: timeout: 30000,
+})
playgrounds/electron/src/renderer/src/components/orpc-query.tsx (3)

4-11: Implement error handling with more details than just "Something went wrong"

The infinite query setup is well-structured with good pagination parameters, but the error handling could be improved.

Consider enhancing the error handling to provide more context to users:

const { data, refetch, fetchNextPage, hasNextPage, status, error } = useSuspenseInfiniteQuery(
  orpc.planet.list.infiniteOptions({
    input: cursor => ({ cursor, limit: 10 }),
    getNextPageParam: lastPage => lastPage.length === 10 ? lastPage.at(-1)?.id : null,
    initialPageParam: 0,
  }),
)

if (status === 'error') {
  return (
    <p>
-     Something went wrong.
+     Error loading planets: {error instanceof Error ? error.message : 'Unknown error'}
    </p>
  )
}

13-19: Consider adding a loading state indicator

The component currently doesn't show a loading state when fetching additional pages.

You could add a loading indicator when fetching the next page:

const { data, refetch, fetchNextPage, hasNextPage, status, isFetchingNextPage } = useSuspenseInfiniteQuery(
  // ... existing options
)

// ... existing code

<button
  type="button"
  onClick={() => fetchNextPage()}
  disabled={!hasNextPage || isFetchingNextPage}
>
-  Load more
+  {isFetchingNextPage ? 'Loading...' : 'Load more'}
</button>

47-63: Add accessibility improvements to the table footer

The current implementation could benefit from some accessibility improvements.

<tfoot>
  <tr>
    <td colSpan={4}>
      <button
        type="button"
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage}
+       aria-label="Load more planets"
      >
        Load more
      </button>

      <button 
        type="button" 
        onClick={() => refetch()}
+       aria-label="Refresh planet list"
      >
        Refresh
      </button>
    </td>
  </tr>
</tfoot>
packages/server/src/adapters/electron-ipc/expose.ts (1)

15-18: Consider adding error handling for IPC operations

The IPC operations don't have any error handling, which could make debugging issues difficult.

contextBridge.exposeInMainWorld(channel, {
-  send: message => ipcRenderer.send(channel, message),
+  send: message => {
+    try {
+      ipcRenderer.send(channel, message);
+    } catch (error) {
+      console.error(`Error sending message on channel ${channel}:`, error);
+      throw error;
+    }
+  },
  receive: callback => ipcRenderer.on(channel, (_, message) => callback(message)),
} satisfies experimental_ExposedORPCHandlerChannel)
playgrounds/electron/src/main/middlewares/retry.ts (1)

26-32: Add logging for retry attempts

The current implementation doesn't log retry attempts, making it difficult to debug issues.

catch (e) {
  if (times >= options.times) {
+   console.warn(`Retry middleware: maximum retries (${options.times}) reached, re-throwing error`, e)
    throw e
  }

+ console.info(`Retry middleware: attempt ${times + 1}/${options.times} failed, retrying...`, e)
  times++
}
playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (1)

41-55: Enhance form accessibility and validation

The form could benefit from additional accessibility attributes and client-side validation.

<form
+ aria-label="Create Planet Form"
  onSubmit={(e) => {
    // ...existing code
  }}
>
- <label>
+ <label htmlFor="planet-name">
    Name
-   <input type="text" name="name" required />
+   <input id="planet-name" type="text" name="name" required minLength="3" maxLength="50" />
  </label>
- <label>
+ <label htmlFor="planet-description">
    Description
-   <textarea name="description" />
+   <textarea id="planet-description" name="description" maxLength="500" />
  </label>
- <label>
+ <label htmlFor="planet-image">
    Image
-   <input type="file" name="image" accept="image/*" />
+   <input id="planet-image" type="file" name="image" accept="image/*" aria-describedby="image-help" />
+   <span id="image-help" className="help-text">Optional. Maximum file size: 5MB</span>
  </label>
  <button type="submit">Submit</button>
</form>
playgrounds/electron/src/main/index.ts (2)

57-58: Replace console.log with structured logging.

Using console.log for IPC test responses doesn't provide context about which component is logging or the severity level.

Consider using a structured logging approach:

-  ipcMain.on('ping', () => console.log('pong'))
+  ipcMain.on('ping', () => console.log('[IPC Test] Received ping, responding with pong'))

65-66: Missing braces for multi-line if statement.

The if statement has no braces which can lead to bugs if more statements are added later.

-    if (BrowserWindow.getAllWindows().length === 0)
-      createWindow()
+    if (BrowserWindow.getAllWindows().length === 0) {
+      createWindow()
+    }
packages/client/src/adapters/electron-ipc/rpc-link.test.ts (5)

6-6: Replace delete operator with undefined assignment.

Using the delete operator on globals can impact performance as noted by the static analysis tool.

-  delete (globalThis as any)['orpc:default']
+  (globalThis as any)['orpc:default'] = undefined
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-6: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


11-13: Fix typo in test description and improve error assertion.

The test description has a grammatical error, and the error assertion doesn't verify the specific error message.

-  it('it throw if not find exposed channel', () => {
-    expect(() => new RPCLink()).toThrow()
+  it('should throw if exposed channel is not found', () => {
+    expect(() => new RPCLink()).toThrow(/exposed channel not found/i)

18-21: Fix indentation in the code.

There's unusual indentation with a semicolon at the beginning of line 18.

-          ; (globalThis as any)['orpc:default'] = {
-      send,
-      receive,
-    }
+    (globalThis as any)['orpc:default'] = {
+      send,
+      receive,
+    }

30-30: Use a more explicit approach for asynchronous test flow.

Using setTimeout with 0 delay is a less clear way to wait for the next event loop cycle.

-      await new Promise(resolve => setTimeout(resolve, 0))
+      await Promise.resolve() // Wait for next event loop cycle

55-55: Duplicate timeout pattern that could be extracted.

The same timeout pattern is used in both test cases and could be extracted to a helper function.

+  const flushPromises = () => Promise.resolve()

   // In the first test:
-      await new Promise(resolve => setTimeout(resolve, 0))
+      await flushPromises()

   // In the second test:
-      await new Promise(resolve => setTimeout(resolve, 0))
+      await flushPromises()
packages/server/package.json (1)

63-66: Specify experimental status in package exports.

The exports don't indicate the experimental status of the Electron IPC adapter, unlike how it's imported in the code with an experimental_ prefix.

Consider renaming the export or adding a note in the documentation to clearly indicate the experimental status.

playgrounds/electron/src/renderer/src/playground-query.ts (1)

18-21: Empty error handling logic.

The error handling checks for a defined error but doesn't do anything with it.

Either implement proper error handling or remove the empty block:

     onError(error) {
       if (isDefinedError(error)) {
         const id = error.data.id
-        //    ^    type-safe
+        console.error(`Error updating planet with ID ${id}:`, error.message)
+        // Consider showing a notification or updating UI state here
       }
     },
packages/server/src/adapters/electron-ipc/rpc-handler.test.ts (3)

37-50: Improve type safety in request message creation

Currently, the request messages are being cast with as any which bypasses TypeScript's type checking. This could mask potential type-related issues, especially when handling binary data.

Consider using proper type declarations instead of as any casts where possible:

-const ping_request_message = await encodeRequestMessage(19, MessageType.REQUEST, {
+type RequestPayload = Parameters<typeof encodeRequestMessage>[2];
+
+const ping_request_message = await encodeRequestMessage(19, MessageType.REQUEST, {
  url: new URL('orpc:/ping'),
  body: { json: 'input' },
  headers: {},
  method: 'POST',
-}) as any
+} as RequestPayload)

-const file_request_message = new TextEncoder().encode(await encodeRequestMessage(19, MessageType.REQUEST, {
+const file_request_message = new TextEncoder().encode(await encodeRequestMessage(19, MessageType.REQUEST, {
  url: new URL('orpc:/file'),
  body: { json: 'input' },
  headers: {},
  method: 'POST',
-}) as string) as any
+} as RequestPayload) as string)

33-33: Consider adding a type annotation for clarity

The variable onMessage is initialized as any type, which can lead to type safety issues later in the test.

-let onMessage: any
+let onMessage: (event: { sender: any }, message: any) => void

68-81: Add a timeout to the test to prevent potential hanging

This test relies on setTimeout for asynchronous operations but doesn't have an explicit timeout. While it's unlikely to cause issues with the current implementation, it's a good practice to add timeouts to prevent tests from hanging indefinitely if there's an issue.

-it('on success', async () => {
+it('on success', async () => {
  onMessage({ sender }, ping_request_message)

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

  const [id, , payload] = (await decodeResponseMessage(sender.send.mock.calls[0]![1]))

  expect(id).toBeTypeOf('number')
  expect(payload).toEqual({
    status: 200,
    headers: {},
    body: { json: 'pong' },
  })
-})
+}, 100)
packages/client/src/adapters/electron-ipc/link-client.ts (2)

23-27: Improve error message for missing ORPC handler channel

When the exposed ORPC handler channel is not found in the global context, a more detailed error message could help users with troubleshooting.

if (!exposed) {
-  throw new Error(`Cannot find exposed ORPC handler channel at globalThis['${channel}']`)
+  throw new Error(`Cannot find exposed ORPC handler channel at globalThis['${channel}']. Make sure you have called 'exposeORPCHandlerChannel()' in your preload script and your preload script is properly configured in your Electron app.`)
}

38-41: Simplify the call method implementation

The current implementation creates a new object with a body accessor function that immediately resolves the body. This seems unnecessary as the body is already resolved.

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) }
+  return { ...response, body: () => Promise.resolve(response.body) }
}

Actually, it might be more efficient to just return:

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) }
+  return response
}

This depends on whether response already implements the StandardLazyResponse interface correctly. If it does, there's no need to create a new object.

playgrounds/electron/package.json (1)

22-25: Use specific versions for internal dependencies

The ORPC libraries are specified with "next" tag instead of specific versions. This could lead to unexpected breaking changes when new versions are released.

Consider pinning internal dependencies to specific versions or version ranges to ensure predictable builds and prevent unexpected breaking changes.

- "@orpc/client": "next",
- "@orpc/react-query": "next",
- "@orpc/server": "next",
- "@orpc/zod": "next",
+ "@orpc/client": "^1.0.0",
+ "@orpc/react-query": "^1.0.0",
+ "@orpc/server": "^1.0.0",
+ "@orpc/zod": "^1.0.0",
playgrounds/electron/src/main/schemas/user.ts (1)

4-5: Type exports defined before their implementations

The type exports on lines 4-5 reference schemas that are defined later in the file. While this works in TypeScript, it's generally better practice to define types after their implementations for readability.

Consider moving the type exports after their schema implementations:

import { oz } from '@orpc/zod'
import { z } from 'zod'

- export type NewUser = z.infer<typeof NewUserSchema>
- export type User = z.infer<typeof UserSchema>
-
export const NewUserSchema = oz.openapi(
  /* ... */
)

export const UserSchema = oz.openapi(
  /* ... */
)

+ export type NewUser = z.infer<typeof NewUserSchema>
+ export type User = z.infer<typeof UserSchema>
packages/client/src/adapters/electron-ipc/rpc-link.ts (1)

7-8: Improve interface readability with line breaks

The interface declaration extends across a long line which reduces readability. Breaking this into multiple lines would improve code clarity.

- export interface experimental_RPCLinkOptions<T extends ClientContext>
-   extends Omit<StandardRPCLinkOptions<T>, 'url' | 'headers' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, LinkElectronIPCClientOptions {}
+ export interface experimental_RPCLinkOptions<T extends ClientContext>
+   extends Omit<
+     StandardRPCLinkOptions<T>, 
+     'url' | 'headers' | 'method' | 'fallbackMethod' | 'maxUrlLength'
+   >, 
+   LinkElectronIPCClientOptions {}
playgrounds/electron/electron-builder.yml (1)

21-28: Consider enabling notarization for macOS production builds

Notarization is currently disabled (notarize: false), which is fine for development but may cause security warnings when distributing the app to macOS users in production. For the playground this is acceptable, but document this requirement if this configuration will be used as a template for production apps.

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

73-75: Add bounds checking to list method

The list method doesn't handle cases where cursor + limit exceeds the array length, which could potentially cause issues.

list: async (limit: number, cursor: number) => {
-  return planets.slice(cursor, cursor + limit)
+  return planets.slice(cursor, Math.min(cursor + limit, planets.length))
},
playgrounds/electron/src/main/schemas/planet.ts (1)

22-28: Consider adding validation for imageUrl pattern

The imageUrl field is validated as a URL, but you might want to consider adding a pattern validation to ensure it matches the format used in your create and update methods (e.g., https://example.com/cdn/{filename}).

- imageUrl: z.string().url().optional(),
+ imageUrl: z.string().url().regex(/^https:\/\/example\.com\/cdn\/.*/).optional(),
packages/server/src/adapters/electron-ipc/handler.ts (2)

38-47: Lifecycle-clean-up looks good – but watch for other WebContents exit signals

Hooking the destroyed event is great, but WebContents can also disappear via crashed, did-fail-load, or did-navigate (which produces a fresh WebContents instance).
If a crash/navigation occurs without emitting destroyed, the ServerPeer may linger in #peers, preventing GC of large message buffers.

It’s worth listening for crashed and did-navigate (checking event.isDestroyed() on callback) and performing the same peer.close() / map cleanup.


40-41: Broaden binary payload handling

raw instanceof Blob only covers browser-side Blob. In Node/Electron, large binary bodies are more commonly Buffer or ArrayBuffer.
To avoid silently sending an unsupported object over IPC, convert additional types, e.g.:

-event.sender.send(this.#channel, raw instanceof Blob ? await raw.arrayBuffer() : raw)
+const payload =
+  raw instanceof Blob      ? await raw.arrayBuffer()
+  : Buffer.isBuffer(raw)   ? raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength)
+  : raw
+event.sender.send(this.#channel, payload)
playgrounds/electron/src/main/router/planet.ts (4)

16-20: Minor: default() + min() interplay

limit and cursor declare both min() and default().
Zod applies default() first, then validation, so the defaults (10, 0) themselves pass the min() constraint. That’s fine, but if your intent was “optional but validated only when provided”, you can achieve that more clearly with .optional().default(value) (avoids the double-validation).


40-44: Retry middleware – consider back-off

Three immediate retries in a tight while(true) loop can hammer the database if it is already under load. A simple exponential back-off (e.g., await delay(2 ** times * 50)) inside the catch branch would mitigate burst traffic during transient failures.


70-75: errors.NOT_FOUND schema: simplify with .pick()

You can avoid repeating the id type by re-using the existing schema:

-data: z.object({ id: UpdatePlanetSchema.shape.id }),
+data: UpdatePlanetSchema.pick({ id: true }),

This reduces maintenance friction if the id constraints change later.


81-95: Remove commented-out legacy code before merging

The alternative ORPCError throw (lines 90-94) is valuable history but clutters the production code path. Move it to git history or project docs to keep the source clean.

📜 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 cac5dd3 and 7f5c7ce.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • apps/content/.vitepress/config.ts (2 hunks)
  • apps/content/docs/adapters/ipc.md (1 hunks)
  • apps/content/docs/integrations/electron-ipc.md (1 hunks)
  • apps/content/docs/playgrounds.md (2 hunks)
  • eslint.config.js (1 hunks)
  • package.json (1 hunks)
  • packages/client/package.json (3 hunks)
  • packages/client/src/adapters/electron-ipc/consts.ts (1 hunks)
  • packages/client/src/adapters/electron-ipc/index.ts (1 hunks)
  • packages/client/src/adapters/electron-ipc/link-client.ts (1 hunks)
  • packages/client/src/adapters/electron-ipc/rpc-link.test.ts (1 hunks)
  • packages/client/src/adapters/electron-ipc/rpc-link.ts (1 hunks)
  • packages/client/src/adapters/electron-ipc/types.ts (1 hunks)
  • packages/server/package.json (4 hunks)
  • packages/server/src/adapters/electron-ipc/expose.test.ts (1 hunks)
  • packages/server/src/adapters/electron-ipc/expose.ts (1 hunks)
  • packages/server/src/adapters/electron-ipc/handler.ts (1 hunks)
  • packages/server/src/adapters/electron-ipc/index.ts (1 hunks)
  • packages/server/src/adapters/electron-ipc/rpc-handler.test.ts (1 hunks)
  • packages/server/src/adapters/electron-ipc/rpc-handler.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 (15)
packages/server/src/adapters/electron-ipc/rpc-handler.ts (3)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/standard/rpc-handler.ts (2)
  • StandardRPCHandlerOptions (10-11)
  • StandardRPCHandler (13-22)
packages/server/src/router.ts (1)
  • Router (12-17)
playgrounds/electron/src/renderer/src/components/orpc-query.tsx (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (11-11)
playgrounds/electron/src/renderer/src/main.tsx (1)
playgrounds/solid-start/src/app.tsx (1)
  • App (8-22)
playgrounds/electron/src/renderer/src/playground-query.ts (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (11-11)
playgrounds/electron/src/renderer/src/playground-client.ts (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (11-11)
playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (1)
playgrounds/electron/src/renderer/src/lib/orpc.ts (1)
  • orpc (11-11)
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 (14-19)
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/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/middlewares/retry.ts (1)
packages/server/src/builder.ts (1)
  • os (336-352)
packages/server/src/adapters/electron-ipc/expose.ts (2)
packages/client/src/adapters/electron-ipc/consts.ts (1)
  • DEFAULT_ORPC_HANDLER_CHANNEL (1-1)
packages/client/src/adapters/electron-ipc/types.ts (1)
  • experimental_ExposedORPCHandlerChannel (1-4)
playgrounds/electron/src/main/schemas/user.ts (1)
packages/zod/src/index.ts (1)
  • oz (16-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)
playgrounds/electron/src/main/router/planet.ts (3)
playgrounds/electron/src/main/orpc.ts (2)
  • pub (5-6)
  • authed (8-9)
playgrounds/electron/src/main/middlewares/retry.ts (1)
  • retry (3-35)
playgrounds/electron/src/main/schemas/planet.ts (3)
  • PlanetSchema (22-28)
  • NewPlanetSchema (9-13)
  • UpdatePlanetSchema (15-20)
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/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)
🪛 markdownlint-cli2 (0.17.2)
playgrounds/electron/README.md

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

(MD045, no-alt-text)

🪛 GitHub Check: codecov/patch
packages/server/src/adapters/electron-ipc/index.ts

[warning] 2-3: packages/server/src/adapters/electron-ipc/index.ts#L2-L3
Added lines #L2 - L3 were not covered by tests

🪛 Biome (1.9.4)
packages/client/src/adapters/electron-ipc/rpc-link.test.ts

[error] 6-6: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

packages/server/src/adapters/electron-ipc/expose.test.ts

[error] 15-15: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

packages/server/src/adapters/electron-ipc/rpc-handler.test.ts

[error] 61-66: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

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

1-3: Enable VSCode format-on-save
This setting correctly enforces code formatting on every save, which helps maintain consistency in the playground.

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

90-90: Add IPC adapter link to the sidebar
This correctly integrates the new IPC adapter documentation under the “Adapters” section, making it discoverable.


109-109: Add Electron IPC integration link to the sidebar
Including this under “Integrations” ensures users can easily find the Electron IPC guide.

playgrounds/electron/tsconfig.json (1)

1-4: Root TypeScript Project References
The tsconfig.json correctly references both tsconfig.node.json and tsconfig.web.json. Ensure each referenced config has "composite": true to support project references.

packages/client/src/adapters/electron-ipc/index.ts (1)

1-4: Centralize Electron IPC client exports
Re-exporting from consts, link-client, rpc-link, and types provides a clear, single entry point for consumers.

package.json (1)

51-56: Optimize pnpm build with onlyBuiltDependencies
Defining electron and esbuild here streamlines native builds for the Electron playground and adapters. This is a solid enhancement for build performance.

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

1-2: Approve import usage in the renderer entry point.
Imports CreatePlanetMutationForm and ListPlanetsQuery correctly reflect the new oRPC components.


16-17: Approve default export.
The default export of the App component follows convention for React entry points.

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

1-8: Approve global window augmentation.
The declare global augmentation with electron: ElectronAPI and api: unknown aligns with the preload script’s exposure, and the use of import type ensures this remains a type-only import.

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

1-17: Approve minimal HTML and CSP configuration.
The provided HTML structure and strict Content Security Policy correctly initialize the renderer and mitigate script injection risks. Ensure this policy doesn’t block any Vite HMR scripts in development.

You may want to verify that hot module replacement (HMR) works under this CSP during local development.

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

1-13: Approve Node-targeted TypeScript config.
Extending the Electron Toolkit’s base config and adjusting moduleResolution to bundler is appropriate for the Electron main/preload context. Disabling unused checks here helps reduce noise during development.

apps/content/docs/adapters/ipc.md (3)

1-4: Frontmatter is well-formed
The YAML frontmatter correctly defines title and description for this page.


10-13: Table formatting and links look good
The adapter table is clear and the links point to the correct Electron docs and integration guide.


14-16: Verify docs link resolution
Ensure that the relative link [docs](/docs/integrations/electron-ipc) correctly resolves in the VitePress site.

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

1-4: Imports and setup are correct
The import of StrictMode, createRoot, and React Query components is appropriate for a React 18+ application.

playgrounds/electron/electron.vite.config.ts (1)

1-4: Imports are correct
Using node:path and electron-vite imports aligns with the recommended pattern for Electron Vite configs.

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

3-6: Base config extension is correct
Extending @electron-toolkit/tsconfig/tsconfig.web.json and setting jsx/moduleResolution is appropriate for a React renderer.


15-16: Review disabling unused checks
You’ve set noUnusedLocals and noUnusedParameters to false, which may allow dead code to accumulate. Verify that this is intentional.

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

27-27: Consider conditionally exposing ORPC handler channel.

The exposeORPCHandlerChannel() function is called unconditionally, outside of the context isolation check. Consider whether this should also be conditional based on context isolation, or add a comment explaining why it's called unconditionally.

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

5-20: Well-organized router structure

The router implementation provides a clean, hierarchical organization of RPC procedures grouped by domain (auth, planet) with clear naming conventions. This structure makes the API surface easy to understand and maintain.

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

14-22: Clean implementation of SSE using async generator

This is a good example of leveraging async generators to implement server-sent events. The yield pattern with setTimeout creates a clean streaming implementation.

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

9-11: Well-typed client implementation with React Query integration

The implementation uses TypeScript's type system effectively to create a strongly-typed client that matches the main process router structure. The integration with React Query is clean and follows best practices.

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

1-2: Good import choice for querying with oRPC and React Query

Using useSuspenseInfiniteQuery is a good choice for paginated data fetching, and the minimal imports keep the component focused.

packages/server/src/adapters/electron-ipc/expose.ts (2)

1-3: Good import organization for the IPC adapter

The imports are well-structured, with type imports separated from value imports.


5-11: Well-documented function with clear purpose

The JSDoc documentation clearly explains the function's purpose and includes a link to further documentation.

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

1-2: Good import for server middleware context

The import is clean and focused, only bringing in what's needed.


3-16: Good implementation of middleware with context type safety

The middleware initialization with typed context is well done. The early return for non-retry contexts prevents unnecessary processing.

playgrounds/electron/src/renderer/src/components/orpc-mutation.tsx (1)

1-2: Good import choice for mutations with oRPC and React Query

Using useMutation and useQueryClient together is the right approach for cache invalidation after mutations.

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

4-4: Use a stable import rather than experimental APIs.

The code imports an experimental API and renames it to hide the experimental nature:

import { experimental_RPCHandler as RPCHandler } from '@orpc/server/electron-ipc'

Experimental APIs are subject to breaking changes in future releases. Consider one of these approaches:

  1. Keep the experimental prefix to make it clear to other developers that this API may change
  2. Check if a stable version of this API is available
  3. Document the risk of using experimental APIs in comments
apps/content/docs/integrations/electron-ipc.md (1)

1-46: Comprehensive and well-structured documentation for Electron IPC integration

The documentation clearly outlines the three-step process for integrating oRPC with Electron's IPC mechanism across main, preload, and renderer processes. The code examples are concise and demonstrate the proper usage of the experimental APIs.

The reference to additional information in the IPC Adapter guide and the note about client-side examples are helpful for users seeking more detailed context.

packages/server/src/adapters/electron-ipc/rpc-handler.ts (1)

1-21: Well-structured and cleanly implemented adapter class

The experimental_RPCHandler class provides a clear integration between the standard RPC handler and Electron IPC handler. The class extends the appropriate base class and follows the composition pattern by instantiating the StandardRPCHandler with the provided router and options.

The code structure is clean, and the type definitions are comprehensive with proper JSDoc comments for documentation.

playgrounds/electron/package.json (1)

26-38:

Details

❓ Verification inconclusive

Verify dependency compatibility

The dependencies include very recent versions (React 19, Node 22, TypeScript 5.8) which might have compatibility issues or be in beta/unstable state.


🌐 Web query:

Is React 19 stable and production-ready? What are the compatibility considerations?

💡 Result:

Is React 19 Stable and Production-Ready?

React 19 is officially stable and considered production-ready as of its December 2024 release[2][5][1]. The React core team has thoroughly tested the release, and it introduces significant improvements in performance, developer experience, and scalability, building upon the solid foundation of React 18[2][1][5]. Notably, features like the React Compiler, Actions API, improved Suspense, and React Server Components are now stable and ready for use in modern applications[1][2][4][5].

“React 19 is now stable! ... React v19 is now available on npm!” [2]

Compatibility Considerations

While React 19 itself is stable, upgrading existing projects—especially those that depend on a wide ecosystem of third-party libraries—requires careful attention. Here are the key compatibility points to consider:

Backward Compatibility

  • React 19 maintains backward compatibility with most existing React 18 code, and the migration process is designed to be smooth, with detailed upgrade guides provided by the React team[1][2][5].

Third-Party Library Support

  • Many popular libraries have not yet released versions fully compatible with React 19, leading to potential warnings or errors during installation or runtime[6][9].
  • The stricter peer dependency checking introduced in npm v7+ now throws installation errors for mismatches that might have previously gone unnoticed. This exposes incompatibilities more clearly but can also make upgrades more disruptive[6][9].
  • Developers have reported that, nearly a year after release, “many packages don’t support it without —force or legacy deps,” so you may encounter issues if your project relies on a broad array of community libraries[6].

Migration Procedures and Best Practices

  • Check Package Compatibility: Before upgrading, audit your dependencies to ensure that they explicitly support React 19[9][6][7].
  • Test Thoroughly: Focus on areas where breaking changes could impact your code—such as stricter ref forwarding, lifecycle method updates, and the new behaviors in Suspense and Server Components[9][5].
  • Handle Peer Dependencies Carefully: If you hit peer dependency errors, options include using --force or --legacy-peer-deps with npm, or leveraging the overrides field in package.json—but these are workarounds and may lead to runtime problems if underlying incompatibility exists[6].
  • Gradual Upgrades: For larger codebases or libraries, consider supporting both React 18 and 19 through conditional exports and careful TypeScript typing, as demonstrated by the MUI X team[7].

Notable Breaking Changes

  • Stricter ref handling: Updates to useRef() and RefObject typing may require refactoring, especially in TypeScript projects[7][9].
  • Lifecycle cleanups: React 19 cleans up effects more aggressively, which may reveal previously unnoticed bugs in component unmount logic[9].
  • Error boundaries and reporting: Error handling APIs are more robust, with new hooks and logging behaviors introduced[2][5].

Framework and Tooling Support

  • Frameworks like Next.js and TanStack Start have been actively updating to integrate the latest React features, but project defaults and documentation may still lag, with some tools defaulting to React 18[8][6].
  • Review community migration guides and changelogs for your primary frameworks before upgrading core dependencies[7][9].

Summary Table: React 19 Stability and Compatibility

Aspect Status/Consideration
Core React 19 Stable and production-ready[1][2][5]
Backward Compatibility Strong, smooth migration path from React 18[1][2][5]
Third-party Library Support Mixed—many libraries still updating, check before upgrading[6][9]
npm Peer Dependencies Enforced strictly, can block incompatible installs[6][9]
Ref & Lifecycle Changes Breaking changes may require small refactors[7][9]
Server Component Support Now considered stable in React 19[4][5]
Framework Support (Next.js) Mostly compatible, but verify before large upgrades[8][7]

Conclusion

React 19 is stable and production-ready, offering notable performance gains and new features[1][2][5]. However, real-world adoption depends on the compatibility of your wider stack, especially third-party libraries and tooling. Before upgrading, audit dependencies, test thoroughly, and consult migration documentation to ensure a smooth transition[6][9][7]. If you rely heavily on community packages, you may find it prudent to wait for more ecosystem updates—or prepare to address compatibility issues as you encounter them.

Citations:


React 19 is stable, but audit your dependencies for compatibility

React 19 is officially stable and production-ready, with strong backward compatibility from React 18. However, many community libraries are still updating their peer dependencies, so it’s important to verify that your stack supports React 19 before shipping.

Please review the following in playgrounds/electron/package.json (lines 26–38):

  • Confirm that each dependency declares React 19 support in its peerDependencies, especially:
    • @tanstack/react-query@^5.72.3
    • @vitejs/plugin-react@^4.4.1 and vite@^6.3.5
    • Any other React-related plugins or tooling
  • Run npm install (or pnpm install) and address any peer-dependency warnings or errors.
  • If you encounter conflicts, consider using the overrides field or --legacy-peer-deps, but only as a temporary workaround.
  • Test key areas (ref forwarding, Suspense, Server Components) to catch subtle breaking changes.
playgrounds/electron/electron-builder.yml (2)

42-43: Verify auto-update URL for production use

The auto-update URL is currently set to a placeholder (https://example.com/auto-updates). Ensure this is intentional for the playground, or update it to the actual URL before deploying to production.


1-11: App configuration looks good

The basic app configuration is well-structured with appropriate app ID, product name, and file inclusions/exclusions for the Electron build process.

packages/client/package.json (2)

43-47: Export configuration for Electron IPC adapter is properly structured

The export entries for the new Electron IPC adapter are correctly added to the publishConfig section.


55-56: Source exports properly configured

The source exports for the Electron IPC adapter are correctly specified.

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

14-29: Good use of deduplication middleware pattern

The middleware implementation correctly follows the documented best practice for deduplication by using the nullish coalescing operator to avoid unnecessary creation of the fake DB.


67-91: Create method implementation looks good

The create method correctly generates a new ID, handles optional image URLs, and adds the new planet to the in-memory database.

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

9-13: NewPlanetSchema is well-structured

The schema properly defines required and optional fields with appropriate validations. Good use of the oz.file() validator with type restriction to only accept image files.


15-20: UpdatePlanetSchema looks good

The schema correctly extends the new planet schema with a required ID field.

Comment thread playgrounds/electron/README.md
Comment thread packages/client/src/adapters/electron-ipc/types.ts
Comment thread playgrounds/electron/src/main/schemas/auth.ts
Comment thread packages/server/src/adapters/electron-ipc/expose.test.ts
Comment thread playgrounds/electron/src/main/middlewares/auth.ts
Comment thread packages/client/src/adapters/electron-ipc/rpc-link.ts
Comment thread packages/client/package.json
Comment thread playgrounds/electron/src/main/middlewares/db.ts
Comment thread packages/server/src/adapters/electron-ipc/handler.ts
Comment thread packages/server/src/adapters/electron-ipc/handler.ts
@aortbals
Copy link
Copy Markdown

@unnoq really appreciate your work on adding this! I was discussing this with my colleague @mutewinter and I'm taking a look at the PR. We're big fans of orpc and would love to be able to use this in Electron. I'm testing it locally and will provide feedback where I can. The instructions are straightforward and make sense. I also ran the playground locally and it works fine for me. Very straightforward.

One thing so far: I'm seeing an issue because the contextBridge API is being required downstream via the barrel file which ends up included in the main process where that module doesn't exist (it's only available in the render and preload processes). We have a pretty complex build process, but fundamentally it's similar to how the playground is setup with vite. I'm curious as to why there is a difference here and will provide an update when I figure it out. My hunch is that it's tree shaken out of the main build in the playground but not in our app for some reason.

Here is the exception that's thrown:

import { contextBridge, ipcRenderer, ipcMain } from 'electron';
         ^^^^^^^^^^^^^
SyntaxError: The requested module 'electron' does not provide an export named 'contextBridge'

@dinwwwh
Copy link
Copy Markdown
Member Author

dinwwwh commented May 21, 2025

Thanks @aortbals. By moving the API related to contextBridge to @orpc/client/electron-ipc, would that solve the problem? I noticed that trpc-electron also mixes contextBridge with other APIs in its main exports.

@dinwwwh
Copy link
Copy Markdown
Member Author

dinwwwh commented May 21, 2025

Checkout my new implementation with Message Port: https://github.com/unnoq/orpc/pull/528

@dinwwwh dinwwwh closed this May 21, 2025
@aortbals
Copy link
Copy Markdown

Great, i'll take a look. FWIW, I did determine it was tree-shaking that allowed it to work in the playground with the import and it wasn't working due to our custom use of the externalizeDepsPlugin plugin. I think it would have been best to address it by providing an additional export from the server package or moving it to the client like you mentioned. For electron, the best practice is typically to isolate the three runtime environments between the main process, preload, and renderer.

dinwwwh referenced this pull request May 22, 2025
Replaces: https://github.com/unnoq/orpc/pull/500

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants