@pgpmjs/transform 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
4
+ Copyright (c) 2025 Constructive <developers@constructive.io>
5
+ Copyright (c) 2020-present, Interweb, Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @pgpmjs/transform
2
+
3
+ Change-aware SQL transformation for PGPM. A **superset of [`@pgsql/transform`](https://www.npmjs.com/package/@pgsql/transform)** โ€” everything from the pure AST layer is re-exported, plus the layers that understand pgpm *changes*:
4
+
5
+ - **`categorizeChange` / `buildCategoryOf` / `TIER_PROFILE`** โ€” profile-driven categorization of migration changes (schema / functionality / security / fixtures) from their AST facts, ready for `@pgpmjs/core`'s `boundary: 'category'` rebundle mode.
6
+ - **`resolveFixtureClosure`** โ€” transitive dependency closure over a set of changes (name + deploy SQL + optional plan dependencies): forward producers of every referenced object/schema/role, attached fixtures (policies/grants/RLS targeting closure members), explicit unresolved-reference reporting.
7
+ - **`makeSchemaTranspiler` / `makeNamespaceValidator`** โ€” drivers plugging `transformSql`/`classifyStatements` into the pgpm migration-bundle seams (`transpileBundle`'s `renameChange`/`transformScript`, `applyBundle`'s `validateReferences`).
8
+
9
+ Re-exported from `@pgsql/transform`: `transformSql` (schema-name rewriting incl. PL/pgSQL bodies), `classifyStatements` (per-statement AST facts), `qualifyUnqualified`, and round-trip validation utilities.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @pgpmjs/transform
15
+ ```
16
+
17
+ The parser runs on a WASM build of the real PostgreSQL parser; call `loadModule()` from `plpgsql-parser` once before using any synchronous API.
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ import { loadModule } from 'plpgsql-parser';
23
+ import { buildCategoryOf, resolveFixtureClosure, TIER_PROFILE, transformSql } from '@pgpmjs/transform';
24
+
25
+ await loadModule();
26
+
27
+ const categoryOf = buildCategoryOf(changes, TIER_PROFILE);
28
+ const closure = resolveFixtureClosure(allChanges, ['schemas/api/procedures/get_totals']);
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Education and Tutorials
34
+
35
+ 1. ๐Ÿš€ [Quickstart: Getting Up and Running](https://constructive.io/learn/quickstart)
36
+ Get started with modular databases in minutes. Install prerequisites and deploy your first module.
37
+
38
+ 2. ๐Ÿ“ฆ [Modular PostgreSQL Development with Database Packages](https://constructive.io/learn/modular-postgres)
39
+ Learn to organize PostgreSQL projects with pgpm workspaces and reusable database modules.
40
+
41
+ 3. โœ๏ธ [Authoring Database Changes](https://constructive.io/learn/authoring-database-changes)
42
+ Master the workflow for adding, organizing, and managing database changes with pgpm.
43
+
44
+ 4. ๐Ÿงช [End-to-End PostgreSQL Testing with TypeScript](https://constructive.io/learn/e2e-postgres-testing)
45
+ Master end-to-end PostgreSQL testing with ephemeral databases, RLS testing, and CI/CD automation.
46
+
47
+ 5. โšก [Supabase Testing](https://constructive.io/learn/supabase)
48
+ Use TypeScript-first tools to test Supabase projects with realistic RLS, policies, and auth contexts.
49
+
50
+ 6. ๐Ÿ’ง [Drizzle ORM Testing](https://constructive.io/learn/drizzle-testing)
51
+ Run full-stack tests with Drizzle ORM, including database setup, teardown, and RLS enforcement.
52
+
53
+ 7. ๐Ÿ”ง [Troubleshooting](https://constructive.io/learn/troubleshooting)
54
+ Common issues and solutions for pgpm, PostgreSQL, and testing.
55
+
56
+ ## Related Constructive Tooling
57
+
58
+ ### ๐Ÿ“ฆ Package Management
59
+
60
+ * [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **๐Ÿ–ฅ๏ธ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
61
+
62
+ ### ๐Ÿงช Testing
63
+
64
+ * [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **๐Ÿ“Š Isolated testing environments** with per-test transaction rollbacksโ€”ideal for integration tests, complex migrations, and RLS simulation.
65
+ * [pgsql-seed](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-seed): **๐ŸŒฑ PostgreSQL seeding utilities** for CSV, JSON, SQL data loading, and pgpm deployment.
66
+ * [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **๐Ÿงช Supabase-native test harness** preconfigured for the local Supabase stackโ€”per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
67
+ * [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **๐Ÿ” Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
68
+ * [pg-query-context](https://github.com/constructive-io/constructive/tree/main/postgres/pg-query-context): **๐Ÿ”’ Session context injection** to add session-local context (e.g., `SET LOCAL`) into queriesโ€”ideal for setting `role`, `jwt.claims`, and other session settings.
69
+
70
+ ### ๐Ÿง  Parsing & AST
71
+
72
+ * [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): **๐Ÿ”„ SQL conversion engine** that interprets and converts PostgreSQL syntax.
73
+ * [libpg-query-node](https://www.npmjs.com/package/libpg-query): **๐ŸŒ‰ Node.js bindings** for `libpg_query`, converting SQL into parse trees.
74
+ * [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): **๐Ÿ“ฆ Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
75
+ * [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **๐Ÿท๏ธ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
76
+ * [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **๐Ÿ“ Type definitions** for PostgreSQL AST nodes in TypeScript.
77
+ * [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **๐Ÿ› ๏ธ AST utilities** for constructing and transforming PostgreSQL syntax trees.
78
+
79
+ ### ๐Ÿ“š Documentation & Skills
80
+
81
+ * [constructive-skills](https://github.com/constructive-io/constructive-skills): **๐Ÿ“– Platform documentation and AI agent skills** โ€” feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.
82
+
83
+ Install skills for AI coding agents:
84
+
85
+ ```bash
86
+ # All platform skills (security, blueprints, codegen, billing, etc.)
87
+ npx skills add constructive-io/constructive-skills
88
+
89
+ # Individual repo skills (pgpm, testing, CLI, search, etc.)
90
+ npx skills add https://github.com/constructive-io/constructive --skill pgpm
91
+ npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
92
+ ```
93
+
94
+ ## Credits
95
+
96
+ **๐Ÿ›  Built by the [Constructive](https://constructive.io) team โ€” creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
97
+
98
+ ## Disclaimer
99
+
100
+ AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
101
+
102
+ No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bundle transpile/apply drivers.
3
+ *
4
+ * Adapters that plug this package's AST transforms into the pgpm migration
5
+ * bundle seams (`transpileBundle`'s `renameChange`/`transformScript` and
6
+ * `applyBundle`'s `validateReferences`). The seams are structurally typed on
7
+ * purpose โ€” no dependency on `@pgpmjs/bundle`/`@pgpmjs/core` โ€” so the driver
8
+ * stays a pure function factory over `transformSql` and `classifyStatements`.
9
+ */
10
+ import { SchemaTransformResult, TransformSqlOptions } from '@pgsql/transform';
11
+ /** Identity of the script being transformed/validated (matches the bundle seams). */
12
+ export interface BundleScriptContext {
13
+ change: string;
14
+ kind: 'deploy' | 'revert' | 'verify';
15
+ }
16
+ export interface SchemaTranspilerOptions {
17
+ /** Old schema name โ†’ new schema name. Drives both dimensions. */
18
+ schemaMap: Record<string, string>;
19
+ /** Forwarded to {@link transformSql} (round-trip validation, extra passes). */
20
+ transform?: TransformSqlOptions;
21
+ }
22
+ export interface SchemaTranspiler {
23
+ /**
24
+ * Change-name/path rewrite (the pgpm structural dimension): renames the
25
+ * segment following any `schemas` path segment, e.g.
26
+ * `schemas/auth/tables/users` โ†’ `schemas/tenant_auth/tables/users`.
27
+ */
28
+ renameChange: (name: string) => string;
29
+ /**
30
+ * SQL body rewrite (the AST dimension): full AST transform of every mapped
31
+ * schema reference via {@link transformSql}, including PL/pgSQL bodies.
32
+ * Throws if a mapped schema survives untransformed.
33
+ */
34
+ transformScript: (sql: string, ctx: BundleScriptContext) => string;
35
+ /**
36
+ * Accumulated report across every script this transpiler has transformed:
37
+ * schemas found/transformed and any per-script errors.
38
+ */
39
+ result: SchemaTransformResult;
40
+ }
41
+ /**
42
+ * Build the caller-supplied callbacks for `transpileBundle` from a single
43
+ * schema map, so the folder/plan rename and the in-SQL rewrite stay in
44
+ * lockstep.
45
+ */
46
+ export declare function makeSchemaTranspiler(options: SchemaTranspilerOptions): SchemaTranspiler;
47
+ export interface NamespaceValidatorOptions {
48
+ /** Schemas the bundle is allowed to create objects in or reference. */
49
+ allowedSchemas: string[];
50
+ /**
51
+ * Also flag statements whose PL/pgSQL bodies execute dynamic SQL โ€” their
52
+ * references are invisible to the AST, so containment cannot be proven.
53
+ * Off by default (dynamic SQL is common in legitimate functions).
54
+ */
55
+ flagDynamicSql?: boolean;
56
+ }
57
+ /**
58
+ * Build an `applyBundle`-compatible `validateReferences` callback: returns a
59
+ * description of every schema-qualified object a script creates or references
60
+ * outside the allowed namespace. Unqualified references resolve via
61
+ * search_path and are not reported.
62
+ */
63
+ export declare function makeNamespaceValidator(options: NamespaceValidatorOptions): (sql: string, ctx: BundleScriptContext) => string[];
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Bundle transpile/apply drivers.
4
+ *
5
+ * Adapters that plug this package's AST transforms into the pgpm migration
6
+ * bundle seams (`transpileBundle`'s `renameChange`/`transformScript` and
7
+ * `applyBundle`'s `validateReferences`). The seams are structurally typed on
8
+ * purpose โ€” no dependency on `@pgpmjs/bundle`/`@pgpmjs/core` โ€” so the driver
9
+ * stays a pure function factory over `transformSql` and `classifyStatements`.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.makeSchemaTranspiler = makeSchemaTranspiler;
13
+ exports.makeNamespaceValidator = makeNamespaceValidator;
14
+ const transform_1 = require("@pgsql/transform");
15
+ const transform_2 = require("@pgsql/transform");
16
+ /**
17
+ * Build the caller-supplied callbacks for `transpileBundle` from a single
18
+ * schema map, so the folder/plan rename and the in-SQL rewrite stay in
19
+ * lockstep.
20
+ */
21
+ function makeSchemaTranspiler(options) {
22
+ const mapping = new Map(Object.entries(options.schemaMap));
23
+ const result = {
24
+ schemasFound: new Set(),
25
+ schemasTransformed: new Map(),
26
+ errors: []
27
+ };
28
+ const renameChange = (name) => {
29
+ const parts = name.split('/');
30
+ for (let i = 0; i < parts.length - 1; i++) {
31
+ if (parts[i] === 'schemas' && mapping.has(parts[i + 1])) {
32
+ parts[i + 1] = mapping.get(parts[i + 1]);
33
+ }
34
+ }
35
+ return parts.join('/');
36
+ };
37
+ const transformScript = (sql, _ctx) => {
38
+ return (0, transform_2.transformSql)(sql, mapping, options.transform, result).content;
39
+ };
40
+ return { renameChange, transformScript, result };
41
+ }
42
+ /**
43
+ * Build an `applyBundle`-compatible `validateReferences` callback: returns a
44
+ * description of every schema-qualified object a script creates or references
45
+ * outside the allowed namespace. Unqualified references resolve via
46
+ * search_path and are not reported.
47
+ */
48
+ function makeNamespaceValidator(options) {
49
+ const allowed = new Set(options.allowedSchemas);
50
+ return (sql, _ctx) => {
51
+ const violations = new Set();
52
+ for (const facts of (0, transform_1.classifyStatements)(sql)) {
53
+ for (const ref of [...facts.creates, ...facts.references, ...facts.fkTargets]) {
54
+ if (ref.schema && !allowed.has(ref.schema)) {
55
+ violations.add(`${facts.nodeTag}: ${ref.schema}.${ref.name}`);
56
+ }
57
+ }
58
+ if (options.flagDynamicSql && facts.dynamicSql) {
59
+ violations.add(`${facts.nodeTag}: dynamic SQL โ€” references not statically verifiable`);
60
+ }
61
+ }
62
+ return [...violations];
63
+ };
64
+ }
@@ -0,0 +1,52 @@
1
+ import { StatementFacts } from '@pgsql/transform';
2
+ /**
3
+ * A category is a chunk-seam key a change is assigned to. Free-form string; the
4
+ * built-in {@link TIER_PROFILE} uses `'security' | 'functionality' | 'fixtures'
5
+ * | 'schema'`.
6
+ *
7
+ * This module is the classifier-driven counterpart to `@pgpmjs/core`'s
8
+ * `boundary: 'category'` rebundle mode: core stays classifier-agnostic and only
9
+ * consumes a `categoryOf(changeName)` seam function, while the rules that decide
10
+ * *what* each change is (the profile) live here, next to the AST facts.
11
+ */
12
+ export type ChangeCategory = string;
13
+ /**
14
+ * Decides a change's category from the AST facts of its (deploy) statements.
15
+ * A profile is a pure function of the facts โ€” swap it to carve a monolith along
16
+ * different seams (e.g. per-role admin/users modules) without touching core.
17
+ */
18
+ export interface CategoryProfile {
19
+ /** Identifier for the profile (for logging / provenance). */
20
+ name: string;
21
+ /** Map a change's statement facts to its category. */
22
+ categorize: (facts: StatementFacts[], changeName: string) => ChangeCategory;
23
+ }
24
+ /**
25
+ * Default tier profile: isolates the security surface and procedural code out
26
+ * of the schema base. Precedence, highest first โ€” a change is placed in the
27
+ * most specialized tier any of its statements reaches:
28
+ *
29
+ * security any securityRelevant statement (policy/grant/RLS/role/owner)
30
+ * functionality functions and triggers
31
+ * fixtures seed DML (INSERT/UPDATE/DELETE)
32
+ * schema schemas, tables, views, indexes, types, constraints (base)
33
+ *
34
+ * Ordering/dependencies across the resulting chunks are still resolved by
35
+ * core's rebundle graph, so this only decides *naming/grouping*, never deploy
36
+ * order.
37
+ */
38
+ export declare const TIER_PROFILE: CategoryProfile;
39
+ /**
40
+ * Classify a single change's deploy SQL into its category under `profile`
41
+ * (defaults to {@link TIER_PROFILE}).
42
+ */
43
+ export declare function categorizeChange(sql: string, changeName: string, profile?: CategoryProfile): ChangeCategory;
44
+ /**
45
+ * Build a `categoryOf(changeName)` seam function from a map of change name to
46
+ * its deploy SQL โ€” ready to pass straight to `@pgpmjs/core`'s rebundle
47
+ * `boundary: 'category'` / `categoryOf` options.
48
+ *
49
+ * Changes absent from `changeSql` return `undefined`, letting core fall back to
50
+ * its folder key for anything the classifier did not see.
51
+ */
52
+ export declare function buildCategoryOf(changeSql: Record<string, string>, profile?: CategoryProfile): (changeName: string) => ChangeCategory | undefined;
package/categorize.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TIER_PROFILE = void 0;
4
+ exports.categorizeChange = categorizeChange;
5
+ exports.buildCategoryOf = buildCategoryOf;
6
+ const transform_1 = require("@pgsql/transform");
7
+ /**
8
+ * Default tier profile: isolates the security surface and procedural code out
9
+ * of the schema base. Precedence, highest first โ€” a change is placed in the
10
+ * most specialized tier any of its statements reaches:
11
+ *
12
+ * security any securityRelevant statement (policy/grant/RLS/role/owner)
13
+ * functionality functions and triggers
14
+ * fixtures seed DML (INSERT/UPDATE/DELETE)
15
+ * schema schemas, tables, views, indexes, types, constraints (base)
16
+ *
17
+ * Ordering/dependencies across the resulting chunks are still resolved by
18
+ * core's rebundle graph, so this only decides *naming/grouping*, never deploy
19
+ * order.
20
+ */
21
+ exports.TIER_PROFILE = {
22
+ name: 'tier',
23
+ categorize(facts) {
24
+ if (facts.length === 0)
25
+ return 'schema';
26
+ if (facts.some(f => f.securityRelevant))
27
+ return 'security';
28
+ if (facts.some(f => f.kind === 'function' || f.kind === 'trigger'))
29
+ return 'functionality';
30
+ if (facts.some(f => f.kind === 'seed_dml'))
31
+ return 'fixtures';
32
+ return 'schema';
33
+ },
34
+ };
35
+ /**
36
+ * Classify a single change's deploy SQL into its category under `profile`
37
+ * (defaults to {@link TIER_PROFILE}).
38
+ */
39
+ function categorizeChange(sql, changeName, profile = exports.TIER_PROFILE) {
40
+ return profile.categorize((0, transform_1.classifyStatements)(sql), changeName);
41
+ }
42
+ /**
43
+ * Build a `categoryOf(changeName)` seam function from a map of change name to
44
+ * its deploy SQL โ€” ready to pass straight to `@pgpmjs/core`'s rebundle
45
+ * `boundary: 'category'` / `categoryOf` options.
46
+ *
47
+ * Changes absent from `changeSql` return `undefined`, letting core fall back to
48
+ * its folder key for anything the classifier did not see.
49
+ */
50
+ function buildCategoryOf(changeSql, profile = exports.TIER_PROFILE) {
51
+ const categories = new Map();
52
+ for (const [name, sql] of Object.entries(changeSql)) {
53
+ categories.set(name, categorizeChange(sql, name, profile));
54
+ }
55
+ return (changeName) => categories.get(changeName);
56
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Bundle transpile/apply drivers.
3
+ *
4
+ * Adapters that plug this package's AST transforms into the pgpm migration
5
+ * bundle seams (`transpileBundle`'s `renameChange`/`transformScript` and
6
+ * `applyBundle`'s `validateReferences`). The seams are structurally typed on
7
+ * purpose โ€” no dependency on `@pgpmjs/bundle`/`@pgpmjs/core` โ€” so the driver
8
+ * stays a pure function factory over `transformSql` and `classifyStatements`.
9
+ */
10
+ import { classifyStatements } from '@pgsql/transform';
11
+ import { transformSql } from '@pgsql/transform';
12
+ /**
13
+ * Build the caller-supplied callbacks for `transpileBundle` from a single
14
+ * schema map, so the folder/plan rename and the in-SQL rewrite stay in
15
+ * lockstep.
16
+ */
17
+ export function makeSchemaTranspiler(options) {
18
+ const mapping = new Map(Object.entries(options.schemaMap));
19
+ const result = {
20
+ schemasFound: new Set(),
21
+ schemasTransformed: new Map(),
22
+ errors: []
23
+ };
24
+ const renameChange = (name) => {
25
+ const parts = name.split('/');
26
+ for (let i = 0; i < parts.length - 1; i++) {
27
+ if (parts[i] === 'schemas' && mapping.has(parts[i + 1])) {
28
+ parts[i + 1] = mapping.get(parts[i + 1]);
29
+ }
30
+ }
31
+ return parts.join('/');
32
+ };
33
+ const transformScript = (sql, _ctx) => {
34
+ return transformSql(sql, mapping, options.transform, result).content;
35
+ };
36
+ return { renameChange, transformScript, result };
37
+ }
38
+ /**
39
+ * Build an `applyBundle`-compatible `validateReferences` callback: returns a
40
+ * description of every schema-qualified object a script creates or references
41
+ * outside the allowed namespace. Unqualified references resolve via
42
+ * search_path and are not reported.
43
+ */
44
+ export function makeNamespaceValidator(options) {
45
+ const allowed = new Set(options.allowedSchemas);
46
+ return (sql, _ctx) => {
47
+ const violations = new Set();
48
+ for (const facts of classifyStatements(sql)) {
49
+ for (const ref of [...facts.creates, ...facts.references, ...facts.fkTargets]) {
50
+ if (ref.schema && !allowed.has(ref.schema)) {
51
+ violations.add(`${facts.nodeTag}: ${ref.schema}.${ref.name}`);
52
+ }
53
+ }
54
+ if (options.flagDynamicSql && facts.dynamicSql) {
55
+ violations.add(`${facts.nodeTag}: dynamic SQL โ€” references not statically verifiable`);
56
+ }
57
+ }
58
+ return [...violations];
59
+ };
60
+ }
@@ -0,0 +1,51 @@
1
+ import { classifyStatements } from '@pgsql/transform';
2
+ /**
3
+ * Default tier profile: isolates the security surface and procedural code out
4
+ * of the schema base. Precedence, highest first โ€” a change is placed in the
5
+ * most specialized tier any of its statements reaches:
6
+ *
7
+ * security any securityRelevant statement (policy/grant/RLS/role/owner)
8
+ * functionality functions and triggers
9
+ * fixtures seed DML (INSERT/UPDATE/DELETE)
10
+ * schema schemas, tables, views, indexes, types, constraints (base)
11
+ *
12
+ * Ordering/dependencies across the resulting chunks are still resolved by
13
+ * core's rebundle graph, so this only decides *naming/grouping*, never deploy
14
+ * order.
15
+ */
16
+ export const TIER_PROFILE = {
17
+ name: 'tier',
18
+ categorize(facts) {
19
+ if (facts.length === 0)
20
+ return 'schema';
21
+ if (facts.some(f => f.securityRelevant))
22
+ return 'security';
23
+ if (facts.some(f => f.kind === 'function' || f.kind === 'trigger'))
24
+ return 'functionality';
25
+ if (facts.some(f => f.kind === 'seed_dml'))
26
+ return 'fixtures';
27
+ return 'schema';
28
+ },
29
+ };
30
+ /**
31
+ * Classify a single change's deploy SQL into its category under `profile`
32
+ * (defaults to {@link TIER_PROFILE}).
33
+ */
34
+ export function categorizeChange(sql, changeName, profile = TIER_PROFILE) {
35
+ return profile.categorize(classifyStatements(sql), changeName);
36
+ }
37
+ /**
38
+ * Build a `categoryOf(changeName)` seam function from a map of change name to
39
+ * its deploy SQL โ€” ready to pass straight to `@pgpmjs/core`'s rebundle
40
+ * `boundary: 'category'` / `categoryOf` options.
41
+ *
42
+ * Changes absent from `changeSql` return `undefined`, letting core fall back to
43
+ * its folder key for anything the classifier did not see.
44
+ */
45
+ export function buildCategoryOf(changeSql, profile = TIER_PROFILE) {
46
+ const categories = new Map();
47
+ for (const [name, sql] of Object.entries(changeSql)) {
48
+ categories.set(name, categorizeChange(sql, name, profile));
49
+ }
50
+ return (changeName) => categories.get(changeName);
51
+ }
@@ -0,0 +1,197 @@
1
+ import { TIER_PROFILE } from './categorize';
2
+ import { classifyStatements } from '@pgsql/transform';
3
+ function objectKey(schema, name) {
4
+ return `${schema ?? ''}\u0000${name}`;
5
+ }
6
+ function analyze(change, profile) {
7
+ const facts = classifyStatements(change.sql);
8
+ const category = profile.categorize(facts, change.name);
9
+ const creates = [];
10
+ const createsSchemas = [];
11
+ const createsRoles = [];
12
+ const needsObjects = [];
13
+ const needsSchemas = new Set();
14
+ const needsRoles = new Set();
15
+ for (const f of facts) {
16
+ for (const c of f.creates) {
17
+ if (f.kind === 'schema')
18
+ createsSchemas.push(c.name);
19
+ else {
20
+ creates.push(c);
21
+ if (c.schema)
22
+ needsSchemas.add(c.schema);
23
+ }
24
+ }
25
+ if (f.nodeTag === 'CreateRoleStmt')
26
+ createsRoles.push(...f.roles);
27
+ for (const r of [...f.references, ...f.fkTargets]) {
28
+ if (r.schema)
29
+ needsObjects.push(r);
30
+ }
31
+ for (const s of f.referencedSchemas)
32
+ needsSchemas.add(s);
33
+ }
34
+ const created = new Set(createsRoles);
35
+ for (const f of facts) {
36
+ if (f.nodeTag === 'CreateRoleStmt')
37
+ continue;
38
+ for (const role of f.roles)
39
+ if (!created.has(role))
40
+ needsRoles.add(role);
41
+ }
42
+ for (const s of createsSchemas)
43
+ needsSchemas.delete(s);
44
+ return {
45
+ name: change.name,
46
+ category,
47
+ facts,
48
+ dependencies: change.dependencies ?? [],
49
+ creates,
50
+ createsSchemas,
51
+ createsRoles,
52
+ needsObjects,
53
+ needsSchemas: [...needsSchemas],
54
+ needsRoles: [...needsRoles],
55
+ isFixture: category === 'security' || category === 'fixtures'
56
+ };
57
+ }
58
+ /**
59
+ * Resolve the fixture closure of a schema slice.
60
+ *
61
+ * Pure and deterministic โ€” no I/O โ€” and runs to a fixpoint in both directions:
62
+ * - forward: a selected change pulls in the producers of the objects/schemas/
63
+ * roles it references, plus its declared plan dependencies (prerequisites);
64
+ * - reverse: any fixture (security/fixtures-tier change) that attaches to an
65
+ * object already in the closure is pulled in (attached fixtures), and then
66
+ * resolved for its own prerequisites.
67
+ *
68
+ * Output order follows the input (plan) order, so it stays deploy-safe.
69
+ */
70
+ export function resolveFixtureClosure(allChanges, selection, options = {}) {
71
+ const profile = options.profile ?? TIER_PROFILE;
72
+ const includePrerequisites = options.includePrerequisites ?? true;
73
+ const analyses = allChanges.map(c => analyze(c, profile));
74
+ const byName = new Map(analyses.map(a => [a.name, a]));
75
+ const producerByObject = new Map();
76
+ const producerBySchema = new Map();
77
+ const producerByRole = new Map();
78
+ for (const a of analyses) {
79
+ for (const c of a.creates) {
80
+ const key = objectKey(c.schema, c.name);
81
+ if (!producerByObject.has(key))
82
+ producerByObject.set(key, a.name);
83
+ }
84
+ for (const s of a.createsSchemas) {
85
+ if (!producerBySchema.has(s))
86
+ producerBySchema.set(s, a.name);
87
+ }
88
+ for (const r of a.createsRoles) {
89
+ if (!producerByRole.has(r))
90
+ producerByRole.set(r, a.name);
91
+ }
92
+ }
93
+ const reason = new Map();
94
+ for (const name of selection) {
95
+ if (byName.has(name))
96
+ reason.set(name, 'selected');
97
+ }
98
+ const producedObjectKeys = new Set();
99
+ const producedSchemas = new Set();
100
+ const refreshProduced = () => {
101
+ producedObjectKeys.clear();
102
+ producedSchemas.clear();
103
+ for (const name of reason.keys()) {
104
+ const a = byName.get(name);
105
+ for (const c of a.creates)
106
+ producedObjectKeys.add(objectKey(c.schema, c.name));
107
+ for (const s of a.createsSchemas)
108
+ producedSchemas.add(s);
109
+ }
110
+ };
111
+ let changed = true;
112
+ while (changed) {
113
+ changed = false;
114
+ // forward: pull producers of what current members need
115
+ for (const name of [...reason.keys()]) {
116
+ const a = byName.get(name);
117
+ const pull = (producer, r) => {
118
+ if (!producer || reason.has(producer))
119
+ return;
120
+ const pa = byName.get(producer);
121
+ reason.set(producer, pa.isFixture ? 'fixture' : r);
122
+ changed = true;
123
+ };
124
+ for (const dep of a.dependencies) {
125
+ pull(byName.has(dep) ? dep : undefined, 'prerequisite');
126
+ }
127
+ if (!includePrerequisites)
128
+ continue;
129
+ for (const o of a.needsObjects)
130
+ pull(producerByObject.get(objectKey(o.schema, o.name)), 'prerequisite');
131
+ for (const s of a.needsSchemas)
132
+ pull(producerBySchema.get(s), 'prerequisite');
133
+ for (const role of a.needsRoles)
134
+ pull(producerByRole.get(role), 'prerequisite');
135
+ }
136
+ // reverse: pull attached fixtures that target current members
137
+ refreshProduced();
138
+ for (const a of analyses) {
139
+ if (reason.has(a.name) || !a.isFixture)
140
+ continue;
141
+ const objectAttaches = a.needsObjects.some(o => producedObjectKeys.has(objectKey(o.schema, o.name))) ||
142
+ a.creates.some(c => producedObjectKeys.has(objectKey(c.schema, c.name)));
143
+ // Schema-scoped fixtures (e.g. ALTER DEFAULT PRIVILEGES IN SCHEMA x) name no
144
+ // specific object; attach them by schema. Object-scoped fixtures attach only
145
+ // via their object refs, so a shared schema does not drag in every fixture.
146
+ const schemaScoped = a.needsObjects.length === 0 && a.creates.length === 0;
147
+ const schemaAttaches = schemaScoped &&
148
+ (a.needsSchemas.some(s => producedSchemas.has(s)) ||
149
+ a.createsSchemas.some(s => producedSchemas.has(s)));
150
+ if (objectAttaches || schemaAttaches) {
151
+ reason.set(a.name, 'fixture');
152
+ changed = true;
153
+ }
154
+ }
155
+ }
156
+ const order = analyses.filter(a => reason.has(a.name)).map(a => a.name);
157
+ const changes = order.map(name => {
158
+ const a = byName.get(name);
159
+ return { name, category: a.category, reason: reason.get(name) };
160
+ });
161
+ const roles = new Set();
162
+ const unresolvedObjects = [];
163
+ const unresolvedObjectKeys = new Set();
164
+ const unresolvedSchemas = new Set();
165
+ const unresolvedRoles = new Set();
166
+ for (const name of order) {
167
+ const a = byName.get(name);
168
+ for (const role of a.needsRoles) {
169
+ roles.add(role);
170
+ if (!producerByRole.has(role))
171
+ unresolvedRoles.add(role);
172
+ }
173
+ for (const o of a.needsObjects) {
174
+ const key = objectKey(o.schema, o.name);
175
+ if (!producerByObject.has(key) && !unresolvedObjectKeys.has(key)) {
176
+ unresolvedObjectKeys.add(key);
177
+ unresolvedObjects.push(o);
178
+ }
179
+ }
180
+ for (const s of a.needsSchemas) {
181
+ if (!producerBySchema.has(s))
182
+ unresolvedSchemas.add(s);
183
+ }
184
+ }
185
+ return {
186
+ order,
187
+ changes,
188
+ fixtures: changes.filter(c => c.reason === 'fixture').map(c => c.name),
189
+ prerequisites: changes.filter(c => c.reason === 'prerequisite').map(c => c.name),
190
+ roles: [...roles],
191
+ unresolved: {
192
+ objects: unresolvedObjects,
193
+ schemas: [...unresolvedSchemas],
194
+ roles: [...unresolvedRoles]
195
+ }
196
+ };
197
+ }
package/esm/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from '@pgsql/transform';
2
+ export { makeNamespaceValidator, makeSchemaTranspiler, } from './bundle-driver';
3
+ export { buildCategoryOf, categorizeChange, TIER_PROFILE, } from './categorize';
4
+ export { resolveFixtureClosure } from './fixture-closure';
@@ -0,0 +1,71 @@
1
+ import { CategoryProfile, ChangeCategory } from './categorize';
2
+ import { QualifiedName } from '@pgsql/transform';
3
+ /**
4
+ * A change plus its deploy SQL. Optional plan `dependencies` (from `pgpm.plan`)
5
+ * are followed as authoritative prerequisites in addition to AST-derived edges.
6
+ */
7
+ export interface ClosureInputChange {
8
+ name: string;
9
+ sql: string;
10
+ dependencies?: string[];
11
+ }
12
+ /** Why a change ended up in the closure. */
13
+ export type ClosureReason = 'selected' | 'prerequisite' | 'fixture';
14
+ /**
15
+ * A change included in the closure, with its category and the reason it was pulled.
16
+ */
17
+ export interface ClosureChange {
18
+ name: string;
19
+ category: ChangeCategory;
20
+ reason: ClosureReason;
21
+ }
22
+ /**
23
+ * The fixture closure of a schema slice.
24
+ *
25
+ * Everything the selected changes need to actually *work*: schema/functionality
26
+ * prerequisites they depend on, plus the fixtures that attach to them (policies,
27
+ * grants, RLS enables, role bindings, seed rows / SPRT-style support changes).
28
+ * References that nothing in the input produces are surfaced in `unresolved`
29
+ * rather than silently dropped.
30
+ */
31
+ export interface FixtureClosure {
32
+ /** Full closure (selection + prerequisites + fixtures) in input/plan order. */
33
+ order: string[];
34
+ /** Per-change detail in the same order as {@link order}. */
35
+ changes: ClosureChange[];
36
+ /** Closure members categorized as security/fixtures (attached fixtures). */
37
+ fixtures: string[];
38
+ /** Non-selected schema/functionality members pulled in as prerequisites. */
39
+ prerequisites: string[];
40
+ /** Roles required across the closure. */
41
+ roles: string[];
42
+ /** Requirements no input change produces โ€” explicit, never dropped. */
43
+ unresolved: {
44
+ objects: QualifiedName[];
45
+ schemas: string[];
46
+ roles: string[];
47
+ };
48
+ }
49
+ export interface ResolveFixtureClosureOptions {
50
+ /** Category profile used to label changes (default {@link TIER_PROFILE}). */
51
+ profile?: CategoryProfile;
52
+ /**
53
+ * When false, do not follow AST references from selected changes to their
54
+ * schema/functionality producers (only pull attached fixtures + plan deps).
55
+ * Default true.
56
+ */
57
+ includePrerequisites?: boolean;
58
+ }
59
+ /**
60
+ * Resolve the fixture closure of a schema slice.
61
+ *
62
+ * Pure and deterministic โ€” no I/O โ€” and runs to a fixpoint in both directions:
63
+ * - forward: a selected change pulls in the producers of the objects/schemas/
64
+ * roles it references, plus its declared plan dependencies (prerequisites);
65
+ * - reverse: any fixture (security/fixtures-tier change) that attaches to an
66
+ * object already in the closure is pulled in (attached fixtures), and then
67
+ * resolved for its own prerequisites.
68
+ *
69
+ * Output order follows the input (plan) order, so it stays deploy-safe.
70
+ */
71
+ export declare function resolveFixtureClosure(allChanges: ClosureInputChange[], selection: string[], options?: ResolveFixtureClosureOptions): FixtureClosure;
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveFixtureClosure = resolveFixtureClosure;
4
+ const categorize_1 = require("./categorize");
5
+ const transform_1 = require("@pgsql/transform");
6
+ function objectKey(schema, name) {
7
+ return `${schema ?? ''}\u0000${name}`;
8
+ }
9
+ function analyze(change, profile) {
10
+ const facts = (0, transform_1.classifyStatements)(change.sql);
11
+ const category = profile.categorize(facts, change.name);
12
+ const creates = [];
13
+ const createsSchemas = [];
14
+ const createsRoles = [];
15
+ const needsObjects = [];
16
+ const needsSchemas = new Set();
17
+ const needsRoles = new Set();
18
+ for (const f of facts) {
19
+ for (const c of f.creates) {
20
+ if (f.kind === 'schema')
21
+ createsSchemas.push(c.name);
22
+ else {
23
+ creates.push(c);
24
+ if (c.schema)
25
+ needsSchemas.add(c.schema);
26
+ }
27
+ }
28
+ if (f.nodeTag === 'CreateRoleStmt')
29
+ createsRoles.push(...f.roles);
30
+ for (const r of [...f.references, ...f.fkTargets]) {
31
+ if (r.schema)
32
+ needsObjects.push(r);
33
+ }
34
+ for (const s of f.referencedSchemas)
35
+ needsSchemas.add(s);
36
+ }
37
+ const created = new Set(createsRoles);
38
+ for (const f of facts) {
39
+ if (f.nodeTag === 'CreateRoleStmt')
40
+ continue;
41
+ for (const role of f.roles)
42
+ if (!created.has(role))
43
+ needsRoles.add(role);
44
+ }
45
+ for (const s of createsSchemas)
46
+ needsSchemas.delete(s);
47
+ return {
48
+ name: change.name,
49
+ category,
50
+ facts,
51
+ dependencies: change.dependencies ?? [],
52
+ creates,
53
+ createsSchemas,
54
+ createsRoles,
55
+ needsObjects,
56
+ needsSchemas: [...needsSchemas],
57
+ needsRoles: [...needsRoles],
58
+ isFixture: category === 'security' || category === 'fixtures'
59
+ };
60
+ }
61
+ /**
62
+ * Resolve the fixture closure of a schema slice.
63
+ *
64
+ * Pure and deterministic โ€” no I/O โ€” and runs to a fixpoint in both directions:
65
+ * - forward: a selected change pulls in the producers of the objects/schemas/
66
+ * roles it references, plus its declared plan dependencies (prerequisites);
67
+ * - reverse: any fixture (security/fixtures-tier change) that attaches to an
68
+ * object already in the closure is pulled in (attached fixtures), and then
69
+ * resolved for its own prerequisites.
70
+ *
71
+ * Output order follows the input (plan) order, so it stays deploy-safe.
72
+ */
73
+ function resolveFixtureClosure(allChanges, selection, options = {}) {
74
+ const profile = options.profile ?? categorize_1.TIER_PROFILE;
75
+ const includePrerequisites = options.includePrerequisites ?? true;
76
+ const analyses = allChanges.map(c => analyze(c, profile));
77
+ const byName = new Map(analyses.map(a => [a.name, a]));
78
+ const producerByObject = new Map();
79
+ const producerBySchema = new Map();
80
+ const producerByRole = new Map();
81
+ for (const a of analyses) {
82
+ for (const c of a.creates) {
83
+ const key = objectKey(c.schema, c.name);
84
+ if (!producerByObject.has(key))
85
+ producerByObject.set(key, a.name);
86
+ }
87
+ for (const s of a.createsSchemas) {
88
+ if (!producerBySchema.has(s))
89
+ producerBySchema.set(s, a.name);
90
+ }
91
+ for (const r of a.createsRoles) {
92
+ if (!producerByRole.has(r))
93
+ producerByRole.set(r, a.name);
94
+ }
95
+ }
96
+ const reason = new Map();
97
+ for (const name of selection) {
98
+ if (byName.has(name))
99
+ reason.set(name, 'selected');
100
+ }
101
+ const producedObjectKeys = new Set();
102
+ const producedSchemas = new Set();
103
+ const refreshProduced = () => {
104
+ producedObjectKeys.clear();
105
+ producedSchemas.clear();
106
+ for (const name of reason.keys()) {
107
+ const a = byName.get(name);
108
+ for (const c of a.creates)
109
+ producedObjectKeys.add(objectKey(c.schema, c.name));
110
+ for (const s of a.createsSchemas)
111
+ producedSchemas.add(s);
112
+ }
113
+ };
114
+ let changed = true;
115
+ while (changed) {
116
+ changed = false;
117
+ // forward: pull producers of what current members need
118
+ for (const name of [...reason.keys()]) {
119
+ const a = byName.get(name);
120
+ const pull = (producer, r) => {
121
+ if (!producer || reason.has(producer))
122
+ return;
123
+ const pa = byName.get(producer);
124
+ reason.set(producer, pa.isFixture ? 'fixture' : r);
125
+ changed = true;
126
+ };
127
+ for (const dep of a.dependencies) {
128
+ pull(byName.has(dep) ? dep : undefined, 'prerequisite');
129
+ }
130
+ if (!includePrerequisites)
131
+ continue;
132
+ for (const o of a.needsObjects)
133
+ pull(producerByObject.get(objectKey(o.schema, o.name)), 'prerequisite');
134
+ for (const s of a.needsSchemas)
135
+ pull(producerBySchema.get(s), 'prerequisite');
136
+ for (const role of a.needsRoles)
137
+ pull(producerByRole.get(role), 'prerequisite');
138
+ }
139
+ // reverse: pull attached fixtures that target current members
140
+ refreshProduced();
141
+ for (const a of analyses) {
142
+ if (reason.has(a.name) || !a.isFixture)
143
+ continue;
144
+ const objectAttaches = a.needsObjects.some(o => producedObjectKeys.has(objectKey(o.schema, o.name))) ||
145
+ a.creates.some(c => producedObjectKeys.has(objectKey(c.schema, c.name)));
146
+ // Schema-scoped fixtures (e.g. ALTER DEFAULT PRIVILEGES IN SCHEMA x) name no
147
+ // specific object; attach them by schema. Object-scoped fixtures attach only
148
+ // via their object refs, so a shared schema does not drag in every fixture.
149
+ const schemaScoped = a.needsObjects.length === 0 && a.creates.length === 0;
150
+ const schemaAttaches = schemaScoped &&
151
+ (a.needsSchemas.some(s => producedSchemas.has(s)) ||
152
+ a.createsSchemas.some(s => producedSchemas.has(s)));
153
+ if (objectAttaches || schemaAttaches) {
154
+ reason.set(a.name, 'fixture');
155
+ changed = true;
156
+ }
157
+ }
158
+ }
159
+ const order = analyses.filter(a => reason.has(a.name)).map(a => a.name);
160
+ const changes = order.map(name => {
161
+ const a = byName.get(name);
162
+ return { name, category: a.category, reason: reason.get(name) };
163
+ });
164
+ const roles = new Set();
165
+ const unresolvedObjects = [];
166
+ const unresolvedObjectKeys = new Set();
167
+ const unresolvedSchemas = new Set();
168
+ const unresolvedRoles = new Set();
169
+ for (const name of order) {
170
+ const a = byName.get(name);
171
+ for (const role of a.needsRoles) {
172
+ roles.add(role);
173
+ if (!producerByRole.has(role))
174
+ unresolvedRoles.add(role);
175
+ }
176
+ for (const o of a.needsObjects) {
177
+ const key = objectKey(o.schema, o.name);
178
+ if (!producerByObject.has(key) && !unresolvedObjectKeys.has(key)) {
179
+ unresolvedObjectKeys.add(key);
180
+ unresolvedObjects.push(o);
181
+ }
182
+ }
183
+ for (const s of a.needsSchemas) {
184
+ if (!producerBySchema.has(s))
185
+ unresolvedSchemas.add(s);
186
+ }
187
+ }
188
+ return {
189
+ order,
190
+ changes,
191
+ fixtures: changes.filter(c => c.reason === 'fixture').map(c => c.name),
192
+ prerequisites: changes.filter(c => c.reason === 'prerequisite').map(c => c.name),
193
+ roles: [...roles],
194
+ unresolved: {
195
+ objects: unresolvedObjects,
196
+ schemas: [...unresolvedSchemas],
197
+ roles: [...unresolvedRoles]
198
+ }
199
+ };
200
+ }
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from '@pgsql/transform';
2
+ export type { BundleScriptContext, NamespaceValidatorOptions, SchemaTranspiler, SchemaTranspilerOptions, } from './bundle-driver';
3
+ export { makeNamespaceValidator, makeSchemaTranspiler, } from './bundle-driver';
4
+ export type { CategoryProfile, ChangeCategory, } from './categorize';
5
+ export { buildCategoryOf, categorizeChange, TIER_PROFILE, } from './categorize';
6
+ export type { ClosureChange, ClosureInputChange, ClosureReason, FixtureClosure, ResolveFixtureClosureOptions, } from './fixture-closure';
7
+ export { resolveFixtureClosure } from './fixture-closure';
package/index.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.resolveFixtureClosure = exports.TIER_PROFILE = exports.categorizeChange = exports.buildCategoryOf = exports.makeSchemaTranspiler = exports.makeNamespaceValidator = void 0;
18
+ __exportStar(require("@pgsql/transform"), exports);
19
+ var bundle_driver_1 = require("./bundle-driver");
20
+ Object.defineProperty(exports, "makeNamespaceValidator", { enumerable: true, get: function () { return bundle_driver_1.makeNamespaceValidator; } });
21
+ Object.defineProperty(exports, "makeSchemaTranspiler", { enumerable: true, get: function () { return bundle_driver_1.makeSchemaTranspiler; } });
22
+ var categorize_1 = require("./categorize");
23
+ Object.defineProperty(exports, "buildCategoryOf", { enumerable: true, get: function () { return categorize_1.buildCategoryOf; } });
24
+ Object.defineProperty(exports, "categorizeChange", { enumerable: true, get: function () { return categorize_1.categorizeChange; } });
25
+ Object.defineProperty(exports, "TIER_PROFILE", { enumerable: true, get: function () { return categorize_1.TIER_PROFILE; } });
26
+ var fixture_closure_1 = require("./fixture-closure");
27
+ Object.defineProperty(exports, "resolveFixtureClosure", { enumerable: true, get: function () { return fixture_closure_1.resolveFixtureClosure; } });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@pgpmjs/transform",
3
+ "version": "0.2.0",
4
+ "author": "Constructive <developers@constructive.io>",
5
+ "description": "Change-aware SQL transformation for PGPM โ€” superset of @pgsql/transform plus change categorization, fixture closure, and bundle transpile/apply drivers",
6
+ "main": "index.js",
7
+ "module": "esm/index.js",
8
+ "types": "index.d.ts",
9
+ "homepage": "https://github.com/constructive-io/constructive",
10
+ "license": "MIT",
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "directory": "dist"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/constructive-io/constructive"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/constructive-io/constructive/issues"
21
+ },
22
+ "scripts": {
23
+ "clean": "makage clean",
24
+ "prepack": "npm run build",
25
+ "build": "makage build",
26
+ "build:dev": "makage build --dev",
27
+ "lint": "eslint . --fix",
28
+ "test": "jest",
29
+ "test:watch": "jest --watch"
30
+ },
31
+ "keywords": [
32
+ "sql",
33
+ "postgres",
34
+ "postgresql",
35
+ "pgpm",
36
+ "pgpmjs",
37
+ "transform",
38
+ "classify",
39
+ "plpgsql"
40
+ ],
41
+ "devDependencies": {
42
+ "@pgpmjs/bundle": "^0.3.2",
43
+ "makage": "^0.3.0"
44
+ },
45
+ "dependencies": {
46
+ "@pgsql/transform": "^18.3.1",
47
+ "plpgsql-parser": "^18.2.1"
48
+ },
49
+ "gitHead": "cc1b2af2c73c23b99a221e6bd8a8ba76c7ce79e6"
50
+ }