@rudderjs/cli 4.4.0 → 4.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.
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +83 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/doctor/boot-status.d.ts +10 -0
- package/dist/doctor/boot-status.d.ts.map +1 -0
- package/dist/doctor/boot-status.js +15 -0
- package/dist/doctor/boot-status.js.map +1 -0
- package/dist/doctor/built-in/_fs.d.ts +11 -0
- package/dist/doctor/built-in/_fs.d.ts.map +1 -0
- package/dist/doctor/built-in/_fs.js +53 -0
- package/dist/doctor/built-in/_fs.js.map +1 -0
- package/dist/doctor/built-in/deps.d.ts +2 -0
- package/dist/doctor/built-in/deps.d.ts.map +1 -0
- package/dist/doctor/built-in/deps.js +81 -0
- package/dist/doctor/built-in/deps.js.map +1 -0
- package/dist/doctor/built-in/env-vars.d.ts +2 -0
- package/dist/doctor/built-in/env-vars.d.ts.map +1 -0
- package/dist/doctor/built-in/env-vars.js +99 -0
- package/dist/doctor/built-in/env-vars.js.map +1 -0
- package/dist/doctor/built-in/index.d.ts +13 -0
- package/dist/doctor/built-in/index.d.ts.map +1 -0
- package/dist/doctor/built-in/index.js +18 -0
- package/dist/doctor/built-in/index.js.map +1 -0
- package/dist/doctor/built-in/node-version.d.ts +2 -0
- package/dist/doctor/built-in/node-version.d.ts.map +1 -0
- package/dist/doctor/built-in/node-version.js +72 -0
- package/dist/doctor/built-in/node-version.js.map +1 -0
- package/dist/doctor/built-in/package-manager.d.ts +2 -0
- package/dist/doctor/built-in/package-manager.d.ts.map +1 -0
- package/dist/doctor/built-in/package-manager.js +55 -0
- package/dist/doctor/built-in/package-manager.js.map +1 -0
- package/dist/doctor/built-in/runtime.d.ts +2 -0
- package/dist/doctor/built-in/runtime.d.ts.map +1 -0
- package/dist/doctor/built-in/runtime.js +82 -0
- package/dist/doctor/built-in/runtime.js.map +1 -0
- package/dist/doctor/built-in/structure.d.ts +2 -0
- package/dist/doctor/built-in/structure.d.ts.map +1 -0
- package/dist/doctor/built-in/structure.js +93 -0
- package/dist/doctor/built-in/structure.js.map +1 -0
- package/dist/doctor/fixer.d.ts +39 -0
- package/dist/doctor/fixer.d.ts.map +1 -0
- package/dist/doctor/fixer.js +80 -0
- package/dist/doctor/fixer.js.map +1 -0
- package/dist/doctor/load-package-checks.d.ts +2 -0
- package/dist/doctor/load-package-checks.d.ts.map +1 -0
- package/dist/doctor/load-package-checks.js +53 -0
- package/dist/doctor/load-package-checks.js.map +1 -0
- package/dist/doctor/orchestrator.d.ts +34 -0
- package/dist/doctor/orchestrator.d.ts.map +1 -0
- package/dist/doctor/orchestrator.js +65 -0
- package/dist/doctor/orchestrator.js.map +1 -0
- package/dist/doctor/reporter.d.ts +21 -0
- package/dist/doctor/reporter.d.ts.map +1 -0
- package/dist/doctor/reporter.js +151 -0
- package/dist/doctor/reporter.js.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/doctor/built-in/runtime.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { registerDoctorCheck } from '@rudderjs/console';
|
|
4
|
+
import { getBootStatus } from '../boot-status.js';
|
|
5
|
+
// ─── runtime:app-boot ─────────────────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// The boot itself happens in the doctor command's --deep handler (BEFORE
|
|
8
|
+
// runChecks runs); the check's only job is to surface the captured status.
|
|
9
|
+
// This indirection means a boot crash doesn't blow up the orchestrator —
|
|
10
|
+
// every subsequent runtime check still gets a chance to render, and we can
|
|
11
|
+
// recognize "boot didn't happen" (status = null) as a "should never see"
|
|
12
|
+
// signal (it'd mean the doctor command was invoked without --deep but
|
|
13
|
+
// someone left needsBoot on a check by mistake).
|
|
14
|
+
registerDoctorCheck({
|
|
15
|
+
id: 'runtime:app-boot',
|
|
16
|
+
category: 'runtime',
|
|
17
|
+
title: 'Application bootstrap',
|
|
18
|
+
needsBoot: true,
|
|
19
|
+
run() {
|
|
20
|
+
const status = getBootStatus();
|
|
21
|
+
if (!status) {
|
|
22
|
+
return {
|
|
23
|
+
status: 'warn',
|
|
24
|
+
message: 'boot was not attempted (internal — should not see this)',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (status.ok) {
|
|
28
|
+
return { status: 'ok', message: `booted in ${status.durationMs.toFixed(0)}ms` };
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
status: 'error',
|
|
32
|
+
message: `boot threw: ${(status.error ?? '').split('\n')[0]}`,
|
|
33
|
+
detail: status.error ?? '',
|
|
34
|
+
fix: 'Read the stack trace above — usually a missing env var, an unreachable service (DB / Redis / SMTP), or a provider whose dependency hasn\'t been registered. Run without `--deep` to see env/deps checks first.',
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// ─── runtime:port-free ────────────────────────────────────
|
|
39
|
+
function portInUseHolder(port) {
|
|
40
|
+
// `lsof` is preinstalled on macOS / most Linux. The Windows equivalent is
|
|
41
|
+
// `netstat -ano | findstr :3000` — we don't shell out there since this is
|
|
42
|
+
// a best-effort hint and Windows users have less to gain from a PID.
|
|
43
|
+
if (process.platform === 'win32')
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const out = execSync(`lsof -ti :${port}`, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 1000 });
|
|
47
|
+
const pid = out.toString().trim().split('\n')[0];
|
|
48
|
+
return pid || null;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
registerDoctorCheck({
|
|
55
|
+
id: 'runtime:port-free',
|
|
56
|
+
category: 'runtime',
|
|
57
|
+
title: 'PORT free',
|
|
58
|
+
needsBoot: true,
|
|
59
|
+
async run() {
|
|
60
|
+
const port = Number(process.env['PORT'] ?? 3000);
|
|
61
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
62
|
+
return { status: 'warn', message: `PORT="${process.env['PORT']}" is not a valid number` };
|
|
63
|
+
}
|
|
64
|
+
const bound = await new Promise((resolve) => {
|
|
65
|
+
const server = net.createServer();
|
|
66
|
+
server.once('error', (err) => resolve({ ok: false, code: err.code ?? 'UNKNOWN' }));
|
|
67
|
+
server.once('listening', () => server.close(() => resolve({ ok: true })));
|
|
68
|
+
server.listen(port, '127.0.0.1');
|
|
69
|
+
});
|
|
70
|
+
if (bound.ok)
|
|
71
|
+
return { status: 'ok', message: `${port} available` };
|
|
72
|
+
if (bound.code === 'EADDRINUSE') {
|
|
73
|
+
const pid = portInUseHolder(port);
|
|
74
|
+
const fix = pid
|
|
75
|
+
? `Stop the process: \`kill ${pid}\` (or set PORT=<other> in .env)`
|
|
76
|
+
: `Set PORT=<other> in .env, or free port ${port}`;
|
|
77
|
+
return { status: 'error', message: `${port} in use${pid ? ` (PID ${pid})` : ''}`, fix };
|
|
78
|
+
}
|
|
79
|
+
return { status: 'warn', message: `bind failed: ${bound.code}` };
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/doctor/built-in/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../../src/doctor/built-in/structure.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { registerDoctorCheck } from '@rudderjs/console';
|
|
2
|
+
import { fileExists, readFileSafe, anyExists } from './_fs.js';
|
|
3
|
+
registerDoctorCheck({
|
|
4
|
+
id: 'structure:bootstrap-app',
|
|
5
|
+
category: 'structure',
|
|
6
|
+
title: 'bootstrap/app.ts',
|
|
7
|
+
run() {
|
|
8
|
+
if (!fileExists('bootstrap/app.ts')) {
|
|
9
|
+
return {
|
|
10
|
+
status: 'error',
|
|
11
|
+
message: 'missing',
|
|
12
|
+
fix: 'Scaffold a fresh app via `pnpm create rudder@latest`, or write bootstrap/app.ts using Application.configure({...}).create()',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// Lexical-parse only — we deliberately don't import the module here
|
|
16
|
+
// (it boots providers and would defeat the skip-boot fast path).
|
|
17
|
+
const text = readFileSafe('bootstrap/app.ts') ?? '';
|
|
18
|
+
if (!/Application\.configure/.test(text)) {
|
|
19
|
+
return {
|
|
20
|
+
status: 'warn',
|
|
21
|
+
message: 'present but does not call Application.configure(…)',
|
|
22
|
+
fix: 'See docs/guide/bootstrap.md for the expected shape',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return { status: 'ok', message: 'parses' };
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
registerDoctorCheck({
|
|
29
|
+
id: 'structure:bootstrap-providers',
|
|
30
|
+
category: 'structure',
|
|
31
|
+
title: 'bootstrap/providers.ts',
|
|
32
|
+
run() {
|
|
33
|
+
if (!fileExists('bootstrap/providers.ts')) {
|
|
34
|
+
return {
|
|
35
|
+
status: 'error',
|
|
36
|
+
message: 'missing',
|
|
37
|
+
fix: 'Create bootstrap/providers.ts with `export default [...(await defaultProviders())]`',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const text = readFileSafe('bootstrap/providers.ts') ?? '';
|
|
41
|
+
if (!/export\s+default/.test(text)) {
|
|
42
|
+
return {
|
|
43
|
+
status: 'warn',
|
|
44
|
+
message: 'present but has no default export',
|
|
45
|
+
fix: 'bootstrap/providers.ts must `export default [...]`',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { status: 'ok', message: 'has default export' };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
registerDoctorCheck({
|
|
52
|
+
id: 'structure:routes',
|
|
53
|
+
category: 'structure',
|
|
54
|
+
title: 'routes/*',
|
|
55
|
+
run() {
|
|
56
|
+
const have = ['routes/web.ts', 'routes/api.ts', 'routes/console.ts'].filter(fileExists);
|
|
57
|
+
if (have.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
status: 'error',
|
|
60
|
+
message: 'no routes/* files found',
|
|
61
|
+
fix: 'Create at least one of routes/web.ts, routes/api.ts',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return { status: 'ok', message: have.join(', ') };
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
registerDoctorCheck({
|
|
68
|
+
id: 'structure:welcome-view',
|
|
69
|
+
category: 'structure',
|
|
70
|
+
title: 'Welcome view / index page',
|
|
71
|
+
run() {
|
|
72
|
+
// The scaffolder ships one of these depending on the recipe:
|
|
73
|
+
// - Controller-view mode: app/Views/Welcome.* (with `export const route = '/'`)
|
|
74
|
+
// - Vike-direct mode: pages/index/+Page.*
|
|
75
|
+
const viewCandidates = [
|
|
76
|
+
'app/Views/Welcome.tsx', 'app/Views/Welcome.vue', 'app/Views/Welcome.jsx',
|
|
77
|
+
'app/Views/Welcome.ts', 'app/Views/Welcome.js', 'app/Views/Welcome.html',
|
|
78
|
+
];
|
|
79
|
+
const pageCandidates = [
|
|
80
|
+
'pages/index/+Page.tsx', 'pages/index/+Page.vue', 'pages/index/+Page.jsx',
|
|
81
|
+
'pages/index/+Page.ts',
|
|
82
|
+
];
|
|
83
|
+
if (anyExists(viewCandidates) || anyExists(pageCandidates)) {
|
|
84
|
+
return { status: 'ok', message: 'found' };
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
status: 'warn',
|
|
88
|
+
message: 'no landing page found',
|
|
89
|
+
fix: 'Add app/Views/Welcome.tsx (with `export const route = \'/\'`) or pages/index/+Page.tsx',
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../../src/doctor/built-in/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DoctorCheck, DoctorResult } from '@rudderjs/console';
|
|
2
|
+
import type { CheckOutcome } from './orchestrator.js';
|
|
3
|
+
export interface FixOutcome {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
/** Status of the check *before* the fixer ran. */
|
|
7
|
+
before: CheckOutcome['status'];
|
|
8
|
+
/** Status of the check *after* the fixer ran. */
|
|
9
|
+
after: DoctorResult['status'];
|
|
10
|
+
/** Did the user skip (declined the prompt)? */
|
|
11
|
+
skipped: boolean;
|
|
12
|
+
/** Fixer's own message ("ran prisma generate", "vendored 4 files", …). */
|
|
13
|
+
message: string;
|
|
14
|
+
/** If the fixer itself threw, the captured error. */
|
|
15
|
+
error?: string;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
}
|
|
18
|
+
export interface FixOptions {
|
|
19
|
+
/** Skip prompts — assume yes. */
|
|
20
|
+
yes?: boolean;
|
|
21
|
+
/** Override the prompt for tests. Returns true → run, false → skip. */
|
|
22
|
+
prompt?: (check: DoctorCheck, outcome: CheckOutcome) => Promise<boolean> | boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface FixResult {
|
|
25
|
+
outcomes: FixOutcome[];
|
|
26
|
+
/** How many fixers were eligible (failing + has fixer). */
|
|
27
|
+
eligible: number;
|
|
28
|
+
/** How many fixers actually ran (not skipped). */
|
|
29
|
+
applied: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Iterate outcomes from a fast-path doctor run, and for every failing check
|
|
33
|
+
* that declares a `fixer()`, prompt the user (unless `yes` is set) and run it.
|
|
34
|
+
*
|
|
35
|
+
* Fixers must be idempotent regenerate-style operations — a fixer that throws
|
|
36
|
+
* is caught and reported as a red fix outcome; doctor itself never crashes.
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyFixes(outcomes: CheckOutcome[], opts?: FixOptions): Promise<FixResult>;
|
|
39
|
+
//# sourceMappingURL=fixer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixer.d.ts","sourceRoot":"","sources":["../../src/doctor/fixer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAQ,MAAM,CAAA;IAChB,KAAK,EAAK,MAAM,CAAA;IAChB,kDAAkD;IAClD,MAAM,EAAI,YAAY,CAAC,QAAQ,CAAC,CAAA;IAChC,iDAAiD;IACjD,KAAK,EAAK,YAAY,CAAC,QAAQ,CAAC,CAAA;IAChC,+CAA+C;IAC/C,OAAO,EAAG,OAAO,CAAA;IACjB,0EAA0E;IAC1E,OAAO,EAAG,MAAM,CAAA;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAI,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,uEAAuE;IACvE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CACnF;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAG,UAAU,EAAE,CAAA;IACvB,2DAA2D;IAC3D,QAAQ,EAAG,MAAM,CAAA;IACjB,kDAAkD;IAClD,OAAO,EAAI,MAAM,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,YAAY,EAAE,EACxB,IAAI,GAAM,UAAe,GACxB,OAAO,CAAC,SAAS,CAAC,CA2DpB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getRegisteredChecks } from '@rudderjs/console';
|
|
2
|
+
/**
|
|
3
|
+
* Iterate outcomes from a fast-path doctor run, and for every failing check
|
|
4
|
+
* that declares a `fixer()`, prompt the user (unless `yes` is set) and run it.
|
|
5
|
+
*
|
|
6
|
+
* Fixers must be idempotent regenerate-style operations — a fixer that throws
|
|
7
|
+
* is caught and reported as a red fix outcome; doctor itself never crashes.
|
|
8
|
+
*/
|
|
9
|
+
export async function applyFixes(outcomes, opts = {}) {
|
|
10
|
+
// Build an id → check map so we can find each fixer by outcome id.
|
|
11
|
+
const byId = new Map();
|
|
12
|
+
for (const c of getRegisteredChecks())
|
|
13
|
+
byId.set(c.id, c);
|
|
14
|
+
// Eligible = failed (warn|error) AND check declares a fixer.
|
|
15
|
+
const eligible = outcomes.filter(o => o.status !== 'ok' && byId.get(o.id)?.fixer);
|
|
16
|
+
if (eligible.length === 0) {
|
|
17
|
+
return { outcomes: [], eligible: 0, applied: 0 };
|
|
18
|
+
}
|
|
19
|
+
const promptFn = opts.prompt ?? (opts.yes ? () => true : defaultPrompt);
|
|
20
|
+
const fixOutcomes = [];
|
|
21
|
+
let applied = 0;
|
|
22
|
+
for (const outcome of eligible) {
|
|
23
|
+
const check = byId.get(outcome.id);
|
|
24
|
+
const fixer = check.fixer;
|
|
25
|
+
const accepted = await promptFn(check, outcome);
|
|
26
|
+
if (!accepted) {
|
|
27
|
+
fixOutcomes.push({
|
|
28
|
+
id: outcome.id,
|
|
29
|
+
title: outcome.title,
|
|
30
|
+
before: outcome.status,
|
|
31
|
+
after: outcome.status,
|
|
32
|
+
skipped: true,
|
|
33
|
+
message: 'skipped',
|
|
34
|
+
durationMs: 0,
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const t0 = performance.now();
|
|
39
|
+
let result;
|
|
40
|
+
let error;
|
|
41
|
+
try {
|
|
42
|
+
result = await fixer();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
46
|
+
result = { status: 'error', message: `fixer threw: ${msg}` };
|
|
47
|
+
error = msg;
|
|
48
|
+
}
|
|
49
|
+
applied++;
|
|
50
|
+
const fo = {
|
|
51
|
+
id: outcome.id,
|
|
52
|
+
title: outcome.title,
|
|
53
|
+
before: outcome.status,
|
|
54
|
+
after: result.status,
|
|
55
|
+
skipped: false,
|
|
56
|
+
message: result.message,
|
|
57
|
+
durationMs: performance.now() - t0,
|
|
58
|
+
};
|
|
59
|
+
if (error)
|
|
60
|
+
fo.error = error;
|
|
61
|
+
fixOutcomes.push(fo);
|
|
62
|
+
}
|
|
63
|
+
return { outcomes: fixOutcomes, eligible: eligible.length, applied };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Default interactive prompt — uses @clack/prompts. Lazy-imported so the
|
|
67
|
+
* test path can pass `prompt: () => true/false` without pulling clack into
|
|
68
|
+
* the test process at all.
|
|
69
|
+
*/
|
|
70
|
+
async function defaultPrompt(check, outcome) {
|
|
71
|
+
const { confirm, isCancel } = await import('@clack/prompts');
|
|
72
|
+
const result = await confirm({
|
|
73
|
+
message: `Apply fix for ${check.title}? (${outcome.message})`,
|
|
74
|
+
initialValue: true,
|
|
75
|
+
});
|
|
76
|
+
if (isCancel(result))
|
|
77
|
+
return false;
|
|
78
|
+
return result === true;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=fixer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixer.js","sourceRoot":"","sources":["../../src/doctor/fixer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-package-checks.d.ts","sourceRoot":"","sources":["../../src/doctor/load-package-checks.ts"],"names":[],"mappings":"AAkCA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBvD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy-load doctor checks from installed framework packages.
|
|
3
|
+
*
|
|
4
|
+
* Only invoked when `rudder doctor` runs — checks are useless outside the
|
|
5
|
+
* doctor command, so paying the import cost on every CLI invocation would
|
|
6
|
+
* be wasteful.
|
|
7
|
+
*
|
|
8
|
+
* Each contributing package exports a `<package>/doctor` subpath whose
|
|
9
|
+
* import has the side effect of calling `registerDoctorCheck()` for its
|
|
10
|
+
* rules. Adding a new contributing package: append to PACKAGES_WITH_CHECKS
|
|
11
|
+
* AND ensure the package's `package.json#exports` declares the subpath.
|
|
12
|
+
*
|
|
13
|
+
* Resolution: imports resolve from `process.cwd()`, NOT from this file's
|
|
14
|
+
* location. The cli doesn't declare these packages as dependencies — the
|
|
15
|
+
* USER's app does, and pnpm's strict mode means cli's node_modules doesn't
|
|
16
|
+
* see them. createRequire'ing from the user's package.json fixes that.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { pathToFileURL } from 'node:url';
|
|
21
|
+
const PACKAGES_WITH_CHECKS = [
|
|
22
|
+
// Phase 3 first wave — every package here ships a `<package>/doctor`
|
|
23
|
+
// subpath whose side-effect import calls `registerDoctorCheck()` for its
|
|
24
|
+
// rules. The dynamic import is `tryImport`-wrapped below, so packages not
|
|
25
|
+
// installed in the user's app are silently skipped.
|
|
26
|
+
'@rudderjs/auth', '@rudderjs/session', '@rudderjs/hash',
|
|
27
|
+
'@rudderjs/orm-prisma', '@rudderjs/orm-drizzle',
|
|
28
|
+
'@rudderjs/cashier-paddle',
|
|
29
|
+
'@rudderjs/queue-bullmq', '@rudderjs/queue-inngest',
|
|
30
|
+
'@rudderjs/mail',
|
|
31
|
+
'@rudderjs/ai', '@rudderjs/mcp',
|
|
32
|
+
'@rudderjs/telescope', '@rudderjs/pulse', '@rudderjs/horizon',
|
|
33
|
+
];
|
|
34
|
+
export async function loadPackageChecks() {
|
|
35
|
+
// Resolve via direct path through the user's `node_modules/<pkg>/dist/doctor.js`,
|
|
36
|
+
// not `import('<pkg>/doctor')` — the `./doctor` subpath only ships an `import`
|
|
37
|
+
// condition (no `require`/`default`), which `createRequire().resolve()`
|
|
38
|
+
// refuses to match. Walking the symlink/file path bypasses the conditional
|
|
39
|
+
// exports machinery and works the same on pnpm (symlinked) and npm/yarn
|
|
40
|
+
// (flat node_modules). Documented as the ESM-only-peer resolution workaround.
|
|
41
|
+
await Promise.all(PACKAGES_WITH_CHECKS.map(async (pkg) => {
|
|
42
|
+
try {
|
|
43
|
+
const target = path.join(process.cwd(), 'node_modules', pkg, 'dist', 'doctor.js');
|
|
44
|
+
if (!fs.existsSync(target))
|
|
45
|
+
return;
|
|
46
|
+
await import(/* @vite-ignore */ pathToFileURL(target).href);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* package not installed, or its doctor entry failed to load */
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=load-package-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-package-checks.js","sourceRoot":"","sources":["../../src/doctor/load-package-checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,MAAM,oBAAoB,GAAa;IACrC,qEAAqE;IACrE,yEAAyE;IACzE,0EAA0E;IAC1E,oDAAoD;IACpD,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB;IACvD,sBAAsB,EAAE,uBAAuB;IAC/C,0BAA0B;IAC1B,wBAAwB,EAAE,yBAAyB;IACnD,gBAAgB;IAChB,cAAc,EAAE,eAAe;IAC/B,qBAAqB,EAAE,iBAAiB,EAAE,mBAAmB;CAC9D,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,kFAAkF;IAClF,+EAA+E;IAC/E,wEAAwE;IACxE,2EAA2E;IAC3E,wEAAwE;IACxE,8EAA8E;IAC9E,MAAM,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,CAAA;YACjF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAM;YAClC,MAAM,MAAM,CAAC,kBAAkB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAA;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;IACH,CAAC,CAAC,CAAC,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DoctorStatus } from '@rudderjs/console';
|
|
2
|
+
export interface CheckOutcome {
|
|
3
|
+
id: string;
|
|
4
|
+
category: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: DoctorStatus;
|
|
7
|
+
message: string;
|
|
8
|
+
fix?: string;
|
|
9
|
+
detail?: string;
|
|
10
|
+
/** Wall-clock ms for the check's `run()`. */
|
|
11
|
+
durationMs: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RunOptions {
|
|
14
|
+
/** If true, include checks marked `needsBoot: true`. */
|
|
15
|
+
deep?: boolean;
|
|
16
|
+
/** Filter checks by id substring (used by `--only` flag in future). */
|
|
17
|
+
filter?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RunResult {
|
|
20
|
+
outcomes: CheckOutcome[];
|
|
21
|
+
totalMs: number;
|
|
22
|
+
counts: {
|
|
23
|
+
ok: number;
|
|
24
|
+
warn: number;
|
|
25
|
+
error: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Collect every registered check, filter by `deep` / `filter`, run them
|
|
30
|
+
* concurrently within each category (categories run sequentially so the
|
|
31
|
+
* report renders in declared order).
|
|
32
|
+
*/
|
|
33
|
+
export declare function runChecks(opts?: RunOptions): Promise<RunResult>;
|
|
34
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/doctor/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAQ,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAK,MAAM,CAAA;IAChB,MAAM,EAAI,YAAY,CAAA;IACtB,OAAO,EAAG,MAAM,CAAA;IAChB,GAAG,CAAC,EAAM,MAAM,CAAA;IAChB,MAAM,CAAC,EAAG,MAAM,CAAA;IAChB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,IAAI,CAAC,EAAK,OAAO,CAAA;IACjB,uEAAuE;IACvE,MAAM,CAAC,EAAG,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,OAAO,EAAG,MAAM,CAAA;IAChB,MAAM,EAAI;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACtD;AA6BD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA8BzE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getRegisteredChecks } from '@rudderjs/console';
|
|
2
|
+
/**
|
|
3
|
+
* Run a single check. Thrown errors are caught and reported as red outcomes
|
|
4
|
+
* with the message `unhandled exception: <e.message>` so a single buggy check
|
|
5
|
+
* never crashes the doctor command itself.
|
|
6
|
+
*/
|
|
7
|
+
async function runOne(check) {
|
|
8
|
+
const t0 = performance.now();
|
|
9
|
+
let result;
|
|
10
|
+
try {
|
|
11
|
+
result = await check.run();
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15
|
+
result = { status: 'error', message: `unhandled exception: ${msg}` };
|
|
16
|
+
}
|
|
17
|
+
const outcome = {
|
|
18
|
+
id: check.id,
|
|
19
|
+
category: check.category,
|
|
20
|
+
title: check.title,
|
|
21
|
+
status: result.status,
|
|
22
|
+
message: result.message,
|
|
23
|
+
durationMs: performance.now() - t0,
|
|
24
|
+
};
|
|
25
|
+
if (result.fix !== undefined)
|
|
26
|
+
outcome.fix = result.fix;
|
|
27
|
+
if (result.detail !== undefined)
|
|
28
|
+
outcome.detail = result.detail;
|
|
29
|
+
return outcome;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Collect every registered check, filter by `deep` / `filter`, run them
|
|
33
|
+
* concurrently within each category (categories run sequentially so the
|
|
34
|
+
* report renders in declared order).
|
|
35
|
+
*/
|
|
36
|
+
export async function runChecks(opts = {}) {
|
|
37
|
+
const t0 = performance.now();
|
|
38
|
+
const all = getRegisteredChecks().filter(c => {
|
|
39
|
+
if (!opts.deep && c.needsBoot)
|
|
40
|
+
return false;
|
|
41
|
+
// --only <substring> matches EITHER id OR category — `--only orm`
|
|
42
|
+
// catches `orm-prisma:db-connect` AND `orm-drizzle:schema`; `--only runtime`
|
|
43
|
+
// catches every `category: 'runtime'` check regardless of its package prefix.
|
|
44
|
+
if (opts.filter && !c.id.includes(opts.filter) && !c.category.includes(opts.filter))
|
|
45
|
+
return false;
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
// Group by category preserving first-seen order
|
|
49
|
+
const byCat = new Map();
|
|
50
|
+
for (const c of all) {
|
|
51
|
+
const list = byCat.get(c.category) ?? [];
|
|
52
|
+
list.push(c);
|
|
53
|
+
byCat.set(c.category, list);
|
|
54
|
+
}
|
|
55
|
+
const outcomes = [];
|
|
56
|
+
for (const [, checks] of byCat) {
|
|
57
|
+
const results = await Promise.all(checks.map(runOne));
|
|
58
|
+
outcomes.push(...results);
|
|
59
|
+
}
|
|
60
|
+
const counts = { ok: 0, warn: 0, error: 0 };
|
|
61
|
+
for (const o of outcomes)
|
|
62
|
+
counts[o.status]++;
|
|
63
|
+
return { outcomes, totalMs: performance.now() - t0, counts };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/doctor/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RunResult } from './orchestrator.js';
|
|
2
|
+
import type { FixResult } from './fixer.js';
|
|
3
|
+
export interface ReportOptions {
|
|
4
|
+
/** Show `detail` blocks for every outcome (default: only on failure). */
|
|
5
|
+
verbose?: boolean;
|
|
6
|
+
/** Suppress ANSI colors (auto-detected via NO_COLOR or non-TTY). */
|
|
7
|
+
plain?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render a doctor report as one block of stdout-ready text. The caller is
|
|
11
|
+
* responsible for printing — keeps this unit pure for tests.
|
|
12
|
+
*/
|
|
13
|
+
export declare function renderReport(result: RunResult, opts?: ReportOptions): string;
|
|
14
|
+
/** Map outcome counts to a process exit code: 0 if no errors, 1 otherwise. */
|
|
15
|
+
export declare function exitCodeFor(result: RunResult): number;
|
|
16
|
+
/**
|
|
17
|
+
* Render the per-fix outcomes from `applyFixes()` as a single block.
|
|
18
|
+
* Shows before → after status for each eligible check + a summary line.
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderFixReport(result: FixResult, opts?: ReportOptions): string;
|
|
21
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/doctor/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAoB3C,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oEAAoE;IACpE,KAAK,CAAC,EAAI,OAAO,CAAA;CAClB;AAeD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAqDhF;AAgBD,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CA+CnF"}
|