@supabase/pg-delta 1.0.0-alpha.10 → 1.0.0-alpha.11
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/dist/cli/commands/declarative-export.js +12 -17
- package/dist/cli/commands/plan.js +10 -13
- package/dist/cli/commands/sync.js +8 -12
- package/dist/cli/utils/integrations.d.ts +30 -6
- package/dist/cli/utils/integrations.js +98 -6
- package/dist/core/change-utils.d.ts +9 -0
- package/dist/core/change-utils.js +71 -0
- package/dist/core/change.types.d.ts +22 -0
- package/dist/core/change.types.js +37 -1
- package/dist/core/depend.js +25 -0
- package/dist/core/export/file-mapper.d.ts +2 -2
- package/dist/core/integrations/filter/dsl.d.ts +78 -74
- package/dist/core/integrations/filter/dsl.js +127 -79
- package/dist/core/integrations/filter/flatten.d.ts +51 -0
- package/dist/core/integrations/filter/flatten.js +116 -0
- package/dist/core/integrations/integration-dsl.d.ts +17 -1
- package/dist/core/integrations/merge.d.ts +20 -0
- package/dist/core/integrations/merge.js +60 -0
- package/dist/core/integrations/serialize/dsl.d.ts +7 -4
- package/dist/core/integrations/serialize/dsl.js +2 -2
- package/dist/core/integrations/supabase.js +23 -8
- package/dist/core/objects/aggregate/changes/aggregate.types.d.ts +1 -0
- package/dist/core/objects/base.change.d.ts +10 -0
- package/dist/core/objects/base.change.js +10 -0
- package/dist/core/objects/base.model.d.ts +4 -1
- package/dist/core/objects/base.model.js +5 -2
- package/dist/core/objects/collation/changes/collation.types.d.ts +1 -0
- package/dist/core/objects/domain/changes/domain.create.d.ts +1 -1
- package/dist/core/objects/domain/changes/domain.create.js +7 -1
- package/dist/core/objects/domain/changes/domain.types.d.ts +1 -0
- package/dist/core/objects/event-trigger/changes/event-trigger.types.d.ts +1 -0
- package/dist/core/objects/extension/changes/extension.types.d.ts +1 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.d.ts +1 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.d.ts +1 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts +1 -0
- package/dist/core/objects/foreign-data-wrapper/server/changes/server.types.d.ts +1 -0
- package/dist/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.d.ts +1 -0
- package/dist/core/objects/index/changes/index.types.d.ts +1 -0
- package/dist/core/objects/language/changes/language.types.d.ts +1 -0
- package/dist/core/objects/materialized-view/changes/materialized-view.types.d.ts +1 -0
- package/dist/core/objects/procedure/changes/procedure.types.d.ts +1 -0
- package/dist/core/objects/publication/changes/publication.types.d.ts +1 -0
- package/dist/core/objects/rls-policy/changes/rls-policy.types.d.ts +1 -0
- package/dist/core/objects/role/changes/role.types.d.ts +1 -0
- package/dist/core/objects/rule/changes/rule.types.d.ts +1 -0
- package/dist/core/objects/schema/changes/schema.types.d.ts +1 -0
- package/dist/core/objects/sequence/changes/sequence.types.d.ts +1 -0
- package/dist/core/objects/subscription/changes/subscription.types.d.ts +1 -0
- package/dist/core/objects/table/changes/table.types.d.ts +1 -0
- package/dist/core/objects/trigger/changes/trigger.types.d.ts +1 -0
- package/dist/core/objects/type/composite-type/changes/composite-type.types.d.ts +1 -0
- package/dist/core/objects/type/enum/changes/enum.types.d.ts +1 -0
- package/dist/core/objects/type/range/changes/range.types.d.ts +1 -0
- package/dist/core/objects/type/type.types.d.ts +1 -0
- package/dist/core/objects/view/changes/view.types.d.ts +1 -0
- package/dist/core/objects/view/view.diff.js +24 -13
- package/dist/core/postgres-config.d.ts +2 -2
- package/dist/core/sort/custom-constraints.js +1 -1
- package/dist/core/sort/logical-sort.js +3 -24
- package/package.json +5 -1
- package/src/cli/commands/declarative-export.ts +19 -27
- package/src/cli/commands/plan.ts +14 -20
- package/src/cli/commands/sync.ts +8 -15
- package/src/cli/utils/integrations.test.ts +210 -3
- package/src/cli/utils/integrations.ts +134 -6
- package/src/core/catalog.snapshot.test.ts +11 -2
- package/src/core/change-utils.test.ts +61 -0
- package/src/core/change-utils.ts +73 -0
- package/src/core/change.types.ts +50 -0
- package/src/core/depend.ts +25 -0
- package/src/core/export/file-mapper.ts +7 -2
- package/src/core/integrations/filter/dsl.test.ts +299 -60
- package/src/core/integrations/filter/dsl.ts +208 -169
- package/src/core/integrations/filter/flatten.test.ts +282 -0
- package/src/core/integrations/filter/flatten.ts +150 -0
- package/src/core/integrations/integration-dsl.ts +17 -1
- package/src/core/integrations/merge.test.ts +128 -0
- package/src/core/integrations/merge.ts +72 -0
- package/src/core/integrations/serialize/dsl.test.ts +6 -6
- package/src/core/integrations/serialize/dsl.ts +7 -4
- package/src/core/integrations/supabase.ts +23 -8
- package/src/core/objects/aggregate/changes/aggregate.types.ts +1 -0
- package/src/core/objects/base.change.ts +10 -0
- package/src/core/objects/base.model.test.ts +43 -0
- package/src/core/objects/base.model.ts +5 -2
- package/src/core/objects/collation/changes/collation.types.ts +1 -0
- package/src/core/objects/domain/changes/domain.create.ts +17 -1
- package/src/core/objects/domain/changes/domain.types.ts +1 -0
- package/src/core/objects/event-trigger/changes/event-trigger.types.ts +1 -0
- package/src/core/objects/extension/changes/extension.types.ts +1 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.ts +1 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.ts +1 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +1 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.types.ts +1 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.ts +1 -0
- package/src/core/objects/index/changes/index.types.ts +1 -0
- package/src/core/objects/language/changes/language.types.ts +1 -0
- package/src/core/objects/materialized-view/changes/materialized-view.types.ts +1 -0
- package/src/core/objects/procedure/changes/procedure.types.ts +1 -0
- package/src/core/objects/publication/changes/publication.types.ts +1 -0
- package/src/core/objects/rls-policy/changes/rls-policy.types.ts +1 -0
- package/src/core/objects/role/changes/role.types.ts +1 -0
- package/src/core/objects/rule/changes/rule.types.ts +1 -0
- package/src/core/objects/schema/changes/schema.types.ts +1 -0
- package/src/core/objects/sequence/changes/sequence.types.ts +1 -0
- package/src/core/objects/subscription/changes/subscription.types.ts +1 -0
- package/src/core/objects/table/changes/table.types.ts +1 -0
- package/src/core/objects/trigger/changes/trigger.types.ts +1 -0
- package/src/core/objects/type/composite-type/changes/composite-type.types.ts +1 -0
- package/src/core/objects/type/enum/changes/enum.types.ts +1 -0
- package/src/core/objects/type/range/changes/range.types.ts +1 -0
- package/src/core/objects/type/type.types.ts +1 -0
- package/src/core/objects/view/changes/view.types.ts +1 -0
- package/src/core/objects/view/view.diff.test.ts +96 -0
- package/src/core/objects/view/view.diff.ts +30 -15
- package/src/core/postgres-config.ts +2 -2
- package/src/core/sort/custom-constraints.ts +1 -1
- package/src/core/sort/logical-sort.ts +3 -27
- package/src/typedoc.ts +248 -0
- package/dist/core/integrations/filter/extractors.d.ts +0 -12
- package/dist/core/integrations/filter/extractors.js +0 -178
- package/src/core/integrations/filter/extractors.test.ts +0 -244
- package/src/core/integrations/filter/extractors.ts +0 -187
|
@@ -5,10 +5,12 @@ import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { buildCommand } from "@stricli/core";
|
|
7
7
|
import chalk from "chalk";
|
|
8
|
+
import { deserializeCatalog } from "../../core/catalog.snapshot.js";
|
|
8
9
|
import { exportDeclarativeSchema } from "../../core/export/index.js";
|
|
10
|
+
import { compileSerializeDSL, } from "../../core/integrations/serialize/dsl.js";
|
|
9
11
|
import { createPlan } from "../../core/plan/index.js";
|
|
10
12
|
import { assertSafePath, buildFileTree, computeFileDiff, formatExportSummary, } from "../utils/export-display.js";
|
|
11
|
-
import {
|
|
13
|
+
import { resolveIntegrationOptions } from "../utils/integrations.js";
|
|
12
14
|
import { isPostgresUrl, loadCatalogFromFile } from "../utils/resolve-input.js";
|
|
13
15
|
function parseJsonFlag(label, value) {
|
|
14
16
|
try {
|
|
@@ -142,22 +144,17 @@ After export, a tip is printed with the command to apply the schema to an empty
|
|
|
142
144
|
`.trim(),
|
|
143
145
|
},
|
|
144
146
|
async func(flags) {
|
|
145
|
-
const {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const integrationDSL = await loadIntegrationDSL(flags.integration);
|
|
151
|
-
filterOption = filterOption ?? integrationDSL.filter;
|
|
152
|
-
serializeOption = serializeOption ?? integrationDSL.serialize;
|
|
153
|
-
integrationEmptyCatalog = integrationDSL.emptyCatalog;
|
|
154
|
-
}
|
|
147
|
+
const { filter, serialize, emptyCatalog: integrationEmptyCatalog, } = await resolveIntegrationOptions({
|
|
148
|
+
filter: flags.filter,
|
|
149
|
+
serialize: flags.serialize,
|
|
150
|
+
integration: flags.integration,
|
|
151
|
+
});
|
|
155
152
|
const resolvedSource = flags.source
|
|
156
153
|
? isPostgresUrl(flags.source)
|
|
157
154
|
? flags.source
|
|
158
155
|
: await loadCatalogFromFile(flags.source)
|
|
159
156
|
: integrationEmptyCatalog
|
|
160
|
-
?
|
|
157
|
+
? deserializeCatalog(integrationEmptyCatalog)
|
|
161
158
|
: null;
|
|
162
159
|
const resolvedTarget = isPostgresUrl(flags.target)
|
|
163
160
|
? flags.target
|
|
@@ -169,8 +166,8 @@ After export, a tip is printed with the command to apply the schema to an empty
|
|
|
169
166
|
// changes that depend on filtered objects (e.g. RLS policies that
|
|
170
167
|
// reference auth.uid() when the auth schema is filtered out).
|
|
171
168
|
const planResult = await createPlan(resolvedSource, resolvedTarget, {
|
|
172
|
-
filter
|
|
173
|
-
serialize
|
|
169
|
+
filter,
|
|
170
|
+
serialize,
|
|
174
171
|
skipDefaultPrivilegeSubtraction: true,
|
|
175
172
|
});
|
|
176
173
|
if (!planResult) {
|
|
@@ -195,9 +192,7 @@ After export, a tip is printed with the command to apply the schema to an empty
|
|
|
195
192
|
: undefined,
|
|
196
193
|
};
|
|
197
194
|
}
|
|
198
|
-
const serializeFn =
|
|
199
|
-
? compileSerializeDSL(serializeOption)
|
|
200
|
-
: undefined;
|
|
195
|
+
const serializeFn = serialize !== undefined ? compileSerializeDSL(serialize) : undefined;
|
|
201
196
|
const output = exportDeclarativeSchema(planResult, {
|
|
202
197
|
integration: serializeFn !== undefined ? { serialize: serializeFn } : undefined,
|
|
203
198
|
formatOptions: flags["format-options"] ?? undefined,
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { writeFile } from "node:fs/promises";
|
|
5
5
|
import { buildCommand } from "@stricli/core";
|
|
6
|
+
import { deserializeCatalog } from "../../core/catalog.snapshot.js";
|
|
6
7
|
import { createPlan } from "../../core/plan/index.js";
|
|
7
8
|
import { setCommandExitCode } from "../exit-code.js";
|
|
8
|
-
import {
|
|
9
|
+
import { resolveIntegrationOptions } from "../utils/integrations.js";
|
|
9
10
|
import { isPostgresUrl, loadCatalogFromFile } from "../utils/resolve-input.js";
|
|
10
11
|
import { formatPlanForDisplay } from "../utils.js";
|
|
11
12
|
export const planCommand = buildCommand({
|
|
@@ -106,29 +107,25 @@ json/sql outputs are available for artifacts or piping.
|
|
|
106
107
|
`.trim(),
|
|
107
108
|
},
|
|
108
109
|
async func(flags) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
filterOption = filterOption ?? integrationDSL.filter;
|
|
115
|
-
serializeOption = serializeOption ?? integrationDSL.serialize;
|
|
116
|
-
integrationEmptyCatalog = integrationDSL.emptyCatalog;
|
|
117
|
-
}
|
|
110
|
+
const { filter, serialize, emptyCatalog: integrationEmptyCatalog, } = await resolveIntegrationOptions({
|
|
111
|
+
filter: flags.filter,
|
|
112
|
+
serialize: flags.serialize,
|
|
113
|
+
integration: flags.integration,
|
|
114
|
+
});
|
|
118
115
|
const resolvedSource = flags.source
|
|
119
116
|
? isPostgresUrl(flags.source)
|
|
120
117
|
? flags.source
|
|
121
118
|
: await loadCatalogFromFile(flags.source)
|
|
122
119
|
: integrationEmptyCatalog
|
|
123
|
-
?
|
|
120
|
+
? deserializeCatalog(integrationEmptyCatalog)
|
|
124
121
|
: null;
|
|
125
122
|
const resolvedTarget = isPostgresUrl(flags.target)
|
|
126
123
|
? flags.target
|
|
127
124
|
: await loadCatalogFromFile(flags.target);
|
|
128
125
|
const planResult = await createPlan(resolvedSource, resolvedTarget, {
|
|
129
126
|
role: flags.role,
|
|
130
|
-
filter
|
|
131
|
-
serialize
|
|
127
|
+
filter,
|
|
128
|
+
serialize,
|
|
132
129
|
});
|
|
133
130
|
if (!planResult) {
|
|
134
131
|
this.process.stdout.write("No changes detected.\n");
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { buildCommand } from "@stricli/core";
|
|
5
5
|
import { applyPlan } from "../../core/plan/apply.js";
|
|
6
6
|
import { createPlan } from "../../core/plan/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveIntegrationOptions } from "../utils/integrations.js";
|
|
8
8
|
import { formatPlanForDisplay, handleApplyResult, promptConfirmation, validatePlanRisk, } from "../utils.js";
|
|
9
9
|
export const syncCommand = buildCommand({
|
|
10
10
|
parameters: {
|
|
@@ -91,20 +91,16 @@ Exit codes:
|
|
|
91
91
|
`.trim(),
|
|
92
92
|
},
|
|
93
93
|
async func(flags) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Use integration DSL if explicit flags not provided
|
|
100
|
-
filterOption = filterOption ?? integrationDSL.filter;
|
|
101
|
-
serializeOption = serializeOption ?? integrationDSL.serialize;
|
|
102
|
-
}
|
|
94
|
+
const { filter, serialize } = await resolveIntegrationOptions({
|
|
95
|
+
filter: flags.filter,
|
|
96
|
+
serialize: flags.serialize,
|
|
97
|
+
integration: flags.integration,
|
|
98
|
+
});
|
|
103
99
|
// 1. Create the plan
|
|
104
100
|
const planResult = await createPlan(flags.source, flags.target, {
|
|
105
101
|
role: flags.role,
|
|
106
|
-
filter
|
|
107
|
-
serialize
|
|
102
|
+
filter,
|
|
103
|
+
serialize,
|
|
108
104
|
});
|
|
109
105
|
if (!planResult) {
|
|
110
106
|
this.process.stdout.write("No changes detected.\n");
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilities for loading integrations from files.
|
|
3
3
|
*/
|
|
4
|
+
import type { CatalogSnapshot } from "../../core/catalog.snapshot.ts";
|
|
5
|
+
import type { FilterDSL } from "../../core/integrations/filter/dsl.ts";
|
|
4
6
|
import type { IntegrationDSL } from "../../core/integrations/integration-dsl.ts";
|
|
7
|
+
import type { SerializeDSL } from "../../core/integrations/serialize/dsl.ts";
|
|
5
8
|
/**
|
|
6
|
-
* Load an integration DSL
|
|
7
|
-
* If the path ends with .json, treats it as a JSON file path directly.
|
|
8
|
-
* Otherwise, tries to load from core integrations (TypeScript) first,
|
|
9
|
-
* then falls back to treating as a JSON file path.
|
|
9
|
+
* Load an integration DSL, recursively resolving `extends` chains.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* When an integration has `extends`, the referenced integration(s) are loaded
|
|
12
|
+
* and merged: filters are AND-combined, serialize rules concatenated (base first),
|
|
13
|
+
* and emptyCatalog uses the most-specific value.
|
|
14
|
+
*
|
|
15
|
+
* Circular extends are detected and rejected with a descriptive error.
|
|
16
|
+
*
|
|
17
|
+
* @param nameOrPath - Integration name (e.g., "supabase") or file path
|
|
18
|
+
* @returns The fully resolved IntegrationDSL
|
|
13
19
|
*/
|
|
14
20
|
export declare function loadIntegrationDSL(nameOrPath: string): Promise<IntegrationDSL>;
|
|
21
|
+
interface ResolvedIntegrationOptions {
|
|
22
|
+
filter?: FilterDSL;
|
|
23
|
+
serialize?: SerializeDSL;
|
|
24
|
+
emptyCatalog?: CatalogSnapshot;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load an integration (if provided) and merge its filter/serialize with CLI flags.
|
|
28
|
+
*
|
|
29
|
+
* - Filters are AND-combined (integration ∧ CLI flag)
|
|
30
|
+
* - Serialize rules are concatenated (integration first = higher priority)
|
|
31
|
+
* - emptyCatalog is extracted from the integration
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveIntegrationOptions(options: {
|
|
34
|
+
filter?: FilterDSL;
|
|
35
|
+
serialize?: SerializeDSL;
|
|
36
|
+
integration?: string;
|
|
37
|
+
}): Promise<ResolvedIntegrationOptions>;
|
|
38
|
+
export {};
|
|
@@ -10,16 +10,14 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
10
10
|
return path;
|
|
11
11
|
};
|
|
12
12
|
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { mergeIntegrations } from "../../core/integrations/merge.js";
|
|
13
14
|
/**
|
|
14
|
-
* Load
|
|
15
|
-
* If the path ends with .json, treats it as a JSON file path directly.
|
|
16
|
-
* Otherwise, tries to load from core integrations (TypeScript) first,
|
|
17
|
-
* then falls back to treating as a JSON file path.
|
|
15
|
+
* Load a raw integration DSL from a file or core integration (without resolving extends).
|
|
18
16
|
*
|
|
19
17
|
* @param nameOrPath - Integration name (e.g., "supabase") or file path (e.g., "./my-integration.json")
|
|
20
|
-
* @returns The loaded IntegrationDSL
|
|
18
|
+
* @returns The loaded IntegrationDSL (unresolved)
|
|
21
19
|
*/
|
|
22
|
-
|
|
20
|
+
async function loadRawIntegrationDSL(nameOrPath) {
|
|
23
21
|
// If path ends with .json, treat it as a JSON file path directly
|
|
24
22
|
if (nameOrPath.endsWith(".json")) {
|
|
25
23
|
const content = await readFile(nameOrPath, "utf-8");
|
|
@@ -42,3 +40,97 @@ export async function loadIntegrationDSL(nameOrPath) {
|
|
|
42
40
|
const content = await readFile(nameOrPath, "utf-8");
|
|
43
41
|
return JSON.parse(content);
|
|
44
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Load a core integration DSL by name only (no file path fallback).
|
|
45
|
+
*
|
|
46
|
+
* Used for resolving `extends` chains, which only support core integration names
|
|
47
|
+
* (e.g., "supabase"), not file paths.
|
|
48
|
+
*
|
|
49
|
+
* @param name - Core integration name (e.g., "supabase")
|
|
50
|
+
* @returns The loaded IntegrationDSL (unresolved)
|
|
51
|
+
*/
|
|
52
|
+
async function loadCoreIntegrationDSL(name) {
|
|
53
|
+
try {
|
|
54
|
+
const module = await import(__rewriteRelativeImportExtension(`../../core/integrations/${name}.ts`));
|
|
55
|
+
if (name in module) {
|
|
56
|
+
return module[name];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Module not found
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Unknown core integration: "${name}". extends only supports core integration names (e.g., "supabase").`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Load an integration DSL, recursively resolving `extends` chains.
|
|
66
|
+
*
|
|
67
|
+
* When an integration has `extends`, the referenced integration(s) are loaded
|
|
68
|
+
* and merged: filters are AND-combined, serialize rules concatenated (base first),
|
|
69
|
+
* and emptyCatalog uses the most-specific value.
|
|
70
|
+
*
|
|
71
|
+
* Circular extends are detected and rejected with a descriptive error.
|
|
72
|
+
*
|
|
73
|
+
* @param nameOrPath - Integration name (e.g., "supabase") or file path
|
|
74
|
+
* @returns The fully resolved IntegrationDSL
|
|
75
|
+
*/
|
|
76
|
+
export async function loadIntegrationDSL(nameOrPath) {
|
|
77
|
+
return resolveIntegration(nameOrPath, new Set());
|
|
78
|
+
}
|
|
79
|
+
async function resolveIntegration(nameOrPath, visited, preloadedRaw) {
|
|
80
|
+
if (visited.has(nameOrPath)) {
|
|
81
|
+
throw new Error(`Circular extends detected: ${[...visited, nameOrPath].join(" → ")}`);
|
|
82
|
+
}
|
|
83
|
+
visited.add(nameOrPath);
|
|
84
|
+
const raw = preloadedRaw ?? (await loadRawIntegrationDSL(nameOrPath));
|
|
85
|
+
if (!raw.extends) {
|
|
86
|
+
return raw;
|
|
87
|
+
}
|
|
88
|
+
// Resolve base integrations (extends only supports core integration names)
|
|
89
|
+
const extendsArray = Array.isArray(raw.extends) ? raw.extends : [raw.extends];
|
|
90
|
+
const baseIntegrations = [];
|
|
91
|
+
for (const baseName of extendsArray) {
|
|
92
|
+
const baseRaw = await loadCoreIntegrationDSL(baseName);
|
|
93
|
+
baseIntegrations.push(await resolveIntegration(baseName, new Set(visited), baseRaw));
|
|
94
|
+
}
|
|
95
|
+
// Remove extends from the current integration before merging
|
|
96
|
+
const { extends: _, ...current } = raw;
|
|
97
|
+
// Merge: bases first (higher priority serialize), then current (most-specific)
|
|
98
|
+
return mergeIntegrations([...baseIntegrations, current]);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Load an integration (if provided) and merge its filter/serialize with CLI flags.
|
|
102
|
+
*
|
|
103
|
+
* - Filters are AND-combined (integration ∧ CLI flag)
|
|
104
|
+
* - Serialize rules are concatenated (integration first = higher priority)
|
|
105
|
+
* - emptyCatalog is extracted from the integration
|
|
106
|
+
*/
|
|
107
|
+
export async function resolveIntegrationOptions(options) {
|
|
108
|
+
if (!options.integration) {
|
|
109
|
+
return {
|
|
110
|
+
filter: options.filter,
|
|
111
|
+
serialize: options.serialize,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const integrationDSL = await loadIntegrationDSL(options.integration);
|
|
115
|
+
// AND-combine integration filter with CLI --filter
|
|
116
|
+
let filter;
|
|
117
|
+
if (integrationDSL.filter && options.filter) {
|
|
118
|
+
filter = { and: [integrationDSL.filter, options.filter] };
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
filter = options.filter ?? integrationDSL.filter;
|
|
122
|
+
}
|
|
123
|
+
// Concatenate serialize rules (integration first = higher priority)
|
|
124
|
+
let serialize;
|
|
125
|
+
if (integrationDSL.serialize && options.serialize) {
|
|
126
|
+
serialize = [...integrationDSL.serialize, ...options.serialize];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
serialize = options.serialize ?? integrationDSL.serialize;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
filter,
|
|
133
|
+
serialize,
|
|
134
|
+
emptyCatalog: integrationDSL.emptyCatalog,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Change } from "./change.types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Extract the schema name from a Change using the model sub-object.
|
|
4
|
+
*
|
|
5
|
+
* This is a convenience function used by the filter DSL (for schema
|
|
6
|
+
* normalization) and the sort module. It reads the `schema` (or `name`
|
|
7
|
+
* for schema objectType) from the model sub-object.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getSchema(change: Change): string | null;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract the schema name from a Change using the model sub-object.
|
|
3
|
+
*
|
|
4
|
+
* This is a convenience function used by the filter DSL (for schema
|
|
5
|
+
* normalization) and the sort module. It reads the `schema` (or `name`
|
|
6
|
+
* for schema objectType) from the model sub-object.
|
|
7
|
+
*/
|
|
8
|
+
export function getSchema(change) {
|
|
9
|
+
if (change.scope === "default_privilege") {
|
|
10
|
+
return change.inSchema;
|
|
11
|
+
}
|
|
12
|
+
switch (change.objectType) {
|
|
13
|
+
case "aggregate":
|
|
14
|
+
return change.aggregate.schema;
|
|
15
|
+
case "collation":
|
|
16
|
+
return change.collation.schema;
|
|
17
|
+
case "composite_type":
|
|
18
|
+
return change.compositeType.schema;
|
|
19
|
+
case "domain":
|
|
20
|
+
return change.domain.schema;
|
|
21
|
+
case "enum":
|
|
22
|
+
return change.enum.schema;
|
|
23
|
+
case "event_trigger":
|
|
24
|
+
return change.eventTrigger.function_schema;
|
|
25
|
+
case "extension":
|
|
26
|
+
return change.extension.schema;
|
|
27
|
+
case "index":
|
|
28
|
+
return change.index.schema;
|
|
29
|
+
case "language":
|
|
30
|
+
return null;
|
|
31
|
+
case "materialized_view":
|
|
32
|
+
return change.materializedView.schema;
|
|
33
|
+
case "procedure":
|
|
34
|
+
return change.procedure.schema;
|
|
35
|
+
case "publication":
|
|
36
|
+
return null;
|
|
37
|
+
case "range":
|
|
38
|
+
return change.range.schema;
|
|
39
|
+
case "rls_policy":
|
|
40
|
+
return change.policy.schema;
|
|
41
|
+
case "role":
|
|
42
|
+
return null;
|
|
43
|
+
case "rule":
|
|
44
|
+
return change.rule.schema;
|
|
45
|
+
case "schema":
|
|
46
|
+
return change.schema.name;
|
|
47
|
+
case "sequence":
|
|
48
|
+
return change.sequence.schema;
|
|
49
|
+
case "subscription":
|
|
50
|
+
return null;
|
|
51
|
+
case "table":
|
|
52
|
+
return change.table.schema;
|
|
53
|
+
case "trigger":
|
|
54
|
+
return change.trigger.schema;
|
|
55
|
+
case "view":
|
|
56
|
+
return change.view.schema;
|
|
57
|
+
case "foreign_data_wrapper":
|
|
58
|
+
return null;
|
|
59
|
+
case "server":
|
|
60
|
+
return null;
|
|
61
|
+
case "user_mapping":
|
|
62
|
+
return null;
|
|
63
|
+
case "foreign_table":
|
|
64
|
+
return change.foreignTable.schema;
|
|
65
|
+
default: {
|
|
66
|
+
// exhaustiveness check
|
|
67
|
+
const _exhaustive = change;
|
|
68
|
+
return _exhaustive;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -19,4 +19,26 @@ import type { TableChange } from "./objects/table/changes/table.types.ts";
|
|
|
19
19
|
import type { TriggerChange } from "./objects/trigger/changes/trigger.types.ts";
|
|
20
20
|
import type { TypeChange } from "./objects/type/type.types.ts";
|
|
21
21
|
import type { ViewChange } from "./objects/view/changes/view.types.ts";
|
|
22
|
+
/**
|
|
23
|
+
* Discriminated union of all PostgreSQL object change types.
|
|
24
|
+
*
|
|
25
|
+
* Every member shares a common `objectType` discriminant (e.g. `"table"`,
|
|
26
|
+
* `"view"`, `"role"`) that the filter DSL pattern-matches against. Use
|
|
27
|
+
* {@link OBJECT_TYPE_TO_PROPERTY_KEY} to map an `objectType` value to the
|
|
28
|
+
* corresponding JS property key on the Change instance.
|
|
29
|
+
*
|
|
30
|
+
* @category Change Types
|
|
31
|
+
*/
|
|
22
32
|
export type Change = AggregateChange | CollationChange | DomainChange | ExtensionChange | IndexChange | LanguageChange | MaterializedViewChange | SubscriptionChange | PublicationChange | ProcedureChange | RlsPolicyChange | RoleChange | SchemaChange | SequenceChange | TableChange | TriggerChange | EventTriggerChange | RuleChange | TypeChange | ViewChange | ForeignDataWrapperChange;
|
|
33
|
+
/**
|
|
34
|
+
* Exhaustive map from every `objectType` discriminant value to the JS property
|
|
35
|
+
* key that holds the model sub-object on the corresponding {@link Change}.
|
|
36
|
+
*
|
|
37
|
+
* Used internally by the filter DSL flattening logic to locate nested
|
|
38
|
+
* properties and expose them as `<objectType>/<field>` paths.
|
|
39
|
+
*
|
|
40
|
+
* @category Change Types
|
|
41
|
+
*/
|
|
42
|
+
export declare const OBJECT_TYPE_TO_PROPERTY_KEY: {
|
|
43
|
+
[K in Change["objectType"]]: string;
|
|
44
|
+
};
|
|
@@ -1 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Exhaustive map from every `objectType` discriminant value to the JS property
|
|
3
|
+
* key that holds the model sub-object on the corresponding {@link Change}.
|
|
4
|
+
*
|
|
5
|
+
* Used internally by the filter DSL flattening logic to locate nested
|
|
6
|
+
* properties and expose them as `<objectType>/<field>` paths.
|
|
7
|
+
*
|
|
8
|
+
* @category Change Types
|
|
9
|
+
*/
|
|
10
|
+
export const OBJECT_TYPE_TO_PROPERTY_KEY = {
|
|
11
|
+
aggregate: "aggregate",
|
|
12
|
+
collation: "collation",
|
|
13
|
+
composite_type: "compositeType",
|
|
14
|
+
domain: "domain",
|
|
15
|
+
enum: "enum",
|
|
16
|
+
event_trigger: "eventTrigger",
|
|
17
|
+
extension: "extension",
|
|
18
|
+
foreign_data_wrapper: "foreignDataWrapper",
|
|
19
|
+
foreign_table: "foreignTable",
|
|
20
|
+
index: "index",
|
|
21
|
+
language: "language",
|
|
22
|
+
materialized_view: "materializedView",
|
|
23
|
+
procedure: "procedure",
|
|
24
|
+
publication: "publication",
|
|
25
|
+
range: "range",
|
|
26
|
+
rls_policy: "policy",
|
|
27
|
+
role: "role",
|
|
28
|
+
rule: "rule",
|
|
29
|
+
schema: "schema",
|
|
30
|
+
sequence: "sequence",
|
|
31
|
+
server: "server",
|
|
32
|
+
subscription: "subscription",
|
|
33
|
+
table: "table",
|
|
34
|
+
trigger: "trigger",
|
|
35
|
+
user_mapping: "userMapping",
|
|
36
|
+
view: "view",
|
|
37
|
+
};
|
package/dist/core/depend.js
CHANGED
|
@@ -1390,6 +1390,29 @@ export async function extractDepends(pool) {
|
|
|
1390
1390
|
JOIN pg_namespace ns ON ns.oid = idx_rel.relnamespace
|
|
1391
1391
|
WHERE idx_rel.relkind = 'i'
|
|
1392
1392
|
),
|
|
1393
|
+
index_extension_deps AS (
|
|
1394
|
+
-- Indexes depend on extensions that provide their operator classes
|
|
1395
|
+
-- (e.g. gin_trgm_ops from pg_trgm). Without this, CREATE INDEX can be
|
|
1396
|
+
-- ordered before CREATE EXTENSION when schemas sort alphabetically.
|
|
1397
|
+
SELECT DISTINCT
|
|
1398
|
+
format('index:%I.%I.%I', ns.nspname, tbl.relname, idx_rel.relname) AS dependent_stable_id,
|
|
1399
|
+
format('extension:%I', ext.extname) AS referenced_stable_id,
|
|
1400
|
+
'n'::"char" AS deptype,
|
|
1401
|
+
ns.nspname AS dep_schema,
|
|
1402
|
+
NULL::text AS ref_schema
|
|
1403
|
+
FROM pg_class idx_rel
|
|
1404
|
+
JOIN pg_index idx ON idx.indexrelid = idx_rel.oid
|
|
1405
|
+
JOIN pg_class tbl ON tbl.oid = idx.indrelid
|
|
1406
|
+
JOIN pg_namespace ns ON ns.oid = idx_rel.relnamespace
|
|
1407
|
+
JOIN LATERAL unnest(idx.indclass) WITH ORDINALITY AS oc(oid, ord) ON true
|
|
1408
|
+
JOIN pg_opclass opcl ON opcl.oid = oc.oid
|
|
1409
|
+
JOIN pg_depend d ON d.classid = 'pg_opclass'::regclass
|
|
1410
|
+
AND d.objid = opcl.oid
|
|
1411
|
+
AND d.refclassid = 'pg_extension'::regclass
|
|
1412
|
+
AND d.deptype = 'e'
|
|
1413
|
+
JOIN pg_extension ext ON ext.oid = d.refobjid
|
|
1414
|
+
WHERE idx_rel.relkind IN ('i', 'I')
|
|
1415
|
+
),
|
|
1393
1416
|
ownership_deps AS (
|
|
1394
1417
|
-- Schema ownership dependencies
|
|
1395
1418
|
SELECT DISTINCT
|
|
@@ -1803,6 +1826,8 @@ export async function extractDepends(pool) {
|
|
|
1803
1826
|
UNION ALL
|
|
1804
1827
|
SELECT dependent_stable_id, referenced_stable_id, deptype, dep_schema, ref_schema FROM index_table_deps
|
|
1805
1828
|
UNION ALL
|
|
1829
|
+
SELECT dependent_stable_id, referenced_stable_id, deptype, dep_schema, ref_schema FROM index_extension_deps
|
|
1830
|
+
UNION ALL
|
|
1806
1831
|
SELECT dependent_stable_id, referenced_stable_id, deptype, dep_schema, ref_schema FROM ownership_deps
|
|
1807
1832
|
UNION ALL
|
|
1808
1833
|
SELECT dependent_stable_id, referenced_stable_id, deptype, dep_schema, ref_schema FROM publication_deps
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Map changes to declarative schema file paths.
|
|
3
3
|
*/
|
|
4
4
|
import type { Change } from "../change.types.ts";
|
|
5
|
-
import type { FilePath, Grouping } from "./types.ts";
|
|
5
|
+
import type { FilePath, Grouping, GroupingPattern } from "./types.ts";
|
|
6
6
|
export declare function getFilePath(change: Change): FilePath;
|
|
7
7
|
/** A compiled grouping pattern: pre-built RegExp + group name. */
|
|
8
8
|
export interface CompiledPattern {
|
|
@@ -20,7 +20,7 @@ interface CompilePatternsResult {
|
|
|
20
20
|
* (no throw), so the returned `compiled` array may be shorter than the input.
|
|
21
21
|
* Any skipped patterns are reported in `warnings`.
|
|
22
22
|
*/
|
|
23
|
-
export declare function compilePatterns(patterns:
|
|
23
|
+
export declare function compilePatterns(patterns: GroupingPattern[]): CompilePatternsResult;
|
|
24
24
|
/**
|
|
25
25
|
* Create a file mapper that applies regex-based grouping on top of the
|
|
26
26
|
* default `getFilePath` mapping.
|