Skip to content

Commit 32e9982

Browse files
committed
feat: US-186 - Add node-fetch project-matrix fixture
1 parent d09ebb8 commit 32e9982

6 files changed

Lines changed: 101 additions & 1 deletion

File tree

docs/nodejs-compatibility.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ The [project-matrix test suite](https://github.com/rivet-dev/secure-exec/tree/ma
7171
| [astro](https://npmjs.com/package/astro) | Web Framework | Island architecture, SSR, multi-framework |
7272
| [hono](https://npmjs.com/package/hono) | Web Framework | ESM imports, lightweight HTTP |
7373
| [axios](https://npmjs.com/package/axios) | HTTP Client | HTTP client requests via fetch adapter, JSON APIs |
74+
| [node-fetch](https://npmjs.com/package/node-fetch) | HTTP Client | Fetch polyfill using http module, stream piping |
7475
| [dotenv](https://npmjs.com/package/dotenv) | Configuration | Environment variable loading, fs reads |
7576
| [semver](https://npmjs.com/package/semver) | Utility | Version parsing and comparison |
7677
| [ssh2](https://npmjs.com/package/ssh2) | Networking | SSH client/server, crypto, streams, events |

packages/secure-exec-core/isolate-runtime/src/inject/require-setup.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,34 @@
10421042
return result;
10431043
}
10441044

1045+
// Fix stream prototype chain broken by esbuild's circular-dep resolution.
1046+
// stream-browserify → readable-stream → require('stream') creates a cycle;
1047+
// esbuild gives Readable a stale Stream ref, so Readable extends EventEmitter
1048+
// directly instead of Stream. Insert Stream.prototype into the chain so
1049+
// `passThrough instanceof Stream` works (node-fetch, undici, etc. depend on this).
1050+
if (name === 'stream') {
1051+
if (
1052+
typeof result === 'function' &&
1053+
result.prototype &&
1054+
typeof result.Readable === 'function'
1055+
) {
1056+
var readableProto = result.Readable.prototype;
1057+
var streamProto = result.prototype;
1058+
// Only patch if Stream.prototype is not already in the chain
1059+
if (
1060+
readableProto &&
1061+
streamProto &&
1062+
!(readableProto instanceof result)
1063+
) {
1064+
// Insert Stream.prototype between Readable.prototype and its current parent
1065+
var currentParent = Object.getPrototypeOf(readableProto);
1066+
Object.setPrototypeOf(streamProto, currentParent);
1067+
Object.setPrototypeOf(readableProto, streamProto);
1068+
}
1069+
}
1070+
return result;
1071+
}
1072+
10451073
if (name === 'path') {
10461074
if (result.win32 === null || result.win32 === undefined) {
10471075
result.win32 = result.posix || result;

packages/secure-exec-core/src/generated/isolate-runtime.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"entry": "src/index.js",
3+
"expectation": "pass"
4+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "project-matrix-node-fetch-pass",
3+
"private": true,
4+
"type": "commonjs",
5+
"dependencies": {
6+
"node-fetch": "2.7.0"
7+
}
8+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"use strict";
2+
3+
const http = require("http");
4+
const fetch = require("node-fetch");
5+
6+
const server = http.createServer((req, res) => {
7+
if (req.method === "GET" && req.url === "/hello") {
8+
res.writeHead(200, { "Content-Type": "application/json" });
9+
res.end(JSON.stringify({ message: "hello" }));
10+
} else if (req.method === "GET" && req.url === "/users/42") {
11+
res.writeHead(200, { "Content-Type": "application/json" });
12+
res.end(JSON.stringify({ id: "42", name: "test-user" }));
13+
} else if (req.method === "POST" && req.url === "/data") {
14+
let body = "";
15+
req.on("data", (chunk) => (body += chunk));
16+
req.on("end", () => {
17+
res.writeHead(200, { "Content-Type": "application/json" });
18+
res.end(JSON.stringify({ method: "POST", received: JSON.parse(body) }));
19+
});
20+
} else {
21+
res.writeHead(404);
22+
res.end();
23+
}
24+
});
25+
26+
async function main() {
27+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
28+
const port = server.address().port;
29+
const base = "http://127.0.0.1:" + port;
30+
31+
try {
32+
const results = [];
33+
34+
const r1 = await fetch(base + "/hello");
35+
const b1 = await r1.json();
36+
results.push({ route: "GET /hello", status: r1.status, body: b1 });
37+
38+
const r2 = await fetch(base + "/users/42");
39+
const b2 = await r2.json();
40+
results.push({ route: "GET /users/42", status: r2.status, body: b2 });
41+
42+
const r3 = await fetch(base + "/data", {
43+
method: "POST",
44+
headers: { "Content-Type": "application/json" },
45+
body: JSON.stringify({ key: "value" }),
46+
});
47+
const b3 = await r3.json();
48+
results.push({ route: "POST /data", status: r3.status, body: b3 });
49+
50+
console.log(JSON.stringify(results));
51+
} finally {
52+
await new Promise((resolve) => server.close(resolve));
53+
}
54+
}
55+
56+
main().catch((err) => {
57+
console.error(err.message);
58+
process.exit(1);
59+
});

0 commit comments

Comments
 (0)