@pgpmjs/core 7.1.0 → 7.2.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 (94) hide show
  1. package/bundle/apply.d.ts +93 -0
  2. package/bundle/apply.js +137 -0
  3. package/bundle/index.d.ts +12 -0
  4. package/bundle/index.js +31 -0
  5. package/core/class/pgpm.d.ts +2 -2
  6. package/core/class/pgpm.js +6 -6
  7. package/esm/bundle/apply.js +131 -0
  8. package/esm/bundle/index.js +15 -0
  9. package/esm/core/class/pgpm.js +6 -6
  10. package/esm/index.js +5 -3
  11. package/esm/migrate/client.js +1 -1
  12. package/esm/migrate/utils/hash.js +3 -6
  13. package/esm/modules/modules.js +1 -1
  14. package/esm/packaging/package.js +1 -1
  15. package/esm/rebundle/module.js +1 -1
  16. package/esm/rebundle/rebundle.js +2 -2
  17. package/esm/rebundle/workspace.js +1 -1
  18. package/esm/refactor/rename.js +5 -27
  19. package/esm/resolution/deps.js +2 -2
  20. package/esm/resolution/resolve.js +2 -2
  21. package/esm/slice/slice.js +1 -1
  22. package/index.d.ts +4 -2
  23. package/index.js +6 -4
  24. package/migrate/client.js +1 -1
  25. package/migrate/utils/hash.d.ts +2 -4
  26. package/migrate/utils/hash.js +4 -8
  27. package/modules/modules.d.ts +1 -1
  28. package/modules/modules.js +1 -1
  29. package/package.json +4 -2
  30. package/packaging/package.js +1 -1
  31. package/rebundle/module.js +1 -1
  32. package/rebundle/rebundle.js +2 -2
  33. package/rebundle/types.d.ts +1 -1
  34. package/rebundle/workspace.js +1 -1
  35. package/refactor/rename.d.ts +2 -8
  36. package/refactor/rename.js +6 -29
  37. package/resolution/deps.d.ts +1 -1
  38. package/resolution/deps.js +2 -2
  39. package/resolution/resolve.js +2 -2
  40. package/slice/slice.d.ts +1 -1
  41. package/slice/slice.js +1 -1
  42. package/slice/types.d.ts +1 -1
  43. package/workspace/minimal.d.ts +1 -1
  44. package/esm/files/extension/index.js +0 -3
  45. package/esm/files/extension/reader.js +0 -79
  46. package/esm/files/extension/writer.js +0 -99
  47. package/esm/files/index.js +0 -6
  48. package/esm/files/plan/generator.js +0 -82
  49. package/esm/files/plan/index.js +0 -5
  50. package/esm/files/plan/parser.js +0 -297
  51. package/esm/files/plan/validators.js +0 -181
  52. package/esm/files/plan/writer.js +0 -142
  53. package/esm/files/sql/header.js +0 -259
  54. package/esm/files/sql/index.js +0 -2
  55. package/esm/files/sql/writer.js +0 -107
  56. package/esm/files/sql-scripts/index.js +0 -2
  57. package/esm/files/sql-scripts/reader.js +0 -19
  58. package/esm/files/types/index.js +0 -1
  59. package/esm/files/types/package.js +0 -1
  60. package/esm/utils/author.js +0 -11
  61. package/files/extension/index.d.ts +0 -2
  62. package/files/extension/index.js +0 -19
  63. package/files/extension/reader.d.ts +0 -24
  64. package/files/extension/reader.js +0 -86
  65. package/files/extension/writer.d.ts +0 -48
  66. package/files/extension/writer.js +0 -107
  67. package/files/index.d.ts +0 -5
  68. package/files/index.js +0 -22
  69. package/files/plan/generator.d.ts +0 -23
  70. package/files/plan/generator.js +0 -90
  71. package/files/plan/index.d.ts +0 -4
  72. package/files/plan/index.js +0 -21
  73. package/files/plan/parser.d.ts +0 -28
  74. package/files/plan/parser.js +0 -304
  75. package/files/plan/validators.d.ts +0 -52
  76. package/files/plan/validators.js +0 -187
  77. package/files/plan/writer.d.ts +0 -27
  78. package/files/plan/writer.js +0 -152
  79. package/files/sql/header.d.ts +0 -114
  80. package/files/sql/header.js +0 -266
  81. package/files/sql/index.d.ts +0 -2
  82. package/files/sql/index.js +0 -18
  83. package/files/sql/writer.d.ts +0 -12
  84. package/files/sql/writer.js +0 -114
  85. package/files/sql-scripts/index.d.ts +0 -1
  86. package/files/sql-scripts/index.js +0 -18
  87. package/files/sql-scripts/reader.d.ts +0 -8
  88. package/files/sql-scripts/reader.js +0 -23
  89. package/files/types/index.d.ts +0 -46
  90. package/files/types/index.js +0 -17
  91. package/files/types/package.d.ts +0 -20
  92. package/files/types/package.js +0 -2
  93. package/utils/author.d.ts +0 -5
  94. package/utils/author.js +0 -14
@@ -1,187 +0,0 @@
1
- "use strict";
2
- /**
3
- * Validators for Sqitch change and tag names according to the spec:
4
- * https://sqitch.org/docs/manual/sqitchchanges/
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.isValidChangeName = isValidChangeName;
8
- exports.isValidTagName = isValidTagName;
9
- exports.parseReference = parseReference;
10
- exports.isValidDependency = isValidDependency;
11
- /**
12
- * Check if a character is punctuation (excluding underscore)
13
- */
14
- function isPunctuation(char) {
15
- // Common punctuation marks, excluding underscore
16
- const punctuation = /[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{|}~]/;
17
- return punctuation.test(char);
18
- }
19
- /**
20
- * Validate a change name according to Sqitch rules:
21
- * - ≥1 character
22
- * - Cannot contain whitespace
23
- * - First and last characters must not be punctuation (except _)
24
- * - Must not end in ~, ^, /, = or % followed by digits
25
- * - Disallowed characters anywhere: :, @, #, \
26
- */
27
- function isValidChangeName(name) {
28
- if (!name || name.length === 0) {
29
- return false;
30
- }
31
- // Check for whitespace
32
- if (/\s/.test(name)) {
33
- return false;
34
- }
35
- // Check for disallowed characters anywhere
36
- if (/[:@#\\]/.test(name)) {
37
- return false;
38
- }
39
- // Check first character - must not be punctuation except underscore
40
- const firstChar = name[0];
41
- if (firstChar !== '_' && isPunctuation(firstChar)) {
42
- return false;
43
- }
44
- // Check last character - must not be punctuation except underscore
45
- const lastChar = name[name.length - 1];
46
- if (lastChar !== '_' && isPunctuation(lastChar)) {
47
- return false;
48
- }
49
- // Check for endings like ~N, ^N, /N, =N, %N where N is digits
50
- if (/[~^/=%]\d+$/.test(name)) {
51
- return false;
52
- }
53
- return true;
54
- }
55
- /**
56
- * Validate a tag name according to Sqitch rules:
57
- * - Same as change name rules
58
- * - Additionally, must not contain /
59
- */
60
- function isValidTagName(name) {
61
- if (!isValidChangeName(name)) {
62
- return false;
63
- }
64
- // Tags must not contain /
65
- if (name.includes('/')) {
66
- return false;
67
- }
68
- return true;
69
- }
70
- /**
71
- * Check if a string is a valid SHA1 hash (40 hex characters)
72
- */
73
- function isSHA1(str) {
74
- return /^[0-9a-f]{40}$/i.test(str);
75
- }
76
- /**
77
- * Parse a reference string into its components
78
- */
79
- function parseReference(ref) {
80
- if (!ref) {
81
- return null;
82
- }
83
- const result = {};
84
- // Check for package qualifier
85
- let workingRef = ref;
86
- const colonIndex = ref.indexOf(':');
87
- if (colonIndex > 0) {
88
- const projectPart = ref.substring(0, colonIndex);
89
- const remainingPart = ref.substring(colonIndex + 1);
90
- // Check if this is actually a package qualifier or just a colon in the name
91
- // Package names should be valid identifiers (alphanumeric, dash, underscore)
92
- // Also check that the remaining part doesn't have more colons
93
- if (/^[a-zA-Z0-9_-]+$/.test(projectPart) &&
94
- remainingPart.length > 0 &&
95
- remainingPart.indexOf(':') === -1) {
96
- result.package = projectPart;
97
- workingRef = remainingPart;
98
- }
99
- else {
100
- // Not a valid package qualifier, treat the whole thing as a reference
101
- workingRef = ref;
102
- }
103
- }
104
- // Check for symbolic references
105
- if (workingRef === 'HEAD' || workingRef === '@HEAD') {
106
- result.symbolic = 'HEAD';
107
- return result;
108
- }
109
- if (workingRef === 'ROOT' || workingRef === '@ROOT') {
110
- result.symbolic = 'ROOT';
111
- return result;
112
- }
113
- // Check for relative references (e.g., HEAD^, @beta~2, HEAD^^)
114
- // Handle multiple ^ characters
115
- const multiCaretMatch = workingRef.match(/^(.+?)(\^+)$/);
116
- if (multiCaretMatch) {
117
- const [, base, carets] = multiCaretMatch;
118
- result.relative = {
119
- base: base, // Keep the original base including @ prefix
120
- direction: '^',
121
- count: carets.length
122
- };
123
- return result;
124
- }
125
- // Handle ^N or ~N format
126
- const relativeMatch = workingRef.match(/^(.+?)([~^])(\d+)$/);
127
- if (relativeMatch) {
128
- const [, base, direction, countStr] = relativeMatch;
129
- const count = parseInt(countStr, 10);
130
- result.relative = {
131
- base: base, // Keep the original base including @ prefix
132
- direction: direction,
133
- count
134
- };
135
- return result;
136
- }
137
- // Handle single ^ or ~ without number
138
- const singleRelativeMatch = workingRef.match(/^(.+?)([~^])$/);
139
- if (singleRelativeMatch) {
140
- const [, base, direction] = singleRelativeMatch;
141
- result.relative = {
142
- base: base, // Keep the original base including @ prefix
143
- direction: direction,
144
- count: 1
145
- };
146
- return result;
147
- }
148
- // Check if it's a SHA1
149
- if (isSHA1(workingRef)) {
150
- result.sha1 = workingRef;
151
- return result;
152
- }
153
- // Check for tag reference (starts with @)
154
- if (workingRef.startsWith('@')) {
155
- const tagName = workingRef.substring(1);
156
- if (isValidTagName(tagName)) {
157
- result.tag = tagName;
158
- return result;
159
- }
160
- return null; // Invalid tag name
161
- }
162
- // Check for change@tag format
163
- const atIndex = workingRef.indexOf('@');
164
- if (atIndex > 0) {
165
- const changeName = workingRef.substring(0, atIndex);
166
- const tagName = workingRef.substring(atIndex + 1);
167
- if (isValidChangeName(changeName) && isValidTagName(tagName)) {
168
- result.change = changeName;
169
- result.tag = tagName;
170
- return result;
171
- }
172
- return null; // Invalid change or tag name
173
- }
174
- // Must be a plain change name
175
- if (isValidChangeName(workingRef)) {
176
- result.change = workingRef;
177
- return result;
178
- }
179
- return null; // Invalid reference
180
- }
181
- /**
182
- * Validate a dependency reference
183
- * Dependencies can be any valid reference format
184
- */
185
- function isValidDependency(dep) {
186
- return parseReference(dep) !== null;
187
- }
@@ -1,27 +0,0 @@
1
- import { Change, ExtendedPlanFile, PgpmRow, Tag } from '../types';
2
- export interface PlanWriteOptions {
3
- outdir: string;
4
- name: string;
5
- replacer: (str: string) => string;
6
- author?: string;
7
- }
8
- /**
9
- * Write a PGPM plan file based on the provided rows
10
- */
11
- export declare function writePgpmPlan(rows: PgpmRow[], opts: PlanWriteOptions): void;
12
- /**
13
- * Write a plan file with the provided content
14
- */
15
- export declare function writePlanFile(planPath: string, plan: ExtendedPlanFile): void;
16
- /**
17
- * Generate content for a plan file
18
- */
19
- export declare function generatePlanFileContent(plan: ExtendedPlanFile): string;
20
- /**
21
- * Generate a line for a change in a plan file
22
- */
23
- export declare function generateChangeLineContent(change: Change, changeOrder?: Map<string, number>): string;
24
- /**
25
- * Generate a line for a tag in a plan file
26
- */
27
- export declare function generateTagLineContent(tag: Tag): string;
@@ -1,152 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writePgpmPlan = writePgpmPlan;
7
- exports.writePlanFile = writePlanFile;
8
- exports.generatePlanFileContent = generatePlanFileContent;
9
- exports.generateChangeLineContent = generateChangeLineContent;
10
- exports.generateTagLineContent = generateTagLineContent;
11
- const fs_1 = __importDefault(require("fs"));
12
- const path_1 = __importDefault(require("path"));
13
- const author_1 = require("../../utils/author");
14
- /**
15
- * Sort dependencies by their order in the supplied plan, so prerequisites
16
- * (parent migrations) appear before their dependents in the bracket.
17
- * Internal dependencies are sorted by their plan index, and external
18
- * dependencies (not in the plan) preserve their original order.
19
- */
20
- const sortDependencies = (deps, order) => {
21
- const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
22
- return [...deps].sort((a, b) => {
23
- const aIndex = order.get(a);
24
- const bIndex = order.get(b);
25
- const aOriginal = originalOrder.get(a);
26
- const bOriginal = originalOrder.get(b);
27
- if (aIndex !== undefined && bIndex !== undefined)
28
- return aIndex - bIndex || aOriginal - bOriginal;
29
- if (aIndex !== undefined)
30
- return -1;
31
- if (bIndex !== undefined)
32
- return 1;
33
- return aOriginal - bOriginal;
34
- });
35
- };
36
- /**
37
- * Write a PGPM plan file based on the provided rows
38
- */
39
- function writePgpmPlan(rows, opts) {
40
- const dir = path_1.default.resolve(path_1.default.join(opts.outdir, opts.name));
41
- fs_1.default.mkdirSync(dir, { recursive: true });
42
- const date = () => '2017-08-11T08:11:51Z'; // stubbed timestamp
43
- const { fullName, email } = (0, author_1.parseAuthor)(opts.author || 'constructive');
44
- const authorName = fullName;
45
- const authorEmail = email || `${fullName}@5b0c196eeb62`;
46
- const duplicates = {};
47
- // Build an order map from the row list so brackets can be sorted by plan order
48
- const rowOrder = new Map(rows.map((row, index) => [row.deploy, index]));
49
- const plan = opts.replacer(`%syntax-version=1.0.0
50
- %project=constructive-extension-name
51
- %uri=constructive-extension-name
52
-
53
- ${rows
54
- .map((row) => {
55
- if (duplicates[row.deploy]) {
56
- console.log('DUPLICATE ' + row.deploy);
57
- return '';
58
- }
59
- duplicates[row.deploy] = true;
60
- if (row.deps?.length) {
61
- return `${row.deploy} [${sortDependencies(row.deps, rowOrder).join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
62
- }
63
- return `${row.deploy} ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
64
- })
65
- .join('\n')}
66
- `);
67
- fs_1.default.writeFileSync(path_1.default.join(dir, 'pgpm.plan'), plan);
68
- }
69
- /**
70
- * Write a plan file with the provided content
71
- */
72
- function writePlanFile(planPath, plan) {
73
- const content = generatePlanFileContent(plan);
74
- fs_1.default.writeFileSync(planPath, content);
75
- }
76
- /**
77
- * Generate content for a plan file
78
- */
79
- function generatePlanFileContent(plan) {
80
- const { package: packageName, uri, changes, tags } = plan;
81
- let content = `%syntax-version=1.0.0\n`;
82
- content += `%project=${packageName}\n`;
83
- if (uri) {
84
- content += `%uri=${uri}\n`;
85
- }
86
- content += `\n`;
87
- // Build an order map from the change list so brackets can be sorted by plan order
88
- const changeOrder = new Map(changes.map((change, index) => [change.name, index]));
89
- // Add changes and their associated tags
90
- for (const change of changes) {
91
- content += generateChangeLineContent(change, changeOrder);
92
- content += `\n`;
93
- const associatedTags = tags.filter(tag => tag.change === change.name);
94
- for (const tag of associatedTags) {
95
- content += generateTagLineContent(tag);
96
- content += `\n`;
97
- }
98
- }
99
- return content;
100
- }
101
- /**
102
- * Generate a line for a change in a plan file
103
- */
104
- function generateChangeLineContent(change, changeOrder) {
105
- const { name, dependencies, timestamp, planner, email, comment } = change;
106
- let line = name;
107
- // Add dependencies if present, sorted by plan order
108
- if (dependencies && dependencies.length > 0) {
109
- line += ` [${sortDependencies(dependencies, changeOrder || new Map()).join(' ')}]`;
110
- }
111
- // Add timestamp if present
112
- if (timestamp) {
113
- line += ` ${timestamp}`;
114
- // Add planner if present
115
- if (planner) {
116
- line += ` ${planner}`;
117
- // Add email if present
118
- if (email) {
119
- line += ` <${email}>`;
120
- }
121
- }
122
- }
123
- // Add comment if present
124
- if (comment) {
125
- line += ` # ${comment}`;
126
- }
127
- return line;
128
- }
129
- /**
130
- * Generate a line for a tag in a plan file
131
- */
132
- function generateTagLineContent(tag) {
133
- const { name, timestamp, planner, email, comment } = tag;
134
- let line = `@${name}`;
135
- // Add timestamp if present
136
- if (timestamp) {
137
- line += ` ${timestamp}`;
138
- // Add planner if present
139
- if (planner) {
140
- line += ` ${planner}`;
141
- // Add email if present
142
- if (email) {
143
- line += ` <${email}>`;
144
- }
145
- }
146
- }
147
- // Add comment if present
148
- if (comment) {
149
- line += ` # ${comment}`;
150
- }
151
- return line;
152
- }
@@ -1,114 +0,0 @@
1
- import { ParsedReference } from '../plan/validators';
2
- /**
3
- * A parsed `-- requires:` line from a pgpm SQL script header.
4
- */
5
- export interface PgpmHeaderRequire {
6
- /** The dependency reference exactly as written (e.g. `schema`, `pkg:tables/users`, `pkg:@v1`). */
7
- raw: string;
8
- /** Structured breakdown of the reference (package/change/tag), when parseable. */
9
- ref: ParsedReference | null;
10
- }
11
- /**
12
- * The parsed header block of a pgpm SQL script (deploy/revert/verify).
13
- *
14
- * The header is everything from the top of the file through the last
15
- * header-relevant comment line (`-- Deploy|Revert|Verify ...`,
16
- * `-- requires: ...`, and any interleaved comment/blank lines), with the
17
- * remainder of the file preserved as `body`.
18
- */
19
- export interface PgpmHeader {
20
- /** Header verb: Deploy, Revert or Verify. Null when no header line was found. */
21
- verb: 'Deploy' | 'Revert' | 'Verify' | null;
22
- /** The project (extension) name from `-- Deploy <project>:<change>`, if present. */
23
- project: string | null;
24
- /**
25
- * The change name from the header line. By convention this must equal the
26
- * script's path identity: `deploy/<change>.sql`.
27
- */
28
- change: string | null;
29
- /** Whether the header line carried the legacy ` to pg` suffix. */
30
- toPg: boolean;
31
- /** Parsed `-- requires:` dependencies, in file order. */
32
- requires: PgpmHeaderRequire[];
33
- /** The raw header lines, byte-exact, in file order. */
34
- lines: string[];
35
- }
36
- /**
37
- * Result of splitting a SQL script into header and body.
38
- */
39
- export interface ParsedPgpmScript {
40
- header: PgpmHeader;
41
- /** Everything after the header block, byte-exact. */
42
- body: string;
43
- }
44
- /**
45
- * Parse the header block of a pgpm SQL script into a structured model.
46
- *
47
- * Recognizes both header formats:
48
- * `-- Deploy <project>:<change> to pg` (legacy sqitch-compatible)
49
- * `-- Deploy <change>` / `-- Deploy: <change>` (new)
50
- * along with `-- requires: <ref>` lines where `<ref>` may be a plain change
51
- * name, `{package}:{change}`, `@tag`, or `{package}:@tag`.
52
- *
53
- * The parse is lossless: `header.lines` plus `body` reassemble the original
54
- * content byte-exactly (see {@link writePgpmScript}).
55
- */
56
- export declare function parsePgpmHeader(content: string): ParsedPgpmScript;
57
- /**
58
- * Serialize a header back to its raw lines. When the structured fields have
59
- * been modified via {@link renameInHeader}, the raw lines already reflect the
60
- * change; this simply joins header and body losslessly.
61
- */
62
- export declare function writePgpmScript(script: ParsedPgpmScript): string;
63
- /**
64
- * Rewrite change names in a script header according to a rename map
65
- * (old change name -> new change name). Rewrites both the `-- Deploy` line's
66
- * change identity and any `-- requires:` lines referencing renamed changes,
67
- * preserving format (project qualifier, ` to pg` suffix, tag refs untouched).
68
- *
69
- * Returns the number of substitutions applied; mutates `script` in place.
70
- */
71
- export declare function renameInHeader(script: ParsedPgpmScript, renames: Map<string, string>): number;
72
- /**
73
- * Options for {@link scanDeployScript}.
74
- */
75
- export interface ScanDeployScriptOptions {
76
- /** The script's path identity key (e.g. `/deploy/tables/users.sql`). */
77
- key: string;
78
- /** The current extension (project) name. */
79
- extname: string;
80
- /** Maps a change name to its path identity key. */
81
- makeKey: (change: string) => string;
82
- }
83
- /**
84
- * Scan a deploy script the way dependency resolution historically has:
85
- * line-by-line over the whole file, collecting `-- requires: <ref>` lines
86
- * verbatim and validating any `-- Deploy <project>:<change>[ to pg]` line
87
- * against the script's path identity and project name.
88
- *
89
- * Kept strictly line-regex-compatible with the original resolver behavior
90
- * (requires lines are honored anywhere in the file, and the legacy
91
- * `-- Deploy: <change>` colon form is not identity-validated).
92
- *
93
- * @throws when the Deploy line's project or change identity mismatches.
94
- */
95
- export declare function scanDeployScript(content: string, options: ScanDeployScriptOptions): {
96
- requires: string[];
97
- };
98
- /**
99
- * A single plan-vs-header discrepancy found by {@link verifyPlanMatchesHeaders}.
100
- */
101
- export interface HeaderDriftIssue {
102
- change: string;
103
- file: string;
104
- kind: 'missing-file' | 'identity-mismatch' | 'requires-drift';
105
- message: string;
106
- }
107
- /**
108
- * Verify that every change in a module's pgpm.plan has a deploy script whose
109
- * header identity matches its path, and report dependency drift between the
110
- * plan's dep lists and the scripts' `-- requires:` lines.
111
- *
112
- * Read-only diagnostic; returns issues rather than throwing.
113
- */
114
- export declare function verifyPlanMatchesHeaders(moduleDir: string): HeaderDriftIssue[];