@codemation/cli 0.0.7 → 0.0.11
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.
- package/dist/{CliBin-Bx1lFBi5.js → CliBin-BAnFX1wL.js} +82 -19
- package/dist/bin.js +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -1
- package/package.json +8 -3
- package/src/commands/DevCommand.ts +1 -0
- package/src/dev/CliDevProxyServer.ts +25 -15
- package/src/dev/CliDevProxyServerFactory.ts +4 -1
- package/src/dev/ListenPortConflictDescriber.ts +83 -0
- package/src/runtime/TypeScriptRuntimeConfigurator.ts +0 -7
|
@@ -7,7 +7,7 @@ import { randomUUID } from "node:crypto";
|
|
|
7
7
|
import { access, copyFile, cp, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
|
-
import { spawn } from "node:child_process";
|
|
10
|
+
import { execFile, spawn } from "node:child_process";
|
|
11
11
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
12
|
import { config, parse } from "dotenv";
|
|
13
13
|
import { watch } from "chokidar";
|
|
@@ -391,6 +391,7 @@ var DevCommand = class {
|
|
|
391
391
|
});
|
|
392
392
|
}
|
|
393
393
|
async runQueuedRebuild(prepared, state, gatewayBaseUrl, proxyServer, request) {
|
|
394
|
+
request.changedPaths;
|
|
394
395
|
proxyServer.setBuildStatus("building");
|
|
395
396
|
proxyServer.broadcastBuildStarted();
|
|
396
397
|
try {
|
|
@@ -1574,8 +1575,9 @@ var CliDevProxyServer = class {
|
|
|
1574
1575
|
childWorkflowSocket = null;
|
|
1575
1576
|
server = null;
|
|
1576
1577
|
uiProxyTarget = null;
|
|
1577
|
-
constructor(listenPort) {
|
|
1578
|
+
constructor(listenPort, listenPortConflictDescriber) {
|
|
1578
1579
|
this.listenPort = listenPort;
|
|
1580
|
+
this.listenPortConflictDescriber = listenPortConflictDescriber;
|
|
1579
1581
|
}
|
|
1580
1582
|
async start() {
|
|
1581
1583
|
if (this.server) return;
|
|
@@ -1597,7 +1599,9 @@ var CliDevProxyServer = class {
|
|
|
1597
1599
|
this.handleUpgrade(request, socket, head);
|
|
1598
1600
|
});
|
|
1599
1601
|
await new Promise((resolve, reject) => {
|
|
1600
|
-
server.once("error",
|
|
1602
|
+
server.once("error", (error) => {
|
|
1603
|
+
this.rejectListenError(error, reject);
|
|
1604
|
+
});
|
|
1601
1605
|
server.listen(this.listenPort, "127.0.0.1", () => {
|
|
1602
1606
|
resolve();
|
|
1603
1607
|
});
|
|
@@ -1681,7 +1685,6 @@ var CliDevProxyServer = class {
|
|
|
1681
1685
|
async handleHttpRequest(req, res) {
|
|
1682
1686
|
const pathname = this.safePathname(req.url ?? "");
|
|
1683
1687
|
const uiProxyTarget = this.uiProxyTarget;
|
|
1684
|
-
const activeRuntimeTarget = this.activeRuntime ? `http://127.0.0.1:${this.activeRuntime.httpPort}` : void 0;
|
|
1685
1688
|
if (pathname === "/api/dev/health" && req.method === "GET") {
|
|
1686
1689
|
res.writeHead(200, { "content-type": "application/json" });
|
|
1687
1690
|
res.end(JSON.stringify({
|
|
@@ -1690,15 +1693,6 @@ var CliDevProxyServer = class {
|
|
|
1690
1693
|
}));
|
|
1691
1694
|
return;
|
|
1692
1695
|
}
|
|
1693
|
-
if (pathname === "/api/dev/bootstrap-summary") {
|
|
1694
|
-
if (!activeRuntimeTarget) {
|
|
1695
|
-
res.writeHead(503, { "content-type": "text/plain" });
|
|
1696
|
-
res.end("Runtime is rebuilding.");
|
|
1697
|
-
return;
|
|
1698
|
-
}
|
|
1699
|
-
this.proxy.web(req, res, { target: activeRuntimeTarget });
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
1696
|
if (uiProxyTarget && pathname.startsWith("/api/auth/")) {
|
|
1703
1697
|
this.proxy.web(req, res, { target: uiProxyTarget.replace(/\/$/, "") });
|
|
1704
1698
|
return;
|
|
@@ -1709,7 +1703,7 @@ var CliDevProxyServer = class {
|
|
|
1709
1703
|
res.end("Runtime is rebuilding.");
|
|
1710
1704
|
return;
|
|
1711
1705
|
}
|
|
1712
|
-
this.proxy.web(req, res, { target:
|
|
1706
|
+
this.proxy.web(req, res, { target: `http://127.0.0.1:${this.activeRuntime.httpPort}` });
|
|
1713
1707
|
return;
|
|
1714
1708
|
}
|
|
1715
1709
|
if (uiProxyTarget) {
|
|
@@ -1747,6 +1741,16 @@ var CliDevProxyServer = class {
|
|
|
1747
1741
|
return url.split("?")[0] ?? url;
|
|
1748
1742
|
}
|
|
1749
1743
|
}
|
|
1744
|
+
async rejectListenError(error, reject) {
|
|
1745
|
+
if (error.code !== "EADDRINUSE") {
|
|
1746
|
+
reject(error);
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
const description = await this.listenPortConflictDescriber.describeLoopbackPort(this.listenPort);
|
|
1750
|
+
const baseMessage = `Dev gateway port ${this.listenPort} is already in use on 127.0.0.1.`;
|
|
1751
|
+
const suffix = description === null ? " Stop the process using that port or change the configured Codemation dev port." : ` Listener: ${description}. Stop that process or change the configured Codemation dev port.`;
|
|
1752
|
+
reject(new Error(`${baseMessage}${suffix}`, { cause: error instanceof Error ? error : void 0 }));
|
|
1753
|
+
}
|
|
1750
1754
|
broadcastDev(message) {
|
|
1751
1755
|
const text = JSON.stringify(message);
|
|
1752
1756
|
for (const client of this.devClients) if (client.readyState === WebSocket.OPEN) client.send(text);
|
|
@@ -1911,11 +1915,74 @@ var CliDevProxyServer = class {
|
|
|
1911
1915
|
}
|
|
1912
1916
|
};
|
|
1913
1917
|
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/dev/ListenPortConflictDescriber.ts
|
|
1920
|
+
var ListenPortConflictDescriber = class {
|
|
1921
|
+
constructor(platform = process$1.platform) {
|
|
1922
|
+
this.platform = platform;
|
|
1923
|
+
}
|
|
1924
|
+
async describeLoopbackPort(port) {
|
|
1925
|
+
if (!Number.isInteger(port) || port <= 0) return null;
|
|
1926
|
+
if (this.platform !== "linux" && this.platform !== "darwin") return null;
|
|
1927
|
+
const raw = await this.readLsofOutput(port);
|
|
1928
|
+
if (raw === null) return null;
|
|
1929
|
+
const occupants = this.parseLsofOutput(raw);
|
|
1930
|
+
if (occupants.length === 0) return null;
|
|
1931
|
+
return occupants.map((occupant) => `pid=${occupant.pid} command=${occupant.command} endpoint=${occupant.endpoint}`).join("; ");
|
|
1932
|
+
}
|
|
1933
|
+
async readLsofOutput(port) {
|
|
1934
|
+
try {
|
|
1935
|
+
return await new Promise((resolve, reject) => {
|
|
1936
|
+
execFile("lsof", [
|
|
1937
|
+
"-nP",
|
|
1938
|
+
`-iTCP:${port}`,
|
|
1939
|
+
"-sTCP:LISTEN",
|
|
1940
|
+
"-Fpcn"
|
|
1941
|
+
], (error, stdout) => {
|
|
1942
|
+
if (error) {
|
|
1943
|
+
reject(error);
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
resolve(stdout);
|
|
1947
|
+
});
|
|
1948
|
+
});
|
|
1949
|
+
} catch {
|
|
1950
|
+
return null;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
parseLsofOutput(raw) {
|
|
1954
|
+
const occupants = [];
|
|
1955
|
+
let currentPid = null;
|
|
1956
|
+
let currentCommand = null;
|
|
1957
|
+
for (const line of raw.split("\n")) {
|
|
1958
|
+
if (line.length < 2) continue;
|
|
1959
|
+
const prefix = line[0];
|
|
1960
|
+
const value = line.slice(1).trim();
|
|
1961
|
+
if (prefix === "p") {
|
|
1962
|
+
currentPid = Number.parseInt(value, 10);
|
|
1963
|
+
currentCommand = null;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (prefix === "c") {
|
|
1967
|
+
currentCommand = value;
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
if (prefix === "n" && currentPid !== null && currentCommand !== null) occupants.push({
|
|
1971
|
+
pid: currentPid,
|
|
1972
|
+
command: currentCommand,
|
|
1973
|
+
endpoint: value
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
return occupants;
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1914
1980
|
//#endregion
|
|
1915
1981
|
//#region src/dev/CliDevProxyServerFactory.ts
|
|
1916
1982
|
var CliDevProxyServerFactory = class {
|
|
1983
|
+
listenPortConflictDescriber = new ListenPortConflictDescriber();
|
|
1917
1984
|
create(gatewayPort) {
|
|
1918
|
-
return new CliDevProxyServer(gatewayPort);
|
|
1985
|
+
return new CliDevProxyServer(gatewayPort, this.listenPortConflictDescriber);
|
|
1919
1986
|
}
|
|
1920
1987
|
};
|
|
1921
1988
|
|
|
@@ -2919,12 +2986,8 @@ var NextHostConsumerServerCommandFactory = class {
|
|
|
2919
2986
|
//#region src/runtime/TypeScriptRuntimeConfigurator.ts
|
|
2920
2987
|
var TypeScriptRuntimeConfigurator = class {
|
|
2921
2988
|
configure(repoRoot) {
|
|
2922
|
-
if (this.hasExplicitOverride()) return;
|
|
2923
2989
|
process$1.env.CODEMATION_TSCONFIG_PATH = path.resolve(repoRoot, "tsconfig.base.json");
|
|
2924
2990
|
}
|
|
2925
|
-
hasExplicitOverride() {
|
|
2926
|
-
return typeof process$1.env.CODEMATION_TSCONFIG_PATH === "string" && process$1.env.CODEMATION_TSCONFIG_PATH.length > 0;
|
|
2927
|
-
}
|
|
2928
2991
|
};
|
|
2929
2992
|
|
|
2930
2993
|
//#endregion
|
package/dist/bin.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -23283,7 +23283,6 @@ declare class CliPathResolver {
|
|
|
23283
23283
|
//#region src/runtime/TypeScriptRuntimeConfigurator.d.ts
|
|
23284
23284
|
declare class TypeScriptRuntimeConfigurator {
|
|
23285
23285
|
configure(repoRoot: string): void;
|
|
23286
|
-
private hasExplicitOverride;
|
|
23287
23286
|
}
|
|
23288
23287
|
//#endregion
|
|
23289
23288
|
//#region src/commands/BuildCommand.d.ts
|
|
@@ -23418,6 +23417,15 @@ declare class DevBootstrapSummaryFetcher {
|
|
|
23418
23417
|
fetch(gatewayBaseUrl: string): Promise<DevBootstrapSummaryJson | null>;
|
|
23419
23418
|
}
|
|
23420
23419
|
//#endregion
|
|
23420
|
+
//#region src/dev/ListenPortConflictDescriber.d.ts
|
|
23421
|
+
declare class ListenPortConflictDescriber {
|
|
23422
|
+
private readonly platform;
|
|
23423
|
+
constructor(platform?: NodeJS.Platform);
|
|
23424
|
+
describeLoopbackPort(port: number): Promise<string | null>;
|
|
23425
|
+
private readLsofOutput;
|
|
23426
|
+
private parseLsofOutput;
|
|
23427
|
+
}
|
|
23428
|
+
//#endregion
|
|
23421
23429
|
//#region src/dev/CliDevProxyServer.d.ts
|
|
23422
23430
|
type ProxyRuntimeTarget = Readonly<{
|
|
23423
23431
|
httpPort: number;
|
|
@@ -23426,6 +23434,7 @@ type ProxyRuntimeTarget = Readonly<{
|
|
|
23426
23434
|
type BuildStatus = "idle" | "building";
|
|
23427
23435
|
declare class CliDevProxyServer {
|
|
23428
23436
|
private readonly listenPort;
|
|
23437
|
+
private readonly listenPortConflictDescriber;
|
|
23429
23438
|
private readonly proxy;
|
|
23430
23439
|
private readonly devClients;
|
|
23431
23440
|
private readonly devWss;
|
|
@@ -23438,7 +23447,7 @@ declare class CliDevProxyServer {
|
|
|
23438
23447
|
private childWorkflowSocket;
|
|
23439
23448
|
private server;
|
|
23440
23449
|
private uiProxyTarget;
|
|
23441
|
-
constructor(listenPort: number);
|
|
23450
|
+
constructor(listenPort: number, listenPortConflictDescriber: ListenPortConflictDescriber);
|
|
23442
23451
|
start(): Promise<void>;
|
|
23443
23452
|
stop(): Promise<void>;
|
|
23444
23453
|
setUiProxyTarget(target: string | null): void;
|
|
@@ -23452,6 +23461,7 @@ declare class CliDevProxyServer {
|
|
|
23452
23461
|
private handleHttpRequest;
|
|
23453
23462
|
private handleUpgrade;
|
|
23454
23463
|
private safePathname;
|
|
23464
|
+
private rejectListenError;
|
|
23455
23465
|
private broadcastDev;
|
|
23456
23466
|
private broadcastWorkflowLifecycleToSubscribedRooms;
|
|
23457
23467
|
private connectWorkflowClient;
|
|
@@ -23471,6 +23481,7 @@ declare class CliDevProxyServer {
|
|
|
23471
23481
|
//#endregion
|
|
23472
23482
|
//#region src/dev/CliDevProxyServerFactory.d.ts
|
|
23473
23483
|
declare class CliDevProxyServerFactory {
|
|
23484
|
+
private readonly listenPortConflictDescriber;
|
|
23474
23485
|
create(gatewayPort: number): CliDevProxyServer;
|
|
23475
23486
|
}
|
|
23476
23487
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CliProgram, i as CliPathResolver, n as CliProgramFactory, o as ConsumerOutputBuilder, r as CodemationCliApplicationSession, s as ConsumerBuildOptionsParser, t as CliBin } from "./CliBin-
|
|
1
|
+
import { a as CliProgram, i as CliPathResolver, n as CliProgramFactory, o as ConsumerOutputBuilder, r as CodemationCliApplicationSession, s as ConsumerBuildOptionsParser, t as CliBin } from "./CliBin-BAnFX1wL.js";
|
|
2
2
|
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
3
|
|
|
4
4
|
export { CliBin, CliPathResolver, CliProgram, CliProgramFactory, CodemationCliApplicationSession, CodemationPluginDiscovery, ConsumerBuildOptionsParser, ConsumerOutputBuilder };
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"author": "Made Relevant B.V.",
|
|
8
8
|
"homepage": "https://www.maderelevant.com",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/MadeRelevant/codemation",
|
|
12
|
+
"directory": "packages/cli"
|
|
13
|
+
},
|
|
9
14
|
"type": "module",
|
|
10
15
|
"main": "./dist/index.js",
|
|
11
16
|
"types": "./dist/index.d.ts",
|
|
@@ -32,8 +37,8 @@
|
|
|
32
37
|
"reflect-metadata": "^0.2.2",
|
|
33
38
|
"typescript": "^5.9.3",
|
|
34
39
|
"ws": "^8.19.0",
|
|
35
|
-
"@codemation/host": "0.0.
|
|
36
|
-
"@codemation/next-host": "0.0.
|
|
40
|
+
"@codemation/host": "0.0.11",
|
|
41
|
+
"@codemation/next-host": "0.0.11"
|
|
37
42
|
},
|
|
38
43
|
"devDependencies": {
|
|
39
44
|
"@types/http-proxy": "^1.17.15",
|
|
@@ -3,6 +3,7 @@ import { ApiPaths } from "@codemation/host";
|
|
|
3
3
|
import httpProxy from "http-proxy";
|
|
4
4
|
import type { Duplex } from "node:stream";
|
|
5
5
|
import { WebSocket, WebSocketServer } from "ws";
|
|
6
|
+
import type { ListenPortConflictDescriber } from "./ListenPortConflictDescriber";
|
|
6
7
|
|
|
7
8
|
type WorkflowClientMessage =
|
|
8
9
|
| Readonly<{ kind: "subscribe"; roomId: string }>
|
|
@@ -29,7 +30,10 @@ export class CliDevProxyServer {
|
|
|
29
30
|
private server: HttpServer | null = null;
|
|
30
31
|
private uiProxyTarget: string | null = null;
|
|
31
32
|
|
|
32
|
-
constructor(
|
|
33
|
+
constructor(
|
|
34
|
+
private readonly listenPort: number,
|
|
35
|
+
private readonly listenPortConflictDescriber: ListenPortConflictDescriber,
|
|
36
|
+
) {}
|
|
33
37
|
|
|
34
38
|
async start(): Promise<void> {
|
|
35
39
|
if (this.server) {
|
|
@@ -53,7 +57,9 @@ export class CliDevProxyServer {
|
|
|
53
57
|
this.handleUpgrade(request, socket, head);
|
|
54
58
|
});
|
|
55
59
|
await new Promise<void>((resolve, reject) => {
|
|
56
|
-
server.once("error",
|
|
60
|
+
server.once("error", (error) => {
|
|
61
|
+
void this.rejectListenError(error, reject);
|
|
62
|
+
});
|
|
57
63
|
server.listen(this.listenPort, "127.0.0.1", () => {
|
|
58
64
|
resolve();
|
|
59
65
|
});
|
|
@@ -147,7 +153,6 @@ export class CliDevProxyServer {
|
|
|
147
153
|
private async handleHttpRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
148
154
|
const pathname = this.safePathname(req.url ?? "");
|
|
149
155
|
const uiProxyTarget = this.uiProxyTarget;
|
|
150
|
-
const activeRuntimeTarget = this.activeRuntime ? `http://127.0.0.1:${this.activeRuntime.httpPort}` : undefined;
|
|
151
156
|
if (pathname === "/api/dev/health" && req.method === "GET") {
|
|
152
157
|
res.writeHead(200, { "content-type": "application/json" });
|
|
153
158
|
res.end(
|
|
@@ -160,17 +165,6 @@ export class CliDevProxyServer {
|
|
|
160
165
|
);
|
|
161
166
|
return;
|
|
162
167
|
}
|
|
163
|
-
if (pathname === "/api/dev/bootstrap-summary") {
|
|
164
|
-
if (!activeRuntimeTarget) {
|
|
165
|
-
res.writeHead(503, { "content-type": "text/plain" });
|
|
166
|
-
res.end("Runtime is rebuilding.");
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
this.proxy.web(req, res, {
|
|
170
|
-
target: activeRuntimeTarget,
|
|
171
|
-
});
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
168
|
if (uiProxyTarget && pathname.startsWith("/api/auth/")) {
|
|
175
169
|
this.proxy.web(req, res, {
|
|
176
170
|
target: uiProxyTarget.replace(/\/$/, ""),
|
|
@@ -184,7 +178,7 @@ export class CliDevProxyServer {
|
|
|
184
178
|
return;
|
|
185
179
|
}
|
|
186
180
|
this.proxy.web(req, res, {
|
|
187
|
-
target:
|
|
181
|
+
target: `http://127.0.0.1:${this.activeRuntime.httpPort}`,
|
|
188
182
|
});
|
|
189
183
|
return;
|
|
190
184
|
}
|
|
@@ -230,6 +224,22 @@ export class CliDevProxyServer {
|
|
|
230
224
|
}
|
|
231
225
|
}
|
|
232
226
|
|
|
227
|
+
private async rejectListenError(error: unknown, reject: (reason?: unknown) => void): Promise<void> {
|
|
228
|
+
const errorWithCode = error as Error & Readonly<{ code?: unknown }>;
|
|
229
|
+
if (errorWithCode.code !== "EADDRINUSE") {
|
|
230
|
+
reject(error);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const description = await this.listenPortConflictDescriber.describeLoopbackPort(this.listenPort);
|
|
235
|
+
const baseMessage = `Dev gateway port ${this.listenPort} is already in use on 127.0.0.1.`;
|
|
236
|
+
const suffix =
|
|
237
|
+
description === null
|
|
238
|
+
? " Stop the process using that port or change the configured Codemation dev port."
|
|
239
|
+
: ` Listener: ${description}. Stop that process or change the configured Codemation dev port.`;
|
|
240
|
+
reject(new Error(`${baseMessage}${suffix}`, { cause: error instanceof Error ? error : undefined }));
|
|
241
|
+
}
|
|
242
|
+
|
|
233
243
|
private broadcastDev(message: Readonly<Record<string, unknown>>): void {
|
|
234
244
|
const text = JSON.stringify(message);
|
|
235
245
|
for (const client of this.devClients) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CliDevProxyServer } from "./CliDevProxyServer";
|
|
2
|
+
import { ListenPortConflictDescriber } from "./ListenPortConflictDescriber";
|
|
2
3
|
|
|
3
4
|
export class CliDevProxyServerFactory {
|
|
5
|
+
private readonly listenPortConflictDescriber = new ListenPortConflictDescriber();
|
|
6
|
+
|
|
4
7
|
create(gatewayPort: number): CliDevProxyServer {
|
|
5
|
-
return new CliDevProxyServer(gatewayPort);
|
|
8
|
+
return new CliDevProxyServer(gatewayPort, this.listenPortConflictDescriber);
|
|
6
9
|
}
|
|
7
10
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
|
|
4
|
+
type PortOccupant = Readonly<{
|
|
5
|
+
pid: number;
|
|
6
|
+
command: string;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export class ListenPortConflictDescriber {
|
|
11
|
+
constructor(private readonly platform: NodeJS.Platform = process.platform) {}
|
|
12
|
+
|
|
13
|
+
async describeLoopbackPort(port: number): Promise<string | null> {
|
|
14
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (this.platform !== "linux" && this.platform !== "darwin") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const raw = await this.readLsofOutput(port);
|
|
22
|
+
if (raw === null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const occupants = this.parseLsofOutput(raw);
|
|
26
|
+
if (occupants.length === 0) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return occupants
|
|
31
|
+
.map((occupant) => `pid=${occupant.pid} command=${occupant.command} endpoint=${occupant.endpoint}`)
|
|
32
|
+
.join("; ");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async readLsofOutput(port: number): Promise<string | null> {
|
|
36
|
+
try {
|
|
37
|
+
return await new Promise<string>((resolve, reject) => {
|
|
38
|
+
execFile("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fpcn"], (error, stdout) => {
|
|
39
|
+
if (error) {
|
|
40
|
+
reject(error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
resolve(stdout);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private parseLsofOutput(raw: string): ReadonlyArray<PortOccupant> {
|
|
52
|
+
const occupants: PortOccupant[] = [];
|
|
53
|
+
let currentPid: number | null = null;
|
|
54
|
+
let currentCommand: string | null = null;
|
|
55
|
+
|
|
56
|
+
for (const line of raw.split("\n")) {
|
|
57
|
+
if (line.length < 2) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const prefix = line[0];
|
|
61
|
+
const value = line.slice(1).trim();
|
|
62
|
+
|
|
63
|
+
if (prefix === "p") {
|
|
64
|
+
currentPid = Number.parseInt(value, 10);
|
|
65
|
+
currentCommand = null;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (prefix === "c") {
|
|
69
|
+
currentCommand = value;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (prefix === "n" && currentPid !== null && currentCommand !== null) {
|
|
73
|
+
occupants.push({
|
|
74
|
+
pid: currentPid,
|
|
75
|
+
command: currentCommand,
|
|
76
|
+
endpoint: value,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return occupants;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -3,13 +3,6 @@ import process from "node:process";
|
|
|
3
3
|
|
|
4
4
|
export class TypeScriptRuntimeConfigurator {
|
|
5
5
|
configure(repoRoot: string): void {
|
|
6
|
-
if (this.hasExplicitOverride()) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
6
|
process.env.CODEMATION_TSCONFIG_PATH = path.resolve(repoRoot, "tsconfig.base.json");
|
|
10
7
|
}
|
|
11
|
-
|
|
12
|
-
private hasExplicitOverride(): boolean {
|
|
13
|
-
return typeof process.env.CODEMATION_TSCONFIG_PATH === "string" && process.env.CODEMATION_TSCONFIG_PATH.length > 0;
|
|
14
|
-
}
|
|
15
8
|
}
|