mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Add an experimental `net/promises` namespace, accessible via
`require('node:net/promises')` or `net.promises`, mirroring the existing
`fs/promises` and `dns/promises` modules.
It provides promise-based variants of net's one-shot lifecycle
operations:
- `connect()` returns a promise that fulfills with a connected Socket
once the 'connect' event fires, and rejects on connection failure or
when an optional AbortSignal is aborted, destroying the socket.
- `listen()` creates a server, begins listening, and returns a promise
that fulfills with the listening Server once the 'listening' event
fires. It rejects if the server fails to bind or an optional
AbortSignal is aborted, closing the server.
The functions are named for the actions they await, forming a parallel
`connect()`/`listen()` pair. This is intentionally not the callback
API's factory taxonomy: there `createConnection()` is canonical because
it both creates a socket and initiates connecting, whereas
`createServer()` is a pure factory that does not listen. A
`createConnection`/`createServer` pairing would be inconsistent here
because `createServer` has no completion to await, so no
`createConnection` alias is provided.
Refs: https://github.com/nodejs/node/issues/21482
Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ethan Arrowood <ethan@arrowood.dev>
PR-URL: https://github.com/nodejs/node/pull/63965
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
198 lines
5.7 KiB
JavaScript
198 lines
5.7 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const fixtures = require('../common/fixtures');
|
|
const { builtinModules } = require('module');
|
|
const publicModules = builtinModules.filter((lib) => !lib.startsWith('_'));
|
|
|
|
const { isMainThread } = require('worker_threads');
|
|
|
|
if (!isMainThread) {
|
|
common.skip('process.chdir is not available in Workers');
|
|
}
|
|
|
|
// We have to change the directory to ../fixtures before requiring repl
|
|
// in order to make the tests for completion of node_modules work properly
|
|
// since repl modifies module.paths.
|
|
process.chdir(fixtures.fixturesDir);
|
|
|
|
const repl = require('repl');
|
|
const { startNewREPLServer } = require('../common/repl');
|
|
|
|
// Tab completion on require on builtin modules works
|
|
{
|
|
const { replServer } = startNewREPLServer();
|
|
|
|
replServer.complete(
|
|
"require('",
|
|
common.mustCall(function(error, data) {
|
|
assert.strictEqual(error, null);
|
|
publicModules.forEach((lib) => {
|
|
assert(
|
|
data[0].includes(lib) &&
|
|
(lib.startsWith('node:') || data[0].includes(`node:${lib}`)),
|
|
`${lib} not found`
|
|
);
|
|
});
|
|
const newModule = 'foobar';
|
|
assert(!builtinModules.includes(newModule));
|
|
repl.builtinModules.push(newModule);
|
|
replServer.complete(
|
|
"require('",
|
|
common.mustCall((_, [modules]) => {
|
|
assert.strictEqual(data[0].length + 1, modules.length);
|
|
assert(modules.includes(newModule));
|
|
})
|
|
);
|
|
})
|
|
);
|
|
}
|
|
|
|
// Tab completion on require on builtin modules works (with extra spaces and "n" prefix)
|
|
{
|
|
const { replServer } = startNewREPLServer();
|
|
|
|
replServer.complete(
|
|
"require\t( 'n",
|
|
common.mustCall(function(error, data) {
|
|
assert.strictEqual(error, null);
|
|
assert.strictEqual(data.length, 2);
|
|
assert.strictEqual(data[1], 'n');
|
|
// require(...) completions include `node:`-prefixed modules:
|
|
let lastIndex = -1;
|
|
|
|
for (const lib of publicModules.filter((lib) => !lib.startsWith('node:'))) {
|
|
lastIndex = data[0].indexOf(`node:${lib}`);
|
|
assert.notStrictEqual(lastIndex, -1);
|
|
}
|
|
assert.strictEqual(data[0][lastIndex + 1], '');
|
|
// The Node.js modules that start with n:
|
|
assert.strictEqual(data[0][lastIndex + 2], 'net');
|
|
assert.strictEqual(data[0][lastIndex + 3], 'net/promises');
|
|
assert.strictEqual(data[0][lastIndex + 4], '');
|
|
// It's possible to pick up non-core modules too
|
|
for (const completion of data[0].slice(lastIndex + 5)) {
|
|
assert.match(completion, /^n/);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
// Tab completion on require on external modules works
|
|
{
|
|
const expected = ['@nodejsscope', '@nodejsscope/'];
|
|
|
|
const { replServer } = startNewREPLServer();
|
|
|
|
// Require calls should handle all types of quotation marks.
|
|
for (const quotationMark of ["'", '"', '`']) {
|
|
replServer.complete(
|
|
'require(`@nodejs',
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.deepStrictEqual(data, [expected, '@nodejs']);
|
|
})
|
|
);
|
|
|
|
// Completions should not be greedy in case the quotation ends.
|
|
const input = `require(${quotationMark}@nodejsscope${quotationMark}`;
|
|
replServer.complete(
|
|
input,
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.deepStrictEqual(data, [[], undefined]);
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Completions should find modules and handle whitespace after the opening bracket.
|
|
const { replServer } = startNewREPLServer();
|
|
|
|
replServer.complete(
|
|
'require \t("no_ind',
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']);
|
|
})
|
|
);
|
|
}
|
|
|
|
// Test tab completion for require() relative to the current directory
|
|
{
|
|
const { replServer } = startNewREPLServer();
|
|
|
|
const cwd = process.cwd();
|
|
process.chdir(__dirname);
|
|
|
|
["require('.", 'require(".'].forEach((input) => {
|
|
replServer.complete(
|
|
input,
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.strictEqual(data.length, 2);
|
|
assert.strictEqual(data[1], '.');
|
|
assert.strictEqual(data[0].length, 2);
|
|
assert.ok(data[0].includes('./'));
|
|
assert.ok(data[0].includes('../'));
|
|
})
|
|
);
|
|
});
|
|
|
|
["require('..", 'require("..'].forEach((input) => {
|
|
replServer.complete(
|
|
input,
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.deepStrictEqual(data, [['../'], '..']);
|
|
})
|
|
);
|
|
});
|
|
|
|
['./', './test-'].forEach((path) => {
|
|
[`require('${path}`, `require("${path}`].forEach((input) => {
|
|
replServer.complete(
|
|
input,
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.strictEqual(data.length, 2);
|
|
assert.strictEqual(data[1], path);
|
|
assert.ok(data[0].includes('./test-repl-tab-complete'));
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
['../parallel/', '../parallel/test-'].forEach((path) => {
|
|
[`require('${path}`, `require("${path}`].forEach((input) => {
|
|
replServer.complete(
|
|
input,
|
|
common.mustCall((err, data) => {
|
|
assert.strictEqual(err, null);
|
|
assert.strictEqual(data.length, 2);
|
|
assert.strictEqual(data[1], path);
|
|
assert.ok(data[0].includes('../parallel/test-repl-tab-complete'));
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
{
|
|
const path = '../fixtures/repl-folder-extensions/f';
|
|
replServer.complete(
|
|
`require('${path}`,
|
|
common.mustSucceed((data) => {
|
|
assert.strictEqual(data.length, 2);
|
|
assert.strictEqual(data[1], path);
|
|
assert.ok(
|
|
data[0].includes('../fixtures/repl-folder-extensions/foo.js')
|
|
);
|
|
})
|
|
);
|
|
}
|
|
|
|
process.chdir(cwd);
|
|
}
|