# src/node/ — Node.js Compatibility (TypeScript) ## OVERVIEW TypeScript and JavaScript layer implementing Node.js compatible built-in modules for Workers. It is split across multiple layers: 1. An **internal layer** consisting of: - Non-user-importable TypeScript and JavaScript files in `internal/` that implement core logic, utilities, and C++ JSG module declarations. - C++ JSG modules (`src/workerd/api/node/`) expose native ops via `node-internal:*` specifiers - Some native internal modules may be implemented in Rust 2. A **public layer** of TypeScript files at the top-level that are user-importable. It is common, but not required, for top-level `.ts` files to re-export from `internal/` via `node-internal:` specifiers. This allows for a clean separation between public API surface and internal implementation details. See `README.md` for 12 policy rules governing compat scope and philosophy. ## MODULE GATING `node:*` modules are gated behind the `nodejs_compat` compatibility flag. The `node:async_hooks` module can be enabled individually via the `nodejs_als` compatibility flag. Runtime compat flags checked via `Cloudflare.compatibilityFlags['flag_name']`: | Flag | Guards | | -------------------------------------- | --------------------------------------------------------------------- | | `enable_nodejs_http_server_modules` | http Server/createServer, https Server | | `nodejs_zlib` | zlib streaming classes (Deflate, Gzip, etc.) | | `enable_nodejs_process_v2` | Extended process/events functionality | | `remove_nodejs_compat_eol_v22/v23/v24` | EOL deprecation of specific API surfaces (crypto, util, tls, process) | Most modules require `nodejs_compat` + `nodejs_compat_v2` flags (enforced by C++ side, not visible here). ## ADDING NEW INTERNAL MODULES New `.ts` and `.js` files in `internal/` are **auto-discovered** by the build system via `glob(["internal/*.ts", "internal/*.js"])` in `BUILD.bazel`. No explicit registration is needed — just create the file and it becomes importable via its `node-internal:` specifier. For example, creating `internal/internal_fs_glob.ts` immediately makes `node-internal:internal_fs_glob` available as an import specifier. No changes to `BUILD.bazel` or any registration macro are required. **What does require explicit wiring:** - New top-level `node:*` public modules need a corresponding `.ts` file in `src/node/` and must be registered in the C++ module registry (`src/workerd/api/node/node.h`, the `NODEJS_MODULES` macro). - New C++ JSG native modules require a `.d.ts` declaration file in `internal/` to declare the shape of the native module. ## CONVENTIONS - **`internal/*.js`** (not `.ts`) = upstream Node.js ports (streams_readable.js, streams_writable.js, etc.); paired with `.d.ts` type declarations - **`internal/*.d.ts`** without matching `.ts` = declares C++ JSG module shape (crypto.d.ts → `node-internal:crypto`) - **`internal/*.d.ts`** with matching `.js` = types for ported JS (streams_readable.d.ts → streams_readable.js) - Top-level files are thin: import from `node-internal:*`, re-export with `export { ... }` / `export * from` - Feature-gated exports use `if (!flag) { throw ... }` or conditional class assignment patterns - Shared validators in `internal/validators.ts`; shared errors in `internal/internal_errors.ts` - `_` prefix files (e.g., `_http_agent.ts`, `_stream_readable.ts`) = Node.js legacy internal module aliases - Some Node.js compat APIs are non-functional stubs that are either non-ops or throw when called