Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
port gist examples into docs
  • Loading branch information
dario-piotrowicz committed Apr 6, 2025
commit 0c1abbece2430d5ff7770128d837867cb412fa68
222 changes: 209 additions & 13 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -911,35 +911,231 @@ possible to connect to a long-running Node.js process without restarting it.

### Examples

* Running a "full-featured" (`terminal`) REPL over
a `net.Server` and `net.Socket` instance:
<https://gist.github.com/TooTallNate/2209310>.
#### Full-featured "terminal" REPL over `net.Server` and `net.Socket`

* Running a REPL instance over [`curl(1)`][]:
<https://gist.github.com/TooTallNate/2053342>.
This is an example on how to run a "full-featured" (terminal) REPL using
[`net.Server`][] and [`net.Socket`][]

**Note**: This example is intended purely for educational purposes to demonstrate how
Node.js REPLs can be started using different I/O streams.
It should **not** be used in production environments or any context where security
is a concern without additional protective measures.
If you need to implement REPLs in a real-world application, consider alternative
approaches that mitigate these risks, such as using secure input mechanisms and
avoiding open network interfaces.
The following script starts an HTTP server on port `1337` that allows
clients to establish socket connections to its REPL instance.

```mjs
// repl-server.js
import repl from 'node:repl';
import net from 'node:net';

net
.createServer((socket) => {
const r = repl.start({
prompt: `socket ${socket.remoteAddress}:${socket.remotePort}> `,
input: socket,
output: socket,
terminal: true,
useGlobal: false,
});
r.on('exit', () => {
socket.end();
});
r.context.socket = socket;
})
.listen(1337);
```

```cjs
// repl-server.js
const repl = require('node:repl');
const net = require('node:net');

net
.createServer((socket) => {
const r = repl.start({
prompt: `socket ${socket.remoteAddress}:${socket.remotePort}> `,
input: socket,
output: socket,
terminal: true,
useGlobal: false,
});
r.on('exit', () => {
socket.end();
});
r.context.socket = socket;
})
.listen(1337);
```

While the following implements a client that can create a socket connection
with the above defined server over port `1337`.

```mjs
// repl-client.js
import net from 'node:net';
import process from 'node:process';

const sock = net.connect(1337);

process.stdin.pipe(sock);
sock.pipe(process.stdout);

sock.on('connect', () => {
process.stdin.resume();
process.stdin.setRawMode(true);
});

sock.on('close', () => {
process.stdin.setRawMode(false);
process.stdin.pause();
sock.removeListener('close', done);
});

process.stdin.on('end', () => {
sock.destroy();
console.log();
});

process.stdin.on('data', (b) => {
if (b.length === 1 && b[0] === 4) {
process.stdin.emit('end');
}
});
```

```cjs
// repl-client.js
const net = require('node:net');

const sock = net.connect(1337);

process.stdin.pipe(sock);
sock.pipe(process.stdout);

sock.on('connect', () => {
process.stdin.resume();
process.stdin.setRawMode(true);
});

sock.on('close', () => {
process.stdin.setRawMode(false);
process.stdin.pause();
sock.removeListener('close', done);
});

process.stdin.on('end', () => {
sock.destroy();
console.log();
});

process.stdin.on('data', (b) => {
if (b.length === 1 && b[0] === 4) {
process.stdin.emit('end');
}
});
```

To run the example open two different terminals on your machine, start the server
with `node repl-server.js` in one terminal and `node repl-client.js` on the other.

Original code from: [gist.github.com/TooTallNate/2209310][full-featured REPL gist]

#### REPL over `curl`

This is an example on how to run a REPL instance over [`curl()`][]

The following script starts an HTTP server on port `8000` that can accept
a connection established via [`curl()`][].

```mjs
import http from 'node:http';
import repl from 'node:repl';
import { Buffer } from 'node:buffer';

const buf0 = Buffer.from([0]);

const server = http.createServer((req, res) => {
res.setHeader('content-type', 'multipart/octet-stream');

repl.start({
prompt: 'curl repl> ',
input: req,
output: res,
terminal: false,
useColors: true,
useGlobal: false,
});

// Hack to thread stdin and stdout
// simultaneously in curl's single thread
const iv = setInterval(() => {
res.write(buf0);
}, 100);

req.on('close', () => {
clearInterval(iv);
});
});
server.listen(8000);
```

```cjs
const http = require('node:http');
const repl = require('node:repl');
const buf0 = Buffer.from([0]);

const server = http.createServer((req, res) => {
res.setHeader('content-type', 'multipart/octet-stream');

repl.start({
prompt: 'curl repl> ',
input: req,
output: res,
terminal: false,
useColors: true,
useGlobal: false,
});

// Hack to thread stdin and stdout
// simultaneously in curl's single thread
const iv = setInterval(() => {
res.write(buf0);
}, 100);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed? I can't see what difference it makes

Copy link
Member Author

@dario-piotrowicz dario-piotrowicz Apr 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I just tried the example without it (I'm on Ubuntu) and it does not seem necessary

I wasn't actually fully sure why this was needed and feared that removing might break the example under some conditions / on some platform? 🤷

However do think that quite likely this is not needed, so I'm totally happy removing it, we can always re-add it if in some cases it is right? 🙂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed 🙂 a977fbe

(PS: thanks so much for the suggestion 🫶)

req.on('close', () => {
clearInterval(iv);
});
});
server.listen(8000);
```

When the above script is running you can then use [`curl()`][] to connect to
the server and connect to its REPL instance by running `curl -sSNT. localhost:8000`

**Warning** This example is intended purely for educational purposes to demonstrate how
Node.js REPLs can be started using different I/O streams.
It should **not** be used in production environments or any context where security
is a concern without additional protective measures.
If you need to implement REPLs in a real-world application, consider alternative
approaches that mitigate these risks, such as using secure input mechanisms and
avoiding open network interfaces.

Original code from: [gist.github.com/TooTallNate/2053342][REPL over curl gist]

[REPL over curl gist]: https://gist.github.com/TooTallNate/2053342
[TTY keybindings]: readline.md#tty-keybindings
[ZSH]: https://en.wikipedia.org/wiki/Z_shell
[`'uncaughtException'`]: process.md#event-uncaughtexception
[`--no-experimental-repl-await`]: cli.md#--no-experimental-repl-await
[`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`]: errors.md#err_domain_cannot_set_uncaught_exception_capture
[`ERR_INVALID_REPL_INPUT`]: errors.md#err_invalid_repl_input
[`curl(1)`]: https://curl.haxx.se/docs/manpage.html
[`curl()`]: https://curl.haxx.se/docs/manpage.html
[`domain`]: domain.md
[`module.builtinModules`]: module.md#modulebuiltinmodules
[`net.Server`]: net.md#class-netserver
[`net.Socket`]: net.md#class-netsocket
[`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn
[`readline.InterfaceCompleter`]: readline.md#use-of-the-completer-function
[`repl.ReplServer`]: #class-replserver
[`repl.start()`]: #replstartoptions
[`reverse-i-search`]: #reverse-i-search
[`util.inspect()`]: util.md#utilinspectobject-options
[custom evaluation functions]: #custom-evaluation-functions
[full-featured REPL gist]: https://gist.github.com/TooTallNate/2209310
[stream]: stream.md
Loading