@nwire/cli 0.9.1 → 0.10.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.
- package/dist/cache-runner.d.ts +0 -1
- package/dist/cache-runner.js +107 -9
- package/dist/cli.d.ts +3 -6
- package/dist/cli.js +5 -7
- package/dist/commands/bench.d.ts +0 -1
- package/dist/commands/bench.js +0 -1
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +13 -17
- package/dist/commands/cache.d.ts +3 -3
- package/dist/commands/cache.js +7 -17
- package/dist/commands/check.d.ts +10 -3
- package/dist/commands/check.js +34 -10
- package/dist/commands/dev.d.ts +0 -1
- package/dist/commands/dev.js +0 -1
- package/dist/commands/doctor.d.ts +1 -2
- package/dist/commands/doctor.js +14 -15
- package/dist/commands/fmt.d.ts +8 -4
- package/dist/commands/fmt.js +35 -12
- package/dist/commands/greeting.d.ts +0 -1
- package/dist/commands/greeting.js +0 -1
- package/dist/commands/infra.d.ts +0 -1
- package/dist/commands/infra.js +0 -1
- package/dist/commands/lint.d.ts +7 -3
- package/dist/commands/lint.js +34 -13
- package/dist/commands/logs.d.ts +0 -1
- package/dist/commands/logs.js +0 -1
- package/dist/commands/ls.d.ts +1 -1
- package/dist/commands/ls.js +3 -1
- package/dist/commands/mcp.d.ts +0 -1
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/please.d.ts +0 -1
- package/dist/commands/please.js +0 -1
- package/dist/commands/ps.d.ts +0 -1
- package/dist/commands/ps.js +0 -1
- package/dist/commands/replay.d.ts +0 -1
- package/dist/commands/replay.js +0 -1
- package/dist/commands/run.d.ts +0 -1
- package/dist/commands/run.js +0 -1
- package/dist/commands/studio.d.ts +0 -1
- package/dist/commands/studio.js +16 -6
- package/dist/commands/test.d.ts +0 -1
- package/dist/commands/test.js +0 -1
- package/dist/commands/trace.d.ts +0 -1
- package/dist/commands/trace.js +0 -1
- package/dist/commands/watch.d.ts +0 -1
- package/dist/commands/watch.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/lib/colors.d.ts +0 -1
- package/dist/lib/colors.js +0 -1
- package/dist/lib/dev-entry.d.ts +0 -1
- package/dist/lib/dev-entry.js +0 -1
- package/dist/lib/ensure-scan.d.ts +28 -0
- package/dist/lib/ensure-scan.js +41 -0
- package/dist/lib/exec.d.ts +0 -1
- package/dist/lib/exec.js +0 -1
- package/dist/lib/layout.d.ts +40 -0
- package/dist/lib/layout.js +128 -0
- package/dist/lib/package-manager.d.ts +17 -0
- package/dist/lib/package-manager.js +67 -0
- package/dist/lib/process-state.d.ts +0 -1
- package/dist/lib/process-state.js +0 -1
- package/dist/lib/project.d.ts +3 -4
- package/dist/lib/project.js +16 -34
- package/dist/lib/run-task.d.ts +0 -1
- package/dist/lib/run-task.js +0 -1
- package/dist/lib/scan-cache.d.ts +27 -0
- package/dist/lib/scan-cache.js +102 -0
- package/dist/lib/script-runner.d.ts +42 -0
- package/dist/lib/script-runner.js +90 -0
- package/dist/lib/sse.d.ts +0 -1
- package/dist/lib/sse.js +0 -1
- package/dist/lib/version.d.ts +8 -0
- package/dist/lib/version.js +29 -0
- package/dist/lib/vite-node.d.ts +0 -1
- package/dist/lib/vite-node.js +0 -1
- package/dist/lib/wire-discovery.d.ts +0 -1
- package/dist/lib/wire-discovery.js +0 -1
- package/dist/load-config.d.ts +0 -1
- package/dist/load-config.js +0 -1
- package/dist/ls-runner.d.ts +0 -1
- package/dist/ls-runner.js +34 -40
- package/dist/ui/dev-dashboard.d.ts +0 -1
- package/dist/ui/dev-dashboard.js +0 -1
- package/dist/ui/greeting.d.ts +0 -1
- package/dist/ui/greeting.js +0 -1
- package/dist/ui/process-table.d.ts +0 -1
- package/dist/ui/process-table.js +0 -1
- package/package.json +5 -6
- package/dist/__tests__/bench.test.d.ts +0 -2
- package/dist/__tests__/bench.test.d.ts.map +0 -1
- package/dist/__tests__/bench.test.js +0 -94
- package/dist/__tests__/bench.test.js.map +0 -1
- package/dist/__tests__/doctor.test.d.ts +0 -10
- package/dist/__tests__/doctor.test.d.ts.map +0 -1
- package/dist/__tests__/doctor.test.js +0 -105
- package/dist/__tests__/doctor.test.js.map +0 -1
- package/dist/__tests__/replay.test.d.ts +0 -2
- package/dist/__tests__/replay.test.d.ts.map +0 -1
- package/dist/__tests__/replay.test.js +0 -203
- package/dist/__tests__/replay.test.js.map +0 -1
- package/dist/__tests__/trace.test.d.ts +0 -2
- package/dist/__tests__/trace.test.d.ts.map +0 -1
- package/dist/__tests__/trace.test.js +0 -136
- package/dist/__tests__/trace.test.js.map +0 -1
- package/dist/__tests__/watch.test.d.ts +0 -2
- package/dist/__tests__/watch.test.d.ts.map +0 -1
- package/dist/__tests__/watch.test.js +0 -110
- package/dist/__tests__/watch.test.js.map +0 -1
- package/dist/cache-runner.d.ts.map +0 -1
- package/dist/cache-runner.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/bench.d.ts.map +0 -1
- package/dist/commands/bench.js.map +0 -1
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/cache.d.ts.map +0 -1
- package/dist/commands/cache.js.map +0 -1
- package/dist/commands/check.d.ts.map +0 -1
- package/dist/commands/check.js.map +0 -1
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/fmt.d.ts.map +0 -1
- package/dist/commands/fmt.js.map +0 -1
- package/dist/commands/greeting.d.ts.map +0 -1
- package/dist/commands/greeting.js.map +0 -1
- package/dist/commands/infra.d.ts.map +0 -1
- package/dist/commands/infra.js.map +0 -1
- package/dist/commands/lint.d.ts.map +0 -1
- package/dist/commands/lint.js.map +0 -1
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/ls.d.ts.map +0 -1
- package/dist/commands/ls.js.map +0 -1
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/please.d.ts.map +0 -1
- package/dist/commands/please.js.map +0 -1
- package/dist/commands/ps.d.ts.map +0 -1
- package/dist/commands/ps.js.map +0 -1
- package/dist/commands/replay.d.ts.map +0 -1
- package/dist/commands/replay.js.map +0 -1
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands/run.js.map +0 -1
- package/dist/commands/studio.d.ts.map +0 -1
- package/dist/commands/studio.js.map +0 -1
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/trace.d.ts.map +0 -1
- package/dist/commands/trace.js.map +0 -1
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/watch.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/kernel-instance.d.ts +0 -8
- package/dist/kernel-instance.d.ts.map +0 -1
- package/dist/kernel-instance.js +0 -13
- package/dist/kernel-instance.js.map +0 -1
- package/dist/lib/colors.d.ts.map +0 -1
- package/dist/lib/colors.js.map +0 -1
- package/dist/lib/dev-entry.d.ts.map +0 -1
- package/dist/lib/dev-entry.js.map +0 -1
- package/dist/lib/exec.d.ts.map +0 -1
- package/dist/lib/exec.js.map +0 -1
- package/dist/lib/process-state.d.ts.map +0 -1
- package/dist/lib/process-state.js.map +0 -1
- package/dist/lib/project.d.ts.map +0 -1
- package/dist/lib/project.js.map +0 -1
- package/dist/lib/run-task.d.ts.map +0 -1
- package/dist/lib/run-task.js.map +0 -1
- package/dist/lib/sse.d.ts.map +0 -1
- package/dist/lib/sse.js.map +0 -1
- package/dist/lib/vite-node.d.ts.map +0 -1
- package/dist/lib/vite-node.js.map +0 -1
- package/dist/lib/wire-discovery.d.ts.map +0 -1
- package/dist/lib/wire-discovery.js.map +0 -1
- package/dist/load-config.d.ts.map +0 -1
- package/dist/load-config.js.map +0 -1
- package/dist/ls-runner.d.ts.map +0 -1
- package/dist/ls-runner.js.map +0 -1
- package/dist/ui/dev-dashboard.d.ts.map +0 -1
- package/dist/ui/dev-dashboard.js.map +0 -1
- package/dist/ui/greeting.d.ts.map +0 -1
- package/dist/ui/greeting.js.map +0 -1
- package/dist/ui/process-table.d.ts.map +0 -1
- package/dist/ui/process-table.js.map +0 -1
package/dist/ui/process-table.js
CHANGED
|
@@ -23,4 +23,3 @@ export function ProcessTable({ records }) {
|
|
|
23
23
|
}
|
|
24
24
|
return (_jsxs(Box, { flexDirection: "column", paddingY: 1, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Box, { width: 10, children: _jsx(Text, { dimColor: true, children: "ID" }) }), _jsx(Box, { width: 24, children: _jsx(Text, { dimColor: true, children: "NAME" }) }), _jsx(Box, { width: 10, children: _jsx(Text, { dimColor: true, children: "STATUS" }) }), _jsx(Box, { width: 8, children: _jsx(Text, { dimColor: true, children: "PORT" }) }), _jsx(Box, { width: 8, children: _jsx(Text, { dimColor: true, children: "PID" }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "STARTED" }) })] }), records.map((r) => (_jsxs(Box, { children: [_jsx(Box, { width: 10, children: _jsx(Text, { children: shortId(r.id) }) }), _jsx(Box, { width: 24, children: _jsx(Text, { children: r.name }) }), _jsx(Box, { width: 10, children: _jsx(Text, { color: statusColor(r.status), children: r.status }) }), _jsx(Box, { width: 8, children: _jsx(Text, { children: r.port ?? "—" }) }), _jsx(Box, { width: 8, children: _jsx(Text, { children: r.pid }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: r.startedAt }) })] }, r.id)))] }));
|
|
25
25
|
}
|
|
26
|
-
//# sourceMappingURL=process-table.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Nwire CLI — branded TUI
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Nwire CLI — branded TUI. Dev, run, please, build, test, fmt, lint, check, ps, logs, cache, ls, studio. One surface for the whole framework.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
7
|
"dev-tool",
|
|
@@ -36,10 +36,9 @@
|
|
|
36
36
|
"listr2": "^9.0.4",
|
|
37
37
|
"picocolors": "^1.1.1",
|
|
38
38
|
"react": "^18.3.1",
|
|
39
|
-
"@nwire/
|
|
40
|
-
"@nwire/mcp": "0.
|
|
41
|
-
"@nwire/scan": "0.
|
|
42
|
-
"@nwire/hooks": "0.9.1"
|
|
39
|
+
"@nwire/hooks": "0.10.0",
|
|
40
|
+
"@nwire/mcp": "0.10.0",
|
|
41
|
+
"@nwire/scan": "0.10.0"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
44
|
"@types/node": "^22.19.9",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bench.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bench.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `nwire bench`.
|
|
3
|
-
*
|
|
4
|
-
* The command shells out to a real `k6` binary at runtime, so we don't
|
|
5
|
-
* exercise the full execution path here. We do test the pieces that
|
|
6
|
-
* deserve assertions:
|
|
7
|
-
*
|
|
8
|
-
* - scenario path resolution (walks up from CWD and finds the right file)
|
|
9
|
-
* - the k6 argv builder (env flags + script tail)
|
|
10
|
-
* - the command meta + arg shape
|
|
11
|
-
*/
|
|
12
|
-
import { describe, it, expect, afterEach, beforeEach } from "vitest";
|
|
13
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
14
|
-
import { tmpdir } from "node:os";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
import { benchCommand, buildK6Args, resolveScenarioPath } from "../commands/bench.js";
|
|
17
|
-
let target;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
target = mkdtempSync(join(tmpdir(), "bench-test-"));
|
|
20
|
-
});
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
if (target)
|
|
23
|
-
rmSync(target, { recursive: true, force: true });
|
|
24
|
-
});
|
|
25
|
-
describe("nwire bench — command meta", () => {
|
|
26
|
-
it("registers a `bench` subcommand with the expected flags", () => {
|
|
27
|
-
const meta = benchCommand.meta;
|
|
28
|
-
expect(meta.name).toBe("bench");
|
|
29
|
-
expect(benchCommand.args).toMatchObject({
|
|
30
|
-
scenario: expect.any(Object),
|
|
31
|
-
vus: expect.any(Object),
|
|
32
|
-
duration: expect.any(Object),
|
|
33
|
-
ramp: expect.any(Object),
|
|
34
|
-
"base-url": expect.any(Object),
|
|
35
|
-
out: expect.any(Object),
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe("resolveScenarioPath", () => {
|
|
40
|
-
it("returns null when no scripts/k6 folder exists anywhere in the walk", () => {
|
|
41
|
-
expect(resolveScenarioPath("action", target, [target])).toBeNull();
|
|
42
|
-
});
|
|
43
|
-
it("finds a script when scripts/k6/<file> exists in CWD", () => {
|
|
44
|
-
mkdirSync(join(target, "scripts", "k6"), { recursive: true });
|
|
45
|
-
writeFileSync(join(target, "scripts", "k6", "action-dispatch.js"), "// stub\n");
|
|
46
|
-
const found = resolveScenarioPath("action", target, [target]);
|
|
47
|
-
expect(found).toBe(join(target, "scripts", "k6", "action-dispatch.js"));
|
|
48
|
-
});
|
|
49
|
-
it("walks up from a root to find the framework's scripts/k6", () => {
|
|
50
|
-
mkdirSync(join(target, "scripts", "k6"), { recursive: true });
|
|
51
|
-
writeFileSync(join(target, "scripts", "k6", "mixed-workload.js"), "// stub\n");
|
|
52
|
-
mkdirSync(join(target, "nested", "deeper"), { recursive: true });
|
|
53
|
-
const found = resolveScenarioPath("mixed", target, [join(target, "nested", "deeper")]);
|
|
54
|
-
expect(found).toBe(join(target, "scripts", "k6", "mixed-workload.js"));
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe("buildK6Args", () => {
|
|
58
|
-
it("emits run + env vars + script path", () => {
|
|
59
|
-
const argv = buildK6Args({
|
|
60
|
-
script: "/path/to/action-dispatch.js",
|
|
61
|
-
args: {
|
|
62
|
-
scenario: "action",
|
|
63
|
-
vus: 50,
|
|
64
|
-
duration: "30s",
|
|
65
|
-
ramp: "10s",
|
|
66
|
-
baseUrl: "http://localhost:3030",
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
expect(argv[0]).toBe("run");
|
|
70
|
-
expect(argv).toContain("-e");
|
|
71
|
-
expect(argv).toContain("VUS=50");
|
|
72
|
-
expect(argv).toContain("DURATION=30s");
|
|
73
|
-
expect(argv).toContain("RAMP=10s");
|
|
74
|
-
expect(argv).toContain("BASE_URL=http://localhost:3030");
|
|
75
|
-
expect(argv[argv.length - 1]).toBe("/path/to/action-dispatch.js");
|
|
76
|
-
});
|
|
77
|
-
it("inserts --summary-export when a summaryOut path is provided", () => {
|
|
78
|
-
const argv = buildK6Args({
|
|
79
|
-
script: "/x.js",
|
|
80
|
-
args: {
|
|
81
|
-
scenario: "read",
|
|
82
|
-
vus: 1,
|
|
83
|
-
duration: "1s",
|
|
84
|
-
ramp: "1s",
|
|
85
|
-
baseUrl: "http://localhost:1",
|
|
86
|
-
},
|
|
87
|
-
summaryOut: "/tmp/read.json",
|
|
88
|
-
});
|
|
89
|
-
expect(argv).toContain("--summary-export");
|
|
90
|
-
const i = argv.indexOf("--summary-export");
|
|
91
|
-
expect(argv[i + 1]).toBe("/tmp/read.json");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
//# sourceMappingURL=bench.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bench.test.js","sourceRoot":"","sources":["../../src/__tests__/bench.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `nwire doctor` — checks run cleanly against fixture directories.
|
|
3
|
-
*
|
|
4
|
-
* Each test builds a tmpdir with the right shape (empty / L4-with-inspect /
|
|
5
|
-
* L4-without-inspect / docker-compose-collision) and asserts the relevant
|
|
6
|
-
* check produces the expected status. Live port probes are exercised
|
|
7
|
-
* against ephemeral bindings.
|
|
8
|
-
*/
|
|
9
|
-
export {};
|
|
10
|
-
//# sourceMappingURL=doctor.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/doctor.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `nwire doctor` — checks run cleanly against fixture directories.
|
|
3
|
-
*
|
|
4
|
-
* Each test builds a tmpdir with the right shape (empty / L4-with-inspect /
|
|
5
|
-
* L4-without-inspect / docker-compose-collision) and asserts the relevant
|
|
6
|
-
* check produces the expected status. Live port probes are exercised
|
|
7
|
-
* against ephemeral bindings.
|
|
8
|
-
*/
|
|
9
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { createServer } from "node:net";
|
|
13
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
14
|
-
import { runDoctor } from "../commands/doctor.js";
|
|
15
|
-
let target;
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
target = mkdtempSync(join(tmpdir(), "doctor-test-"));
|
|
18
|
-
});
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
if (target)
|
|
21
|
-
rmSync(target, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
function pickCheck(results, name) {
|
|
24
|
-
const hit = results.find((r) => r.check.name === name);
|
|
25
|
-
if (!hit)
|
|
26
|
-
throw new Error(`expected check "${name}" in results`);
|
|
27
|
-
return hit.result;
|
|
28
|
-
}
|
|
29
|
-
describe("nwire doctor — workspace shape detection", () => {
|
|
30
|
-
it("workspace fails on a totally empty dir", async () => {
|
|
31
|
-
const results = await runDoctor({ cwd: target });
|
|
32
|
-
expect(pickCheck(results, "workspace").status).toBe("fail");
|
|
33
|
-
});
|
|
34
|
-
it("workspace warns when package.json exists but no apps/src/nwire.config", async () => {
|
|
35
|
-
writeFileSync(join(target, "package.json"), JSON.stringify({ name: "x" }));
|
|
36
|
-
const results = await runDoctor({ cwd: target });
|
|
37
|
-
expect(pickCheck(results, "workspace").status).toBe("warn");
|
|
38
|
-
});
|
|
39
|
-
it("workspace passes when apps/ is present", async () => {
|
|
40
|
-
writeFileSync(join(target, "package.json"), JSON.stringify({ name: "x" }));
|
|
41
|
-
mkdirSync(join(target, "apps"));
|
|
42
|
-
const results = await runDoctor({ cwd: target });
|
|
43
|
-
expect(pickCheck(results, "workspace").status).toBe("pass");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
describe("nwire doctor — inspect-mount audit", () => {
|
|
47
|
-
it("warns when an L4-shaped main.ts is missing api.inspect(app)", async () => {
|
|
48
|
-
writeFileSync(join(target, "package.json"), "{}");
|
|
49
|
-
mkdirSync(join(target, "apps", "svc"), { recursive: true });
|
|
50
|
-
writeFileSync(join(target, "apps", "svc", "main.ts"), `import { createApp } from "@nwire/forge";\n` +
|
|
51
|
-
`const app = createApp({ modules: [] });\n` +
|
|
52
|
-
`await app.start();\n`);
|
|
53
|
-
const results = await runDoctor({ cwd: target, filter: "inspect-mount" });
|
|
54
|
-
const r = pickCheck(results, "inspect-mount");
|
|
55
|
-
expect(r.status).toBe("warn");
|
|
56
|
-
expect(r.hint).toContain("apps/svc/main.ts");
|
|
57
|
-
});
|
|
58
|
-
it("passes when L4 main.ts calls .inspect(app)", async () => {
|
|
59
|
-
writeFileSync(join(target, "package.json"), "{}");
|
|
60
|
-
mkdirSync(join(target, "apps", "svc"), { recursive: true });
|
|
61
|
-
writeFileSync(join(target, "apps", "svc", "main.ts"), `import { createApp } from "@nwire/forge";\n` +
|
|
62
|
-
`const app = createApp({ modules: [] });\n` +
|
|
63
|
-
`await app.start();\n` +
|
|
64
|
-
`api.inspect(app);\n`);
|
|
65
|
-
const results = await runDoctor({ cwd: target, filter: "inspect-mount" });
|
|
66
|
-
expect(pickCheck(results, "inspect-mount").status).toBe("pass");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("nwire doctor — port collision detection", () => {
|
|
70
|
-
it("warns when probe port 9_400 is taken", async () => {
|
|
71
|
-
const srv = createServer();
|
|
72
|
-
await new Promise((res) => srv.listen(9_400, "127.0.0.1", res));
|
|
73
|
-
try {
|
|
74
|
-
const results = await runDoctor({ cwd: target, filter: "probe-port" });
|
|
75
|
-
expect(pickCheck(results, "probe-port").status).toBe("warn");
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
await new Promise((res) => srv.close(() => res()));
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
it("compose-port-collisions fails when docker-compose maps :9400", async () => {
|
|
82
|
-
writeFileSync(join(target, "package.json"), "{}");
|
|
83
|
-
writeFileSync(join(target, "docker-compose.yml"), `services:\n badservice:\n image: foo\n ports:\n - "9400:9400"\n`);
|
|
84
|
-
const results = await runDoctor({ cwd: target, filter: "compose" });
|
|
85
|
-
const r = pickCheck(results, "compose-port-collisions");
|
|
86
|
-
expect(r.status).toBe("fail");
|
|
87
|
-
expect(r.message).toContain("9400");
|
|
88
|
-
});
|
|
89
|
-
it("compose check passes with no compose file", async () => {
|
|
90
|
-
const results = await runDoctor({ cwd: target, filter: "compose" });
|
|
91
|
-
expect(pickCheck(results, "compose-port-collisions").status).toBe("info");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
describe("nwire doctor — filter", () => {
|
|
95
|
-
it("returns only matching checks", async () => {
|
|
96
|
-
const results = await runDoctor({ cwd: target, filter: "node-version" });
|
|
97
|
-
expect(results.length).toBe(1);
|
|
98
|
-
expect(results[0].check.name).toBe("node-version");
|
|
99
|
-
});
|
|
100
|
-
it("returns empty array when nothing matches", async () => {
|
|
101
|
-
const results = await runDoctor({ cwd: target, filter: "zzzzzz" });
|
|
102
|
-
expect(results.length).toBe(0);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
//# sourceMappingURL=doctor.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.test.js","sourceRoot":"","sources":["../../src/__tests__/doctor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replay.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/replay.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `nwire replay`.
|
|
3
|
-
*
|
|
4
|
-
* Same shape as `trace.test.ts`: we don't drive the full citty command
|
|
5
|
-
* end-to-end (it calls `process.exit`), we exercise the composable parts:
|
|
6
|
-
*
|
|
7
|
-
* • command meta + arg shape
|
|
8
|
-
* • loadRecording() against real fixture files
|
|
9
|
-
* • postReplay() happy path against a stubbed wire
|
|
10
|
-
* • postReplay() returns null when wire answers 404 / 501 (graceful fallback)
|
|
11
|
-
* • renderOfflineDiff() output (offline fallback)
|
|
12
|
-
*/
|
|
13
|
-
import { describe, it, expect, afterEach } from "vitest";
|
|
14
|
-
import { createServer } from "node:http";
|
|
15
|
-
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
16
|
-
import { tmpdir } from "node:os";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import { replayCommand, loadRecording, postReplay, renderOfflineDiff, renderReplayResult, } from "../commands/replay.js";
|
|
19
|
-
let server;
|
|
20
|
-
let tmp;
|
|
21
|
-
afterEach(async () => {
|
|
22
|
-
if (server) {
|
|
23
|
-
await new Promise((res) => server.close(() => res()));
|
|
24
|
-
server = undefined;
|
|
25
|
-
}
|
|
26
|
-
if (tmp) {
|
|
27
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
28
|
-
tmp = undefined;
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
function fixtureRecording() {
|
|
32
|
-
return {
|
|
33
|
-
hookId: "h_test",
|
|
34
|
-
hookName: "submission.submit",
|
|
35
|
-
runId: "r_1",
|
|
36
|
-
outcome: "completed",
|
|
37
|
-
steps: [
|
|
38
|
-
{
|
|
39
|
-
hookName: "submission.submit",
|
|
40
|
-
hookId: "h_test",
|
|
41
|
-
runId: "r_1",
|
|
42
|
-
stepId: 0,
|
|
43
|
-
stepKind: "chain",
|
|
44
|
-
stepName: "validate",
|
|
45
|
-
phase: "start",
|
|
46
|
-
ts: 0,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
hookName: "submission.submit",
|
|
50
|
-
hookId: "h_test",
|
|
51
|
-
runId: "r_1",
|
|
52
|
-
stepId: 0,
|
|
53
|
-
stepKind: "chain",
|
|
54
|
-
stepName: "validate",
|
|
55
|
-
phase: "end",
|
|
56
|
-
ts: 1,
|
|
57
|
-
durationMs: 1,
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
ctxIn: { studentId: "s1" },
|
|
61
|
-
ctxOut: { studentId: "s1", validated: true },
|
|
62
|
-
capturedAt: "2026-05-29T10:00:00.000Z",
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function writeFixture(rec) {
|
|
66
|
-
tmp = mkdtempSync(join(tmpdir(), "replay-test-"));
|
|
67
|
-
const path = join(tmp, "rec.json");
|
|
68
|
-
writeFileSync(path, JSON.stringify(rec));
|
|
69
|
-
return path;
|
|
70
|
-
}
|
|
71
|
-
describe("nwire replay — command meta", () => {
|
|
72
|
-
it("registers a `replay` subcommand with a positional file + optional --hook", () => {
|
|
73
|
-
expect(replayCommand.meta).toBeDefined();
|
|
74
|
-
const meta = replayCommand.meta;
|
|
75
|
-
expect(meta.name).toBe("replay");
|
|
76
|
-
const args = replayCommand
|
|
77
|
-
.args;
|
|
78
|
-
expect(args.file).toMatchObject({ type: "positional", required: true });
|
|
79
|
-
expect(args.hook).toMatchObject({ type: "string" });
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
describe("loadRecording", () => {
|
|
83
|
-
it("loads + parses a recording JSON file", () => {
|
|
84
|
-
const rec = fixtureRecording();
|
|
85
|
-
const path = writeFixture(rec);
|
|
86
|
-
const out = loadRecording(path);
|
|
87
|
-
expect(out.hookName).toBe("submission.submit");
|
|
88
|
-
expect(out.steps).toHaveLength(2);
|
|
89
|
-
});
|
|
90
|
-
it("rejects a file whose shape doesn't look like a Recording", () => {
|
|
91
|
-
tmp = mkdtempSync(join(tmpdir(), "replay-test-"));
|
|
92
|
-
const path = join(tmp, "bad.json");
|
|
93
|
-
writeFileSync(path, JSON.stringify({ hello: "world" }));
|
|
94
|
-
expect(() => loadRecording(path)).toThrow(/not a Recording/);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
describe("postReplay — happy path against a stubbed wire", () => {
|
|
98
|
-
it("POSTs the recording and parses the wire response", async () => {
|
|
99
|
-
const rec = fixtureRecording();
|
|
100
|
-
server = createServer((req, res) => {
|
|
101
|
-
if (req.method === "POST" && req.url === "/_nwire/hooks/replay") {
|
|
102
|
-
const chunks = [];
|
|
103
|
-
req.on("data", (c) => chunks.push(c));
|
|
104
|
-
req.on("end", () => {
|
|
105
|
-
const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
106
|
-
expect(body.recording.hookName).toBe("submission.submit");
|
|
107
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
108
|
-
res.end(JSON.stringify({
|
|
109
|
-
matches: true,
|
|
110
|
-
drift: [],
|
|
111
|
-
recorded: body.recording,
|
|
112
|
-
replayed: { stepCount: 2, outcome: "completed" },
|
|
113
|
-
}));
|
|
114
|
-
});
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
res.writeHead(404);
|
|
118
|
-
res.end();
|
|
119
|
-
});
|
|
120
|
-
await new Promise((res) => server.listen(0, "127.0.0.1", res));
|
|
121
|
-
const port = server.address().port;
|
|
122
|
-
const result = await postReplay({
|
|
123
|
-
url: `http://127.0.0.1:${port}`,
|
|
124
|
-
recording: rec,
|
|
125
|
-
});
|
|
126
|
-
expect(result).not.toBeNull();
|
|
127
|
-
expect(result.matches).toBe(true);
|
|
128
|
-
expect(result.replayed.stepCount).toBe(2);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe("postReplay — graceful fallback when endpoint is missing", () => {
|
|
132
|
-
it("returns null on 404 (no /_nwire/hooks/replay mount)", async () => {
|
|
133
|
-
server = createServer((_req, res) => {
|
|
134
|
-
res.writeHead(404);
|
|
135
|
-
res.end();
|
|
136
|
-
});
|
|
137
|
-
await new Promise((res) => server.listen(0, "127.0.0.1", res));
|
|
138
|
-
const port = server.address().port;
|
|
139
|
-
const result = await postReplay({
|
|
140
|
-
url: `http://127.0.0.1:${port}`,
|
|
141
|
-
recording: fixtureRecording(),
|
|
142
|
-
});
|
|
143
|
-
expect(result).toBeNull();
|
|
144
|
-
});
|
|
145
|
-
it("returns null on 501 Not Implemented", async () => {
|
|
146
|
-
server = createServer((_req, res) => {
|
|
147
|
-
res.writeHead(501);
|
|
148
|
-
res.end();
|
|
149
|
-
});
|
|
150
|
-
await new Promise((res) => server.listen(0, "127.0.0.1", res));
|
|
151
|
-
const port = server.address().port;
|
|
152
|
-
const result = await postReplay({
|
|
153
|
-
url: `http://127.0.0.1:${port}`,
|
|
154
|
-
recording: fixtureRecording(),
|
|
155
|
-
});
|
|
156
|
-
expect(result).toBeNull();
|
|
157
|
-
});
|
|
158
|
-
it("throws on other non-OK statuses (5xx that isn't 501)", async () => {
|
|
159
|
-
server = createServer((_req, res) => {
|
|
160
|
-
res.writeHead(500);
|
|
161
|
-
res.end("boom");
|
|
162
|
-
});
|
|
163
|
-
await new Promise((res) => server.listen(0, "127.0.0.1", res));
|
|
164
|
-
const port = server.address().port;
|
|
165
|
-
await expect(postReplay({ url: `http://127.0.0.1:${port}`, recording: fixtureRecording() })).rejects.toThrow(/500/);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
describe("renderOfflineDiff", () => {
|
|
169
|
-
it("includes the offline-only banner + the step trace", () => {
|
|
170
|
-
const out = renderOfflineDiff(fixtureRecording());
|
|
171
|
-
expect(out).toContain("/_nwire/hooks/replay");
|
|
172
|
-
expect(out).toContain("offline-only");
|
|
173
|
-
expect(out).toContain("submission.submit");
|
|
174
|
-
expect(out).toContain("validate");
|
|
175
|
-
expect(out).toContain("outcome=completed");
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
describe("renderReplayResult", () => {
|
|
179
|
-
it("prints the match banner + step alignment", () => {
|
|
180
|
-
const rec = fixtureRecording();
|
|
181
|
-
const out = renderReplayResult({
|
|
182
|
-
matches: true,
|
|
183
|
-
drift: [],
|
|
184
|
-
recorded: rec,
|
|
185
|
-
replayed: { stepCount: rec.steps.length, outcome: "completed" },
|
|
186
|
-
});
|
|
187
|
-
expect(out).toContain("matches");
|
|
188
|
-
expect(out).toContain("validate");
|
|
189
|
-
});
|
|
190
|
-
it("surfaces drift entries when matches=false", () => {
|
|
191
|
-
const rec = fixtureRecording();
|
|
192
|
-
const out = renderReplayResult({
|
|
193
|
-
matches: false,
|
|
194
|
-
drift: ["step[1]: recorded=chain/validate/end replayed=chain/validate/error"],
|
|
195
|
-
recorded: rec,
|
|
196
|
-
replayed: { stepCount: 2, outcome: "failed" },
|
|
197
|
-
});
|
|
198
|
-
expect(out).toContain("drift");
|
|
199
|
-
expect(out).toContain("validate/error");
|
|
200
|
-
expect(out).toContain("outcome=failed");
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
//# sourceMappingURL=replay.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replay.test.js","sourceRoot":"","sources":["../../src/__tests__/replay.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trace.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/trace.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `nwire trace`.
|
|
3
|
-
*
|
|
4
|
-
* Long-running stream behaviour is intentionally NOT exercised end-to-end
|
|
5
|
-
* (timing-flakey). We test the small composable pieces: command meta,
|
|
6
|
-
* wire discovery against a real loopback HTTP server, the SSE parser,
|
|
7
|
-
* and the line/tree formatters.
|
|
8
|
-
*/
|
|
9
|
-
import { describe, it, expect, afterEach } from "vitest";
|
|
10
|
-
import { createServer } from "node:http";
|
|
11
|
-
import { traceCommand, formatSummaryLine, renderTree, shortId, formatTimestamp, } from "../commands/trace.js";
|
|
12
|
-
import { SseParser } from "../lib/sse.js";
|
|
13
|
-
import { findWireUrl } from "../lib/wire-discovery.js";
|
|
14
|
-
let server;
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
if (server) {
|
|
17
|
-
await new Promise((res) => server.close(() => res()));
|
|
18
|
-
server = undefined;
|
|
19
|
-
}
|
|
20
|
-
delete process.env.NWIRE_INSPECT_URL;
|
|
21
|
-
});
|
|
22
|
-
describe("nwire trace — command meta", () => {
|
|
23
|
-
it("registers a `trace` subcommand with optional --correlation and --limit", () => {
|
|
24
|
-
expect(traceCommand.meta).toBeDefined();
|
|
25
|
-
const meta = traceCommand.meta;
|
|
26
|
-
expect(meta.name).toBe("trace");
|
|
27
|
-
expect(traceCommand.args).toMatchObject({
|
|
28
|
-
correlation: expect.any(Object),
|
|
29
|
-
limit: expect.any(Object),
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe("findWireUrl — port-probe discovery", () => {
|
|
34
|
-
it("returns the URL of a stubbed HTTP server that answers /_nwire/events/recent", async () => {
|
|
35
|
-
server = createServer((req, res) => {
|
|
36
|
-
if (req.url?.startsWith("/_nwire/events/recent")) {
|
|
37
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
38
|
-
res.end("[]");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
res.writeHead(404);
|
|
42
|
-
res.end();
|
|
43
|
-
});
|
|
44
|
-
await new Promise((res) => server.listen(0, "127.0.0.1", res));
|
|
45
|
-
const port = server.address().port;
|
|
46
|
-
process.env.NWIRE_INSPECT_URL = `http://127.0.0.1:${port}`;
|
|
47
|
-
const url = await findWireUrl();
|
|
48
|
-
expect(url).toBe(`http://127.0.0.1:${port}`);
|
|
49
|
-
});
|
|
50
|
-
it("strips a trailing slash from NWIRE_INSPECT_URL", async () => {
|
|
51
|
-
process.env.NWIRE_INSPECT_URL = "http://127.0.0.1:9999/";
|
|
52
|
-
expect(await findWireUrl()).toBe("http://127.0.0.1:9999");
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe("SseParser — frame extraction", () => {
|
|
56
|
-
it("parses a single complete frame", () => {
|
|
57
|
-
const parser = new SseParser();
|
|
58
|
-
const out = parser.feed(`data: {"a":1}\n\n`);
|
|
59
|
-
expect(out).toEqual([{ a: 1 }]);
|
|
60
|
-
});
|
|
61
|
-
it("parses multiple frames in one feed call", () => {
|
|
62
|
-
const parser = new SseParser();
|
|
63
|
-
const out = parser.feed(`data: {"a":1}\n\ndata: {"b":2}\n\ndata: {"c":3}\n\n`);
|
|
64
|
-
expect(out).toEqual([{ a: 1 }, { b: 2 }, { c: 3 }]);
|
|
65
|
-
});
|
|
66
|
-
it("buffers a partial frame across two feed calls", () => {
|
|
67
|
-
const parser = new SseParser();
|
|
68
|
-
expect(parser.feed(`data: {"a":`)).toEqual([]);
|
|
69
|
-
expect(parser.feed(`1}\n\n`)).toEqual([{ a: 1 }]);
|
|
70
|
-
});
|
|
71
|
-
it("ignores SSE comment / heartbeat frames", () => {
|
|
72
|
-
const parser = new SseParser();
|
|
73
|
-
const out = parser.feed(`: heartbeat\n\ndata: {"k":"v"}\n\n`);
|
|
74
|
-
expect(out).toEqual([{ k: "v" }]);
|
|
75
|
-
});
|
|
76
|
-
it("joins multi-line data: payloads with newline", () => {
|
|
77
|
-
const parser = new SseParser();
|
|
78
|
-
const out = parser.feed(`data: {"a":\ndata: 1}\n\n`);
|
|
79
|
-
expect(out).toEqual([{ a: 1 }]);
|
|
80
|
-
});
|
|
81
|
-
it("tolerates \\r\\n line endings", () => {
|
|
82
|
-
const parser = new SseParser();
|
|
83
|
-
const out = parser.feed(`data: {"a":1}\r\n\r\n`);
|
|
84
|
-
expect(out).toEqual([{ a: 1 }]);
|
|
85
|
-
});
|
|
86
|
-
it("drops frames whose JSON is malformed", () => {
|
|
87
|
-
const parser = new SseParser();
|
|
88
|
-
expect(parser.feed(`data: {not-json}\n\n`)).toEqual([]);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
describe("trace — formatters", () => {
|
|
92
|
-
it("formatTimestamp pads to HH:MM:SS.mmm", () => {
|
|
93
|
-
const ts = formatTimestamp("2026-05-29T10:05:03.040Z");
|
|
94
|
-
expect(ts).toMatch(/^\d{2}:\d{2}:\d{2}\.\d{3}$/);
|
|
95
|
-
});
|
|
96
|
-
it("shortId returns 8 chars (strips known prefixes)", () => {
|
|
97
|
-
expect(shortId("evt_abc12345678")).toBe("12345678");
|
|
98
|
-
expect(shortId(undefined)).toBe("--------");
|
|
99
|
-
});
|
|
100
|
-
it("formatSummaryLine includes the event name", () => {
|
|
101
|
-
const line = formatSummaryLine({
|
|
102
|
-
name: "Submission.Submitted",
|
|
103
|
-
envelope: {
|
|
104
|
-
messageId: "m_abcdefgh",
|
|
105
|
-
causationId: "c_zyxwvuts",
|
|
106
|
-
correlationId: "k_corrcorr",
|
|
107
|
-
occurredAt: "2026-05-29T10:00:00.000Z",
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
// strip ANSI for readability — picocolors emits codes only when stdout is a TTY,
|
|
111
|
-
// so under vitest most colours are dropped already.
|
|
112
|
-
expect(line).toContain("Submission.Submitted");
|
|
113
|
-
expect(line).toMatch(/\d{2}:\d{2}:\d{2}\.\d{3}/);
|
|
114
|
-
});
|
|
115
|
-
it("renderTree nests children under their causation parent", () => {
|
|
116
|
-
const tree = renderTree([
|
|
117
|
-
{ name: "Root", envelope: { messageId: "ROOT" } },
|
|
118
|
-
{ name: "ChildA", envelope: { messageId: "A", causationId: "ROOT" } },
|
|
119
|
-
{ name: "ChildB", envelope: { messageId: "B", causationId: "ROOT" } },
|
|
120
|
-
{ name: "GrandA", envelope: { messageId: "GA", causationId: "A" } },
|
|
121
|
-
]);
|
|
122
|
-
expect(tree).toContain("Root");
|
|
123
|
-
expect(tree).toContain("ChildA");
|
|
124
|
-
expect(tree).toContain("ChildB");
|
|
125
|
-
expect(tree).toContain("GrandA");
|
|
126
|
-
// ChildA appears before GrandA in the output (parent before child)
|
|
127
|
-
expect(tree.indexOf("ChildA")).toBeLessThan(tree.indexOf("GrandA"));
|
|
128
|
-
});
|
|
129
|
-
it("renderTree treats orphans as additional roots", () => {
|
|
130
|
-
const tree = renderTree([
|
|
131
|
-
{ name: "Orphan", envelope: { messageId: "X", causationId: "missing-parent" } },
|
|
132
|
-
]);
|
|
133
|
-
expect(tree).toContain("Orphan");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
//# sourceMappingURL=trace.test.js.map
|