@pgpmjs/core 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/class/pgpm.d.ts +10 -0
- package/core/class/pgpm.js +44 -2
- package/core/template-scaffold.d.ts +1 -1
- package/core/template-scaffold.js +4 -4
- package/esm/core/class/pgpm.js +44 -2
- package/esm/core/template-scaffold.js +2 -2
- package/esm/export/export-migrations.js +50 -17
- package/export/export-migrations.js +50 -17
- package/package.json +5 -5
package/core/class/pgpm.d.ts
CHANGED
|
@@ -145,6 +145,16 @@ export declare class PgpmPackage {
|
|
|
145
145
|
installed: string[];
|
|
146
146
|
installedVersions: Record<string, string>;
|
|
147
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Returns all pgpm modules installed in the workspace extensions directory.
|
|
150
|
+
* Unlike getInstalledModules(), this does NOT require being inside a module.
|
|
151
|
+
* It scans the workspace/extensions/ directory directly to find installed packages.
|
|
152
|
+
*
|
|
153
|
+
* This is useful for checking what's available at workspace level before a module exists.
|
|
154
|
+
*
|
|
155
|
+
* @returns Array of installed npm package names (e.g., '@pgpm/base32', '@pgpm/totp')
|
|
156
|
+
*/
|
|
157
|
+
getWorkspaceInstalledModules(): string[];
|
|
148
158
|
/**
|
|
149
159
|
* Updates installed pgpm modules to their latest versions from npm.
|
|
150
160
|
* Re-installs each module to get the latest version.
|
package/core/class/pgpm.js
CHANGED
|
@@ -62,6 +62,11 @@ const package_1 = require("../../packaging/package");
|
|
|
62
62
|
const deps_1 = require("../../resolution/deps");
|
|
63
63
|
const target_utils_1 = require("../../utils/target-utils");
|
|
64
64
|
const logger = new logger_1.Logger('pgpm');
|
|
65
|
+
/**
|
|
66
|
+
* Directory name for workspace extensions.
|
|
67
|
+
* Extensions are installed globally in the workspace's extensions/ directory.
|
|
68
|
+
*/
|
|
69
|
+
const EXTENSIONS_DIR = 'extensions';
|
|
65
70
|
function getUTCTimestamp(d = new Date()) {
|
|
66
71
|
return (d.getUTCFullYear() +
|
|
67
72
|
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
@@ -786,7 +791,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
786
791
|
this.ensureWorkspace();
|
|
787
792
|
this.ensureModule();
|
|
788
793
|
const originalDir = process.cwd();
|
|
789
|
-
const skitchExtDir = path_1.default.join(this.workspacePath,
|
|
794
|
+
const skitchExtDir = path_1.default.join(this.workspacePath, EXTENSIONS_DIR);
|
|
790
795
|
const pkgJsonPath = path_1.default.join(this.modulePath, 'package.json');
|
|
791
796
|
if (!fs_1.default.existsSync(pkgJsonPath)) {
|
|
792
797
|
throw new Error(`No package.json found at module path: ${this.modulePath}`);
|
|
@@ -889,7 +894,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
889
894
|
}
|
|
890
895
|
const pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
|
|
891
896
|
const dependencies = pkgData.dependencies || {};
|
|
892
|
-
const skitchExtDir = path_1.default.join(this.workspacePath,
|
|
897
|
+
const skitchExtDir = path_1.default.join(this.workspacePath, EXTENSIONS_DIR);
|
|
893
898
|
const installed = [];
|
|
894
899
|
const installedVersions = {};
|
|
895
900
|
for (const [name, version] of Object.entries(dependencies)) {
|
|
@@ -901,6 +906,43 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
901
906
|
}
|
|
902
907
|
return { installed, installedVersions };
|
|
903
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* Returns all pgpm modules installed in the workspace extensions directory.
|
|
911
|
+
* Unlike getInstalledModules(), this does NOT require being inside a module.
|
|
912
|
+
* It scans the workspace/extensions/ directory directly to find installed packages.
|
|
913
|
+
*
|
|
914
|
+
* This is useful for checking what's available at workspace level before a module exists.
|
|
915
|
+
*
|
|
916
|
+
* @returns Array of installed npm package names (e.g., '@pgpm/base32', '@pgpm/totp')
|
|
917
|
+
*/
|
|
918
|
+
getWorkspaceInstalledModules() {
|
|
919
|
+
this.ensureWorkspace();
|
|
920
|
+
const extensionsDir = path_1.default.join(this.workspacePath, EXTENSIONS_DIR);
|
|
921
|
+
if (!fs_1.default.existsSync(extensionsDir)) {
|
|
922
|
+
return [];
|
|
923
|
+
}
|
|
924
|
+
const installed = [];
|
|
925
|
+
const entries = fs_1.default.readdirSync(extensionsDir, { withFileTypes: true });
|
|
926
|
+
for (const entry of entries) {
|
|
927
|
+
if (!entry.isDirectory())
|
|
928
|
+
continue;
|
|
929
|
+
if (entry.name.startsWith('@')) {
|
|
930
|
+
// Handle scoped packages like @pgpm/base32
|
|
931
|
+
const scopeDir = path_1.default.join(extensionsDir, entry.name);
|
|
932
|
+
const scopedEntries = fs_1.default.readdirSync(scopeDir, { withFileTypes: true });
|
|
933
|
+
for (const scopedEntry of scopedEntries) {
|
|
934
|
+
if (scopedEntry.isDirectory()) {
|
|
935
|
+
installed.push(`${entry.name}/${scopedEntry.name}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
// Handle non-scoped packages
|
|
941
|
+
installed.push(entry.name);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return installed;
|
|
945
|
+
}
|
|
904
946
|
/**
|
|
905
947
|
* Updates installed pgpm modules to their latest versions from npm.
|
|
906
948
|
* Re-installs each module to get the latest version.
|
|
@@ -8,9 +8,9 @@ exports.inspectTemplate = inspectTemplate;
|
|
|
8
8
|
exports.scaffoldTemplate = scaffoldTemplate;
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const
|
|
11
|
+
const genomic_1 = require("genomic");
|
|
12
12
|
exports.DEFAULT_TEMPLATE_REPO = 'https://github.com/constructive-io/pgpm-boilerplates.git';
|
|
13
|
-
exports.DEFAULT_TEMPLATE_TTL_MS =
|
|
13
|
+
exports.DEFAULT_TEMPLATE_TTL_MS = 1 * 24 * 60 * 60 * 1000; // 1 day
|
|
14
14
|
exports.DEFAULT_TEMPLATE_TOOL_NAME = 'pgpm';
|
|
15
15
|
function resolveCacheBaseDir(cacheBaseDir) {
|
|
16
16
|
if (cacheBaseDir) {
|
|
@@ -26,7 +26,7 @@ function resolveCacheBaseDir(cacheBaseDir) {
|
|
|
26
26
|
}
|
|
27
27
|
function inspectTemplate(options) {
|
|
28
28
|
const { fromPath, templateRepo = exports.DEFAULT_TEMPLATE_REPO, branch, cacheTtlMs = exports.DEFAULT_TEMPLATE_TTL_MS, toolName = exports.DEFAULT_TEMPLATE_TOOL_NAME, cwd, cacheBaseDir, dir, } = options;
|
|
29
|
-
const scaffolder = new
|
|
29
|
+
const scaffolder = new genomic_1.TemplateScaffolder({
|
|
30
30
|
toolName,
|
|
31
31
|
ttlMs: cacheTtlMs,
|
|
32
32
|
cacheBaseDir: resolveCacheBaseDir(cacheBaseDir),
|
|
@@ -62,7 +62,7 @@ function inspectTemplate(options) {
|
|
|
62
62
|
}
|
|
63
63
|
async function scaffoldTemplate(options) {
|
|
64
64
|
const { fromPath, outputDir, templateRepo = exports.DEFAULT_TEMPLATE_REPO, branch, answers, noTty = false, cacheTtlMs = exports.DEFAULT_TEMPLATE_TTL_MS, toolName = exports.DEFAULT_TEMPLATE_TOOL_NAME, cwd, cacheBaseDir, dir, prompter, } = options;
|
|
65
|
-
const scaffolder = new
|
|
65
|
+
const scaffolder = new genomic_1.TemplateScaffolder({
|
|
66
66
|
toolName,
|
|
67
67
|
ttlMs: cacheTtlMs,
|
|
68
68
|
cacheBaseDir: resolveCacheBaseDir(cacheBaseDir),
|
package/esm/core/class/pgpm.js
CHANGED
|
@@ -23,6 +23,11 @@ import { packageModule } from '../../packaging/package';
|
|
|
23
23
|
import { resolveExtensionDependencies, resolveDependencies } from '../../resolution/deps';
|
|
24
24
|
import { parseTarget } from '../../utils/target-utils';
|
|
25
25
|
const logger = new Logger('pgpm');
|
|
26
|
+
/**
|
|
27
|
+
* Directory name for workspace extensions.
|
|
28
|
+
* Extensions are installed globally in the workspace's extensions/ directory.
|
|
29
|
+
*/
|
|
30
|
+
const EXTENSIONS_DIR = 'extensions';
|
|
26
31
|
function getUTCTimestamp(d = new Date()) {
|
|
27
32
|
return (d.getUTCFullYear() +
|
|
28
33
|
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
@@ -747,7 +752,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
747
752
|
this.ensureWorkspace();
|
|
748
753
|
this.ensureModule();
|
|
749
754
|
const originalDir = process.cwd();
|
|
750
|
-
const skitchExtDir = path.join(this.workspacePath,
|
|
755
|
+
const skitchExtDir = path.join(this.workspacePath, EXTENSIONS_DIR);
|
|
751
756
|
const pkgJsonPath = path.join(this.modulePath, 'package.json');
|
|
752
757
|
if (!fs.existsSync(pkgJsonPath)) {
|
|
753
758
|
throw new Error(`No package.json found at module path: ${this.modulePath}`);
|
|
@@ -850,7 +855,7 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
850
855
|
}
|
|
851
856
|
const pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
852
857
|
const dependencies = pkgData.dependencies || {};
|
|
853
|
-
const skitchExtDir = path.join(this.workspacePath,
|
|
858
|
+
const skitchExtDir = path.join(this.workspacePath, EXTENSIONS_DIR);
|
|
854
859
|
const installed = [];
|
|
855
860
|
const installedVersions = {};
|
|
856
861
|
for (const [name, version] of Object.entries(dependencies)) {
|
|
@@ -862,6 +867,43 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
862
867
|
}
|
|
863
868
|
return { installed, installedVersions };
|
|
864
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* Returns all pgpm modules installed in the workspace extensions directory.
|
|
872
|
+
* Unlike getInstalledModules(), this does NOT require being inside a module.
|
|
873
|
+
* It scans the workspace/extensions/ directory directly to find installed packages.
|
|
874
|
+
*
|
|
875
|
+
* This is useful for checking what's available at workspace level before a module exists.
|
|
876
|
+
*
|
|
877
|
+
* @returns Array of installed npm package names (e.g., '@pgpm/base32', '@pgpm/totp')
|
|
878
|
+
*/
|
|
879
|
+
getWorkspaceInstalledModules() {
|
|
880
|
+
this.ensureWorkspace();
|
|
881
|
+
const extensionsDir = path.join(this.workspacePath, EXTENSIONS_DIR);
|
|
882
|
+
if (!fs.existsSync(extensionsDir)) {
|
|
883
|
+
return [];
|
|
884
|
+
}
|
|
885
|
+
const installed = [];
|
|
886
|
+
const entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
|
|
887
|
+
for (const entry of entries) {
|
|
888
|
+
if (!entry.isDirectory())
|
|
889
|
+
continue;
|
|
890
|
+
if (entry.name.startsWith('@')) {
|
|
891
|
+
// Handle scoped packages like @pgpm/base32
|
|
892
|
+
const scopeDir = path.join(extensionsDir, entry.name);
|
|
893
|
+
const scopedEntries = fs.readdirSync(scopeDir, { withFileTypes: true });
|
|
894
|
+
for (const scopedEntry of scopedEntries) {
|
|
895
|
+
if (scopedEntry.isDirectory()) {
|
|
896
|
+
installed.push(`${entry.name}/${scopedEntry.name}`);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
// Handle non-scoped packages
|
|
902
|
+
installed.push(entry.name);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
return installed;
|
|
906
|
+
}
|
|
865
907
|
/**
|
|
866
908
|
* Updates installed pgpm modules to their latest versions from npm.
|
|
867
909
|
* Re-installs each module to get the latest version.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { TemplateScaffolder } from '
|
|
3
|
+
import { TemplateScaffolder } from 'genomic';
|
|
4
4
|
export const DEFAULT_TEMPLATE_REPO = 'https://github.com/constructive-io/pgpm-boilerplates.git';
|
|
5
|
-
export const DEFAULT_TEMPLATE_TTL_MS =
|
|
5
|
+
export const DEFAULT_TEMPLATE_TTL_MS = 1 * 24 * 60 * 60 * 1000; // 1 day
|
|
6
6
|
export const DEFAULT_TEMPLATE_TOOL_NAME = 'pgpm';
|
|
7
7
|
function resolveCacheBaseDir(cacheBaseDir) {
|
|
8
8
|
if (cacheBaseDir) {
|
|
@@ -3,6 +3,7 @@ import { sync as glob } from 'glob';
|
|
|
3
3
|
import { toSnakeCase } from 'komoji';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { getPgPool } from 'pg-cache';
|
|
6
|
+
import { PgpmPackage } from '../core/class/pgpm';
|
|
6
7
|
import { writePgpmFiles, writePgpmPlan } from '../files';
|
|
7
8
|
import { getMissingInstallableModules } from '../modules/modules';
|
|
8
9
|
import { exportMeta } from './export-meta';
|
|
@@ -40,18 +41,22 @@ const SERVICE_REQUIRED_EXTENSIONS = [
|
|
|
40
41
|
];
|
|
41
42
|
/**
|
|
42
43
|
* Checks which pgpm modules from the extensions list are missing from the workspace
|
|
43
|
-
* and prompts the user to install them
|
|
44
|
+
* and prompts the user if they want to install them.
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
+
* This function only does detection and prompting - it does NOT install.
|
|
47
|
+
* Use installMissingModules() after the module is created to do the actual installation.
|
|
48
|
+
*
|
|
49
|
+
* @param project - The PgpmPackage instance (only needs workspace context)
|
|
46
50
|
* @param extensions - List of extension names (control file names)
|
|
47
51
|
* @param prompter - Optional prompter for interactive confirmation
|
|
48
|
-
* @returns
|
|
52
|
+
* @returns Object with missing modules and whether user wants to install them
|
|
49
53
|
*/
|
|
50
|
-
const
|
|
51
|
-
|
|
54
|
+
const detectMissingModules = async (project, extensions, prompter) => {
|
|
55
|
+
// Use workspace-level check - doesn't require being inside a module
|
|
56
|
+
const installed = project.getWorkspaceInstalledModules();
|
|
52
57
|
const missingModules = getMissingInstallableModules(extensions, installed);
|
|
53
58
|
if (missingModules.length === 0) {
|
|
54
|
-
return [];
|
|
59
|
+
return { missingModules: [], shouldInstall: false };
|
|
55
60
|
}
|
|
56
61
|
const missingNames = missingModules.map(m => m.npmName);
|
|
57
62
|
console.log(`\nMissing pgpm modules detected: ${missingNames.join(', ')}`);
|
|
@@ -64,14 +69,27 @@ const promptAndInstallMissingModules = async (project, extensions, prompter) =>
|
|
|
64
69
|
default: true
|
|
65
70
|
}
|
|
66
71
|
]);
|
|
67
|
-
|
|
68
|
-
console.log('Installing missing modules...');
|
|
69
|
-
await project.installModules(...missingNames);
|
|
70
|
-
console.log('Modules installed successfully.');
|
|
71
|
-
return missingModules.map(m => m.controlName);
|
|
72
|
-
}
|
|
72
|
+
return { missingModules, shouldInstall: install };
|
|
73
73
|
}
|
|
74
|
-
return
|
|
74
|
+
return { missingModules, shouldInstall: false };
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Installs missing modules into a specific module directory.
|
|
78
|
+
* Must be called after the module has been created.
|
|
79
|
+
*
|
|
80
|
+
* @param moduleDir - The directory of the module to install into
|
|
81
|
+
* @param missingModules - Array of missing modules to install
|
|
82
|
+
*/
|
|
83
|
+
const installMissingModules = async (moduleDir, missingModules) => {
|
|
84
|
+
if (missingModules.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const missingNames = missingModules.map(m => m.npmName);
|
|
88
|
+
console.log('Installing missing modules...');
|
|
89
|
+
// Create a new PgpmPackage instance pointing to the module directory
|
|
90
|
+
const moduleProject = new PgpmPackage(moduleDir);
|
|
91
|
+
await moduleProject.installModules(...missingNames);
|
|
92
|
+
console.log('Modules installed successfully.');
|
|
75
93
|
};
|
|
76
94
|
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter }) => {
|
|
77
95
|
outdir = outdir + '/';
|
|
@@ -104,8 +122,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
104
122
|
// Build description for the database extension package
|
|
105
123
|
const dbExtensionDesc = extensionDesc || `${name} database schema for ${databaseName}`;
|
|
106
124
|
if (results?.rows?.length > 0) {
|
|
107
|
-
|
|
108
|
-
await
|
|
125
|
+
// Detect missing modules at workspace level and prompt user
|
|
126
|
+
const dbMissingResult = await detectMissingModules(project, [...DB_REQUIRED_EXTENSIONS], prompter);
|
|
127
|
+
// Create/prepare the module directory
|
|
128
|
+
const dbModuleDir = await preparePackage({
|
|
109
129
|
project,
|
|
110
130
|
author,
|
|
111
131
|
outdir,
|
|
@@ -114,6 +134,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
114
134
|
extensions: [...DB_REQUIRED_EXTENSIONS],
|
|
115
135
|
prompter
|
|
116
136
|
});
|
|
137
|
+
// Install missing modules if user confirmed (now that module exists)
|
|
138
|
+
if (dbMissingResult.shouldInstall) {
|
|
139
|
+
await installMissingModules(dbModuleDir, dbMissingResult.missingModules);
|
|
140
|
+
}
|
|
117
141
|
writePgpmPlan(results.rows, opts);
|
|
118
142
|
writePgpmFiles(results.rows, opts);
|
|
119
143
|
let meta = await exportMeta({
|
|
@@ -124,8 +148,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
124
148
|
meta = replacer(meta);
|
|
125
149
|
// Build description for the meta/service extension package
|
|
126
150
|
const metaDesc = metaExtensionDesc || `${metaExtensionName} service utilities for managing domains, APIs, and services`;
|
|
127
|
-
|
|
128
|
-
await
|
|
151
|
+
// Detect missing modules at workspace level and prompt user
|
|
152
|
+
const svcMissingResult = await detectMissingModules(project, [...SERVICE_REQUIRED_EXTENSIONS], prompter);
|
|
153
|
+
// Create/prepare the module directory
|
|
154
|
+
const svcModuleDir = await preparePackage({
|
|
129
155
|
project,
|
|
130
156
|
author,
|
|
131
157
|
outdir,
|
|
@@ -134,6 +160,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
134
160
|
extensions: [...SERVICE_REQUIRED_EXTENSIONS],
|
|
135
161
|
prompter
|
|
136
162
|
});
|
|
163
|
+
// Install missing modules if user confirmed (now that module exists)
|
|
164
|
+
if (svcMissingResult.shouldInstall) {
|
|
165
|
+
await installMissingModules(svcModuleDir, svcMissingResult.missingModules);
|
|
166
|
+
}
|
|
137
167
|
const metaReplacer = makeReplacer({
|
|
138
168
|
schemas: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name)),
|
|
139
169
|
name: metaExtensionName
|
|
@@ -204,6 +234,8 @@ export const exportMigrations = async ({ project, options, dbInfo, author, outdi
|
|
|
204
234
|
/**
|
|
205
235
|
* Creates a PGPM package directory or resets the deploy/revert/verify directories if it exists.
|
|
206
236
|
* If the module already exists and a prompter is provided, prompts the user for confirmation.
|
|
237
|
+
*
|
|
238
|
+
* @returns The absolute path to the created/prepared module directory
|
|
207
239
|
*/
|
|
208
240
|
const preparePackage = async ({ project, author, outdir, name, description, extensions, prompter }) => {
|
|
209
241
|
const curDir = process.cwd();
|
|
@@ -245,6 +277,7 @@ const preparePackage = async ({ project, author, outdir, name, description, exte
|
|
|
245
277
|
rmSync(path.resolve(pgpmDir, 'verify'), { recursive: true, force: true });
|
|
246
278
|
}
|
|
247
279
|
process.chdir(curDir);
|
|
280
|
+
return pgpmDir;
|
|
248
281
|
};
|
|
249
282
|
/**
|
|
250
283
|
* Generates a function for replacing schema names and extension names in strings.
|
|
@@ -9,6 +9,7 @@ const glob_1 = require("glob");
|
|
|
9
9
|
const komoji_1 = require("komoji");
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const pg_cache_1 = require("pg-cache");
|
|
12
|
+
const pgpm_1 = require("../core/class/pgpm");
|
|
12
13
|
const files_1 = require("../files");
|
|
13
14
|
const modules_1 = require("../modules/modules");
|
|
14
15
|
const export_meta_1 = require("./export-meta");
|
|
@@ -46,18 +47,22 @@ const SERVICE_REQUIRED_EXTENSIONS = [
|
|
|
46
47
|
];
|
|
47
48
|
/**
|
|
48
49
|
* Checks which pgpm modules from the extensions list are missing from the workspace
|
|
49
|
-
* and prompts the user to install them
|
|
50
|
+
* and prompts the user if they want to install them.
|
|
50
51
|
*
|
|
51
|
-
*
|
|
52
|
+
* This function only does detection and prompting - it does NOT install.
|
|
53
|
+
* Use installMissingModules() after the module is created to do the actual installation.
|
|
54
|
+
*
|
|
55
|
+
* @param project - The PgpmPackage instance (only needs workspace context)
|
|
52
56
|
* @param extensions - List of extension names (control file names)
|
|
53
57
|
* @param prompter - Optional prompter for interactive confirmation
|
|
54
|
-
* @returns
|
|
58
|
+
* @returns Object with missing modules and whether user wants to install them
|
|
55
59
|
*/
|
|
56
|
-
const
|
|
57
|
-
|
|
60
|
+
const detectMissingModules = async (project, extensions, prompter) => {
|
|
61
|
+
// Use workspace-level check - doesn't require being inside a module
|
|
62
|
+
const installed = project.getWorkspaceInstalledModules();
|
|
58
63
|
const missingModules = (0, modules_1.getMissingInstallableModules)(extensions, installed);
|
|
59
64
|
if (missingModules.length === 0) {
|
|
60
|
-
return [];
|
|
65
|
+
return { missingModules: [], shouldInstall: false };
|
|
61
66
|
}
|
|
62
67
|
const missingNames = missingModules.map(m => m.npmName);
|
|
63
68
|
console.log(`\nMissing pgpm modules detected: ${missingNames.join(', ')}`);
|
|
@@ -70,14 +75,27 @@ const promptAndInstallMissingModules = async (project, extensions, prompter) =>
|
|
|
70
75
|
default: true
|
|
71
76
|
}
|
|
72
77
|
]);
|
|
73
|
-
|
|
74
|
-
console.log('Installing missing modules...');
|
|
75
|
-
await project.installModules(...missingNames);
|
|
76
|
-
console.log('Modules installed successfully.');
|
|
77
|
-
return missingModules.map(m => m.controlName);
|
|
78
|
-
}
|
|
78
|
+
return { missingModules, shouldInstall: install };
|
|
79
79
|
}
|
|
80
|
-
return
|
|
80
|
+
return { missingModules, shouldInstall: false };
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Installs missing modules into a specific module directory.
|
|
84
|
+
* Must be called after the module has been created.
|
|
85
|
+
*
|
|
86
|
+
* @param moduleDir - The directory of the module to install into
|
|
87
|
+
* @param missingModules - Array of missing modules to install
|
|
88
|
+
*/
|
|
89
|
+
const installMissingModules = async (moduleDir, missingModules) => {
|
|
90
|
+
if (missingModules.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const missingNames = missingModules.map(m => m.npmName);
|
|
94
|
+
console.log('Installing missing modules...');
|
|
95
|
+
// Create a new PgpmPackage instance pointing to the module directory
|
|
96
|
+
const moduleProject = new pgpm_1.PgpmPackage(moduleDir);
|
|
97
|
+
await moduleProject.installModules(...missingNames);
|
|
98
|
+
console.log('Modules installed successfully.');
|
|
81
99
|
};
|
|
82
100
|
const exportMigrationsToDisk = async ({ project, options, database, databaseId, databaseName, author, outdir, schema_names, extensionName, extensionDesc, metaExtensionName, metaExtensionDesc, prompter }) => {
|
|
83
101
|
outdir = outdir + '/';
|
|
@@ -110,8 +128,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
110
128
|
// Build description for the database extension package
|
|
111
129
|
const dbExtensionDesc = extensionDesc || `${name} database schema for ${databaseName}`;
|
|
112
130
|
if (results?.rows?.length > 0) {
|
|
113
|
-
|
|
114
|
-
await
|
|
131
|
+
// Detect missing modules at workspace level and prompt user
|
|
132
|
+
const dbMissingResult = await detectMissingModules(project, [...DB_REQUIRED_EXTENSIONS], prompter);
|
|
133
|
+
// Create/prepare the module directory
|
|
134
|
+
const dbModuleDir = await preparePackage({
|
|
115
135
|
project,
|
|
116
136
|
author,
|
|
117
137
|
outdir,
|
|
@@ -120,6 +140,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
120
140
|
extensions: [...DB_REQUIRED_EXTENSIONS],
|
|
121
141
|
prompter
|
|
122
142
|
});
|
|
143
|
+
// Install missing modules if user confirmed (now that module exists)
|
|
144
|
+
if (dbMissingResult.shouldInstall) {
|
|
145
|
+
await installMissingModules(dbModuleDir, dbMissingResult.missingModules);
|
|
146
|
+
}
|
|
123
147
|
(0, files_1.writePgpmPlan)(results.rows, opts);
|
|
124
148
|
(0, files_1.writePgpmFiles)(results.rows, opts);
|
|
125
149
|
let meta = await (0, export_meta_1.exportMeta)({
|
|
@@ -130,8 +154,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
130
154
|
meta = replacer(meta);
|
|
131
155
|
// Build description for the meta/service extension package
|
|
132
156
|
const metaDesc = metaExtensionDesc || `${metaExtensionName} service utilities for managing domains, APIs, and services`;
|
|
133
|
-
|
|
134
|
-
await
|
|
157
|
+
// Detect missing modules at workspace level and prompt user
|
|
158
|
+
const svcMissingResult = await detectMissingModules(project, [...SERVICE_REQUIRED_EXTENSIONS], prompter);
|
|
159
|
+
// Create/prepare the module directory
|
|
160
|
+
const svcModuleDir = await preparePackage({
|
|
135
161
|
project,
|
|
136
162
|
author,
|
|
137
163
|
outdir,
|
|
@@ -140,6 +166,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
|
|
|
140
166
|
extensions: [...SERVICE_REQUIRED_EXTENSIONS],
|
|
141
167
|
prompter
|
|
142
168
|
});
|
|
169
|
+
// Install missing modules if user confirmed (now that module exists)
|
|
170
|
+
if (svcMissingResult.shouldInstall) {
|
|
171
|
+
await installMissingModules(svcModuleDir, svcMissingResult.missingModules);
|
|
172
|
+
}
|
|
143
173
|
const metaReplacer = makeReplacer({
|
|
144
174
|
schemas: schemas.rows.filter((schema) => schema_names.includes(schema.schema_name)),
|
|
145
175
|
name: metaExtensionName
|
|
@@ -211,6 +241,8 @@ exports.exportMigrations = exportMigrations;
|
|
|
211
241
|
/**
|
|
212
242
|
* Creates a PGPM package directory or resets the deploy/revert/verify directories if it exists.
|
|
213
243
|
* If the module already exists and a prompter is provided, prompts the user for confirmation.
|
|
244
|
+
*
|
|
245
|
+
* @returns The absolute path to the created/prepared module directory
|
|
214
246
|
*/
|
|
215
247
|
const preparePackage = async ({ project, author, outdir, name, description, extensions, prompter }) => {
|
|
216
248
|
const curDir = process.cwd();
|
|
@@ -252,6 +284,7 @@ const preparePackage = async ({ project, author, outdir, name, description, exte
|
|
|
252
284
|
(0, fs_1.rmSync)(path_1.default.resolve(pgpmDir, 'verify'), { recursive: true, force: true });
|
|
253
285
|
}
|
|
254
286
|
process.chdir(curDir);
|
|
287
|
+
return pgpmDir;
|
|
255
288
|
};
|
|
256
289
|
/**
|
|
257
290
|
* Generates a function for replacing schema names and extension names in strings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PGPM Package and Migration Tools",
|
|
6
6
|
"main": "index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@pgsql/types": "^17.6.2",
|
|
45
45
|
"@types/pg": "^8.16.0",
|
|
46
46
|
"copyfiles": "^2.4.1",
|
|
47
|
-
"inquirerer": "^
|
|
47
|
+
"inquirerer": "^4.1.1",
|
|
48
48
|
"makage": "^0.1.9"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"@pgpmjs/logger": "^1.3.5",
|
|
53
53
|
"@pgpmjs/server-utils": "^2.8.11",
|
|
54
54
|
"@pgpmjs/types": "^2.12.8",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"csv-to-pg": "^3.2.0",
|
|
56
|
+
"genomic": "^5.0.2",
|
|
57
57
|
"glob": "^13.0.0",
|
|
58
58
|
"komoji": "^0.7.11",
|
|
59
59
|
"parse-package-name": "^1.0.0",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"pgsql-parser": "^17.9.5",
|
|
65
65
|
"yanse": "^0.1.8"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "7ac931c063d48e281349b748ba2eb9c9f47ffb06"
|
|
68
68
|
}
|