1/**
2 * functions to load Node.js apis via `globalThis.process`. trivially bundlable
3 * for the browser. does not depend on `@types/node` and does not intend to
4 * define types for the entire api. instead, this file's types are used for
5 * other library modules like `lib/log.ts` to bind to the system.
6 *
7 * if you are using a competent bundler, you can define `globalThis.process` as
8 * a bundling constant (esbuild: `--define`) to enable tree shaking across the
9 * library to only include browser code paths.
10 *
11 * @module
12 */
13
14export const process: Process | undefined = (globalThis as { process?: Process }).process
15 && (globalThis as { process?: Process }).process?.versions?.node
16 ? (globalThis as { process?: Process }).process
17 : undefined;
18
19export const isServer: boolean = !!process;
20
21/** partial types for Node.js `globalThis.process` */
22interface Process {
23 getBuiltinModule<K extends keyof Builtins>(name: K): Builtins[K] | null;
24 binding?<K extends keyof Bindings>(name: K): Bindings[K] | null;
25 addListener(event: string, callback: () => void): this;
26 prependListener(event: string, callback: () => void): this;
27 removeListener(event: string, callback: () => void): this;
28 listenerCount(event: string): number;
29 kill(pid: number, signal: string): boolean;
30 cwd(): string;
31 env: Partial<Record<string, string>>;
32 pid: number;
33 ppid: number;
34 platform: string;
35 versions: { node?: string; bun?: string; deno?: string };
36 stdin: TtyInput;
37 stdout: Tty;
38 stderr: Tty;
39}
40
41/** the raw mode members are only present when the stream is a tty */
42interface TtyInput {
43 isRaw: boolean;
44 setRawMode(mode: boolean): this;
45 addListener(event: "data", callback: (chunk: Uint8Array | string) => void): this;
46 removeListener(event: "data", callback: (chunk: Uint8Array | string) => void): this;
47 listenerCount(event: string): number;
48 pause(): this;
49 resume(): this;
50}
51
52interface Tty {
53 isTTY: boolean;
54 /** only present when the stream is a tty */
55 columns: number | undefined;
56 /** only present when the stream is a tty */
57 rows: number | undefined;
58 addListener(event: string, callback: () => void): this;
59 removeListener(event: string, callback: () => void): this;
60 end(text?: string | Uint8Array): this;
61 write(text: string | Uint8Array): boolean;
62}
63
64/**
65 * Subset of Node.js builtin types needed to not depend on the problematic
66 * "@types/node" package.
67 */
68interface Builtins {
69 "child_process": {
70 spawnSync(command: string, args: string[], options: { stdio: "ignore" }): unknown;
71 };
72 "fs": {
73 existsSync(file: string): boolean;
74 readFileSync(file: string, encoding: "utf-8"): string;
75 readFileSync(file: string): Uint8Array;
76 writeFileSync(file: string, data: string, options?: { mode?: number }): void;
77 appendFileSync(file: string, data: string, options?: { mode?: number }): void;
78 writeSync(fd: number, data: string): number;
79 mkdirSync(dir: string, options: { recursive: true }): void;
80 };
81 "inspector": {
82 Session: new() => {
83 connect(): void;
84 post(
85 method: string,
86 params: object,
87 // the result is whatever the Chrome DevTools Protocol method returns
88 callback: (error: Error | null, result: any) => void,
89 ): void;
90 };
91 };
92 "path": {
93 isAbsolute(file: string): boolean;
94 dirname(file: string): string;
95 basename(file: string): string;
96 join(...parts: string[]): string;
97 relative(from: string, to: string): string;
98 resolve(...parts: string[]): string;
99 sep: string;
100 };
101 "tty": {
102 isatty(fd: number): boolean;
103 };
104 "url": {
105 pathToFileURL(file: string): URL;
106 };
107 "util": {
108 inspect(object: unknown, options?: { colors?: boolean }): string;
109 formatWithOptions(
110 options: { colors?: boolean },
111 ...args: unknown[]
112 ): string;
113 };
114 "stream": {
115 Readable: {
116 toWeb(nodeStream: unknown): ReadableStream;
117 fromWeb(webStream: ReadableStream): { pipe: (other: unknown) => void };
118 };
119 Writable: {
120 prototype: {
121 write: (value: string) => boolean;
122 };
123 };
124 };
125}
126
127/** subset of Node.js binding types */
128interface Bindings {
129 /**
130 * key value mapping of internal module id to its source code.
131 * ```
132 * { "fs": "// Copyright Joyent, Inc. and other Node contributors.\n...
133 * ```
134 */
135 "natives": Partial<Record<string, string>>;
136}
137
138/** return a built-in module */
139export function builtin<K extends keyof Builtins>(
140 name: K,
141): Builtins[K] | undefined {
142 return process?.getBuiltinModule(name) ?? undefined;
143}
144
145/** return a built-in semi-private binding */
146export function binding<K extends keyof Bindings>(
147 name: K,
148): Bindings[K] | undefined {
149 if (!process) return undefined;
150 try {
151 return process.binding?.(name) ?? undefined;
152 } catch {
153 return undefined;
154 }
155}
156
157/** copy of `keyof typeof import('node:os').constants.errno` */
158// deno-fmt-ignore-next
159export type ErrorCode =
160 | (string & {})
161 | "E2BIG"
162 | "EACCES"
163 | "EADDRINUSE"
164 | "EADDRNOTAVAIL"
165 | "EAFNOSUPPORT"
166 | "EAGAIN"
167 | "EALREADY"
168 | "EBADF"
169 | "EBADMSG"
170 | "EBUSY"
171 | "ECANCELED"
172 | "ECHILD"
173 | "ECONNABORTED"
174 | "ECONNREFUSED"
175 | "ECONNRESET"
176 | "EDEADLK"
177 | "EDESTADDRREQ"
178 | "EDOM"
179 | "EDQUOT"
180 | "EEXIST"
181 | "EFAULT"
182 | "EFBIG"
183 | "EHOSTUNREACH"
184 | "EIDRM"
185 | "EILSEQ"
186 | "EINPROGRESS"
187 | "EINTR"
188 | "EINVAL"
189 | "EIO"
190 | "EISCONN"
191 | "EISDIR"
192 | "ELOOP"
193 | "EMFILE"
194 | "EMLINK"
195 | "EMSGSIZE"
196 | "EMULTIHOP"
197 | "ENAMETOOLONG"
198 | "ENETDOWN"
199 | "ENETRESET"
200 | "ENETUNREACH"
201 | "ENFILE"
202 | "ENOBUFS"
203 | "ENODATA"
204 | "ENODEV"
205 | "ENOENT"
206 | "ENOEXEC"
207 | "ENOLCK"
208 | "ENOLINK"
209 | "ENOMEM"
210 | "ENOMSG"
211 | "ENOPROTOOPT"
212 | "ENOSPC"
213 | "ENOSR"
214 | "ENOSTR"
215 | "ENOSYS"
216 | "ENOTCONN"
217 | "ENOTDIR"
218 | "ENOTEMPTY"
219 | "ENOTSOCK"
220 | "ENOTSUP"
221 | "ENOTTY"
222 | "ENXIO"
223 | "EOPNOTSUPP"
224 | "EOVERFLOW"
225 | "EPERM"
226 | "EPIPE"
227 | "EPROTO"
228 | "EPROTONOSUPPORT"
229 | "EPROTOTYPE"
230 | "ERANGE"
231 | "EROFS"
232 | "ESPIPE"
233 | "ESRCH"
234 | "ESTALE"
235 | "ETIME"
236 | "ETIMEDOUT"
237 | "ETXTBSY"
238 | "EWOULDBLOCK"
239 | "EXDEV"
240 | "WSAEINTR"
241 | "WSAEBADF"
242 | "WSAEACCES"
243 | "WSAEFAULT"
244 | "WSAEINVAL"
245 | "WSAEMFILE"
246 | "WSAEWOULDBLOCK"
247 | "WSAEINPROGRESS"
248 | "WSAEALREADY"
249 | "WSAENOTSOCK"
250 | "WSAEDESTADDRREQ"
251 | "WSAEMSGSIZE"
252 | "WSAEPROTOTYPE"
253 | "WSAENOPROTOOPT"
254 | "WSAEPROTONOSUPPORT"
255 | "WSAESOCKTNOSUPPORT"
256 | "WSAEOPNOTSUPP"
257 | "WSAEPFNOSUPPORT"
258 | "WSAEAFNOSUPPORT"
259 | "WSAEADDRINUSE"
260 | "WSAEADDRNOTAVAIL"
261 | "WSAENETDOWN"
262 | "WSAENETUNREACH"
263 | "WSAENETRESET"
264 | "WSAECONNABORTED"
265 | "WSAECONNRESET"
266 | "WSAENOBUFS"
267 | "WSAEISCONN"
268 | "WSAENOTCONN"
269 | "WSAESHUTDOWN"
270 | "WSAETOOMANYREFS"
271 | "WSAETIMEDOUT"
272 | "WSAECONNREFUSED"
273 | "WSAELOOP"
274 | "WSAENAMETOOLONG"
275 | "WSAEHOSTDOWN"
276 | "WSAEHOSTUNREACH"
277 | "WSAENOTEMPTY"
278 | "WSAEPROCLIM"
279 | "WSAEUSERS"
280 | "WSAEDQUOT"
281 | "WSAESTALE"
282 | "WSAEREMOTE"
283 | "WSASYSNOTREADY"
284 | "WSAVERNOTSUPPORTED"
285 | "WSANOTINITIALISED"
286 | "WSAEDISCON"
287 | "WSAENOMORE"
288 | "WSAECANCELLED"
289 | "WSAEINVALIDPROCTABLE"
290 | "WSAEINVALIDPROVIDER"
291 | "WSAEPROVIDERFAILEDINIT"
292 | "WSASYSCALLFAILURE"
293 | "WSASERVICE_NOT_FOUND"
294 | "WSATYPE_NOT_FOUND"
295 | "WSA_E_NO_MORE"
296 | "WSA_E_CANCELLED"
297 | "WSAEREFUSED";