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 =
15 (globalThis as { process?: Process }).process &&
16 (globalThis as { process?: Process }).process?.versions?.node
17 ? (globalThis as { process?: Process }).process
18 : undefined;
19
20export const isServer: boolean = !!process;
21
22/** partial types for Node.js `globalThis.process` */
23interface Process {
24 getBuiltinModule<K extends keyof Builtins>(name: K): Builtins[K] | null;
25 binding?<K extends keyof Bindings>(name: K): Bindings[K] | null;
26 addListener(event: string, callback: () => void): this;
27 prependListener(event: string, callback: () => void): this;
28 removeListener(event: string, callback: () => void): this;
29 listenerCount(event: string): number;
30 kill(pid: number, signal: string): boolean;
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 resolve(...parts: string[]): string;
98 sep: string;
99 };
100 tty: {
101 isatty(fd: number): boolean;
102 };
103 url: {
104 pathToFileURL(file: string): URL;
105 };
106 util: {
107 inspect(object: unknown, options?: { colors?: boolean }): string;
108 formatWithOptions(options: { colors?: boolean }, ...args: unknown[]): string;
109 };
110 stream: {
111 Readable: {
112 toWeb(nodeStream: unknown): ReadableStream;
113 fromWeb(webStream: ReadableStream): { pipe: (other: unknown) => void };
114 };
115 Writable: {
116 prototype: {
117 write: (value: string) => boolean;
118 };
119 };
120 };
121}
122
123/** subset of Node.js binding types */
124interface Bindings {
125 /**
126 * key value mapping of internal module id to its source code.
127 * ```
128 * { "fs": "// Copyright Joyent, Inc. and other Node contributors.\n...
129 * ```
130 */
131 natives: Partial<Record<string, string>>;
132}
133
134/** return a built-in module */
135export function builtin<K extends keyof Builtins>(name: K): Builtins[K] | undefined {
136 return process?.getBuiltinModule(name) ?? undefined;
137}
138
139/** return a built-in semi-private binding */
140export function binding<K extends keyof Bindings>(name: K): Bindings[K] | undefined {
141 if (!process) return undefined;
142 try {
143 return process.binding?.(name) ?? undefined;
144 } catch {
145 return undefined;
146 }
147}
148
149/** copy of `keyof typeof import('node:os').constants.errno` */
150// deno-fmt-ignore-next
151export type ErrorCode =
152 | (string & {})
153 | "E2BIG"
154 | "EACCES"
155 | "EADDRINUSE"
156 | "EADDRNOTAVAIL"
157 | "EAFNOSUPPORT"
158 | "EAGAIN"
159 | "EALREADY"
160 | "EBADF"
161 | "EBADMSG"
162 | "EBUSY"
163 | "ECANCELED"
164 | "ECHILD"
165 | "ECONNABORTED"
166 | "ECONNREFUSED"
167 | "ECONNRESET"
168 | "EDEADLK"
169 | "EDESTADDRREQ"
170 | "EDOM"
171 | "EDQUOT"
172 | "EEXIST"
173 | "EFAULT"
174 | "EFBIG"
175 | "EHOSTUNREACH"
176 | "EIDRM"
177 | "EILSEQ"
178 | "EINPROGRESS"
179 | "EINTR"
180 | "EINVAL"
181 | "EIO"
182 | "EISCONN"
183 | "EISDIR"
184 | "ELOOP"
185 | "EMFILE"
186 | "EMLINK"
187 | "EMSGSIZE"
188 | "EMULTIHOP"
189 | "ENAMETOOLONG"
190 | "ENETDOWN"
191 | "ENETRESET"
192 | "ENETUNREACH"
193 | "ENFILE"
194 | "ENOBUFS"
195 | "ENODATA"
196 | "ENODEV"
197 | "ENOENT"
198 | "ENOEXEC"
199 | "ENOLCK"
200 | "ENOLINK"
201 | "ENOMEM"
202 | "ENOMSG"
203 | "ENOPROTOOPT"
204 | "ENOSPC"
205 | "ENOSR"
206 | "ENOSTR"
207 | "ENOSYS"
208 | "ENOTCONN"
209 | "ENOTDIR"
210 | "ENOTEMPTY"
211 | "ENOTSOCK"
212 | "ENOTSUP"
213 | "ENOTTY"
214 | "ENXIO"
215 | "EOPNOTSUPP"
216 | "EOVERFLOW"
217 | "EPERM"
218 | "EPIPE"
219 | "EPROTO"
220 | "EPROTONOSUPPORT"
221 | "EPROTOTYPE"
222 | "ERANGE"
223 | "EROFS"
224 | "ESPIPE"
225 | "ESRCH"
226 | "ESTALE"
227 | "ETIME"
228 | "ETIMEDOUT"
229 | "ETXTBSY"
230 | "EWOULDBLOCK"
231 | "EXDEV"
232 | "WSAEINTR"
233 | "WSAEBADF"
234 | "WSAEACCES"
235 | "WSAEFAULT"
236 | "WSAEINVAL"
237 | "WSAEMFILE"
238 | "WSAEWOULDBLOCK"
239 | "WSAEINPROGRESS"
240 | "WSAEALREADY"
241 | "WSAENOTSOCK"
242 | "WSAEDESTADDRREQ"
243 | "WSAEMSGSIZE"
244 | "WSAEPROTOTYPE"
245 | "WSAENOPROTOOPT"
246 | "WSAEPROTONOSUPPORT"
247 | "WSAESOCKTNOSUPPORT"
248 | "WSAEOPNOTSUPP"
249 | "WSAEPFNOSUPPORT"
250 | "WSAEAFNOSUPPORT"
251 | "WSAEADDRINUSE"
252 | "WSAEADDRNOTAVAIL"
253 | "WSAENETDOWN"
254 | "WSAENETUNREACH"
255 | "WSAENETRESET"
256 | "WSAECONNABORTED"
257 | "WSAECONNRESET"
258 | "WSAENOBUFS"
259 | "WSAEISCONN"
260 | "WSAENOTCONN"
261 | "WSAESHUTDOWN"
262 | "WSAETOOMANYREFS"
263 | "WSAETIMEDOUT"
264 | "WSAECONNREFUSED"
265 | "WSAELOOP"
266 | "WSAENAMETOOLONG"
267 | "WSAEHOSTDOWN"
268 | "WSAEHOSTUNREACH"
269 | "WSAENOTEMPTY"
270 | "WSAEPROCLIM"
271 | "WSAEUSERS"
272 | "WSAEDQUOT"
273 | "WSAESTALE"
274 | "WSAEREMOTE"
275 | "WSASYSNOTREADY"
276 | "WSAVERNOTSUPPORTED"
277 | "WSANOTINITIALISED"
278 | "WSAEDISCON"
279 | "WSAENOMORE"
280 | "WSAECANCELLED"
281 | "WSAEINVALIDPROCTABLE"
282 | "WSAEINVALIDPROVIDER"
283 | "WSAEPROVIDERFAILEDINIT"
284 | "WSASYSCALLFAILURE"
285 | "WSASERVICE_NOT_FOUND"
286 | "WSATYPE_NOT_FOUND"
287 | "WSA_E_NO_MORE"
288 | "WSA_E_CANCELLED"
289 | "WSAEREFUSED";