Initial commit

This commit is contained in:
2026-08-30 15:23:57 +02:00
commit dab5679098
1293 changed files with 240952 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# server configuration
INTERNAL_DB_HOST=selfdev-mariadb
INTERNAL_DB_PORT=3306
INTERNAL_DASHBOARD_PORT=3001
# security configuration
INTERNAL_SECRET=53638124b089c718137c6f2a91fa2dfa157c0b440a4dcf695b810fc0dd8f2416
API_SECRET=19cc352caabec690e55575e339ae2011a7724af85b6cf59e4c4a08b166a6cb8a
MASTER_PEPPER=2d49d7e0367bdcbccc6ba063057654121bb41b4fccad8345cb046faab7a9db12
MASTER_SALT=51656
AUTH_SALT=25066
# SMPT information
SMTP_HOST=smtp.strato.de
SMTP_PORT=465
SMTP_USER=noreply@lupinexmedia.de
SMTP_PASS="Gk70mdF3UaRgWKvxbwP5"
+38
View File
@@ -0,0 +1,38 @@
/*
! /configuration/mapping.js
= Central Mapping Configuration
*/
import path from 'path';
import url from 'url';
import dotenv from 'dotenv';
// $ import .env
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.resolve(__dirname, '.env') });
// % declare directories
const rootDir = path.resolve(__dirname, '..');
const dirs = {
rootDir: rootDir,
serverDir: path.resolve(rootDir, 'servers'),
// § data structure directories
configDir: path.resolve(rootDir, 'configuration'),
dataDir: path.resolve(rootDir, 'database'),
// § other directories
serviceDir: path.resolve(rootDir, 'services'),
interfaceDir: path.resolve(rootDir, 'ui'),
resourcesDir: path.resolve(rootDir, 'resources')
};
// % set database config
const dbConfig = {
host: process.env.INTERNAL_DB_HOST,
port: process.env.INTERNAL_DB_PORT,
databases: {},
users: {}
};
export { dirs, dbConfig };
+576
View File
@@ -0,0 +1,576 @@
- [Service Manager](#service-manager)
- [Available methods](#available-methods)
- [Available services](#available-services)
- [Usage](#usage)
- [MySQL Service](#mysql-service)
- ["Service Manager" usage](#service-manager-usage)
- [Database \& Role Mapping (Overview)](#database--role-mapping-overview)
- [Using the pools](#using-the-pools)
- [WebSocket Service](#websocket-service)
- ["Service Manager" usage](#service-manager-usage-1)
- [Connection Manager](#connection-manager)
- [Using the WebSocket Connection Manager](#using-the-websocket-connection-manager)
- [Heartbeat system](#heartbeat-system)
- [WebSocket communication format](#websocket-communication-format)
- [Scheduler Service](#scheduler-service)
- ["Service Manager" usage](#service-manager-usage-2)
- [Scheduler Memory](#scheduler-memory)
- [Task structure](#task-structure)
- [Usage](#usage-1)
- [Logging Service](#logging-service)
- ["Service Manager" usage](#service-manager-usage-3)
- [Logger Service Internals](#logger-service-internals)
- [Writing logs](#writing-logs)
- [Read Logs](#read-logs)
- [Authentication](#authentication)
- [Available functions](#available-functions)
- [Usage](#usage-2)
---
## Service Manager
### Available methods
- start
- stop
- restart
- status
### Available services
- mysql
- websocket
- logger
- scheduler
### Usage
**Note:** All service functions are async, use `await` when calling them.
See the documentation of the services themselves below for further information on how to use the functions.
```js
const services = require('./services/manager.js'); // from the ROOT-Directory of the service
// start a service
const result = await services.start.mysql();
console.log(result);
// stop a service
const result = await services.stop.websocket();
console.log(result);
// restart a service
const result = await services.restart.logger();
console.log(result);
// get status of a service
const status = await services.status.scheduler();
console.log(status);
```
---
## MySQL Service
The MySQL service is started via the service manager and provides connection pools for each database, method (read/write/delete/backup) and role (everyone/auth/system).
### "Service Manager" usage
```js
const services = require('./services/manager.js'); // from the ROOT-Directory of the service
// Start the mysql service and get the pools
const mysqlData = await services.start.mysql();
console.log(mysqlData.online); // true if started successfully
const pools = require('./services/database/pools.js').get(); // from the ROOT-Directory of the service
// Stop the mysql service
const stopResult = await services.stop.mysql(pools);
console.log(stopResult.online); // false if all pools were closed
// Restart the mysql service
const restartResult = await services.restart.mysql(pools);
console.log(restartResult.online);
// Check status
const status = await services.status.mysql(pools);
console.log(status);
/* Example output:
{
dbName1: { r: { e: { online: true }, a: { online: true }, s: { online: true } }, ... },
dbName2: { ... }
}
*/
```
### Database & Role Mapping (Overview)
- **Databases**
- name
- name1
- **Methods**
- r = read
- w = write
- d = delete
- b = backup
- **Roles**
- e = everyone
- a = auth
- s = system
> For the full configuration, see `ROOT/service.backend/mapping.js`
### Using the pools
```js
const pools = require('./services/database/pools.js').get(); // from the ROOT-Directory of the service
// Example: read (r) from a table in db "name" with auth (a) role
const pool = pools.name.r.a;
const [rows] = await pool.query('SELECT * FROM users WHERE active = 1');
console.log(rows);
// Example: write (w) to a table in db "name1" with system (s) role
const writePool = pools.name1.w.s;
await writePool.query('INSERT INTO logs (event) VALUES (?)', ['Test']);
// Example: delete (d) rows from a table in db "name" with auth (a) role
const deletePool = pools.name.d.a;
await deletePool.query('DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY');
// Example: backup (b) method with system (s) role
const backupPool = pools.name1.b.s;
const [backupData] = await backupPool.query('SELECT * FROM important_table');
console.log(backupData);
```
---
## WebSocket Service
The WebSocket service is started via the service manager.
### "Service Manager" usage
```js
const services = require('./services/manager.js'); // from the ROOT-Directory of the service
// Start the websocket service
const wsData = await services.start.websocket();
console.log(wsData.online); // true if started successfully
// Stop the websocket service
const stopResult = await services.stop.websocket();
console.log(stopResult.online);// false if stopped successfully
// Restart the websocket service
const restartResult = await services.restart.websocket();
console.log(restartResult.online);
// Check status
const status = await services.status.websocket();
console.log(status);
/* Example output:
{
online: true,
clients: {
"connectionId1": {
metadata: { connectedAt: 1678321234567 },
lastHeartbeat: 1678321240000
}
}
}
*/
```
**Note**: Restarting the WebSocket service (`services.restart.websocket()`) reloads all actions, including any changes or new files in subfolders.
### Connection Manager
Active WebSocket connections are managed centrally via `services/websocket/connections.js`.
The connection manager:
- Stores all active connections
- Assigns a unique ID to each client
- Tracks heartbeat timestamps
- Automatically removes disconnected or stale clients
- Provides broadcast functionality
This works similarly to the MySQL pool manager:
Connections are stored once and can be accessed globally without restarting the service.
#### Using the WebSocket Connection Manager
```js
const connections = require('./services/websocket/connections.js') // from the ROOT-Directory of the service
// Suppose you have a new WebSocket client "ws"
const clientMetadata = { username: 'Username' };
// 1. Add the client to the manager
const connectionId = connections.add(ws, clientMetadata);
console.log('New connection ID:', connectionId);
// 2. Start the heartbeat mechanism to keep connections alive (every 30 seconds)
connections.startHeartbeat();
// 3. Broadcast a message to all clients
connections.broadcast({ action: 'notify', message: 'Server is running!' });
// 4. Get a specific connection's info
const clientData = connections.get(connectionId);
console.log(clientData);
// 5. Get all active connections
const allClients = connections.getAll();
console.log(allClients);
// 6. Remove a client (e.g., when they disconnect)
connections.remove(connectionId);
// 7. Stop the heartbeat when shutting down the server
connections.stopHeartbeat();
// 8. Clear all connections (e.g., when stopping the WebSocket service)
connections.clearAll();
```
### Heartbeat system
The service automatically sends periodic ping frames to all clients.
If a client does not respond within two intervals, the connection is removed automatically.
This ensures that stale or broken connections are cleaned up without manual intervention.
### WebSocket communication format
After a client connects to the WebSocket server, all communication must follow this message format:
```json
{
action: "",
data: { ... }
}
```
- action: string that matches a loaded action file (ROOT/websocket)
- data: payload passed to the action handler
Example:
```json
{
"action": "user.getUserData", // in this case, we have the action file at 'ROOT/websocket/user/getUserData.js'
"data": { "userId": 42 }
}
```
All the actions are stored in the "websocket" directory of this service. The system automatically loads all .js files inside this folder and all subfolders recursively. Action names are generated from the folder structure using dot-notation.
Example:
- websocket/user/getUserData.js → "user.getUserData"
- websocket/system/internal/restart.js → "system.internal.restart"
Each action file must export an async function:
```js
module.exports = async (data, ws, connectionId) => {
// data = payload from client
// ws = WebSocket instance
// connectionId = internal connection ID
}
// Detailed example:
module.exports = async (data, ws, connectionId) => {
try {
console.log(`TestAction triggered by connection ${connectionId}`);
console.log('Received data:', data);
// set response object
const response = {
message: 'TestAction executed successfully!',
receivedData: data,
timestamp: Date.now(),
connectionId
};
// send response back to the client
ws.send(JSON.stringify({
action: 'test.testaction.response', // optional: own response action
data: response
}));
return response;
} catch (err) {
console.error('Error in TestAction:', err);
ws.send(JSON.stringify({
action: 'test.testaction.error', // optional: own response action
data: { message: err.message }
}));
throw err;
}
};
```
On the client side it looks like this:
```js
// Connect to server
const ws = new WebSocket('ws://localhost:3001');
// Send a message
ws.send(JSON.stringify({
action: 'test.testaction',
data: { foo: 'bar', num: 42 }
}));
// Get a message
ws.onmessage = (msg) => {
const parsed = JSON.parse(msg.data);
console.log('Server response:', parsed);
};
```
---
## Scheduler Service
The Scheduler service is started via the service manager. It manages tasks that can be scheduled to run once at a specific time or repeatedly at defined intervals.
### "Service Manager" usage
```js
const services = require('./services/manager.js'); // from the ROOT-Directory of the service
// Start the scheduler service
const schedulerData = await services.start.scheduler();
console.log(schedulerData.online); // true if started successfully
const tasks = schedulerData.tasks;
// Stop the scheduler service
const stopResult = await services.stop.scheduler();
console.log(stopResult.online); // false if all tasks were stopped
// Restart the scheduler service
const restartResult = await services.restart.scheduler();
console.log(restartResult.online);
// Check status
const status = await services.status.scheduler();
console.log(status);
/* Example output:
{
"taskName1": { active: true, nextRun: 1678325400000, lastRun: 1678321800000, error: null },
"taskName2": { active: false, nextRun: null, lastRun: 1678320000000, error: 'Some error' }
}
*/
```
### Scheduler Memory
The scheduler memory holds all task states centrally. All other modules (start, stop, status) use it to manage tasks.
#### Task structure
```json
{
"taskName": {
"active": true, // whether the task is currently running
"oneTime": false, // true = runs only once, false = repeats
"intervalInSec": 3600, // interval in seconds for repeating tasks
"lastRun": null, // timestamp of last execution
"nextRun": null, // timestamp of next execution
"error": null, // last error if task failed
"action": "taskName" // name of the JS file in ROOT/tasks to execute
}
}
```
**Note**:
- The tasks object in memory contains all loaded tasks.
- The timer property exists internally in memory but is not stored in JSON.
- When a task is active, its timer starts automatically. One-time tasks (oneTime: true) execute once after intervalInSec, while recurring tasks (oneTime: false) repeat indefinitely at the specified interval.
- Each task executes the function exported by its action file in ROOT/tasks. Errors are saved in task.error
- The functions in the tasks folder should be `async` to ensure proper execution of asynchronous operations.
#### Usage
```js
const memory = require('./services/scheduler/memory.js'); // from the ROOT-Directory of the service
// Load all tasks from tasks.json
// Recurring tasks (oneTime: false) are started automatically
// One-time tasks with active: true also start automatically
memory.loadTasks();
// Get a single task
const task = memory.getTask('updater');
console.log(task);
// Get all tasks
const allTasks = memory.getAllTasks();
console.log(allTasks);
// Add or update a task
// If active: true, the timer starts immediately; if oneTime: true, task executes only once after interval
memory.setTask('cleanup', {
active: true,
oneTime: true,
intervalInSec: 10, // in this case, the interval will set delay
action: 'cleanup'
});
// Remove a task
memory.removeTask('cleanup');
```
**Note**:
- The memory object is shared globally. Any changes you make here immediately affect the scheduler service.
- Automatic execution, lastRun/nextRun tracking, and error logging are all handled internally.
---
## Logging Service
The Logging service is started via the service manager. It provides a central way to write log data into the logging database, using the system write pool. The service can be started, stopped, restarted, and its status can be checked. When offline, no log entries are written.
### "Service Manager" usage
```js
const services = require('./services/manager.js'); // from the ROOT-Directory of the service
// Start the logger service
const loggerData = await services.start.logger();
console.log(loggerData.online); // true if started successfully
const tasks = loggerData.tasks;
// Stop the logger service
const stopResult = await services.stop.logger();
console.log(stopResult.online); // false if stopped
// Restart the logger service
const restartResult = await services.restart.logger();
console.log(restartResult.online);
// Check status
const status = await services.status.logger();
console.log(status);
/* Example output:
{ "online": true }
*/
```
### Logger Service Internals
- The logger service maintains a single `online` flag to determine whether logging is active. This is checked before every write to ensure no logs are written while offline.
- When the service is stopped, the `online` flag is set to `false`, preventing any writes to the database.
- Log entries are written dynamically to tables in the `logging` database via the `write()` function.
- The MySQL write pool (`w.s`, `write` method → `system` role) is used internally to insert data.
#### Writing logs
Logs are written using the exported `write()` function from `ROOT/service.backend/services/logger/writeLogs.js`. This function dynamically maps an object of key/value pairs (`logData`) to columns in a specified table. The service must be online for writes to succeed.
```js
const logger = require('./services/logger/writeLogs.js'); // from the ROOT-Directory of the service
module.exports = async (data, ws, connectionId) => {
try {
// Decide internally which table to log to
const tableName = 'access_logs';
// Write log entry (log data can be specified via data.logData or any other object)
const result = await logger.write(data, tableName);
console.log('Log write result:', result);
// Send response back to WebSocket client as needed
} catch (err) {
console.error('Error in TestAction:', err);
throw err;
}
};
```
**Note**:
- If the logger service is offline (`online: false`), the `write()` function returns `{ success: false, error: 'Logger is inactive' }`.
- Columns in `logData` must match the table schema. Values are dynamically inserted into placeholders to prevent SQL injection. Keys not present in the table will be ignored or cause an error depending on DB configuration.
- Any log type (access, error, event, custom) can be handled with the same `write()` function. The table name determines the type.
#### Read Logs
```js
const loggerRead = require('./services/logger/readLogs.js'); // from the ROOT-Directory of the service
// 1. Get all logs from a table
const allLogs = await loggerRead.getAll('access_logs');
console.log(allLogs);
// 2. Get filtered logs from a table
const filteredLogs = await loggerRead.get('access_logs', { user: 'user', action: 'login' });
console.log(filteredLogs);
```
---
## Authentication
The authentication service handles user registration, login, account updates, and email-based account activation. It provides secure password hashing, session management via JWT, and role assignment.
### Available functions
- register
- login
- update
- activate
- check
### Usage
```js
const auth = require('./middleware/auth.js'); // from the ROOT-Directory of the service
// Register a new user
const newUser = await auth.register({
userEmail: 'user@example.com',
userName: 'myUsername',
userPass: 'strongPassword',
userPicture: '/path/to/picture.png', // optional
fromProject: 'myProject'
});
console.log(newUser);
/* Example output:
{
userId: 'uuid-v4',
authKey: 'generated-auth-key',
sessionKey: 'jwt-session-token',
userName: 'myUsername',
userEmail: 'user@example.com',
userPicture: 'uuid-v4.png',
fromProject: 'myProject',
assignedRoles: ['default']
}
*/
// Login
const loggedIn = await auth.login({
userLogin: 'user@example.com', // or username
userPass: 'strongPassword',
sessionKey: null // optional: existing session key for auto-login
});
console.log(loggedIn);
// Update user information
const updatedUser = await auth.update({
userId: loggedIn.userId,
authKey: loggedIn.authKey,
newUserName: 'newUsername', // optional
newUserEmail: 'newEmail@example.com', // optional
newUserPass: 'newPassword', // optional
newUserPicture: '/path/to/new.png', // optional
assignedRoles: ['default', 'activated'], // optional
additionalPermissions: ['admin'], // optional
state: 'active' // optional
});
console.log(updatedUser);
// Activate account (after receiving activation email)
const activation = await auth.activate({
activationToken: '<JWT-TOKEN-FROM-EMAIL>'
});
console.log(activation);
/* Example output:
{
message: 'Account successfully activated.',
assignedRoles: ['default', 'activated']
}
*/
```
**Note**:
- *Passwords* are hashed with bcrypt using a configurable number of salt rounds (`SALT_ROUNDS` in `.env`).
- *Sessions* are managed with JWTs and expire according to `JWT_EXPIRES`.
- *User pictures* are resized to `250x250px` PNGs automatically and stored in the user picture directory.
- *Roles* are stored as JSON arrays in the database and include a default role `'default'` for all new users.
- *Activation* requires the user to click the link sent via email. Once activated, the role `'activated'` is added automatically.
+65
View File
@@ -0,0 +1,65 @@
# Layer -1 ("Layer minus 1") Data Center ([*← Zurück*](../README.md))
Das **Data Center** (im Folgenden als "**L-1**" bezeichnet) ist das Herzstück des **"LNX-Connect"-Ökosystems** (im Folgenden "**LCE**"; "*E*" für "Ecosystem"). **L-1** ist das einzige Modul, das Zugriff auf die Server-interne Datenbank erhält und nur Anfragen vom **Gateway** (im Folgenden "**L0**") akzeptiert und verarbeitet.
---
<h2>Inhaltsverzeichnis</h2>
- [Layer -1 ("Layer minus 1") Data Center (*← Zurück*)](#layer--1-layer-minus-1--data-center--zurück)
- [Definition](#definition)
- [Aufgaben und Verantwortlichkeiten](#aufgaben-und-verantwortlichkeiten)
- [Kommunikation und Zugriffskontrolle](#kommunikation-und-zugriffskontrolle)
- [Datenverarbeitung](#datenverarbeitung)
- [Datenintegrität und Validierung](#datenintegrität-und-validierung)
- [Fehlerbehandlung](#fehlerbehandlung)
- [Module](#module)
---
## Definition
Neben der Datenbank ([*MySQL*](https://www.mysql.com/de/)) ist **L-1** das zentrale Herzstück des **LCE**. Durch eine implementierte Sicherheitsebene mittels [JWT](https://www.npmjs.com/package/jsonwebtoken) sowie weiterer sicherheitsrelevanter Mechanismen wird sichergestellt, dass ausschließlich **L0** Zugriff auf diesen Layer erhält.
## Aufgaben und Verantwortlichkeiten
Da **L-1** der einzige Layer mit Zugriff auf die interne Datenbank ist, besteht die Aufgabe dieses Layers darin, die in der Datenbank gespeicherten Datensätze zu verwalten sowie deren Verarbeitung zu steuern. Die folgenden Aufgabenbereiche gehören dazu:
- Authentifizierung der Anfrage bzw. des Anfragenstellers
- Verarbeitungsschritte nach Authentifizierung:
- Überprüfung der empfangenen Datensätze auf Fehler
- Abgleich der gelieferten Daten mit der Datenbank
- Senden bzw. Schreiben von angefragten oder gelieferten Daten
## Kommunikation und Zugriffskontrolle
**L-1** wird ausschließlich von **L0** über eine interne WebSocket-Verbindung angesprochen, welcher auf `127.0.0.1` lauscht. Dadurch wird sichergestellt, dass kein externer Zugriff direkt auf die Datenbank erfolgen kann. Sämtliche Anfragen müssen den gesicherten Weg über **L0** durchlaufen, wo diese vorab authentifiziert und validiert werden.
Die Kommunikation erfolgt nach einem Request-/Response-Prinzip, wobei **L-1** ausschließlich *reaktiv* arbeitet und keine eigenen Anfragen initiiert.
## Datenverarbeitung
Die Datenverarbeitung in **L-1** erfolgt strikt nach einem definierten Ablauf. Jede eingehende Anfrage wird zunächst auf Gültigkeit geprüft und anschließend entsprechend ihres Typs verarbeitet. Dabei wird zwischen Lese-, Schreib- und Löschoperationen (*CRUD*; Create, Read, Update, Delete) unterschieden.
Nach erfolgreicher Authentifizierung und Validierung werden die angeforderten Operationen auf der Datenbank ausgeführt. Anschließend wird eine entsprechende Antwort generiert und an **L0** zurückgesendet, welcher die Antwort dann an den jeweiligen Client weiterleitet. Dieser strukturierte Ablauf stellt sicher, dass all Datenoperationen kontrolliert und nachvollziehbar durchgeführt werden.
## Datenintegrität und Validierung
**L-1** stellt sicher, dass alle verarbeiteten Daten konsistent und fehlerfrei bleiben. Eingehende Datensätze werden vor der Verarbeitung auf Struktur, Vollständigkeit und mögliche Fehler überprüft.
Durch den Abgleich mit bestehenden Daten in der Datenbank wird verhindert, dass inkonsistente oder widersprüchliche Informationen gespeichert oder ausgegeben werden. Diese Validierungsmechanismen gewährleisten die Integrität der gesamten Datenbasis innerhalb des Systems.
## Fehlerbehandlung
Tritt während der Verarbeitung einer Anfrage ein Fehler auf, wird dieser von **L-1** erkannt und entsprechend behandelt. Ungültige oder fehlerhafte Anfragen werden nicht verarbeitet, sondern mit einer passenden Fehlermeldung beantwortet.
Dabei wird sichergestellt, dass keine inkonsistenten Zustände in der Datenbank entstehen. Fehler werden zudem intern erfasst, um eine spätere Analyse und Optimierung des Systems zu ermöglichen.
## Module
**L-1** ist in mehrere Module unterteilt, die jeweils spezifische Aufgaben innerhalb der Datenverarbeitung übernehmen:
- **Service-Manager-Modul** — Koordination der internen Module → [Mehr](./modules/service-manager.md)
- **WebSocket-Modul** — Interne sichere WebSocket-Kommunikation → [Mehr](./modules/websocket-service.md)
- **DB-Service-Modul** — Datenbank-Manager und Pools → [Mehr](./modules/database-service.md)
- **Auth-Modul** — Anfrageauthentifizierung → [Mehr](./modules/auth-service.md)
- **Validierungsmodul** — Datenstruktur- und Konsistenzprüfung → [Mehr](./modules/validate-service.md)
- **Datenverarbeitungsmodul** — Datenbank-Operations-Manager → [Mehr](./modules/operations-manager.md)
- **Loggingmodul** — Systemereignisse und Fehler protokollieren → [Mehr](./modules/logging-service.md)
+24
View File
@@ -0,0 +1,24 @@
/*
! /index.js
= Central Project Entry-Point
*/
// % import node & npm modules
import path from 'path';
import { createServer } from 'http';
// % import modules
import { dirs, dbConfig } from './configuration/mapping.js';
import { generate, generatePrefixed } from './services/idGenerator.js';
// % set neccessary directories and paths
const expressDir = path.resolve(dirs.serverDir, 'express');
const webSocketDir = path.resolve(dirs.serverDir, 'websocket');
async function startConnect() {
const express = await import(`${expressDir}/server.js`);
const websocket = await import(`${webSocketDir}/server.js`);
const server = createServer(express.default);
}
startConnect();
+55
View File
@@ -0,0 +1,55 @@
{
"name": "lnx-connect",
"version": "1.0.0",
"description": "",
"keywords": [
"connect",
"eco",
"system",
"system",
"lnx",
"control"
],
"homepage": "https://github.com/LupiNexMedia/lnx-connect#readme",
"bugs": {
"url": "https://github.com/LupiNexMedia/lnx-connect/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/LupiNexMedia/lnx-connect.git"
},
"license": "UNLICENSED",
"author": "LupiNex Media",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "nodemon index.js"
},
"dependencies": {
"bcrypt": "^6.0.0",
"body-parser": "^2.2.2",
"browser-sync": "^3.0.4",
"chalk": "^5.6.2",
"chokidar": "^5.0.0",
"clean-css": "^5.3.3",
"cookie-parser": "^1.4.7",
"cors": "^2.8.6",
"crypto": "^1.0.1",
"dotenv": "^17.4.0",
"ejs": "^5.0.1",
"express": "^5.2.1",
"express-minify": "^1.0.0",
"express-minify-html-2": "^2.0.0",
"fs": "^0.0.1-security",
"jsonwebtoken": "^9.0.3",
"mysql2": "^3.20.0",
"nodemailer": "^8.0.4",
"nodemon": "^3.1.14",
"os": "^0.1.2",
"path": "^0.12.7",
"sharp": "^0.34.5",
"uglify-js": "^3.19.3",
"uuid": "^13.0.0",
"ws": "^8.20.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -0,0 +1,21 @@
{
"name": "LupiNex Connect",
"short_name": "LupiNex Connect",
"icons": [
{
"src": "/img/favicons/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/img/favicons/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 KiB

+49
View File
@@ -0,0 +1,49 @@
/*
! /servers/Dashboard/functions.js Central Dashboard Server Functions
? This modules exports the functionality for the express dashboard server
*/
// % core variables
const ip = process.env.INTERNAL_HOST_IP;
const port = process.env.INTERNAL_DASHBOARD_PORT;
// $ server start function
async function boot(server) {
// ~ start express server
server.listen(port, ip, () => {
console.info(`Express server is now running on http://${ip}:${port}`)
});
// ~ return running express server reference
return server;
};
// $ server shutdown function
async function shutdown(server) {
// ~ shutdown running express server
server.close();
// ~ return express server reference
return server;
};
// $ server restart function
async function restart(server) {
// ~ shutdown the running express server
await shutdown(server);
// ~ restart the express server
await boot(server);
// ~ return the server reference
return server;
};
// $ server status function
async function status(server) {
// ~ get and return server listening status
return server.listening ? 'online' : 'offline';
};
// § export server functions module
module.exports = { boot, shutdown, restart, status };
@@ -0,0 +1,32 @@
/*
! /servers/dashboard/routers/index.js Central Gateway Routing Aggregator
? This module dynamically collects and registeres all modular ".router.js" files within the "routers" directory
*/
// % import nodejs packages
const router = require('express').Router();
const path = require('path');
const fs = require('fs');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
// % read router files recursively
const routerRoot = path.resolve(dirs.dashboardDir, 'routers');
const routerFiles = fs.readdirSync(routerRoot)
.filter(file => /\.router\.js$/.test(file))
.sort((a, b) => {
if (a === 'root.router.js') return 1;
if (b === 'root.router.js') return -1;
return a.localeCompare(b);
});
// $ configure used routers
routerFiles.forEach(file => {
const routerPath = path.resolve(routerRoot, file);
router.use(require(routerPath));
});
// § export router module
module.exports = router;
+35
View File
@@ -0,0 +1,35 @@
/*
! /servers/dashboard/server.js Central Dashboard Server Manager
? This module orchestrates the express lifecycle, handles initialization, middleware configs and router integrations.
*/
// % import nodejs packages
const express = require('express');
const http = require('http');
const path = require('path');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
// % set up express server
const app = express();
const server = http.createServer(app);
// $ server setup
app.use(express.json()); // add json support for express
app.use(express.static(path.resolve(dirs.interfaceDir, 'resources'))); // set resources directory (e.g. scripts, style sheets, images ...)
app.set('view engine', 'ejs'); // set "embedded javascript" as view engine
app.set('views', path.resolve(dirs.interfaceDir, 'views')); // set the folder where the .ejs files are located
// $ router setup
const router = require(path.resolve(dirs.dashboardDir, 'routers'));
app.use(router);
// $ server functions
const {
boot, shutdown, restart, status
} = require(path.resolve(dirs.dashboardDir, 'functions.js'));
// $ export server module
module.exports = { boot, shutdown, restart, status, server };
View File
+93
View File
@@ -0,0 +1,93 @@
/*
! /servers/gateway/functions.js Central Gateway Server Functions
? This modules exports the functionality for the express gateway server
*/
// % import nodejs dependencies
const WebSocket = require('ws');
// % core variables
const ip = process.env.INTERNAL_HOST_IP;
const port = process.env.INTERNAL_GATEWAY_PORT;
// % create a reference for the websocket client
let wsClient = null;
// % create an event emitter for the gateway
const EventEmitter = require('events');
const gatewayEvents = new EventEmitter();
// $ helper: send messages through websocket
function sendToWs(payload) {
if (wsClient && wsClient.readyState === WebSocket.OPEN) {
wsClient.send(JSON.stringify(payload));
} else {
console.error('WebSocket not connected.');
}
}
// $ server start function
async function boot(server) {
// ~ start express server
server.listen(port, ip, () => {
console.info(`Express server is now running on http://${ip}:${port}`);
});
// ~ connect to the websocket server
const wsUrl = `ws://${process.env.INTERNAL_HOST_IP}:${process.env.INTERNAL_WEBSOCKET_PORT}?key=${process.env.INTERNAL_SECRET}&codename=gateway`;
wsClient = new WebSocket(wsUrl);
wsClient.on('open', () => {
console.info('Connected to central websocket server');
});
wsClient.on('message', (data) => {
const msg = JSON.parse(data);
gatewayEvents.emit('ws_message', msg);
});
wsClient.on('error', (err) => {
console.error('WebSocket error:', err.message);
});
// ~ return running express server reference
return server;
};
// $ server shutdown function
async function shutdown(server) {
// ~ close websocket server
if (wsClient) {
wsClient.close();
wsClient = null;
}
// ~ shutdown running express server
return new Promise((resolve) => {
server.close(() => {
console.info('Express server shut down.');
resolve(server);
});
});
};
// $ server restart function
async function restart(server) {
// ~ shutdown the running express server
await shutdown(server);
// ~ restart the express server
await boot(server);
// ~ return the server reference
return server;
};
// $ server status function
async function status(server) {
// ~ get and return server listening status
return server.listening ? 'online' : 'offline';
};
// § export server functions module
module.exports = { boot, shutdown, restart, status, sendToWs, gatewayEvents };
+32
View File
@@ -0,0 +1,32 @@
/*
! /servers/gateway/routers/index.js Central Gateway Routing Aggregator
? This module dynamically collects and registeres all modular ".router.js" files within the "routers" directory
*/
// % import nodejs packages
const router = require('express').Router();
const path = require('path');
const fs = require('fs');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
// % read router files recursively
const routerRoot = path.resolve(dirs.gatewayDir, 'routers');
const routerFiles = fs.readdirSync(routerRoot)
.filter(file => /\.router\.js$/.test(file))
.sort((a, b) => {
if (a === 'root.router.js') return 1;
if (b === 'root.router.js') return -1;
return a.localeCompare(b);
});
// $ configure used routers
routerFiles.forEach(file => {
const routerPath = path.resolve(routerRoot, file);
router.use(require(routerPath));
});
// § export router module
module.exports = router;
@@ -0,0 +1,30 @@
/*
! /servers/gateway/routers/template.router.js
*/
// % import nodejs dependencies
const router = require('express').Router();
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
const { gatewayEvents, sendToWs } = path.resolve(dirs.gateway, 'functions.js');
// $ template router with websocket integration
router.post('/test', (req, res) => {
const correlationId = '';
sendToWs({
type: 'actionName',
correlationId,
data: req.body
});
// ~ wait for callback through central event bus
gatewayEvents.once('ws_message', (msg) => {
if (msg.correlationId === correlationId) res.json(msg.data);
});
});
// § export routers
module.exports = router;
+32
View File
@@ -0,0 +1,32 @@
/*
! /servers/gateway/server.js Central Gateway Server Manager
? This module orchestrates the express lifecycle, handles initialization, middleware configs and router integrations
*/
// % import nodejs packages
const express = require('express');
const http = require('http');
const path = require('path');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
// % set up express server
const app = express();
const server = http.createServer(app);
// $ server setup
app.use(express.json()); // add json support for express
// $ router setup
const router = require(path.resolve(dirs.gatewayDir, 'routers'));
app.use(router);
// $ server functions
const {
boot, shutdown, restart, status
} = require(path.resolve(dirs.gatewayDir, 'functions.js'));
// $ export server module
module.exports = { boot, shutdown, restart, status, server };
@@ -0,0 +1,8 @@
/*
! /servers/websocket/actions/template.js A simple websocket action template
? This module is just a template for the websocket actions
*/
module.exports = (connectionManager, data, id) => {
};
+185
View File
@@ -0,0 +1,185 @@
/*
! /servers/websocket/connections.js Central WebSocket Connection Manager
? This module handles all websocket connections as a pool and automatically sends heartbeats
*/
// % import nodejs dependencies
const path = require('path');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..');
const { generatePrefixed } = require(path.resolve(rootDir, 'services', 'idgen.js'));
// % create a new connection mapping
let connections = new Map();
let registry = new Map();
// % set heartbeat settings
let heartbeatInterval = null;
const heartbeatTime = 5000; // 5 seconds
// $ add a connection
function add(socket, codename = null) {
// % generate a unique id for websocket connections
const id = generatePrefixed('ws');
// ~ add connection to connection pool
const conn = { socket, codename, lastHeartbeat: Date.now() };
connections.set(id, conn);
// ~ add registry keys if codename is provided
if (codename) registry.set(codename, id);
// ~ listen for 'pong' events from client to update heartbeat timestamp
socket.on('pong', () => conn.lastHeartbeat = Date.now());
// ~ error handling
socket.on('error', (err) => remove(id));
// ~ close connection
socket.on('close', () => remove(id));
// ~ return client id
return id;
}
// $ remove a connection by id
function remove(id) {
// % find id in connection map
const conn = connections.get(id);
// ~ close and remove connection
if (conn) {
// ~ cleanup registry if codename exists
if (conn.codename) registry.delete(conn.codename);
// ~ close connection
if (conn.socket.readyState === conn.socket.OPEN) conn.socket.close();
// ~ remove connection from pool
connections.delete(id);
}
}
// $ remove all connections
function removeAll() {
// ~ loop through connection mapping
for (const id of connections.keys()) {
remove(id);
}
}
// $ get a connection by id
function get(id) {
return connections.get(id);
}
// $ get a connection by codename
function getByCodename(codename) {
// % get id from codename
const id = registry.get(codename);
// ~ return connection
return id ? connections.get(id) : null;
}
// $ get all connections
function getAll() {
// % create a reference for all connections
const all = {};
// ~ loop through connections and fill "all" reference
for (const [id, data] of connections.entries()) {
all[id] = {
codename: data.codename,
lastHeartbeat: data.lastHeartbeat
};
}
// ~ return the connection reference
return all;
}
// $ send a message to a specific connection by id
function send(id, message) {
// % get id
const conn = connections.get(id);
// ~ check if connection exists and is open
if (conn && conn.socket.readyState === conn.socket.OPEN) {
conn.socket.send(JSON.stringify(message));
return true;
}
// ~ return immediately after a failure
return false;
}
// $ send a message to a specific connection by codename
function sendTo(codename, message) {
// % get id
const id = registry.get(codename);
// ~ send a message if id exists
if (id) return send(id, message);
// ~ immediately return if id not exists
return false;
}
// $ broadcast to all connections
function broadcast(message) {
// % serialize payload
const payload = JSON.stringify(message);
// ~ loop through all established connections
for (const conn of connections.values()) {
if (conn.socket.readyState === conn.socket.OPEN) {
conn.socket.send(payload);
}
}
}
// $ start the heartbeat system
function startBeat() {
if (heartbeatInterval) return;
heartbeatInterval = setInterval(() => {
const now = Date.now();
for (const [id, conn] of connections.entries()) {
// ~ send ping to active connections
if (conn.socket.readyState === conn.socket.OPEN) {
conn.socket.ping();
}
// ~ remove stale connections (no pong in 2 heartbeat intervals)
if (now - conn.lastHeartbeat > heartbeatTime * 2) {
remove(id);
}
}
}, heartbeatTime);
}
// $ stop the heartbeat system
function stopBeat() {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
}
// § export the websocket connection module
module.exports = {
add,
remove,
removeAll,
get,
getByCodename,
getAll,
send,
sendTo,
broadcast,
startBeat,
stopBeat
};
+134
View File
@@ -0,0 +1,134 @@
/*
! /servers/websocket/server.js Central Websocket Server Manager
? This module orchestrates the websocket lifecycle, handles connections, heartbeats and actions
*/
// % import nodejs packages
const path = require('path');
const fs = require('fs');
const http = require('http');
const WebSocket = require('ws');
// % import external dependencies
const rootDir = path.resolve(__dirname, '..', '..');
const { dirs } = require(path.resolve(rootDir, 'configuration', 'mapping.js'));
const connectionManager = require(path.resolve(dirs.websocketDir, 'connections.js'));
// % declare action directories
const wsActionsDir = path.resolve(dirs.websocketDir, 'actions');
// % declare reference for actions
const actions = {};
// % declare references for the websocket server
const ip = process.env.INTERNAL_HOST_IP;
const port = process.env.INTERNAL_WEBSOCKET_PORT;
let server = null;
// $ load all websocket actions recursively
// ? The loader is loading all actions with a dot notation
// ?? Usage: actionName || folder.actionName || folder.folder2.actionName ....
async function loadActions(dir = wsActionsDir, prefix = '') {
// ~ reset the action container to ensure a clean state during reloads
for (const key in actions) delete actions[key];
// % read all content synchronously
const files = fs.readdirSync(dir);
// ~ loop through each file or folder found in the directory
for (const file of files) {
// % construct the absolute path to the current item
const fullPath = path.resolve(dir, file);
// ~ retrieve file system metadata
const stat = fs.statSync(fullPath);
// ~ check if item is a folder
if (stat.isDirectory()) {
// ~ recurse into subdirs and append the folder name to the prefix
loadActions(fullPath, prefix + file + '.');
} else if (file.endsWith('.js')) {
// ~ generate a unique action name by stripping the file extension and include the prefix
const actionName = prefix + file.replace('.js', '');
// ~ invalidate the require cache to allow loading the updated code
delete require.cache[require.resolve(fullPath)];
// ~ load the fresh module version into the actions registry
actions[actionName] = require(fullPath);
}
}
}
// $ function to start the websocket server
async function start() {
// ~ initially load all websocket actions
await loadActions();
try {
// ~ set up a new websocket server
const wss = new WebSocket.Server({
host: ip,
port: port
});
wss.on('connection', async (socket, req) => {
// ~ authorize connection
const url = new URL(req.url, `http://${req.headers.host}`);
const key = url.searchParams.get('key');
const codename = url.searchParams.get('codename');
if (key !== process.env.INTERNAL_SECRET) {
socket.close(1008, 'Unauthorized');
return;
}
// ~ get connection id and establish connection
const id = await connectionManager.add(socket, codename);
connectionManager.sendTo(codename, id);
// ~ register websocket messages
socket.on('message', async (data) => {
// ~ parse json data
const msg = JSON.parse(data);
// % get user id and validate
const valid = connectionManager.get(id);
if (valid.socket.readyState !== valid.socket.OPEN) {
connectionManager.remove(id);
return;
};
// % declare action type (e.g. folder.actionName)
const type = msg.type;
if (!type) {
connectionManager.send(id, 'Action value is empty. Please declare an action value.');
return;
};
// ~ set action handler
const actionHandler = actions[msg.type];
if (!actionHandler) {
connectionManager.send(id, `Action ${actionHandler} not found.`);
return;
}
// ~ send action to handler
try {
await actionHandler(connectionManager, msg.data, id);
} catch(e) {
console.error('Error while trying to execute the websocket action!', e);
return;
}
});
});
// ~ start the heartbeat system with the connection manager
connectionManager.startBeat();
} catch(e) {
console.error('Error while trying to start the websocket server', e);
connectionManager.stopBeat();
}
}
// § export websocket server and functions
module.exports = { start, loadActions };
@@ -0,0 +1,51 @@
// node dependencies
const nodemailer = require('nodemailer');
require('dotenv').config({ path: '../.env' });
async function sendStratoMail({ to, subject, html }) {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT),
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
await transporter.sendMail({
from: `"LupiNex Media" <${process.env.SMTP_USER}>`,
to,
subject,
html
});
}
module.exports = {
sendMail: async (userId, userName, userEmail) => {
// 1. Generate activation token (signed JWT)
const activationToken = jwt.sign({ user_id: userId }, JWT_SECRET, { expiresIn: '7d' });
// 2. Build activation URL
const activationLink = `${process.env.FRONTEND_URL}/activate?token=${activationToken}`;
// 3. Build email content
const subject = 'Please activate your account';
const html = `
<p>Hello ${userName},</p>
<p>Thank you for your registration. Please click the link below to activate your account:</p>
<p><a href="${activationLink}">Activate Account</a></p>
<p>The link is valid for 7 days.</p>
`;
// 4. Send email via Strato SMTP
await sendStratoMail({
to: userEmail,
subject,
html
});
return { message: 'Activation mail sent.' };
}
}
+384
View File
@@ -0,0 +1,384 @@
// node dependencies
const { v4: uuidv4 } = require('uuid');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: '../.env' });
// variables
const SALT_ROUNDS = parseInt(process.env.SALT_ROUNDS);
const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRES = '30d';
// file paths
const USER_PIC_DIR = '/mnt/openarchive/.lnx_connect/userPictures';
const TMP_UPLOAD_DIR = '/mnt/openarchive/.lnx_connect/tmp';
// dependencies
const pools = require('../services/database/pools');
const { sendMail } = require('./activator');
// helper
async function saveUserPicture(userId, tmpFilePath) {
if (!fs.existsSync(USER_PIC_DIR)) fs.mkdirSync(USER_PIC_DIR, { recursive: true });
const destPath = path.join(USER_PIC_DIR, `${userId}.png`);
if (tmpFilePath && fs.existsSync(tmpFilePath)) {
// resize uploaded image
await sharp(tmpFilePath)
.resize(250, 250)
.png({ compressionLevel: 9 })
.toFile(destPath);
// delete tmp file
fs.unlinkSync(tmpFilePath);
return `${userId}.png`
} else {
return `default.png`;
}
}
// export functions
module.exports = {
// register function
register: async (data) => {
// 0. Declare dbs to use
const read = pools.users.r.s;
const write = pools.users.w.s;
// 1. Get incoming variables
const {
userEmail: user_email,
userName: user_name,
userPass: user_pass,
userPicture: user_picture,
fromProject: from_project
} = data;
// 2. Check if email or username already exists
const existing = await read.query(`
SELECT user_email, user_name
FROM users
WHERE user_email = ? OR user_name = ?
`, [user_email, user_name]);
// If exists throw error
if (existing.length > 0) {
const existingUser = existing[0];
if (existingUser.user_email === user_email) throw new Error('User with that mail already exists.');
if (existingUser.user_name === user_name) throw new Error('User with that name already exists.');
}
// 3. Generate IDs and keys
const user_id = uuidv4();
const authKeyPlain = uuidv4();
const auth_key = await bcrypt.hash(authKeyPlain, SALT_ROUNDS);
const pass_hash = await bcrypt.hash(user_pass, SALT_ROUNDS);
const session_key = jwt.sign({ user_id }, JWT_SECRET, { expiresIn: JWT_EXPIRES });
// 4. Handle user picture
const tmpFileName = path.basename(user_picture || '');
const tmpFilePath = path.join(TMP_UPLOAD_DIR, tmpFileName);
const user_picture_file = await saveUserPicture(user_id, tmpFilePath);
// 5. Set default role
const default_role = ['default'];
// 5. Write data to db
await write.query(`
INSERT INTO users (
user_id,
auth_key,
session_key,
user_name,
user_email,
user_pass,
user_picture,
from_project,
assigned_roles,
state,
last_active
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
`, [
user_id,
auth_key,
session_key,
user_name,
user_email,
pass_hash,
user_picture_file,
from_project,
JSON.stringify(default_role),
'active'
]);
// send activation mail
sendMail(user_id, user_name, user_email);
// Objectize result
const result = {
userId: user_id,
authKey: authKeyPlain,
sessionKey: session_key,
userName: user_name,
userEmail: user_email,
userPicture: user_picture_file,
fromProject: from_project,
assignedRoles: default_role
}
// Return result
return result;
},
// login function
login: async (data) => {
// Dummy hash used to normalize login timing
const DUMMY_HASH = '$2b$10$nC7jBLRF/HwpZLkt79xCreF5Npsh5NeVFnGzOrRF7sVxlI9yJk99i';
// 0. Declare dbs to use
const read = pools.users.r.s;
const write = pools.users.w.s;
// 1. Get incoming variables
const {
userLogin: user_login,
userPass: user_pass,
sessionKey: session_key
} = data;
const login = (user_login || '').trim(); // trim login name/mail
// 2. Check if user exists
const existing = await read.query(`
SELECT
user_id,
auth_key,
session_key,
user_name,
user_email,
user_pass,
user_picture,
from_project,
assigned_roles,
additional_permissions,
state
FROM users
WHERE user_email = ? OR user_name = ?
`, [login, login]);
let user = existing[0];
// Run fake bcrypt compare to match timing
if (!user) {
await bcrypt.compare(user_pass || '', DUMMY_HASH);
throw new Error('Invalid login credentials.');
}
// 3. Check user state
if (user.state === 'banned' || user.state === 'pending') throw new Error(`User ${user.user_name} is ${user.state}.`);
// 4. Login validation
let loginValid = false;
let autoLogin = false;
// 4.1. Session login (auto login)
if (session_key) {
try {
const decoded = jwt.verify(session_key, JWT_SECRET);
if (decoded.user_id === user.user_id && session_key === user.session_key) {
loginValid = true;
autoLogin = true;
};
} catch (err) {
loginValid = false;
autoLogin = false;
}
}
// 4.2. Password login
let new_session_key;
if (!loginValid) {
// match passwords
const passMatch = await bcrypt.compare(user_pass || '', user.user_pass);
if (!passMatch) throw new Error('Invalid login credentials.');
loginValid = true;
// generate new session key
new_session_key = jwt.sign({ user_id: user.user_id }, JWT_SECRET, { expiresIn: JWT_EXPIRES });
}
const newAuthKeyPlain = uuidv4();
const newAuthKeyHash = await bcrypt.hash(newAuthKeyPlain, SALT_ROUNDS);
// 6. Update session after login
if (loginValid && !autoLogin) {
await write.query(`
UPDATE users
SET auth_key = ?, session_key = ?, last_active = NOW()
WHERE user_id = ?
`, [newAuthKeyHash, new_session_key, user.user_id]);
}
if (loginValid && autoLogin) {
await write.query(`
UPDATE users
SET auth_key = ?, last_active = NOW()
WHERE user_id = ?
`, [newAuthKeyHash, user.user_id]);
}
// Objectize result
const result = {
userId: user.user_id,
authKey: autoLogin ? user.auth_key : newAuthKeyPlain,
sessionKey: autoLogin ? user.session_key : new_session_key,
userName: user.user_name,
userEmail: user.user_email,
userPicture: user.user_picture,
fromProject: user.from_project,
assignedRoles: user.assigned_roles ? JSON.parse(user.assigned_roles) : [],
additionalPermissions: user.additional_permissions ? JSON.parse(user.additional_permissions) : []
}
// Return result
return result;
},
// update function
update: async (data) => {
// 0. Declare dbs to use
const read = pools.users.r.s;
const write = pools.users.w.s;
// 1. Get incoming variables
const {
userId: user_id,
authKey: auth_key,
newUserName: user_name,
newUserEmail: user_email,
newUserPass: user_pass,
newUserPicture: user_picture,
assignedRoles: assigned_roles,
additionalPermissions: additional_permissions,
state: new_state
} = data;
// 2. Fetch current user
const existing = await read.query(`
SELECT
user_id,
auth_key,
user_name,
user_email,
user_pass,
user_picture,
assigned_roles,
additional_permissions,
state
FROM users
WHERE user_id = ?
`, [user_id]);
if (!existing[0]) throw new Error('User not found.');
const user = existing[0];
// 3. Check auth key
const authMatch = await bcrypt.compare(auth_key, user.auth_key);
if (!authMatch) throw new Error('Invalid auth key.');
// 4. Prepare fields to update
const updates = {};
if (user_name && user_name !== user.user_name) updates.user_name = user_name;
if (user_email && user_email !== user.user_email) updates.user_email = user_email;
if (user_pass) updates.user_pass = await bcrypt.hash(user_pass, SALT_ROUNDS);
if (assigned_roles) updates.assigned_roles = JSON.stringify(assigned_roles);
if (additional_permissions) updates.additional_permissions = JSON.stringify(additional_permissions);
if (new_state && new_state !== user.state) updates.state = new_state;
// 5. Handle new user picture
if (user_picture) {
const tmpFileName = path.basename(user_picture || '');
const tmpFilePath = path.join(TMP_UPLOAD_DIR, tmpFileName);
updates.user_picture = await saveUserPicture(user_id, tmpFilePath);
}
// 6. Build dynamic update query
const fields = Object.keys(updates);
if (fields.length === 0) return { message: 'Nothing to update.' };
const placeholders = fields.map(f => `${f} = ?`).join(', ');
const values = fields.map(f => updates[f]);
values.push(user_id);
await write.query(`
UPDATE users
SET ${placeholders}, last_active = NOW()
WHERE user_id = ?
`, values);
// 7. Return updated user data
const updatedUser = await read.query(`
SELECT
user_id,
user_name,
user_email,
user_picture,
from_project,
assigned_roles,
additional_permissions,
state
FROM users
WHERE user_id = ?
`, [user_id]);
return updatedUser[0];
},
// activation function
activate: async (data) => {
// 0. Declare dbs to use
const read = pools.users.r.s;
const write = pools.users.w.s;
// 1. Get incoming token
const { activationToken } = data;
if (!activationToken) throw new Error('No activation token provided.');
let payload;
try {
// 2. Verify token
payload = jwt.verify(activationToken, JWT_SECRET);
} catch (err) {
throw new Error('Invalid or expired activation token.');
}
const userId = payload.user_id;
// 3. Fetch user
const existing = await read.query(`
SELECT user_id, assigned_roles
FROM users
WHERE user_id = ?
`, [userId]);
if (!existing[0]) throw new Error('User not found.');
const user = existing[0];
// 4. Add "activated" role if not present
const roles = user.assigned_roles ? JSON.parse(user.assigned_roles) : [];
if (!roles.includes('activated')) roles.push('activated');
// 5. Update DB
await write.query(`
UPDATE users
SET assigned_roles = ?, last_active = NOW()
WHERE user_id = ?
`, [JSON.stringify(roles), userId]);
return { message: 'Account successfully activated.', assignedRoles: roles };
},
// check auth function
check: async (data) => {},
};
@@ -0,0 +1,68 @@
// ROOT/services/logger/readLogs.js
// Import the MySQL pools from your database service
const pools = require('../database/pools').get();
// Database name used for logging
const dbName = 'logging';
// Use the Read pool with the "system" role
const readPool = pools[dbName].r.s;
/*
* Function: getAllLogs
* Description: Retrieves all log entries from a specified table.
* @param {string} tableName - Name of the table to query.
* @returns {Array|Object} - Returns an array of log entries, or an error object.
*/
async function getAllLogs(tableName) {
if (!tableName) throw new Error('Table name is required');
try {
const [rows] = await readPool.query(`SELECT * FROM ${tableName}`);
return rows;
} catch (err) {
console.error('Error reading logs from DB:', err);
return { success: false, error: err };
}
}
/*
* Function: getLogs
* Description: Retrieves log entries from a specified table that match optional filter criteria.
* @param {string} tableName - Name of the table to query.
* @param {Object} filter - Optional key/value pairs representing column filters.
* Example: { user: 'andi', success: true }
* @returns {Array|Object} - Returns an array of filtered log entries, or an error object.
*/
async function getLogs(tableName, filter = {}) {
if (!tableName) throw new Error('Table name is required');
// Start building the SQL query
let sql = `SELECT * FROM ${tableName}`;
const values = [];
const filterKeys = Object.keys(filter);
if (filterKeys.length > 0) {
// Build WHERE clauses dynamically
const whereClauses = filterKeys.map(key => {
values.push(filter[key]);
return `${key} = ?`;
});
sql += ' WHERE ' + whereClauses.join(' AND ');
}
try {
const [rows] = await readPool.query(sql, values);
return rows;
} catch (err) {
console.error('Error reading filtered logs from DB:', err);
return { success: false, error: err };
}
}
// Export both functions for external use
module.exports = {
getAll: getAllLogs,
get: getLogs
};
@@ -0,0 +1,18 @@
let online = false;
module.exports = async function() {
try {
online = true;
console.log('Logger service started!');
return { online: online };
} catch (err) {
console.error('Logger service could not be started:', err);
return { online: false, error: err };
}
};
// Export online status for other modules
module.exports.online = () => online;
// Setter to change online status from other modules
module.exports.setOnline = (value) => { online = value; };
@@ -0,0 +1,5 @@
const logger = require('./start');
module.exports = async function() {
return { online: logger.online() };
};
@@ -0,0 +1,8 @@
const logger = require('./start');
module.exports = async function() {
// Set logger offline via setter in start.js
logger.setOnline(false);
console.log('Logger service stopped!');
return { online: logger.online() };
};
@@ -0,0 +1,56 @@
const loggerService = require('./start');
// Import the MySQL pools from your database service
const pools = require('../database/pools').get();
// Name of the database used for logging
const dbName = 'logging';
// Get the Write pool with the "system" role
const writePool = pools[dbName].w.s;
/*
* Generic function to write log data to any table
* @param {string} tableName - Name of the table to write to
* @param {Object} logData - Key/value pairs representing columns and values
* @returns {Object} - Success status and optional error
*/
async function writeToTable(tableName, logData) {
// Check if logger is active
if (!loggerService.online()) {
return { success: false, error: 'Logger is inactive' };
}
// Validate input
if (!tableName || !logData) throw new Error('Table name and log data are required');
// Extract columns and values dynamically from the logData object
const columns = Object.keys(logData).join(', '); // e.g., "user, action, success"
const placeholders = Object.keys(logData).map(() => '?').join(', '); // e.g., "?, ?, ?"
const values = Object.values(logData); // e.g., ["user", "login", true]
// Build the INSERT SQL statement
const sql = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders})`;
try {
// Execute the query on the write pool
await writePool.query(sql, values);
// Return success
return { success: true };
} catch (err) {
// Log the error to the console for debugging
console.error('Error writing log to DB:', err);
// Return failure state
return { success: false, error: err };
}
}
/*
* Generic write function for any other log types.
* This allows dynamic handling of new log types without modifying the module.
*/
module.exports.write = async function(logData, tableName) {
return writeToTable(tableName, logData);
};
@@ -0,0 +1,25 @@
// variables
const services = { start: {}, stop: {}, restart: {}, status: {} };
const serviceNames = ['mysql','websocket','logger','scheduler'];
// dynamic manager
['start', 'stop', 'status'].forEach(action => {
serviceNames.forEach(name => {
services[action][name] = async () => {
const file = `./${name}/${action}.js`;
const func = require(file);
return await func();
};
});
});
// restart function
serviceNames.forEach(name => {
services.restart[name] = async () => {
await services.stop[name]();
await services.start[name]();
};
});
// function export
module.exports = services;
@@ -0,0 +1,191 @@
// node dependencies
const fs = require('fs');
const path = require('path');
// Path to the tasks.json file, which stores all tasks persistently
const tasksFile = path.join(__dirname, 'tasks.json');
module.exports = {
// In-memory object holding all tasks
/* Each task has the following structure:
tasks[taskName] = {
active: false, // whether the task is currently running
timer: null, // reference to setTimeout for execution
nextRun: null, // timestamp of next scheduled execution
lastRun: null, // timestamp of last execution
error: null, // last error if the task failed
oneTime: true/false, // if true, task runs only once
intervalInSec: 0 // interval in seconds for repeated execution
action: string // name of the JS file in ROOT/tasks
}
*/
tasks: {},
// Load tasks from tasks.json into memory
loadTasks: function() {
// Create empty file if missing
if (!fs.existsSync(tasksFile)) {
fs.writeFileSync(tasksFile, '{}', 'utf-8');
}
// Read and parse JSON
const rawData = fs.readFileSync(tasksFile, 'utf-8');
const data = JSON.parse(rawData);
// Load each task into memory
for (const name in data) {
const taskData = data[name];
// Ensure timer is reset in memory
taskData.timer = null;
this.tasks[name] = taskData;
// If task is active, automatically start the timer
if (taskData.active) {
this._startTimer(name);
}
}
},
// Save current in-memory tasks to tasks.json - Removes timer references before saving
saveTasks: function() {
const dataToSave = {};
for (const name in this.tasks) {
const task = { ...this.tasks[name] };
delete task.timer; // timers cannot be persisted
dataToSave[name] = task;
}
fs.writeFileSync(tasksFile, JSON.stringify(dataToSave, null, 4), 'utf-8');
},
// Add or update a task in memory and save it to JSON
// If the task does not exist, initialize default fields
setTask: function(name, data) {
// If task does not exist, create default structure
if (!this.tasks[name]) {
this.tasks[name] = {
active: false,
timer: null,
nextRun: null,
lastRun: null,
error: null,
oneTime: false,
intervalInSec: null,
action: null
};
}
// Merge new data
Object.assign(this.tasks[name], data);
// Stop any running timer before updating
if (this.tasks[name].timer) {
clearTimeout(this.tasks[name].timer);
this.tasks[name].timer = null;
}
// Immediately start the timer if task is active
if (this.tasks[name].active) {
this._startTimer(name);
}
// Persist to JSON
this.saveTasks();
},
// Remove a task completely from memory and JSON
removeTask: function(name) {
const task = this.tasks[name];
// Stop running timer if it exists
if (task && task.timer) clearTimeout(task.timer);
// Remove from memory
delete this.tasks[name];
// Persist changes
this.saveTasks();
},
// Retrieve a single task by name
getTask: function(name) {
return this.tasks[name] || null;
},
// Retrieve all tasks in memory
getAllTasks: function() {
return this.tasks;
},
/*
* Internal function to start the timer for a task
* - Loads the action from ROOT/tasks
* - Executes the function at intervals or once depending on task.oneTime
* @param {string} name - Task name
*/
_startTimer: function(name) {
const task = this.tasks[name];
// Task must exist and have an action defined
if (!task || !task.action) return;
// Build path to task action file
const taskPath = path.join(__dirname, '../../../tasks', task.action + '.js');
let actionFunc;
try {
// Load the exported function from the task file
actionFunc = require(taskPath);
if (typeof actionFunc !== 'function') {
throw new Error('Task action must export a function');
}
} catch (err) {
console.error(`Failed to load action for task "${name}":`, err.message);
task.error = err.message;
this.saveTasks();
return;
}
// Convert interval to milliseconds
const intervalMs = task.intervalInSec ? task.intervalInSec * 1000 : 0;
// Function executed by the timer
const executeTask = async () => {
task.lastRun = Date.now();
try {
// Call the action function
await actionFunc();
task.error = null;
} catch (err) {
task.error = err.message || String(err);
}
if (!task.oneTime && intervalMs && task.active) {
// Recurring task: schedule next run
task.nextRun = Date.now() + intervalMs;
task.timer = setTimeout(executeTask, intervalMs);
} else if (task.oneTime) {
// One-time task: deactivate after execution
task.active = false;
task.nextRun = null;
task.timer = null;
}
// Save updated task info
this.saveTasks();
};
// Activate the task and schedule first execution
task.active = true;
task.nextRun = Date.now() + intervalMs;
task.timer = setTimeout(executeTask, intervalMs);
// Save memory state
this.saveTasks();
}
};
@@ -0,0 +1,36 @@
const memory = require('./memory.js'); // Scheduler memory
module.exports = async function() {
try {
// Load tasks from tasks.json into memory
// Active tasks will automatically start their timers
memory.loadTasks();
// Build the return object with task status
const tasksStatus = {};
const allTasks = memory.getAllTasks();
for (const name in allTasks) {
const t = allTasks[name];
tasksStatus[name] = {
active: t.active,
oneTime: t.oneTime,
intervalInSec: t.intervalInSec,
lastRun: t.lastRun,
nextRun: t.nextRun,
error: t.error,
action: t.action
};
}
return {
online: true, // service is running
tasks: tasksStatus
};
} catch (err) {
console.error('Failed to start scheduler service:', err);
return {
online: false,
tasks: {}
};
}
};
@@ -0,0 +1,36 @@
const memory = require('./memory.js'); // Scheduler memory
module.exports = async function() {
try {
const allTasks = memory.getAllTasks();
// Build the return object with task status
const tasksStatus = {};
for (const name in allTasks) {
const t = allTasks[name];
tasksStatus[name] = {
active: t.active, // whether the task is currently marked active
oneTime: t.oneTime, // one-time or recurring
intervalInSec: t.intervalInSec,
lastRun: t.lastRun, // last execution timestamp
nextRun: t.nextRun, // next scheduled execution timestamp
error: t.error, // last error if occurred
action: t.action // linked action file
};
}
// Determine if service is online (any task has a timer running)
const online = Object.values(allTasks).some(t => t.timer);
return {
online: online,
tasks: tasksStatus
};
} catch (err) {
console.error('Failed to retrieve scheduler status:', err);
return {
online: false,
tasks: {}
};
}
};
@@ -0,0 +1,42 @@
const memory = require('./memory.js'); // Scheduler memory
module.exports = async function() {
try {
const allTasks = memory.getAllTasks();
// Stop all running timers without changing 'active' state
for (const name in allTasks) {
const task = allTasks[name];
if (task.timer) {
clearTimeout(task.timer);
task.timer = null;
}
}
// Build the return object with task status
const tasksStatus = {};
for (const name in allTasks) {
const t = allTasks[name];
tasksStatus[name] = {
active: t.active,
oneTime: t.oneTime,
intervalInSec: t.intervalInSec,
lastRun: t.lastRun,
nextRun: t.nextRun,
error: t.error,
action: t.action
};
}
return {
online: false, // service is stopped
tasks: tasksStatus
};
} catch (err) {
console.error('Failed to stop scheduler service:', err);
return {
online: false,
tasks: {}
};
}
};
@@ -0,0 +1 @@
{}
@@ -0,0 +1,86 @@
// node dependencies
const WebSocket = require('ws');
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: '../../.env' });
// internal dependencies
const connections = require('./connections.js'); // connection manager
const stopService = require('./stop.js');
module.exports = async function startWebsocketServer() {
loadActions(actionsDir);
try {
const wss = new WebSocket.Server({
host: process.env.HOST,
port: parseInt(process.env.WS_PORT)
});
stopService.setServer(wss);
console.log('WebSocket server started on host localhost and port 3001');
// Start heartbeat system
connections.startHeartbeat();
wss.on('connection', (ws, req) => {
if (key !== process.env.INTERNAL_SECRET || url.origin !== process.env.CONN_URI) {
ws.close();
return;
}
// Add client to connection manager
const connectionId = connections.add(ws, {
connectedAt: Date.now()
});
console.log('New client connected:', connectionId);
// Handle incoming messages
ws.on('message', async (raw) => {
let msg;
try {
msg = JSON.parse(raw);
} catch (err) {
console.error('Invalid JSON received:', raw);
return;
}
// Expected format:
// { action: "namespace.actionName", data: {...} }
if (!msg.action) return;
const actionHandler = actions[msg.action];
if (!actionHandler) {
console.warn('Unknown action:', msg.action);
return;
}
try {
await actionHandler(msg.data, msg.sec, ws, connectionId);
} catch (err) {
console.error('Error executing action:', msg.action, err);
}
});
// Remove connection on close
ws.on('close', () => {
console.log('Client disconnected:', connectionId);
connections.remove(connectionId);
});
// Remove connection on error
ws.on('error', (err) => {
console.error('WebSocket error on', connectionId, err);
connections.remove(connectionId);
});
});
return { online: true, server: wss };
} catch (err) {
console.error('WebSocket server failed to start:', err);
return { online: false, error: err };
}
};
@@ -0,0 +1,36 @@
// internal dependencies
const connections = require('./connections.js'); // connection manager
const stopService = require('./stop.js'); // for server reference
/*
* Get WebSocket service status
* Returns:
* {
* online: boolean,
* clients: {
* connectionId1: { metadata: {...}, lastHeartbeat: ... },
* connectionId2: { ... }
* }
* }
*/
module.exports = async function websocketStatus() {
try {
// check if server exists and is running
const serverOnline = !!stopService.getServer();
// get active clients
const clients = connections.getAll(); // object with connectionId -> { metadata, lastHeartbeat }
return {
online: serverOnline,
clients
};
} catch (err) {
console.error('Error getting WebSocket status:', err);
return {
online: false,
clients: {},
error: err
};
}
};
@@ -0,0 +1,42 @@
// internal dependencies
const connections = require('./connections.js');
// This will hold a reference to the running WebSocket server
let serverInstance = null;
/*
* Stop the WebSocket service
* - Stops heartbeat
* - Closes all active connections
* - Closes the WebSocket server
*/
async function stopService () {
try {
if (!serverInstance) return { online: false };
// Stop heartbeat system
connections.stopHeartbeat();
// Terminate all active connections
connections.clearAll();
// Close WebSocket server
await new Promise((resolve, reject) => {
serverInstance.close((err) => {
if (err) return reject(err);
resolve();
});
});
serverInstance = null;
console.log('WebSocket server stopped successfully');
return { online: false };
} catch (err) {
console.error('Error stopping WebSocket server:', err);
return { online: true, error: err };
}
};
module.exports = stopService;
+12
View File
@@ -0,0 +1,12 @@
/*
! /services/idGenerator.js
= Central Unique ID-Generator
? This module will create unique ids using crypto
*/
// % import nodejs dependencies
import crypto from 'crypto';
// $ create a new unique id
export function generate() { return crypto.randomBytes(32).toString('hex'); } // simple id generator
export function generatePrefixed(prefix) { return `${prefix}_${generate()}`; } // prefixed id generator
+21
View File
@@ -0,0 +1,21 @@
PORT=3000
# DATABASE CONNECTION DETAILS
DB_CONN_IP=selfdev-mariadb
DB_CONN_PORT=3306
# DATABASE CREDENTIALS (read)
DB_READ_USER=
DB_READ_PASS=
# DATABASE CREDENTIALS (write)
DB_WRITE_USER=
DB_WRITE_PASS=
# DATABASE CREDENTIALS (delete)
DB_DELETE_USER=
DB_DELETE_PASS=
# DATABASE CREDENTIALS (backup)
DB_BACKUP_USER=
DB_BACKUP_PASS=
+1
View File
@@ -0,0 +1 @@
[]
+1
View File
@@ -0,0 +1 @@
# LupiNex Connect
+13
View File
@@ -0,0 +1,13 @@
/*
! /actions/.templateAction.js
? Action socket.io handler template.
*/
// % export default action function
export default async function handleAction(socket, dbWorker, data) {
// $ Business logic for processing the chat message
console.log(`Received message from ${socket.id}:`, data.message);
// $ Respond back to the client or broadcast
socket.emit('chat.response', { status: 'success', text: 'Message received!' });
}
+133
View File
@@ -0,0 +1,133 @@
/*
! /database/commands.js
? The command library for mariadb
*/
// % imports
import pools from './pools.js'; // functions "add(this)", "remove(this.name)", "status(name)"
// = command manager class
export default class Commands {
constructor(name, db, protocol = 'read', address = process.env.DB_CONN_IP) {
this.name = name; // persistent identifier
this.address = address; // database adress (host)
this.database = db; // database name
this.protocol = protocol; // user protocol
this.pool = null; // pool reference
this.connStat = false; // connection status
}
// $ add a connection to the pool manager
async connect() {
console.log(`[Database] Connecting command instance "${this.name}" to database "${this.database}"...`);
const result = await pools.add(this);
this.connStat = result.connStat;
this.pool = result.pool;
console.log(`[Database] Connection status for "${this.name}": ${this.connStat ? 'SUCCESS' : 'FAILED'}`);
}
// $ remove a connection from the pool manager
async disconnect() {
if (this.connStat) {
console.log(`[Database] Disconnecting instance "${this.name}"...`);
const result = await pools.remove(this.name);
this.connStat = result?.connStat ?? false;
this.pool = null;
console.log(`[Database] Instance "${this.name}" successfully disconnected.`);
}
}
// $ get the status of a connection from the pool manager
async status() {
if (this.connStat) {
console.log(`[Database] Checking status for instance "${this.name}"...`);
const result = await pools.status(this.name);
this.connStat = result?.connStat ?? false;
this.pool = result?.pool ?? null;
console.log(`[Database] Status for "${this.name}": Active = ${this.connStat}`);
return result;
} else {
console.log(`[Database] Status check skipped for "${this.name}" (currently not connected).`);
}
}
// $ execute an sql query
async query(query, params = []) {
// ~ check if connection is established
if (!this.connStat || !this.pool) {
console.error(`[Database Error] Tried to execute query on disconnected instance "${this.name}".`);
throw new Error(`Database connection '${this.name}' is not established.`);
}
// ~ connection reference
let conn;
// ~ try executing the query
try {
conn = await this.pool.getConnection();
const rows = await conn.query(query, params);
console.log(`[Database Query] [${this.name}] Executed successfully (${duration}ms):`, { query, params });
return rows;
// ~ throw error if failed
} catch(e) {
console.error(`[Database Error] Query failed in "${this.name}":`, e.message);
console.error(`[Failed Query]:`, query, params);
throw e;
// ~ release the connection back to the pool
} finally {
if (conn) conn.release();
}
}
// $ insert a record into a table
async insert(table, data) {
if (Object.keys(data).length === 0) return null;
const keys = Object.keys(data);
const values = Object.values(data);
const placeholders = keys.map(() => '?').join(', ');
const columns = keys.join(', ');
const query = `INSERT INTO ${table} (${columns}) VALUES (${placeholders})`;
return await this.query(query, values);
}
// $ update a record in a table
async update(table, data, where, whereParams = []) {
if (Object.keys(data).length === 0) return null;
const keys = Object.keys(data);
const values = Object.values(data);
const setClause = keys.map(key => `${key} = ?`).join(', ');
const query = `UPDATE ${table} SET ${setClause} WHERE ${where}`;
return await this.query(query, [...values, ...whereParams]);
}
// $ select records from a table
async select(table, columns = '*', where = '', params = []) {
let query = `SELECT ${Array.isArray(columns) ? columns.join(', ') : columns} FROM ${table}`;
if (where) {
query += ` WHERE ${where}`;
}
return await this.query(query, params);
}
// $ delete records from a table
async delete(table, where, params = []) {
const query = `DELETE FROM ${table} WHERE ${where}`;
return await this.query(query, params);
}
// $ create a new table
async createTable(table, columns = 'id INT AUTO_INCREMENT PRIMARY KEY') {
const query = `CREATE TABLE IF NOT EXISTS ${table} (${columns}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci`;
return await this.query(query);
}
// $ alter an existing table
async alterTable(table, modificationClause) {
const query = `ALTER TABLE ${table} ${modificationClause}`;
return await this.query(query);
}
};
+143
View File
@@ -0,0 +1,143 @@
/*
! /database/index.js
? The central database worker factory for multi-project management.
*/
// % imports
/*
= The MariaDB-"Commands"-Library:
^ Create a new instance:
?? Create the command instance: "const dbName = new Commands(name, db, protocol, address);"
? Available protocols are "read", "write", "delete" and "backup".
? Address and protocol can be left empty for the default values. (selfdev-mariadb, read)
? Example: "const dbName = new Commands('reader', 'myDatabase');"
^ Connection Usage:
?? Add a new connection: "await dbName.connect();"
?? Remove a connection: "await dbName.disconnect();"
?? Check the status of a connection: "await dbName.status();"
^ Basic Query Usage:
?? Execute a basic sql query: "await dbName.query(query, params = []);"
? Example: "await dbName.query('SELECT * FROM langs');"
? Queries can be everything that is possible with mariadb.
? Returns an array of objects (for SELECT) or a result metadata object (for INSERT/UPDATE/DELETE).
^ Table Management Usage:
?? Execute a table creation: "await dbName.createTable(table, columns = 'id INT AUTO_INCREMENT PRIMARY KEY');"
? Example: "await dbName.createTable('projects', 'id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255)');"
? Automatically applies the ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci.
? Returns an object containing metadata like { affectedRows: 0, warningCount: 0 } or execution result.
?? Execute a table alteration: "await dbName.alterTable(table, modificationClause);"
? Example: "await dbName.alterTable('projects', 'ADD COLUMN created_at TIMESTAMP');"
? Returns an object containing metadata like { affectedRows: 0, warningCount: 0 } or execution result.
^ Helper Query Command Usage:
?? Execute an insert query: "await dbName.insert(table, data);"
? Example: "await dbName.insert('users', { name: 'Dummy', role: 'admin' });"
? Returns an object containing metadata like "{ affectedRows: 1, insertId: 12n }" or null if data is empty.
?? Execute an update query: "await dbName.update(table, data, where, whereParams = []);"
? Example: "await dbName.update('users', { role: 'editor' }, 'id = ?', [1]);"
? Returns an object containing metadata like { affectedRows: 1, warningCount: 0 } or null if data is empty.
?? Execute a selection query: "await dbName.select(table, columns = '*', where = '', params = []);"
? Example: "await dbName.select('users', ['id', 'name'], 'role = ?', ['admin']);"
? Returns an array of matching row objects, e.g. [{ id: 1, name: 'Dummy' }].
?? Execute a deletion query: "await dbName.delete(table, where, params = []);"
? Example: "await dbName.delete('users', 'id = ?', [1]);"
? Returns an object containing metadata like { affectedRows: 1 }.
*/
import Commands from "./commands.js";
// % internal registry for all dynamic project database instances
const registry = new Map();
/*
= The Database-Worker-Factory:
^ Get or create a project instance:
?? Usage: "const db = dbWorker.get(projectName, address);"
? Example: "const db = dbWorker.get('myProject', 'db_host_address');"
? Returns an object containing the command instances (e.g. { read, write, delete, backup }).
^ Connect all protocols for a certain project:
?? Usage: "await dbWorker.connectProject(projectName);"
? Example: "await dbWorker.connectProject('myProject');"
? Returns nothing.
^ Disconnect all protocols for a certain project:
?? Usage: "await dbWorker.disconnectProject(projectName);"
? Example: "await dbWorker.disconnectProject('myProject');"
? Returns nothing.
^ Query Usage Example:
?? const db = dbWorker.get('myProject');
?? await db.read.connect(); // optional, if "connectProject" was not called
?? const users = await db.read.select('users', '*', 'active = ?', [1]);
?? await db.write.insert('users', { name: 'Dummy' });
*/
export default {
// $ get or initialize database for a specific project
get(projectName, address) {
if (!projectName) {
throw new Error("A project name is required to get a database instance.");
}
// ~ return an existing project instance if already created
if (registry.has(projectName)) {
console.log(`[Database] Returning existing instance for project: "${projectName}"`);
return registry.get(projectName);
}
// $ debug-output
console.log(`[Database] Initializing new instances (read, write, delete, backup) for: "${projectName}"`);
// ~ create new instances (r,w,d,b) for the given project database
const instances = {
read: new Commands(`${projectName}_reader`, projectName, 'read', address || null),
write: new Commands(`${projectName}_writer`, projectName, 'write', address || null),
delete: new Commands(`${projectName}_deleter`, projectName, 'delete', address || null),
backup: new Commands(`${projectName}_backuper`, projectName, 'backup', address || null)
};
// ~ store the instance in the registry
registry.set(projectName, instances);
// ~ return result
return instances;
},
// $ helper to connect all instances of a specific project at once
async connectProject(projectName) {
console.log(`[Database] Connecting all instances for project: "${projectName}"...`);
const db = this.get(projectName);
await Promise.all([
db.read.connect(),
db.write.connect(),
db.delete.connect(),
db.backup.connect()
]);
},
// $ helper to disconnect all instances of a specific project
async disconnectProject(projectName) {
console.log(`[Database] Disconnecting all instances for project: "${projectName}"...`);
if (registry.has(projectName)) {
const db = registry.get(projectName);
await Promise.all([
db.read.disconnect(),
db.write.disconnect(),
db.delete.disconnect(),
db.backup.disconnect()
]);
registry.delete(projectName);
console.log(`[Database] Successfully disconnected and removed from registry: "${projectName}"`);
} else {
console.log(`[Database] Tried to disconnect project "${projectName}", but it was not found in registry.`);
}
}
};
+87
View File
@@ -0,0 +1,87 @@
/*
! /database/pools.js
? The main database pool (connection) manager
*/
// % imports
import mariadb from 'mariadb';
// % database credentials mapping
const CREDENTIALS_MAP = {
read: { user: process.env.DB_READ_USER, pass: process.env.DB_READ_PASS },
write: { user: process.env.DB_WRITE_USER, pass: process.env.DB_WRITE_PASS },
delete: { user: process.env.DB_DELETE_USER, pass: process.env.DB_DELETE_PASS },
backup: { user: process.env.DB_BACKUP_USER, pass: process.env.DB_BACKUP_PASS }
}
// $ get database credentials
function getCredentials(protocol = 'read') {
const key = String(protocol).toLowerCase();
return CREDENTIALS_MAP[key] ?? CREDENTIALS_MAP.read;
}
// = pool manager class
class PoolManager {
constructor() {
this.pools = new Map();
}
// $ function to add a connection
async add(config) {
// ~ check if this pool is already existing → then return
if (this.pools.has(config.name)) {
return { pool: this.pools.get(config.name), connStat: true };
}
// ~ get database credentials by protocol
const { user, pass } = getCredentials(config.protocol);
// ~ create the mariadb pool
const pool = mariadb.createPool({
host: config.address,
port: Number(process.env.DB_CONN_PORT),
user,
password: pass,
database: config.database,
waitForConnections: true,
connectionLimit: 5,
queueLimit: 0,
enableKeepAlive: true,
keepAliveInitialDelay: 10000
});
// ~ add the pool to the map
this.pools.set(config.name, pool);
// ~ return the pool
return { pool, connStat: true };
}
// $ function to remove a connection
async remove(name) {
if (this.pools.has(name)) {
const pool = this.pools.get(name);
await pool.end();
this.pools.delete(name);
}
return { connStat: false };
}
// $ function to get the status of a connection
async status(name) {
if (this.pools.has(name)) {
try {
const pool = this.pools.get(name);
await pool.query('SELECT 1');
return { pool, connStat: true };
} catch(err) {
return { connStat: false, msg: err }
}
} else {
return { connStat: false };
}
}
}
// $ export the pool manager
export default new PoolManager();
View File
View File
View File
+24
View File
@@ -0,0 +1,24 @@
/*
! /index.js
? The central API interface and orchestration layer for external requests.
= LupiNex Connect API Roadmap:
^ Database-Worker-Factory:
DONE: The Database-Worker-Factory (/database/index.js) [30.08.2026] || The database documentation and usage can be found here.
DONE: The Database-Pool (Connection) Manager (/database/pools.js) [30.08.2026]
DONE: The MariaDB-"Commands"-Library (/database/commands.js) [30.08.2026]
^ WebSocket and Express Server:
DONE: The Express-WebSocket-Loader-Module (/server/index.js) [30.08.2026] || The server documentation and usage can be found here.
DONE: Autoloader for express routes (/server/routers.js) [30.08.2026]
DONE: Autoloader for socket.io actions (/server/actions.js) [30.08.2026]
TODO: Create a few actions for socket.io (/actions)
TODO: Create a few routers for express (/routers)
^ Validation-Middlewares:
TODO:
*/
// % imports
import 'dotenv/config'; // import the dotenv config
import server from './server/index.js'; // import the server structures (express, socket.io) // always at the end
Generated Vendored
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../ejs/bin/cli.js" "$@"
else
exec node "$basedir/../ejs/bin/cli.js" "$@"
fi
Generated Vendored
+17
View File
@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\ejs\bin\cli.js" %*
Generated Vendored
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../ejs/bin/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../ejs/bin/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../ejs/bin/cli.js" $args
} else {
& "node$exe" "$basedir/../ejs/bin/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
+1258
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
(The MIT License)
Copyright (c) 2014 Component contributors <dev@component.io>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
+79
View File
@@ -0,0 +1,79 @@
# `@socket.io/component-emitter`
Event emitter component.
This project is a fork of the [`component-emitter`](https://github.com/sindresorhus/component-emitter) project, with [Socket.IO](https://socket.io/)-specific TypeScript typings.
## Installation
```
$ npm i @socket.io/component-emitter
```
## API
### Emitter(obj)
The `Emitter` may also be used as a mixin. For example
a "plain" object may become an emitter, or you may
extend an existing prototype.
As an `Emitter` instance:
```js
import { Emitter } from '@socket.io/component-emitter';
var emitter = new Emitter;
emitter.emit('something');
```
As a mixin:
```js
import { Emitter } from '@socket.io/component-emitter';
var user = { name: 'tobi' };
Emitter(user);
user.emit('im a user');
```
As a prototype mixin:
```js
import { Emitter } from '@socket.io/component-emitter';
Emitter(User.prototype);
```
### Emitter#on(event, fn)
Register an `event` handler `fn`.
### Emitter#once(event, fn)
Register a single-shot `event` handler `fn`,
removed immediately after it is invoked the
first time.
### Emitter#off(event, fn)
* Pass `event` and `fn` to remove a listener.
* Pass `event` to remove all listeners on that event.
* Pass nothing to remove all listeners on all events.
### Emitter#emit(event, ...)
Emit an `event` with variable option args.
### Emitter#listeners(event)
Return an array of callbacks, or an empty array.
### Emitter#hasListeners(event)
Check if this emitter has `event` handlers.
## License
MIT
+179
View File
@@ -0,0 +1,179 @@
/**
* An events map is an interface that maps event names to their value, which
* represents the type of the `on` listener.
*/
export interface EventsMap {
[event: string]: any;
}
/**
* The default events map, used if no EventsMap is given. Using this EventsMap
* is equivalent to accepting all event names, and any data.
*/
export interface DefaultEventsMap {
[event: string]: (...args: any[]) => void;
}
/**
* Returns a union type containing all the keys of an event map.
*/
export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
/** The tuple type representing the parameters of an event listener */
export type EventParams<
Map extends EventsMap,
Ev extends EventNames<Map>
> = Parameters<Map[Ev]>;
/**
* The event names that are either in ReservedEvents or in UserEvents
*/
export type ReservedOrUserEventNames<
ReservedEventsMap extends EventsMap,
UserEvents extends EventsMap
> = EventNames<ReservedEventsMap> | EventNames<UserEvents>;
/**
* Type of a listener of a user event or a reserved event. If `Ev` is in
* `ReservedEvents`, the reserved event listener is returned.
*/
export type ReservedOrUserListener<
ReservedEvents extends EventsMap,
UserEvents extends EventsMap,
Ev extends ReservedOrUserEventNames<ReservedEvents, UserEvents>
> = FallbackToUntypedListener<
Ev extends EventNames<ReservedEvents>
? ReservedEvents[Ev]
: Ev extends EventNames<UserEvents>
? UserEvents[Ev]
: never
>;
/**
* Returns an untyped listener type if `T` is `never`; otherwise, returns `T`.
*
* This is a hack to mitigate https://github.com/socketio/socket.io/issues/3833.
* Needed because of https://github.com/microsoft/TypeScript/issues/41778
*/
type FallbackToUntypedListener<T> = [T] extends [never]
? (...args: any[]) => void | Promise<void>
: T;
/**
* Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
* parameters for mappings of event names to event data types, and strictly
* types method calls to the `EventEmitter` according to these event maps.
*
* @typeParam ListenEvents - `EventsMap` of user-defined events that can be
* listened to with `on` or `once`
* @typeParam EmitEvents - `EventsMap` of user-defined events that can be
* emitted with `emit`
* @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
* emitted by socket.io with `emitReserved`, and can be listened to with
* `listen`.
*/
export class Emitter<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap,
ReservedEvents extends EventsMap = {}
> {
/**
* Adds the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
on<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Adds a one-time `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
once<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
off<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Emits an event.
*
* @param ev Name of the event
* @param args Values to send to listeners of this event
*/
emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): this;
/**
* Emits a reserved event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can emit its own reserved events.
*
* @param ev Reserved event name
* @param args Arguments to emit along with the event
*/
protected emitReserved<Ev extends EventNames<ReservedEvents>>(
ev: Ev,
...args: EventParams<ReservedEvents, Ev>
): this;
/**
* Returns the listeners listening to an event.
*
* @param event Event name
* @returns Array of listeners subscribed to `event`
*/
listeners<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
event: Ev
): ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>[];
/**
* Returns true if there is a listener for this event.
*
* @param event Event name
* @returns boolean
*/
hasListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(event: Ev): boolean;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
removeListener<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes all `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
*/
removeAllListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(ev?: Ev): this;
}
+176
View File
@@ -0,0 +1,176 @@
/**
* Expose `Emitter`.
*/
exports.Emitter = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
}
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks['$' + event] = this._callbacks['$' + event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
function on() {
this.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks['$' + event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks['$' + event];
return this;
}
// remove specific handler
var cb;
for (var i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
// Remove event specific arrays for event types that no
// one is subscribed for to avoid memory leak.
if (callbacks.length === 0) {
delete this._callbacks['$' + event];
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = new Array(arguments.length - 1)
, callbacks = this._callbacks['$' + event];
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
// alias used for reserved events (protected method)
Emitter.prototype.emitReserved = Emitter.prototype.emit;
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks['$' + event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
+4
View File
@@ -0,0 +1,4 @@
{
"name": "@socket.io/component-emitter",
"type": "commonjs"
}
+179
View File
@@ -0,0 +1,179 @@
/**
* An events map is an interface that maps event names to their value, which
* represents the type of the `on` listener.
*/
export interface EventsMap {
[event: string]: any;
}
/**
* The default events map, used if no EventsMap is given. Using this EventsMap
* is equivalent to accepting all event names, and any data.
*/
export interface DefaultEventsMap {
[event: string]: (...args: any[]) => void;
}
/**
* Returns a union type containing all the keys of an event map.
*/
export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
/** The tuple type representing the parameters of an event listener */
export type EventParams<
Map extends EventsMap,
Ev extends EventNames<Map>
> = Parameters<Map[Ev]>;
/**
* The event names that are either in ReservedEvents or in UserEvents
*/
export type ReservedOrUserEventNames<
ReservedEventsMap extends EventsMap,
UserEvents extends EventsMap
> = EventNames<ReservedEventsMap> | EventNames<UserEvents>;
/**
* Type of a listener of a user event or a reserved event. If `Ev` is in
* `ReservedEvents`, the reserved event listener is returned.
*/
export type ReservedOrUserListener<
ReservedEvents extends EventsMap,
UserEvents extends EventsMap,
Ev extends ReservedOrUserEventNames<ReservedEvents, UserEvents>
> = FallbackToUntypedListener<
Ev extends EventNames<ReservedEvents>
? ReservedEvents[Ev]
: Ev extends EventNames<UserEvents>
? UserEvents[Ev]
: never
>;
/**
* Returns an untyped listener type if `T` is `never`; otherwise, returns `T`.
*
* This is a hack to mitigate https://github.com/socketio/socket.io/issues/3833.
* Needed because of https://github.com/microsoft/TypeScript/issues/41778
*/
type FallbackToUntypedListener<T> = [T] extends [never]
? (...args: any[]) => void | Promise<void>
: T;
/**
* Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
* parameters for mappings of event names to event data types, and strictly
* types method calls to the `EventEmitter` according to these event maps.
*
* @typeParam ListenEvents - `EventsMap` of user-defined events that can be
* listened to with `on` or `once`
* @typeParam EmitEvents - `EventsMap` of user-defined events that can be
* emitted with `emit`
* @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
* emitted by socket.io with `emitReserved`, and can be listened to with
* `listen`.
*/
export class Emitter<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap,
ReservedEvents extends EventsMap = {}
> {
/**
* Adds the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
on<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Adds a one-time `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
once<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
off<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Emits an event.
*
* @param ev Name of the event
* @param args Values to send to listeners of this event
*/
emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): this;
/**
* Emits a reserved event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can emit its own reserved events.
*
* @param ev Reserved event name
* @param args Arguments to emit along with the event
*/
protected emitReserved<Ev extends EventNames<ReservedEvents>>(
ev: Ev,
...args: EventParams<ReservedEvents, Ev>
): this;
/**
* Returns the listeners listening to an event.
*
* @param event Event name
* @returns Array of listeners subscribed to `event`
*/
listeners<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
event: Ev
): ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>[];
/**
* Returns true if there is a listener for this event.
*
* @param event Event name
* @returns boolean
*/
hasListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(event: Ev): boolean;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
removeListener<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes all `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
*/
removeAllListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(ev?: Ev): this;
}
+169
View File
@@ -0,0 +1,169 @@
/**
* Initialize a new `Emitter`.
*
* @api public
*/
export function Emitter(obj) {
if (obj) return mixin(obj);
}
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks['$' + event] = this._callbacks['$' + event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
function on() {
this.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks['$' + event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks['$' + event];
return this;
}
// remove specific handler
var cb;
for (var i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
// Remove event specific arrays for event types that no
// one is subscribed for to avoid memory leak.
if (callbacks.length === 0) {
delete this._callbacks['$' + event];
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = new Array(arguments.length - 1)
, callbacks = this._callbacks['$' + event];
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
// alias used for reserved events (protected method)
Emitter.prototype.emitReserved = Emitter.prototype.emit;
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks['$' + event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
+4
View File
@@ -0,0 +1,4 @@
{
"name": "@socket.io/component-emitter",
"type": "module"
}
+28
View File
@@ -0,0 +1,28 @@
{
"name": "@socket.io/component-emitter",
"description": "Event emitter",
"version": "3.1.2",
"license": "MIT",
"devDependencies": {
"mocha": "*",
"should": "*"
},
"component": {
"scripts": {
"emitter/index.js": "index.js"
}
},
"main": "./lib/cjs/index.js",
"module": "./lib/esm/index.js",
"types": "./lib/cjs/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/socketio/emitter.git"
},
"scripts": {
"test": "make test"
},
"files": [
"lib/"
]
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
+75
View File
@@ -0,0 +1,75 @@
# Installation
> `npm install --save @types/cors`
# Summary
This package contains type definitions for cors (https://github.com/expressjs/cors/).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/cors.
## [index.d.ts](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/cors/index.d.ts)
````ts
/// <reference types="node" />
import { IncomingHttpHeaders } from "http";
type StaticOrigin = boolean | string | RegExp | Array<boolean | string | RegExp>;
type CustomOrigin = (
requestOrigin: string | undefined,
callback: (err: Error | null, origin?: StaticOrigin) => void,
) => void;
declare namespace e {
interface CorsRequest {
method?: string | undefined;
headers: IncomingHttpHeaders;
}
interface CorsOptions {
/**
* @default '*'
*/
origin?: StaticOrigin | CustomOrigin | undefined;
/**
* @default 'GET,HEAD,PUT,PATCH,POST,DELETE'
*/
methods?: string | string[] | undefined;
allowedHeaders?: string | string[] | undefined;
exposedHeaders?: string | string[] | undefined;
credentials?: boolean | undefined;
maxAge?: number | undefined;
/**
* @default false
*/
preflightContinue?: boolean | undefined;
/**
* @default 204
*/
optionsSuccessStatus?: number | undefined;
}
type CorsOptionsDelegate<T extends CorsRequest = CorsRequest> = (
req: T,
callback: (err: Error | null, options?: CorsOptions) => void,
) => void;
}
declare function e<T extends e.CorsRequest = e.CorsRequest>(
options?: e.CorsOptions | e.CorsOptionsDelegate<T>,
): (
req: T,
res: {
statusCode?: number | undefined;
setHeader(key: string, value: string): any;
end(): any;
},
next: (err?: any) => any,
) => void;
export = e;
````
### Additional Details
* Last updated: Sat, 07 Jun 2025 02:15:25 GMT
* Dependencies: [@types/node](https://npmjs.com/package/@types/node)
# Credits
These definitions were written by [Alan Plum](https://github.com/pluma), [Gaurav Sharma](https://github.com/gtpan77), and [Sebastian Beltran](https://github.com/bjohansebas).
+56
View File
@@ -0,0 +1,56 @@
/// <reference types="node" />
import { IncomingHttpHeaders } from "http";
type StaticOrigin = boolean | string | RegExp | Array<boolean | string | RegExp>;
type CustomOrigin = (
requestOrigin: string | undefined,
callback: (err: Error | null, origin?: StaticOrigin) => void,
) => void;
declare namespace e {
interface CorsRequest {
method?: string | undefined;
headers: IncomingHttpHeaders;
}
interface CorsOptions {
/**
* @default '*'
*/
origin?: StaticOrigin | CustomOrigin | undefined;
/**
* @default 'GET,HEAD,PUT,PATCH,POST,DELETE'
*/
methods?: string | string[] | undefined;
allowedHeaders?: string | string[] | undefined;
exposedHeaders?: string | string[] | undefined;
credentials?: boolean | undefined;
maxAge?: number | undefined;
/**
* @default false
*/
preflightContinue?: boolean | undefined;
/**
* @default 204
*/
optionsSuccessStatus?: number | undefined;
}
type CorsOptionsDelegate<T extends CorsRequest = CorsRequest> = (
req: T,
callback: (err: Error | null, options?: CorsOptions) => void,
) => void;
}
declare function e<T extends e.CorsRequest = e.CorsRequest>(
options?: e.CorsOptions | e.CorsOptionsDelegate<T>,
): (
req: T,
res: {
statusCode?: number | undefined;
setHeader(key: string, value: string): any;
end(): any;
},
next: (err?: any) => any,
) => void;
export = e;
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@types/cors",
"version": "2.8.19",
"description": "TypeScript definitions for cors",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/cors",
"license": "MIT",
"contributors": [
{
"name": "Alan Plum",
"githubUsername": "pluma",
"url": "https://github.com/pluma"
},
{
"name": "Gaurav Sharma",
"githubUsername": "gtpan77",
"url": "https://github.com/gtpan77"
},
{
"name": "Sebastian Beltran",
"githubUsername": "bjohansebas",
"url": "https://github.com/bjohansebas"
}
],
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/cors"
},
"scripts": {},
"dependencies": {
"@types/node": "*"
},
"peerDependencies": {},
"typesPublisherContentHash": "a090e558c5f443573318c2955deecddc840bd8dfaac7cdedf31c7f6ede8d0b47",
"typeScriptVersion": "5.1"
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
+15
View File
@@ -0,0 +1,15 @@
# Installation
> `npm install --save @types/geojson`
# Summary
This package contains type definitions for geojson (https://geojson.org/).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/geojson.
### Additional Details
* Last updated: Thu, 23 Jan 2025 18:36:51 GMT
* Dependencies: none
# Credits
These definitions were written by [Jacob Bruun](https://github.com/cobster), [Arne Schubert](https://github.com/atd-schubert), [Jeff Jacobson](https://github.com/JeffJacobson), [Ilia Choly](https://github.com/icholy), and [Dan Vanderkam](https://github.com/danvk).
+202
View File
@@ -0,0 +1,202 @@
// Note: as of the RFC 7946 version of GeoJSON, Coordinate Reference Systems
// are no longer supported. (See https://tools.ietf.org/html/rfc7946#appendix-B)}
export as namespace GeoJSON;
/**
* The valid values for the "type" property of GeoJSON geometry objects.
* https://tools.ietf.org/html/rfc7946#section-1.4
*/
export type GeoJsonGeometryTypes = Geometry["type"];
/**
* The value values for the "type" property of GeoJSON Objects.
* https://tools.ietf.org/html/rfc7946#section-1.4
*/
export type GeoJsonTypes = GeoJSON["type"];
/**
* Bounding box
* https://tools.ietf.org/html/rfc7946#section-5
*/
export type BBox = [number, number, number, number] | [number, number, number, number, number, number];
/**
* A Position is an array of coordinates.
* https://tools.ietf.org/html/rfc7946#section-3.1.1
* Array should contain between two and three elements.
* The previous GeoJSON specification allowed more elements (e.g., which could be used to represent M values),
* but the current specification only allows X, Y, and (optionally) Z to be defined.
*
* Note: the type will not be narrowed down to `[number, number] | [number, number, number]` due to
* marginal benefits and the large impact of breaking change.
*
* See previous discussions on the type narrowing:
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21590|Nov 2017}
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/67773|Dec 2023}
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/71441| Dec 2024}
*
* One can use a
* {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|user-defined type guard that returns a type predicate}
* to determine if a position is a 2D or 3D position.
*
* @example
* import type { Position } from 'geojson';
*
* type StrictPosition = [x: number, y: number] | [x: number, y: number, z: number]
*
* function isStrictPosition(position: Position): position is StrictPosition {
* return position.length === 2 || position.length === 3
* };
*
* let position: Position = [-116.91, 45.54];
*
* let x: number;
* let y: number;
* let z: number | undefined;
*
* if (isStrictPosition(position)) {
* // `tsc` would throw an error if we tried to destructure a fourth parameter
* [x, y, z] = position;
* } else {
* throw new TypeError("Position is not a 2D or 3D point");
* }
*/
export type Position = number[];
/**
* The base GeoJSON object.
* https://tools.ietf.org/html/rfc7946#section-3
* The GeoJSON specification also allows foreign members
* (https://tools.ietf.org/html/rfc7946#section-6.1)
* Developers should use "&" type in TypeScript or extend the interface
* to add these foreign members.
*/
export interface GeoJsonObject {
// Don't include foreign members directly into this type def.
// in order to preserve type safety.
// [key: string]: any;
/**
* Specifies the type of GeoJSON object.
*/
type: GeoJsonTypes;
/**
* Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.
* The value of the bbox member is an array of length 2*n where n is the number of dimensions
* represented in the contained geometries, with all axes of the most southwesterly point
* followed by all axes of the more northeasterly point.
* The axes order of a bbox follows the axes order of geometries.
* https://tools.ietf.org/html/rfc7946#section-5
*/
bbox?: BBox | undefined;
}
/**
* Union of GeoJSON objects.
*/
export type GeoJSON<G extends Geometry | null = Geometry, P = GeoJsonProperties> =
| G
| Feature<G, P>
| FeatureCollection<G, P>;
/**
* Geometry object.
* https://tools.ietf.org/html/rfc7946#section-3
*/
export type Geometry = Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection;
export type GeometryObject = Geometry;
/**
* Point geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.2
*/
export interface Point extends GeoJsonObject {
type: "Point";
coordinates: Position;
}
/**
* MultiPoint geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.3
*/
export interface MultiPoint extends GeoJsonObject {
type: "MultiPoint";
coordinates: Position[];
}
/**
* LineString geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.4
*/
export interface LineString extends GeoJsonObject {
type: "LineString";
coordinates: Position[];
}
/**
* MultiLineString geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.5
*/
export interface MultiLineString extends GeoJsonObject {
type: "MultiLineString";
coordinates: Position[][];
}
/**
* Polygon geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.6
*/
export interface Polygon extends GeoJsonObject {
type: "Polygon";
coordinates: Position[][];
}
/**
* MultiPolygon geometry object.
* https://tools.ietf.org/html/rfc7946#section-3.1.7
*/
export interface MultiPolygon extends GeoJsonObject {
type: "MultiPolygon";
coordinates: Position[][][];
}
/**
* Geometry Collection
* https://tools.ietf.org/html/rfc7946#section-3.1.8
*/
export interface GeometryCollection<G extends Geometry = Geometry> extends GeoJsonObject {
type: "GeometryCollection";
geometries: G[];
}
export type GeoJsonProperties = { [name: string]: any } | null;
/**
* A feature object which contains a geometry and associated properties.
* https://tools.ietf.org/html/rfc7946#section-3.2
*/
export interface Feature<G extends Geometry | null = Geometry, P = GeoJsonProperties> extends GeoJsonObject {
type: "Feature";
/**
* The feature's geometry
*/
geometry: G;
/**
* A value that uniquely identifies this feature in a
* https://tools.ietf.org/html/rfc7946#section-3.2.
*/
id?: string | number | undefined;
/**
* Properties associated with this feature.
*/
properties: P;
}
/**
* A collection of feature objects.
* https://tools.ietf.org/html/rfc7946#section-3.3
*/
export interface FeatureCollection<G extends Geometry | null = Geometry, P = GeoJsonProperties> extends GeoJsonObject {
type: "FeatureCollection";
features: Array<Feature<G, P>>;
}
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@types/geojson",
"version": "7946.0.16",
"description": "TypeScript definitions for geojson",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/geojson",
"license": "MIT",
"contributors": [
{
"name": "Jacob Bruun",
"githubUsername": "cobster",
"url": "https://github.com/cobster"
},
{
"name": "Arne Schubert",
"githubUsername": "atd-schubert",
"url": "https://github.com/atd-schubert"
},
{
"name": "Jeff Jacobson",
"githubUsername": "JeffJacobson",
"url": "https://github.com/JeffJacobson"
},
{
"name": "Ilia Choly",
"githubUsername": "icholy",
"url": "https://github.com/icholy"
},
{
"name": "Dan Vanderkam",
"githubUsername": "danvk",
"url": "https://github.com/danvk"
}
],
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/geojson"
},
"scripts": {},
"dependencies": {},
"peerDependencies": {},
"typesPublisherContentHash": "e7997f4827a9a92b60c7a6cb27e8f18fa760803e9dd021965e95604338b72e88",
"typeScriptVersion": "5.0"
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
+15
View File
@@ -0,0 +1,15 @@
# Installation
> `npm install --save @types/node`
# Summary
This package contains type definitions for node (https://nodejs.org/).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node.
### Additional Details
* Last updated: Thu, 27 Aug 2026 00:14:49 GMT
* Dependencies: [undici-types](https://npmjs.com/package/undici-types)
# Credits
These definitions were written by [Microsoft TypeScript](https://github.com/Microsoft), [Alberto Schiabel](https://github.com/jkomyno), [Andrew Makarov](https://github.com/r3nya), [Benjamin Toueg](https://github.com/btoueg), [David Junger](https://github.com/touffy), [Mohsen Azimi](https://github.com/mohsen1), [Nikita Galkin](https://github.com/galkin), [Sebastian Silbermann](https://github.com/eps1lon), [Wilco Bakker](https://github.com/WilcoBakker), [Marcin Kopacz](https://github.com/chyzwar), [Trivikram Kamat](https://github.com/trivikr), [Junxiao Shi](https://github.com/yoursunny), [Ilia Baryshnikov](https://github.com/qwelias), [ExE Boss](https://github.com/ExE-Boss), [Piotr Błażejewicz](https://github.com/peterblazejewicz), [Anna Henningsen](https://github.com/addaleax), [Victor Perin](https://github.com/victorperin), [NodeJS Contributors](https://github.com/NodeJS), [Linus Unnebäck](https://github.com/LinusU), [wafuwafu13](https://github.com/wafuwafu13), [Matteo Collina](https://github.com/mcollina), [Dmitry Semigradsky](https://github.com/Semigradsky), [René](https://github.com/Renegade334), and [Yagiz Nizipli](https://github.com/anonrig).
+1077
View File
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
declare module "node:assert/strict" {
import {
Assert,
AssertionError,
AssertionErrorOptions,
AssertMessageFunction,
AssertOptions,
AssertPredicate,
AssertStrict,
deepStrictEqual,
doesNotMatch,
doesNotReject,
doesNotThrow,
fail,
ifError,
match,
notDeepStrictEqual,
notStrictEqual,
ok,
partialDeepStrictEqual,
rejects,
strictEqual,
throws,
} from "node:assert";
function strict(
value: unknown,
message?: Error | AssertMessageFunction,
): asserts value;
function strict(
value: unknown,
message: string,
...args: unknown[]
): asserts value;
namespace strict {
export {
Assert,
AssertionError,
AssertionErrorOptions,
AssertOptions,
AssertPredicate,
AssertStrict,
deepStrictEqual,
deepStrictEqual as deepEqual,
doesNotMatch,
doesNotReject,
doesNotThrow,
fail,
ifError,
match,
notDeepStrictEqual,
notDeepStrictEqual as notDeepEqual,
notStrictEqual,
notStrictEqual as notEqual,
ok,
partialDeepStrictEqual,
rejects,
strict,
strictEqual,
strictEqual as equal,
throws,
};
}
export = strict;
}
declare module "assert/strict" {
import strict = require("node:assert/strict");
export = strict;
}
+711
View File
@@ -0,0 +1,711 @@
declare module "node:async_hooks" {
/**
* ```js
* import { executionAsyncId } from 'node:async_hooks';
* import fs from 'node:fs';
*
* console.log(executionAsyncId()); // 1 - bootstrap
* const path = '.';
* fs.open(path, 'r', (err, fd) => {
* console.log(executionAsyncId()); // 6 - open()
* });
* ```
*
* The ID returned from `executionAsyncId()` is related to execution timing, not
* causality (which is covered by `triggerAsyncId()`):
*
* ```js
* const server = net.createServer((conn) => {
* // Returns the ID of the server, not of the new connection, because the
* // callback runs in the execution scope of the server's MakeCallback().
* async_hooks.executionAsyncId();
*
* }).listen(port, () => {
* // Returns the ID of a TickObject (process.nextTick()) because all
* // callbacks passed to .listen() are wrapped in a nextTick().
* async_hooks.executionAsyncId();
* });
* ```
*
* Promise contexts may not get precise `executionAsyncIds` by default.
* See the section on [promise execution tracking](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#promise-execution-tracking).
* @since v8.1.0
* @return The `asyncId` of the current execution context. Useful to track when something calls.
*/
function executionAsyncId(): number;
/**
* Resource objects returned by `executionAsyncResource()` are most often internal
* Node.js handle objects with undocumented APIs. Using any functions or properties
* on the object is likely to crash your application and should be avoided.
*
* Using `executionAsyncResource()` in the top-level execution context will
* return an empty object as there is no handle or request object to use,
* but having an object representing the top-level can be helpful.
*
* ```js
* import { open } from 'node:fs';
* import { executionAsyncId, executionAsyncResource } from 'node:async_hooks';
*
* console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
* open(new URL(import.meta.url), 'r', (err, fd) => {
* console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
* });
* ```
*
* This can be used to implement continuation local storage without the
* use of a tracking `Map` to store the metadata:
*
* ```js
* import { createServer } from 'node:http';
* import {
* executionAsyncId,
* executionAsyncResource,
* createHook,
* } from 'node:async_hooks';
* const sym = Symbol('state'); // Private symbol to avoid pollution
*
* createHook({
* init(asyncId, type, triggerAsyncId, resource) {
* const cr = executionAsyncResource();
* if (cr) {
* resource[sym] = cr[sym];
* }
* },
* }).enable();
*
* const server = createServer((req, res) => {
* executionAsyncResource()[sym] = { state: req.url };
* setTimeout(function() {
* res.end(JSON.stringify(executionAsyncResource()[sym]));
* }, 100);
* }).listen(3000);
* ```
* @since v13.9.0, v12.17.0
* @return The resource representing the current execution. Useful to store data within the resource.
*/
function executionAsyncResource(): object;
/**
* ```js
* const server = net.createServer((conn) => {
* // The resource that caused (or triggered) this callback to be called
* // was that of the new connection. Thus the return value of triggerAsyncId()
* // is the asyncId of "conn".
* async_hooks.triggerAsyncId();
*
* }).listen(port, () => {
* // Even though all callbacks passed to .listen() are wrapped in a nextTick()
* // the callback itself exists because the call to the server's .listen()
* // was made. So the return value would be the ID of the server.
* async_hooks.triggerAsyncId();
* });
* ```
*
* Promise contexts may not get valid `triggerAsyncId`s by default. See
* the section on [promise execution tracking](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#promise-execution-tracking).
* @return The ID of the resource responsible for calling the callback that is currently being executed.
*/
function triggerAsyncId(): number;
interface HookCallbacks {
/**
* The [`init` callback](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#initasyncid-type-triggerasyncid-resource).
*/
init?(asyncId: number, type: string, triggerAsyncId: number, resource: object): void;
/**
* The [`before` callback](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#beforeasyncid).
*/
before?(asyncId: number): void;
/**
* The [`after` callback](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#afterasyncid).
*/
after?(asyncId: number): void;
/**
* The [`promiseResolve` callback](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#promiseresolveasyncid).
*/
promiseResolve?(asyncId: number): void;
/**
* The [`destroy` callback](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#destroyasyncid).
*/
destroy?(asyncId: number): void;
/**
* Whether the hook should track `Promise`s. Cannot be `false` if
* `promiseResolve` is set.
* @default true
*/
trackPromises?: boolean | undefined;
}
interface AsyncHook {
/**
* Enable the callbacks for a given AsyncHook instance. If no callbacks are provided enabling is a noop.
*/
enable(): this;
/**
* Disable the callbacks for a given AsyncHook instance from the global pool of AsyncHook callbacks to be executed. Once a hook has been disabled it will not be called again until enabled.
*/
disable(): this;
}
/**
* Registers functions to be called for different lifetime events of each async
* operation.
*
* The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
* respective asynchronous event during a resource's lifetime.
*
* All callbacks are optional. For example, if only resource cleanup needs to
* be tracked, then only the `destroy` callback needs to be passed. The
* specifics of all functions that can be passed to `callbacks` is in the
* [Hook Callbacks](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#hook-callbacks) section.
*
* ```js
* import { createHook } from 'node:async_hooks';
*
* const asyncHook = createHook({
* init(asyncId, type, triggerAsyncId, resource) { },
* destroy(asyncId) { },
* });
* ```
*
* The callbacks will be inherited via the prototype chain:
*
* ```js
* class MyAsyncCallbacks {
* init(asyncId, type, triggerAsyncId, resource) { }
* destroy(asyncId) {}
* }
*
* class MyAddedCallbacks extends MyAsyncCallbacks {
* before(asyncId) { }
* after(asyncId) { }
* }
*
* const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
* ```
*
* Because promises are asynchronous resources whose lifecycle is tracked
* via the async hooks mechanism, the `init()`, `before()`, `after()`, and
* `destroy()` callbacks _must not_ be async functions that return promises.
* @since v8.1.0
* @param options The [Hook Callbacks](https://nodejs.org/docs/latest-v26.x/api/async_hooks.html#hook-callbacks) to register
* @returns Instance used for disabling and enabling hooks
*/
function createHook(options: HookCallbacks): AsyncHook;
interface AsyncResourceOptions {
/**
* The ID of the execution context that created this async event.
* @default executionAsyncId()
*/
triggerAsyncId?: number | undefined;
/**
* Disables automatic `emitDestroy` when the object is garbage collected.
* This usually does not need to be set (even if `emitDestroy` is called
* manually), unless the resource's `asyncId` is retrieved and the
* sensitive API's `emitDestroy` is called with it.
* @default false
*/
requireManualDestroy?: boolean | undefined;
}
/**
* The class `AsyncResource` is designed to be extended by the embedder's async
* resources. Using this, users can easily trigger the lifetime events of their
* own resources.
*
* The `init` hook will trigger when an `AsyncResource` is instantiated.
*
* The following is an overview of the `AsyncResource` API.
*
* ```js
* import { AsyncResource, executionAsyncId } from 'node:async_hooks';
*
* // AsyncResource() is meant to be extended. Instantiating a
* // new AsyncResource() also triggers init. If triggerAsyncId is omitted then
* // async_hook.executionAsyncId() is used.
* const asyncResource = new AsyncResource(
* type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false },
* );
*
* // Run a function in the execution context of the resource. This will
* // * establish the context of the resource
* // * trigger the AsyncHooks before callbacks
* // * call the provided function `fn` with the supplied arguments
* // * trigger the AsyncHooks after callbacks
* // * restore the original execution context
* asyncResource.runInAsyncScope(fn, thisArg, ...args);
*
* // Call AsyncHooks destroy callbacks.
* asyncResource.emitDestroy();
*
* // Return the unique ID assigned to the AsyncResource instance.
* asyncResource.asyncId();
*
* // Return the trigger ID for the AsyncResource instance.
* asyncResource.triggerAsyncId();
* ```
*/
class AsyncResource {
/**
* AsyncResource() is meant to be extended. Instantiating a
* new AsyncResource() also triggers init. If triggerAsyncId is omitted then
* async_hook.executionAsyncId() is used.
* @param type The type of async event.
* @param triggerAsyncId The ID of the execution context that created
* this async event (default: `executionAsyncId()`), or an
* AsyncResourceOptions object (since v9.3.0)
*/
constructor(type: string, triggerAsyncId?: number | AsyncResourceOptions);
/**
* Binds the given function to the current execution context.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current execution context.
* @param type An optional name to associate with the underlying `AsyncResource`.
*/
static bind<Func extends (this: ThisArg, ...args: any[]) => any, ThisArg>(
fn: Func,
type?: string,
thisArg?: ThisArg,
): Func;
/**
* Binds the given function to execute to this `AsyncResource`'s scope.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current `AsyncResource`.
*/
bind<Func extends (...args: any[]) => any>(fn: Func): Func;
/**
* Call the provided function with the provided arguments in the execution context
* of the async resource. This will establish the context, trigger the AsyncHooks
* before callbacks, call the function, trigger the AsyncHooks after callbacks, and
* then restore the original execution context.
* @since v9.6.0
* @param fn The function to call in the execution context of this async resource.
* @param thisArg The receiver to be used for the function call.
* @param args Optional arguments to pass to the function.
*/
runInAsyncScope<This, Result>(
fn: (this: This, ...args: any[]) => Result,
thisArg?: This,
...args: any[]
): Result;
/**
* Call all `destroy` hooks. This should only ever be called once. An error will
* be thrown if it is called more than once. This **must** be manually called. If
* the resource is left to be collected by the GC then the `destroy` hooks will
* never be called.
* @return A reference to `asyncResource`.
*/
emitDestroy(): this;
/**
* @return The unique `asyncId` assigned to the resource.
*/
asyncId(): number;
/**
* @return The same `triggerAsyncId` that is passed to the `AsyncResource` constructor.
*/
triggerAsyncId(): number;
}
interface AsyncLocalStorageOptions {
/**
* The default value to be used when no store is provided.
*/
defaultValue?: any;
/**
* A name for the `AsyncLocalStorage` value.
*/
name?: string | undefined;
}
/**
* This class creates stores that stay coherent through asynchronous operations.
*
* While you can create your own implementation on top of the `node:async_hooks` module, `AsyncLocalStorage` should be preferred as it is a performant and memory
* safe implementation that involves significant optimizations that are non-obvious
* to implement.
*
* The following example uses `AsyncLocalStorage` to build a simple logger
* that assigns IDs to incoming HTTP requests and includes them in messages
* logged within each request.
*
* ```js
* import http from 'node:http';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* function logWithId(msg) {
* const id = asyncLocalStorage.getStore();
* console.log(`${id !== undefined ? id : '-'}:`, msg);
* }
*
* let idSeq = 0;
* http.createServer((req, res) => {
* asyncLocalStorage.run(idSeq++, () => {
* logWithId('start');
* // Imagine any chain of async operations here
* setImmediate(() => {
* logWithId('finish');
* res.end();
* });
* });
* }).listen(8080);
*
* http.get('http://localhost:8080');
* http.get('http://localhost:8080');
* // Prints:
* // 0: start
* // 0: finish
* // 1: start
* // 1: finish
* ```
*
* Each instance of `AsyncLocalStorage` maintains an independent storage context.
* Multiple instances can safely exist simultaneously without risk of interfering
* with each other's data.
* @since v13.10.0, v12.17.0
*/
class AsyncLocalStorage<T> {
/**
* Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
* `run()` call or after an `enterWith()` call.
*/
constructor(options?: AsyncLocalStorageOptions);
/**
* Binds the given function to the current execution context.
* @since v19.8.0
* @param fn The function to bind to the current execution context.
* @return A new function that calls `fn` within the captured execution context.
*/
static bind<Func extends (...args: any[]) => any>(fn: Func): Func;
/**
* Captures the current execution context and returns a function that accepts a
* function as an argument. Whenever the returned function is called, it
* calls the function passed to it within the captured context.
*
* ```js
* const asyncLocalStorage = new AsyncLocalStorage();
* const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot());
* const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore()));
* console.log(result); // returns 123
* ```
*
* AsyncLocalStorage.snapshot() can replace the use of AsyncResource for simple
* async context tracking purposes, for example:
*
* ```js
* class Foo {
* #runInAsyncScope = AsyncLocalStorage.snapshot();
*
* get() { return this.#runInAsyncScope(() => asyncLocalStorage.getStore()); }
* }
*
* const foo = asyncLocalStorage.run(123, () => new Foo());
* console.log(asyncLocalStorage.run(321, () => foo.get())); // returns 123
* ```
* @since v19.8.0
* @return A new function with the signature `(fn: (...args) : R, ...args) : R`.
*/
static snapshot(): <R, TArgs extends any[]>(fn: (...args: TArgs) => R, ...args: TArgs) => R;
/**
* Disables the instance of `AsyncLocalStorage`. All subsequent calls
* to `asyncLocalStorage.getStore()` will return `undefined` until `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
*
* When calling `asyncLocalStorage.disable()`, all current contexts linked to the
* instance will be exited.
*
* Calling `asyncLocalStorage.disable()` is required before the `asyncLocalStorage` can be garbage collected. This does not apply to stores
* provided by the `asyncLocalStorage`, as those objects are garbage collected
* along with the corresponding async resources.
*
* Use this method when the `asyncLocalStorage` is not in use anymore
* in the current process.
* @since v13.10.0, v12.17.0
* @experimental
*/
disable(): void;
/**
* Returns the current store.
* If called outside of an asynchronous context initialized by
* calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
* returns `undefined`.
* @since v13.10.0, v12.17.0
*/
getStore(): T | undefined;
/**
* The name of the `AsyncLocalStorage` instance if provided.
* @since v24.0.0
*/
readonly name: string;
/**
* Runs a function synchronously within a context and returns its
* return value. The store is not accessible outside of the callback function.
* The store is accessible to any asynchronous operations created within the
* callback.
*
* The optional `args` are passed to the callback function.
*
* If the callback function throws an error, the error is thrown by `run()` too.
* The stacktrace is not impacted by this call and the context is exited.
*
* Example:
*
* ```js
* const store = { id: 2 };
* try {
* asyncLocalStorage.run(store, () => {
* asyncLocalStorage.getStore(); // Returns the store object
* setTimeout(() => {
* asyncLocalStorage.getStore(); // Returns the store object
* }, 200);
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns undefined
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
*/
run<R>(store: T, callback: () => R): R;
run<R, TArgs extends any[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* Runs a function synchronously outside of a context and returns its
* return value. The store is not accessible within the callback function or
* the asynchronous operations created within the callback. Any `getStore()` call done within the callback function will always return `undefined`.
*
* The optional `args` are passed to the callback function.
*
* If the callback function throws an error, the error is thrown by `exit()` too.
* The stacktrace is not impacted by this call and the context is re-entered.
*
* Example:
*
* ```js
* // Within a call to run
* try {
* asyncLocalStorage.getStore(); // Returns the store object or value
* asyncLocalStorage.exit(() => {
* asyncLocalStorage.getStore(); // Returns undefined
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns the same object or value
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
* @experimental
*/
exit<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* Creates a disposable scope that enters the given store and automatically
* restores the previous store value when the scope is disposed. This method is
* designed to work with JavaScript's explicit resource management (`using` syntax).
*
* Example:
*
* ```js
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* {
* using _ = asyncLocalStorage.withScope('my-store');
* console.log(asyncLocalStorage.getStore()); // Prints: my-store
* }
*
* console.log(asyncLocalStorage.getStore()); // Prints: undefined
* ```
*
* The `withScope()` method is particularly useful for managing context in
* synchronous code where you want to ensure the previous store value is restored
* when exiting a block, even if an error is thrown.
*
* ```js
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* try {
* using _ = asyncLocalStorage.withScope('my-store');
* console.log(asyncLocalStorage.getStore()); // Prints: my-store
* throw new Error('test');
* } catch (e) {
* // Store is automatically restored even after error
* console.log(asyncLocalStorage.getStore()); // Prints: undefined
* }
* ```
*
* **Important:** When using `withScope()` in async functions before the first
* `await`, be aware that the scope change will affect the caller's context. The
* synchronous portion of an async function (before the first `await`) runs
* immediately when called, and when it reaches the first `await`, it returns the
* promise to the caller. At that point, the scope change becomes visible in the
* caller's context and will persist in subsequent synchronous code until something
* else changes the scope value. For async operations, prefer using `run()` which
* properly isolates context across async boundaries.
*
* ```js
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* async function example() {
* using _ = asyncLocalStorage.withScope('my-store');
* console.log(asyncLocalStorage.getStore()); // Prints: my-store
* await someAsyncOperation(); // Function pauses here and returns promise
* console.log(asyncLocalStorage.getStore()); // Prints: my-store
* }
*
* // Calling without await
* example(); // Synchronous portion runs, then pauses at first await
* // After the promise is returned, the scope 'my-store' is now active in caller!
* console.log(asyncLocalStorage.getStore()); // Prints: my-store (unexpected!)
* ```
* @since v25.9.0
* @experimental
*/
withScope(store: T): RunScope;
/**
* Transitions into the context for the remainder of the current
* synchronous execution and then persists the store through any following
* asynchronous calls.
*
* Example:
*
* ```js
* const store = { id: 1 };
* // Replaces previous store with the given store object
* asyncLocalStorage.enterWith(store);
* asyncLocalStorage.getStore(); // Returns the store object
* someAsyncOperation(() => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
* ```
*
* This transition will continue for the _entire_ synchronous execution.
* This means that if, for example, the context is entered within an event
* handler subsequent event handlers will also run within that context unless
* specifically bound to another context with an `AsyncResource`. That is why `run()` should be preferred over `enterWith()` unless there are strong reasons
* to use the latter method.
*
* ```js
* const store = { id: 1 };
*
* emitter.on('my-event', () => {
* asyncLocalStorage.enterWith(store);
* });
* emitter.on('my-event', () => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
*
* asyncLocalStorage.getStore(); // Returns undefined
* emitter.emit('my-event');
* asyncLocalStorage.getStore(); // Returns the same object
* ```
* @since v13.11.0, v12.17.0
* @experimental
*/
enterWith(store: T): void;
}
/**
* A disposable scope returned by `asyncLocalStorage.withScope()` that
* automatically restores the previous store value when disposed. This class
* implements the [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management) protocol and is designed to work
* with JavaScript's `using` syntax.
*
* The scope automatically restores the previous store value when the `using` block
* exits, whether through normal completion or by throwing an error.
* @since v25.9.0
* @experimental
*/
interface RunScope extends Disposable {
/**
* Explicitly ends the scope and restores the previous store value. This method
* is idempotent: calling it multiple times has the same effect as calling it once.
*
* The `[Symbol.dispose]()` method defers to `dispose()`.
*
* If `withScope()` is called without the `using` keyword, `dispose()` must be
* called manually to restore the previous store value. Forgetting to call
* `dispose()` will cause the store value to persist for the remainder of the
* current execution context:
*
* ```js
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const storage = new AsyncLocalStorage();
*
* // Without using, the scope must be disposed manually
* const scope = storage.withScope('my-store');
* // storage.getStore() === 'my-store' here
*
* scope.dispose(); // Restore previous value
* // storage.getStore() === undefined here
* ```
* @since v25.9.0
*/
dispose(): void;
}
/**
* @since v17.2.0, v16.14.0
* @return A map of provider types to the corresponding numeric id.
* This map contains all the event types that might be emitted by the `async_hooks.init()` event.
*/
namespace asyncWrapProviders {
const NONE: number;
const DIRHANDLE: number;
const DNSCHANNEL: number;
const ELDHISTOGRAM: number;
const FILEHANDLE: number;
const FILEHANDLECLOSEREQ: number;
const FIXEDSIZEBLOBCOPY: number;
const FSEVENTWRAP: number;
const FSREQCALLBACK: number;
const FSREQPROMISE: number;
const GETADDRINFOREQWRAP: number;
const GETNAMEINFOREQWRAP: number;
const HEAPSNAPSHOT: number;
const HTTP2SESSION: number;
const HTTP2STREAM: number;
const HTTP2PING: number;
const HTTP2SETTINGS: number;
const HTTPINCOMINGMESSAGE: number;
const HTTPCLIENTREQUEST: number;
const JSSTREAM: number;
const JSUDPWRAP: number;
const MESSAGEPORT: number;
const PIPECONNECTWRAP: number;
const PIPESERVERWRAP: number;
const PIPEWRAP: number;
const PROCESSWRAP: number;
const PROMISE: number;
const QUERYWRAP: number;
const SHUTDOWNWRAP: number;
const SIGNALWRAP: number;
const STATWATCHER: number;
const STREAMPIPE: number;
const TCPCONNECTWRAP: number;
const TCPSERVERWRAP: number;
const TCPWRAP: number;
const TTYWRAP: number;
const UDPSENDWRAP: number;
const UDPWRAP: number;
const SIGINTWATCHDOG: number;
const WORKER: number;
const WORKERHEAPSNAPSHOT: number;
const WRITEWRAP: number;
const ZLIB: number;
const CHECKPRIMEREQUEST: number;
const PBKDF2REQUEST: number;
const KEYPAIRGENREQUEST: number;
const KEYGENREQUEST: number;
const KEYEXPORTREQUEST: number;
const CIPHERREQUEST: number;
const DERIVEBITSREQUEST: number;
const HASHREQUEST: number;
const RANDOMBYTESREQUEST: number;
const RANDOMPRIMEREQUEST: number;
const SCRYPTREQUEST: number;
const SIGNREQUEST: number;
const TLSWRAP: number;
const VERIFYREQUEST: number;
}
}
declare module "async_hooks" {
export * from "node:async_hooks";
}
+471
View File
@@ -0,0 +1,471 @@
declare module "node:buffer" {
type ImplicitArrayBuffer<T extends WithImplicitCoercion<ArrayBufferLike>> = T extends
{ valueOf(): infer V extends ArrayBufferLike } ? V : T;
global {
interface BufferConstructor {
// see buffer.d.ts for implementation shared with all TypeScript versions
/**
* Allocates a new buffer containing the given {str}.
*
* @param str String to store in buffer.
* @param encoding encoding to use, optional. Default is 'utf8'
* @deprecated since v10.0.0 - Use `Buffer.from(string[, encoding])` instead.
*/
new(str: string, encoding?: BufferEncoding): Buffer<ArrayBuffer>;
/**
* Allocates a new buffer of {size} octets.
*
* @param size count of octets to allocate.
* @deprecated since v10.0.0 - Use `Buffer.alloc()` instead (also see `Buffer.allocUnsafe()`).
*/
new(size: number): Buffer<ArrayBuffer>;
/**
* Allocates a new buffer containing the given {array} of octets.
*
* @param array The octets to store.
* @deprecated since v10.0.0 - Use `Buffer.from(array)` instead.
*/
new(array: ArrayLike<number>): Buffer<ArrayBuffer>;
/**
* Produces a Buffer backed by the same allocated memory as
* the given {ArrayBuffer}/{SharedArrayBuffer}.
*
* @param arrayBuffer The ArrayBuffer with which to share memory.
* @deprecated since v10.0.0 - Use `Buffer.from(arrayBuffer[, byteOffset[, length]])` instead.
*/
new<TArrayBuffer extends ArrayBufferLike = ArrayBuffer>(arrayBuffer: TArrayBuffer): Buffer<TArrayBuffer>;
/**
* Allocates a new `Buffer` using an `array` of bytes in the range `0` `255`.
* Array entries outside that range will be truncated to fit into it.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* // Creates a new Buffer containing the UTF-8 bytes of the string 'buffer'.
* const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
* ```
*
* If `array` is an `Array`-like object (that is, one with a `length` property of
* type `number`), it is treated as if it is an array, unless it is a `Buffer` or
* a `Uint8Array`. This means all other `TypedArray` variants get treated as an
* `Array`. To create a `Buffer` from the bytes backing a `TypedArray`, use
* `Buffer.copyBytesFrom()`.
*
* A `TypeError` will be thrown if `array` is not an `Array` or another type
* appropriate for `Buffer.from()` variants.
*
* `Buffer.from(array)` and `Buffer.from(string)` may also use the internal
* `Buffer` pool like `Buffer.allocUnsafe()` does.
* @since v5.10.0
*/
from(array: WithImplicitCoercion<ArrayLike<number>>): Buffer<ArrayBuffer>;
/**
* This creates a view of the `ArrayBuffer` without copying the underlying
* memory. For example, when passed a reference to the `.buffer` property of a
* `TypedArray` instance, the newly created `Buffer` will share the same
* allocated memory as the `TypedArray`'s underlying `ArrayBuffer`.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const arr = new Uint16Array(2);
*
* arr[0] = 5000;
* arr[1] = 4000;
*
* // Shares memory with `arr`.
* const buf = Buffer.from(arr.buffer);
*
* console.log(buf);
* // Prints: <Buffer 88 13 a0 0f>
*
* // Changing the original Uint16Array changes the Buffer also.
* arr[1] = 6000;
*
* console.log(buf);
* // Prints: <Buffer 88 13 70 17>
* ```
*
* The optional `byteOffset` and `length` arguments specify a memory range within
* the `arrayBuffer` that will be shared by the `Buffer`.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const ab = new ArrayBuffer(10);
* const buf = Buffer.from(ab, 0, 2);
*
* console.log(buf.length);
* // Prints: 2
* ```
*
* A `TypeError` will be thrown if `arrayBuffer` is not an `ArrayBuffer` or a
* `SharedArrayBuffer` or another type appropriate for `Buffer.from()`
* variants.
*
* It is important to remember that a backing `ArrayBuffer` can cover a range
* of memory that extends beyond the bounds of a `TypedArray` view. A new
* `Buffer` created using the `buffer` property of a `TypedArray` may extend
* beyond the range of the `TypedArray`:
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const arrA = Uint8Array.from([0x63, 0x64, 0x65, 0x66]); // 4 elements
* const arrB = new Uint8Array(arrA.buffer, 1, 2); // 2 elements
* console.log(arrA.buffer === arrB.buffer); // true
*
* const buf = Buffer.from(arrB.buffer);
* console.log(buf);
* // Prints: <Buffer 63 64 65 66>
* ```
* @since v5.10.0
* @param arrayBuffer An `ArrayBuffer`, `SharedArrayBuffer`, for example the
* `.buffer` property of a `TypedArray`.
* @param byteOffset Index of first byte to expose. **Default:** `0`.
* @param length Number of bytes to expose. **Default:**
* `arrayBuffer.byteLength - byteOffset`.
*/
from<TArrayBuffer extends WithImplicitCoercion<ArrayBufferLike>>(
arrayBuffer: TArrayBuffer,
byteOffset?: number,
length?: number,
): Buffer<ImplicitArrayBuffer<TArrayBuffer>>;
/**
* Creates a new `Buffer` containing `string`. The `encoding` parameter identifies
* the character encoding to be used when converting `string` into bytes.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf1 = Buffer.from('this is a tést');
* const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex');
*
* console.log(buf1.toString());
* // Prints: this is a tést
* console.log(buf2.toString());
* // Prints: this is a tést
* console.log(buf1.toString('latin1'));
* // Prints: this is a tést
* ```
*
* A `TypeError` will be thrown if `string` is not a string or another type
* appropriate for `Buffer.from()` variants.
*
* `Buffer.from(string)` may also use the internal `Buffer` pool like
* `Buffer.allocUnsafe()` does.
* @since v5.10.0
* @param string A string to encode.
* @param encoding The encoding of `string`. **Default:** `'utf8'`.
*/
from(string: WithImplicitCoercion<string>, encoding?: BufferEncoding): Buffer<ArrayBuffer>;
from(arrayOrString: WithImplicitCoercion<ArrayLike<number> | string>): Buffer<ArrayBuffer>;
/**
* Creates a new Buffer using the passed {data}
* @param values to create a new Buffer
*/
of(...items: number[]): Buffer<ArrayBuffer>;
/**
* Returns a new `Buffer` which is the result of concatenating all the `Buffer` instances in the `list` together.
*
* If the list has no items, or if the `totalLength` is 0, then a new zero-length `Buffer` is returned.
*
* If `totalLength` is not provided, it is calculated from the `Buffer` instances
* in `list` by adding their lengths.
*
* If `totalLength` is provided, it must be an unsigned integer. If the
* combined length of the `Buffer`s in `list` exceeds `totalLength`, the result is
* truncated to `totalLength`. If the combined length of the `Buffer`s in `list` is
* less than `totalLength`, the remaining space is filled with zeros.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* // Create a single `Buffer` from a list of three `Buffer` instances.
*
* const buf1 = Buffer.alloc(10);
* const buf2 = Buffer.alloc(14);
* const buf3 = Buffer.alloc(18);
* const totalLength = buf1.length + buf2.length + buf3.length;
*
* console.log(totalLength);
* // Prints: 42
*
* const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
*
* console.log(bufA);
* // Prints: <Buffer 00 00 00 00 ...>
* console.log(bufA.length);
* // Prints: 42
* ```
*
* `Buffer.concat()` may also use the internal `Buffer` pool like `Buffer.allocUnsafe()` does.
* @since v0.7.11
* @param list List of `Buffer` or {@link Uint8Array} instances to concatenate.
* @param totalLength Total length of the `Buffer` instances in `list` when concatenated.
*/
concat(list: readonly Uint8Array[], totalLength?: number): Buffer<ArrayBuffer>;
/**
* Copies the underlying memory of `view` into a new `Buffer`.
*
* ```js
* const u16 = new Uint16Array([0, 0xffff]);
* const buf = Buffer.copyBytesFrom(u16, 1, 1);
* u16[1] = 0;
* console.log(buf.length); // 2
* console.log(buf[0]); // 255
* console.log(buf[1]); // 255
* ```
* @since v19.8.0
* @param view The {TypedArray} to copy.
* @param [offset=0] The starting offset within `view`.
* @param [length=view.length - offset] The number of elements from `view` to copy.
*/
copyBytesFrom(view: NodeJS.TypedArray, offset?: number, length?: number): Buffer<ArrayBuffer>;
/**
* Allocates a new `Buffer` of `size` bytes. If `fill` is `undefined`, the`Buffer` will be zero-filled.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.alloc(5);
*
* console.log(buf);
* // Prints: <Buffer 00 00 00 00 00>
* ```
*
* If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_OUT_OF_RANGE` is thrown.
*
* If `fill` is specified, the allocated `Buffer` will be initialized by calling `buf.fill(fill)`.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.alloc(5, 'a');
*
* console.log(buf);
* // Prints: <Buffer 61 61 61 61 61>
* ```
*
* If both `fill` and `encoding` are specified, the allocated `Buffer` will be
* initialized by calling `buf.fill(fill, encoding)`.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
*
* console.log(buf);
* // Prints: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
* ```
*
* Calling `Buffer.alloc()` can be measurably slower than the alternative `Buffer.allocUnsafe()` but ensures that the newly created `Buffer` instance
* contents will never contain sensitive data from previous allocations, including
* data that might not have been allocated for `Buffer`s.
*
* A `TypeError` will be thrown if `size` is not a number.
* @since v5.10.0
* @param size The desired length of the new `Buffer`.
* @param [fill=0] A value to pre-fill the new `Buffer` with.
* @param [encoding='utf8'] If `fill` is a string, this is its encoding.
*/
alloc(size: number, fill?: string | Uint8Array | number, encoding?: BufferEncoding): Buffer<ArrayBuffer>;
/**
* Allocates a new `Buffer` of `size` bytes. If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_OUT_OF_RANGE` is thrown.
*
* The underlying memory for `Buffer` instances created in this way is _not_
* _initialized_. The contents of the newly created `Buffer` are unknown and _may contain sensitive data_. Use `Buffer.alloc()` instead to initialize`Buffer` instances with zeroes.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.allocUnsafe(10);
*
* console.log(buf);
* // Prints (contents may vary): <Buffer a0 8b 28 3f 01 00 00 00 50 32>
*
* buf.fill(0);
*
* console.log(buf);
* // Prints: <Buffer 00 00 00 00 00 00 00 00 00 00>
* ```
*
* A `TypeError` will be thrown if `size` is not a number.
*
* The `Buffer` module pre-allocates an internal `Buffer` instance of
* size `Buffer.poolSize` that is used as a pool for the fast allocation of new `Buffer` instances created using `Buffer.allocUnsafe()`, `Buffer.from(array)`,
* and `Buffer.concat()` only when `size` is less than `Buffer.poolSize >>> 1` (floor of `Buffer.poolSize` divided by two).
*
* Use of this pre-allocated internal memory pool is a key difference between
* calling `Buffer.alloc(size, fill)` vs. `Buffer.allocUnsafe(size).fill(fill)`.
* Specifically, `Buffer.alloc(size, fill)` will _never_ use the internal `Buffer`pool, while `Buffer.allocUnsafe(size).fill(fill)`_will_ use the internal`Buffer` pool if `size` is less
* than or equal to half `Buffer.poolSize`. The
* difference is subtle but can be important when an application requires the
* additional performance that `Buffer.allocUnsafe()` provides.
* @since v5.10.0
* @param size The desired length of the new `Buffer`.
*/
allocUnsafe(size: number): Buffer<ArrayBuffer>;
/**
* Allocates a new `Buffer` of `size` bytes. If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_OUT_OF_RANGE` is thrown. A zero-length `Buffer` is created if
* `size` is 0.
*
* The underlying memory for `Buffer` instances created in this way is _not_
* _initialized_. The contents of the newly created `Buffer` are unknown and _may contain sensitive data_. Use `buf.fill(0)` to initialize
* such `Buffer` instances with zeroes.
*
* When using `Buffer.allocUnsafe()` to allocate new `Buffer` instances,
* allocations less than `Buffer.poolSize >>> 1` (32KiB when default poolSize is used) are sliced
* from a single pre-allocated `Buffer`. This allows applications to avoid the
* garbage collection overhead of creating many individually allocated `Buffer`
* instances. This approach improves both performance and memory usage by
* eliminating the need to track and clean up as many individual `ArrayBuffer` objects.
*
* However, in the case where a developer may need to retain a small chunk of
* memory from a pool for an indeterminate amount of time, it may be appropriate
* to create an un-pooled `Buffer` instance using `Buffer.allocUnsafeSlow()` and
* then copying out the relevant bits.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* // Need to keep around a few small chunks of memory.
* const store = [];
*
* socket.on('readable', () => {
* let data;
* while (null !== (data = readable.read())) {
* // Allocate for retained data.
* const sb = Buffer.allocUnsafeSlow(10);
*
* // Copy the data into the new allocation.
* data.copy(sb, 0, 0, 10);
*
* store.push(sb);
* }
* });
* ```
*
* A `TypeError` will be thrown if `size` is not a number.
* @since v5.12.0
* @param size The desired length of the new `Buffer`.
*/
allocUnsafeSlow(size: number): Buffer<ArrayBuffer>;
}
interface Buffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> extends Uint8Array<TArrayBuffer> {
// see buffer.d.ts for implementation shared with all TypeScript versions
/**
* Returns a new `Buffer` that references the same memory as the original, but
* offset and cropped by the `start` and `end` indices.
*
* This method is not compatible with the `Uint8Array.prototype.slice()`,
* which is a superclass of `Buffer`. To copy the slice, use`Uint8Array.prototype.slice()`.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.from('buffer');
*
* const copiedBuf = Uint8Array.prototype.slice.call(buf);
* copiedBuf[0]++;
* console.log(copiedBuf.toString());
* // Prints: cuffer
*
* console.log(buf.toString());
* // Prints: buffer
*
* // With buf.slice(), the original buffer is modified.
* const notReallyCopiedBuf = buf.slice();
* notReallyCopiedBuf[0]++;
* console.log(notReallyCopiedBuf.toString());
* // Prints: cuffer
* console.log(buf.toString());
* // Also prints: cuffer (!)
* ```
* @since v0.3.0
* @deprecated Use `subarray` instead.
* @param [start=0] Where the new `Buffer` will start.
* @param [end=buf.length] Where the new `Buffer` will end (not inclusive).
*/
slice(start?: number, end?: number): Buffer<ArrayBuffer>;
/**
* Returns a new `Buffer` that references the same memory as the original, but
* offset and cropped by the `start` and `end` indices.
*
* Specifying `end` greater than `buf.length` will return the same result as
* that of `end` equal to `buf.length`.
*
* This method is inherited from [`TypedArray.prototype.subarray()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray).
*
* Modifying the new `Buffer` slice will modify the memory in the original `Buffer`because the allocated memory of the two objects overlap.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* // Create a `Buffer` with the ASCII alphabet, take a slice, and modify one byte
* // from the original `Buffer`.
*
* const buf1 = Buffer.allocUnsafe(26);
*
* for (let i = 0; i < 26; i++) {
* // 97 is the decimal ASCII value for 'a'.
* buf1[i] = i + 97;
* }
*
* const buf2 = buf1.subarray(0, 3);
*
* console.log(buf2.toString('ascii', 0, buf2.length));
* // Prints: abc
*
* buf1[0] = 33;
*
* console.log(buf2.toString('ascii', 0, buf2.length));
* // Prints: !bc
* ```
*
* Specifying negative indexes causes the slice to be generated relative to the
* end of `buf` rather than the beginning.
*
* ```js
* import { Buffer } from 'node:buffer';
*
* const buf = Buffer.from('buffer');
*
* console.log(buf.subarray(-6, -1).toString());
* // Prints: buffe
* // (Equivalent to buf.subarray(0, 5).)
*
* console.log(buf.subarray(-6, -2).toString());
* // Prints: buff
* // (Equivalent to buf.subarray(0, 4).)
*
* console.log(buf.subarray(-5, -2).toString());
* // Prints: uff
* // (Equivalent to buf.subarray(1, 4).)
* ```
* @since v3.0.0
* @param [start=0] Where the new `Buffer` will start.
* @param [end=buf.length] Where the new `Buffer` will end (not inclusive).
*/
subarray(start?: number, end?: number): Buffer<TArrayBuffer>;
}
// TODO: remove globals in future version
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedBuffer = Buffer<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type AllowSharedBuffer = Buffer<ArrayBufferLike>;
}
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type BufferView<T extends NodeJS.ArrayBufferView> = T extends NodeJS.ArrayBufferView<infer B> ? Buffer<B> : never;
}
+1799
View File
File diff suppressed because it is too large Load Diff
+1361
View File
File diff suppressed because it is too large Load Diff
+432
View File
@@ -0,0 +1,432 @@
declare module "node:cluster" {
import * as child_process from "node:child_process";
import { EventEmitter, InternalEventEmitter } from "node:events";
class Worker implements EventEmitter {
constructor(options?: cluster.WorkerOptions);
/**
* Each new worker is given its own unique id, this id is stored in the `id`.
*
* While a worker is alive, this is the key that indexes it in `cluster.workers`.
* @since v0.8.0
*/
id: number;
/**
* All workers are created using [`child_process.fork()`](https://nodejs.org/docs/latest-v26.x/api/child_process.html#child_processforkmodulepath-args-options), the returned object
* from this function is stored as `.process`. In a worker, the global `process` is stored.
*
* See: [Child Process module](https://nodejs.org/docs/latest-v26.x/api/child_process.html#child_processforkmodulepath-args-options).
*
* Workers will call `process.exit(0)` if the `'disconnect'` event occurs
* on `process` and `.exitedAfterDisconnect` is not `true`. This protects against
* accidental disconnection.
* @since v0.7.0
*/
process: child_process.ChildProcess;
/**
* Send a message to a worker or primary, optionally with a handle.
*
* In the primary, this sends a message to a specific worker. It is identical to [`ChildProcess.send()`](https://nodejs.org/docs/latest-v26.x/api/child_process.html#subprocesssendmessage-sendhandle-options-callback).
*
* In a worker, this sends a message to the primary. It is identical to `process.send()`.
*
* This example will echo back all messages from the primary:
*
* ```js
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* worker.send('hi there');
*
* } else if (cluster.isWorker) {
* process.on('message', (msg) => {
* process.send(msg);
* });
* }
* ```
* @since v0.7.0
* @param options The `options` argument, if present, is an object used to parameterize the sending of certain types of handles.
*/
send(message: child_process.Serializable, callback?: (error: Error | null) => void): boolean;
send(
message: child_process.Serializable,
sendHandle: child_process.SendHandle,
callback?: (error: Error | null) => void,
): boolean;
send(
message: child_process.Serializable,
sendHandle: child_process.SendHandle,
options?: child_process.MessageOptions,
callback?: (error: Error | null) => void,
): boolean;
/**
* This function will kill the worker. In the primary worker, it does this by
* disconnecting the `worker.process`, and once disconnected, killing with `signal`. In the worker, it does it by killing the process with `signal`.
*
* The `kill()` function kills the worker process without waiting for a graceful
* disconnect, it has the same behavior as `worker.process.kill()`.
*
* This method is aliased as `worker.destroy()` for backwards compatibility.
*
* In a worker, `process.kill()` exists, but it is not this function;
* it is [`kill()`](https://nodejs.org/docs/latest-v26.x/api/process.html#processkillpid-signal).
* @since v0.9.12
* @param [signal='SIGTERM'] Name of the kill signal to send to the worker process.
*/
kill(signal?: string): void;
destroy(signal?: string): void;
/**
* In a worker, this function will close all servers, wait for the `'close'` event
* on those servers, and then disconnect the IPC channel.
*
* In the primary, an internal message is sent to the worker causing it to call `.disconnect()` on itself.
*
* Causes `.exitedAfterDisconnect` to be set.
*
* After a server is closed, it will no longer accept new connections,
* but connections may be accepted by any other listening worker. Existing
* connections will be allowed to close as usual. When no more connections exist,
* see `server.close()`, the IPC channel to the worker will close allowing it
* to die gracefully.
*
* The above applies _only_ to server connections, client connections are not
* automatically closed by workers, and disconnect does not wait for them to close
* before exiting.
*
* In a worker, `process.disconnect` exists, but it is not this function;
* it is `disconnect()`.
*
* Because long living server connections may block workers from disconnecting, it
* may be useful to send a message, so application specific actions may be taken to
* close them. It also may be useful to implement a timeout, killing a worker if
* the `'disconnect'` event has not been emitted after some time.
*
* ```js
* import net from 'node:net';
*
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* let timeout;
*
* worker.on('listening', (address) => {
* worker.send('shutdown');
* worker.disconnect();
* timeout = setTimeout(() => {
* worker.kill();
* }, 2000);
* });
*
* worker.on('disconnect', () => {
* clearTimeout(timeout);
* });
*
* } else if (cluster.isWorker) {
* const server = net.createServer((socket) => {
* // Connections never end
* });
*
* server.listen(8000);
*
* process.on('message', (msg) => {
* if (msg === 'shutdown') {
* // Initiate graceful close of any connections to server
* }
* });
* }
* ```
* @since v0.7.7
* @return A reference to `worker`.
*/
disconnect(): this;
/**
* This function returns `true` if the worker is connected to its primary via its
* IPC channel, `false` otherwise. A worker is connected to its primary after it
* has been created. It is disconnected after the `'disconnect'` event is emitted.
* @since v0.11.14
*/
isConnected(): boolean;
/**
* This function returns `true` if the worker's process has terminated (either
* because of exiting or being signaled). Otherwise, it returns `false`.
*
* ```js
* import cluster from 'node:cluster';
* import http from 'node:http';
* import { availableParallelism } from 'node:os';
* import process from 'node:process';
*
* const numCPUs = availableParallelism();
*
* if (cluster.isPrimary) {
* console.log(`Primary ${process.pid} is running`);
*
* // Fork workers.
* for (let i = 0; i < numCPUs; i++) {
* cluster.fork();
* }
*
* cluster.on('fork', (worker) => {
* console.log('worker is dead:', worker.isDead());
* });
*
* cluster.on('exit', (worker, code, signal) => {
* console.log('worker is dead:', worker.isDead());
* });
* } else {
* // Workers can share any TCP connection. In this case, it is an HTTP server.
* http.createServer((req, res) => {
* res.writeHead(200);
* res.end(`Current process\n ${process.pid}`);
* process.kill(process.pid);
* }).listen(8000);
* }
* ```
* @since v0.11.14
*/
isDead(): boolean;
/**
* This property is `true` if the worker exited due to `.disconnect()`.
* If the worker exited any other way, it is `false`. If the
* worker has not exited, it is `undefined`.
*
* The boolean `worker.exitedAfterDisconnect` allows distinguishing between
* voluntary and accidental exit, the primary may choose not to respawn a worker
* based on this value.
*
* ```js
* cluster.on('exit', (worker, code, signal) => {
* if (worker.exitedAfterDisconnect === true) {
* console.log('Oh, it was just voluntary no need to worry');
* }
* });
*
* // kill worker
* worker.kill();
* ```
* @since v6.0.0
*/
exitedAfterDisconnect: boolean;
}
interface Worker extends InternalEventEmitter<cluster.WorkerEventMap> {}
type _Worker = Worker;
namespace cluster {
interface Worker extends _Worker {}
interface WorkerOptions {
id?: number | undefined;
process?: child_process.ChildProcess | undefined;
state?: string | undefined;
}
interface WorkerEventMap {
"disconnect": [];
"error": [error: Error];
"exit": [code: number, signal: string];
"listening": [address: Address];
"message": [message: any, handle: child_process.SendHandle];
"online": [];
}
interface ClusterSettings {
/**
* List of string arguments passed to the Node.js executable.
* @default process.execArgv
*/
execArgv?: string[] | undefined;
/**
* File path to worker file.
* @default process.argv[1]
*/
exec?: string | undefined;
/**
* String arguments passed to worker.
* @default process.argv.slice(2)
*/
args?: readonly string[] | undefined;
/**
* Whether or not to send output to parent's stdio.
* @default false
*/
silent?: boolean | undefined;
/**
* Configures the stdio of forked processes. Because the cluster module relies on IPC to function, this configuration must
* contain an `'ipc'` entry. When this option is provided, it overrides `silent`. See [`child_prcess.spawn()`](https://nodejs.org/docs/latest-v26.x/api/child_process.html#child_processspawncommand-args-options)'s
* [`stdio`](https://nodejs.org/docs/latest-v26.x/api/child_process.html#optionsstdio).
*/
stdio?: any[] | undefined;
/**
* Sets the user identity of the process. (See [`setuid(2)`](https://man7.org/linux/man-pages/man2/setuid.2.html).)
*/
uid?: number | undefined;
/**
* Sets the group identity of the process. (See [`setgid(2)`](https://man7.org/linux/man-pages/man2/setgid.2.html).)
*/
gid?: number | undefined;
/**
* Sets inspector port of worker. This can be a number, or a function that takes no arguments and returns a number.
* By default each worker gets its own port, incremented from the primary's `process.debugPort`.
*/
inspectPort?: number | (() => number) | undefined;
/**
* Specify the kind of serialization used for sending messages between processes. Possible values are `'json'` and `'advanced'`.
* See [Advanced serialization for `child_process`](https://nodejs.org/docs/latest-v26.x/api/child_process.html#advanced-serialization) for more details.
* @default false
*/
serialization?: "json" | "advanced" | undefined;
/**
* Current working directory of the worker process.
* @default undefined (inherits from parent process)
*/
cwd?: string | undefined;
/**
* Hide the forked processes console window that would normally be created on Windows systems.
* @default false
*/
windowsHide?: boolean | undefined;
}
interface Address {
address: string;
port: number;
/**
* The `addressType` is one of:
*
* * `4` (TCPv4)
* * `6` (TCPv6)
* * `-1` (Unix domain socket)
* * `'udp4'` or `'udp6'` (UDPv4 or UDPv6)
*/
addressType: 4 | 6 | -1 | "udp4" | "udp6";
}
interface ClusterEventMap {
"disconnect": [worker: Worker];
"exit": [worker: Worker, code: number, signal: string];
"fork": [worker: Worker];
"listening": [worker: Worker, address: Address];
"message": [worker: Worker, message: any, handle: child_process.SendHandle];
"online": [worker: Worker];
"setup": [settings: ClusterSettings];
}
interface Cluster extends InternalEventEmitter<ClusterEventMap> {
/**
* A `Worker` object contains all public information and method about a worker.
* In the primary it can be obtained using `cluster.workers`. In a worker
* it can be obtained using `cluster.worker`.
* @since v0.7.0
*/
Worker: typeof Worker;
disconnect(callback?: () => void): void;
/**
* Spawn a new worker process.
*
* This can only be called from the primary process.
* @param env Key/value pairs to add to worker process environment.
* @since v0.6.0
*/
fork(env?: any): Worker;
/** @deprecated since v16.0.0 - use isPrimary. */
readonly isMaster: boolean;
/**
* True if the process is a primary. This is determined by the `process.env.NODE_UNIQUE_ID`. If `process.env.NODE_UNIQUE_ID`
* is undefined, then `isPrimary` is `true`.
* @since v16.0.0
*/
readonly isPrimary: boolean;
/**
* True if the process is not a primary (it is the negation of `cluster.isPrimary`).
* @since v0.6.0
*/
readonly isWorker: boolean;
/**
* The scheduling policy, either `cluster.SCHED_RR` for round-robin or `cluster.SCHED_NONE` to leave it to the operating system. This is a
* global setting and effectively frozen once either the first worker is spawned, or [`.setupPrimary()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clustersetupprimarysettings)
* is called, whichever comes first.
*
* `SCHED_RR` is the default on all operating systems except Windows. Windows will change to `SCHED_RR` once libuv is able to effectively distribute
* IOCP handles without incurring a large performance hit.
*
* `cluster.schedulingPolicy` can also be set through the `NODE_CLUSTER_SCHED_POLICY` environment variable. Valid values are `'rr'` and `'none'`.
* @since v0.11.2
*/
schedulingPolicy: number;
/**
* After calling [`.setupPrimary()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clustersetupprimarysettings)
* (or [`.fork()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clusterforkenv)) this settings object will contain
* the settings, including the default values.
*
* This object is not intended to be changed or set manually.
* @since v0.7.1
*/
readonly settings: ClusterSettings;
/** @deprecated since v16.0.0 - use [`.setupPrimary()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clustersetupprimarysettings) instead. */
setupMaster(settings?: ClusterSettings): void;
/**
* `setupPrimary` is used to change the default 'fork' behavior. Once called, the settings will be present in `cluster.settings`.
*
* Any settings changes only affect future calls to [`.fork()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clusterforkenv)
* and have no effect on workers that are already running.
*
* The only attribute of a worker that cannot be set via `.setupPrimary()` is the `env` passed to
* [`.fork()`](https://nodejs.org/docs/latest-v26.x/api/cluster.html#clusterforkenv).
*
* The defaults above apply to the first call only; the defaults for later calls are the current values at the time of
* `cluster.setupPrimary()` is called.
*
* ```js
* import cluster from 'node:cluster';
*
* cluster.setupPrimary({
* exec: 'worker.js',
* args: ['--use', 'https'],
* silent: true,
* });
* cluster.fork(); // https worker
* cluster.setupPrimary({
* exec: 'worker.js',
* args: ['--use', 'http'],
* });
* cluster.fork(); // http worker
* ```
*
* This can only be called from the primary process.
* @since v16.0.0
*/
setupPrimary(settings?: ClusterSettings): void;
/**
* A reference to the current worker object. Not available in the primary process.
*
* ```js
* import cluster from 'node:cluster';
*
* if (cluster.isPrimary) {
* console.log('I am primary');
* cluster.fork();
* cluster.fork();
* } else if (cluster.isWorker) {
* console.log(`I am worker #${cluster.worker.id}`);
* }
* ```
* @since v0.7.0
*/
readonly worker?: Worker;
/**
* A hash that stores the active worker objects, keyed by `id` field. This makes it easy to loop through all the workers. It is only available in the primary process.
*
* A worker is removed from `cluster.workers` after the worker has disconnected _and_ exited. The order between these two events cannot be determined in advance. However, it
* is guaranteed that the removal from the `cluster.workers` list happens before the last `'disconnect'` or `'exit'` event is emitted.
*
* ```js
* import cluster from 'node:cluster';
*
* for (const worker of Object.values(cluster.workers)) {
* worker.send('big announcement to all workers');
* }
* ```
* @since v0.7.0
*/
readonly workers?: NodeJS.Dict<Worker>;
readonly SCHED_NONE: number;
readonly SCHED_RR: number;
}
}
var cluster: cluster.Cluster;
export = cluster;
}
declare module "cluster" {
import cluster = require("node:cluster");
export = cluster;
}
+93
View File
@@ -0,0 +1,93 @@
declare module "node:console" {
import { InspectOptions } from "node:util";
namespace console {
interface ConsoleOptions {
stdout: NodeJS.WritableStream;
stderr?: NodeJS.WritableStream | undefined;
/**
* Ignore errors when writing to the underlying streams.
* @default true
*/
ignoreErrors?: boolean | undefined;
/**
* Set color support for this `Console` instance. Setting to true enables coloring while inspecting
* values. Setting to `false` disables coloring while inspecting values. Setting to `'auto'` makes color
* support depend on the value of the `isTTY` property and the value returned by `getColorDepth()` on the
* respective stream. This option can not be used, if `inspectOptions.colors` is set as well.
* @default 'auto'
*/
colorMode?: boolean | "auto" | undefined;
/**
* Specifies options that are passed along to
* [`util.inspect()`](https://nodejs.org/docs/latest-v26.x/api/util.html#utilinspectobject-options).
*/
inspectOptions?: InspectOptions | ReadonlyMap<NodeJS.WritableStream, InspectOptions> | undefined;
/**
* Set group indentation.
* @default 2
*/
groupIndentation?: number | undefined;
}
interface Console {
readonly Console: {
prototype: Console;
new(stdout: NodeJS.WritableStream, stderr?: NodeJS.WritableStream, ignoreErrors?: boolean): Console;
new(options: ConsoleOptions): Console;
};
assert(condition?: unknown, ...data: any[]): void;
clear(): void;
count(label?: string): void;
countReset(label?: string): void;
debug(...data: any[]): void;
dir(item?: any, options?: InspectOptions): void;
dirxml(...data: any[]): void;
error(...data: any[]): void;
group(...data: any[]): void;
groupCollapsed(...data: any[]): void;
groupEnd(): void;
info(...data: any[]): void;
log(...data: any[]): void;
table(tabularData?: any, properties?: string[]): void;
time(label?: string): void;
timeEnd(label?: string): void;
timeLog(label?: string, ...data: any[]): void;
trace(...data: any[]): void;
warn(...data: any[]): void;
/**
* This method does not display anything unless used in the inspector. The `console.profile()`
* method starts a JavaScript CPU profile with an optional label until {@link profileEnd}
* is called. The profile is then added to the Profile panel of the inspector.
*
* ```js
* console.profile('MyLabel');
* // Some code
* console.profileEnd('MyLabel');
* // Adds the profile 'MyLabel' to the Profiles panel of the inspector.
* ```
* @since v8.0.0
*/
profile(label?: string): void;
/**
* This method does not display anything unless used in the inspector. Stops the current
* JavaScript CPU profiling session if one has been started and prints the report to the
* Profiles panel of the inspector. See {@link profile} for an example.
*
* If this method is called without a label, the most recently started profile is stopped.
* @since v8.0.0
*/
profileEnd(label?: string): void;
/**
* This method does not display anything unless used in the inspector. The `console.timeStamp()`
* method adds an event with the label `'label'` to the Timeline panel of the inspector.
* @since v8.0.0
*/
timeStamp(label?: string): void;
}
}
var console: console.Console;
export = console;
}
declare module "console" {
import console = require("node:console");
export = console;
}
+14
View File
@@ -0,0 +1,14 @@
declare module "node:constants" {
const constants:
& typeof import("node:os").constants.dlopen
& typeof import("node:os").constants.errno
& typeof import("node:os").constants.priority
& typeof import("node:os").constants.signals
& typeof import("node:fs").constants
& typeof import("node:crypto").constants;
export = constants;
}
declare module "constants" {
import constants = require("node:constants");
export = constants;
}
+3952
View File
File diff suppressed because it is too large Load Diff
+652
View File
@@ -0,0 +1,652 @@
declare module "node:dgram" {
import { NonSharedBuffer } from "node:buffer";
import * as dns from "node:dns";
import { Abortable, EventEmitter, InternalEventEmitter } from "node:events";
import { AddressInfo, BlockList } from "node:net";
interface RemoteInfo {
address: string;
family: "IPv4" | "IPv6";
port: number;
size: number;
}
interface BindOptions {
port?: number | undefined;
address?: string | undefined;
exclusive?: boolean | undefined;
fd?: number | undefined;
}
interface BindSyncOptions {
port?: number | undefined;
address?: string | undefined;
}
type SocketType = "udp4" | "udp6";
interface SocketOptions extends Abortable {
type: SocketType;
reuseAddr?: boolean | undefined;
reusePort?: boolean | undefined;
/**
* @default false
*/
ipv6Only?: boolean | undefined;
recvBufferSize?: number | undefined;
sendBufferSize?: number | undefined;
lookup?:
| ((
hostname: string,
options: dns.LookupOneOptions,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
) => void)
| undefined;
receiveBlockList?: BlockList | undefined;
sendBlockList?: BlockList | undefined;
}
/**
* Creates a `dgram.Socket` object. Once the socket is created, calling `socket.bind()` will instruct the socket to begin listening for datagram
* messages. When `address` and `port` are not passed to `socket.bind()` the
* method will bind the socket to the "all interfaces" address on a random port
* (it does the right thing for both `udp4` and `udp6` sockets). The bound address
* and port can be retrieved using `socket.address().address` and `socket.address().port`.
*
* If the `signal` option is enabled, calling `.abort()` on the corresponding `AbortController` is similar to calling `.close()` on the socket:
*
* ```js
* const controller = new AbortController();
* const { signal } = controller;
* const server = dgram.createSocket({ type: 'udp4', signal });
* server.on('message', (msg, rinfo) => {
* console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
* });
* // Later, when you want to close the server.
* controller.abort();
* ```
* @since v0.11.13
* @param options Available options are:
* @param callback Attached as a listener for `'message'` events. Optional.
*/
function createSocket(type: SocketType, callback?: (msg: NonSharedBuffer, rinfo: RemoteInfo) => void): Socket;
function createSocket(options: SocketOptions, callback?: (msg: NonSharedBuffer, rinfo: RemoteInfo) => void): Socket;
interface SocketEventMap {
"close": [];
"connect": [];
"error": [err: Error];
"listening": [];
"message": [msg: NonSharedBuffer, rinfo: RemoteInfo];
}
/**
* Encapsulates the datagram functionality.
*
* New instances of `dgram.Socket` are created using {@link createSocket}.
* The `new` keyword is not to be used to create `dgram.Socket` instances.
* @since v0.1.99
*/
class Socket implements EventEmitter {
/**
* Tells the kernel to join a multicast group at the given `multicastAddress` and `multicastInterface` using the `IP_ADD_MEMBERSHIP` socket option. If the `multicastInterface` argument is not
* specified, the operating system will choose
* one interface and will add membership to it. To add membership to every
* available interface, call `addMembership` multiple times, once per interface.
*
* When called on an unbound socket, this method will implicitly bind to a random
* port, listening on all interfaces.
*
* When sharing a UDP socket across multiple `cluster` workers, the`socket.addMembership()` function must be called only once or an`EADDRINUSE` error will occur:
*
* ```js
* import cluster from 'node:cluster';
* import dgram from 'node:dgram';
*
* if (cluster.isPrimary) {
* cluster.fork(); // Works ok.
* cluster.fork(); // Fails with EADDRINUSE.
* } else {
* const s = dgram.createSocket('udp4');
* s.bind(1234, () => {
* s.addMembership('224.0.0.114');
* });
* }
* ```
* @since v0.6.9
*/
addMembership(multicastAddress: string, multicastInterface?: string): void;
/**
* Returns an object containing the address information for a socket.
* For UDP sockets, this object will contain `address`, `family`, and `port` properties.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.1.99
*/
address(): AddressInfo;
/**
* For UDP sockets, causes the `dgram.Socket` to listen for datagram
* messages on a named `port` and optional `address`. If `port` is not
* specified or is `0`, the operating system will attempt to bind to a
* random port. If `address` is not specified, the operating system will
* attempt to listen on all addresses. Once binding is complete, a
* `'listening'` event is emitted and the optional `callback` function is
* called.
*
* Specifying both a `'listening'` event listener and passing a
* `callback` to the `socket.bind()` method is not harmful but not very
* useful.
*
* A bound datagram socket keeps the Node.js process running to receive
* datagram messages.
*
* If binding fails, an `'error'` event is generated. In rare case (e.g.
* attempting to bind with a closed socket), an `Error` may be thrown.
*
* Example of a UDP server listening on port 41234:
*
* ```js
* import dgram from 'node:dgram';
*
* const server = dgram.createSocket('udp4');
*
* server.on('error', (err) => {
* console.error(`server error:\n${err.stack}`);
* server.close();
* });
*
* server.on('message', (msg, rinfo) => {
* console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
* });
*
* server.on('listening', () => {
* const address = server.address();
* console.log(`server listening ${address.address}:${address.port}`);
* });
*
* server.bind(41234);
* // Prints: server listening 0.0.0.0:41234
* ```
* @since v0.1.99
* @param callback with no parameters. Called when binding is complete.
*/
bind(port?: number, address?: string, callback?: () => void): this;
bind(port: number, callback: () => void): this;
bind(callback: () => void): this;
/**
* For UDP sockets, causes the `dgram.Socket` to listen for datagram
* messages on a named `port` and optional `address` that are passed as
* properties of an `options` object passed as the first argument. If
* `port` is not specified or is `0`, the operating system will attempt
* to bind to a random port. If `address` is not specified, the operating
* system will attempt to listen on all addresses. Once binding is
* complete, a `'listening'` event is emitted and the optional `callback`
* function is called.
*
* The `options` object may contain a `fd` property. When a `fd` greater
* than `0` is set, it will wrap around an existing socket with the given
* file descriptor. In this case, the properties of `port` and `address`
* will be ignored.
*
* Specifying both a `'listening'` event listener and passing a
* `callback` to the `socket.bind()` method is not harmful but not very
* useful.
*
* The `options` object may contain an additional `exclusive` property that is
* used when using `dgram.Socket` objects with the [`cluster`](https://nodejs.org/docs/latest-v26.x/api/cluster.html) module. When
* `exclusive` is set to `false` (the default), cluster workers will use the same
* underlying socket handle allowing connection handling duties to be shared.
* When `exclusive` is `true`, however, the handle is not shared and attempted
* port sharing results in an error. Creating a `dgram.Socket` with the `reusePort`
* option set to `true` causes `exclusive` to always be `true` when `socket.bind()`
* is called.
*
* A bound datagram socket keeps the Node.js process running to receive
* datagram messages.
*
* If binding fails, an `'error'` event is generated. In rare case (e.g.
* attempting to bind with a closed socket), an `Error` may be thrown.
*
* An example socket listening on an exclusive port is shown below.
*
* ```js
* socket.bind({
* address: 'localhost',
* port: 8000,
* exclusive: true,
* });
* ```
* @since v0.11.14
* @param options Required. Supports the following properties:
*/
bind(options: BindOptions, callback?: () => void): this;
/**
* The synchronous counterpart of `socket.bind()`. `bind(2)` is a local,
* non-blocking system call, so the bind is performed inline and the resolved
* address is returned immediately, including the operating-system-assigned
* ephemeral port when `port` is `0`:
*
* ```js
* const dgram = require('node:dgram');
*
* const socket = dgram.createSocket('udp4');
* const address = socket.bindSync({ address: '0.0.0.0', port: 0 });
* console.log(address); // e.g. { address: '0.0.0.0', family: 'IPv4', port: 53124 }
* ```
*
* A bind failure such as `EADDRINUSE` is thrown synchronously rather than emitted
* as an `'error'` event. After `bindSync()` returns, `socket.address()` is
* valid synchronously and the `'listening'` event is emitted on the next tick.
*
* `address` must be a numeric IP literal; `bindSync()` never performs DNS
* resolution (asynchronous name resolution being the only genuinely blocking part
* of binding). Incoming datagrams continue to be delivered asynchronously via the
* `'message'` event. `bindSync()` always binds the socket's own handle and
* does not participate in [`cluster`](https://nodejs.org/docs/latest-v26.x/api/cluster.html) handle sharing.
* @since v26.4.0
* @returns The bound address as returned by `socket.address()`.
*/
bindSync(options?: BindSyncOptions): AddressInfo;
/**
* Close the underlying socket and stop listening for data on it. If a callback is
* provided, it is added as a listener for the `'close'` event.
* @since v0.1.99
* @param callback Called when the socket has been closed.
*/
close(callback?: () => void): this;
/**
* Associates the `dgram.Socket` to a remote address and port. Every
* message sent by this handle is automatically sent to that destination. Also,
* the socket will only receive messages from that remote peer.
* Trying to call `connect()` on an already connected socket will result
* in an `ERR_SOCKET_DGRAM_IS_CONNECTED` exception. If `address` is not
* provided, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for `udp6` sockets)
* will be used by default. Once the connection is complete, a `'connect'` event
* is emitted and the optional `callback` function is called. In case of failure,
* the `callback` is called or, failing this, an `'error'` event is emitted.
* @since v12.0.0
* @param callback Called when the connection is completed or on error.
*/
connect(port: number, address?: string, callback?: () => void): void;
connect(port: number, callback: () => void): void;
/**
* The synchronous counterpart of `socket.connect()`. For a UDP socket
* `connect(2)` only records the default peer address and is a local, non-blocking
* system call, so the association is performed inline. Any error raised by the
* call itself (for example `EAFNOSUPPORT` for a mismatched address family) is
* thrown synchronously rather than reported via the `'error'` event. Because
* `connect(2)` does not probe reachability, errors such as `ECONNREFUSED` are
* still surfaced asynchronously on a later send or receive, exactly as for
* `socket.connect()`:
*
* ```js
* const dgram = require('node:dgram');
*
* const socket = dgram.createSocket('udp4');
* socket.connectSync(41234, '127.0.0.1');
* console.log(socket.remoteAddress()); // { address: '127.0.0.1', family: 'IPv4', port: 41234 }
* ```
*
* If the socket is still unbound it is bound synchronously first. After
* `connectSync()` returns, `socket.remoteAddress()` is valid synchronously
* and the `'connect'` event is emitted on the next tick. Trying to call
* `connectSync()` on an already connected socket throws an
* `ERR_SOCKET_DGRAM_IS_CONNECTED` exception, and calling it while an
* asynchronous [`socket.bind()`][] is still in progress throws an
* `ERR_SOCKET_ALREADY_BOUND` exception.
*
* `address` must be a numeric IP literal; `connectSync()` never performs DNS
* resolution (asynchronous name resolution being the only genuinely blocking part
* of connecting).
* @since v26.4.0
* @param address A numeric IP address to connect to. Unlike
* `socket.connect()`, no DNS resolution is performed, so a host name is not
* accepted. If omitted, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for
* `udp6` sockets) is used.
*/
connectSync(port: number, address?: string): void;
/**
* A synchronous function that disassociates a connected `dgram.Socket` from
* its remote address. Trying to call `disconnect()` on an unbound or already
* disconnected socket will result in an `ERR_SOCKET_DGRAM_NOT_CONNECTED` exception.
* @since v12.0.0
*/
disconnect(): void;
/**
* Instructs the kernel to leave a multicast group at `multicastAddress` using the `IP_DROP_MEMBERSHIP` socket option. This method is automatically called by the
* kernel when the socket is closed or the process terminates, so most apps will
* never have reason to call this.
*
* If `multicastInterface` is not specified, the operating system will attempt to
* drop membership on all valid interfaces.
* @since v0.6.9
*/
dropMembership(multicastAddress: string, multicastInterface?: string): void;
/**
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
* @return the `SO_RCVBUF` socket receive buffer size in bytes.
*/
getRecvBufferSize(): number;
/**
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
* @return the `SO_SNDBUF` socket send buffer size in bytes.
*/
getSendBufferSize(): number;
/**
* @since v18.8.0, v16.19.0
* @return Number of bytes queued for sending.
*/
getSendQueueSize(): number;
/**
* @since v18.8.0, v16.19.0
* @return Number of send requests currently in the queue awaiting to be processed.
*/
getSendQueueCount(): number;
/**
* By default, binding a socket will cause it to block the Node.js process from
* exiting as long as the socket is open. The `socket.unref()` method can be used
* to exclude the socket from the reference counting that keeps the Node.js
* process active. The `socket.ref()` method adds the socket back to the reference
* counting and restores the default behavior.
*
* Calling `socket.ref()` multiples times will have no additional effect.
*
* The `socket.ref()` method returns a reference to the socket so calls can be
* chained.
* @since v0.9.1
*/
ref(): this;
/**
* Returns an object containing the `address`, `family`, and `port` of the remote
* endpoint. This method throws an `ERR_SOCKET_DGRAM_NOT_CONNECTED` exception
* if the socket is not connected.
* @since v12.0.0
*/
remoteAddress(): AddressInfo;
/**
* Broadcasts a datagram on the socket.
* For connectionless sockets, the destination `port` and `address` must be
* specified. Connected sockets, on the other hand, will use their associated
* remote endpoint, so the `port` and `address` arguments must not be set.
*
* The `msg` argument contains the message to be sent.
* Depending on its type, different behavior can apply. If `msg` is a `Buffer`,
* any `TypedArray` or a `DataView`,
* the `offset` and `length` specify the offset within the `Buffer` where the
* message begins and the number of bytes in the message, respectively.
* If `msg` is a `String`, then it is automatically converted to a `Buffer` with `'utf8'` encoding. With messages that
* contain multi-byte characters, `offset` and `length` will be calculated with
* respect to `byte length` and not the character position.
* If `msg` is an array, `offset` and `length` must not be specified.
*
* The `address` argument is a string. If the value of `address` is a host name,
* DNS will be used to resolve the address of the host. If `address` is not
* provided or otherwise nullish, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for `udp6` sockets) will be used by default.
*
* If the socket has not been previously bound with a call to `bind`, the socket
* is assigned a random port number and is bound to the "all interfaces" address
* (`'0.0.0.0'` for `udp4` sockets, `'::0'` for `udp6` sockets.)
*
* An optional `callback` function may be specified to as a way of reporting
* DNS errors or for determining when it is safe to reuse the `buf` object.
* DNS lookups delay the time to send for at least one tick of the
* Node.js event loop.
*
* The only way to know for sure that the datagram has been sent is by using a `callback`. If an error occurs and a `callback` is given, the error will be
* passed as the first argument to the `callback`. If a `callback` is not given,
* the error is emitted as an `'error'` event on the `socket` object.
*
* Offset and length are optional but both _must_ be set if either are used.
* They are supported only when the first argument is a `Buffer`, a `TypedArray`,
* or a `DataView`.
*
* This method throws `ERR_SOCKET_BAD_PORT` if called on an unbound socket.
*
* Example of sending a UDP packet to a port on `localhost`;
*
* ```js
* import dgram from 'node:dgram';
* import { Buffer } from 'node:buffer';
*
* const message = Buffer.from('Some bytes');
* const client = dgram.createSocket('udp4');
* client.send(message, 41234, 'localhost', (err) => {
* client.close();
* });
* ```
*
* Example of sending a UDP packet composed of multiple buffers to a port on`127.0.0.1`;
*
* ```js
* import dgram from 'node:dgram';
* import { Buffer } from 'node:buffer';
*
* const buf1 = Buffer.from('Some ');
* const buf2 = Buffer.from('bytes');
* const client = dgram.createSocket('udp4');
* client.send([buf1, buf2], 41234, (err) => {
* client.close();
* });
* ```
*
* Sending multiple buffers might be faster or slower depending on the
* application and operating system. Run benchmarks to
* determine the optimal strategy on a case-by-case basis. Generally speaking,
* however, sending multiple buffers is faster.
*
* Example of sending a UDP packet using a socket connected to a port on `localhost`:
*
* ```js
* import dgram from 'node:dgram';
* import { Buffer } from 'node:buffer';
*
* const message = Buffer.from('Some bytes');
* const client = dgram.createSocket('udp4');
* client.connect(41234, 'localhost', (err) => {
* client.send(message, (err) => {
* client.close();
* });
* });
* ```
* @since v0.1.99
* @param msg Message to be sent.
* @param offset Offset in the buffer where the message starts.
* @param length Number of bytes in the message.
* @param port Destination port.
* @param address Destination host name or IP address.
* @param callback Called when the message has been sent.
*/
send(
msg: string | NodeJS.ArrayBufferView | readonly any[],
port?: number,
address?: string,
callback?: (error: Error | null, bytes: number) => void,
): void;
send(
msg: string | NodeJS.ArrayBufferView | readonly any[],
port?: number,
callback?: (error: Error | null, bytes: number) => void,
): void;
send(
msg: string | NodeJS.ArrayBufferView | readonly any[],
callback?: (error: Error | null, bytes: number) => void,
): void;
send(
msg: string | NodeJS.ArrayBufferView,
offset: number,
length: number,
port?: number,
address?: string,
callback?: (error: Error | null, bytes: number) => void,
): void;
send(
msg: string | NodeJS.ArrayBufferView,
offset: number,
length: number,
port?: number,
callback?: (error: Error | null, bytes: number) => void,
): void;
send(
msg: string | NodeJS.ArrayBufferView,
offset: number,
length: number,
callback?: (error: Error | null, bytes: number) => void,
): void;
/**
* Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP
* packets may be sent to a local interface's broadcast address.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.6.9
*/
setBroadcast(flag: boolean): void;
/**
* _All references to scope in this section are referring to [IPv6 Zone Indices](https://en.wikipedia.org/wiki/IPv6_address#Scoped_literal_IPv6_addresses), which are defined by [RFC
* 4007](https://tools.ietf.org/html/rfc4007). In string form, an IP_
* _with a scope index is written as `'IP%scope'` where scope is an interface name_
* _or interface number._
*
* Sets the default outgoing multicast interface of the socket to a chosen
* interface or back to system interface selection. The `multicastInterface` must
* be a valid string representation of an IP from the socket's family.
*
* For IPv4 sockets, this should be the IP configured for the desired physical
* interface. All packets sent to multicast on the socket will be sent on the
* interface determined by the most recent successful use of this call.
*
* For IPv6 sockets, `multicastInterface` should include a scope to indicate the
* interface as in the examples that follow. In IPv6, individual `send` calls can
* also use explicit scope in addresses, so only packets sent to a multicast
* address without specifying an explicit scope are affected by the most recent
* successful use of this call.
*
* This method throws `EBADF` if called on an unbound socket.
*
* #### Example: IPv6 outgoing multicast interface
*
* On most systems, where scope format uses the interface name:
*
* ```js
* const socket = dgram.createSocket('udp6');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('::%eth1');
* });
* ```
*
* On Windows, where scope format uses an interface number:
*
* ```js
* const socket = dgram.createSocket('udp6');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('::%2');
* });
* ```
*
* #### Example: IPv4 outgoing multicast interface
*
* All systems use an IP of the host on the desired physical interface:
*
* ```js
* const socket = dgram.createSocket('udp4');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('10.0.0.2');
* });
* ```
* @since v8.6.0
*/
setMulticastInterface(multicastInterface: string): void;
/**
* Sets or clears the `IP_MULTICAST_LOOP` socket option. When set to `true`,
* multicast packets will also be received on the local interface.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.3.8
*/
setMulticastLoopback(flag: boolean): boolean;
/**
* Sets the `IP_MULTICAST_TTL` socket option. While TTL generally stands for
* "Time to Live", in this context it specifies the number of IP hops that a
* packet is allowed to travel through, specifically for multicast traffic. Each
* router or gateway that forwards a packet decrements the TTL. If the TTL is
* decremented to 0 by a router, it will not be forwarded.
*
* The `ttl` argument may be between 0 and 255\. The default on most systems is `1`.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.3.8
*/
setMulticastTTL(ttl: number): number;
/**
* Sets the `SO_RCVBUF` socket option. Sets the maximum socket receive buffer
* in bytes.
*
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
*/
setRecvBufferSize(size: number): void;
/**
* Sets the `SO_SNDBUF` socket option. Sets the maximum socket send buffer
* in bytes.
*
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
*/
setSendBufferSize(size: number): void;
/**
* Sets the `IP_TTL` socket option. While TTL generally stands for "Time to Live",
* in this context it specifies the number of IP hops that a packet is allowed to
* travel through. Each router or gateway that forwards a packet decrements the
* TTL. If the TTL is decremented to 0 by a router, it will not be forwarded.
* Changing TTL values is typically done for network probes or when multicasting.
*
* The `ttl` argument may be between 1 and 255\. The default on most systems
* is 64.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.1.101
*/
setTTL(ttl: number): number;
/**
* By default, binding a socket will cause it to block the Node.js process from
* exiting as long as the socket is open. The `socket.unref()` method can be used
* to exclude the socket from the reference counting that keeps the Node.js
* process active, allowing the process to exit even if the socket is still
* listening.
*
* Calling `socket.unref()` multiple times will have no additional effect.
*
* The `socket.unref()` method returns a reference to the socket so calls can be
* chained.
* @since v0.9.1
*/
unref(): this;
/**
* Tells the kernel to join a source-specific multicast channel at the given `sourceAddress` and `groupAddress`, using the `multicastInterface` with the `IP_ADD_SOURCE_MEMBERSHIP` socket
* option. If the `multicastInterface` argument
* is not specified, the operating system will choose one interface and will add
* membership to it. To add membership to every available interface, call `socket.addSourceSpecificMembership()` multiple times, once per interface.
*
* When called on an unbound socket, this method will implicitly bind to a random
* port, listening on all interfaces.
* @since v13.1.0, v12.16.0
*/
addSourceSpecificMembership(sourceAddress: string, groupAddress: string, multicastInterface?: string): void;
/**
* Instructs the kernel to leave a source-specific multicast channel at the given `sourceAddress` and `groupAddress` using the `IP_DROP_SOURCE_MEMBERSHIP` socket option. This method is
* automatically called by the kernel when the
* socket is closed or the process terminates, so most apps will never have
* reason to call this.
*
* If `multicastInterface` is not specified, the operating system will attempt to
* drop membership on all valid interfaces.
* @since v13.1.0, v12.16.0
*/
dropSourceSpecificMembership(sourceAddress: string, groupAddress: string, multicastInterface?: string): void;
/**
* Calls `socket.close()` and returns a promise that fulfills when the socket has closed.
* @since v20.5.0
*/
[Symbol.asyncDispose](): Promise<void>;
}
interface Socket extends InternalEventEmitter<SocketEventMap> {}
}
declare module "dgram" {
export * from "node:dgram";
}
+794
View File
@@ -0,0 +1,794 @@
declare module "node:diagnostics_channel" {
import { AsyncLocalStorage } from "node:async_hooks";
/**
* Check if there are active subscribers to the named channel. This is helpful if
* the message you want to send might be expensive to prepare.
*
* This API is optional but helpful when trying to publish messages from very
* performance-sensitive code.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* if (diagnostics_channel.hasSubscribers('my-channel')) {
* // There are subscribers, prepare and publish message
* }
* ```
* @since v15.1.0, v14.17.0
* @param name The channel name
* @return If there are active subscribers
*/
function hasSubscribers(name: string | symbol): boolean;
/**
* This is the primary entry-point for anyone wanting to publish to a named
* channel. It produces a channel object which is optimized to reduce overhead at
* publish time as much as possible.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
* ```
* @since v15.1.0, v14.17.0
* @param name The channel name
* @return The named channel object
*/
// eslint-disable-next-line @definitelytyped/no-unnecessary-generics
function channel<ContextType = any, StoreType = ContextType>(
name: string | symbol,
): Channel<ContextType, StoreType>;
type ChannelListener = (message: unknown, name: string | symbol) => void;
/**
* Register a message handler to subscribe to this channel. This message handler
* will be run synchronously whenever a message is published to the channel. Any
* errors thrown in the message handler will trigger an `'uncaughtException'`.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* diagnostics_channel.subscribe('my-channel', (message, name) => {
* // Received data
* });
* ```
* @since v18.7.0, v16.17.0
* @param name The channel name
* @param onMessage The handler to receive channel messages
*/
function subscribe(name: string | symbol, onMessage: ChannelListener): void;
/**
* Remove a message handler previously registered to this channel with {@link subscribe}.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* function onMessage(message, name) {
* // Received data
* }
*
* diagnostics_channel.subscribe('my-channel', onMessage);
*
* diagnostics_channel.unsubscribe('my-channel', onMessage);
* ```
* @since v18.7.0, v16.17.0
* @param name The channel name
* @param onMessage The previous subscribed handler to remove
* @return `true` if the handler was found, `false` otherwise.
*/
function unsubscribe(name: string | symbol, onMessage: ChannelListener): boolean;
/**
* Creates a `TracingChannel` wrapper for the given `TracingChannel Channels`. If a name is given, the corresponding tracing
* channels will be created in the form of `tracing:${name}:${eventType}` where `eventType` corresponds to the types of `TracingChannel Channels`.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channelsByName = diagnostics_channel.tracingChannel('my-channel');
*
* // or...
*
* const channelsByCollection = diagnostics_channel.tracingChannel({
* start: diagnostics_channel.channel('tracing:my-channel:start'),
* end: diagnostics_channel.channel('tracing:my-channel:end'),
* asyncStart: diagnostics_channel.channel('tracing:my-channel:asyncStart'),
* asyncEnd: diagnostics_channel.channel('tracing:my-channel:asyncEnd'),
* error: diagnostics_channel.channel('tracing:my-channel:error'),
* });
* ```
* @since v19.9.0
* @experimental
* @param nameOrChannels Channel name or object containing all the `TracingChannel Channels`
* @return Collection of channels to trace with
*/
function tracingChannel<ContextType extends object = object, StoreType = ContextType>(
nameOrChannels: string | TracingChannelCollection<ContextType, StoreType>,
): TracingChannel<ContextType, StoreType>;
/**
* Creates a {@link BoundedChannel} wrapper for the given channels. If a name is
* given, the corresponding channels will be created in the form of
* `tracing:${name}:${eventType}` where `eventType` is `start` or `end`.
*
* A `BoundedChannel` is a simplified version of {@link TracingChannel} that only
* traces synchronous operations. It only has `start` and `end` events, without
* `asyncStart`, `asyncEnd`, or `error` events, making it suitable for tracing
* operations that don't involve asynchronous continuations or error handling.
*
* ```js
* import { boundedChannel, channel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* // or...
*
* const wc2 = boundedChannel({
* start: channel('tracing:my-operation:start'),
* end: channel('tracing:my-operation:end'),
* });
* ```
* @since v26.1.0
* @experimental
* @param nameOrChannels Channel name or
* object containing all the [BoundedChannel Channels](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#boundedchannel-channels)
*/
function boundedChannel<ContextType extends object = object, StoreType = ContextType>(
nameOrChannels: string | BoundedChannelCollection<ContextType, StoreType>,
): BoundedChannel<ContextType, StoreType>;
/**
* The class `Channel` represents an individual named channel within the data
* pipeline. It is used to track subscribers and to publish messages when there
* are subscribers present. It exists as a separate object to avoid channel
* lookups at publish time, enabling very fast publish speeds and allowing
* for heavy use while incurring very minimal cost. Channels are created with {@link channel}, constructing a channel directly
* with `new Channel(name)` is not supported.
* @since v15.1.0, v14.17.0
*/
class Channel<ContextType = any, StoreType = ContextType> {
private constructor();
readonly name: string | symbol;
/**
* Check if there are active subscribers to this channel. This is helpful if
* the message you want to send might be expensive to prepare.
*
* This API is optional but helpful when trying to publish messages from very
* performance-sensitive code.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* if (channel.hasSubscribers) {
* // There are subscribers, prepare and publish message
* }
* ```
* @since v15.1.0, v14.17.0
*/
readonly hasSubscribers: boolean;
/**
* Publish a message to any subscribers to the channel. This will trigger
* message handlers synchronously so they will execute within the same context.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.publish({
* some: 'message',
* });
* ```
* @since v15.1.0, v14.17.0
* @param message The message to send to the channel subscribers
*/
publish(message: unknown): void;
/**
* Register a message handler to subscribe to this channel. This message handler
* will be run synchronously whenever a message is published to the channel. Any
* errors thrown in the message handler will trigger an `'uncaughtException'`.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.subscribe((message, name) => {
* // Received data
* });
* ```
* @since v15.1.0, v14.17.0
* @param onMessage The handler to receive channel messages
*/
subscribe(onMessage: ChannelListener): void;
/**
* Remove a message handler previously registered to this channel with `channel.subscribe(onMessage)`.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* function onMessage(message, name) {
* // Received data
* }
*
* channel.subscribe(onMessage);
*
* channel.unsubscribe(onMessage);
* ```
* @since v15.1.0, v14.17.0
* @param onMessage The previous subscribed handler to remove
* @return `true` if the handler was found, `false` otherwise.
*/
unsubscribe(onMessage: ChannelListener): void;
/**
* When `channel.runStores(context, ...)` is called, the given context data
* will be applied to any store bound to the channel. If the store has already been
* bound the previous `transform` function will be replaced with the new one.
* The `transform` function may be omitted to set the given context data as the
* context directly.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const store = new AsyncLocalStorage();
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.bindStore(store, (data) => {
* return { data };
* });
* ```
* @since v19.9.0
* @experimental
* @param store The store to which to bind the context data
* @param transform Transform context data before setting the store context
*/
bindStore(store: AsyncLocalStorage<StoreType>, transform?: (context: ContextType) => StoreType): void;
/**
* Remove a message handler previously registered to this channel with `channel.bindStore(store)`.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const store = new AsyncLocalStorage();
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.bindStore(store);
* channel.unbindStore(store);
* ```
* @since v19.9.0
* @experimental
* @param store The store to unbind from the channel.
* @return `true` if the store was found, `false` otherwise.
*/
unbindStore(store: AsyncLocalStorage<StoreType>): boolean;
/**
* Applies the given data to any AsyncLocalStorage instances bound to the channel
* for the duration of the given function, then publishes to the channel within
* the scope of that data is applied to the stores.
*
* If a transform function was given to `channel.bindStore(store)` it will be
* applied to transform the message data before it becomes the context value for
* the store. The prior storage context is accessible from within the transform
* function in cases where context linking is required.
*
* The context applied to the store should be accessible in any async code which
* continues from execution which began during the given function, however
* there are some situations in which `context loss` may occur.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const store = new AsyncLocalStorage();
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.bindStore(store, (message) => {
* const parent = store.getStore();
* return new Span(message, parent);
* });
* channel.runStores({ some: 'message' }, () => {
* store.getStore(); // Span({ some: 'message' })
* });
* ```
* @since v19.9.0
* @experimental
* @param context Message to send to subscribers and bind to stores
* @param fn Handler to run within the entered storage context
* @param thisArg The receiver to be used for the function call.
* @param args Optional arguments to pass to the function.
*/
runStores<ThisArg = any, Args extends any[] = any[], Result = any>(
context: ContextType,
fn: (this: ThisArg, ...args: Args) => Result,
thisArg?: ThisArg,
...args: Args
): Result;
/**
* Creates a disposable scope that binds the given data to any AsyncLocalStorage
* instances bound to the channel and publishes it to subscribers. The scope
* automatically restores the previous storage contexts when disposed.
*
* This method enables the use of JavaScript's explicit resource management
* (`using` syntax with `Symbol.dispose`) to manage store contexts without
* closure wrapping.
*
* ```js
* import { channel } from 'node:diagnostics_channel';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const store = new AsyncLocalStorage();
* const ch = channel('my-channel');
*
* ch.bindStore(store, (message) => {
* return { ...message, timestamp: Date.now() };
* });
*
* {
* using scope = ch.withStoreScope({ request: 'data' });
* // Store is entered, data is published
* console.log(store.getStore()); // { request: 'data', timestamp: ... }
* }
* // Store is automatically restored on scope exit
* ```
* @since v26.1.0
* @experimental
*/
withStoreScope(data: ContextType): RunStoresScope;
}
/**
* The class `RunStoresScope` represents a disposable scope created by
* `channel.withStoreScope(data)`. It manages the lifecycle of store
* contexts and ensures they are properly restored when the scope exits.
*
* The scope must be used with the `using` syntax to ensure proper disposal.
* @since v26.1.0
* @experimental
*/
interface RunStoresScope extends Disposable {}
interface TracingChannelSubscribers<ContextType extends object> {
start: (message: ContextType) => void;
end: (
message: ContextType & {
error?: unknown;
result?: unknown;
},
) => void;
asyncStart: (
message: ContextType & {
error?: unknown;
result?: unknown;
},
) => void;
asyncEnd: (
message: ContextType & {
error?: unknown;
result?: unknown;
},
) => void;
error: (
message: ContextType & {
error: unknown;
},
) => void;
}
interface TracingChannelCollection<ContextType extends object = object, StoreType = ContextType> {
start: Channel<ContextType, StoreType>;
end: Channel<ContextType, StoreType>;
asyncStart: Channel<ContextType, StoreType>;
asyncEnd: Channel<ContextType, StoreType>;
error: Channel<ContextType, StoreType>;
}
/**
* The class `TracingChannel` is a collection of `TracingChannel Channels` which
* together express a single traceable action. It is used to formalize and
* simplify the process of producing events for tracing application flow. {@link tracingChannel} is used to construct a `TracingChannel`. As with `Channel` it is recommended to create and reuse a
* single `TracingChannel` at the top-level of the file rather than creating them
* dynamically.
* @since v19.9.0
* @experimental
*/
interface TracingChannel<ContextType extends object = object, StoreType = ContextType>
extends TracingChannelCollection<ContextType, StoreType>
{
/**
* Helper to subscribe a collection of functions to the corresponding channels.
* This is the same as calling `channel.subscribe(onMessage)` on each channel
* individually.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* channels.subscribe({
* start(message) {
* // Handle start message
* },
* end(message) {
* // Handle end message
* },
* asyncStart(message) {
* // Handle asyncStart message
* },
* asyncEnd(message) {
* // Handle asyncEnd message
* },
* error(message) {
* // Handle error message
* },
* });
* ```
* @since v19.9.0
* @experimental
* @param subscribers Set of `TracingChannel Channels` subscribers
*/
subscribe(subscribers: NodeJS.PartialOptions<TracingChannelSubscribers<ContextType>>): void;
/**
* Helper to unsubscribe a collection of functions from the corresponding channels.
* This is the same as calling `channel.unsubscribe(onMessage)` on each channel
* individually.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* channels.unsubscribe({
* start(message) {
* // Handle start message
* },
* end(message) {
* // Handle end message
* },
* asyncStart(message) {
* // Handle asyncStart message
* },
* asyncEnd(message) {
* // Handle asyncEnd message
* },
* error(message) {
* // Handle error message
* },
* });
* ```
* @since v19.9.0
* @experimental
* @param subscribers Set of `TracingChannel Channels` subscribers
* @return `true` if all handlers were successfully unsubscribed, and `false` otherwise.
*/
unsubscribe(subscribers: NodeJS.PartialOptions<TracingChannelSubscribers<ContextType>>): void;
/**
* Trace a synchronous function call. This will always produce a `start event` and `end event` around the execution and may produce an `error event` if the given function throws an error.
* This will run the given function using `channel.runStores(context, ...)` on the `start` channel which ensures all
* events should have any bound stores set to match this trace context.
*
* To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions
* which are added after the trace begins will not receive future events from that trace, only future traces will be seen.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* channels.traceSync(() => {
* // Do something
* }, {
* some: 'thing',
* });
* ```
* @since v19.9.0
* @experimental
* @param fn Function to wrap a trace around
* @param context Shared object to correlate events through
* @param thisArg The receiver to be used for the function call
* @param args Optional arguments to pass to the function
* @return The return value of the given function
*/
traceSync<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Result,
context?: ContextType,
thisArg?: ThisArg,
...args: Args
): Result;
/**
* Trace an asynchronous function call which returns a `Promise` or
* [thenable object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables). This will always produce a [`start` event](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#startevent) and
* [`end` event](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#endevent) around the synchronous portion of the function execution, and
* will produce an [`asyncStart` event](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#asyncstartevent) and [`asyncEnd` event](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#asyncendevent) when the
* returned promise is resolved or rejected. It may also produce an
* [`error` event](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html#errorevent) if the given function throws an error or the returned promise
* is rejected. This will run the given function using
* [`channel.runStores(context, ...)`](https://nodejs.org/docs/latest-v26.x/api/diagnostics_channel.html##channelrunstorescontext-fn-thisarg-args) on the `start` channel which ensures all
* events should have any bound stores set to match this trace context.
*
* If the value returned by `fn` is not a Promise or thenable, then it will be
* returned with a warning, and no `asyncStart` or `asyncEnd` events will be
* produced.
*
* To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions
* which are added after the trace begins will not receive future events from that trace, only future traces will be seen.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* channels.tracePromise(async () => {
* // Do something
* }, {
* some: 'thing',
* });
* ```
* @since v19.9.0
* @experimental
* @param fn Function to wrap a trace around
* @param context Shared object to correlate trace events through
* @param thisArg The receiver to be used for the function call
* @param args Optional arguments to pass to the function
* @returns The return value of the given function, or the result of
* calling `.then(...)` on the return value if the tracing channel has active
* subscribers. If the return value is not a Promise or thenable, then
* it is returned as-is and a warning is emitted.
*/
tracePromise<ThisArg = any, Args extends any[] = any[], Result extends PromiseLike<unknown> = any>(
fn: (this: ThisArg, ...args: Args) => Result,
context?: ContextType,
thisArg?: ThisArg,
...args: Args
): Result;
/**
* Trace a callback-receiving function call. This will always produce a `start event` and `end event` around the synchronous portion of the
* function execution, and will produce a `asyncStart event` and `asyncEnd event` around the callback execution. It may also produce an `error event` if the given function throws an error or
* the returned
* promise rejects. This will run the given function using `channel.runStores(context, ...)` on the `start` channel which ensures all
* events should have any bound stores set to match this trace context.
*
* The `position` will be -1 by default to indicate the final argument should
* be used as the callback.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* channels.traceCallback((arg1, callback) => {
* // Do something
* callback(null, 'result');
* }, 1, {
* some: 'thing',
* }, thisArg, arg1, callback);
* ```
*
* The callback will also be run with `channel.runStores(context, ...)` which
* enables context loss recovery in some cases.
*
* To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions
* which are added after the trace begins will not receive future events from that trace, only future traces will be seen.
*
* ```js
* import diagnostics_channel from 'node:diagnostics_channel';
* import { AsyncLocalStorage } from 'node:async_hooks';
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
* const myStore = new AsyncLocalStorage();
*
* // The start channel sets the initial store data to something
* // and stores that store data value on the trace context object
* channels.start.bindStore(myStore, (data) => {
* const span = new Span(data);
* data.span = span;
* return span;
* });
*
* // Then asyncStart can restore from that data it stored previously
* channels.asyncStart.bindStore(myStore, (data) => {
* return data.span;
* });
* ```
* @since v19.9.0
* @experimental
* @param fn callback using function to wrap a trace around
* @param position Zero-indexed argument position of expected callback
* @param context Shared object to correlate trace events through
* @param thisArg The receiver to be used for the function call
* @param args Optional arguments to pass to the function
* @return The return value of the given function
*/
traceCallback<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Result,
position?: number,
context?: ContextType,
thisArg?: ThisArg,
...args: Args
): Result;
/**
* `true` if any of the individual channels has a subscriber, `false` if not.
*
* This is a helper method available on a {@link TracingChannel} instance to check
* if any of the [TracingChannel Channels](https://nodejs.org/api/diagnostics_channel.html#tracingchannel-channels) have subscribers.
* A `true` is returned if any of them have at least one subscriber, a `false` is returned otherwise.
*
* ```js
* const diagnostics_channel = require('node:diagnostics_channel');
*
* const channels = diagnostics_channel.tracingChannel('my-channel');
*
* if (channels.hasSubscribers) {
* // Do something
* }
* ```
* @since v22.0.0, v20.13.0
*/
readonly hasSubscribers: boolean;
}
interface BoundedChannelSubscribers<ContextType extends object> {
start: (message: ContextType) => void;
end: (message: ContextType) => void;
}
interface BoundedChannelCollection<ContextType extends object = object, StoreType = ContextType> {
start: Channel<ContextType, StoreType>;
end: Channel<ContextType, StoreType>;
}
/**
* The class `BoundedChannel` is a simplified version of {@link TracingChannel} that
* only traces synchronous operations. It consists of two channels (`start` and
* `end`) instead of five, omitting the `asyncStart`, `asyncEnd`, and `error`
* events. This makes it suitable for tracing operations that don't involve
* asynchronous continuations or error handling.
*
* Like `TracingChannel`, it is recommended to create and reuse a single
* `BoundedChannel` at the top-level of the file rather than creating them
* dynamically.
* @since v26.1.0
* @experimental
*/
interface BoundedChannel<ContextType extends object = object, StoreType = ContextType>
extends BoundedChannelCollection<ContextType, StoreType>
{
/**
* Check if any of the `start` or `end` channels have subscribers.
*
* ```js
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* if (wc.hasSubscribers) {
* // There are subscribers, perform traced operation
* }
* ```
* @since v26.1.0
*/
readonly hasSubscribers: boolean;
/**
* Subscribe to the bounded channel events. This is equivalent to calling
* [`channel.subscribe(onMessage)`][] on each channel individually.
*
* ```mjs
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* wc.subscribe({
* start(message) {
* // Handle start
* },
* end(message) {
* // Handle end
* },
* });
* ```
* @since v26.1.0
* @param handlers Set of channel subscribers
*/
subscribe(handlers: NodeJS.PartialOptions<BoundedChannelSubscribers<ContextType>>): void;
/**
* Unsubscribe from the bounded channel events. This is equivalent to calling
* [`channel.unsubscribe(onMessage)`][] on each channel individually.
*
* ```js
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* const handlers = {
* start(message) {},
* end(message) {},
* };
*
* wc.subscribe(handlers);
* wc.unsubscribe(handlers);
* ```
* @since v26.1.0
* @param handlers Set of channel subscribers
* @returns `true` if all handlers were successfully unsubscribed,
* `false` otherwise.
*/
unsubscribe(handlers: NodeJS.PartialOptions<BoundedChannelSubscribers<ContextType>>): boolean;
/**
* Trace a synchronous function call. This will produce a `start` event and `end`
* event around the execution. This runs the given function using
* [`channel.runStores(context, ...)`][] on the `start` channel which ensures all
* events have any bound stores set to match this trace context.
*
* ```js
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* const result = wc.run({ operationId: '123' }, () => {
* // Perform operation
* return 42;
* });
* ```
* @since v26.1.0
* @param context Shared object to correlate events through
* @param fn Function to wrap a trace around
* @param thisArg The receiver to be used for the function call
* @param args Optional arguments to pass to the function
* @returns The return value of the given function
*/
run<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Result,
context?: ContextType,
thisArg?: ThisArg,
...args: Args
): Result;
/**
* Create a disposable scope for tracing a synchronous operation using JavaScript's
* explicit resource management (`using` syntax). The scope automatically publishes
* `start` and `end` events, enters bound stores, and handles cleanup when disposed.
*
* ```js
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* const context = { operationId: '123' };
* {
* using scope = wc.withScope(context);
* // Stores are entered, start event is published
*
* // Perform work and set result on context
* context.result = 42;
* }
* // End event is published, stores are restored automatically
* ```
* @since v26.1.0
* @param context Shared object to correlate events through
* @returns Disposable scope object
*/
withScope(context: ContextType): BoundedChannelScope;
}
/**
* The class `BoundedChannelScope` represents a disposable scope created by
* `boundedChannel.withScope(context)`. It manages the lifecycle of a traced
* operation, automatically publishing events and managing store contexts.
*
* The scope must be used with the `using` syntax to ensure proper disposal.
*
* ```js
* import { boundedChannel } from 'node:diagnostics_channel';
*
* const wc = boundedChannel('my-operation');
*
* const context = {};
* {
* using scope = wc.withScope(context);
* // Start event is published, stores are entered
* context.result = performOperation();
* // End event is automatically published at end of block
* }
* ```
* @since v26.1.0
* @experimental
*/
interface BoundedChannelScope extends Disposable {}
}
declare module "diagnostics_channel" {
export * from "node:diagnostics_channel";
}
+876
View File
@@ -0,0 +1,876 @@
declare module "node:dns" {
// Supported getaddrinfo flags.
/**
* Limits returned address types to the types of non-loopback addresses configured on the system. For example, IPv4 addresses are
* only returned if the current system has at least one IPv4 address configured.
*/
const ADDRCONFIG: number;
/**
* If the IPv6 family was specified, but no IPv6 addresses were found, then return IPv4 mapped IPv6 addresses. It is not supported
* on some operating systems (e.g. FreeBSD 10.1).
*/
const V4MAPPED: number;
/**
* If `dns.V4MAPPED` is specified, return resolved IPv6 addresses as
* well as IPv4 mapped IPv6 addresses.
*/
const ALL: number;
interface LookupOptions {
/**
* The record family. Must be `4`, `6`, or `0`. For backward compatibility reasons, `'IPv4'` and `'IPv6'` are interpreted
* as `4` and `6` respectively. The value 0 indicates that either an IPv4 or IPv6 address is returned. If the value `0` is used
* with `{ all: true } (see below)`, both IPv4 and IPv6 addresses are returned.
* @default 0
*/
family?: number | "IPv4" | "IPv6" | undefined;
/**
* One or more [supported `getaddrinfo`](https://nodejs.org/docs/latest-v26.x/api/dns.html#supported-getaddrinfo-flags) flags. Multiple flags may be
* passed by bitwise `OR`ing their values.
*/
hints?: number | undefined;
/**
* When `true`, the callback returns all resolved addresses in an array. Otherwise, returns a single address.
* @default false
*/
all?: boolean | undefined;
/**
* When `verbatim`, the resolved addresses are returned unsorted. When `ipv4first`, the resolved addresses are sorted
* by placing IPv4 addresses before IPv6 addresses. When `ipv6first`, the resolved addresses are sorted by placing IPv6
* addresses before IPv4 addresses. Default value is configurable using
* {@link setDefaultResultOrder} or [`--dns-result-order`](https://nodejs.org/docs/latest-v26.x/api/cli.html#--dns-result-orderorder).
* @default `verbatim` (addresses are not reordered)
* @since v22.1.0
*/
order?: "ipv4first" | "ipv6first" | "verbatim" | undefined;
/**
* When `true`, the callback receives IPv4 and IPv6 addresses in the order the DNS resolver returned them. When `false`, IPv4
* addresses are placed before IPv6 addresses. This option will be deprecated in favor of `order`. When both are specified,
* `order` has higher precedence. New code should only use `order`. Default value is configurable using {@link setDefaultResultOrder}
* @default true (addresses are not reordered)
* @deprecated Please use `order` option
*/
verbatim?: boolean | undefined;
}
interface LookupOneOptions extends LookupOptions {
all?: false | undefined;
}
interface LookupAllOptions extends LookupOptions {
all: true;
}
interface LookupAddress {
/**
* A string representation of an IPv4 or IPv6 address.
*/
address: string;
/**
* `4` or `6`, denoting the family of `address`, or `0` if the address is not an IPv4 or IPv6 address. `0` is a likely indicator of a
* bug in the name resolution service used by the operating system.
*/
family: number;
}
/**
* Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
* AAAA (IPv6) record. All `option` properties are optional. If `options` is an
* integer, then it must be `4` or `6` if `options` is `0` or not provided, then
* IPv4 and IPv6 addresses are both returned if found.
*
* With the `all` option set to `true`, the arguments for `callback` change to `(err, addresses)`, with `addresses` being an array of objects with the
* properties `address` and `family`.
*
* On error, `err` is an `Error` object, where `err.code` is the error code.
* Keep in mind that `err.code` will be set to `'ENOTFOUND'` not only when
* the host name does not exist but also when the lookup fails in other ways
* such as no available file descriptors.
*
* `dns.lookup()` does not necessarily have anything to do with the DNS protocol.
* The implementation uses an operating system facility that can associate names
* with addresses and vice versa. This implementation can have subtle but
* important consequences on the behavior of any Node.js program. Please take some
* time to consult the [Implementation considerations section](https://nodejs.org/docs/latest-v26.x/api/dns.html#implementation-considerations)
* before using `dns.lookup()`.
*
* Example usage:
*
* ```js
* import dns from 'node:dns';
* const options = {
* family: 6,
* hints: dns.ADDRCONFIG | dns.V4MAPPED,
* };
* dns.lookup('example.com', options, (err, address, family) =>
* console.log('address: %j family: IPv%s', address, family));
* // address: "2606:2800:220:1:248:1893:25c8:1946" family: IPv6
*
* // When options.all is true, the result will be an Array.
* options.all = true;
* dns.lookup('example.com', options, (err, addresses) =>
* console.log('addresses: %j', addresses));
* // addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]
* ```
*
* If this method is invoked as its [util.promisify()](https://nodejs.org/docs/latest-v26.x/api/util.html#utilpromisifyoriginal) ed
* version, and `all` is not set to `true`, it returns a `Promise` for an `Object` with `address` and `family` properties.
* @since v0.1.90
*/
function lookup(
hostname: string,
family: number,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
function lookup(
hostname: string,
options: LookupOneOptions,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
function lookup(
hostname: string,
options: LookupAllOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void,
): void;
function lookup(
hostname: string,
options: LookupOptions,
callback: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family: number) => void,
): void;
function lookup(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
namespace lookup {
function __promisify__(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
function __promisify__(hostname: string, options?: LookupOneOptions | number): Promise<LookupAddress>;
function __promisify__(hostname: string, options: LookupOptions): Promise<LookupAddress | LookupAddress[]>;
}
/**
* Resolves the given `address` and `port` into a host name and service using
* the operating system's underlying `getnameinfo` implementation.
*
* If `address` is not a valid IP address, a `TypeError` will be thrown.
* The `port` will be coerced to a number. If it is not a legal port, a `TypeError` will be thrown.
*
* On an error, `err` is an [`Error`](https://nodejs.org/docs/latest-v26.x/api/errors.html#class-error) object,
* where `err.code` is the error code.
*
* ```js
* import dns from 'node:dns';
* dns.lookupService('127.0.0.1', 22, (err, hostname, service) => {
* console.log(hostname, service);
* // Prints: localhost ssh
* });
* ```
*
* If this method is invoked as its [util.promisify()](https://nodejs.org/docs/latest-v26.x/api/util.html#utilpromisifyoriginal) ed
* version, it returns a `Promise` for an `Object` with `hostname` and `service` properties.
* @since v0.11.14
*/
function lookupService(
address: string,
port: number,
callback: (err: NodeJS.ErrnoException | null, hostname: string, service: string) => void,
): void;
namespace lookupService {
function __promisify__(
address: string,
port: number,
): Promise<{
hostname: string;
service: string;
}>;
}
interface ResolveOptions {
ttl: boolean;
}
interface ResolveWithTtlOptions extends ResolveOptions {
ttl: true;
}
interface RecordWithTtl {
address: string;
ttl: number;
}
interface AnyARecord extends RecordWithTtl {
type: "A";
}
interface AnyAaaaRecord extends RecordWithTtl {
type: "AAAA";
}
interface CaaRecord {
critical: number;
issue?: string | undefined;
issuewild?: string | undefined;
iodef?: string | undefined;
contactemail?: string | undefined;
contactphone?: string | undefined;
}
interface AnyCaaRecord extends CaaRecord {
type: "CAA";
}
interface MxRecord {
priority: number;
exchange: string;
}
interface AnyMxRecord extends MxRecord {
type: "MX";
}
interface NaptrRecord {
flags: string;
service: string;
regexp: string;
replacement: string;
order: number;
preference: number;
}
interface AnyNaptrRecord extends NaptrRecord {
type: "NAPTR";
}
interface SoaRecord {
nsname: string;
hostmaster: string;
serial: number;
refresh: number;
retry: number;
expire: number;
minttl: number;
}
interface AnySoaRecord extends SoaRecord {
type: "SOA";
}
interface SrvRecord {
priority: number;
weight: number;
port: number;
name: string;
}
interface AnySrvRecord extends SrvRecord {
type: "SRV";
}
interface TlsaRecord {
certUsage: number;
selector: number;
match: number;
data: ArrayBuffer;
}
interface AnyTlsaRecord extends TlsaRecord {
type: "TLSA";
}
interface AnyTxtRecord {
type: "TXT";
entries: string[];
}
interface AnyNsRecord {
type: "NS";
value: string;
}
interface AnyPtrRecord {
type: "PTR";
value: string;
}
interface AnyCnameRecord {
type: "CNAME";
value: string;
}
type AnyRecord =
| AnyARecord
| AnyAaaaRecord
| AnyCaaRecord
| AnyCnameRecord
| AnyMxRecord
| AnyNaptrRecord
| AnyNsRecord
| AnyPtrRecord
| AnySoaRecord
| AnySrvRecord
| AnyTlsaRecord
| AnyTxtRecord;
/**
* Uses the DNS protocol to resolve a host name (e.g. `'nodejs.org'`) into an array
* of the resource records. The `callback` function has arguments `(err, records)`. When successful, `records` will be an array of resource
* records. The type and structure of individual results varies based on `rrtype`:
*
* <omitted>
*
* On error, `err` is an [`Error`](https://nodejs.org/docs/latest-v26.x/api/errors.html#class-error) object,
* where `err.code` is one of the `DNS error codes`.
* @since v0.1.27
* @param hostname Host name to resolve.
* @param [rrtype='A'] Resource record type.
*/
function resolve(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "A" | "AAAA" | "CNAME" | "NS" | "PTR",
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "ANY",
callback: (err: NodeJS.ErrnoException | null, addresses: AnyRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "CAA",
callback: (err: NodeJS.ErrnoException | null, address: CaaRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "MX",
callback: (err: NodeJS.ErrnoException | null, addresses: MxRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "NAPTR",
callback: (err: NodeJS.ErrnoException | null, addresses: NaptrRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "SOA",
callback: (err: NodeJS.ErrnoException | null, addresses: SoaRecord) => void,
): void;
function resolve(
hostname: string,
rrtype: "SRV",
callback: (err: NodeJS.ErrnoException | null, addresses: SrvRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "TLSA",
callback: (err: NodeJS.ErrnoException | null, addresses: TlsaRecord[]) => void,
): void;
function resolve(
hostname: string,
rrtype: "TXT",
callback: (err: NodeJS.ErrnoException | null, addresses: string[][]) => void,
): void;
function resolve(
hostname: string,
rrtype: string,
callback: (
err: NodeJS.ErrnoException | null,
addresses:
| string[]
| CaaRecord[]
| MxRecord[]
| NaptrRecord[]
| SoaRecord
| SrvRecord[]
| TlsaRecord[]
| string[][]
| AnyRecord[],
) => void,
): void;
namespace resolve {
function __promisify__(hostname: string, rrtype?: "A" | "AAAA" | "CNAME" | "NS" | "PTR"): Promise<string[]>;
function __promisify__(hostname: string, rrtype: "ANY"): Promise<AnyRecord[]>;
function __promisify__(hostname: string, rrtype: "CAA"): Promise<CaaRecord[]>;
function __promisify__(hostname: string, rrtype: "MX"): Promise<MxRecord[]>;
function __promisify__(hostname: string, rrtype: "NAPTR"): Promise<NaptrRecord[]>;
function __promisify__(hostname: string, rrtype: "SOA"): Promise<SoaRecord>;
function __promisify__(hostname: string, rrtype: "SRV"): Promise<SrvRecord[]>;
function __promisify__(hostname: string, rrtype: "TLSA"): Promise<TlsaRecord[]>;
function __promisify__(hostname: string, rrtype: "TXT"): Promise<string[][]>;
function __promisify__(
hostname: string,
rrtype: string,
): Promise<
| string[]
| CaaRecord[]
| MxRecord[]
| NaptrRecord[]
| SoaRecord
| SrvRecord[]
| TlsaRecord[]
| string[][]
| AnyRecord[]
>;
}
/**
* Uses the DNS protocol to resolve a IPv4 addresses (`A` records) for the `hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of IPv4 addresses (e.g.`['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
* @since v0.1.16
* @param hostname Host name to resolve.
*/
function resolve4(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
function resolve4(
hostname: string,
options: ResolveWithTtlOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: RecordWithTtl[]) => void,
): void;
function resolve4(
hostname: string,
options: ResolveOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: string[] | RecordWithTtl[]) => void,
): void;
namespace resolve4 {
function __promisify__(hostname: string): Promise<string[]>;
function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
}
/**
* Uses the DNS protocol to resolve IPv6 addresses (`AAAA` records) for the `hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of IPv6 addresses.
* @since v0.1.16
* @param hostname Host name to resolve.
*/
function resolve6(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
function resolve6(
hostname: string,
options: ResolveWithTtlOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: RecordWithTtl[]) => void,
): void;
function resolve6(
hostname: string,
options: ResolveOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: string[] | RecordWithTtl[]) => void,
): void;
namespace resolve6 {
function __promisify__(hostname: string): Promise<string[]>;
function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
}
/**
* Uses the DNS protocol to resolve `CNAME` records for the `hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of canonical name records available for the `hostname` (e.g. `['bar.example.com']`).
* @since v0.3.2
*/
function resolveCname(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
namespace resolveCname {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve `CAA` records for the `hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of certification authority authorization records
* available for the `hostname` (e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'}, {critical: 128, issue: 'pki.example.com'}]`).
* @since v15.0.0, v14.17.0
*/
function resolveCaa(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, records: CaaRecord[]) => void,
): void;
namespace resolveCaa {
function __promisify__(hostname: string): Promise<CaaRecord[]>;
}
/**
* Uses the DNS protocol to resolve mail exchange records (`MX` records) for the `hostname`. The `addresses` argument passed to the `callback` function will
* contain an array of objects containing both a `priority` and `exchange` property (e.g. `[{priority: 10, exchange: 'mx.example.com'}, ...]`).
* @since v0.1.27
*/
function resolveMx(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: MxRecord[]) => void,
): void;
namespace resolveMx {
function __promisify__(hostname: string): Promise<MxRecord[]>;
}
/**
* Uses the DNS protocol to resolve regular expression-based records (`NAPTR` records) for the `hostname`. The `addresses` argument passed to the `callback` function will contain an array of
* objects with the following properties:
*
* * `flags`
* * `service`
* * `regexp`
* * `replacement`
* * `order`
* * `preference`
*
* ```js
* {
* flags: 's',
* service: 'SIP+D2U',
* regexp: '',
* replacement: '_sip._udp.example.com',
* order: 30,
* preference: 100,
* }
* ```
* @since v0.9.12
*/
function resolveNaptr(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: NaptrRecord[]) => void,
): void;
namespace resolveNaptr {
function __promisify__(hostname: string): Promise<NaptrRecord[]>;
}
/**
* Uses the DNS protocol to resolve name server records (`NS` records) for the `hostname`. The `addresses` argument passed to the `callback` function will
* contain an array of name server records available for `hostname` (e.g. `['ns1.example.com', 'ns2.example.com']`).
* @since v0.1.90
*/
function resolveNs(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
namespace resolveNs {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve pointer records (`PTR` records) for the `hostname`. The `addresses` argument passed to the `callback` function will
* be an array of strings containing the reply records.
* @since v6.0.0
*/
function resolvePtr(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void,
): void;
namespace resolvePtr {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve a start of authority record (`SOA` record) for
* the `hostname`. The `address` argument passed to the `callback` function will
* be an object with the following properties:
*
* * `nsname`
* * `hostmaster`
* * `serial`
* * `refresh`
* * `retry`
* * `expire`
* * `minttl`
*
* ```js
* {
* nsname: 'ns.example.com',
* hostmaster: 'root.example.com',
* serial: 2013101809,
* refresh: 10000,
* retry: 2400,
* expire: 604800,
* minttl: 3600,
* }
* ```
* @since v0.11.10
*/
function resolveSoa(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, address: SoaRecord) => void,
): void;
namespace resolveSoa {
function __promisify__(hostname: string): Promise<SoaRecord>;
}
/**
* Uses the DNS protocol to resolve service records (`SRV` records) for the `hostname`. The `addresses` argument passed to the `callback` function will
* be an array of objects with the following properties:
*
* * `priority`
* * `weight`
* * `port`
* * `name`
*
* ```js
* {
* priority: 10,
* weight: 5,
* port: 21223,
* name: 'service.example.com',
* }
* ```
* @since v0.1.27
*/
function resolveSrv(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: SrvRecord[]) => void,
): void;
namespace resolveSrv {
function __promisify__(hostname: string): Promise<SrvRecord[]>;
}
/**
* Uses the DNS protocol to resolve certificate associations (`TLSA` records) for
* the `hostname`. The `records` argument passed to the `callback` function is an
* array of objects with these properties:
*
* * `certUsage`
* * `selector`
* * `match`
* * `data`
*
* ```js
* {
* certUsage: 3,
* selector: 1,
* match: 1,
* data: [ArrayBuffer],
* }
* ```
* @since v23.9.0, v22.15.0
*/
function resolveTlsa(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: TlsaRecord[]) => void,
): void;
namespace resolveTlsa {
function __promisify__(hostname: string): Promise<TlsaRecord[]>;
}
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the `hostname`. The `records` argument passed to the `callback` function is a
* two-dimensional array of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
* one record. Depending on the use case, these could be either joined together or
* treated separately.
* @since v0.1.27
*/
function resolveTxt(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[][]) => void,
): void;
namespace resolveTxt {
function __promisify__(hostname: string): Promise<string[][]>;
}
/**
* Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
* The `ret` argument passed to the `callback` function will be an array containing
* various types of records. Each object has a property `type` that indicates the
* type of the current record. And depending on the `type`, additional properties
* will be present on the object:
*
* <omitted>
*
* Here is an example of the `ret` object passed to the callback:
*
* ```js
* [ { type: 'A', address: '127.0.0.1', ttl: 299 },
* { type: 'CNAME', value: 'example.com' },
* { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
* { type: 'NS', value: 'ns1.example.com' },
* { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
* { type: 'SOA',
* nsname: 'ns1.example.com',
* hostmaster: 'admin.example.com',
* serial: 156696742,
* refresh: 900,
* retry: 900,
* expire: 1800,
* minttl: 60 } ];
* ```
*
* DNS server operators may choose not to respond to `ANY` queries. It may be better to call individual methods like {@link resolve4}, {@link resolveMx}, and so on. For more details, see
* [RFC 8482](https://tools.ietf.org/html/rfc8482).
*/
function resolveAny(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, addresses: AnyRecord[]) => void,
): void;
namespace resolveAny {
function __promisify__(hostname: string): Promise<AnyRecord[]>;
}
/**
* Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an
* array of host names.
*
* On error, `err` is an [`Error`](https://nodejs.org/docs/latest-v26.x/api/errors.html#class-error) object, where `err.code` is
* one of the [DNS error codes](https://nodejs.org/docs/latest-v26.x/api/dns.html#error-codes).
* @since v0.1.16
*/
function reverse(
ip: string,
callback: (err: NodeJS.ErrnoException | null, hostnames: string[]) => void,
): void;
/**
* Get the default value for `order` in {@link lookup} and [`dnsPromises.lookup()`](https://nodejs.org/docs/latest-v26.x/api/dns.html#dnspromiseslookuphostname-options).
* The value could be:
*
* * `ipv4first`: for `order` defaulting to `ipv4first`.
* * `ipv6first`: for `order` defaulting to `ipv6first`.
* * `verbatim`: for `order` defaulting to `verbatim`.
* @since v18.17.0
*/
function getDefaultResultOrder(): "ipv4first" | "ipv6first" | "verbatim";
/**
* Sets the IP address and port of servers to be used when performing DNS
* resolution. The `servers` argument is an array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted
* addresses. If the port is the IANA default DNS port (53) it can be omitted.
*
* ```js
* dns.setServers([
* '4.4.4.4',
* '[2001:4860:4860::8888]',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]);
* ```
*
* An error will be thrown if an invalid address is provided.
*
* The `dns.setServers()` method must not be called while a DNS query is in
* progress.
*
* The {@link setServers} method affects only {@link resolve}, `dns.resolve*()` and {@link reverse} (and specifically _not_ {@link lookup}).
*
* This method works much like [resolve.conf](https://man7.org/linux/man-pages/man5/resolv.conf.5.html).
* That is, if attempting to resolve with the first server provided results in a `NOTFOUND` error, the `resolve()` method will _not_ attempt to resolve with
* subsequent servers provided. Fallback DNS servers will only be used if the
* earlier ones time out or result in some other error.
* @since v0.11.3
* @param servers array of [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952#section-6) formatted addresses
*/
function setServers(servers: readonly string[]): void;
/**
* Returns an array of IP address strings, formatted according to [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6),
* that are currently configured for DNS resolution. A string will include a port
* section if a custom port is used.
*
* ```js
* [
* '4.4.4.4',
* '2001:4860:4860::8888',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]
* ```
* @since v0.11.3
*/
function getServers(): string[];
/**
* Set the default value of `order` in {@link lookup} and [`dnsPromises.lookup()`](https://nodejs.org/docs/latest-v26.x/api/dns.html#dnspromiseslookuphostname-options).
* The value could be:
*
* * `ipv4first`: sets default `order` to `ipv4first`.
* * `ipv6first`: sets default `order` to `ipv6first`.
* * `verbatim`: sets default `order` to `verbatim`.
*
* The default is `verbatim` and {@link setDefaultResultOrder} have higher
* priority than [`--dns-result-order`](https://nodejs.org/docs/latest-v26.x/api/cli.html#--dns-result-orderorder). When using
* [worker threads](https://nodejs.org/docs/latest-v26.x/api/worker_threads.html), {@link setDefaultResultOrder} from the main
* thread won't affect the default dns orders in workers.
* @since v16.4.0, v14.18.0
* @param order must be `'ipv4first'`, `'ipv6first'` or `'verbatim'`.
*/
function setDefaultResultOrder(order: "ipv4first" | "ipv6first" | "verbatim"): void;
// Error codes
const NODATA: "ENODATA";
const FORMERR: "EFORMERR";
const SERVFAIL: "ESERVFAIL";
const NOTFOUND: "ENOTFOUND";
const NOTIMP: "ENOTIMP";
const REFUSED: "EREFUSED";
const BADQUERY: "EBADQUERY";
const BADNAME: "EBADNAME";
const BADFAMILY: "EBADFAMILY";
const BADRESP: "EBADRESP";
const CONNREFUSED: "ECONNREFUSED";
const TIMEOUT: "ETIMEOUT";
const EOF: "EOF";
const FILE: "EFILE";
const NOMEM: "ENOMEM";
const DESTRUCTION: "EDESTRUCTION";
const BADSTR: "EBADSTR";
const BADFLAGS: "EBADFLAGS";
const NONAME: "ENONAME";
const BADHINTS: "EBADHINTS";
const NOTINITIALIZED: "ENOTINITIALIZED";
const LOADIPHLPAPI: "ELOADIPHLPAPI";
const ADDRGETNETWORKPARAMS: "EADDRGETNETWORKPARAMS";
const CANCELLED: "ECANCELLED";
interface ResolverOptions {
/**
* Query timeout in milliseconds, or `-1` to use the default timeout.
*/
timeout?: number | undefined;
/**
* The number of tries the resolver will try contacting each name server before giving up.
* @default 4
*/
tries?: number | undefined;
/**
* The max retry timeout, in milliseconds.
* @default 0
*/
maxTimeout?: number | undefined;
}
/**
* An independent resolver for DNS requests.
*
* Creating a new resolver uses the default server settings. Setting
* the servers used for a resolver using [`resolver.setServers()`](https://nodejs.org/docs/latest-v26.x/api/dns.html#dnssetserversservers) does not affect
* other resolvers:
*
* ```js
* import { Resolver } from 'node:dns';
* const resolver = new Resolver();
* resolver.setServers(['4.4.4.4']);
*
* // This request will use the server at 4.4.4.4, independent of global settings.
* resolver.resolve4('example.org', (err, addresses) => {
* // ...
* });
* ```
*
* The following methods from the `node:dns` module are available:
*
* * `resolver.getServers()`
* * `resolver.resolve()`
* * `resolver.resolve4()`
* * `resolver.resolve6()`
* * `resolver.resolveAny()`
* * `resolver.resolveCaa()`
* * `resolver.resolveCname()`
* * `resolver.resolveMx()`
* * `resolver.resolveNaptr()`
* * `resolver.resolveNs()`
* * `resolver.resolvePtr()`
* * `resolver.resolveSoa()`
* * `resolver.resolveSrv()`
* * `resolver.resolveTxt()`
* * `resolver.reverse()`
* * `resolver.setServers()`
* @since v8.3.0
*/
class Resolver {
constructor(options?: ResolverOptions);
/**
* Cancel all outstanding DNS queries made by this resolver. The corresponding
* callbacks will be called with an error with code `ECANCELLED`.
* @since v8.3.0
*/
cancel(): void;
getServers: typeof getServers;
resolve: typeof resolve;
resolve4: typeof resolve4;
resolve6: typeof resolve6;
resolveAny: typeof resolveAny;
resolveCaa: typeof resolveCaa;
resolveCname: typeof resolveCname;
resolveMx: typeof resolveMx;
resolveNaptr: typeof resolveNaptr;
resolveNs: typeof resolveNs;
resolvePtr: typeof resolvePtr;
resolveSoa: typeof resolveSoa;
resolveSrv: typeof resolveSrv;
resolveTlsa: typeof resolveTlsa;
resolveTxt: typeof resolveTxt;
reverse: typeof reverse;
/**
* The resolver instance will send its requests from the specified IP address.
* This allows programs to specify outbound interfaces when used on multi-homed
* systems.
*
* If a v4 or v6 address is not specified, it is set to the default and the
* operating system will choose a local address automatically.
*
* The resolver will use the v4 local address when making requests to IPv4 DNS
* servers, and the v6 local address when making requests to IPv6 DNS servers.
* The `rrtype` of resolution requests has no impact on the local address used.
* @since v15.1.0, v14.17.0
* @param [ipv4='0.0.0.0'] A string representation of an IPv4 address.
* @param [ipv6='::0'] A string representation of an IPv6 address.
*/
setLocalAddress(ipv4?: string, ipv6?: string): void;
setServers: typeof setServers;
}
}
declare module "node:dns" {
export * as promises from "node:dns/promises";
}
declare module "dns" {
export * from "node:dns";
}
+497
View File
@@ -0,0 +1,497 @@
declare module "node:dns/promises" {
import {
AnyRecord,
CaaRecord,
LookupAddress,
LookupAllOptions,
LookupOneOptions,
LookupOptions,
MxRecord,
NaptrRecord,
RecordWithTtl,
ResolveOptions,
ResolverOptions,
ResolveWithTtlOptions,
SoaRecord,
SrvRecord,
TlsaRecord,
} from "node:dns";
/**
* Returns an array of IP address strings, formatted according to [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6),
* that are currently configured for DNS resolution. A string will include a port
* section if a custom port is used.
*
* ```js
* [
* '4.4.4.4',
* '2001:4860:4860::8888',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]
* ```
* @since v10.6.0
*/
function getServers(): string[];
/**
* Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
* AAAA (IPv6) record. All `option` properties are optional. If `options` is an
* integer, then it must be `4` or `6` if `options` is not provided, then IPv4
* and IPv6 addresses are both returned if found.
*
* With the `all` option set to `true`, the `Promise` is resolved with `addresses` being an array of objects with the properties `address` and `family`.
*
* On error, the `Promise` is rejected with an [`Error`](https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error) object, where `err.code` is the error code.
* Keep in mind that `err.code` will be set to `'ENOTFOUND'` not only when
* the host name does not exist but also when the lookup fails in other ways
* such as no available file descriptors.
*
* [`dnsPromises.lookup()`](https://nodejs.org/docs/latest-v20.x/api/dns.html#dnspromiseslookuphostname-options) does not necessarily have anything to do with the DNS
* protocol. The implementation uses an operating system facility that can
* associate names with addresses and vice versa. This implementation can have
* subtle but important consequences on the behavior of any Node.js program. Please
* take some time to consult the [Implementation considerations section](https://nodejs.org/docs/latest-v20.x/api/dns.html#implementation-considerations) before
* using `dnsPromises.lookup()`.
*
* Example usage:
*
* ```js
* import dns from 'node:dns';
* const dnsPromises = dns.promises;
* const options = {
* family: 6,
* hints: dns.ADDRCONFIG | dns.V4MAPPED,
* };
*
* dnsPromises.lookup('example.com', options).then((result) => {
* console.log('address: %j family: IPv%s', result.address, result.family);
* // address: "2606:2800:220:1:248:1893:25c8:1946" family: IPv6
* });
*
* // When options.all is true, the result will be an Array.
* options.all = true;
* dnsPromises.lookup('example.com', options).then((result) => {
* console.log('addresses: %j', result);
* // addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]
* });
* ```
* @since v10.6.0
*/
function lookup(hostname: string, family: number): Promise<LookupAddress>;
function lookup(hostname: string, options: LookupOneOptions): Promise<LookupAddress>;
function lookup(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
function lookup(hostname: string, options: LookupOptions): Promise<LookupAddress | LookupAddress[]>;
function lookup(hostname: string): Promise<LookupAddress>;
/**
* Resolves the given `address` and `port` into a host name and service using
* the operating system's underlying `getnameinfo` implementation.
*
* If `address` is not a valid IP address, a `TypeError` will be thrown.
* The `port` will be coerced to a number. If it is not a legal port, a `TypeError` will be thrown.
*
* On error, the `Promise` is rejected with an [`Error`](https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error) object, where `err.code` is the error code.
*
* ```js
* import dnsPromises from 'node:dns';
* dnsPromises.lookupService('127.0.0.1', 22).then((result) => {
* console.log(result.hostname, result.service);
* // Prints: localhost ssh
* });
* ```
* @since v10.6.0
*/
function lookupService(
address: string,
port: number,
): Promise<{
hostname: string;
service: string;
}>;
/**
* Uses the DNS protocol to resolve a host name (e.g. `'nodejs.org'`) into an array
* of the resource records. When successful, the `Promise` is resolved with an
* array of resource records. The type and structure of individual results vary
* based on `rrtype`:
*
* <omitted>
*
* On error, the `Promise` is rejected with an [`Error`](https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error) object, where `err.code`
* is one of the [DNS error codes](https://nodejs.org/docs/latest-v20.x/api/dns.html#error-codes).
* @since v10.6.0
* @param hostname Host name to resolve.
* @param [rrtype='A'] Resource record type.
*/
function resolve(hostname: string): Promise<string[]>;
function resolve(hostname: string, rrtype: "A" | "AAAA" | "CNAME" | "NS" | "PTR"): Promise<string[]>;
function resolve(hostname: string, rrtype: "ANY"): Promise<AnyRecord[]>;
function resolve(hostname: string, rrtype: "CAA"): Promise<CaaRecord[]>;
function resolve(hostname: string, rrtype: "MX"): Promise<MxRecord[]>;
function resolve(hostname: string, rrtype: "NAPTR"): Promise<NaptrRecord[]>;
function resolve(hostname: string, rrtype: "SOA"): Promise<SoaRecord>;
function resolve(hostname: string, rrtype: "SRV"): Promise<SrvRecord[]>;
function resolve(hostname: string, rrtype: "TLSA"): Promise<TlsaRecord[]>;
function resolve(hostname: string, rrtype: "TXT"): Promise<string[][]>;
function resolve(hostname: string, rrtype: string): Promise<
| string[]
| CaaRecord[]
| MxRecord[]
| NaptrRecord[]
| SoaRecord
| SrvRecord[]
| TlsaRecord[]
| string[][]
| AnyRecord[]
>;
/**
* Uses the DNS protocol to resolve IPv4 addresses (`A` records) for the `hostname`. On success, the `Promise` is resolved with an array of IPv4
* addresses (e.g. `['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
* @since v10.6.0
* @param hostname Host name to resolve.
*/
function resolve4(hostname: string): Promise<string[]>;
function resolve4(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function resolve4(hostname: string, options: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
/**
* Uses the DNS protocol to resolve IPv6 addresses (`AAAA` records) for the `hostname`. On success, the `Promise` is resolved with an array of IPv6
* addresses.
* @since v10.6.0
* @param hostname Host name to resolve.
*/
function resolve6(hostname: string): Promise<string[]>;
function resolve6(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function resolve6(hostname: string, options: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
/**
* Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
* On success, the `Promise` is resolved with an array containing various types of
* records. Each object has a property `type` that indicates the type of the
* current record. And depending on the `type`, additional properties will be
* present on the object:
*
* <omitted>
*
* Here is an example of the result object:
*
* ```js
* [ { type: 'A', address: '127.0.0.1', ttl: 299 },
* { type: 'CNAME', value: 'example.com' },
* { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
* { type: 'NS', value: 'ns1.example.com' },
* { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
* { type: 'SOA',
* nsname: 'ns1.example.com',
* hostmaster: 'admin.example.com',
* serial: 156696742,
* refresh: 900,
* retry: 900,
* expire: 1800,
* minttl: 60 } ];
* ```
* @since v10.6.0
*/
function resolveAny(hostname: string): Promise<AnyRecord[]>;
/**
* Uses the DNS protocol to resolve `CAA` records for the `hostname`. On success,
* the `Promise` is resolved with an array of objects containing available
* certification authority authorization records available for the `hostname` (e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'},{critical: 128, issue: 'pki.example.com'}]`).
* @since v15.0.0, v14.17.0
*/
function resolveCaa(hostname: string): Promise<CaaRecord[]>;
/**
* Uses the DNS protocol to resolve `CNAME` records for the `hostname`. On success,
* the `Promise` is resolved with an array of canonical name records available for
* the `hostname` (e.g. `['bar.example.com']`).
* @since v10.6.0
*/
function resolveCname(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve mail exchange records (`MX` records) for the `hostname`. On success, the `Promise` is resolved with an array of objects
* containing both a `priority` and `exchange` property (e.g.`[{priority: 10, exchange: 'mx.example.com'}, ...]`).
* @since v10.6.0
*/
function resolveMx(hostname: string): Promise<MxRecord[]>;
/**
* Uses the DNS protocol to resolve regular expression-based records (`NAPTR` records) for the `hostname`. On success, the `Promise` is resolved with an array
* of objects with the following properties:
*
* * `flags`
* * `service`
* * `regexp`
* * `replacement`
* * `order`
* * `preference`
*
* ```js
* {
* flags: 's',
* service: 'SIP+D2U',
* regexp: '',
* replacement: '_sip._udp.example.com',
* order: 30,
* preference: 100,
* }
* ```
* @since v10.6.0
*/
function resolveNaptr(hostname: string): Promise<NaptrRecord[]>;
/**
* Uses the DNS protocol to resolve name server records (`NS` records) for the `hostname`. On success, the `Promise` is resolved with an array of name server
* records available for `hostname` (e.g.`['ns1.example.com', 'ns2.example.com']`).
* @since v10.6.0
*/
function resolveNs(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve pointer records (`PTR` records) for the `hostname`. On success, the `Promise` is resolved with an array of strings
* containing the reply records.
* @since v10.6.0
*/
function resolvePtr(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve a start of authority record (`SOA` record) for
* the `hostname`. On success, the `Promise` is resolved with an object with the
* following properties:
*
* * `nsname`
* * `hostmaster`
* * `serial`
* * `refresh`
* * `retry`
* * `expire`
* * `minttl`
*
* ```js
* {
* nsname: 'ns.example.com',
* hostmaster: 'root.example.com',
* serial: 2013101809,
* refresh: 10000,
* retry: 2400,
* expire: 604800,
* minttl: 3600,
* }
* ```
* @since v10.6.0
*/
function resolveSoa(hostname: string): Promise<SoaRecord>;
/**
* Uses the DNS protocol to resolve service records (`SRV` records) for the `hostname`. On success, the `Promise` is resolved with an array of objects with
* the following properties:
*
* * `priority`
* * `weight`
* * `port`
* * `name`
*
* ```js
* {
* priority: 10,
* weight: 5,
* port: 21223,
* name: 'service.example.com',
* }
* ```
* @since v10.6.0
*/
function resolveSrv(hostname: string): Promise<SrvRecord[]>;
/**
* Uses the DNS protocol to resolve certificate associations (`TLSA` records) for
* the `hostname`. On success, the `Promise` is resolved with an array of objectsAdd commentMore actions
* with these properties:
*
* * `certUsage`
* * `selector`
* * `match`
* * `data`
*
* ```js
* {
* certUsage: 3,
* selector: 1,
* match: 1,
* data: [ArrayBuffer],
* }
* ```
* @since v23.9.0, v22.15.0
*/
function resolveTlsa(hostname: string): Promise<TlsaRecord[]>;
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the `hostname`. On success, the `Promise` is resolved with a two-dimensional array
* of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
* one record. Depending on the use case, these could be either joined together or
* treated separately.
* @since v10.6.0
*/
function resolveTxt(hostname: string): Promise<string[][]>;
/**
* Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an
* array of host names.
*
* On error, the `Promise` is rejected with an [`Error`](https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error) object, where `err.code`
* is one of the [DNS error codes](https://nodejs.org/docs/latest-v20.x/api/dns.html#error-codes).
* @since v10.6.0
*/
function reverse(ip: string): Promise<string[]>;
/**
* Get the default value for `verbatim` in {@link lookup} and [dnsPromises.lookup()](https://nodejs.org/docs/latest-v20.x/api/dns.html#dnspromiseslookuphostname-options).
* The value could be:
*
* * `ipv4first`: for `verbatim` defaulting to `false`.
* * `verbatim`: for `verbatim` defaulting to `true`.
* @since v20.1.0
*/
function getDefaultResultOrder(): "ipv4first" | "verbatim";
/**
* Sets the IP address and port of servers to be used when performing DNS
* resolution. The `servers` argument is an array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted
* addresses. If the port is the IANA default DNS port (53) it can be omitted.
*
* ```js
* dnsPromises.setServers([
* '4.4.4.4',
* '[2001:4860:4860::8888]',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]);
* ```
*
* An error will be thrown if an invalid address is provided.
*
* The `dnsPromises.setServers()` method must not be called while a DNS query is in
* progress.
*
* This method works much like [resolve.conf](https://man7.org/linux/man-pages/man5/resolv.conf.5.html).
* That is, if attempting to resolve with the first server provided results in a `NOTFOUND` error, the `resolve()` method will _not_ attempt to resolve with
* subsequent servers provided. Fallback DNS servers will only be used if the
* earlier ones time out or result in some other error.
* @since v10.6.0
* @param servers array of `RFC 5952` formatted addresses
*/
function setServers(servers: readonly string[]): void;
/**
* Set the default value of `order` in `dns.lookup()` and `{@link lookup}`. The value could be:
*
* * `ipv4first`: sets default `order` to `ipv4first`.
* * `ipv6first`: sets default `order` to `ipv6first`.
* * `verbatim`: sets default `order` to `verbatim`.
*
* The default is `verbatim` and [dnsPromises.setDefaultResultOrder()](https://nodejs.org/docs/latest-v20.x/api/dns.html#dnspromisessetdefaultresultorderorder)
* have higher priority than [`--dns-result-order`](https://nodejs.org/docs/latest-v20.x/api/cli.html#--dns-result-orderorder).
* When using [worker threads](https://nodejs.org/docs/latest-v20.x/api/worker_threads.html), [`dnsPromises.setDefaultResultOrder()`](https://nodejs.org/docs/latest-v20.x/api/dns.html#dnspromisessetdefaultresultorderorder)
* from the main thread won't affect the default dns orders in workers.
* @since v16.4.0, v14.18.0
* @param order must be `'ipv4first'`, `'ipv6first'` or `'verbatim'`.
*/
function setDefaultResultOrder(order: "ipv4first" | "ipv6first" | "verbatim"): void;
// Error codes
const NODATA: "ENODATA";
const FORMERR: "EFORMERR";
const SERVFAIL: "ESERVFAIL";
const NOTFOUND: "ENOTFOUND";
const NOTIMP: "ENOTIMP";
const REFUSED: "EREFUSED";
const BADQUERY: "EBADQUERY";
const BADNAME: "EBADNAME";
const BADFAMILY: "EBADFAMILY";
const BADRESP: "EBADRESP";
const CONNREFUSED: "ECONNREFUSED";
const TIMEOUT: "ETIMEOUT";
const EOF: "EOF";
const FILE: "EFILE";
const NOMEM: "ENOMEM";
const DESTRUCTION: "EDESTRUCTION";
const BADSTR: "EBADSTR";
const BADFLAGS: "EBADFLAGS";
const NONAME: "ENONAME";
const BADHINTS: "EBADHINTS";
const NOTINITIALIZED: "ENOTINITIALIZED";
const LOADIPHLPAPI: "ELOADIPHLPAPI";
const ADDRGETNETWORKPARAMS: "EADDRGETNETWORKPARAMS";
const CANCELLED: "ECANCELLED";
/**
* An independent resolver for DNS requests.
*
* Creating a new resolver uses the default server settings. Setting
* the servers used for a resolver using [`resolver.setServers()`](https://nodejs.org/docs/latest-v20.x/api/dns.html#dnspromisessetserversservers) does not affect
* other resolvers:
*
* ```js
* import { promises } from 'node:dns';
* const resolver = new promises.Resolver();
* resolver.setServers(['4.4.4.4']);
*
* // This request will use the server at 4.4.4.4, independent of global settings.
* resolver.resolve4('example.org').then((addresses) => {
* // ...
* });
*
* // Alternatively, the same code can be written using async-await style.
* (async function() {
* const addresses = await resolver.resolve4('example.org');
* })();
* ```
*
* The following methods from the `dnsPromises` API are available:
*
* * `resolver.getServers()`
* * `resolver.resolve()`
* * `resolver.resolve4()`
* * `resolver.resolve6()`
* * `resolver.resolveAny()`
* * `resolver.resolveCaa()`
* * `resolver.resolveCname()`
* * `resolver.resolveMx()`
* * `resolver.resolveNaptr()`
* * `resolver.resolveNs()`
* * `resolver.resolvePtr()`
* * `resolver.resolveSoa()`
* * `resolver.resolveSrv()`
* * `resolver.resolveTxt()`
* * `resolver.reverse()`
* * `resolver.setServers()`
* @since v10.6.0
*/
class Resolver {
constructor(options?: ResolverOptions);
/**
* Cancel all outstanding DNS queries made by this resolver. The corresponding
* callbacks will be called with an error with code `ECANCELLED`.
* @since v8.3.0
*/
cancel(): void;
getServers: typeof getServers;
resolve: typeof resolve;
resolve4: typeof resolve4;
resolve6: typeof resolve6;
resolveAny: typeof resolveAny;
resolveCaa: typeof resolveCaa;
resolveCname: typeof resolveCname;
resolveMx: typeof resolveMx;
resolveNaptr: typeof resolveNaptr;
resolveNs: typeof resolveNs;
resolvePtr: typeof resolvePtr;
resolveSoa: typeof resolveSoa;
resolveSrv: typeof resolveSrv;
resolveTlsa: typeof resolveTlsa;
resolveTxt: typeof resolveTxt;
reverse: typeof reverse;
/**
* The resolver instance will send its requests from the specified IP address.
* This allows programs to specify outbound interfaces when used on multi-homed
* systems.
*
* If a v4 or v6 address is not specified, it is set to the default and the
* operating system will choose a local address automatically.
*
* The resolver will use the v4 local address when making requests to IPv4 DNS
* servers, and the v6 local address when making requests to IPv6 DNS servers.
* The `rrtype` of resolution requests has no impact on the local address used.
* @since v15.1.0, v14.17.0
* @param [ipv4='0.0.0.0'] A string representation of an IPv4 address.
* @param [ipv6='::0'] A string representation of an IPv6 address.
*/
setLocalAddress(ipv4?: string, ipv6?: string): void;
setServers: typeof setServers;
}
}
declare module "dns/promises" {
export * from "node:dns/promises";
}
+150
View File
@@ -0,0 +1,150 @@
declare module "node:domain" {
import { EventEmitter } from "node:events";
/**
* The `Domain` class encapsulates the functionality of routing errors and
* uncaught exceptions to the active `Domain` object.
*
* To handle the errors that it catches, listen to its `'error'` event.
*/
class Domain extends EventEmitter {
/**
* An array of event emitters that have been explicitly added to the domain.
*/
members: EventEmitter[];
/**
* The `enter()` method is plumbing used by the `run()`, `bind()`, and `intercept()` methods to set the active domain. It sets `domain.active` and `process.domain` to the domain, and implicitly
* pushes the domain onto the domain
* stack managed by the domain module (see {@link exit} for details on the
* domain stack). The call to `enter()` delimits the beginning of a chain of
* asynchronous calls and I/O operations bound to a domain.
*
* Calling `enter()` changes only the active domain, and does not alter the domain
* itself. `enter()` and `exit()` can be called an arbitrary number of times on a
* single domain.
*/
enter(): void;
/**
* The `exit()` method exits the current domain, popping it off the domain stack.
* Any time execution is going to switch to the context of a different chain of
* asynchronous calls, it's important to ensure that the current domain is exited.
* The call to `exit()` delimits either the end of or an interruption to the chain
* of asynchronous calls and I/O operations bound to a domain.
*
* If there are multiple, nested domains bound to the current execution context, `exit()` will exit any domains nested within this domain.
*
* Calling `exit()` changes only the active domain, and does not alter the domain
* itself. `enter()` and `exit()` can be called an arbitrary number of times on a
* single domain.
*/
exit(): void;
/**
* Run the supplied function in the context of the domain, implicitly
* binding all event emitters, timers, and low-level requests that are
* created in that context. Optionally, arguments can be passed to
* the function.
*
* This is the most basic way to use a domain.
*
* ```js
* import domain from 'node:domain';
* import fs from 'node:fs';
* const d = domain.create();
* d.on('error', (er) => {
* console.error('Caught error!', er);
* });
* d.run(() => {
* process.nextTick(() => {
* setTimeout(() => { // Simulating some various async stuff
* fs.open('non-existent file', 'r', (er, fd) => {
* if (er) throw er;
* // proceed...
* });
* }, 100);
* });
* });
* ```
*
* In this example, the `d.on('error')` handler will be triggered, rather
* than crashing the program.
*/
run<T>(fn: (...args: any[]) => T, ...args: any[]): T;
/**
* Explicitly adds an emitter to the domain. If any event handlers called by
* the emitter throw an error, or if the emitter emits an `'error'` event, it
* will be routed to the domain's `'error'` event, just like with implicit
* binding.
*
* If the `EventEmitter` was already bound to a domain, it is removed from that
* one, and bound to this one instead.
* @param emitter emitter to be added to the domain
*/
add(emitter: EventEmitter): void;
/**
* The opposite of {@link add}. Removes domain handling from the
* specified emitter.
* @param emitter emitter to be removed from the domain
*/
remove(emitter: EventEmitter): void;
/**
* The returned function will be a wrapper around the supplied callback
* function. When the returned function is called, any errors that are
* thrown will be routed to the domain's `'error'` event.
*
* ```js
* const d = domain.create();
*
* function readSomeFile(filename, cb) {
* fs.readFile(filename, 'utf8', d.bind((er, data) => {
* // If this throws, it will also be passed to the domain.
* return cb(er, data ? JSON.parse(data) : null);
* }));
* }
*
* d.on('error', (er) => {
* // An error occurred somewhere. If we throw it now, it will crash the program
* // with the normal line number and stack message.
* });
* ```
* @param callback The callback function
* @return The bound function
*/
bind<T extends Function>(callback: T): T;
/**
* This method is almost identical to {@link bind}. However, in
* addition to catching thrown errors, it will also intercept `Error` objects sent as the first argument to the function.
*
* In this way, the common `if (err) return callback(err);` pattern can be replaced
* with a single error handler in a single place.
*
* ```js
* const d = domain.create();
*
* function readSomeFile(filename, cb) {
* fs.readFile(filename, 'utf8', d.intercept((data) => {
* // Note, the first argument is never passed to the
* // callback since it is assumed to be the 'Error' argument
* // and thus intercepted by the domain.
*
* // If this throws, it will also be passed to the domain
* // so the error-handling logic can be moved to the 'error'
* // event on the domain instead of being repeated throughout
* // the program.
* return cb(null, JSON.parse(data));
* }));
* }
*
* d.on('error', (er) => {
* // An error occurred somewhere. If we throw it now, it will crash the program
* // with the normal line number and stack message.
* });
* ```
* @param callback The callback function
* @return The intercepted function
*/
intercept<T extends Function>(callback: T): T;
}
function create(): Domain;
}
declare module "domain" {
export * from "node:domain";
}
+1008
View File
File diff suppressed because it is too large Load Diff
+486
View File
@@ -0,0 +1,486 @@
declare module "node:ffi" {
import { NonSharedBuffer } from "node:buffer";
interface FunctionSignature {
return?: ReturnType | undefined;
arguments?: readonly ArgumentType[] | undefined;
}
interface FunctionDefinitions {
[symbol: string]: FunctionSignature;
}
type CallbackFunction<R extends ReturnType = any, P extends readonly ArgumentType[] = any[]> = (
...args: { [K in keyof P]: ArgumentTypeMap[DataTypeMap[P[K]]] }
) => ReturnTypeMap[DataTypeMap[R]];
interface WrappedFunction<R extends ReturnType = any, P extends readonly ArgumentType[] = any[]>
extends CallbackFunction<R, P>
{
readonly pointer: bigint;
}
type CallbackFunctionFromSignature<T extends FunctionSignature> = CallbackFunction<
ReturnTypeFromFunctionSignature<T>,
ArgumentTypesFromFunctionSignature<T>
>;
type WrappedFunctionFromSignature<T extends FunctionSignature> = WrappedFunction<
ReturnTypeFromFunctionSignature<T>,
ArgumentTypesFromFunctionSignature<T>
>;
type WrappedFunctionsFromDefinitions<T extends FunctionDefinitions> = {
[K in keyof T]: WrappedFunctionFromSignature<T[K]>;
};
type ReturnTypeFromFunctionSignature<T extends FunctionSignature> = "return" extends keyof T
? T extends { return: infer R extends ReturnType } ? R : any
: "void";
type ArgumentTypesFromFunctionSignature<T extends FunctionSignature> = "arguments" extends keyof T
? T extends { arguments: infer P extends readonly ArgumentType[] } ? P : any[]
: [];
interface DynamicLibraryResult<T extends FunctionDefinitions> extends Disposable {
lib: DynamicLibrary;
functions: WrappedFunctionsFromDefinitions<T>;
}
/**
* The native shared library suffix for the current platform:
*
* * `'dylib'` on macOS
* * `'so'` on Unix-like platforms
* * `'dll'` on Windows
*
* This can be used to build portable library paths:
*
* ```js
* const { suffix } = require('node:ffi');
*
* const path = `libsqlite3.${suffix}`;
* ```
* @since v26.1.0
*/
const suffix: string;
/**
* Loads a dynamic library and resolves the requested function definitions.
*
* On Windows passing `null` is not supported.
*
* When `definitions` is omitted, `functions` is returned as an empty object until
* symbols are resolved explicitly.
*
* The returned object also implements the explicit resource management protocol,
* so it can be used with the `using` declaration. Disposing the returned
* object closes the library handle.
*
* ```js
* import { dlopen } from 'node:ffi';
*
* {
* using handle = dlopen('./mylib.so', {
* add_i32: { arguments: ['i32', 'i32'], return: 'i32' },
* });
* console.log(handle.functions.add_i32(20, 22));
* } // handle.lib.close() is invoked automatically here.
* ```
*
* ```js
* import { dlopen } from 'node:ffi';
*
* const { lib, functions } = dlopen('./mylib.so', {
* add_i32: { arguments: ['i32', 'i32'], return: 'i32' },
* string_length: { arguments: ['pointer'], return: 'u64' },
* });
*
* console.log(functions.add_i32(20, 22));
* ```
* @since v26.1.0
* @param path Path to a dynamic library, or `null` to resolve symbols
* from the current process image.
* @param definitions Symbol definitions to resolve immediately.
*/
function dlopen<const T extends FunctionDefinitions = {}>(
path: string | null,
definitions?: T,
): DynamicLibraryResult<T>;
/**
* Closes a dynamic library.
*
* This is equivalent to calling `handle.close()`.
* @since v26.1.0
*/
function dlclose(handle: DynamicLibrary): void;
/**
* Resolves a symbol address from a loaded library.
*
* This is equivalent to calling `handle.getSymbol(symbol)`.
* @since v26.1.0
*/
function dlsym(handle: DynamicLibrary, symbol: string): bigint;
/**
* @since v26.1.0
*/
class DynamicLibrary {
/**
* Loads the dynamic library without resolving any functions eagerly.
*
* On Windows passing `null` is not supported.
*
* ```js
* const { DynamicLibrary } = require('node:ffi');
*
* const lib = new DynamicLibrary('./mylib.so');
* ```
* @param path Path to a dynamic library, or `null` to resolve symbols
* from the current process image.
*/
constructor(path: string | null);
/**
* The path used to load the library.
*/
readonly path: string;
/**
* An object containing previously resolved symbol addresses as `bigint` values.
*/
readonly symbols: { [symbol: string]: bigint };
/**
* Closes the library handle.
*
* `DynamicLibrary` implements the explicit resource management protocol, so a
* library instance can be managed with the `using` declaration. Leaving the
* enclosing scope invokes `library.close()` automatically.
*
* ```js
* import { DynamicLibrary } from 'node:ffi';
*
* {
* using lib = new DynamicLibrary('./mylib.so');
* // Use `lib` here; `lib.close()` is called when the block exits.
* }
* ```
*
* Calling `library.close()` (or disposing the library) more than once is a no-op.
*
* After a library has been closed:
*
* * Resolved function wrappers become invalid.
* * Further symbol and function resolution throws.
* * Registered callbacks are invalidated.
*
* Closing a library does not make previously exported callback pointers safe to
* reuse. Node.js does not track or revoke callback pointers that have already
* been handed to native code.
*
* If native code still holds a callback pointer after `library.close()` or after
* `library.unregisterCallback(pointer)`, invoking that pointer has undefined
* behavior, is not allowed, and is dangerous: it can crash the process, produce
* incorrect output, or corrupt memory. Native code must stop using callback
* addresses before the library is closed or before the callback is unregistered.
*
* Calling `library.close()` from one of the library's active callbacks is
* unsupported and dangerous. The callback must return before the library is
* closed.
*/
close(): void;
/**
* Calls `library.close()`. This allows `DynamicLibrary` instances to be used with
* the `using` declaration for automatic cleanup when the enclosing scope
* exits. It is a no-op on a library that has already been closed.
* @since v26.1.0
*/
[Symbol.dispose](): void;
/**
* Resolves a symbol and returns a callable JavaScript wrapper.
*
* The returned function has a `.pointer` property containing the native function
* address as a `bigint`.
*
* If the same symbol has already been resolved, requesting it again with a
* different signature throws.
*
* ```js
* const { DynamicLibrary } = require('node:ffi');
*
* const lib = new DynamicLibrary('./mylib.so');
* const add = lib.getFunction('add_i32', {
* arguments: ['i32', 'i32'],
* return: 'i32',
* });
*
* console.log(add(20, 22));
* console.log(add.pointer);
* ```
*/
getFunction<const T extends FunctionSignature>(name: string, signature: T): WrappedFunctionFromSignature<T>;
/**
* When `definitions` is provided, resolves each named symbol and returns an
* object containing callable wrappers.
*
* When `definitions` is omitted, returns wrappers for all functions that have
* already been resolved on the library.
*/
getFunctions(): { [symbol: string]: WrappedFunction };
getFunctions<const T extends FunctionDefinitions>(definitions: T): WrappedFunctionsFromDefinitions<T>;
/**
* Resolves a symbol and returns its native address as a `bigint`.
*/
getSymbol(name: string): bigint;
/**
* Returns an object containing all previously resolved symbol addresses.
*/
getSymbols(): Record<string, bigint>;
/**
* Creates a native callback pointer backed by a JavaScript function.
*
* When `signature` is omitted, the callback uses a default `void ()` signature.
*
* The return value is the callback pointer address as a `bigint`. It can be
* passed to native functions expecting a callback pointer.
*
* ```js
* const { DynamicLibrary } = require('node:ffi');
*
* const lib = new DynamicLibrary('./mylib.so');
*
* const callback = lib.registerCallback(
* { arguments: ['i32'], return: 'i32' },
* (value) => value * 2,
* );
* ```
*
* Callbacks are subject to the following restrictions:
*
* * They must be invoked on the same system thread where they were created.
* * They must not throw exceptions.
* * They must not return promises.
* * They must return a value compatible with the declared return type.
* * They must not call `library.close()` on their owning library while running.
* * They must not unregister themselves while running.
*
* Closing the owning library or unregistering the currently executing callback
* from inside the callback is unsupported and dangerous. Doing so may crash the
* process, produce incorrect output, or corrupt memory.
*/
registerCallback(callback: () => void): bigint;
registerCallback<const T extends FunctionSignature>(
signature: T,
callback: CallbackFunctionFromSignature<T>,
): bigint;
/**
* Releases a callback previously created with `library.registerCallback()`.
*
* Calling `library.unregisterCallback(pointer)` for a callback that is currently
* executing is unsupported and dangerous. The callback must return before it is
* unregistered.
*
* After `library.unregisterCallback(pointer)` returns, invoking that callback
* pointer from native code has undefined behavior, is not allowed, and is
* dangerous: it can crash the process, produce incorrect output, or corrupt
* memory.
*/
unregisterCallback(pointer: bigint): void;
/**
* Keeps the callback strongly referenced by JavaScript.
*/
refCallback(pointer: bigint): void;
/**
* Allows the callback to become weakly referenced by JavaScript.
*
* If the callback function is later garbage collected, subsequent native
* invocations become a no-op. Non-void return values are zero-initialized before
* returning to native code.
*/
unrefCallback(pointer: bigint): void;
}
function getInt8(pointer: bigint, offset?: number): number;
function getUint8(pointer: bigint, offset?: number): number;
function getInt16(pointer: bigint, offset?: number): number;
function getUint16(pointer: bigint, offset?: number): number;
function getInt32(pointer: bigint, offset?: number): number;
function getUint32(pointer: bigint, offset?: number): number;
function getInt64(pointer: bigint, offset?: number): bigint;
function getUint64(pointer: bigint, offset?: number): bigint;
function getFloat32(pointer: bigint, offset?: number): number;
function getFloat64(pointer: bigint, offset?: number): number;
function setInt8(pointer: bigint, offset: number, value: number): void;
function setUint8(pointer: bigint, offset: number, value: number): void;
function setInt16(pointer: bigint, offset: number, value: number): void;
function setUint16(pointer: bigint, offset: number, value: number): void;
function setInt32(pointer: bigint, offset: number, value: number): void;
function setUint32(pointer: bigint, offset: number, value: number): void;
function setInt64(pointer: bigint, offset: number, value: number | bigint): void;
function setUint64(pointer: bigint, offset: number, value: number | bigint): void;
function setFloat32(pointer: bigint, offset: number, value: number): void;
function setFloat64(pointer: bigint, offset: number, value: number): void;
/**
* Reads a NUL-terminated UTF-8 string from native memory.
*
* If `pointer` is `0n`, `null` is returned.
*
* This function does not validate that `pointer` refers to readable memory or
* that the pointed-to data is terminated with `\0`. Passing an invalid pointer,
* a pointer to freed memory, or a pointer to bytes without a terminating NUL can
* read unrelated memory, crash the process, or produce truncated or garbled
* output.
* @since v26.1.0
*/
function toString(pointer: bigint): string | null;
/**
* Creates a `Buffer` from native memory.
*
* When `copy` is `true`, the returned `Buffer` owns its own copied memory.
* When `copy` is `false`, the returned `Buffer` references the original native
* memory directly.
*
* Using `copy: false` is a zero-copy escape hatch. The returned `Buffer` is a
* writable view onto foreign memory, so writes in JavaScript update the original
* native memory directly. The caller must guarantee that:
*
* * `pointer` remains valid for the entire lifetime of the returned `Buffer`.
* * `length` stays within the allocated native region.
* * no native code frees or repurposes that memory while JavaScript still uses
* the `Buffer`.
* * Memory protection is observed. For example, read-only memory pages must not
* be written to.
*
* If these guarantees are not met, reading or writing the `Buffer` can corrupt
* memory or crash the process.
* @since v26.1.0
* @param copy When `false`, creates a zero-copy view. **Default:** `true`.
*/
function toBuffer(pointer: bigint, length: number, copy?: boolean): NonSharedBuffer;
/**
* Creates an `ArrayBuffer` from native memory.
*
* When `copy` is `true`, the returned `ArrayBuffer` contains copied bytes.
* When `copy` is `false`, the returned `ArrayBuffer` references the original
* native memory directly.
*
* The same lifetime and bounds requirements described for
* `ffi.toBuffer(pointer, length, copy)` apply
* here. With `copy: false`, the
* returned `ArrayBuffer` is a zero-copy view of foreign memory and is only safe
* while that memory remains allocated, unchanged in layout, and valid for the
* entire exposed range.
* @since v26.1.0
* @param copy When `false`, creates a zero-copy view. **Default:** `true`.
*/
function toArrayBuffer(pointer: bigint, length: number, copy?: boolean): ArrayBuffer;
/**
* Copies a JavaScript string into native memory and appends a trailing NUL
* terminator.
*
* `length` must be large enough to hold the full encoded string plus the trailing
* NUL terminator. For UTF-16 and UCS-2 encodings, the trailing terminator uses
* two zero bytes.
*
* `pointer` must refer to writable native memory with at least `length` bytes of
* available storage. This function does not allocate memory on its own.
*
* `string` must be a JavaScript string. `encoding` must be a string.
* @since v26.1.0
* @param encoding **Default:** `'utf8'`.
*/
function exportString(string: string, pointer: bigint, length: number, encoding?: BufferEncoding): void;
/**
* Copies bytes from a `Buffer` into native memory.
*
* `length` must be at least `buffer.length`.
*
* `pointer` must refer to writable native memory with at least `length` bytes of
* available storage. This function does not allocate memory on its own.
*
* `buffer` must be a Node.js `Buffer`.
* @since v26.1.0
*/
function exportBuffer(buffer: Buffer, pointer: bigint, length: number): void;
/**
* Copies bytes from an `ArrayBuffer` into native memory.
*
* `length` must be at least `arrayBuffer.byteLength`.
*
* `pointer` must refer to writable native memory with at least `length` bytes of
* available storage. This function does not allocate memory on its own.
* @since v26.1.0
*/
function exportArrayBuffer(arrayBuffer: ArrayBuffer, pointer: bigint, length: number): void;
/**
* Copies bytes from an `ArrayBufferView` into native memory.
*
* `length` must be at least `arrayBufferView.byteLength`.
*
* `pointer` must refer to writable native memory with at least `length` bytes of
* available storage. This function does not allocate memory on its own.
* @since v26.1.0
*/
function exportArrayBufferView(arrayBufferView: NodeJS.ArrayBufferView, pointer: bigint, length: number): void;
/**
* Returns the raw memory address of JavaScript-managed byte storage.
*
* This is unsafe and dangerous. The returned pointer can become invalid if the
* underlying memory is detached, resized, transferred, or otherwise invalidated.
* Using stale pointers can cause memory corruption or process crashes.
* @since v26.1.0
*/
function getRawPointer(source: ArrayBuffer | NodeJS.ArrayBufferView): bigint;
type ReturnType = { [K in keyof DataTypeMap]: K }[keyof DataTypeMap];
type ArgumentType = Exclude<ReturnType, "void">;
interface DataTypeMap {
void: "void";
char: "number";
bool: "number";
i8: "number";
int8: "number";
u8: "number";
uint8: "number";
i16: "number";
int16: "number";
u16: "number";
uint16: "number";
i32: "number";
int32: "number";
u32: "number";
uint32: "number";
i64: "bigint";
int64: "bigint";
u64: "bigint";
uint64: "bigint";
float: "number";
f32: "number";
double: "number";
f64: "number";
pointer: "pointer";
ptr: "pointer";
function: "pointer";
buffer: "pointer";
arraybuffer: "pointer";
string: "pointer";
str: "pointer";
}
interface ArgumentTypeMap {
"number": number;
"bigint": bigint;
"pointer": bigint | string | ArrayBuffer | NodeJS.ArrayBufferView | null;
}
interface ReturnTypeMap {
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
"void": void;
"number": number;
"bigint": bigint;
"pointer": bigint | null;
}
enum types {
VOID = "void",
POINTER = "pointer",
BUFFER = "buffer",
ARRAY_BUFFER = "arraybuffer",
FUNCTION = "function",
BOOL = "bool",
CHAR = "char",
STRING = "string",
FLOAT = "float",
DOUBLE = "double",
INT_8 = "int8",
UINT_8 = "uint8",
INT_16 = "int16",
UINT_16 = "uint16",
INT_32 = "int32",
UINT_32 = "uint32",
INT_64 = "int64",
UINT_64 = "uint64",
FLOAT_32 = "float32",
FLOAT_64 = "float64",
}
}
+4803
View File
File diff suppressed because it is too large Load Diff
+1477
View File
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
declare var global: typeof globalThis;
declare var process: NodeJS.Process;
interface ErrorConstructor {
/**
* Creates a `.stack` property on `targetObject`, which when accessed returns
* a string representing the location in the code at which
* `Error.captureStackTrace()` was called.
*
* ```js
* const myObject = {};
* Error.captureStackTrace(myObject);
* myObject.stack; // Similar to `new Error().stack`
* ```
*
* The first line of the trace will be prefixed with
* `${myObject.name}: ${myObject.message}`.
*
* The optional `constructorOpt` argument accepts a function. If given, all frames
* above `constructorOpt`, including `constructorOpt`, will be omitted from the
* generated stack trace.
*
* The `constructorOpt` argument is useful for hiding implementation
* details of error generation from the user. For instance:
*
* ```js
* function a() {
* b();
* }
*
* function b() {
* c();
* }
*
* function c() {
* // Create an error without stack trace to avoid calculating the stack trace twice.
* const { stackTraceLimit } = Error;
* Error.stackTraceLimit = 0;
* const error = new Error();
* Error.stackTraceLimit = stackTraceLimit;
*
* // Capture the stack trace above function b
* Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
* throw error;
* }
*
* a();
* ```
*/
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
/**
* @see https://v8.dev/docs/stack-trace-api#customizing-stack-traces
*/
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
/**
* The `Error.stackTraceLimit` property specifies the number of stack frames
* collected by a stack trace (whether generated by `new Error().stack` or
* `Error.captureStackTrace(obj)`).
*
* The default value is `10` but may be set to any valid JavaScript number. Changes
* will affect any stack trace captured _after_ the value has been changed.
*
* If set to a non-number value, or set to a negative number, stack traces will
* not capture any frames.
*/
stackTraceLimit: number;
}
/**
* Enable this API with the `--expose-gc` CLI flag.
*/
declare var gc: NodeJS.GCFunction | undefined;
declare namespace NodeJS {
interface CallSite {
getColumnNumber(): number | null;
getEnclosingColumnNumber(): number | null;
getEnclosingLineNumber(): number | null;
getEvalOrigin(): string | undefined;
getFileName(): string | null;
getFunction(): Function | undefined;
getFunctionName(): string | null;
getLineNumber(): number | null;
getMethodName(): string | null;
getPosition(): number;
getPromiseIndex(): number | null;
getScriptHash(): string;
getScriptNameOrSourceURL(): string | null;
getThis(): unknown;
getTypeName(): string | null;
isAsync(): boolean;
isConstructor(): boolean;
isEval(): boolean;
isNative(): boolean;
isPromiseAll(): boolean;
isToplevel(): boolean;
}
interface ErrnoException extends Error {
errno?: number;
code?: string;
path?: string;
syscall?: string;
}
interface RefCounted {
ref(): this;
unref(): this;
}
interface Dict<T> {
[key: string]: T | undefined;
}
interface ReadOnlyDict<T> {
readonly [key: string]: T | undefined;
}
type PartialOptions<T> = { [K in keyof T]?: T[K] | undefined };
interface GCFunction {
(minor?: boolean): void;
(options: NodeJS.GCOptions & { execution: "async" }): Promise<void>;
(options: NodeJS.GCOptions): void;
}
interface GCOptions {
execution?: "sync" | "async" | undefined;
flavor?: "regular" | "last-resort" | undefined;
type?: "major-snapshot" | "major" | "minor" | undefined;
filename?: string | undefined;
}
/** An iterable iterator returned by the Node.js API. */
interface Iterator<T, TReturn = undefined, TNext = any> extends IteratorObject<T, TReturn, TNext> {
[Symbol.iterator](): NodeJS.Iterator<T, TReturn, TNext>;
}
/** An async iterable iterator returned by the Node.js API. */
interface AsyncIterator<T, TReturn = undefined, TNext = any> extends AsyncIteratorObject<T, TReturn, TNext> {
[Symbol.asyncIterator](): NodeJS.AsyncIterator<T, TReturn, TNext>;
}
/** The [`BufferSource`](https://webidl.spec.whatwg.org/#BufferSource) type from the Web IDL specification. */
type BufferSource = NonSharedArrayBufferView | ArrayBuffer;
/** The [`AllowSharedBufferSource`](https://webidl.spec.whatwg.org/#AllowSharedBufferSource) type from the Web IDL specification. */
type AllowSharedBufferSource = ArrayBufferView | ArrayBufferLike;
}
+101
View File
@@ -0,0 +1,101 @@
export {}; // Make this a module
declare global {
namespace NodeJS {
type TypedArray<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> =
| Uint8Array<TArrayBuffer>
| Uint8ClampedArray<TArrayBuffer>
| Uint16Array<TArrayBuffer>
| Uint32Array<TArrayBuffer>
| Int8Array<TArrayBuffer>
| Int16Array<TArrayBuffer>
| Int32Array<TArrayBuffer>
| BigUint64Array<TArrayBuffer>
| BigInt64Array<TArrayBuffer>
| Float16Array<TArrayBuffer>
| Float32Array<TArrayBuffer>
| Float64Array<TArrayBuffer>;
type ArrayBufferView<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> =
| TypedArray<TArrayBuffer>
| DataView<TArrayBuffer>;
// The following aliases are required to allow use of non-shared ArrayBufferViews in @types/node
// while maintaining compatibility with TS <=5.6.
// TODO: remove once @types/node no longer supports TS 5.6, and replace with native types.
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedUint8Array = Uint8Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedUint8ClampedArray = Uint8ClampedArray<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedUint16Array = Uint16Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedUint32Array = Uint32Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedInt8Array = Int8Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedInt16Array = Int16Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedInt32Array = Int32Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedBigUint64Array = BigUint64Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedBigInt64Array = BigInt64Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedFloat16Array = Float16Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedFloat32Array = Float32Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedFloat64Array = Float64Array<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedDataView = DataView<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedTypedArray = TypedArray<ArrayBuffer>;
/**
* @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
* TypeScript versions earlier than 5.7.
*/
type NonSharedArrayBufferView = ArrayBufferView<ArrayBuffer>;
}
}
+2218
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More