@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.
@@ -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: drops are
20
- * `@pgsql/scripts`' `revertFor` inverses of the object's own deploy
21
- * statements (never hand-templated), reverts recreate what was dropped, and
22
- * verifies come from `verifyFor`.
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
- for (const [print, extra] of to.extras) {
211
- if (!from.extras.has(print)) {
212
- const at = extra.AlterTableCmd;
213
- if (at)
214
- stmts.push(alter(at));
215
- else if (extra.Constraint) {
216
- stmts.push(alter({ subtype: 'AT_AddConstraint', def: extra }));
217
- }
218
- else {
219
- warnings.push(`${diff.path}: unrecognized added table element; emit a manual ALTER`);
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
- /** `@pgsql/scripts` inverse of a deploy script (drops in reverse topo order). */
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 { sql, warnings: w } = revertFor(classifyStatements(deploy));
258
- warnings.push(...w.map(x => `${context}: ${x}`));
259
- return sql;
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
- /** `@pgsql/scripts` existence checks for a deploy script. */
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 { sql, warnings: w } = verifyFor(classifyStatements(deploy));
264
- warnings.push(...w.map(x => `${context}: ${x}`));
265
- return sql;
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);
@@ -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.
@@ -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 { GranularityChange, RestructuredChange, RestructureModuleOptions, RestructureModuleResult, } from './granularity-driver';
6
- export { defaultChangeName, restructureChanges, } from './granularity-driver';
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 { GeneratedScript, RegeneratedScripts, } from './regen';
16
- export { classifyScript, isStubScript, regenerateScripts, } from './regen';
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.parseSqlProgram = exports.emitSqlProgram = exports.regenerateScripts = exports.isStubScript = exports.classifyScript = exports.resolveFixtureClosure = exports.TIER_PROFILE = exports.categorizeChange = exports.buildCategoryOf = exports.RESIDUAL_UNIT_PATH = exports.partitionUnits = exports.PartitionCycleError = exports.diffSchemas = exports.diffChangeSets = exports.restructureChanges = exports.defaultChangeName = exports.makeSchemaTranspiler = exports.makeNamespaceValidator = exports.buildSchemaRouter = exports.buildExtensionRouter = exports.loadModule = void 0;
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 semantic_diff_driver_1 = require("./semantic-diff-driver");
30
- Object.defineProperty(exports, "diffChangeSets", { enumerable: true, get: function () { return semantic_diff_driver_1.diffChangeSets; } });
31
- Object.defineProperty(exports, "diffSchemas", { enumerable: true, get: function () { return semantic_diff_driver_1.diffSchemas; } });
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 categorize_1 = require("./categorize");
37
- Object.defineProperty(exports, "buildCategoryOf", { enumerable: true, get: function () { return categorize_1.buildCategoryOf; } });
38
- Object.defineProperty(exports, "categorizeChange", { enumerable: true, get: function () { return categorize_1.categorizeChange; } });
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 program_1 = require("./program");
47
- Object.defineProperty(exports, "emitSqlProgram", { enumerable: true, get: function () { return program_1.emitSqlProgram; } });
48
- Object.defineProperty(exports, "parseSqlProgram", { enumerable: true, get: function () { return program_1.parseSqlProgram; } });
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; } });
@@ -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;
@@ -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.17.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.2",
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.0",
48
- "@pgsql/transform": "^18.11.0",
49
- "plpgsql-parser": "^18.2.3"
48
+ "@pgsql/scripts": "^18.3.1",
49
+ "@pgsql/transform": "^18.14.1",
50
+ "plpgsql-parser": "^18.5.1"
50
51
  },
51
- "gitHead": "45cd961cf956d2d28e4d09b20db4204982c33593"
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>;