peakroute 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,132 @@
1
+ import * as node_tls from 'node:tls';
2
+ import * as http from 'node:http';
3
+ import * as net from 'node:net';
4
+
5
+ /** Route info used by the proxy server to map hostnames to ports. */
6
+ interface RouteInfo {
7
+ hostname: string;
8
+ port: number;
9
+ }
10
+ interface ProxyServerOptions {
11
+ /** Called on each request to get the current route table. */
12
+ getRoutes: () => RouteInfo[];
13
+ /** The port the proxy is listening on (used to build correct URLs). */
14
+ proxyPort: number;
15
+ /** Optional error logger; defaults to console.error. */
16
+ onError?: (message: string) => void;
17
+ /** When provided, enables HTTP/2 over TLS (HTTPS). */
18
+ tls?: {
19
+ cert: Buffer;
20
+ key: Buffer;
21
+ /** SNI callback for per-hostname certificate selection. */
22
+ SNICallback?: (servername: string, cb: (err: Error | null, ctx?: node_tls.SecureContext) => void) => void;
23
+ };
24
+ }
25
+
26
+ /** Response header used to identify a peakroute proxy (for health checks). */
27
+ declare const PEAKROUTE_HEADER = "X-Peakroute";
28
+ /** Server type returned by createProxyServer (plain HTTP/1.1 or net.Server TLS wrapper). */
29
+ type ProxyServer = http.Server | net.Server;
30
+ /**
31
+ * Create an HTTP proxy server that routes requests based on the Host header.
32
+ *
33
+ * Uses Node's built-in http module for proxying (no external dependencies).
34
+ * The `getRoutes` callback is invoked on every request so callers can provide
35
+ * either a static list or a live-updating one.
36
+ *
37
+ * When `tls` is provided, creates an HTTP/2 secure server with HTTP/1.1
38
+ * fallback (`allowHTTP1: true`). This enables HTTP/2 multiplexing for
39
+ * browsers while keeping WebSocket upgrades working over HTTP/1.1.
40
+ */
41
+ declare function createProxyServer(options: ProxyServerOptions): ProxyServer;
42
+
43
+ /** File permission mode for route and state files. */
44
+ declare const FILE_MODE = 420;
45
+ /** Directory permission mode for the user state directory. */
46
+ declare const DIR_MODE = 493;
47
+ /** Directory permission mode for the system state directory (world-writable with sticky bit). */
48
+ declare const SYSTEM_DIR_MODE = 1023;
49
+ /** File permission mode for shared state files in the system state directory. */
50
+ declare const SYSTEM_FILE_MODE = 438;
51
+ interface RouteMapping extends RouteInfo {
52
+ pid: number;
53
+ }
54
+ /**
55
+ * Thrown when a route is already registered by a live process and --force
56
+ * was not specified.
57
+ */
58
+ declare class RouteConflictError extends Error {
59
+ readonly hostname: string;
60
+ readonly existingPid: number;
61
+ constructor(hostname: string, existingPid: number);
62
+ }
63
+ /**
64
+ * Manages route mappings stored as a JSON file on disk.
65
+ * Supports file locking and stale-route cleanup.
66
+ */
67
+ declare class RouteStore {
68
+ /** The state directory path. */
69
+ readonly dir: string;
70
+ private readonly routesPath;
71
+ private readonly lockPath;
72
+ readonly pidPath: string;
73
+ readonly portFilePath: string;
74
+ private readonly onWarning;
75
+ constructor(dir: string, options?: {
76
+ onWarning?: (message: string) => void;
77
+ });
78
+ private isSystemDir;
79
+ private get dirMode();
80
+ private get fileMode();
81
+ ensureDir(): void;
82
+ getRoutesPath(): string;
83
+ private static readonly sleepBuffer;
84
+ private syncSleep;
85
+ private acquireLock;
86
+ private releaseLock;
87
+ private isProcessAlive;
88
+ /**
89
+ * Load routes from disk, filtering out stale entries whose owning process
90
+ * is no longer alive. Stale-route cleanup is only persisted when the caller
91
+ * already holds the lock (i.e. inside addRoute/removeRoute) to avoid
92
+ * unprotected concurrent writes.
93
+ */
94
+ loadRoutes(persistCleanup?: boolean): RouteMapping[];
95
+ private saveRoutes;
96
+ addRoute(hostname: string, port: number, pid: number, force?: boolean): void;
97
+ removeRoute(hostname: string): void;
98
+ }
99
+
100
+ /**
101
+ * Wrapper seguro para chmod que é no-op no Windows.
102
+ * PR #50 pattern: evita condicionais espalhadas pelo código.
103
+ */
104
+ declare function chmodSafe(path: string, mode: number): void;
105
+ /**
106
+ * Async version of chmodSafe.
107
+ */
108
+ declare function chmodSafeAsync(path: string, mode: number): Promise<void>;
109
+ /**
110
+ * When running under sudo, fix file ownership so the real user can
111
+ * read/write the file later without sudo. No-op when not running as root.
112
+ * On Windows, this is a no-op since there's no concept of uid/gid.
113
+ */
114
+ declare function fixOwnership(...paths: string[]): void;
115
+ /** Type guard for Node.js system errors with an error code. */
116
+ declare function isErrnoException(err: unknown): err is NodeJS.ErrnoException;
117
+ /**
118
+ * Escape HTML special characters to prevent XSS.
119
+ */
120
+ declare function escapeHtml(str: string): string;
121
+ /**
122
+ * Format a .localhost URL. Omits the port when it matches the protocol default
123
+ * (80 for HTTP, 443 for HTTPS).
124
+ */
125
+ declare function formatUrl(hostname: string, proxyPort: number, tls?: boolean): string;
126
+ /**
127
+ * Parse and normalize a hostname input for use as a .localhost subdomain.
128
+ * Strips protocol prefixes, validates characters, and appends .localhost if needed.
129
+ */
130
+ declare function parseHostname(input: string): string;
131
+
132
+ export { DIR_MODE, FILE_MODE, PEAKROUTE_HEADER, type ProxyServer, type ProxyServerOptions, RouteConflictError, type RouteInfo, type RouteMapping, RouteStore, SYSTEM_DIR_MODE, SYSTEM_FILE_MODE, chmodSafe, chmodSafeAsync, createProxyServer, escapeHtml, fixOwnership, formatUrl, isErrnoException, parseHostname };
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ import {
2
+ DIR_MODE,
3
+ FILE_MODE,
4
+ PEAKROUTE_HEADER,
5
+ RouteConflictError,
6
+ RouteStore,
7
+ SYSTEM_DIR_MODE,
8
+ SYSTEM_FILE_MODE,
9
+ chmodSafe,
10
+ chmodSafeAsync,
11
+ createProxyServer,
12
+ escapeHtml,
13
+ fixOwnership,
14
+ formatUrl,
15
+ isErrnoException,
16
+ parseHostname
17
+ } from "./chunk-OWNUHDR5.js";
18
+ export {
19
+ DIR_MODE,
20
+ FILE_MODE,
21
+ PEAKROUTE_HEADER,
22
+ RouteConflictError,
23
+ RouteStore,
24
+ SYSTEM_DIR_MODE,
25
+ SYSTEM_FILE_MODE,
26
+ chmodSafe,
27
+ chmodSafeAsync,
28
+ createProxyServer,
29
+ escapeHtml,
30
+ fixOwnership,
31
+ formatUrl,
32
+ isErrnoException,
33
+ parseHostname
34
+ };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "peakroute",
3
+ "version": "0.5.0",
4
+ "description": "Replace port numbers with stable, named .localhost URLs. For humans and agents. (Formerly portless)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "bin": {
15
+ "peakroute": "./dist/cli.js"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "os": [
24
+ "darwin",
25
+ "linux",
26
+ "win32"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "lint": "eslint src/",
32
+ "lint:fix": "eslint src/ --fix",
33
+ "prepublishOnly": "bun run build",
34
+ "test": "vitest run",
35
+ "test:coverage": "vitest run --coverage",
36
+ "test:e2e": "echo 'No e2e tests defined yet'",
37
+ "test:watch": "vitest",
38
+ "typecheck": "tsc --noEmit"
39
+ },
40
+ "keywords": [
41
+ "local",
42
+ "development",
43
+ "proxy",
44
+ "localhost"
45
+ ],
46
+ "author": "Fala Dev",
47
+ "license": "Apache-2.0",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/faladev/peakroute.git"
51
+ },
52
+ "homepage": "https://github.com/faladev/peakroute#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/faladev/peakroute/issues"
55
+ },
56
+ "dependencies": {
57
+ "chalk": "^5.3.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^20.11.0",
61
+ "@vitest/coverage-v8": "^4.0.18",
62
+ "eslint": "^9.39.2",
63
+ "tsup": "^8.0.1",
64
+ "typescript": "^5.3.3",
65
+ "vitest": "^4.0.18"
66
+ }
67
+ }