@pgpmjs/core 3.0.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.
Files changed (140) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +99 -0
  3. package/core/boilerplate-scanner.d.ts +41 -0
  4. package/core/boilerplate-scanner.js +106 -0
  5. package/core/boilerplate-types.d.ts +52 -0
  6. package/core/boilerplate-types.js +6 -0
  7. package/core/class/pgpm.d.ts +150 -0
  8. package/core/class/pgpm.js +1470 -0
  9. package/core/template-scaffold.d.ts +29 -0
  10. package/core/template-scaffold.js +168 -0
  11. package/esm/core/boilerplate-scanner.js +96 -0
  12. package/esm/core/boilerplate-types.js +5 -0
  13. package/esm/core/class/pgpm.js +1430 -0
  14. package/esm/core/template-scaffold.js +161 -0
  15. package/esm/export/export-meta.js +240 -0
  16. package/esm/export/export-migrations.js +180 -0
  17. package/esm/extensions/extensions.js +31 -0
  18. package/esm/files/extension/index.js +3 -0
  19. package/esm/files/extension/reader.js +79 -0
  20. package/esm/files/extension/writer.js +63 -0
  21. package/esm/files/index.js +6 -0
  22. package/esm/files/plan/generator.js +49 -0
  23. package/esm/files/plan/index.js +5 -0
  24. package/esm/files/plan/parser.js +296 -0
  25. package/esm/files/plan/validators.js +181 -0
  26. package/esm/files/plan/writer.js +114 -0
  27. package/esm/files/sql/index.js +1 -0
  28. package/esm/files/sql/writer.js +107 -0
  29. package/esm/files/sql-scripts/index.js +2 -0
  30. package/esm/files/sql-scripts/reader.js +19 -0
  31. package/esm/files/types/index.js +1 -0
  32. package/esm/files/types/package.js +1 -0
  33. package/esm/index.js +21 -0
  34. package/esm/init/client.js +144 -0
  35. package/esm/init/sql/bootstrap-roles.sql +55 -0
  36. package/esm/init/sql/bootstrap-test-roles.sql +72 -0
  37. package/esm/migrate/clean.js +23 -0
  38. package/esm/migrate/client.js +551 -0
  39. package/esm/migrate/index.js +5 -0
  40. package/esm/migrate/sql/procedures.sql +258 -0
  41. package/esm/migrate/sql/schema.sql +37 -0
  42. package/esm/migrate/types.js +1 -0
  43. package/esm/migrate/utils/event-logger.js +28 -0
  44. package/esm/migrate/utils/hash.js +27 -0
  45. package/esm/migrate/utils/transaction.js +125 -0
  46. package/esm/modules/modules.js +49 -0
  47. package/esm/packaging/package.js +96 -0
  48. package/esm/packaging/transform.js +70 -0
  49. package/esm/projects/deploy.js +123 -0
  50. package/esm/projects/revert.js +75 -0
  51. package/esm/projects/verify.js +61 -0
  52. package/esm/resolution/deps.js +526 -0
  53. package/esm/resolution/resolve.js +101 -0
  54. package/esm/utils/debug.js +147 -0
  55. package/esm/utils/target-utils.js +37 -0
  56. package/esm/workspace/paths.js +43 -0
  57. package/esm/workspace/utils.js +31 -0
  58. package/export/export-meta.d.ts +8 -0
  59. package/export/export-meta.js +244 -0
  60. package/export/export-migrations.d.ts +17 -0
  61. package/export/export-migrations.js +187 -0
  62. package/extensions/extensions.d.ts +5 -0
  63. package/extensions/extensions.js +35 -0
  64. package/files/extension/index.d.ts +2 -0
  65. package/files/extension/index.js +19 -0
  66. package/files/extension/reader.d.ts +24 -0
  67. package/files/extension/reader.js +86 -0
  68. package/files/extension/writer.d.ts +39 -0
  69. package/files/extension/writer.js +70 -0
  70. package/files/index.d.ts +5 -0
  71. package/files/index.js +22 -0
  72. package/files/plan/generator.d.ts +22 -0
  73. package/files/plan/generator.js +57 -0
  74. package/files/plan/index.d.ts +4 -0
  75. package/files/plan/index.js +21 -0
  76. package/files/plan/parser.d.ts +27 -0
  77. package/files/plan/parser.js +303 -0
  78. package/files/plan/validators.d.ts +52 -0
  79. package/files/plan/validators.js +187 -0
  80. package/files/plan/writer.d.ts +27 -0
  81. package/files/plan/writer.js +124 -0
  82. package/files/sql/index.d.ts +1 -0
  83. package/files/sql/index.js +17 -0
  84. package/files/sql/writer.d.ts +12 -0
  85. package/files/sql/writer.js +114 -0
  86. package/files/sql-scripts/index.d.ts +1 -0
  87. package/files/sql-scripts/index.js +18 -0
  88. package/files/sql-scripts/reader.d.ts +8 -0
  89. package/files/sql-scripts/reader.js +23 -0
  90. package/files/types/index.d.ts +46 -0
  91. package/files/types/index.js +17 -0
  92. package/files/types/package.d.ts +20 -0
  93. package/files/types/package.js +2 -0
  94. package/index.d.ts +21 -0
  95. package/index.js +45 -0
  96. package/init/client.d.ts +26 -0
  97. package/init/client.js +148 -0
  98. package/init/sql/bootstrap-roles.sql +55 -0
  99. package/init/sql/bootstrap-test-roles.sql +72 -0
  100. package/migrate/clean.d.ts +1 -0
  101. package/migrate/clean.js +27 -0
  102. package/migrate/client.d.ts +80 -0
  103. package/migrate/client.js +555 -0
  104. package/migrate/index.d.ts +5 -0
  105. package/migrate/index.js +21 -0
  106. package/migrate/sql/procedures.sql +258 -0
  107. package/migrate/sql/schema.sql +37 -0
  108. package/migrate/types.d.ts +67 -0
  109. package/migrate/types.js +2 -0
  110. package/migrate/utils/event-logger.d.ts +13 -0
  111. package/migrate/utils/event-logger.js +32 -0
  112. package/migrate/utils/hash.d.ts +12 -0
  113. package/migrate/utils/hash.js +32 -0
  114. package/migrate/utils/transaction.d.ts +27 -0
  115. package/migrate/utils/transaction.js +129 -0
  116. package/modules/modules.d.ts +31 -0
  117. package/modules/modules.js +56 -0
  118. package/package.json +70 -0
  119. package/packaging/package.d.ts +19 -0
  120. package/packaging/package.js +102 -0
  121. package/packaging/transform.d.ts +22 -0
  122. package/packaging/transform.js +75 -0
  123. package/projects/deploy.d.ts +8 -0
  124. package/projects/deploy.js +160 -0
  125. package/projects/revert.d.ts +15 -0
  126. package/projects/revert.js +112 -0
  127. package/projects/verify.d.ts +8 -0
  128. package/projects/verify.js +98 -0
  129. package/resolution/deps.d.ts +57 -0
  130. package/resolution/deps.js +531 -0
  131. package/resolution/resolve.d.ts +37 -0
  132. package/resolution/resolve.js +107 -0
  133. package/utils/debug.d.ts +21 -0
  134. package/utils/debug.js +153 -0
  135. package/utils/target-utils.d.ts +5 -0
  136. package/utils/target-utils.js +40 -0
  137. package/workspace/paths.d.ts +14 -0
  138. package/workspace/paths.js +50 -0
  139. package/workspace/utils.d.ts +8 -0
  140. package/workspace/utils.js +36 -0
@@ -0,0 +1,70 @@
1
+ import { deparse } from 'pgsql-deparser';
2
+ import { parse } from 'pgsql-parser';
3
+ /**
4
+ * Recursively transforms the properties of an object based on a transformation map.
5
+ *
6
+ * @param obj - The object to transform.
7
+ * @param props - A map of properties and their transformation rules.
8
+ * @returns A new object with transformed properties.
9
+ */
10
+ export const transformProps = (obj, props) => {
11
+ let copy;
12
+ // Handle primitive types, null, or undefined
13
+ if (obj === null || typeof obj !== 'object')
14
+ return obj;
15
+ // Handle Date
16
+ if (obj instanceof Date) {
17
+ copy = new Date();
18
+ copy.setTime(obj.getTime());
19
+ return copy;
20
+ }
21
+ // Handle Array
22
+ if (Array.isArray(obj)) {
23
+ copy = [];
24
+ for (let i = 0, len = obj.length; i < len; i++) {
25
+ copy[i] = transformProps(obj[i], props);
26
+ }
27
+ return copy;
28
+ }
29
+ // Handle Object
30
+ if (obj instanceof Object || typeof obj === 'object') {
31
+ copy = {};
32
+ for (const attr in obj) {
33
+ if (Object.prototype.hasOwnProperty.call(obj, attr)) {
34
+ if (props.hasOwnProperty(attr)) {
35
+ const propRule = props[attr];
36
+ if (typeof propRule === 'function') {
37
+ // Apply function transformation
38
+ copy[attr] = propRule(obj[attr]);
39
+ }
40
+ else if (typeof propRule === 'object' && propRule !== null) {
41
+ // Apply value-based transformation
42
+ if (propRule.hasOwnProperty(obj[attr])) {
43
+ copy[attr] = propRule[obj[attr]];
44
+ }
45
+ else {
46
+ copy[attr] = transformProps(obj[attr], props);
47
+ }
48
+ }
49
+ }
50
+ else {
51
+ copy[attr] = transformProps(obj[attr], props);
52
+ }
53
+ }
54
+ }
55
+ return copy;
56
+ }
57
+ throw new Error("Unable to copy obj! Its type isn't supported.");
58
+ };
59
+ /**
60
+ * Parses a SQL statement, transforms its properties, and regenerates the SQL.
61
+ *
62
+ * @param statement - The SQL statement to transform.
63
+ * @param props - A map of properties and their transformation rules.
64
+ * @returns The transformed SQL statement.
65
+ */
66
+ export const transform = async (statement, props) => {
67
+ let tree = await parse(statement);
68
+ tree = transformProps(tree, props);
69
+ return await deparse(tree);
70
+ };
@@ -0,0 +1,123 @@
1
+ import { getEnvOptions } from '@pgpmjs/env';
2
+ import { Logger } from '@pgpmjs/logger';
3
+ import { errors } from '@pgpmjs/types';
4
+ import { resolve } from 'path';
5
+ import * as path from 'path';
6
+ import { getPgPool } from 'pg-cache';
7
+ import { PgpmPackage } from '../core/class/pgpm';
8
+ 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
+ };
16
+ const log = new Logger('deploy');
17
+ export const deployProject = async (opts, name, database, pkg, toChange) => {
18
+ const mergedOpts = getEnvOptions(opts);
19
+ log.info(`๐Ÿ” Gathering modules from ${pkg.workspacePath}...`);
20
+ const modules = pkg.getModuleMap();
21
+ if (!modules[name]) {
22
+ log.error(`โŒ Module "${name}" not found in modules list.`);
23
+ throw errors.MODULE_NOT_FOUND({ name });
24
+ }
25
+ const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
26
+ const moduleProject = new PgpmPackage(modulePath);
27
+ log.info(`๐Ÿ“ฆ Resolving dependencies for ${name}...`);
28
+ const extensions = moduleProject.getModuleExtensions();
29
+ const pgPool = getPgPool({ ...opts.pg, database });
30
+ log.success(`๐Ÿš€ Starting deployment to database ${database}...`);
31
+ for (const extension of extensions.resolved) {
32
+ try {
33
+ if (extensions.external.includes(extension)) {
34
+ const msg = `CREATE EXTENSION IF NOT EXISTS "${extension}" CASCADE;`;
35
+ log.info(`๐Ÿ“ฅ Installing external extension: ${extension}`);
36
+ log.debug(`> ${msg}`);
37
+ await pgPool.query(msg);
38
+ }
39
+ else {
40
+ const modulePath = resolve(pkg.workspacePath, modules[extension].path);
41
+ log.info(`๐Ÿ“‚ Deploying local module: ${extension}`);
42
+ log.debug(`โ†’ Path: ${modulePath}`);
43
+ if (mergedOpts.deployment.fast) {
44
+ // Use fast deployment strategy
45
+ const localProject = new PgpmPackage(modulePath);
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);
50
+ continue;
51
+ }
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
+ }
91
+ }
92
+ else {
93
+ // Use new migration system
94
+ log.debug(`โ†’ Command: launchql migrate deploy db:pg:${database}`);
95
+ try {
96
+ const client = new PgpmMigrate(mergedOpts.pg);
97
+ const result = await client.deploy({
98
+ modulePath,
99
+ toChange,
100
+ useTransaction: mergedOpts.deployment.useTx,
101
+ logOnly: mergedOpts.deployment.logOnly
102
+ });
103
+ if (result.failed) {
104
+ throw errors.OPERATION_FAILED({ operation: 'Deployment', target: result.failed });
105
+ }
106
+ }
107
+ catch (deployError) {
108
+ log.error(`โŒ Deployment failed for module ${extension}`);
109
+ console.error(deployError);
110
+ throw errors.DEPLOYMENT_FAILED({ type: 'Deployment', module: extension });
111
+ }
112
+ }
113
+ }
114
+ }
115
+ catch (err) {
116
+ log.error(`๐Ÿ›‘ Error during deployment: ${err instanceof Error ? err.message : err}`);
117
+ console.error(err); // Keep raw error output for stack traces
118
+ throw errors.DEPLOYMENT_FAILED({ type: 'Deployment', module: extension });
119
+ }
120
+ }
121
+ log.success(`โœ… Deployment complete for ${name}.`);
122
+ return extensions;
123
+ };
@@ -0,0 +1,75 @@
1
+ import { Logger } from '@pgpmjs/logger';
2
+ import { errors } from '@pgpmjs/types';
3
+ import { resolve } from 'path';
4
+ import * as path from 'path';
5
+ import { getPgPool } from 'pg-cache';
6
+ import { PgpmPackage } from '../core/class/pgpm';
7
+ import { PgpmMigrate } from '../migrate/client';
8
+ const log = new Logger('revert');
9
+ export const revertProject = async (opts, name, database, pkg, options) => {
10
+ log.info(`๐Ÿ” Gathering modules from ${pkg.workspacePath}...`);
11
+ const modules = pkg.getModuleMap();
12
+ if (!modules[name]) {
13
+ log.error(`โŒ Module "${name}" not found in modules list.`);
14
+ throw errors.MODULE_NOT_FOUND({ name });
15
+ }
16
+ const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
17
+ const moduleProject = new PgpmPackage(modulePath);
18
+ log.info(`๐Ÿ“ฆ Resolving dependencies for ${name}...`);
19
+ const extensions = moduleProject.getModuleExtensions();
20
+ const pgPool = getPgPool({
21
+ ...opts.pg,
22
+ database
23
+ });
24
+ log.success(`๐Ÿงน Starting revert process on database ${database}...`);
25
+ const reversedExtensions = [...extensions.resolved].reverse();
26
+ for (const extension of reversedExtensions) {
27
+ try {
28
+ if (extensions.external.includes(extension)) {
29
+ const msg = `DROP EXTENSION IF EXISTS "${extension}" RESTRICT;`;
30
+ log.warn(`โš ๏ธ Dropping external extension: ${extension}`);
31
+ log.debug(`> ${msg}`);
32
+ try {
33
+ await pgPool.query(msg);
34
+ }
35
+ catch (err) {
36
+ if (err.code === '2BP01') { // dependent_objects_still_exist
37
+ log.warn(`โš ๏ธ Cannot drop extension ${extension} due to dependencies, skipping`);
38
+ }
39
+ else {
40
+ throw err;
41
+ }
42
+ }
43
+ }
44
+ else {
45
+ const modulePath = resolve(pkg.workspacePath, modules[extension].path);
46
+ log.info(`๐Ÿ“‚ Reverting local module: ${extension}`);
47
+ log.debug(`โ†’ Path: ${modulePath}`);
48
+ // Use new migration system
49
+ log.debug(`โ†’ Command: launchql migrate revert db:pg:${database}`);
50
+ try {
51
+ const client = new PgpmMigrate(opts.pg);
52
+ const result = await client.revert({
53
+ modulePath,
54
+ toChange: options?.toChange,
55
+ useTransaction: options?.useTransaction
56
+ });
57
+ if (result.failed) {
58
+ throw errors.OPERATION_FAILED({ operation: 'Revert', target: result.failed });
59
+ }
60
+ }
61
+ catch (revertError) {
62
+ log.error(`โŒ Revert failed for module ${extension}`);
63
+ throw errors.DEPLOYMENT_FAILED({ type: 'Revert', module: extension });
64
+ }
65
+ }
66
+ }
67
+ catch (e) {
68
+ log.error(`๐Ÿ›‘ Error during revert: ${e instanceof Error ? e.message : e}`);
69
+ console.error(e); // optional raw stack trace
70
+ throw errors.DEPLOYMENT_FAILED({ type: 'Revert', module: extension });
71
+ }
72
+ }
73
+ log.success(`โœ… Revert complete for ${name}.`);
74
+ return extensions;
75
+ };
@@ -0,0 +1,61 @@
1
+ import { Logger } from '@pgpmjs/logger';
2
+ import { errors } from '@pgpmjs/types';
3
+ import { resolve } from 'path';
4
+ import * as path from 'path';
5
+ import { getPgPool } from 'pg-cache';
6
+ import { PgpmPackage } from '../core/class/pgpm';
7
+ import { PgpmMigrate } from '../migrate/client';
8
+ const log = new Logger('verify');
9
+ export const verifyProject = async (opts, name, database, pkg, options) => {
10
+ log.info(`๐Ÿ” Gathering modules from ${pkg.workspacePath}...`);
11
+ const modules = pkg.getModuleMap();
12
+ if (!modules[name]) {
13
+ log.error(`โŒ Module "${name}" not found in modules list.`);
14
+ throw errors.MODULE_NOT_FOUND({ name });
15
+ }
16
+ const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
17
+ const moduleProject = new PgpmPackage(modulePath);
18
+ log.info(`๐Ÿ“ฆ Resolving dependencies for ${name}...`);
19
+ const extensions = moduleProject.getModuleExtensions();
20
+ const pgPool = getPgPool({
21
+ ...opts.pg,
22
+ database
23
+ });
24
+ log.success(`๐Ÿ”Ž Verifying deployment of ${name} on database ${database}...`);
25
+ for (const extension of extensions.resolved) {
26
+ try {
27
+ if (extensions.external.includes(extension)) {
28
+ const query = `SELECT 1/count(*) FROM pg_available_extensions WHERE name = $1`;
29
+ log.info(`๐Ÿ” Verifying external extension: ${extension}`);
30
+ log.debug(`> ${query}`);
31
+ await pgPool.query(query, [extension]);
32
+ }
33
+ else {
34
+ const modulePath = resolve(pkg.workspacePath, modules[extension].path);
35
+ log.info(`๐Ÿ“‚ Verifying local module: ${extension}`);
36
+ log.debug(`โ†’ Path: ${modulePath}`);
37
+ log.debug(`โ†’ Command: launchql migrate verify db:pg:${database}`);
38
+ try {
39
+ const client = new PgpmMigrate(opts.pg);
40
+ const result = await client.verify({
41
+ modulePath
42
+ });
43
+ if (result.failed.length > 0) {
44
+ throw errors.OPERATION_FAILED({ operation: 'Verification', reason: `${result.failed.length} changes: ${result.failed.join(', ')}` });
45
+ }
46
+ }
47
+ catch (verifyError) {
48
+ log.error(`โŒ Verification failed for module ${extension}`);
49
+ throw errors.DEPLOYMENT_FAILED({ type: 'Verify', module: extension });
50
+ }
51
+ }
52
+ }
53
+ catch (e) {
54
+ log.error(`๐Ÿ›‘ Error during verification: ${e instanceof Error ? e.message : e}`);
55
+ console.error(e);
56
+ throw errors.DEPLOYMENT_FAILED({ type: 'Verify', module: extension });
57
+ }
58
+ }
59
+ log.success(`โœ… Verification complete for ${name}.`);
60
+ return extensions;
61
+ };