frg-data-diff 2.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 (62) hide show
  1. package/README.md +628 -0
  2. package/dist/apply/apply-diff.d.ts +24 -0
  3. package/dist/apply/apply-diff.js +205 -0
  4. package/dist/apply/conflict.d.ts +20 -0
  5. package/dist/apply/conflict.js +14 -0
  6. package/dist/apply/plan.d.ts +13 -0
  7. package/dist/apply/plan.js +37 -0
  8. package/dist/cli/apply.d.ts +8 -0
  9. package/dist/cli/apply.js +270 -0
  10. package/dist/cli/generator.d.ts +9 -0
  11. package/dist/cli/generator.js +804 -0
  12. package/dist/cli/pg-triggers.d.ts +2 -0
  13. package/dist/cli/pg-triggers.js +185 -0
  14. package/dist/cli/root.d.ts +8 -0
  15. package/dist/cli/root.js +231 -0
  16. package/dist/cli/sql.d.ts +9 -0
  17. package/dist/cli/sql.js +158 -0
  18. package/dist/config/config-schema.d.ts +380 -0
  19. package/dist/config/config-schema.js +95 -0
  20. package/dist/config/load-config.d.ts +12 -0
  21. package/dist/config/load-config.js +87 -0
  22. package/dist/config/resolve-options.d.ts +95 -0
  23. package/dist/config/resolve-options.js +183 -0
  24. package/dist/config/write-config.d.ts +18 -0
  25. package/dist/config/write-config.js +103 -0
  26. package/dist/db/connection.d.ts +28 -0
  27. package/dist/db/connection.js +34 -0
  28. package/dist/db/metadata.d.ts +70 -0
  29. package/dist/db/metadata.js +238 -0
  30. package/dist/db/pg-triggers.d.ts +27 -0
  31. package/dist/db/pg-triggers.js +59 -0
  32. package/dist/db/sql.d.ts +72 -0
  33. package/dist/db/sql.js +250 -0
  34. package/dist/diff/diff-schema.d.ts +380 -0
  35. package/dist/diff/diff-schema.js +67 -0
  36. package/dist/diff/generate-diff.d.ts +20 -0
  37. package/dist/diff/generate-diff.js +224 -0
  38. package/dist/diff/pg-triggers-diff.d.ts +11 -0
  39. package/dist/diff/pg-triggers-diff.js +161 -0
  40. package/dist/diff/serialize-value.d.ts +35 -0
  41. package/dist/diff/serialize-value.js +242 -0
  42. package/dist/diff/write-diff-yaml.d.ts +3 -0
  43. package/dist/diff/write-diff-yaml.js +48 -0
  44. package/dist/schema-diff/generate-schema-diff.d.ts +12 -0
  45. package/dist/schema-diff/generate-schema-diff.js +355 -0
  46. package/dist/schema-diff/generate-schema-sql.d.ts +20 -0
  47. package/dist/schema-diff/generate-schema-sql.js +187 -0
  48. package/dist/schema-diff/schema-diff-schema.d.ts +1088 -0
  49. package/dist/schema-diff/schema-diff-schema.js +70 -0
  50. package/dist/shared/env-values.d.ts +11 -0
  51. package/dist/shared/env-values.js +100 -0
  52. package/dist/shared/generator-wizard.d.ts +10 -0
  53. package/dist/shared/generator-wizard.js +337 -0
  54. package/dist/shared/identifiers.d.ts +24 -0
  55. package/dist/shared/identifiers.js +45 -0
  56. package/dist/shared/prompts.d.ts +26 -0
  57. package/dist/shared/prompts.js +104 -0
  58. package/dist/shared/summary.d.ts +33 -0
  59. package/dist/shared/summary.js +41 -0
  60. package/dist/sql/generate-sql.d.ts +19 -0
  61. package/dist/sql/generate-sql.js +223 -0
  62. package/package.json +39 -0
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schemaDiffJsonSchema = void 0;
4
+ exports.validateSchemaDiffJson = validateSchemaDiffJson;
5
+ const zod_1 = require("zod");
6
+ const schemaColumnDefinitionSchema = zod_1.z.object({
7
+ name: zod_1.z.string(),
8
+ type: zod_1.z.string(),
9
+ nullable: zod_1.z.boolean(),
10
+ default: zod_1.z.string().nullable(),
11
+ isGenerated: zod_1.z.boolean(),
12
+ generationExpression: zod_1.z.string().nullable(),
13
+ ordinalPosition: zod_1.z.number().int(),
14
+ });
15
+ const schemaColumnChangeSchema = zod_1.z.object({
16
+ column: zod_1.z.string(),
17
+ from: schemaColumnDefinitionSchema,
18
+ to: schemaColumnDefinitionSchema,
19
+ });
20
+ const schemaCreateTableSchema = zod_1.z.object({
21
+ columns: zod_1.z.array(schemaColumnDefinitionSchema),
22
+ primaryKey: zod_1.z.array(zod_1.z.string()),
23
+ });
24
+ const schemaPrimaryKeyChangeSchema = zod_1.z.object({
25
+ from: zod_1.z.array(zod_1.z.string()),
26
+ to: zod_1.z.array(zod_1.z.string()),
27
+ dropConstraintName: zod_1.z.string().nullable(),
28
+ });
29
+ const schemaTableDiffSchema = zod_1.z.object({
30
+ table: zod_1.z.string(),
31
+ schema: zod_1.z.string(),
32
+ sourceExists: zod_1.z.boolean(),
33
+ destExists: zod_1.z.boolean(),
34
+ createTable: schemaCreateTableSchema.nullable(),
35
+ dropTable: zod_1.z.boolean(),
36
+ addColumns: zod_1.z.array(schemaColumnDefinitionSchema),
37
+ alterColumns: zod_1.z.array(schemaColumnChangeSchema),
38
+ dropColumns: zod_1.z.array(schemaColumnDefinitionSchema),
39
+ primaryKeyChange: schemaPrimaryKeyChangeSchema.nullable(),
40
+ });
41
+ const schemaDiffSummarySchema = zod_1.z.object({
42
+ tablesCompared: zod_1.z.number().int(),
43
+ tablesToCreate: zod_1.z.number().int(),
44
+ tablesToDrop: zod_1.z.number().int(),
45
+ columnsToAdd: zod_1.z.number().int(),
46
+ columnsToAlter: zod_1.z.number().int(),
47
+ columnsToDrop: zod_1.z.number().int(),
48
+ primaryKeysToChange: zod_1.z.number().int(),
49
+ });
50
+ exports.schemaDiffJsonSchema = zod_1.z.object({
51
+ format: zod_1.z.literal("postgres-schema-diff-json/v1"),
52
+ generatedAt: zod_1.z.string(),
53
+ source: zod_1.z.object({ schema: zod_1.z.string() }),
54
+ dest: zod_1.z.object({ schema: zod_1.z.string() }),
55
+ options: zod_1.z.object({
56
+ tables: zod_1.z.array(zod_1.z.string()),
57
+ excludedTables: zod_1.z.array(zod_1.z.string()),
58
+ }),
59
+ tables: zod_1.z.array(schemaTableDiffSchema),
60
+ summary: schemaDiffSummarySchema,
61
+ });
62
+ function validateSchemaDiffJson(data) {
63
+ const result = exports.schemaDiffJsonSchema.safeParse(data);
64
+ if (!result.success) {
65
+ throw new Error("Invalid schema diff JSON format:\n" +
66
+ JSON.stringify(result.error.format(), null, 2));
67
+ }
68
+ return result.data;
69
+ }
70
+ //# sourceMappingURL=schema-diff-schema.js.map
@@ -0,0 +1,11 @@
1
+ export declare function isEnvReference(value: string): boolean;
2
+ export declare function extractEnvVarName(value: string): string | null;
3
+ export declare function formatEnvReferenceHelp(): string;
4
+ export declare function resolveListValues(values: string[], label: string, env?: NodeJS.ProcessEnv): string[];
5
+ export declare function resolveStringValue(value: string, label: string, env?: NodeJS.ProcessEnv): string;
6
+ export declare function resolveBooleanValue(value: boolean | string, label: string, env?: NodeJS.ProcessEnv): boolean;
7
+ export declare function resolveEnumValue<T extends string>(value: T | string, label: string, allowedValues: readonly T[], env?: NodeJS.ProcessEnv): T;
8
+ export declare function resolvePortValue(value: number | string, label: string, env?: NodeJS.ProcessEnv): number;
9
+ export declare function formatSecretValue(value: string): string;
10
+ export declare function formatVisibleValue(rawValue: string, resolvedValue: string): string;
11
+ //# sourceMappingURL=env-values.d.ts.map
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEnvReference = isEnvReference;
4
+ exports.extractEnvVarName = extractEnvVarName;
5
+ exports.formatEnvReferenceHelp = formatEnvReferenceHelp;
6
+ exports.resolveListValues = resolveListValues;
7
+ exports.resolveStringValue = resolveStringValue;
8
+ exports.resolveBooleanValue = resolveBooleanValue;
9
+ exports.resolveEnumValue = resolveEnumValue;
10
+ exports.resolvePortValue = resolvePortValue;
11
+ exports.formatSecretValue = formatSecretValue;
12
+ exports.formatVisibleValue = formatVisibleValue;
13
+ const ENV_REFERENCE_PATTERN = /^\$([A-Za-z_][A-Za-z0-9_]*)$/;
14
+ function isEnvReference(value) {
15
+ return ENV_REFERENCE_PATTERN.test(value);
16
+ }
17
+ function extractEnvVarName(value) {
18
+ const match = value.match(ENV_REFERENCE_PATTERN);
19
+ return match ? match[1] : null;
20
+ }
21
+ function formatEnvReferenceHelp() {
22
+ return "$ENV_VAR";
23
+ }
24
+ function resolveListValues(values, label, env = process.env) {
25
+ const resolved = [];
26
+ for (const value of values) {
27
+ const envVarName = extractEnvVarName(value);
28
+ if (!envVarName) {
29
+ const trimmed = value.trim();
30
+ if (trimmed) {
31
+ resolved.push(trimmed);
32
+ }
33
+ continue;
34
+ }
35
+ const envValue = env[envVarName];
36
+ if (envValue === undefined || envValue === "") {
37
+ console.error(`Missing required environment variable for ${label}: ${envVarName}`);
38
+ process.exit(1);
39
+ }
40
+ resolved.push(...envValue
41
+ .split(",")
42
+ .map((item) => item.trim())
43
+ .filter(Boolean));
44
+ }
45
+ return resolved;
46
+ }
47
+ function resolveStringValue(value, label, env = process.env) {
48
+ const envVarName = extractEnvVarName(value);
49
+ if (!envVarName) {
50
+ return value;
51
+ }
52
+ const resolved = env[envVarName];
53
+ if (resolved === undefined || resolved === "") {
54
+ console.error(`Missing required environment variable for ${label}: ${envVarName}`);
55
+ process.exit(1);
56
+ }
57
+ return resolved;
58
+ }
59
+ function resolveBooleanValue(value, label, env = process.env) {
60
+ if (typeof value === "boolean") {
61
+ return value;
62
+ }
63
+ const resolved = resolveStringValue(value, label, env).trim().toLowerCase();
64
+ if (["y", "yes", "true", "1", "on"].includes(resolved)) {
65
+ return true;
66
+ }
67
+ if (["n", "no", "false", "0", "off"].includes(resolved)) {
68
+ return false;
69
+ }
70
+ console.error(`${label} must resolve to yes or no.`);
71
+ process.exit(1);
72
+ }
73
+ function resolveEnumValue(value, label, allowedValues, env = process.env) {
74
+ const resolved = resolveStringValue(value, label, env);
75
+ if (allowedValues.includes(resolved)) {
76
+ return resolved;
77
+ }
78
+ console.error(`${label} must resolve to one of: ${allowedValues.join(", ")}.`);
79
+ process.exit(1);
80
+ }
81
+ function resolvePortValue(value, label, env = process.env) {
82
+ const rawValue = typeof value === "number"
83
+ ? String(value)
84
+ : resolveStringValue(value, label, env);
85
+ const port = Number(rawValue);
86
+ if (Number.isInteger(port) && port >= 1 && port <= 65535) {
87
+ return port;
88
+ }
89
+ console.error(`${label} must resolve to an integer from 1 to 65535.`);
90
+ process.exit(1);
91
+ }
92
+ function formatSecretValue(value) {
93
+ return isEnvReference(value) ? value : "[literal value hidden]";
94
+ }
95
+ function formatVisibleValue(rawValue, resolvedValue) {
96
+ return isEnvReference(rawValue)
97
+ ? `${rawValue} -> ${resolvedValue}`
98
+ : rawValue;
99
+ }
100
+ //# sourceMappingURL=env-values.js.map
@@ -0,0 +1,10 @@
1
+ import type { GeneratorOptionInput, ResolvedGeneratorOptions } from "../config/resolve-options";
2
+ type PromptFn = (message: string) => Promise<string>;
3
+ interface WizardDependencies {
4
+ env?: NodeJS.ProcessEnv;
5
+ envrcPath?: string;
6
+ onEnvrcWrite?: () => void;
7
+ }
8
+ export declare function promptForGeneratorOptions(defaults: ResolvedGeneratorOptions, promptFn?: PromptFn, dependencies?: WizardDependencies): Promise<GeneratorOptionInput>;
9
+ export {};
10
+ //# sourceMappingURL=generator-wizard.d.ts.map
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.promptForGeneratorOptions = promptForGeneratorOptions;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const readline = __importStar(require("readline"));
40
+ const prompts_1 = require("./prompts");
41
+ const env_values_1 = require("./env-values");
42
+ const DEFAULT_ENVRC_PATH = ".envrc";
43
+ async function promptForGeneratorOptions(defaults, promptFn, dependencies = {}) {
44
+ console.log("No parameters provided. Starting generator setup wizard.");
45
+ console.log(`Connection values can be entered as plain text or ${(0, env_values_1.formatEnvReferenceHelp)()}.`);
46
+ console.log(`Use ${(0, env_values_1.formatEnvReferenceHelp)()} to keep values out of the config file.`);
47
+ console.log(`Passwords can also be plain text, but ${(0, env_values_1.formatEnvReferenceHelp)()} is recommended.\n`);
48
+ const promptSession = promptFn ? undefined : createPromptSession();
49
+ const ask = promptFn ?? promptSession.prompt;
50
+ const env = dependencies.env ?? process.env;
51
+ const envrcPath = dependencies.envrcPath ?? DEFAULT_ENVRC_PATH;
52
+ const onEnvrcWrite = dependencies.onEnvrcWrite;
53
+ try {
54
+ const sourcePgHost = await promptConfigString("Source host", defaults.sourcePgHost, ask, env, envrcPath, {}, onEnvrcWrite);
55
+ const sourcePgPort = await promptPort("Source port", defaults.sourcePgPort, ask, env, envrcPath);
56
+ const sourcePgUser = await promptConfigString("Source user", defaults.sourcePgUser, ask, env, envrcPath, {}, onEnvrcWrite);
57
+ const sourcePgPassword = await promptConfigString("Source password ($ENV_VAR or plain text)", defaults.sourcePgPassword, ask, env, envrcPath, { secretDefault: true }, onEnvrcWrite);
58
+ const sourcePgSsl = await promptBoolean("Use SSL for source database", defaults.sourcePgSsl, ask, env, envrcPath, onEnvrcWrite);
59
+ const sourcePgDatabase = await promptConfigString("Source database", defaults.sourcePgDatabase, ask, env, envrcPath, {}, onEnvrcWrite);
60
+ console.log("");
61
+ const destPgHost = await promptConfigString("Destination host", defaults.destPgHost, ask, env, envrcPath, {}, onEnvrcWrite);
62
+ const destPgPort = await promptPort("Destination port", defaults.destPgPort, ask, env, envrcPath);
63
+ const destPgUser = await promptConfigString("Destination user", defaults.destPgUser, ask, env, envrcPath, {}, onEnvrcWrite);
64
+ const destPgPassword = await promptConfigString("Destination password ($ENV_VAR or plain text)", defaults.destPgPassword, ask, env, envrcPath, { secretDefault: true }, onEnvrcWrite);
65
+ const destPgSsl = await promptBoolean("Use SSL for destination database", defaults.destPgSsl, ask, env, envrcPath, onEnvrcWrite);
66
+ const destPgDatabase = await promptConfigString("Destination database", defaults.destPgDatabase, ask, env, envrcPath, {}, onEnvrcWrite);
67
+ console.log("");
68
+ const schema = await promptConfigString("Schema", defaults.schema, ask, env, envrcPath, {}, onEnvrcWrite);
69
+ const tables = await promptList('Tables to compare (comma-separated, "*" supported)', defaults.tables, ask, env, envrcPath, onEnvrcWrite);
70
+ const excludeTables = await promptOptionalList('Tables to exclude (comma-separated, "*" supported)', defaults.excludeTables, ask, env, envrcPath, onEnvrcWrite);
71
+ const ignoreColumns = await promptOptionalList("Columns to ignore (comma-separated)", defaults.ignoreColumns, ask, env, envrcPath, onEnvrcWrite);
72
+ const includeDeletes = await promptBoolean("Include deletes", defaults.includeDeletes, ask, env, envrcPath, onEnvrcWrite);
73
+ const skipMissingPk = await promptBoolean("Skip tables without primary keys", defaults.skipMissingPk, ask, env, envrcPath, onEnvrcWrite);
74
+ const output = await promptConfigString("Output file", defaults.output, ask, env, envrcPath, {}, onEnvrcWrite);
75
+ const generateSql = await promptBoolean("Generate SQL script after diff", defaults.generateSql ?? true, ask, env, envrcPath, onEnvrcWrite);
76
+ console.log("");
77
+ const schemaDiffTables = await promptList('Schema diff tables to compare (comma-separated, "*" supported)', defaults.schemaDiffTables, ask, env, envrcPath, onEnvrcWrite);
78
+ const schemaDiffExcludeTables = await promptOptionalList('Schema diff tables to exclude (comma-separated, "*" supported)', defaults.schemaDiffExcludeTables, ask, env, envrcPath, onEnvrcWrite);
79
+ const schemaDiffOutput = await promptConfigString("Schema diff output file", defaults.schemaDiffOutput, ask, env, envrcPath, {}, onEnvrcWrite);
80
+ console.log("");
81
+ const pgTriggersTables = await promptList('PostgreSQL triggers tables to compare (comma-separated, "*" supported)', defaults.pgTriggersTables, ask, env, envrcPath, onEnvrcWrite);
82
+ const pgTriggersExcludeTables = await promptOptionalList('PostgreSQL triggers tables to exclude (comma-separated, "*" supported)', defaults.pgTriggersExcludeTables, ask, env, envrcPath, onEnvrcWrite);
83
+ const pgTriggersOutput = await promptConfigString("PostgreSQL triggers output file", defaults.pgTriggersOutput, ask, env, envrcPath, {}, onEnvrcWrite);
84
+ const generatePgTriggers = await promptBoolean("Generate a PostgreSQL triggers and functions diff? (SQL script)", defaults.generatePgTriggers ?? true, ask, env, envrcPath, onEnvrcWrite);
85
+ console.log("");
86
+ const pretty = await promptBoolean("Pretty-print JSON", defaults.pretty, ask, env, envrcPath, onEnvrcWrite);
87
+ return {
88
+ sourcePgHost,
89
+ sourcePgPort,
90
+ sourcePgUser,
91
+ sourcePgPassword,
92
+ sourcePgSsl,
93
+ sourcePgDatabase,
94
+ destPgHost,
95
+ destPgPort,
96
+ destPgUser,
97
+ destPgPassword,
98
+ destPgSsl,
99
+ destPgDatabase,
100
+ schema,
101
+ tables,
102
+ excludeTables,
103
+ schemaDiffTables,
104
+ schemaDiffExcludeTables,
105
+ pgTriggersTables,
106
+ pgTriggersExcludeTables,
107
+ ignoreColumns,
108
+ includeDeletes,
109
+ skipMissingPk,
110
+ output,
111
+ schemaDiffOutput,
112
+ pgTriggersOutput,
113
+ pretty,
114
+ generateSql,
115
+ generatePgTriggers,
116
+ };
117
+ }
118
+ finally {
119
+ promptSession?.close();
120
+ }
121
+ }
122
+ async function promptRequired(label, defaultValue, promptFn) {
123
+ while (true) {
124
+ const answer = await promptFn(formatPrompt(label, defaultValue));
125
+ const value = answer || defaultValue;
126
+ if (value)
127
+ return value;
128
+ console.log(`${label} is required.`);
129
+ }
130
+ }
131
+ async function promptPort(label, defaultValue, promptFn, env, envrcPath) {
132
+ while (true) {
133
+ const displayedDefault = formatDefault(defaultValue);
134
+ const answer = await promptFn(formatPrompt(label, displayedDefault));
135
+ const value = (answer || displayedDefault).trim();
136
+ if ((0, env_values_1.isEnvReference)(value)) {
137
+ await ensureEnvReferenceValue(value, promptFn, env, envrcPath);
138
+ const resolvedPort = Number(env[(0, env_values_1.extractEnvVarName)(value)]);
139
+ if (Number.isInteger(resolvedPort) &&
140
+ resolvedPort >= 1 &&
141
+ resolvedPort <= 65535) {
142
+ return value;
143
+ }
144
+ console.log(`${label} must resolve to an integer from 1 to 65535.`);
145
+ continue;
146
+ }
147
+ const port = Number(value);
148
+ if (Number.isInteger(port) && port >= 1 && port <= 65535)
149
+ return port;
150
+ console.log(`${label} must be an integer from 1 to 65535.`);
151
+ }
152
+ }
153
+ async function promptConfigString(label, defaultValue, promptFn, env, envrcPath, options = {}, onEnvrcWrite) {
154
+ while (true) {
155
+ const displayedDefault = formatDefault(defaultValue, env, options);
156
+ const answer = await promptFn(formatPrompt(label, displayedDefault));
157
+ const value = answer || defaultValue;
158
+ if (!value) {
159
+ console.log(`${label} is required.`);
160
+ continue;
161
+ }
162
+ if (value.startsWith("$") && !(0, env_values_1.isEnvReference)(value)) {
163
+ console.log(`${label} must use ${(0, env_values_1.formatEnvReferenceHelp)()} when referencing an environment variable.`);
164
+ continue;
165
+ }
166
+ if ((0, env_values_1.isEnvReference)(value)) {
167
+ await ensureEnvReferenceValue(value, promptFn, env, envrcPath, onEnvrcWrite);
168
+ }
169
+ return value;
170
+ }
171
+ }
172
+ async function promptList(label, defaultValue, promptFn, env, envrcPath, onEnvrcWrite) {
173
+ while (true) {
174
+ const answer = await promptFn(formatPrompt(label, defaultValue.join(", ")));
175
+ const values = parseList(answer || defaultValue.join(","));
176
+ const listRefsValid = await ensureEnvReferencesForList(values, promptFn, env, envrcPath, label, onEnvrcWrite);
177
+ if (!listRefsValid) {
178
+ continue;
179
+ }
180
+ const resolved = (0, env_values_1.resolveListValues)(values, label, env);
181
+ if (resolved.length > 0)
182
+ return values;
183
+ console.log(`${label} requires at least one value.`);
184
+ }
185
+ }
186
+ async function promptOptionalList(label, defaultValue, promptFn, env, envrcPath, onEnvrcWrite) {
187
+ while (true) {
188
+ const promptLabel = defaultValue.length > 0 ? `${label} (type none to clear)` : label;
189
+ const answer = await promptFn(formatPrompt(promptLabel, defaultValue.join(", ")));
190
+ if (answer.trim().toLowerCase() === "none") {
191
+ return [];
192
+ }
193
+ const values = !answer && defaultValue.length > 0 ? defaultValue : parseList(answer);
194
+ const listRefsValid = await ensureEnvReferencesForList(values, promptFn, env, envrcPath, label, onEnvrcWrite);
195
+ if (!listRefsValid) {
196
+ continue;
197
+ }
198
+ (0, env_values_1.resolveListValues)(values, label, env);
199
+ return values;
200
+ }
201
+ }
202
+ async function promptBoolean(label, defaultValue, promptFn, env, envrcPath, onEnvrcWrite) {
203
+ let defaultText;
204
+ if (typeof defaultValue === "string") {
205
+ defaultText = defaultValue;
206
+ }
207
+ else {
208
+ defaultText = defaultValue ? "yes" : "no";
209
+ }
210
+ while (true) {
211
+ const answer = await promptFn(formatPrompt(`${label} (yes/no)`, defaultText));
212
+ const value = answer || defaultText;
213
+ if (value.startsWith("$") && !(0, env_values_1.isEnvReference)(value)) {
214
+ console.log(`${label} must use ${(0, env_values_1.formatEnvReferenceHelp)()} when referencing an environment variable.`);
215
+ continue;
216
+ }
217
+ if ((0, env_values_1.isEnvReference)(value)) {
218
+ await ensureEnvReferenceValue(value, promptFn, env, envrcPath, onEnvrcWrite);
219
+ assertValidBooleanReference(value, label, env);
220
+ return value;
221
+ }
222
+ const normalizedValue = value.toLowerCase();
223
+ if (["y", "yes", "true"].includes(normalizedValue))
224
+ return true;
225
+ if (["n", "no", "false"].includes(normalizedValue))
226
+ return false;
227
+ console.log(`${label} must be yes or no.`);
228
+ }
229
+ }
230
+ function formatPrompt(label, defaultValue) {
231
+ return defaultValue ? `${label} [${defaultValue}]: ` : `${label}: `;
232
+ }
233
+ function formatDefault(value, env = process.env, options = {}) {
234
+ if (typeof value === "number") {
235
+ return String(value);
236
+ }
237
+ if (!value) {
238
+ return "";
239
+ }
240
+ if (options.secretDefault && (0, env_values_1.isEnvReference)(value)) {
241
+ const envVarName = (0, env_values_1.extractEnvVarName)(value);
242
+ if (envVarName && env[envVarName] !== undefined && env[envVarName] !== "") {
243
+ return `${value} loaded`;
244
+ }
245
+ return `${value} not set`;
246
+ }
247
+ if (options.secretDefault && !(0, env_values_1.isEnvReference)(value)) {
248
+ return "existing literal hidden";
249
+ }
250
+ return value;
251
+ }
252
+ function parseList(value) {
253
+ return value
254
+ .split(",")
255
+ .map((item) => item.trim())
256
+ .filter(Boolean);
257
+ }
258
+ function writeEnvrcValue(envrcPath, envVarName, value) {
259
+ const resolvedPath = path.resolve(envrcPath);
260
+ const line = `export ${envVarName}=${shellQuote(value)}`;
261
+ const existing = fs.existsSync(resolvedPath)
262
+ ? fs.readFileSync(resolvedPath, "utf-8")
263
+ : "";
264
+ const lines = existing ? existing.replace(/\n$/, "").split("\n") : [];
265
+ const assignmentPattern = new RegExp(`^(?:export\\s+)?${envVarName}=`);
266
+ const index = lines.findIndex((existingLine) => assignmentPattern.test(existingLine.trim()));
267
+ if (index >= 0) {
268
+ lines[index] = line;
269
+ }
270
+ else {
271
+ lines.push(line);
272
+ }
273
+ fs.writeFileSync(resolvedPath, `${lines.join("\n")}\n`, {
274
+ encoding: "utf-8",
275
+ mode: 0o600,
276
+ });
277
+ }
278
+ async function ensureEnvReferenceValue(envReference, promptFn, env, envrcPath, onEnvrcWrite) {
279
+ const envVarName = (0, env_values_1.extractEnvVarName)(envReference);
280
+ if (!envVarName) {
281
+ return;
282
+ }
283
+ if (env[envVarName] !== undefined && env[envVarName] !== "") {
284
+ return;
285
+ }
286
+ while (true) {
287
+ const value = await promptFn(`${envVarName} is not set. Enter value to store in ${envrcPath}: `);
288
+ if (!value) {
289
+ console.log("Value is required.");
290
+ continue;
291
+ }
292
+ env[envVarName] = value;
293
+ writeEnvrcValue(envrcPath, envVarName, value);
294
+ onEnvrcWrite?.();
295
+ console.log(`${envVarName} written to ${path.resolve(envrcPath)} and loaded for this run.`);
296
+ return;
297
+ }
298
+ }
299
+ async function ensureEnvReferencesForList(values, promptFn, env, envrcPath, label, onEnvrcWrite) {
300
+ for (const value of values) {
301
+ if (value.startsWith("$") && !(0, env_values_1.isEnvReference)(value)) {
302
+ console.log(`${label} must use ${(0, env_values_1.formatEnvReferenceHelp)()} when referencing an environment variable.`);
303
+ return false;
304
+ }
305
+ if ((0, env_values_1.isEnvReference)(value)) {
306
+ await ensureEnvReferenceValue(value, promptFn, env, envrcPath, onEnvrcWrite);
307
+ }
308
+ }
309
+ return true;
310
+ }
311
+ function assertValidBooleanReference(envReference, label, env) {
312
+ (0, env_values_1.resolveBooleanValue)(envReference, label, env);
313
+ }
314
+ function shellQuote(value) {
315
+ return `'${value.replace(/'/g, `'\\''`)}'`;
316
+ }
317
+ function createPromptSession() {
318
+ if (!process.stdin.isTTY) {
319
+ return {
320
+ prompt: prompts_1.prompt,
321
+ close: () => undefined,
322
+ };
323
+ }
324
+ const rl = readline.createInterface({
325
+ input: process.stdin,
326
+ output: process.stdout,
327
+ });
328
+ return {
329
+ prompt: (message) => new Promise((resolve) => {
330
+ rl.question(message, (answer) => {
331
+ resolve(answer.trim());
332
+ });
333
+ }),
334
+ close: () => rl.close(),
335
+ };
336
+ }
337
+ //# sourceMappingURL=generator-wizard.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Safe identifier quoting for PostgreSQL.
3
+ * All table names, column names, and schema names MUST be quoted through these helpers.
4
+ */
5
+ /**
6
+ * Quotes a PostgreSQL identifier (table name, column name, schema name).
7
+ * Doubles any embedded double-quote characters.
8
+ */
9
+ export declare function quoteIdentifier(name: string): string;
10
+ /**
11
+ * Quotes a schema-qualified table name.
12
+ */
13
+ export declare function quoteQualifiedTable(schema: string, table: string): string;
14
+ /**
15
+ * Validates that an identifier contains only safe characters.
16
+ * This is a defense-in-depth check — identifiers are always quoted,
17
+ * but this catches obvious injection attempts early.
18
+ */
19
+ export declare function validateIdentifier(name: string, context: string): void;
20
+ /**
21
+ * Validates an array of identifiers (e.g., column names).
22
+ */
23
+ export declare function validateIdentifiers(names: string[], context: string): void;
24
+ //# sourceMappingURL=identifiers.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Safe identifier quoting for PostgreSQL.
4
+ * All table names, column names, and schema names MUST be quoted through these helpers.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.quoteIdentifier = quoteIdentifier;
8
+ exports.quoteQualifiedTable = quoteQualifiedTable;
9
+ exports.validateIdentifier = validateIdentifier;
10
+ exports.validateIdentifiers = validateIdentifiers;
11
+ /**
12
+ * Quotes a PostgreSQL identifier (table name, column name, schema name).
13
+ * Doubles any embedded double-quote characters.
14
+ */
15
+ function quoteIdentifier(name) {
16
+ return '"' + name.replace(/"/g, '""') + '"';
17
+ }
18
+ /**
19
+ * Quotes a schema-qualified table name.
20
+ */
21
+ function quoteQualifiedTable(schema, table) {
22
+ return `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
23
+ }
24
+ /**
25
+ * Validates that an identifier contains only safe characters.
26
+ * This is a defense-in-depth check — identifiers are always quoted,
27
+ * but this catches obvious injection attempts early.
28
+ */
29
+ function validateIdentifier(name, context) {
30
+ if (typeof name !== "string" || name.length === 0) {
31
+ throw new Error(`Invalid identifier (${context}): must be a non-empty string`);
32
+ }
33
+ if (name.length > 63) {
34
+ throw new Error(`Invalid identifier (${context}): "${name}" exceeds PostgreSQL max identifier length of 63`);
35
+ }
36
+ }
37
+ /**
38
+ * Validates an array of identifiers (e.g., column names).
39
+ */
40
+ function validateIdentifiers(names, context) {
41
+ for (const name of names) {
42
+ validateIdentifier(name, context);
43
+ }
44
+ }
45
+ //# sourceMappingURL=identifiers.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Prompts the user with the given message and waits for input.
3
+ * Returns the trimmed input string.
4
+ */
5
+ export declare function prompt(message: string): Promise<string>;
6
+ /**
7
+ * Asks the user to type "yes" to proceed.
8
+ * Returns true if the user typed exactly "yes", false otherwise.
9
+ */
10
+ export declare function confirmProceed(message?: string, defaultYes?: boolean): Promise<boolean>;
11
+ /**
12
+ * Asks the user whether to create a config file.
13
+ * Returns true if the user typed exactly "yes".
14
+ */
15
+ export declare function confirmCreateConfig(): Promise<boolean>;
16
+ /**
17
+ * Asks the user whether to update an existing config file with the current options.
18
+ * Returns true if the user typed exactly "yes".
19
+ */
20
+ export declare function confirmUpdateConfig(): Promise<boolean>;
21
+ /**
22
+ * Asks the user whether to run `direnv allow` in the current directory.
23
+ * Returns true if the user typed exactly "yes".
24
+ */
25
+ export declare function confirmDirenvAllow(): Promise<boolean>;
26
+ //# sourceMappingURL=prompts.d.ts.map