laxy-verify 1.2.0 → 1.2.2
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/README.md +12 -17
- package/dist/audit/broken-links.d.ts +21 -21
- package/dist/audit/broken-links.js +86 -86
- package/dist/auth.d.ts +11 -11
- package/dist/auth.js +222 -222
- package/dist/cli.js +868 -806
- package/dist/comment.d.ts +21 -21
- package/dist/comment.js +125 -125
- package/dist/config.d.ts +13 -0
- package/dist/config.js +43 -3
- package/dist/crawler.d.ts +36 -36
- package/dist/crawler.js +357 -357
- package/dist/e2e.d.ts +49 -49
- package/dist/e2e.js +565 -565
- package/dist/entitlement.d.ts +11 -11
- package/dist/entitlement.js +90 -90
- package/dist/init.js +87 -87
- package/dist/multi-viewport.d.ts +31 -31
- package/dist/multi-viewport.js +298 -298
- package/dist/playwright-runner.d.ts +16 -16
- package/dist/playwright-runner.js +208 -208
- package/dist/report-markdown.d.ts +39 -39
- package/dist/report-markdown.js +386 -386
- package/dist/security-audit.d.ts +9 -9
- package/dist/security-audit.js +64 -64
- package/dist/serve.d.ts +13 -13
- package/dist/serve.js +196 -196
- package/dist/trend.d.ts +50 -50
- package/dist/trend.js +148 -148
- package/dist/verification-core/index.d.ts +3 -3
- package/dist/verification-core/index.js +19 -19
- package/dist/verification-core/report.d.ts +14 -14
- package/dist/verification-core/report.js +409 -409
- package/dist/verification-core/tier-policy.d.ts +13 -13
- package/dist/verification-core/tier-policy.js +60 -60
- package/dist/verification-core/types.d.ts +108 -108
- package/dist/verification-core/types.js +2 -2
- package/dist/visual-diff.d.ts +26 -26
- package/dist/visual-diff.js +178 -178
- package/package.json +1 -1
package/dist/security-audit.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export interface SecurityAuditResult {
|
|
2
|
-
totalVulnerabilities: number;
|
|
3
|
-
critical: number;
|
|
4
|
-
high: number;
|
|
5
|
-
moderate: number;
|
|
6
|
-
low: number;
|
|
7
|
-
summary: string;
|
|
8
|
-
}
|
|
9
|
-
export declare function runSecurityAudit(cwd: string, timeoutMs?: number): Promise<SecurityAuditResult>;
|
|
1
|
+
export interface SecurityAuditResult {
|
|
2
|
+
totalVulnerabilities: number;
|
|
3
|
+
critical: number;
|
|
4
|
+
high: number;
|
|
5
|
+
moderate: number;
|
|
6
|
+
low: number;
|
|
7
|
+
summary: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function runSecurityAudit(cwd: string, timeoutMs?: number): Promise<SecurityAuditResult>;
|
package/dist/security-audit.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runSecurityAudit = runSecurityAudit;
|
|
4
|
-
/**
|
|
5
|
-
* npm audit wrapper for Pro/Pro+ security scanning.
|
|
6
|
-
*
|
|
7
|
-
* Runs `npm audit --json` in the project directory and extracts
|
|
8
|
-
* severity counts + a short summary for the verification report.
|
|
9
|
-
*/
|
|
10
|
-
const node_child_process_1 = require("node:child_process");
|
|
11
|
-
async function runSecurityAudit(cwd, timeoutMs = 30000) {
|
|
12
|
-
console.log(" Running security audit (npm audit)...");
|
|
13
|
-
return new Promise((resolve) => {
|
|
14
|
-
const chunks = [];
|
|
15
|
-
const proc = process.platform === "win32"
|
|
16
|
-
? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/c", "npm audit --json"], {
|
|
17
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
-
cwd,
|
|
19
|
-
})
|
|
20
|
-
: (0, node_child_process_1.spawn)("npm", ["audit", "--json"], {
|
|
21
|
-
shell: true,
|
|
22
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
-
cwd,
|
|
24
|
-
});
|
|
25
|
-
const timer = setTimeout(() => {
|
|
26
|
-
try {
|
|
27
|
-
proc.kill();
|
|
28
|
-
}
|
|
29
|
-
catch { }
|
|
30
|
-
resolve({ totalVulnerabilities: 0, critical: 0, high: 0, moderate: 0, low: 0, summary: "Audit timed out" });
|
|
31
|
-
}, timeoutMs);
|
|
32
|
-
proc.stdout?.on("data", (chunk) => chunks.push(chunk.toString()));
|
|
33
|
-
proc.stderr?.on("data", () => { }); // ignore stderr
|
|
34
|
-
proc.on("exit", () => {
|
|
35
|
-
clearTimeout(timer);
|
|
36
|
-
const raw = chunks.join("");
|
|
37
|
-
try {
|
|
38
|
-
const json = JSON.parse(raw);
|
|
39
|
-
// npm audit v2 format
|
|
40
|
-
const meta = json.metadata?.vulnerabilities ?? json.vulnerabilities ?? {};
|
|
41
|
-
const critical = meta.critical ?? 0;
|
|
42
|
-
const high = meta.high ?? 0;
|
|
43
|
-
const moderate = meta.moderate ?? 0;
|
|
44
|
-
const low = meta.low ?? 0;
|
|
45
|
-
const total = critical + high + moderate + low;
|
|
46
|
-
const parts = [];
|
|
47
|
-
if (critical > 0)
|
|
48
|
-
parts.push(`${critical} critical`);
|
|
49
|
-
if (high > 0)
|
|
50
|
-
parts.push(`${high} high`);
|
|
51
|
-
if (moderate > 0)
|
|
52
|
-
parts.push(`${moderate} moderate`);
|
|
53
|
-
if (low > 0)
|
|
54
|
-
parts.push(`${low} low`);
|
|
55
|
-
const summary = total === 0 ? "No known vulnerabilities" : parts.join(", ");
|
|
56
|
-
console.log(` Security: ${summary}`);
|
|
57
|
-
resolve({ totalVulnerabilities: total, critical, high, moderate, low, summary });
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
resolve({ totalVulnerabilities: 0, critical: 0, high: 0, moderate: 0, low: 0, summary: "Audit parse failed" });
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runSecurityAudit = runSecurityAudit;
|
|
4
|
+
/**
|
|
5
|
+
* npm audit wrapper for Pro/Pro+ security scanning.
|
|
6
|
+
*
|
|
7
|
+
* Runs `npm audit --json` in the project directory and extracts
|
|
8
|
+
* severity counts + a short summary for the verification report.
|
|
9
|
+
*/
|
|
10
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
async function runSecurityAudit(cwd, timeoutMs = 30000) {
|
|
12
|
+
console.log(" Running security audit (npm audit)...");
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
const proc = process.platform === "win32"
|
|
16
|
+
? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/c", "npm audit --json"], {
|
|
17
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
+
cwd,
|
|
19
|
+
})
|
|
20
|
+
: (0, node_child_process_1.spawn)("npm", ["audit", "--json"], {
|
|
21
|
+
shell: true,
|
|
22
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
+
cwd,
|
|
24
|
+
});
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
try {
|
|
27
|
+
proc.kill();
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
resolve({ totalVulnerabilities: 0, critical: 0, high: 0, moderate: 0, low: 0, summary: "Audit timed out" });
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
proc.stdout?.on("data", (chunk) => chunks.push(chunk.toString()));
|
|
33
|
+
proc.stderr?.on("data", () => { }); // ignore stderr
|
|
34
|
+
proc.on("exit", () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
const raw = chunks.join("");
|
|
37
|
+
try {
|
|
38
|
+
const json = JSON.parse(raw);
|
|
39
|
+
// npm audit v2 format
|
|
40
|
+
const meta = json.metadata?.vulnerabilities ?? json.vulnerabilities ?? {};
|
|
41
|
+
const critical = meta.critical ?? 0;
|
|
42
|
+
const high = meta.high ?? 0;
|
|
43
|
+
const moderate = meta.moderate ?? 0;
|
|
44
|
+
const low = meta.low ?? 0;
|
|
45
|
+
const total = critical + high + moderate + low;
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (critical > 0)
|
|
48
|
+
parts.push(`${critical} critical`);
|
|
49
|
+
if (high > 0)
|
|
50
|
+
parts.push(`${high} high`);
|
|
51
|
+
if (moderate > 0)
|
|
52
|
+
parts.push(`${moderate} moderate`);
|
|
53
|
+
if (low > 0)
|
|
54
|
+
parts.push(`${low} low`);
|
|
55
|
+
const summary = total === 0 ? "No known vulnerabilities" : parts.join(", ");
|
|
56
|
+
console.log(` Security: ${summary}`);
|
|
57
|
+
resolve({ totalVulnerabilities: total, critical, high, moderate, low, summary });
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
resolve({ totalVulnerabilities: 0, critical: 0, high: 0, moderate: 0, low: 0, summary: "Audit parse failed" });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export declare class PortConflictError extends Error {
|
|
2
|
-
constructor(port: number);
|
|
3
|
-
}
|
|
4
|
-
export declare class DevServerTimeoutError extends Error {
|
|
5
|
-
constructor(port: number, timeoutSec: number);
|
|
6
|
-
}
|
|
7
|
-
export interface ServeResult {
|
|
8
|
-
pid: number;
|
|
9
|
-
port: number;
|
|
10
|
-
}
|
|
11
|
-
export declare function probeServerStatus(port: number): Promise<number | null>;
|
|
12
|
-
export declare function startDevServer(command: string, port: number, timeoutSec: number, cwd?: string): Promise<ServeResult>;
|
|
13
|
-
export declare function stopDevServer(pid: number): Promise<void>;
|
|
1
|
+
export declare class PortConflictError extends Error {
|
|
2
|
+
constructor(port: number);
|
|
3
|
+
}
|
|
4
|
+
export declare class DevServerTimeoutError extends Error {
|
|
5
|
+
constructor(port: number, timeoutSec: number);
|
|
6
|
+
}
|
|
7
|
+
export interface ServeResult {
|
|
8
|
+
pid: number;
|
|
9
|
+
port: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function probeServerStatus(port: number): Promise<number | null>;
|
|
12
|
+
export declare function startDevServer(command: string, port: number, timeoutSec: number, cwd?: string): Promise<ServeResult>;
|
|
13
|
+
export declare function stopDevServer(pid: number): Promise<void>;
|
package/dist/serve.js
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.DevServerTimeoutError = exports.PortConflictError = void 0;
|
|
40
|
-
exports.probeServerStatus = probeServerStatus;
|
|
41
|
-
exports.startDevServer = startDevServer;
|
|
42
|
-
exports.stopDevServer = stopDevServer;
|
|
43
|
-
const node_child_process_1 = require("node:child_process");
|
|
44
|
-
const fs = __importStar(require("node:fs"));
|
|
45
|
-
const http = __importStar(require("node:http"));
|
|
46
|
-
const os = __importStar(require("node:os"));
|
|
47
|
-
const path = __importStar(require("node:path"));
|
|
48
|
-
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
49
|
-
class PortConflictError extends Error {
|
|
50
|
-
constructor(port) {
|
|
51
|
-
super(`Port ${port} is already in use. Please free the port or configure a different one in .laxy.yml`);
|
|
52
|
-
this.name = "PortConflictError";
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
exports.PortConflictError = PortConflictError;
|
|
56
|
-
class DevServerTimeoutError extends Error {
|
|
57
|
-
constructor(port, timeoutSec) {
|
|
58
|
-
super(`Dev server did not respond with a healthy page on port ${port} within ${timeoutSec}s`);
|
|
59
|
-
this.name = "DevServerTimeoutError";
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
exports.DevServerTimeoutError = DevServerTimeoutError;
|
|
63
|
-
async function killProcessTree(pid) {
|
|
64
|
-
await new Promise((resolve) => {
|
|
65
|
-
let settled = false;
|
|
66
|
-
const finish = () => {
|
|
67
|
-
if (settled)
|
|
68
|
-
return;
|
|
69
|
-
settled = true;
|
|
70
|
-
resolve();
|
|
71
|
-
};
|
|
72
|
-
const fallbackTimer = setTimeout(finish, 5000);
|
|
73
|
-
fallbackTimer.unref?.();
|
|
74
|
-
try {
|
|
75
|
-
(0, tree_kill_1.default)(pid, "SIGKILL", () => {
|
|
76
|
-
clearTimeout(fallbackTimer);
|
|
77
|
-
finish();
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
clearTimeout(fallbackTimer);
|
|
82
|
-
finish();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
function writeWindowsDevWrapper(command, port, cwd) {
|
|
87
|
-
const wrapperDir = path.join(os.tmpdir(), "laxy-verify");
|
|
88
|
-
fs.mkdirSync(wrapperDir, { recursive: true });
|
|
89
|
-
const wrapperPath = path.join(wrapperDir, `dev-wrapper-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.cjs`);
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.DevServerTimeoutError = exports.PortConflictError = void 0;
|
|
40
|
+
exports.probeServerStatus = probeServerStatus;
|
|
41
|
+
exports.startDevServer = startDevServer;
|
|
42
|
+
exports.stopDevServer = stopDevServer;
|
|
43
|
+
const node_child_process_1 = require("node:child_process");
|
|
44
|
+
const fs = __importStar(require("node:fs"));
|
|
45
|
+
const http = __importStar(require("node:http"));
|
|
46
|
+
const os = __importStar(require("node:os"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
49
|
+
class PortConflictError extends Error {
|
|
50
|
+
constructor(port) {
|
|
51
|
+
super(`Port ${port} is already in use. Please free the port or configure a different one in .laxy.yml`);
|
|
52
|
+
this.name = "PortConflictError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.PortConflictError = PortConflictError;
|
|
56
|
+
class DevServerTimeoutError extends Error {
|
|
57
|
+
constructor(port, timeoutSec) {
|
|
58
|
+
super(`Dev server did not respond with a healthy page on port ${port} within ${timeoutSec}s`);
|
|
59
|
+
this.name = "DevServerTimeoutError";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.DevServerTimeoutError = DevServerTimeoutError;
|
|
63
|
+
async function killProcessTree(pid) {
|
|
64
|
+
await new Promise((resolve) => {
|
|
65
|
+
let settled = false;
|
|
66
|
+
const finish = () => {
|
|
67
|
+
if (settled)
|
|
68
|
+
return;
|
|
69
|
+
settled = true;
|
|
70
|
+
resolve();
|
|
71
|
+
};
|
|
72
|
+
const fallbackTimer = setTimeout(finish, 5000);
|
|
73
|
+
fallbackTimer.unref?.();
|
|
74
|
+
try {
|
|
75
|
+
(0, tree_kill_1.default)(pid, "SIGKILL", () => {
|
|
76
|
+
clearTimeout(fallbackTimer);
|
|
77
|
+
finish();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
clearTimeout(fallbackTimer);
|
|
82
|
+
finish();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function writeWindowsDevWrapper(command, port, cwd) {
|
|
87
|
+
const wrapperDir = path.join(os.tmpdir(), "laxy-verify");
|
|
88
|
+
fs.mkdirSync(wrapperDir, { recursive: true });
|
|
89
|
+
const wrapperPath = path.join(wrapperDir, `dev-wrapper-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.cjs`);
|
|
90
90
|
const source = `"use strict";
|
|
91
91
|
const { spawn } = require("node:child_process");
|
|
92
92
|
|
|
@@ -140,110 +140,110 @@ const watchdog = setInterval(() => {
|
|
|
140
140
|
}, 1000);
|
|
141
141
|
|
|
142
142
|
watchdog.unref?.();
|
|
143
|
-
`;
|
|
144
|
-
fs.writeFileSync(wrapperPath, source, "utf-8");
|
|
145
|
-
return wrapperPath;
|
|
146
|
-
}
|
|
147
|
-
function httpGet(url) {
|
|
148
|
-
return new Promise((resolve) => {
|
|
149
|
-
http
|
|
150
|
-
.get(url, { timeout: 2000 }, (res) => {
|
|
151
|
-
resolve(res.statusCode ?? null);
|
|
152
|
-
})
|
|
153
|
-
.on("error", () => {
|
|
154
|
-
resolve(null);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function probeServerStatus(port) {
|
|
159
|
-
return httpGet(`http://localhost:${port}/`);
|
|
160
|
-
}
|
|
161
|
-
async function startDevServer(command, port, timeoutSec, cwd) {
|
|
162
|
-
return new Promise((resolve, reject) => {
|
|
163
|
-
console.log(`Starting dev server: ${command}${cwd ? ` (cwd: ${cwd})` : ""}`);
|
|
164
|
-
let settled = false;
|
|
165
|
-
const wrapperPath = process.platform === "win32"
|
|
166
|
-
? writeWindowsDevWrapper(command, port, cwd)
|
|
167
|
-
: null;
|
|
168
|
-
const proc = process.platform === "win32"
|
|
169
|
-
? (0, node_child_process_1.spawn)("node", [wrapperPath], {
|
|
170
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
171
|
-
env: {
|
|
172
|
-
...process.env,
|
|
173
|
-
PORT: String(port),
|
|
174
|
-
LAXY_VERIFY_PARENT_PID: String(process.pid),
|
|
175
|
-
},
|
|
176
|
-
})
|
|
177
|
-
: (0, node_child_process_1.spawn)(command, {
|
|
178
|
-
shell: true,
|
|
179
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
180
|
-
env: { ...process.env, PORT: String(port) },
|
|
181
|
-
cwd,
|
|
182
|
-
});
|
|
183
|
-
proc.stdout?.on("data", (chunk) => {
|
|
184
|
-
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
185
|
-
for (const line of lines)
|
|
186
|
-
console.log(` [dev] ${line}`);
|
|
187
|
-
});
|
|
188
|
-
proc.stderr?.on("data", (chunk) => {
|
|
189
|
-
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
190
|
-
for (const line of lines)
|
|
191
|
-
console.error(` [dev] ${line}`);
|
|
192
|
-
});
|
|
193
|
-
proc.on("error", (err) => {
|
|
194
|
-
if (settled)
|
|
195
|
-
return;
|
|
196
|
-
settled = true;
|
|
197
|
-
if (err.code === "EADDRINUSE") {
|
|
198
|
-
reject(new PortConflictError(port));
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
reject(new Error(`Dev server error: ${err.message}`));
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
proc.on("exit", (code) => {
|
|
205
|
-
if (wrapperPath) {
|
|
206
|
-
fs.rmSync(wrapperPath, { force: true });
|
|
207
|
-
}
|
|
208
|
-
if (settled)
|
|
209
|
-
return;
|
|
210
|
-
settled = true;
|
|
211
|
-
reject(new Error(`Dev server exited before becoming ready (exit code ${code ?? 1}).`));
|
|
212
|
-
});
|
|
213
|
-
const deadline = Date.now() + timeoutSec * 1000;
|
|
214
|
-
const poll = async () => {
|
|
215
|
-
if (Date.now() >= deadline) {
|
|
216
|
-
if (settled)
|
|
217
|
-
return;
|
|
218
|
-
settled = true;
|
|
219
|
-
if (proc.pid) {
|
|
220
|
-
await killProcessTree(proc.pid);
|
|
221
|
-
}
|
|
222
|
-
if (wrapperPath) {
|
|
223
|
-
fs.rmSync(wrapperPath, { force: true });
|
|
224
|
-
}
|
|
225
|
-
reject(new DevServerTimeoutError(port, timeoutSec));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const status = await probeServerStatus(port);
|
|
229
|
-
if (status !== null) {
|
|
230
|
-
if (status >= 200 && status < 400) {
|
|
231
|
-
if (settled)
|
|
232
|
-
return;
|
|
233
|
-
settled = true;
|
|
234
|
-
console.log(`Dev server ready on port ${port} (HTTP ${status})`);
|
|
235
|
-
resolve({ pid: proc.pid, port });
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
console.log(`Dev server returned HTTP ${status}, waiting for a healthy app surface...`);
|
|
239
|
-
}
|
|
240
|
-
const nextPoll = setTimeout(poll, 500);
|
|
241
|
-
nextPoll.unref?.();
|
|
242
|
-
};
|
|
243
|
-
poll();
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
async function stopDevServer(pid) {
|
|
247
|
-
console.log(`Stopping dev server (PID ${pid})`);
|
|
248
|
-
await killProcessTree(pid);
|
|
249
|
-
}
|
|
143
|
+
`;
|
|
144
|
+
fs.writeFileSync(wrapperPath, source, "utf-8");
|
|
145
|
+
return wrapperPath;
|
|
146
|
+
}
|
|
147
|
+
function httpGet(url) {
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
http
|
|
150
|
+
.get(url, { timeout: 2000 }, (res) => {
|
|
151
|
+
resolve(res.statusCode ?? null);
|
|
152
|
+
})
|
|
153
|
+
.on("error", () => {
|
|
154
|
+
resolve(null);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function probeServerStatus(port) {
|
|
159
|
+
return httpGet(`http://localhost:${port}/`);
|
|
160
|
+
}
|
|
161
|
+
async function startDevServer(command, port, timeoutSec, cwd) {
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
console.log(`Starting dev server: ${command}${cwd ? ` (cwd: ${cwd})` : ""}`);
|
|
164
|
+
let settled = false;
|
|
165
|
+
const wrapperPath = process.platform === "win32"
|
|
166
|
+
? writeWindowsDevWrapper(command, port, cwd)
|
|
167
|
+
: null;
|
|
168
|
+
const proc = process.platform === "win32"
|
|
169
|
+
? (0, node_child_process_1.spawn)("node", [wrapperPath], {
|
|
170
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
171
|
+
env: {
|
|
172
|
+
...process.env,
|
|
173
|
+
PORT: String(port),
|
|
174
|
+
LAXY_VERIFY_PARENT_PID: String(process.pid),
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
: (0, node_child_process_1.spawn)(command, {
|
|
178
|
+
shell: true,
|
|
179
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
180
|
+
env: { ...process.env, PORT: String(port) },
|
|
181
|
+
cwd,
|
|
182
|
+
});
|
|
183
|
+
proc.stdout?.on("data", (chunk) => {
|
|
184
|
+
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
185
|
+
for (const line of lines)
|
|
186
|
+
console.log(` [dev] ${line}`);
|
|
187
|
+
});
|
|
188
|
+
proc.stderr?.on("data", (chunk) => {
|
|
189
|
+
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
190
|
+
for (const line of lines)
|
|
191
|
+
console.error(` [dev] ${line}`);
|
|
192
|
+
});
|
|
193
|
+
proc.on("error", (err) => {
|
|
194
|
+
if (settled)
|
|
195
|
+
return;
|
|
196
|
+
settled = true;
|
|
197
|
+
if (err.code === "EADDRINUSE") {
|
|
198
|
+
reject(new PortConflictError(port));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
reject(new Error(`Dev server error: ${err.message}`));
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
proc.on("exit", (code) => {
|
|
205
|
+
if (wrapperPath) {
|
|
206
|
+
fs.rmSync(wrapperPath, { force: true });
|
|
207
|
+
}
|
|
208
|
+
if (settled)
|
|
209
|
+
return;
|
|
210
|
+
settled = true;
|
|
211
|
+
reject(new Error(`Dev server exited before becoming ready (exit code ${code ?? 1}).`));
|
|
212
|
+
});
|
|
213
|
+
const deadline = Date.now() + timeoutSec * 1000;
|
|
214
|
+
const poll = async () => {
|
|
215
|
+
if (Date.now() >= deadline) {
|
|
216
|
+
if (settled)
|
|
217
|
+
return;
|
|
218
|
+
settled = true;
|
|
219
|
+
if (proc.pid) {
|
|
220
|
+
await killProcessTree(proc.pid);
|
|
221
|
+
}
|
|
222
|
+
if (wrapperPath) {
|
|
223
|
+
fs.rmSync(wrapperPath, { force: true });
|
|
224
|
+
}
|
|
225
|
+
reject(new DevServerTimeoutError(port, timeoutSec));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const status = await probeServerStatus(port);
|
|
229
|
+
if (status !== null) {
|
|
230
|
+
if (status >= 200 && status < 400) {
|
|
231
|
+
if (settled)
|
|
232
|
+
return;
|
|
233
|
+
settled = true;
|
|
234
|
+
console.log(`Dev server ready on port ${port} (HTTP ${status})`);
|
|
235
|
+
resolve({ pid: proc.pid, port });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
console.log(`Dev server returned HTTP ${status}, waiting for a healthy app surface...`);
|
|
239
|
+
}
|
|
240
|
+
const nextPoll = setTimeout(poll, 500);
|
|
241
|
+
nextPoll.unref?.();
|
|
242
|
+
};
|
|
243
|
+
poll();
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async function stopDevServer(pid) {
|
|
247
|
+
console.log(`Stopping dev server (PID ${pid})`);
|
|
248
|
+
await killProcessTree(pid);
|
|
249
|
+
}
|