Skip to content

Commit e9b0c66

Browse files
Use workerd node:process v2 when available (#10577)
* Use workerd `node:process` v2 when available * update worker-log tests to account for nodejs compat process v2 flag --------- Co-authored-by: Peter Bacon Darwin <[email protected]>
1 parent 88132bc commit e9b0c66

File tree

7 files changed

+205
-122
lines changed

7 files changed

+205
-122
lines changed

.changeset/red-wasps-kick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/unenv-preset": patch
3+
---
4+
5+
Use workerd `node:process` v2 when available

fixtures/worker-logs/tests/index.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,23 @@ describe("'wrangler dev' correctly displays logs", () => {
306306
expect(output).toMatchInlineSnapshot(`[]`);
307307
});
308308
});
309+
310+
describe("nodejs compat process v2", () => {
311+
test("default behavior", async ({ expect }) => {
312+
const output = await getWranglerDevOutput("module", [
313+
"--compatibility-flags=enable_nodejs_process_v2",
314+
"--compatibility-flags=nodejs_compat",
315+
]);
316+
expect(output).toMatchInlineSnapshot(`
317+
[
318+
"<<<<< console.info() message >>>>>",
319+
"<<<<< console.log() message >>>>>",
320+
"X [ERROR] <<<<< console.error() message >>>>>",
321+
"stderr: <<<<< stderr.write() message >>>>>",
322+
"stdout: <<<<< stdout.write() message >>>>>",
323+
"▲ [WARNING] <<<<< console.warning() message >>>>>",
324+
]
325+
`);
326+
});
327+
});
309328
});

packages/unenv-preset/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
},
5050
"peerDependencies": {
5151
"unenv": "2.0.0-rc.21",
52-
"workerd": "^1.20250828.1"
52+
"workerd": "^1.20250912.0"
5353
},
5454
"peerDependenciesMeta": {
5555
"workerd": {

packages/unenv-preset/src/runtime/node/process.ts

Lines changed: 111 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,121 +14,171 @@ const globalProcess: NodeJS.Process = (globalThis as any)["pro" + "cess"];
1414
export const getBuiltinModule: NodeJS.Process["getBuiltinModule"] =
1515
globalProcess.getBuiltinModule;
1616

17-
export const { exit, platform, nextTick } = getBuiltinModule(
18-
"node:process"
19-
) as NodeJS.Process;
17+
const workerdProcess = getBuiltinModule("node:process");
18+
19+
// Workerd has 2 different implementation for `node:process`
20+
//
21+
// See:
22+
// - [workerd `process` v1](https://github.com/cloudflare/workerd/blob/main/src/node/internal/legacy_process.ts)
23+
// - [workerd `process` v2](https://github.com/cloudflare/workerd/blob/main/src/node/internal/public_process.ts)
24+
// - [`enable_nodejs_process_v2` flag](https://github.com/cloudflare/workerd/blob/main/src/workerd/io/compatibility-date.capnp)
25+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26+
const isWorkerdProcessV2 = (globalThis as any).Cloudflare.compatibilityFlags
27+
.enable_nodejs_process_v2;
2028

2129
const unenvProcess = new UnenvProcess({
2230
env: globalProcess.env,
23-
hrtime: UnenvHrTime,
24-
nextTick,
31+
// `hrtime` is only available from workerd process v2
32+
hrtime: isWorkerdProcessV2 ? workerdProcess.hrtime : UnenvHrTime,
33+
// `nextTick` is available from workerd process v1
34+
nextTick: workerdProcess.nextTick,
2535
});
2636

37+
// APIs implemented by workerd module in both v1 and v2
38+
// Note that `env`, `hrtime` and `nextTick` are always retrieved from `unenv`
39+
export const { exit, features, platform } = workerdProcess;
40+
41+
// APIs that can be implemented by either `unenv` or `workerd`.
42+
// They are always retrieved from `unenv` which might use their `workerd` implementation.
43+
export const {
44+
// Always implemented by workerd
45+
env,
46+
// Only implemented in workerd v2
47+
hrtime,
48+
// Always implemented by workerd
49+
nextTick,
50+
} = unenvProcess;
51+
52+
// APIs that are not implemented by `workerd` (whether v1 or v2)
53+
// They are retrieved from `unenv`.
2754
export const {
55+
_channel,
56+
_disconnect,
57+
_events,
58+
_eventsCount,
59+
_handleQueue,
60+
_maxListeners,
61+
_pendingMessage,
62+
_send,
63+
assert,
64+
disconnect,
65+
mainModule,
66+
} = unenvProcess;
67+
68+
// API that are only implemented starting from v2 of workerd process
69+
// They are retrieved from unenv when process v1 is used
70+
export const {
71+
// @ts-expect-error `_debugEnd` is missing typings
72+
_debugEnd,
73+
// @ts-expect-error `_debugProcess` is missing typings
74+
_debugProcess,
75+
// @ts-expect-error `_exiting` is missing typings
76+
_exiting,
77+
// @ts-expect-error `_fatalException` is missing typings
78+
_fatalException,
79+
// @ts-expect-error `_getActiveHandles` is missing typings
80+
_getActiveHandles,
81+
// @ts-expect-error `_getActiveRequests` is missing typings
82+
_getActiveRequests,
83+
// @ts-expect-error `_kill` is missing typings
84+
_kill,
85+
// @ts-expect-error `_linkedBinding` is missing typings
86+
_linkedBinding,
87+
// @ts-expect-error `_preload_modules` is missing typings
88+
_preload_modules,
89+
// @ts-expect-error `_rawDebug` is missing typings
90+
_rawDebug,
91+
// @ts-expect-error `_startProfilerIdleNotifier` is missing typings
92+
_startProfilerIdleNotifier,
93+
// @ts-expect-error `_stopProfilerIdleNotifier` is missing typings
94+
_stopProfilerIdleNotifier,
95+
// @ts-expect-error `_tickCallback` is missing typings
96+
_tickCallback,
2897
abort,
2998
addListener,
3099
allowedNodeEnvironmentFlags,
31-
hasUncaughtExceptionCaptureCallback,
32-
setUncaughtExceptionCaptureCallback,
33-
loadEnvFile,
34-
sourceMapsEnabled,
35100
arch,
36101
argv,
37102
argv0,
103+
availableMemory,
104+
// @ts-expect-error `binding` is missing typings
105+
binding,
106+
channel,
38107
chdir,
39108
config,
40109
connected,
41110
constrainedMemory,
42-
availableMemory,
43111
cpuUsage,
44112
cwd,
45113
debugPort,
46114
dlopen,
47-
disconnect,
115+
// @ts-expect-error `domain` is missing typings
116+
domain,
48117
emit,
49118
emitWarning,
50-
env,
51119
eventNames,
52120
execArgv,
53121
execPath,
122+
exitCode,
54123
finalization,
55-
features,
56124
getActiveResourcesInfo,
125+
getegid,
126+
geteuid,
127+
getgid,
128+
getgroups,
57129
getMaxListeners,
58-
hrtime,
130+
getuid,
131+
hasUncaughtExceptionCaptureCallback,
132+
// @ts-expect-error `initgroups` is missing typings
133+
initgroups,
59134
kill,
60-
listeners,
61135
listenerCount,
136+
listeners,
137+
loadEnvFile,
62138
memoryUsage,
63-
on,
139+
// @ts-expect-error `moduleLoadList` is missing typings
140+
moduleLoadList,
64141
off,
142+
on,
65143
once,
144+
// @ts-expect-error `openStdin` is missing typings
145+
openStdin,
146+
permission,
66147
pid,
67148
ppid,
68149
prependListener,
69150
prependOnceListener,
70151
rawListeners,
152+
// @ts-expect-error `reallyExit` is missing typings
153+
reallyExit,
154+
ref,
71155
release,
72156
removeAllListeners,
73157
removeListener,
74158
report,
75159
resourceUsage,
160+
send,
161+
setegid,
162+
seteuid,
163+
setgid,
164+
setgroups,
76165
setMaxListeners,
77166
setSourceMapsEnabled,
167+
setuid,
168+
setUncaughtExceptionCaptureCallback,
169+
sourceMapsEnabled,
78170
stderr,
79171
stdin,
80172
stdout,
81-
title,
82173
throwDeprecation,
174+
title,
83175
traceDeprecation,
84176
umask,
177+
unref,
85178
uptime,
86179
version,
87180
versions,
88-
domain,
89-
initgroups,
90-
moduleLoadList,
91-
reallyExit,
92-
openStdin,
93-
assert,
94-
binding,
95-
send,
96-
exitCode,
97-
channel,
98-
getegid,
99-
geteuid,
100-
getgid,
101-
getgroups,
102-
getuid,
103-
setegid,
104-
seteuid,
105-
setgid,
106-
setgroups,
107-
setuid,
108-
permission,
109-
mainModule,
110-
_events,
111-
_eventsCount,
112-
_exiting,
113-
_maxListeners,
114-
_debugEnd,
115-
_debugProcess,
116-
_fatalException,
117-
_getActiveHandles,
118-
_getActiveRequests,
119-
_kill,
120-
_preload_modules,
121-
_rawDebug,
122-
_startProfilerIdleNotifier,
123-
_stopProfilerIdleNotifier,
124-
_tickCallback,
125-
_disconnect,
126-
_handleQueue,
127-
_pendingMessage,
128-
_channel,
129-
_send,
130-
_linkedBinding,
131-
} = unenvProcess;
181+
} = isWorkerdProcessV2 ? workerdProcess : unenvProcess;
132182

133183
const _process = {
134184
abort,

packages/wrangler/e2e/unenv-preset/preset.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,34 @@ const testConfigs: TestConfig[] = [
188188
},
189189
},
190190
],
191+
// node:process v2
192+
[
193+
{
194+
name: "process v1 by date",
195+
compatibilityDate: "2024-09-23",
196+
expectRuntimeFlags: {
197+
enable_nodejs_process_v2: false,
198+
},
199+
},
200+
// TODO: add a config when v2 is enabled by default (>= 2025-09-15)
201+
{
202+
name: "process v2 by flag",
203+
compatibilityDate: "2024-09-23",
204+
compatibilityFlags: ["enable_nodejs_process_v2"],
205+
expectRuntimeFlags: {
206+
enable_nodejs_process_v2: true,
207+
},
208+
},
209+
// TODO: change the date pass the default enabled date (>= 2025-09-15)
210+
{
211+
name: "process v1 by flag",
212+
compatibilityDate: "2024-09-23",
213+
compatibilityFlags: ["disable_nodejs_process_v2"],
214+
expectRuntimeFlags: {
215+
enable_nodejs_process_v2: false,
216+
},
217+
},
218+
],
191219
].flat() as TestConfig[];
192220

193221
describe.each(testConfigs)(

packages/wrangler/e2e/unenv-preset/worker/index.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,4 +505,43 @@ export const WorkerdTests: Record<string, () => void> = {
505505
assert.strictEqual(typeof http2.connect, "function");
506506
assert.strictEqual(http2.constants.HTTP2_HEADER_STATUS, ":status");
507507
},
508+
509+
async testProcess() {
510+
const mProcess = await import("node:process");
511+
const gProcess = globalThis.process;
512+
513+
const useV2 = getRuntimeFlagValue("enable_nodejs_process_v2");
514+
515+
for (const p of [mProcess, gProcess]) {
516+
if (useV2) {
517+
// workerd implementation only
518+
assert.equal(p.arch, "x64");
519+
assert.equal(p.title, "workerd");
520+
} else {
521+
// unenv implementation only
522+
assert.equal(p.arch, "");
523+
assert.equal(p.title, "");
524+
}
525+
526+
assert.doesNotThrow(() => p.chdir("/tmp"));
527+
assert.equal(typeof p.cwd(), "string");
528+
529+
assert.equal(typeof p.addListener, "function");
530+
assert.equal(typeof p.eventNames, "function");
531+
assert.equal(typeof p.getMaxListeners, "function");
532+
assert.equal(typeof p.listenerCount, "function");
533+
assert.equal(typeof p.listeners, "function");
534+
assert.equal(typeof p.off, "function");
535+
assert.equal(typeof p.on, "function");
536+
assert.equal(typeof p.once, "function");
537+
assert.equal(typeof p.prependListener, "function");
538+
assert.equal(typeof p.prependOnceListener, "function");
539+
assert.equal(typeof p.rawListeners, "function");
540+
assert.equal(typeof p.removeAllListeners, "function");
541+
assert.equal(typeof p.removeListener, "function");
542+
assert.equal(typeof p.setMaxListeners, "function");
543+
assert.equal(typeof (p as any).binding, "function");
544+
assert.equal(typeof p.permission, "object");
545+
}
546+
},
508547
};

0 commit comments

Comments
 (0)