@prisma-next/family-sql 0.3.0-dev.12 → 0.3.0-dev.123
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/LICENSE +201 -0
- package/README.md +34 -7
- package/dist/assembly-Dzumaba1.mjs +159 -0
- package/dist/assembly-Dzumaba1.mjs.map +1 -0
- package/dist/control-adapter.d.mts +60 -0
- package/dist/control-adapter.d.mts.map +1 -0
- package/dist/control-adapter.mjs +1 -0
- package/dist/control-instance-BKuHINR7.d.mts +411 -0
- package/dist/control-instance-BKuHINR7.d.mts.map +1 -0
- package/dist/control.d.mts +128 -0
- package/dist/control.d.mts.map +1 -0
- package/dist/control.mjs +683 -0
- package/dist/control.mjs.map +1 -0
- package/dist/runtime.d.mts +27 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +38 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-verify.d.mts +48 -0
- package/dist/schema-verify.d.mts.map +1 -0
- package/dist/schema-verify.mjs +4 -0
- package/dist/test-utils.d.mts +2 -0
- package/dist/test-utils.mjs +3 -0
- package/dist/verify-BfMETJcM.mjs +108 -0
- package/dist/verify-BfMETJcM.mjs.map +1 -0
- package/dist/verify-sql-schema-C3Pit9o4.mjs +1085 -0
- package/dist/verify-sql-schema-C3Pit9o4.mjs.map +1 -0
- package/dist/verify-sql-schema-DhHnkpPa.d.mts +67 -0
- package/dist/verify-sql-schema-DhHnkpPa.d.mts.map +1 -0
- package/dist/verify.d.mts +31 -0
- package/dist/verify.d.mts.map +1 -0
- package/dist/verify.mjs +3 -0
- package/package.json +35 -46
- package/src/core/assembly.ts +265 -59
- package/src/core/control-adapter.ts +15 -0
- package/src/core/{descriptor.ts → control-descriptor.ts} +15 -11
- package/src/core/{instance.ts → control-instance.ts} +106 -248
- package/src/core/migrations/contract-to-schema-ir.ts +265 -0
- package/src/core/migrations/types.ts +193 -168
- package/src/core/runtime-descriptor.ts +19 -41
- package/src/core/runtime-instance.ts +11 -133
- package/src/core/schema-verify/verify-helpers.ts +201 -105
- package/src/core/schema-verify/verify-sql-schema.ts +918 -413
- package/src/core/verify.ts +4 -13
- package/src/exports/control.ts +29 -6
- package/src/exports/runtime.ts +2 -6
- package/src/exports/schema-verify.ts +10 -2
- package/src/exports/test-utils.ts +1 -1
- package/dist/chunk-BHEGVBY7.js +0 -772
- package/dist/chunk-BHEGVBY7.js.map +0 -1
- package/dist/chunk-SQ2VWYDV.js +0 -589
- package/dist/chunk-SQ2VWYDV.js.map +0 -1
- package/dist/chunk-SU7LN2UH.js +0 -96
- package/dist/chunk-SU7LN2UH.js.map +0 -1
- package/dist/core/assembly.d.ts +0 -25
- package/dist/core/assembly.d.ts.map +0 -1
- package/dist/core/control-adapter.d.ts +0 -42
- package/dist/core/control-adapter.d.ts.map +0 -1
- package/dist/core/descriptor.d.ts +0 -24
- package/dist/core/descriptor.d.ts.map +0 -1
- package/dist/core/instance.d.ts +0 -140
- package/dist/core/instance.d.ts.map +0 -1
- package/dist/core/migrations/plan-helpers.d.ts +0 -20
- package/dist/core/migrations/plan-helpers.d.ts.map +0 -1
- package/dist/core/migrations/policies.d.ts +0 -6
- package/dist/core/migrations/policies.d.ts.map +0 -1
- package/dist/core/migrations/types.d.ts +0 -280
- package/dist/core/migrations/types.d.ts.map +0 -1
- package/dist/core/runtime-descriptor.d.ts +0 -19
- package/dist/core/runtime-descriptor.d.ts.map +0 -1
- package/dist/core/runtime-instance.d.ts +0 -54
- package/dist/core/runtime-instance.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-helpers.d.ts +0 -50
- package/dist/core/schema-verify/verify-helpers.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-sql-schema.d.ts +0 -45
- package/dist/core/schema-verify/verify-sql-schema.d.ts.map +0 -1
- package/dist/core/verify.d.ts +0 -39
- package/dist/core/verify.d.ts.map +0 -1
- package/dist/exports/control-adapter.d.ts +0 -2
- package/dist/exports/control-adapter.d.ts.map +0 -1
- package/dist/exports/control-adapter.js +0 -1
- package/dist/exports/control-adapter.js.map +0 -1
- package/dist/exports/control.d.ts +0 -13
- package/dist/exports/control.d.ts.map +0 -1
- package/dist/exports/control.js +0 -149
- package/dist/exports/control.js.map +0 -1
- package/dist/exports/runtime.d.ts +0 -8
- package/dist/exports/runtime.d.ts.map +0 -1
- package/dist/exports/runtime.js +0 -64
- package/dist/exports/runtime.js.map +0 -1
- package/dist/exports/schema-verify.d.ts +0 -11
- package/dist/exports/schema-verify.d.ts.map +0 -1
- package/dist/exports/schema-verify.js +0 -11
- package/dist/exports/schema-verify.js.map +0 -1
- package/dist/exports/test-utils.d.ts +0 -7
- package/dist/exports/test-utils.d.ts.map +0 -1
- package/dist/exports/test-utils.js +0 -17
- package/dist/exports/test-utils.js.map +0 -1
- package/dist/exports/verify.d.ts +0 -2
- package/dist/exports/verify.d.ts.map +0 -1
- package/dist/exports/verify.js +0 -11
- package/dist/exports/verify.js.map +0 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/contract/framework-components';
|
|
2
|
+
import type { ColumnDefault } from '@prisma-next/contract/types';
|
|
3
|
+
import type { MigrationPlannerConflict } from '@prisma-next/core-control-plane/types';
|
|
4
|
+
import type {
|
|
5
|
+
ForeignKey,
|
|
6
|
+
Index,
|
|
7
|
+
SqlContract,
|
|
8
|
+
SqlStorage,
|
|
9
|
+
StorageColumn,
|
|
10
|
+
StorageTable,
|
|
11
|
+
UniqueConstraint,
|
|
12
|
+
} from '@prisma-next/sql-contract/types';
|
|
13
|
+
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
14
|
+
import type {
|
|
15
|
+
DependencyIR,
|
|
16
|
+
SqlAnnotations,
|
|
17
|
+
SqlColumnIR,
|
|
18
|
+
SqlForeignKeyIR,
|
|
19
|
+
SqlIndexIR,
|
|
20
|
+
SqlSchemaIR,
|
|
21
|
+
SqlTableIR,
|
|
22
|
+
SqlUniqueIR,
|
|
23
|
+
} from '@prisma-next/sql-schema-ir/types';
|
|
24
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
25
|
+
import { collectInitDependencies } from './types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Target-specific callback that expands a column's base `nativeType` and optional
|
|
29
|
+
* `typeParams` into the fully-qualified type string used by the database
|
|
30
|
+
* (e.g. `character` + `{ length: 36 }` → `character(36)`).
|
|
31
|
+
*
|
|
32
|
+
* This lives in the family layer as a callback rather than importing a concrete
|
|
33
|
+
* implementation because each target (Postgres, MySQL, SQLite, …) has its own
|
|
34
|
+
* parameterization syntax. The target wires its expander when calling
|
|
35
|
+
* `contractToSchemaIR`, keeping the family layer target-agnostic.
|
|
36
|
+
*/
|
|
37
|
+
export type NativeTypeExpander = (input: {
|
|
38
|
+
readonly nativeType: string;
|
|
39
|
+
readonly codecId?: string;
|
|
40
|
+
readonly typeParams?: Record<string, unknown>;
|
|
41
|
+
}) => string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Target-specific callback that renders a `ColumnDefault` into the raw SQL literal
|
|
45
|
+
* string stored in `SqlColumnIR.default`.
|
|
46
|
+
*
|
|
47
|
+
* Default value serialization is target-specific (quoting, casting, type syntax vary
|
|
48
|
+
* between Postgres, MySQL, SQLite, …). This callback follows the same IoC pattern as
|
|
49
|
+
* `NativeTypeExpander`: the target provides its renderer when calling
|
|
50
|
+
* `contractToSchemaIR`, keeping the family layer target-agnostic.
|
|
51
|
+
*/
|
|
52
|
+
export type DefaultRenderer = (def: ColumnDefault, column: StorageColumn) => string;
|
|
53
|
+
|
|
54
|
+
function convertColumn(
|
|
55
|
+
name: string,
|
|
56
|
+
column: StorageColumn,
|
|
57
|
+
expandNativeType: NativeTypeExpander | undefined,
|
|
58
|
+
renderDefault: DefaultRenderer | undefined,
|
|
59
|
+
): SqlColumnIR {
|
|
60
|
+
const nativeType = expandNativeType
|
|
61
|
+
? expandNativeType({
|
|
62
|
+
nativeType: column.nativeType,
|
|
63
|
+
codecId: column.codecId,
|
|
64
|
+
...ifDefined('typeParams', column.typeParams),
|
|
65
|
+
})
|
|
66
|
+
: column.nativeType;
|
|
67
|
+
return {
|
|
68
|
+
name,
|
|
69
|
+
nativeType,
|
|
70
|
+
nullable: column.nullable,
|
|
71
|
+
...ifDefined(
|
|
72
|
+
'default',
|
|
73
|
+
column.default != null && renderDefault ? renderDefault(column.default, column) : undefined,
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function convertUnique(unique: UniqueConstraint): SqlUniqueIR {
|
|
79
|
+
return {
|
|
80
|
+
columns: unique.columns,
|
|
81
|
+
...ifDefined('name', unique.name),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function convertIndex(index: Index): SqlIndexIR {
|
|
86
|
+
return {
|
|
87
|
+
columns: index.columns,
|
|
88
|
+
unique: false,
|
|
89
|
+
...ifDefined('name', index.name),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function convertForeignKey(fk: ForeignKey): SqlForeignKeyIR {
|
|
94
|
+
return {
|
|
95
|
+
columns: fk.columns,
|
|
96
|
+
referencedTable: fk.references.table,
|
|
97
|
+
referencedColumns: fk.references.columns,
|
|
98
|
+
...ifDefined('name', fk.name),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function convertTable(
|
|
103
|
+
name: string,
|
|
104
|
+
table: StorageTable,
|
|
105
|
+
expandNativeType: NativeTypeExpander | undefined,
|
|
106
|
+
renderDefault: DefaultRenderer | undefined,
|
|
107
|
+
): SqlTableIR {
|
|
108
|
+
const columns: Record<string, SqlColumnIR> = {};
|
|
109
|
+
for (const [colName, colDef] of Object.entries(table.columns)) {
|
|
110
|
+
columns[colName] = convertColumn(colName, colDef, expandNativeType, renderDefault);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const satisfiedIndexColumns = new Set([
|
|
114
|
+
...table.indexes.map((idx) => idx.columns.join(',')),
|
|
115
|
+
...table.uniques.map((unique) => unique.columns.join(',')),
|
|
116
|
+
...(table.primaryKey ? [table.primaryKey.columns.join(',')] : []),
|
|
117
|
+
]);
|
|
118
|
+
const fkBackingIndexes: SqlIndexIR[] = [];
|
|
119
|
+
for (const fk of table.foreignKeys) {
|
|
120
|
+
if (fk.index === false) continue;
|
|
121
|
+
const key = fk.columns.join(',');
|
|
122
|
+
if (satisfiedIndexColumns.has(key)) continue;
|
|
123
|
+
fkBackingIndexes.push({
|
|
124
|
+
columns: fk.columns,
|
|
125
|
+
unique: false,
|
|
126
|
+
name: defaultIndexName(name, fk.columns),
|
|
127
|
+
});
|
|
128
|
+
satisfiedIndexColumns.add(key);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
name,
|
|
133
|
+
columns,
|
|
134
|
+
...ifDefined('primaryKey', table.primaryKey),
|
|
135
|
+
foreignKeys: table.foreignKeys.map(convertForeignKey),
|
|
136
|
+
uniques: table.uniques.map(convertUnique),
|
|
137
|
+
indexes: [...table.indexes.map(convertIndex), ...fkBackingIndexes],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Detects destructive changes between two contract storages.
|
|
143
|
+
*
|
|
144
|
+
* The additive-only planner silently ignores removals (tables, columns).
|
|
145
|
+
* This function detects those removals so callers can report them as conflicts
|
|
146
|
+
* rather than silently producing an empty plan.
|
|
147
|
+
*
|
|
148
|
+
* Returns an empty array if no destructive changes are found.
|
|
149
|
+
*/
|
|
150
|
+
export function detectDestructiveChanges(
|
|
151
|
+
from: SqlStorage | null,
|
|
152
|
+
to: SqlStorage,
|
|
153
|
+
): readonly MigrationPlannerConflict[] {
|
|
154
|
+
if (!from) return [];
|
|
155
|
+
|
|
156
|
+
const hasOwn = (value: object, key: string): boolean => Object.hasOwn(value, key);
|
|
157
|
+
|
|
158
|
+
const conflicts: MigrationPlannerConflict[] = [];
|
|
159
|
+
|
|
160
|
+
for (const tableName of Object.keys(from.tables)) {
|
|
161
|
+
if (!hasOwn(to.tables, tableName)) {
|
|
162
|
+
conflicts.push({
|
|
163
|
+
kind: 'tableRemoved',
|
|
164
|
+
summary: `Table "${tableName}" was removed`,
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const toTable = to.tables[tableName] as StorageTable;
|
|
170
|
+
const fromTable = from.tables[tableName];
|
|
171
|
+
if (!fromTable) continue;
|
|
172
|
+
|
|
173
|
+
for (const columnName of Object.keys(fromTable.columns)) {
|
|
174
|
+
if (!hasOwn(toTable.columns, columnName)) {
|
|
175
|
+
conflicts.push({
|
|
176
|
+
kind: 'columnRemoved',
|
|
177
|
+
summary: `Column "${tableName}"."${columnName}" was removed`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return conflicts;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface ContractToSchemaIROptions {
|
|
187
|
+
readonly annotationNamespace: string;
|
|
188
|
+
readonly expandNativeType?: NativeTypeExpander;
|
|
189
|
+
readonly renderDefault?: DefaultRenderer;
|
|
190
|
+
readonly frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Converts an `SqlContract` to `SqlSchemaIR`.
|
|
195
|
+
*
|
|
196
|
+
* Reads `contract.storage` for tables, `contract.storage.types` for type
|
|
197
|
+
* annotations, and derives database dependencies from `frameworkComponents`
|
|
198
|
+
* (each component's `databaseDependencies.init[].id`).
|
|
199
|
+
* Storage-type annotations are written under `options.annotationNamespace`.
|
|
200
|
+
*
|
|
201
|
+
* Drops codec metadata (`codecId`, `typeRef`) since the schema IR only represents
|
|
202
|
+
* structural information. When `expandNativeType` is provided, parameterized types
|
|
203
|
+
* are expanded (e.g. `character` + `{ length: 36 }` → `character(36)`) so the
|
|
204
|
+
* resulting IR compares correctly against the "to" contract during planning.
|
|
205
|
+
*
|
|
206
|
+
* Returns an empty schema IR when `contract` is `null` (new project).
|
|
207
|
+
*/
|
|
208
|
+
export function contractToSchemaIR(
|
|
209
|
+
contract: SqlContract<SqlStorage> | null,
|
|
210
|
+
options: ContractToSchemaIROptions,
|
|
211
|
+
): SqlSchemaIR {
|
|
212
|
+
if (options.annotationNamespace.length === 0) {
|
|
213
|
+
throw new Error('annotationNamespace must be a non-empty string');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!contract) {
|
|
217
|
+
return { tables: {}, dependencies: [] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const storage = contract.storage;
|
|
221
|
+
const tables: Record<string, SqlTableIR> = {};
|
|
222
|
+
for (const [tableName, tableDef] of Object.entries(storage.tables)) {
|
|
223
|
+
tables[tableName] = convertTable(
|
|
224
|
+
tableName,
|
|
225
|
+
tableDef,
|
|
226
|
+
options.expandNativeType,
|
|
227
|
+
options.renderDefault,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const dependencies = deduplicateDependencyIRs(
|
|
232
|
+
collectInitDependencies(options.frameworkComponents ?? []),
|
|
233
|
+
);
|
|
234
|
+
const annotations = deriveAnnotations(storage, options.annotationNamespace);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
tables,
|
|
238
|
+
dependencies,
|
|
239
|
+
...ifDefined('annotations', annotations),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function deduplicateDependencyIRs(
|
|
244
|
+
deps: readonly { readonly id: string }[],
|
|
245
|
+
): readonly DependencyIR[] {
|
|
246
|
+
const seen = new Set<string>();
|
|
247
|
+
const result: DependencyIR[] = [];
|
|
248
|
+
for (const dep of deps) {
|
|
249
|
+
if (dep.id.trim().length === 0) {
|
|
250
|
+
throw new Error('Dependency id must be a non-empty string');
|
|
251
|
+
}
|
|
252
|
+
if (seen.has(dep.id)) continue;
|
|
253
|
+
seen.add(dep.id);
|
|
254
|
+
result.push({ id: dep.id });
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function deriveAnnotations(
|
|
260
|
+
storage: SqlStorage,
|
|
261
|
+
annotationNamespace: string,
|
|
262
|
+
): SqlAnnotations | undefined {
|
|
263
|
+
if (!storage.types || Object.keys(storage.types).length === 0) return undefined;
|
|
264
|
+
return { [annotationNamespace]: { storageTypes: storage.types } };
|
|
265
|
+
}
|