feat(client, server): Message Port Adapter#528
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis 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
Changes (continued)
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)
Electron Integration: Renderer to Main Process via Message PortsequenceDiagram
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)
Browser Extension Integration: Popup to Background via Message PortsequenceDiagram
participant Popup
participant Background
Popup->>Background: browser.runtime.connect()
Background->>Background: handler.upgrade(port)
Popup->>Popup: client = new RPCLink(port)
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov ReportAll modified and coverable lines are covered by tests ✅ 📢 Thoughts on this report? Let us know! |
More templates
@orpc/arktype
@orpc/client
@orpc/nest
@orpc/contract
@orpc/openapi
@orpc/openapi-client
@orpc/react
@orpc/react-query
@orpc/server
@orpc/shared
@orpc/solid-query
@orpc/standard-server
@orpc/standard-server-fetch
@orpc/standard-server-node
@orpc/standard-server-peer
@orpc/svelte-query
@orpc/valibot
@orpc/vue-colada
@orpc/vue-query
@orpc/zod
commit: |
There was a problem hiding this comment.
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 likedist/,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 analtattribute. 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 fromhandlerandrpc-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
alttext 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 theAppreturn type asReact.JSX.Element. Consider adding: React.JSX.Elementto maintain consistency and leverage TypeScript’s explicit typing.playgrounds/electron/src/renderer/src/main.tsx (1)
1-14: Modern React setup with proper state managementThe 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 extensionThe 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 handlingThe 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:
- Add exponential backoff between retry attempts
- Add selective retry based on error types
- 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 assignmentThe 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 feedbackThe 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 statusThe 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 assignmentThe 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 feedbackThe 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 APIThe code example uses
experimental_RPCHandlerwhich 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 APISimilar to the main thread example, the experimental nature of
experimental_RPCLinkshould 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 linksThe 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 patternThe
build:winscript usespnpm run build && electron-builder --winpattern, whilebuild:macandbuild:linuxscripts don't include thepnpm runprefix.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 descriptionThe 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 consistencyThe 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 supportThe 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 feedbackWhile 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 tableThe 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 detailsThe 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 elementsThe 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-errorto bypass TypeScript checks when adding properties to thewindowobject. 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-errorcomments:- // @ts-expect-error (define in dts) window.electron = electronAPI - // @ts-expect-error (define in dts) window.api = apiplaygrounds/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
callmethod doesn't include explicit error handling. While errors fromthis.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: ClientPeerplaygrounds/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 constraintsThe 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 examplesThe 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 extensionThe 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.logUsing
console.logfor 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 specificityThe 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
getSessionfunction 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 extensionpackages/client/src/adapters/message-port/rpc-link.test.ts (2)
70-76: The port closure test could be more robustThe 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 casesThe test suite is missing an
afterEachblock 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 creationThe 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 valueThis 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
anytype 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 importThe 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 definitionsThe 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 UpdatePlanetSchemaThe
UpdatePlanetSchemarequires 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 indexThe pagination logic uses
slice(cursor, cursor + limit)which assumes thatcursoris 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 handlingThe 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 updatePlanetWhile 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:
- Removing the commented "Normal Approach" section and related comments
- 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 implementationsThis 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 onMessagePortMessageWhile the function handles different port types well, there's room for improvement in error handling.
Consider adding:
- Try/catch blocks around the callback invocations to prevent errors in user code from breaking the message handling
- Type guards for the data to ensure it matches
SupportedMessagePortDataexport 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 MessagePortMainLikeThe current interface allows
anydata 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
⛔ Files ignored due to path filters (7)
playgrounds/browser-extension/public/icon/128.pngis excluded by!**/*.pngplaygrounds/browser-extension/public/icon/16.pngis excluded by!**/*.pngplaygrounds/browser-extension/public/icon/32.pngis excluded by!**/*.pngplaygrounds/browser-extension/public/icon/48.pngis excluded by!**/*.pngplaygrounds/browser-extension/public/icon/96.pngis excluded by!**/*.pngplaygrounds/browser-extension/public/wxt.svgis excluded by!**/*.svgpnpm-lock.yamlis 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: Broadennode/prefer-global/processrule scope
The ESLint rule'node/prefer-global/process'is now disabled for the entireplaygrounds/**directory, accommodating the new Electron and browser extension playgrounds. Please confirm this broad scope is intentional and doesn’t suppress necessary warnings whereprocessshould remain the preferred global.apps/content/docs/playgrounds.md (1)
43-44: Local clone commands look good.The new
degitcommands 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
.gitignorepatterns covernode_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, andrpc-linksimplifies 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.jsonandtsconfig.web.jsonenable project references by setting"composite": truein theircompilerOptions.
🏁 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.jsonLength of output: 230
Composite project setup confirmed
Both playgrounds/electron/tsconfig.node.json and playgrounds/electron/tsconfig.web.json have
"composite": truein 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:
- Creates an RPC handler with the imported router
- Sets up a listener for runtime connections
- 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.
- 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].
- 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].
- 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:
- 1: https://www.typescriptlang.org/docs/handbook/modules/reference.html
- 2: https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix
- 3: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-6.html
- 4: https://dev.to/mscamargo/why-you-should-avoid-using-the-i-prefix-for-interfaces-in-typescript-43gd
- 5: https://google.github.io/styleguide/tsguide.html
- 6: https://konadu.dev/is-nextjs-using-unreleased-experimental-react-features
- 7: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_operators
- 8: https://firebase.google.com/docs/ml/manage-hosted-models
- 9: https://github.com/apollographql/apollo-server/blob/main/CHANGELOG_historical.md
- 10: https://www.scribd.com/document/556232208/Docs-Streamlit-Io-en-0-81-0
Use of Experimental RPCHandler
The import ofexperimental_RPCHandlersignals 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: Thetsconfig.node.jsoncorrectly extends the Electron toolkit base (@electron-toolkit/tsconfig/tsconfig.node.json), enables composite builds, and scopes module resolution to"bundler". Including theelectron-vite/nodetypes 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 forCreatePlanetMutationFormandListPlanetsQuerycorrectly reference the siblingcomponentsdirectory and match the project structure.playgrounds/browser-extension/tsconfig.json (1)
1-8: Configuration Correctness: Thetsconfig.jsonrightly extends the base./.wxt/tsconfig.json, enables the React 17+ JSX transform with"react-jsx", and allows importing.tsextensions. This aligns well with the browser extension’s popup setup.playgrounds/electron/src/renderer/src/App.tsx (1)
1-17: Component Structure Review: TheAppcomponent 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 popupThis 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 testingThe 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 communicationThe test setup correctly:
- Creates a MessageChannel with two ports
- Defines a simple echo procedure
- Sets up the server-side handler with port1
- Sets up the client-side link with port2
- 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 approachGreat use of parameterized testing with
it.eachto validate that all supported data types can be correctly transmitted through the MessagePort adapter.
26-32: Good coverage of nested data structuresThis 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 implementationThe imports correctly bring in
eventIteratorandosfrom@orpc/serveralong 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 valuesUsing a named constant for the maximum number of events improves code readability and maintainability.
6-12: Well-structured route definition with clear metadataThe 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 generatorThe handler implementation:
- Uses
eventIteratorwith proper Zod schema validation for type safety- Correctly implements the async generator pattern for streaming events
- Includes appropriate rate limiting (1 second between events)
- 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 environmentsThis 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 setupThe imports correctly bring in the base
osobject and the necessary middleware components for database access and authentication.
5-9: Well-designed middleware layering patternThe middleware setup follows a clean layering pattern:
pubchain applies database middleware for all routesauthedchain extendspubby adding authentication middlewareThis 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 classThe implementation is clean and follows good practice for extending the
MessagePortHandlerand utilizingStandardRPCHandler. 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
safewrapper 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
pubproviding database access andauthedextending 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/zodas 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 --jsonLength of output: 628
Confirm ORPC “next” tag vs. stable version
All four ORPC packages in playgrounds/electron/package.json currently pin to the
nextdist-tag, which resolves to the canary build0.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 thelatesttag (currently1.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.0No 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 QueryThe component correctly sets up infinite querying with pagination parameters and a cursor-based approach. The implementation of
getNextPageParamis 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 controlsThe 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 referencesThe 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 implementationThe 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 implementationThe 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 noteThe 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 parametersThe 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" buttonThe "Load more" button is correctly implemented with the
disabledattribute tied to thehasNextPagevalue, 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 importsThe 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 groupingThe 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.infiniteOptionsin 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
safeutility, 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 (
pubvsauthed) 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/mainLength 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.tsLength 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
mehandler
TherequiredAuthMiddlewarewired intoauthedalready throws ifcontext.session.useris missing and guarantees thatcontext.useris defined when your handler runs. You can safely returncontext.userwithout 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 integrationThis 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 linkThe 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 --includesyntax. 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.tsLength of output: 2378
Cleanup on port disconnection already implemented
Theexperimental_MessagePortHandler.upgrademethod registers anonMessagePortClose(port, …)listener that callspeer.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 postMessagePortMessageThe test effectively verifies that the function correctly delegates to the port's postMessage method.
16-94: Comprehensive test coverage for onMessagePortMessageThe tests thoroughly cover all supported message event listener patterns, preference order, and error handling.
96-174: Comprehensive test coverage for onMessagePortCloseThe 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
imageUrlwhile UpdatePlanetSchema usesimage. 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 caseThe test effectively validates the RPCLink functionality by:
- Creating an expectation for the promise resolution
- Verifying the message structure sent to the server
- 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 handlingThis 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 goodThe test properly verifies the response structure and data after sending a request message.
100-114: Good test for abort handlingThe test properly verifies that abort signals are propagated correctly.
116-129: Good test for port closure handlingThe test properly verifies that port closure aborts in-flight requests.
131-144: Good test for 404 handlingThe 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 schemaThe
NewPlanetSchemaandUpdatePlanetSchemahave animagefield for file upload, whilePlanetSchemahas animageUrlstring 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 instantiationGreat 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 methodThe 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 concernsThe 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 endpointThe
listPlanetshandler 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 endpointThe 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 endpointThe 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 portsThe type definitions effectively abstract the differences between message port implementations across platforms (Electron, Browser Extensions, standard Web MessagePort).
The
SupportedMessagePortDatatype appropriately restricts message data to what can be safely cloned.
Replaces: https://github.com/unnoq/orpc/pull/500
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
Chores