@pgpmjs/transform 0.20.0 → 0.22.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.js +11 -11
- package/categorize.d.ts +1 -1
- package/categorize.js +2 -2
- package/esm/bundle-driver.js +1 -1
- package/esm/categorize.js +1 -1
- package/esm/fixture-closure.js +1 -1
- package/esm/granularity-driver.js +2 -1
- package/esm/index.js +1 -1
- package/esm/module-source.js +29 -9
- package/esm/partition-driver.js +3 -2
- package/esm/program.js +1 -1
- package/esm/regen.js +1 -1
- package/esm/semantic-diff-driver.js +37 -1
- package/fixture-closure.d.ts +1 -1
- package/fixture-closure.js +2 -2
- package/granularity-driver.d.ts +2 -1
- package/granularity-driver.js +2 -1
- package/index.d.ts +2 -2
- package/index.js +2 -1
- package/module-source.js +28 -8
- package/package.json +9 -8
- package/partition-driver.d.ts +3 -2
- package/partition-driver.js +3 -2
- package/program.d.ts +1 -1
- package/program.js +2 -2
- package/regen.d.ts +1 -1
- package/regen.js +2 -2
- package/semantic-diff-driver.d.ts +35 -0
- package/semantic-diff-driver.js +40 -3
- package/sub-object.d.ts +1 -1
package/bundle-driver.js
CHANGED
|
@@ -13,8 +13,8 @@ exports.buildExtensionRouter = buildExtensionRouter;
|
|
|
13
13
|
exports.buildSchemaRouter = buildSchemaRouter;
|
|
14
14
|
exports.makeSchemaTranspiler = makeSchemaTranspiler;
|
|
15
15
|
exports.makeNamespaceValidator = makeNamespaceValidator;
|
|
16
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
16
17
|
const transform_1 = require("@pgsql/transform");
|
|
17
|
-
const transform_2 = require("@pgsql/transform");
|
|
18
18
|
/**
|
|
19
19
|
* Build an {@link ExtensionRouter} from the declarative {@link ExtensionRoutingInput}:
|
|
20
20
|
* an explicit `routes` spec when given, otherwise the "move everything matched
|
|
@@ -27,8 +27,8 @@ function buildExtensionRouter(input) {
|
|
|
27
27
|
if (input.inventory)
|
|
28
28
|
opts.inventory = input.inventory;
|
|
29
29
|
if (input.routes)
|
|
30
|
-
return new
|
|
31
|
-
return
|
|
30
|
+
return new transform_1.ExtensionRouter(input.routes, opts);
|
|
31
|
+
return transform_1.ExtensionRouter.toSchema(input.toSchema ?? null, {
|
|
32
32
|
...opts,
|
|
33
33
|
extensions: input.only,
|
|
34
34
|
from: input.from
|
|
@@ -75,7 +75,7 @@ function buildSchemaRouter(options) {
|
|
|
75
75
|
...(route.toName !== undefined ? { name: route.toName } : {})
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
return new
|
|
78
|
+
return new transform_1.SchemaRouter(spec);
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Rewrite the schema segment of a pgpm change path to the object's routed
|
|
@@ -125,14 +125,14 @@ function makeSchemaTranspiler(options) {
|
|
|
125
125
|
// after schema routing on each script. Each is a self-contained parse →
|
|
126
126
|
// rewrite → deparse pass, so they compose by threading the SQL through.
|
|
127
127
|
const extRouter = options.extensions ? buildExtensionRouter(options.extensions) : undefined;
|
|
128
|
-
const roleRouter = options.roles ?
|
|
129
|
-
const extensionResult = extRouter ? (0,
|
|
130
|
-
const roleResult = roleRouter ? (0,
|
|
128
|
+
const roleRouter = options.roles ? transform_1.RoleRouter.from(options.roles) : undefined;
|
|
129
|
+
const extensionResult = extRouter ? (0, transform_1.createExtensionResult)() : undefined;
|
|
130
|
+
const roleResult = roleRouter ? (0, transform_1.createRoleResult)() : undefined;
|
|
131
131
|
const renameChange = (name) => renameChangePath(name, router);
|
|
132
132
|
const transformScript = (sql, _ctx) => {
|
|
133
|
-
let out = (0,
|
|
133
|
+
let out = (0, transform_1.transformSql)(sql, router, options.transform, result).content;
|
|
134
134
|
if (extRouter) {
|
|
135
|
-
const r = (0,
|
|
135
|
+
const r = (0, transform_1.transformExtensions)(out, extRouter);
|
|
136
136
|
out = r.sql;
|
|
137
137
|
for (const [name, schema] of r.result.installsMoved) {
|
|
138
138
|
extensionResult.installsMoved.set(name, schema);
|
|
@@ -142,7 +142,7 @@ function makeSchemaTranspiler(options) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
if (roleRouter) {
|
|
145
|
-
const r = (0,
|
|
145
|
+
const r = (0, transform_1.transformRoles)(out, roleRouter);
|
|
146
146
|
out = r.sql;
|
|
147
147
|
for (const [name, count] of r.result.rolesRenamed) {
|
|
148
148
|
roleResult.rolesRenamed.set(name, (roleResult.rolesRenamed.get(name) ?? 0) + count);
|
|
@@ -162,7 +162,7 @@ function makeNamespaceValidator(options) {
|
|
|
162
162
|
const allowed = new Set(options.allowedSchemas);
|
|
163
163
|
return (sql, _ctx) => {
|
|
164
164
|
const violations = new Set();
|
|
165
|
-
for (const facts of (0,
|
|
165
|
+
for (const facts of (0, semantics_1.classifyStatements)(sql)) {
|
|
166
166
|
for (const ref of [...facts.creates, ...facts.references, ...facts.fkTargets]) {
|
|
167
167
|
if (ref.schema && !allowed.has(ref.schema)) {
|
|
168
168
|
violations.add(`${facts.nodeTag}: ${ref.schema}.${ref.name}`);
|
package/categorize.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StatementFacts } from '@pgsql/
|
|
1
|
+
import { StatementFacts } from '@pgsql/semantics';
|
|
2
2
|
/**
|
|
3
3
|
* A category is a chunk-seam key a change is assigned to. Free-form string; the
|
|
4
4
|
* built-in {@link TIER_PROFILE} uses `'security' | 'functionality' | 'fixtures'
|
package/categorize.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TIER_PROFILE = void 0;
|
|
4
4
|
exports.categorizeChange = categorizeChange;
|
|
5
5
|
exports.buildCategoryOf = buildCategoryOf;
|
|
6
|
-
const
|
|
6
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
7
7
|
/**
|
|
8
8
|
* Default tier profile: isolates the security surface and procedural code out
|
|
9
9
|
* of the schema base. Precedence, highest first — a change is placed in the
|
|
@@ -37,7 +37,7 @@ exports.TIER_PROFILE = {
|
|
|
37
37
|
* (defaults to {@link TIER_PROFILE}).
|
|
38
38
|
*/
|
|
39
39
|
function categorizeChange(sql, changeName, profile = exports.TIER_PROFILE) {
|
|
40
|
-
return profile.categorize((0,
|
|
40
|
+
return profile.categorize((0, semantics_1.classifyStatements)(sql), changeName);
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Build a `categoryOf(changeName)` seam function from a map of change name to
|
package/esm/bundle-driver.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* purpose — no dependency on `@pgpmjs/bundle`/`@pgpmjs/core` — so the driver
|
|
8
8
|
* stays a pure function factory over `transformSql` and `classifyStatements`.
|
|
9
9
|
*/
|
|
10
|
-
import { classifyStatements } from '@pgsql/
|
|
10
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
11
11
|
import { createExtensionResult, createRoleResult, ExtensionRouter, RoleRouter, SchemaRouter, transformExtensions, transformRoles, transformSql } from '@pgsql/transform';
|
|
12
12
|
/**
|
|
13
13
|
* Build an {@link ExtensionRouter} from the declarative {@link ExtensionRoutingInput}:
|
package/esm/categorize.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifyStatements } from '@pgsql/
|
|
1
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
2
2
|
/**
|
|
3
3
|
* Default tier profile: isolates the security surface and procedural code out
|
|
4
4
|
* of the schema base. Precedence, highest first — a change is placed in the
|
package/esm/fixture-closure.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pathFor } from '@pgpmjs/naming-spec';
|
|
2
2
|
import { revertFor, verifyFor } from '@pgsql/scripts';
|
|
3
|
-
import {
|
|
3
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
4
|
+
import { buildStatementGraph, identityOf, restructureSql } from '@pgsql/transform';
|
|
4
5
|
import { nameUnnamedConstraints, subObjectIdentityOf } from './sub-object';
|
|
5
6
|
export const CHANGE_GRANULARITIES = ['alteration', 'object', 'single'];
|
|
6
7
|
export const isChangeGranularity = (value) => CHANGE_GRANULARITIES.includes(value);
|
package/esm/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { PartitionCycleError, partitionUnits, RESIDUAL_UNIT_PATH, } from './part
|
|
|
11
11
|
export { emitSqlProgram, parseSqlProgram } from './program';
|
|
12
12
|
export { classifyScript, isStubScript, regenerateScripts, } from './regen';
|
|
13
13
|
export { EXPORT_GRANULARITIES, isExportGranularity, restructureExportRows } from './restructure';
|
|
14
|
-
export { diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
14
|
+
export { coverChanges, diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
15
15
|
export { nameUnnamedConstraints, subObjectIdentityOf } from './sub-object';
|
|
16
16
|
export * from '@pgsql/transform';
|
|
17
17
|
export { loadModule } from 'plpgsql-parser';
|
package/esm/module-source.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Module ingestion for the dials pipeline: load an on-disk pgpm module into
|
|
3
|
-
* the flattened, classified change list the transform/partition drivers
|
|
4
|
-
* consume. `pgpm transform` uses this today; a future `pgpm import` (dumps)
|
|
5
|
-
* and `pgpm diff` are expected to reuse the same seam.
|
|
6
|
-
*/
|
|
7
|
-
import { parsePgpmHeader, parsePlanFile, readScript } from '@pgpmjs/ast';
|
|
1
|
+
import { parsePgpmHeader, parsePlanFile, readScript, resolveReference } from '@pgpmjs/ast';
|
|
8
2
|
import * as path from 'path';
|
|
9
3
|
const TX_LINE = /^\s*(BEGIN|COMMIT|ROLLBACK)\s*;\s*$/i;
|
|
10
4
|
/**
|
|
@@ -17,6 +11,31 @@ export const stripTransactionWrapper = (sql) => sql
|
|
|
17
11
|
.filter(line => !TX_LINE.test(line))
|
|
18
12
|
.join('\n')
|
|
19
13
|
.trim();
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a plan dependency token to a concrete change name.
|
|
16
|
+
*
|
|
17
|
+
* Non-tag tokens (a bare change name, or a `pkg:change` cross-package name)
|
|
18
|
+
* pass through unchanged — they are already the identity the ledger and
|
|
19
|
+
* generated plans record. A tag token (`@tag`) is resolved to its change via
|
|
20
|
+
* the plan that defines it, so a dependency written against a tag is not
|
|
21
|
+
* re-emitted verbatim into a generated plan or a ledger row. Cross-package
|
|
22
|
+
* tags (`pkg:@tag`) cannot be resolved from a single module's plan and are
|
|
23
|
+
* left as-is with a warning; the resolver returns them unchanged. Anything
|
|
24
|
+
* that fails to resolve is kept verbatim (never dropped) with a warning.
|
|
25
|
+
*/
|
|
26
|
+
const resolveDependencyToken = (token, plan, warnings, changeName) => {
|
|
27
|
+
if (!token.includes('@'))
|
|
28
|
+
return token;
|
|
29
|
+
const resolved = resolveReference(token, plan, plan.package);
|
|
30
|
+
if (resolved.error || !resolved.change) {
|
|
31
|
+
warnings.push(`${changeName}: could not resolve dependency tag "${token}" (${resolved.error ?? 'no matching change'}); left as-is`);
|
|
32
|
+
return token;
|
|
33
|
+
}
|
|
34
|
+
if (resolved.change === token) {
|
|
35
|
+
warnings.push(`${changeName}: dependency tag "${token}" is cross-package; left unresolved (resolve it in workspace context)`);
|
|
36
|
+
}
|
|
37
|
+
return resolved.change;
|
|
38
|
+
};
|
|
20
39
|
/**
|
|
21
40
|
* Load a pgpm module from disk: parse pgpm.plan and read each change's
|
|
22
41
|
* deploy script, stripping pgpm headers and transaction wrappers so the
|
|
@@ -30,9 +49,10 @@ export const loadModuleSource = (moduleDir) => {
|
|
|
30
49
|
const reasons = planResult.errors.map(e => e.message).join(', ');
|
|
31
50
|
throw new Error(`Failed to parse pgpm.plan in ${modulePath}: ${reasons}`);
|
|
32
51
|
}
|
|
52
|
+
const plan = planResult.data;
|
|
33
53
|
const warnings = [];
|
|
34
54
|
const changes = [];
|
|
35
|
-
for (const change of
|
|
55
|
+
for (const change of plan.changes) {
|
|
36
56
|
const raw = readScript(modulePath, 'deploy', change.name);
|
|
37
57
|
if (!raw) {
|
|
38
58
|
warnings.push(`${change.name}: no deploy script found, skipping`);
|
|
@@ -41,7 +61,7 @@ export const loadModuleSource = (moduleDir) => {
|
|
|
41
61
|
const { body } = parsePgpmHeader(raw);
|
|
42
62
|
changes.push({
|
|
43
63
|
name: change.name,
|
|
44
|
-
dependencies: change.dependencies ?? [],
|
|
64
|
+
dependencies: (change.dependencies ?? []).map(dep => resolveDependencyToken(dep, plan, warnings, change.name)),
|
|
45
65
|
deploy: stripTransactionWrapper(body)
|
|
46
66
|
});
|
|
47
67
|
}
|
package/esm/partition-driver.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Partition dial: project one deploy surface into a set of pgpm packages.
|
|
3
3
|
*
|
|
4
|
-
* The object graph (statements classified by `@pgsql/
|
|
4
|
+
* The object graph (statements classified by `@pgsql/semantics`, grouped
|
|
5
5
|
* into identity-keyed units) is the source of truth; which package a unit
|
|
6
6
|
* lives in is a *derived projection* of a declarative partition config.
|
|
7
7
|
* Units can be assigned by schema, by object kind, by explicit cherry-pick
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* pgpm.plan + deploy trees, feed a bundle, ...).
|
|
22
22
|
*/
|
|
23
23
|
import { pathFor } from '@pgpmjs/naming-spec';
|
|
24
|
-
import {
|
|
24
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
25
|
+
import { buildStatementGraph, identityOf } from '@pgsql/transform';
|
|
25
26
|
/** A partition whose packages require each other in a cycle — unshippable. */
|
|
26
27
|
export class PartitionCycleError extends Error {
|
|
27
28
|
packages;
|
package/esm/program.js
CHANGED
package/esm/regen.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { revertFor, verifyFor } from '@pgsql/scripts';
|
|
2
|
-
import { classifyStatements } from '@pgsql/
|
|
2
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
3
3
|
/**
|
|
4
4
|
* Classify a pgpm script's substantive statements: every top-level statement
|
|
5
5
|
* except transaction control (`BEGIN`/`COMMIT`/`ROLLBACK` wrappers).
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { pathFor } from '@pgpmjs/naming-spec';
|
|
30
30
|
import { existenceCheck, invertStatement, revertFor, verifyFor } from '@pgsql/scripts';
|
|
31
|
-
import {
|
|
31
|
+
import { classifyStatements } from '@pgsql/semantics';
|
|
32
|
+
import { buildStatementGraph, cleanTree, identityOf } from '@pgsql/transform';
|
|
32
33
|
import { Deparser, parseSync } from 'plpgsql-parser';
|
|
33
34
|
import { defaultConstraintName } from './constraint-names';
|
|
34
35
|
import { restructureChanges } from './granularity-driver';
|
|
@@ -501,3 +502,38 @@ export function diffChangeSets(from, to, options = {}) {
|
|
|
501
502
|
const flatten = (changes) => changes.map(c => c.deploy.trim()).filter(Boolean).join('\n\n');
|
|
502
503
|
return diffSchemas(flatten(from), flatten(to), options);
|
|
503
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Cover each `to`-side change against a semantic diff's per-object delta:
|
|
507
|
+
* which of the objects the change touches are already present and identical
|
|
508
|
+
* on the `from` side. A `satisfied` change deploys nothing new — its ledger
|
|
509
|
+
* entry can be backfilled instead of executing it; an `unsatisfied` change is
|
|
510
|
+
* the genuine delta. Identity keying is shared with the diff itself, so
|
|
511
|
+
* regrouped/renamed/reordered authorship of the same objects covers the same.
|
|
512
|
+
*
|
|
513
|
+
* `diff` must come from diffing the same `from` side against these changes
|
|
514
|
+
* (`diffChangeSets(fromChanges, toChanges)`).
|
|
515
|
+
*/
|
|
516
|
+
export function coverChanges(changes, diff, options = {}) {
|
|
517
|
+
const style = options.style ?? 'directory';
|
|
518
|
+
const deltaByKey = new Map();
|
|
519
|
+
for (const obj of diff.objects) {
|
|
520
|
+
deltaByKey.set(identityKey(obj.identity), obj.delta);
|
|
521
|
+
}
|
|
522
|
+
return changes.map(change => {
|
|
523
|
+
const sideWarnings = [];
|
|
524
|
+
const side = readSide(change.deploy, sideWarnings, change.name);
|
|
525
|
+
const objects = side.order.map(key => {
|
|
526
|
+
const unit = side.units.get(key);
|
|
527
|
+
return {
|
|
528
|
+
path: pathFor(unit.identity, { style }),
|
|
529
|
+
delta: deltaByKey.get(key) ?? 'unchanged'
|
|
530
|
+
};
|
|
531
|
+
});
|
|
532
|
+
const touched = objects.filter(o => o.delta !== 'unchanged').length;
|
|
533
|
+
const status = objects.length === 0 ? 'inert'
|
|
534
|
+
: touched === 0 ? 'satisfied'
|
|
535
|
+
: touched === objects.length ? 'unsatisfied'
|
|
536
|
+
: 'partial';
|
|
537
|
+
return { name: change.name, status, objects };
|
|
538
|
+
});
|
|
539
|
+
}
|
package/fixture-closure.d.ts
CHANGED
package/fixture-closure.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveFixtureClosure = resolveFixtureClosure;
|
|
4
|
-
const
|
|
4
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
5
5
|
const categorize_1 = require("./categorize");
|
|
6
6
|
function objectKey(schema, name) {
|
|
7
7
|
return `${schema ?? ''}\u0000${name}`;
|
|
8
8
|
}
|
|
9
9
|
function analyze(change, profile) {
|
|
10
|
-
const facts = (0,
|
|
10
|
+
const facts = (0, semantics_1.classifyStatements)(change.sql);
|
|
11
11
|
const category = profile.categorize(facts, change.name);
|
|
12
12
|
const creates = [];
|
|
13
13
|
const createsSchemas = [];
|
package/granularity-driver.d.ts
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* - `consolidated` — additionally inlines FKs proven safe by the graph.
|
|
19
19
|
*/
|
|
20
20
|
import type { ObjectIdentity as NamingIdentity } from '@pgpmjs/naming-spec';
|
|
21
|
-
import type {
|
|
21
|
+
import type { StatementFacts } from '@pgsql/semantics';
|
|
22
|
+
import type { Granularity } from '@pgsql/transform';
|
|
22
23
|
export type { Granularity } from '@pgsql/transform';
|
|
23
24
|
/**
|
|
24
25
|
* How statements are distributed across pgpm changes — orthogonal to
|
package/granularity-driver.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.defaultChangeName = defaultChangeName;
|
|
|
5
5
|
exports.restructureChanges = restructureChanges;
|
|
6
6
|
const naming_spec_1 = require("@pgpmjs/naming-spec");
|
|
7
7
|
const scripts_1 = require("@pgsql/scripts");
|
|
8
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
8
9
|
const transform_1 = require("@pgsql/transform");
|
|
9
10
|
const sub_object_1 = require("./sub-object");
|
|
10
11
|
exports.CHANGE_GRANULARITIES = ['alteration', 'object', 'single'];
|
|
@@ -57,7 +58,7 @@ function restructureChanges(changes, options) {
|
|
|
57
58
|
// Re-classify the emitted script; group statements by the object they
|
|
58
59
|
// target (creates[0]), so a table's CREATE and its remaining ALTERs land
|
|
59
60
|
// in the same change regardless of statement kind.
|
|
60
|
-
const facts = (0,
|
|
61
|
+
const facts = (0, semantics_1.classifyStatements)(sql);
|
|
61
62
|
if (options.changeGranularity === 'single') {
|
|
62
63
|
const name = options.singleChangeName ?? 'module/init';
|
|
63
64
|
const inputNames = new Set(changes.map(c => c.name));
|
package/index.d.ts
CHANGED
|
@@ -24,8 +24,8 @@ export type { GeneratedScript, RegeneratedScripts, } from './regen';
|
|
|
24
24
|
export { classifyScript, isStubScript, regenerateScripts, } from './regen';
|
|
25
25
|
export type { ExportGranularity, RestructureExportRowsOptions, RestructureExportRowsResult } from './restructure';
|
|
26
26
|
export { EXPORT_GRANULARITIES, isExportGranularity, restructureExportRows } from './restructure';
|
|
27
|
-
export type { DiffInputChange, ObjectDelta, SemanticDeltaChange, SemanticDiffOptions, SemanticDiffResult, SemanticObjectDiff, } from './semantic-diff-driver';
|
|
28
|
-
export { diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
27
|
+
export type { ChangeCoverage, ChangeObjectCoverage, ChangeObjectDelta, ChangeSatisfaction, DiffInputChange, ObjectDelta, SemanticDeltaChange, SemanticDiffOptions, SemanticDiffResult, SemanticObjectDiff, } from './semantic-diff-driver';
|
|
28
|
+
export { coverChanges, diffChangeSets, diffSchemas, } from './semantic-diff-driver';
|
|
29
29
|
export { nameUnnamedConstraints, subObjectIdentityOf } from './sub-object';
|
|
30
30
|
export * from '@pgsql/transform';
|
|
31
31
|
export { loadModule } from 'plpgsql-parser';
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@ 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.loadModule = exports.subObjectIdentityOf = exports.nameUnnamedConstraints = 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.appendModule = exports.restructureChanges = exports.isChangeGranularity = exports.defaultChangeName = exports.CHANGE_GRANULARITIES = exports.resolveFixtureClosure = exports.firstColumnRef = exports.defaultConstraintName = exports.TIER_PROFILE = exports.categorizeChange = exports.buildCategoryOf = exports.withoutColumnOrder = exports.snapshotCatalog = exports.diffCatalogSnapshots = exports.makeSchemaTranspiler = exports.makeNamespaceValidator = exports.buildSchemaRouter = exports.buildExtensionRouter = void 0;
|
|
17
|
+
exports.loadModule = exports.subObjectIdentityOf = exports.nameUnnamedConstraints = exports.diffSchemas = exports.diffChangeSets = exports.coverChanges = 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.appendModule = exports.restructureChanges = exports.isChangeGranularity = exports.defaultChangeName = exports.CHANGE_GRANULARITIES = exports.resolveFixtureClosure = exports.firstColumnRef = exports.defaultConstraintName = exports.TIER_PROFILE = exports.categorizeChange = exports.buildCategoryOf = exports.withoutColumnOrder = exports.snapshotCatalog = exports.diffCatalogSnapshots = exports.makeSchemaTranspiler = exports.makeNamespaceValidator = exports.buildSchemaRouter = exports.buildExtensionRouter = void 0;
|
|
18
18
|
var bundle_driver_1 = require("./bundle-driver");
|
|
19
19
|
Object.defineProperty(exports, "buildExtensionRouter", { enumerable: true, get: function () { return bundle_driver_1.buildExtensionRouter; } });
|
|
20
20
|
Object.defineProperty(exports, "buildSchemaRouter", { enumerable: true, get: function () { return bundle_driver_1.buildSchemaRouter; } });
|
|
@@ -65,6 +65,7 @@ Object.defineProperty(exports, "EXPORT_GRANULARITIES", { enumerable: true, get:
|
|
|
65
65
|
Object.defineProperty(exports, "isExportGranularity", { enumerable: true, get: function () { return restructure_1.isExportGranularity; } });
|
|
66
66
|
Object.defineProperty(exports, "restructureExportRows", { enumerable: true, get: function () { return restructure_1.restructureExportRows; } });
|
|
67
67
|
var semantic_diff_driver_1 = require("./semantic-diff-driver");
|
|
68
|
+
Object.defineProperty(exports, "coverChanges", { enumerable: true, get: function () { return semantic_diff_driver_1.coverChanges; } });
|
|
68
69
|
Object.defineProperty(exports, "diffChangeSets", { enumerable: true, get: function () { return semantic_diff_driver_1.diffChangeSets; } });
|
|
69
70
|
Object.defineProperty(exports, "diffSchemas", { enumerable: true, get: function () { return semantic_diff_driver_1.diffSchemas; } });
|
|
70
71
|
var sub_object_1 = require("./sub-object");
|
package/module-source.js
CHANGED
|
@@ -34,12 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
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
37
|
const ast_1 = require("@pgpmjs/ast");
|
|
44
38
|
const path = __importStar(require("path"));
|
|
45
39
|
const TX_LINE = /^\s*(BEGIN|COMMIT|ROLLBACK)\s*;\s*$/i;
|
|
@@ -54,6 +48,31 @@ const stripTransactionWrapper = (sql) => sql
|
|
|
54
48
|
.join('\n')
|
|
55
49
|
.trim();
|
|
56
50
|
exports.stripTransactionWrapper = stripTransactionWrapper;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a plan dependency token to a concrete change name.
|
|
53
|
+
*
|
|
54
|
+
* Non-tag tokens (a bare change name, or a `pkg:change` cross-package name)
|
|
55
|
+
* pass through unchanged — they are already the identity the ledger and
|
|
56
|
+
* generated plans record. A tag token (`@tag`) is resolved to its change via
|
|
57
|
+
* the plan that defines it, so a dependency written against a tag is not
|
|
58
|
+
* re-emitted verbatim into a generated plan or a ledger row. Cross-package
|
|
59
|
+
* tags (`pkg:@tag`) cannot be resolved from a single module's plan and are
|
|
60
|
+
* left as-is with a warning; the resolver returns them unchanged. Anything
|
|
61
|
+
* that fails to resolve is kept verbatim (never dropped) with a warning.
|
|
62
|
+
*/
|
|
63
|
+
const resolveDependencyToken = (token, plan, warnings, changeName) => {
|
|
64
|
+
if (!token.includes('@'))
|
|
65
|
+
return token;
|
|
66
|
+
const resolved = (0, ast_1.resolveReference)(token, plan, plan.package);
|
|
67
|
+
if (resolved.error || !resolved.change) {
|
|
68
|
+
warnings.push(`${changeName}: could not resolve dependency tag "${token}" (${resolved.error ?? 'no matching change'}); left as-is`);
|
|
69
|
+
return token;
|
|
70
|
+
}
|
|
71
|
+
if (resolved.change === token) {
|
|
72
|
+
warnings.push(`${changeName}: dependency tag "${token}" is cross-package; left unresolved (resolve it in workspace context)`);
|
|
73
|
+
}
|
|
74
|
+
return resolved.change;
|
|
75
|
+
};
|
|
57
76
|
/**
|
|
58
77
|
* Load a pgpm module from disk: parse pgpm.plan and read each change's
|
|
59
78
|
* deploy script, stripping pgpm headers and transaction wrappers so the
|
|
@@ -67,9 +86,10 @@ const loadModuleSource = (moduleDir) => {
|
|
|
67
86
|
const reasons = planResult.errors.map(e => e.message).join(', ');
|
|
68
87
|
throw new Error(`Failed to parse pgpm.plan in ${modulePath}: ${reasons}`);
|
|
69
88
|
}
|
|
89
|
+
const plan = planResult.data;
|
|
70
90
|
const warnings = [];
|
|
71
91
|
const changes = [];
|
|
72
|
-
for (const change of
|
|
92
|
+
for (const change of plan.changes) {
|
|
73
93
|
const raw = (0, ast_1.readScript)(modulePath, 'deploy', change.name);
|
|
74
94
|
if (!raw) {
|
|
75
95
|
warnings.push(`${change.name}: no deploy script found, skipping`);
|
|
@@ -78,7 +98,7 @@ const loadModuleSource = (moduleDir) => {
|
|
|
78
98
|
const { body } = (0, ast_1.parsePgpmHeader)(raw);
|
|
79
99
|
changes.push({
|
|
80
100
|
name: change.name,
|
|
81
|
-
dependencies: change.dependencies ?? [],
|
|
101
|
+
dependencies: (change.dependencies ?? []).map(dep => resolveDependencyToken(dep, plan, warnings, change.name)),
|
|
82
102
|
deploy: (0, exports.stripTransactionWrapper)(body)
|
|
83
103
|
});
|
|
84
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/transform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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,15 +39,16 @@
|
|
|
39
39
|
"plpgsql"
|
|
40
40
|
],
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@pgpmjs/bundle": "^0.
|
|
42
|
+
"@pgpmjs/bundle": "^0.14.1",
|
|
43
43
|
"makage": "^0.3.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@pgpmjs/ast": "^0.
|
|
47
|
-
"@pgpmjs/naming-spec": "^0.
|
|
48
|
-
"@pgsql/scripts": "^18.3.
|
|
49
|
-
"@pgsql/
|
|
50
|
-
"
|
|
46
|
+
"@pgpmjs/ast": "^0.11.1",
|
|
47
|
+
"@pgpmjs/naming-spec": "^0.3.0",
|
|
48
|
+
"@pgsql/scripts": "^18.3.4",
|
|
49
|
+
"@pgsql/semantics": "^18.1.2",
|
|
50
|
+
"@pgsql/transform": "^18.15.2",
|
|
51
|
+
"plpgsql-parser": "^18.5.3"
|
|
51
52
|
},
|
|
52
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "6da0705f8aa49d62dc2defb8b135b71674611483"
|
|
53
54
|
}
|
package/partition-driver.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Partition dial: project one deploy surface into a set of pgpm packages.
|
|
3
3
|
*
|
|
4
|
-
* The object graph (statements classified by `@pgsql/
|
|
4
|
+
* The object graph (statements classified by `@pgsql/semantics`, grouped
|
|
5
5
|
* into identity-keyed units) is the source of truth; which package a unit
|
|
6
6
|
* lives in is a *derived projection* of a declarative partition config.
|
|
7
7
|
* Units can be assigned by schema, by object kind, by explicit cherry-pick
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* pgpm.plan + deploy trees, feed a bundle, ...).
|
|
22
22
|
*/
|
|
23
23
|
import { PathStyle } from '@pgpmjs/naming-spec';
|
|
24
|
-
import {
|
|
24
|
+
import { StatementKind } from '@pgsql/semantics';
|
|
25
|
+
import { ObjectIdentity, ObjectIdentityKind } from '@pgsql/transform';
|
|
25
26
|
/** One deployable unit: an object (by identity) and the statements that build it. */
|
|
26
27
|
export interface PartitionUnit {
|
|
27
28
|
/** The object's identity, or `null` for split riders and the residual unit. */
|
package/partition-driver.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.partitionUnits = partitionUnits;
|
|
|
5
5
|
/**
|
|
6
6
|
* Partition dial: project one deploy surface into a set of pgpm packages.
|
|
7
7
|
*
|
|
8
|
-
* The object graph (statements classified by `@pgsql/
|
|
8
|
+
* The object graph (statements classified by `@pgsql/semantics`, grouped
|
|
9
9
|
* into identity-keyed units) is the source of truth; which package a unit
|
|
10
10
|
* lives in is a *derived projection* of a declarative partition config.
|
|
11
11
|
* Units can be assigned by schema, by object kind, by explicit cherry-pick
|
|
@@ -25,6 +25,7 @@ exports.partitionUnits = partitionUnits;
|
|
|
25
25
|
* pgpm.plan + deploy trees, feed a bundle, ...).
|
|
26
26
|
*/
|
|
27
27
|
const naming_spec_1 = require("@pgpmjs/naming-spec");
|
|
28
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
28
29
|
const transform_1 = require("@pgsql/transform");
|
|
29
30
|
/** A partition whose packages require each other in a cycle — unshippable. */
|
|
30
31
|
class PartitionCycleError extends Error {
|
|
@@ -84,7 +85,7 @@ exports.RESIDUAL_UNIT_PATH = 'misc/statements';
|
|
|
84
85
|
* otherwise) that depend on their host unit.
|
|
85
86
|
*/
|
|
86
87
|
function buildUnitGraph(sql, style, splitRiders) {
|
|
87
|
-
const facts = (0,
|
|
88
|
+
const facts = (0, semantics_1.classifyStatements)(sql);
|
|
88
89
|
const graph = (0, transform_1.buildStatementGraph)(facts);
|
|
89
90
|
const unitOf = new Array(facts.length).fill(-1);
|
|
90
91
|
const units = [];
|
package/program.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* untouched statements). A program with no dirty statements emits its source
|
|
10
10
|
* byte-identically, so content-addressed hashes are preserved.
|
|
11
11
|
*/
|
|
12
|
-
import type { StatementFacts } from '@pgsql/
|
|
12
|
+
import type { StatementFacts } from '@pgsql/semantics';
|
|
13
13
|
/** A statement's location in the source script (byte offsets). */
|
|
14
14
|
export type SqlStatementSpan = StatementFacts['span'];
|
|
15
15
|
/** One statement of a parsed SQL script. */
|
package/program.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseSqlProgram = parseSqlProgram;
|
|
4
4
|
exports.emitSqlProgram = emitSqlProgram;
|
|
5
|
-
const
|
|
5
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
6
6
|
const plpgsql_parser_1 = require("plpgsql-parser");
|
|
7
7
|
/**
|
|
8
8
|
* Parse a SQL script into a statement program. Requires `loadModule()` from
|
|
@@ -11,7 +11,7 @@ const plpgsql_parser_1 = require("plpgsql-parser");
|
|
|
11
11
|
function parseSqlProgram(source) {
|
|
12
12
|
const parseResult = (0, plpgsql_parser_1.parseSql)(source);
|
|
13
13
|
const stmts = parseResult?.stmts ?? [];
|
|
14
|
-
const facts = (0,
|
|
14
|
+
const facts = (0, semantics_1.classifyStatements)(source);
|
|
15
15
|
const statements = [];
|
|
16
16
|
for (let i = 0; i < stmts.length; i++) {
|
|
17
17
|
const rawStmt = stmts[i];
|
package/regen.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Requires `loadModule()` from `plpgsql-parser` to have completed.
|
|
13
13
|
*/
|
|
14
14
|
import type { GeneratedScript } from '@pgsql/scripts';
|
|
15
|
-
import type { StatementFacts } from '@pgsql/
|
|
15
|
+
import type { StatementFacts } from '@pgsql/semantics';
|
|
16
16
|
export type { GeneratedScript } from '@pgsql/scripts';
|
|
17
17
|
/** Generated revert and verify scripts for one change's deploy SQL. */
|
|
18
18
|
export interface RegeneratedScripts {
|
package/regen.js
CHANGED
|
@@ -4,13 +4,13 @@ exports.classifyScript = classifyScript;
|
|
|
4
4
|
exports.isStubScript = isStubScript;
|
|
5
5
|
exports.regenerateScripts = regenerateScripts;
|
|
6
6
|
const scripts_1 = require("@pgsql/scripts");
|
|
7
|
-
const
|
|
7
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
8
8
|
/**
|
|
9
9
|
* Classify a pgpm script's substantive statements: every top-level statement
|
|
10
10
|
* except transaction control (`BEGIN`/`COMMIT`/`ROLLBACK` wrappers).
|
|
11
11
|
*/
|
|
12
12
|
function classifyScript(sql) {
|
|
13
|
-
return (0,
|
|
13
|
+
return (0, semantics_1.classifyStatements)(sql).filter(f => f.nodeTag !== 'TransactionStmt');
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Whether a script is an empty stub: nothing but comments, whitespace, and
|
|
@@ -90,3 +90,38 @@ export interface DiffInputChange {
|
|
|
90
90
|
* schema yields an identical diff.
|
|
91
91
|
*/
|
|
92
92
|
export declare function diffChangeSets(from: DiffInputChange[], to: DiffInputChange[], options?: SemanticDiffOptions): SemanticDiffResult;
|
|
93
|
+
/** How one object a change touches relates to the diff's delta. */
|
|
94
|
+
export type ChangeObjectDelta = ObjectDelta | 'unchanged';
|
|
95
|
+
/**
|
|
96
|
+
* Whether a `to`-side change is already satisfied by the `from` side:
|
|
97
|
+
* - `satisfied`: every object it touches is unchanged in the diff
|
|
98
|
+
* - `unsatisfied`: every object it touches is added/modified in the diff
|
|
99
|
+
* - `partial`: a mix of unchanged and delta objects
|
|
100
|
+
* - `inert`: no identifiable objects (seed data, grants-only, dynamic SQL)
|
|
101
|
+
*/
|
|
102
|
+
export type ChangeSatisfaction = 'satisfied' | 'unsatisfied' | 'partial' | 'inert';
|
|
103
|
+
export interface ChangeObjectCoverage {
|
|
104
|
+
/** Canonical naming-spec path for the object. */
|
|
105
|
+
path: string;
|
|
106
|
+
delta: ChangeObjectDelta;
|
|
107
|
+
}
|
|
108
|
+
export interface ChangeCoverage {
|
|
109
|
+
/** The input change's name (as given, e.g. `pkg:change`). */
|
|
110
|
+
name: string;
|
|
111
|
+
status: ChangeSatisfaction;
|
|
112
|
+
objects: ChangeObjectCoverage[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Cover each `to`-side change against a semantic diff's per-object delta:
|
|
116
|
+
* which of the objects the change touches are already present and identical
|
|
117
|
+
* on the `from` side. A `satisfied` change deploys nothing new — its ledger
|
|
118
|
+
* entry can be backfilled instead of executing it; an `unsatisfied` change is
|
|
119
|
+
* the genuine delta. Identity keying is shared with the diff itself, so
|
|
120
|
+
* regrouped/renamed/reordered authorship of the same objects covers the same.
|
|
121
|
+
*
|
|
122
|
+
* `diff` must come from diffing the same `from` side against these changes
|
|
123
|
+
* (`diffChangeSets(fromChanges, toChanges)`).
|
|
124
|
+
*/
|
|
125
|
+
export declare function coverChanges(changes: DiffInputChange[], diff: SemanticDiffResult, options?: {
|
|
126
|
+
style?: PathStyle;
|
|
127
|
+
}): ChangeCoverage[];
|
package/semantic-diff-driver.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.diffSchemas = diffSchemas;
|
|
4
4
|
exports.diffChangeSets = diffChangeSets;
|
|
5
|
+
exports.coverChanges = coverChanges;
|
|
5
6
|
/**
|
|
6
7
|
* Semantic diff driver: identity-keyed schema delta between two deploy
|
|
7
8
|
* surfaces.
|
|
@@ -32,6 +33,7 @@ exports.diffChangeSets = diffChangeSets;
|
|
|
32
33
|
*/
|
|
33
34
|
const naming_spec_1 = require("@pgpmjs/naming-spec");
|
|
34
35
|
const scripts_1 = require("@pgsql/scripts");
|
|
36
|
+
const semantics_1 = require("@pgsql/semantics");
|
|
35
37
|
const transform_1 = require("@pgsql/transform");
|
|
36
38
|
const plpgsql_parser_1 = require("plpgsql-parser");
|
|
37
39
|
const constraint_names_1 = require("./constraint-names");
|
|
@@ -44,7 +46,7 @@ const identityKey = (id) => `${id.kind}\u0000${id.schema ?? ''}\u0000${id.table
|
|
|
44
46
|
* statements are reported via `warnings`.
|
|
45
47
|
*/
|
|
46
48
|
function readSide(sql, warnings, label) {
|
|
47
|
-
const facts = (0,
|
|
49
|
+
const facts = (0, semantics_1.classifyStatements)(sql);
|
|
48
50
|
const graph = (0, transform_1.buildStatementGraph)(facts);
|
|
49
51
|
const units = new Map();
|
|
50
52
|
const order = [];
|
|
@@ -367,7 +369,7 @@ function diffSchemas(from, to, options = {}) {
|
|
|
367
369
|
* statement to surface its not-derivable comment and warning.
|
|
368
370
|
*/
|
|
369
371
|
const inverseOf = (deploy, context) => {
|
|
370
|
-
const facts = (0,
|
|
372
|
+
const facts = (0, semantics_1.classifyStatements)(deploy);
|
|
371
373
|
const graph = (0, transform_1.buildStatementGraph)(facts);
|
|
372
374
|
const pieces = [];
|
|
373
375
|
for (const i of [...graph.order].reverse()) {
|
|
@@ -390,7 +392,7 @@ function diffSchemas(from, to, options = {}) {
|
|
|
390
392
|
*/
|
|
391
393
|
const verifyOf = (deploy, context) => {
|
|
392
394
|
const pieces = [];
|
|
393
|
-
for (const fact of (0,
|
|
395
|
+
for (const fact of (0, semantics_1.classifyStatements)(deploy)) {
|
|
394
396
|
const nodes = (0, scripts_1.existenceCheck)(fact);
|
|
395
397
|
if (nodes === null) {
|
|
396
398
|
const { warnings: w } = (0, scripts_1.verifyFor)([fact]);
|
|
@@ -505,3 +507,38 @@ function diffChangeSets(from, to, options = {}) {
|
|
|
505
507
|
const flatten = (changes) => changes.map(c => c.deploy.trim()).filter(Boolean).join('\n\n');
|
|
506
508
|
return diffSchemas(flatten(from), flatten(to), options);
|
|
507
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Cover each `to`-side change against a semantic diff's per-object delta:
|
|
512
|
+
* which of the objects the change touches are already present and identical
|
|
513
|
+
* on the `from` side. A `satisfied` change deploys nothing new — its ledger
|
|
514
|
+
* entry can be backfilled instead of executing it; an `unsatisfied` change is
|
|
515
|
+
* the genuine delta. Identity keying is shared with the diff itself, so
|
|
516
|
+
* regrouped/renamed/reordered authorship of the same objects covers the same.
|
|
517
|
+
*
|
|
518
|
+
* `diff` must come from diffing the same `from` side against these changes
|
|
519
|
+
* (`diffChangeSets(fromChanges, toChanges)`).
|
|
520
|
+
*/
|
|
521
|
+
function coverChanges(changes, diff, options = {}) {
|
|
522
|
+
const style = options.style ?? 'directory';
|
|
523
|
+
const deltaByKey = new Map();
|
|
524
|
+
for (const obj of diff.objects) {
|
|
525
|
+
deltaByKey.set(identityKey(obj.identity), obj.delta);
|
|
526
|
+
}
|
|
527
|
+
return changes.map(change => {
|
|
528
|
+
const sideWarnings = [];
|
|
529
|
+
const side = readSide(change.deploy, sideWarnings, change.name);
|
|
530
|
+
const objects = side.order.map(key => {
|
|
531
|
+
const unit = side.units.get(key);
|
|
532
|
+
return {
|
|
533
|
+
path: (0, naming_spec_1.pathFor)(unit.identity, { style }),
|
|
534
|
+
delta: deltaByKey.get(key) ?? 'unchanged'
|
|
535
|
+
};
|
|
536
|
+
});
|
|
537
|
+
const touched = objects.filter(o => o.delta !== 'unchanged').length;
|
|
538
|
+
const status = objects.length === 0 ? 'inert'
|
|
539
|
+
: touched === 0 ? 'satisfied'
|
|
540
|
+
: touched === objects.length ? 'unsatisfied'
|
|
541
|
+
: 'partial';
|
|
542
|
+
return { name: change.name, status, objects };
|
|
543
|
+
});
|
|
544
|
+
}
|
package/sub-object.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* every alteration.
|
|
11
11
|
*/
|
|
12
12
|
import type { ObjectIdentity } from '@pgpmjs/naming-spec';
|
|
13
|
-
import type { StatementFacts } from '@pgsql/
|
|
13
|
+
import type { StatementFacts } from '@pgsql/semantics';
|
|
14
14
|
/**
|
|
15
15
|
* The column or constraint a one-command `ALTER TABLE .. ADD COLUMN` /
|
|
16
16
|
* `ADD CONSTRAINT` statement adds, as a naming-spec identity
|