@pgpmjs/core 6.33.0 → 6.34.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/esm/index.js CHANGED
@@ -13,6 +13,7 @@ export * from './core/boilerplate-types';
13
13
  export * from './core/boilerplate-scanner';
14
14
  // Export package-files functionality (now integrated into core)
15
15
  export * from './files';
16
+ export * from './refactor';
16
17
  export { cleanSql } from './migrate/clean';
17
18
  export { PgpmMigrate } from './migrate/client';
18
19
  export { PgpmInit } from './init/client';
@@ -0,0 +1 @@
1
+ export * from './rename';
@@ -0,0 +1,200 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmdirSync, statSync, writeFileSync } from 'fs';
2
+ import { dirname, join, relative, sep } from 'path';
3
+ import { parsePlanFileSimple } from '../files/plan/parser';
4
+ import { parsePgpmHeader, renameInHeader, writePgpmScript } from '../files/sql/header';
5
+ const SCRIPT_DIRS = ['deploy', 'revert', 'verify'];
6
+ function escapeRe(str) {
7
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
8
+ }
9
+ function findSqlFiles(dir) {
10
+ const files = [];
11
+ if (!existsSync(dir))
12
+ return files;
13
+ const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
14
+ for (const entry of entries) {
15
+ const fullPath = join(dir, entry.name);
16
+ if (entry.isDirectory()) {
17
+ files.push(...findSqlFiles(fullPath));
18
+ }
19
+ else if (entry.isFile() && entry.name.endsWith('.sql')) {
20
+ files.push(fullPath);
21
+ }
22
+ }
23
+ return files;
24
+ }
25
+ function removeEmptyDirs(dir, stopAt) {
26
+ let current = dir;
27
+ while (current !== stopAt && current.startsWith(stopAt)) {
28
+ if (!existsSync(current) || readdirSync(current).length > 0)
29
+ return;
30
+ rmdirSync(current);
31
+ current = dirname(current);
32
+ }
33
+ }
34
+ /**
35
+ * Validate a rename map against a module directory: every source change must
36
+ * exist in the plan, no target may collide with an existing change, and no
37
+ * two sources may map to the same target.
38
+ *
39
+ * @throws on any violation.
40
+ */
41
+ export function validateRenames(moduleDir, renames) {
42
+ const planPath = join(moduleDir, 'pgpm.plan');
43
+ const plan = parsePlanFileSimple(planPath);
44
+ const changeNames = new Set(plan.changes.map(c => c.name));
45
+ const targets = new Set();
46
+ for (const [from, to] of renames) {
47
+ if (from === to) {
48
+ throw new Error(`Rename maps change to itself: ${from}`);
49
+ }
50
+ if (!changeNames.has(from)) {
51
+ throw new Error(`Cannot rename unknown change: ${from}`);
52
+ }
53
+ if (changeNames.has(to) && !renames.has(to)) {
54
+ throw new Error(`Rename target already exists in plan: ${to}`);
55
+ }
56
+ if (targets.has(to)) {
57
+ throw new Error(`Multiple changes rename to the same target: ${to}`);
58
+ }
59
+ targets.add(to);
60
+ }
61
+ }
62
+ /**
63
+ * Rewrite change names in a pgpm.plan's content according to a rename map.
64
+ * Textual and format-preserving: only the change-name token at the start of a
65
+ * change line, plain (same-package) dependency refs inside `[...]`, and tag
66
+ * lines' change field are rewritten; timestamps, planners, comments,
67
+ * cross-package refs, and tag refs are untouched.
68
+ */
69
+ export function renameInPlanContent(content, renames) {
70
+ const froms = Array.from(renames.keys()).sort((a, b) => b.length - a.length);
71
+ return content
72
+ .split('\n')
73
+ .map(line => {
74
+ const trimmed = line.trim();
75
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('%'))
76
+ return line;
77
+ let result = line;
78
+ for (const from of froms) {
79
+ const to = renames.get(from);
80
+ const re = new RegExp(`(^|[\\s\\[])${escapeRe(from)}(?=$|[\\s\\]@])`, 'g');
81
+ result = result.replace(re, `$1${to}`);
82
+ }
83
+ return result;
84
+ })
85
+ .join('\n');
86
+ }
87
+ /**
88
+ * Atomically rename/move changes within a pgpm module: the coupled 4-way
89
+ * rewrite of (1) deploy/revert/verify script files on disk, (2) the moved
90
+ * scripts' own header identity, (3) `-- requires:` headers in every script
91
+ * that references a renamed change, and (4) the pgpm.plan.
92
+ *
93
+ * SQL statement bodies are NOT touched: schema-name rewrites inside SQL are a
94
+ * separate AST-level concern for callers to compose (e.g. transform passes).
95
+ */
96
+ export function renameChanges(moduleDir, renames, options = {}) {
97
+ const dryRun = options.dryRun ?? false;
98
+ const result = {
99
+ filesMoved: [],
100
+ headersRewritten: [],
101
+ planRewritten: false
102
+ };
103
+ if (dryRun)
104
+ result.dryRunReport = [];
105
+ if (renames.size === 0)
106
+ return result;
107
+ validateRenames(moduleDir, renames);
108
+ // 1. Move script files
109
+ for (const scriptDir of SCRIPT_DIRS) {
110
+ for (const [from, to] of renames) {
111
+ const oldPath = join(moduleDir, scriptDir, `${from}.sql`);
112
+ if (!existsSync(oldPath) || !statSync(oldPath).isFile())
113
+ continue;
114
+ const newPath = join(moduleDir, scriptDir, `${to}.sql`);
115
+ if (existsSync(newPath)) {
116
+ throw new Error(`Rename target file already exists: ${relative(moduleDir, newPath)}`);
117
+ }
118
+ if (dryRun) {
119
+ result.dryRunReport.push(`Would move: ${relative(moduleDir, oldPath)} -> ${relative(moduleDir, newPath)}`);
120
+ }
121
+ else {
122
+ mkdirSync(dirname(newPath), { recursive: true });
123
+ renameSync(oldPath, newPath);
124
+ removeEmptyDirs(dirname(oldPath), join(moduleDir, scriptDir));
125
+ }
126
+ result.filesMoved.push({
127
+ from: [scriptDir, `${from}.sql`].join(sep),
128
+ to: [scriptDir, `${to}.sql`].join(sep)
129
+ });
130
+ }
131
+ }
132
+ // 2 + 3. Rewrite headers in every script (identity of moved files,
133
+ // requires of dependents)
134
+ for (const scriptDir of SCRIPT_DIRS) {
135
+ const dir = join(moduleDir, scriptDir);
136
+ for (const file of findSqlFiles(dir)) {
137
+ const content = readFileSync(file, 'utf-8');
138
+ const script = parsePgpmHeader(content);
139
+ const count = renameInHeader(script, renames);
140
+ if (count === 0)
141
+ continue;
142
+ const rewritten = writePgpmScript(script);
143
+ if (rewritten === content)
144
+ continue;
145
+ if (dryRun) {
146
+ result.dryRunReport.push(`Would rewrite header: ${relative(moduleDir, file)}`);
147
+ }
148
+ else {
149
+ writeFileSync(file, rewritten);
150
+ }
151
+ result.headersRewritten.push(relative(moduleDir, file));
152
+ }
153
+ }
154
+ // In dry-run mode files were not moved, so dependents' headers are found at
155
+ // their original paths above; the moved files' own headers are still
156
+ // reported through the same scan.
157
+ // 4. Rewrite pgpm.plan
158
+ const planPath = join(moduleDir, 'pgpm.plan');
159
+ const planContent = readFileSync(planPath, 'utf-8');
160
+ const newPlanContent = renameInPlanContent(planContent, renames);
161
+ if (newPlanContent !== planContent) {
162
+ if (dryRun) {
163
+ result.dryRunReport.push('Would rewrite: pgpm.plan');
164
+ }
165
+ else {
166
+ writeFileSync(planPath, newPlanContent);
167
+ }
168
+ result.planRewritten = true;
169
+ }
170
+ return result;
171
+ }
172
+ /**
173
+ * Derive a change rename map from a schema mapping (old schema name -> new
174
+ * schema name) by rewriting the `schemas/<name>/` path segment of every plan
175
+ * change that lives under a renamed schema.
176
+ *
177
+ * This is the module-level counterpart of directory-based schema renames:
178
+ * `schemas/my-app/tables/users` with mapping `my-app -> my_app` yields
179
+ * `schemas/my_app/tables/users`.
180
+ */
181
+ export function deriveRenamesFromSchemaMapping(moduleDir, schemaMapping) {
182
+ const renames = new Map();
183
+ const planPath = join(moduleDir, 'pgpm.plan');
184
+ const plan = parsePlanFileSimple(planPath);
185
+ const schemas = Array.from(schemaMapping.keys()).sort((a, b) => b.length - a.length);
186
+ for (const change of plan.changes) {
187
+ let renamed = change.name;
188
+ for (const schema of schemas) {
189
+ const to = schemaMapping.get(schema);
190
+ if (!to || to === schema)
191
+ continue;
192
+ const re = new RegExp(`(^|/)(schemas/)${escapeRe(schema)}(/|$)`, 'g');
193
+ renamed = renamed.replace(re, `$1$2${to}$3`);
194
+ }
195
+ if (renamed !== change.name) {
196
+ renames.set(change.name, renamed);
197
+ }
198
+ }
199
+ return renames;
200
+ }
package/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from './core/template-scaffold';
12
12
  export * from './core/boilerplate-types';
13
13
  export * from './core/boilerplate-scanner';
14
14
  export * from './files';
15
+ export * from './refactor';
15
16
  export { cleanSql } from './migrate/clean';
16
17
  export { PgpmMigrate } from './migrate/client';
17
18
  export { PgpmInit } from './init/client';
package/index.js CHANGED
@@ -30,6 +30,7 @@ __exportStar(require("./core/boilerplate-types"), exports);
30
30
  __exportStar(require("./core/boilerplate-scanner"), exports);
31
31
  // Export package-files functionality (now integrated into core)
32
32
  __exportStar(require("./files"), exports);
33
+ __exportStar(require("./refactor"), exports);
33
34
  var clean_1 = require("./migrate/clean");
34
35
  Object.defineProperty(exports, "cleanSql", { enumerable: true, get: function () { return clean_1.cleanSql; } });
35
36
  var client_1 = require("./migrate/client");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpmjs/core",
3
- "version": "6.33.0",
3
+ "version": "6.34.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PGPM Package and Migration Tools",
6
6
  "main": "index.js",
@@ -64,5 +64,5 @@
64
64
  "pgsql-parser": "^17.9.17",
65
65
  "yanse": "^0.2.1"
66
66
  },
67
- "gitHead": "6a1d3e65a4a786e602efe5ec3835238eb1648734"
67
+ "gitHead": "08b12923f099191b2d1592b9c897f9f04b4950aa"
68
68
  }
@@ -0,0 +1 @@
1
+ export * from './rename';
@@ -0,0 +1,17 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./rename"), exports);
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Options for {@link renameChanges}.
3
+ */
4
+ export interface RenameChangesOptions {
5
+ /** Report planned operations without touching the filesystem. Default: false */
6
+ dryRun?: boolean;
7
+ }
8
+ /**
9
+ * Result of a {@link renameChanges} run.
10
+ */
11
+ export interface RenameChangesResult {
12
+ /** deploy/revert/verify script files moved (old path -> new path, module-relative). */
13
+ filesMoved: Array<{
14
+ from: string;
15
+ to: string;
16
+ }>;
17
+ /** Script files whose headers were rewritten (identity and/or requires). */
18
+ headersRewritten: string[];
19
+ /** Whether pgpm.plan was rewritten. */
20
+ planRewritten: boolean;
21
+ /** Dry-run report lines (only populated when dryRun). */
22
+ dryRunReport?: string[];
23
+ }
24
+ /**
25
+ * Validate a rename map against a module directory: every source change must
26
+ * exist in the plan, no target may collide with an existing change, and no
27
+ * two sources may map to the same target.
28
+ *
29
+ * @throws on any violation.
30
+ */
31
+ export declare function validateRenames(moduleDir: string, renames: Map<string, string>): void;
32
+ /**
33
+ * Rewrite change names in a pgpm.plan's content according to a rename map.
34
+ * Textual and format-preserving: only the change-name token at the start of a
35
+ * change line, plain (same-package) dependency refs inside `[...]`, and tag
36
+ * lines' change field are rewritten; timestamps, planners, comments,
37
+ * cross-package refs, and tag refs are untouched.
38
+ */
39
+ export declare function renameInPlanContent(content: string, renames: Map<string, string>): string;
40
+ /**
41
+ * Atomically rename/move changes within a pgpm module: the coupled 4-way
42
+ * rewrite of (1) deploy/revert/verify script files on disk, (2) the moved
43
+ * scripts' own header identity, (3) `-- requires:` headers in every script
44
+ * that references a renamed change, and (4) the pgpm.plan.
45
+ *
46
+ * SQL statement bodies are NOT touched: schema-name rewrites inside SQL are a
47
+ * separate AST-level concern for callers to compose (e.g. transform passes).
48
+ */
49
+ export declare function renameChanges(moduleDir: string, renames: Map<string, string>, options?: RenameChangesOptions): RenameChangesResult;
50
+ /**
51
+ * Derive a change rename map from a schema mapping (old schema name -> new
52
+ * schema name) by rewriting the `schemas/<name>/` path segment of every plan
53
+ * change that lives under a renamed schema.
54
+ *
55
+ * This is the module-level counterpart of directory-based schema renames:
56
+ * `schemas/my-app/tables/users` with mapping `my-app -> my_app` yields
57
+ * `schemas/my_app/tables/users`.
58
+ */
59
+ export declare function deriveRenamesFromSchemaMapping(moduleDir: string, schemaMapping: Map<string, string>): Map<string, string>;
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateRenames = validateRenames;
4
+ exports.renameInPlanContent = renameInPlanContent;
5
+ exports.renameChanges = renameChanges;
6
+ exports.deriveRenamesFromSchemaMapping = deriveRenamesFromSchemaMapping;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const parser_1 = require("../files/plan/parser");
10
+ const header_1 = require("../files/sql/header");
11
+ const SCRIPT_DIRS = ['deploy', 'revert', 'verify'];
12
+ function escapeRe(str) {
13
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
14
+ }
15
+ function findSqlFiles(dir) {
16
+ const files = [];
17
+ if (!(0, fs_1.existsSync)(dir))
18
+ return files;
19
+ const entries = (0, fs_1.readdirSync)(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
20
+ for (const entry of entries) {
21
+ const fullPath = (0, path_1.join)(dir, entry.name);
22
+ if (entry.isDirectory()) {
23
+ files.push(...findSqlFiles(fullPath));
24
+ }
25
+ else if (entry.isFile() && entry.name.endsWith('.sql')) {
26
+ files.push(fullPath);
27
+ }
28
+ }
29
+ return files;
30
+ }
31
+ function removeEmptyDirs(dir, stopAt) {
32
+ let current = dir;
33
+ while (current !== stopAt && current.startsWith(stopAt)) {
34
+ if (!(0, fs_1.existsSync)(current) || (0, fs_1.readdirSync)(current).length > 0)
35
+ return;
36
+ (0, fs_1.rmdirSync)(current);
37
+ current = (0, path_1.dirname)(current);
38
+ }
39
+ }
40
+ /**
41
+ * Validate a rename map against a module directory: every source change must
42
+ * exist in the plan, no target may collide with an existing change, and no
43
+ * two sources may map to the same target.
44
+ *
45
+ * @throws on any violation.
46
+ */
47
+ function validateRenames(moduleDir, renames) {
48
+ const planPath = (0, path_1.join)(moduleDir, 'pgpm.plan');
49
+ const plan = (0, parser_1.parsePlanFileSimple)(planPath);
50
+ const changeNames = new Set(plan.changes.map(c => c.name));
51
+ const targets = new Set();
52
+ for (const [from, to] of renames) {
53
+ if (from === to) {
54
+ throw new Error(`Rename maps change to itself: ${from}`);
55
+ }
56
+ if (!changeNames.has(from)) {
57
+ throw new Error(`Cannot rename unknown change: ${from}`);
58
+ }
59
+ if (changeNames.has(to) && !renames.has(to)) {
60
+ throw new Error(`Rename target already exists in plan: ${to}`);
61
+ }
62
+ if (targets.has(to)) {
63
+ throw new Error(`Multiple changes rename to the same target: ${to}`);
64
+ }
65
+ targets.add(to);
66
+ }
67
+ }
68
+ /**
69
+ * Rewrite change names in a pgpm.plan's content according to a rename map.
70
+ * Textual and format-preserving: only the change-name token at the start of a
71
+ * change line, plain (same-package) dependency refs inside `[...]`, and tag
72
+ * lines' change field are rewritten; timestamps, planners, comments,
73
+ * cross-package refs, and tag refs are untouched.
74
+ */
75
+ function renameInPlanContent(content, renames) {
76
+ const froms = Array.from(renames.keys()).sort((a, b) => b.length - a.length);
77
+ return content
78
+ .split('\n')
79
+ .map(line => {
80
+ const trimmed = line.trim();
81
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('%'))
82
+ return line;
83
+ let result = line;
84
+ for (const from of froms) {
85
+ const to = renames.get(from);
86
+ const re = new RegExp(`(^|[\\s\\[])${escapeRe(from)}(?=$|[\\s\\]@])`, 'g');
87
+ result = result.replace(re, `$1${to}`);
88
+ }
89
+ return result;
90
+ })
91
+ .join('\n');
92
+ }
93
+ /**
94
+ * Atomically rename/move changes within a pgpm module: the coupled 4-way
95
+ * rewrite of (1) deploy/revert/verify script files on disk, (2) the moved
96
+ * scripts' own header identity, (3) `-- requires:` headers in every script
97
+ * that references a renamed change, and (4) the pgpm.plan.
98
+ *
99
+ * SQL statement bodies are NOT touched: schema-name rewrites inside SQL are a
100
+ * separate AST-level concern for callers to compose (e.g. transform passes).
101
+ */
102
+ function renameChanges(moduleDir, renames, options = {}) {
103
+ const dryRun = options.dryRun ?? false;
104
+ const result = {
105
+ filesMoved: [],
106
+ headersRewritten: [],
107
+ planRewritten: false
108
+ };
109
+ if (dryRun)
110
+ result.dryRunReport = [];
111
+ if (renames.size === 0)
112
+ return result;
113
+ validateRenames(moduleDir, renames);
114
+ // 1. Move script files
115
+ for (const scriptDir of SCRIPT_DIRS) {
116
+ for (const [from, to] of renames) {
117
+ const oldPath = (0, path_1.join)(moduleDir, scriptDir, `${from}.sql`);
118
+ if (!(0, fs_1.existsSync)(oldPath) || !(0, fs_1.statSync)(oldPath).isFile())
119
+ continue;
120
+ const newPath = (0, path_1.join)(moduleDir, scriptDir, `${to}.sql`);
121
+ if ((0, fs_1.existsSync)(newPath)) {
122
+ throw new Error(`Rename target file already exists: ${(0, path_1.relative)(moduleDir, newPath)}`);
123
+ }
124
+ if (dryRun) {
125
+ result.dryRunReport.push(`Would move: ${(0, path_1.relative)(moduleDir, oldPath)} -> ${(0, path_1.relative)(moduleDir, newPath)}`);
126
+ }
127
+ else {
128
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(newPath), { recursive: true });
129
+ (0, fs_1.renameSync)(oldPath, newPath);
130
+ removeEmptyDirs((0, path_1.dirname)(oldPath), (0, path_1.join)(moduleDir, scriptDir));
131
+ }
132
+ result.filesMoved.push({
133
+ from: [scriptDir, `${from}.sql`].join(path_1.sep),
134
+ to: [scriptDir, `${to}.sql`].join(path_1.sep)
135
+ });
136
+ }
137
+ }
138
+ // 2 + 3. Rewrite headers in every script (identity of moved files,
139
+ // requires of dependents)
140
+ for (const scriptDir of SCRIPT_DIRS) {
141
+ const dir = (0, path_1.join)(moduleDir, scriptDir);
142
+ for (const file of findSqlFiles(dir)) {
143
+ const content = (0, fs_1.readFileSync)(file, 'utf-8');
144
+ const script = (0, header_1.parsePgpmHeader)(content);
145
+ const count = (0, header_1.renameInHeader)(script, renames);
146
+ if (count === 0)
147
+ continue;
148
+ const rewritten = (0, header_1.writePgpmScript)(script);
149
+ if (rewritten === content)
150
+ continue;
151
+ if (dryRun) {
152
+ result.dryRunReport.push(`Would rewrite header: ${(0, path_1.relative)(moduleDir, file)}`);
153
+ }
154
+ else {
155
+ (0, fs_1.writeFileSync)(file, rewritten);
156
+ }
157
+ result.headersRewritten.push((0, path_1.relative)(moduleDir, file));
158
+ }
159
+ }
160
+ // In dry-run mode files were not moved, so dependents' headers are found at
161
+ // their original paths above; the moved files' own headers are still
162
+ // reported through the same scan.
163
+ // 4. Rewrite pgpm.plan
164
+ const planPath = (0, path_1.join)(moduleDir, 'pgpm.plan');
165
+ const planContent = (0, fs_1.readFileSync)(planPath, 'utf-8');
166
+ const newPlanContent = renameInPlanContent(planContent, renames);
167
+ if (newPlanContent !== planContent) {
168
+ if (dryRun) {
169
+ result.dryRunReport.push('Would rewrite: pgpm.plan');
170
+ }
171
+ else {
172
+ (0, fs_1.writeFileSync)(planPath, newPlanContent);
173
+ }
174
+ result.planRewritten = true;
175
+ }
176
+ return result;
177
+ }
178
+ /**
179
+ * Derive a change rename map from a schema mapping (old schema name -> new
180
+ * schema name) by rewriting the `schemas/<name>/` path segment of every plan
181
+ * change that lives under a renamed schema.
182
+ *
183
+ * This is the module-level counterpart of directory-based schema renames:
184
+ * `schemas/my-app/tables/users` with mapping `my-app -> my_app` yields
185
+ * `schemas/my_app/tables/users`.
186
+ */
187
+ function deriveRenamesFromSchemaMapping(moduleDir, schemaMapping) {
188
+ const renames = new Map();
189
+ const planPath = (0, path_1.join)(moduleDir, 'pgpm.plan');
190
+ const plan = (0, parser_1.parsePlanFileSimple)(planPath);
191
+ const schemas = Array.from(schemaMapping.keys()).sort((a, b) => b.length - a.length);
192
+ for (const change of plan.changes) {
193
+ let renamed = change.name;
194
+ for (const schema of schemas) {
195
+ const to = schemaMapping.get(schema);
196
+ if (!to || to === schema)
197
+ continue;
198
+ const re = new RegExp(`(^|/)(schemas/)${escapeRe(schema)}(/|$)`, 'g');
199
+ renamed = renamed.replace(re, `$1$2${to}$3`);
200
+ }
201
+ if (renamed !== change.name) {
202
+ renames.set(change.name, renamed);
203
+ }
204
+ }
205
+ return renames;
206
+ }