postgresai 0.16.0-rc.4 → 0.16.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.
Files changed (85) hide show
  1. package/README.md +154 -0
  2. package/dist/bin/postgres-ai.js +2911 -255
  3. package/package.json +12 -3
  4. package/schemas/A002.schema.json +63 -0
  5. package/schemas/A003.schema.json +73 -0
  6. package/schemas/A004.schema.json +81 -0
  7. package/schemas/A007.schema.json +71 -0
  8. package/schemas/A013.schema.json +61 -0
  9. package/schemas/D001.schema.json +71 -0
  10. package/schemas/D004.schema.json +136 -0
  11. package/schemas/F001.schema.json +73 -0
  12. package/schemas/F002.schema.json +108 -0
  13. package/schemas/F003.schema.json +138 -0
  14. package/schemas/F004.schema.json +125 -0
  15. package/schemas/F005.schema.json +131 -0
  16. package/schemas/F009.schema.json +155 -0
  17. package/schemas/G001.schema.json +135 -0
  18. package/schemas/G003.schema.json +90 -0
  19. package/schemas/H001.schema.json +141 -0
  20. package/schemas/H002.schema.json +129 -0
  21. package/schemas/H004.schema.json +128 -0
  22. package/schemas/I001.schema.json +149 -0
  23. package/schemas/K001.schema.json +161 -0
  24. package/schemas/K003.schema.json +163 -0
  25. package/schemas/K004.schema.json +110 -0
  26. package/schemas/K005.schema.json +110 -0
  27. package/schemas/K006.schema.json +110 -0
  28. package/schemas/K007.schema.json +110 -0
  29. package/schemas/K008.schema.json +110 -0
  30. package/schemas/M001.schema.json +119 -0
  31. package/schemas/M002.schema.json +110 -0
  32. package/schemas/M003.schema.json +128 -0
  33. package/schemas/N001.schema.json +161 -0
  34. package/schemas/query.schema.json +62 -0
  35. package/CHANGELOG.md +0 -11
  36. package/bin/postgres-ai.ts +0 -5578
  37. package/bun.lock +0 -258
  38. package/bunfig.toml +0 -20
  39. package/lib/aas-onboard.ts +0 -251
  40. package/lib/auth-server.ts +0 -285
  41. package/lib/checkup-api.ts +0 -526
  42. package/lib/checkup-dictionary.ts +0 -103
  43. package/lib/checkup-summary.ts +0 -338
  44. package/lib/checkup.ts +0 -2261
  45. package/lib/config.ts +0 -171
  46. package/lib/init.ts +0 -1152
  47. package/lib/instances.ts +0 -245
  48. package/lib/issues.ts +0 -1060
  49. package/lib/mcp-server.ts +0 -667
  50. package/lib/metrics-loader.ts +0 -134
  51. package/lib/pkce.ts +0 -79
  52. package/lib/reports.ts +0 -373
  53. package/lib/storage.ts +0 -367
  54. package/lib/supabase.ts +0 -826
  55. package/lib/util.ts +0 -134
  56. package/packages/postgres-ai/README.md +0 -26
  57. package/packages/postgres-ai/bin/postgres-ai.js +0 -27
  58. package/packages/postgres-ai/package.json +0 -27
  59. package/scripts/embed-checkup-dictionary.ts +0 -115
  60. package/scripts/embed-metrics.ts +0 -160
  61. package/scripts/generate-release-notes.ts +0 -668
  62. package/test/PERMISSION_CHECK_TEST_SUMMARY.md +0 -139
  63. package/test/aas-onboard.test.ts +0 -301
  64. package/test/auth.test.ts +0 -287
  65. package/test/checkup.integration.test.ts +0 -413
  66. package/test/checkup.test.ts +0 -3626
  67. package/test/compose-cmd.test.ts +0 -120
  68. package/test/config-consistency.test.ts +0 -352
  69. package/test/init.integration.test.ts +0 -438
  70. package/test/init.test.ts +0 -1816
  71. package/test/issues.cli.test.ts +0 -1162
  72. package/test/issues.test.ts +0 -456
  73. package/test/mcp-server.test.ts +0 -2530
  74. package/test/monitoring.test.ts +0 -746
  75. package/test/permission-check-sql.test.ts +0 -116
  76. package/test/reports.cli.test.ts +0 -793
  77. package/test/reports.test.ts +0 -977
  78. package/test/schema-validation.test.ts +0 -231
  79. package/test/storage.test.ts +0 -935
  80. package/test/supabase.test.ts +0 -709
  81. package/test/targets-add-config.test.ts +0 -28
  82. package/test/test-utils.ts +0 -190
  83. package/test/upgrade.test.ts +0 -1056
  84. package/test/util.test.ts +0 -44
  85. package/tsconfig.json +0 -20
@@ -1,526 +0,0 @@
1
- import * as http from "http";
2
- import * as https from "https";
3
- import { URL } from "url";
4
- import { normalizeBaseUrl } from "./util";
5
-
6
- /**
7
- * Retry configuration for network operations
8
- */
9
- export interface RetryConfig {
10
- maxAttempts: number;
11
- initialDelayMs: number;
12
- maxDelayMs: number;
13
- backoffMultiplier: number;
14
- }
15
-
16
- const DEFAULT_RETRY_CONFIG: RetryConfig = {
17
- maxAttempts: 3,
18
- initialDelayMs: 1000,
19
- maxDelayMs: 10000,
20
- backoffMultiplier: 2,
21
- };
22
-
23
- /**
24
- * Check if an error is retryable (network errors, timeouts, 5xx errors)
25
- */
26
- function isRetryableError(err: unknown): boolean {
27
- if (err instanceof RpcError) {
28
- // Retry on server errors (5xx), not on client errors (4xx)
29
- return err.statusCode >= 500 && err.statusCode < 600;
30
- }
31
-
32
- // Check for Node.js error codes (works on Error and Error-like objects)
33
- if (typeof err === "object" && err !== null && "code" in err) {
34
- const code = String((err as { code: unknown }).code);
35
- if (["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT"].includes(code)) {
36
- return true;
37
- }
38
- }
39
-
40
- if (err instanceof Error) {
41
- const msg = err.message.toLowerCase();
42
- // Retry on network-related errors based on message content
43
- return (
44
- msg.includes("timeout") ||
45
- msg.includes("timed out") ||
46
- msg.includes("econnreset") ||
47
- msg.includes("econnrefused") ||
48
- msg.includes("enotfound") ||
49
- msg.includes("socket hang up") ||
50
- msg.includes("network")
51
- );
52
- }
53
-
54
- return false;
55
- }
56
-
57
- /**
58
- * Execute an async function with exponential backoff retry.
59
- * Retries on network errors, timeouts, and 5xx server errors.
60
- * Does not retry on 4xx client errors.
61
- *
62
- * @param fn - Async function to execute
63
- * @param config - Optional retry configuration (uses defaults if not provided)
64
- * @param onRetry - Optional callback invoked before each retry attempt
65
- * @returns Promise resolving to the function result
66
- * @throws The last error if all retry attempts fail or error is non-retryable
67
- *
68
- * @example
69
- * ```typescript
70
- * const result = await withRetry(
71
- * () => fetchData(),
72
- * { maxAttempts: 3 },
73
- * (attempt, err, delay) => console.log(`Retry ${attempt}, waiting ${delay}ms`)
74
- * );
75
- * ```
76
- */
77
- export async function withRetry<T>(
78
- fn: () => Promise<T>,
79
- config: Partial<RetryConfig> = {},
80
- onRetry?: (attempt: number, error: unknown, delayMs: number) => void
81
- ): Promise<T> {
82
- const { maxAttempts, initialDelayMs, maxDelayMs, backoffMultiplier } = {
83
- ...DEFAULT_RETRY_CONFIG,
84
- ...config,
85
- };
86
-
87
- let lastError: unknown;
88
- let delayMs = initialDelayMs;
89
-
90
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
91
- try {
92
- return await fn();
93
- } catch (err) {
94
- lastError = err;
95
-
96
- if (attempt === maxAttempts || !isRetryableError(err)) {
97
- throw err;
98
- }
99
-
100
- if (onRetry) {
101
- onRetry(attempt, err, delayMs);
102
- }
103
-
104
- await new Promise((resolve) => setTimeout(resolve, delayMs));
105
- delayMs = Math.min(delayMs * backoffMultiplier, maxDelayMs);
106
- }
107
- }
108
-
109
- throw lastError;
110
- }
111
-
112
- /**
113
- * Error thrown when an RPC call to the PostgresAI API fails.
114
- * Contains detailed information about the failure for debugging and display.
115
- */
116
- export class RpcError extends Error {
117
- /** Name of the RPC endpoint that failed */
118
- rpcName: string;
119
- /** HTTP status code returned by the server */
120
- statusCode: number;
121
- /** Raw response body text */
122
- payloadText: string;
123
- /** Parsed JSON response body, or null if parsing failed */
124
- payloadJson: any | null;
125
-
126
- constructor(params: { rpcName: string; statusCode: number; payloadText: string; payloadJson: any | null }) {
127
- const { rpcName, statusCode, payloadText, payloadJson } = params;
128
- super(`RPC ${rpcName} failed: HTTP ${statusCode}`);
129
- this.name = "RpcError";
130
- this.rpcName = rpcName;
131
- this.statusCode = statusCode;
132
- this.payloadText = payloadText;
133
- this.payloadJson = payloadJson;
134
- }
135
- }
136
-
137
- /**
138
- * Format an RpcError for human-readable console display.
139
- * Extracts message, details, and hint from the error payload if available.
140
- *
141
- * @param err - The RpcError to format
142
- * @returns Array of lines suitable for console output
143
- */
144
- export function formatRpcErrorForDisplay(err: RpcError): string[] {
145
- const lines: string[] = [];
146
- lines.push(`Error: RPC ${err.rpcName} failed: HTTP ${err.statusCode}`);
147
-
148
- const obj = err.payloadJson && typeof err.payloadJson === "object" ? err.payloadJson : null;
149
- const details = obj && typeof (obj as any).details === "string" ? (obj as any).details : "";
150
- const hint = obj && typeof (obj as any).hint === "string" ? (obj as any).hint : "";
151
- const message = obj && typeof (obj as any).message === "string" ? (obj as any).message : "";
152
-
153
- if (message) lines.push(`Message: ${message}`);
154
- if (details) lines.push(`Details: ${details}`);
155
- if (hint) lines.push(`Hint: ${hint}`);
156
-
157
- // Fallback to raw payload if we couldn't extract anything useful.
158
- if (!message && !details && !hint) {
159
- const t = (err.payloadText || "").trim();
160
- if (t) lines.push(t);
161
- }
162
- return lines;
163
- }
164
-
165
- function unwrapRpcResponse(parsed: unknown): any {
166
- // Some deployments return a plain object, others return an array of rows,
167
- // and some wrap OUT params under a "result" key.
168
- if (Array.isArray(parsed)) {
169
- if (parsed.length === 1) return unwrapRpcResponse(parsed[0]);
170
- return parsed;
171
- }
172
- if (parsed && typeof parsed === "object") {
173
- const obj = parsed as any;
174
- if (obj.result !== undefined) return obj.result;
175
- }
176
- return parsed as any;
177
- }
178
-
179
- // Default timeout for HTTP requests (30 seconds)
180
- const HTTP_TIMEOUT_MS = 30_000;
181
-
182
- async function postRpc<T>(params: {
183
- apiKey: string;
184
- apiBaseUrl: string;
185
- rpcName: string;
186
- bodyObj: Record<string, unknown>;
187
- timeoutMs?: number;
188
- }): Promise<T> {
189
- const { apiKey, apiBaseUrl, rpcName, bodyObj, timeoutMs = HTTP_TIMEOUT_MS } = params;
190
-
191
- // NOTE: API key validation removed intentionally to allow markdown conversion without auth.
192
- // When apiKey is empty, API returns partial markdown (observations only, no full reports).
193
- // API will return 401/403 for endpoints that require authentication.
194
-
195
- const base = normalizeBaseUrl(apiBaseUrl);
196
- const url = new URL(`${base}/rpc/${rpcName}`);
197
- const body = JSON.stringify(bodyObj);
198
-
199
- const headers: Record<string, string> = {
200
- // API key is sent in BOTH header and body (see bodyObj.access_token):
201
- // - Header: Used by the API gateway/proxy for HTTP authentication
202
- // - Body: Passed to PostgreSQL RPC function for in-database authorization
203
- // This is intentional for defense-in-depth; backend validates both.
204
- "access-token": apiKey,
205
- "Prefer": "return=representation",
206
- "Content-Type": "application/json",
207
- "Content-Length": Buffer.byteLength(body).toString(),
208
- };
209
-
210
- // Use AbortController for clean timeout handling
211
- const controller = new AbortController();
212
- let timeoutId: ReturnType<typeof setTimeout> | null = null;
213
- let settled = false;
214
-
215
- return new Promise((resolve, reject) => {
216
- const settledReject = (err: Error) => {
217
- if (settled) return;
218
- settled = true;
219
- if (timeoutId) clearTimeout(timeoutId);
220
- reject(err);
221
- };
222
-
223
- const settledResolve = (value: T) => {
224
- if (settled) return;
225
- settled = true;
226
- if (timeoutId) clearTimeout(timeoutId);
227
- resolve(value);
228
- };
229
-
230
- // Transport is picked from the URL protocol so the CLI can talk to a
231
- // local-dev PostgREST over plain HTTP. Production URLs are always HTTPS;
232
- // to guard against typos (e.g. a missing 's' in 'https://') silently
233
- // leaking the API key in cleartext, refuse HTTP to non-loopback hosts
234
- // unless the operator explicitly opts in via CHECKUP_ALLOW_HTTP=1.
235
- if (url.protocol === "http:") {
236
- // WHATWG URL keeps IPv6 literals bracketed in .hostname
237
- // (e.g. `[::1]`), so strip the brackets before matching the allowlist.
238
- const hostname = url.hostname.replace(/^\[|\]$/g, "");
239
- const isLoopback = ["localhost", "127.0.0.1", "::1"].includes(hostname);
240
- if (!isLoopback && process.env.CHECKUP_ALLOW_HTTP !== "1") {
241
- throw new Error(
242
- `Refusing to send API key over plaintext HTTP to '${url.host}'. ` +
243
- `Use https://, a loopback hostname, or set CHECKUP_ALLOW_HTTP=1.`
244
- );
245
- }
246
- }
247
- const transport = url.protocol === "http:" ? http : https;
248
- const req = transport.request(
249
- url,
250
- {
251
- method: "POST",
252
- headers,
253
- signal: controller.signal,
254
- },
255
- (res) => {
256
- // Response started (headers received) - clear the connection timeout.
257
- // Once the server starts responding, we let it complete rather than
258
- // timing out mid-response which would cause confusing errors.
259
- if (timeoutId) {
260
- clearTimeout(timeoutId);
261
- timeoutId = null;
262
- }
263
- let data = "";
264
- res.on("data", (chunk) => (data += chunk));
265
- res.on("end", () => {
266
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
267
- try {
268
- const parsed = JSON.parse(data);
269
- settledResolve(unwrapRpcResponse(parsed) as T);
270
- } catch {
271
- settledReject(new Error(`Failed to parse RPC response: ${data}`));
272
- }
273
- } else {
274
- const statusCode = res.statusCode || 0;
275
- let payloadJson: any | null = null;
276
- if (data) {
277
- try {
278
- payloadJson = JSON.parse(data);
279
- } catch {
280
- payloadJson = null;
281
- }
282
- }
283
- settledReject(new RpcError({ rpcName, statusCode, payloadText: data, payloadJson }));
284
- }
285
- });
286
- res.on("error", (err) => {
287
- settledReject(err);
288
- });
289
- }
290
- );
291
-
292
- // Set up connection timeout - applies until response headers are received.
293
- // Once response starts, timeout is cleared (see response callback above).
294
- timeoutId = setTimeout(() => {
295
- controller.abort();
296
- req.destroy(); // Backup: ensure request is terminated
297
- settledReject(new Error(`RPC ${rpcName} timed out after ${timeoutMs}ms (no response)`));
298
- }, timeoutMs);
299
-
300
- req.on("error", (err: Error) => {
301
- // Handle abort as timeout (may already be rejected by timeout handler)
302
- if (err.name === "AbortError" || (err as any).code === "ABORT_ERR") {
303
- settledReject(new Error(`RPC ${rpcName} timed out after ${timeoutMs}ms`));
304
- return;
305
- }
306
- // Provide clearer error for common network issues
307
- if ((err as any).code === "ECONNREFUSED") {
308
- settledReject(new Error(`RPC ${rpcName} failed: connection refused to ${url.host}`));
309
- } else if ((err as any).code === "ENOTFOUND") {
310
- settledReject(new Error(`RPC ${rpcName} failed: DNS lookup failed for ${url.host}`));
311
- } else if ((err as any).code === "ECONNRESET") {
312
- settledReject(new Error(`RPC ${rpcName} failed: connection reset by server`));
313
- } else {
314
- settledReject(err);
315
- }
316
- });
317
-
318
- req.write(body);
319
- req.end();
320
- });
321
- }
322
-
323
- /**
324
- * Result of an API key pre-flight verification.
325
- * - "valid": the key was accepted by the API
326
- * - "invalid": the API definitively rejected the key (HTTP 401/403)
327
- * - "unknown": verification could not be completed (network error, timeout,
328
- * unexpected status) — callers should warn and continue, not block the run
329
- */
330
- export type ApiKeyVerification =
331
- | { status: "valid" }
332
- | { status: "invalid"; statusCode: number }
333
- | { status: "unknown"; detail: string };
334
-
335
- // Timeout for the auth pre-flight (shorter than regular RPC timeout: this is
336
- // an optional fast check and must not noticeably delay the run when the API
337
- // is slow or unreachable).
338
- const VERIFY_API_KEY_TIMEOUT_MS = 10_000;
339
-
340
- /**
341
- * Verify an API key with a cheap, side-effect-free authenticated call
342
- * (GET /checkup_reports?limit=1 — the same endpoint the `reports` command
343
- * uses) so expensive work can fail fast on bad credentials.
344
- *
345
- * Only a definitive HTTP 401/403 is reported as "invalid". Network errors,
346
- * timeouts, and unexpected statuses are reported as "unknown" so a transient
347
- * pre-flight failure never blocks a run that might otherwise succeed.
348
- */
349
- export async function verifyApiKey(params: {
350
- apiKey: string;
351
- apiBaseUrl: string;
352
- timeoutMs?: number;
353
- }): Promise<ApiKeyVerification> {
354
- const { apiKey, apiBaseUrl, timeoutMs = VERIFY_API_KEY_TIMEOUT_MS } = params;
355
- const base = normalizeBaseUrl(apiBaseUrl);
356
- const url = new URL(`${base}/checkup_reports`);
357
- url.searchParams.set("limit", "1");
358
-
359
- // Same plaintext-HTTP guard as postRpc: never send the API key over plain
360
- // HTTP to a non-loopback host. Report "unknown" rather than aborting the
361
- // run — the upload path raises the definitive, actionable error.
362
- if (url.protocol === "http:") {
363
- const hostname = url.hostname.replace(/^\[|\]$/g, "");
364
- const isLoopback = ["localhost", "127.0.0.1", "::1"].includes(hostname);
365
- if (!isLoopback && process.env.CHECKUP_ALLOW_HTTP !== "1") {
366
- return {
367
- status: "unknown",
368
- detail: `refusing to send API key over plaintext HTTP to '${url.host}'`,
369
- };
370
- }
371
- }
372
-
373
- const controller = new AbortController();
374
- const timer = setTimeout(() => controller.abort(), timeoutMs);
375
- try {
376
- const response = await fetch(url.toString(), {
377
- method: "GET",
378
- headers: { "access-token": apiKey },
379
- signal: controller.signal,
380
- });
381
- // Drain the body so the connection is released cleanly.
382
- await response.text().catch(() => "");
383
- if (response.status === 401 || response.status === 403) {
384
- return { status: "invalid", statusCode: response.status };
385
- }
386
- if (response.ok) {
387
- return { status: "valid" };
388
- }
389
- return { status: "unknown", detail: `HTTP ${response.status}` };
390
- } catch (err) {
391
- const message = err instanceof Error ? err.message : String(err);
392
- return { status: "unknown", detail: message };
393
- } finally {
394
- clearTimeout(timer);
395
- }
396
- }
397
-
398
- /**
399
- * Create a new checkup report in the PostgresAI backend.
400
- * This creates the parent report container; individual check results
401
- * are uploaded separately via uploadCheckupReportJson().
402
- *
403
- * @param params - Configuration for report creation
404
- * @param params.apiKey - PostgresAI API access token
405
- * @param params.apiBaseUrl - Base URL of the PostgresAI API
406
- * @param params.project - Project name or ID to associate the report with
407
- * @param params.status - Optional initial status for the report
408
- * @returns Promise resolving to the created report ID
409
- * @throws {RpcError} On API failures (4xx/5xx responses)
410
- * @throws {Error} On network errors or unexpected response format
411
- */
412
- export async function createCheckupReport(params: {
413
- apiKey: string;
414
- apiBaseUrl: string;
415
- project: string;
416
- status?: string;
417
- }): Promise<{ reportId: number }> {
418
- const { apiKey, apiBaseUrl, project, status } = params;
419
- const bodyObj: Record<string, unknown> = {
420
- access_token: apiKey,
421
- project,
422
- };
423
- if (status) bodyObj.status = status;
424
-
425
- const resp = await postRpc<any>({
426
- apiKey,
427
- apiBaseUrl,
428
- rpcName: "checkup_report_create",
429
- bodyObj,
430
- });
431
- const reportId = Number(resp?.report_id);
432
- if (!Number.isFinite(reportId) || reportId <= 0) {
433
- throw new Error(`Unexpected checkup_report_create response: ${JSON.stringify(resp)}`);
434
- }
435
- return { reportId };
436
- }
437
-
438
- /**
439
- * Upload a JSON check result to an existing checkup report.
440
- * Each check (e.g., H001, A003) is uploaded as a separate JSON file.
441
- *
442
- * @param params - Configuration for the upload
443
- * @param params.apiKey - PostgresAI API access token
444
- * @param params.apiBaseUrl - Base URL of the PostgresAI API
445
- * @param params.reportId - ID of the parent report (from createCheckupReport)
446
- * @param params.filename - Filename for the uploaded JSON (e.g., "H001.json")
447
- * @param params.checkId - Check identifier (e.g., "H001", "A003")
448
- * @param params.jsonText - JSON content as a string
449
- * @returns Promise resolving to the created report chunk ID
450
- * @throws {RpcError} On API failures (4xx/5xx responses)
451
- * @throws {Error} On network errors or unexpected response format
452
- */
453
- export async function uploadCheckupReportJson(params: {
454
- apiKey: string;
455
- apiBaseUrl: string;
456
- reportId: number;
457
- filename: string;
458
- checkId: string;
459
- jsonText: string;
460
- }): Promise<{ reportChunkId: number }> {
461
- const { apiKey, apiBaseUrl, reportId, filename, checkId, jsonText } = params;
462
- const bodyObj: Record<string, unknown> = {
463
- access_token: apiKey,
464
- checkup_report_id: reportId,
465
- filename,
466
- check_id: checkId,
467
- data: jsonText,
468
- type: "json",
469
- generate_issue: true,
470
- };
471
-
472
- const resp = await postRpc<any>({
473
- apiKey,
474
- apiBaseUrl,
475
- rpcName: "checkup_report_file_post",
476
- bodyObj,
477
- });
478
- // Backend has a typo: "report_chunck_id" (with 'ck') - handle both spellings for compatibility
479
- const chunkId = Number(resp?.report_chunck_id ?? resp?.report_chunk_id);
480
- if (!Number.isFinite(chunkId) || chunkId <= 0) {
481
- throw new Error(`Unexpected checkup_report_file_post response: ${JSON.stringify(resp)}`);
482
- }
483
- return { reportChunkId: chunkId };
484
- }
485
-
486
- /**
487
- * Convert a checkup report JSON to markdown format using the PostgresAI API.
488
- * This calls the v1.checkup_report_json_to_markdown RPC function.
489
- *
490
- * @param params - Configuration for the conversion
491
- * @param params.apiKey - PostgresAI API access token
492
- * @param params.apiBaseUrl - Base URL of the PostgresAI API
493
- * @param params.checkId - Check identifier (e.g., "H001", "A003")
494
- * @param params.jsonPayload - The JSON data from the check report
495
- * @param params.reportType - Optional report type parameter
496
- * @returns Promise resolving to the markdown content as JSON
497
- * @throws {RpcError} On API failures (4xx/5xx responses)
498
- * @throws {Error} On network errors or unexpected response format
499
- */
500
- export async function convertCheckupReportJsonToMarkdown(params: {
501
- apiKey: string;
502
- apiBaseUrl: string;
503
- checkId: string;
504
- jsonPayload: any;
505
- reportType?: string;
506
- }): Promise<any> {
507
- const { apiKey, apiBaseUrl, checkId, jsonPayload, reportType } = params;
508
- const bodyObj: Record<string, unknown> = {
509
- check_id: checkId,
510
- json_payload: jsonPayload,
511
- access_token: apiKey,
512
- };
513
-
514
- if (reportType) {
515
- bodyObj.report_type = reportType;
516
- }
517
-
518
- const resp = await postRpc<any>({
519
- apiKey,
520
- apiBaseUrl,
521
- rpcName: "checkup_report_json_to_markdown",
522
- bodyObj,
523
- });
524
-
525
- return resp;
526
- }
@@ -1,103 +0,0 @@
1
- /**
2
- * Checkup Dictionary Module
3
- * =========================
4
- * Provides access to the checkup report dictionary data embedded at build time.
5
- *
6
- * The dictionary is fetched from https://postgres.ai/api/general/checkup_dictionary
7
- * during the build process and embedded into checkup-dictionary-embedded.ts.
8
- *
9
- * This ensures no API calls are made at runtime while keeping the data up-to-date.
10
- */
11
-
12
- import { CHECKUP_DICTIONARY_DATA } from "./checkup-dictionary-embedded";
13
-
14
- /**
15
- * A checkup dictionary entry describing a single check type.
16
- */
17
- export interface CheckupDictionaryEntry {
18
- /** Unique check code (e.g., "A001", "H002") */
19
- code: string;
20
- /** Human-readable title for the check */
21
- title: string;
22
- /** Brief description of what the check covers */
23
- description: string;
24
- /** Category grouping (e.g., "system", "indexes", "vacuum") */
25
- category: string;
26
- /** Optional sort order within category */
27
- sort_order: number | null;
28
- /** Whether this is a system-level report */
29
- is_system_report: boolean;
30
- }
31
-
32
- /**
33
- * Module-level cache for O(1) lookups by code.
34
- * Initialized at module load time from embedded data.
35
- * Keys are normalized to uppercase for case-insensitive lookups.
36
- */
37
- const dictionaryByCode: Map<string, CheckupDictionaryEntry> = new Map(
38
- CHECKUP_DICTIONARY_DATA.map((entry) => [entry.code.toUpperCase(), entry])
39
- );
40
-
41
- /**
42
- * Get all checkup dictionary entries.
43
- *
44
- * @returns Array of all checkup dictionary entries
45
- */
46
- export function getAllCheckupEntries(): CheckupDictionaryEntry[] {
47
- return CHECKUP_DICTIONARY_DATA;
48
- }
49
-
50
- /**
51
- * Get a checkup dictionary entry by its code.
52
- *
53
- * @param code - The check code (e.g., "A001", "H002"). Lookup is case-insensitive.
54
- * @returns The dictionary entry or null if not found
55
- */
56
- export function getCheckupEntry(code: string): CheckupDictionaryEntry | null {
57
- return dictionaryByCode.get(code.toUpperCase()) ?? null;
58
- }
59
-
60
- /**
61
- * Check if a code exists in the dictionary.
62
- *
63
- * @param code - The check code to validate
64
- * @returns True if the code exists in the dictionary
65
- */
66
- export function isValidCheckupCode(code: string): boolean {
67
- return dictionaryByCode.has(code.toUpperCase());
68
- }
69
-
70
- /**
71
- * Get all check codes as an array.
72
- *
73
- * @returns Array of all check codes (e.g., ["A001", "A002", ...])
74
- */
75
- export function getAllCheckupCodes(): string[] {
76
- return CHECKUP_DICTIONARY_DATA.map((entry) => entry.code);
77
- }
78
-
79
- /**
80
- * Get checkup entries filtered by category.
81
- *
82
- * @param category - The category to filter by (e.g., "indexes", "vacuum")
83
- * @returns Array of entries in the specified category
84
- */
85
- export function getCheckupEntriesByCategory(category: string): CheckupDictionaryEntry[] {
86
- return CHECKUP_DICTIONARY_DATA.filter(
87
- (entry) => entry.category.toLowerCase() === category.toLowerCase()
88
- );
89
- }
90
-
91
- /**
92
- * Build a code-to-title mapping object.
93
- * Useful for backwards compatibility with CHECK_INFO style usage.
94
- *
95
- * @returns Object mapping check codes to titles (e.g., { "A001": "System information", ... })
96
- */
97
- export function buildCheckInfoMap(): Record<string, string> {
98
- const result: Record<string, string> = {};
99
- for (const entry of CHECKUP_DICTIONARY_DATA) {
100
- result[entry.code] = entry.title;
101
- }
102
- return result;
103
- }