dbctx 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.
Files changed (68) hide show
  1. package/.prettierrc.cjs +7 -0
  2. package/.turbo/turbo-build.log +5 -0
  3. package/dist/App.d.ts +23 -0
  4. package/dist/App.d.ts.map +1 -0
  5. package/dist/App.js +9 -0
  6. package/dist/components/Error.d.ts +7 -0
  7. package/dist/components/Error.d.ts.map +1 -0
  8. package/dist/components/Error.js +6 -0
  9. package/dist/db/analyze.d.mts +3 -0
  10. package/dist/db/analyze.d.mts.map +1 -0
  11. package/dist/db/analyze.mjs +16 -0
  12. package/dist/db/attributes.d.mts +16 -0
  13. package/dist/db/attributes.d.mts.map +1 -0
  14. package/dist/db/attributes.mjs +37 -0
  15. package/dist/db/enums.d.mts +8 -0
  16. package/dist/db/enums.d.mts.map +1 -0
  17. package/dist/db/enums.mjs +24 -0
  18. package/dist/db/file-stats.d.mts +11 -0
  19. package/dist/db/file-stats.d.mts.map +1 -0
  20. package/dist/db/file-stats.mjs +43 -0
  21. package/dist/db/foreign-keys.d.mts +14 -0
  22. package/dist/db/foreign-keys.d.mts.map +1 -0
  23. package/dist/db/foreign-keys.mjs +44 -0
  24. package/dist/db/index.d.mts +10 -0
  25. package/dist/db/index.d.mts.map +1 -0
  26. package/dist/db/index.mjs +10 -0
  27. package/dist/db/indexes.d.mts +16 -0
  28. package/dist/db/indexes.d.mts.map +1 -0
  29. package/dist/db/indexes.mjs +38 -0
  30. package/dist/db/relations.d.mts +10 -0
  31. package/dist/db/relations.d.mts.map +1 -0
  32. package/dist/db/relations.mjs +23 -0
  33. package/dist/db/stats.d.mts +12 -0
  34. package/dist/db/stats.d.mts.map +1 -0
  35. package/dist/db/stats.mjs +34 -0
  36. package/dist/db/version.d.mts +13 -0
  37. package/dist/db/version.d.mts.map +1 -0
  38. package/dist/db/version.mjs +19 -0
  39. package/dist/index.d.mts +17 -0
  40. package/dist/index.d.mts.map +1 -0
  41. package/dist/index.mjs +75 -0
  42. package/dist/logger.d.mts +3 -0
  43. package/dist/logger.d.mts.map +1 -0
  44. package/dist/logger.mjs +7 -0
  45. package/dist/schemas/index.d.mts +2 -0
  46. package/dist/schemas/index.d.mts.map +1 -0
  47. package/dist/schemas/index.mjs +91 -0
  48. package/dist/validatePaths.d.mts +12 -0
  49. package/dist/validatePaths.d.mts.map +1 -0
  50. package/dist/validatePaths.mjs +48 -0
  51. package/package.json +43 -0
  52. package/src/App.tsx +44 -0
  53. package/src/components/Error.tsx +16 -0
  54. package/src/db/analyze.mts +22 -0
  55. package/src/db/attributes.mts +58 -0
  56. package/src/db/enums.mts +43 -0
  57. package/src/db/file-stats.mts +57 -0
  58. package/src/db/foreign-keys.mts +61 -0
  59. package/src/db/index.mts +9 -0
  60. package/src/db/indexes.mts +57 -0
  61. package/src/db/relations.mts +35 -0
  62. package/src/db/stats.mts +51 -0
  63. package/src/db/version.mts +34 -0
  64. package/src/index.mts +116 -0
  65. package/src/logger.mts +10 -0
  66. package/src/schemas/index.mts +102 -0
  67. package/src/validatePaths.mts +77 -0
  68. package/tsconfig.json +104 -0
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ export type TSSLMode = "disable" | "allow" | "prefer" | "require" | "verify-ca" | "verify-full";
3
+ export interface ConnectionOptions {
4
+ hostname?: string;
5
+ port?: number;
6
+ database?: string;
7
+ username?: string;
8
+ sslmode?: TSSLMode;
9
+ sslcert?: string;
10
+ sslkey?: string;
11
+ sslrootcert?: string;
12
+ sshHost?: string;
13
+ sshPort?: number;
14
+ sshUsername?: string;
15
+ sshPrivateKey?: string;
16
+ }
17
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"./src/","sources":["index.mts"],"names":[],"mappings":";AAOA,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;AAEhG,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B"}
package/dist/index.mjs ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { render } from "ink";
4
+ import { Error } from "./components/Error.js";
5
+ import { App } from "./App.js";
6
+ import { validatePathOptions } from "./validatePaths.mjs";
7
+ const program = new Command();
8
+ program
9
+ .name("dbctx")
10
+ .description("AI generated docs from your application database")
11
+ .version("1.0.0")
12
+ .argument("[connection-string]", "PostgreSQL connection string (env: DATABASE_URL)")
13
+ .option("-h, --hostname <host>", "database hostname (env: PGHOST, default: localhost)")
14
+ .option("-p, --port <port>", "database port (env: PGPORT, default: 5432)", parseInt)
15
+ .option("-d, --database <name>", "database name (env: PGDATABASE, default: postgres)")
16
+ .option("-U, --username <user>", "database username (env: PGUSER, default: postgres)")
17
+ .option("--sslmode <mode>", "SSL mode: disable|allow|prefer|require|verify-ca|verify-full (env: PGSSLMODE, default: prefer)")
18
+ .option("--sslcert <path>", "path to client certificate (env: PGSSLCERT)")
19
+ .option("--sslkey <path>", "path to client private key (env: PGSSLKEY)")
20
+ .option("--sslrootcert <path>", "path to root CA certificate (env: PGSSLROOTCERT)")
21
+ .option("--ssh-host <host>", "SSH tunnel host (bastion/jump server)")
22
+ .option("--ssh-port <port>", "SSH tunnel port (default: 22)", parseInt)
23
+ .option("--ssh-username <user>", "SSH tunnel username (default: $USER)")
24
+ .option("--ssh-private-key <path>", "path to SSH private key (default: ~/.ssh/id_rsa)")
25
+ .action((connectionString, options) => {
26
+ const hasConnectionString = Boolean(connectionString);
27
+ const hasIndividualOptions = Boolean(options.hostname || options.port || options.database || options.username);
28
+ if (hasConnectionString && hasIndividualOptions) {
29
+ render(Error({
30
+ message: "Cannot use both connection string and individual options (-h, -p, -d, -U). Use one or the other.",
31
+ }));
32
+ process.exit(1);
33
+ }
34
+ // Validate path options exist on filesystem
35
+ const { errors: pathErrors, expandedPaths } = validatePathOptions(options);
36
+ if (pathErrors.length > 0) {
37
+ const errorMessages = pathErrors
38
+ .map(({ option, path }) => ` ${option}: ${path}`)
39
+ .join("\n");
40
+ render(Error({
41
+ message: `The following paths do not exist:\n${errorMessages}`,
42
+ }));
43
+ process.exit(1);
44
+ }
45
+ // Replace original paths with expanded paths (~ resolved to home directory)
46
+ const validatedOptions = {
47
+ ...options,
48
+ ...expandedPaths,
49
+ };
50
+ if (hasIndividualOptions) {
51
+ const requiredOptions = [
52
+ { key: "hostname", flag: "-h/--hostname" },
53
+ { key: "port", flag: "-p/--port" },
54
+ { key: "database", flag: "-d/--database" },
55
+ { key: "username", flag: "-U/--username" },
56
+ ];
57
+ const missing = requiredOptions
58
+ .filter(({ key }) => !validatedOptions[key])
59
+ .map(({ flag }) => flag);
60
+ if (missing.length > 0) {
61
+ render(Error({
62
+ message: `Missing required options: ${missing.join(", ")}`,
63
+ }));
64
+ process.exit(1);
65
+ }
66
+ }
67
+ // All validations passed - build config and render the app
68
+ const config = {
69
+ connectionString,
70
+ ...validatedOptions,
71
+ };
72
+ render(App({ config }));
73
+ });
74
+ program.parse();
75
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgubWpzIiwic291cmNlUm9vdCI6Ii4vc3JjLyIsInNvdXJjZXMiOlsiaW5kZXgubXRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDN0IsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzlDLE9BQU8sRUFBRSxHQUFHLEVBQTBCLE1BQU0sVUFBVSxDQUFDO0FBQ3ZELE9BQU8sRUFBRSxtQkFBbUIsRUFBNkIsTUFBTSxxQkFBcUIsQ0FBQztBQW1CckYsTUFBTSxPQUFPLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztBQUU5QixPQUFPO0tBQ0YsSUFBSSxDQUFDLE9BQU8sQ0FBQztLQUNiLFdBQVcsQ0FBQyxrREFBa0QsQ0FBQztLQUMvRCxPQUFPLENBQUMsT0FBTyxDQUFDO0tBQ2hCLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxrREFBa0QsQ0FBQztLQUNuRixNQUFNLENBQUMsdUJBQXVCLEVBQUUscURBQXFELENBQUM7S0FDdEYsTUFBTSxDQUFDLG1CQUFtQixFQUFFLDRDQUE0QyxFQUFFLFFBQVEsQ0FBQztLQUNuRixNQUFNLENBQUMsdUJBQXVCLEVBQUUsb0RBQW9ELENBQUM7S0FDckYsTUFBTSxDQUFDLHVCQUF1QixFQUFFLG9EQUFvRCxDQUFDO0tBQ3JGLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxnR0FBZ0csQ0FBQztLQUM1SCxNQUFNLENBQUMsa0JBQWtCLEVBQUUsNkNBQTZDLENBQUM7S0FDekUsTUFBTSxDQUFDLGlCQUFpQixFQUFFLDRDQUE0QyxDQUFDO0tBQ3ZFLE1BQU0sQ0FBQyxzQkFBc0IsRUFBRSxrREFBa0QsQ0FBQztLQUNsRixNQUFNLENBQUMsbUJBQW1CLEVBQUUsdUNBQXVDLENBQUM7S0FDcEUsTUFBTSxDQUFDLG1CQUFtQixFQUFFLCtCQUErQixFQUFFLFFBQVEsQ0FBQztLQUN0RSxNQUFNLENBQUMsdUJBQXVCLEVBQUUsc0NBQXNDLENBQUM7S0FDdkUsTUFBTSxDQUFDLDBCQUEwQixFQUFFLGtEQUFrRCxDQUFDO0tBQ3RGLE1BQU0sQ0FBQyxDQUFDLGdCQUFvQyxFQUFFLE9BQTBCLEVBQUUsRUFBRTtJQUN6RSxNQUFNLG1CQUFtQixHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RELE1BQU0sb0JBQW9CLEdBQUcsT0FBTyxDQUNoQyxPQUFPLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQyxJQUFJLElBQUksT0FBTyxDQUFDLFFBQVEsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUMzRSxDQUFDO0lBRUYsSUFBSSxtQkFBbUIsSUFBSSxvQkFBb0IsRUFBRSxDQUFDO1FBQzlDLE1BQU0sQ0FDRixLQUFLLENBQUM7WUFDRixPQUFPLEVBQ0gsa0dBQWtHO1NBQ3pHLENBQUMsQ0FDTCxDQUFDO1FBRUYsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQixDQUFDO0lBRUQsNENBQTRDO0lBQzVDLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxHQUFHLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRTNFLElBQUksVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLGFBQWEsR0FBRyxVQUFVO2FBQzNCLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBd0IsRUFBRSxFQUFFLENBQUMsS0FBSyxNQUFNLEtBQUssSUFBSSxFQUFFLENBQUM7YUFDdkUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRWhCLE1BQU0sQ0FDRixLQUFLLENBQUM7WUFDRixPQUFPLEVBQUUsc0NBQXNDLGFBQWEsRUFBRTtTQUNqRSxDQUFDLENBQ0wsQ0FBQztRQUVGLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEIsQ0FBQztJQUVELDRFQUE0RTtJQUM1RSxNQUFNLGdCQUFnQixHQUFzQjtRQUN4QyxHQUFHLE9BQU87UUFDVixHQUFHLGFBQWE7S0FDbkIsQ0FBQztJQUVGLElBQUksb0JBQW9CLEVBQUUsQ0FBQztRQUN2QixNQUFNLGVBQWUsR0FBRztZQUNwQixFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRTtZQUMxQyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRTtZQUNsQyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRTtZQUMxQyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRTtTQUNwQyxDQUFDO1FBRVgsTUFBTSxPQUFPLEdBQUcsZUFBZTthQUMxQixNQUFNLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDO2FBQzNDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRTdCLElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNyQixNQUFNLENBQ0YsS0FBSyxDQUFDO2dCQUNGLE9BQU8sRUFBRSw2QkFBNkIsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTthQUM3RCxDQUFDLENBQ0wsQ0FBQztZQUVGLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsQ0FBQztJQUNMLENBQUM7SUFFRCwyREFBMkQ7SUFDM0QsTUFBTSxNQUFNLEdBQXNCO1FBQzlCLGdCQUFnQjtRQUNoQixHQUFHLGdCQUFnQjtLQUN0QixDQUFDO0lBRUYsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztBQUM1QixDQUFDLENBQUMsQ0FBQztBQUVQLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIiMhL3Vzci9iaW4vZW52IG5vZGVcbmltcG9ydCB7IENvbW1hbmQgfSBmcm9tIFwiY29tbWFuZGVyXCI7XG5pbXBvcnQgeyByZW5kZXIgfSBmcm9tIFwiaW5rXCI7XG5pbXBvcnQgeyBFcnJvciB9IGZyb20gXCIuL2NvbXBvbmVudHMvRXJyb3IuanNcIjtcbmltcG9ydCB7IEFwcCwgdHlwZSBUQ29ubmVjdGlvbkNvbmZpZyB9IGZyb20gXCIuL0FwcC5qc1wiO1xuaW1wb3J0IHsgdmFsaWRhdGVQYXRoT3B0aW9ucywgdHlwZSBUUGF0aFZhbGlkYXRpb25FcnJvciB9IGZyb20gXCIuL3ZhbGlkYXRlUGF0aHMubWpzXCI7XG5cbmV4cG9ydCB0eXBlIFRTU0xNb2RlID0gXCJkaXNhYmxlXCIgfCBcImFsbG93XCIgfCBcInByZWZlclwiIHwgXCJyZXF1aXJlXCIgfCBcInZlcmlmeS1jYVwiIHwgXCJ2ZXJpZnktZnVsbFwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIENvbm5lY3Rpb25PcHRpb25zIHtcbiAgICBob3N0bmFtZT86IHN0cmluZztcbiAgICBwb3J0PzogbnVtYmVyO1xuICAgIGRhdGFiYXNlPzogc3RyaW5nO1xuICAgIHVzZXJuYW1lPzogc3RyaW5nO1xuICAgIHNzbG1vZGU/OiBUU1NMTW9kZTtcbiAgICBzc2xjZXJ0Pzogc3RyaW5nO1xuICAgIHNzbGtleT86IHN0cmluZztcbiAgICBzc2xyb290Y2VydD86IHN0cmluZztcbiAgICBzc2hIb3N0Pzogc3RyaW5nO1xuICAgIHNzaFBvcnQ/OiBudW1iZXI7XG4gICAgc3NoVXNlcm5hbWU/OiBzdHJpbmc7XG4gICAgc3NoUHJpdmF0ZUtleT86IHN0cmluZztcbn1cblxuY29uc3QgcHJvZ3JhbSA9IG5ldyBDb21tYW5kKCk7XG5cbnByb2dyYW1cbiAgICAubmFtZShcImRiY3R4XCIpXG4gICAgLmRlc2NyaXB0aW9uKFwiQUkgZ2VuZXJhdGVkIGRvY3MgZnJvbSB5b3VyIGFwcGxpY2F0aW9uIGRhdGFiYXNlXCIpXG4gICAgLnZlcnNpb24oXCIxLjAuMFwiKVxuICAgIC5hcmd1bWVudChcIltjb25uZWN0aW9uLXN0cmluZ11cIiwgXCJQb3N0Z3JlU1FMIGNvbm5lY3Rpb24gc3RyaW5nIChlbnY6IERBVEFCQVNFX1VSTClcIilcbiAgICAub3B0aW9uKFwiLWgsIC0taG9zdG5hbWUgPGhvc3Q+XCIsIFwiZGF0YWJhc2UgaG9zdG5hbWUgKGVudjogUEdIT1NULCBkZWZhdWx0OiBsb2NhbGhvc3QpXCIpXG4gICAgLm9wdGlvbihcIi1wLCAtLXBvcnQgPHBvcnQ+XCIsIFwiZGF0YWJhc2UgcG9ydCAoZW52OiBQR1BPUlQsIGRlZmF1bHQ6IDU0MzIpXCIsIHBhcnNlSW50KVxuICAgIC5vcHRpb24oXCItZCwgLS1kYXRhYmFzZSA8bmFtZT5cIiwgXCJkYXRhYmFzZSBuYW1lIChlbnY6IFBHREFUQUJBU0UsIGRlZmF1bHQ6IHBvc3RncmVzKVwiKVxuICAgIC5vcHRpb24oXCItVSwgLS11c2VybmFtZSA8dXNlcj5cIiwgXCJkYXRhYmFzZSB1c2VybmFtZSAoZW52OiBQR1VTRVIsIGRlZmF1bHQ6IHBvc3RncmVzKVwiKVxuICAgIC5vcHRpb24oXCItLXNzbG1vZGUgPG1vZGU+XCIsIFwiU1NMIG1vZGU6IGRpc2FibGV8YWxsb3d8cHJlZmVyfHJlcXVpcmV8dmVyaWZ5LWNhfHZlcmlmeS1mdWxsIChlbnY6IFBHU1NMTU9ERSwgZGVmYXVsdDogcHJlZmVyKVwiKVxuICAgIC5vcHRpb24oXCItLXNzbGNlcnQgPHBhdGg+XCIsIFwicGF0aCB0byBjbGllbnQgY2VydGlmaWNhdGUgKGVudjogUEdTU0xDRVJUKVwiKVxuICAgIC5vcHRpb24oXCItLXNzbGtleSA8cGF0aD5cIiwgXCJwYXRoIHRvIGNsaWVudCBwcml2YXRlIGtleSAoZW52OiBQR1NTTEtFWSlcIilcbiAgICAub3B0aW9uKFwiLS1zc2xyb290Y2VydCA8cGF0aD5cIiwgXCJwYXRoIHRvIHJvb3QgQ0EgY2VydGlmaWNhdGUgKGVudjogUEdTU0xST09UQ0VSVClcIilcbiAgICAub3B0aW9uKFwiLS1zc2gtaG9zdCA8aG9zdD5cIiwgXCJTU0ggdHVubmVsIGhvc3QgKGJhc3Rpb24vanVtcCBzZXJ2ZXIpXCIpXG4gICAgLm9wdGlvbihcIi0tc3NoLXBvcnQgPHBvcnQ+XCIsIFwiU1NIIHR1bm5lbCBwb3J0IChkZWZhdWx0OiAyMilcIiwgcGFyc2VJbnQpXG4gICAgLm9wdGlvbihcIi0tc3NoLXVzZXJuYW1lIDx1c2VyPlwiLCBcIlNTSCB0dW5uZWwgdXNlcm5hbWUgKGRlZmF1bHQ6ICRVU0VSKVwiKVxuICAgIC5vcHRpb24oXCItLXNzaC1wcml2YXRlLWtleSA8cGF0aD5cIiwgXCJwYXRoIHRvIFNTSCBwcml2YXRlIGtleSAoZGVmYXVsdDogfi8uc3NoL2lkX3JzYSlcIilcbiAgICAuYWN0aW9uKChjb25uZWN0aW9uU3RyaW5nOiBzdHJpbmcgfCB1bmRlZmluZWQsIG9wdGlvbnM6IENvbm5lY3Rpb25PcHRpb25zKSA9PiB7XG4gICAgICAgIGNvbnN0IGhhc0Nvbm5lY3Rpb25TdHJpbmcgPSBCb29sZWFuKGNvbm5lY3Rpb25TdHJpbmcpO1xuICAgICAgICBjb25zdCBoYXNJbmRpdmlkdWFsT3B0aW9ucyA9IEJvb2xlYW4oXG4gICAgICAgICAgICBvcHRpb25zLmhvc3RuYW1lIHx8IG9wdGlvbnMucG9ydCB8fCBvcHRpb25zLmRhdGFiYXNlIHx8IG9wdGlvbnMudXNlcm5hbWVcbiAgICAgICAgKTtcblxuICAgICAgICBpZiAoaGFzQ29ubmVjdGlvblN0cmluZyAmJiBoYXNJbmRpdmlkdWFsT3B0aW9ucykge1xuICAgICAgICAgICAgcmVuZGVyKFxuICAgICAgICAgICAgICAgIEVycm9yKHtcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTpcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiQ2Fubm90IHVzZSBib3RoIGNvbm5lY3Rpb24gc3RyaW5nIGFuZCBpbmRpdmlkdWFsIG9wdGlvbnMgKC1oLCAtcCwgLWQsIC1VKS4gVXNlIG9uZSBvciB0aGUgb3RoZXIuXCIsXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFZhbGlkYXRlIHBhdGggb3B0aW9ucyBleGlzdCBvbiBmaWxlc3lzdGVtXG4gICAgICAgIGNvbnN0IHsgZXJyb3JzOiBwYXRoRXJyb3JzLCBleHBhbmRlZFBhdGhzIH0gPSB2YWxpZGF0ZVBhdGhPcHRpb25zKG9wdGlvbnMpO1xuXG4gICAgICAgIGlmIChwYXRoRXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGVycm9yTWVzc2FnZXMgPSBwYXRoRXJyb3JzXG4gICAgICAgICAgICAgICAgLm1hcCgoeyBvcHRpb24sIHBhdGggfTogVFBhdGhWYWxpZGF0aW9uRXJyb3IpID0+IGAgICR7b3B0aW9ufTogJHtwYXRofWApXG4gICAgICAgICAgICAgICAgLmpvaW4oXCJcXG5cIik7XG5cbiAgICAgICAgICAgIHJlbmRlcihcbiAgICAgICAgICAgICAgICBFcnJvcih7XG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgZm9sbG93aW5nIHBhdGhzIGRvIG5vdCBleGlzdDpcXG4ke2Vycm9yTWVzc2FnZXN9YCxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmVwbGFjZSBvcmlnaW5hbCBwYXRocyB3aXRoIGV4cGFuZGVkIHBhdGhzICh+IHJlc29sdmVkIHRvIGhvbWUgZGlyZWN0b3J5KVxuICAgICAgICBjb25zdCB2YWxpZGF0ZWRPcHRpb25zOiBDb25uZWN0aW9uT3B0aW9ucyA9IHtcbiAgICAgICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgICAgICAuLi5leHBhbmRlZFBhdGhzLFxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChoYXNJbmRpdmlkdWFsT3B0aW9ucykge1xuICAgICAgICAgICAgY29uc3QgcmVxdWlyZWRPcHRpb25zID0gW1xuICAgICAgICAgICAgICAgIHsga2V5OiBcImhvc3RuYW1lXCIsIGZsYWc6IFwiLWgvLS1ob3N0bmFtZVwiIH0sXG4gICAgICAgICAgICAgICAgeyBrZXk6IFwicG9ydFwiLCBmbGFnOiBcIi1wLy0tcG9ydFwiIH0sXG4gICAgICAgICAgICAgICAgeyBrZXk6IFwiZGF0YWJhc2VcIiwgZmxhZzogXCItZC8tLWRhdGFiYXNlXCIgfSxcbiAgICAgICAgICAgICAgICB7IGtleTogXCJ1c2VybmFtZVwiLCBmbGFnOiBcIi1VLy0tdXNlcm5hbWVcIiB9LFxuICAgICAgICAgICAgXSBhcyBjb25zdDtcblxuICAgICAgICAgICAgY29uc3QgbWlzc2luZyA9IHJlcXVpcmVkT3B0aW9uc1xuICAgICAgICAgICAgICAgIC5maWx0ZXIoKHsga2V5IH0pID0+ICF2YWxpZGF0ZWRPcHRpb25zW2tleV0pXG4gICAgICAgICAgICAgICAgLm1hcCgoeyBmbGFnIH0pID0+IGZsYWcpO1xuXG4gICAgICAgICAgICBpZiAobWlzc2luZy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgcmVuZGVyKFxuICAgICAgICAgICAgICAgICAgICBFcnJvcih7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgTWlzc2luZyByZXF1aXJlZCBvcHRpb25zOiAke21pc3Npbmcuam9pbihcIiwgXCIpfWAsXG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEFsbCB2YWxpZGF0aW9ucyBwYXNzZWQgLSBidWlsZCBjb25maWcgYW5kIHJlbmRlciB0aGUgYXBwXG4gICAgICAgIGNvbnN0IGNvbmZpZzogVENvbm5lY3Rpb25Db25maWcgPSB7XG4gICAgICAgICAgICBjb25uZWN0aW9uU3RyaW5nLFxuICAgICAgICAgICAgLi4udmFsaWRhdGVkT3B0aW9ucyxcbiAgICAgICAgfTtcblxuICAgICAgICByZW5kZXIoQXBwKHsgY29uZmlnIH0pKTtcbiAgICB9KTtcblxucHJvZ3JhbS5wYXJzZSgpO1xuIl19
@@ -0,0 +1,3 @@
1
+ import winston from 'winston';
2
+ export declare const logger: winston.Logger;
3
+ //# sourceMappingURL=logger.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.mts","sourceRoot":"./src/","sources":["logger.mts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,eAAO,MAAM,MAAM,gBAOjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import winston from 'winston';
2
+ export const logger = winston.createLogger({
3
+ level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
4
+ format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
5
+ transports: [new winston.transports.Console()],
6
+ });
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLm1qcyIsInNvdXJjZVJvb3QiOiIuL3NyYy8iLCJzb3VyY2VzIjpbImxvZ2dlci5tdHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxPQUFPLE1BQU0sU0FBUyxDQUFDO0FBRTlCLE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDO0lBQ3ZDLEtBQUssRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxhQUFhLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTTtJQUNoRSxNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQzFCLE9BQU8sQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLEVBQzFCLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQ3hCO0lBQ0QsVUFBVSxFQUFFLENBQUMsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxDQUFDO0NBQ2pELENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB3aW5zdG9uIGZyb20gJ3dpbnN0b24nO1xuXG5leHBvcnQgY29uc3QgbG9nZ2VyID0gd2luc3Rvbi5jcmVhdGVMb2dnZXIoe1xuICAgIGxldmVsOiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50JyA/ICdkZWJ1ZycgOiAnaW5mbycsXG4gICAgZm9ybWF0OiB3aW5zdG9uLmZvcm1hdC5jb21iaW5lKFxuICAgICAgICB3aW5zdG9uLmZvcm1hdC50aW1lc3RhbXAoKSxcbiAgICAgICAgd2luc3Rvbi5mb3JtYXQuanNvbigpLFxuICAgICksXG4gICAgdHJhbnNwb3J0czogW25ldyB3aW5zdG9uLnRyYW5zcG9ydHMuQ29uc29sZSgpXSxcbn0pO1xuIl19
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"./src/","sources":["schemas/index.mts"],"names":[],"mappings":""}
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod';
2
+ const relationAttribute = z
3
+ .object({
4
+ name: z.string(),
5
+ data_type: z.string(),
6
+ is_nullable: z.boolean(),
7
+ comment: z.string().nullable(),
8
+ })
9
+ .and(z.discriminatedUnion('kind', [
10
+ z.discriminatedUnion('has_default', [
11
+ z.object({
12
+ kind: z.literal('regular'),
13
+ has_default: z.literal(false),
14
+ }),
15
+ z.object({
16
+ kind: z.literal('regular'),
17
+ has_default: z.literal(true),
18
+ default_expression: z.string(),
19
+ }),
20
+ ]),
21
+ z.object({
22
+ kind: z.literal('generated'),
23
+ generated_storage: z.enum(['stored', 'virtual']),
24
+ generated_expression: z.string(),
25
+ }),
26
+ ]));
27
+ const indexInfo = z
28
+ .object({
29
+ index_name: z.string(),
30
+ relation_name: z.string(),
31
+ attributes: z.array(z.string()),
32
+ definition: z.string(),
33
+ comment: z.string().nullable(),
34
+ })
35
+ .and(z.discriminatedUnion('index_kind', [
36
+ z.object({ index_kind: z.literal('primary') }),
37
+ z.object({ index_kind: z.literal('unique') }),
38
+ z.object({
39
+ index_kind: z.literal('exclusion'),
40
+ exclusion_operators: z.array(z.string()),
41
+ }),
42
+ z.object({ index_kind: z.literal('plain') }),
43
+ ]))
44
+ .and(z.discriminatedUnion('is_partial', [
45
+ z.object({ is_partial: z.literal(false) }),
46
+ z.object({
47
+ is_partial: z.literal(true),
48
+ partial_predicate: z.string(),
49
+ }),
50
+ ]));
51
+ const reference = z.object({
52
+ constraint_name: z.string(),
53
+ attributes: z.array(z.string()),
54
+ referenced_relation: z.string(),
55
+ referenced_attributes: z.array(z.string()),
56
+ on_update: z.enum([
57
+ 'NO ACTION',
58
+ 'RESTRICT',
59
+ 'CASCADE',
60
+ 'SET NULL',
61
+ 'SET DEFAULT',
62
+ ]),
63
+ on_delete: z.enum([
64
+ 'NO ACTION',
65
+ 'RESTRICT',
66
+ 'CASCADE',
67
+ 'SET NULL',
68
+ 'SET DEFAULT',
69
+ ]),
70
+ comment: z.string().nullable(),
71
+ });
72
+ const relation = z.object({
73
+ name: z.string(),
74
+ comment: z.string().nullable(),
75
+ kind: z.enum(['partitioned_table', 'table', 'view', 'materialized_view']),
76
+ summary: z.string().optional(),
77
+ details: z.string().optional(),
78
+ attributes: z.record(z.string(), relationAttribute),
79
+ references: z.record(z.string(), reference),
80
+ indexes: z.record(z.string(), indexInfo),
81
+ });
82
+ const relations = z.record(z.string(), relation);
83
+ const database = z.object({
84
+ type: z.enum(['postgresql']),
85
+ name: z.string(),
86
+ comment: z.string().nullable(),
87
+ summary: z.string().optional(),
88
+ details: z.string().optional(),
89
+ relations: relations,
90
+ });
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgubWpzIiwic291cmNlUm9vdCI6Ii4vc3JjLyIsInNvdXJjZXMiOlsic2NoZW1hcy9pbmRleC5tdHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLENBQUMsRUFBQyxNQUFNLEtBQUssQ0FBQztBQUV0QixNQUFNLGlCQUFpQixHQUFHLENBQUM7S0FDdEIsTUFBTSxDQUFDO0lBQ0osSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7SUFDaEIsU0FBUyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7SUFDckIsV0FBVyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUU7SUFDeEIsT0FBTyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDakMsQ0FBQztLQUNELEdBQUcsQ0FDQSxDQUFDLENBQUMsa0JBQWtCLENBQUMsTUFBTSxFQUFFO0lBQ3pCLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxhQUFhLEVBQUU7UUFDaEMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztZQUNMLElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQztZQUMxQixXQUFXLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7U0FDaEMsQ0FBQztRQUNGLENBQUMsQ0FBQyxNQUFNLENBQUM7WUFDTCxJQUFJLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUM7WUFDMUIsV0FBVyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQzVCLGtCQUFrQixFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7U0FDakMsQ0FBQztLQUNMLENBQUM7SUFDRixDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ0wsSUFBSSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDO1FBQzVCLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDaEQsb0JBQW9C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UUFBUSxFQUFFO0lBQzlCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzlCLFVBQVUsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQztJQUNuRCxVQUFVLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsU0FBUyxDQUFDO0lBQzNDLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxTQUFTLENBQUM7Q0FDM0MsQ0FBQyxDQUFDO0FBRUgsTUFBTSxTQUFTLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFFakQsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN0QixJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzVCLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0lBQ2hCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzlCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzlCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzlCLFNBQVMsRUFBRSxTQUFTO0NBQ3ZCLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7en0gZnJvbSAnem9kJztcblxuY29uc3QgcmVsYXRpb25BdHRyaWJ1dGUgPSB6XG4gICAgLm9iamVjdCh7XG4gICAgICAgIG5hbWU6IHouc3RyaW5nKCksXG4gICAgICAgIGRhdGFfdHlwZTogei5zdHJpbmcoKSxcbiAgICAgICAgaXNfbnVsbGFibGU6IHouYm9vbGVhbigpLFxuICAgICAgICBjb21tZW50OiB6LnN0cmluZygpLm51bGxhYmxlKCksXG4gICAgfSlcbiAgICAuYW5kKFxuICAgICAgICB6LmRpc2NyaW1pbmF0ZWRVbmlvbigna2luZCcsIFtcbiAgICAgICAgICAgIHouZGlzY3JpbWluYXRlZFVuaW9uKCdoYXNfZGVmYXVsdCcsIFtcbiAgICAgICAgICAgICAgICB6Lm9iamVjdCh7XG4gICAgICAgICAgICAgICAgICAgIGtpbmQ6IHoubGl0ZXJhbCgncmVndWxhcicpLFxuICAgICAgICAgICAgICAgICAgICBoYXNfZGVmYXVsdDogei5saXRlcmFsKGZhbHNlKSxcbiAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICB6Lm9iamVjdCh7XG4gICAgICAgICAgICAgICAgICAgIGtpbmQ6IHoubGl0ZXJhbCgncmVndWxhcicpLFxuICAgICAgICAgICAgICAgICAgICBoYXNfZGVmYXVsdDogei5saXRlcmFsKHRydWUpLFxuICAgICAgICAgICAgICAgICAgICBkZWZhdWx0X2V4cHJlc3Npb246IHouc3RyaW5nKCksXG4gICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICBdKSxcbiAgICAgICAgICAgIHoub2JqZWN0KHtcbiAgICAgICAgICAgICAgICBraW5kOiB6LmxpdGVyYWwoJ2dlbmVyYXRlZCcpLFxuICAgICAgICAgICAgICAgIGdlbmVyYXRlZF9zdG9yYWdlOiB6LmVudW0oWydzdG9yZWQnLCAndmlydHVhbCddKSxcbiAgICAgICAgICAgICAgICBnZW5lcmF0ZWRfZXhwcmVzc2lvbjogei5zdHJpbmcoKSxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICBdKSxcbiAgICApO1xuXG5jb25zdCBpbmRleEluZm8gPSB6XG4gICAgLm9iamVjdCh7XG4gICAgICAgIGluZGV4X25hbWU6IHouc3RyaW5nKCksXG4gICAgICAgIHJlbGF0aW9uX25hbWU6IHouc3RyaW5nKCksXG4gICAgICAgIGF0dHJpYnV0ZXM6IHouYXJyYXkoei5zdHJpbmcoKSksXG4gICAgICAgIGRlZmluaXRpb246IHouc3RyaW5nKCksXG4gICAgICAgIGNvbW1lbnQ6IHouc3RyaW5nKCkubnVsbGFibGUoKSxcbiAgICB9KVxuICAgIC5hbmQoXG4gICAgICAgIHouZGlzY3JpbWluYXRlZFVuaW9uKCdpbmRleF9raW5kJywgW1xuICAgICAgICAgICAgei5vYmplY3QoeyBpbmRleF9raW5kOiB6LmxpdGVyYWwoJ3ByaW1hcnknKSB9KSxcbiAgICAgICAgICAgIHoub2JqZWN0KHsgaW5kZXhfa2luZDogei5saXRlcmFsKCd1bmlxdWUnKSB9KSxcbiAgICAgICAgICAgIHoub2JqZWN0KHtcbiAgICAgICAgICAgICAgICBpbmRleF9raW5kOiB6LmxpdGVyYWwoJ2V4Y2x1c2lvbicpLFxuICAgICAgICAgICAgICAgIGV4Y2x1c2lvbl9vcGVyYXRvcnM6IHouYXJyYXkoei5zdHJpbmcoKSksXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHoub2JqZWN0KHsgaW5kZXhfa2luZDogei5saXRlcmFsKCdwbGFpbicpIH0pLFxuICAgICAgICBdKSxcbiAgICApXG4gICAgLmFuZChcbiAgICAgICAgei5kaXNjcmltaW5hdGVkVW5pb24oJ2lzX3BhcnRpYWwnLCBbXG4gICAgICAgICAgICB6Lm9iamVjdCh7IGlzX3BhcnRpYWw6IHoubGl0ZXJhbChmYWxzZSkgfSksXG4gICAgICAgICAgICB6Lm9iamVjdCh7XG4gICAgICAgICAgICAgICAgaXNfcGFydGlhbDogei5saXRlcmFsKHRydWUpLFxuICAgICAgICAgICAgICAgIHBhcnRpYWxfcHJlZGljYXRlOiB6LnN0cmluZygpLFxuICAgICAgICAgICAgfSksXG4gICAgICAgIF0pLFxuICAgICk7XG5cbmNvbnN0IHJlZmVyZW5jZSA9IHoub2JqZWN0KHtcbiAgICBjb25zdHJhaW50X25hbWU6IHouc3RyaW5nKCksXG4gICAgYXR0cmlidXRlczogei5hcnJheSh6LnN0cmluZygpKSxcbiAgICByZWZlcmVuY2VkX3JlbGF0aW9uOiB6LnN0cmluZygpLFxuICAgIHJlZmVyZW5jZWRfYXR0cmlidXRlczogei5hcnJheSh6LnN0cmluZygpKSxcbiAgICBvbl91cGRhdGU6IHouZW51bShbXG4gICAgICAgICdOTyBBQ1RJT04nLFxuICAgICAgICAnUkVTVFJJQ1QnLFxuICAgICAgICAnQ0FTQ0FERScsXG4gICAgICAgICdTRVQgTlVMTCcsXG4gICAgICAgICdTRVQgREVGQVVMVCcsXG4gICAgXSksXG4gICAgb25fZGVsZXRlOiB6LmVudW0oW1xuICAgICAgICAnTk8gQUNUSU9OJyxcbiAgICAgICAgJ1JFU1RSSUNUJyxcbiAgICAgICAgJ0NBU0NBREUnLFxuICAgICAgICAnU0VUIE5VTEwnLFxuICAgICAgICAnU0VUIERFRkFVTFQnLFxuICAgIF0pLFxuICAgIGNvbW1lbnQ6IHouc3RyaW5nKCkubnVsbGFibGUoKSxcbn0pO1xuXG5jb25zdCByZWxhdGlvbiA9IHoub2JqZWN0KHtcbiAgICBuYW1lOiB6LnN0cmluZygpLFxuICAgIGNvbW1lbnQ6IHouc3RyaW5nKCkubnVsbGFibGUoKSxcbiAgICBraW5kOiB6LmVudW0oWydwYXJ0aXRpb25lZF90YWJsZScsICd0YWJsZScsICd2aWV3JywgJ21hdGVyaWFsaXplZF92aWV3J10pLFxuICAgIHN1bW1hcnk6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICBkZXRhaWxzOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gICAgYXR0cmlidXRlczogei5yZWNvcmQoei5zdHJpbmcoKSwgcmVsYXRpb25BdHRyaWJ1dGUpLFxuICAgIHJlZmVyZW5jZXM6IHoucmVjb3JkKHouc3RyaW5nKCksIHJlZmVyZW5jZSksXG4gICAgaW5kZXhlczogei5yZWNvcmQoei5zdHJpbmcoKSwgaW5kZXhJbmZvKSxcbn0pO1xuXG5jb25zdCByZWxhdGlvbnMgPSB6LnJlY29yZCh6LnN0cmluZygpLCByZWxhdGlvbik7XG5cbmNvbnN0IGRhdGFiYXNlID0gei5vYmplY3Qoe1xuICAgIHR5cGU6IHouZW51bShbJ3Bvc3RncmVzcWwnXSksXG4gICAgbmFtZTogei5zdHJpbmcoKSxcbiAgICBjb21tZW50OiB6LnN0cmluZygpLm51bGxhYmxlKCksXG4gICAgc3VtbWFyeTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgIGRldGFpbHM6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICByZWxhdGlvbnM6IHJlbGF0aW9ucyxcbn0pO1xuIl19
@@ -0,0 +1,12 @@
1
+ type TPathOptionKey = "sslcert" | "sslkey" | "sslrootcert" | "sshPrivateKey";
2
+ export type TPathValidationError = {
3
+ option: string;
4
+ path: string;
5
+ error: string;
6
+ };
7
+ export declare const validatePathOptions: <T extends Partial<Record<TPathOptionKey, string>>>(options: T) => {
8
+ errors: TPathValidationError[];
9
+ expandedPaths: Partial<Record<TPathOptionKey, string>>;
10
+ };
11
+ export {};
12
+ //# sourceMappingURL=validatePaths.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validatePaths.d.mts","sourceRoot":"./src/","sources":["validatePaths.mts"],"names":[],"mappings":"AAQA,KAAK,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAE7E,MAAM,MAAM,oBAAoB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAyBF,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,EACjF,SAAS,CAAC,KACX;IAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAA;CAmC1F,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { resolve } from "node:path";
4
+ const expandTilde = (path) => path.startsWith("~/") || path === "~"
5
+ ? path.replace(/^~/, homedir())
6
+ : path;
7
+ const normalizePath = (path) => resolve(expandTilde(path));
8
+ const validatePathExists = (path) => {
9
+ const expandedPath = normalizePath(path);
10
+ return existsSync(expandedPath)
11
+ ? { valid: true, expandedPath }
12
+ : { valid: false, error: `Path does not exist: ${path}` };
13
+ };
14
+ const PATH_OPTION_LABELS = {
15
+ sslcert: "--sslcert",
16
+ sslkey: "--sslkey",
17
+ sslrootcert: "--sslrootcert",
18
+ sshPrivateKey: "--ssh-private-key",
19
+ };
20
+ export const validatePathOptions = (options) => {
21
+ const pathKeys = ["sslcert", "sslkey", "sslrootcert", "sshPrivateKey"];
22
+ const results = pathKeys
23
+ .filter((key) => options[key] !== undefined)
24
+ .map((key) => {
25
+ const path = options[key];
26
+ const result = validatePathExists(path);
27
+ return {
28
+ key,
29
+ path,
30
+ result,
31
+ };
32
+ });
33
+ const errors = results
34
+ .filter((r) => !r.result.valid)
35
+ .map((r) => ({
36
+ option: PATH_OPTION_LABELS[r.key],
37
+ path: r.path,
38
+ error: r.result.error,
39
+ }));
40
+ const expandedPaths = results
41
+ .filter((r) => r.result.valid)
42
+ .reduce((acc, r) => ({
43
+ ...acc,
44
+ [r.key]: r.result.expandedPath,
45
+ }), {});
46
+ return { errors, expandedPaths };
47
+ };
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGVQYXRocy5tanMiLCJzb3VyY2VSb290IjoiLi9zcmMvIiwic291cmNlcyI6WyJ2YWxpZGF0ZVBhdGhzLm10cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQWNwQyxNQUFNLFdBQVcsR0FBRyxDQUFDLElBQVksRUFBVSxFQUFFLENBQ3pDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxLQUFLLEdBQUc7SUFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDO0lBQy9CLENBQUMsQ0FBQyxJQUFJLENBQUM7QUFFZixNQUFNLGFBQWEsR0FBRyxDQUFDLElBQVksRUFBVSxFQUFFLENBQzNDLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztBQUUvQixNQUFNLGtCQUFrQixHQUFHLENBQUMsSUFBWSxFQUF5QixFQUFFO0lBQy9ELE1BQU0sWUFBWSxHQUFHLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUV6QyxPQUFPLFVBQVUsQ0FBQyxZQUFZLENBQUM7UUFDM0IsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUU7UUFDL0IsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsd0JBQXdCLElBQUksRUFBRSxFQUFFLENBQUM7QUFDbEUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxrQkFBa0IsR0FBbUM7SUFDdkQsT0FBTyxFQUFFLFdBQVc7SUFDcEIsTUFBTSxFQUFFLFVBQVU7SUFDbEIsV0FBVyxFQUFFLGVBQWU7SUFDNUIsYUFBYSxFQUFFLG1CQUFtQjtDQUNyQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsQ0FDL0IsT0FBVSxFQUNnRixFQUFFO0lBQzVGLE1BQU0sUUFBUSxHQUFxQixDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsYUFBYSxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBRXpGLE1BQU0sT0FBTyxHQUFHLFFBQVE7U0FDbkIsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssU0FBUyxDQUFDO1NBQzNDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQ1QsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBVyxDQUFDO1FBQ3BDLE1BQU0sTUFBTSxHQUFHLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXhDLE9BQU87WUFDSCxHQUFHO1lBQ0gsSUFBSTtZQUNKLE1BQU07U0FDVCxDQUFDO0lBQ04sQ0FBQyxDQUFDLENBQUM7SUFFUCxNQUFNLE1BQU0sR0FBRyxPQUFPO1NBQ2pCLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUM5QixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDVCxNQUFNLEVBQUUsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUNqQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUk7UUFDWixLQUFLLEVBQUcsQ0FBQyxDQUFDLE1BQTBDLENBQUMsS0FBSztLQUM3RCxDQUFDLENBQUMsQ0FBQztJQUVSLE1BQU0sYUFBYSxHQUFHLE9BQU87U0FDeEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUM3QixNQUFNLENBQ0gsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ1QsR0FBRyxHQUFHO1FBQ04sQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUcsQ0FBQyxDQUFDLE1BQWdELENBQUMsWUFBWTtLQUM1RSxDQUFDLEVBQ0YsRUFBNkMsQ0FDaEQsQ0FBQztJQUVOLE9BQU8sRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLENBQUM7QUFDckMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhpc3RzU3luYyB9IGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgeyBob21lZGlyIH0gZnJvbSBcIm5vZGU6b3NcIjtcbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5cbnR5cGUgVFBhdGhWYWxpZGF0aW9uUmVzdWx0ID1cbiAgICB8IHsgdmFsaWQ6IHRydWU7IGV4cGFuZGVkUGF0aDogc3RyaW5nIH1cbiAgICB8IHsgdmFsaWQ6IGZhbHNlOyBlcnJvcjogc3RyaW5nIH07XG5cbnR5cGUgVFBhdGhPcHRpb25LZXkgPSBcInNzbGNlcnRcIiB8IFwic3Nsa2V5XCIgfCBcInNzbHJvb3RjZXJ0XCIgfCBcInNzaFByaXZhdGVLZXlcIjtcblxuZXhwb3J0IHR5cGUgVFBhdGhWYWxpZGF0aW9uRXJyb3IgPSB7XG4gICAgb3B0aW9uOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIGVycm9yOiBzdHJpbmc7XG59O1xuXG5jb25zdCBleHBhbmRUaWxkZSA9IChwYXRoOiBzdHJpbmcpOiBzdHJpbmcgPT5cbiAgICBwYXRoLnN0YXJ0c1dpdGgoXCJ+L1wiKSB8fCBwYXRoID09PSBcIn5cIlxuICAgICAgICA/IHBhdGgucmVwbGFjZSgvXn4vLCBob21lZGlyKCkpXG4gICAgICAgIDogcGF0aDtcblxuY29uc3Qgbm9ybWFsaXplUGF0aCA9IChwYXRoOiBzdHJpbmcpOiBzdHJpbmcgPT5cbiAgICByZXNvbHZlKGV4cGFuZFRpbGRlKHBhdGgpKTtcblxuY29uc3QgdmFsaWRhdGVQYXRoRXhpc3RzID0gKHBhdGg6IHN0cmluZyk6IFRQYXRoVmFsaWRhdGlvblJlc3VsdCA9PiB7XG4gICAgY29uc3QgZXhwYW5kZWRQYXRoID0gbm9ybWFsaXplUGF0aChwYXRoKTtcblxuICAgIHJldHVybiBleGlzdHNTeW5jKGV4cGFuZGVkUGF0aClcbiAgICAgICAgPyB7IHZhbGlkOiB0cnVlLCBleHBhbmRlZFBhdGggfVxuICAgICAgICA6IHsgdmFsaWQ6IGZhbHNlLCBlcnJvcjogYFBhdGggZG9lcyBub3QgZXhpc3Q6ICR7cGF0aH1gIH07XG59O1xuXG5jb25zdCBQQVRIX09QVElPTl9MQUJFTFM6IFJlY29yZDxUUGF0aE9wdGlvbktleSwgc3RyaW5nPiA9IHtcbiAgICBzc2xjZXJ0OiBcIi0tc3NsY2VydFwiLFxuICAgIHNzbGtleTogXCItLXNzbGtleVwiLFxuICAgIHNzbHJvb3RjZXJ0OiBcIi0tc3Nscm9vdGNlcnRcIixcbiAgICBzc2hQcml2YXRlS2V5OiBcIi0tc3NoLXByaXZhdGUta2V5XCIsXG59O1xuXG5leHBvcnQgY29uc3QgdmFsaWRhdGVQYXRoT3B0aW9ucyA9IDxUIGV4dGVuZHMgUGFydGlhbDxSZWNvcmQ8VFBhdGhPcHRpb25LZXksIHN0cmluZz4+PihcbiAgICBvcHRpb25zOiBUXG4pOiB7IGVycm9yczogVFBhdGhWYWxpZGF0aW9uRXJyb3JbXTsgZXhwYW5kZWRQYXRoczogUGFydGlhbDxSZWNvcmQ8VFBhdGhPcHRpb25LZXksIHN0cmluZz4+IH0gPT4ge1xuICAgIGNvbnN0IHBhdGhLZXlzOiBUUGF0aE9wdGlvbktleVtdID0gW1wic3NsY2VydFwiLCBcInNzbGtleVwiLCBcInNzbHJvb3RjZXJ0XCIsIFwic3NoUHJpdmF0ZUtleVwiXTtcblxuICAgIGNvbnN0IHJlc3VsdHMgPSBwYXRoS2V5c1xuICAgICAgICAuZmlsdGVyKChrZXkpID0+IG9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkKVxuICAgICAgICAubWFwKChrZXkpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBhdGggPSBvcHRpb25zW2tleV0gYXMgc3RyaW5nO1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gdmFsaWRhdGVQYXRoRXhpc3RzKHBhdGgpO1xuXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgICAgICBwYXRoLFxuICAgICAgICAgICAgICAgIHJlc3VsdCxcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuXG4gICAgY29uc3QgZXJyb3JzID0gcmVzdWx0c1xuICAgICAgICAuZmlsdGVyKChyKSA9PiAhci5yZXN1bHQudmFsaWQpXG4gICAgICAgIC5tYXAoKHIpID0+ICh7XG4gICAgICAgICAgICBvcHRpb246IFBBVEhfT1BUSU9OX0xBQkVMU1tyLmtleV0sXG4gICAgICAgICAgICBwYXRoOiByLnBhdGgsXG4gICAgICAgICAgICBlcnJvcjogKHIucmVzdWx0IGFzIHsgdmFsaWQ6IGZhbHNlOyBlcnJvcjogc3RyaW5nIH0pLmVycm9yLFxuICAgICAgICB9KSk7XG5cbiAgICBjb25zdCBleHBhbmRlZFBhdGhzID0gcmVzdWx0c1xuICAgICAgICAuZmlsdGVyKChyKSA9PiByLnJlc3VsdC52YWxpZClcbiAgICAgICAgLnJlZHVjZShcbiAgICAgICAgICAgIChhY2MsIHIpID0+ICh7XG4gICAgICAgICAgICAgICAgLi4uYWNjLFxuICAgICAgICAgICAgICAgIFtyLmtleV06IChyLnJlc3VsdCBhcyB7IHZhbGlkOiB0cnVlOyBleHBhbmRlZFBhdGg6IHN0cmluZyB9KS5leHBhbmRlZFBhdGgsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHt9IGFzIFBhcnRpYWw8UmVjb3JkPFRQYXRoT3B0aW9uS2V5LCBzdHJpbmc+PlxuICAgICAgICApO1xuXG4gICAgcmV0dXJuIHsgZXJyb3JzLCBleHBhbmRlZFBhdGhzIH07XG59O1xuIl19
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "dbctx",
3
+ "version": "1.0.0",
4
+ "description": "dbctx CLI",
5
+ "type": "module",
6
+ "main": "dist/index.mjs",
7
+ "bin": {
8
+ "dbctx": "./dist/index.mjs"
9
+ },
10
+ "scripts": {
11
+ "start": "tsc && node ./dist/index.mjs",
12
+ "dev": "tsc --watch --preserveWatchOutput",
13
+ "build": "tsc",
14
+ "build:ts:watch": "tsc --watch --preserveWatchOutput"
15
+ },
16
+ "keywords": [],
17
+ "author": {
18
+ "name": "AirState",
19
+ "email": "hello@airstate.dev",
20
+ "url": "https://airstate.dev"
21
+ },
22
+ "license": "UNLICENSED",
23
+ "dependencies": {
24
+ "commander": "14.0.3",
25
+ "es-toolkit": "1.44.0",
26
+ "ink": "6.6.0",
27
+ "pg": "8.18.0",
28
+ "react": "19.2.4",
29
+ "winston": "3.19.0",
30
+ "zod": "4.3.6"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "24.0.0",
34
+ "@types/pg": "8.16.0",
35
+ "@types/react": "19.2.10",
36
+ "concurrently": "9.2.1",
37
+ "cross-env": "7.0.3",
38
+ "typescript": "5.9.3"
39
+ },
40
+ "volta": {
41
+ "node": "24.0.0"
42
+ }
43
+ }
package/src/App.tsx ADDED
@@ -0,0 +1,44 @@
1
+ import { Text, Box } from "ink";
2
+ import type { ReactNode } from "react";
3
+ import type { TSSLMode } from "./index.mjs";
4
+
5
+ export type TConnectionConfig = {
6
+ connectionString?: string;
7
+ hostname?: string;
8
+ port?: number;
9
+ database?: string;
10
+ username?: string;
11
+ sslmode?: TSSLMode;
12
+ sslcert?: string;
13
+ sslkey?: string;
14
+ sslrootcert?: string;
15
+ sshHost?: string;
16
+ sshPort?: number;
17
+ sshUsername?: string;
18
+ sshPrivateKey?: string;
19
+ };
20
+
21
+ type TAppProps = {
22
+ config: TConnectionConfig;
23
+ };
24
+
25
+ export function App({ config }: TAppProps): ReactNode {
26
+ const connectionTarget = config.connectionString
27
+ ? config.connectionString.replace(/:[^:@]+@/, ":***@") // mask password
28
+ : `${config.hostname}:${config.port}/${config.database}`;
29
+
30
+ return (
31
+ <Box flexDirection="column" padding={1}>
32
+ <Box marginBottom={1}>
33
+ <Text color="cyan" bold>
34
+ dbctx
35
+ </Text>
36
+ <Text dimColor> - AI generated docs from your application database</Text>
37
+ </Box>
38
+ <Box>
39
+ <Text dimColor>Connecting to: </Text>
40
+ <Text color="yellow">{connectionTarget}</Text>
41
+ </Box>
42
+ </Box>
43
+ );
44
+ }
@@ -0,0 +1,16 @@
1
+ import { Text, Box } from "ink";
2
+ import type { ReactNode } from "react";
3
+
4
+ interface ErrorProps {
5
+ message: string;
6
+ }
7
+
8
+ export function Error({ message }: ErrorProps): ReactNode {
9
+ return (
10
+ <Box>
11
+ <Text color="red" bold>
12
+ Error: {message}
13
+ </Text>
14
+ </Box>
15
+ );
16
+ }
@@ -0,0 +1,22 @@
1
+ import type { Pool } from "pg";
2
+
3
+ export const analyzeRelation = async (pool: Pool, relationName: string): Promise<void> => {
4
+ // Validate relation exists and get properly quoted name to prevent SQL injection
5
+ const validation = await pool.query<{ quoted_name: string }>(
6
+ `
7
+ SELECT format('%I', c.relname) AS quoted_name
8
+ FROM pg_catalog.pg_class c
9
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10
+ WHERE n.nspname = 'public'
11
+ AND c.relname = $1
12
+ AND c.relkind IN ('r', 'p', 'm')
13
+ `,
14
+ [relationName]
15
+ );
16
+
17
+ if (validation.rows.length === 0) {
18
+ throw new Error(`Relation "${relationName}" not found in public schema`);
19
+ }
20
+
21
+ await pool.query(`ANALYZE public.${validation.rows[0].quoted_name}`);
22
+ };
@@ -0,0 +1,58 @@
1
+ import type { Pool } from "pg";
2
+
3
+ export type TAttributeKind = "regular" | "generated";
4
+
5
+ export type TGeneratedStorage = "stored" | "virtual";
6
+
7
+ export type TAttributeInfo = {
8
+ attribute_name: string;
9
+ data_type: string;
10
+ is_nullable: boolean;
11
+ has_default: boolean;
12
+ default_expression: string | null;
13
+ kind: TAttributeKind;
14
+ generated_storage: TGeneratedStorage | null;
15
+ generated_expression: string | null;
16
+ comment: string | null;
17
+ };
18
+
19
+ export const fetchRelationAttributes = async (pool: Pool, relationName: string): Promise<TAttributeInfo[]> => {
20
+ const result = await pool.query<TAttributeInfo>(
21
+ `
22
+ SELECT
23
+ a.attname AS attribute_name,
24
+ format_type(a.atttypid, a.atttypemod) AS data_type,
25
+ NOT a.attnotnull AS is_nullable,
26
+ ad.adbin IS NOT NULL AS has_default,
27
+ CASE
28
+ WHEN a.attgenerated = '' AND ad.adbin IS NOT NULL THEN pg_get_expr(ad.adbin, ad.adrelid)
29
+ ELSE NULL
30
+ END AS default_expression,
31
+ CASE a.attgenerated
32
+ WHEN '' THEN 'regular'
33
+ ELSE 'generated'
34
+ END AS kind,
35
+ CASE a.attgenerated
36
+ WHEN 's' THEN 'stored'
37
+ WHEN 'v' THEN 'virtual'
38
+ ELSE NULL
39
+ END AS generated_storage,
40
+ CASE
41
+ WHEN a.attgenerated != '' THEN pg_get_expr(ad.adbin, ad.adrelid)
42
+ ELSE NULL
43
+ END AS generated_expression,
44
+ col_description(c.oid, a.attnum) AS comment
45
+ FROM pg_catalog.pg_class c
46
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
47
+ LEFT JOIN pg_catalog.pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum
48
+ WHERE c.relname = $1
49
+ AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
50
+ AND a.attnum > 0
51
+ AND NOT a.attisdropped
52
+ ORDER BY a.attnum
53
+ `,
54
+ [relationName]
55
+ );
56
+
57
+ return result.rows;
58
+ };
@@ -0,0 +1,43 @@
1
+ import type { Pool } from "pg";
2
+ import { groupBy } from "es-toolkit";
3
+
4
+ type TEnumValueRow = {
5
+ enum_name: string;
6
+ value: string;
7
+ sort_order: number;
8
+ comment: string | null;
9
+ };
10
+
11
+ export type TEnumInfo = {
12
+ values: string[];
13
+ comment: string | null;
14
+ };
15
+
16
+ export type TGroupedEnums = Record<string, TEnumInfo>;
17
+
18
+ export const fetchPublicEnums = async (pool: Pool): Promise<TGroupedEnums> => {
19
+ const result = await pool.query<TEnumValueRow>(`
20
+ SELECT
21
+ t.typname AS enum_name,
22
+ e.enumlabel AS value,
23
+ e.enumsortorder AS sort_order,
24
+ obj_description(t.oid, 'pg_type') AS comment
25
+ FROM pg_catalog.pg_type t
26
+ JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid
27
+ JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
28
+ WHERE n.nspname = 'public'
29
+ ORDER BY enum_name, sort_order
30
+ `);
31
+
32
+ const grouped = groupBy(result.rows, (row) => row.enum_name);
33
+
34
+ return Object.fromEntries(
35
+ Object.entries(grouped).map(([name, rows]) => [
36
+ name,
37
+ {
38
+ values: rows.map((r) => r.value),
39
+ comment: rows[0]?.comment ?? null,
40
+ },
41
+ ])
42
+ );
43
+ };
@@ -0,0 +1,57 @@
1
+ import type { Pool } from "pg";
2
+
3
+ export type TRelationFileStats = {
4
+ relation_name: string;
5
+ file_path: string | null;
6
+ modification_time: Date;
7
+ size_bytes: number;
8
+ is_partitioned: boolean;
9
+ partition_count: number | null;
10
+ };
11
+
12
+ export const fetchRelationFileStats = async (pool: Pool, relationName: string): Promise<TRelationFileStats> => {
13
+ const result = await pool.query<TRelationFileStats>(
14
+ `
15
+ WITH target AS (
16
+ SELECT c.oid, c.relname, c.relkind
17
+ FROM pg_catalog.pg_class c
18
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
19
+ WHERE n.nspname = 'public'
20
+ AND c.relname = $1
21
+ AND c.relkind IN ('r', 'p', 'm')
22
+ ),
23
+ partition_stats AS (
24
+ SELECT
25
+ (pg_stat_file(pg_relation_filepath(child.oid))).modification AS modification_time,
26
+ (pg_stat_file(pg_relation_filepath(child.oid))).size AS size_bytes
27
+ FROM target t
28
+ JOIN pg_catalog.pg_inherits i ON i.inhparent = t.oid
29
+ JOIN pg_catalog.pg_class child ON child.oid = i.inhrelid
30
+ WHERE t.relkind = 'p'
31
+ )
32
+ SELECT
33
+ t.relname AS relation_name,
34
+ CASE
35
+ WHEN t.relkind = 'p' THEN NULL
36
+ ELSE pg_relation_filepath(t.oid)
37
+ END AS file_path,
38
+ CASE
39
+ WHEN t.relkind = 'p' THEN (SELECT MAX(modification_time) FROM partition_stats)
40
+ ELSE (pg_stat_file(pg_relation_filepath(t.oid))).modification
41
+ END AS modification_time,
42
+ CASE
43
+ WHEN t.relkind = 'p' THEN (SELECT COALESCE(SUM(size_bytes), 0) FROM partition_stats)
44
+ ELSE (pg_stat_file(pg_relation_filepath(t.oid))).size
45
+ END AS size_bytes,
46
+ t.relkind = 'p' AS is_partitioned,
47
+ CASE
48
+ WHEN t.relkind = 'p' THEN (SELECT COUNT(*) FROM partition_stats)
49
+ ELSE NULL
50
+ END AS partition_count
51
+ FROM target t
52
+ `,
53
+ [relationName]
54
+ );
55
+
56
+ return result.rows[0];
57
+ };