@pgpmjs/transform 0.17.0 → 0.19.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/bundle-driver.d.ts +1 -1
- package/catalog-check.d.ts +47 -0
- package/catalog-check.js +130 -0
- package/esm/catalog-check.js +124 -0
- package/esm/fixture-closure.js +1 -1
- package/esm/index.js +11 -6
- package/esm/module-emit.js +65 -0
- package/esm/module-source.js +54 -0
- package/esm/partition.js +89 -0
- package/esm/restructure.js +62 -0
- package/esm/semantic-diff-driver.js +72 -26
- package/fixture-closure.d.ts +1 -1
- package/fixture-closure.js +1 -1
- package/index.d.ts +20 -10
- package/index.js +34 -16
- package/module-emit.d.ts +39 -0
- package/module-emit.js +104 -0
- package/module-source.d.ts +33 -0
- package/module-source.js +92 -0
- package/package.json +7 -6
- package/partition.d.ts +32 -0
- package/partition.js +127 -0
- package/restructure.d.ts +38 -0
- package/restructure.js +67 -0
- package/semantic-diff-driver.d.ts +7 -4
- package/semantic-diff-driver.js +71 -25
|
@@ -16,15 +16,18 @@
|
|
|
16
16
|
* modified objects routed through the granularity pipeline
|
|
17
17
|
* (`restructureChanges`), so names come from the naming spec and `requires`
|
|
18
18
|
* from the statement graph — the same derivation every other projection uses.
|
|
19
|
-
* Every emitted change carries a full deploy/revert/verify triple:
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* Every emitted change carries a full deploy/revert/verify triple: inverses
|
|
20
|
+
* and existence checks are composed at the AST level via `@pgsql/scripts`'
|
|
21
|
+
* node-level API (`invertStatement`/`existenceCheck`) — never hand-templated
|
|
22
|
+
* and never round-tripped through deparsed text — deparsing only once at the
|
|
23
|
+
* end. Statements the node layer cannot derive fall back to the text layer
|
|
24
|
+
* (`revertFor`/`verifyFor`) solely to surface its not-derivable comment and
|
|
25
|
+
* warning.
|
|
23
26
|
*
|
|
24
27
|
* Requires `loadModule()` from `plpgsql-parser` before use.
|
|
25
28
|
*/
|
|
26
29
|
import { pathFor } from '@pgpmjs/naming-spec';
|
|
27
|
-
import { revertFor, verifyFor } from '@pgsql/scripts';
|
|
30
|
+
import { existenceCheck, invertStatement, revertFor, verifyFor } from '@pgsql/scripts';
|
|
28
31
|
import { buildStatementGraph, classifyStatements, cleanTree, identityOf } from '@pgsql/transform';
|
|
29
32
|
import { Deparser, parseSync } from 'plpgsql-parser';
|
|
30
33
|
import { restructureChanges } from './granularity-driver';
|
|
@@ -207,32 +210,46 @@ function diffTable(from, to, diff, warnings) {
|
|
|
207
210
|
stmts.push(alter({ subtype: 'AT_DropColumn', name }));
|
|
208
211
|
}
|
|
209
212
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
213
|
+
// Self-inverse ALTER TABLE subtypes: a removed extra with one of these
|
|
214
|
+
// undoes with the paired subtype rather than a DROP CONSTRAINT.
|
|
215
|
+
const INVERSE_SUBTYPE = {
|
|
216
|
+
AT_EnableRowSecurity: 'AT_DisableRowSecurity',
|
|
217
|
+
AT_DisableRowSecurity: 'AT_EnableRowSecurity',
|
|
218
|
+
AT_ForceRowSecurity: 'AT_NoForceRowSecurity',
|
|
219
|
+
AT_NoForceRowSecurity: 'AT_ForceRowSecurity'
|
|
220
|
+
};
|
|
221
|
+
// Removed extras drop before added ones: a changed constraint that keeps
|
|
222
|
+
// its name must DROP CONSTRAINT before the replacement ADD CONSTRAINT.
|
|
223
223
|
for (const [print, extra] of from.extras) {
|
|
224
224
|
if (to.extras.has(print))
|
|
225
225
|
continue;
|
|
226
226
|
const conname = extra.Constraint?.conname
|
|
227
227
|
?? extra
|
|
228
228
|
.AlterTableCmd?.def?.Constraint?.conname;
|
|
229
|
+
const subtype = extra.AlterTableCmd?.subtype;
|
|
229
230
|
if (conname) {
|
|
230
231
|
stmts.push(alter({ subtype: 'AT_DropConstraint', name: conname }));
|
|
231
232
|
}
|
|
233
|
+
else if (subtype && INVERSE_SUBTYPE[subtype]) {
|
|
234
|
+
stmts.push(alter({ subtype: INVERSE_SUBTYPE[subtype] }));
|
|
235
|
+
}
|
|
232
236
|
else {
|
|
233
237
|
warnings.push(`${diff.path}: removed table element has no name to drop; emit a manual ALTER`);
|
|
234
238
|
}
|
|
235
239
|
}
|
|
240
|
+
for (const [print, extra] of to.extras) {
|
|
241
|
+
if (!from.extras.has(print)) {
|
|
242
|
+
const at = extra.AlterTableCmd;
|
|
243
|
+
if (at)
|
|
244
|
+
stmts.push(alter(at));
|
|
245
|
+
else if (extra.Constraint) {
|
|
246
|
+
stmts.push(alter({ subtype: 'AT_AddConstraint', def: extra }));
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
warnings.push(`${diff.path}: unrecognized added table element; emit a manual ALTER`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
236
253
|
return stmts;
|
|
237
254
|
}
|
|
238
255
|
/**
|
|
@@ -252,17 +269,46 @@ export function diffSchemas(from, to, options = {}) {
|
|
|
252
269
|
const forwardSql = [];
|
|
253
270
|
const modifiedChanges = [];
|
|
254
271
|
const dropUnits = [];
|
|
255
|
-
/**
|
|
272
|
+
/**
|
|
273
|
+
* `@pgsql/scripts` inverse of a deploy script (drops in reverse topo
|
|
274
|
+
* order), composed at the AST level via `invertStatement` and deparsed
|
|
275
|
+
* once. Underivable statements fall back to `revertFor` on the single
|
|
276
|
+
* statement to surface its not-derivable comment and warning.
|
|
277
|
+
*/
|
|
256
278
|
const inverseOf = (deploy, context) => {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
279
|
+
const facts = classifyStatements(deploy);
|
|
280
|
+
const graph = buildStatementGraph(facts);
|
|
281
|
+
const pieces = [];
|
|
282
|
+
for (const i of [...graph.order].reverse()) {
|
|
283
|
+
const nodes = invertStatement(facts[i]);
|
|
284
|
+
if (nodes === null) {
|
|
285
|
+
const { sql, warnings: w } = revertFor([facts[i]]);
|
|
286
|
+
warnings.push(...w.map(x => `${context}: ${x}`));
|
|
287
|
+
if (sql.trim())
|
|
288
|
+
pieces.push(sql);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
pieces.push(...nodes.map(n => deparseStmt(n)));
|
|
292
|
+
}
|
|
293
|
+
return pieces.join('\n');
|
|
260
294
|
};
|
|
261
|
-
/**
|
|
295
|
+
/**
|
|
296
|
+
* `@pgsql/scripts` existence checks for a deploy script, composed from
|
|
297
|
+
* `existenceCheck`'s SelectStmt nodes and deparsed once. Underivable
|
|
298
|
+
* statements fall back to `verifyFor` to surface the warning.
|
|
299
|
+
*/
|
|
262
300
|
const verifyOf = (deploy, context) => {
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
301
|
+
const pieces = [];
|
|
302
|
+
for (const fact of classifyStatements(deploy)) {
|
|
303
|
+
const nodes = existenceCheck(fact);
|
|
304
|
+
if (nodes === null) {
|
|
305
|
+
const { warnings: w } = verifyFor([fact]);
|
|
306
|
+
warnings.push(...w.map(x => `${context}: ${x}`));
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
pieces.push(...nodes.map(n => deparseStmt(n)));
|
|
310
|
+
}
|
|
311
|
+
return pieces.join('\n\n');
|
|
266
312
|
};
|
|
267
313
|
for (const key of b.order) {
|
|
268
314
|
const unit = b.units.get(key);
|
package/fixture-closure.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CategoryProfile, ChangeCategory } from './categorize';
|
|
2
1
|
import { QualifiedName } from '@pgsql/transform';
|
|
2
|
+
import { CategoryProfile, ChangeCategory } from './categorize';
|
|
3
3
|
/**
|
|
4
4
|
* A change plus its deploy SQL. Optional plan `dependencies` (from `pgpm.plan`)
|
|
5
5
|
* are followed as authoritative prerequisites in addition to AST-derived edges.
|
package/fixture-closure.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveFixtureClosure = resolveFixtureClosure;
|
|
4
|
-
const categorize_1 = require("./categorize");
|
|
5
4
|
const transform_1 = require("@pgsql/transform");
|
|
5
|
+
const categorize_1 = require("./categorize");
|
|
6
6
|
function objectKey(schema, name) {
|
|
7
7
|
return `${schema ?? ''}\u0000${name}`;
|
|
8
8
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
export * from '@pgsql/transform';
|
|
2
|
-
export { loadModule } from 'plpgsql-parser';
|
|
3
1
|
export type { BundleScriptContext, ExtensionRoutingInput, NamespaceValidatorOptions, SchemaObjectRoute, SchemaTranspiler, SchemaTranspilerOptions, } from './bundle-driver';
|
|
4
2
|
export { buildExtensionRouter, buildSchemaRouter, makeNamespaceValidator, makeSchemaTranspiler, } from './bundle-driver';
|
|
5
|
-
export type {
|
|
6
|
-
export {
|
|
7
|
-
export type { DiffInputChange, ObjectDelta, SemanticDeltaChange, SemanticDiffOptions, SemanticDiffResult, SemanticObjectDiff, } from './semantic-diff-driver';
|
|
8
|
-
export { diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
9
|
-
export type { PartitionConfig, PartitionedChange, PartitionedPackage, PartitionInputChange, PartitionRule, PartitionUnit, PartitionUnitsResult, UnitSelector, } from './partition-driver';
|
|
10
|
-
export { PartitionCycleError, partitionUnits, RESIDUAL_UNIT_PATH, } from './partition-driver';
|
|
3
|
+
export type { CatalogQueryable, CatalogSnapshot } from './catalog-check';
|
|
4
|
+
export { diffCatalogSnapshots, snapshotCatalog, withoutColumnOrder } from './catalog-check';
|
|
11
5
|
export type { CategoryProfile, ChangeCategory, } from './categorize';
|
|
12
6
|
export { buildCategoryOf, categorizeChange, TIER_PROFILE, } from './categorize';
|
|
13
7
|
export type { ClosureChange, ClosureInputChange, ClosureReason, FixtureClosure, ResolveFixtureClosureOptions, } from './fixture-closure';
|
|
14
8
|
export { resolveFixtureClosure } from './fixture-closure';
|
|
15
|
-
export type {
|
|
16
|
-
export {
|
|
9
|
+
export type { GranularityChange, RestructuredChange, RestructureModuleOptions, RestructureModuleResult, } from './granularity-driver';
|
|
10
|
+
export { defaultChangeName, restructureChanges, } from './granularity-driver';
|
|
11
|
+
export type { PgpmModuleModel } from './module-emit';
|
|
12
|
+
export { checkOverwrite, writeControlFile, writeModule } from './module-emit';
|
|
13
|
+
export type { ModuleSource, ModuleSourceChange } from './module-source';
|
|
14
|
+
export { loadModuleSource, stripTransactionWrapper } from './module-source';
|
|
15
|
+
export type { PartitionedPackageRows, PartitionExportRowsResult } from './partition';
|
|
16
|
+
export { parsePartitionConfig, partitionExportRows } from './partition';
|
|
17
|
+
export type { PartitionConfig, PartitionedChange, PartitionedPackage, PartitionInputChange, PartitionRule, PartitionUnit, PartitionUnitsResult, UnitSelector, } from './partition-driver';
|
|
18
|
+
export { PartitionCycleError, partitionUnits, RESIDUAL_UNIT_PATH, } from './partition-driver';
|
|
17
19
|
export type { SqlProgram, SqlStatementAst, SqlStatementSpan, } from './program';
|
|
18
20
|
export { emitSqlProgram, parseSqlProgram } from './program';
|
|
21
|
+
export type { GeneratedScript, RegeneratedScripts, } from './regen';
|
|
22
|
+
export { classifyScript, isStubScript, regenerateScripts, } from './regen';
|
|
23
|
+
export type { ExportGranularity, RestructureExportRowsOptions, RestructureExportRowsResult } from './restructure';
|
|
24
|
+
export { EXPORT_GRANULARITIES, isExportGranularity, restructureExportRows } from './restructure';
|
|
25
|
+
export type { DiffInputChange, ObjectDelta, SemanticDeltaChange, SemanticDiffOptions, SemanticDiffResult, SemanticObjectDiff, } from './semantic-diff-driver';
|
|
26
|
+
export { diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
27
|
+
export * from '@pgsql/transform';
|
|
28
|
+
export { loadModule } from 'plpgsql-parser';
|
package/index.js
CHANGED
|
@@ -14,35 +14,53 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
__exportStar(require("@pgsql/transform"), exports);
|
|
19
|
-
var plpgsql_parser_1 = require("plpgsql-parser");
|
|
20
|
-
Object.defineProperty(exports, "loadModule", { enumerable: true, get: function () { return plpgsql_parser_1.loadModule; } });
|
|
17
|
+
exports.loadModule = exports.diffSchemas = exports.diffChangeSets = exports.restructureExportRows = exports.isExportGranularity = exports.EXPORT_GRANULARITIES = exports.regenerateScripts = exports.isStubScript = exports.classifyScript = exports.parseSqlProgram = exports.emitSqlProgram = exports.RESIDUAL_UNIT_PATH = exports.partitionUnits = exports.PartitionCycleError = exports.partitionExportRows = exports.parsePartitionConfig = exports.stripTransactionWrapper = exports.loadModuleSource = exports.writeModule = exports.writeControlFile = exports.checkOverwrite = exports.restructureChanges = exports.defaultChangeName = exports.resolveFixtureClosure = exports.TIER_PROFILE = exports.categorizeChange = exports.buildCategoryOf = exports.withoutColumnOrder = exports.snapshotCatalog = exports.diffCatalogSnapshots = exports.makeSchemaTranspiler = exports.makeNamespaceValidator = exports.buildSchemaRouter = exports.buildExtensionRouter = void 0;
|
|
21
18
|
var bundle_driver_1 = require("./bundle-driver");
|
|
22
19
|
Object.defineProperty(exports, "buildExtensionRouter", { enumerable: true, get: function () { return bundle_driver_1.buildExtensionRouter; } });
|
|
23
20
|
Object.defineProperty(exports, "buildSchemaRouter", { enumerable: true, get: function () { return bundle_driver_1.buildSchemaRouter; } });
|
|
24
21
|
Object.defineProperty(exports, "makeNamespaceValidator", { enumerable: true, get: function () { return bundle_driver_1.makeNamespaceValidator; } });
|
|
25
22
|
Object.defineProperty(exports, "makeSchemaTranspiler", { enumerable: true, get: function () { return bundle_driver_1.makeSchemaTranspiler; } });
|
|
23
|
+
var catalog_check_1 = require("./catalog-check");
|
|
24
|
+
Object.defineProperty(exports, "diffCatalogSnapshots", { enumerable: true, get: function () { return catalog_check_1.diffCatalogSnapshots; } });
|
|
25
|
+
Object.defineProperty(exports, "snapshotCatalog", { enumerable: true, get: function () { return catalog_check_1.snapshotCatalog; } });
|
|
26
|
+
Object.defineProperty(exports, "withoutColumnOrder", { enumerable: true, get: function () { return catalog_check_1.withoutColumnOrder; } });
|
|
27
|
+
var categorize_1 = require("./categorize");
|
|
28
|
+
Object.defineProperty(exports, "buildCategoryOf", { enumerable: true, get: function () { return categorize_1.buildCategoryOf; } });
|
|
29
|
+
Object.defineProperty(exports, "categorizeChange", { enumerable: true, get: function () { return categorize_1.categorizeChange; } });
|
|
30
|
+
Object.defineProperty(exports, "TIER_PROFILE", { enumerable: true, get: function () { return categorize_1.TIER_PROFILE; } });
|
|
31
|
+
var fixture_closure_1 = require("./fixture-closure");
|
|
32
|
+
Object.defineProperty(exports, "resolveFixtureClosure", { enumerable: true, get: function () { return fixture_closure_1.resolveFixtureClosure; } });
|
|
26
33
|
var granularity_driver_1 = require("./granularity-driver");
|
|
27
34
|
Object.defineProperty(exports, "defaultChangeName", { enumerable: true, get: function () { return granularity_driver_1.defaultChangeName; } });
|
|
28
35
|
Object.defineProperty(exports, "restructureChanges", { enumerable: true, get: function () { return granularity_driver_1.restructureChanges; } });
|
|
29
|
-
var
|
|
30
|
-
Object.defineProperty(exports, "
|
|
31
|
-
Object.defineProperty(exports, "
|
|
36
|
+
var module_emit_1 = require("./module-emit");
|
|
37
|
+
Object.defineProperty(exports, "checkOverwrite", { enumerable: true, get: function () { return module_emit_1.checkOverwrite; } });
|
|
38
|
+
Object.defineProperty(exports, "writeControlFile", { enumerable: true, get: function () { return module_emit_1.writeControlFile; } });
|
|
39
|
+
Object.defineProperty(exports, "writeModule", { enumerable: true, get: function () { return module_emit_1.writeModule; } });
|
|
40
|
+
var module_source_1 = require("./module-source");
|
|
41
|
+
Object.defineProperty(exports, "loadModuleSource", { enumerable: true, get: function () { return module_source_1.loadModuleSource; } });
|
|
42
|
+
Object.defineProperty(exports, "stripTransactionWrapper", { enumerable: true, get: function () { return module_source_1.stripTransactionWrapper; } });
|
|
43
|
+
var partition_1 = require("./partition");
|
|
44
|
+
Object.defineProperty(exports, "parsePartitionConfig", { enumerable: true, get: function () { return partition_1.parsePartitionConfig; } });
|
|
45
|
+
Object.defineProperty(exports, "partitionExportRows", { enumerable: true, get: function () { return partition_1.partitionExportRows; } });
|
|
32
46
|
var partition_driver_1 = require("./partition-driver");
|
|
33
47
|
Object.defineProperty(exports, "PartitionCycleError", { enumerable: true, get: function () { return partition_driver_1.PartitionCycleError; } });
|
|
34
48
|
Object.defineProperty(exports, "partitionUnits", { enumerable: true, get: function () { return partition_driver_1.partitionUnits; } });
|
|
35
49
|
Object.defineProperty(exports, "RESIDUAL_UNIT_PATH", { enumerable: true, get: function () { return partition_driver_1.RESIDUAL_UNIT_PATH; } });
|
|
36
|
-
var
|
|
37
|
-
Object.defineProperty(exports, "
|
|
38
|
-
Object.defineProperty(exports, "
|
|
39
|
-
Object.defineProperty(exports, "TIER_PROFILE", { enumerable: true, get: function () { return categorize_1.TIER_PROFILE; } });
|
|
40
|
-
var fixture_closure_1 = require("./fixture-closure");
|
|
41
|
-
Object.defineProperty(exports, "resolveFixtureClosure", { enumerable: true, get: function () { return fixture_closure_1.resolveFixtureClosure; } });
|
|
50
|
+
var program_1 = require("./program");
|
|
51
|
+
Object.defineProperty(exports, "emitSqlProgram", { enumerable: true, get: function () { return program_1.emitSqlProgram; } });
|
|
52
|
+
Object.defineProperty(exports, "parseSqlProgram", { enumerable: true, get: function () { return program_1.parseSqlProgram; } });
|
|
42
53
|
var regen_1 = require("./regen");
|
|
43
54
|
Object.defineProperty(exports, "classifyScript", { enumerable: true, get: function () { return regen_1.classifyScript; } });
|
|
44
55
|
Object.defineProperty(exports, "isStubScript", { enumerable: true, get: function () { return regen_1.isStubScript; } });
|
|
45
56
|
Object.defineProperty(exports, "regenerateScripts", { enumerable: true, get: function () { return regen_1.regenerateScripts; } });
|
|
46
|
-
var
|
|
47
|
-
Object.defineProperty(exports, "
|
|
48
|
-
Object.defineProperty(exports, "
|
|
57
|
+
var restructure_1 = require("./restructure");
|
|
58
|
+
Object.defineProperty(exports, "EXPORT_GRANULARITIES", { enumerable: true, get: function () { return restructure_1.EXPORT_GRANULARITIES; } });
|
|
59
|
+
Object.defineProperty(exports, "isExportGranularity", { enumerable: true, get: function () { return restructure_1.isExportGranularity; } });
|
|
60
|
+
Object.defineProperty(exports, "restructureExportRows", { enumerable: true, get: function () { return restructure_1.restructureExportRows; } });
|
|
61
|
+
var semantic_diff_driver_1 = require("./semantic-diff-driver");
|
|
62
|
+
Object.defineProperty(exports, "diffChangeSets", { enumerable: true, get: function () { return semantic_diff_driver_1.diffChangeSets; } });
|
|
63
|
+
Object.defineProperty(exports, "diffSchemas", { enumerable: true, get: function () { return semantic_diff_driver_1.diffSchemas; } });
|
|
64
|
+
__exportStar(require("@pgsql/transform"), exports);
|
|
65
|
+
var plpgsql_parser_1 = require("plpgsql-parser");
|
|
66
|
+
Object.defineProperty(exports, "loadModule", { enumerable: true, get: function () { return plpgsql_parser_1.loadModule; } });
|
package/module-emit.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module emission — the single seam every dials command renders through.
|
|
3
|
+
*
|
|
4
|
+
* The engine (semantic diff, granularity, partition) produces plan-ordered
|
|
5
|
+
* {@link PgpmRow}s; a {@link PgpmModuleModel} is just those rows plus the
|
|
6
|
+
* module's name and control-file requires. `writeModule` is the one writer
|
|
7
|
+
* that turns a model into an on-disk pgpm module (`.control` + `pgpm.plan` +
|
|
8
|
+
* deploy/revert/verify trees) — so `pgpm diff`, `pgpm transform`, and
|
|
9
|
+
* `pgpm import` all emit byte-identical module layouts instead of each
|
|
10
|
+
* carrying its own copy of the writer.
|
|
11
|
+
*/
|
|
12
|
+
import { PgpmRow } from '@pgpmjs/ast';
|
|
13
|
+
/**
|
|
14
|
+
* A pgpm module as an in-memory value: a name, the extensions it requires
|
|
15
|
+
* (control-file names), and its changes in plan order. `writeModule` and the
|
|
16
|
+
* other projections (linear SQL, bundle) are pure functions of this model.
|
|
17
|
+
*/
|
|
18
|
+
export interface PgpmModuleModel {
|
|
19
|
+
/** Package (module) name. */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Module names this package requires (control-file names). */
|
|
22
|
+
requires: string[];
|
|
23
|
+
/** Changes in plan order. */
|
|
24
|
+
rows: PgpmRow[];
|
|
25
|
+
}
|
|
26
|
+
/** Write a module's Postgres extension `.control` file. */
|
|
27
|
+
export declare const writeControlFile: (dir: string, name: string, requires: string[]) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Write a {@link PgpmModuleModel} to `<outBase>/<name>` as a full pgpm module:
|
|
30
|
+
* a `.control` file, `pgpm.plan`, and deploy/revert/verify script trees.
|
|
31
|
+
* The deploy/revert/verify directories are cleared first so a re-emit never
|
|
32
|
+
* leaves stale scripts behind. Returns the module directory.
|
|
33
|
+
*/
|
|
34
|
+
export declare const writeModule: (outBase: string, model: PgpmModuleModel, extraRequires?: string[]) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Guard against clobbering: writing into the source directory requires
|
|
37
|
+
* `--write`, as does overwriting any existing package directory.
|
|
38
|
+
*/
|
|
39
|
+
export declare const checkOverwrite: (targetDir: string, sourcePath: string, write: boolean) => string | null;
|
package/module-emit.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
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.checkOverwrite = exports.writeModule = exports.writeControlFile = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* Module emission — the single seam every dials command renders through.
|
|
39
|
+
*
|
|
40
|
+
* The engine (semantic diff, granularity, partition) produces plan-ordered
|
|
41
|
+
* {@link PgpmRow}s; a {@link PgpmModuleModel} is just those rows plus the
|
|
42
|
+
* module's name and control-file requires. `writeModule` is the one writer
|
|
43
|
+
* that turns a model into an on-disk pgpm module (`.control` + `pgpm.plan` +
|
|
44
|
+
* deploy/revert/verify trees) — so `pgpm diff`, `pgpm transform`, and
|
|
45
|
+
* `pgpm import` all emit byte-identical module layouts instead of each
|
|
46
|
+
* carrying its own copy of the writer.
|
|
47
|
+
*/
|
|
48
|
+
const ast_1 = require("@pgpmjs/ast");
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
/** Write a module's Postgres extension `.control` file. */
|
|
52
|
+
const writeControlFile = (dir, name, requires) => {
|
|
53
|
+
const lines = [
|
|
54
|
+
`# ${name} extension`,
|
|
55
|
+
`comment = '${name} extension'`,
|
|
56
|
+
`default_version = '0.0.1'`,
|
|
57
|
+
`relocatable = false`,
|
|
58
|
+
`superuser = false`
|
|
59
|
+
];
|
|
60
|
+
if (requires.length) {
|
|
61
|
+
lines.push(`requires = '${requires.join(',')}'`);
|
|
62
|
+
}
|
|
63
|
+
fs.writeFileSync(path.join(dir, `${name}.control`), lines.join('\n') + '\n');
|
|
64
|
+
};
|
|
65
|
+
exports.writeControlFile = writeControlFile;
|
|
66
|
+
/**
|
|
67
|
+
* Write a {@link PgpmModuleModel} to `<outBase>/<name>` as a full pgpm module:
|
|
68
|
+
* a `.control` file, `pgpm.plan`, and deploy/revert/verify script trees.
|
|
69
|
+
* The deploy/revert/verify directories are cleared first so a re-emit never
|
|
70
|
+
* leaves stale scripts behind. Returns the module directory.
|
|
71
|
+
*/
|
|
72
|
+
const writeModule = (outBase, model, extraRequires = []) => {
|
|
73
|
+
const dir = path.join(outBase, model.name);
|
|
74
|
+
fs.rmSync(path.join(dir, 'deploy'), { recursive: true, force: true });
|
|
75
|
+
fs.rmSync(path.join(dir, 'revert'), { recursive: true, force: true });
|
|
76
|
+
fs.rmSync(path.join(dir, 'verify'), { recursive: true, force: true });
|
|
77
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
78
|
+
(0, exports.writeControlFile)(dir, model.name, [...extraRequires, ...model.requires]);
|
|
79
|
+
const opts = {
|
|
80
|
+
outdir: outBase,
|
|
81
|
+
name: model.name,
|
|
82
|
+
replacer: (str) => str.split('constructive-extension-name').join(model.name)
|
|
83
|
+
};
|
|
84
|
+
(0, ast_1.writePgpmPlan)(model.rows, opts);
|
|
85
|
+
(0, ast_1.writePgpmFiles)(model.rows, opts);
|
|
86
|
+
return dir;
|
|
87
|
+
};
|
|
88
|
+
exports.writeModule = writeModule;
|
|
89
|
+
/**
|
|
90
|
+
* Guard against clobbering: writing into the source directory requires
|
|
91
|
+
* `--write`, as does overwriting any existing package directory.
|
|
92
|
+
*/
|
|
93
|
+
const checkOverwrite = (targetDir, sourcePath, write) => {
|
|
94
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
95
|
+
const resolvedSource = path.resolve(sourcePath);
|
|
96
|
+
if (resolvedTarget === resolvedSource && !write) {
|
|
97
|
+
return `Refusing to overwrite the source module at ${resolvedSource} (pass --write to allow).`;
|
|
98
|
+
}
|
|
99
|
+
if (resolvedTarget !== resolvedSource && fs.existsSync(resolvedTarget) && !write) {
|
|
100
|
+
return `Output directory ${resolvedTarget} already exists (pass --write to overwrite).`;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
exports.checkOverwrite = checkOverwrite;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** One change of a loaded module: headerless, unwrapped deploy SQL. */
|
|
2
|
+
export interface ModuleSourceChange {
|
|
3
|
+
/** Change name (plan token). */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Change names this change requires, as recorded in the plan. */
|
|
6
|
+
dependencies: string[];
|
|
7
|
+
/** Deploy SQL with the pgpm header and BEGIN/COMMIT wrapper stripped. */
|
|
8
|
+
deploy: string;
|
|
9
|
+
}
|
|
10
|
+
/** A pgpm module loaded from disk, flattened in plan order. */
|
|
11
|
+
export interface ModuleSource {
|
|
12
|
+
/** Module (project) name from pgpm.plan. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Absolute module directory. */
|
|
15
|
+
modulePath: string;
|
|
16
|
+
/** Changes in plan order. */
|
|
17
|
+
changes: ModuleSourceChange[];
|
|
18
|
+
/** Non-fatal notes (missing deploy scripts, etc.). */
|
|
19
|
+
warnings: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Strip the transaction wrapper some modules carry inside their scripts
|
|
23
|
+
* (standalone `BEGIN;` / `COMMIT;` / `ROLLBACK;` lines). The caller owns
|
|
24
|
+
* transaction control.
|
|
25
|
+
*/
|
|
26
|
+
export declare const stripTransactionWrapper: (sql: string) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Load a pgpm module from disk: parse pgpm.plan and read each change's
|
|
29
|
+
* deploy script, stripping pgpm headers and transaction wrappers so the
|
|
30
|
+
* result can be flattened straight into the dials pipeline
|
|
31
|
+
* (`restructureChanges` / `partitionUnits`).
|
|
32
|
+
*/
|
|
33
|
+
export declare const loadModuleSource: (moduleDir: string) => ModuleSource;
|
package/module-source.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
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.loadModuleSource = exports.stripTransactionWrapper = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* Module ingestion for the dials pipeline: load an on-disk pgpm module into
|
|
39
|
+
* the flattened, classified change list the transform/partition drivers
|
|
40
|
+
* consume. `pgpm transform` uses this today; a future `pgpm import` (dumps)
|
|
41
|
+
* and `pgpm diff` are expected to reuse the same seam.
|
|
42
|
+
*/
|
|
43
|
+
const ast_1 = require("@pgpmjs/ast");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const TX_LINE = /^\s*(BEGIN|COMMIT|ROLLBACK)\s*;\s*$/i;
|
|
46
|
+
/**
|
|
47
|
+
* Strip the transaction wrapper some modules carry inside their scripts
|
|
48
|
+
* (standalone `BEGIN;` / `COMMIT;` / `ROLLBACK;` lines). The caller owns
|
|
49
|
+
* transaction control.
|
|
50
|
+
*/
|
|
51
|
+
const stripTransactionWrapper = (sql) => sql
|
|
52
|
+
.split('\n')
|
|
53
|
+
.filter(line => !TX_LINE.test(line))
|
|
54
|
+
.join('\n')
|
|
55
|
+
.trim();
|
|
56
|
+
exports.stripTransactionWrapper = stripTransactionWrapper;
|
|
57
|
+
/**
|
|
58
|
+
* Load a pgpm module from disk: parse pgpm.plan and read each change's
|
|
59
|
+
* deploy script, stripping pgpm headers and transaction wrappers so the
|
|
60
|
+
* result can be flattened straight into the dials pipeline
|
|
61
|
+
* (`restructureChanges` / `partitionUnits`).
|
|
62
|
+
*/
|
|
63
|
+
const loadModuleSource = (moduleDir) => {
|
|
64
|
+
const modulePath = path.resolve(moduleDir);
|
|
65
|
+
const planResult = (0, ast_1.parsePlanFile)(path.join(modulePath, 'pgpm.plan'));
|
|
66
|
+
if (!planResult.data) {
|
|
67
|
+
const reasons = planResult.errors.map(e => e.message).join(', ');
|
|
68
|
+
throw new Error(`Failed to parse pgpm.plan in ${modulePath}: ${reasons}`);
|
|
69
|
+
}
|
|
70
|
+
const warnings = [];
|
|
71
|
+
const changes = [];
|
|
72
|
+
for (const change of planResult.data.changes) {
|
|
73
|
+
const raw = (0, ast_1.readScript)(modulePath, 'deploy', change.name);
|
|
74
|
+
if (!raw) {
|
|
75
|
+
warnings.push(`${change.name}: no deploy script found, skipping`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const { body } = (0, ast_1.parsePgpmHeader)(raw);
|
|
79
|
+
changes.push({
|
|
80
|
+
name: change.name,
|
|
81
|
+
dependencies: change.dependencies ?? [],
|
|
82
|
+
deploy: (0, exports.stripTransactionWrapper)(body)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name: planResult.data.package,
|
|
87
|
+
modulePath,
|
|
88
|
+
changes,
|
|
89
|
+
warnings
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
exports.loadModuleSource = loadModuleSource;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/transform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Change-aware SQL transformation for PGPM — superset of @pgsql/transform plus change categorization, fixture closure, and bundle transpile/apply drivers",
|
|
6
6
|
"main": "index.js",
|
|
@@ -39,14 +39,15 @@
|
|
|
39
39
|
"plpgsql"
|
|
40
40
|
],
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@pgpmjs/bundle": "^0.13.
|
|
42
|
+
"@pgpmjs/bundle": "^0.13.3",
|
|
43
43
|
"makage": "^0.3.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
+
"@pgpmjs/ast": "^0.10.3",
|
|
46
47
|
"@pgpmjs/naming-spec": "^0.1.2",
|
|
47
|
-
"@pgsql/scripts": "^18.1
|
|
48
|
-
"@pgsql/transform": "^18.
|
|
49
|
-
"plpgsql-parser": "^18.
|
|
48
|
+
"@pgsql/scripts": "^18.3.1",
|
|
49
|
+
"@pgsql/transform": "^18.14.1",
|
|
50
|
+
"plpgsql-parser": "^18.5.1"
|
|
50
51
|
},
|
|
51
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "c20b6be96b39bf71c0e2df0f4b51e05deacd006b"
|
|
52
53
|
}
|
package/partition.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Partition dial wiring shared by `pgpm transform --partition` and
|
|
3
|
+
* `pgpm export --partition`: parse a partition config file, run
|
|
4
|
+
* `partitionUnits` (`@pgpmjs/transform`), and lift the emitted packages back
|
|
5
|
+
* onto the PgpmRow seam with generated revert/verify per change.
|
|
6
|
+
*/
|
|
7
|
+
import { PgpmRow } from '@pgpmjs/ast';
|
|
8
|
+
import { PartitionConfig } from './partition-driver';
|
|
9
|
+
export type { PartitionConfig } from './partition-driver';
|
|
10
|
+
/**
|
|
11
|
+
* Parse and validate a partition config file (JSON matching
|
|
12
|
+
* `PartitionConfig`: rules, defaultPackage, optional style/splitRiders).
|
|
13
|
+
* Throws with a message naming the offending field.
|
|
14
|
+
*/
|
|
15
|
+
export declare const parsePartitionConfig: (filePath: string) => PartitionConfig;
|
|
16
|
+
/** One partitioned package on the PgpmRow seam, ready to write to disk. */
|
|
17
|
+
export interface PartitionedPackageRows {
|
|
18
|
+
name: string;
|
|
19
|
+
/** Package names this package requires (cross-package edges). */
|
|
20
|
+
requires: string[];
|
|
21
|
+
rows: PgpmRow[];
|
|
22
|
+
}
|
|
23
|
+
export interface PartitionExportRowsResult {
|
|
24
|
+
packages: PartitionedPackageRows[];
|
|
25
|
+
warnings: string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Partition changes into packages and generate revert/verify for every
|
|
29
|
+
* emitted change. Cross-package dependencies keep the `<pkg>:<path>`
|
|
30
|
+
* convention in `deps`. May throw `PartitionCycleError`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const partitionExportRows: (rows: PgpmRow[], config: PartitionConfig) => Promise<PartitionExportRowsResult>;
|