@prisma-next/target-postgres 0.4.0-dev.6 → 0.4.0-dev.7
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/control.mjs +137 -3
- package/dist/control.mjs.map +1 -1
- package/package.json +19 -17
- package/src/core/migrations/planner.ts +84 -7
- package/src/core/migrations/scaffolding.ts +140 -0
- package/src/exports/control.ts +5 -2
package/package.json
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-postgres",
|
|
3
|
-
"version": "0.4.0-dev.
|
|
3
|
+
"version": "0.4.0-dev.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Postgres target pack for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"arktype": "^2.0.0",
|
|
9
|
-
"
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/family-sql": "0.4.0-dev.
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/sql-
|
|
14
|
-
"@prisma-next/sql-
|
|
15
|
-
"@prisma-next/sql-
|
|
16
|
-
"@prisma-next/
|
|
17
|
-
"@prisma-next/sql-
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/sql-runtime": "0.4.0-dev.
|
|
9
|
+
"pathe": "^2.0.3",
|
|
10
|
+
"@prisma-next/contract": "0.4.0-dev.7",
|
|
11
|
+
"@prisma-next/family-sql": "0.4.0-dev.7",
|
|
12
|
+
"@prisma-next/framework-components": "0.4.0-dev.7",
|
|
13
|
+
"@prisma-next/sql-builder": "0.4.0-dev.7",
|
|
14
|
+
"@prisma-next/sql-contract": "0.4.0-dev.7",
|
|
15
|
+
"@prisma-next/sql-operations": "0.4.0-dev.7",
|
|
16
|
+
"@prisma-next/errors": "0.4.0-dev.7",
|
|
17
|
+
"@prisma-next/sql-errors": "0.4.0-dev.7",
|
|
18
|
+
"@prisma-next/sql-relational-core": "0.4.0-dev.7",
|
|
19
|
+
"@prisma-next/sql-runtime": "0.4.0-dev.7",
|
|
20
|
+
"@prisma-next/sql-schema-ir": "0.4.0-dev.7",
|
|
21
|
+
"@prisma-next/utils": "0.4.0-dev.7"
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {
|
|
22
24
|
"tsdown": "0.18.4",
|
|
23
25
|
"typescript": "5.9.3",
|
|
24
26
|
"vitest": "4.0.17",
|
|
25
|
-
"@prisma-next/adapter-postgres": "0.4.0-dev.
|
|
26
|
-
"@prisma-next/driver-postgres": "0.4.0-dev.
|
|
27
|
+
"@prisma-next/adapter-postgres": "0.4.0-dev.7",
|
|
28
|
+
"@prisma-next/driver-postgres": "0.4.0-dev.7",
|
|
27
29
|
"@prisma-next/test-utils": "0.0.1",
|
|
28
|
-
"@prisma-next/extension-pgvector": "0.4.0-dev.
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/
|
|
30
|
+
"@prisma-next/extension-pgvector": "0.4.0-dev.7",
|
|
31
|
+
"@prisma-next/tsdown": "0.0.0",
|
|
32
|
+
"@prisma-next/tsconfig": "0.0.0"
|
|
31
33
|
},
|
|
32
34
|
"files": [
|
|
33
35
|
"dist",
|
|
@@ -3,24 +3,30 @@ import {
|
|
|
3
3
|
parsePostgresDefault,
|
|
4
4
|
quoteIdentifier,
|
|
5
5
|
} from '@prisma-next/adapter-postgres/control';
|
|
6
|
+
import { errorPlanDoesNotSupportAuthoringSurface } from '@prisma-next/errors/migration';
|
|
6
7
|
import type {
|
|
7
8
|
CodecControlHooks,
|
|
8
9
|
ComponentDatabaseDependency,
|
|
9
10
|
MigrationOperationPolicy,
|
|
10
|
-
SqlMigrationPlanner,
|
|
11
11
|
SqlMigrationPlannerPlanOptions,
|
|
12
12
|
SqlMigrationPlanOperation,
|
|
13
13
|
SqlPlannerConflict,
|
|
14
|
+
SqlPlannerFailureResult,
|
|
14
15
|
} from '@prisma-next/family-sql/control';
|
|
15
16
|
import {
|
|
16
17
|
collectInitDependencies,
|
|
17
18
|
createMigrationPlan,
|
|
18
19
|
extractCodecControlHooks,
|
|
19
20
|
plannerFailure,
|
|
20
|
-
plannerSuccess,
|
|
21
21
|
} from '@prisma-next/family-sql/control';
|
|
22
22
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
23
|
-
import type {
|
|
23
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
24
|
+
import type {
|
|
25
|
+
MigrationPlanner,
|
|
26
|
+
MigrationPlanWithAuthoringSurface,
|
|
27
|
+
MigrationScaffoldContext,
|
|
28
|
+
SchemaIssue,
|
|
29
|
+
} from '@prisma-next/framework-components/control';
|
|
24
30
|
import type {
|
|
25
31
|
StorageColumn,
|
|
26
32
|
StorageTable,
|
|
@@ -58,6 +64,7 @@ import {
|
|
|
58
64
|
type PlanningMode,
|
|
59
65
|
type PostgresPlanTargetDetails,
|
|
60
66
|
} from './planner-target-details';
|
|
67
|
+
import { renderDescriptorTypeScript } from './scaffolding';
|
|
61
68
|
|
|
62
69
|
type PlannerFrameworkComponents = SqlMigrationPlannerPlanOptions extends {
|
|
63
70
|
readonly frameworkComponents: infer T;
|
|
@@ -89,17 +96,79 @@ const DEFAULT_PLANNER_CONFIG: PlannerConfig = {
|
|
|
89
96
|
|
|
90
97
|
export function createPostgresMigrationPlanner(
|
|
91
98
|
config: Partial<PlannerConfig> = {},
|
|
92
|
-
):
|
|
99
|
+
): PostgresMigrationPlanner {
|
|
93
100
|
return new PostgresMigrationPlanner({
|
|
94
101
|
...DEFAULT_PLANNER_CONFIG,
|
|
95
102
|
...config,
|
|
96
103
|
});
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Postgres planner success plan: the SQL-typed plan (so target-detail
|
|
108
|
+
* typing is preserved for direct SQL callers, including this package's unit
|
|
109
|
+
* tests) plus the framework-required `renderTypeScript()` stub.
|
|
110
|
+
*/
|
|
111
|
+
export type PostgresPlanWithRender = ReturnType<
|
|
112
|
+
typeof createMigrationPlan<PostgresPlanTargetDetails>
|
|
113
|
+
> & {
|
|
114
|
+
renderTypeScript(): string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Result of `PostgresMigrationPlanner.plan()`. A discriminated union that
|
|
119
|
+
* satisfies both the framework's `MigrationPlannerResult` (success plan
|
|
120
|
+
* carries `renderTypeScript()`) and the SQL family's typed result shape
|
|
121
|
+
* (success plan is a `SqlMigrationPlan<PostgresPlanTargetDetails>`).
|
|
122
|
+
*/
|
|
123
|
+
export type PostgresPlanResult =
|
|
124
|
+
| { readonly kind: 'success'; readonly plan: PostgresPlanWithRender }
|
|
125
|
+
| SqlPlannerFailureResult;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Postgres migration planner.
|
|
129
|
+
*
|
|
130
|
+
* Implements the framework's `MigrationPlanner<'sql', 'postgres'>` directly
|
|
131
|
+
* — meaning it owns `emptyMigration()` and attaches the `renderTypeScript()`
|
|
132
|
+
* stub to success plans (Postgres uses descriptor-flow authoring, so
|
|
133
|
+
* `renderTypeScript()` throws on planner-produced plans). No external wrapper
|
|
134
|
+
* is required at the descriptor level.
|
|
135
|
+
*
|
|
136
|
+
* `plan()` accepts the framework's option shape (with `contract`/`schema`
|
|
137
|
+
* typed as `unknown`); SQL-typed callers may pass the more specific
|
|
138
|
+
* `SqlMigrationPlannerPlanOptions`, since those structurally satisfy the
|
|
139
|
+
* looser framework contract. Internally we treat options as the SQL-typed
|
|
140
|
+
* superset so the existing planner logic stays identical.
|
|
141
|
+
*
|
|
142
|
+
* `fromHash` is accepted but ignored: Postgres is descriptor-flow and never
|
|
143
|
+
* needs it (only class-flow planners like Mongo populate `describe()` from
|
|
144
|
+
* `fromHash`).
|
|
145
|
+
*/
|
|
146
|
+
export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {
|
|
100
147
|
constructor(private readonly config: PlannerConfig) {}
|
|
101
148
|
|
|
102
|
-
plan(options:
|
|
149
|
+
plan(options: {
|
|
150
|
+
readonly contract: unknown;
|
|
151
|
+
readonly schema: unknown;
|
|
152
|
+
readonly policy: MigrationOperationPolicy;
|
|
153
|
+
readonly fromHash?: string;
|
|
154
|
+
readonly schemaName?: string;
|
|
155
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
156
|
+
}): PostgresPlanResult {
|
|
157
|
+
return this.planSql(options as SqlMigrationPlannerPlanOptions);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface {
|
|
161
|
+
return {
|
|
162
|
+
targetId: 'postgres',
|
|
163
|
+
destination: { storageHash: context.toHash },
|
|
164
|
+
operations: [],
|
|
165
|
+
renderTypeScript(): string {
|
|
166
|
+
return renderDescriptorTypeScript([], context);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
|
|
103
172
|
const schemaName = options.schemaName ?? this.config.defaultSchema;
|
|
104
173
|
const policyResult = this.ensureAdditivePolicy(options.policy);
|
|
105
174
|
if (policyResult) {
|
|
@@ -176,7 +245,15 @@ class PostgresMigrationPlanner implements SqlMigrationPlanner<PostgresPlanTarget
|
|
|
176
245
|
operations,
|
|
177
246
|
});
|
|
178
247
|
|
|
179
|
-
return
|
|
248
|
+
return Object.freeze({
|
|
249
|
+
kind: 'success' as const,
|
|
250
|
+
plan: Object.freeze({
|
|
251
|
+
...plan,
|
|
252
|
+
renderTypeScript(): string {
|
|
253
|
+
throw errorPlanDoesNotSupportAuthoringSurface({ targetId: 'postgres' });
|
|
254
|
+
},
|
|
255
|
+
}),
|
|
256
|
+
});
|
|
180
257
|
}
|
|
181
258
|
|
|
182
259
|
private ensureAdditivePolicy(policy: MigrationOperationPolicy) {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MigrationScaffoldContext,
|
|
3
|
+
OperationDescriptor,
|
|
4
|
+
} from '@prisma-next/framework-components/control';
|
|
5
|
+
import { relative } from 'pathe';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Postgres's internal plan shape. Happens to be descriptor-shaped today — that
|
|
9
|
+
* is a target-internal choice; the framework has no opinion.
|
|
10
|
+
*/
|
|
11
|
+
type PostgresPlan = readonly OperationDescriptor[];
|
|
12
|
+
|
|
13
|
+
function serializeQueryInput(input: unknown): string {
|
|
14
|
+
if (typeof input === 'boolean') return String(input);
|
|
15
|
+
if (typeof input === 'symbol') return 'TODO /* fill in using db.sql.from(...) */';
|
|
16
|
+
if (input === null || input === undefined) return 'null';
|
|
17
|
+
if (Array.isArray(input)) {
|
|
18
|
+
if (input.length === 0) return '[]';
|
|
19
|
+
if (input.every((item) => typeof item === 'symbol'))
|
|
20
|
+
return '[TODO /* fill in using db.sql.from(...) */]';
|
|
21
|
+
return `[${input.map(serializeQueryInput).join(', ')}]`;
|
|
22
|
+
}
|
|
23
|
+
return JSON.stringify(input);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderDescriptor(desc: OperationDescriptor): string {
|
|
27
|
+
switch (desc.kind) {
|
|
28
|
+
case 'createTable':
|
|
29
|
+
return `createTable(${JSON.stringify(desc['table'])})`;
|
|
30
|
+
case 'dropTable':
|
|
31
|
+
return `dropTable(${JSON.stringify(desc['table'])})`;
|
|
32
|
+
case 'addColumn': {
|
|
33
|
+
const args = [JSON.stringify(desc['table']), JSON.stringify(desc['column'])];
|
|
34
|
+
if (desc['overrides']) {
|
|
35
|
+
args.push(JSON.stringify(desc['overrides']));
|
|
36
|
+
}
|
|
37
|
+
return `addColumn(${args.join(', ')})`;
|
|
38
|
+
}
|
|
39
|
+
case 'dropColumn':
|
|
40
|
+
return `dropColumn(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
41
|
+
case 'alterColumnType': {
|
|
42
|
+
const opts: Record<string, unknown> = {};
|
|
43
|
+
if (desc['using']) opts['using'] = desc['using'];
|
|
44
|
+
if (desc['toType']) opts['toType'] = desc['toType'];
|
|
45
|
+
const hasOpts = Object.keys(opts).length > 0;
|
|
46
|
+
return hasOpts
|
|
47
|
+
? `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])}, ${JSON.stringify(opts)})`
|
|
48
|
+
: `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
49
|
+
}
|
|
50
|
+
case 'setNotNull':
|
|
51
|
+
return `setNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
52
|
+
case 'dropNotNull':
|
|
53
|
+
return `dropNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
54
|
+
case 'setDefault':
|
|
55
|
+
return `setDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
56
|
+
case 'dropDefault':
|
|
57
|
+
return `dropDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
|
|
58
|
+
case 'addPrimaryKey':
|
|
59
|
+
return `addPrimaryKey(${JSON.stringify(desc['table'])})`;
|
|
60
|
+
case 'addUnique':
|
|
61
|
+
return `addUnique(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
|
|
62
|
+
case 'addForeignKey':
|
|
63
|
+
return `addForeignKey(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
|
|
64
|
+
case 'dropConstraint':
|
|
65
|
+
return `dropConstraint(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['constraintName'])})`;
|
|
66
|
+
case 'createIndex':
|
|
67
|
+
return `createIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
|
|
68
|
+
case 'dropIndex':
|
|
69
|
+
return `dropIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['indexName'])})`;
|
|
70
|
+
case 'createEnumType':
|
|
71
|
+
return desc['values']
|
|
72
|
+
? `createEnumType(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`
|
|
73
|
+
: `createEnumType(${JSON.stringify(desc['typeName'])})`;
|
|
74
|
+
case 'addEnumValues':
|
|
75
|
+
return `addEnumValues(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`;
|
|
76
|
+
case 'dropEnumType':
|
|
77
|
+
return `dropEnumType(${JSON.stringify(desc['typeName'])})`;
|
|
78
|
+
case 'renameType':
|
|
79
|
+
return `renameType(${JSON.stringify(desc['fromName'])}, ${JSON.stringify(desc['toName'])})`;
|
|
80
|
+
case 'createDependency':
|
|
81
|
+
return `createDependency(${JSON.stringify(desc['dependencyId'])})`;
|
|
82
|
+
case 'dataTransform':
|
|
83
|
+
return `dataTransform(${JSON.stringify(desc['name'])}, {\n check: ${serializeQueryInput(desc['check'])},\n run: ${serializeQueryInput(desc['run'])},\n })`;
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`Unknown Postgres descriptor kind: ${desc.kind}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderPreamble(
|
|
90
|
+
plan: PostgresPlan,
|
|
91
|
+
context: MigrationScaffoldContext,
|
|
92
|
+
): ReadonlyArray<string> {
|
|
93
|
+
const hasDataTransform = plan.some((d) => d.kind === 'dataTransform');
|
|
94
|
+
|
|
95
|
+
if (hasDataTransform && context.contractJsonPath) {
|
|
96
|
+
const relativeContractDts = relative(context.packageDir, context.contractJsonPath).replace(
|
|
97
|
+
/\.json$/,
|
|
98
|
+
'.d',
|
|
99
|
+
);
|
|
100
|
+
const importList = [...new Set(plan.map((d) => d.kind))];
|
|
101
|
+
importList.push('TODO');
|
|
102
|
+
return [
|
|
103
|
+
`import type { Contract } from "${relativeContractDts}"`,
|
|
104
|
+
`import { createBuilders } from "@prisma-next/target-postgres/migration-builders"`,
|
|
105
|
+
'',
|
|
106
|
+
`const { ${importList.join(', ')} } = createBuilders<Contract>()`,
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const importList = [...new Set(plan.map((d) => d.kind))];
|
|
111
|
+
if (importList.length === 0) {
|
|
112
|
+
importList.push('createTable');
|
|
113
|
+
}
|
|
114
|
+
if (hasDataTransform) {
|
|
115
|
+
importList.push('TODO');
|
|
116
|
+
}
|
|
117
|
+
return [
|
|
118
|
+
`import { ${importList.join(', ')} } from "@prisma-next/target-postgres/migration-builders"`,
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Render a Postgres descriptor list to a `migration.ts` source string.
|
|
124
|
+
*
|
|
125
|
+
* Internal to the Postgres target — no longer part of the framework SPI.
|
|
126
|
+
* Invoked from two paths:
|
|
127
|
+
* - the migrations capability's `renderDescriptorTypeScript` hook (called by
|
|
128
|
+
* the CLI after `planWithDescriptors` to seed an editable authoring
|
|
129
|
+
* surface, and by `emptyMigration()` to produce the `migration new` stub);
|
|
130
|
+
* - directly by this package's unit tests.
|
|
131
|
+
*/
|
|
132
|
+
export function renderDescriptorTypeScript(
|
|
133
|
+
descriptors: readonly OperationDescriptor[],
|
|
134
|
+
context: MigrationScaffoldContext,
|
|
135
|
+
): string {
|
|
136
|
+
const preamble = renderPreamble(descriptors, context);
|
|
137
|
+
const calls = descriptors.map((d) => ` ${renderDescriptor(d)},`);
|
|
138
|
+
const body = calls.length > 0 ? `\n${calls.join('\n')}\n` : '';
|
|
139
|
+
return [...preamble, '', `export default () => [${body}]`, ''].join('\n');
|
|
140
|
+
}
|
package/src/exports/control.ts
CHANGED
|
@@ -17,7 +17,6 @@ import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
|
17
17
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
18
18
|
import type {
|
|
19
19
|
ControlTargetInstance,
|
|
20
|
-
MigrationPlanner,
|
|
21
20
|
MigrationRunner,
|
|
22
21
|
OperationDescriptor,
|
|
23
22
|
} from '@prisma-next/framework-components/control';
|
|
@@ -33,6 +32,7 @@ import { createPostgresMigrationPlanner } from '../core/migrations/planner';
|
|
|
33
32
|
import { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';
|
|
34
33
|
import type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';
|
|
35
34
|
import { createPostgresMigrationRunner } from '../core/migrations/runner';
|
|
35
|
+
import { renderDescriptorTypeScript } from '../core/migrations/scaffolding';
|
|
36
36
|
|
|
37
37
|
function parseDescriptors(descriptors: readonly OperationDescriptor[]) {
|
|
38
38
|
const result = MigrationDescriptorArraySchema([...descriptors]);
|
|
@@ -127,7 +127,7 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
|
|
|
127
127
|
...postgresTargetDescriptorMeta,
|
|
128
128
|
migrations: {
|
|
129
129
|
createPlanner(_family: SqlControlFamilyInstance) {
|
|
130
|
-
return createPostgresMigrationPlanner()
|
|
130
|
+
return createPostgresMigrationPlanner();
|
|
131
131
|
},
|
|
132
132
|
createRunner(family) {
|
|
133
133
|
return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;
|
|
@@ -199,6 +199,9 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
|
|
|
199
199
|
db,
|
|
200
200
|
});
|
|
201
201
|
},
|
|
202
|
+
renderDescriptorTypeScript(descriptors, context) {
|
|
203
|
+
return renderDescriptorTypeScript(descriptors, context);
|
|
204
|
+
},
|
|
202
205
|
},
|
|
203
206
|
create(): ControlTargetInstance<'sql', 'postgres'> {
|
|
204
207
|
return {
|