@prisma-next/target-postgres 0.14.0-dev.6 → 0.14.0-dev.61
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/authoring-BLkL4fFG.mjs +528 -0
- package/dist/authoring-BLkL4fFG.mjs.map +1 -0
- package/dist/canonicalize-CAbXtVtB.d.mts +37 -0
- package/dist/canonicalize-CAbXtVtB.d.mts.map +1 -0
- package/dist/{codec-ids-CnXu9Qy3.d.mts → codec-ids-BzrFF-I4.d.mts} +3 -2
- package/dist/codec-ids-BzrFF-I4.d.mts.map +1 -0
- package/dist/{codec-ids-BvytN2P8.mjs → codec-ids-C_-Hj6bL.mjs} +3 -2
- package/dist/codec-ids-C_-Hj6bL.mjs.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-BiIWziRO.d.mts} +3 -2
- package/dist/codec-types-BiIWziRO.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/codecs-CkVQrZ4G.mjs +913 -0
- package/dist/codecs-CkVQrZ4G.mjs.map +1 -0
- package/dist/{codecs--0A5_4Bq.d.mts → codecs-Cm-nS4mQ.d.mts} +111 -4
- package/dist/codecs-Cm-nS4mQ.d.mts.map +1 -0
- package/dist/codecs.d.mts +2 -2
- package/dist/codecs.mjs +3 -757
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +33 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +4 -4
- package/dist/control-policy-BpmChlAK.mjs +1151 -0
- package/dist/control-policy-BpmChlAK.mjs.map +1 -0
- package/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +901 -35
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-DPzwBMhW.d.mts} +3 -3
- package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-DPzwBMhW.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-QDyOSeLc.mjs → ddl-BovUVFVi.mjs} +77 -6
- package/dist/ddl-BovUVFVi.mjs.map +1 -0
- package/dist/ddl.d.mts +3 -2
- package/dist/ddl.mjs +2 -2
- package/dist/default-normalizer.d.mts +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +247 -1
- package/dist/default-normalizer.mjs.map +1 -0
- package/dist/descriptor-meta-C1T4BXJ8.mjs +22 -0
- package/dist/descriptor-meta-C1T4BXJ8.mjs.map +1 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
- package/dist/diff-database-schema-RMwnSOnz.mjs +298 -0
- package/dist/diff-database-schema-RMwnSOnz.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +46 -0
- package/dist/diff-database-schema.d.mts.map +1 -0
- package/dist/diff-database-schema.mjs +2 -0
- package/dist/errors-DO-w0EUS.mjs +35 -0
- package/dist/errors-DO-w0EUS.mjs.map +1 -0
- package/dist/errors.d.mts +11 -9
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/issue-planner.d.mts +35 -11
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +2 -2
- package/dist/migration.d.mts +5 -5
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -4
- package/dist/namespace-ids-xtp_ZY86.mjs +7 -0
- package/dist/namespace-ids-xtp_ZY86.mjs.map +1 -0
- package/dist/native-type-normalizer.mjs +37 -1
- package/dist/native-type-normalizer.mjs.map +1 -0
- package/dist/nodes-B815pj_X.mjs +162 -0
- package/dist/nodes-B815pj_X.mjs.map +1 -0
- package/dist/nodes-BtQyZha6.d.mts +146 -0
- package/dist/nodes-BtQyZha6.d.mts.map +1 -0
- package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-CI00nS4z.mjs} +300 -31
- package/dist/op-factory-call-CI00nS4z.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-D6_7-7Vc.d.mts} +68 -15
- package/dist/op-factory-call-D6_7-7Vc.d.mts.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +287 -3
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-CAHZN1Ez.mjs +422 -0
- package/dist/planner-CAHZN1Ez.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-B8Nn6nHN.mjs} +12 -27
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +4 -6
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
- package/dist/planner-identity-values.d.mts +8 -2
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-a-tjfKsA.d.mts} +4 -4
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-a-tjfKsA.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-flkG4Y7n.mjs} +3 -3
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-flkG4Y7n.mjs.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-jqUUGyQR.mjs → planner-sql-checks-C1-Kk3Eu.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-C1-Kk3Eu.mjs.map} +1 -1
- package/dist/planner-sql-checks.d.mts +2 -2
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
- package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner.d.mts +72 -17
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +3 -2
- package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-DWLNQbdR.mjs} +45 -41
- package/dist/postgres-contract-view-DWLNQbdR.mjs.map +1 -0
- package/dist/postgres-database-schema-node-BVdOffjC.d.mts +221 -0
- package/dist/postgres-database-schema-node-BVdOffjC.d.mts.map +1 -0
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-Bzzi0Asr.d.mts} +28 -9
- package/dist/postgres-migration-Bzzi0Asr.d.mts.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-C92swCzd.mjs} +53 -45
- package/dist/postgres-migration-C92swCzd.mjs.map +1 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs +41 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs.map +1 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts +52 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
- package/dist/postgres-role-schema-node-BmCboEa_.mjs +198 -0
- package/dist/postgres-role-schema-node-BmCboEa_.mjs.map +1 -0
- package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-COEca21H.mjs} +188 -10
- package/dist/postgres-schema-COEca21H.mjs.map +1 -0
- package/dist/postgres-schema-DsrtYp2v.d.mts +269 -0
- package/dist/postgres-schema-DsrtYp2v.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-9FCtCUz2.mjs +153 -0
- package/dist/postgres-table-schema-node-9FCtCUz2.mjs.map +1 -0
- package/dist/render-ops.d.mts +1 -1
- package/dist/{render-typescript-KMgosran.mjs → render-typescript-7yqILcwr.mjs} +45 -16
- package/dist/render-typescript-7yqILcwr.mjs.map +1 -0
- package/dist/render-typescript.mjs +1 -1
- package/dist/resolve-ddl-schema-ZkKxt2Fm.mjs +22 -0
- package/dist/resolve-ddl-schema-ZkKxt2Fm.mjs.map +1 -0
- package/dist/rls-canonicalize.d.mts +20 -0
- package/dist/rls-canonicalize.d.mts.map +1 -0
- package/dist/rls-canonicalize.mjs +2 -0
- package/dist/runtime.d.mts +6 -8
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +3 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/schema-ir-annotations.d.mts +15 -0
- package/dist/schema-ir-annotations.d.mts.map +1 -0
- package/dist/schema-ir-annotations.mjs +2 -0
- package/dist/types.d.mts +4 -140
- package/dist/types.mjs +5 -2
- package/dist/wire-name-rHHtr1h0.mjs +75 -0
- package/dist/wire-name-rHHtr1h0.mjs.map +1 -0
- package/package.json +23 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +71 -1
- package/src/core/authoring.ts +467 -2
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +2 -0
- package/src/core/codecs.ts +180 -1
- package/src/core/ddl/nodes.ts +141 -2
- package/src/core/default-normalizer.ts +122 -2
- package/src/core/descriptor-meta.ts +10 -1
- package/src/core/entity-kinds.ts +41 -0
- package/src/core/errors.ts +14 -12
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/column-ddl-rendering.ts +117 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +160 -0
- package/src/core/migrations/control-policy.ts +126 -44
- package/src/core/migrations/diff-database-schema.ts +253 -0
- package/src/core/migrations/issue-planner.ts +692 -652
- package/src/core/migrations/op-factory-call.ts +189 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/constraints.ts +1 -1
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/dependencies.ts +2 -1
- package/src/core/migrations/operations/rls.ts +187 -0
- package/src/core/migrations/operations/shared.ts +0 -30
- package/src/core/migrations/planner-ddl-builders.ts +20 -58
- package/src/core/migrations/planner-identity-values.ts +1 -1
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-sql-checks.ts +1 -1
- package/src/core/migrations/planner-strategies.ts +248 -216
- package/src/core/migrations/planner-target-details.ts +3 -1
- package/src/core/migrations/planner.ts +386 -59
- package/src/core/migrations/postgres-migration.ts +70 -86
- package/src/core/migrations/render-typescript.ts +42 -15
- package/src/core/migrations/resolve-ddl-schema.ts +26 -0
- package/src/core/migrations/runner.ts +10 -16
- package/src/core/migrations/verify-postgres-namespaces.ts +47 -31
- package/src/core/namespace-ids.ts +3 -0
- package/src/core/postgres-contract-serializer.ts +46 -71
- package/src/core/postgres-contract-view.ts +48 -0
- package/src/core/postgres-native-enum.ts +51 -0
- package/src/core/postgres-rls-enablement.ts +34 -0
- package/src/core/postgres-rls-policy.ts +64 -0
- package/src/core/postgres-role.ts +59 -0
- package/src/core/postgres-schema-verifier.ts +10 -7
- package/src/core/postgres-schema.ts +64 -15
- package/src/core/postgres-validators.ts +34 -0
- package/src/core/psl-infer/infer-psl-contract.ts +1326 -0
- package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
- package/src/core/psl-infer/postgres-type-map.ts +128 -0
- package/src/core/rls/canonicalize.ts +48 -0
- package/src/core/rls/wire-name.ts +33 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +61 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +79 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +90 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +65 -0
- package/src/core/schema-ir/postgres-table-schema-node.ts +140 -0
- package/src/core/schema-ir/schema-node-kinds.ts +113 -0
- package/src/exports/codecs.ts +3 -0
- package/src/exports/contract-free.ts +11 -1
- package/src/exports/control.ts +27 -33
- package/src/exports/ddl.ts +6 -0
- package/src/exports/diff-database-schema.ts +7 -0
- package/src/exports/issue-planner.ts +1 -1
- package/src/exports/migration.ts +1 -0
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/src/exports/planner-identity-values.ts +4 -1
- package/src/exports/planner.ts +1 -0
- package/src/exports/rls-canonicalize.ts +7 -0
- package/src/exports/runtime.ts +1 -0
- package/src/exports/schema-ir-annotations.ts +1 -0
- package/src/exports/types.ts +34 -0
- package/dist/codec-ids-BvytN2P8.mjs.map +0 -1
- package/dist/codec-ids-CnXu9Qy3.d.mts.map +0 -1
- package/dist/codec-types-DHCkwPKE.d.mts.map +0 -1
- package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
- package/dist/ddl-QDyOSeLc.mjs.map +0 -1
- package/dist/default-normalizer-DyyCHQWs.mjs +0 -131
- package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
- package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
- package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
- package/dist/errors-CUk87ByX.mjs +0 -33
- package/dist/errors-CUk87ByX.mjs.map +0 -1
- package/dist/issue-planner-DL6g3CmE.mjs +0 -824
- package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
- package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
- package/dist/native-type-normalizer-Bc9XJzWC.mjs.map +0 -1
- package/dist/nodes-Bbhs2rwj.mjs +0 -76
- package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
- package/dist/nodes-pLeLgdis.d.mts +0 -67
- package/dist/nodes-pLeLgdis.d.mts.map +0 -1
- package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
- package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
- package/dist/planner-Bs_baQax.mjs +0 -324
- package/dist/planner-Bs_baQax.mjs.map +0 -1
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
- package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
- package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
- package/dist/postgres-migration-B5jKrXv3.mjs.map +0 -1
- package/dist/postgres-migration-Y4YBJqkS.d.mts.map +0 -1
- package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
- package/dist/render-typescript-KMgosran.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/src/core/postgres-enum-type-schema.ts +0 -17
|
@@ -10,20 +10,27 @@ import {
|
|
|
10
10
|
isAuthoringEntityTypeDescriptor,
|
|
11
11
|
} from '@prisma-next/framework-components/authoring';
|
|
12
12
|
import {
|
|
13
|
+
type AnyEntityKindDescriptor,
|
|
13
14
|
type Namespace,
|
|
14
|
-
NamespaceBase,
|
|
15
15
|
UNBOUND_NAMESPACE_ID,
|
|
16
16
|
} from '@prisma-next/framework-components/ir';
|
|
17
|
-
import type {
|
|
17
|
+
import type { SqlNamespaceInput, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
18
18
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
19
19
|
import type { JsonObject } from '@prisma-next/utils/json';
|
|
20
20
|
import { postgresAuthoringEntityTypes } from './authoring';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
21
|
+
import { PG_INT_CODEC_ID, PG_TEXT_CODEC_ID } from './codec-ids';
|
|
22
|
+
import {
|
|
23
|
+
nativeEnumEntityKind,
|
|
24
|
+
policyEntityKind,
|
|
25
|
+
rlsEnablementEntityKind,
|
|
26
|
+
roleEntityKind,
|
|
27
|
+
} from './entity-kinds';
|
|
28
|
+
import { PostgresSchema } from './postgres-schema';
|
|
23
29
|
|
|
24
30
|
const POSTGRES_AUTHORING_CTX: AuthoringEntityContext = {
|
|
25
31
|
family: 'sql',
|
|
26
32
|
target: 'postgres',
|
|
33
|
+
enumInferenceCodecs: { text: PG_TEXT_CODEC_ID, int: PG_INT_CODEC_ID },
|
|
27
34
|
};
|
|
28
35
|
|
|
29
36
|
function isAuthoringEntityTypeFactoryOutput(
|
|
@@ -32,7 +39,8 @@ function isAuthoringEntityTypeFactoryOutput(
|
|
|
32
39
|
return (
|
|
33
40
|
typeof output === 'object' &&
|
|
34
41
|
output !== null &&
|
|
35
|
-
|
|
42
|
+
'factory' in output &&
|
|
43
|
+
typeof output.factory === 'function'
|
|
36
44
|
);
|
|
37
45
|
}
|
|
38
46
|
|
|
@@ -65,34 +73,35 @@ function collectStorageTypesHydrators(
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
export class PostgresContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {
|
|
68
|
-
constructor() {
|
|
76
|
+
constructor(extraPackEntityKinds: readonly AnyEntityKindDescriptor[] = []) {
|
|
69
77
|
const storageTypesHydrators = collectStorageTypesHydrators(postgresAuthoringEntityTypes);
|
|
70
|
-
super(storageTypesHydrators
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
super(storageTypesHydrators, [
|
|
79
|
+
policyEntityKind,
|
|
80
|
+
roleEntityKind,
|
|
81
|
+
rlsEnablementEntityKind,
|
|
82
|
+
nativeEnumEntityKind,
|
|
83
|
+
...extraPackEntityKinds,
|
|
84
|
+
]);
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
protected override hydrateSqlNamespaceEntry(
|
|
78
88
|
nsId: string,
|
|
79
|
-
raw:
|
|
80
|
-
): Namespace |
|
|
81
|
-
if (raw instanceof NamespaceBase) {
|
|
82
|
-
return raw;
|
|
83
|
-
}
|
|
89
|
+
raw: Record<string, unknown>,
|
|
90
|
+
): Namespace | SqlNamespaceInput {
|
|
84
91
|
const hydrated = blindCast<
|
|
85
|
-
|
|
86
|
-
'
|
|
92
|
+
SqlNamespaceInput,
|
|
93
|
+
'raw is always plain JSON, so super.hydrateSqlNamespaceEntry returns SqlNamespaceInput'
|
|
87
94
|
>(super.hydrateSqlNamespaceEntry(nsId, raw));
|
|
88
95
|
const { id, entries } = hydrated;
|
|
89
96
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (id === UNBOUND_NAMESPACE_ID &&
|
|
97
|
+
const allSlotsEmpty = Object.values(entries).every(
|
|
98
|
+
(slot) => slot === undefined || Object.keys(slot).length === 0,
|
|
99
|
+
);
|
|
100
|
+
if (id === UNBOUND_NAMESPACE_ID && allSlotsEmpty) {
|
|
94
101
|
return PostgresSchema.unbound;
|
|
95
102
|
}
|
|
103
|
+
const valueSetSlot = entries['valueSet'];
|
|
104
|
+
const hasValueSets = valueSetSlot !== undefined && Object.keys(valueSetSlot).length > 0;
|
|
96
105
|
return new PostgresSchema({
|
|
97
106
|
id,
|
|
98
107
|
entries: {
|
|
@@ -106,24 +115,16 @@ export class PostgresContractSerializer extends SqlContractSerializerBase<Contra
|
|
|
106
115
|
override serializeContract(contract: Contract<SqlStorage>): JsonObject {
|
|
107
116
|
const { storage, ...rest } = contract;
|
|
108
117
|
const namespacesJson: Record<string, JsonObject> = {};
|
|
118
|
+
// Each namespace serializes to its id, its schema-kind tag, and the
|
|
119
|
+
// base's generic entries walk — every enumerable kind on
|
|
120
|
+
// `PostgresSchema.entries`, including `native_enum`.
|
|
109
121
|
for (const [nsId, ns] of Object.entries(storage.namespaces)) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
|
|
117
|
-
entries: {
|
|
118
|
-
table: Object.fromEntries(
|
|
119
|
-
Object.entries(ns.entries.table ?? {}).map(([tableName, table]) => [
|
|
120
|
-
tableName,
|
|
121
|
-
this.serializeJsonValue(table) as JsonObject,
|
|
122
|
-
]),
|
|
123
|
-
),
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
}
|
|
122
|
+
const isUnboundSlot = ns.id === UNBOUND_NAMESPACE_ID;
|
|
123
|
+
namespacesJson[nsId] = {
|
|
124
|
+
id: ns.id,
|
|
125
|
+
kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
|
|
126
|
+
entries: this.serializeNamespaceEntries(ns.entries),
|
|
127
|
+
};
|
|
127
128
|
}
|
|
128
129
|
const storageOut: Record<string, unknown> = {
|
|
129
130
|
storageHash: String(storage.storageHash),
|
|
@@ -132,42 +133,16 @@ export class PostgresContractSerializer extends SqlContractSerializerBase<Contra
|
|
|
132
133
|
if (storage.types !== undefined) {
|
|
133
134
|
const typesOut: Record<string, JsonObject> = {};
|
|
134
135
|
for (const [name, entry] of Object.entries(storage.types)) {
|
|
135
|
-
typesOut[name] = this.
|
|
136
|
+
typesOut[name] = this.serializeJsonObject(entry);
|
|
136
137
|
}
|
|
137
138
|
storageOut['types'] = typesOut;
|
|
138
139
|
}
|
|
139
|
-
return
|
|
140
|
+
return blindCast<
|
|
141
|
+
JsonObject,
|
|
142
|
+
'contract minus storage plus a JSON-shaped storageOut is a JsonObject'
|
|
143
|
+
>({
|
|
140
144
|
...rest,
|
|
141
145
|
storage: storageOut,
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private serializePostgresNamespace(ns: PostgresSchema, isUnboundSlot: boolean): JsonObject {
|
|
146
|
-
const tablesOut: Record<string, JsonObject> = {};
|
|
147
|
-
for (const [tableName, table] of Object.entries(ns.table)) {
|
|
148
|
-
tablesOut[tableName] = this.serializeJsonValue(table) as JsonObject;
|
|
149
|
-
}
|
|
150
|
-
const valueSetEntries = ns.valueSet;
|
|
151
|
-
const valueSetOut: Record<string, JsonObject> = {};
|
|
152
|
-
if (valueSetEntries !== undefined) {
|
|
153
|
-
for (const [valueSetName, valueSet] of Object.entries(valueSetEntries)) {
|
|
154
|
-
valueSetOut[valueSetName] = blindCast<
|
|
155
|
-
JsonObject,
|
|
156
|
-
'serializeJsonValue round-trips the value-set node through JSON, yielding a JsonObject'
|
|
157
|
-
>(this.serializeJsonValue(valueSet));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
id: ns.id,
|
|
162
|
-
kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
|
|
163
|
-
entries: {
|
|
164
|
-
table: tablesOut,
|
|
165
|
-
...(Object.keys(valueSetOut).length > 0 ? { valueSet: valueSetOut } : {}),
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private serializeJsonValue(value: unknown): unknown {
|
|
171
|
-
return JSON.parse(JSON.stringify(value)) as unknown;
|
|
146
|
+
});
|
|
172
147
|
}
|
|
173
148
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import {
|
|
3
|
+
buildSqlSchemaQualifiedView,
|
|
4
|
+
type SqlSchemaQualifiedView,
|
|
5
|
+
} from '@prisma-next/sql-contract/contract-view';
|
|
6
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
7
|
+
import { PostgresContractSerializer } from './postgres-contract-serializer';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A schema-qualified Postgres contract view: the deserialized contract
|
|
11
|
+
* intersected with a single `namespace` member holding every schema by id. It is
|
|
12
|
+
* substitutable for `Contract` (carries `storage`, `domain`, …) and reaches each
|
|
13
|
+
* schema's entities through `view.namespace.<schema>`:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const view = PostgresContractView.fromJson<Contract>(contractJson);
|
|
17
|
+
* view.namespace.public.table.users // typed table leaf in the public schema
|
|
18
|
+
* view.namespace.auth.table.users // the auth schema's own users table
|
|
19
|
+
* view.namespace.public.entries.policy.X // pack-contributed kind (RLS / #771 path)
|
|
20
|
+
* view.namespace.__unbound__.table.X // default schema, keyed by its raw id
|
|
21
|
+
* view.storage // the full contract is still present
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* This mirrors the runtime `db.enums.<ns>` keying exactly (the default schema
|
|
25
|
+
* keeps its literal `__unbound__` id). Schema names are NOT promoted to the
|
|
26
|
+
* contract root, so there is no collision with contract envelope fields — a
|
|
27
|
+
* schema named `storage` is `view.namespace.storage`, while `view.storage`
|
|
28
|
+
* stays the contract's `storage`.
|
|
29
|
+
*/
|
|
30
|
+
export type PostgresContractView<TContract extends Contract<SqlStorage> = Contract<SqlStorage>> =
|
|
31
|
+
SqlSchemaQualifiedView<TContract>;
|
|
32
|
+
|
|
33
|
+
export const PostgresContractView = {
|
|
34
|
+
/** Wrap an already-deserialized Postgres contract in a schema-qualified view. */
|
|
35
|
+
from<TContract extends Contract<SqlStorage>>(
|
|
36
|
+
contract: TContract,
|
|
37
|
+
): PostgresContractView<TContract> {
|
|
38
|
+
return buildSqlSchemaQualifiedView(contract);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/** Deserialize a Postgres contract JSON envelope and wrap it in a view. */
|
|
42
|
+
fromJson<TContract extends Contract<SqlStorage> = Contract<SqlStorage>>(
|
|
43
|
+
json: unknown,
|
|
44
|
+
): PostgresContractView<TContract> {
|
|
45
|
+
const contract = new PostgresContractSerializer().deserializeContract<TContract>(json);
|
|
46
|
+
return buildSqlSchemaQualifiedView(contract);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import { SqlNode } from '@prisma-next/sql-contract/types';
|
|
4
|
+
|
|
5
|
+
export interface PostgresNativeEnumInput {
|
|
6
|
+
/** The Postgres type name (`CREATE TYPE <typeName> AS ENUM (…)`). */
|
|
7
|
+
readonly typeName: string;
|
|
8
|
+
/** Member values in declaration order — this is the Postgres enum sort order. */
|
|
9
|
+
readonly members: readonly string[];
|
|
10
|
+
readonly control?: ControlPolicy;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Postgres contract-IR class for a native enum type (`CREATE TYPE … AS ENUM (…)`).
|
|
15
|
+
*
|
|
16
|
+
* This is an authored, serialized Contract-IR entity — it is registered as an entity
|
|
17
|
+
* kind, extends `SqlNode`, and is stored in `contract.json`. It is NOT a DiffableNode;
|
|
18
|
+
* the schema-diff tree will use `PostgresNativeEnumSchemaNode` when the managed phase
|
|
19
|
+
* builds it.
|
|
20
|
+
*
|
|
21
|
+
* A member is a value, not a name→value pair — matching `CREATE TYPE … AS ENUM
|
|
22
|
+
* ('a', 'b')`, which has no separate member name.
|
|
23
|
+
*
|
|
24
|
+
* Target-only concept — no SQL-family abstract. Extends `SqlNode` directly,
|
|
25
|
+
* frozen at construction via `freezeNode(this)`. The `kind: 'postgres-enum'`
|
|
26
|
+
* discriminant is enumerable so it survives JSON. Lives at
|
|
27
|
+
* `storage.namespaces[ns].entries.native_enum[HandleName]`; the entries key
|
|
28
|
+
* (`native_enum`) is the entity-kind descriptor's `kind`, decoupled from this
|
|
29
|
+
* node's own `kind` literal — the same shape as `table`/`StorageTable` and
|
|
30
|
+
* `valueSet`/`StorageValueSet`.
|
|
31
|
+
*/
|
|
32
|
+
export class PostgresNativeEnum extends SqlNode {
|
|
33
|
+
static is(node: unknown): node is PostgresNativeEnum {
|
|
34
|
+
return (
|
|
35
|
+
typeof node === 'object' && node !== null && 'kind' in node && node.kind === 'postgres-enum'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override readonly kind = 'postgres-enum' as const;
|
|
40
|
+
readonly typeName: string;
|
|
41
|
+
readonly members: readonly string[];
|
|
42
|
+
declare readonly control?: ControlPolicy;
|
|
43
|
+
|
|
44
|
+
constructor(input: PostgresNativeEnumInput) {
|
|
45
|
+
super();
|
|
46
|
+
this.typeName = input.typeName;
|
|
47
|
+
this.members = Object.freeze([...input.members]);
|
|
48
|
+
if (input.control !== undefined) this.control = input.control;
|
|
49
|
+
freezeNode(this);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
2
|
+
import { SqlNode } from '@prisma-next/sql-contract/types';
|
|
3
|
+
|
|
4
|
+
export interface PostgresRlsEnablementInput {
|
|
5
|
+
/** Name of the table this marker declares RLS-controlled. */
|
|
6
|
+
readonly tableName: string;
|
|
7
|
+
/** Namespace coordinate (schema name). Markers are schema-scoped like policies. */
|
|
8
|
+
readonly namespaceId: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Postgres contract-IR marker for `@@rls` on a model: the table named here is
|
|
13
|
+
* RLS-controlled (`ENABLE ROW LEVEL SECURITY` is driven by this marker, never
|
|
14
|
+
* by the policy set).
|
|
15
|
+
*
|
|
16
|
+
* This is an authored, serialized Contract-IR entity — it is registered as an
|
|
17
|
+
* entity kind, extends `SqlNode`, and is stored in `contract.json` under
|
|
18
|
+
* `entries.rls[tableName]`. Target-only concept — no SQL-family abstract.
|
|
19
|
+
* Frozen at construction via `freezeNode(this)`. The `kind: 'rls'`
|
|
20
|
+
* discriminant is enumerable so it survives JSON serialization; the literal
|
|
21
|
+
* matches the entries key (one-string rule).
|
|
22
|
+
*/
|
|
23
|
+
export class PostgresRlsEnablement extends SqlNode {
|
|
24
|
+
override readonly kind = 'rls' as const;
|
|
25
|
+
readonly tableName: string;
|
|
26
|
+
readonly namespaceId: string;
|
|
27
|
+
|
|
28
|
+
constructor(input: PostgresRlsEnablementInput) {
|
|
29
|
+
super();
|
|
30
|
+
this.tableName = input.tableName;
|
|
31
|
+
this.namespaceId = input.namespaceId;
|
|
32
|
+
freezeNode(this);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
2
|
+
import { SqlNode } from '@prisma-next/sql-contract/types';
|
|
3
|
+
|
|
4
|
+
export type RlsPolicyOperation = 'select' | 'insert' | 'update' | 'delete' | 'all';
|
|
5
|
+
|
|
6
|
+
export interface PostgresRlsPolicyInput {
|
|
7
|
+
/** Full wire name: `<prefix>_<8hex>`. Stored as-is; hashing is not this class's job. */
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/** User-supplied prefix (the part before the `_<8hex>` suffix). */
|
|
10
|
+
readonly prefix: string;
|
|
11
|
+
/** Name of the table this policy attaches to, by name within the same schema. */
|
|
12
|
+
readonly tableName: string;
|
|
13
|
+
/** Namespace coordinate (schema name). Policies are schema-scoped. */
|
|
14
|
+
readonly namespaceId: string;
|
|
15
|
+
readonly operation: RlsPolicyOperation;
|
|
16
|
+
/** Sorted role names rendered in `TO <roles>`. Plain strings in this slice. */
|
|
17
|
+
readonly roles: readonly string[];
|
|
18
|
+
/** USING predicate SQL string, if present. */
|
|
19
|
+
readonly using?: string;
|
|
20
|
+
/** WITH CHECK predicate SQL string, if present. */
|
|
21
|
+
readonly withCheck?: string;
|
|
22
|
+
/** `true` = `AS PERMISSIVE`, `false` = `AS RESTRICTIVE`. */
|
|
23
|
+
readonly permissive: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Postgres contract-IR class for a row-level security policy (`CREATE POLICY … ON …`).
|
|
28
|
+
*
|
|
29
|
+
* This is an authored, serialized Contract-IR entity — it is registered as an entity
|
|
30
|
+
* kind, extends `SqlNode`, and is stored in `contract.json`. It is NOT a DiffableNode;
|
|
31
|
+
* the schema-diff tree uses `PostgresPolicySchemaNode` for that role.
|
|
32
|
+
*
|
|
33
|
+
* Target-only concept — no SQL-family abstract. Extends `SqlNode` directly.
|
|
34
|
+
* Frozen at construction via `freezeNode(this)`. The `kind: 'policy'`
|
|
35
|
+
* discriminant is enumerable (overrides SqlNode's non-enumerable `'sql'`) so it
|
|
36
|
+
* survives JSON serialization and enables dispatch. The literal matches the
|
|
37
|
+
* entries key (one-string rule: node.kind === entries key === entity kind).
|
|
38
|
+
*/
|
|
39
|
+
export class PostgresRlsPolicy extends SqlNode {
|
|
40
|
+
override readonly kind = 'policy' as const;
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly prefix: string;
|
|
43
|
+
readonly tableName: string;
|
|
44
|
+
readonly namespaceId: string;
|
|
45
|
+
readonly operation: RlsPolicyOperation;
|
|
46
|
+
readonly roles: readonly string[];
|
|
47
|
+
declare readonly using?: string;
|
|
48
|
+
declare readonly withCheck?: string;
|
|
49
|
+
readonly permissive: boolean;
|
|
50
|
+
|
|
51
|
+
constructor(input: PostgresRlsPolicyInput) {
|
|
52
|
+
super();
|
|
53
|
+
this.name = input.name;
|
|
54
|
+
this.prefix = input.prefix;
|
|
55
|
+
this.tableName = input.tableName;
|
|
56
|
+
this.namespaceId = input.namespaceId;
|
|
57
|
+
this.operation = input.operation;
|
|
58
|
+
this.roles = Object.freeze([...input.roles]);
|
|
59
|
+
if (input.using !== undefined) this.using = input.using;
|
|
60
|
+
if (input.withCheck !== undefined) this.withCheck = input.withCheck;
|
|
61
|
+
this.permissive = input.permissive;
|
|
62
|
+
freezeNode(this);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
2
|
+
import { SqlNode } from '@prisma-next/sql-contract/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Roles are referenced by the contract but never owned — the framework
|
|
6
|
+
* issues no `CREATE`/`DROP ROLE`, so a role's effective governance is always
|
|
7
|
+
* `external`: a missing declared role fails verify, an extra live role is
|
|
8
|
+
* tolerated, and the planner emits zero role DDL. `external` is the only
|
|
9
|
+
* value a role's control can ever carry.
|
|
10
|
+
*/
|
|
11
|
+
export const ROLE_DEFAULT_CONTROL_POLICY = 'external';
|
|
12
|
+
|
|
13
|
+
export interface PostgresRoleInput {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/**
|
|
16
|
+
* Namespace coordinate. Roles are cluster-scoped; pass `UNBOUND_NAMESPACE_ID`
|
|
17
|
+
* from `@prisma-next/framework-components/ir`.
|
|
18
|
+
*/
|
|
19
|
+
readonly namespaceId: string;
|
|
20
|
+
/**
|
|
21
|
+
* Defaults to {@link ROLE_DEFAULT_CONTROL_POLICY} when omitted. Roles are
|
|
22
|
+
* referenced but never owned, so `'external'` is the only allowed value.
|
|
23
|
+
*/
|
|
24
|
+
readonly control?: 'external';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Postgres contract-IR class for a database role (`CREATE ROLE …`).
|
|
29
|
+
*
|
|
30
|
+
* This is an authored, serialized Contract-IR entity — it is registered as an entity
|
|
31
|
+
* kind, extends `SqlNode`, and is stored in `contract.json`. It is NOT a DiffableNode;
|
|
32
|
+
* the schema-diff tree uses `PostgresRoleSchemaNode` for that role.
|
|
33
|
+
*
|
|
34
|
+
* Roles are cluster-scoped, so their namespace coordinate is always
|
|
35
|
+
* `UNBOUND_NAMESPACE_ID`. Target-only concept — no SQL-family abstract.
|
|
36
|
+
* Extends `SqlNode` directly, frozen at construction via `freezeNode(this)`.
|
|
37
|
+
* The `kind: 'role'` discriminant is enumerable so it survives JSON.
|
|
38
|
+
* Matches the entries key (one-string rule).
|
|
39
|
+
*
|
|
40
|
+
* `control` always carries a value (never omitted): it defaults to
|
|
41
|
+
* {@link ROLE_DEFAULT_CONTROL_POLICY} in the constructor, so both a
|
|
42
|
+
* freshly-authored role and one hydrated from an older `contract.json`
|
|
43
|
+
* lacking the field resolve the same way — the control-policy resolver
|
|
44
|
+
* reads it directly off the entity instead of special-casing role issues.
|
|
45
|
+
*/
|
|
46
|
+
export class PostgresRole extends SqlNode {
|
|
47
|
+
override readonly kind = 'role' as const;
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly namespaceId: string;
|
|
50
|
+
readonly control: 'external';
|
|
51
|
+
|
|
52
|
+
constructor(input: PostgresRoleInput) {
|
|
53
|
+
super();
|
|
54
|
+
this.name = input.name;
|
|
55
|
+
this.namespaceId = input.namespaceId;
|
|
56
|
+
this.control = input.control ?? ROLE_DEFAULT_CONTROL_POLICY;
|
|
57
|
+
freezeNode(this);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
2
|
import { SqlSchemaVerifierBase } from '@prisma-next/family-sql/ir';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
SchemaDiffIssue,
|
|
5
|
+
SchemaVerifyOptions,
|
|
6
|
+
} from '@prisma-next/framework-components/control';
|
|
4
7
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
5
8
|
import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Postgres target `SchemaVerifier` concretion. Plugs into the
|
|
9
12
|
* SQL-shared verification surface; production verification today still
|
|
10
|
-
* routes through the
|
|
11
|
-
* carries options (codec hooks,
|
|
12
|
-
* that the framework-level
|
|
13
|
-
* surface.
|
|
13
|
+
* routes through the family verify verdict (`verifySqlSchemaByDiff` over
|
|
14
|
+
* `diffPostgresSchema`), which carries options (codec hooks,
|
|
15
|
+
* normalizers, framework components) that the framework-level
|
|
16
|
+
* `SchemaVerifyOptions` shape does not yet surface.
|
|
14
17
|
*
|
|
15
18
|
* The hooks return the empty list pending the call-site migration that
|
|
16
19
|
* routes the existing verifier behaviour through the SPI — at that
|
|
@@ -25,13 +28,13 @@ export class PostgresSchemaVerifier extends SqlSchemaVerifierBase<
|
|
|
25
28
|
> {
|
|
26
29
|
protected verifyCommonSqlSchema(
|
|
27
30
|
_options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,
|
|
28
|
-
): readonly
|
|
31
|
+
): readonly SchemaDiffIssue[] {
|
|
29
32
|
return [];
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
protected verifyTargetExtensions(
|
|
33
36
|
_options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,
|
|
34
|
-
): readonly
|
|
37
|
+
): readonly SchemaDiffIssue[] {
|
|
35
38
|
return [];
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -1,24 +1,45 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import {
|
|
2
3
|
freezeNode,
|
|
3
4
|
hydrateNamespaceEntities,
|
|
4
|
-
NamespaceBase,
|
|
5
5
|
UNBOUND_NAMESPACE_ID,
|
|
6
6
|
} from '@prisma-next/framework-components/ir';
|
|
7
7
|
import { composeSqlEntityKinds } from '@prisma-next/sql-contract/entity-kinds';
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
import {
|
|
9
|
+
SqlNamespaceBase,
|
|
10
|
+
type SqlNamespaceEntries,
|
|
11
|
+
type SqlNamespaceInput,
|
|
12
|
+
type SqlStorage,
|
|
13
|
+
type StorageTable,
|
|
14
|
+
type StorageValueSet,
|
|
14
15
|
} from '@prisma-next/sql-contract/types';
|
|
16
|
+
|
|
15
17
|
import { type CfExpr, cfExpr } from '@prisma-next/sql-relational-core/contract-free';
|
|
16
18
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
17
19
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
18
20
|
import { PostgresTableSource } from './ast/table-source';
|
|
19
21
|
import { PG_TEXT_CODEC_ID } from './codec-ids';
|
|
22
|
+
import {
|
|
23
|
+
nativeEnumEntityKind,
|
|
24
|
+
policyEntityKind,
|
|
25
|
+
rlsEnablementEntityKind,
|
|
26
|
+
roleEntityKind,
|
|
27
|
+
} from './entity-kinds';
|
|
28
|
+
import type { PostgresNativeEnum } from './postgres-native-enum';
|
|
29
|
+
import type { PostgresRlsEnablement } from './postgres-rls-enablement';
|
|
30
|
+
import type { PostgresRlsPolicy } from './postgres-rls-policy';
|
|
31
|
+
import type { PostgresRole } from './postgres-role';
|
|
20
32
|
import { escapeLiteral } from './sql-utils';
|
|
21
33
|
|
|
34
|
+
export type PostgresContract = Contract<SqlStorage> & { readonly target: 'postgres' };
|
|
35
|
+
|
|
36
|
+
export type PostgresNamespaceEntries = SqlNamespaceEntries & {
|
|
37
|
+
readonly policy?: Readonly<Record<string, PostgresRlsPolicy>>;
|
|
38
|
+
readonly role?: Readonly<Record<string, PostgresRole>>;
|
|
39
|
+
readonly rls?: Readonly<Record<string, PostgresRlsEnablement>>;
|
|
40
|
+
readonly native_enum?: Readonly<Record<string, PostgresNativeEnum>>;
|
|
41
|
+
};
|
|
42
|
+
|
|
22
43
|
export interface PostgresSchemaInput {
|
|
23
44
|
readonly id: string;
|
|
24
45
|
readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
|
|
@@ -38,7 +59,7 @@ export interface PostgresSchemaInput {
|
|
|
38
59
|
* prefix entirely — call sites stay polymorphic and never branch on
|
|
39
60
|
* `id === UNBOUND_NAMESPACE_ID`.
|
|
40
61
|
*/
|
|
41
|
-
export class PostgresSchema extends
|
|
62
|
+
export class PostgresSchema extends SqlNamespaceBase {
|
|
42
63
|
/**
|
|
43
64
|
* Stable singleton reference for the late-bound slot. Materialised
|
|
44
65
|
* lazily below the singleton subclass declaration so the static
|
|
@@ -50,13 +71,22 @@ export class PostgresSchema extends NamespaceBase {
|
|
|
50
71
|
|
|
51
72
|
declare readonly kind: 'schema';
|
|
52
73
|
readonly id: string;
|
|
53
|
-
readonly entries:
|
|
74
|
+
readonly entries: PostgresNamespaceEntries;
|
|
54
75
|
|
|
55
76
|
constructor(input: PostgresSchemaInput) {
|
|
56
77
|
super();
|
|
57
78
|
this.id = input.id;
|
|
58
79
|
|
|
59
|
-
const dispatched = hydrateNamespaceEntities(
|
|
80
|
+
const dispatched = hydrateNamespaceEntities(
|
|
81
|
+
input.entries,
|
|
82
|
+
composeSqlEntityKinds([
|
|
83
|
+
policyEntityKind,
|
|
84
|
+
roleEntityKind,
|
|
85
|
+
rlsEnablementEntityKind,
|
|
86
|
+
nativeEnumEntityKind,
|
|
87
|
+
]),
|
|
88
|
+
'carry',
|
|
89
|
+
);
|
|
60
90
|
|
|
61
91
|
// Drop an empty valueSet so presence signals non-emptiness.
|
|
62
92
|
const valueSetRaw = dispatched['valueSet'];
|
|
@@ -65,11 +95,15 @@ export class PostgresSchema extends NamespaceBase {
|
|
|
65
95
|
? { ...dispatched, valueSet: undefined }
|
|
66
96
|
: dispatched;
|
|
67
97
|
|
|
98
|
+
const entriesInput: Record<string, Readonly<Record<string, unknown>> | undefined> = {
|
|
99
|
+
...withPresence,
|
|
100
|
+
};
|
|
101
|
+
|
|
68
102
|
this.entries = Object.freeze(
|
|
69
103
|
blindCast<
|
|
70
|
-
|
|
71
|
-
'composeSqlEntityKinds() supplies table→StorageTable
|
|
72
|
-
>(
|
|
104
|
+
PostgresNamespaceEntries,
|
|
105
|
+
'composeSqlEntityKinds([policyEntityKind, roleEntityKind, rlsEnablementEntityKind, nativeEnumEntityKind]) supplies table→StorageTable, valueSet→StorageValueSet, policy→PostgresRlsPolicy, role→PostgresRole, rls→PostgresRlsEnablement, native_enum→PostgresNativeEnum descriptors'
|
|
106
|
+
>(entriesInput),
|
|
73
107
|
);
|
|
74
108
|
Object.defineProperty(this, 'kind', {
|
|
75
109
|
value: 'schema',
|
|
@@ -88,6 +122,18 @@ export class PostgresSchema extends NamespaceBase {
|
|
|
88
122
|
return this.entries.valueSet;
|
|
89
123
|
}
|
|
90
124
|
|
|
125
|
+
get policy(): Readonly<Record<string, PostgresRlsPolicy>> {
|
|
126
|
+
return this.entries.policy ?? Object.freeze({});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get role(): Readonly<Record<string, PostgresRole>> {
|
|
130
|
+
return this.entries.role ?? Object.freeze({});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get rls(): Readonly<Record<string, PostgresRlsEnablement>> {
|
|
134
|
+
return this.entries.rls ?? Object.freeze({});
|
|
135
|
+
}
|
|
136
|
+
|
|
91
137
|
/**
|
|
92
138
|
* The bare schema qualifier as it would appear in a rendered SQL
|
|
93
139
|
* fragment (already quoted). The unbound-schema singleton overrides
|
|
@@ -245,9 +291,12 @@ export function isPostgresSchema(ns: unknown): ns is PostgresSchema {
|
|
|
245
291
|
* The factory has no per-call state — every named id deterministically
|
|
246
292
|
* maps to a distinct schema instance — so callers can pass it through
|
|
247
293
|
* by reference and trust the resulting `SqlStorage.namespaces` map to
|
|
248
|
-
* be value-stable for a given input set.
|
|
294
|
+
* be value-stable for a given input set. Native-enum column type names are
|
|
295
|
+
* schema-qualified earlier, at column construction, via the target's
|
|
296
|
+
* `authoring.qualifyColumnType` hook (`postgresQualifyColumnType`), so this
|
|
297
|
+
* factory just materialises the namespace from already-qualified entries.
|
|
249
298
|
*/
|
|
250
|
-
export function postgresCreateNamespace(input:
|
|
299
|
+
export function postgresCreateNamespace(input: SqlNamespaceInput): PostgresSchema {
|
|
251
300
|
const schemaInput: PostgresSchemaInput = {
|
|
252
301
|
id: input.id,
|
|
253
302
|
entries: {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type } from 'arktype';
|
|
2
|
+
|
|
3
|
+
export const PostgresRoleSchema = type({
|
|
4
|
+
kind: "'role'",
|
|
5
|
+
name: 'string',
|
|
6
|
+
namespaceId: 'string',
|
|
7
|
+
'control?': "'external'",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const PostgresRlsPolicySchema = type({
|
|
11
|
+
kind: "'policy'",
|
|
12
|
+
name: 'string',
|
|
13
|
+
prefix: 'string',
|
|
14
|
+
tableName: 'string',
|
|
15
|
+
namespaceId: 'string',
|
|
16
|
+
operation: "'select' | 'insert' | 'update' | 'delete' | 'all'",
|
|
17
|
+
roles: type.string.array().readonly(),
|
|
18
|
+
'using?': 'string',
|
|
19
|
+
'withCheck?': 'string',
|
|
20
|
+
permissive: 'boolean',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const PostgresRlsEnablementSchema = type({
|
|
24
|
+
kind: "'rls'",
|
|
25
|
+
tableName: 'string',
|
|
26
|
+
namespaceId: 'string',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const PostgresNativeEnumSchema = type({
|
|
30
|
+
kind: "'postgres-enum'",
|
|
31
|
+
typeName: 'string',
|
|
32
|
+
members: type.string.array().readonly(),
|
|
33
|
+
'control?': "'managed' | 'tolerated' | 'external' | 'observed'",
|
|
34
|
+
});
|