pgpm 5.14.0 → 5.15.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.
@@ -17,6 +17,10 @@ Deploy Command:
17
17
  Options:
18
18
  --help, -h Show this help message
19
19
  --createdb Create database if it doesn't exist
20
+ --engine <name> Migration backend to deploy to (default: pg).
21
+ Built-in engines: pg (Postgres server), pglite (in-process WASM).
22
+ --driver <pkg> Driver plugin package to deploy through (escape hatch for --engine)
23
+ --pglite[=dataDir] Sugar for --engine pglite; persists to <dataDir> when given
20
24
  --recursive Deploy recursively through dependencies
21
25
  --package <name> Target specific package
22
26
  --to <target> Deploy to specific change or tag
@@ -36,6 +40,8 @@ Examples:
36
40
  pgpm deploy --package mypackage --to @v1.0.0 Deploy specific package to tag
37
41
  pgpm deploy --fast --no-tx Fast deployment without transactions
38
42
  pgpm deploy --bundled Same as --fast
43
+ pgpm deploy --engine pglite Deploy into in-process PGlite (no server)
44
+ pgpm deploy --pglite=./.pglite Deploy into a persisted PGlite data directory
39
45
  `;
40
46
  exports.default = async (argv, prompter, _options) => {
41
47
  // Show usage if explicitly requested
@@ -117,11 +123,17 @@ exports.default = async (argv, prompter, _options) => {
117
123
  return;
118
124
  }
119
125
  log.debug(`Using current directory: ${cwd}`);
126
+ const { engine, capabilities } = (0, utils_1.getActiveEngine)();
120
127
  if (createdb) {
121
- log.info(`Creating database ${database}...`);
122
- (0, child_process_1.execSync)(`createdb ${database}`, {
123
- env: (0, pg_env_1.getSpawnEnvWithPg)(pgEnv)
124
- });
128
+ if (capabilities.createdb) {
129
+ log.info(`Creating database ${database}...`);
130
+ (0, child_process_1.execSync)(`createdb ${database}`, {
131
+ env: (0, pg_env_1.getSpawnEnvWithPg)(pgEnv)
132
+ });
133
+ }
134
+ else {
135
+ log.info(`Skipping createdb: the "${engine.name}" engine's instance is the database.`);
136
+ }
125
137
  }
126
138
  let packageName;
127
139
  if (recursive) {
@@ -22,6 +22,7 @@ Options:
22
22
  --to <target> Revert to specific change or tag
23
23
  --to Interactive selection of deployed changes
24
24
  --tx Use transactions (default: true)
25
+ --engine <name> Migration backend to revert in (default: pg; also pglite)
25
26
  --cwd <directory> Working directory (default: current directory)
26
27
 
27
28
  Examples:
@@ -21,6 +21,7 @@ Options:
21
21
  --package <name> Verify specific package
22
22
  --to <target> Verify up to specific change or tag
23
23
  --to Interactive selection of deployed changes
24
+ --engine <name> Migration backend to verify against (default: pg; also pglite)
24
25
  --cwd <directory> Working directory (default: current directory)
25
26
 
26
27
  Examples:
package/commands.js CHANGED
@@ -37,6 +37,12 @@ const update_1 = __importDefault(require("./commands/update"));
37
37
  const upgrade_1 = __importDefault(require("./commands/upgrade"));
38
38
  const verify_1 = __importDefault(require("./commands/verify"));
39
39
  const utils_2 = require("./utils");
40
+ /**
41
+ * Commands that never talk to a database and must not activate a driver.
42
+ * `init` owns its own `--pglite` meaning (scaffold from the PGlite boilerplates),
43
+ * and it runs before the plugin it would scaffold is even installed.
44
+ */
45
+ const ENGINE_EXEMPT_COMMANDS = new Set(['init']);
40
46
  const withPgTeardown = (fn, skipTeardown = false) => async (...args) => {
41
47
  try {
42
48
  await fn(...args);
@@ -144,7 +150,27 @@ const commands = async (argv, prompter, options) => {
144
150
  console.log(utils_2.usageText);
145
151
  await (0, inquirerer_1.cliExitWithError)(`Unknown command: ${command}`, { beforeExit: pg_cache_1.teardownPgPools });
146
152
  }
147
- await commandFn(newArgv, prompter, options);
153
+ // Activate the selected migration backend (`--engine`/`--driver`/`--pglite` or
154
+ // pgpm.json) before the command runs: the driver plugin registers its
155
+ // pool/client factories, so the unmodified engine targets it. The built-in
156
+ // `pg` engine activates nothing and behaves exactly as before.
157
+ const engineArgv = newArgv;
158
+ const { engine, capabilities } = ENGINE_EXEMPT_COMMANDS.has(command)
159
+ ? (0, utils_2.getActiveEngine)()
160
+ : await (0, utils_2.activateEngine)(engineArgv, engineArgv.cwd).catch(async (error) => {
161
+ await (0, inquirerer_1.cliExitWithError)(error.message, { beforeExit: pg_cache_1.teardownPgPools });
162
+ throw error;
163
+ });
164
+ try {
165
+ const blocked = (0, utils_2.engineCommandBlocker)(command, engine, capabilities);
166
+ if (blocked) {
167
+ await (0, inquirerer_1.cliExitWithError)(blocked, { beforeExit: pg_cache_1.teardownPgPools });
168
+ }
169
+ await commandFn(newArgv, prompter, options);
170
+ }
171
+ finally {
172
+ await (0, utils_2.deactivateEngine)();
173
+ }
148
174
  prompter.close();
149
175
  return argv;
150
176
  };
@@ -3,7 +3,7 @@ import { getEnvOptions } from '@pgpmjs/env';
3
3
  import { Logger } from '@pgpmjs/logger';
4
4
  import { execSync } from 'child_process';
5
5
  import { getPgEnvOptions, getSpawnEnvWithPg, } from 'pg-env';
6
- import { getTargetDatabase, resolvePackageAlias } from '../utils';
6
+ import { getActiveEngine, getTargetDatabase, resolvePackageAlias } from '../utils';
7
7
  import { selectPackage } from '../utils/module-utils';
8
8
  const deployUsageText = `
9
9
  Deploy Command:
@@ -15,6 +15,10 @@ Deploy Command:
15
15
  Options:
16
16
  --help, -h Show this help message
17
17
  --createdb Create database if it doesn't exist
18
+ --engine <name> Migration backend to deploy to (default: pg).
19
+ Built-in engines: pg (Postgres server), pglite (in-process WASM).
20
+ --driver <pkg> Driver plugin package to deploy through (escape hatch for --engine)
21
+ --pglite[=dataDir] Sugar for --engine pglite; persists to <dataDir> when given
18
22
  --recursive Deploy recursively through dependencies
19
23
  --package <name> Target specific package
20
24
  --to <target> Deploy to specific change or tag
@@ -34,6 +38,8 @@ Examples:
34
38
  pgpm deploy --package mypackage --to @v1.0.0 Deploy specific package to tag
35
39
  pgpm deploy --fast --no-tx Fast deployment without transactions
36
40
  pgpm deploy --bundled Same as --fast
41
+ pgpm deploy --engine pglite Deploy into in-process PGlite (no server)
42
+ pgpm deploy --pglite=./.pglite Deploy into a persisted PGlite data directory
37
43
  `;
38
44
  export default async (argv, prompter, _options) => {
39
45
  // Show usage if explicitly requested
@@ -115,11 +121,17 @@ export default async (argv, prompter, _options) => {
115
121
  return;
116
122
  }
117
123
  log.debug(`Using current directory: ${cwd}`);
124
+ const { engine, capabilities } = getActiveEngine();
118
125
  if (createdb) {
119
- log.info(`Creating database ${database}...`);
120
- execSync(`createdb ${database}`, {
121
- env: getSpawnEnvWithPg(pgEnv)
122
- });
126
+ if (capabilities.createdb) {
127
+ log.info(`Creating database ${database}...`);
128
+ execSync(`createdb ${database}`, {
129
+ env: getSpawnEnvWithPg(pgEnv)
130
+ });
131
+ }
132
+ else {
133
+ log.info(`Skipping createdb: the "${engine.name}" engine's instance is the database.`);
134
+ }
123
135
  }
124
136
  let packageName;
125
137
  if (recursive) {
@@ -20,6 +20,7 @@ Options:
20
20
  --to <target> Revert to specific change or tag
21
21
  --to Interactive selection of deployed changes
22
22
  --tx Use transactions (default: true)
23
+ --engine <name> Migration backend to revert in (default: pg; also pglite)
23
24
  --cwd <directory> Working directory (default: current directory)
24
25
 
25
26
  Examples:
@@ -19,6 +19,7 @@ Options:
19
19
  --package <name> Verify specific package
20
20
  --to <target> Verify up to specific change or tag
21
21
  --to Interactive selection of deployed changes
22
+ --engine <name> Migration backend to verify against (default: pg; also pglite)
22
23
  --cwd <directory> Working directory (default: current directory)
23
24
 
24
25
  Examples:
package/esm/commands.js CHANGED
@@ -30,7 +30,13 @@ import tune from './commands/tune';
30
30
  import updateCmd from './commands/update';
31
31
  import upgrade from './commands/upgrade';
32
32
  import verify from './commands/verify';
33
- import { usageText } from './utils';
33
+ import { activateEngine, deactivateEngine, engineCommandBlocker, getActiveEngine, usageText } from './utils';
34
+ /**
35
+ * Commands that never talk to a database and must not activate a driver.
36
+ * `init` owns its own `--pglite` meaning (scaffold from the PGlite boilerplates),
37
+ * and it runs before the plugin it would scaffold is even installed.
38
+ */
39
+ const ENGINE_EXEMPT_COMMANDS = new Set(['init']);
34
40
  const withPgTeardown = (fn, skipTeardown = false) => async (...args) => {
35
41
  try {
36
42
  await fn(...args);
@@ -137,7 +143,27 @@ export const commands = async (argv, prompter, options) => {
137
143
  console.log(usageText);
138
144
  await cliExitWithError(`Unknown command: ${command}`, { beforeExit: teardownPgPools });
139
145
  }
140
- await commandFn(newArgv, prompter, options);
146
+ // Activate the selected migration backend (`--engine`/`--driver`/`--pglite` or
147
+ // pgpm.json) before the command runs: the driver plugin registers its
148
+ // pool/client factories, so the unmodified engine targets it. The built-in
149
+ // `pg` engine activates nothing and behaves exactly as before.
150
+ const engineArgv = newArgv;
151
+ const { engine, capabilities } = ENGINE_EXEMPT_COMMANDS.has(command)
152
+ ? getActiveEngine()
153
+ : await activateEngine(engineArgv, engineArgv.cwd).catch(async (error) => {
154
+ await cliExitWithError(error.message, { beforeExit: teardownPgPools });
155
+ throw error;
156
+ });
157
+ try {
158
+ const blocked = engineCommandBlocker(command, engine, capabilities);
159
+ if (blocked) {
160
+ await cliExitWithError(blocked, { beforeExit: teardownPgPools });
161
+ }
162
+ await commandFn(newArgv, prompter, options);
163
+ }
164
+ finally {
165
+ await deactivateEngine();
166
+ }
141
167
  prompter.close();
142
168
  return argv;
143
169
  };
@@ -50,6 +50,11 @@ export const usageText = `
50
50
  -h, --help Display this help information
51
51
  -v, --version Display version information
52
52
  --cwd <directory> Working directory (default: current directory)
53
+ --engine <name> Migration backend: pg (default, Postgres server) or
54
+ pglite (in-process WASM Postgres, no server). Also set by
55
+ the "engine" key of pgpm.json or PGPM_ENGINE.
56
+ --driver <pkg> Driver plugin package backing the engine (escape hatch)
57
+ --pglite[=dataDir] Sugar for --engine pglite, persisted to <dataDir> if given
53
58
 
54
59
  Individual Command Help:
55
60
  pgpm <command> --help Display detailed help for specific command
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Commands that need a capability the backend may not have. Gating is declared
3
+ * here rather than branching per backend inside each command, so a new driver
4
+ * plugin only has to describe itself through {@link PgpmDriverCapabilities}.
5
+ */
6
+ const COMMAND_REQUIREMENTS = {
7
+ docker: 'serverLifecycle',
8
+ kill: 'serverLifecycle',
9
+ tune: 'serverLifecycle',
10
+ 'admin-users': 'multiConnection',
11
+ dump: 'dump',
12
+ };
13
+ const REQUIREMENT_REASONS = {
14
+ createdb: 'it cannot create databases (the instance is the database)',
15
+ dump: 'it has no pg_dump',
16
+ serverLifecycle: 'it has no server to manage',
17
+ multiConnection: 'it is a single-session backend',
18
+ };
19
+ /**
20
+ * The reason a command cannot run on the active engine, or undefined when it can.
21
+ */
22
+ export const engineCommandBlocker = (command, engine, capabilities) => {
23
+ const requirement = COMMAND_REQUIREMENTS[command];
24
+ if (!requirement || capabilities[requirement])
25
+ return undefined;
26
+ return (`pgpm ${command} is not supported by the "${engine.name}" engine — ` +
27
+ `${REQUIREMENT_REASONS[requirement]}.`);
28
+ };
@@ -0,0 +1,89 @@
1
+ import { getEnvOptions } from '@pgpmjs/env';
2
+ import { BUILTIN_ENGINES, DEFAULT_ENGINE, } from '@pgpmjs/types';
3
+ import { activateDriver, driverOverrideFromArgv, PGLITE_DRIVER_PLUGIN } from './driver';
4
+ /** Everything a real Postgres server can do — the built-in `pg` engine. */
5
+ export const SERVER_CAPABILITIES = {
6
+ createdb: true,
7
+ dump: true,
8
+ serverLifecycle: true,
9
+ multiConnection: true,
10
+ };
11
+ /**
12
+ * The engines available by name: the built-ins, overridden/extended by the
13
+ * `engines` block of `pgpm.json` (sqitch's `[engine "name"]` sections).
14
+ *
15
+ * A configured entry is merged over its built-in, so declaring only options
16
+ * (e.g. `engines.pglite.options.dataDir`) keeps the built-in plugin.
17
+ */
18
+ export const engineDefinitions = (config) => {
19
+ const definitions = { ...BUILTIN_ENGINES };
20
+ for (const [name, configured] of Object.entries(config.engines ?? {})) {
21
+ definitions[name] = { ...definitions[name], ...configured };
22
+ }
23
+ return definitions;
24
+ };
25
+ const definitionToDriver = (definition, options) => definition.plugin
26
+ ? { plugin: definition.plugin, options: { ...definition.options, ...options } }
27
+ : undefined;
28
+ const lookupEngine = (name, config, options) => {
29
+ const definition = engineDefinitions(config)[name];
30
+ if (!definition) {
31
+ const known = Object.keys(engineDefinitions(config)).sort().join(', ');
32
+ throw new Error(`Unknown pgpm engine "${name}". Known engines: ${known}.\n` +
33
+ 'Declare it in the "engines" block of pgpm.json, or name its plugin with --driver <package>.');
34
+ }
35
+ return { name, driver: definitionToDriver(definition, options) };
36
+ };
37
+ /**
38
+ * Resolve which engine a command targets, mirroring sqitch's `core.engine` model:
39
+ * a short engine name selects the backend, and the built-in default is `pg`.
40
+ *
41
+ * Precedence (first match wins):
42
+ * 1. `--driver <package>` — name a driver plugin directly (escape hatch)
43
+ * 2. `--pglite[=dataDir]` — sugar for `--engine pglite`
44
+ * 3. `--engine <name>`
45
+ * 4. the `driver` block of `pgpm.json` — plugin-level configuration
46
+ * 5. the `engine` key of `pgpm.json` / `PGPM_ENGINE`
47
+ * 6. `pg`, the built-in server path
48
+ */
49
+ export const resolveEngine = (argv, config) => {
50
+ const override = driverOverrideFromArgv(argv);
51
+ if (override) {
52
+ if (override.plugin === PGLITE_DRIVER_PLUGIN && !argv.driver) {
53
+ // `--pglite` is the pglite engine, so it inherits that engine's configured
54
+ // options (e.g. a dataDir declared in pgpm.json) instead of bypassing them.
55
+ return lookupEngine('pglite', config, override.options);
56
+ }
57
+ return { name: override.plugin, driver: override };
58
+ }
59
+ if (argv.engine)
60
+ return lookupEngine(argv.engine, config);
61
+ if (config.driver)
62
+ return { name: config.driver.plugin, driver: config.driver };
63
+ return lookupEngine(config.engine ?? DEFAULT_ENGINE, config);
64
+ };
65
+ let active;
66
+ /**
67
+ * The engine activated for the running command. Commands read this to skip
68
+ * server-only steps (e.g. `deploy --createdb`) instead of hard-coding backends.
69
+ * Defaults to the built-in server path when nothing has been activated.
70
+ */
71
+ export const getActiveEngine = () => active ?? { engine: { name: DEFAULT_ENGINE }, capabilities: SERVER_CAPABILITIES };
72
+ /**
73
+ * Resolve the engine from argv + `pgpm.json` and activate its driver plugin.
74
+ * The driver has registered its pool/client factories by the time this resolves,
75
+ * so the unmodified pgpm engine runs against the selected backend.
76
+ */
77
+ export const activateEngine = async (argv, cwd = process.cwd()) => {
78
+ const config = getEnvOptions({}, cwd);
79
+ const engine = resolveEngine(argv, config);
80
+ const session = await activateDriver(engine.driver, undefined, cwd);
81
+ active = { engine, session, capabilities: session?.capabilities ?? SERVER_CAPABILITIES };
82
+ return active;
83
+ };
84
+ /** Tear down the active driver session and restore the built-in server path. */
85
+ export const deactivateEngine = async () => {
86
+ const session = active?.session;
87
+ active = undefined;
88
+ await session?.teardown();
89
+ };
@@ -3,6 +3,8 @@ export * from './deployed-changes';
3
3
  export * from './display';
4
4
  export * from './doctor';
5
5
  export * from './driver';
6
+ export * from './engine';
7
+ export * from './engine-gating';
6
8
  export * from './module-utils';
7
9
  export * from './npm-version';
8
10
  export * from './package-alias';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgpm",
3
- "version": "5.14.0",
3
+ "version": "5.15.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,19 +46,19 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@inquirerer/utils": "^3.3.9",
49
- "@pgpmjs/core": "^7.15.0",
50
- "@pgpmjs/env": "^2.38.0",
51
- "@pgpmjs/export": "^1.12.2",
49
+ "@pgpmjs/core": "^7.16.0",
50
+ "@pgpmjs/env": "^2.39.0",
51
+ "@pgpmjs/export": "^1.12.3",
52
52
  "@pgpmjs/logger": "^2.22.0",
53
- "@pgpmjs/slice": "^0.10.1",
54
- "@pgpmjs/types": "^2.46.0",
53
+ "@pgpmjs/slice": "^0.10.2",
54
+ "@pgpmjs/types": "^2.47.0",
55
55
  "@pgsql/quotes": "^18.1.0",
56
56
  "appstash": "^0.7.0",
57
57
  "find-and-require-package-json": "^0.9.1",
58
58
  "genomic": "^5.6.2",
59
59
  "inquirerer": "^4.9.1",
60
60
  "js-yaml": "^4.1.0",
61
- "pg-cache": "^3.23.1",
61
+ "pg-cache": "^3.23.2",
62
62
  "pg-env": "^1.26.0",
63
63
  "pgsql-deparser": "^18.1.1",
64
64
  "semver": "^7.8.1",
@@ -77,5 +77,5 @@
77
77
  "pg",
78
78
  "pgsql"
79
79
  ],
80
- "gitHead": "5ef8341033a8fbd85841b770e996e468dd15333b"
80
+ "gitHead": "0286d994d98717cb9b28842233d2db6d1e37c48e"
81
81
  }
@@ -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 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\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 package Package module for distribution\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
@@ -53,6 +53,11 @@ exports.usageText = `
53
53
  -h, --help Display this help information
54
54
  -v, --version Display version information
55
55
  --cwd <directory> Working directory (default: current directory)
56
+ --engine <name> Migration backend: pg (default, Postgres server) or
57
+ pglite (in-process WASM Postgres, no server). Also set by
58
+ the "engine" key of pgpm.json or PGPM_ENGINE.
59
+ --driver <pkg> Driver plugin package backing the engine (escape hatch)
60
+ --pglite[=dataDir] Sugar for --engine pglite, persisted to <dataDir> if given
56
61
 
57
62
  Individual Command Help:
58
63
  pgpm <command> --help Display detailed help for specific command
@@ -0,0 +1,6 @@
1
+ import { PgpmDriverCapabilities } from '@pgpmjs/types';
2
+ import { ResolvedEngine } from './engine';
3
+ /**
4
+ * The reason a command cannot run on the active engine, or undefined when it can.
5
+ */
6
+ export declare const engineCommandBlocker: (command: string, engine: ResolvedEngine, capabilities: PgpmDriverCapabilities) => string | undefined;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.engineCommandBlocker = void 0;
4
+ /**
5
+ * Commands that need a capability the backend may not have. Gating is declared
6
+ * here rather than branching per backend inside each command, so a new driver
7
+ * plugin only has to describe itself through {@link PgpmDriverCapabilities}.
8
+ */
9
+ const COMMAND_REQUIREMENTS = {
10
+ docker: 'serverLifecycle',
11
+ kill: 'serverLifecycle',
12
+ tune: 'serverLifecycle',
13
+ 'admin-users': 'multiConnection',
14
+ dump: 'dump',
15
+ };
16
+ const REQUIREMENT_REASONS = {
17
+ createdb: 'it cannot create databases (the instance is the database)',
18
+ dump: 'it has no pg_dump',
19
+ serverLifecycle: 'it has no server to manage',
20
+ multiConnection: 'it is a single-session backend',
21
+ };
22
+ /**
23
+ * The reason a command cannot run on the active engine, or undefined when it can.
24
+ */
25
+ const engineCommandBlocker = (command, engine, capabilities) => {
26
+ const requirement = COMMAND_REQUIREMENTS[command];
27
+ if (!requirement || capabilities[requirement])
28
+ return undefined;
29
+ return (`pgpm ${command} is not supported by the "${engine.name}" engine — ` +
30
+ `${REQUIREMENT_REASONS[requirement]}.`);
31
+ };
32
+ exports.engineCommandBlocker = engineCommandBlocker;
@@ -0,0 +1,57 @@
1
+ import { PgpmDriverCapabilities, PgpmDriverConfig, PgpmDriverSession, PgpmEngineConfig, PgpmOptions } from '@pgpmjs/types';
2
+ /** Everything a real Postgres server can do — the built-in `pg` engine. */
3
+ export declare const SERVER_CAPABILITIES: PgpmDriverCapabilities;
4
+ /** The engine/driver selectors every command accepts. */
5
+ export interface EngineArgv {
6
+ engine?: string;
7
+ driver?: string;
8
+ pglite?: boolean | string;
9
+ }
10
+ /** The engine a command runs against, plus the driver plugin backing it. */
11
+ export interface ResolvedEngine {
12
+ /** Engine name, for messages: a registered name or a plugin package. */
13
+ name: string;
14
+ /** Driver plugin to activate; undefined = built-in `pg` (server) path. */
15
+ driver?: PgpmDriverConfig;
16
+ }
17
+ /**
18
+ * The engines available by name: the built-ins, overridden/extended by the
19
+ * `engines` block of `pgpm.json` (sqitch's `[engine "name"]` sections).
20
+ *
21
+ * A configured entry is merged over its built-in, so declaring only options
22
+ * (e.g. `engines.pglite.options.dataDir`) keeps the built-in plugin.
23
+ */
24
+ export declare const engineDefinitions: (config: PgpmOptions) => Record<string, PgpmEngineConfig>;
25
+ /**
26
+ * Resolve which engine a command targets, mirroring sqitch's `core.engine` model:
27
+ * a short engine name selects the backend, and the built-in default is `pg`.
28
+ *
29
+ * Precedence (first match wins):
30
+ * 1. `--driver <package>` — name a driver plugin directly (escape hatch)
31
+ * 2. `--pglite[=dataDir]` — sugar for `--engine pglite`
32
+ * 3. `--engine <name>`
33
+ * 4. the `driver` block of `pgpm.json` — plugin-level configuration
34
+ * 5. the `engine` key of `pgpm.json` / `PGPM_ENGINE`
35
+ * 6. `pg`, the built-in server path
36
+ */
37
+ export declare const resolveEngine: (argv: EngineArgv, config: PgpmOptions) => ResolvedEngine;
38
+ /** The engine activated for the running command. */
39
+ export interface ActiveEngine {
40
+ engine: ResolvedEngine;
41
+ session?: PgpmDriverSession;
42
+ capabilities: PgpmDriverCapabilities;
43
+ }
44
+ /**
45
+ * The engine activated for the running command. Commands read this to skip
46
+ * server-only steps (e.g. `deploy --createdb`) instead of hard-coding backends.
47
+ * Defaults to the built-in server path when nothing has been activated.
48
+ */
49
+ export declare const getActiveEngine: () => ActiveEngine;
50
+ /**
51
+ * Resolve the engine from argv + `pgpm.json` and activate its driver plugin.
52
+ * The driver has registered its pool/client factories by the time this resolves,
53
+ * so the unmodified pgpm engine runs against the selected backend.
54
+ */
55
+ export declare const activateEngine: (argv: EngineArgv, cwd?: string) => Promise<ActiveEngine>;
56
+ /** Tear down the active driver session and restore the built-in server path. */
57
+ export declare const deactivateEngine: () => Promise<void>;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deactivateEngine = exports.activateEngine = exports.getActiveEngine = exports.resolveEngine = exports.engineDefinitions = exports.SERVER_CAPABILITIES = void 0;
4
+ const env_1 = require("@pgpmjs/env");
5
+ const types_1 = require("@pgpmjs/types");
6
+ const driver_1 = require("./driver");
7
+ /** Everything a real Postgres server can do — the built-in `pg` engine. */
8
+ exports.SERVER_CAPABILITIES = {
9
+ createdb: true,
10
+ dump: true,
11
+ serverLifecycle: true,
12
+ multiConnection: true,
13
+ };
14
+ /**
15
+ * The engines available by name: the built-ins, overridden/extended by the
16
+ * `engines` block of `pgpm.json` (sqitch's `[engine "name"]` sections).
17
+ *
18
+ * A configured entry is merged over its built-in, so declaring only options
19
+ * (e.g. `engines.pglite.options.dataDir`) keeps the built-in plugin.
20
+ */
21
+ const engineDefinitions = (config) => {
22
+ const definitions = { ...types_1.BUILTIN_ENGINES };
23
+ for (const [name, configured] of Object.entries(config.engines ?? {})) {
24
+ definitions[name] = { ...definitions[name], ...configured };
25
+ }
26
+ return definitions;
27
+ };
28
+ exports.engineDefinitions = engineDefinitions;
29
+ const definitionToDriver = (definition, options) => definition.plugin
30
+ ? { plugin: definition.plugin, options: { ...definition.options, ...options } }
31
+ : undefined;
32
+ const lookupEngine = (name, config, options) => {
33
+ const definition = (0, exports.engineDefinitions)(config)[name];
34
+ if (!definition) {
35
+ const known = Object.keys((0, exports.engineDefinitions)(config)).sort().join(', ');
36
+ throw new Error(`Unknown pgpm engine "${name}". Known engines: ${known}.\n` +
37
+ 'Declare it in the "engines" block of pgpm.json, or name its plugin with --driver <package>.');
38
+ }
39
+ return { name, driver: definitionToDriver(definition, options) };
40
+ };
41
+ /**
42
+ * Resolve which engine a command targets, mirroring sqitch's `core.engine` model:
43
+ * a short engine name selects the backend, and the built-in default is `pg`.
44
+ *
45
+ * Precedence (first match wins):
46
+ * 1. `--driver <package>` — name a driver plugin directly (escape hatch)
47
+ * 2. `--pglite[=dataDir]` — sugar for `--engine pglite`
48
+ * 3. `--engine <name>`
49
+ * 4. the `driver` block of `pgpm.json` — plugin-level configuration
50
+ * 5. the `engine` key of `pgpm.json` / `PGPM_ENGINE`
51
+ * 6. `pg`, the built-in server path
52
+ */
53
+ const resolveEngine = (argv, config) => {
54
+ const override = (0, driver_1.driverOverrideFromArgv)(argv);
55
+ if (override) {
56
+ if (override.plugin === driver_1.PGLITE_DRIVER_PLUGIN && !argv.driver) {
57
+ // `--pglite` is the pglite engine, so it inherits that engine's configured
58
+ // options (e.g. a dataDir declared in pgpm.json) instead of bypassing them.
59
+ return lookupEngine('pglite', config, override.options);
60
+ }
61
+ return { name: override.plugin, driver: override };
62
+ }
63
+ if (argv.engine)
64
+ return lookupEngine(argv.engine, config);
65
+ if (config.driver)
66
+ return { name: config.driver.plugin, driver: config.driver };
67
+ return lookupEngine(config.engine ?? types_1.DEFAULT_ENGINE, config);
68
+ };
69
+ exports.resolveEngine = resolveEngine;
70
+ let active;
71
+ /**
72
+ * The engine activated for the running command. Commands read this to skip
73
+ * server-only steps (e.g. `deploy --createdb`) instead of hard-coding backends.
74
+ * Defaults to the built-in server path when nothing has been activated.
75
+ */
76
+ const getActiveEngine = () => active ?? { engine: { name: types_1.DEFAULT_ENGINE }, capabilities: exports.SERVER_CAPABILITIES };
77
+ exports.getActiveEngine = getActiveEngine;
78
+ /**
79
+ * Resolve the engine from argv + `pgpm.json` and activate its driver plugin.
80
+ * The driver has registered its pool/client factories by the time this resolves,
81
+ * so the unmodified pgpm engine runs against the selected backend.
82
+ */
83
+ const activateEngine = async (argv, cwd = process.cwd()) => {
84
+ const config = (0, env_1.getEnvOptions)({}, cwd);
85
+ const engine = (0, exports.resolveEngine)(argv, config);
86
+ const session = await (0, driver_1.activateDriver)(engine.driver, undefined, cwd);
87
+ active = { engine, session, capabilities: session?.capabilities ?? exports.SERVER_CAPABILITIES };
88
+ return active;
89
+ };
90
+ exports.activateEngine = activateEngine;
91
+ /** Tear down the active driver session and restore the built-in server path. */
92
+ const deactivateEngine = async () => {
93
+ const session = active?.session;
94
+ active = undefined;
95
+ await session?.teardown();
96
+ };
97
+ exports.deactivateEngine = deactivateEngine;
package/utils/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export * from './deployed-changes';
3
3
  export * from './display';
4
4
  export * from './doctor';
5
5
  export * from './driver';
6
+ export * from './engine';
7
+ export * from './engine-gating';
6
8
  export * from './module-utils';
7
9
  export * from './npm-version';
8
10
  export * from './package-alias';
package/utils/index.js CHANGED
@@ -19,6 +19,8 @@ __exportStar(require("./deployed-changes"), exports);
19
19
  __exportStar(require("./display"), exports);
20
20
  __exportStar(require("./doctor"), exports);
21
21
  __exportStar(require("./driver"), exports);
22
+ __exportStar(require("./engine"), exports);
23
+ __exportStar(require("./engine-gating"), exports);
22
24
  __exportStar(require("./module-utils"), exports);
23
25
  __exportStar(require("./npm-version"), exports);
24
26
  __exportStar(require("./package-alias"), exports);