@pgpmjs/core 7.13.0 → 7.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.
@@ -9,6 +9,10 @@ import { parse } from 'parse-package-name';
9
9
  import path, { dirname, resolve } from 'path';
10
10
  import { getPgPool } from 'pg-cache';
11
11
  import yanse from 'yanse';
12
+ import { resolveEffectiveModulePath } from '../../apply/materialize';
13
+ import { hasApplySpec, readApplySpec } from '../../apply/apply-spec';
14
+ import { isReuseSpec, resolveSharedModuleName } from '../../apply/reuse';
15
+ import { APPLY_SPEC_FILE } from '../../apply/types';
12
16
  import { getAvailableExtensions } from '../../extensions/extensions';
13
17
  import { generatePlan, writePlan, writePlanFile } from '@pgpmjs/ast/files';
14
18
  import { getExtensionInfo, getExtensionName, getInstalledExtensions, parseControlFile, writeExtensions } from '@pgpmjs/ast/files';
@@ -18,7 +22,7 @@ import { parsePlanFile } from '@pgpmjs/ast/files/plan/parser';
18
22
  import { isValidChangeName, isValidTagName, parseReference } from '@pgpmjs/ast/files/plan/validators';
19
23
  import { PgpmMigrate } from '../../migrate/client';
20
24
  import { getExtensionsAndModules, getExtensionsAndModulesChanges, latestChange, latestChangeAndVersion } from '../../modules/modules';
21
- import { packageModule } from '../../packaging/package';
25
+ import { deployModuleFast, isBundledDeployResult } from '../../bundle/deploy-bundled';
22
26
  import { syncModuleVersions } from '../../packaging/sync-versions';
23
27
  import { resolveDependencies, resolveExtensionDependencies } from '../../resolution/deps';
24
28
  import { movePath } from '../../utils/fs';
@@ -242,11 +246,61 @@ export class PgpmPackage {
242
246
  }
243
247
  });
244
248
  // Parse the selected control files
245
- return Array.from(selectedFiles.entries()).reduce((acc, [moduleName, file]) => {
249
+ const moduleMap = Array.from(selectedFiles.entries()).reduce((acc, [moduleName, file]) => {
246
250
  const module = parseControlFile(file, this.workspacePath);
247
251
  acc[moduleName] = module;
248
252
  return acc;
249
253
  }, {});
254
+ return this.addApplyModules(moduleMap, packageDirs);
255
+ }
256
+ /**
257
+ * Discover apply-spec proxy modules (`pgpm.apply.json`) and synthesize
258
+ * module-map entries for them so they participate in dependency resolution
259
+ * by name like any other module. A proxy needs no `.control` or `pgpm.plan`;
260
+ * its `requires` default to the source module's requires. Control-file
261
+ * modules win name collisions.
262
+ */
263
+ addApplyModules(moduleMap, packageDirs) {
264
+ const specFiles = [...new Set(packageDirs.flatMap(dir => glob.sync(globPattern(dir, `**/${APPLY_SPEC_FILE}`))))].filter((file) => !/node_modules/.test(file)).sort((a, b) => a.localeCompare(b));
265
+ for (const file of specFiles) {
266
+ let spec;
267
+ try {
268
+ spec = readApplySpec(path.dirname(file));
269
+ }
270
+ catch (err) {
271
+ console.warn(`Skipping invalid apply spec ${file}: ${err instanceof Error ? err.message : err}`);
272
+ continue;
273
+ }
274
+ const name = spec.name;
275
+ if (moduleMap[name])
276
+ continue;
277
+ const source = moduleMap[spec.source.module];
278
+ const relPath = path
279
+ .dirname(path.relative(this.workspacePath, file))
280
+ .replace(/\\/g, '/');
281
+ const baseRequires = spec.requires ?? source?.requires ?? [];
282
+ const version = spec.version ?? source?.version ?? '0.0.1';
283
+ if (isReuseSpec(spec)) {
284
+ // A reuse proxy expands into two modules that both materialize from
285
+ // this one directory: a shared module (deployed once, reused across
286
+ // tenants) and the per-tenant module that `requires` it. Multiple
287
+ // tenant proxies over the same source/shared/seed config resolve to
288
+ // the same deterministic shared name, so it is added at most once.
289
+ const sharedName = resolveSharedModuleName(spec);
290
+ if (!moduleMap[sharedName]) {
291
+ moduleMap[sharedName] = { path: relPath, requires: baseRequires, version };
292
+ }
293
+ moduleMap[name] = {
294
+ path: relPath,
295
+ requires: [...baseRequires, sharedName],
296
+ version
297
+ };
298
+ }
299
+ else {
300
+ moduleMap[name] = { path: relPath, requires: baseRequires, version };
301
+ }
302
+ }
303
+ return moduleMap;
250
304
  }
251
305
  getModuleMap() {
252
306
  if (!this.workspacePath)
@@ -1196,6 +1250,38 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1196
1250
  external: external
1197
1251
  };
1198
1252
  }
1253
+ /**
1254
+ * Dependency closure for a named module. Proxy (apply-spec) modules have no
1255
+ * pgpm.plan, so they resolve straight off the workspace module map instead
1256
+ * of instantiating a module-rooted package.
1257
+ */
1258
+ getModuleExtensionsFor(name) {
1259
+ const modules = this.getModuleMap();
1260
+ if (!modules[name]) {
1261
+ throw errors.MODULE_NOT_FOUND({ name });
1262
+ }
1263
+ const modulePath = resolve(this.workspacePath, modules[name].path);
1264
+ if (hasApplySpec(modulePath)) {
1265
+ return resolveExtensionDependencies(name, modules);
1266
+ }
1267
+ return this.getModuleProject(name).getModuleExtensions();
1268
+ }
1269
+ /**
1270
+ * Directory the migration engine should run against for a named module:
1271
+ * the module's own directory, or — for proxy (apply-spec) modules — the
1272
+ * materialized transpiled instance.
1273
+ */
1274
+ async getEffectiveModulePathFor(name) {
1275
+ const modules = this.getModuleMap();
1276
+ if (!modules[name]) {
1277
+ throw errors.MODULE_NOT_FOUND({ name });
1278
+ }
1279
+ const modulePath = resolve(this.workspacePath, modules[name].path);
1280
+ if (hasApplySpec(modulePath)) {
1281
+ return resolveEffectiveModulePath(name, modulePath, modules, this.workspacePath);
1282
+ }
1283
+ return this.getModuleProject(name).getModulePath();
1284
+ }
1199
1285
  parsePackageTarget(target) {
1200
1286
  let name;
1201
1287
  let toChange;
@@ -1227,12 +1313,6 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1227
1313
  const log = new Logger('deploy');
1228
1314
  const { name, toChange } = this.parsePackageTarget(target);
1229
1315
  if (recursive) {
1230
- // Cache for fast deployment
1231
- const deployFastCache = {};
1232
- const getCacheKey = (pg, name, database) => {
1233
- const { host, port, user } = pg ?? {};
1234
- return `${host}:${port}:${user}:${database}:${name}`;
1235
- };
1236
1316
  const modules = this.getModuleMap();
1237
1317
  let extensions;
1238
1318
  if (name === null) {
@@ -1240,8 +1320,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1240
1320
  extensions = await this.resolveWorkspaceExtensionDependencies();
1241
1321
  }
1242
1322
  else {
1243
- const moduleProject = this.getModuleProject(name);
1244
- extensions = moduleProject.getModuleExtensions();
1323
+ extensions = this.getModuleExtensionsFor(name);
1245
1324
  }
1246
1325
  const pgPool = getPgPool(opts.pg);
1247
1326
  const targetDescription = name === null ? 'all modules' : name;
@@ -1254,52 +1333,24 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1254
1333
  await pgPool.query(msg);
1255
1334
  }
1256
1335
  else {
1257
- const modulePath = resolve(this.workspacePath, modules[extension].path);
1336
+ const modulePath = await resolveEffectiveModulePath(extension, resolve(this.workspacePath, modules[extension].path), modules, this.workspacePath);
1258
1337
  log.info(`📂 Deploying local module: ${extension}`);
1259
- if (opts.deployment.fast) {
1260
- const localProject = this.getModuleProject(extension);
1261
- const cacheKey = getCacheKey(opts.pg, extension, opts.pg.database);
1262
- if (opts.deployment.cache && deployFastCache[cacheKey]) {
1263
- log.warn(`⚡ Using cached pkg for ${extension}.`);
1264
- await pgPool.query(deployFastCache[cacheKey].sql);
1338
+ if (opts.deployment.fast || opts.deployment.bundled) {
1339
+ // Fast strategy: one-shot execution plus a bulk-written
1340
+ // pgpm_migrate ledger, from the module's verified bundle artifact
1341
+ // when it has one and from `deploy/` when it does not.
1342
+ const outcome = await deployModuleFast(modulePath, {
1343
+ config: opts.pg,
1344
+ logOnly: opts.deployment.logOnly,
1345
+ useTransaction: opts.deployment.useTx,
1346
+ hashMethod: opts.deployment.hashMethod
1347
+ });
1348
+ if (isBundledDeployResult(outcome)) {
1265
1349
  continue;
1266
1350
  }
1267
- let pkg;
1268
- try {
1269
- pkg = await packageModule(localProject.modulePath, {
1270
- usePlan: opts.deployment.usePlan,
1271
- extension: false
1272
- });
1273
- }
1274
- catch (err) {
1275
- const errorLines = [];
1276
- errorLines.push(`❌ Failed to package module "${extension}" at path: ${modulePath}`);
1277
- errorLines.push(` Module Path: ${modulePath}`);
1278
- errorLines.push(` Workspace Path: ${this.workspacePath}`);
1279
- errorLines.push(` Error Code: ${err.code || 'N/A'}`);
1280
- errorLines.push(` Error Message: ${err.message || 'Unknown error'}`);
1281
- if (err.code === 'ENOENT') {
1282
- errorLines.push('💡 Hint: File or directory not found. Check if the module path is correct.');
1283
- }
1284
- else if (err.code === 'EACCES') {
1285
- errorLines.push('💡 Hint: Permission denied. Check file permissions.');
1286
- }
1287
- else if (err.message && err.message.includes('pgpm.plan')) {
1288
- errorLines.push('💡 Hint: pgpm.plan file issue. Check if the plan file exists and is valid.');
1289
- }
1290
- log.error(errorLines.join('\n'));
1291
- console.error(err);
1292
- throw errors.DEPLOYMENT_FAILED({
1293
- type: 'Deployment',
1294
- module: extension
1295
- });
1296
- }
1297
- await pgPool.query(pkg.sql);
1298
- if (opts.deployment.cache) {
1299
- deployFastCache[cacheKey] = pkg;
1300
- }
1351
+ log.info(`↩️ Fast deploy unavailable for ${extension} (${outcome}); using per-change path.`);
1301
1352
  }
1302
- else {
1353
+ {
1303
1354
  try {
1304
1355
  const client = new PgpmMigrate(opts.pg);
1305
1356
  // Only apply toChange to the target module, not its dependencies
@@ -1335,8 +1386,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1335
1386
  if (name === null) {
1336
1387
  throw errors.WORKSPACE_OPERATION_ERROR({ operation: 'deployment' });
1337
1388
  }
1338
- const moduleProject = this.getModuleProject(name);
1339
- const modulePath = moduleProject.getModulePath();
1389
+ const modulePath = await this.getEffectiveModulePathFor(name);
1340
1390
  if (!modulePath) {
1341
1391
  throw errors.PATH_NOT_FOUND({ path: name, type: 'module' });
1342
1392
  }
@@ -1403,7 +1453,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1403
1453
  }
1404
1454
  }
1405
1455
  else {
1406
- const modulePath = resolve(this.workspacePath, modules[extension].path);
1456
+ const modulePath = await resolveEffectiveModulePath(extension, resolve(this.workspacePath, modules[extension].path), modules, this.workspacePath);
1407
1457
  log.info(`📂 Reverting local module: ${extension}`);
1408
1458
  try {
1409
1459
  const client = new PgpmMigrate(opts.pg);
@@ -1436,8 +1486,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1436
1486
  if (name === null) {
1437
1487
  throw errors.WORKSPACE_OPERATION_ERROR({ operation: 'revert' });
1438
1488
  }
1439
- const moduleProject = this.getModuleProject(name);
1440
- const modulePath = moduleProject.getModulePath();
1489
+ const modulePath = await this.getEffectiveModulePathFor(name);
1441
1490
  if (!modulePath) {
1442
1491
  throw errors.PATH_NOT_FOUND({ path: name, type: 'module' });
1443
1492
  }
@@ -1464,8 +1513,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1464
1513
  extensions = await this.resolveWorkspaceExtensionDependencies();
1465
1514
  }
1466
1515
  else {
1467
- const moduleProject = this.getModuleProject(name);
1468
- extensions = moduleProject.getModuleExtensions();
1516
+ extensions = this.getModuleExtensionsFor(name);
1469
1517
  }
1470
1518
  const pgPool = getPgPool(opts.pg);
1471
1519
  const targetDescription = name === null ? 'all modules' : name;
@@ -1478,7 +1526,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1478
1526
  await pgPool.query(query, [extension]);
1479
1527
  }
1480
1528
  else {
1481
- const modulePath = resolve(this.workspacePath, modules[extension].path);
1529
+ const modulePath = await resolveEffectiveModulePath(extension, resolve(this.workspacePath, modules[extension].path), modules, this.workspacePath);
1482
1530
  log.info(`📂 Verifying local module: ${extension}`);
1483
1531
  try {
1484
1532
  const client = new PgpmMigrate(opts.pg);
@@ -1510,8 +1558,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
1510
1558
  if (name === null) {
1511
1559
  throw errors.WORKSPACE_OPERATION_ERROR({ operation: 'verification' });
1512
1560
  }
1513
- const moduleProject = this.getModuleProject(name);
1514
- const modulePath = moduleProject.getModulePath();
1561
+ const modulePath = await this.getEffectiveModulePathFor(name);
1515
1562
  if (!modulePath) {
1516
1563
  throw errors.PATH_NOT_FOUND({ path: name, type: 'module' });
1517
1564
  }
package/esm/index.js CHANGED
@@ -16,6 +16,7 @@ export * from './core/boilerplate-scanner';
16
16
  // Re-export the module AST layer (moved to the @pgpmjs/ast leaf package)
17
17
  export * from '@pgpmjs/ast/files';
18
18
  export * from '@pgpmjs/ast/module';
19
+ export * from './apply';
19
20
  export * from './bundle';
20
21
  export * from './refactor';
21
22
  export { cleanSql } from './migrate/clean';
@@ -4,6 +4,7 @@ import { relative } from 'path';
4
4
  import { deparse } from 'pgsql-deparser';
5
5
  import { parse } from 'pgsql-parser';
6
6
  import { getExtensionName } from '@pgpmjs/ast/files';
7
+ import { writeBundleArtifact } from '../bundle/artifact';
7
8
  import { resolve, resolveWithPlan } from '../resolution/resolve';
8
9
  import { transformProps } from './transform';
9
10
  const log = new Logger('package');
@@ -78,7 +79,7 @@ export const packageModule = async (packageDir, { usePlan = true, extension = tr
78
79
  throw e;
79
80
  }
80
81
  };
81
- export const writePackage = async ({ version, extension = true, usePlan = true, packageDir, outputDiff = false, }) => {
82
+ export const writePackage = async ({ version, extension = true, usePlan = true, packageDir, outputDiff = false, bundle = true, }) => {
82
83
  const pkgPath = `${packageDir}/package.json`;
83
84
  const pkg = require(pkgPath);
84
85
  const extname = await getExtensionName(packageDir);
@@ -115,4 +116,10 @@ export const writePackage = async ({ version, extension = true, usePlan = true,
115
116
  const writePath = `${outPath}/${sqlFileName}`;
116
117
  writeFileSync(writePath, sql);
117
118
  log.success(`${relative(packageDir, writePath)} written`);
119
+ if (bundle) {
120
+ const bundlePath = await writeBundleArtifact(packageDir, version, {
121
+ createdWith: '@pgpmjs/core'
122
+ });
123
+ log.success(`${relative(packageDir, bundlePath)} written`);
124
+ }
118
125
  };
@@ -4,15 +4,12 @@ import { errors } from '@pgpmjs/types';
4
4
  import { resolve } from 'path';
5
5
  import * as path from 'path';
6
6
  import { getPgPool } from 'pg-cache';
7
+ import { resolveEffectiveModulePath } from '../apply/materialize';
8
+ import { hasApplySpec } from '../apply/apply-spec';
9
+ import { deployModuleFast, isBundledDeployResult } from '../bundle/deploy-bundled';
7
10
  import { PgpmPackage } from '../core/class/pgpm';
8
11
  import { PgpmMigrate } from '../migrate/client';
9
- import { packageModule } from '../packaging/package';
10
- // Cache for fast deployment
11
- const deployFastCache = {};
12
- const getCacheKey = (pg, name, database) => {
13
- const { host, port, user } = pg ?? {};
14
- return `${host}:${port}:${user}:${database}:${name}`;
15
- };
12
+ import { resolveExtensionDependencies } from '../resolution/deps';
16
13
  const log = new Logger('deploy');
17
14
  export const deployProject = async (opts, name, database, pkg, toChange) => {
18
15
  const mergedOpts = getEnvOptions(opts);
@@ -23,9 +20,12 @@ export const deployProject = async (opts, name, database, pkg, toChange) => {
23
20
  throw errors.MODULE_NOT_FOUND({ name });
24
21
  }
25
22
  const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
26
- const moduleProject = new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir });
27
23
  log.info(`📦 Resolving dependencies for ${name}...`);
28
- const extensions = moduleProject.getModuleExtensions();
24
+ // Proxy (apply-spec) modules have no pgpm.plan, so resolve straight off the
25
+ // workspace module map instead of instantiating a module-rooted package.
26
+ const extensions = hasApplySpec(modulePath)
27
+ ? resolveExtensionDependencies(name, modules)
28
+ : new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir }).getModuleExtensions();
29
29
  const pgPool = getPgPool({ ...opts.pg, database });
30
30
  log.success(`🚀 Starting deployment to database ${database}...`);
31
31
  for (const extension of extensions.resolved) {
@@ -37,59 +37,32 @@ export const deployProject = async (opts, name, database, pkg, toChange) => {
37
37
  await pgPool.query(msg);
38
38
  }
39
39
  else {
40
- const modulePath = resolve(pkg.workspacePath, modules[extension].path);
40
+ const sourcePath = resolve(pkg.workspacePath, modules[extension].path);
41
+ const modulePath = await resolveEffectiveModulePath(extension, sourcePath, modules, pkg.workspacePath);
41
42
  log.info(`📂 Deploying local module: ${extension}`);
42
43
  log.debug(`→ Path: ${modulePath}`);
43
- if (mergedOpts.deployment.fast) {
44
- // Use fast deployment strategy
45
- const localProject = new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir });
46
- const cacheKey = getCacheKey(mergedOpts.pg, extension, database);
47
- if (mergedOpts.deployment.cache && deployFastCache[cacheKey]) {
48
- log.warn(`⚡ Using cached package for ${extension}.`);
49
- await pgPool.query(deployFastCache[cacheKey].sql);
44
+ if (modulePath !== sourcePath) {
45
+ log.info(`🔁 Applying transpiled module (spec in ${sourcePath})`);
46
+ }
47
+ // The fast strategy is opt-in; everything else uses the per-change path.
48
+ if (mergedOpts.deployment.fast || mergedOpts.deployment.bundled) {
49
+ // Fast strategy: execute the module in one shot AND record the
50
+ // migration ledger, from the pre-built bundle artifact when one
51
+ // verifies and from `deploy/` otherwise. If the semantics cannot be
52
+ // honoured, fall through to the per-change migration path rather than
53
+ // deploying without a ledger.
54
+ const outcome = await deployModuleFast(modulePath, {
55
+ config: { ...mergedOpts.pg, database },
56
+ logOnly: mergedOpts.deployment.logOnly,
57
+ useTransaction: mergedOpts.deployment.useTx,
58
+ hashMethod: mergedOpts.deployment.hashMethod
59
+ });
60
+ if (isBundledDeployResult(outcome)) {
50
61
  continue;
51
62
  }
52
- let modulePackage;
53
- try {
54
- modulePackage = await packageModule(localProject.modulePath, {
55
- usePlan: mergedOpts.deployment.usePlan,
56
- extension: false
57
- });
58
- }
59
- catch (err) {
60
- // Build comprehensive error message
61
- const errorLines = [];
62
- errorLines.push(`❌ Failed to package module "${extension}" at path: ${modulePath}`);
63
- errorLines.push(` Module Path: ${modulePath}`);
64
- errorLines.push(` Workspace Path: ${pkg.workspacePath}`);
65
- errorLines.push(` Error Code: ${err.code || 'N/A'}`);
66
- errorLines.push(` Error Message: ${err.message || 'Unknown error'}`);
67
- // Provide debugging hints
68
- if (err.code === 'ENOENT') {
69
- errorLines.push('💡 Hint: File or directory not found. Check if the module path is correct.');
70
- }
71
- else if (err.code === 'EACCES') {
72
- errorLines.push('💡 Hint: Permission denied. Check file permissions.');
73
- }
74
- else if (err.message && err.message.includes('pgpm.plan')) {
75
- errorLines.push('💡 Hint: pgpm.plan file issue. Check if the plan file exists and is valid.');
76
- }
77
- // Log the consolidated error message
78
- log.error(errorLines.join('\n'));
79
- console.error(err); // Preserve full stack trace
80
- throw errors.DEPLOYMENT_FAILED({
81
- type: 'Deployment',
82
- module: extension
83
- });
84
- }
85
- log.debug(`→ Command: sqitch deploy db:pg:${database}`);
86
- log.debug(`> ${modulePackage.sql}`);
87
- await pgPool.query(modulePackage.sql);
88
- if (mergedOpts.deployment.cache) {
89
- deployFastCache[cacheKey] = modulePackage;
90
- }
63
+ log.info(`↩️ Fast deploy unavailable for ${extension} (${outcome}); using per-change path.`);
91
64
  }
92
- else {
65
+ {
93
66
  // Use new migration system
94
67
  log.debug(`→ Command: constructive migrate deploy db:pg:${database}`);
95
68
  try {
@@ -3,8 +3,11 @@ import { errors } from '@pgpmjs/types';
3
3
  import { resolve } from 'path';
4
4
  import * as path from 'path';
5
5
  import { getPgPool } from 'pg-cache';
6
+ import { resolveEffectiveModulePath } from '../apply/materialize';
7
+ import { hasApplySpec } from '../apply/apply-spec';
6
8
  import { PgpmPackage } from '../core/class/pgpm';
7
9
  import { PgpmMigrate } from '../migrate/client';
10
+ import { resolveExtensionDependencies } from '../resolution/deps';
8
11
  const log = new Logger('revert');
9
12
  export const revertProject = async (opts, name, database, pkg, options) => {
10
13
  log.info(`🔍 Gathering modules from ${pkg.workspacePath}...`);
@@ -14,9 +17,12 @@ export const revertProject = async (opts, name, database, pkg, options) => {
14
17
  throw errors.MODULE_NOT_FOUND({ name });
15
18
  }
16
19
  const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
17
- const moduleProject = new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir });
18
20
  log.info(`📦 Resolving dependencies for ${name}...`);
19
- const extensions = moduleProject.getModuleExtensions();
21
+ // Proxy (apply-spec) modules have no pgpm.plan, so resolve straight off the
22
+ // workspace module map instead of instantiating a module-rooted package.
23
+ const extensions = hasApplySpec(modulePath)
24
+ ? resolveExtensionDependencies(name, modules)
25
+ : new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir }).getModuleExtensions();
20
26
  const pgPool = getPgPool({
21
27
  ...opts.pg,
22
28
  database
@@ -42,7 +48,8 @@ export const revertProject = async (opts, name, database, pkg, options) => {
42
48
  }
43
49
  }
44
50
  else {
45
- const modulePath = resolve(pkg.workspacePath, modules[extension].path);
51
+ const sourcePath = resolve(pkg.workspacePath, modules[extension].path);
52
+ const modulePath = await resolveEffectiveModulePath(extension, sourcePath, modules, pkg.workspacePath);
46
53
  log.info(`📂 Reverting local module: ${extension}`);
47
54
  log.debug(`→ Path: ${modulePath}`);
48
55
  // Use new migration system
@@ -3,8 +3,11 @@ import { errors } from '@pgpmjs/types';
3
3
  import { resolve } from 'path';
4
4
  import * as path from 'path';
5
5
  import { getPgPool } from 'pg-cache';
6
+ import { resolveEffectiveModulePath } from '../apply/materialize';
7
+ import { hasApplySpec } from '../apply/apply-spec';
6
8
  import { PgpmPackage } from '../core/class/pgpm';
7
9
  import { PgpmMigrate } from '../migrate/client';
10
+ import { resolveExtensionDependencies } from '../resolution/deps';
8
11
  const log = new Logger('verify');
9
12
  export const verifyProject = async (opts, name, database, pkg, options) => {
10
13
  log.info(`🔍 Gathering modules from ${pkg.workspacePath}...`);
@@ -14,9 +17,12 @@ export const verifyProject = async (opts, name, database, pkg, options) => {
14
17
  throw errors.MODULE_NOT_FOUND({ name });
15
18
  }
16
19
  const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
17
- const moduleProject = new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir });
18
20
  log.info(`📦 Resolving dependencies for ${name}...`);
19
- const extensions = moduleProject.getModuleExtensions();
21
+ // Proxy (apply-spec) modules have no pgpm.plan, so resolve straight off the
22
+ // workspace module map instead of instantiating a module-rooted package.
23
+ const extensions = hasApplySpec(modulePath)
24
+ ? resolveExtensionDependencies(name, modules)
25
+ : new PgpmPackage(modulePath, { extensionsDir: pkg.extensionsDir }).getModuleExtensions();
20
26
  const pgPool = getPgPool({
21
27
  ...opts.pg,
22
28
  database
@@ -31,7 +37,8 @@ export const verifyProject = async (opts, name, database, pkg, options) => {
31
37
  await pgPool.query(query, [extension]);
32
38
  }
33
39
  else {
34
- const modulePath = resolve(pkg.workspacePath, modules[extension].path);
40
+ const sourcePath = resolve(pkg.workspacePath, modules[extension].path);
41
+ const modulePath = await resolveEffectiveModulePath(extension, sourcePath, modules, pkg.workspacePath);
35
42
  log.info(`📂 Verifying local module: ${extension}`);
36
43
  log.debug(`→ Path: ${modulePath}`);
37
44
  log.debug(`→ Command: constructive migrate verify db:pg:${database}`);
package/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from './core/boilerplate-types';
15
15
  export * from './core/boilerplate-scanner';
16
16
  export * from '@pgpmjs/ast/files';
17
17
  export * from '@pgpmjs/ast/module';
18
+ export * from './apply';
18
19
  export * from './bundle';
19
20
  export * from './refactor';
20
21
  export { cleanSql } from './migrate/clean';
package/index.js CHANGED
@@ -33,6 +33,7 @@ __exportStar(require("./core/boilerplate-scanner"), exports);
33
33
  // Re-export the module AST layer (moved to the @pgpmjs/ast leaf package)
34
34
  __exportStar(require("@pgpmjs/ast/files"), exports);
35
35
  __exportStar(require("@pgpmjs/ast/module"), exports);
36
+ __exportStar(require("./apply"), exports);
36
37
  __exportStar(require("./bundle"), exports);
37
38
  __exportStar(require("./refactor"), exports);
38
39
  var clean_1 = require("./migrate/clean");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpmjs/core",
3
- "version": "7.13.0",
3
+ "version": "7.15.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PGPM Package and Migration Tools",
6
6
  "main": "index.js",
@@ -48,23 +48,24 @@
48
48
  "makage": "^0.3.0"
49
49
  },
50
50
  "dependencies": {
51
- "@pgpmjs/ast": "^0.9.0",
52
- "@pgpmjs/bundle": "^0.10.0",
53
- "@pgpmjs/env": "^2.37.0",
51
+ "@pgpmjs/ast": "^0.9.2",
52
+ "@pgpmjs/bundle": "^0.12.0",
53
+ "@pgpmjs/env": "^2.38.0",
54
54
  "@pgpmjs/logger": "^2.22.0",
55
- "@pgpmjs/server-utils": "^3.23.0",
56
- "@pgpmjs/slice": "^0.9.0",
57
- "@pgpmjs/types": "^2.45.0",
55
+ "@pgpmjs/server-utils": "^3.23.1",
56
+ "@pgpmjs/slice": "^0.10.1",
57
+ "@pgpmjs/transform": "^0.11.0",
58
+ "@pgpmjs/types": "^2.46.0",
58
59
  "csv-to-pg": "^4.8.0",
59
60
  "genomic": "^5.6.2",
60
61
  "glob": "^13.0.6",
61
62
  "parse-package-name": "^1.0.0",
62
63
  "pg": "^8.21.0",
63
- "pg-cache": "^3.23.0",
64
+ "pg-cache": "^3.23.1",
64
65
  "pg-env": "^1.26.0",
65
66
  "pgsql-deparser": "^18.1.1",
66
67
  "pgsql-parser": "^18.1.1",
67
68
  "yanse": "^0.2.1"
68
69
  },
69
- "gitHead": "72961803894504c1a9308f9c6c349ff7b48bbe14"
70
+ "gitHead": "5ef8341033a8fbd85841b770e996e468dd15333b"
70
71
  }
@@ -9,6 +9,12 @@ interface PackageModuleOptions {
9
9
  interface WritePackageOptions extends PackageModuleOptions {
10
10
  version: string;
11
11
  packageDir: string;
12
+ /**
13
+ * Also emit the content-addressed bundle artifact
14
+ * `sql/<name>--<version>.bundle.tar.gz` (default true). It is what
15
+ * `deploy --bundled` consumes.
16
+ */
17
+ bundle?: boolean;
12
18
  }
13
19
  export interface MergeSqlOptions {
14
20
  /** Strip `TransactionStmt`/`CreateExtensionStmt` before deparsing (extension mode) */
@@ -38,5 +44,5 @@ export declare const packageModule: (packageDir: string, { usePlan, extension, p
38
44
  tree1?: string;
39
45
  tree2?: string;
40
46
  }>;
41
- export declare const writePackage: ({ version, extension, usePlan, packageDir, outputDiff, }: WritePackageOptions) => Promise<void>;
47
+ export declare const writePackage: ({ version, extension, usePlan, packageDir, outputDiff, bundle, }: WritePackageOptions) => Promise<void>;
42
48
  export {};
@@ -7,6 +7,7 @@ const path_1 = require("path");
7
7
  const pgsql_deparser_1 = require("pgsql-deparser");
8
8
  const pgsql_parser_1 = require("pgsql-parser");
9
9
  const files_1 = require("@pgpmjs/ast/files");
10
+ const artifact_1 = require("../bundle/artifact");
10
11
  const resolve_1 = require("../resolution/resolve");
11
12
  const transform_1 = require("./transform");
12
13
  const log = new logger_1.Logger('package');
@@ -84,7 +85,7 @@ const packageModule = async (packageDir, { usePlan = true, extension = true, pre
84
85
  }
85
86
  };
86
87
  exports.packageModule = packageModule;
87
- const writePackage = async ({ version, extension = true, usePlan = true, packageDir, outputDiff = false, }) => {
88
+ const writePackage = async ({ version, extension = true, usePlan = true, packageDir, outputDiff = false, bundle = true, }) => {
88
89
  const pkgPath = `${packageDir}/package.json`;
89
90
  const pkg = require(pkgPath);
90
91
  const extname = await (0, files_1.getExtensionName)(packageDir);
@@ -121,5 +122,11 @@ const writePackage = async ({ version, extension = true, usePlan = true, package
121
122
  const writePath = `${outPath}/${sqlFileName}`;
122
123
  (0, fs_1.writeFileSync)(writePath, sql);
123
124
  log.success(`${(0, path_1.relative)(packageDir, writePath)} written`);
125
+ if (bundle) {
126
+ const bundlePath = await (0, artifact_1.writeBundleArtifact)(packageDir, version, {
127
+ createdWith: '@pgpmjs/core'
128
+ });
129
+ log.success(`${(0, path_1.relative)(packageDir, bundlePath)} written`);
130
+ }
124
131
  };
125
132
  exports.writePackage = writePackage;