pgpm 5.17.4 → 5.19.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.
@@ -25,11 +25,17 @@ Options:
25
25
  --metaExtensionName <name> Meta extension name (default: svc)
26
26
  --exclude-categories <list> Comma-separated sql_actions categories to omit
27
27
  (e.g. security,permissions,auth,memberships). Works in SQL and GraphQL modes.
28
+ --granularity <level> Restructure the exported database module through the
29
+ dials pipeline: atomic | object | consolidated.
30
+ Change paths are derived from the naming spec and
31
+ requires from the statement graph. When omitted,
32
+ sql_actions rows are exported unchanged.
28
33
  --cwd <directory> Working directory (default: current directory)
29
34
 
30
35
  Examples:
31
36
  pgpm export Export migrations from selected database (SQL mode)
32
37
  pgpm export --exclude-categories security,permissions,auth,memberships
38
+ pgpm export --granularity consolidated
33
39
  pgpm export --graphql-endpoint 'http://[::1]:3002/graphql' --migrate-endpoint 'http://[::1]:3000/graphql' --migrate-host db_migrate.localhost:3000
34
40
  `;
35
41
  exports.default = async (argv, prompter, _options) => {
@@ -54,6 +60,13 @@ exports.default = async (argv, prompter, _options) => {
54
60
  : Array.isArray(excludeCategoriesRaw)
55
61
  ? excludeCategoriesRaw
56
62
  : undefined;
63
+ const granularityRaw = argv.granularity;
64
+ if (granularityRaw !== undefined && !(0, export_1.isExportGranularity)(granularityRaw)) {
65
+ console.error(`Invalid --granularity "${granularityRaw}". Expected one of: ${export_1.EXPORT_GRANULARITIES.join(', ')}.`);
66
+ prompter.close();
67
+ return;
68
+ }
69
+ const granularity = granularityRaw;
57
70
  if (graphqlEndpoint) {
58
71
  // =========================================================================
59
72
  // GraphQL export mode
@@ -150,7 +163,8 @@ exports.default = async (argv, prompter, _options) => {
150
163
  prompter,
151
164
  argv,
152
165
  username,
153
- excludeCategories
166
+ excludeCategories,
167
+ granularity
154
168
  });
155
169
  }
156
170
  else {
@@ -270,7 +284,8 @@ exports.default = async (argv, prompter, _options) => {
270
284
  prompter,
271
285
  argv,
272
286
  username,
273
- excludeCategories
287
+ excludeCategories,
288
+ granularity
274
289
  });
275
290
  }
276
291
  prompter.close();
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@pgpmjs/core");
4
+ const logger_1 = require("@pgpmjs/logger");
5
+ const log = new logger_1.Logger('package');
4
6
  const packageUsageText = `
5
7
  Package Command:
6
8
 
@@ -17,11 +19,23 @@ Options:
17
19
  --bundle Also emit sql/<name>--<version>.bundle.tar.gz (default: true)
18
20
  --cwd <directory> Working directory (default: current directory)
19
21
 
22
+ Check mode (no writes — verify committed artifacts are in sync with deploy/):
23
+ --check Verify committed sql/<name>--<version>.bundle.tar.gz
24
+ matches deploy/ for changed modules; exit 1 on drift
25
+ --since <ref> Git branch/ref/tag to diff HEAD against for change
26
+ detection (default: PR base in CI, else working tree)
27
+ --all Check every workspace module (skip change detection)
28
+ --dependents Also re-check modules that require a changed module
29
+ --no-fail-fast Report all drifted modules instead of stopping at the first
30
+
20
31
  Examples:
21
32
  pgpm package Package with defaults
22
33
  pgpm package --no-plan Package without plan
23
34
  pgpm package --outputDiff Package and export AST diff files if mismatch detected
24
35
  pgpm package --no-bundle Package without emitting the bundle artifact
36
+ pgpm package --check Verify changed modules' bundles are in sync
37
+ pgpm package --check --since origin/main Verify modules changed vs origin/main
38
+ pgpm package --check --all Verify every module in the workspace
25
39
  `;
26
40
  exports.default = async (argv, prompter, _options) => {
27
41
  // Show usage if explicitly requested
@@ -29,6 +43,40 @@ exports.default = async (argv, prompter, _options) => {
29
43
  console.log(packageUsageText);
30
44
  process.exit(0);
31
45
  }
46
+ // Check mode: verify committed artifacts, never write.
47
+ if (argv.check) {
48
+ const result = await (0, core_1.checkPackages)({
49
+ cwd: argv.cwd,
50
+ since: typeof argv.since === 'string' ? argv.since : undefined,
51
+ all: argv.all === true,
52
+ dependents: argv.dependents === true,
53
+ failFast: argv.failFast !== false,
54
+ });
55
+ if (argv.all) {
56
+ log.info(`Checking all ${result.targeted.length} workspace module(s)`);
57
+ }
58
+ else {
59
+ log.info(`Change detection${result.base ? ` vs ${result.base}` : ' (working tree)'}: ` +
60
+ `${result.changedModules.length} changed module(s)` +
61
+ (result.targeted.length !== result.changedModules.length
62
+ ? ` (${result.targeted.length} incl. dependents)`
63
+ : ''));
64
+ }
65
+ if (result.drifted.length) {
66
+ for (const drift of result.drifted) {
67
+ log.error(`✖ ${drift.name}: ${drift.detail}`);
68
+ }
69
+ log.error(`${result.drifted.length} module(s) out of sync. Run \`pgpm package\` in each and commit the artifacts.`);
70
+ process.exit(1);
71
+ }
72
+ if (result.checked.length === 0) {
73
+ log.success('No changed modules to check.');
74
+ }
75
+ else {
76
+ log.success(`${result.checked.length} module(s) in sync.`);
77
+ }
78
+ return argv;
79
+ }
32
80
  const questions = [
33
81
  {
34
82
  type: 'confirm',
@@ -0,0 +1,3 @@
1
+ import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
2
+ declare const _default: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<Partial<ParsedArgs>>;
3
+ export default _default;
@@ -0,0 +1,171 @@
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
+ const core_1 = require("@pgpmjs/core");
37
+ const logger_1 = require("@pgpmjs/logger");
38
+ const transform_1 = require("@pgpmjs/transform");
39
+ const fs = __importStar(require("fs"));
40
+ const inquirerer_1 = require("inquirerer");
41
+ const path = __importStar(require("path"));
42
+ const log = new logger_1.Logger('regen');
43
+ const regenUsageText = `
44
+ Regen Command:
45
+
46
+ pgpm regen [change...] [OPTIONS]
47
+
48
+ Generate revert/verify scripts from deploy scripts. By default sweeps every
49
+ change in pgpm.plan and fills in scripts that are missing or empty stubs
50
+ (only comments and a BEGIN/COMMIT wrapper). Reverts are mechanical inverses
51
+ in reverse dependency order; verifies are existence checks per created
52
+ object. Statements with no derivable inverse become
53
+ '-- revert not derivable: <reason>' comments.
54
+
55
+ Arguments:
56
+ change Regenerate only the named change(s) (targeted mode)
57
+
58
+ Options:
59
+ --help, -h Show this help message
60
+ --revert-only Only generate revert scripts
61
+ --verify-only Only generate verify scripts
62
+ --dry-run Report what would be written without writing
63
+ --force Overwrite non-empty existing scripts
64
+ --cwd <directory> Working directory (default: current directory)
65
+
66
+ Examples:
67
+ pgpm regen Fill in all missing/stub revert+verify scripts
68
+ pgpm regen schemas/app/tables/users/table Regenerate one change
69
+ pgpm regen --verify-only --dry-run Preview verify generation
70
+ pgpm regen foo --force Overwrite foo's scripts even if non-empty
71
+ `;
72
+ const wrapScript = (type, change, body) => {
73
+ const header = type === 'revert' ? `-- Revert ${change} from pg` : `-- Verify ${change} on pg`;
74
+ const close = type === 'revert' ? 'COMMIT;' : 'ROLLBACK;';
75
+ const sql = body.trim();
76
+ return `${header}\n\nBEGIN;\n\n${sql ? `${sql}\n\n` : ''}${close}\n`;
77
+ };
78
+ exports.default = async (argv, prompter, _options) => {
79
+ if (argv.help || argv.h) {
80
+ console.log(regenUsageText);
81
+ process.exit(0);
82
+ }
83
+ const cwd = argv.cwd || process.cwd();
84
+ const revertOnly = Boolean(argv['revert-only'] ?? argv.revertOnly);
85
+ const verifyOnly = Boolean(argv['verify-only'] ?? argv.verifyOnly);
86
+ const dryRun = Boolean(argv['dry-run'] ?? argv.dryRun);
87
+ const force = Boolean(argv.force);
88
+ if (revertOnly && verifyOnly) {
89
+ await (0, inquirerer_1.cliExitWithError)('--revert-only and --verify-only are mutually exclusive');
90
+ }
91
+ const types = revertOnly
92
+ ? ['revert']
93
+ : verifyOnly
94
+ ? ['verify']
95
+ : ['revert', 'verify'];
96
+ const pkg = new core_1.PgpmPackage(path.resolve(cwd));
97
+ if (!pkg.isInModule()) {
98
+ await (0, inquirerer_1.cliExitWithError)('This command must be run inside a pgpm module (or pass --cwd <module>).');
99
+ }
100
+ const modulePath = pkg.modulePath;
101
+ const planResult = (0, core_1.parsePlanFile)(path.join(modulePath, 'pgpm.plan'));
102
+ if (!planResult.data) {
103
+ await (0, inquirerer_1.cliExitWithError)(`Failed to parse pgpm.plan: ${planResult.errors.map(e => e.message).join(', ')}`);
104
+ }
105
+ const planChanges = planResult.data.changes.map(c => c.name);
106
+ const requested = argv._?.filter(Boolean) ?? [];
107
+ let targets;
108
+ if (requested.length > 0) {
109
+ const missing = requested.filter(name => !planChanges.includes(name));
110
+ if (missing.length > 0) {
111
+ await (0, inquirerer_1.cliExitWithError)(`Change(s) not found in pgpm.plan: ${missing.join(', ')}`);
112
+ }
113
+ targets = requested;
114
+ }
115
+ else {
116
+ targets = planChanges;
117
+ }
118
+ const result = { generated: [], skipped: [], warnings: [] };
119
+ await (0, transform_1.loadModule)();
120
+ for (const change of targets) {
121
+ const deploySql = (0, core_1.readScript)(modulePath, 'deploy', change);
122
+ if (!deploySql) {
123
+ result.warnings.push(`${change}: no deploy script found, skipping`);
124
+ continue;
125
+ }
126
+ const needed = types.filter(type => {
127
+ const existing = (0, core_1.readScript)(modulePath, type, change);
128
+ return force || (0, transform_1.isStubScript)(existing);
129
+ });
130
+ const untouched = types.filter(type => !needed.includes(type));
131
+ for (const type of untouched) {
132
+ result.skipped.push(`${type}/${change}`);
133
+ }
134
+ if (needed.length === 0)
135
+ continue;
136
+ let scripts;
137
+ try {
138
+ scripts = (0, transform_1.regenerateScripts)(deploySql);
139
+ }
140
+ catch (err) {
141
+ result.warnings.push(`${change}: failed to parse deploy script (${err?.message ?? err})`);
142
+ continue;
143
+ }
144
+ for (const warning of scripts.revert.warnings) {
145
+ result.warnings.push(`${change}: ${warning}`);
146
+ }
147
+ for (const warning of scripts.verify.warnings) {
148
+ result.warnings.push(`${change}: ${warning}`);
149
+ }
150
+ for (const type of needed) {
151
+ const body = type === 'revert' ? scripts.revert.sql : scripts.verify.sql;
152
+ const content = wrapScript(type, change, body);
153
+ const target = `${type}/${change}`;
154
+ if (dryRun) {
155
+ log.info(`would write ${target}.sql`);
156
+ }
157
+ else {
158
+ const filePath = path.join(modulePath, type, `${change}.sql`);
159
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
160
+ fs.writeFileSync(filePath, content);
161
+ }
162
+ result.generated.push(target);
163
+ }
164
+ }
165
+ for (const warning of result.warnings) {
166
+ log.warn(warning);
167
+ }
168
+ const verb = dryRun ? 'would generate' : 'generated';
169
+ log.success(`regen: ${verb} ${result.generated.length} script(s), skipped ${result.skipped.length} existing, ${result.warnings.length} warning(s)`);
170
+ return argv;
171
+ };
package/commands.js CHANGED
@@ -26,6 +26,7 @@ const materialize_1 = __importDefault(require("./commands/materialize"));
26
26
  const migrate_1 = __importDefault(require("./commands/migrate"));
27
27
  const package_1 = __importDefault(require("./commands/package"));
28
28
  const plan_1 = __importDefault(require("./commands/plan"));
29
+ const regen_1 = __importDefault(require("./commands/regen"));
29
30
  const remove_1 = __importDefault(require("./commands/remove"));
30
31
  const rename_1 = __importDefault(require("./commands/rename"));
31
32
  const revert_1 = __importDefault(require("./commands/revert"));
@@ -57,6 +58,7 @@ const ENGINE_EXEMPT_COMMANDS = new Set([
57
58
  'package',
58
59
  'materialize',
59
60
  'plan',
61
+ 'regen',
60
62
  'remove',
61
63
  'rename',
62
64
  'slice',
@@ -93,6 +95,7 @@ const createPgpmCommandMap = (skipPgTeardown = false) => {
93
95
  init: pgt(init_1.default),
94
96
  extension: pgt(extension_1.default),
95
97
  plan: pgt(plan_1.default),
98
+ regen: regen_1.default,
96
99
  export: pgt(export_1.default),
97
100
  package: pgt(package_1.default),
98
101
  tag: pgt(tag_1.default),
@@ -1,5 +1,5 @@
1
1
  import { PgpmPackage } from '@pgpmjs/core';
2
- import { exportMigrations, exportGraphQL, GraphQLClient, graphqlRowToPostgresRow } from '@pgpmjs/export';
2
+ import { exportMigrations, exportGraphQL, GraphQLClient, graphqlRowToPostgresRow, isExportGranularity, EXPORT_GRANULARITIES } from '@pgpmjs/export';
3
3
  import { getEnvOptions } from '@pgpmjs/env';
4
4
  import { getGitConfigInfo } from '@pgpmjs/types';
5
5
  import { resolve } from 'path';
@@ -23,11 +23,17 @@ Options:
23
23
  --metaExtensionName <name> Meta extension name (default: svc)
24
24
  --exclude-categories <list> Comma-separated sql_actions categories to omit
25
25
  (e.g. security,permissions,auth,memberships). Works in SQL and GraphQL modes.
26
+ --granularity <level> Restructure the exported database module through the
27
+ dials pipeline: atomic | object | consolidated.
28
+ Change paths are derived from the naming spec and
29
+ requires from the statement graph. When omitted,
30
+ sql_actions rows are exported unchanged.
26
31
  --cwd <directory> Working directory (default: current directory)
27
32
 
28
33
  Examples:
29
34
  pgpm export Export migrations from selected database (SQL mode)
30
35
  pgpm export --exclude-categories security,permissions,auth,memberships
36
+ pgpm export --granularity consolidated
31
37
  pgpm export --graphql-endpoint 'http://[::1]:3002/graphql' --migrate-endpoint 'http://[::1]:3000/graphql' --migrate-host db_migrate.localhost:3000
32
38
  `;
33
39
  export default async (argv, prompter, _options) => {
@@ -52,6 +58,13 @@ export default async (argv, prompter, _options) => {
52
58
  : Array.isArray(excludeCategoriesRaw)
53
59
  ? excludeCategoriesRaw
54
60
  : undefined;
61
+ const granularityRaw = argv.granularity;
62
+ if (granularityRaw !== undefined && !isExportGranularity(granularityRaw)) {
63
+ console.error(`Invalid --granularity "${granularityRaw}". Expected one of: ${EXPORT_GRANULARITIES.join(', ')}.`);
64
+ prompter.close();
65
+ return;
66
+ }
67
+ const granularity = granularityRaw;
55
68
  if (graphqlEndpoint) {
56
69
  // =========================================================================
57
70
  // GraphQL export mode
@@ -148,7 +161,8 @@ export default async (argv, prompter, _options) => {
148
161
  prompter,
149
162
  argv,
150
163
  username,
151
- excludeCategories
164
+ excludeCategories,
165
+ granularity
152
166
  });
153
167
  }
154
168
  else {
@@ -268,7 +282,8 @@ export default async (argv, prompter, _options) => {
268
282
  prompter,
269
283
  argv,
270
284
  username,
271
- excludeCategories
285
+ excludeCategories,
286
+ granularity
272
287
  });
273
288
  }
274
289
  prompter.close();
@@ -1,4 +1,6 @@
1
- import { PgpmPackage, writePackage } from '@pgpmjs/core';
1
+ import { checkPackages, PgpmPackage, writePackage } from '@pgpmjs/core';
2
+ import { Logger } from '@pgpmjs/logger';
3
+ const log = new Logger('package');
2
4
  const packageUsageText = `
3
5
  Package Command:
4
6
 
@@ -15,11 +17,23 @@ Options:
15
17
  --bundle Also emit sql/<name>--<version>.bundle.tar.gz (default: true)
16
18
  --cwd <directory> Working directory (default: current directory)
17
19
 
20
+ Check mode (no writes — verify committed artifacts are in sync with deploy/):
21
+ --check Verify committed sql/<name>--<version>.bundle.tar.gz
22
+ matches deploy/ for changed modules; exit 1 on drift
23
+ --since <ref> Git branch/ref/tag to diff HEAD against for change
24
+ detection (default: PR base in CI, else working tree)
25
+ --all Check every workspace module (skip change detection)
26
+ --dependents Also re-check modules that require a changed module
27
+ --no-fail-fast Report all drifted modules instead of stopping at the first
28
+
18
29
  Examples:
19
30
  pgpm package Package with defaults
20
31
  pgpm package --no-plan Package without plan
21
32
  pgpm package --outputDiff Package and export AST diff files if mismatch detected
22
33
  pgpm package --no-bundle Package without emitting the bundle artifact
34
+ pgpm package --check Verify changed modules' bundles are in sync
35
+ pgpm package --check --since origin/main Verify modules changed vs origin/main
36
+ pgpm package --check --all Verify every module in the workspace
23
37
  `;
24
38
  export default async (argv, prompter, _options) => {
25
39
  // Show usage if explicitly requested
@@ -27,6 +41,40 @@ export default async (argv, prompter, _options) => {
27
41
  console.log(packageUsageText);
28
42
  process.exit(0);
29
43
  }
44
+ // Check mode: verify committed artifacts, never write.
45
+ if (argv.check) {
46
+ const result = await checkPackages({
47
+ cwd: argv.cwd,
48
+ since: typeof argv.since === 'string' ? argv.since : undefined,
49
+ all: argv.all === true,
50
+ dependents: argv.dependents === true,
51
+ failFast: argv.failFast !== false,
52
+ });
53
+ if (argv.all) {
54
+ log.info(`Checking all ${result.targeted.length} workspace module(s)`);
55
+ }
56
+ else {
57
+ log.info(`Change detection${result.base ? ` vs ${result.base}` : ' (working tree)'}: ` +
58
+ `${result.changedModules.length} changed module(s)` +
59
+ (result.targeted.length !== result.changedModules.length
60
+ ? ` (${result.targeted.length} incl. dependents)`
61
+ : ''));
62
+ }
63
+ if (result.drifted.length) {
64
+ for (const drift of result.drifted) {
65
+ log.error(`✖ ${drift.name}: ${drift.detail}`);
66
+ }
67
+ log.error(`${result.drifted.length} module(s) out of sync. Run \`pgpm package\` in each and commit the artifacts.`);
68
+ process.exit(1);
69
+ }
70
+ if (result.checked.length === 0) {
71
+ log.success('No changed modules to check.');
72
+ }
73
+ else {
74
+ log.success(`${result.checked.length} module(s) in sync.`);
75
+ }
76
+ return argv;
77
+ }
30
78
  const questions = [
31
79
  {
32
80
  type: 'confirm',
@@ -0,0 +1,136 @@
1
+ import { parsePlanFile, PgpmPackage, readScript } from '@pgpmjs/core';
2
+ import { Logger } from '@pgpmjs/logger';
3
+ import { isStubScript, loadModule, regenerateScripts } from '@pgpmjs/transform';
4
+ import * as fs from 'fs';
5
+ import { cliExitWithError } from 'inquirerer';
6
+ import * as path from 'path';
7
+ const log = new Logger('regen');
8
+ const regenUsageText = `
9
+ Regen Command:
10
+
11
+ pgpm regen [change...] [OPTIONS]
12
+
13
+ Generate revert/verify scripts from deploy scripts. By default sweeps every
14
+ change in pgpm.plan and fills in scripts that are missing or empty stubs
15
+ (only comments and a BEGIN/COMMIT wrapper). Reverts are mechanical inverses
16
+ in reverse dependency order; verifies are existence checks per created
17
+ object. Statements with no derivable inverse become
18
+ '-- revert not derivable: <reason>' comments.
19
+
20
+ Arguments:
21
+ change Regenerate only the named change(s) (targeted mode)
22
+
23
+ Options:
24
+ --help, -h Show this help message
25
+ --revert-only Only generate revert scripts
26
+ --verify-only Only generate verify scripts
27
+ --dry-run Report what would be written without writing
28
+ --force Overwrite non-empty existing scripts
29
+ --cwd <directory> Working directory (default: current directory)
30
+
31
+ Examples:
32
+ pgpm regen Fill in all missing/stub revert+verify scripts
33
+ pgpm regen schemas/app/tables/users/table Regenerate one change
34
+ pgpm regen --verify-only --dry-run Preview verify generation
35
+ pgpm regen foo --force Overwrite foo's scripts even if non-empty
36
+ `;
37
+ const wrapScript = (type, change, body) => {
38
+ const header = type === 'revert' ? `-- Revert ${change} from pg` : `-- Verify ${change} on pg`;
39
+ const close = type === 'revert' ? 'COMMIT;' : 'ROLLBACK;';
40
+ const sql = body.trim();
41
+ return `${header}\n\nBEGIN;\n\n${sql ? `${sql}\n\n` : ''}${close}\n`;
42
+ };
43
+ export default async (argv, prompter, _options) => {
44
+ if (argv.help || argv.h) {
45
+ console.log(regenUsageText);
46
+ process.exit(0);
47
+ }
48
+ const cwd = argv.cwd || process.cwd();
49
+ const revertOnly = Boolean(argv['revert-only'] ?? argv.revertOnly);
50
+ const verifyOnly = Boolean(argv['verify-only'] ?? argv.verifyOnly);
51
+ const dryRun = Boolean(argv['dry-run'] ?? argv.dryRun);
52
+ const force = Boolean(argv.force);
53
+ if (revertOnly && verifyOnly) {
54
+ await cliExitWithError('--revert-only and --verify-only are mutually exclusive');
55
+ }
56
+ const types = revertOnly
57
+ ? ['revert']
58
+ : verifyOnly
59
+ ? ['verify']
60
+ : ['revert', 'verify'];
61
+ const pkg = new PgpmPackage(path.resolve(cwd));
62
+ if (!pkg.isInModule()) {
63
+ await cliExitWithError('This command must be run inside a pgpm module (or pass --cwd <module>).');
64
+ }
65
+ const modulePath = pkg.modulePath;
66
+ const planResult = parsePlanFile(path.join(modulePath, 'pgpm.plan'));
67
+ if (!planResult.data) {
68
+ await cliExitWithError(`Failed to parse pgpm.plan: ${planResult.errors.map(e => e.message).join(', ')}`);
69
+ }
70
+ const planChanges = planResult.data.changes.map(c => c.name);
71
+ const requested = argv._?.filter(Boolean) ?? [];
72
+ let targets;
73
+ if (requested.length > 0) {
74
+ const missing = requested.filter(name => !planChanges.includes(name));
75
+ if (missing.length > 0) {
76
+ await cliExitWithError(`Change(s) not found in pgpm.plan: ${missing.join(', ')}`);
77
+ }
78
+ targets = requested;
79
+ }
80
+ else {
81
+ targets = planChanges;
82
+ }
83
+ const result = { generated: [], skipped: [], warnings: [] };
84
+ await loadModule();
85
+ for (const change of targets) {
86
+ const deploySql = readScript(modulePath, 'deploy', change);
87
+ if (!deploySql) {
88
+ result.warnings.push(`${change}: no deploy script found, skipping`);
89
+ continue;
90
+ }
91
+ const needed = types.filter(type => {
92
+ const existing = readScript(modulePath, type, change);
93
+ return force || isStubScript(existing);
94
+ });
95
+ const untouched = types.filter(type => !needed.includes(type));
96
+ for (const type of untouched) {
97
+ result.skipped.push(`${type}/${change}`);
98
+ }
99
+ if (needed.length === 0)
100
+ continue;
101
+ let scripts;
102
+ try {
103
+ scripts = regenerateScripts(deploySql);
104
+ }
105
+ catch (err) {
106
+ result.warnings.push(`${change}: failed to parse deploy script (${err?.message ?? err})`);
107
+ continue;
108
+ }
109
+ for (const warning of scripts.revert.warnings) {
110
+ result.warnings.push(`${change}: ${warning}`);
111
+ }
112
+ for (const warning of scripts.verify.warnings) {
113
+ result.warnings.push(`${change}: ${warning}`);
114
+ }
115
+ for (const type of needed) {
116
+ const body = type === 'revert' ? scripts.revert.sql : scripts.verify.sql;
117
+ const content = wrapScript(type, change, body);
118
+ const target = `${type}/${change}`;
119
+ if (dryRun) {
120
+ log.info(`would write ${target}.sql`);
121
+ }
122
+ else {
123
+ const filePath = path.join(modulePath, type, `${change}.sql`);
124
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
125
+ fs.writeFileSync(filePath, content);
126
+ }
127
+ result.generated.push(target);
128
+ }
129
+ }
130
+ for (const warning of result.warnings) {
131
+ log.warn(warning);
132
+ }
133
+ const verb = dryRun ? 'would generate' : 'generated';
134
+ log.success(`regen: ${verb} ${result.generated.length} script(s), skipped ${result.skipped.length} existing, ${result.warnings.length} warning(s)`);
135
+ return argv;
136
+ };
package/esm/commands.js CHANGED
@@ -20,6 +20,7 @@ import materialize from './commands/materialize';
20
20
  import migrate from './commands/migrate';
21
21
  import _package from './commands/package';
22
22
  import plan from './commands/plan';
23
+ import regen from './commands/regen';
23
24
  import remove from './commands/remove';
24
25
  import renameCmd from './commands/rename';
25
26
  import revert from './commands/revert';
@@ -51,6 +52,7 @@ const ENGINE_EXEMPT_COMMANDS = new Set([
51
52
  'package',
52
53
  'materialize',
53
54
  'plan',
55
+ 'regen',
54
56
  'remove',
55
57
  'rename',
56
58
  'slice',
@@ -87,6 +89,7 @@ export const createPgpmCommandMap = (skipPgTeardown = false) => {
87
89
  init: pgt(init),
88
90
  extension: pgt(extension),
89
91
  plan: pgt(plan),
92
+ regen,
90
93
  export: pgt(_export),
91
94
  package: pgt(_package),
92
95
  tag: pgt(tag),
@@ -11,6 +11,7 @@ export const usageText = `
11
11
  init Initialize workspace or module
12
12
  extension Manage module dependencies
13
13
  plan Generate module deployment plans
14
+ regen Generate revert/verify scripts from deploy scripts
14
15
  package Package module for distribution
15
16
  materialize Transpile an apply proxy into a plain, committed module
16
17
  sync-versions Sync .control/Makefile/sql metadata to package.json versions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgpm",
3
- "version": "5.17.4",
3
+ "version": "5.19.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PostgreSQL Package Manager - Database migration and package management CLI",
6
6
  "main": "index.js",
@@ -46,11 +46,12 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@inquirerer/utils": "^3.3.9",
49
- "@pgpmjs/core": "^7.20.4",
49
+ "@pgpmjs/core": "^7.21.1",
50
50
  "@pgpmjs/env": "^2.40.2",
51
- "@pgpmjs/export": "^1.13.4",
51
+ "@pgpmjs/export": "^1.14.1",
52
52
  "@pgpmjs/logger": "^2.23.2",
53
- "@pgpmjs/slice": "^0.13.4",
53
+ "@pgpmjs/slice": "^0.13.6",
54
+ "@pgpmjs/transform": "^0.17.0",
54
55
  "@pgpmjs/types": "^2.49.2",
55
56
  "@pgsql/quotes": "^18.1.0",
56
57
  "appstash": "^0.7.0",
@@ -60,7 +61,7 @@
60
61
  "js-yaml": "^4.1.0",
61
62
  "pg-cache": "^3.24.2",
62
63
  "pg-env": "^1.27.2",
63
- "pgsql-deparser": "^18.1.1",
64
+ "pgsql-deparser": "^18.2.0",
64
65
  "semver": "^7.8.1",
65
66
  "shelljs": "^0.10.0",
66
67
  "yanse": "^0.2.1"
@@ -77,5 +78,5 @@
77
78
  "pg",
78
79
  "pgsql"
79
80
  ],
80
- "gitHead": "90f447482b056a4949134565db2665f21d11aca3"
81
+ "gitHead": "45cd961cf956d2d28e4d09b20db4204982c33593"
81
82
  }
@@ -1 +1 @@
1
- export declare const usageText = "\n Usage: pgpm <command> [options]\n\n Core Database Operations:\n add Add database changes to plans and create SQL files\n deploy Deploy database changes and migrations\n verify Verify database state and migrations\n revert Revert database changes and migrations\n\n Project Management:\n init Initialize workspace or module\n extension Manage module dependencies\n plan Generate module deployment plans\n package Package module for distribution\n materialize Transpile an apply proxy into a plain, committed module\n sync-versions Sync .control/Makefile/sql metadata to package.json versions\n export Export database migrations from existing databases\n update Update pgpm to the latest version\n cache Manage cached templates (clean)\n upgrade Upgrade installed pgpm modules to latest versions (alias: up)\n\n Database Administration:\n dump Dump a database to a sql file\n kill Terminate database connections and optionally drop databases\n install Install database modules\n tag Add tags to changes for versioning\n clear Clear database state\n remove Remove database changes\n analyze Analyze database structure\n rename Rename database changes\n admin-users Manage admin users\n tune Tune PostgreSQL for throwaway environments (CI/test)\n\n Testing:\n test-packages Run integration tests on all workspace packages\n\n Migration Tools:\n migrate Migration management subcommands\n init Initialize migration tracking\n status Show migration status\n list List all changes\n deps Show change dependencies\n \n Development Tools:\n docker Manage Docker containers (start/stop/ls, --minio)\n doctor Check local dependencies (node, docker, psql) with install guidance\n env Manage environment variables (--supabase, --minio)\n test-packages Run integration tests on workspace packages\n \n Global Options:\n -h, --help Display this help information\n -v, --version Display version information\n --cwd <directory> Working directory (default: current directory)\n --engine <name> Migration backend: pg (default, Postgres server) or\n pglite (in-process WASM Postgres, no server). Also set by\n the \"engine\" key of pgpm.json or PGPM_ENGINE.\n --driver <pkg> Driver plugin package backing the engine (escape hatch)\n --pglite[=dataDir] Sugar for --engine pglite, persisted to <dataDir> if given\n\n Individual Command Help:\n pgpm <command> --help Display detailed help for specific command\n pgpm <command> -h Display detailed help for specific command\n\n Examples:\n pgpm deploy --help Show deploy command options\n pgpm init workspace Initialize new workspace\n pgpm install @pgpm/base32 Install a database module\n ";
1
+ export declare const usageText = "\n Usage: pgpm <command> [options]\n\n Core Database Operations:\n add Add database changes to plans and create SQL files\n deploy Deploy database changes and migrations\n verify Verify database state and migrations\n revert Revert database changes and migrations\n\n Project Management:\n init Initialize workspace or module\n extension Manage module dependencies\n plan Generate module deployment plans\n regen Generate revert/verify scripts from deploy scripts\n package Package module for distribution\n materialize Transpile an apply proxy into a plain, committed module\n sync-versions Sync .control/Makefile/sql metadata to package.json versions\n export Export database migrations from existing databases\n update Update pgpm to the latest version\n cache Manage cached templates (clean)\n upgrade Upgrade installed pgpm modules to latest versions (alias: up)\n\n Database Administration:\n dump Dump a database to a sql file\n kill Terminate database connections and optionally drop databases\n install Install database modules\n tag Add tags to changes for versioning\n clear Clear database state\n remove Remove database changes\n analyze Analyze database structure\n rename Rename database changes\n admin-users Manage admin users\n tune Tune PostgreSQL for throwaway environments (CI/test)\n\n Testing:\n test-packages Run integration tests on all workspace packages\n\n Migration Tools:\n migrate Migration management subcommands\n init Initialize migration tracking\n status Show migration status\n list List all changes\n deps Show change dependencies\n \n Development Tools:\n docker Manage Docker containers (start/stop/ls, --minio)\n doctor Check local dependencies (node, docker, psql) with install guidance\n env Manage environment variables (--supabase, --minio)\n test-packages Run integration tests on workspace packages\n \n Global Options:\n -h, --help Display this help information\n -v, --version Display version information\n --cwd <directory> Working directory (default: current directory)\n --engine <name> Migration backend: pg (default, Postgres server) or\n pglite (in-process WASM Postgres, no server). Also set by\n the \"engine\" key of pgpm.json or PGPM_ENGINE.\n --driver <pkg> Driver plugin package backing the engine (escape hatch)\n --pglite[=dataDir] Sugar for --engine pglite, persisted to <dataDir> if given\n\n Individual Command Help:\n pgpm <command> --help Display detailed help for specific command\n pgpm <command> -h Display detailed help for specific command\n\n Examples:\n pgpm deploy --help Show deploy command options\n pgpm init workspace Initialize new workspace\n pgpm install @pgpm/base32 Install a database module\n ";
package/utils/display.js CHANGED
@@ -14,6 +14,7 @@ exports.usageText = `
14
14
  init Initialize workspace or module
15
15
  extension Manage module dependencies
16
16
  plan Generate module deployment plans
17
+ regen Generate revert/verify scripts from deploy scripts
17
18
  package Package module for distribution
18
19
  materialize Transpile an apply proxy into a plain, committed module
19
20
  sync-versions Sync .control/Makefile/sql metadata to package.json versions