@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,98 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.verifyProject = void 0;
37
+ const logger_1 = require("@pgpmjs/logger");
38
+ const types_1 = require("@pgpmjs/types");
39
+ const path_1 = require("path");
40
+ const path = __importStar(require("path"));
41
+ const pg_cache_1 = require("pg-cache");
42
+ const pgpm_1 = require("../core/class/pgpm");
43
+ const client_1 = require("../migrate/client");
44
+ const log = new logger_1.Logger('verify');
45
+ const verifyProject = async (opts, name, database, pkg, options) => {
46
+ log.info(`🔍 Gathering modules from ${pkg.workspacePath}...`);
47
+ const modules = pkg.getModuleMap();
48
+ if (!modules[name]) {
49
+ log.error(`❌ Module "${name}" not found in modules list.`);
50
+ throw types_1.errors.MODULE_NOT_FOUND({ name });
51
+ }
52
+ const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
53
+ const moduleProject = new pgpm_1.PgpmPackage(modulePath);
54
+ log.info(`📦 Resolving dependencies for ${name}...`);
55
+ const extensions = moduleProject.getModuleExtensions();
56
+ const pgPool = (0, pg_cache_1.getPgPool)({
57
+ ...opts.pg,
58
+ database
59
+ });
60
+ log.success(`🔎 Verifying deployment of ${name} on database ${database}...`);
61
+ for (const extension of extensions.resolved) {
62
+ try {
63
+ if (extensions.external.includes(extension)) {
64
+ const query = `SELECT 1/count(*) FROM pg_available_extensions WHERE name = $1`;
65
+ log.info(`🔍 Verifying external extension: ${extension}`);
66
+ log.debug(`> ${query}`);
67
+ await pgPool.query(query, [extension]);
68
+ }
69
+ else {
70
+ const modulePath = (0, path_1.resolve)(pkg.workspacePath, modules[extension].path);
71
+ log.info(`📂 Verifying local module: ${extension}`);
72
+ log.debug(`→ Path: ${modulePath}`);
73
+ log.debug(`→ Command: launchql migrate verify db:pg:${database}`);
74
+ try {
75
+ const client = new client_1.PgpmMigrate(opts.pg);
76
+ const result = await client.verify({
77
+ modulePath
78
+ });
79
+ if (result.failed.length > 0) {
80
+ throw types_1.errors.OPERATION_FAILED({ operation: 'Verification', reason: `${result.failed.length} changes: ${result.failed.join(', ')}` });
81
+ }
82
+ }
83
+ catch (verifyError) {
84
+ log.error(`❌ Verification failed for module ${extension}`);
85
+ throw types_1.errors.DEPLOYMENT_FAILED({ type: 'Verify', module: extension });
86
+ }
87
+ }
88
+ }
89
+ catch (e) {
90
+ log.error(`🛑 Error during verification: ${e instanceof Error ? e.message : e}`);
91
+ console.error(e);
92
+ throw types_1.errors.DEPLOYMENT_FAILED({ type: 'Verify', module: extension });
93
+ }
94
+ }
95
+ log.success(`✅ Verification complete for ${name}.`);
96
+ return extensions;
97
+ };
98
+ exports.verifyProject = verifyProject;
@@ -0,0 +1,57 @@
1
+ import { ExtendedPlanFile } from '../files/types';
2
+ /**
3
+ * Represents a dependency graph where keys are module identifiers
4
+ * and values are arrays of their direct dependencies
5
+ */
6
+ interface DependencyGraph {
7
+ [key: string]: string[];
8
+ }
9
+ /**
10
+ * Result object returned by dependency resolution functions
11
+ */
12
+ export interface DependencyResult {
13
+ /** Array of external dependencies that are not part of the current package */
14
+ external: string[];
15
+ /** Array of modules in topologically sorted order for deployment */
16
+ resolved: string[];
17
+ /** The complete dependency graph mapping modules to their dependencies */
18
+ deps: DependencyGraph;
19
+ /** Mapping of resolved tags to their target changes (only for resolveDependencies) */
20
+ resolvedTags?: Record<string, string>;
21
+ }
22
+ /**
23
+ * Resolves dependencies for extension modules using a pre-built module map.
24
+ * This is a simpler version that works with module metadata rather than parsing SQL files.
25
+ *
26
+ * @param name - The name of the module to resolve dependencies for
27
+ * @param modules - Record mapping module names to their dependency requirements
28
+ * @returns Object containing external dependencies and resolved dependency order
29
+ */
30
+ export declare const resolveExtensionDependencies: (name: string, modules: Record<string, {
31
+ requires: string[];
32
+ }>) => {
33
+ external: string[];
34
+ resolved: string[];
35
+ };
36
+ export interface DependencyResolutionOptions {
37
+ /**
38
+ * How to handle tag references:
39
+ * - 'preserve': Keep tags as-is, as changes in plan files
40
+ * - 'resolve': Resolve tags to their target changes
41
+ * - 'internal': Resolve internally but preserve in output
42
+ */
43
+ tagResolution?: 'preserve' | 'resolve' | 'internal';
44
+ /**
45
+ * Whether to load and parse plan files for tag resolution
46
+ * Only applicable when tagResolution is 'resolve' or 'internal'
47
+ */
48
+ loadPlanFiles?: boolean;
49
+ /**
50
+ * Custom plan file loader function
51
+ * Allows overriding the default plan file loading logic
52
+ */
53
+ planFileLoader?: (projectName: string, currentProject: string, packageDir: string) => ExtendedPlanFile | null;
54
+ source?: 'sql' | 'plan';
55
+ }
56
+ export declare const resolveDependencies: (packageDir: string, extname: string, options?: DependencyResolutionOptions) => DependencyResult;
57
+ export {};
@@ -0,0 +1,531 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveDependencies = exports.resolveExtensionDependencies = void 0;
4
+ const fs_1 = require("fs");
5
+ const glob_1 = require("glob");
6
+ const path_1 = require("path");
7
+ const pgpm_1 = require("../core/class/pgpm");
8
+ const parser_1 = require("../files/plan/parser");
9
+ const types_1 = require("@pgpmjs/types");
10
+ /**
11
+ * Core dependency resolution algorithm that handles circular dependency detection
12
+ * and topological sorting of dependencies. This unified implementation eliminates
13
+ * code duplication between getDeps, resolveExtensionDependencies, and resolveDependencies.
14
+ *
15
+ * @param deps - The dependency graph mapping modules to their dependencies
16
+ * @param external - Array to collect external dependencies
17
+ * @param options - Configuration options for customizing resolver behavior
18
+ * @returns A function that performs dependency resolution with the given configuration
19
+ */
20
+ function createDependencyResolver(deps, external, options) {
21
+ const { handleExternalDep, transformModule, makeKey = (module) => module, extname } = options;
22
+ return function dep_resolve(sqlmodule, resolved, unresolved) {
23
+ unresolved.push(sqlmodule);
24
+ let moduleToResolve = sqlmodule;
25
+ let edges;
26
+ let returnEarly = false;
27
+ if (transformModule) {
28
+ const result = transformModule(sqlmodule, extname);
29
+ moduleToResolve = result.module;
30
+ edges = result.edges;
31
+ returnEarly = result.returnEarly || false;
32
+ }
33
+ else {
34
+ edges = deps[makeKey(sqlmodule)];
35
+ }
36
+ // Handle external dependencies if no edges found
37
+ if (!edges) {
38
+ if (handleExternalDep) {
39
+ handleExternalDep(sqlmodule, deps, external);
40
+ edges = deps[sqlmodule] || [];
41
+ }
42
+ else {
43
+ throw types_1.errors.MODULE_NOT_FOUND({ name: sqlmodule });
44
+ }
45
+ }
46
+ if (returnEarly) {
47
+ const index = unresolved.indexOf(sqlmodule);
48
+ unresolved.splice(index, 1);
49
+ return;
50
+ }
51
+ // Process each dependency
52
+ for (const dep of edges) {
53
+ if (!resolved.includes(dep)) {
54
+ if (unresolved.includes(dep)) {
55
+ throw types_1.errors.CIRCULAR_DEPENDENCY({ module: moduleToResolve, dependency: dep });
56
+ }
57
+ dep_resolve(dep, resolved, unresolved);
58
+ }
59
+ }
60
+ resolved.push(moduleToResolve);
61
+ const index = unresolved.indexOf(sqlmodule);
62
+ unresolved.splice(index, 1);
63
+ };
64
+ }
65
+ /**
66
+ * Generates a standardized key for SQL deployment files
67
+ * @param sqlmodule - The module name (e.g., 'users/create')
68
+ * @returns The standardized file path key (e.g., '/deploy/users/create.sql')
69
+ */
70
+ const makeKey = (sqlmodule) => `/deploy/${sqlmodule}.sql`;
71
+ /**
72
+ * Resolves dependencies for extension modules using a pre-built module map.
73
+ * This is a simpler version that works with module metadata rather than parsing SQL files.
74
+ *
75
+ * @param name - The name of the module to resolve dependencies for
76
+ * @param modules - Record mapping module names to their dependency requirements
77
+ * @returns Object containing external dependencies and resolved dependency order
78
+ */
79
+ const resolveExtensionDependencies = (name, modules) => {
80
+ if (!modules[name]) {
81
+ throw types_1.errors.MODULE_NOT_FOUND({ name });
82
+ }
83
+ const external = [];
84
+ const deps = Object.keys(modules).reduce((memo, key) => {
85
+ memo[key] = modules[key].requires;
86
+ return memo;
87
+ }, {});
88
+ // Handle external dependencies for resolveExtensionDependencies - simpler than getDeps
89
+ const handleExternalDep = (dep, deps, external) => {
90
+ external.push(dep);
91
+ deps[dep] = [];
92
+ };
93
+ // Create the dependency resolver with resolveExtensionDependencies-specific configuration
94
+ const dep_resolve = createDependencyResolver(deps, external, {
95
+ handleExternalDep,
96
+ extname: name // For resolveExtensionDependencies, we use the module name as extname
97
+ });
98
+ const resolved = [];
99
+ const unresolved = [];
100
+ dep_resolve(name, resolved, unresolved);
101
+ return { external, resolved };
102
+ };
103
+ exports.resolveExtensionDependencies = resolveExtensionDependencies;
104
+ //
105
+ //
106
+ //
107
+ //
108
+ //
109
+ // - for each change in the plan, create a node in the dependency graph and add edges for any declared dependencies.
110
+ //
111
+ //
112
+ // - Cross-package references:
113
+ //
114
+ //
115
+ //
116
+ //
117
+ // resolveDependencies overview
118
+ // - Purpose: compute dependency graph and apply order for a package/module.
119
+ // - Sources: 'sql' (parse headers + topo + extensions-first) vs 'plan' (use plan.changes order directly).
120
+ // - Tags: 'preserve' (keep), 'internal' (map for traversal), 'resolve' (replace with change names).
121
+ // - Output: { external, resolved, deps, resolvedTags? }.
122
+ // Detailed notes are placed inline near the relevant code paths below.
123
+ const resolveDependencies = (packageDir, extname, options = {}) => {
124
+ const { tagResolution = 'preserve', loadPlanFiles = true, planFileLoader, source = 'sql' } = options;
125
+ // For 'resolve' and 'internal' modes, we need plan file loading
126
+ const planCache = {};
127
+ // Helper function to load a plan file for a package
128
+ const loadPlanFile = (packageName) => {
129
+ if (!loadPlanFiles) {
130
+ return null;
131
+ }
132
+ if (planFileLoader) {
133
+ return planFileLoader(packageName, extname, packageDir);
134
+ }
135
+ if (planCache[packageName]) {
136
+ return planCache[packageName];
137
+ }
138
+ try {
139
+ let planPath;
140
+ if (packageName === extname) {
141
+ // For the current package
142
+ planPath = (0, path_1.join)(packageDir, 'pgpm.plan');
143
+ }
144
+ else {
145
+ // For external packages, use PgpmPackage to find module path
146
+ const project = new pgpm_1.PgpmPackage(packageDir);
147
+ const moduleMap = project.getModuleMap();
148
+ const module = moduleMap[packageName];
149
+ if (!module) {
150
+ throw types_1.errors.MODULE_NOT_FOUND({ name: packageName });
151
+ }
152
+ const workspacePath = project.getWorkspacePath();
153
+ if (!workspacePath) {
154
+ throw new Error(`No workspace found for module ${packageName}`);
155
+ }
156
+ planPath = (0, path_1.join)(workspacePath, module.path, 'pgpm.plan');
157
+ }
158
+ const result = (0, parser_1.parsePlanFile)(planPath);
159
+ if (result.data) {
160
+ planCache[packageName] = result.data;
161
+ return result.data;
162
+ }
163
+ }
164
+ catch (error) {
165
+ // Plan file not found or parse error
166
+ console.warn(`Could not load plan file for package ${packageName}: ${error}`);
167
+ }
168
+ return null;
169
+ };
170
+ // Plan-mode branch: use plan.changes order directly; build graph from plan deps (no topo or resort).
171
+ // - Loads the current package plan and throws if missing.
172
+ // - For each change in plan, adds a node; edges come from change.dependencies.
173
+ // - Tag handling per tagResolution: 'preserve' keeps tokens, 'internal' maps for traversal, 'resolve' replaces with change names.
174
+ // - Cross-package refs "pkg:change" are recorded in external and kept as graph nodes for coordination by callers.
175
+ // - Internal refs like "extname:change" are normalized to "change".
176
+ const resolveTagToChange = (projectName, tagName) => {
177
+ const plan = loadPlanFile(projectName);
178
+ if (!plan)
179
+ return null;
180
+ const tag = plan.tags.find(t => t.name === tagName);
181
+ if (!tag)
182
+ return null;
183
+ return tag.change;
184
+ };
185
+ if (source === 'plan') {
186
+ const plan = loadPlanFile(extname);
187
+ if (!plan) {
188
+ throw types_1.errors.PLAN_PARSE_ERROR({ planPath: `${extname}/pgpm.plan`, errors: 'Plan file not found or failed to parse while using plan-only resolution' });
189
+ }
190
+ const external = [];
191
+ const deps = {};
192
+ const tagMappings = {};
193
+ const normalizeInternal = (dep) => {
194
+ if (/:/.test(dep)) {
195
+ const [project, localKey] = dep.split(':', 2);
196
+ if (project === extname)
197
+ return localKey;
198
+ }
199
+ return dep;
200
+ };
201
+ const resolveTagDep = (projectName, tagName) => {
202
+ const change = resolveTagToChange(projectName, tagName);
203
+ if (!change)
204
+ return null;
205
+ return `${projectName}:${change}`;
206
+ };
207
+ for (const ch of plan.changes) {
208
+ const key = makeKey(ch.name);
209
+ deps[key] = [];
210
+ const changeDeps = ch.dependencies || [];
211
+ for (const rawDep of changeDeps) {
212
+ let dep = rawDep.trim();
213
+ if (dep.includes('@')) {
214
+ const m = dep.match(/^([^:]+):@(.+)$/);
215
+ if (m) {
216
+ const projectName = m[1];
217
+ const tagName = m[2];
218
+ const resolved = resolveTagDep(projectName, tagName);
219
+ if (resolved) {
220
+ if (tagResolution === 'resolve')
221
+ dep = resolved;
222
+ else if (tagResolution === 'internal')
223
+ tagMappings[dep] = resolved;
224
+ }
225
+ }
226
+ else {
227
+ const m2 = dep.match(/^@(.+)$/);
228
+ if (m2) {
229
+ const tagName = m2[1];
230
+ const resolved = resolveTagDep(extname, tagName);
231
+ if (resolved) {
232
+ if (tagResolution === 'resolve')
233
+ dep = resolved;
234
+ else if (tagResolution === 'internal')
235
+ tagMappings[dep] = resolved;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ if (/:/.test(dep)) {
241
+ const [project] = dep.split(':', 2);
242
+ if (project !== extname) {
243
+ external.push(dep);
244
+ if (!deps[dep])
245
+ deps[dep] = [];
246
+ deps[key].push(dep);
247
+ continue;
248
+ }
249
+ deps[key].push(normalizeInternal(dep));
250
+ continue;
251
+ }
252
+ deps[key].push(dep);
253
+ }
254
+ }
255
+ const transformModule = (sqlmodule, extnameLocal) => {
256
+ const originalModule = sqlmodule;
257
+ if (tagResolution === 'preserve') {
258
+ let moduleToResolve = sqlmodule;
259
+ let edges = deps[makeKey(sqlmodule)];
260
+ if (/:/.test(sqlmodule)) {
261
+ const [project, localKey] = sqlmodule.split(':', 2);
262
+ if (project === extnameLocal) {
263
+ moduleToResolve = localKey;
264
+ edges = deps[makeKey(localKey)];
265
+ if (!edges)
266
+ throw types_1.errors.MODULE_NOT_FOUND({ name: `${localKey} (from ${project}:${localKey})` });
267
+ }
268
+ else {
269
+ external.push(sqlmodule);
270
+ deps[sqlmodule] = deps[sqlmodule] || [];
271
+ return { module: sqlmodule, edges: [], returnEarly: true };
272
+ }
273
+ }
274
+ else {
275
+ if (!edges)
276
+ throw types_1.errors.MODULE_NOT_FOUND({ name: sqlmodule });
277
+ }
278
+ return { module: moduleToResolve, edges };
279
+ }
280
+ if (/:/.test(originalModule)) {
281
+ const [project] = originalModule.split(':', 2);
282
+ if (project !== extnameLocal) {
283
+ external.push(originalModule);
284
+ deps[originalModule] = deps[originalModule] || [];
285
+ return { module: originalModule, edges: [], returnEarly: true };
286
+ }
287
+ }
288
+ let moduleToResolve = sqlmodule;
289
+ if (tagResolution === 'internal' && tagMappings[sqlmodule]) {
290
+ moduleToResolve = tagMappings[sqlmodule];
291
+ }
292
+ let edges = deps[makeKey(moduleToResolve)];
293
+ if (/:/.test(moduleToResolve)) {
294
+ const [project, localKey] = moduleToResolve.split(':', 2);
295
+ if (project === extnameLocal) {
296
+ moduleToResolve = localKey;
297
+ edges = deps[makeKey(localKey)];
298
+ if (!edges)
299
+ throw types_1.errors.MODULE_NOT_FOUND({ name: `${localKey} (from ${project}:${localKey})` });
300
+ }
301
+ }
302
+ else {
303
+ if (!edges) {
304
+ edges = deps[makeKey(sqlmodule)];
305
+ if (!edges)
306
+ throw types_1.errors.MODULE_NOT_FOUND({ name: sqlmodule });
307
+ }
308
+ }
309
+ if (tagResolution === 'internal' && edges) {
310
+ const processedEdges = edges.map(dep => {
311
+ if (/:/.test(dep)) {
312
+ const [project] = dep.split(':', 2);
313
+ if (project !== extnameLocal)
314
+ return dep;
315
+ }
316
+ if (tagMappings[dep])
317
+ return tagMappings[dep];
318
+ return dep;
319
+ });
320
+ return { module: moduleToResolve, edges: processedEdges };
321
+ }
322
+ return { module: moduleToResolve, edges };
323
+ };
324
+ // or extension-first resorting. Externals are still tracked in the deps graph and external array.
325
+ const resolved = plan.changes.map(ch => ch.name);
326
+ return { external, resolved, deps, resolvedTags: tagMappings };
327
+ }
328
+ const external = [];
329
+ const deps = {};
330
+ const tagMappings = {};
331
+ // Process SQL files and build dependency graph
332
+ const files = (0, glob_1.sync)(`${packageDir}/deploy/**/*.sql`);
333
+ for (const file of files) {
334
+ const data = (0, fs_1.readFileSync)(file, 'utf-8');
335
+ const lines = data.split('\n');
336
+ const key = '/' + (0, path_1.relative)(packageDir, file);
337
+ deps[key] = [];
338
+ for (const line of lines) {
339
+ // Handle requires statements
340
+ const requiresMatch = line.match(/^-- requires: (.*)/);
341
+ if (requiresMatch) {
342
+ const dep = requiresMatch[1].trim();
343
+ // For 'preserve' mode, just add the dependency as-is (like original getDeps)
344
+ if (tagResolution === 'preserve') {
345
+ deps[key].push(dep);
346
+ continue;
347
+ }
348
+ // For other modes, handle tag resolution
349
+ if (dep.includes('@')) {
350
+ const match = dep.match(/^([^:]+):@(.+)$/);
351
+ if (match) {
352
+ const [, projectName, tagName] = match;
353
+ const taggedChange = resolveTagToChange(projectName, tagName);
354
+ if (taggedChange) {
355
+ if (tagResolution === 'resolve') {
356
+ // Full resolution: replace tag with actual change
357
+ const resolvedDep = `${projectName}:${taggedChange}`;
358
+ deps[key].push(resolvedDep);
359
+ }
360
+ else if (tagResolution === 'internal') {
361
+ // Internal resolution: keep tag in deps but track mapping
362
+ tagMappings[dep] = `${projectName}:${taggedChange}`;
363
+ deps[key].push(dep);
364
+ }
365
+ }
366
+ else {
367
+ // Could not resolve tag, keep it as is
368
+ deps[key].push(dep);
369
+ }
370
+ }
371
+ else {
372
+ // Invalid tag format, keep as is
373
+ deps[key].push(dep);
374
+ }
375
+ }
376
+ else {
377
+ // Not a tag, keep as is
378
+ deps[key].push(dep);
379
+ }
380
+ continue;
381
+ }
382
+ // Handle deploy statements - exactly as in original
383
+ let m2;
384
+ let keyToTest;
385
+ if (/:/.test(line)) {
386
+ m2 = line.match(/^-- Deploy ([^:]*):([\w\/]+)(?:\s+to\s+pg)?/);
387
+ if (m2) {
388
+ const actualProject = m2[1];
389
+ keyToTest = m2[2];
390
+ if (extname !== actualProject) {
391
+ throw new Error(`Mismatched project name in deploy file:
392
+ Expected project: ${extname}
393
+ Found in line : ${actualProject}
394
+ Line : ${line}`);
395
+ }
396
+ const expectedKey = makeKey(keyToTest);
397
+ if (key !== expectedKey) {
398
+ throw new Error(`Deployment script path or internal name mismatch:
399
+ Expected key : ${key}
400
+ Found in line : ${expectedKey}
401
+ Line : ${line}`);
402
+ }
403
+ }
404
+ }
405
+ else {
406
+ m2 = line.match(/^-- Deploy (.*?)(?:\s+to\s+pg)?\s*$/);
407
+ if (m2) {
408
+ keyToTest = m2[1].trim();
409
+ if (key !== makeKey(keyToTest)) {
410
+ throw new Error('deployment script in wrong place or is named wrong internally\n' + line);
411
+ }
412
+ }
413
+ }
414
+ }
415
+ }
416
+ const transformModule = (sqlmodule, extname) => {
417
+ const originalModule = sqlmodule;
418
+ // For 'preserve' mode, use simpler logic (like original getDeps)
419
+ if (tagResolution === 'preserve') {
420
+ let moduleToResolve = sqlmodule;
421
+ let edges = deps[makeKey(sqlmodule)];
422
+ if (/:/.test(sqlmodule)) {
423
+ // Has a prefix — could be internal or external
424
+ const [project, localKey] = sqlmodule.split(':', 2);
425
+ if (project === extname) {
426
+ // Internal reference to current package
427
+ moduleToResolve = localKey;
428
+ edges = deps[makeKey(localKey)];
429
+ if (!edges) {
430
+ throw types_1.errors.MODULE_NOT_FOUND({ name: `${localKey} (from ${project}:${localKey})` });
431
+ }
432
+ }
433
+ else {
434
+ // External reference — always OK, even if not in deps yet
435
+ external.push(sqlmodule);
436
+ deps[sqlmodule] = [];
437
+ return { module: sqlmodule, edges: [], returnEarly: true };
438
+ }
439
+ }
440
+ else {
441
+ // No prefix — must be internal
442
+ if (!edges) {
443
+ throw types_1.errors.MODULE_NOT_FOUND({ name: sqlmodule });
444
+ }
445
+ }
446
+ return { module: moduleToResolve, edges };
447
+ }
448
+ // Check if the ORIGINAL module (before tag resolution) is external
449
+ if (/:/.test(originalModule)) {
450
+ const [project, localKey] = originalModule.split(':', 2);
451
+ if (project !== extname) {
452
+ // External reference — always OK, even if not in deps yet
453
+ external.push(originalModule);
454
+ deps[originalModule] = deps[originalModule] || [];
455
+ return { module: originalModule, edges: [], returnEarly: true };
456
+ }
457
+ }
458
+ // For internal resolution mode, check if this module is a tag and resolve it
459
+ let moduleToResolve = sqlmodule;
460
+ if (tagResolution === 'internal' && tagMappings[sqlmodule]) {
461
+ moduleToResolve = tagMappings[sqlmodule];
462
+ }
463
+ let edges = deps[makeKey(moduleToResolve)];
464
+ if (/:/.test(moduleToResolve)) {
465
+ // Has a prefix — must be internal since we already handled external above
466
+ const [project, localKey] = moduleToResolve.split(':', 2);
467
+ if (project === extname) {
468
+ // Internal reference to current package
469
+ moduleToResolve = localKey;
470
+ edges = deps[makeKey(localKey)];
471
+ if (!edges) {
472
+ throw types_1.errors.MODULE_NOT_FOUND({ name: `${localKey} (from ${project}:${localKey})` });
473
+ }
474
+ }
475
+ }
476
+ else {
477
+ // No prefix — must be internal
478
+ if (!edges) {
479
+ // Check if we have edges for the original module
480
+ edges = deps[makeKey(sqlmodule)];
481
+ if (!edges) {
482
+ throw types_1.errors.MODULE_NOT_FOUND({ name: sqlmodule });
483
+ }
484
+ }
485
+ }
486
+ // For internal resolution, process dependencies through tag mappings
487
+ if (tagResolution === 'internal' && edges) {
488
+ const processedEdges = edges.map(dep => {
489
+ // Check if this dependency is external - if so, don't resolve tags
490
+ if (/:/.test(dep)) {
491
+ const [project, localKey] = dep.split(':', 2);
492
+ if (project !== extname) {
493
+ // External dependency - keep original tag name
494
+ return dep;
495
+ }
496
+ }
497
+ // Internal dependency - apply tag mapping if available
498
+ if (tagMappings[dep]) {
499
+ return tagMappings[dep];
500
+ }
501
+ return dep;
502
+ });
503
+ return { module: moduleToResolve, edges: processedEdges };
504
+ }
505
+ return { module: moduleToResolve, edges };
506
+ };
507
+ // Create the dependency resolver with resolveDependencies-specific configuration
508
+ const dep_resolve = createDependencyResolver(deps, external, {
509
+ transformModule,
510
+ makeKey,
511
+ extname
512
+ });
513
+ let resolved = [];
514
+ const unresolved = [];
515
+ // Synthetic root '_virtual/app' seeds local deploy/* modules into resolver for topo ordering.
516
+ // Removed after resolution; not present in returned output.
517
+ // Followed by extension-first reordering for deterministic application in SQL mode only.
518
+ // Add synthetic root node - exactly as in original
519
+ deps[makeKey('_virtual/app')] = Object.keys(deps)
520
+ .filter((dep) => dep.startsWith('/deploy/'))
521
+ .map((dep) => dep.replace(/^\/deploy\//, '').replace(/\.sql$/, ''));
522
+ dep_resolve('_virtual/app', resolved, unresolved);
523
+ const index = resolved.indexOf('_virtual/app');
524
+ resolved.splice(index, 1);
525
+ delete deps[makeKey('_virtual/app')];
526
+ const extensions = resolved.filter((module) => module.startsWith('extensions/'));
527
+ const normalSql = resolved.filter((module) => !module.startsWith('extensions/'));
528
+ resolved = [...extensions, ...normalSql];
529
+ return { external, resolved, deps, resolvedTags: tagMappings };
530
+ };
531
+ exports.resolveDependencies = resolveDependencies;