@dasasian/firebase-structured-logger 0.9.0 → 1.0.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/README.md +135 -32
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/logger.d.ts +6 -2
- package/dist/client/logger.d.ts.map +1 -1
- package/dist/client/logger.js +16 -7
- package/dist/client/logger.js.map +1 -1
- package/dist/client/rateLimiter.d.ts +12 -5
- package/dist/client/rateLimiter.d.ts.map +1 -1
- package/dist/client/rateLimiter.js +61 -12
- package/dist/client/rateLimiter.js.map +1 -1
- package/dist/functions/httpHandler.d.ts +1 -1
- package/dist/functions/httpHandler.js +1 -1
- package/dist/functions/index.d.ts +1 -1
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/logHandler.d.ts +10 -5
- package/dist/functions/logHandler.d.ts.map +1 -1
- package/dist/functions/logHandler.js +16 -5
- package/dist/functions/logHandler.js.map +1 -1
- package/dist/functions/logger.js +1 -1
- package/dist/functions/logger.js.map +1 -1
- package/dist/functions/sourceMapCache.js +1 -1
- package/dist/functions/sourceMapCache.js.map +1 -1
- package/dist/shared/deprecate.d.ts +4 -0
- package/dist/shared/deprecate.d.ts.map +1 -0
- package/dist/shared/deprecate.js +30 -0
- package/dist/shared/deprecate.js.map +1 -0
- package/dist/shared/nodeModules.d.ts +39 -0
- package/dist/shared/nodeModules.d.ts.map +1 -0
- package/dist/shared/nodeModules.js +120 -0
- package/dist/shared/nodeModules.js.map +1 -0
- package/dist/shared/versionRange.d.ts +17 -0
- package/dist/shared/versionRange.d.ts.map +1 -0
- package/dist/shared/versionRange.js +48 -0
- package/dist/shared/versionRange.js.map +1 -0
- package/dist/tools/doctor.d.ts +54 -0
- package/dist/tools/doctor.d.ts.map +1 -0
- package/dist/tools/doctor.js +381 -0
- package/dist/tools/doctor.js.map +1 -0
- package/dist/tools/index.js +34 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/uploadSourceMaps.d.ts +7 -0
- package/dist/tools/uploadSourceMaps.d.ts.map +1 -1
- package/dist/tools/uploadSourceMaps.js.map +1 -1
- package/package.json +11 -11
- package/skills/query-logs/SKILL.md +14 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reading facts out of an installed `node_modules` tree.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `fsl doctor` (duplicate Storage copies, installed peer versions) and
|
|
6
|
+
* `smoke/install.ts` (the same duplicate-copy check, against a real install). Moved
|
|
7
|
+
* here so the two do not carry their own copies of the same walker — doctor's fake
|
|
8
|
+
* projects and the smoke install are different trees, but "find every copy of this
|
|
9
|
+
* package under here" is the same question in both.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.readPackageJson = readPackageJson;
|
|
46
|
+
exports.findPackageCopies = findPackageCopies;
|
|
47
|
+
exports.findTopLevelVersion = findTopLevelVersion;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
/**
|
|
51
|
+
* Read `<dir>/package.json`, distinguishing "not installed" from "installed but
|
|
52
|
+
* unreadable" — doctor treats the second as `could-not-check`, never as absent.
|
|
53
|
+
*/
|
|
54
|
+
function readPackageJson(dir) {
|
|
55
|
+
const file = path.join(dir, 'package.json');
|
|
56
|
+
if (!fs.existsSync(file))
|
|
57
|
+
return { ok: false, reason: 'missing' };
|
|
58
|
+
try {
|
|
59
|
+
return { ok: true, pkg: JSON.parse(fs.readFileSync(file, 'utf-8')) };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { ok: false, reason: 'malformed' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Every installed copy of `packageName` under `rootDir/node_modules`, including
|
|
67
|
+
* copies nested under another package's own `node_modules` (npm dedupe leaves
|
|
68
|
+
* exactly one; an un-deduped tree can have several).
|
|
69
|
+
*/
|
|
70
|
+
function findPackageCopies(rootDir, packageName) {
|
|
71
|
+
const found = [];
|
|
72
|
+
const targetSuffix = path.join(...packageName.split('/'));
|
|
73
|
+
const walk = (nodeModules) => {
|
|
74
|
+
if (!fs.existsSync(nodeModules))
|
|
75
|
+
return;
|
|
76
|
+
for (const entry of safeReaddir(nodeModules)) {
|
|
77
|
+
if (entry.startsWith('.'))
|
|
78
|
+
continue;
|
|
79
|
+
const entryDir = path.join(nodeModules, entry);
|
|
80
|
+
const pkgDirs = entry.startsWith('@')
|
|
81
|
+
? safeReaddir(entryDir).map((n) => path.join(entryDir, n))
|
|
82
|
+
: [entryDir];
|
|
83
|
+
for (const pkgDir of pkgDirs) {
|
|
84
|
+
if (pkgDir.endsWith(targetSuffix)) {
|
|
85
|
+
const read = readPackageJson(pkgDir);
|
|
86
|
+
if (read.ok && typeof read.pkg.version === 'string') {
|
|
87
|
+
found.push({ dir: pkgDir, version: read.pkg.version });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
walk(path.join(pkgDir, 'node_modules'));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
walk(path.join(rootDir, 'node_modules'));
|
|
95
|
+
return found;
|
|
96
|
+
}
|
|
97
|
+
function safeReaddir(dir) {
|
|
98
|
+
try {
|
|
99
|
+
return fs.readdirSync(dir);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The version of `packageName` installed directly under one of `searchDirs`,
|
|
107
|
+
* checked in order — the first directory with a top-level copy wins. Used to
|
|
108
|
+
* resolve peers (`firebase`, `firebase-admin`, `firebase-functions`), which are
|
|
109
|
+
* singletons in a working tree, unlike `@google-cloud/storage`.
|
|
110
|
+
*/
|
|
111
|
+
function findTopLevelVersion(searchDirs, packageName) {
|
|
112
|
+
for (const dir of searchDirs) {
|
|
113
|
+
const pkgDir = path.join(dir, 'node_modules', ...packageName.split('/'));
|
|
114
|
+
const read = readPackageJson(pkgDir);
|
|
115
|
+
if (read.ok && typeof read.pkg.version === 'string')
|
|
116
|
+
return read.pkg.version;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=nodeModules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeModules.js","sourceRoot":"","sources":["../../src/shared/nodeModules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one range shape this package's own `peerDependencies` use: `>=X.Y.Z`.
|
|
3
|
+
*
|
|
4
|
+
* Not a general semver comparator — `unsupported-peer` reads ranges from this
|
|
5
|
+
* package's own `package.json` at runtime rather than copying them into doctor
|
|
6
|
+
* (so the two cannot drift), and every peer range there is a plain minimum today.
|
|
7
|
+
* If a future range needs `^` or `||`, this returns `undefined` rather than a
|
|
8
|
+
* wrong answer, and the caller skips the check instead of guessing.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether `version` meets `range`, or `undefined` when `range` is not a plain
|
|
12
|
+
* `>=X.Y.Z` minimum — the caller's signal to skip rather than misreport.
|
|
13
|
+
*/
|
|
14
|
+
export declare function meetsMinimum(version: string, range: string): boolean | undefined;
|
|
15
|
+
/** The leading major version number in a string like `22.11.0`, `^22`, or `nodejs20`. */
|
|
16
|
+
export declare function leadingMajorVersion(text: string): number | undefined;
|
|
17
|
+
//# sourceMappingURL=versionRange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionRange.d.ts","sourceRoot":"","sources":["../../src/shared/versionRange.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkBH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAOhF;AAED,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGpE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The one range shape this package's own `peerDependencies` use: `>=X.Y.Z`.
|
|
4
|
+
*
|
|
5
|
+
* Not a general semver comparator — `unsupported-peer` reads ranges from this
|
|
6
|
+
* package's own `package.json` at runtime rather than copying them into doctor
|
|
7
|
+
* (so the two cannot drift), and every peer range there is a plain minimum today.
|
|
8
|
+
* If a future range needs `^` or `||`, this returns `undefined` rather than a
|
|
9
|
+
* wrong answer, and the caller skips the check instead of guessing.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.meetsMinimum = meetsMinimum;
|
|
13
|
+
exports.leadingMajorVersion = leadingMajorVersion;
|
|
14
|
+
const VERSION = /^(\d+)\.(\d+)\.(\d+)/;
|
|
15
|
+
const MINIMUM_RANGE = /^>=\s*(\d+)\.(\d+)\.(\d+)/;
|
|
16
|
+
function parseVersion(version) {
|
|
17
|
+
const match = VERSION.exec(version);
|
|
18
|
+
if (!match)
|
|
19
|
+
return undefined;
|
|
20
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
21
|
+
}
|
|
22
|
+
function compare(a, b) {
|
|
23
|
+
for (let i = 0; i < 3; i++) {
|
|
24
|
+
if (a[i] !== b[i])
|
|
25
|
+
return a[i] - b[i];
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether `version` meets `range`, or `undefined` when `range` is not a plain
|
|
31
|
+
* `>=X.Y.Z` minimum — the caller's signal to skip rather than misreport.
|
|
32
|
+
*/
|
|
33
|
+
function meetsMinimum(version, range) {
|
|
34
|
+
const rangeMatch = MINIMUM_RANGE.exec(range.trim());
|
|
35
|
+
if (!rangeMatch)
|
|
36
|
+
return undefined;
|
|
37
|
+
const minimum = [Number(rangeMatch[1]), Number(rangeMatch[2]), Number(rangeMatch[3])];
|
|
38
|
+
const parsed = parseVersion(version);
|
|
39
|
+
if (!parsed)
|
|
40
|
+
return undefined;
|
|
41
|
+
return compare(parsed, minimum) >= 0;
|
|
42
|
+
}
|
|
43
|
+
/** The leading major version number in a string like `22.11.0`, `^22`, or `nodejs20`. */
|
|
44
|
+
function leadingMajorVersion(text) {
|
|
45
|
+
const match = /(\d+)/.exec(text);
|
|
46
|
+
return match ? Number(match[1]) : undefined;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=versionRange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionRange.js","sourceRoot":"","sources":["../../src/shared/versionRange.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAsBH,oCAOC;AAGD,kDAGC;AAjCD,MAAM,OAAO,GAAG,sBAAsB,CAAA;AACtC,MAAM,aAAa,GAAG,2BAA2B,CAAA;AAEjD,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED,SAAS,OAAO,CAAC,CAA2B,EAAE,CAA2B;IACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,OAAe,EAAE,KAAa;IACzD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACnD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IACjC,MAAM,OAAO,GAA6B,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/G,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,OAAO,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;AACtC,CAAC;AAED,yFAAyF;AACzF,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChC,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC7C,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fsl doctor` — checks a project's setup from disk and reports how it will behave.
|
|
3
|
+
*
|
|
4
|
+
* Every fact comes from a file with a fixed format: `firebase.json`, `package.json`,
|
|
5
|
+
* each installed package's own `package.json`, `dist/`, and the embedded `.release`
|
|
6
|
+
* marker. Nothing
|
|
7
|
+
* here parses source code or guesses at behavior — a check that cannot read what it
|
|
8
|
+
* needs reports `could-not-check`, never a pass (see CLAUDE.md, "fsl doctor — facts,
|
|
9
|
+
* not guesses"). The finding ids, levels, and the `--json` shape are the README's
|
|
10
|
+
* "Check your setup" section, verbatim; this file exists to make that section true.
|
|
11
|
+
*/
|
|
12
|
+
export type SetupKind = 'firebase' | 'node' | 'browser-only' | 'backend-only';
|
|
13
|
+
export type LoggingPath = 'firebase-functions' | 'stdout' | 'none';
|
|
14
|
+
export type TraceSource = 'trigger' | 'header' | 'none';
|
|
15
|
+
export type StoragePath = 'firebase-admin' | 'google-cloud-storage' | 'none';
|
|
16
|
+
export type FindingLevel = 'error' | 'warning';
|
|
17
|
+
export type FindingId = 'maps-published' | 'node-version' | 'callable-without-firebase-functions' | 'could-not-check' | 'duplicate-storage' | 'unsupported-peer' | 'embedded-maps-without-release';
|
|
18
|
+
export interface DoctorSetup {
|
|
19
|
+
kind: SetupKind;
|
|
20
|
+
backend: string | null;
|
|
21
|
+
dist: string | null;
|
|
22
|
+
logging: LoggingPath;
|
|
23
|
+
trace: TraceSource;
|
|
24
|
+
storage: StoragePath;
|
|
25
|
+
callable: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface DoctorFinding {
|
|
28
|
+
id: FindingId;
|
|
29
|
+
level: FindingLevel;
|
|
30
|
+
message: string;
|
|
31
|
+
fix: string;
|
|
32
|
+
}
|
|
33
|
+
export interface DoctorReport {
|
|
34
|
+
setup: DoctorSetup;
|
|
35
|
+
findings: DoctorFinding[];
|
|
36
|
+
exitCode: number;
|
|
37
|
+
}
|
|
38
|
+
export interface DoctorOptions {
|
|
39
|
+
projectRoot: string;
|
|
40
|
+
backend?: string;
|
|
41
|
+
dist?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A peer whose supported range cannot be evaluated is `could-not-check`, never a pass:
|
|
45
|
+
* the ranges are read from this package's own `peerDependencies`, and a range shape
|
|
46
|
+
* `meetsMinimum` does not understand must surface rather than skip the check.
|
|
47
|
+
*/
|
|
48
|
+
export declare function checkUnsupportedPeers(searchDirs: string[], ownPeerDependencies: Record<string, string>, findings: DoctorFinding[]): void;
|
|
49
|
+
export declare function runDoctor(options: DoctorOptions): DoctorReport;
|
|
50
|
+
/** `report.exitCode` ignores `--strict`; the CLI applies it here. */
|
|
51
|
+
export declare function exitCodeFor(report: DoctorReport, strict: boolean): number;
|
|
52
|
+
/** Renders the human-readable report `fsl doctor` prints without `--json`. */
|
|
53
|
+
export declare function formatDoctorReport(report: DoctorReport): string;
|
|
54
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/tools/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,cAAc,CAAA;AAC7E,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,QAAQ,GAAG,MAAM,CAAA;AAClE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AACvD,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,MAAM,CAAA;AAC5E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,CAAA;AAE9C,MAAM,MAAM,SAAS,GACjB,gBAAgB,GAChB,cAAc,GACd,qCAAqC,GACrC,iBAAiB,GACjB,mBAAmB,GACnB,kBAAkB,GAClB,+BAA+B,CAAA;AAEnC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,WAAW,CAAA;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,SAAS,CAAA;IACb,KAAK,EAAE,YAAY,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,CAAA;IAClB,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAkMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAAE,EACpB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,QAAQ,EAAE,aAAa,EAAE,GACxB,IAAI,CAuBN;AA8BD,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAoC9D;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAIzE;AA2BD,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA+B/D"}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `fsl doctor` — checks a project's setup from disk and reports how it will behave.
|
|
4
|
+
*
|
|
5
|
+
* Every fact comes from a file with a fixed format: `firebase.json`, `package.json`,
|
|
6
|
+
* each installed package's own `package.json`, `dist/`, and the embedded `.release`
|
|
7
|
+
* marker. Nothing
|
|
8
|
+
* here parses source code or guesses at behavior — a check that cannot read what it
|
|
9
|
+
* needs reports `could-not-check`, never a pass (see CLAUDE.md, "fsl doctor — facts,
|
|
10
|
+
* not guesses"). The finding ids, levels, and the `--json` shape are the README's
|
|
11
|
+
* "Check your setup" section, verbatim; this file exists to make that section true.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.checkUnsupportedPeers = checkUnsupportedPeers;
|
|
48
|
+
exports.runDoctor = runDoctor;
|
|
49
|
+
exports.exitCodeFor = exitCodeFor;
|
|
50
|
+
exports.formatDoctorReport = formatDoctorReport;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const paths_js_1 = require("../shared/paths.js");
|
|
54
|
+
const nodeModules_js_1 = require("../shared/nodeModules.js");
|
|
55
|
+
const versionRange_js_1 = require("../shared/versionRange.js");
|
|
56
|
+
const PEER_PACKAGES = ['firebase', 'firebase-admin', 'firebase-functions'];
|
|
57
|
+
function readFirebaseJson(projectRoot) {
|
|
58
|
+
const file = path.join(projectRoot, 'firebase.json');
|
|
59
|
+
if (!fs.existsSync(file))
|
|
60
|
+
return { ok: 'absent' };
|
|
61
|
+
try {
|
|
62
|
+
return { ok: true, config: JSON.parse(fs.readFileSync(file, 'utf-8')) };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
finding: couldNotCheck('firebase.json could not be parsed as JSON.', 'Fix the syntax error in firebase.json.'),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function couldNotCheck(message, fix) {
|
|
72
|
+
return { id: 'could-not-check', level: 'error', message, fix };
|
|
73
|
+
}
|
|
74
|
+
function firstOf(value) {
|
|
75
|
+
return Array.isArray(value) ? value[0] : value;
|
|
76
|
+
}
|
|
77
|
+
function hostingPublicDirs(hosting) {
|
|
78
|
+
const configs = Array.isArray(hosting) ? hosting : hosting ? [hosting] : [];
|
|
79
|
+
return configs.map((c) => c.public).filter((p) => typeof p === 'string');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Determine the setup this project has, from `firebase.json` when present, or from
|
|
83
|
+
* `--backend`/`--dist` otherwise. `dist` here is "the folder hosting serves" — under
|
|
84
|
+
* Firebase that is `hosting.public`; outside Firebase it is whatever `--dist` names.
|
|
85
|
+
*/
|
|
86
|
+
function detectSetup(options, findings) {
|
|
87
|
+
const firebaseJson = readFirebaseJson(options.projectRoot);
|
|
88
|
+
if (firebaseJson.ok === false) {
|
|
89
|
+
findings.push(firebaseJson.finding);
|
|
90
|
+
return { kind: 'firebase', backendDir: null, distDirs: [] };
|
|
91
|
+
}
|
|
92
|
+
if (firebaseJson.ok === true) {
|
|
93
|
+
const functionsConfig = firstOf(firebaseJson.config.functions);
|
|
94
|
+
const backend = functionsConfig?.source ?? 'functions';
|
|
95
|
+
const distDirs = hostingPublicDirs(firebaseJson.config.hosting);
|
|
96
|
+
return {
|
|
97
|
+
kind: 'firebase',
|
|
98
|
+
backendDir: path.resolve(options.projectRoot, backend),
|
|
99
|
+
distDirs: distDirs.map((d) => path.resolve(options.projectRoot, d)),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const backendDir = options.backend ? path.resolve(options.projectRoot, options.backend) : null;
|
|
103
|
+
const distDirs = options.dist ? [path.resolve(options.projectRoot, options.dist)] : [];
|
|
104
|
+
const kind = backendDir && distDirs.length ? 'node' : backendDir ? 'backend-only' : distDirs.length ? 'browser-only' : 'backend-only';
|
|
105
|
+
return { kind, backendDir, distDirs };
|
|
106
|
+
}
|
|
107
|
+
function findMapFiles(dir) {
|
|
108
|
+
const results = [];
|
|
109
|
+
if (!fs.existsSync(dir))
|
|
110
|
+
return results;
|
|
111
|
+
const walk = (current) => {
|
|
112
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
113
|
+
const full = path.join(current, entry.name);
|
|
114
|
+
if (entry.isDirectory())
|
|
115
|
+
walk(full);
|
|
116
|
+
else if (entry.isFile() && entry.name.endsWith('.map'))
|
|
117
|
+
results.push(full);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
walk(dir);
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
function checkMapsPublished(distDirs, findings) {
|
|
124
|
+
for (const dist of distDirs) {
|
|
125
|
+
const maps = findMapFiles(dist);
|
|
126
|
+
if (maps.length === 0)
|
|
127
|
+
continue;
|
|
128
|
+
findings.push({
|
|
129
|
+
id: 'maps-published',
|
|
130
|
+
level: 'error',
|
|
131
|
+
message: `${maps.length} .map file(s) found under ${dist}, which hosting serves — your source code is public.`,
|
|
132
|
+
fix: `Run fsl upload-sourcemaps before deploying, so maps leave ${dist} instead of being published.`,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkNodeVersion(backendDir, firebaseRuntime, findings) {
|
|
138
|
+
if (!backendDir)
|
|
139
|
+
return;
|
|
140
|
+
if (firebaseRuntime) {
|
|
141
|
+
const major = (0, versionRange_js_1.leadingMajorVersion)(firebaseRuntime);
|
|
142
|
+
if (major !== undefined && major < 22) {
|
|
143
|
+
findings.push({
|
|
144
|
+
id: 'node-version',
|
|
145
|
+
level: 'error',
|
|
146
|
+
message: `firebase.json sets the functions runtime to ${firebaseRuntime}, below the supported floor of Node 22.`,
|
|
147
|
+
fix: 'Set functions.runtime to "nodejs22" (or later) in firebase.json.',
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (major !== undefined)
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const read = (0, nodeModules_js_1.readPackageJson)(backendDir);
|
|
155
|
+
if (!read.ok) {
|
|
156
|
+
if (read.reason === 'missing') {
|
|
157
|
+
findings.push(couldNotCheck(`No package.json in ${backendDir}. Run npm install first.`, 'Run npm install in the backend directory.'));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
findings.push(couldNotCheck(`${path.join(backendDir, 'package.json')} could not be parsed as JSON.`, 'Fix the syntax error in the backend package.json.'));
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const engines = read.pkg.engines;
|
|
165
|
+
const engineNode = engines?.node;
|
|
166
|
+
if (!engineNode)
|
|
167
|
+
return;
|
|
168
|
+
const major = (0, versionRange_js_1.leadingMajorVersion)(engineNode);
|
|
169
|
+
if (major !== undefined && major < 22) {
|
|
170
|
+
findings.push({
|
|
171
|
+
id: 'node-version',
|
|
172
|
+
level: 'error',
|
|
173
|
+
message: `${path.join(backendDir, 'package.json')} sets engines.node to "${engineNode}", below the supported floor of Node 22.`,
|
|
174
|
+
fix: 'Set engines.node to ">=22" in the backend package.json.',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function checkCallable(kind, backendDir, findings) {
|
|
179
|
+
if (kind !== 'firebase' || !backendDir)
|
|
180
|
+
return false;
|
|
181
|
+
const version = (0, nodeModules_js_1.findTopLevelVersion)([backendDir], 'firebase-functions');
|
|
182
|
+
if (version)
|
|
183
|
+
return true;
|
|
184
|
+
findings.push({
|
|
185
|
+
id: 'callable-without-firebase-functions',
|
|
186
|
+
level: 'error',
|
|
187
|
+
message: `firebase.json configures functions in ${backendDir}, but firebase-functions is not installed there.`,
|
|
188
|
+
fix: `Run npm install firebase-functions in ${backendDir}.`,
|
|
189
|
+
});
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
function checkDuplicateStorage(backendDir, findings) {
|
|
193
|
+
if (!backendDir || !fs.existsSync(path.join(backendDir, 'node_modules')))
|
|
194
|
+
return;
|
|
195
|
+
const copies = (0, nodeModules_js_1.findPackageCopies)(backendDir, '@google-cloud/storage');
|
|
196
|
+
const versions = [...new Set(copies.map((c) => c.version))];
|
|
197
|
+
if (versions.length <= 1)
|
|
198
|
+
return;
|
|
199
|
+
const placed = copies.map((c) => `${c.version} ${describeOwner(ownerOf(c.dir))}`);
|
|
200
|
+
findings.push({
|
|
201
|
+
id: 'duplicate-storage',
|
|
202
|
+
level: 'warning',
|
|
203
|
+
message: `${copies.length} copies of @google-cloud/storage (${placed.join(', ')})`,
|
|
204
|
+
fix: 'npm dedupe',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function displayPath(projectRoot, dir) {
|
|
208
|
+
const relative = path.relative(projectRoot, dir);
|
|
209
|
+
if (relative === '')
|
|
210
|
+
return '.';
|
|
211
|
+
return relative.startsWith('..') || path.isAbsolute(relative) ? relative : `./${relative}`;
|
|
212
|
+
}
|
|
213
|
+
function ownerOf(packageDir) {
|
|
214
|
+
const parts = packageDir.split(path.sep);
|
|
215
|
+
const nodeModules = parts.lastIndexOf('node_modules');
|
|
216
|
+
const previous = parts.lastIndexOf('node_modules', nodeModules - 1);
|
|
217
|
+
if (previous === -1)
|
|
218
|
+
return null;
|
|
219
|
+
const owner = parts.slice(previous + 1, nodeModules);
|
|
220
|
+
return owner.join('/');
|
|
221
|
+
}
|
|
222
|
+
function describeOwner(owner) {
|
|
223
|
+
return owner === null ? 'at the top level' : `inside ${owner}`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* A peer whose supported range cannot be evaluated is `could-not-check`, never a pass:
|
|
227
|
+
* the ranges are read from this package's own `peerDependencies`, and a range shape
|
|
228
|
+
* `meetsMinimum` does not understand must surface rather than skip the check.
|
|
229
|
+
*/
|
|
230
|
+
function checkUnsupportedPeers(searchDirs, ownPeerDependencies, findings) {
|
|
231
|
+
for (const peer of PEER_PACKAGES) {
|
|
232
|
+
const range = ownPeerDependencies[peer];
|
|
233
|
+
if (!range)
|
|
234
|
+
continue;
|
|
235
|
+
const installed = (0, nodeModules_js_1.findTopLevelVersion)(searchDirs, peer);
|
|
236
|
+
if (!installed)
|
|
237
|
+
continue;
|
|
238
|
+
const meets = (0, versionRange_js_1.meetsMinimum)(installed, range);
|
|
239
|
+
if (meets === undefined) {
|
|
240
|
+
findings.push({
|
|
241
|
+
id: 'could-not-check',
|
|
242
|
+
level: 'error',
|
|
243
|
+
message: `Could not compare ${peer} ${installed} with the supported range ${range}.`,
|
|
244
|
+
fix: 'Report this at https://github.com/dasasian/firebase-structured-logger/issues — doctor needs to learn this range shape.',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else if (meets === false) {
|
|
248
|
+
findings.push({
|
|
249
|
+
id: 'unsupported-peer',
|
|
250
|
+
level: 'warning',
|
|
251
|
+
message: `${peer} ${installed} is installed, outside this package's supported range (${range}).`,
|
|
252
|
+
fix: `Upgrade ${peer} to satisfy ${range}.`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function checkEmbeddedMapsWithoutRelease(backendDir, findings) {
|
|
258
|
+
if (!backendDir)
|
|
259
|
+
return;
|
|
260
|
+
const dir = (0, paths_js_1.embeddedDir)(backendDir);
|
|
261
|
+
if (!fs.existsSync(dir))
|
|
262
|
+
return;
|
|
263
|
+
const hasMaps = fs.readdirSync(dir).some((f) => f.endsWith('.map'));
|
|
264
|
+
if (!hasMaps)
|
|
265
|
+
return;
|
|
266
|
+
if (fs.existsSync((0, paths_js_1.embeddedMarkerPath)(backendDir)))
|
|
267
|
+
return;
|
|
268
|
+
findings.push({
|
|
269
|
+
id: 'embedded-maps-without-release',
|
|
270
|
+
level: 'warning',
|
|
271
|
+
message: `${dir} has embedded maps but no ${paths_js_1.RELEASE_MARKER} marker — an older release's stack can resolve against the wrong map.`,
|
|
272
|
+
fix: 'Re-run fsl upload-sourcemaps --embed-sourcemaps, which writes the marker.',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/** This package's own `package.json`, read at runtime — never copied into doctor. */
|
|
276
|
+
function readOwnPackageJson() {
|
|
277
|
+
const read = (0, nodeModules_js_1.readPackageJson)(path.join(__dirname, '..', '..'));
|
|
278
|
+
if (!read.ok)
|
|
279
|
+
throw new Error('fsl doctor could not read its own package.json');
|
|
280
|
+
return read.pkg;
|
|
281
|
+
}
|
|
282
|
+
function resolveStorage(searchDirs) {
|
|
283
|
+
if ((0, nodeModules_js_1.findTopLevelVersion)(searchDirs, 'firebase-admin'))
|
|
284
|
+
return 'firebase-admin';
|
|
285
|
+
if ((0, nodeModules_js_1.findTopLevelVersion)(searchDirs, '@google-cloud/storage'))
|
|
286
|
+
return 'google-cloud-storage';
|
|
287
|
+
return 'none';
|
|
288
|
+
}
|
|
289
|
+
function runDoctor(options) {
|
|
290
|
+
const findings = [];
|
|
291
|
+
const { kind, backendDir, distDirs } = detectSetup(options, findings);
|
|
292
|
+
const firebaseJson = readFirebaseJson(options.projectRoot);
|
|
293
|
+
const firebaseRuntime = firebaseJson.ok === true ? firstOf(firebaseJson.config.functions)?.runtime : undefined;
|
|
294
|
+
checkMapsPublished(distDirs, findings);
|
|
295
|
+
checkNodeVersion(backendDir, firebaseRuntime, findings);
|
|
296
|
+
const callable = checkCallable(kind, backendDir, findings);
|
|
297
|
+
checkDuplicateStorage(backendDir, findings);
|
|
298
|
+
const searchDirs = [backendDir, options.projectRoot].filter((d) => d !== null);
|
|
299
|
+
const ownPkg = readOwnPackageJson();
|
|
300
|
+
checkUnsupportedPeers(searchDirs, ownPkg.peerDependencies ?? {}, findings);
|
|
301
|
+
checkEmbeddedMapsWithoutRelease(backendDir, findings);
|
|
302
|
+
const hasFirebaseFunctions = backendDir !== null && (0, nodeModules_js_1.findTopLevelVersion)([backendDir], 'firebase-functions') !== undefined;
|
|
303
|
+
const logging = kind === 'firebase' && hasFirebaseFunctions ? 'firebase-functions' : backendDir ? 'stdout' : 'none';
|
|
304
|
+
const trace = kind === 'firebase' && hasFirebaseFunctions ? 'trigger' : backendDir ? 'header' : 'none';
|
|
305
|
+
const storage = resolveStorage(searchDirs);
|
|
306
|
+
const setup = {
|
|
307
|
+
kind,
|
|
308
|
+
backend: backendDir ? displayPath(options.projectRoot, backendDir) : null,
|
|
309
|
+
dist: distDirs[0] ? displayPath(options.projectRoot, distDirs[0]) : null,
|
|
310
|
+
logging,
|
|
311
|
+
trace,
|
|
312
|
+
storage,
|
|
313
|
+
callable,
|
|
314
|
+
};
|
|
315
|
+
const hasError = findings.some((f) => f.level === 'error');
|
|
316
|
+
const exitCode = hasError ? 1 : 0;
|
|
317
|
+
return { setup, findings, exitCode };
|
|
318
|
+
}
|
|
319
|
+
/** `report.exitCode` ignores `--strict`; the CLI applies it here. */
|
|
320
|
+
function exitCodeFor(report, strict) {
|
|
321
|
+
if (report.findings.some((f) => f.level === 'error'))
|
|
322
|
+
return 1;
|
|
323
|
+
if (strict && report.findings.some((f) => f.level === 'warning'))
|
|
324
|
+
return 1;
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
const SETUP_LABEL = {
|
|
328
|
+
firebase: 'Firebase',
|
|
329
|
+
node: 'Node',
|
|
330
|
+
'browser-only': 'browser only',
|
|
331
|
+
'backend-only': 'backend only',
|
|
332
|
+
};
|
|
333
|
+
const LOGGING_LABEL = {
|
|
334
|
+
'firebase-functions': 'firebase-functions write()',
|
|
335
|
+
stdout: 'stdout JSON (createHttpLogHandler)',
|
|
336
|
+
none: 'not applicable — no backend given',
|
|
337
|
+
};
|
|
338
|
+
const TRACE_LABEL = {
|
|
339
|
+
trigger: 'from each Cloud Functions trigger',
|
|
340
|
+
header: 'from the x-cloud-trace-context header',
|
|
341
|
+
none: 'not applicable — no backend given',
|
|
342
|
+
};
|
|
343
|
+
const STORAGE_LABEL = {
|
|
344
|
+
'firebase-admin': 'firebase-admin, default bucket',
|
|
345
|
+
'google-cloud-storage': '@google-cloud/storage — needs a bucket named in code (not checked)',
|
|
346
|
+
none: 'not configured',
|
|
347
|
+
};
|
|
348
|
+
/** Renders the human-readable report `fsl doctor` prints without `--json`. */
|
|
349
|
+
function formatDoctorReport(report) {
|
|
350
|
+
const { setup, findings } = report;
|
|
351
|
+
const backendNoun = setup.kind === 'firebase' ? 'functions' : 'backend';
|
|
352
|
+
const location = setup.backend && setup.dist
|
|
353
|
+
? `${backendNoun} in ${setup.backend}, web build in ${setup.dist}`
|
|
354
|
+
: setup.backend
|
|
355
|
+
? `backend in ${setup.backend}`
|
|
356
|
+
: setup.dist
|
|
357
|
+
? `web build in ${setup.dist}`
|
|
358
|
+
: 'nothing to check — pass --backend and/or --dist';
|
|
359
|
+
const lines = [
|
|
360
|
+
`Setup: ${SETUP_LABEL[setup.kind]} — ${location}`,
|
|
361
|
+
'',
|
|
362
|
+
pad('Logging', LOGGING_LABEL[setup.logging]),
|
|
363
|
+
pad('Trace ids', TRACE_LABEL[setup.trace]),
|
|
364
|
+
pad('Storage', STORAGE_LABEL[setup.storage]),
|
|
365
|
+
pad('Callable', setup.callable ? 'createClientLogFunction available' : 'not available'),
|
|
366
|
+
];
|
|
367
|
+
if (findings.length > 0) {
|
|
368
|
+
lines.push('');
|
|
369
|
+
for (const finding of findings) {
|
|
370
|
+
const icon = finding.level === 'error' ? '✗' : '⚠';
|
|
371
|
+
const indent = ' '.repeat(4);
|
|
372
|
+
lines.push(` ${icon} ${finding.id.padEnd(20)} ${finding.message}`);
|
|
373
|
+
lines.push(`${indent}${' '.repeat(20)}Fix: ${finding.fix}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return lines.join('\n');
|
|
377
|
+
}
|
|
378
|
+
function pad(label, value) {
|
|
379
|
+
return ` ${label.padEnd(15)}${value}`;
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=doctor.js.map
|