Skip to content

Separate host and client exports#5

Merged
hbmartin merged 1 commit intomainfrom
separate-host-and-client-exports
Sep 17, 2025
Merged

Separate host and client exports#5
hbmartin merged 1 commit intomainfrom
separate-host-and-client-exports

Conversation

@hbmartin
Copy link
Copy Markdown
Owner

@hbmartin hbmartin commented Sep 17, 2025

PR Type

Enhancement


Description

  • Separate host and client exports into dedicated entry points

  • Reorganize type definitions into structured modules

  • Move shared utilities to appropriate locations

  • Remove main index.ts export file


Diagram Walkthrough

flowchart LR
  A["index.ts"] --> B["host.ts"]
  A --> C["client.ts"]
  D["types/"] --> E["types/log.ts"]
  D --> F["types/rpc.ts"]
  D --> G["types/reducer.ts"]
  H["Shared utilities"] --> I["host/utils.ts"]
  H --> J["client/ipcReducer.ts"]
Loading

File Walkthrough

Relevant files
Enhancement
19 files
client.ts
Update client exports and type imports                                     
+5/-5     
WebviewLogger.ts
Move types to shared location                                                       
+2/-25   
ipcReducer.ts
Extract utility function for client                                           
+10/-0   
types.ts
Add client-specific type definitions                                         
+15/-0   
useLogger.ts
Update import paths for reorganization                                     
+3/-3     
useVscodeState.ts
Update imports for type reorganization                                     
+3/-10   
host.ts
Update host exports and type imports                                         
+3/-5     
BaseWebviewViewProvider.ts
Update imports for reorganized types                                         
+3/-5     
ILogger.ts
Remove file, moved to types                                                           
+0/-19   
WebviewApiProvider.ts
Update import path for types                                                         
+1/-1     
logger.ts
Update import path for ILogger                                                     
+1/-1     
utils.ts
Extract utility function for host                                               
+21/-0   
index.ts
Remove main index export file                                                       
+0/-15   
index.ts
Create types module index                                                               
+4/-0     
log.ts
Move logging types to dedicated module                                     
+42/-0   
reducer.ts
Reorganize reducer types and utilities                                     
+8/-41   
rpc.ts
Move RPC types to dedicated module                                             
+0/-11   
WebviewContext.tsx
Update import paths for types                                                       
+2/-1     
WebviewProvider.tsx
Update import paths for reorganization                                     
+1/-1     
Configuration changes
3 files
vite.config.ts
Remove main index entry point                                                       
+0/-1     
settings.local.json
Add Claude configuration file                                                       
+9/-0     
package.json
Update exports for separate entry points                                 
+6/-8     


Important

Separate host and client exports by restructuring package.json, updating vite.config.ts, and modifying TypeScript imports/exports.

  • Behavior:
    • Separate host and client exports in package.json by defining distinct paths for host and client in exports and typesVersions.
    • Update vite.config.ts to build separate host and client entries.
  • Code Structure:
    • Move ILogger and LogLevel from host/ILogger.ts to types/log.ts.
    • Move isLogMessage and LogMessage from host/WebviewLogger.ts to types/log.ts.
    • Move isMyActionMessage from types/ipcReducer.ts to host/utils.ts.
    • Move StateReducer and WebviewLayout from types/ipcReducer.ts to client/types.ts.
    • Move FnKeys and Patches from types/ipcReducer.ts to types/reducer.ts.
  • File Changes:
    • Delete src/lib/index.ts and src/lib/host/ILogger.ts.
    • Rename src/lib/types/ipcReducer.ts to src/lib/types/reducer.ts.
    • Rename src/lib/host/WebviewLogger.ts to src/lib/client/WebviewLogger.ts.
    • Create src/lib/host/utils.ts for utility functions.
    • Create src/lib/types/log.ts for logging types.
    • Create src/lib/types/index.ts to export types from log.ts and rpc.ts.

This description was created by Ellipsis for 8e75d3e. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • Breaking Changes

    • Removed the root package export and index entry; consumers should import from dedicated host and client entry points.
    • Adjusted published type paths; TypeScript resolution now uses typesVersions mapping.
  • New Features

    • Centralized, shared logging types and runtime validation utilities.
    • Added utility to safely identify and handle action messages.
  • Refactor

    • Reorganized public API surface and type locations for clearer separation between host and client.
    • Streamlined imports and type boundaries without changing runtime behavior.
  • Chores

    • Added a local settings file for AI tool permissions.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Sep 17, 2025

Reviewer's Guide

This PR reorganizes the library’s exports and type definitions to clearly separate host- and client-side modules, consolidates shared types into dedicated folders, and updates package.json and build configs accordingly.

Entity relationship diagram for shared types and their usage

erDiagram
    LOGGER ||--o| LOGMESSAGE : logs
    LOGMESSAGE }|..|| LOGLEVEL : has
    HOSTCALLS ||--o| LOGMESSAGE : can log
    CLIENTCALLS ||--o| LOGMESSAGE : can log
    STATE_REDUCER ||--o| PATCHES : uses
    ACTION ||--o| STATE_REDUCER : triggers
    WEBVIEWKEY ||--o| ACTION : identifies
    WEBVIEWKEY ||--o| PATCH : identifies
Loading

Class diagram for new logging types (ILogger, LogMessage, LogLevel)

classDiagram
    class ILogger {
        +debug(message: string, data?: Record<string, unknown>): void
        +info(message: string, data?: Record<string, unknown>): void
        +warn(message: string, data?: Record<string, unknown>): void
        +error(message: string, data?: Record<string, unknown>): void
        +dispose(): void
    }
    class LogMessage {
        +type: 'log'
        +level: LogLevel
        +message: string
        +data?: Record<string, unknown>
    }
    class LogLevel {
        <<enum>>
        DEBUG
        INFO
        WARN
        ERROR
    }
    LogMessage --> LogLevel
    WebviewLogger ..|> ILogger
    Logger ..|> ILogger
Loading

File-Level Changes

Change Details Files
Consolidate IPC messaging types and split helpers
  • Rename ipcReducer.ts to reducer.ts and centralize shared type definitions
  • Extract isFnKey into client/ipcReducer.ts and isMyActionMessage into host/utils.ts
  • Unify ActionDelegate, Patch, and Patches in reducer.ts
src/lib/types/reducer.ts
src/lib/client/ipcReducer.ts
src/lib/host/utils.ts
Centralize logging types and implementations
  • Create types/log.ts for ILogger, LogLevel, LogMessage, and isLogMessage
  • Update WebviewLogger and host logger to import from the new log.ts
src/lib/types/log.ts
src/lib/client/WebviewLogger.ts
src/lib/host/logger.ts
Revise package.json exports and build entries
  • Remove default main/module/types fields
  • Add typesVersions and explicit exports for ./host and ./client
  • Drop default index entry in vite.config.ts
package.json
vite.config.ts
Adjust entrypoint re-exports for host and client
  • Prune stale re-exports and adjust import paths to new modules
  • Expose host/client-specific types under ./host and ./client paths
src/lib/client.ts
src/lib/host.ts
Introduce central types index
  • Add types/index.ts to re-export rpc, log modules, and Brand
src/lib/types/index.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 17, 2025

Walkthrough

The PR restructures public exports and types, removes the index barrel and root export, introduces centralized logging/types modules, relocates/rehomes several type definitions, adds two type guards/utilities, and updates build entries and TypeScript type routing. Multiple imports are repointed to new modules without changing runtime behavior.

Changes

Cohort / File(s) Summary
Build & packaging
package.json, vite.config.ts, .claude/settings.local.json
Removed main/module/types and root export; added typesVersions for client/host typings; dropped lib entry for index in Vite (host/client only); added local Claude settings permitting Bash(npm run build:*).
Client modules & types
src/lib/client.ts, src/lib/client/types.ts, src/lib/client/WebviewContext.tsx, src/lib/client/WebviewLogger.ts, src/lib/client/WebviewProvider.tsx, src/lib/client/ipcReducer.ts, src/lib/client/useLogger.ts, src/lib/client/useVscodeState.ts
Reorganized public exports and type locations; added WebviewLayout, VsCodeApi, StateReducer types; moved/removed some exports (e.g., dropped isViewApiResponse/Event, WebviewContextValue export); added isFnKey utility; repointed imports to new local/type modules; centralized LogMessage typing.
Host modules
src/lib/host.ts, src/lib/host/BaseWebviewViewProvider.ts, src/lib/host/WebviewApiProvider.ts, src/lib/host/logger.ts, src/lib/host/utils.ts, src/lib/host/ILogger.ts
Centralized ILogger/LogLevel/LogMessage in types; updated exports to remove/relocate several types; added isMyActionMessage utility; removed host/ILogger definitions; adjusted import paths; no runtime control-flow changes.
Types restructuring
src/lib/types/index.ts, src/lib/types/log.ts, src/lib/types/reducer.ts, src/lib/types/rpc.ts
Added types barrel and logging module (ILogger, LogLevel, LogMessage, isLogMessage); moved Brand to types index; added ActionDelegate; removed StateReducer (moved), VsCodeApi/WebviewLayout (moved to client/types), Brand from rpc; removed isFnKey/isMyActionMessage from reducer.
Barrel removal
src/lib/index.ts
Removed all re-exports from the root index, eliminating the library’s aggregated public surface.

Sequence Diagram(s)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Review effort 3/5

Poem

I tidied the trails where types hop free,
Moved logs to a burrow marked “/types/log.ts” with glee.
The index burrow closed—two doors remain: host and client lane.
New guards sniff actions, keen and spry—
Thump, thump! The build runs by.
Carrot-shaped pointers, neatly aligned—
Ship it, and watch those patches bind. 🥕✨

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch separate-host-and-client-exports

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Separate host and client exports" is concise, a single clear sentence, and accurately summarizes the primary change in this PR (splitting the public export surface between host and client, removing the central index barrel and adjusting package/vite exports), matching the provided change summary and PR objectives. It is specific enough for a reviewer scanning history to understand the main intent.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented Sep 17, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • CI

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #5 at branch `separate-host-and-client-exports`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Summary of Changes

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

This pull request introduces a significant architectural refactoring to separate the library's host-side and client-side components into distinct, modular export entry points. This change aims to enhance code organization, clarify dependencies, and optimize bundle sizes by allowing consumers to import only the necessary parts. The refactoring also includes a comprehensive reorganization and centralization of various types and utility functions into more logical file structures, improving overall maintainability and clarity.

Highlights

  • Modular Exports: The library now provides distinct entry points for host and client components, allowing consumers to import specific parts rather than a single, combined bundle. This is reflected in the updated package.json exports.
  • Type Centralization: Common types, including logging interfaces (ILogger, LogLevel) and RPC-related definitions, have been consolidated into a new src/lib/types directory for improved organization and reusability across host and client modules.
  • Refactored IPC Reducer Logic: IPC reducer-related types and utility functions have been reorganized. Core reducer types are now in src/lib/types/reducer.ts, while specific helper functions like isFnKey and isMyActionMessage have been moved to dedicated client-side (src/lib/client/ipcReducer.ts) and host-side (src/lib/host/utils.ts) utility files respectively.
Using Gemini Code Assist

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

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

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

Customization

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

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

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

Footnotes

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

@qodo-code-review
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Lint, Type Check, and Test

Failed stage: Run ESLint [❌]

Failure summary:

The action failed during the lint step. ESLint reported 2 errors in
src/lib/client/WebviewProvider.tsx:
- Line 13: @typescript-eslint/no-import-type-side-effects — An
inline type import would leave a side-effect import at runtime. It needs to be converted to a
top-level type qualifier to fully remove the import at compile time.
- Line 14: import/order — The
../utils import should appear before the import of ./types.
These lint errors caused the npm run
lint command to exit with code 1, failing the workflow.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

137:  run `npm fund` for details
138:  found 0 vulnerabilities
139:  ##[group]Run npm run typecheck
140:  �[36;1mnpm run typecheck�[0m
141:  shell: /usr/bin/bash -e {0}
142:  ##[endgroup]
143:  > react-vscode-webview-ipc@0.1.0 typecheck
144:  > tsc --noEmit
145:  ##[group]Run npm run lint
146:  �[36;1mnpm run lint�[0m
147:  shell: /usr/bin/bash -e {0}
148:  ##[endgroup]
149:  > react-vscode-webview-ipc@0.1.0 lint
150:  > eslint .
151:  /home/runner/work/react-vscode-webview-ipc/react-vscode-webview-ipc/src/lib/client/WebviewProvider.tsx
152:  ##[error]  13:1  error  TypeScript will only remove the inline type specifiers which will leave behind a side effect import at runtime. Convert this to a top-level type qualifier to properly remove the entire import  @typescript-eslint/no-import-type-side-effects
153:  ##[error]  14:1  error  `../utils` import should occur before import of `./types`                                                                                                                                        import/order
154:  ✖ 2 problems (2 errors, 0 warnings)
155:  2 errors and 0 warnings potentially fixable with the `--fix` option.
156:  ##[error]Process completed with exit code 1.
157:  Post job cleanup.

@qodo-code-review
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Broken Exports

The client entry no longer re-exports validator functions like isViewApiResponse and isViewApiEvent. If downstream code relied on these from the client entry, this is a breaking change; verify intended surface and update documentation/migration notes accordingly.

export { isViewApiRequest, type CtxKey } from './types';
export type { ClientCalls, HostCalls, ViewApiResponse, ViewApiError } from './types';
export type { WebviewKey } from './types/reducer';
export type { StateReducer, WebviewLayout } from './client/types';
Public API Move

VsCodeApi, WebviewLayout, and StateReducer types were moved into client/types. Confirm they’re intentionally client-only and not needed by host consumers; otherwise consider re-exporting from root host/client entries to avoid fragmentation.

export type WebviewLayout = 'sidebar' | 'panel';
// VS Code webview API

export interface VsCodeApi {
  postMessage(message: unknown): Thenable<boolean>;
  getState(): unknown;
  setState(state: unknown): void;
}

export type StateReducer<S, A> = {
  [Key in FnKeys<A>]: (prevState: S, patch: Patches<A>[Key]) => S;
};
Type Resolution

With typesVersions and removal of root index types, ensure editors/consumers resolve host and client typings correctly across ESM/CJS and TS path resolution. Validate that deep imports (e.g., .../types) aren’t required by consumers.

"typesVersions": {
  "*": {
    "host": ["./dist/host.d.ts"],
    "client": ["./dist/client.d.ts"]
  }
},

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • You’ve removed the default “.” export and main/module entries in package.json—either add back a root export under exports or confirm that all consumers now import exclusively from “./host” and “./client”.
  • The commented-out WebviewContextValue export in src/lib/client.ts looks unintentional; either remove it entirely or restore it if it’s still needed by consumers.
  • In vite.config.ts the index entry was commented out—if you still need to build a combined bundle, re-add it or update any downstream scripts that expect an index build.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- You’ve removed the default “.” export and main/module entries in package.json—either add back a root export under exports or confirm that all consumers now import exclusively from “./host” and “./client”.
- The commented-out `WebviewContextValue` export in src/lib/client.ts looks unintentional; either remove it entirely or restore it if it’s still needed by consumers.
- In vite.config.ts the `index` entry was commented out—if you still need to build a combined bundle, re-add it or update any downstream scripts that expect an `index` build.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@qodo-code-review
Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

Copy link
Copy Markdown

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 8e75d3e in 2 minutes and 34 seconds. Click for details.
  • Reviewed 525 lines of code in 22 files
  • Skipped 0 files when reviewing.
  • Skipped posting 10 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. .claude/settings.local.json:9
  • Draft comment:
    Add a newline at end of file for consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While having a newline at end of file is a common convention, this is a very minor stylistic issue. It would likely be caught by linters or formatters if it was important to the project. The file is a new JSON configuration file and JSON doesn't strictly require trailing newlines. The comment is technically correct and follows a common best practice. Missing newlines can cause issues with some tools. However, this is an extremely minor stylistic issue that would be better handled by automated tooling rather than manual review comments. It doesn't impact functionality. Delete this comment as it's too minor of an issue to warrant a manual review comment. This kind of feedback should be handled by linters/formatters.
2. package.json:16
  • Draft comment:
    Verify that separate host/client exports are correctly consumed by downstream projects.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is asking the PR author to verify something, which is against the rules. It doesn't provide a specific suggestion or point out a specific issue in the code.
3. src/lib/client.ts:1
  • Draft comment:
    Remove or document the commented-out export: WebviewContextValue.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% Commented-out code can be a code smell and cleaning it up is generally good practice. However, this is a very minor issue. The comment doesn't indicate any functional problems, and there might be a temporary reason for keeping this commented code that we're not aware of without more context. The comment is technically correct as a code quality suggestion, but it's a very minor issue that doesn't affect functionality. We might be missing context about why the code was commented out temporarily. While it's a valid code quality suggestion, it's not important enough to warrant a PR comment. The author likely has a reason for commenting it out rather than deleting it. This comment should be removed as it's too minor of an issue and doesn't affect functionality. It violates the rule about not making purely informative comments.
4. src/lib/client/WebviewLogger.ts:1
  • Draft comment:
    Ensure the updated import paths reflect the new module organization.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is asking the author to ensure something, which violates the rule against asking the author to ensure behavior is intended or tested. It doesn't provide a specific suggestion or point out a specific issue.
5. src/lib/client/ipcReducer.ts:3
  • Draft comment:
    The isFnKey utility is well implemented; no changes needed.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None
6. src/lib/client/types.ts:30
  • Draft comment:
    DeferredPromise implementation is solid; ensure timeout management works during component unmount.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
7. src/lib/client/useVscodeState.ts:87
  • Draft comment:
    The action proxy robustly filters dangerous keys; consider adding tests for edge cases.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
8. src/lib/host/BaseWebviewViewProvider.ts:83
  • Draft comment:
    Using 'satisfies Patch' ensures type safety for patch messages; this is a good practice.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None
9. src/lib/types/reducer.ts:1
  • Draft comment:
    Renaming from ipcReducer to reducer improves clarity; ensure all import paths are updated accordingly.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 50% <= threshold 50% The comment suggests a code improvement by renaming a variable for clarity, which is a valid suggestion. However, it also includes a request to ensure all import paths are updated, which violates the rule against asking the author to ensure something. The first part of the comment is useful, but the second part is not allowed.
10. vite.config.ts:17
  • Draft comment:
    Host and client entry points are clearly separated; confirm that external dependencies (e.g. 'vscode') remain external.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the PR author to confirm something about external dependencies, which is against the rules. It doesn't provide a specific code suggestion or point out a specific issue with the code.

Workflow ID: wflow_Oz7tlkJ3poBtxNQM

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant and well-executed refactoring to separate host and client code into distinct entry points. The reorganization of types into structured modules and the relocation of shared utilities greatly improve the project's structure and maintainability. The changes are consistent and align with the stated goals. I have one suggestion to improve the readability and robustness of the new isMyActionMessage type guard.

Comment thread src/lib/host/utils.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
package.json (1)

22-33: Root import removed: treat as a breaking change and communicate it.

Consumers must switch from react-vscode-webview-ipc to subpaths react-vscode-webview-ipc/host and /client. Bump version accordingly and add a CHANGELOG/README note.

-  "version": "0.1.0",
+  "version": "0.2.0",

Optionally add a README “Migration” section and release notes describing the removal of the root entrypoint.

src/lib/host/WebviewApiProvider.ts (1)

32-85: Async pruning bug: failed views are never removed.

You push into failedViews inside async .then rejection handlers, but prune synchronously right after the loop—before rejections run. Move pruning into the rejection/catch path and delete immediately.

-    // Track views that fail to receive messages
-    const failedViews: string[] = [];
-
     // Send to all connected views
     for (const [viewId, connectedView] of this.connectedViews.entries()) {
       // eslint-disable-next-line sonarjs/no-try-promise
       try {
-        // Wrap postMessage in try-catch to handle synchronous exceptions
-        const postPromise = connectedView.view.webview.postMessage(event);
-
-        // Handle async failures
-        postPromise.then(
-          () => {
-            // Message sent successfully
-          },
-          (error: unknown) => {
-            this.logger.error(
-              `Failed to send event ${String(key)} to view ${connectedView.context.viewType}:${viewId}: ${getErrorMessage(error)}`
-            );
-
-            // Mark view for removal
-            failedViews.push(viewId);
-          }
-        );
+        // Wrap postMessage; prune on async failure
+        void connectedView.view.webview.postMessage(event).catch((error: unknown) => {
+          this.logger.error(
+            `Failed to send event ${String(key)} to view ${connectedView.context.viewType}:${viewId}: ${getErrorMessage(error)}`
+          );
+          this.logger.warn(
+            `Removing failed webview ${connectedView.context.viewType}:${viewId} from connectedViews`
+          );
+          this.connectedViews.delete(viewId);
+          this.logger.info(
+            `Removed 1 failed webview from connectedViews. Remaining: ${this.connectedViews.size}`
+          );
+        });
       } catch (error) {
         // Handle synchronous exceptions from postMessage
-        this.logger.error(
-          `Exception while sending event ${String(key)} to view ${connectedView.context.viewType}:${viewId}: ${String(error)}`
-        );
-
-        // Mark view for removal
-        failedViews.push(viewId);
+        this.logger.error(
+          `Exception while sending event ${String(key)} to view ${connectedView.context.viewType}:${viewId}: ${getErrorMessage(error)}`
+        );
+        this.logger.warn(
+          `Removing failed webview ${connectedView.context.viewType}:${viewId} from connectedViews`
+        );
+        this.connectedViews.delete(viewId);
       }
     }
-
-    // Prune failed views after iteration to avoid modifying collection during iteration
-    if (failedViews.length > 0) {
-      for (const viewId of failedViews) {
-        const connectedView = this.connectedViews.get(viewId as WebviewKey);
-        if (connectedView) {
-          this.logger.warn(
-            `Removing failed webview ${connectedView.context.viewType}:${viewId} from connectedViews`
-          );
-
-          // Only remove from connected views - let webviews handle their own disposal lifecycle
-          this.connectedViews.delete(viewId as WebviewKey);
-        }
-      }
-
-      this.logger.info(
-        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-        `Removed ${failedViews.length} failed webview(s) from connectedViews. Remaining: ${this.connectedViews.size}`
-      );
-    }
🧹 Nitpick comments (16)
.claude/settings.local.json (1)

3-5: Tighten allowed commands to least privilege.

Limit to the single build script you actually use instead of a wildcard.

-      "Bash(npm run build:*)"
+      "Bash(npm run build)"
package.json (2)

53-56: Peer deps look fine; confirm both subpaths truly require React.

If the host entry is framework-agnostic, consider scoping React peer to the client subpath only (docs), or marking as optional via peerDependencyMeta.


57-92: Depcheck warnings: prune or ignore test-only deps.

Either remove unused devDeps or teach depcheck to ignore them to keep CI green.

Option A — remove if genuinely unused:

-    "@testing-library/dom": "^10.4.1",
-    "@testing-library/jest-dom": "^6.8.0",
-    "@testing-library/react": "^16.3.0",
-    "@testing-library/user-event": "^14.6.1",
-    "@vscode/test-cli": "^0.0.11",
-    "@vscode/test-electron": "^2.5.2",
-    "eslint-import-resolver-typescript": "^4.4.4",
-    "eslint-plugin-storybook": "^9.1.6",

Option B — configure depcheck ignore list:

   "scripts": {
     "preview": "vite preview"
   },
+  "depcheck": {
+    "ignoreMatches": [
+      "@testing-library/*",
+      "@vscode/test-*",
+      "eslint-import-resolver-typescript",
+      "eslint-plugin-storybook"
+    ]
+  },
src/lib/client/ipcReducer.ts (1)

3-10: Type guard is solid; tighten the types to avoid casts.

Use PropertyKey and a dictionary bound so you don’t need as keyof T to index.

-export function isFnKey<T extends object>(
-  prop: string | symbol | number,
-  obj: T
-): prop is FnKeys<T> {
+export function isFnKey<T extends Record<PropertyKey, unknown>>(
+  prop: PropertyKey,
+  obj: T
+): prop is FnKeys<T> {
   return (
-    Object.prototype.hasOwnProperty.call(obj, prop) && typeof obj[prop as keyof T] === 'function'
+    Object.prototype.hasOwnProperty.call(obj, prop) &&
+    typeof (obj as Record<PropertyKey, unknown>)[prop] === 'function'
   );
 }
src/lib/types/log.ts (1)

28-41: Harden isLogMessage validation (range-check level, validate data when present).

Prevents false positives and noisy logs from malformed payloads.

 export function isLogMessage(value: unknown): value is LogMessage {
   if (value === null || typeof value !== 'object' || Array.isArray(value)) {
     return false;
   }
   if (!('type' in value) || value.type !== 'log') {
     return false;
   }
-  if (!('level' in value) || typeof value.level !== 'number') {
+  if (!('level' in value) || typeof value.level !== 'number') {
     return false;
   }
   if (!('message' in value) || typeof value.message !== 'string') {
     return false;
   }
-  return true;
+  // Narrow level to known enum range
+  const lvl = (value as { level: number }).level;
+  if (lvl < LogLevel.DEBUG || lvl > LogLevel.ERROR) {
+    return false;
+  }
+  // If data is present, it must be a non-null object
+  if ('data' in value && (value as { data?: unknown }).data !== undefined) {
+    const d = (value as { data?: unknown }).data;
+    if (d === null || typeof d !== 'object' || Array.isArray(d)) {
+      return false;
+    }
+  }
+  return true;
 }
src/lib/host/WebviewApiProvider.ts (1)

67-78: Avoid brand erasure and casts.

If you keep a failed list, type it as WebviewKey[] to preserve branding and remove as WebviewKey casts. Superseded by the immediate-delete refactor above.

src/lib/client/WebviewProvider.tsx (1)

32-36: Drop unused generic H to simplify JSX usage and inference.

H isn’t inferred from props and only annotates a call; keeping it can make JSX generics brittle.

-export const WebviewProvider = <T extends ClientCalls, H extends HostCalls>({
+export const WebviewProvider = <T extends ClientCalls>({
   children,
   viewType,
   contextKey,
 }: WebviewProviderProps<T>) => {
@@
-      } else if (isViewApiEvent<H>(message)) {
+      } else if (isViewApiEvent(message)) {

Also applies to: 139-151

src/lib/client/WebviewLogger.ts (1)

1-2: Split mixed import to satisfy lint rule and avoid side‑effect imports.

-import { LogLevel, type ILogger, type LogMessage } from '../types';
+import { LogLevel } from '../types';
+import type { ILogger, LogMessage } from '../types';
 import type { VsCodeApi } from './types';
src/lib/client/useVscodeState.ts (2)

8-21: Strengthen patch guard: require string keys

Align with host guard and structured-clone limitations by asserting key is a string.

   'key' in message &&
   'patch' in message &&
+  typeof (message as Record<string, unknown>).key === 'string' &&
   message.type === PATCH &&

80-90: Block symbol actions in the actor

Prevent creating actions with symbol keys; they won’t serialize and will be rejected by the updated guards.

-    get(_, prop) {
-      if (typeof prop !== 'string' && typeof prop !== 'symbol') {
+    get(_, prop) {
+      if (typeof prop !== 'string') {
         throw new TypeError(`Invalid action type: ${String(prop)}`);
       }
-      if (typeof prop === 'string' && dangerousKeys.has(prop)) {
+      if (dangerousKeys.has(prop)) {
         throw new Error(`Dangerous action key is blocked: ${prop}`);
       }
src/lib/client/types.ts (2)

51-55: Fix VsCodeApi.postMessage return type for webview side

In the webview context, postMessage returns void. Using Thenable can confuse consumers.

 export interface VsCodeApi {
-  postMessage(message: unknown): Thenable<boolean>;
+  postMessage(message: unknown): void;
   getState(): unknown;
   setState(state: unknown): void;
 }

If you intended to unify with vscode.Webview.postMessage (host side) which is Thenable, clarify the duality in docs or split the types.


57-59: Constrain StateReducer to object-shaped action maps

Prevents misuse and improves inference.

-export type StateReducer<S, A> = {
+export type StateReducer<S, A extends object> = {
   [Key in FnKeys<A>]: (prevState: S, patch: Patches<A>[Key]) => S;
 };
src/lib/host/BaseWebviewViewProvider.ts (1)

49-74: Harden action handling with try/catch

A throwing delegate can propagate and destabilize the host. Catch, log, and consider posting an error response or skipping the patch.

-    const messageListener = webviewView.webview.onDidReceiveMessage(async (message) => {
+    const messageListener = webviewView.webview.onDidReceiveMessage(async (message) => {
       if (isLogMessage(message)) {
         this.handleLogMessage(message);
         return;
       }
       if (isMyActionMessage<A>(message, this.providerId)) {
         this.logger.debug('Received action message from webview', { message });
-
-        const delegateFn = this.webviewActionDelegate[message.key];
-        if (typeof delegateFn !== 'function') {
-          throw new TypeError(`Unknown action key: ${String(message.key)}`);
-        }
-
-        const patch = await delegateFn(...message.params);
-
-        this._view?.webview.postMessage({
-          type: PATCH,
-          providerId: this.providerId,
-          key: message.key,
-          patch,
-        });
+        try {
+          const delegateFn = this.webviewActionDelegate[message.key];
+          if (typeof delegateFn !== 'function') {
+            throw new TypeError(`Unknown action key: ${String(message.key)}`);
+          }
+          const patch = await delegateFn(...message.params);
+          this._view?.webview.postMessage({
+            type: PATCH,
+            providerId: this.providerId,
+            key: message.key,
+            patch,
+          });
+        } catch (err) {
+          this.logger.error('Action handler failed', { err, key: String(message.key) });
+        }
         return;
       }
src/lib/types/reducer.ts (1)

7-11: Limit FnKeys to string keys and bound T to object

Prevents symbol keys from flowing into Action/Patch and aligns with messaging constraints.

-export type FnKeys<T> = {
+export type FnKeys<T extends object> = Extract<{
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
-}[keyof T];
+}[keyof T], string>;
src/lib/host.ts (1)

4-6: Breaking surface change: confirm SemVer and release notes

Removing prior exports (e.g., HostCalls/WebviewLayout) and slimming types is a breaking API for consumers. Bump major and document migration in the changelog.

src/lib/client.ts (1)

7-7: HostCalls in the client barrel may blur host/client boundaries.

If the goal is separation, consider keeping HostCalls in a host/shared entrypoint instead of client.

-export type { ClientCalls, HostCalls, ViewApiResponse, ViewApiError } from './types';
+export type { ClientCalls, ViewApiResponse, ViewApiError } from './types';

Outside this file (example):

// src/lib/host.ts
export type { HostCalls } from './types';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b06c44c and 8e75d3e.

📒 Files selected for processing (22)
  • .claude/settings.local.json (1 hunks)
  • package.json (1 hunks)
  • src/lib/client.ts (1 hunks)
  • src/lib/client/WebviewContext.tsx (1 hunks)
  • src/lib/client/WebviewLogger.ts (1 hunks)
  • src/lib/client/WebviewProvider.tsx (1 hunks)
  • src/lib/client/ipcReducer.ts (1 hunks)
  • src/lib/client/types.ts (2 hunks)
  • src/lib/client/useLogger.ts (1 hunks)
  • src/lib/client/useVscodeState.ts (1 hunks)
  • src/lib/host.ts (1 hunks)
  • src/lib/host/BaseWebviewViewProvider.ts (1 hunks)
  • src/lib/host/ILogger.ts (0 hunks)
  • src/lib/host/WebviewApiProvider.ts (1 hunks)
  • src/lib/host/logger.ts (1 hunks)
  • src/lib/host/utils.ts (1 hunks)
  • src/lib/index.ts (0 hunks)
  • src/lib/types/index.ts (1 hunks)
  • src/lib/types/log.ts (1 hunks)
  • src/lib/types/reducer.ts (2 hunks)
  • src/lib/types/rpc.ts (0 hunks)
  • vite.config.ts (0 hunks)
💤 Files with no reviewable changes (4)
  • vite.config.ts
  • src/lib/index.ts
  • src/lib/host/ILogger.ts
  • src/lib/types/rpc.ts
🧰 Additional context used
🧬 Code graph analysis (5)
src/lib/host/utils.ts (1)
src/lib/types/reducer.ts (3)
  • WebviewKey (3-3)
  • Action (17-22)
  • ACT (5-5)
src/lib/client/ipcReducer.ts (1)
src/lib/types/reducer.ts (1)
  • FnKeys (7-10)
src/lib/client/types.ts (2)
src/lib/client.ts (2)
  • WebviewLayout (9-9)
  • StateReducer (9-9)
src/lib/types/reducer.ts (2)
  • FnKeys (7-10)
  • Patches (36-43)
src/lib/types/log.ts (1)
src/lib/host.ts (1)
  • ILogger (5-5)
src/lib/types/reducer.ts (1)
src/lib/host.ts (1)
  • ActionDelegate (6-6)
🪛 GitHub Check: Lint, Type Check, and Test
src/lib/client/WebviewProvider.tsx

[failure] 13-13:
TypeScript will only remove the inline type specifiers which will leave behind a side effect import at runtime. Convert this to a top-level type qualifier to properly remove the entire import

🪛 GitHub Actions: CI
src/lib/client/WebviewProvider.tsx

[error] 13-13: ESLint: @typescript-eslint/no-import-type-side-effects - TypeScript will only remove the inline type specifiers which will leave behind a side effect import at runtime. Convert this to a top-level type qualifier to properly remove the entire import.

src/lib/host/BaseWebviewViewProvider.ts

[error] 1-1: Missing dependencies: 'vscode' required by ./src/lib/host/BaseWebviewViewProvider.ts.

package.json

[warning] 1-1: Depcheck reported unused devDependencies: @testing-library/dom, @testing-library/jest-dom, @testing-library/react, @testing-library/user-event, @vscode/test-cli, @vscode/test-electron, eslint-import-resolver-typescript, eslint-plugin-storybook.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (8)
package.json (1)

16-21: typesVersions mapping looks correct — build produced no d.ts; manual verification required.

Build output in the sandbox showed "Missing d.ts outputs" and "sh: 1: tsc: not found", so the import-resolution smoke test couldn't run. Confirm locally that npm run build emits dist/host.d.ts and dist/client.d.ts, then run the smoke test with TypeScript available (e.g., create the temp tsconfig and run npx tsc -p <tmp> --noEmit) to verify imports from react-vscode-webview-ipc/host and /client resolve.

.claude/settings.local.json (1)

1-9: Confirmed: .claude/settings.local.json will not be published.
package.json has "files": ["dist"] and npm pack --dry-run listed only README.md and package.json (total files: 2); .claude/settings.local.json is present in the repo but was not included in the packed tarball.

src/lib/types/index.ts (1)

1-4: Types barrel looks good.

Re-exports and Brand alias are clear and cohesive.

src/lib/client/useLogger.ts (1)

3-5: LGTM.

Type-only imports and module boundaries are aligned with the new layout.

src/lib/client/WebviewContext.tsx (1)

1-2: Import split LGTM

Moving VsCodeApi to client-local types while keeping Host/Client calls from ../types is consistent with the new surface split. No behavioral change.

src/lib/types/reducer.ts (1)

24-29: ActionDelegate move looks good

Centralizing ActionDelegate here simplifies host/client import paths. No issues spotted.

src/lib/client.ts (2)

6-6: Restore or deprecate exports: isViewApiResponse / isViewApiEvent

Removing these re-exports is a breaking change for consumers — either re-export them from src/lib/client.ts for one release (mark with @deprecated) or document the migration.
Defined: src/lib/types/rpc.ts (isViewApiResponse at ~line 87, isViewApiEvent at ~line 121). Used: src/lib/client/WebviewProvider.tsx (imports at top; runtime checks at ~lines 121 and 139).
Suggested one-line fix (non-breaking): add to src/lib/client.ts:
export { isViewApiResponse, isViewApiEvent } from './types';


8-9: No runtime exports — export type is correct.

WebviewKey is a type alias (src/lib/types/reducer.ts); WebviewLayout and StateReducer are type aliases (src/lib/client/types.ts). No change required.

Comment thread src/lib/client.ts
Comment thread src/lib/client/WebviewProvider.tsx
@@ -1,17 +1,15 @@
import * as vscode from 'vscode';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

CI failing: add ‘vscode’ as a dependency and externalize for bundling

The build reports “Missing dependencies: 'vscode'…”. Add vscode as a devDependency (and optionally peerDependency) and ensure bundlers mark it external.

Minimal package.json patch:

 {
   "name": "react-vscode-webview-ipc",
   "version": "…",
+  "peerDependencies": {
+    "vscode": "*"
+  },
   "devDependencies": {
+    "vscode": "*"
   }
 }

Also configure your bundler (tsup/rollup/esbuild) to externalize "vscode" (e.g., tsup: { external: ['vscode'] }).


🏁 Script executed:

#!/bin/bash
# Verify presence and usage
rg -n "from 'vscode'" -g 'src/**'
jq '.peerDependencies, .devDependencies' package.json

Length of output: 1415


CI failing — add 'vscode' dev/peer dependency and externalize in bundler

Build reports "Missing dependencies: 'vscode'". Add "vscode" to package.json (devDependency and peerDependency) and mark it external in your bundler. Affected files importing vscode: src/lib/host/logger.ts, src/lib/host/WebviewApiProvider.ts, src/lib/host/BaseWebviewViewProvider.ts.

Minimal package.json patch:

 {
   "name": "react-vscode-webview-ipc",
   "version": "…",
+  "peerDependencies": {
+    "vscode": "*"
+  },
   "devDependencies": {
+    "vscode": "*"
   }
 }

Also configure your bundler (tsup/rollup/esbuild) to externalize "vscode" (e.g., tsup: { external: ['vscode'] }).

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Missing dependencies: 'vscode' required by ./src/lib/host/BaseWebviewViewProvider.ts.

Comment thread src/lib/host/logger.ts
Comment thread src/lib/host/utils.ts
@hbmartin hbmartin merged commit 3dbccba into main Sep 17, 2025
3 of 4 checks passed
@hbmartin hbmartin deleted the separate-host-and-client-exports branch September 17, 2025 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant