@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.
Files changed (123) hide show
  1. package/dist/cli/commands/declarative-export.js +12 -17
  2. package/dist/cli/commands/plan.js +10 -13
  3. package/dist/cli/commands/sync.js +8 -12
  4. package/dist/cli/utils/integrations.d.ts +30 -6
  5. package/dist/cli/utils/integrations.js +98 -6
  6. package/dist/core/change-utils.d.ts +9 -0
  7. package/dist/core/change-utils.js +71 -0
  8. package/dist/core/change.types.d.ts +22 -0
  9. package/dist/core/change.types.js +37 -1
  10. package/dist/core/depend.js +25 -0
  11. package/dist/core/export/file-mapper.d.ts +2 -2
  12. package/dist/core/integrations/filter/dsl.d.ts +78 -74
  13. package/dist/core/integrations/filter/dsl.js +127 -79
  14. package/dist/core/integrations/filter/flatten.d.ts +51 -0
  15. package/dist/core/integrations/filter/flatten.js +116 -0
  16. package/dist/core/integrations/integration-dsl.d.ts +17 -1
  17. package/dist/core/integrations/merge.d.ts +20 -0
  18. package/dist/core/integrations/merge.js +60 -0
  19. package/dist/core/integrations/serialize/dsl.d.ts +7 -4
  20. package/dist/core/integrations/serialize/dsl.js +2 -2
  21. package/dist/core/integrations/supabase.js +23 -8
  22. package/dist/core/objects/aggregate/changes/aggregate.types.d.ts +1 -0
  23. package/dist/core/objects/base.change.d.ts +10 -0
  24. package/dist/core/objects/base.change.js +10 -0
  25. package/dist/core/objects/base.model.d.ts +4 -1
  26. package/dist/core/objects/base.model.js +5 -2
  27. package/dist/core/objects/collation/changes/collation.types.d.ts +1 -0
  28. package/dist/core/objects/domain/changes/domain.create.d.ts +1 -1
  29. package/dist/core/objects/domain/changes/domain.create.js +7 -1
  30. package/dist/core/objects/domain/changes/domain.types.d.ts +1 -0
  31. package/dist/core/objects/event-trigger/changes/event-trigger.types.d.ts +1 -0
  32. package/dist/core/objects/extension/changes/extension.types.d.ts +1 -0
  33. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.d.ts +1 -0
  34. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.d.ts +1 -0
  35. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts +1 -0
  36. package/dist/core/objects/foreign-data-wrapper/server/changes/server.types.d.ts +1 -0
  37. package/dist/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.d.ts +1 -0
  38. package/dist/core/objects/index/changes/index.types.d.ts +1 -0
  39. package/dist/core/objects/language/changes/language.types.d.ts +1 -0
  40. package/dist/core/objects/materialized-view/changes/materialized-view.types.d.ts +1 -0
  41. package/dist/core/objects/procedure/changes/procedure.types.d.ts +1 -0
  42. package/dist/core/objects/publication/changes/publication.types.d.ts +1 -0
  43. package/dist/core/objects/rls-policy/changes/rls-policy.types.d.ts +1 -0
  44. package/dist/core/objects/role/changes/role.types.d.ts +1 -0
  45. package/dist/core/objects/rule/changes/rule.types.d.ts +1 -0
  46. package/dist/core/objects/schema/changes/schema.types.d.ts +1 -0
  47. package/dist/core/objects/sequence/changes/sequence.types.d.ts +1 -0
  48. package/dist/core/objects/subscription/changes/subscription.types.d.ts +1 -0
  49. package/dist/core/objects/table/changes/table.types.d.ts +1 -0
  50. package/dist/core/objects/trigger/changes/trigger.types.d.ts +1 -0
  51. package/dist/core/objects/type/composite-type/changes/composite-type.types.d.ts +1 -0
  52. package/dist/core/objects/type/enum/changes/enum.types.d.ts +1 -0
  53. package/dist/core/objects/type/range/changes/range.types.d.ts +1 -0
  54. package/dist/core/objects/type/type.types.d.ts +1 -0
  55. package/dist/core/objects/view/changes/view.types.d.ts +1 -0
  56. package/dist/core/objects/view/view.diff.js +24 -13
  57. package/dist/core/postgres-config.d.ts +2 -2
  58. package/dist/core/sort/custom-constraints.js +1 -1
  59. package/dist/core/sort/logical-sort.js +3 -24
  60. package/package.json +5 -1
  61. package/src/cli/commands/declarative-export.ts +19 -27
  62. package/src/cli/commands/plan.ts +14 -20
  63. package/src/cli/commands/sync.ts +8 -15
  64. package/src/cli/utils/integrations.test.ts +210 -3
  65. package/src/cli/utils/integrations.ts +134 -6
  66. package/src/core/catalog.snapshot.test.ts +11 -2
  67. package/src/core/change-utils.test.ts +61 -0
  68. package/src/core/change-utils.ts +73 -0
  69. package/src/core/change.types.ts +50 -0
  70. package/src/core/depend.ts +25 -0
  71. package/src/core/export/file-mapper.ts +7 -2
  72. package/src/core/integrations/filter/dsl.test.ts +299 -60
  73. package/src/core/integrations/filter/dsl.ts +208 -169
  74. package/src/core/integrations/filter/flatten.test.ts +282 -0
  75. package/src/core/integrations/filter/flatten.ts +150 -0
  76. package/src/core/integrations/integration-dsl.ts +17 -1
  77. package/src/core/integrations/merge.test.ts +128 -0
  78. package/src/core/integrations/merge.ts +72 -0
  79. package/src/core/integrations/serialize/dsl.test.ts +6 -6
  80. package/src/core/integrations/serialize/dsl.ts +7 -4
  81. package/src/core/integrations/supabase.ts +23 -8
  82. package/src/core/objects/aggregate/changes/aggregate.types.ts +1 -0
  83. package/src/core/objects/base.change.ts +10 -0
  84. package/src/core/objects/base.model.test.ts +43 -0
  85. package/src/core/objects/base.model.ts +5 -2
  86. package/src/core/objects/collation/changes/collation.types.ts +1 -0
  87. package/src/core/objects/domain/changes/domain.create.ts +17 -1
  88. package/src/core/objects/domain/changes/domain.types.ts +1 -0
  89. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +1 -0
  90. package/src/core/objects/extension/changes/extension.types.ts +1 -0
  91. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.ts +1 -0
  92. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.ts +1 -0
  93. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +1 -0
  94. package/src/core/objects/foreign-data-wrapper/server/changes/server.types.ts +1 -0
  95. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.ts +1 -0
  96. package/src/core/objects/index/changes/index.types.ts +1 -0
  97. package/src/core/objects/language/changes/language.types.ts +1 -0
  98. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +1 -0
  99. package/src/core/objects/procedure/changes/procedure.types.ts +1 -0
  100. package/src/core/objects/publication/changes/publication.types.ts +1 -0
  101. package/src/core/objects/rls-policy/changes/rls-policy.types.ts +1 -0
  102. package/src/core/objects/role/changes/role.types.ts +1 -0
  103. package/src/core/objects/rule/changes/rule.types.ts +1 -0
  104. package/src/core/objects/schema/changes/schema.types.ts +1 -0
  105. package/src/core/objects/sequence/changes/sequence.types.ts +1 -0
  106. package/src/core/objects/subscription/changes/subscription.types.ts +1 -0
  107. package/src/core/objects/table/changes/table.types.ts +1 -0
  108. package/src/core/objects/trigger/changes/trigger.types.ts +1 -0
  109. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +1 -0
  110. package/src/core/objects/type/enum/changes/enum.types.ts +1 -0
  111. package/src/core/objects/type/range/changes/range.types.ts +1 -0
  112. package/src/core/objects/type/type.types.ts +1 -0
  113. package/src/core/objects/view/changes/view.types.ts +1 -0
  114. package/src/core/objects/view/view.diff.test.ts +96 -0
  115. package/src/core/objects/view/view.diff.ts +30 -15
  116. package/src/core/postgres-config.ts +2 -2
  117. package/src/core/sort/custom-constraints.ts +1 -1
  118. package/src/core/sort/logical-sort.ts +3 -27
  119. package/src/typedoc.ts +248 -0
  120. package/dist/core/integrations/filter/extractors.d.ts +0 -12
  121. package/dist/core/integrations/filter/extractors.js +0 -178
  122. package/src/core/integrations/filter/extractors.test.ts +0 -244
  123. 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 { loadIntegrationDSL } from "../utils/integrations.js";
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 { compileSerializeDSL } = await import("../../core/integrations/serialize/dsl.js");
146
- let filterOption = flags.filter;
147
- let serializeOption = flags.serialize;
148
- let integrationEmptyCatalog;
149
- if (flags.integration) {
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
- ? (await import("../../core/catalog.snapshot.js")).deserializeCatalog(integrationEmptyCatalog)
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: filterOption,
173
- serialize: serializeOption,
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 = serializeOption !== undefined
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 { loadIntegrationDSL } from "../utils/integrations.js";
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
- let filterOption = flags.filter;
110
- let serializeOption = flags.serialize;
111
- let integrationEmptyCatalog;
112
- if (flags.integration) {
113
- const integrationDSL = await loadIntegrationDSL(flags.integration);
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
- ? (await import("../../core/catalog.snapshot.js")).deserializeCatalog(integrationEmptyCatalog)
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: filterOption,
131
- serialize: serializeOption,
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 { loadIntegrationDSL } from "../utils/integrations.js";
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
- // Load integration if provided and extract filter/serialize DSL
95
- let filterOption = flags.filter;
96
- let serializeOption = flags.serialize;
97
- if (flags.integration) {
98
- const integrationDSL = await loadIntegrationDSL(flags.integration);
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: filterOption,
107
- serialize: serializeOption,
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 from a file or core integration.
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
- * @param nameOrPath - Integration name (e.g., "supabase") or file path (e.g., "./my-integration.json")
12
- * @returns The loaded IntegrationDSL
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 an integration DSL from a file or core integration.
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
- export async function loadIntegrationDSL(nameOrPath) {
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
- export {};
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
+ };
@@ -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: import("./types.ts").GroupingPattern[]): CompilePatternsResult;
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.