Skip to content

fix(plugin-defi): add runtime patch for jito-ts/rpc-websockets compatibility (#466)#522

Open
Walle2131235 wants to merge 4 commits into
sendaifun:v2from
Walle2131235:fix-jito-runtime-patch
Open

fix(plugin-defi): add runtime patch for jito-ts/rpc-websockets compatibility (#466)#522
Walle2131235 wants to merge 4 commits into
sendaifun:v2from
Walle2131235:fix-jito-runtime-patch

Conversation

@Walle2131235
Copy link
Copy Markdown

Summary

This PR adds a runtime patch to fix the jito-ts/rpc-websockets compatibility issue for npm users.

Problem

Issue #466: When installing @solana-agent-kit/plugin-defi via npm, users encounter:

Error: Cannot find module 'rpc-websockets/dist/lib/client'

This is caused by jito-ts bundling @solana/web3.js@1.77.4 which expects rpc-websockets/dist/lib/client.js, but modern rpc-websockets uses .cjs extensions.

Solution

Add a runtime patch (runtime-patch.ts) that:

  1. Runs automatically when the plugin is imported
  2. Copies .cjs files to .js files in node_modules/rpc-websockets/dist/lib/
  3. Only runs in Node.js environments (not browser)
  4. Is idempotent and safe to run multiple times

Testing

  • Build succeeds: pnpm build:plugin-defi
  • Patch runs at import time before jito-ts is loaded

Fixes #466

walle added 4 commits March 3, 2026 11:46
…ibility

- Add runtime-patch.ts that copies .cjs files to .js at module load time
- Import patch at top of index.ts before any jito-ts transitively loaded code
- Fixes issue sendaifun#466: jito-ts bundles old @solana/web3.js@1.77.4 which expects
  rpc-websockets/dist/lib/client.js but modern rpc-websockets uses .cjs

The patch is applied automatically when the plugin is imported in Node.js
environments, making npm installations work without manual workarounds.
…ssues

Documents the rpc-websockets error and provides workaround for npm users.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to address npm-user runtime failures in @solana-agent-kit/plugin-defi caused by a transitive jito-ts → old @solana/web3.js expecting rpc-websockets/dist/lib/client(.js) by introducing an import-time compatibility patch.

Changes:

  • Add an import-time runtime patch that copies rpc-websockets *.cjs files to *.js counterparts to satisfy the old web3.js resolver.
  • Import the patch at the top of the plugin entrypoint and document the workaround in the plugin README.
  • Add a new markdown article file at repo root (appears unrelated to the PR’s stated purpose).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
polish-solana-ecosystem-article.md Adds a long-form article (does not appear related to the runtime patch fix).
packages/plugin-defi/src/runtime-patch.ts Implements the runtime filesystem patch to create client.js/server.js from client.cjs/server.cjs.
packages/plugin-defi/src/runtime-patch.test.ts Adds tests for the patch module import/guard behavior.
packages/plugin-defi/src/index.ts Imports the runtime patch before other plugin imports.
packages/plugin-defi/README.md Adds troubleshooting guidance and a manual workaround for the missing module error.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


it('should only run in Node.js environment', () => {
// In Node.js, process.versions.node should exist
const isNode = typeof process !== 'undefined' && process.versions?.node;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The isNode expression uses &&, so in Node it evaluates to the Node version string (e.g. "22.x"), not a boolean. As written, typeof isNode will be "string" and this test will fail. Convert it to a boolean (e.g., Boolean(...)) or assert the expected string/undefined shape instead.

Suggested change
const isNode = typeof process !== 'undefined' && process.versions?.node;
const isNode = Boolean(typeof process !== 'undefined' && process.versions?.node);

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +19
import { existsSync, copyFileSync } from 'fs';
import { join } from 'path';

// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This module claims to be safe in non-Node (browser) environments, but the top-level static imports from fs and path can still break browser/edge bundling or runtime before the process.versions?.node guard is evaluated. Consider loading Node built-ins only inside the Node-only branch (or isolating this file to Node-only entrypoints) so importing the plugin in browser builds doesn’t error.

Suggested change
import { existsSync, copyFileSync } from 'fs';
import { join } from 'path';
// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
// Note: Node built-ins (fs, path) are loaded lazily inside the Node-only block
// below to avoid breaking browser/edge bundling.
// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
// Lazy-load Node built-ins so importing this module is safe in browser environments.
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { existsSync, copyFileSync } = require('fs');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { join } = require('path');

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +51
import { join } from 'path';

// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
const possiblePaths = [
// npm install location
'rpc-websockets/dist/lib',
// Monorepo location
'../../node_modules/rpc-websockets/dist/lib',
// pnpm location
'../rpc-websockets/dist/lib',
];

for (const relativePath of possiblePaths) {
try {
const libPath = join(process.cwd(), 'node_modules', relativePath);

if (existsSync(libPath)) {
const files = [
{ src: 'client.cjs', dest: 'client.js' },
{ src: 'server.cjs', dest: 'server.js' },
];

for (const { src, dest } of files) {
const srcPath = join(libPath, src);
const destPath = join(libPath, dest);

if (existsSync(srcPath) && !existsSync(destPath)) {
copyFileSync(srcPath, destPath);
// Console log removed for production
}
}
break; // Found and patched, stop searching
}
} catch {
// Continue to next path
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The patch locates rpc-websockets via process.cwd() + hard-coded node_modules paths. This is brittle when the process is launched from a subdirectory, when using different package manager layouts, or when the plugin is consumed from a monorepo. A more reliable approach is to resolve the installed rpc-websockets location via Node’s resolver (e.g., resolving rpc-websockets/package.json from this package) and then derive dist/lib from that path.

Suggested change
import { join } from 'path';
// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
const possiblePaths = [
// npm install location
'rpc-websockets/dist/lib',
// Monorepo location
'../../node_modules/rpc-websockets/dist/lib',
// pnpm location
'../rpc-websockets/dist/lib',
];
for (const relativePath of possiblePaths) {
try {
const libPath = join(process.cwd(), 'node_modules', relativePath);
if (existsSync(libPath)) {
const files = [
{ src: 'client.cjs', dest: 'client.js' },
{ src: 'server.cjs', dest: 'server.js' },
];
for (const { src, dest } of files) {
const srcPath = join(libPath, src);
const destPath = join(libPath, dest);
if (existsSync(srcPath) && !existsSync(destPath)) {
copyFileSync(srcPath, destPath);
// Console log removed for production
}
}
break; // Found and patched, stop searching
}
} catch {
// Continue to next path
import { join, dirname } from 'path';
// Only apply patch in Node.js environment (not browser)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
// Resolve the installed rpc-websockets package via Node's module resolver
const pkgJsonPath =
typeof require !== 'undefined'
? require.resolve('rpc-websockets/package.json')
: undefined;
if (pkgJsonPath) {
const packageRoot = dirname(pkgJsonPath);
const libPath = join(packageRoot, 'dist', 'lib');
if (existsSync(libPath)) {
const files = [
{ src: 'client.cjs', dest: 'client.js' },
{ src: 'server.cjs', dest: 'server.js' },
];
for (const { src, dest } of files) {
const srcPath = join(libPath, src);
const destPath = join(libPath, dest);
if (existsSync(srcPath) && !existsSync(destPath)) {
copyFileSync(srcPath, destPath);
// Console log removed for production
}
}

Copilot uses AI. Check for mistakes.
@@ -1,3 +1,7 @@
// Apply runtime patch for jito-ts/rpc-websockets compatibility (issue #466)
// Must be imported before any code that transitively uses jito-ts
import "./runtime-patch.js";
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

./runtime-patch.js does not exist in src/ (only runtime-patch.ts exists). This can break TypeScript/editor resolution and any build that doesn’t apply TS-to-JS extension remapping. Prefer importing ./runtime-patch (or adjust TS module resolution settings consistently across the repo if you want .js specifiers in TS sources).

Suggested change
import "./runtime-patch.js";
import "./runtime-patch";

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
# The Rise of Poland's Solana Ecosystem: A Deep Dive into Innovation

*How Polish developers are building the future of DeFi, privacy, and derivatives on Solana*

## Introduction
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This new top-level article file appears unrelated to the PR’s stated purpose (runtime patch for plugin-defi’s rpc-websockets compatibility). If it was added accidentally, it should be removed from this PR; otherwise, it likely needs its own PR/title/description aligned with documentation/content changes.

Copilot uses AI. Check for mistakes.
Comment on lines +118 to +126
### npm Installation Issues (rpc-websockets)

If you encounter the following error when installing via npm:

```
Error: Cannot find module 'rpc-websockets/dist/lib/client'
```

This is caused by a transitive dependency (`jito-ts`) bundling an older version of `@solana/web3.js`. The plugin includes an automatic runtime patch that should resolve this issue.
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The error described here typically occurs at runtime when importing/executing the plugin, not during npm install. Consider rewording to avoid confusion (e.g., “when running your app after installing via npm”).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jito-ts dependency conflict in @solana-agent-kit/plugin-defi plugin

2 participants