@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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration merging — combines multiple IntegrationDSL objects.
|
|
3
|
+
*
|
|
4
|
+
* - Filters are AND-combined
|
|
5
|
+
* - Serialize rules are concatenated (earlier integrations first = higher priority)
|
|
6
|
+
* - emptyCatalog: most-specific (last) integration's value wins
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Merge an ordered list of integrations into a single IntegrationDSL.
|
|
10
|
+
*
|
|
11
|
+
* Integrations are ordered from base (first) to most-specific (last).
|
|
12
|
+
* - Filters: AND-combined (all must pass)
|
|
13
|
+
* - Serialize: concatenated (base rules first → higher priority, first-match-wins)
|
|
14
|
+
* - emptyCatalog: most-specific non-undefined value wins
|
|
15
|
+
*
|
|
16
|
+
* @param integrations - Ordered list of integrations (base first, most-specific last)
|
|
17
|
+
* @returns A single merged IntegrationDSL
|
|
18
|
+
*/
|
|
19
|
+
export function mergeIntegrations(integrations) {
|
|
20
|
+
if (integrations.length === 0)
|
|
21
|
+
return {};
|
|
22
|
+
if (integrations.length === 1)
|
|
23
|
+
return integrations[0];
|
|
24
|
+
// Collect all filters
|
|
25
|
+
const filters = [];
|
|
26
|
+
for (const integration of integrations) {
|
|
27
|
+
if (integration.filter) {
|
|
28
|
+
filters.push(integration.filter);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Collect all serialize rules (base first = higher priority)
|
|
32
|
+
const serializeRules = [];
|
|
33
|
+
for (const integration of integrations) {
|
|
34
|
+
if (integration.serialize) {
|
|
35
|
+
serializeRules.push(...integration.serialize);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// emptyCatalog: most-specific (last) non-undefined wins
|
|
39
|
+
let emptyCatalog;
|
|
40
|
+
for (let i = integrations.length - 1; i >= 0; i--) {
|
|
41
|
+
if (integrations[i].emptyCatalog !== undefined) {
|
|
42
|
+
emptyCatalog = integrations[i].emptyCatalog;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const merged = {};
|
|
47
|
+
if (filters.length === 1) {
|
|
48
|
+
merged.filter = filters[0];
|
|
49
|
+
}
|
|
50
|
+
else if (filters.length > 1) {
|
|
51
|
+
merged.filter = { and: filters };
|
|
52
|
+
}
|
|
53
|
+
if (serializeRules.length > 0) {
|
|
54
|
+
merged.serialize = serializeRules;
|
|
55
|
+
}
|
|
56
|
+
if (emptyCatalog !== undefined) {
|
|
57
|
+
merged.emptyCatalog = emptyCatalog;
|
|
58
|
+
}
|
|
59
|
+
return merged;
|
|
60
|
+
}
|
|
@@ -27,8 +27,11 @@ type SerializeRule = {
|
|
|
27
27
|
options: SerializeOptions;
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* Array of serialization rules evaluated in order. The first matching rule's
|
|
31
|
+
* options are passed to `change.serialize()`. If no rule matches, default
|
|
32
|
+
* serialization is used.
|
|
33
|
+
*
|
|
34
|
+
* @category Integration
|
|
32
35
|
*/
|
|
33
36
|
export type SerializeDSL = SerializeRule[];
|
|
34
37
|
/**
|
|
@@ -45,9 +48,9 @@ export type SerializeDSL = SerializeRule[];
|
|
|
45
48
|
* const serializer = compileSerializeDSL([
|
|
46
49
|
* {
|
|
47
50
|
* when: {
|
|
48
|
-
*
|
|
51
|
+
* objectType: "schema",
|
|
49
52
|
* operation: "create",
|
|
50
|
-
* owner: ["service_role"]
|
|
53
|
+
* "schema/owner": ["service_role"]
|
|
51
54
|
* },
|
|
52
55
|
* options: { skipAuthorization: true }
|
|
53
56
|
* }
|
|
@@ -18,9 +18,9 @@ import { evaluatePattern } from "../filter/dsl.js";
|
|
|
18
18
|
* const serializer = compileSerializeDSL([
|
|
19
19
|
* {
|
|
20
20
|
* when: {
|
|
21
|
-
*
|
|
21
|
+
* objectType: "schema",
|
|
22
22
|
* operation: "create",
|
|
23
|
-
* owner: ["service_role"]
|
|
23
|
+
* "schema/owner": ["service_role"]
|
|
24
24
|
* },
|
|
25
25
|
* options: { skipAuthorization: true }
|
|
26
26
|
* }
|
|
@@ -65,38 +65,53 @@ export const supabase = {
|
|
|
65
65
|
// TODO: emptyCatalog: undefined -- populate by running catalog-export on a clean Supabase container
|
|
66
66
|
filter: {
|
|
67
67
|
or: [
|
|
68
|
+
// Include user schema CREATE operations (only schemas not in system list)
|
|
68
69
|
{
|
|
69
70
|
and: [
|
|
70
71
|
{
|
|
71
|
-
|
|
72
|
+
objectType: "schema",
|
|
72
73
|
operation: "create",
|
|
73
74
|
scope: "object",
|
|
74
75
|
},
|
|
75
76
|
{
|
|
76
77
|
not: {
|
|
77
|
-
schema
|
|
78
|
+
// Schema objects have name, not schema — use schema/name
|
|
79
|
+
"schema/name": [...SUPABASE_SYSTEM_SCHEMAS],
|
|
78
80
|
},
|
|
79
81
|
},
|
|
80
82
|
],
|
|
81
83
|
},
|
|
84
|
+
// Include extension CREATEs
|
|
82
85
|
{
|
|
83
|
-
|
|
86
|
+
objectType: "extension",
|
|
84
87
|
operation: "create",
|
|
85
88
|
scope: "object",
|
|
86
89
|
},
|
|
90
|
+
// Exclude system objects
|
|
87
91
|
{
|
|
88
92
|
not: {
|
|
89
93
|
or: [
|
|
94
|
+
// Objects in system schemas (*/schema matches table/schema, view/schema, etc.)
|
|
90
95
|
{
|
|
91
|
-
schema: [...SUPABASE_SYSTEM_SCHEMAS],
|
|
96
|
+
"*/schema": [...SUPABASE_SYSTEM_SCHEMAS],
|
|
92
97
|
},
|
|
98
|
+
// Schema objects whose own name is a system schema
|
|
93
99
|
{
|
|
94
|
-
|
|
100
|
+
"schema/name": [...SUPABASE_SYSTEM_SCHEMAS],
|
|
95
101
|
},
|
|
102
|
+
// Objects owned by system roles (*/owner matches table/owner, schema/owner, etc.)
|
|
103
|
+
{
|
|
104
|
+
"*/owner": [...SUPABASE_SYSTEM_ROLES],
|
|
105
|
+
},
|
|
106
|
+
// Role objects whose own name is a system role
|
|
107
|
+
{
|
|
108
|
+
"role/name": [...SUPABASE_SYSTEM_ROLES],
|
|
109
|
+
},
|
|
110
|
+
// Membership changes for system roles
|
|
96
111
|
{
|
|
97
112
|
and: [
|
|
98
113
|
{
|
|
99
|
-
|
|
114
|
+
objectType: "role",
|
|
100
115
|
scope: "membership",
|
|
101
116
|
},
|
|
102
117
|
{
|
|
@@ -112,10 +127,10 @@ export const supabase = {
|
|
|
112
127
|
serialize: [
|
|
113
128
|
{
|
|
114
129
|
when: {
|
|
115
|
-
|
|
130
|
+
objectType: "schema",
|
|
116
131
|
operation: "create",
|
|
117
132
|
scope: "object",
|
|
118
|
-
owner: [...SUPABASE_SYSTEM_ROLES],
|
|
133
|
+
"schema/owner": [...SUPABASE_SYSTEM_ROLES],
|
|
119
134
|
},
|
|
120
135
|
options: {
|
|
121
136
|
skipAuthorization: true,
|
|
@@ -3,4 +3,5 @@ import type { CommentAggregate } from "./aggregate.comment.ts";
|
|
|
3
3
|
import type { CreateAggregate } from "./aggregate.create.ts";
|
|
4
4
|
import type { DropAggregate } from "./aggregate.drop.ts";
|
|
5
5
|
import type { AggregatePrivilege } from "./aggregate.privilege.ts";
|
|
6
|
+
/** Union of all aggregate-related change variants (`objectType: "aggregate"`). @category Change Types */
|
|
6
7
|
export type AggregateChange = AlterAggregate | CommentAggregate | CreateAggregate | DropAggregate | AggregatePrivilege;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
type ChangeOperation = "create" | "alter" | "drop";
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for all change objects.
|
|
4
|
+
*
|
|
5
|
+
* Every concrete change (e.g. `CreateTable`, `AlterView`) extends this class and
|
|
6
|
+
* provides an `operation`, `objectType`, and `scope`. The filter DSL flattens
|
|
7
|
+
* these properties — along with the model sub-object — into path/value pairs
|
|
8
|
+
* for pattern matching.
|
|
9
|
+
*
|
|
10
|
+
* @category Base
|
|
11
|
+
*/
|
|
2
12
|
export declare abstract class BaseChange {
|
|
3
13
|
/**
|
|
4
14
|
* The operation of the change.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for all change objects.
|
|
3
|
+
*
|
|
4
|
+
* Every concrete change (e.g. `CreateTable`, `AlterView`) extends this class and
|
|
5
|
+
* provides an `operation`, `objectType`, and `scope`. The filter DSL flattens
|
|
6
|
+
* these properties — along with the model sub-object — into path/value pairs
|
|
7
|
+
* for pattern matching.
|
|
8
|
+
*
|
|
9
|
+
* @category Base
|
|
10
|
+
*/
|
|
1
11
|
export class BaseChange {
|
|
2
12
|
/**
|
|
3
13
|
* A unique identifier for the change.
|
|
@@ -60,7 +60,10 @@ export declare abstract class BasePgModel {
|
|
|
60
60
|
*/
|
|
61
61
|
abstract get dataFields(): Record<string, unknown>;
|
|
62
62
|
/**
|
|
63
|
-
* Compare this object with another BasePgModel for equality based on stableId and
|
|
63
|
+
* Compare this object with another BasePgModel for equality based on the stableId and
|
|
64
|
+
* the data portion of the stable snapshot. By default, the snapshot's `data` comes
|
|
65
|
+
* from {@link dataFields}, but subclasses may override {@link stableSnapshot} to
|
|
66
|
+
* normalize or otherwise transform the data used for equality.
|
|
64
67
|
*/
|
|
65
68
|
equals(other: BasePgModel): boolean;
|
|
66
69
|
/**
|
|
@@ -28,11 +28,14 @@ export function normalizeColumns(columns) {
|
|
|
28
28
|
}
|
|
29
29
|
export class BasePgModel {
|
|
30
30
|
/**
|
|
31
|
-
* Compare this object with another BasePgModel for equality based on stableId and
|
|
31
|
+
* Compare this object with another BasePgModel for equality based on the stableId and
|
|
32
|
+
* the data portion of the stable snapshot. By default, the snapshot's `data` comes
|
|
33
|
+
* from {@link dataFields}, but subclasses may override {@link stableSnapshot} to
|
|
34
|
+
* normalize or otherwise transform the data used for equality.
|
|
32
35
|
*/
|
|
33
36
|
equals(other) {
|
|
34
37
|
return (this.stableId === other.stableId &&
|
|
35
|
-
deepEqual(this.
|
|
38
|
+
deepEqual(this.stableSnapshot().data, other.stableSnapshot().data));
|
|
36
39
|
}
|
|
37
40
|
/**
|
|
38
41
|
* Stable representation used for equality/fingerprints.
|
|
@@ -2,4 +2,5 @@ import type { AlterCollation } from "./collation.alter.ts";
|
|
|
2
2
|
import type { CommentCollation } from "./collation.comment.ts";
|
|
3
3
|
import type { CreateCollation } from "./collation.create.ts";
|
|
4
4
|
import type { DropCollation } from "./collation.drop.ts";
|
|
5
|
+
/** Union of all collation-related change variants (`objectType: "collation"`). @category Change Types */
|
|
5
6
|
export type CollationChange = AlterCollation | CommentCollation | CreateCollation | DropCollation;
|
|
@@ -24,7 +24,7 @@ export declare class CreateDomain extends CreateDomainChange {
|
|
|
24
24
|
constructor(props: {
|
|
25
25
|
domain: Domain;
|
|
26
26
|
});
|
|
27
|
-
get creates(): `domain:${string}`[];
|
|
27
|
+
get creates(): (`constraint:${string}.${string}.${string}` | `domain:${string}`)[];
|
|
28
28
|
get requires(): string[];
|
|
29
29
|
serialize(): string;
|
|
30
30
|
}
|
|
@@ -26,7 +26,13 @@ export class CreateDomain extends CreateDomainChange {
|
|
|
26
26
|
this.domain = props.domain;
|
|
27
27
|
}
|
|
28
28
|
get creates() {
|
|
29
|
-
|
|
29
|
+
const creates = [this.domain.stableId];
|
|
30
|
+
for (const constraint of this.domain.constraints) {
|
|
31
|
+
if (constraint.check_expression && constraint.validated) {
|
|
32
|
+
creates.push(stableId.constraint(this.domain.schema, this.domain.name, constraint.name));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return creates;
|
|
30
36
|
}
|
|
31
37
|
get requires() {
|
|
32
38
|
const dependencies = new Set();
|
|
@@ -3,4 +3,5 @@ import type { CommentDomain } from "./domain.comment.ts";
|
|
|
3
3
|
import type { CreateDomain } from "./domain.create.ts";
|
|
4
4
|
import type { DropDomain } from "./domain.drop.ts";
|
|
5
5
|
import type { DomainPrivilege } from "./domain.privilege.ts";
|
|
6
|
+
/** Union of all domain-related change variants (`objectType: "domain"`). @category Change Types */
|
|
6
7
|
export type DomainChange = AlterDomain | CommentDomain | CreateDomain | DropDomain | DomainPrivilege;
|
|
@@ -2,4 +2,5 @@ import type { AlterEventTrigger } from "./event-trigger.alter.ts";
|
|
|
2
2
|
import type { CommentEventTrigger } from "./event-trigger.comment.ts";
|
|
3
3
|
import type { CreateEventTrigger } from "./event-trigger.create.ts";
|
|
4
4
|
import type { DropEventTrigger } from "./event-trigger.drop.ts";
|
|
5
|
+
/** Union of all event-trigger-related change variants (`objectType: "event_trigger"`). @category Change Types */
|
|
5
6
|
export type EventTriggerChange = AlterEventTrigger | CommentEventTrigger | CreateEventTrigger | DropEventTrigger;
|
|
@@ -2,4 +2,5 @@ import type { AlterExtension } from "./extension.alter.ts";
|
|
|
2
2
|
import type { CommentExtension } from "./extension.comment.ts";
|
|
3
3
|
import type { CreateExtension } from "./extension.create.ts";
|
|
4
4
|
import type { DropExtension } from "./extension.drop.ts";
|
|
5
|
+
/** Union of all extension-related change variants (`objectType: "extension"`). @category Change Types */
|
|
5
6
|
export type ExtensionChange = AlterExtension | CommentExtension | CreateExtension | DropExtension;
|
|
@@ -3,4 +3,5 @@ import type { CommentForeignDataWrapper } from "./foreign-data-wrapper.comment.t
|
|
|
3
3
|
import type { CreateForeignDataWrapper } from "./foreign-data-wrapper.create.ts";
|
|
4
4
|
import type { DropForeignDataWrapper } from "./foreign-data-wrapper.drop.ts";
|
|
5
5
|
import type { ForeignDataWrapperPrivilege } from "./foreign-data-wrapper.privilege.ts";
|
|
6
|
+
/** Union of all FDW wrapper-level change variants (`objectType: "foreign_data_wrapper"`). @category Change Types */
|
|
6
7
|
export type ForeignDataWrapperChange = AlterForeignDataWrapper | CommentForeignDataWrapper | CreateForeignDataWrapper | DropForeignDataWrapper | ForeignDataWrapperPrivilege;
|
|
@@ -2,4 +2,5 @@ import type { ForeignDataWrapperChange as FDWChange } from "./foreign-data-wrapp
|
|
|
2
2
|
import type { ForeignTableChange } from "./foreign-table/changes/foreign-table.types.ts";
|
|
3
3
|
import type { ServerChange } from "./server/changes/server.types.ts";
|
|
4
4
|
import type { UserMappingChange } from "./user-mapping/changes/user-mapping.types.ts";
|
|
5
|
+
/** Union of all foreign-data-wrapper-related change variants (`objectType: "foreign_data_wrapper" | "server" | "foreign_table" | "user_mapping"`). @category Change Types */
|
|
5
6
|
export type ForeignDataWrapperChange = FDWChange | ServerChange | UserMappingChange | ForeignTableChange;
|
package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ import type { CommentForeignTable } from "./foreign-table.comment.ts";
|
|
|
3
3
|
import type { CreateForeignTable } from "./foreign-table.create.ts";
|
|
4
4
|
import type { DropForeignTable } from "./foreign-table.drop.ts";
|
|
5
5
|
import type { ForeignTablePrivilege } from "./foreign-table.privilege.ts";
|
|
6
|
+
/** Union of all foreign-table-related change variants (`objectType: "foreign_table"`). @category Change Types */
|
|
6
7
|
export type ForeignTableChange = AlterForeignTable | CommentForeignTable | CreateForeignTable | DropForeignTable | ForeignTablePrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentServer } from "./server.comment.ts";
|
|
|
3
3
|
import type { CreateServer } from "./server.create.ts";
|
|
4
4
|
import type { DropServer } from "./server.drop.ts";
|
|
5
5
|
import type { ServerPrivilege } from "./server.privilege.ts";
|
|
6
|
+
/** Union of all server-related change variants (`objectType: "server"`). @category Change Types */
|
|
6
7
|
export type ServerChange = AlterServer | CommentServer | CreateServer | DropServer | ServerPrivilege;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AlterUserMapping } from "./user-mapping.alter.ts";
|
|
2
2
|
import type { CreateUserMapping } from "./user-mapping.create.ts";
|
|
3
3
|
import type { DropUserMapping } from "./user-mapping.drop.ts";
|
|
4
|
+
/** Union of all user-mapping-related change variants (`objectType: "user_mapping"`). @category Change Types */
|
|
4
5
|
export type UserMappingChange = AlterUserMapping | CreateUserMapping | DropUserMapping;
|
|
@@ -2,4 +2,5 @@ import type { AlterIndex } from "./index.alter.ts";
|
|
|
2
2
|
import type { CommentIndex } from "./index.comment.ts";
|
|
3
3
|
import type { CreateIndex } from "./index.create.ts";
|
|
4
4
|
import type { DropIndex } from "./index.drop.ts";
|
|
5
|
+
/** Union of all index-related change variants (`objectType: "index"`). @category Change Types */
|
|
5
6
|
export type IndexChange = AlterIndex | CommentIndex | CreateIndex | DropIndex;
|
|
@@ -3,4 +3,5 @@ import type { CommentLanguage } from "./language.comment.ts";
|
|
|
3
3
|
import type { CreateLanguage } from "./language.create.ts";
|
|
4
4
|
import type { DropLanguage } from "./language.drop.ts";
|
|
5
5
|
import type { LanguagePrivilege } from "./language.privilege.ts";
|
|
6
|
+
/** Union of all language-related change variants (`objectType: "language"`). @category Change Types */
|
|
6
7
|
export type LanguageChange = AlterLanguage | CommentLanguage | CreateLanguage | DropLanguage | LanguagePrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentMaterializedView } from "./materialized-view.comment.ts";
|
|
|
3
3
|
import type { CreateMaterializedView } from "./materialized-view.create.ts";
|
|
4
4
|
import type { DropMaterializedView } from "./materialized-view.drop.ts";
|
|
5
5
|
import type { MaterializedViewPrivilege } from "./materialized-view.privilege.ts";
|
|
6
|
+
/** Union of all materialized-view-related change variants (`objectType: "materialized_view"`). @category Change Types */
|
|
6
7
|
export type MaterializedViewChange = AlterMaterializedView | CommentMaterializedView | CreateMaterializedView | DropMaterializedView | MaterializedViewPrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentProcedure } from "./procedure.comment.ts";
|
|
|
3
3
|
import type { CreateProcedure } from "./procedure.create.ts";
|
|
4
4
|
import type { DropProcedure } from "./procedure.drop.ts";
|
|
5
5
|
import type { ProcedurePrivilege } from "./procedure.privilege.ts";
|
|
6
|
+
/** Union of all procedure-related change variants (`objectType: "procedure"`). @category Change Types */
|
|
6
7
|
export type ProcedureChange = AlterProcedure | CommentProcedure | CreateProcedure | DropProcedure | ProcedurePrivilege;
|
|
@@ -2,4 +2,5 @@ import type { AlterPublicationAddSchemas, AlterPublicationAddTables, AlterPublic
|
|
|
2
2
|
import type { CommentPublication } from "./publication.comment.ts";
|
|
3
3
|
import type { CreatePublication } from "./publication.create.ts";
|
|
4
4
|
import type { DropPublication } from "./publication.drop.ts";
|
|
5
|
+
/** Union of all publication-related change variants (`objectType: "publication"`). @category Change Types */
|
|
5
6
|
export type PublicationChange = AlterPublicationAddSchemas | AlterPublicationAddTables | AlterPublicationDropSchemas | AlterPublicationDropTables | AlterPublicationSetList | AlterPublicationSetOptions | AlterPublicationSetOwner | CommentPublication | CreatePublication | DropPublication;
|
|
@@ -2,4 +2,5 @@ import type { AlterRlsPolicy } from "./rls-policy.alter.ts";
|
|
|
2
2
|
import type { CommentRlsPolicy } from "./rls-policy.comment.ts";
|
|
3
3
|
import type { CreateRlsPolicy } from "./rls-policy.create.ts";
|
|
4
4
|
import type { DropRlsPolicy } from "./rls-policy.drop.ts";
|
|
5
|
+
/** Union of all RLS policy-related change variants (`objectType: "rls_policy"`). @category Change Types */
|
|
5
6
|
export type RlsPolicyChange = AlterRlsPolicy | CommentRlsPolicy | CreateRlsPolicy | DropRlsPolicy;
|
|
@@ -3,4 +3,5 @@ import type { CommentRole } from "./role.comment.ts";
|
|
|
3
3
|
import type { CreateRole } from "./role.create.ts";
|
|
4
4
|
import type { DropRole } from "./role.drop.ts";
|
|
5
5
|
import type { RolePrivilege } from "./role.privilege.ts";
|
|
6
|
+
/** Union of all role-related change variants (`objectType: "role"`). @category Change Types */
|
|
6
7
|
export type RoleChange = AlterRole | CommentRole | CreateRole | DropRole | RolePrivilege;
|
|
@@ -2,4 +2,5 @@ import type { ReplaceRule, SetRuleEnabledState } from "./rule.alter.ts";
|
|
|
2
2
|
import type { CreateCommentOnRule, DropCommentOnRule } from "./rule.comment.ts";
|
|
3
3
|
import type { CreateRule } from "./rule.create.ts";
|
|
4
4
|
import type { DropRule } from "./rule.drop.ts";
|
|
5
|
+
/** Union of all rule-related change variants (`objectType: "rule"`). @category Change Types */
|
|
5
6
|
export type RuleChange = CreateRule | DropRule | ReplaceRule | SetRuleEnabledState | CreateCommentOnRule | DropCommentOnRule;
|
|
@@ -3,4 +3,5 @@ import type { CommentSchema } from "./schema.comment.ts";
|
|
|
3
3
|
import type { CreateSchema } from "./schema.create.ts";
|
|
4
4
|
import type { DropSchema } from "./schema.drop.ts";
|
|
5
5
|
import type { SchemaPrivilege } from "./schema.privilege.ts";
|
|
6
|
+
/** Union of all schema-related change variants (`objectType: "schema"`). @category Change Types */
|
|
6
7
|
export type SchemaChange = AlterSchema | CommentSchema | CreateSchema | DropSchema | SchemaPrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentSequence } from "./sequence.comment.ts";
|
|
|
3
3
|
import type { CreateSequence } from "./sequence.create.ts";
|
|
4
4
|
import type { DropSequence } from "./sequence.drop.ts";
|
|
5
5
|
import type { SequencePrivilege } from "./sequence.privilege.ts";
|
|
6
|
+
/** Union of all sequence-related change variants (`objectType: "sequence"`). @category Change Types */
|
|
6
7
|
export type SequenceChange = AlterSequence | CommentSequence | CreateSequence | DropSequence | SequencePrivilege;
|
|
@@ -2,4 +2,5 @@ import type { AlterSubscriptionDisable, AlterSubscriptionEnable, AlterSubscripti
|
|
|
2
2
|
import type { CommentSubscription } from "./subscription.comment.ts";
|
|
3
3
|
import type { CreateSubscription } from "./subscription.create.ts";
|
|
4
4
|
import type { DropSubscription } from "./subscription.drop.ts";
|
|
5
|
+
/** Union of all subscription-related change variants (`objectType: "subscription"`). @category Change Types */
|
|
5
6
|
export type SubscriptionChange = CreateSubscription | DropSubscription | AlterSubscriptionSetConnection | AlterSubscriptionSetPublication | AlterSubscriptionEnable | AlterSubscriptionDisable | AlterSubscriptionSetOptions | AlterSubscriptionSetOwner | CommentSubscription;
|
|
@@ -3,4 +3,5 @@ import type { CommentTable } from "./table.comment.ts";
|
|
|
3
3
|
import type { CreateTable } from "./table.create.ts";
|
|
4
4
|
import type { DropTable } from "./table.drop.ts";
|
|
5
5
|
import type { TablePrivilege } from "./table.privilege.ts";
|
|
6
|
+
/** Union of all table-related change variants (`objectType: "table"`). @category Change Types */
|
|
6
7
|
export type TableChange = AlterTable | CommentTable | CreateTable | DropTable | TablePrivilege;
|
|
@@ -2,4 +2,5 @@ import type { AlterTrigger } from "./trigger.alter.ts";
|
|
|
2
2
|
import type { CommentTrigger } from "./trigger.comment.ts";
|
|
3
3
|
import type { CreateTrigger } from "./trigger.create.ts";
|
|
4
4
|
import type { DropTrigger } from "./trigger.drop.ts";
|
|
5
|
+
/** Union of all trigger-related change variants (`objectType: "trigger"`). @category Change Types */
|
|
5
6
|
export type TriggerChange = AlterTrigger | CommentTrigger | CreateTrigger | DropTrigger;
|
|
@@ -3,4 +3,5 @@ import type { CommentCompositeType } from "./composite-type.comment.ts";
|
|
|
3
3
|
import type { CreateCompositeType } from "./composite-type.create.ts";
|
|
4
4
|
import type { DropCompositeType } from "./composite-type.drop.ts";
|
|
5
5
|
import type { CompositeTypePrivilege } from "./composite-type.privilege.ts";
|
|
6
|
+
/** Union of all composite-type-related change variants (`objectType: "composite_type"`). @category Change Types */
|
|
6
7
|
export type CompositeTypeChange = AlterCompositeType | CommentCompositeType | CreateCompositeType | DropCompositeType | CompositeTypePrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentEnum } from "./enum.comment.ts";
|
|
|
3
3
|
import type { CreateEnum } from "./enum.create.ts";
|
|
4
4
|
import type { DropEnum } from "./enum.drop.ts";
|
|
5
5
|
import type { EnumPrivilege } from "./enum.privilege.ts";
|
|
6
|
+
/** Union of all enum-related change variants (`objectType: "enum"`). @category Change Types */
|
|
6
7
|
export type EnumChange = AlterEnum | CommentEnum | CreateEnum | DropEnum | EnumPrivilege;
|
|
@@ -3,4 +3,5 @@ import type { CommentRange } from "./range.comment.ts";
|
|
|
3
3
|
import type { CreateRange } from "./range.create.ts";
|
|
4
4
|
import type { DropRange } from "./range.drop.ts";
|
|
5
5
|
import type { RangePrivilege } from "./range.privilege.ts";
|
|
6
|
+
/** Union of all range-related change variants (`objectType: "range"`). @category Change Types */
|
|
6
7
|
export type RangeChange = AlterRange | CommentRange | CreateRange | DropRange | RangePrivilege;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompositeTypeChange } from "./composite-type/changes/composite-type.types.ts";
|
|
2
2
|
import type { EnumChange } from "./enum/changes/enum.types.ts";
|
|
3
3
|
import type { RangeChange } from "./range/changes/range.types.ts";
|
|
4
|
+
/** Union of all type-related change variants (`objectType: "composite_type" | "enum" | "range"`). @category Change Types */
|
|
4
5
|
export type TypeChange = CompositeTypeChange | EnumChange | RangeChange;
|
|
@@ -3,4 +3,5 @@ import type { CommentView } from "./view.comment.ts";
|
|
|
3
3
|
import type { CreateView } from "./view.create.ts";
|
|
4
4
|
import type { DropView } from "./view.drop.ts";
|
|
5
5
|
import type { ViewPrivilege } from "./view.privilege.ts";
|
|
6
|
+
/** Union of all view-related change variants (`objectType: "view"`). @category Change Types */
|
|
6
7
|
export type ViewChange = AlterView | CommentView | CreateView | DropView | ViewPrivilege;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { diffObjects } from "../base.diff.js";
|
|
2
|
+
import { normalizeColumns } from "../base.model.js";
|
|
2
3
|
import { diffPrivileges, emitColumnPrivilegeChanges, } from "../base.privilege-diff.js";
|
|
3
4
|
import { deepEqual, hasNonAlterableChanges } from "../utils.js";
|
|
4
5
|
import { AlterViewChangeOwner, AlterViewResetOptions, AlterViewSetOptions, } from "./changes/view.alter.js";
|
|
@@ -17,35 +18,37 @@ import { GrantViewPrivileges, RevokeGrantOptionViewPrivileges, RevokeViewPrivile
|
|
|
17
18
|
export function diffViews(ctx, main, branch) {
|
|
18
19
|
const { created, dropped, altered } = diffObjects(main, branch);
|
|
19
20
|
const changes = [];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
changes.push(new CreateView({ view: v }));
|
|
21
|
+
const appendCreateViewChanges = (view) => {
|
|
22
|
+
changes.push(new CreateView({ view }));
|
|
23
23
|
// OWNER: If the view should be owned by someone other than the current user,
|
|
24
24
|
// emit ALTER VIEW ... OWNER TO after creation
|
|
25
|
-
if (
|
|
26
|
-
changes.push(new AlterViewChangeOwner({ view
|
|
25
|
+
if (view.owner !== ctx.currentUser) {
|
|
26
|
+
changes.push(new AlterViewChangeOwner({ view, owner: view.owner }));
|
|
27
27
|
}
|
|
28
|
-
if (
|
|
29
|
-
changes.push(new CreateCommentOnView({ view
|
|
28
|
+
if (view.comment !== null) {
|
|
29
|
+
changes.push(new CreateCommentOnView({ view }));
|
|
30
30
|
}
|
|
31
31
|
// PRIVILEGES: For created objects, compare against default privileges state
|
|
32
32
|
// The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
|
|
33
33
|
// so objects are created with the default privileges state in effect.
|
|
34
34
|
// We compare default privileges against desired privileges to generate REVOKE/GRANT statements
|
|
35
35
|
// needed to reach the final desired state.
|
|
36
|
-
const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(ctx.currentUser, "view",
|
|
37
|
-
const creatorFilteredDefaults =
|
|
36
|
+
const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(ctx.currentUser, "view", view.schema ?? "");
|
|
37
|
+
const creatorFilteredDefaults = view.owner !== ctx.currentUser
|
|
38
38
|
? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
|
|
39
39
|
: effectiveDefaults;
|
|
40
|
-
const desiredPrivileges =
|
|
40
|
+
const desiredPrivileges = view.privileges;
|
|
41
41
|
// Filter out owner privileges - owner always has ALL privileges implicitly
|
|
42
42
|
// and shouldn't be compared. Use the view owner as the reference.
|
|
43
|
-
const privilegeResults = diffPrivileges(creatorFilteredDefaults, desiredPrivileges,
|
|
44
|
-
changes.push(...emitColumnPrivilegeChanges(privilegeResults,
|
|
43
|
+
const privilegeResults = diffPrivileges(creatorFilteredDefaults, desiredPrivileges, view.owner);
|
|
44
|
+
changes.push(...emitColumnPrivilegeChanges(privilegeResults, view, view, "view", {
|
|
45
45
|
Grant: GrantViewPrivileges,
|
|
46
46
|
Revoke: RevokeViewPrivileges,
|
|
47
47
|
RevokeGrantOption: RevokeGrantOptionViewPrivileges,
|
|
48
48
|
}, effectiveDefaults, ctx.version));
|
|
49
|
+
};
|
|
50
|
+
for (const viewId of created) {
|
|
51
|
+
appendCreateViewChanges(branch[viewId]);
|
|
49
52
|
}
|
|
50
53
|
for (const viewId of dropped) {
|
|
51
54
|
changes.push(new DropView({ view: main[viewId] }));
|
|
@@ -69,7 +72,15 @@ export function diffViews(ctx, main, branch) {
|
|
|
69
72
|
"partition_bound",
|
|
70
73
|
];
|
|
71
74
|
const nonAlterablePropsChanged = hasNonAlterableChanges(mainView, branchView, NON_ALTERABLE_FIELDS, { options: deepEqual });
|
|
72
|
-
|
|
75
|
+
// Normalize columns (strip position, sort by name) to match stableSnapshot().
|
|
76
|
+
// Position-only differences are safe to ignore here because column order in a
|
|
77
|
+
// view is determined by its definition, which is already checked above via
|
|
78
|
+
// NON_ALTERABLE_FIELDS - a position change always implies a definition change.
|
|
79
|
+
if (!deepEqual(normalizeColumns(mainView.columns), normalizeColumns(branchView.columns))) {
|
|
80
|
+
changes.push(new DropView({ view: mainView }));
|
|
81
|
+
appendCreateViewChanges(branchView);
|
|
82
|
+
}
|
|
83
|
+
else if (nonAlterablePropsChanged) {
|
|
73
84
|
// Replace the entire view using CREATE OR REPLACE to avoid drop when possible
|
|
74
85
|
changes.push(new CreateView({ view: branchView, orReplace: true }));
|
|
75
86
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PostgreSQL connection configuration with custom type handlers.
|
|
3
3
|
*/
|
|
4
|
-
import type { PoolClient, PoolConfig } from "pg";
|
|
4
|
+
import type { ClientBase, PoolClient, PoolConfig } from "pg";
|
|
5
5
|
import { Pool } from "pg";
|
|
6
6
|
/**
|
|
7
7
|
* Options for creating a Pool with event listeners.
|
|
8
8
|
*/
|
|
9
9
|
interface CreatePoolOptions extends Partial<PoolConfig> {
|
|
10
10
|
/** Called when a new client connects to the pool */
|
|
11
|
-
onConnect?: (client:
|
|
11
|
+
onConnect?: (client: ClientBase) => void | Promise<void>;
|
|
12
12
|
/** Called when an idle client emits an error */
|
|
13
13
|
onError?: (err: Error, client: PoolClient) => void;
|
|
14
14
|
/** Called when a client is acquired from the pool */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getSchema } from "../
|
|
1
|
+
import { getSchema } from "../change-utils.js";
|
|
2
2
|
import { GrantRoleDefaultPrivileges, RevokeRoleDefaultPrivileges, } from "../objects/role/changes/role.privilege.js";
|
|
3
3
|
/**
|
|
4
4
|
* Maps object type names to PostgreSQL default privilege objtype codes.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* It groups related changes together while preserving the ability for the dependency
|
|
9
9
|
* resolver to reorder within groups when necessary.
|
|
10
10
|
*/
|
|
11
|
-
import { getSchema } from "../
|
|
11
|
+
import { getSchema } from "../change-utils.js";
|
|
12
12
|
import { getExecutionPhase, isMetadataStableId } from "./utils.js";
|
|
13
13
|
/**
|
|
14
14
|
* Object type ordering for logical grouping.
|
|
@@ -287,27 +287,6 @@ function getParentStableId(change) {
|
|
|
287
287
|
// Fallback: return first requires if available
|
|
288
288
|
return requires.length > 0 ? requires[0] : null;
|
|
289
289
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Extract schema name from a change.
|
|
292
|
-
* Returns the schema name if present, or null for non-schema objects.
|
|
293
|
-
*
|
|
294
|
-
* Uses the getSchema helper which directly accesses schema properties from change objects.
|
|
295
|
-
* For default_privilege changes, accesses the inSchema property directly.
|
|
296
|
-
* For event_trigger changes, groups by their function's schema.
|
|
297
|
-
*/
|
|
298
|
-
function extractSchemaFromChange(change) {
|
|
299
|
-
// Handle default_privilege changes specially (they have inSchema property)
|
|
300
|
-
if (change.scope === "default_privilege") {
|
|
301
|
-
// TypeScript doesn't know about inSchema, but we know it exists for default_privilege changes
|
|
302
|
-
return change.inSchema ?? null;
|
|
303
|
-
}
|
|
304
|
-
// Handle event_trigger changes specially - group by their function's schema
|
|
305
|
-
if (change.objectType === "event_trigger") {
|
|
306
|
-
return change.eventTrigger.function_schema;
|
|
307
|
-
}
|
|
308
|
-
// Use the getSchema helper for all other changes
|
|
309
|
-
return getSchema(change);
|
|
310
|
-
}
|
|
311
290
|
/**
|
|
312
291
|
* Get the effective object type for sorting purposes.
|
|
313
292
|
* For sub-entities, returns the parent's object type (table/view/materialized_view).
|
|
@@ -399,8 +378,8 @@ function sortPhase(changes, phase) {
|
|
|
399
378
|
const changeA = a.change;
|
|
400
379
|
const changeB = b.change;
|
|
401
380
|
// 1. Compare schemas (group objects by schema)
|
|
402
|
-
const schemaA =
|
|
403
|
-
const schemaB =
|
|
381
|
+
const schemaA = getSchema(changeA);
|
|
382
|
+
const schemaB = getSchema(changeB);
|
|
404
383
|
// Non-schema objects (roles, languages, extensions, etc.) sort first
|
|
405
384
|
// Use a special prefix to ensure they come before schema objects
|
|
406
385
|
const schemaKeyA = schemaA === null ? "::" : schemaA;
|