postgresai 0.14.0-dev.37 → 0.14.0-dev.39

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.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Express checkup module - generates JSON reports directly from PostgreSQL
3
+ * without going through Prometheus.
4
+ *
5
+ * This module reuses the same SQL queries from metrics.yml but runs them
6
+ * directly against the target database.
7
+ */
8
+ import { Client } from "pg";
9
+ /**
10
+ * PostgreSQL version information
11
+ */
12
+ export interface PostgresVersion {
13
+ version: string;
14
+ server_version_num: string;
15
+ server_major_ver: string;
16
+ server_minor_ver: string;
17
+ }
18
+ /**
19
+ * Setting information from pg_settings
20
+ */
21
+ export interface SettingInfo {
22
+ setting: string;
23
+ unit: string;
24
+ category: string;
25
+ context: string;
26
+ vartype: string;
27
+ pretty_value: string;
28
+ }
29
+ /**
30
+ * Altered setting (A007) - subset of SettingInfo
31
+ */
32
+ export interface AlteredSetting {
33
+ value: string;
34
+ unit: string;
35
+ category: string;
36
+ pretty_value: string;
37
+ }
38
+ /**
39
+ * Cluster metric (A004)
40
+ */
41
+ export interface ClusterMetric {
42
+ value: string;
43
+ unit: string;
44
+ description: string;
45
+ }
46
+ /**
47
+ * Node result for reports
48
+ */
49
+ export interface NodeResult {
50
+ data: Record<string, any>;
51
+ postgres_version?: PostgresVersion;
52
+ }
53
+ /**
54
+ * Report structure matching JSON schemas
55
+ */
56
+ export interface Report {
57
+ version: string | null;
58
+ build_ts: string | null;
59
+ checkId: string;
60
+ checkTitle: string;
61
+ timestamptz: string;
62
+ nodes: {
63
+ primary: string;
64
+ standbys: string[];
65
+ };
66
+ results: Record<string, NodeResult>;
67
+ }
68
+ /**
69
+ * SQL queries derived from metrics.yml
70
+ * These are the same queries used by pgwatch to export metrics to Prometheus
71
+ */
72
+ export declare const METRICS_SQL: {
73
+ settings: string;
74
+ alteredSettings: string;
75
+ version: string;
76
+ databaseSizes: string;
77
+ clusterStats: string;
78
+ connectionStates: string;
79
+ uptimeInfo: string;
80
+ };
81
+ /**
82
+ * Parse PostgreSQL version number into major and minor components
83
+ */
84
+ export declare function parseVersionNum(versionNum: string): {
85
+ major: string;
86
+ minor: string;
87
+ };
88
+ /**
89
+ * Format bytes to human readable string
90
+ */
91
+ export declare function formatBytes(bytes: number): string;
92
+ /**
93
+ * Get PostgreSQL version information
94
+ */
95
+ export declare function getPostgresVersion(client: Client): Promise<PostgresVersion>;
96
+ /**
97
+ * Get all PostgreSQL settings
98
+ */
99
+ export declare function getSettings(client: Client): Promise<Record<string, SettingInfo>>;
100
+ /**
101
+ * Get altered (non-default) PostgreSQL settings
102
+ */
103
+ export declare function getAlteredSettings(client: Client): Promise<Record<string, AlteredSetting>>;
104
+ /**
105
+ * Get database sizes
106
+ */
107
+ export declare function getDatabaseSizes(client: Client): Promise<Record<string, number>>;
108
+ /**
109
+ * Get cluster general info metrics
110
+ */
111
+ export declare function getClusterInfo(client: Client): Promise<Record<string, ClusterMetric>>;
112
+ /**
113
+ * Create base report structure
114
+ */
115
+ export declare function createBaseReport(checkId: string, checkTitle: string, nodeName: string): Report;
116
+ /**
117
+ * Generate A002 report - Postgres major version
118
+ */
119
+ export declare function generateA002(client: Client, nodeName?: string): Promise<Report>;
120
+ /**
121
+ * Generate A003 report - Postgres settings
122
+ */
123
+ export declare function generateA003(client: Client, nodeName?: string): Promise<Report>;
124
+ /**
125
+ * Generate A004 report - Cluster information
126
+ */
127
+ export declare function generateA004(client: Client, nodeName?: string): Promise<Report>;
128
+ /**
129
+ * Generate A007 report - Altered settings
130
+ */
131
+ export declare function generateA007(client: Client, nodeName?: string): Promise<Report>;
132
+ /**
133
+ * Generate A013 report - Postgres minor version
134
+ */
135
+ export declare function generateA013(client: Client, nodeName?: string): Promise<Report>;
136
+ /**
137
+ * Available report generators
138
+ */
139
+ export declare const REPORT_GENERATORS: Record<string, (client: Client, nodeName: string) => Promise<Report>>;
140
+ /**
141
+ * Check IDs and titles
142
+ */
143
+ export declare const CHECK_INFO: Record<string, string>;
144
+ /**
145
+ * Generate all available reports
146
+ */
147
+ export declare function generateAllReports(client: Client, nodeName?: string, onProgress?: (info: {
148
+ checkId: string;
149
+ checkTitle: string;
150
+ index: number;
151
+ total: number;
152
+ }) => void): Promise<Record<string, Report>>;
153
+ //# sourceMappingURL=checkup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkup.d.ts","sourceRoot":"","sources":["../../lib/checkup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,536 @@
1
+ "use strict";
2
+ /**
3
+ * Express checkup module - generates JSON reports directly from PostgreSQL
4
+ * without going through Prometheus.
5
+ *
6
+ * This module reuses the same SQL queries from metrics.yml but runs them
7
+ * directly against the target database.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.CHECK_INFO = exports.REPORT_GENERATORS = exports.METRICS_SQL = void 0;
44
+ exports.parseVersionNum = parseVersionNum;
45
+ exports.formatBytes = formatBytes;
46
+ exports.getPostgresVersion = getPostgresVersion;
47
+ exports.getSettings = getSettings;
48
+ exports.getAlteredSettings = getAlteredSettings;
49
+ exports.getDatabaseSizes = getDatabaseSizes;
50
+ exports.getClusterInfo = getClusterInfo;
51
+ exports.createBaseReport = createBaseReport;
52
+ exports.generateA002 = generateA002;
53
+ exports.generateA003 = generateA003;
54
+ exports.generateA004 = generateA004;
55
+ exports.generateA007 = generateA007;
56
+ exports.generateA013 = generateA013;
57
+ exports.generateAllReports = generateAllReports;
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const pkg = __importStar(require("../package.json"));
61
+ /**
62
+ * SQL queries derived from metrics.yml
63
+ * These are the same queries used by pgwatch to export metrics to Prometheus
64
+ */
65
+ exports.METRICS_SQL = {
66
+ // From metrics.yml: settings metric
67
+ // Queries pg_settings for all configuration parameters
68
+ settings: `
69
+ SELECT
70
+ name,
71
+ setting,
72
+ COALESCE(unit, '') as unit,
73
+ category,
74
+ context,
75
+ vartype,
76
+ CASE
77
+ WHEN unit = '8kB' THEN pg_size_pretty(setting::bigint * 8192)
78
+ WHEN unit = 'kB' THEN pg_size_pretty(setting::bigint * 1024)
79
+ WHEN unit = 'MB' THEN pg_size_pretty(setting::bigint * 1024 * 1024)
80
+ WHEN unit = 'B' THEN pg_size_pretty(setting::bigint)
81
+ WHEN unit = 'ms' THEN setting || ' ms'
82
+ WHEN unit = 's' THEN setting || ' s'
83
+ WHEN unit = 'min' THEN setting || ' min'
84
+ ELSE setting
85
+ END as pretty_value,
86
+ source,
87
+ CASE WHEN source <> 'default' THEN 0 ELSE 1 END as is_default
88
+ FROM pg_settings
89
+ ORDER BY name
90
+ `,
91
+ // Altered settings - non-default values only (A007)
92
+ alteredSettings: `
93
+ SELECT
94
+ name,
95
+ setting,
96
+ COALESCE(unit, '') as unit,
97
+ category,
98
+ CASE
99
+ WHEN unit = '8kB' THEN pg_size_pretty(setting::bigint * 8192)
100
+ WHEN unit = 'kB' THEN pg_size_pretty(setting::bigint * 1024)
101
+ WHEN unit = 'MB' THEN pg_size_pretty(setting::bigint * 1024 * 1024)
102
+ WHEN unit = 'B' THEN pg_size_pretty(setting::bigint)
103
+ WHEN unit = 'ms' THEN setting || ' ms'
104
+ WHEN unit = 's' THEN setting || ' s'
105
+ WHEN unit = 'min' THEN setting || ' min'
106
+ ELSE setting
107
+ END as pretty_value
108
+ FROM pg_settings
109
+ WHERE source <> 'default'
110
+ ORDER BY name
111
+ `,
112
+ // Version info - extracts server_version and server_version_num
113
+ version: `
114
+ SELECT
115
+ name,
116
+ setting
117
+ FROM pg_settings
118
+ WHERE name IN ('server_version', 'server_version_num')
119
+ `,
120
+ // Database sizes (A004)
121
+ databaseSizes: `
122
+ SELECT
123
+ datname,
124
+ pg_database_size(datname) as size_bytes
125
+ FROM pg_database
126
+ WHERE datistemplate = false
127
+ ORDER BY size_bytes DESC
128
+ `,
129
+ // Cluster statistics (A004)
130
+ clusterStats: `
131
+ SELECT
132
+ sum(numbackends) as total_connections,
133
+ sum(xact_commit) as total_commits,
134
+ sum(xact_rollback) as total_rollbacks,
135
+ sum(blks_read) as blocks_read,
136
+ sum(blks_hit) as blocks_hit,
137
+ sum(tup_returned) as tuples_returned,
138
+ sum(tup_fetched) as tuples_fetched,
139
+ sum(tup_inserted) as tuples_inserted,
140
+ sum(tup_updated) as tuples_updated,
141
+ sum(tup_deleted) as tuples_deleted,
142
+ sum(deadlocks) as total_deadlocks,
143
+ sum(temp_files) as temp_files_created,
144
+ sum(temp_bytes) as temp_bytes_written
145
+ FROM pg_stat_database
146
+ WHERE datname IS NOT NULL
147
+ `,
148
+ // Connection states (A004)
149
+ connectionStates: `
150
+ SELECT
151
+ COALESCE(state, 'null') as state,
152
+ count(*) as count
153
+ FROM pg_stat_activity
154
+ GROUP BY state
155
+ `,
156
+ // Uptime info (A004)
157
+ uptimeInfo: `
158
+ SELECT
159
+ pg_postmaster_start_time() as start_time,
160
+ current_timestamp - pg_postmaster_start_time() as uptime
161
+ `,
162
+ };
163
+ /**
164
+ * Parse PostgreSQL version number into major and minor components
165
+ */
166
+ function parseVersionNum(versionNum) {
167
+ if (!versionNum || versionNum.length < 6) {
168
+ return { major: "", minor: "" };
169
+ }
170
+ try {
171
+ const num = parseInt(versionNum, 10);
172
+ return {
173
+ major: Math.floor(num / 10000).toString(),
174
+ minor: (num % 10000).toString(),
175
+ };
176
+ }
177
+ catch {
178
+ return { major: "", minor: "" };
179
+ }
180
+ }
181
+ /**
182
+ * Format bytes to human readable string
183
+ */
184
+ function formatBytes(bytes) {
185
+ if (bytes === 0)
186
+ return "0 B";
187
+ const units = ["B", "kB", "MB", "GB", "TB", "PB"];
188
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
189
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`;
190
+ }
191
+ /**
192
+ * Get PostgreSQL version information
193
+ */
194
+ async function getPostgresVersion(client) {
195
+ const result = await client.query(exports.METRICS_SQL.version);
196
+ let version = "";
197
+ let serverVersionNum = "";
198
+ for (const row of result.rows) {
199
+ if (row.name === "server_version") {
200
+ version = row.setting;
201
+ }
202
+ else if (row.name === "server_version_num") {
203
+ serverVersionNum = row.setting;
204
+ }
205
+ }
206
+ const { major, minor } = parseVersionNum(serverVersionNum);
207
+ return {
208
+ version,
209
+ server_version_num: serverVersionNum,
210
+ server_major_ver: major,
211
+ server_minor_ver: minor,
212
+ };
213
+ }
214
+ /**
215
+ * Get all PostgreSQL settings
216
+ */
217
+ async function getSettings(client) {
218
+ const result = await client.query(exports.METRICS_SQL.settings);
219
+ const settings = {};
220
+ for (const row of result.rows) {
221
+ settings[row.name] = {
222
+ setting: row.setting,
223
+ unit: row.unit,
224
+ category: row.category,
225
+ context: row.context,
226
+ vartype: row.vartype,
227
+ pretty_value: row.pretty_value,
228
+ };
229
+ }
230
+ return settings;
231
+ }
232
+ /**
233
+ * Get altered (non-default) PostgreSQL settings
234
+ */
235
+ async function getAlteredSettings(client) {
236
+ const result = await client.query(exports.METRICS_SQL.alteredSettings);
237
+ const settings = {};
238
+ for (const row of result.rows) {
239
+ settings[row.name] = {
240
+ value: row.setting,
241
+ unit: row.unit,
242
+ category: row.category,
243
+ pretty_value: row.pretty_value,
244
+ };
245
+ }
246
+ return settings;
247
+ }
248
+ /**
249
+ * Get database sizes
250
+ */
251
+ async function getDatabaseSizes(client) {
252
+ const result = await client.query(exports.METRICS_SQL.databaseSizes);
253
+ const sizes = {};
254
+ for (const row of result.rows) {
255
+ sizes[row.datname] = parseInt(row.size_bytes, 10);
256
+ }
257
+ return sizes;
258
+ }
259
+ /**
260
+ * Get cluster general info metrics
261
+ */
262
+ async function getClusterInfo(client) {
263
+ const info = {};
264
+ // Get cluster statistics
265
+ const statsResult = await client.query(exports.METRICS_SQL.clusterStats);
266
+ if (statsResult.rows.length > 0) {
267
+ const stats = statsResult.rows[0];
268
+ info.total_connections = {
269
+ value: String(stats.total_connections || 0),
270
+ unit: "connections",
271
+ description: "Total active database connections",
272
+ };
273
+ info.total_commits = {
274
+ value: String(stats.total_commits || 0),
275
+ unit: "transactions",
276
+ description: "Total committed transactions",
277
+ };
278
+ info.total_rollbacks = {
279
+ value: String(stats.total_rollbacks || 0),
280
+ unit: "transactions",
281
+ description: "Total rolled back transactions",
282
+ };
283
+ const blocksHit = parseInt(stats.blocks_hit || "0", 10);
284
+ const blocksRead = parseInt(stats.blocks_read || "0", 10);
285
+ const totalBlocks = blocksHit + blocksRead;
286
+ const cacheHitRatio = totalBlocks > 0 ? ((blocksHit / totalBlocks) * 100).toFixed(2) : "0.00";
287
+ info.cache_hit_ratio = {
288
+ value: cacheHitRatio,
289
+ unit: "%",
290
+ description: "Buffer cache hit ratio",
291
+ };
292
+ info.blocks_read = {
293
+ value: String(blocksRead),
294
+ unit: "blocks",
295
+ description: "Total disk blocks read",
296
+ };
297
+ info.blocks_hit = {
298
+ value: String(blocksHit),
299
+ unit: "blocks",
300
+ description: "Total buffer cache hits",
301
+ };
302
+ info.tuples_returned = {
303
+ value: String(stats.tuples_returned || 0),
304
+ unit: "rows",
305
+ description: "Total rows returned by queries",
306
+ };
307
+ info.tuples_fetched = {
308
+ value: String(stats.tuples_fetched || 0),
309
+ unit: "rows",
310
+ description: "Total rows fetched by queries",
311
+ };
312
+ info.tuples_inserted = {
313
+ value: String(stats.tuples_inserted || 0),
314
+ unit: "rows",
315
+ description: "Total rows inserted",
316
+ };
317
+ info.tuples_updated = {
318
+ value: String(stats.tuples_updated || 0),
319
+ unit: "rows",
320
+ description: "Total rows updated",
321
+ };
322
+ info.tuples_deleted = {
323
+ value: String(stats.tuples_deleted || 0),
324
+ unit: "rows",
325
+ description: "Total rows deleted",
326
+ };
327
+ info.total_deadlocks = {
328
+ value: String(stats.total_deadlocks || 0),
329
+ unit: "deadlocks",
330
+ description: "Total deadlocks detected",
331
+ };
332
+ info.temp_files_created = {
333
+ value: String(stats.temp_files_created || 0),
334
+ unit: "files",
335
+ description: "Total temporary files created",
336
+ };
337
+ const tempBytes = parseInt(stats.temp_bytes_written || "0", 10);
338
+ info.temp_bytes_written = {
339
+ value: formatBytes(tempBytes),
340
+ unit: "bytes",
341
+ description: "Total temporary file bytes written",
342
+ };
343
+ }
344
+ // Get connection states
345
+ const connResult = await client.query(exports.METRICS_SQL.connectionStates);
346
+ for (const row of connResult.rows) {
347
+ const stateKey = `connections_${row.state.replace(/\s+/g, "_")}`;
348
+ info[stateKey] = {
349
+ value: String(row.count),
350
+ unit: "connections",
351
+ description: `Connections in '${row.state}' state`,
352
+ };
353
+ }
354
+ // Get uptime info
355
+ const uptimeResult = await client.query(exports.METRICS_SQL.uptimeInfo);
356
+ if (uptimeResult.rows.length > 0) {
357
+ const uptime = uptimeResult.rows[0];
358
+ info.start_time = {
359
+ value: uptime.start_time.toISOString(),
360
+ unit: "timestamp",
361
+ description: "PostgreSQL server start time",
362
+ };
363
+ info.uptime = {
364
+ value: uptime.uptime,
365
+ unit: "interval",
366
+ description: "Server uptime",
367
+ };
368
+ }
369
+ return info;
370
+ }
371
+ /**
372
+ * Create base report structure
373
+ */
374
+ function createBaseReport(checkId, checkTitle, nodeName) {
375
+ const buildTs = resolveBuildTs();
376
+ return {
377
+ version: pkg.version || null,
378
+ build_ts: buildTs,
379
+ checkId,
380
+ checkTitle,
381
+ timestamptz: new Date().toISOString(),
382
+ nodes: {
383
+ primary: nodeName,
384
+ standbys: [],
385
+ },
386
+ results: {},
387
+ };
388
+ }
389
+ function readTextFileSafe(p) {
390
+ try {
391
+ const value = fs.readFileSync(p, "utf8").trim();
392
+ return value || null;
393
+ }
394
+ catch {
395
+ return null;
396
+ }
397
+ }
398
+ function resolveBuildTs() {
399
+ // Follow reporter.py approach: read BUILD_TS from filesystem, with env override.
400
+ // Default: /BUILD_TS (useful in container images).
401
+ const envPath = process.env.PGAI_BUILD_TS_FILE;
402
+ const p = (envPath && envPath.trim()) ? envPath.trim() : "/BUILD_TS";
403
+ const fromFile = readTextFileSafe(p);
404
+ if (fromFile)
405
+ return fromFile;
406
+ // Fallback for packaged CLI: allow placing BUILD_TS next to dist/ (package root).
407
+ // dist/lib/checkup.js => package root: dist/..
408
+ try {
409
+ const pkgRoot = path.resolve(__dirname, "..");
410
+ const fromPkgFile = readTextFileSafe(path.join(pkgRoot, "BUILD_TS"));
411
+ if (fromPkgFile)
412
+ return fromPkgFile;
413
+ }
414
+ catch {
415
+ // ignore
416
+ }
417
+ // Last resort: use package.json mtime as an approximation (non-null, stable-ish).
418
+ try {
419
+ const pkgJsonPath = path.resolve(__dirname, "..", "package.json");
420
+ const st = fs.statSync(pkgJsonPath);
421
+ return st.mtime.toISOString();
422
+ }
423
+ catch {
424
+ return new Date().toISOString();
425
+ }
426
+ }
427
+ /**
428
+ * Generate A002 report - Postgres major version
429
+ */
430
+ async function generateA002(client, nodeName = "node-01") {
431
+ const report = createBaseReport("A002", "Postgres major version", nodeName);
432
+ const postgresVersion = await getPostgresVersion(client);
433
+ report.results[nodeName] = {
434
+ data: {
435
+ version: postgresVersion,
436
+ },
437
+ };
438
+ return report;
439
+ }
440
+ /**
441
+ * Generate A003 report - Postgres settings
442
+ */
443
+ async function generateA003(client, nodeName = "node-01") {
444
+ const report = createBaseReport("A003", "Postgres settings", nodeName);
445
+ const settings = await getSettings(client);
446
+ const postgresVersion = await getPostgresVersion(client);
447
+ report.results[nodeName] = {
448
+ data: settings,
449
+ postgres_version: postgresVersion,
450
+ };
451
+ return report;
452
+ }
453
+ /**
454
+ * Generate A004 report - Cluster information
455
+ */
456
+ async function generateA004(client, nodeName = "node-01") {
457
+ const report = createBaseReport("A004", "Cluster information", nodeName);
458
+ const generalInfo = await getClusterInfo(client);
459
+ const databaseSizes = await getDatabaseSizes(client);
460
+ const postgresVersion = await getPostgresVersion(client);
461
+ report.results[nodeName] = {
462
+ data: {
463
+ general_info: generalInfo,
464
+ database_sizes: databaseSizes,
465
+ },
466
+ postgres_version: postgresVersion,
467
+ };
468
+ return report;
469
+ }
470
+ /**
471
+ * Generate A007 report - Altered settings
472
+ */
473
+ async function generateA007(client, nodeName = "node-01") {
474
+ const report = createBaseReport("A007", "Altered settings", nodeName);
475
+ const alteredSettings = await getAlteredSettings(client);
476
+ const postgresVersion = await getPostgresVersion(client);
477
+ report.results[nodeName] = {
478
+ data: alteredSettings,
479
+ postgres_version: postgresVersion,
480
+ };
481
+ return report;
482
+ }
483
+ /**
484
+ * Generate A013 report - Postgres minor version
485
+ */
486
+ async function generateA013(client, nodeName = "node-01") {
487
+ const report = createBaseReport("A013", "Postgres minor version", nodeName);
488
+ const postgresVersion = await getPostgresVersion(client);
489
+ report.results[nodeName] = {
490
+ data: {
491
+ version: postgresVersion,
492
+ },
493
+ };
494
+ return report;
495
+ }
496
+ /**
497
+ * Available report generators
498
+ */
499
+ exports.REPORT_GENERATORS = {
500
+ A002: generateA002,
501
+ A003: generateA003,
502
+ A004: generateA004,
503
+ A007: generateA007,
504
+ A013: generateA013,
505
+ };
506
+ /**
507
+ * Check IDs and titles
508
+ */
509
+ exports.CHECK_INFO = {
510
+ A002: "Postgres major version",
511
+ A003: "Postgres settings",
512
+ A004: "Cluster information",
513
+ A007: "Altered settings",
514
+ A013: "Postgres minor version",
515
+ };
516
+ /**
517
+ * Generate all available reports
518
+ */
519
+ async function generateAllReports(client, nodeName = "node-01", onProgress) {
520
+ const reports = {};
521
+ const entries = Object.entries(exports.REPORT_GENERATORS);
522
+ const total = entries.length;
523
+ let index = 0;
524
+ for (const [checkId, generator] of entries) {
525
+ index += 1;
526
+ onProgress?.({
527
+ checkId,
528
+ checkTitle: exports.CHECK_INFO[checkId] || checkId,
529
+ index,
530
+ total,
531
+ });
532
+ reports[checkId] = await generator(client, nodeName);
533
+ }
534
+ return reports;
535
+ }
536
+ //# sourceMappingURL=checkup.js.map