mrkeyoor.com_
Sat 08 Aug 21:57 UTC
npmCLI & Toolingupdated 08 Aug 2026

hyperlinker

hyperlinker is a dependency-free CommonJS function that wraps visible text in an OSC 8 terminal hyperlink escape sequence. A supporting terminal displays the short label but opens the separate URI when the user clicks it. An optional object adds hidden OSC 8 parameters such as a shared link id. The package only formats a string: it does not detect terminal support, validate destinations, sanitize control characters, print output, or create HTML links.

Verdict

The implementation is short enough to audit, but it leaves the two hard parts, capability fallback and safe handling of untrusted terminal data, entirely to you. For new CLIs, terminal-link is the more complete default; keep hyperlinker for trusted legacy output.

API stability5/5Version 1.0.0 exports one function with three arguments and has never changed its output contract: OSC 8, serialized optional parameters, URI, BEL, label, and a closing OSC 8 sequence. There are no dependencies or internal abstractions likely to shift underneath callers. This is genuine behavioral simplicity, although it is also frozen behavior, so missing validation, detection, terminator choices, declarations, and module formats should be treated as permanent constraints rather than future roadmap items.
Docs3/5The README clearly explains why terminal hyperlinks exist, shows the basic function, documents all three arguments, calls out the lack of capability detection, and includes a supports-hyperlinks fallback. It does not explain the exact emitted bytes, browser stub, CommonJS-only shape, parameter delimiters, redirected-output behavior, control-sequence injection risk, URI validation, or how to test output. Its linked terminal-support list is useful background but cannot substitute for current per-environment testing.
Maintenance1/5npm version 1.0.0 was published on 2017-11-28, and GitHub's latest default-branch commits are also from November 2017. The repository reports a later push in July 2020, is not archived, and has only one issue and pull request combined, but there is no release or default-branch work covering newer Node module conventions, terminal-support changes, security hardening, type declarations, or automated dependency and CI refreshes.
Ecosystem3/5The package recorded 3,790,113 downloads in the measured week, has no runtime dependencies, and implements the widely adopted OSC 8 convention, so it remains a cheap transitive building block. The repository has 89 stars and its companion supports-hyperlinks package addresses detection. The broader CLI ecosystem now offers terminal-link and ansi-escapes with more complete current packaging and behavior, while hyperlinker itself has no plugins, types, stream integration, or framework adapters.

Use it if

  • You maintain CommonJS CLI output that already depends on hyperlinker's exact BEL-terminated OSC 8 sequence
  • You need one tiny function and will perform support detection, fallback rendering, and input validation yourself
  • Your visible text, URI, and parameters are trusted application constants rather than user-controlled data
  • You target terminal emulators known to support OSC 8 and do not need browser bundling or TypeScript declarations
Skip it if

Setup reality

npm install hyperlinker is the whole package installation: there are no dependencies, native binaries, peers, credentials, or configuration files, and its engines field allows Node 4 or newer. Call the exported CommonJS function with text and URI; it returns a string containing ESC ] 8, semicolon-separated fields, a BEL terminator, your visible text, and a closing sequence. Nothing is printed until you write or log that string. The main first-run surprise is that capability detection is deliberately out of scope. In unsupported terminals, redirected output, CI logs, and many log collectors, escape bytes may be ignored, stripped, or shown literally, while the human-readable output does not include the URL. Pair it with supports-hyperlinks and make the fallback contain the destination. The function performs no type checks, URL parsing, protocol allowlist, encoding, or control-character escaping. Treat text, URI, parameter names, and values as terminal-control input: a BEL or ESC from untrusted data can end the hyperlink sequence and inject further terminal instructions. Parameter pairs use key=value joined by colons, so delimiters inside keys or values are also ambiguous. The package always uses BEL, offers no switch to the ST terminator, and does not expose a stream helper. It ships no TypeScript declarations. Bundlers choose browser.js through the browser field, and that stub throws on every call rather than returning plain text. OSC 8 behavior is controlled by each terminal, not Node, so test the actual terminals, multiplexers, CI capture, and pager combinations your users run.

Patterns

Create a terminal hyperlinkcreate-link

const hyperlinker = require('hyperlinker');

const label = hyperlinker('Open documentation', 'https://example.com/docs');
console.log(label);

The function returns a control-sequence string; console.log is what writes it. Unsupported terminals may show only Open documentation and hide the URL.

Provide a fallback when stdout lacks hyperlink supportdetect-support

const supportsHyperlinks = require('supports-hyperlinks');
const hyperlinker = require('hyperlinker');

function stdoutLink(text, uri) {
  return supportsHyperlinks.stdout
    ? hyperlinker(text, uri)
    : `${text} (${uri})`;
}

console.log(stdoutLink('Build report', reportUrl));

Detection is not included in hyperlinker. Keep the URI visible in fallback text so redirected logs remain useful.

Detect stderr separatelylink-on-stderr

function stderrLink(text, uri) {
  return supportsHyperlinks.stderr
    ? hyperlinker(text, uri)
    : `${text}: ${uri}`;
}

process.stderr.write(stderrLink('Error details', errorUrl) + '\n');

stdout and stderr can have different TTY and redirection states. Do not reuse stdout detection for diagnostics written to stderr.

Link to a local file safelylink-local-file

const { pathToFileURL } = require('url');

const uri = pathToFileURL('/tmp/build report.html').href;
console.log(hyperlinker('Open local report', uri));

pathToFileURL handles spaces, platform path syntax, and percent encoding more reliably than concatenating file:// yourself. Terminal policy may still block local-file links.

Group separated link fragments with an idreuse-link-id

const first = hyperlinker('issue', issueUrl, { id: 'issue-42' });
const second = hyperlinker('#42', issueUrl, { id: 'issue-42' });
console.log(first, 'was fixed in', second);

The OSC 8 id parameter can associate fragments visually, but the README warns that parameter behavior is not widely supported. Use trusted delimiter-free ids.

Apply color inside the clickable labelcolor-link-text

const chalk = require('chalk');

const label = hyperlinker(
  chalk.cyan('project homepage'),
  'https://example.com'
);
console.log(label);

Color control sequences become part of the visible-text region. Test the combination in target terminals because nested terminal controls can interact with link rendering.

Allow only expected URL protocolsvalidate-uri

function safeUri(value) {
  const url = new URL(value);
  if (!['https:', 'http:'].includes(url.protocol)) {
    throw new Error('Unsupported link protocol');
  }
  return url.href;
}

console.log(hyperlinker('Release', safeUri(candidateUrl)));

hyperlinker accepts any string and does not validate schemes. Apply a protocol allowlist before link creation when destinations are not constants.

Reject terminal control bytesreject-control-characters

const CONTROL = /[\u0000-\u001F\u007F-\u009F]/u;

function trustedField(value, field) {
  const text = String(value);
  if (CONTROL.test(text)) throw new Error(`${field} contains control bytes`);
  return text;
}

const output = hyperlinker(
  trustedField(label, 'label'),
  trustedField(uri, 'uri')
);

The package concatenates inputs without escaping. BEL and ESC are especially dangerous because they can terminate or start terminal control sequences.

Write a link as part of a status linewrite-without-extra-space

process.stdout.write('Report: ');
process.stdout.write(hyperlinker('open', reportUrl));
process.stdout.write('\n');

The returned value contains no trailing newline. process.stdout.write gives exact control over spacing where console.log argument separators would be unwanted.

Make escape bytes visible in testsinspect-output

const actual = hyperlinker('docs', 'https://example.com');
console.log(JSON.stringify(actual));
// "\u001b]8;;https://example.com\u0007docs\u001b]8;;\u0007"

Snapshot an escaped representation instead of raw OSC bytes so test reporters and terminals do not interpret the value as a real link.

Assert exact OSC 8 formattingassert-exact-sequence

const expected =
  '\u001B]8;id=docs;https://example.com\u0007' +
  'Docs' +
  '\u001B]8;;\u0007';

assert.equal(hyperlinker('Docs', 'https://example.com', { id: 'docs' }), expected);

Version 1.0.0 uses BEL as the terminator and colon-joins multiple parameters. Exact tests catch accidental control-byte changes.

Disable links when output is redirectedplain-text-for-pipes

const interactive = process.stdout.isTTY && supportsHyperlinks.stdout;
const output = interactive
  ? hyperlinker('artifact', artifactUrl)
  : `artifact: ${artifactUrl}`;

process.stdout.write(output + '\n');

Plain output is safer for files, pipes, pagers, and machine-readable logs. Capability detection can be truthy in environments where your downstream consumer still strips OSC sequences.

Alternatives

PackageRegistryPick it when
terminal-linknpmUse a current hyperlink helper with built-in support detection and a configurable fallback
ansi-escapesnpmUse a maintained collection when hyperlinks are one of several terminal-control operations your CLI needs
supports-hyperlinksnpmUse detection plus a short local OSC 8 formatter when you want explicit fallback and sanitization policy without another link wrapper