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.
- package/README.md +628 -0
- package/dist/apply/apply-diff.d.ts +24 -0
- package/dist/apply/apply-diff.js +205 -0
- package/dist/apply/conflict.d.ts +20 -0
- package/dist/apply/conflict.js +14 -0
- package/dist/apply/plan.d.ts +13 -0
- package/dist/apply/plan.js +37 -0
- package/dist/cli/apply.d.ts +8 -0
- package/dist/cli/apply.js +270 -0
- package/dist/cli/generator.d.ts +9 -0
- package/dist/cli/generator.js +804 -0
- package/dist/cli/pg-triggers.d.ts +2 -0
- package/dist/cli/pg-triggers.js +185 -0
- package/dist/cli/root.d.ts +8 -0
- package/dist/cli/root.js +231 -0
- package/dist/cli/sql.d.ts +9 -0
- package/dist/cli/sql.js +158 -0
- package/dist/config/config-schema.d.ts +380 -0
- package/dist/config/config-schema.js +95 -0
- package/dist/config/load-config.d.ts +12 -0
- package/dist/config/load-config.js +87 -0
- package/dist/config/resolve-options.d.ts +95 -0
- package/dist/config/resolve-options.js +183 -0
- package/dist/config/write-config.d.ts +18 -0
- package/dist/config/write-config.js +103 -0
- package/dist/db/connection.d.ts +28 -0
- package/dist/db/connection.js +34 -0
- package/dist/db/metadata.d.ts +70 -0
- package/dist/db/metadata.js +238 -0
- package/dist/db/pg-triggers.d.ts +27 -0
- package/dist/db/pg-triggers.js +59 -0
- package/dist/db/sql.d.ts +72 -0
- package/dist/db/sql.js +250 -0
- package/dist/diff/diff-schema.d.ts +380 -0
- package/dist/diff/diff-schema.js +67 -0
- package/dist/diff/generate-diff.d.ts +20 -0
- package/dist/diff/generate-diff.js +224 -0
- package/dist/diff/pg-triggers-diff.d.ts +11 -0
- package/dist/diff/pg-triggers-diff.js +161 -0
- package/dist/diff/serialize-value.d.ts +35 -0
- package/dist/diff/serialize-value.js +242 -0
- package/dist/diff/write-diff-yaml.d.ts +3 -0
- package/dist/diff/write-diff-yaml.js +48 -0
- package/dist/schema-diff/generate-schema-diff.d.ts +12 -0
- package/dist/schema-diff/generate-schema-diff.js +355 -0
- package/dist/schema-diff/generate-schema-sql.d.ts +20 -0
- package/dist/schema-diff/generate-schema-sql.js +187 -0
- package/dist/schema-diff/schema-diff-schema.d.ts +1088 -0
- package/dist/schema-diff/schema-diff-schema.js +70 -0
- package/dist/shared/env-values.d.ts +11 -0
- package/dist/shared/env-values.js +100 -0
- package/dist/shared/generator-wizard.d.ts +10 -0
- package/dist/shared/generator-wizard.js +337 -0
- package/dist/shared/identifiers.d.ts +24 -0
- package/dist/shared/identifiers.js +45 -0
- package/dist/shared/prompts.d.ts +26 -0
- package/dist/shared/prompts.js +104 -0
- package/dist/shared/summary.d.ts +33 -0
- package/dist/shared/summary.js +41 -0
- package/dist/sql/generate-sql.d.ts +19 -0
- package/dist/sql/generate-sql.js +223 -0
- package/package.json +39 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveGeneratorOptions = resolveGeneratorOptions;
|
|
4
|
+
exports.resolveApplyOptions = resolveApplyOptions;
|
|
5
|
+
exports.resolveRuntimeGeneratorOptions = resolveRuntimeGeneratorOptions;
|
|
6
|
+
exports.resolveRuntimeApplyOptions = resolveRuntimeApplyOptions;
|
|
7
|
+
const env_values_1 = require("../shared/env-values");
|
|
8
|
+
/**
|
|
9
|
+
* Merges generator CLI args on top of config values.
|
|
10
|
+
* CLI args take precedence over config values.
|
|
11
|
+
*/
|
|
12
|
+
function resolveGeneratorOptions(config, cliArgs) {
|
|
13
|
+
const defaults = {
|
|
14
|
+
schema: "public",
|
|
15
|
+
tables: [],
|
|
16
|
+
excludeTables: [],
|
|
17
|
+
schemaDiffTables: [],
|
|
18
|
+
schemaDiffExcludeTables: [],
|
|
19
|
+
pgTriggersTables: [],
|
|
20
|
+
pgTriggersExcludeTables: [],
|
|
21
|
+
ignoreColumns: [],
|
|
22
|
+
tablesWhereDataFilters: {},
|
|
23
|
+
includeDeletes: true,
|
|
24
|
+
skipMissingPk: true,
|
|
25
|
+
output: "frg-data-diff.json",
|
|
26
|
+
schemaDiffOutput: "frg-schema-diff.json",
|
|
27
|
+
pgTriggersOutput: "frg-triggers-diff.sql",
|
|
28
|
+
pretty: true,
|
|
29
|
+
verbose: false,
|
|
30
|
+
};
|
|
31
|
+
const tables = cliArgs.tables ?? config?.tables ?? defaults.tables;
|
|
32
|
+
const excludeTables = resolveOptionalListOption(cliArgs.excludeTables, config?.excludeTables, defaults.excludeTables);
|
|
33
|
+
const schemaDiffTables = resolveInheritedTableListOption(cliArgs.schemaDiffTables, config?.schemaDiffTables, tables);
|
|
34
|
+
const schemaDiffExcludeTables = resolveOptionalListOption(cliArgs.schemaDiffExcludeTables, config?.schemaDiffExcludeTables, excludeTables);
|
|
35
|
+
const pgTriggersTables = resolveInheritedTableListOption(cliArgs.pgTriggersTables, config?.pgTriggersTables, tables);
|
|
36
|
+
const pgTriggersExcludeTables = resolveOptionalListOption(cliArgs.pgTriggersExcludeTables, config?.pgTriggersExcludeTables, excludeTables);
|
|
37
|
+
const ignoreColumns = resolveOptionalListOption(cliArgs.ignoreColumns, config?.ignoreColumns, defaults.ignoreColumns);
|
|
38
|
+
return {
|
|
39
|
+
sourcePgHost: cliArgs.sourcePgHost ?? config?.sourcePgHost ?? "",
|
|
40
|
+
sourcePgPort: cliArgs.sourcePgPort ?? config?.sourcePgPort ?? 5432,
|
|
41
|
+
sourcePgDatabase: cliArgs.sourcePgDatabase ?? config?.sourcePgDatabase ?? "",
|
|
42
|
+
sourcePgUser: cliArgs.sourcePgUser ?? config?.sourcePgUser ?? "",
|
|
43
|
+
sourcePgPassword: cliArgs.sourcePgPassword ?? config?.sourcePgPassword ?? "",
|
|
44
|
+
sourcePgSsl: cliArgs.sourcePgSsl ?? config?.sourcePgSsl ?? false,
|
|
45
|
+
destPgHost: cliArgs.destPgHost ?? config?.destPgHost ?? "",
|
|
46
|
+
destPgPort: cliArgs.destPgPort ?? config?.destPgPort ?? 5432,
|
|
47
|
+
destPgDatabase: cliArgs.destPgDatabase ?? config?.destPgDatabase ?? "",
|
|
48
|
+
destPgUser: cliArgs.destPgUser ?? config?.destPgUser ?? "",
|
|
49
|
+
destPgPassword: cliArgs.destPgPassword ?? config?.destPgPassword ?? "",
|
|
50
|
+
destPgSsl: cliArgs.destPgSsl ?? config?.destPgSsl ?? false,
|
|
51
|
+
schema: cliArgs.schema ?? config?.schema ?? defaults.schema,
|
|
52
|
+
tables,
|
|
53
|
+
excludeTables,
|
|
54
|
+
schemaDiffTables,
|
|
55
|
+
schemaDiffExcludeTables,
|
|
56
|
+
pgTriggersTables,
|
|
57
|
+
pgTriggersExcludeTables,
|
|
58
|
+
ignoreColumns,
|
|
59
|
+
tablesWhereDataFilters: cliArgs.tablesWhereDataFilters ??
|
|
60
|
+
config?.tablesWhereDataFilters ??
|
|
61
|
+
defaults.tablesWhereDataFilters,
|
|
62
|
+
includeDeletes: cliArgs.includeDeletes ??
|
|
63
|
+
config?.includeDeletes ??
|
|
64
|
+
defaults.includeDeletes,
|
|
65
|
+
skipMissingPk: cliArgs.skipMissingPk ?? config?.skipMissingPk ?? defaults.skipMissingPk,
|
|
66
|
+
output: cliArgs.output ?? config?.output ?? defaults.output,
|
|
67
|
+
schemaDiffOutput: cliArgs.schemaDiffOutput ??
|
|
68
|
+
config?.schemaDiffOutput ??
|
|
69
|
+
defaults.schemaDiffOutput,
|
|
70
|
+
pgTriggersOutput: cliArgs.pgTriggersOutput ??
|
|
71
|
+
config?.pgTriggersOutput ??
|
|
72
|
+
defaults.pgTriggersOutput,
|
|
73
|
+
pretty: cliArgs.pretty ?? config?.pretty ?? defaults.pretty,
|
|
74
|
+
generateSql: cliArgs.generateSql ?? config?.generateSql,
|
|
75
|
+
generatePgTriggers: cliArgs.generatePgTriggers ?? config?.generatePgTriggers,
|
|
76
|
+
verbose: cliArgs.verbose ?? defaults.verbose,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function resolveOptionalListOption(cliValue, configValue, fallback) {
|
|
80
|
+
if (cliValue !== undefined) {
|
|
81
|
+
return cliValue ?? [];
|
|
82
|
+
}
|
|
83
|
+
if (configValue !== undefined) {
|
|
84
|
+
return configValue ?? [];
|
|
85
|
+
}
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
function resolveInheritedTableListOption(cliValue, configValue, fallback) {
|
|
89
|
+
if (cliValue !== undefined && cliValue.length > 0) {
|
|
90
|
+
return cliValue;
|
|
91
|
+
}
|
|
92
|
+
if (configValue !== undefined && configValue.length > 0) {
|
|
93
|
+
return configValue;
|
|
94
|
+
}
|
|
95
|
+
return fallback;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Merges apply CLI args on top of config values.
|
|
99
|
+
* CLI args take precedence over config values.
|
|
100
|
+
*/
|
|
101
|
+
function resolveApplyOptions(config, cliArgs) {
|
|
102
|
+
const defaults = {
|
|
103
|
+
input: "frg-data-diff.json",
|
|
104
|
+
dryRun: true,
|
|
105
|
+
applyInserts: true,
|
|
106
|
+
applyUpdates: true,
|
|
107
|
+
applyDeletes: false,
|
|
108
|
+
conflictMode: "abort",
|
|
109
|
+
insertMode: "strict",
|
|
110
|
+
transaction: true,
|
|
111
|
+
verbose: false,
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
destPgHost: cliArgs.destPgHost ?? config?.destPgHost ?? "",
|
|
115
|
+
destPgPort: cliArgs.destPgPort ?? config?.destPgPort ?? 5432,
|
|
116
|
+
destPgDatabase: cliArgs.destPgDatabase ?? config?.destPgDatabase ?? "",
|
|
117
|
+
destPgUser: cliArgs.destPgUser ?? config?.destPgUser ?? "",
|
|
118
|
+
destPgPassword: cliArgs.destPgPassword ?? config?.destPgPassword ?? "",
|
|
119
|
+
destPgSsl: cliArgs.destPgSsl ?? config?.destPgSsl ?? false,
|
|
120
|
+
input: cliArgs.input ?? config?.input ?? defaults.input,
|
|
121
|
+
dryRun: cliArgs.dryRun ?? config?.dryRun ?? defaults.dryRun,
|
|
122
|
+
applyInserts: cliArgs.applyInserts ?? config?.applyInserts ?? defaults.applyInserts,
|
|
123
|
+
applyUpdates: cliArgs.applyUpdates ?? config?.applyUpdates ?? defaults.applyUpdates,
|
|
124
|
+
applyDeletes: cliArgs.applyDeletes ?? config?.applyDeletes ?? defaults.applyDeletes,
|
|
125
|
+
conflictMode: cliArgs.conflictMode ?? config?.conflictMode ?? defaults.conflictMode,
|
|
126
|
+
insertMode: cliArgs.insertMode ?? config?.insertMode ?? defaults.insertMode,
|
|
127
|
+
transaction: cliArgs.transaction ?? config?.transaction ?? defaults.transaction,
|
|
128
|
+
verbose: cliArgs.verbose ?? defaults.verbose,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function resolveRuntimeGeneratorOptions(options) {
|
|
132
|
+
let generateSql;
|
|
133
|
+
if (options.generateSql !== undefined) {
|
|
134
|
+
generateSql = (0, env_values_1.resolveBooleanValue)(options.generateSql, "generate sql");
|
|
135
|
+
}
|
|
136
|
+
let generatePgTriggers;
|
|
137
|
+
if (options.generatePgTriggers !== undefined) {
|
|
138
|
+
generatePgTriggers = (0, env_values_1.resolveBooleanValue)(options.generatePgTriggers, "generate pg triggers");
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
...options,
|
|
142
|
+
schema: (0, env_values_1.resolveStringValue)(options.schema, "schema"),
|
|
143
|
+
tables: (0, env_values_1.resolveListValues)(options.tables, "tables"),
|
|
144
|
+
excludeTables: (0, env_values_1.resolveListValues)(options.excludeTables, "exclude tables"),
|
|
145
|
+
schemaDiffTables: (0, env_values_1.resolveListValues)(options.schemaDiffTables, "schema diff tables"),
|
|
146
|
+
schemaDiffExcludeTables: (0, env_values_1.resolveListValues)(options.schemaDiffExcludeTables, "schema diff exclude tables"),
|
|
147
|
+
pgTriggersTables: (0, env_values_1.resolveListValues)(options.pgTriggersTables, "pg triggers tables"),
|
|
148
|
+
pgTriggersExcludeTables: (0, env_values_1.resolveListValues)(options.pgTriggersExcludeTables, "pg triggers exclude tables"),
|
|
149
|
+
ignoreColumns: (0, env_values_1.resolveListValues)(options.ignoreColumns, "ignored columns"),
|
|
150
|
+
sourcePgSsl: (0, env_values_1.resolveBooleanValue)(options.sourcePgSsl, "source ssl"),
|
|
151
|
+
destPgSsl: (0, env_values_1.resolveBooleanValue)(options.destPgSsl, "destination ssl"),
|
|
152
|
+
includeDeletes: (0, env_values_1.resolveBooleanValue)(options.includeDeletes, "include deletes"),
|
|
153
|
+
skipMissingPk: (0, env_values_1.resolveBooleanValue)(options.skipMissingPk, "skip missing primary keys"),
|
|
154
|
+
output: (0, env_values_1.resolveStringValue)(options.output, "output file"),
|
|
155
|
+
schemaDiffOutput: (0, env_values_1.resolveStringValue)(options.schemaDiffOutput, "schema diff output file"),
|
|
156
|
+
pgTriggersOutput: (0, env_values_1.resolveStringValue)(options.pgTriggersOutput, "pg triggers output file"),
|
|
157
|
+
pretty: (0, env_values_1.resolveBooleanValue)(options.pretty, "pretty-print json"),
|
|
158
|
+
generateSql,
|
|
159
|
+
generatePgTriggers,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function resolveRuntimeApplyOptions(options) {
|
|
163
|
+
return {
|
|
164
|
+
...options,
|
|
165
|
+
destPgSsl: (0, env_values_1.resolveBooleanValue)(options.destPgSsl, "destination ssl"),
|
|
166
|
+
input: (0, env_values_1.resolveStringValue)(options.input, "input file"),
|
|
167
|
+
dryRun: (0, env_values_1.resolveBooleanValue)(options.dryRun, "dry-run"),
|
|
168
|
+
applyInserts: (0, env_values_1.resolveBooleanValue)(options.applyInserts, "apply inserts"),
|
|
169
|
+
applyUpdates: (0, env_values_1.resolveBooleanValue)(options.applyUpdates, "apply updates"),
|
|
170
|
+
applyDeletes: (0, env_values_1.resolveBooleanValue)(options.applyDeletes, "apply deletes"),
|
|
171
|
+
conflictMode: (0, env_values_1.resolveEnumValue)(options.conflictMode, "conflict mode", [
|
|
172
|
+
"abort",
|
|
173
|
+
"skip",
|
|
174
|
+
"overwrite",
|
|
175
|
+
]),
|
|
176
|
+
insertMode: (0, env_values_1.resolveEnumValue)(options.insertMode, "insert mode", [
|
|
177
|
+
"strict",
|
|
178
|
+
"upsert",
|
|
179
|
+
]),
|
|
180
|
+
transaction: (0, env_values_1.resolveBooleanValue)(options.transaction, "transaction"),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=resolve-options.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ResolvedGeneratorOptions, type ResolvedApplyOptions, type OptionalListInput } from "./resolve-options";
|
|
2
|
+
type WritableGeneratorOptions = Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "ignoreColumns"> & {
|
|
3
|
+
excludeTables: OptionalListInput;
|
|
4
|
+
schemaDiffExcludeTables: OptionalListInput;
|
|
5
|
+
pgTriggersExcludeTables: OptionalListInput;
|
|
6
|
+
ignoreColumns: OptionalListInput;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Builds a config object from resolved generator and apply options.
|
|
10
|
+
* Values may be plain text or $ENV_VAR references, depending on user input.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildConfigFile(generatorOptions: WritableGeneratorOptions, applyOptions: ResolvedApplyOptions): object;
|
|
13
|
+
/**
|
|
14
|
+
* Writes the config object to the given path as formatted JSON.
|
|
15
|
+
*/
|
|
16
|
+
export declare function writeConfig(configPath: string, config: object): void;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=write-config.d.ts.map
|
|
@@ -0,0 +1,103 @@
|
|
|
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.buildConfigFile = buildConfigFile;
|
|
37
|
+
exports.writeConfig = writeConfig;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
/**
|
|
40
|
+
* Builds a config object from resolved generator and apply options.
|
|
41
|
+
* Values may be plain text or $ENV_VAR references, depending on user input.
|
|
42
|
+
*/
|
|
43
|
+
function buildConfigFile(generatorOptions, applyOptions) {
|
|
44
|
+
const generator = {
|
|
45
|
+
sourcePgHost: generatorOptions.sourcePgHost,
|
|
46
|
+
sourcePgPort: generatorOptions.sourcePgPort,
|
|
47
|
+
sourcePgDatabase: generatorOptions.sourcePgDatabase,
|
|
48
|
+
sourcePgUser: generatorOptions.sourcePgUser,
|
|
49
|
+
sourcePgPassword: generatorOptions.sourcePgPassword,
|
|
50
|
+
sourcePgSsl: generatorOptions.sourcePgSsl,
|
|
51
|
+
destPgHost: generatorOptions.destPgHost,
|
|
52
|
+
destPgPort: generatorOptions.destPgPort,
|
|
53
|
+
destPgDatabase: generatorOptions.destPgDatabase,
|
|
54
|
+
destPgUser: generatorOptions.destPgUser,
|
|
55
|
+
destPgPassword: generatorOptions.destPgPassword,
|
|
56
|
+
destPgSsl: generatorOptions.destPgSsl,
|
|
57
|
+
schema: generatorOptions.schema,
|
|
58
|
+
tables: generatorOptions.tables,
|
|
59
|
+
tablesWhereDataFilters: generatorOptions.tablesWhereDataFilters ?? {},
|
|
60
|
+
excludeTables: generatorOptions.excludeTables ?? [],
|
|
61
|
+
schemaDiffTables: generatorOptions.schemaDiffTables,
|
|
62
|
+
schemaDiffExcludeTables: generatorOptions.schemaDiffExcludeTables ?? [],
|
|
63
|
+
pgTriggersTables: generatorOptions.pgTriggersTables,
|
|
64
|
+
pgTriggersExcludeTables: generatorOptions.pgTriggersExcludeTables ?? [],
|
|
65
|
+
ignoreColumns: generatorOptions.ignoreColumns ?? [],
|
|
66
|
+
includeDeletes: generatorOptions.includeDeletes,
|
|
67
|
+
skipMissingPk: generatorOptions.skipMissingPk,
|
|
68
|
+
output: generatorOptions.output,
|
|
69
|
+
schemaDiffOutput: generatorOptions.schemaDiffOutput,
|
|
70
|
+
pgTriggersOutput: generatorOptions.pgTriggersOutput,
|
|
71
|
+
pretty: generatorOptions.pretty,
|
|
72
|
+
generateSql: generatorOptions.generateSql,
|
|
73
|
+
generatePgTriggers: generatorOptions.generatePgTriggers,
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
format: "frg-data-diff-config/v1",
|
|
77
|
+
generator,
|
|
78
|
+
apply: {
|
|
79
|
+
destPgHost: applyOptions.destPgHost,
|
|
80
|
+
destPgPort: applyOptions.destPgPort,
|
|
81
|
+
destPgDatabase: applyOptions.destPgDatabase,
|
|
82
|
+
destPgUser: applyOptions.destPgUser,
|
|
83
|
+
destPgPassword: applyOptions.destPgPassword,
|
|
84
|
+
destPgSsl: applyOptions.destPgSsl,
|
|
85
|
+
input: applyOptions.input,
|
|
86
|
+
dryRun: applyOptions.dryRun,
|
|
87
|
+
applyInserts: applyOptions.applyInserts,
|
|
88
|
+
applyUpdates: applyOptions.applyUpdates,
|
|
89
|
+
applyDeletes: applyOptions.applyDeletes,
|
|
90
|
+
conflictMode: applyOptions.conflictMode,
|
|
91
|
+
insertMode: applyOptions.insertMode,
|
|
92
|
+
transaction: applyOptions.transaction,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Writes the config object to the given path as formatted JSON.
|
|
98
|
+
*/
|
|
99
|
+
function writeConfig(configPath, config) {
|
|
100
|
+
const content = JSON.stringify(config, null, 2) + "\n";
|
|
101
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=write-config.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Pool, type PoolConfig } from "pg";
|
|
2
|
+
export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 30000;
|
|
3
|
+
export interface PgConnectionParams {
|
|
4
|
+
host: string;
|
|
5
|
+
port: number | string;
|
|
6
|
+
database: string;
|
|
7
|
+
user: string;
|
|
8
|
+
password: string;
|
|
9
|
+
ssl: boolean | string;
|
|
10
|
+
}
|
|
11
|
+
export interface ResolvedPgConnectionParams {
|
|
12
|
+
host: string;
|
|
13
|
+
port: number;
|
|
14
|
+
database: string;
|
|
15
|
+
user: string;
|
|
16
|
+
password: string;
|
|
17
|
+
ssl: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function resolveConnectionParams(params: PgConnectionParams, labels: {
|
|
20
|
+
host: string;
|
|
21
|
+
port: string;
|
|
22
|
+
database: string;
|
|
23
|
+
user: string;
|
|
24
|
+
password: string;
|
|
25
|
+
}, env?: NodeJS.ProcessEnv): ResolvedPgConnectionParams;
|
|
26
|
+
export declare function createPool(params: ResolvedPgConnectionParams): Pool;
|
|
27
|
+
export declare function buildPoolConfig(params: ResolvedPgConnectionParams): PoolConfig;
|
|
28
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CONNECTION_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.resolveConnectionParams = resolveConnectionParams;
|
|
5
|
+
exports.createPool = createPool;
|
|
6
|
+
exports.buildPoolConfig = buildPoolConfig;
|
|
7
|
+
const pg_1 = require("pg");
|
|
8
|
+
const env_values_1 = require("../shared/env-values");
|
|
9
|
+
exports.DEFAULT_CONNECTION_TIMEOUT_MS = 30000;
|
|
10
|
+
function resolveConnectionParams(params, labels, env = process.env) {
|
|
11
|
+
return {
|
|
12
|
+
host: (0, env_values_1.resolveStringValue)(params.host, labels.host, env),
|
|
13
|
+
port: (0, env_values_1.resolvePortValue)(params.port, labels.port, env),
|
|
14
|
+
database: (0, env_values_1.resolveStringValue)(params.database, labels.database, env),
|
|
15
|
+
user: (0, env_values_1.resolveStringValue)(params.user, labels.user, env),
|
|
16
|
+
password: (0, env_values_1.resolveStringValue)(params.password, labels.password, env),
|
|
17
|
+
ssl: (0, env_values_1.resolveBooleanValue)(params.ssl, `${labels.host} ssl`, env),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createPool(params) {
|
|
21
|
+
return new pg_1.Pool(buildPoolConfig(params));
|
|
22
|
+
}
|
|
23
|
+
function buildPoolConfig(params) {
|
|
24
|
+
return {
|
|
25
|
+
host: params.host,
|
|
26
|
+
port: params.port,
|
|
27
|
+
database: params.database,
|
|
28
|
+
user: params.user,
|
|
29
|
+
password: params.password,
|
|
30
|
+
ssl: params.ssl ? { rejectUnauthorized: false } : undefined,
|
|
31
|
+
connectionTimeoutMillis: exports.DEFAULT_CONNECTION_TIMEOUT_MS,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type Pool, type PoolClient } from "pg";
|
|
2
|
+
export interface ColumnInfo {
|
|
3
|
+
columnName: string;
|
|
4
|
+
dataType: string;
|
|
5
|
+
udtSchema: string;
|
|
6
|
+
udtName: string;
|
|
7
|
+
isNullable: boolean;
|
|
8
|
+
isGenerated: boolean;
|
|
9
|
+
generationExpression: string | null;
|
|
10
|
+
columnDefault: string | null;
|
|
11
|
+
characterMaximumLength: number | null;
|
|
12
|
+
numericPrecision: number | null;
|
|
13
|
+
numericScale: number | null;
|
|
14
|
+
datetimePrecision: number | null;
|
|
15
|
+
intervalType: string | null;
|
|
16
|
+
intervalPrecision: number | null;
|
|
17
|
+
domainSchema: string | null;
|
|
18
|
+
domainName: string | null;
|
|
19
|
+
ordinalPosition: number;
|
|
20
|
+
}
|
|
21
|
+
export interface TableMetadata {
|
|
22
|
+
schema: string;
|
|
23
|
+
table: string;
|
|
24
|
+
primaryKey: string[];
|
|
25
|
+
primaryKeyConstraintName: string | null;
|
|
26
|
+
columns: ColumnInfo[];
|
|
27
|
+
/** Column names that are not generated (safe to read/write) */
|
|
28
|
+
normalColumns: string[];
|
|
29
|
+
hasPrimaryKey: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetches full metadata for a table: columns, primary key, generated columns.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fetchTableMetadata(client: PoolClient, schema: string, table: string): Promise<TableMetadata>;
|
|
35
|
+
/**
|
|
36
|
+
* Lists all base tables in the given schema.
|
|
37
|
+
*/
|
|
38
|
+
export declare function listTables(client: PoolClient, schema: string): Promise<string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Verifies that all requested tables exist in the database.
|
|
41
|
+
* Returns the list of tables that were found.
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyTablesExist(pool: Pool, schema: string, tables: string[]): Promise<{
|
|
44
|
+
found: string[];
|
|
45
|
+
missing: string[];
|
|
46
|
+
}>;
|
|
47
|
+
export interface ResolvedTablePatterns {
|
|
48
|
+
tables: string[];
|
|
49
|
+
excludedTables: string[];
|
|
50
|
+
}
|
|
51
|
+
interface ResolveTablePatternOptions {
|
|
52
|
+
availability?: "common" | "either";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolves include/exclude table patterns against tables that exist in both databases.
|
|
56
|
+
* Supports "*" as a wildcard within a table name pattern.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveTablePatternsFromTableLists(sourceTables: string[], destTables: string[], includePatterns: string[], excludePatterns: string[], options?: ResolveTablePatternOptions): ResolvedTablePatterns;
|
|
59
|
+
/**
|
|
60
|
+
* Lists tables in both databases and resolves include/exclude patterns.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveTablePatterns(sourcePool: Pool, destPool: Pool, schema: string, includePatterns: string[], excludePatterns: string[]): Promise<ResolvedTablePatterns>;
|
|
63
|
+
export declare function resolveSchemaTablePatterns(sourcePool: Pool, destPool: Pool, schema: string, includePatterns: string[], excludePatterns: string[]): Promise<ResolvedTablePatterns>;
|
|
64
|
+
/**
|
|
65
|
+
* Fetches a map of column name -> data_type for the given table.
|
|
66
|
+
* Used by the apply tool to build type-correct WHERE clauses for guards.
|
|
67
|
+
*/
|
|
68
|
+
export declare function fetchColumnTypeMap(client: PoolClient, schema: string, table: string): Promise<Record<string, string>>;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchTableMetadata = fetchTableMetadata;
|
|
4
|
+
exports.listTables = listTables;
|
|
5
|
+
exports.verifyTablesExist = verifyTablesExist;
|
|
6
|
+
exports.resolveTablePatternsFromTableLists = resolveTablePatternsFromTableLists;
|
|
7
|
+
exports.resolveTablePatterns = resolveTablePatterns;
|
|
8
|
+
exports.resolveSchemaTablePatterns = resolveSchemaTablePatterns;
|
|
9
|
+
exports.fetchColumnTypeMap = fetchColumnTypeMap;
|
|
10
|
+
/**
|
|
11
|
+
* Fetches primary key column names for the given table.
|
|
12
|
+
*/
|
|
13
|
+
async function fetchPrimaryKey(client, schema, table) {
|
|
14
|
+
const result = await client.query(`
|
|
15
|
+
SELECT tc.constraint_name, kcu.column_name, kcu.ordinal_position AS key_seq
|
|
16
|
+
FROM information_schema.table_constraints tc
|
|
17
|
+
JOIN information_schema.key_column_usage kcu
|
|
18
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
19
|
+
AND tc.table_schema = kcu.table_schema
|
|
20
|
+
AND tc.table_name = kcu.table_name
|
|
21
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
22
|
+
AND tc.table_schema = $1
|
|
23
|
+
AND tc.table_name = $2
|
|
24
|
+
ORDER BY kcu.ordinal_position
|
|
25
|
+
`, [schema, table]);
|
|
26
|
+
return {
|
|
27
|
+
columns: result.rows.map((r) => r.column_name),
|
|
28
|
+
constraintName: result.rows[0]?.constraint_name ?? null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetches column information for the given table.
|
|
33
|
+
* Includes generated columns so they can be excluded from comparisons.
|
|
34
|
+
*/
|
|
35
|
+
async function fetchColumns(client, schema, table) {
|
|
36
|
+
const result = await client.query(`
|
|
37
|
+
SELECT
|
|
38
|
+
column_name,
|
|
39
|
+
data_type,
|
|
40
|
+
udt_schema,
|
|
41
|
+
udt_name,
|
|
42
|
+
is_nullable,
|
|
43
|
+
is_generated,
|
|
44
|
+
generation_expression,
|
|
45
|
+
column_default,
|
|
46
|
+
character_maximum_length,
|
|
47
|
+
numeric_precision,
|
|
48
|
+
numeric_scale,
|
|
49
|
+
datetime_precision,
|
|
50
|
+
interval_type,
|
|
51
|
+
interval_precision,
|
|
52
|
+
domain_schema,
|
|
53
|
+
domain_name,
|
|
54
|
+
ordinal_position
|
|
55
|
+
FROM information_schema.columns
|
|
56
|
+
WHERE table_schema = $1
|
|
57
|
+
AND table_name = $2
|
|
58
|
+
ORDER BY ordinal_position
|
|
59
|
+
`, [schema, table]);
|
|
60
|
+
return result.rows.map((r) => ({
|
|
61
|
+
columnName: r.column_name,
|
|
62
|
+
dataType: r.data_type,
|
|
63
|
+
udtSchema: r.udt_schema,
|
|
64
|
+
udtName: r.udt_name,
|
|
65
|
+
isNullable: r.is_nullable === "YES",
|
|
66
|
+
isGenerated: r.is_generated === "ALWAYS",
|
|
67
|
+
generationExpression: r.generation_expression,
|
|
68
|
+
columnDefault: r.column_default,
|
|
69
|
+
characterMaximumLength: r.character_maximum_length,
|
|
70
|
+
numericPrecision: r.numeric_precision,
|
|
71
|
+
numericScale: r.numeric_scale,
|
|
72
|
+
datetimePrecision: r.datetime_precision,
|
|
73
|
+
intervalType: r.interval_type,
|
|
74
|
+
intervalPrecision: r.interval_precision,
|
|
75
|
+
domainSchema: r.domain_schema,
|
|
76
|
+
domainName: r.domain_name,
|
|
77
|
+
ordinalPosition: r.ordinal_position,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetches full metadata for a table: columns, primary key, generated columns.
|
|
82
|
+
*/
|
|
83
|
+
async function fetchTableMetadata(client, schema, table) {
|
|
84
|
+
const [primaryKey, columns] = await Promise.all([
|
|
85
|
+
fetchPrimaryKey(client, schema, table),
|
|
86
|
+
fetchColumns(client, schema, table),
|
|
87
|
+
]);
|
|
88
|
+
const normalColumns = columns
|
|
89
|
+
.filter((c) => !c.isGenerated)
|
|
90
|
+
.map((c) => c.columnName);
|
|
91
|
+
return {
|
|
92
|
+
schema,
|
|
93
|
+
table,
|
|
94
|
+
primaryKey: primaryKey.columns,
|
|
95
|
+
primaryKeyConstraintName: primaryKey.constraintName,
|
|
96
|
+
columns,
|
|
97
|
+
normalColumns,
|
|
98
|
+
hasPrimaryKey: primaryKey.columns.length > 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Lists all base tables in the given schema.
|
|
103
|
+
*/
|
|
104
|
+
async function listTables(client, schema) {
|
|
105
|
+
const result = await client.query(`
|
|
106
|
+
SELECT table_name
|
|
107
|
+
FROM information_schema.tables
|
|
108
|
+
WHERE table_schema = $1
|
|
109
|
+
AND table_type = 'BASE TABLE'
|
|
110
|
+
ORDER BY table_name
|
|
111
|
+
`, [schema]);
|
|
112
|
+
return result.rows.map((r) => r.table_name);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Verifies that all requested tables exist in the database.
|
|
116
|
+
* Returns the list of tables that were found.
|
|
117
|
+
*/
|
|
118
|
+
async function verifyTablesExist(pool, schema, tables) {
|
|
119
|
+
const client = await pool.connect();
|
|
120
|
+
try {
|
|
121
|
+
const existing = await listTables(client, schema);
|
|
122
|
+
const existingSet = new Set(existing);
|
|
123
|
+
const found = tables.filter((t) => existingSet.has(t));
|
|
124
|
+
const missing = tables.filter((t) => !existingSet.has(t));
|
|
125
|
+
return { found, missing };
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
client.release();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolves include/exclude table patterns against tables that exist in both databases.
|
|
133
|
+
* Supports "*" as a wildcard within a table name pattern.
|
|
134
|
+
*/
|
|
135
|
+
function resolveTablePatternsFromTableLists(sourceTables, destTables, includePatterns, excludePatterns, options = {}) {
|
|
136
|
+
const availability = options.availability ?? "common";
|
|
137
|
+
const sourceSet = new Set(sourceTables);
|
|
138
|
+
const destSet = new Set(destTables);
|
|
139
|
+
const availableTables = availability === "either"
|
|
140
|
+
? Array.from(new Set([...sourceTables, ...destTables])).sort()
|
|
141
|
+
: sourceTables.filter((table) => destSet.has(table));
|
|
142
|
+
const resolvedTables = [];
|
|
143
|
+
for (const pattern of includePatterns) {
|
|
144
|
+
const matches = resolvePattern(pattern, availableTables);
|
|
145
|
+
if (matches.length > 0) {
|
|
146
|
+
for (const match of matches) {
|
|
147
|
+
if (!resolvedTables.includes(match)) {
|
|
148
|
+
resolvedTables.push(match);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!hasWildcard(pattern)) {
|
|
154
|
+
const missingFrom = [];
|
|
155
|
+
if (!sourceSet.has(pattern))
|
|
156
|
+
missingFrom.push("source");
|
|
157
|
+
if (!destSet.has(pattern))
|
|
158
|
+
missingFrom.push("destination");
|
|
159
|
+
throw new Error(`Table "${pattern}" was not found in the ${missingFrom.join(" and ")} database.`);
|
|
160
|
+
}
|
|
161
|
+
if (availability === "either") {
|
|
162
|
+
throw new Error(`Table pattern "${pattern}" matched no tables present in either database.`);
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`Table pattern "${pattern}" matched no tables present in both databases.`);
|
|
165
|
+
}
|
|
166
|
+
const excludedTables = Array.from(new Set(excludePatterns.flatMap((pattern) => resolvePattern(pattern, resolvedTables))));
|
|
167
|
+
const tables = resolvedTables.filter((table) => !excludedTables.includes(table));
|
|
168
|
+
if (tables.length === 0) {
|
|
169
|
+
throw new Error("No tables remain after applying include and exclude table patterns.");
|
|
170
|
+
}
|
|
171
|
+
return { tables, excludedTables };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Lists tables in both databases and resolves include/exclude patterns.
|
|
175
|
+
*/
|
|
176
|
+
async function resolveTablePatterns(sourcePool, destPool, schema, includePatterns, excludePatterns) {
|
|
177
|
+
const [sourceClient, destClient] = await Promise.all([
|
|
178
|
+
sourcePool.connect(),
|
|
179
|
+
destPool.connect(),
|
|
180
|
+
]);
|
|
181
|
+
try {
|
|
182
|
+
const [sourceTables, destTables] = await Promise.all([
|
|
183
|
+
listTables(sourceClient, schema),
|
|
184
|
+
listTables(destClient, schema),
|
|
185
|
+
]);
|
|
186
|
+
return resolveTablePatternsFromTableLists(sourceTables, destTables, includePatterns, excludePatterns);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
sourceClient.release();
|
|
190
|
+
destClient.release();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function resolveSchemaTablePatterns(sourcePool, destPool, schema, includePatterns, excludePatterns) {
|
|
194
|
+
const [sourceClient, destClient] = await Promise.all([
|
|
195
|
+
sourcePool.connect(),
|
|
196
|
+
destPool.connect(),
|
|
197
|
+
]);
|
|
198
|
+
try {
|
|
199
|
+
const [sourceTables, destTables] = await Promise.all([
|
|
200
|
+
listTables(sourceClient, schema),
|
|
201
|
+
listTables(destClient, schema),
|
|
202
|
+
]);
|
|
203
|
+
return resolveTablePatternsFromTableLists(sourceTables, destTables, includePatterns, excludePatterns, {
|
|
204
|
+
availability: "either",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
sourceClient.release();
|
|
209
|
+
destClient.release();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Fetches a map of column name -> data_type for the given table.
|
|
214
|
+
* Used by the apply tool to build type-correct WHERE clauses for guards.
|
|
215
|
+
*/
|
|
216
|
+
async function fetchColumnTypeMap(client, schema, table) {
|
|
217
|
+
const columns = await fetchColumns(client, schema, table);
|
|
218
|
+
const map = {};
|
|
219
|
+
for (const col of columns) {
|
|
220
|
+
map[col.columnName] = col.dataType;
|
|
221
|
+
}
|
|
222
|
+
return map;
|
|
223
|
+
}
|
|
224
|
+
function resolvePattern(pattern, availableTables) {
|
|
225
|
+
if (!hasWildcard(pattern)) {
|
|
226
|
+
return availableTables.includes(pattern) ? [pattern] : [];
|
|
227
|
+
}
|
|
228
|
+
const matcher = buildWildcardRegex(pattern);
|
|
229
|
+
return availableTables.filter((table) => matcher.test(table));
|
|
230
|
+
}
|
|
231
|
+
function hasWildcard(pattern) {
|
|
232
|
+
return pattern.includes("*");
|
|
233
|
+
}
|
|
234
|
+
function buildWildcardRegex(pattern) {
|
|
235
|
+
const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
236
|
+
return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=metadata.js.map
|