@pgpmjs/core 3.1.4 β†’ 3.2.1

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/README.md CHANGED
@@ -64,7 +64,6 @@ Common issues and solutions for pgpm, PostgreSQL, and testing.
64
64
  * [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **🏷️ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
65
65
  * [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **πŸ“ Type definitions** for PostgreSQL AST nodes in TypeScript.
66
66
  * [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **πŸ› οΈ AST utilities** for constructing and transforming PostgreSQL syntax trees.
67
- * [pg-ast](https://www.npmjs.com/package/pg-ast): **πŸ” Low-level AST tools** and transformations for Postgres query structures.
68
67
 
69
68
  ### πŸš€ API & Dev Tools
70
69
 
@@ -121,6 +121,60 @@ export declare class PgpmPackage {
121
121
  * and automatically adds it to the current module’s package.json dependencies.
122
122
  */
123
123
  installModules(...pkgstrs: string[]): Promise<void>;
124
+ /**
125
+ * Returns information about which of the specified modules are installed.
126
+ * Checks the module's package.json dependencies to determine installation status.
127
+ *
128
+ * @param moduleNames - Array of npm package names to check (e.g., '@pgpm/base32')
129
+ * @returns Object with installed (array of installed module names) and
130
+ * installedVersions (map of module name to installed version)
131
+ */
132
+ modulesInstalled(moduleNames: string[]): {
133
+ installed: string[];
134
+ installedVersions: Record<string, string>;
135
+ };
136
+ /**
137
+ * Returns all installed pgpm modules from the module's package.json dependencies.
138
+ * Only returns dependencies that exist in the workspace extensions directory.
139
+ *
140
+ * @returns Object with installed module names and their versions
141
+ */
142
+ getInstalledModules(): {
143
+ installed: string[];
144
+ installedVersions: Record<string, string>;
145
+ };
146
+ /**
147
+ * Updates installed pgpm modules to their latest versions from npm.
148
+ * Re-installs each module to get the latest version.
149
+ * Also updates package.json for ALL modules in the workspace that reference the upgraded packages.
150
+ *
151
+ * Note: Extensions are installed globally in the workspace's extensions/ directory,
152
+ * so upgrading affects all modules that depend on the upgraded package.
153
+ *
154
+ * @param options - Options for the update operation
155
+ * @param options.modules - Specific modules to update (defaults to all installed modules)
156
+ * @param options.dryRun - If true, only returns what would be updated without making changes
157
+ * @returns Object with updated modules and their old/new versions
158
+ */
159
+ upgradeModules(options?: {
160
+ modules?: string[];
161
+ dryRun?: boolean;
162
+ }): Promise<{
163
+ updates: Array<{
164
+ name: string;
165
+ oldVersion: string;
166
+ newVersion: string | null;
167
+ }>;
168
+ affectedModules: string[];
169
+ }>;
170
+ /**
171
+ * Updates package.json dependencies for all modules in the workspace that reference
172
+ * the specified packages with their new versions.
173
+ *
174
+ * @param packageVersions - Map of package name to new version
175
+ * @returns Array of module names that were updated
176
+ */
177
+ private updateWorkspaceModuleVersions;
124
178
  /**
125
179
  * Get the set of modules that have been deployed to the database
126
180
  */
@@ -848,6 +848,185 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
848
848
  const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
849
849
  (0, files_2.writeExtensions)(this.modulePath, updatedDeps);
850
850
  }
851
+ /**
852
+ * Returns information about which of the specified modules are installed.
853
+ * Checks the module's package.json dependencies to determine installation status.
854
+ *
855
+ * @param moduleNames - Array of npm package names to check (e.g., '@pgpm/base32')
856
+ * @returns Object with installed (array of installed module names) and
857
+ * installedVersions (map of module name to installed version)
858
+ */
859
+ modulesInstalled(moduleNames) {
860
+ this.ensureModule();
861
+ const pkgJsonPath = path_1.default.join(this.modulePath, 'package.json');
862
+ if (!fs_1.default.existsSync(pkgJsonPath)) {
863
+ return { installed: [], installedVersions: {} };
864
+ }
865
+ const pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
866
+ const dependencies = pkgData.dependencies || {};
867
+ const installed = [];
868
+ const installedVersions = {};
869
+ for (const moduleName of moduleNames) {
870
+ const { name } = (0, parse_package_name_1.parse)(moduleName);
871
+ if (dependencies[name]) {
872
+ installed.push(name);
873
+ installedVersions[name] = dependencies[name];
874
+ }
875
+ }
876
+ return { installed, installedVersions };
877
+ }
878
+ /**
879
+ * Returns all installed pgpm modules from the module's package.json dependencies.
880
+ * Only returns dependencies that exist in the workspace extensions directory.
881
+ *
882
+ * @returns Object with installed module names and their versions
883
+ */
884
+ getInstalledModules() {
885
+ this.ensureModule();
886
+ this.ensureWorkspace();
887
+ const pkgJsonPath = path_1.default.join(this.modulePath, 'package.json');
888
+ if (!fs_1.default.existsSync(pkgJsonPath)) {
889
+ return { installed: [], installedVersions: {} };
890
+ }
891
+ const pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
892
+ const dependencies = pkgData.dependencies || {};
893
+ const skitchExtDir = path_1.default.join(this.workspacePath, 'extensions');
894
+ const installed = [];
895
+ const installedVersions = {};
896
+ for (const [name, version] of Object.entries(dependencies)) {
897
+ const extPath = path_1.default.join(skitchExtDir, name);
898
+ if (fs_1.default.existsSync(extPath)) {
899
+ installed.push(name);
900
+ installedVersions[name] = version;
901
+ }
902
+ }
903
+ return { installed, installedVersions };
904
+ }
905
+ /**
906
+ * Updates installed pgpm modules to their latest versions from npm.
907
+ * Re-installs each module to get the latest version.
908
+ * Also updates package.json for ALL modules in the workspace that reference the upgraded packages.
909
+ *
910
+ * Note: Extensions are installed globally in the workspace's extensions/ directory,
911
+ * so upgrading affects all modules that depend on the upgraded package.
912
+ *
913
+ * @param options - Options for the update operation
914
+ * @param options.modules - Specific modules to update (defaults to all installed modules)
915
+ * @param options.dryRun - If true, only returns what would be updated without making changes
916
+ * @returns Object with updated modules and their old/new versions
917
+ */
918
+ async upgradeModules(options) {
919
+ this.ensureWorkspace();
920
+ this.ensureModule();
921
+ const { modules: specificModules, dryRun = false } = options || {};
922
+ const { installed, installedVersions } = this.getInstalledModules();
923
+ const modulesToUpdate = specificModules
924
+ ? installed.filter(m => specificModules.includes(m))
925
+ : installed;
926
+ if (modulesToUpdate.length === 0) {
927
+ logger.info('No modules to update.');
928
+ return { updates: [], affectedModules: [] };
929
+ }
930
+ const updates = [];
931
+ for (const moduleName of modulesToUpdate) {
932
+ const oldVersion = installedVersions[moduleName];
933
+ let newVersion = null;
934
+ try {
935
+ const result = (0, child_process_1.execSync)(`npm view ${moduleName} version`, {
936
+ encoding: 'utf-8',
937
+ stdio: ['pipe', 'pipe', 'pipe']
938
+ }).trim();
939
+ newVersion = result || null;
940
+ }
941
+ catch {
942
+ logger.warn(`Could not fetch latest version for ${moduleName}`);
943
+ }
944
+ updates.push({
945
+ name: moduleName,
946
+ oldVersion,
947
+ newVersion
948
+ });
949
+ }
950
+ if (dryRun) {
951
+ logger.info('Dry run - no changes made.');
952
+ for (const update of updates) {
953
+ if (update.newVersion && update.newVersion !== update.oldVersion) {
954
+ logger.info(` ${update.name}: ${update.oldVersion} -> ${update.newVersion}`);
955
+ }
956
+ else if (update.newVersion === update.oldVersion) {
957
+ logger.info(` ${update.name}: ${update.oldVersion} (already up to date)`);
958
+ }
959
+ else {
960
+ logger.warn(` ${update.name}: ${update.oldVersion} (could not fetch latest)`);
961
+ }
962
+ }
963
+ const updatesWithChanges = updates.filter(u => u.newVersion && u.newVersion !== u.oldVersion);
964
+ return { updates: updatesWithChanges, affectedModules: [] };
965
+ }
966
+ const modulesToReinstall = updates
967
+ .filter(u => u.newVersion && u.newVersion !== u.oldVersion)
968
+ .map(u => u.name);
969
+ if (modulesToReinstall.length === 0) {
970
+ logger.success('All modules are already up to date.');
971
+ return { updates, affectedModules: [] };
972
+ }
973
+ logger.info(`Upgrading ${modulesToReinstall.length} module(s)...`);
974
+ await this.installModules(...modulesToReinstall);
975
+ const { installedVersions: newVersions } = this.getInstalledModules();
976
+ for (const update of updates) {
977
+ if (modulesToReinstall.includes(update.name)) {
978
+ update.newVersion = newVersions[update.name] || update.newVersion;
979
+ }
980
+ }
981
+ // Update package.json for ALL modules in the workspace that reference the upgraded packages
982
+ const affectedModules = this.updateWorkspaceModuleVersions(modulesToReinstall.reduce((acc, name) => {
983
+ const update = updates.find(u => u.name === name);
984
+ if (update?.newVersion) {
985
+ acc[name] = update.newVersion;
986
+ }
987
+ return acc;
988
+ }, {}));
989
+ if (affectedModules.length > 0) {
990
+ logger.info(`Updated package.json for ${affectedModules.length} module(s) in workspace.`);
991
+ }
992
+ logger.success('Upgrade complete.');
993
+ return { updates, affectedModules };
994
+ }
995
+ /**
996
+ * Updates package.json dependencies for all modules in the workspace that reference
997
+ * the specified packages with their new versions.
998
+ *
999
+ * @param packageVersions - Map of package name to new version
1000
+ * @returns Array of module names that were updated
1001
+ */
1002
+ updateWorkspaceModuleVersions(packageVersions) {
1003
+ this.ensureWorkspace();
1004
+ const moduleMap = this.getModuleMap();
1005
+ const affectedModules = [];
1006
+ for (const [moduleName, moduleInfo] of Object.entries(moduleMap)) {
1007
+ const modulePath = path_1.default.resolve(this.workspacePath, moduleInfo.path);
1008
+ const pkgJsonPath = path_1.default.join(modulePath, 'package.json');
1009
+ if (!fs_1.default.existsSync(pkgJsonPath)) {
1010
+ continue;
1011
+ }
1012
+ const pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
1013
+ const dependencies = pkgData.dependencies || {};
1014
+ let updated = false;
1015
+ for (const [pkgName, newVersion] of Object.entries(packageVersions)) {
1016
+ if (dependencies[pkgName] && dependencies[pkgName] !== newVersion) {
1017
+ dependencies[pkgName] = newVersion;
1018
+ updated = true;
1019
+ }
1020
+ }
1021
+ if (updated) {
1022
+ pkgData.dependencies = sortObjectByKey(dependencies);
1023
+ fs_1.default.writeFileSync(pkgJsonPath, JSON.stringify(pkgData, null, 2));
1024
+ affectedModules.push(moduleName);
1025
+ logger.info(` Updated ${moduleName}/package.json`);
1026
+ }
1027
+ }
1028
+ return affectedModules;
1029
+ }
851
1030
  // ──────────────── Package Operations ────────────────
852
1031
  /**
853
1032
  * Get the set of modules that have been deployed to the database
@@ -809,6 +809,185 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
809
809
  const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
810
810
  writeExtensions(this.modulePath, updatedDeps);
811
811
  }
812
+ /**
813
+ * Returns information about which of the specified modules are installed.
814
+ * Checks the module's package.json dependencies to determine installation status.
815
+ *
816
+ * @param moduleNames - Array of npm package names to check (e.g., '@pgpm/base32')
817
+ * @returns Object with installed (array of installed module names) and
818
+ * installedVersions (map of module name to installed version)
819
+ */
820
+ modulesInstalled(moduleNames) {
821
+ this.ensureModule();
822
+ const pkgJsonPath = path.join(this.modulePath, 'package.json');
823
+ if (!fs.existsSync(pkgJsonPath)) {
824
+ return { installed: [], installedVersions: {} };
825
+ }
826
+ const pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
827
+ const dependencies = pkgData.dependencies || {};
828
+ const installed = [];
829
+ const installedVersions = {};
830
+ for (const moduleName of moduleNames) {
831
+ const { name } = parse(moduleName);
832
+ if (dependencies[name]) {
833
+ installed.push(name);
834
+ installedVersions[name] = dependencies[name];
835
+ }
836
+ }
837
+ return { installed, installedVersions };
838
+ }
839
+ /**
840
+ * Returns all installed pgpm modules from the module's package.json dependencies.
841
+ * Only returns dependencies that exist in the workspace extensions directory.
842
+ *
843
+ * @returns Object with installed module names and their versions
844
+ */
845
+ getInstalledModules() {
846
+ this.ensureModule();
847
+ this.ensureWorkspace();
848
+ const pkgJsonPath = path.join(this.modulePath, 'package.json');
849
+ if (!fs.existsSync(pkgJsonPath)) {
850
+ return { installed: [], installedVersions: {} };
851
+ }
852
+ const pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
853
+ const dependencies = pkgData.dependencies || {};
854
+ const skitchExtDir = path.join(this.workspacePath, 'extensions');
855
+ const installed = [];
856
+ const installedVersions = {};
857
+ for (const [name, version] of Object.entries(dependencies)) {
858
+ const extPath = path.join(skitchExtDir, name);
859
+ if (fs.existsSync(extPath)) {
860
+ installed.push(name);
861
+ installedVersions[name] = version;
862
+ }
863
+ }
864
+ return { installed, installedVersions };
865
+ }
866
+ /**
867
+ * Updates installed pgpm modules to their latest versions from npm.
868
+ * Re-installs each module to get the latest version.
869
+ * Also updates package.json for ALL modules in the workspace that reference the upgraded packages.
870
+ *
871
+ * Note: Extensions are installed globally in the workspace's extensions/ directory,
872
+ * so upgrading affects all modules that depend on the upgraded package.
873
+ *
874
+ * @param options - Options for the update operation
875
+ * @param options.modules - Specific modules to update (defaults to all installed modules)
876
+ * @param options.dryRun - If true, only returns what would be updated without making changes
877
+ * @returns Object with updated modules and their old/new versions
878
+ */
879
+ async upgradeModules(options) {
880
+ this.ensureWorkspace();
881
+ this.ensureModule();
882
+ const { modules: specificModules, dryRun = false } = options || {};
883
+ const { installed, installedVersions } = this.getInstalledModules();
884
+ const modulesToUpdate = specificModules
885
+ ? installed.filter(m => specificModules.includes(m))
886
+ : installed;
887
+ if (modulesToUpdate.length === 0) {
888
+ logger.info('No modules to update.');
889
+ return { updates: [], affectedModules: [] };
890
+ }
891
+ const updates = [];
892
+ for (const moduleName of modulesToUpdate) {
893
+ const oldVersion = installedVersions[moduleName];
894
+ let newVersion = null;
895
+ try {
896
+ const result = execSync(`npm view ${moduleName} version`, {
897
+ encoding: 'utf-8',
898
+ stdio: ['pipe', 'pipe', 'pipe']
899
+ }).trim();
900
+ newVersion = result || null;
901
+ }
902
+ catch {
903
+ logger.warn(`Could not fetch latest version for ${moduleName}`);
904
+ }
905
+ updates.push({
906
+ name: moduleName,
907
+ oldVersion,
908
+ newVersion
909
+ });
910
+ }
911
+ if (dryRun) {
912
+ logger.info('Dry run - no changes made.');
913
+ for (const update of updates) {
914
+ if (update.newVersion && update.newVersion !== update.oldVersion) {
915
+ logger.info(` ${update.name}: ${update.oldVersion} -> ${update.newVersion}`);
916
+ }
917
+ else if (update.newVersion === update.oldVersion) {
918
+ logger.info(` ${update.name}: ${update.oldVersion} (already up to date)`);
919
+ }
920
+ else {
921
+ logger.warn(` ${update.name}: ${update.oldVersion} (could not fetch latest)`);
922
+ }
923
+ }
924
+ const updatesWithChanges = updates.filter(u => u.newVersion && u.newVersion !== u.oldVersion);
925
+ return { updates: updatesWithChanges, affectedModules: [] };
926
+ }
927
+ const modulesToReinstall = updates
928
+ .filter(u => u.newVersion && u.newVersion !== u.oldVersion)
929
+ .map(u => u.name);
930
+ if (modulesToReinstall.length === 0) {
931
+ logger.success('All modules are already up to date.');
932
+ return { updates, affectedModules: [] };
933
+ }
934
+ logger.info(`Upgrading ${modulesToReinstall.length} module(s)...`);
935
+ await this.installModules(...modulesToReinstall);
936
+ const { installedVersions: newVersions } = this.getInstalledModules();
937
+ for (const update of updates) {
938
+ if (modulesToReinstall.includes(update.name)) {
939
+ update.newVersion = newVersions[update.name] || update.newVersion;
940
+ }
941
+ }
942
+ // Update package.json for ALL modules in the workspace that reference the upgraded packages
943
+ const affectedModules = this.updateWorkspaceModuleVersions(modulesToReinstall.reduce((acc, name) => {
944
+ const update = updates.find(u => u.name === name);
945
+ if (update?.newVersion) {
946
+ acc[name] = update.newVersion;
947
+ }
948
+ return acc;
949
+ }, {}));
950
+ if (affectedModules.length > 0) {
951
+ logger.info(`Updated package.json for ${affectedModules.length} module(s) in workspace.`);
952
+ }
953
+ logger.success('Upgrade complete.');
954
+ return { updates, affectedModules };
955
+ }
956
+ /**
957
+ * Updates package.json dependencies for all modules in the workspace that reference
958
+ * the specified packages with their new versions.
959
+ *
960
+ * @param packageVersions - Map of package name to new version
961
+ * @returns Array of module names that were updated
962
+ */
963
+ updateWorkspaceModuleVersions(packageVersions) {
964
+ this.ensureWorkspace();
965
+ const moduleMap = this.getModuleMap();
966
+ const affectedModules = [];
967
+ for (const [moduleName, moduleInfo] of Object.entries(moduleMap)) {
968
+ const modulePath = path.resolve(this.workspacePath, moduleInfo.path);
969
+ const pkgJsonPath = path.join(modulePath, 'package.json');
970
+ if (!fs.existsSync(pkgJsonPath)) {
971
+ continue;
972
+ }
973
+ const pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
974
+ const dependencies = pkgData.dependencies || {};
975
+ let updated = false;
976
+ for (const [pkgName, newVersion] of Object.entries(packageVersions)) {
977
+ if (dependencies[pkgName] && dependencies[pkgName] !== newVersion) {
978
+ dependencies[pkgName] = newVersion;
979
+ updated = true;
980
+ }
981
+ }
982
+ if (updated) {
983
+ pkgData.dependencies = sortObjectByKey(dependencies);
984
+ fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgData, null, 2));
985
+ affectedModules.push(moduleName);
986
+ logger.info(` Updated ${moduleName}/package.json`);
987
+ }
988
+ }
989
+ return affectedModules;
990
+ }
812
991
  // ──────────────── Package Operations ────────────────
813
992
  /**
814
993
  * Get the set of modules that have been deployed to the database
@@ -13,7 +13,7 @@ const config = {
13
13
  },
14
14
  database_extension: {
15
15
  schema: 'collections_public',
16
- table: 'database_extensions',
16
+ table: 'database_extension',
17
17
  fields: {
18
18
  name: 'text',
19
19
  database_id: 'uuid'
@@ -224,6 +224,7 @@ export const exportMeta = async ({ opts, dbname, database_id }) => {
224
224
  await queryAndParse('database', `SELECT * FROM collections_public.database WHERE id = $1`);
225
225
  await queryAndParse('schema', `SELECT * FROM collections_public.schema WHERE database_id = $1`);
226
226
  await queryAndParse('table', `SELECT * FROM collections_public.table WHERE database_id = $1`);
227
+ await queryAndParse('field', `SELECT * FROM collections_public.field WHERE database_id = $1`);
227
228
  await queryAndParse('domains', `SELECT * FROM meta_public.domains WHERE database_id = $1`);
228
229
  await queryAndParse('apis', `SELECT * FROM meta_public.apis WHERE database_id = $1`);
229
230
  await queryAndParse('sites', `SELECT * FROM meta_public.sites WHERE database_id = $1`);
@@ -16,7 +16,7 @@ const config = {
16
16
  },
17
17
  database_extension: {
18
18
  schema: 'collections_public',
19
- table: 'database_extensions',
19
+ table: 'database_extension',
20
20
  fields: {
21
21
  name: 'text',
22
22
  database_id: 'uuid'
@@ -227,6 +227,7 @@ const exportMeta = async ({ opts, dbname, database_id }) => {
227
227
  await queryAndParse('database', `SELECT * FROM collections_public.database WHERE id = $1`);
228
228
  await queryAndParse('schema', `SELECT * FROM collections_public.schema WHERE database_id = $1`);
229
229
  await queryAndParse('table', `SELECT * FROM collections_public.table WHERE database_id = $1`);
230
+ await queryAndParse('field', `SELECT * FROM collections_public.field WHERE database_id = $1`);
230
231
  await queryAndParse('domains', `SELECT * FROM meta_public.domains WHERE database_id = $1`);
231
232
  await queryAndParse('apis', `SELECT * FROM meta_public.apis WHERE database_id = $1`);
232
233
  await queryAndParse('sites', `SELECT * FROM meta_public.sites WHERE database_id = $1`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpmjs/core",
3
- "version": "3.1.4",
3
+ "version": "3.2.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PGPM Package and Migration Tools",
6
6
  "main": "index.js",
@@ -49,9 +49,9 @@
49
49
  "dependencies": {
50
50
  "@pgpmjs/env": "^2.8.8",
51
51
  "@pgpmjs/logger": "^1.3.5",
52
- "@pgpmjs/server-utils": "^2.8.8",
52
+ "@pgpmjs/server-utils": "^2.8.9",
53
53
  "@pgpmjs/types": "^2.12.6",
54
- "create-gen-app": "^0.6.0",
54
+ "create-gen-app": "^0.6.2",
55
55
  "csv-to-pg": "^2.0.10",
56
56
  "glob": "^13.0.0",
57
57
  "komoji": "^0.7.11",
@@ -59,9 +59,9 @@
59
59
  "pg": "^8.16.3",
60
60
  "pg-cache": "^1.6.9",
61
61
  "pg-env": "^1.2.4",
62
- "pgsql-deparser": "^17.12.2",
63
- "pgsql-parser": "^17.9.2",
62
+ "pgsql-deparser": "^17.13.0",
63
+ "pgsql-parser": "^17.9.3",
64
64
  "yanse": "^0.1.8"
65
65
  },
66
- "gitHead": "fc182ff9e4c4745a3e86eda6d58e3b0061f36564"
66
+ "gitHead": "976cc9e0e09201c7df40518a1797f4178fc21c2c"
67
67
  }