frg-data-diff 2.0.0 → 2.1.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/dist/cli/root.js CHANGED
@@ -49,6 +49,7 @@ const commandFiles = {
49
49
  apply: "apply.js",
50
50
  sql: "sql.js",
51
51
  "pg-triggers": "pg-triggers.js",
52
+ "pg-views": "pg-views.js",
52
53
  };
53
54
  const helpText = `frg-data-diff
54
55
 
@@ -62,6 +63,7 @@ Commands:
62
63
  apply Read a JSON diff file and safely apply it to a destination PostgreSQL database.
63
64
  sql Read a JSON diff file and write a plain SQL script for manual review and execution.
64
65
  pg-triggers Compare PostgreSQL triggers and functions and write a SQL diff script.
66
+ pg-views Compare PostgreSQL view definitions and write a SQL diff script.
65
67
  version Print the package version.
66
68
 
67
69
  Basic usage:
@@ -71,12 +73,14 @@ Basic usage:
71
73
  npx frg-data-diff apply --execute
72
74
  npx frg-data-diff sql --yes
73
75
  npx frg-data-diff pg-triggers
76
+ npx frg-data-diff pg-views
74
77
  npx frg-data-diff version
75
78
 
76
79
  Config file:
77
80
  .frg-data-diff.config.json
78
81
 
79
82
  Commands look for .frg-data-diff.config.json in the current directory.
83
+ The config file accepts // line comments and /* block comments */.
80
84
  Create it by running a command with --config or with CLI args on first run.
81
85
 
82
86
  How to generate a diff:
@@ -149,6 +153,8 @@ Config file example (.frg-data-diff.config.json):
149
153
  "schemaDiffOutput": "frg-schema-diff.json",
150
154
  "pgTriggersTables": ["my_table"],
151
155
  "pgTriggersOutput": "frg-triggers-diff.sql",
156
+ "pgViews": ["*"],
157
+ "pgViewsOutput": "frg-views-diff.sql",
152
158
  "includeDeletes": true,
153
159
  "pretty": true
154
160
  },
@@ -19,6 +19,8 @@ export declare const generatorConfigSchema: z.ZodObject<{
19
19
  schemaDiffExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
20
20
  pgTriggersTables: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
21
21
  pgTriggersExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
22
+ pgViews: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
23
+ pgViewsExclude: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
22
24
  ignoreColumns: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
23
25
  tablesWhereDataFilters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
24
26
  includeDeletes: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
@@ -26,9 +28,11 @@ export declare const generatorConfigSchema: z.ZodObject<{
26
28
  output: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
27
29
  schemaDiffOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
28
30
  pgTriggersOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
31
+ pgViewsOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
29
32
  pretty: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
30
33
  generateSql: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
31
34
  generatePgTriggers: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
35
+ generatePgViews: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
32
36
  }, "strip", z.ZodTypeAny, {
33
37
  schema: string;
34
38
  includeDeletes: string | boolean;
@@ -54,11 +58,15 @@ export declare const generatorConfigSchema: z.ZodObject<{
54
58
  schemaDiffExcludeTables?: string[] | null | undefined;
55
59
  pgTriggersTables?: string[] | undefined;
56
60
  pgTriggersExcludeTables?: string[] | null | undefined;
61
+ pgViews?: string[] | undefined;
62
+ pgViewsExclude?: string[] | null | undefined;
57
63
  tablesWhereDataFilters?: Record<string, string> | undefined;
58
64
  schemaDiffOutput?: string | undefined;
59
65
  pgTriggersOutput?: string | undefined;
66
+ pgViewsOutput?: string | undefined;
60
67
  generateSql?: string | boolean | undefined;
61
68
  generatePgTriggers?: string | boolean | undefined;
69
+ generatePgViews?: string | boolean | undefined;
62
70
  }, {
63
71
  tables: string[];
64
72
  sourcePgHost: string;
@@ -80,15 +88,19 @@ export declare const generatorConfigSchema: z.ZodObject<{
80
88
  schemaDiffExcludeTables?: string[] | null | undefined;
81
89
  pgTriggersTables?: string[] | undefined;
82
90
  pgTriggersExcludeTables?: string[] | null | undefined;
91
+ pgViews?: string[] | undefined;
92
+ pgViewsExclude?: string[] | null | undefined;
83
93
  ignoreColumns?: string[] | null | undefined;
84
94
  tablesWhereDataFilters?: Record<string, string> | undefined;
85
95
  skipMissingPk?: string | boolean | undefined;
86
96
  output?: string | undefined;
87
97
  schemaDiffOutput?: string | undefined;
88
98
  pgTriggersOutput?: string | undefined;
99
+ pgViewsOutput?: string | undefined;
89
100
  pretty?: string | boolean | undefined;
90
101
  generateSql?: string | boolean | undefined;
91
102
  generatePgTriggers?: string | boolean | undefined;
103
+ generatePgViews?: string | boolean | undefined;
92
104
  }>;
93
105
  export declare const applyConfigSchema: z.ZodObject<{
94
106
  destPgHost: z.ZodEffects<z.ZodString, string, string>;
@@ -158,6 +170,8 @@ export declare const configSchema: z.ZodObject<{
158
170
  schemaDiffExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
159
171
  pgTriggersTables: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
160
172
  pgTriggersExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
173
+ pgViews: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
174
+ pgViewsExclude: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
161
175
  ignoreColumns: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
162
176
  tablesWhereDataFilters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
163
177
  includeDeletes: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
@@ -165,9 +179,11 @@ export declare const configSchema: z.ZodObject<{
165
179
  output: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
166
180
  schemaDiffOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
167
181
  pgTriggersOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
182
+ pgViewsOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
168
183
  pretty: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
169
184
  generateSql: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
170
185
  generatePgTriggers: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
186
+ generatePgViews: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
171
187
  }, "strip", z.ZodTypeAny, {
172
188
  schema: string;
173
189
  includeDeletes: string | boolean;
@@ -193,11 +209,15 @@ export declare const configSchema: z.ZodObject<{
193
209
  schemaDiffExcludeTables?: string[] | null | undefined;
194
210
  pgTriggersTables?: string[] | undefined;
195
211
  pgTriggersExcludeTables?: string[] | null | undefined;
212
+ pgViews?: string[] | undefined;
213
+ pgViewsExclude?: string[] | null | undefined;
196
214
  tablesWhereDataFilters?: Record<string, string> | undefined;
197
215
  schemaDiffOutput?: string | undefined;
198
216
  pgTriggersOutput?: string | undefined;
217
+ pgViewsOutput?: string | undefined;
199
218
  generateSql?: string | boolean | undefined;
200
219
  generatePgTriggers?: string | boolean | undefined;
220
+ generatePgViews?: string | boolean | undefined;
201
221
  }, {
202
222
  tables: string[];
203
223
  sourcePgHost: string;
@@ -219,15 +239,19 @@ export declare const configSchema: z.ZodObject<{
219
239
  schemaDiffExcludeTables?: string[] | null | undefined;
220
240
  pgTriggersTables?: string[] | undefined;
221
241
  pgTriggersExcludeTables?: string[] | null | undefined;
242
+ pgViews?: string[] | undefined;
243
+ pgViewsExclude?: string[] | null | undefined;
222
244
  ignoreColumns?: string[] | null | undefined;
223
245
  tablesWhereDataFilters?: Record<string, string> | undefined;
224
246
  skipMissingPk?: string | boolean | undefined;
225
247
  output?: string | undefined;
226
248
  schemaDiffOutput?: string | undefined;
227
249
  pgTriggersOutput?: string | undefined;
250
+ pgViewsOutput?: string | undefined;
228
251
  pretty?: string | boolean | undefined;
229
252
  generateSql?: string | boolean | undefined;
230
253
  generatePgTriggers?: string | boolean | undefined;
254
+ generatePgViews?: string | boolean | undefined;
231
255
  }>;
232
256
  apply: z.ZodObject<{
233
257
  destPgHost: z.ZodEffects<z.ZodString, string, string>;
@@ -302,11 +326,15 @@ export declare const configSchema: z.ZodObject<{
302
326
  schemaDiffExcludeTables?: string[] | null | undefined;
303
327
  pgTriggersTables?: string[] | undefined;
304
328
  pgTriggersExcludeTables?: string[] | null | undefined;
329
+ pgViews?: string[] | undefined;
330
+ pgViewsExclude?: string[] | null | undefined;
305
331
  tablesWhereDataFilters?: Record<string, string> | undefined;
306
332
  schemaDiffOutput?: string | undefined;
307
333
  pgTriggersOutput?: string | undefined;
334
+ pgViewsOutput?: string | undefined;
308
335
  generateSql?: string | boolean | undefined;
309
336
  generatePgTriggers?: string | boolean | undefined;
337
+ generatePgViews?: string | boolean | undefined;
310
338
  };
311
339
  apply: {
312
340
  destPgHost: string;
@@ -347,15 +375,19 @@ export declare const configSchema: z.ZodObject<{
347
375
  schemaDiffExcludeTables?: string[] | null | undefined;
348
376
  pgTriggersTables?: string[] | undefined;
349
377
  pgTriggersExcludeTables?: string[] | null | undefined;
378
+ pgViews?: string[] | undefined;
379
+ pgViewsExclude?: string[] | null | undefined;
350
380
  ignoreColumns?: string[] | null | undefined;
351
381
  tablesWhereDataFilters?: Record<string, string> | undefined;
352
382
  skipMissingPk?: string | boolean | undefined;
353
383
  output?: string | undefined;
354
384
  schemaDiffOutput?: string | undefined;
355
385
  pgTriggersOutput?: string | undefined;
386
+ pgViewsOutput?: string | undefined;
356
387
  pretty?: string | boolean | undefined;
357
388
  generateSql?: string | boolean | undefined;
358
389
  generatePgTriggers?: string | boolean | undefined;
390
+ generatePgViews?: string | boolean | undefined;
359
391
  };
360
392
  apply: {
361
393
  destPgHost: string;
@@ -46,6 +46,8 @@ exports.generatorConfigSchema = zod_1.z.object({
46
46
  schemaDiffExcludeTables: zod_1.z.array(envCapableListItem).nullable().optional(),
47
47
  pgTriggersTables: zod_1.z.array(envCapableListItem).optional(),
48
48
  pgTriggersExcludeTables: zod_1.z.array(envCapableListItem).nullable().optional(),
49
+ pgViews: zod_1.z.array(envCapableListItem).optional(),
50
+ pgViewsExclude: zod_1.z.array(envCapableListItem).nullable().optional(),
49
51
  ignoreColumns: zod_1.z.array(envCapableListItem).nullable().default([]),
50
52
  tablesWhereDataFilters: tableWhereDataFilters.optional(),
51
53
  includeDeletes: envCapableBoolean.default(true),
@@ -53,9 +55,11 @@ exports.generatorConfigSchema = zod_1.z.object({
53
55
  output: envCapableString.default("frg-data-diff.json"),
54
56
  schemaDiffOutput: envCapableString.optional(),
55
57
  pgTriggersOutput: envCapableString.optional(),
58
+ pgViewsOutput: envCapableString.optional(),
56
59
  pretty: envCapableBoolean.default(true),
57
60
  generateSql: envCapableBoolean.optional(),
58
61
  generatePgTriggers: envCapableBoolean.optional(),
62
+ generatePgViews: envCapableBoolean.optional(),
59
63
  });
60
64
  exports.applyConfigSchema = zod_1.z.object({
61
65
  destPgHost: envCapableString,
@@ -9,4 +9,5 @@ export declare function findConfigFile(cwd?: string): string | null;
9
9
  * Exits with a clear error if validation fails.
10
10
  */
11
11
  export declare function loadConfig(configPath: string): Config;
12
+ export declare function stripJsonComments(input: string): string;
12
13
  //# sourceMappingURL=load-config.d.ts.map
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DEFAULT_CONFIG_FILENAME = void 0;
37
37
  exports.findConfigFile = findConfigFile;
38
38
  exports.loadConfig = loadConfig;
39
+ exports.stripJsonComments = stripJsonComments;
39
40
  const fs = __importStar(require("fs"));
40
41
  const path = __importStar(require("path"));
41
42
  const config_schema_1 = require("./config-schema");
@@ -67,7 +68,7 @@ function loadConfig(configPath) {
67
68
  }
68
69
  let parsed;
69
70
  try {
70
- parsed = JSON.parse(raw);
71
+ parsed = JSON.parse(stripJsonComments(raw));
71
72
  }
72
73
  catch (err) {
73
74
  console.error(`Failed to parse config file as JSON: ${configPath}`);
@@ -84,4 +85,80 @@ function loadConfig(configPath) {
84
85
  }
85
86
  return result.data;
86
87
  }
88
+ function stripJsonComments(input) {
89
+ let output = "";
90
+ let inString = false;
91
+ let escaped = false;
92
+ let inLineComment = false;
93
+ let inBlockComment = false;
94
+ for (let index = 0; index < input.length; index++) {
95
+ const char = input[index];
96
+ const next = input[index + 1];
97
+ if (inLineComment) {
98
+ if (isLineBreak(char)) {
99
+ inLineComment = false;
100
+ output += char;
101
+ }
102
+ else {
103
+ output += " ";
104
+ }
105
+ continue;
106
+ }
107
+ if (inBlockComment) {
108
+ if (char === "*" && next === "/") {
109
+ output += " ";
110
+ index++;
111
+ inBlockComment = false;
112
+ continue;
113
+ }
114
+ if (isLineBreak(char)) {
115
+ output += char;
116
+ }
117
+ else {
118
+ output += " ";
119
+ }
120
+ continue;
121
+ }
122
+ if (inString) {
123
+ output += char;
124
+ if (escaped) {
125
+ escaped = false;
126
+ continue;
127
+ }
128
+ if (char === "\\") {
129
+ escaped = true;
130
+ continue;
131
+ }
132
+ if (char === '"') {
133
+ inString = false;
134
+ }
135
+ continue;
136
+ }
137
+ if (char === '"') {
138
+ inString = true;
139
+ output += char;
140
+ continue;
141
+ }
142
+ if (char === "/" && next === "/") {
143
+ output += " ";
144
+ index++;
145
+ inLineComment = true;
146
+ continue;
147
+ }
148
+ if (char === "/" && next === "*") {
149
+ output += " ";
150
+ index++;
151
+ inBlockComment = true;
152
+ continue;
153
+ }
154
+ output += char;
155
+ }
156
+ return output;
157
+ }
158
+ function isLineBreak(char) {
159
+ if (char === "\n") {
160
+ return true;
161
+ }
162
+ return char === "\r";
163
+ }
87
164
  //# sourceMappingURL=load-config.js.map
@@ -22,6 +22,8 @@ export interface ResolvedGeneratorOptions {
22
22
  schemaDiffExcludeTables: string[];
23
23
  pgTriggersTables: string[];
24
24
  pgTriggersExcludeTables: string[];
25
+ pgViews: string[];
26
+ pgViewsExclude: string[];
25
27
  ignoreColumns: string[];
26
28
  tablesWhereDataFilters?: Record<string, string>;
27
29
  includeDeletes: boolean | string;
@@ -29,12 +31,14 @@ export interface ResolvedGeneratorOptions {
29
31
  output: string;
30
32
  schemaDiffOutput: string;
31
33
  pgTriggersOutput: string;
34
+ pgViewsOutput: string;
32
35
  pretty: boolean | string;
33
36
  generateSql?: boolean | string;
34
37
  generatePgTriggers?: boolean | string;
38
+ generatePgViews?: boolean | string;
35
39
  verbose: boolean;
36
40
  }
37
- export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions, "sourcePgSsl" | "destPgSsl" | "includeDeletes" | "skipMissingPk" | "pretty" | "generateSql" | "generatePgTriggers"> {
41
+ export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions, "sourcePgSsl" | "destPgSsl" | "includeDeletes" | "skipMissingPk" | "pretty" | "generateSql" | "generatePgTriggers" | "generatePgViews"> {
38
42
  sourcePgSsl: boolean;
39
43
  destPgSsl: boolean;
40
44
  includeDeletes: boolean;
@@ -42,12 +46,14 @@ export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions,
42
46
  pretty: boolean;
43
47
  generateSql?: boolean;
44
48
  generatePgTriggers?: boolean;
49
+ generatePgViews?: boolean;
45
50
  }
46
51
  export type OptionalListInput = string[] | null;
47
- export type GeneratorOptionInput = Partial<Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "ignoreColumns"> & {
52
+ export type GeneratorOptionInput = Partial<Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "pgViewsExclude" | "ignoreColumns"> & {
48
53
  excludeTables: OptionalListInput;
49
54
  schemaDiffExcludeTables: OptionalListInput;
50
55
  pgTriggersExcludeTables: OptionalListInput;
56
+ pgViewsExclude: OptionalListInput;
51
57
  ignoreColumns: OptionalListInput;
52
58
  }>;
53
59
  /**
@@ -18,6 +18,8 @@ function resolveGeneratorOptions(config, cliArgs) {
18
18
  schemaDiffExcludeTables: [],
19
19
  pgTriggersTables: [],
20
20
  pgTriggersExcludeTables: [],
21
+ pgViews: [],
22
+ pgViewsExclude: [],
21
23
  ignoreColumns: [],
22
24
  tablesWhereDataFilters: {},
23
25
  includeDeletes: true,
@@ -25,6 +27,7 @@ function resolveGeneratorOptions(config, cliArgs) {
25
27
  output: "frg-data-diff.json",
26
28
  schemaDiffOutput: "frg-schema-diff.json",
27
29
  pgTriggersOutput: "frg-triggers-diff.sql",
30
+ pgViewsOutput: "frg-views-diff.sql",
28
31
  pretty: true,
29
32
  verbose: false,
30
33
  };
@@ -34,6 +37,8 @@ function resolveGeneratorOptions(config, cliArgs) {
34
37
  const schemaDiffExcludeTables = resolveOptionalListOption(cliArgs.schemaDiffExcludeTables, config?.schemaDiffExcludeTables, excludeTables);
35
38
  const pgTriggersTables = resolveInheritedTableListOption(cliArgs.pgTriggersTables, config?.pgTriggersTables, tables);
36
39
  const pgTriggersExcludeTables = resolveOptionalListOption(cliArgs.pgTriggersExcludeTables, config?.pgTriggersExcludeTables, excludeTables);
40
+ const pgViews = resolveListOption(cliArgs.pgViews, config?.pgViews, defaults.pgViews);
41
+ const pgViewsExclude = resolveOptionalListOption(cliArgs.pgViewsExclude, config?.pgViewsExclude, defaults.pgViewsExclude);
37
42
  const ignoreColumns = resolveOptionalListOption(cliArgs.ignoreColumns, config?.ignoreColumns, defaults.ignoreColumns);
38
43
  return {
39
44
  sourcePgHost: cliArgs.sourcePgHost ?? config?.sourcePgHost ?? "",
@@ -55,6 +60,8 @@ function resolveGeneratorOptions(config, cliArgs) {
55
60
  schemaDiffExcludeTables,
56
61
  pgTriggersTables,
57
62
  pgTriggersExcludeTables,
63
+ pgViews,
64
+ pgViewsExclude,
58
65
  ignoreColumns,
59
66
  tablesWhereDataFilters: cliArgs.tablesWhereDataFilters ??
60
67
  config?.tablesWhereDataFilters ??
@@ -70,12 +77,25 @@ function resolveGeneratorOptions(config, cliArgs) {
70
77
  pgTriggersOutput: cliArgs.pgTriggersOutput ??
71
78
  config?.pgTriggersOutput ??
72
79
  defaults.pgTriggersOutput,
80
+ pgViewsOutput: cliArgs.pgViewsOutput ??
81
+ config?.pgViewsOutput ??
82
+ defaults.pgViewsOutput,
73
83
  pretty: cliArgs.pretty ?? config?.pretty ?? defaults.pretty,
74
84
  generateSql: cliArgs.generateSql ?? config?.generateSql,
75
85
  generatePgTriggers: cliArgs.generatePgTriggers ?? config?.generatePgTriggers,
86
+ generatePgViews: cliArgs.generatePgViews ?? config?.generatePgViews,
76
87
  verbose: cliArgs.verbose ?? defaults.verbose,
77
88
  };
78
89
  }
90
+ function resolveListOption(cliValue, configValue, fallback) {
91
+ if (cliValue !== undefined) {
92
+ return cliValue;
93
+ }
94
+ if (configValue !== undefined) {
95
+ return configValue;
96
+ }
97
+ return fallback;
98
+ }
79
99
  function resolveOptionalListOption(cliValue, configValue, fallback) {
80
100
  if (cliValue !== undefined) {
81
101
  return cliValue ?? [];
@@ -137,6 +157,10 @@ function resolveRuntimeGeneratorOptions(options) {
137
157
  if (options.generatePgTriggers !== undefined) {
138
158
  generatePgTriggers = (0, env_values_1.resolveBooleanValue)(options.generatePgTriggers, "generate pg triggers");
139
159
  }
160
+ let generatePgViews;
161
+ if (options.generatePgViews !== undefined) {
162
+ generatePgViews = (0, env_values_1.resolveBooleanValue)(options.generatePgViews, "generate pg views");
163
+ }
140
164
  return {
141
165
  ...options,
142
166
  schema: (0, env_values_1.resolveStringValue)(options.schema, "schema"),
@@ -146,6 +170,8 @@ function resolveRuntimeGeneratorOptions(options) {
146
170
  schemaDiffExcludeTables: (0, env_values_1.resolveListValues)(options.schemaDiffExcludeTables, "schema diff exclude tables"),
147
171
  pgTriggersTables: (0, env_values_1.resolveListValues)(options.pgTriggersTables, "pg triggers tables"),
148
172
  pgTriggersExcludeTables: (0, env_values_1.resolveListValues)(options.pgTriggersExcludeTables, "pg triggers exclude tables"),
173
+ pgViews: (0, env_values_1.resolveListValues)(options.pgViews, "pg views"),
174
+ pgViewsExclude: (0, env_values_1.resolveListValues)(options.pgViewsExclude, "pg views exclude"),
149
175
  ignoreColumns: (0, env_values_1.resolveListValues)(options.ignoreColumns, "ignored columns"),
150
176
  sourcePgSsl: (0, env_values_1.resolveBooleanValue)(options.sourcePgSsl, "source ssl"),
151
177
  destPgSsl: (0, env_values_1.resolveBooleanValue)(options.destPgSsl, "destination ssl"),
@@ -154,9 +180,11 @@ function resolveRuntimeGeneratorOptions(options) {
154
180
  output: (0, env_values_1.resolveStringValue)(options.output, "output file"),
155
181
  schemaDiffOutput: (0, env_values_1.resolveStringValue)(options.schemaDiffOutput, "schema diff output file"),
156
182
  pgTriggersOutput: (0, env_values_1.resolveStringValue)(options.pgTriggersOutput, "pg triggers output file"),
183
+ pgViewsOutput: (0, env_values_1.resolveStringValue)(options.pgViewsOutput, "pg views output file"),
157
184
  pretty: (0, env_values_1.resolveBooleanValue)(options.pretty, "pretty-print json"),
158
185
  generateSql,
159
186
  generatePgTriggers,
187
+ generatePgViews,
160
188
  };
161
189
  }
162
190
  function resolveRuntimeApplyOptions(options) {
@@ -1,8 +1,9 @@
1
1
  import { type ResolvedGeneratorOptions, type ResolvedApplyOptions, type OptionalListInput } from "./resolve-options";
2
- type WritableGeneratorOptions = Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "ignoreColumns"> & {
2
+ type WritableGeneratorOptions = Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "pgViewsExclude" | "ignoreColumns"> & {
3
3
  excludeTables: OptionalListInput;
4
4
  schemaDiffExcludeTables: OptionalListInput;
5
5
  pgTriggersExcludeTables: OptionalListInput;
6
+ pgViewsExclude: OptionalListInput;
6
7
  ignoreColumns: OptionalListInput;
7
8
  };
8
9
  /**
@@ -62,15 +62,19 @@ function buildConfigFile(generatorOptions, applyOptions) {
62
62
  schemaDiffExcludeTables: generatorOptions.schemaDiffExcludeTables ?? [],
63
63
  pgTriggersTables: generatorOptions.pgTriggersTables,
64
64
  pgTriggersExcludeTables: generatorOptions.pgTriggersExcludeTables ?? [],
65
+ pgViews: generatorOptions.pgViews,
66
+ pgViewsExclude: generatorOptions.pgViewsExclude ?? [],
65
67
  ignoreColumns: generatorOptions.ignoreColumns ?? [],
66
68
  includeDeletes: generatorOptions.includeDeletes,
67
69
  skipMissingPk: generatorOptions.skipMissingPk,
68
70
  output: generatorOptions.output,
69
71
  schemaDiffOutput: generatorOptions.schemaDiffOutput,
70
72
  pgTriggersOutput: generatorOptions.pgTriggersOutput,
73
+ pgViewsOutput: generatorOptions.pgViewsOutput,
71
74
  pretty: generatorOptions.pretty,
72
75
  generateSql: generatorOptions.generateSql,
73
76
  generatePgTriggers: generatorOptions.generatePgTriggers,
77
+ generatePgViews: generatorOptions.generatePgViews,
74
78
  };
75
79
  return {
76
80
  format: "frg-data-diff-config/v1",
@@ -0,0 +1,24 @@
1
+ import { type Pool, type PoolClient } from "pg";
2
+ export type PgViewType = "view" | "materialized_view";
3
+ export interface PgViewInfo {
4
+ viewSchema: string;
5
+ viewName: string;
6
+ viewType: PgViewType;
7
+ viewDefinition: string;
8
+ }
9
+ export interface FetchPgViewsOptions {
10
+ onViewStart?: (viewName: string, viewPosition: number, totalViews: number) => void;
11
+ }
12
+ export interface ResolvedPgViewPatterns {
13
+ views: string[];
14
+ excludedViews: string[];
15
+ }
16
+ /**
17
+ * Lists regular and materialized views in the given schema.
18
+ */
19
+ export declare function listPgViews(client: PoolClient, schema: string): Promise<string[]>;
20
+ export declare function fetchPgView(client: PoolClient, schema: string, viewName: string): Promise<PgViewInfo | undefined>;
21
+ export declare function fetchPgViews(client: PoolClient, schema: string, views: string[], options?: FetchPgViewsOptions): Promise<PgViewInfo[]>;
22
+ export declare function resolvePgViewPatterns(sourcePool: Pool, destPool: Pool, schema: string, includePatterns: string[], excludePatterns: string[]): Promise<ResolvedPgViewPatterns>;
23
+ export declare function resolvePgViewPatternsFromViewLists(sourceViews: string[], destViews: string[], includePatterns: string[], excludePatterns: string[]): ResolvedPgViewPatterns;
24
+ //# sourceMappingURL=pg-views.d.ts.map
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listPgViews = listPgViews;
4
+ exports.fetchPgView = fetchPgView;
5
+ exports.fetchPgViews = fetchPgViews;
6
+ exports.resolvePgViewPatterns = resolvePgViewPatterns;
7
+ exports.resolvePgViewPatternsFromViewLists = resolvePgViewPatternsFromViewLists;
8
+ /**
9
+ * Lists regular and materialized views in the given schema.
10
+ */
11
+ async function listPgViews(client, schema) {
12
+ const result = await client.query(`
13
+ SELECT cls.relname AS view_name
14
+ FROM pg_class cls
15
+ JOIN pg_namespace ns ON ns.oid = cls.relnamespace
16
+ WHERE ns.nspname = $1
17
+ AND cls.relkind IN ('v', 'm')
18
+ ORDER BY cls.relname
19
+ `, [schema]);
20
+ return result.rows.map((row) => row.view_name);
21
+ }
22
+ async function fetchPgView(client, schema, viewName) {
23
+ const result = await client.query(`
24
+ SELECT
25
+ ns.nspname AS view_schema,
26
+ cls.relname AS view_name,
27
+ CASE cls.relkind
28
+ WHEN 'm' THEN 'materialized_view'
29
+ ELSE 'view'
30
+ END AS view_type,
31
+ pg_get_viewdef(cls.oid, true) AS view_definition
32
+ FROM pg_class cls
33
+ JOIN pg_namespace ns ON ns.oid = cls.relnamespace
34
+ WHERE ns.nspname = $1
35
+ AND cls.relname = $2
36
+ AND cls.relkind IN ('v', 'm')
37
+ `, [schema, viewName]);
38
+ const row = result.rows[0];
39
+ if (!row) {
40
+ return undefined;
41
+ }
42
+ return {
43
+ viewSchema: row.view_schema,
44
+ viewName: row.view_name,
45
+ viewType: row.view_type,
46
+ viewDefinition: row.view_definition,
47
+ };
48
+ }
49
+ async function fetchPgViews(client, schema, views, options = {}) {
50
+ const results = [];
51
+ for (const [viewIndex, viewName] of views.entries()) {
52
+ if (options.onViewStart) {
53
+ options.onViewStart(viewName, viewIndex + 1, views.length);
54
+ }
55
+ const view = await fetchPgView(client, schema, viewName);
56
+ if (view) {
57
+ results.push(view);
58
+ }
59
+ }
60
+ return results;
61
+ }
62
+ async function resolvePgViewPatterns(sourcePool, destPool, schema, includePatterns, excludePatterns) {
63
+ const [sourceClient, destClient] = await Promise.all([
64
+ sourcePool.connect(),
65
+ destPool.connect(),
66
+ ]);
67
+ try {
68
+ const [sourceViews, destViews] = await Promise.all([
69
+ listPgViews(sourceClient, schema),
70
+ listPgViews(destClient, schema),
71
+ ]);
72
+ return resolvePgViewPatternsFromViewLists(sourceViews, destViews, includePatterns, excludePatterns);
73
+ }
74
+ finally {
75
+ sourceClient.release();
76
+ destClient.release();
77
+ }
78
+ }
79
+ function resolvePgViewPatternsFromViewLists(sourceViews, destViews, includePatterns, excludePatterns) {
80
+ const sourceSet = new Set(sourceViews);
81
+ const destSet = new Set(destViews);
82
+ const availableViews = Array.from(new Set([...sourceViews, ...destViews])).sort();
83
+ let requestedPatterns = includePatterns;
84
+ if (requestedPatterns.length === 0) {
85
+ requestedPatterns = ["*"];
86
+ }
87
+ const resolvedViews = [];
88
+ for (const pattern of requestedPatterns) {
89
+ const matches = resolvePattern(pattern, availableViews);
90
+ if (matches.length > 0) {
91
+ addUniqueMatches(resolvedViews, matches);
92
+ continue;
93
+ }
94
+ if (hasWildcard(pattern)) {
95
+ if (availableViews.length === 0 || pattern === "*") {
96
+ continue;
97
+ }
98
+ throw new Error(`View pattern "${pattern}" matched no views.`);
99
+ }
100
+ const missingFrom = [];
101
+ if (!sourceSet.has(pattern)) {
102
+ missingFrom.push("source");
103
+ }
104
+ if (!destSet.has(pattern)) {
105
+ missingFrom.push("destination");
106
+ }
107
+ throw new Error(`View "${pattern}" was not found in the ${missingFrom.join(" and ")} database.`);
108
+ }
109
+ const excludedViews = Array.from(new Set(excludePatterns.flatMap((pattern) => resolvePattern(pattern, resolvedViews))));
110
+ const views = resolvedViews.filter((view) => !excludedViews.includes(view));
111
+ return { views, excludedViews };
112
+ }
113
+ function addUniqueMatches(values, matches) {
114
+ for (const match of matches) {
115
+ if (!values.includes(match)) {
116
+ values.push(match);
117
+ }
118
+ }
119
+ }
120
+ function resolvePattern(pattern, availableViews) {
121
+ if (!hasWildcard(pattern)) {
122
+ if (availableViews.includes(pattern)) {
123
+ return [pattern];
124
+ }
125
+ return [];
126
+ }
127
+ const matcher = buildWildcardRegex(pattern);
128
+ return availableViews.filter((view) => matcher.test(view));
129
+ }
130
+ function hasWildcard(pattern) {
131
+ return pattern.includes("*");
132
+ }
133
+ function buildWildcardRegex(pattern) {
134
+ const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
135
+ return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
136
+ }
137
+ //# sourceMappingURL=pg-views.js.map
@@ -0,0 +1,11 @@
1
+ import { type Pool } from "pg";
2
+ export interface GeneratePgViewsDiffOptions {
3
+ schema: string;
4
+ views: string[];
5
+ excludeViews: string[];
6
+ verbose: boolean;
7
+ onProgress?: (message: string) => void;
8
+ onVerboseProgress?: (message: string) => void;
9
+ }
10
+ export declare function generatePgViewsDiffSql(sourcePool: Pool, destPool: Pool, options: GeneratePgViewsDiffOptions): Promise<string>;
11
+ //# sourceMappingURL=pg-views-diff.d.ts.map