@prisma-next/target-postgres 0.5.0-dev.6 → 0.5.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/codec-ids-B-wNPs-9.d.mts +29 -0
- package/dist/codec-ids-B-wNPs-9.d.mts.map +1 -0
- package/dist/{codec-ids-CojIXVf9.mjs → codec-ids-ckQX9Kcg.mjs} +3 -2
- package/dist/{codec-ids-CojIXVf9.mjs.map → codec-ids-ckQX9Kcg.mjs.map} +1 -1
- package/dist/codec-ids.d.mts +2 -28
- package/dist/codec-ids.mjs +2 -2
- package/dist/codec-types-Br-rrBBQ.d.mts +80 -0
- package/dist/codec-types-Br-rrBBQ.d.mts.map +1 -0
- package/dist/codec-types.d.mts +4 -42
- package/dist/codec-types.mjs +1 -3
- package/dist/codecs-DZUnQrrl.d.mts +559 -0
- package/dist/codecs-DZUnQrrl.d.mts.map +1 -0
- package/dist/codecs.d.mts +15 -2
- package/dist/codecs.d.mts.map +1 -0
- package/dist/codecs.mjs +742 -2
- package/dist/codecs.mjs.map +1 -0
- package/dist/control.d.mts +1 -1
- package/dist/control.mjs +26 -68
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-VfEGzXWt.mjs → data-transform-CR_9PvW9.mjs} +24 -5
- package/dist/data-transform-CR_9PvW9.mjs.map +1 -0
- package/dist/data-transform-T71mQkVW.d.mts +39 -0
- package/dist/data-transform-T71mQkVW.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +1 -1
- package/dist/{default-normalizer-DNOpRoOF.mjs → default-normalizer-D4RoM0i6.mjs} +1 -1
- package/dist/{default-normalizer-DNOpRoOF.mjs.map → default-normalizer-D4RoM0i6.mjs.map} +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/{descriptor-meta-BVoVtyp-.mjs → descriptor-meta-B9JFfKCb.mjs} +7 -12
- package/dist/descriptor-meta-B9JFfKCb.mjs.map +1 -0
- package/dist/{errors-AFvEPZ1R.mjs → errors-BT_Duyj-.mjs} +1 -1
- package/dist/{errors-AFvEPZ1R.mjs.map → errors-BT_Duyj-.mjs.map} +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/{issue-planner-CFjB0_oO.mjs → issue-planner-DooWabc2.mjs} +8 -8
- package/dist/{issue-planner-CFjB0_oO.mjs.map → issue-planner-DooWabc2.mjs.map} +1 -1
- package/dist/issue-planner.d.mts +3 -3
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +3 -3
- package/dist/migration.mjs +3 -3
- package/dist/{native-type-normalizer-CInai_oY.mjs → native-type-normalizer-i4IFPL5F.mjs} +1 -1
- package/dist/{native-type-normalizer-CInai_oY.mjs.map → native-type-normalizer-i4IFPL5F.mjs.map} +1 -1
- package/dist/native-type-normalizer.mjs +1 -1
- package/dist/{op-factory-call-BKlruaiC.mjs → op-factory-call-Bvw39XKU.mjs} +2 -2
- package/dist/{op-factory-call-BKlruaiC.mjs.map → op-factory-call-Bvw39XKU.mjs.map} +1 -1
- package/dist/{op-factory-call-C3bWXKSP.d.mts → op-factory-call-SFMIf-Cz.d.mts} +3 -3
- package/dist/{op-factory-call-C3bWXKSP.d.mts.map → op-factory-call-SFMIf-Cz.d.mts.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.d.mts +30 -9
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CLUvVhUN.mjs → planner-BMtFbKfn.mjs} +9 -9
- package/dist/planner-BMtFbKfn.mjs.map +1 -0
- package/dist/{planner-ddl-builders-Dxvw1LHw.mjs → planner-ddl-builders-B6VK92UF.mjs} +3 -3
- package/dist/{planner-ddl-builders-Dxvw1LHw.mjs.map → planner-ddl-builders-B6VK92UF.mjs.map} +1 -1
- package/dist/planner-ddl-builders.d.mts +1 -1
- package/dist/planner-ddl-builders.mjs +1 -1
- package/dist/{planner-identity-values-Dju-o5GF.mjs → planner-identity-values-CC5fa5D9.mjs} +1 -1
- package/dist/{planner-identity-values-Dju-o5GF.mjs.map → planner-identity-values-CC5fa5D9.mjs.map} +1 -1
- package/dist/planner-identity-values.mjs +1 -1
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs → planner-produced-postgres-migration-C0vaAvA8.mjs} +4 -5
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs.map → planner-produced-postgres-migration-C0vaAvA8.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-CRRTno6Z.d.mts → planner-produced-postgres-migration-CyzRgqsq.d.mts} +4 -4
- package/dist/planner-produced-postgres-migration-CyzRgqsq.d.mts.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +5 -4
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-schema-lookup-B7lkypwn.mjs → planner-schema-lookup-B3talum5.mjs} +1 -1
- package/dist/{planner-schema-lookup-B7lkypwn.mjs.map → planner-schema-lookup-B3talum5.mjs.map} +1 -1
- package/dist/planner-schema-lookup.mjs +1 -1
- package/dist/{planner-sql-checks-7jkgm9TX.mjs → planner-sql-checks-uDnwA68k.mjs} +2 -2
- package/dist/{planner-sql-checks-7jkgm9TX.mjs.map → planner-sql-checks-uDnwA68k.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-DH-azLu-.d.mts → planner-target-details-COAiKZjW.d.mts} +1 -1
- package/dist/{planner-target-details-DH-azLu-.d.mts.map → planner-target-details-COAiKZjW.d.mts.map} +1 -1
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner.d.mts +19 -12
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-migration-qtmtbONe.mjs → postgres-migration-BAgHXrjO.mjs} +3 -3
- package/dist/postgres-migration-BAgHXrjO.mjs.map +1 -0
- package/dist/{postgres-migration-BjA3Zmts.d.mts → postgres-migration-Dzxr5BCy.d.mts} +5 -4
- package/dist/postgres-migration-Dzxr5BCy.d.mts.map +1 -0
- package/dist/{render-ops-D6_DHdOK.mjs → render-ops-DddkYOIB.mjs} +1 -1
- package/dist/{render-ops-D6_DHdOK.mjs.map → render-ops-DddkYOIB.mjs.map} +1 -1
- package/dist/render-ops.d.mts +3 -3
- package/dist/render-ops.mjs +1 -1
- package/dist/{render-typescript-1rF_SB4g.mjs → render-typescript-0EtwW-Ip.mjs} +1 -2
- package/dist/render-typescript-0EtwW-Ip.mjs.map +1 -0
- package/dist/render-typescript.d.mts +3 -4
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/render-typescript.mjs +1 -1
- package/dist/runtime.d.mts +5 -9
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +5 -10
- package/dist/runtime.mjs.map +1 -1
- package/dist/{shared-Bxkt8pNO.d.mts → shared-DSVRy4AX.d.mts} +2 -2
- package/dist/{shared-Bxkt8pNO.d.mts.map → shared-DSVRy4AX.d.mts.map} +1 -1
- package/dist/{sql-utils-r-Lw535w.mjs → sql-utils-C9dyHV0x.mjs} +1 -1
- package/dist/{sql-utils-r-Lw535w.mjs.map → sql-utils-C9dyHV0x.mjs.map} +1 -1
- package/dist/sql-utils.mjs +1 -1
- package/dist/{statement-builders-BPnmt6wx.mjs → statement-builders-Ckkq4ryf.mjs} +13 -8
- package/dist/statement-builders-Ckkq4ryf.mjs.map +1 -0
- package/dist/statement-builders.d.mts +10 -3
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/statement-builders.mjs +2 -2
- package/dist/{tables-BmdW_FWO.mjs → tables-CnvPb0Iz.mjs} +3 -3
- package/dist/{tables-BmdW_FWO.mjs.map → tables-CnvPb0Iz.mjs.map} +1 -1
- package/dist/{types-ClK03Ojd.d.mts → types-DWZq_XTl.d.mts} +1 -1
- package/dist/types-DWZq_XTl.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +17 -15
- package/src/core/authoring.ts +5 -11
- package/src/core/codec-helpers.ts +135 -0
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +81 -0
- package/src/core/codecs.ts +941 -547
- package/src/core/descriptor-meta.ts +1 -1
- package/src/core/migrations/operations/data-transform.ts +86 -21
- package/src/core/migrations/planner-produced-postgres-migration.ts +0 -1
- package/src/core/migrations/planner.ts +17 -11
- package/src/core/migrations/postgres-migration.ts +3 -6
- package/src/core/migrations/render-typescript.ts +1 -5
- package/src/core/migrations/runner.ts +43 -112
- package/src/core/migrations/statement-builders.ts +22 -6
- package/src/core/registry.ts +11 -0
- package/src/exports/codec-types.ts +4 -13
- package/src/exports/codecs.ts +49 -2
- package/src/exports/runtime.ts +6 -11
- package/src/exports/statement-builders.ts +1 -1
- package/dist/codec-ids.d.mts.map +0 -1
- package/dist/codec-types.d.mts.map +0 -1
- package/dist/codecs-BoahtY_Q.mjs +0 -385
- package/dist/codecs-BoahtY_Q.mjs.map +0 -1
- package/dist/codecs-D-F2KJqt.d.mts +0 -299
- package/dist/codecs-D-F2KJqt.d.mts.map +0 -1
- package/dist/data-transform-CxFRBIUp.d.mts +0 -32
- package/dist/data-transform-CxFRBIUp.d.mts.map +0 -1
- package/dist/data-transform-VfEGzXWt.mjs.map +0 -1
- package/dist/descriptor-meta-BVoVtyp-.mjs.map +0 -1
- package/dist/planner-CLUvVhUN.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-CRRTno6Z.d.mts.map +0 -1
- package/dist/postgres-migration-BjA3Zmts.d.mts.map +0 -1
- package/dist/postgres-migration-qtmtbONe.mjs.map +0 -1
- package/dist/render-typescript-1rF_SB4g.mjs.map +0 -1
- package/dist/statement-builders-BPnmt6wx.mjs.map +0 -1
- package/dist/types-ClK03Ojd.d.mts.map +0 -1
- package/src/core/json-schema-type-expression.ts +0 -131
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* override get operations() {
|
|
12
12
|
* return [
|
|
13
13
|
* this.dataTransform(endContract, 'backfill emails', {
|
|
14
|
-
* check: () => db.users.
|
|
14
|
+
* check: () => db.users.select('id').where(({ email }) => email.isNull()).limit(1),
|
|
15
15
|
* run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),
|
|
16
16
|
* }),
|
|
17
17
|
* ];
|
|
@@ -23,20 +23,49 @@
|
|
|
23
23
|
* invokes each one, asserts that its `meta.storageHash` matches the
|
|
24
24
|
* `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the
|
|
25
25
|
* plan via the supplied control adapter to a serialized `{sql, params}`
|
|
26
|
-
* payload
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* payload.
|
|
27
|
+
*
|
|
28
|
+
* The factory then lowers the data transform to the unified migration-op
|
|
29
|
+
* shape `{ precheck, execute, postcheck }`. The user's `check` plan is
|
|
30
|
+
* wrapped twice with opposite truth values:
|
|
31
|
+
*
|
|
32
|
+
* - precheck `SELECT EXISTS (<check>) AS ok` asserts there is work to do
|
|
33
|
+
* (precheck is short-circuited by the runner's pre-satisfied-skip path
|
|
34
|
+
* when nothing remains to backfill).
|
|
35
|
+
* - postcheck `SELECT NOT EXISTS (<check>) AS ok` asserts the work is
|
|
36
|
+
* complete after the run steps execute.
|
|
37
|
+
*
|
|
38
|
+
* The `check` plan is therefore expected to be a **rowset query whose
|
|
39
|
+
* presence of any row signals "work remains"** — typically `select('id')
|
|
40
|
+
* .where(<violation predicate>).limit(1)`. Scalar/aggregate shapes
|
|
41
|
+
* (`count(*)`, `bool_and(...)`) do not work under this contract: they
|
|
42
|
+
* always return exactly one row, so `EXISTS` is always true and
|
|
43
|
+
* `NOT EXISTS` is always false. (This is the same row-presence contract
|
|
44
|
+
* the pre-unification runner relied on; the wrapping is just lifting it
|
|
45
|
+
* into SQL.)
|
|
46
|
+
*
|
|
47
|
+
* Each `run` plan becomes an execute step. Because the `Step.params`
|
|
48
|
+
* field threads through `driver.query(sql, params)`, the user's bound
|
|
49
|
+
* values flow through the driver's parameter binder rather than being
|
|
50
|
+
* inlined into the SQL text.
|
|
51
|
+
*
|
|
52
|
+
* The free factory remains usable standalone (tests, ad-hoc tooling,
|
|
53
|
+
* non-class contexts) by passing the adapter explicitly as the fourth
|
|
54
|
+
* argument.
|
|
29
55
|
*/
|
|
30
56
|
|
|
31
57
|
import type { Contract } from '@prisma-next/contract/types';
|
|
32
58
|
import { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';
|
|
33
|
-
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
34
59
|
import type {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from '@prisma-next/
|
|
60
|
+
SqlMigrationPlanOperation,
|
|
61
|
+
SqlMigrationPlanOperationStep,
|
|
62
|
+
} from '@prisma-next/family-sql/control';
|
|
63
|
+
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
64
|
+
import type { SerializedQueryPlan } from '@prisma-next/framework-components/control';
|
|
38
65
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
39
66
|
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
67
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
68
|
+
import type { PostgresPlanTargetDetails } from '../planner-target-details';
|
|
40
69
|
|
|
41
70
|
interface Buildable<R = unknown> {
|
|
42
71
|
build(): SqlQueryPlan<R>;
|
|
@@ -49,36 +78,72 @@ interface Buildable<R = unknown> {
|
|
|
49
78
|
export type DataTransformClosure = () => SqlQueryPlan | Buildable;
|
|
50
79
|
|
|
51
80
|
export interface DataTransformOptions {
|
|
52
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Optional opt-in routing identity. Presence opts the transform into
|
|
83
|
+
* invariant-aware routing; absence means it is path-dependent and
|
|
84
|
+
* not referenceable from refs.
|
|
85
|
+
*/
|
|
86
|
+
readonly invariantId?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Optional pre-flight query. `undefined` means "no check". When
|
|
89
|
+
* supplied, the closure must return a **rowset query** whose
|
|
90
|
+
* presence of any row signals "violations remain". Conventional
|
|
91
|
+
* shape: `db.<table>.select('id').where(<violation>).limit(1)`.
|
|
92
|
+
* Scalar/aggregate shapes do not satisfy this contract.
|
|
93
|
+
*/
|
|
53
94
|
readonly check?: DataTransformClosure;
|
|
54
95
|
/** One or more mutation queries to execute. */
|
|
55
96
|
readonly run: DataTransformClosure | readonly DataTransformClosure[];
|
|
56
97
|
}
|
|
57
98
|
|
|
58
|
-
/**
|
|
59
|
-
* Concrete Postgres flavor of `DataTransformOperation`, re-exported so the
|
|
60
|
-
* `PostgresMigration.dataTransform` instance method can name it without
|
|
61
|
-
* leaking the framework-components symbol into call sites.
|
|
62
|
-
*/
|
|
63
|
-
export type PostgresDataTransformOperation = DataTransformOperation;
|
|
64
|
-
|
|
65
99
|
export function dataTransform<TContract extends Contract<SqlStorage>>(
|
|
66
100
|
contract: TContract,
|
|
67
101
|
name: string,
|
|
68
102
|
options: DataTransformOptions,
|
|
69
103
|
adapter: SqlControlAdapter<'postgres'>,
|
|
70
|
-
):
|
|
104
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
71
105
|
const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)
|
|
72
106
|
? options.run
|
|
73
107
|
: [options.run as DataTransformClosure];
|
|
108
|
+
|
|
109
|
+
const checkPlan = options.check ? invokeAndLower(options.check, contract, adapter, name) : null;
|
|
110
|
+
const runPlans = runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name));
|
|
111
|
+
|
|
112
|
+
const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
|
|
113
|
+
? [
|
|
114
|
+
{
|
|
115
|
+
description: `Check ${name} has work to do`,
|
|
116
|
+
sql: `SELECT EXISTS (${checkPlan.sql}) AS ok`,
|
|
117
|
+
params: checkPlan.params,
|
|
118
|
+
},
|
|
119
|
+
]
|
|
120
|
+
: [];
|
|
121
|
+
|
|
122
|
+
const execute: readonly SqlMigrationPlanOperationStep[] = runPlans.map((plan) => ({
|
|
123
|
+
description: `Run ${name}`,
|
|
124
|
+
sql: plan.sql,
|
|
125
|
+
params: plan.params,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const postcheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
|
|
129
|
+
? [
|
|
130
|
+
{
|
|
131
|
+
description: `Verify ${name} resolved all violations`,
|
|
132
|
+
sql: `SELECT NOT EXISTS (${checkPlan.sql}) AS ok`,
|
|
133
|
+
params: checkPlan.params,
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
: [];
|
|
137
|
+
|
|
74
138
|
return {
|
|
75
139
|
id: `data_migration.${name}`,
|
|
76
140
|
label: `Data transform: ${name}`,
|
|
77
141
|
operationClass: 'data',
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
142
|
+
...ifDefined('invariantId', options.invariantId),
|
|
143
|
+
target: { id: 'postgres' },
|
|
144
|
+
precheck,
|
|
145
|
+
execute,
|
|
146
|
+
postcheck,
|
|
82
147
|
};
|
|
83
148
|
}
|
|
84
149
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import type {
|
|
2
3
|
MigrationOperationPolicy,
|
|
3
4
|
SqlMigrationPlannerPlanOptions,
|
|
@@ -83,20 +84,25 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
83
84
|
readonly contract: unknown;
|
|
84
85
|
readonly schema: unknown;
|
|
85
86
|
readonly policy: MigrationOperationPolicy;
|
|
86
|
-
readonly fromHash?: string;
|
|
87
87
|
/**
|
|
88
88
|
* The "from" contract (state the planner assumes the database starts
|
|
89
|
-
* at)
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* activate.
|
|
89
|
+
* at), or `null` for reconciliation flows. Only `migration plan` ever
|
|
90
|
+
* supplies a non-null value; `db update` / `db init` reconcile against
|
|
91
|
+
* the live schema and pass `null`. When present alongside the
|
|
92
|
+
* `'data'` operation class, strategies that need from/to column-shape
|
|
93
|
+
* comparisons (unsafe type change, nullability tightening) activate.
|
|
94
|
+
*
|
|
95
|
+
* Typed as the framework `Contract | null` to satisfy the
|
|
96
|
+
* `MigrationPlanner` interface contract; `planSql` narrows to the SQL
|
|
97
|
+
* shape via `SqlMigrationPlannerPlanOptions`. Used to populate
|
|
98
|
+
* `describe().from` on the produced plan as
|
|
99
|
+
* `fromContract?.storage.storageHash ?? null`.
|
|
94
100
|
*/
|
|
95
|
-
readonly fromContract
|
|
101
|
+
readonly fromContract: Contract | null;
|
|
96
102
|
readonly schemaName?: string;
|
|
97
103
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
98
104
|
}): PostgresPlanResult {
|
|
99
|
-
return this.planSql(options as SqlMigrationPlannerPlanOptions
|
|
105
|
+
return this.planSql(options as SqlMigrationPlannerPlanOptions);
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface {
|
|
@@ -106,7 +112,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
106
112
|
});
|
|
107
113
|
}
|
|
108
114
|
|
|
109
|
-
private planSql(options: SqlMigrationPlannerPlanOptions
|
|
115
|
+
private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
|
|
110
116
|
const schemaName = options.schemaName ?? this.config.defaultSchema;
|
|
111
117
|
const policyResult = this.ensureAdditivePolicy(options.policy);
|
|
112
118
|
if (policyResult) {
|
|
@@ -125,7 +131,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
125
131
|
// from/to comparisons (unsafe type change, nullable tightening) are
|
|
126
132
|
// inapplicable there — reconciliation falls through to
|
|
127
133
|
// `mapIssueToCall`'s direct destructive handlers.
|
|
128
|
-
fromContract: options.fromContract
|
|
134
|
+
fromContract: options.fromContract,
|
|
129
135
|
schemaName,
|
|
130
136
|
codecHooks,
|
|
131
137
|
storageTypes,
|
|
@@ -142,7 +148,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
142
148
|
return Object.freeze({
|
|
143
149
|
kind: 'success' as const,
|
|
144
150
|
plan: new TypeScriptRenderablePostgresMigration(result.value.calls, {
|
|
145
|
-
from:
|
|
151
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
146
152
|
to: options.contract.storage.storageHash,
|
|
147
153
|
}),
|
|
148
154
|
});
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
3
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
3
4
|
import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
|
|
4
5
|
import type { ControlStack } from '@prisma-next/framework-components/control';
|
|
5
6
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
6
7
|
import { errorPostgresMigrationStackMissing } from '../errors';
|
|
7
|
-
import {
|
|
8
|
-
type DataTransformOptions,
|
|
9
|
-
dataTransform,
|
|
10
|
-
type PostgresDataTransformOperation,
|
|
11
|
-
} from './operations/data-transform';
|
|
8
|
+
import { type DataTransformOptions, dataTransform } from './operations/data-transform';
|
|
12
9
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
13
10
|
|
|
14
11
|
/**
|
|
@@ -64,7 +61,7 @@ export abstract class PostgresMigration extends SqlMigration<
|
|
|
64
61
|
contract: TContract,
|
|
65
62
|
name: string,
|
|
66
63
|
options: DataTransformOptions,
|
|
67
|
-
):
|
|
64
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
68
65
|
if (!this.controlAdapter) {
|
|
69
66
|
throw errorPostgresMigrationStackMissing();
|
|
70
67
|
}
|
|
@@ -14,9 +14,8 @@ import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-n
|
|
|
14
14
|
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
15
15
|
|
|
16
16
|
export interface RenderMigrationMeta {
|
|
17
|
-
readonly from: string;
|
|
17
|
+
readonly from: string | null;
|
|
18
18
|
readonly to: string;
|
|
19
|
-
readonly kind?: string;
|
|
20
19
|
readonly labels?: readonly string[];
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -84,9 +83,6 @@ function buildDescribeMethod(meta: RenderMigrationMeta): string {
|
|
|
84
83
|
lines.push(' return {');
|
|
85
84
|
lines.push(` from: ${JSON.stringify(meta.from)},`);
|
|
86
85
|
lines.push(` to: ${JSON.stringify(meta.to)},`);
|
|
87
|
-
if (meta.kind) {
|
|
88
|
-
lines.push(` kind: ${JSON.stringify(meta.kind)},`);
|
|
89
|
-
}
|
|
90
86
|
if (meta.labels && meta.labels.length > 0) {
|
|
91
87
|
lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
|
|
92
88
|
}
|
|
@@ -12,8 +12,6 @@ import type {
|
|
|
12
12
|
} from '@prisma-next/family-sql/control';
|
|
13
13
|
import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
|
|
14
14
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
15
|
-
import { readMarker } from '@prisma-next/family-sql/verify';
|
|
16
|
-
import type { DataTransformOperation } from '@prisma-next/framework-components/control';
|
|
17
15
|
import { SqlQueryError } from '@prisma-next/sql-errors';
|
|
18
16
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
19
17
|
import type { Result } from '@prisma-next/utils/result';
|
|
@@ -23,7 +21,7 @@ import { normalizeSchemaNativeType } from '../native-type-normalizer';
|
|
|
23
21
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
24
22
|
import {
|
|
25
23
|
buildLedgerInsertStatement,
|
|
26
|
-
|
|
24
|
+
buildMergeMarkerStatements,
|
|
27
25
|
ensureLedgerTableStatement,
|
|
28
26
|
ensureMarkerTableStatement,
|
|
29
27
|
ensurePrismaContractSchemaStatement,
|
|
@@ -45,18 +43,6 @@ const DEFAULT_CONFIG: RunnerConfig = {
|
|
|
45
43
|
|
|
46
44
|
const LOCK_DOMAIN = 'prisma_next.contract.marker';
|
|
47
45
|
|
|
48
|
-
function isDataTransformOperation(op: unknown): op is DataTransformOperation {
|
|
49
|
-
return (
|
|
50
|
-
typeof op === 'object' &&
|
|
51
|
-
op !== null &&
|
|
52
|
-
'operationClass' in op &&
|
|
53
|
-
(op as { operationClass: string }).operationClass === 'data' &&
|
|
54
|
-
'name' in op &&
|
|
55
|
-
'check' in op &&
|
|
56
|
-
'run' in op
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
46
|
/**
|
|
61
47
|
* Deep clones and freezes a record object to prevent mutation.
|
|
62
48
|
* Recursively clones nested objects and arrays to ensure complete isolation.
|
|
@@ -120,7 +106,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
120
106
|
try {
|
|
121
107
|
await this.acquireLock(driver, lockKey);
|
|
122
108
|
await this.ensureControlTables(driver);
|
|
123
|
-
const existingMarker = await readMarker(driver);
|
|
109
|
+
const existingMarker = await this.family.readMarker({ driver });
|
|
124
110
|
|
|
125
111
|
// Validate plan origin matches existing marker (needs marker from DB)
|
|
126
112
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
@@ -128,9 +114,14 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
128
114
|
return markerCheck;
|
|
129
115
|
}
|
|
130
116
|
|
|
131
|
-
// db update (origin: null) always applies; migration-apply (origin set
|
|
117
|
+
// db update (origin: null) always applies; migration-apply (origin set,
|
|
118
|
+
// origin !== destination) skips if marker already matches destination.
|
|
119
|
+
// Self-edges (origin === destination) intentionally bypass the skip:
|
|
120
|
+
// the migration is data-only, and the data transform's own check
|
|
121
|
+
// decides whether `run` fires.
|
|
132
122
|
const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);
|
|
133
|
-
const
|
|
123
|
+
const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;
|
|
124
|
+
const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;
|
|
134
125
|
let applyValue: ApplyPlanSuccessValue;
|
|
135
126
|
|
|
136
127
|
if (skipOperations) {
|
|
@@ -170,9 +161,34 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
170
161
|
});
|
|
171
162
|
}
|
|
172
163
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
// Self-edge no-op detection: a self-edge migration whose ops all
|
|
165
|
+
// self-skipped (every op pre-satisfied) and that brings no new
|
|
166
|
+
// invariants produced no observable change. Skip the marker +
|
|
167
|
+
// ledger writes so an idempotent re-apply of a self-edge data
|
|
168
|
+
// transform doesn't churn updatedAt or pile up empty ledger
|
|
169
|
+
// entries. With data transforms now flowing through the unified
|
|
170
|
+
// op loop, a pre-satisfied DT lands in the
|
|
171
|
+
// `postcheckAlreadySatisfied` skip path which does not increment
|
|
172
|
+
// `operationsExecuted`, so the check below correctly distinguishes
|
|
173
|
+
// "every op self-skipped" from "the plan had ops that ran". `db
|
|
174
|
+
// update` no-ops still write a ledger entry as audit trail.
|
|
175
|
+
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
176
|
+
const existingInvariants = new Set(existingMarker?.invariants ?? []);
|
|
177
|
+
const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>
|
|
178
|
+
existingInvariants.has(id),
|
|
179
|
+
);
|
|
180
|
+
const isSelfEdgeNoOp =
|
|
181
|
+
isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;
|
|
182
|
+
|
|
183
|
+
if (!isSelfEdgeNoOp) {
|
|
184
|
+
await this.upsertMarker(driver, options, existingMarker);
|
|
185
|
+
await this.recordLedgerEntry(
|
|
186
|
+
driver,
|
|
187
|
+
options,
|
|
188
|
+
existingMarker,
|
|
189
|
+
applyValue.executedOperations,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
176
192
|
|
|
177
193
|
await this.commitTransaction(driver);
|
|
178
194
|
committed = true;
|
|
@@ -201,19 +217,6 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
201
217
|
for (const operation of options.plan.operations) {
|
|
202
218
|
options.callbacks?.onOperationStart?.(operation);
|
|
203
219
|
try {
|
|
204
|
-
// Data transform operations have a different execution lifecycle
|
|
205
|
-
if (operation.operationClass === 'data' && isDataTransformOperation(operation)) {
|
|
206
|
-
const dtResult = await this.executeDataTransform(driver, operation, {
|
|
207
|
-
runIdempotency,
|
|
208
|
-
});
|
|
209
|
-
if (!dtResult.ok) {
|
|
210
|
-
return dtResult;
|
|
211
|
-
}
|
|
212
|
-
executedOperations.push(operation);
|
|
213
|
-
operationsExecuted += 1;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
220
|
// Idempotency probe: only run if both postchecks and idempotency checks are enabled
|
|
218
221
|
if (runPostchecks && runIdempotency) {
|
|
219
222
|
const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(
|
|
@@ -266,80 +269,6 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
266
269
|
return ok({ operationsExecuted, executedOperations });
|
|
267
270
|
}
|
|
268
271
|
|
|
269
|
-
/**
|
|
270
|
-
* Executes a data transform operation with the check → (skip or run) → check lifecycle.
|
|
271
|
-
*
|
|
272
|
-
* 1. If check is a query AST: render to SQL, execute. Empty result = already applied (skip).
|
|
273
|
-
* 2. If check is `true`: always skip. If `false`: always run.
|
|
274
|
-
* 3. Execute run ASTs (rendered to SQL) sequentially.
|
|
275
|
-
* 4. Re-execute check as post-run validation. If violations remain, fail.
|
|
276
|
-
*/
|
|
277
|
-
private async executeDataTransform(
|
|
278
|
-
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
279
|
-
op: DataTransformOperation,
|
|
280
|
-
options: { runIdempotency: boolean },
|
|
281
|
-
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
282
|
-
// Step 1: Check (skip guard)
|
|
283
|
-
if (op.check === true) {
|
|
284
|
-
// Always skip, regardless of idempotency setting
|
|
285
|
-
return okVoid();
|
|
286
|
-
}
|
|
287
|
-
if (options.runIdempotency && op.check !== null && op.check !== false) {
|
|
288
|
-
const checkResult = await driver.query(op.check.sql, op.check.params);
|
|
289
|
-
if (checkResult.rows.length === 0) {
|
|
290
|
-
// No violations — already applied, skip
|
|
291
|
-
return okVoid();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Step 2: Execute run steps
|
|
296
|
-
if (op.run) {
|
|
297
|
-
for (const plan of op.run) {
|
|
298
|
-
try {
|
|
299
|
-
await driver.query(plan.sql, plan.params);
|
|
300
|
-
} catch (error: unknown) {
|
|
301
|
-
if (SqlQueryError.is(error)) {
|
|
302
|
-
return runnerFailure(
|
|
303
|
-
'EXECUTION_FAILED',
|
|
304
|
-
`Data transform "${op.name}" failed: ${error.message}`,
|
|
305
|
-
{
|
|
306
|
-
why: error.message,
|
|
307
|
-
meta: {
|
|
308
|
-
operationId: op.id,
|
|
309
|
-
dataTransformName: op.name,
|
|
310
|
-
sql: plan.sql,
|
|
311
|
-
sqlState: error.sqlState,
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
throw error;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Step 3: Post-run validation (check again)
|
|
322
|
-
if (op.check !== null && op.check !== false) {
|
|
323
|
-
const checkResult = await driver.query(op.check.sql, op.check.params);
|
|
324
|
-
if (checkResult.rows.length > 0) {
|
|
325
|
-
return runnerFailure(
|
|
326
|
-
'POSTCHECK_FAILED',
|
|
327
|
-
`Data transform "${op.name}" did not resolve all violations (${checkResult.rows.length} remaining)`,
|
|
328
|
-
{
|
|
329
|
-
why: `After executing the data transform, the check query still returns ${checkResult.rows.length} violation(s).`,
|
|
330
|
-
meta: {
|
|
331
|
-
operationId: op.id,
|
|
332
|
-
dataTransformName: op.name,
|
|
333
|
-
remainingViolations: checkResult.rows.length,
|
|
334
|
-
},
|
|
335
|
-
},
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return okVoid();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
272
|
private async ensureControlTables(
|
|
344
273
|
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
345
274
|
): Promise<void> {
|
|
@@ -355,7 +284,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
355
284
|
phase: 'precheck' | 'postcheck',
|
|
356
285
|
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
357
286
|
for (const step of steps) {
|
|
358
|
-
const result = await driver.query(step.sql);
|
|
287
|
+
const result = await driver.query(step.sql, step.params ?? []);
|
|
359
288
|
if (!this.stepResultIsTrue(result.rows)) {
|
|
360
289
|
const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';
|
|
361
290
|
return runnerFailure(
|
|
@@ -381,7 +310,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
381
310
|
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
382
311
|
for (const step of steps) {
|
|
383
312
|
try {
|
|
384
|
-
await driver.query(step.sql);
|
|
313
|
+
await driver.query(step.sql, step.params ?? []);
|
|
385
314
|
} catch (error: unknown) {
|
|
386
315
|
// Catch SqlQueryError and include normalized metadata
|
|
387
316
|
if (SqlQueryError.is(error)) {
|
|
@@ -445,7 +374,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
445
374
|
return false;
|
|
446
375
|
}
|
|
447
376
|
for (const step of steps) {
|
|
448
|
-
const result = await driver.query(step.sql);
|
|
377
|
+
const result = await driver.query(step.sql, step.params ?? []);
|
|
449
378
|
if (!this.stepResultIsTrue(result.rows)) {
|
|
450
379
|
return false;
|
|
451
380
|
}
|
|
@@ -617,7 +546,8 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
617
546
|
options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,
|
|
618
547
|
existingMarker: ContractMarkerRecord | null,
|
|
619
548
|
): Promise<void> {
|
|
620
|
-
const
|
|
549
|
+
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
550
|
+
const writeStatements = buildMergeMarkerStatements({
|
|
621
551
|
storageHash: options.plan.destination.storageHash,
|
|
622
552
|
profileHash:
|
|
623
553
|
options.plan.destination.profileHash ??
|
|
@@ -626,6 +556,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
626
556
|
contractJson: options.destinationContract,
|
|
627
557
|
canonicalVersion: null,
|
|
628
558
|
meta: {},
|
|
559
|
+
invariants: incomingInvariants,
|
|
629
560
|
});
|
|
630
561
|
const statement = existingMarker ? writeStatements.update : writeStatements.insert;
|
|
631
562
|
await this.executeStatement(driver, statement);
|
|
@@ -17,7 +17,8 @@ export const ensureMarkerTableStatement: SqlStatement = {
|
|
|
17
17
|
canonical_version int,
|
|
18
18
|
updated_at timestamptz not null default now(),
|
|
19
19
|
app_tag text,
|
|
20
|
-
meta jsonb not null default '{}'
|
|
20
|
+
meta jsonb not null default '{}',
|
|
21
|
+
invariants text[] not null default '{}'
|
|
21
22
|
)`,
|
|
22
23
|
params: [],
|
|
23
24
|
};
|
|
@@ -37,16 +38,23 @@ export const ensureLedgerTableStatement: SqlStatement = {
|
|
|
37
38
|
params: [],
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
export interface
|
|
41
|
+
export interface MergeMarkerInput {
|
|
41
42
|
readonly storageHash: string;
|
|
42
43
|
readonly profileHash: string;
|
|
43
44
|
readonly contractJson?: unknown;
|
|
44
45
|
readonly canonicalVersion?: number | null;
|
|
45
46
|
readonly appTag?: string | null;
|
|
46
47
|
readonly meta?: Record<string, unknown>;
|
|
48
|
+
/**
|
|
49
|
+
* Invariants to merge into `marker.invariants`. INSERT writes them as
|
|
50
|
+
* the initial value (callers are expected to pass a sorted, deduped
|
|
51
|
+
* array). UPDATE merges them with the existing column server-side via
|
|
52
|
+
* a single atomic SQL expression.
|
|
53
|
+
*/
|
|
54
|
+
readonly invariants: readonly string[];
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
export function
|
|
57
|
+
export function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
50
58
|
readonly insert: SqlStatement;
|
|
51
59
|
readonly update: SqlStatement;
|
|
52
60
|
} {
|
|
@@ -58,6 +66,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
58
66
|
input.canonicalVersion ?? null,
|
|
59
67
|
input.appTag ?? null,
|
|
60
68
|
jsonParam(input.meta ?? {}),
|
|
69
|
+
input.invariants,
|
|
61
70
|
];
|
|
62
71
|
|
|
63
72
|
return {
|
|
@@ -70,7 +79,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
70
79
|
canonical_version,
|
|
71
80
|
updated_at,
|
|
72
81
|
app_tag,
|
|
73
|
-
meta
|
|
82
|
+
meta,
|
|
83
|
+
invariants
|
|
74
84
|
) values (
|
|
75
85
|
$1,
|
|
76
86
|
$2,
|
|
@@ -79,11 +89,16 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
79
89
|
$5,
|
|
80
90
|
now(),
|
|
81
91
|
$6,
|
|
82
|
-
$7::jsonb
|
|
92
|
+
$7::jsonb,
|
|
93
|
+
$8::text[]
|
|
83
94
|
)`,
|
|
84
95
|
params,
|
|
85
96
|
},
|
|
86
97
|
update: {
|
|
98
|
+
// `invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)`
|
|
99
|
+
// reads the current column value under the UPDATE's row lock, unions
|
|
100
|
+
// with the incoming array, dedupes, and sorts ascending — single
|
|
101
|
+
// statement, atomic, no read-then-write window.
|
|
87
102
|
sql: `update prisma_contract.marker set
|
|
88
103
|
core_hash = $2,
|
|
89
104
|
profile_hash = $3,
|
|
@@ -91,7 +106,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
91
106
|
canonical_version = $5,
|
|
92
107
|
updated_at = now(),
|
|
93
108
|
app_tag = $6,
|
|
94
|
-
meta = $7::jsonb
|
|
109
|
+
meta = $7::jsonb,
|
|
110
|
+
invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
|
|
95
111
|
where id = $1`,
|
|
96
112
|
params,
|
|
97
113
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buildCodecDescriptorRegistry } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
|
|
2
|
+
import type { CodecDescriptorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
|
|
3
|
+
import { codecDescriptors } from './codecs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Registry of every codec descriptor shipped by `@prisma-next/target-postgres`.
|
|
7
|
+
*
|
|
8
|
+
* Public consumer surface for the postgres codec set: the postgres adapter and any other consumer that needs to enumerate or look up a postgres codec by id consumes this rather than the raw descriptor array. See ADR 208.
|
|
9
|
+
*/
|
|
10
|
+
export const postgresCodecRegistry: CodecDescriptorRegistry =
|
|
11
|
+
buildCodecDescriptorRegistry(codecDescriptors);
|
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codec type definitions for the Postgres target.
|
|
3
3
|
*
|
|
4
|
-
* This file
|
|
5
|
-
* These types are imported by generated `contract.d.ts` files for compile-time
|
|
6
|
-
* type inference.
|
|
4
|
+
* This file is the public origin of `CodecTypes`. The `Resolve<...>` materialisation happens here (rather than in `core/codec-type-map.ts`) so the tsdown DTS bundler resolves consumer-side `.d.mts` references via this public entry point rather than a hash-named internal chunk (the `TS2742` family).
|
|
7
5
|
*
|
|
8
|
-
* Lives in `target-postgres` because codec types describe the target's value
|
|
9
|
-
* space - both the control adapter (introspection / schema verification) and
|
|
10
|
-
* the runtime adapter (encode/decode) share the same definitions, and the
|
|
11
|
-
* target package is the natural home that both adapters depend on.
|
|
12
|
-
*
|
|
13
|
-
* Runtime codec implementations are provided by the runtime adapter's
|
|
14
|
-
* codec registry, which is built from `core/codecs.ts`.
|
|
6
|
+
* Lives in `target-postgres` because codec types describe the target's value space — both the control adapter (introspection / schema verification) and the runtime adapter (encode/decode) share the same definitions, and the target package is the natural home that both adapters depend on.
|
|
15
7
|
*/
|
|
16
8
|
|
|
17
9
|
import type { JsonValue } from '@prisma-next/contract/types';
|
|
18
|
-
import type {
|
|
10
|
+
import type { ExtractedCodecTypes, Resolve } from '../core/codec-type-map';
|
|
19
11
|
|
|
20
|
-
export type CodecTypes =
|
|
12
|
+
export type CodecTypes = Resolve<ExtractedCodecTypes>;
|
|
21
13
|
|
|
22
14
|
export type { JsonValue };
|
|
23
|
-
export { dataTypes } from '../core/codecs';
|
|
24
15
|
|
|
25
16
|
type Branded<T, Shape extends Record<string, unknown>> = T & {
|
|
26
17
|
readonly [K in keyof Shape]: Shape[K];
|