zenstack-kit 0.1.1
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 +21 -0
- package/README.md +313 -0
- package/dist/cli/app.d.ts +12 -0
- package/dist/cli/app.d.ts.map +1 -0
- package/dist/cli/app.js +253 -0
- package/dist/cli/commands.d.ts +70 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +308 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/prompt-provider.d.ts +10 -0
- package/dist/cli/prompt-provider.d.ts.map +1 -0
- package/dist/cli/prompt-provider.js +41 -0
- package/dist/cli/prompts.d.ts +27 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +133 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +240 -0
- package/dist/config/index.d.ts +96 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +48 -0
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +44 -0
- package/dist/config-loader.d.ts +6 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +36 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/init-prompts.d.ts +13 -0
- package/dist/init-prompts.d.ts.map +1 -0
- package/dist/init-prompts.js +64 -0
- package/dist/introspect.d.ts +54 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +75 -0
- package/dist/kysely-adapter.d.ts +49 -0
- package/dist/kysely-adapter.d.ts.map +1 -0
- package/dist/kysely-adapter.js +74 -0
- package/dist/migrate-apply.d.ts +18 -0
- package/dist/migrate-apply.d.ts.map +1 -0
- package/dist/migrate-apply.js +61 -0
- package/dist/migrate.d.ts +108 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +127 -0
- package/dist/migrations/apply.d.ts +18 -0
- package/dist/migrations/apply.d.ts.map +1 -0
- package/dist/migrations/apply.js +61 -0
- package/dist/migrations/diff.d.ts +161 -0
- package/dist/migrations/diff.d.ts.map +1 -0
- package/dist/migrations/diff.js +620 -0
- package/dist/migrations/prisma.d.ts +193 -0
- package/dist/migrations/prisma.d.ts.map +1 -0
- package/dist/migrations/prisma.js +929 -0
- package/dist/migrations.d.ts +161 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +620 -0
- package/dist/prisma-migrations.d.ts +160 -0
- package/dist/prisma-migrations.d.ts.map +1 -0
- package/dist/prisma-migrations.js +789 -0
- package/dist/prompts.d.ts +10 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +41 -0
- package/dist/pull.d.ts +23 -0
- package/dist/pull.d.ts.map +1 -0
- package/dist/pull.js +424 -0
- package/dist/schema/introspect.d.ts +54 -0
- package/dist/schema/introspect.d.ts.map +1 -0
- package/dist/schema/introspect.js +75 -0
- package/dist/schema/pull.d.ts +23 -0
- package/dist/schema/pull.d.ts.map +1 -0
- package/dist/schema/pull.js +424 -0
- package/dist/schema/snapshot.d.ts +46 -0
- package/dist/schema/snapshot.d.ts.map +1 -0
- package/dist/schema/snapshot.js +278 -0
- package/dist/schema-snapshot.d.ts +45 -0
- package/dist/schema-snapshot.d.ts.map +1 -0
- package/dist/schema-snapshot.js +265 -0
- package/dist/sql/compiler.d.ts +74 -0
- package/dist/sql/compiler.d.ts.map +1 -0
- package/dist/sql/compiler.js +270 -0
- package/dist/sql/kysely-adapter.d.ts +49 -0
- package/dist/sql/kysely-adapter.d.ts.map +1 -0
- package/dist/sql/kysely-adapter.js +74 -0
- package/dist/sql-compiler.d.ts +74 -0
- package/dist/sql-compiler.d.ts.map +1 -0
- package/dist/sql-compiler.js +243 -0
- package/package.json +81 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema snapshot utilities for ZenStack schemas
|
|
3
|
+
*
|
|
4
|
+
* Uses ZenStack's AST to create a stable, diffable schema snapshot.
|
|
5
|
+
*/
|
|
6
|
+
import { loadDocument } from "@zenstackhq/language";
|
|
7
|
+
import { isDataField, isDataModel, isEnum } from "@zenstackhq/language/ast";
|
|
8
|
+
function getAttribute(node, name) {
|
|
9
|
+
return node.attributes.find((attr) => attr.decl.$refText === name);
|
|
10
|
+
}
|
|
11
|
+
function getAttributeStringArg(attr, names) {
|
|
12
|
+
if (!attr)
|
|
13
|
+
return undefined;
|
|
14
|
+
for (const arg of attr.args) {
|
|
15
|
+
const paramName = arg.$resolvedParam?.name;
|
|
16
|
+
if (paramName && names.includes(paramName)) {
|
|
17
|
+
if (arg.value?.$type === "StringLiteral") {
|
|
18
|
+
return arg.value.value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const firstArg = attr.args[0];
|
|
23
|
+
if (firstArg?.value?.$type === "StringLiteral") {
|
|
24
|
+
return firstArg.value.value;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function getAttributeArrayRefs(attr, name) {
|
|
29
|
+
if (!attr)
|
|
30
|
+
return undefined;
|
|
31
|
+
const arg = attr.args.find((item) => item.$resolvedParam?.name === name) ?? attr.args[0];
|
|
32
|
+
if (!arg?.value || arg.value.$type !== "ArrayExpr") {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const refs = arg.value.items
|
|
36
|
+
.filter((item) => item.$type === "ReferenceExpr")
|
|
37
|
+
.map((item) => item.target.$refText);
|
|
38
|
+
return refs.length > 0 ? refs : undefined;
|
|
39
|
+
}
|
|
40
|
+
function getDefaultValue(field) {
|
|
41
|
+
const attr = getAttribute(field, "@default");
|
|
42
|
+
if (!attr) {
|
|
43
|
+
return { hasDefault: false };
|
|
44
|
+
}
|
|
45
|
+
const valueArg = attr.args.find((arg) => arg.$resolvedParam?.name === "value") ?? attr.args[0];
|
|
46
|
+
const expr = valueArg?.value;
|
|
47
|
+
if (!expr) {
|
|
48
|
+
return { hasDefault: true };
|
|
49
|
+
}
|
|
50
|
+
if (expr.$type === "StringLiteral") {
|
|
51
|
+
return { hasDefault: true, default: expr.value };
|
|
52
|
+
}
|
|
53
|
+
if (expr.$type === "NumberLiteral") {
|
|
54
|
+
return { hasDefault: true, default: Number(expr.value) };
|
|
55
|
+
}
|
|
56
|
+
if (expr.$type === "BooleanLiteral") {
|
|
57
|
+
return { hasDefault: true, default: expr.value };
|
|
58
|
+
}
|
|
59
|
+
// Handle function calls like autoincrement(), now(), etc.
|
|
60
|
+
if (expr.$type === "InvocationExpr") {
|
|
61
|
+
const funcName = expr.function.$refText;
|
|
62
|
+
if (funcName === "autoincrement") {
|
|
63
|
+
return { hasDefault: true, isAutoincrement: true };
|
|
64
|
+
}
|
|
65
|
+
if (funcName === "now") {
|
|
66
|
+
return { hasDefault: true, default: "now()" };
|
|
67
|
+
}
|
|
68
|
+
// Return function name for other functions
|
|
69
|
+
return { hasDefault: true, default: `${funcName}()` };
|
|
70
|
+
}
|
|
71
|
+
return { hasDefault: true };
|
|
72
|
+
}
|
|
73
|
+
function getTableName(model) {
|
|
74
|
+
const mapAttr = getAttribute(model, "@@map");
|
|
75
|
+
const mapped = getAttributeStringArg(mapAttr, ["name", "map"]);
|
|
76
|
+
return mapped ?? model.name.toLowerCase();
|
|
77
|
+
}
|
|
78
|
+
function getColumnName(field) {
|
|
79
|
+
const mapAttr = getAttribute(field, "@map");
|
|
80
|
+
return getAttributeStringArg(mapAttr, ["name", "map"]) ?? field.name;
|
|
81
|
+
}
|
|
82
|
+
function mapFieldTypeToSQL(fieldType) {
|
|
83
|
+
const typeMap = {
|
|
84
|
+
String: "text",
|
|
85
|
+
Int: "integer",
|
|
86
|
+
Float: "double precision",
|
|
87
|
+
Boolean: "boolean",
|
|
88
|
+
DateTime: "timestamp",
|
|
89
|
+
BigInt: "bigint",
|
|
90
|
+
Decimal: "decimal",
|
|
91
|
+
Json: "json",
|
|
92
|
+
Bytes: "blob",
|
|
93
|
+
};
|
|
94
|
+
return typeMap[fieldType] ?? "text";
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Prisma-compatible constraint naming conventions
|
|
98
|
+
*
|
|
99
|
+
* Prisma uses PostgreSQL-aligned naming:
|
|
100
|
+
* - Primary Key: {Table}_pkey
|
|
101
|
+
* - Unique: {Table}_{columns}_key
|
|
102
|
+
* - Index: {Table}_{columns}_idx
|
|
103
|
+
* - Foreign Key: {Table}_{columns}_fkey
|
|
104
|
+
*/
|
|
105
|
+
function buildPrimaryKeyName(tableName, explicitName) {
|
|
106
|
+
return explicitName ?? `${tableName}_pkey`;
|
|
107
|
+
}
|
|
108
|
+
function buildUniqueName(tableName, columns, explicitName) {
|
|
109
|
+
if (explicitName)
|
|
110
|
+
return explicitName;
|
|
111
|
+
return `${tableName}_${columns.join("_")}_key`;
|
|
112
|
+
}
|
|
113
|
+
function buildIndexName(tableName, columns, explicitName) {
|
|
114
|
+
if (explicitName)
|
|
115
|
+
return explicitName;
|
|
116
|
+
return `${tableName}_${columns.join("_")}_idx`;
|
|
117
|
+
}
|
|
118
|
+
function buildForeignKeyName(tableName, columns, _referencedTable, _referencedColumns, explicitName) {
|
|
119
|
+
if (explicitName)
|
|
120
|
+
return explicitName;
|
|
121
|
+
// Prisma uses {Table}_{columns}_fkey (doesn't include referenced table/columns in name)
|
|
122
|
+
return `${tableName}_${columns.join("_")}_fkey`;
|
|
123
|
+
}
|
|
124
|
+
function getFieldType(field) {
|
|
125
|
+
const ref = field.type.reference?.ref;
|
|
126
|
+
if (ref && isDataModel(ref)) {
|
|
127
|
+
return { type: ref.name, isRelation: true };
|
|
128
|
+
}
|
|
129
|
+
if (ref && isEnum(ref)) {
|
|
130
|
+
return { type: ref.name, isRelation: false };
|
|
131
|
+
}
|
|
132
|
+
return { type: field.type.type ?? "String", isRelation: false };
|
|
133
|
+
}
|
|
134
|
+
function getRelationFieldNames(field) {
|
|
135
|
+
const relationAttr = getAttribute(field, "@relation");
|
|
136
|
+
if (!relationAttr)
|
|
137
|
+
return null;
|
|
138
|
+
const fields = getAttributeArrayRefs(relationAttr, "fields");
|
|
139
|
+
const references = getAttributeArrayRefs(relationAttr, "references");
|
|
140
|
+
if (!fields || !references)
|
|
141
|
+
return null;
|
|
142
|
+
const mapName = getAttributeStringArg(relationAttr, ["map", "name"]);
|
|
143
|
+
return { fields, references, mapName };
|
|
144
|
+
}
|
|
145
|
+
function buildFieldNameMap(model) {
|
|
146
|
+
const map = new Map();
|
|
147
|
+
for (const field of model.fields) {
|
|
148
|
+
if (!isDataField(field))
|
|
149
|
+
continue;
|
|
150
|
+
map.set(field.name, getColumnName(field));
|
|
151
|
+
}
|
|
152
|
+
return map;
|
|
153
|
+
}
|
|
154
|
+
function parseModel(model) {
|
|
155
|
+
const tableName = getTableName(model);
|
|
156
|
+
const columns = [];
|
|
157
|
+
const fieldNameMap = buildFieldNameMap(model);
|
|
158
|
+
for (const field of model.fields) {
|
|
159
|
+
if (!isDataField(field))
|
|
160
|
+
continue;
|
|
161
|
+
const typeInfo = getFieldType(field);
|
|
162
|
+
if (typeInfo.isRelation) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const defaultInfo = getDefaultValue(field);
|
|
166
|
+
const columnName = getColumnName(field);
|
|
167
|
+
const sqlType = mapFieldTypeToSQL(typeInfo.type);
|
|
168
|
+
columns.push({
|
|
169
|
+
name: columnName,
|
|
170
|
+
type: sqlType,
|
|
171
|
+
notNull: !field.type.optional,
|
|
172
|
+
isArray: field.type.array ?? false,
|
|
173
|
+
default: defaultInfo.default,
|
|
174
|
+
isAutoincrement: defaultInfo.isAutoincrement,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const modelIdAttr = getAttribute(model, "@@id");
|
|
178
|
+
const modelIdFields = getAttributeArrayRefs(modelIdAttr, "fields");
|
|
179
|
+
const modelIdName = getAttributeStringArg(modelIdAttr, ["name", "map"]);
|
|
180
|
+
const primaryKeyColumns = modelIdFields?.map((name) => fieldNameMap.get(name) ?? name) ?? [];
|
|
181
|
+
const fieldIdColumns = model.fields
|
|
182
|
+
.filter((field) => isDataField(field))
|
|
183
|
+
.filter((field) => !!getAttribute(field, "@id"))
|
|
184
|
+
.map((field) => getColumnName(field));
|
|
185
|
+
const resolvedPrimaryKeyColumns = primaryKeyColumns.length > 0 ? primaryKeyColumns : fieldIdColumns;
|
|
186
|
+
const primaryKey = resolvedPrimaryKeyColumns.length > 0
|
|
187
|
+
? {
|
|
188
|
+
name: buildPrimaryKeyName(tableName, modelIdName),
|
|
189
|
+
columns: resolvedPrimaryKeyColumns,
|
|
190
|
+
}
|
|
191
|
+
: undefined;
|
|
192
|
+
const uniqueConstraints = [];
|
|
193
|
+
const uniqueAttrs = model.attributes.filter((attr) => attr.decl.$refText === "@@unique");
|
|
194
|
+
for (const attr of uniqueAttrs) {
|
|
195
|
+
const columns = getAttributeArrayRefs(attr, "fields");
|
|
196
|
+
if (!columns || columns.length === 0)
|
|
197
|
+
continue;
|
|
198
|
+
const resolvedColumns = columns.map((name) => fieldNameMap.get(name) ?? name);
|
|
199
|
+
const explicitName = getAttributeStringArg(attr, ["name", "map"]);
|
|
200
|
+
uniqueConstraints.push({
|
|
201
|
+
name: buildUniqueName(tableName, resolvedColumns, explicitName),
|
|
202
|
+
columns: resolvedColumns,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
for (const field of model.fields) {
|
|
206
|
+
if (!isDataField(field))
|
|
207
|
+
continue;
|
|
208
|
+
if (!getAttribute(field, "@unique"))
|
|
209
|
+
continue;
|
|
210
|
+
const columnName = getColumnName(field);
|
|
211
|
+
const constraintName = buildUniqueName(tableName, [columnName]);
|
|
212
|
+
if (!uniqueConstraints.some((constraint) => constraint.name === constraintName)) {
|
|
213
|
+
uniqueConstraints.push({ name: constraintName, columns: [columnName] });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const indexes = [];
|
|
217
|
+
const indexAttrs = model.attributes.filter((attr) => attr.decl.$refText === "@@index");
|
|
218
|
+
for (const attr of indexAttrs) {
|
|
219
|
+
const columns = getAttributeArrayRefs(attr, "fields");
|
|
220
|
+
if (!columns || columns.length === 0)
|
|
221
|
+
continue;
|
|
222
|
+
const resolvedColumns = columns.map((name) => fieldNameMap.get(name) ?? name);
|
|
223
|
+
const explicitName = getAttributeStringArg(attr, ["name", "map"]);
|
|
224
|
+
indexes.push({
|
|
225
|
+
name: buildIndexName(tableName, resolvedColumns, explicitName),
|
|
226
|
+
columns: resolvedColumns,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const foreignKeys = [];
|
|
230
|
+
for (const field of model.fields) {
|
|
231
|
+
if (!isDataField(field))
|
|
232
|
+
continue;
|
|
233
|
+
const relation = getRelationFieldNames(field);
|
|
234
|
+
if (!relation)
|
|
235
|
+
continue;
|
|
236
|
+
const refModel = field.type.reference?.ref;
|
|
237
|
+
if (!refModel || !isDataModel(refModel))
|
|
238
|
+
continue;
|
|
239
|
+
const referencedTable = getTableName(refModel);
|
|
240
|
+
const referencedFieldMap = buildFieldNameMap(refModel);
|
|
241
|
+
const referencedColumnNames = relation.references.map((name) => referencedFieldMap.get(name) ?? name);
|
|
242
|
+
const columnNames = relation.fields.map((name) => fieldNameMap.get(name) ?? name);
|
|
243
|
+
foreignKeys.push({
|
|
244
|
+
name: buildForeignKeyName(tableName, columnNames, referencedTable, referencedColumnNames, relation.mapName),
|
|
245
|
+
columns: columnNames,
|
|
246
|
+
referencedTable,
|
|
247
|
+
referencedColumns: referencedColumnNames,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
const sortedColumns = columns.sort((a, b) => a.name.localeCompare(b.name));
|
|
251
|
+
return {
|
|
252
|
+
name: tableName,
|
|
253
|
+
columns: sortedColumns,
|
|
254
|
+
primaryKey,
|
|
255
|
+
uniqueConstraints: uniqueConstraints.sort((a, b) => a.name.localeCompare(b.name)),
|
|
256
|
+
indexes: indexes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
257
|
+
foreignKeys: foreignKeys.sort((a, b) => a.name.localeCompare(b.name)),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export async function generateSchemaSnapshot(schemaPath) {
|
|
261
|
+
const loadResult = await loadDocument(schemaPath);
|
|
262
|
+
if (!loadResult.success) {
|
|
263
|
+
const messages = loadResult.errors.map((error) => String(error)).join("\n");
|
|
264
|
+
throw new Error(`Failed to load schema:\n${messages}`);
|
|
265
|
+
}
|
|
266
|
+
const dataModels = loadResult.model.declarations.filter(isDataModel);
|
|
267
|
+
const tables = dataModels
|
|
268
|
+
.map((model) => parseModel(model))
|
|
269
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
270
|
+
return { tables };
|
|
271
|
+
}
|
|
272
|
+
export function createSnapshot(schema) {
|
|
273
|
+
return {
|
|
274
|
+
version: 2,
|
|
275
|
+
createdAt: new Date().toISOString(),
|
|
276
|
+
schema,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema snapshot utilities for ZenStack schemas
|
|
3
|
+
*
|
|
4
|
+
* Uses ZenStack's AST to create a stable, diffable schema snapshot.
|
|
5
|
+
*/
|
|
6
|
+
export interface SchemaColumn {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
notNull: boolean;
|
|
10
|
+
isArray: boolean;
|
|
11
|
+
default?: string | number | boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface SchemaConstraint {
|
|
14
|
+
name: string;
|
|
15
|
+
columns: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface SchemaIndex {
|
|
18
|
+
name: string;
|
|
19
|
+
columns: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface SchemaForeignKey {
|
|
22
|
+
name: string;
|
|
23
|
+
columns: string[];
|
|
24
|
+
referencedTable: string;
|
|
25
|
+
referencedColumns: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface SchemaTable {
|
|
28
|
+
name: string;
|
|
29
|
+
columns: SchemaColumn[];
|
|
30
|
+
primaryKey?: SchemaConstraint;
|
|
31
|
+
uniqueConstraints: SchemaConstraint[];
|
|
32
|
+
indexes: SchemaIndex[];
|
|
33
|
+
foreignKeys: SchemaForeignKey[];
|
|
34
|
+
}
|
|
35
|
+
export interface SchemaSnapshot {
|
|
36
|
+
tables: SchemaTable[];
|
|
37
|
+
}
|
|
38
|
+
export interface SchemaSnapshotFile {
|
|
39
|
+
version: 2;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
schema: SchemaSnapshot;
|
|
42
|
+
}
|
|
43
|
+
export declare function generateSchemaSnapshot(schemaPath: string): Promise<SchemaSnapshot>;
|
|
44
|
+
export declare function createSnapshot(schema: SchemaSnapshot): SchemaSnapshotFile;
|
|
45
|
+
//# sourceMappingURL=schema-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-snapshot.d.ts","sourceRoot":"","sources":["../src/schema-snapshot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;CACxB;AAmTD,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAaxF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,kBAAkB,CAMzE"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema snapshot utilities for ZenStack schemas
|
|
3
|
+
*
|
|
4
|
+
* Uses ZenStack's AST to create a stable, diffable schema snapshot.
|
|
5
|
+
*/
|
|
6
|
+
import { loadDocument } from "@zenstackhq/language";
|
|
7
|
+
import { isDataField, isDataModel, isEnum } from "@zenstackhq/language/ast";
|
|
8
|
+
function getAttribute(node, name) {
|
|
9
|
+
return node.attributes.find((attr) => attr.decl.$refText === name);
|
|
10
|
+
}
|
|
11
|
+
function getAttributeStringArg(attr, names) {
|
|
12
|
+
if (!attr)
|
|
13
|
+
return undefined;
|
|
14
|
+
for (const arg of attr.args) {
|
|
15
|
+
const paramName = arg.$resolvedParam?.name;
|
|
16
|
+
if (paramName && names.includes(paramName)) {
|
|
17
|
+
if (arg.value?.$type === "StringLiteral") {
|
|
18
|
+
return arg.value.value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const firstArg = attr.args[0];
|
|
23
|
+
if (firstArg?.value?.$type === "StringLiteral") {
|
|
24
|
+
return firstArg.value.value;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function getAttributeArrayRefs(attr, name) {
|
|
29
|
+
if (!attr)
|
|
30
|
+
return undefined;
|
|
31
|
+
const arg = attr.args.find((item) => item.$resolvedParam?.name === name) ?? attr.args[0];
|
|
32
|
+
if (!arg?.value || arg.value.$type !== "ArrayExpr") {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const refs = arg.value.items
|
|
36
|
+
.filter((item) => item.$type === "ReferenceExpr")
|
|
37
|
+
.map((item) => item.target.$refText);
|
|
38
|
+
return refs.length > 0 ? refs : undefined;
|
|
39
|
+
}
|
|
40
|
+
function getDefaultValue(field) {
|
|
41
|
+
const attr = getAttribute(field, "@default");
|
|
42
|
+
if (!attr) {
|
|
43
|
+
return { hasDefault: false };
|
|
44
|
+
}
|
|
45
|
+
const valueArg = attr.args.find((arg) => arg.$resolvedParam?.name === "value") ?? attr.args[0];
|
|
46
|
+
const expr = valueArg?.value;
|
|
47
|
+
if (!expr) {
|
|
48
|
+
return { hasDefault: true };
|
|
49
|
+
}
|
|
50
|
+
if (expr.$type === "StringLiteral") {
|
|
51
|
+
return { hasDefault: true, default: expr.value };
|
|
52
|
+
}
|
|
53
|
+
if (expr.$type === "NumberLiteral") {
|
|
54
|
+
return { hasDefault: true, default: Number(expr.value) };
|
|
55
|
+
}
|
|
56
|
+
if (expr.$type === "BooleanLiteral") {
|
|
57
|
+
return { hasDefault: true, default: expr.value };
|
|
58
|
+
}
|
|
59
|
+
return { hasDefault: true };
|
|
60
|
+
}
|
|
61
|
+
function getTableName(model) {
|
|
62
|
+
const mapAttr = getAttribute(model, "@@map");
|
|
63
|
+
const mapped = getAttributeStringArg(mapAttr, ["name", "map"]);
|
|
64
|
+
return mapped ?? model.name.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
function getColumnName(field) {
|
|
67
|
+
const mapAttr = getAttribute(field, "@map");
|
|
68
|
+
return getAttributeStringArg(mapAttr, ["name", "map"]) ?? field.name;
|
|
69
|
+
}
|
|
70
|
+
function mapFieldTypeToSQL(fieldType) {
|
|
71
|
+
const typeMap = {
|
|
72
|
+
String: "text",
|
|
73
|
+
Int: "integer",
|
|
74
|
+
Float: "double precision",
|
|
75
|
+
Boolean: "boolean",
|
|
76
|
+
DateTime: "timestamp",
|
|
77
|
+
BigInt: "bigint",
|
|
78
|
+
Decimal: "decimal",
|
|
79
|
+
Json: "json",
|
|
80
|
+
Bytes: "blob",
|
|
81
|
+
};
|
|
82
|
+
return typeMap[fieldType] ?? "text";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Prisma-compatible constraint naming conventions
|
|
86
|
+
*
|
|
87
|
+
* Prisma uses PostgreSQL-aligned naming:
|
|
88
|
+
* - Primary Key: {Table}_pkey
|
|
89
|
+
* - Unique: {Table}_{columns}_key
|
|
90
|
+
* - Index: {Table}_{columns}_idx
|
|
91
|
+
* - Foreign Key: {Table}_{columns}_fkey
|
|
92
|
+
*/
|
|
93
|
+
function buildPrimaryKeyName(tableName, explicitName) {
|
|
94
|
+
return explicitName ?? `${tableName}_pkey`;
|
|
95
|
+
}
|
|
96
|
+
function buildUniqueName(tableName, columns, explicitName) {
|
|
97
|
+
if (explicitName)
|
|
98
|
+
return explicitName;
|
|
99
|
+
return `${tableName}_${columns.join("_")}_key`;
|
|
100
|
+
}
|
|
101
|
+
function buildIndexName(tableName, columns, explicitName) {
|
|
102
|
+
if (explicitName)
|
|
103
|
+
return explicitName;
|
|
104
|
+
return `${tableName}_${columns.join("_")}_idx`;
|
|
105
|
+
}
|
|
106
|
+
function buildForeignKeyName(tableName, columns, _referencedTable, _referencedColumns, explicitName) {
|
|
107
|
+
if (explicitName)
|
|
108
|
+
return explicitName;
|
|
109
|
+
// Prisma uses {Table}_{columns}_fkey (doesn't include referenced table/columns in name)
|
|
110
|
+
return `${tableName}_${columns.join("_")}_fkey`;
|
|
111
|
+
}
|
|
112
|
+
function getFieldType(field) {
|
|
113
|
+
const ref = field.type.reference?.ref;
|
|
114
|
+
if (ref && isDataModel(ref)) {
|
|
115
|
+
return { type: ref.name, isRelation: true };
|
|
116
|
+
}
|
|
117
|
+
if (ref && isEnum(ref)) {
|
|
118
|
+
return { type: ref.name, isRelation: false };
|
|
119
|
+
}
|
|
120
|
+
return { type: field.type.type ?? "String", isRelation: false };
|
|
121
|
+
}
|
|
122
|
+
function getRelationFieldNames(field) {
|
|
123
|
+
const relationAttr = getAttribute(field, "@relation");
|
|
124
|
+
if (!relationAttr)
|
|
125
|
+
return null;
|
|
126
|
+
const fields = getAttributeArrayRefs(relationAttr, "fields");
|
|
127
|
+
const references = getAttributeArrayRefs(relationAttr, "references");
|
|
128
|
+
if (!fields || !references)
|
|
129
|
+
return null;
|
|
130
|
+
const mapName = getAttributeStringArg(relationAttr, ["map", "name"]);
|
|
131
|
+
return { fields, references, mapName };
|
|
132
|
+
}
|
|
133
|
+
function buildFieldNameMap(model) {
|
|
134
|
+
const map = new Map();
|
|
135
|
+
for (const field of model.fields) {
|
|
136
|
+
if (!isDataField(field))
|
|
137
|
+
continue;
|
|
138
|
+
map.set(field.name, getColumnName(field));
|
|
139
|
+
}
|
|
140
|
+
return map;
|
|
141
|
+
}
|
|
142
|
+
function parseModel(model) {
|
|
143
|
+
const tableName = getTableName(model);
|
|
144
|
+
const columns = [];
|
|
145
|
+
const fieldNameMap = buildFieldNameMap(model);
|
|
146
|
+
for (const field of model.fields) {
|
|
147
|
+
if (!isDataField(field))
|
|
148
|
+
continue;
|
|
149
|
+
const typeInfo = getFieldType(field);
|
|
150
|
+
if (typeInfo.isRelation) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const defaultInfo = getDefaultValue(field);
|
|
154
|
+
const columnName = getColumnName(field);
|
|
155
|
+
const sqlType = mapFieldTypeToSQL(typeInfo.type);
|
|
156
|
+
columns.push({
|
|
157
|
+
name: columnName,
|
|
158
|
+
type: sqlType,
|
|
159
|
+
notNull: !field.type.optional,
|
|
160
|
+
isArray: field.type.array ?? false,
|
|
161
|
+
default: defaultInfo.default,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const modelIdAttr = getAttribute(model, "@@id");
|
|
165
|
+
const modelIdFields = getAttributeArrayRefs(modelIdAttr, "fields");
|
|
166
|
+
const modelIdName = getAttributeStringArg(modelIdAttr, ["name", "map"]);
|
|
167
|
+
const primaryKeyColumns = modelIdFields?.map((name) => fieldNameMap.get(name) ?? name) ?? [];
|
|
168
|
+
const fieldIdColumns = model.fields
|
|
169
|
+
.filter((field) => isDataField(field))
|
|
170
|
+
.filter((field) => !!getAttribute(field, "@id"))
|
|
171
|
+
.map((field) => getColumnName(field));
|
|
172
|
+
const resolvedPrimaryKeyColumns = primaryKeyColumns.length > 0 ? primaryKeyColumns : fieldIdColumns;
|
|
173
|
+
const primaryKey = resolvedPrimaryKeyColumns.length > 0
|
|
174
|
+
? {
|
|
175
|
+
name: buildPrimaryKeyName(tableName, modelIdName),
|
|
176
|
+
columns: resolvedPrimaryKeyColumns,
|
|
177
|
+
}
|
|
178
|
+
: undefined;
|
|
179
|
+
const uniqueConstraints = [];
|
|
180
|
+
const uniqueAttrs = model.attributes.filter((attr) => attr.decl.$refText === "@@unique");
|
|
181
|
+
for (const attr of uniqueAttrs) {
|
|
182
|
+
const columns = getAttributeArrayRefs(attr, "fields");
|
|
183
|
+
if (!columns || columns.length === 0)
|
|
184
|
+
continue;
|
|
185
|
+
const resolvedColumns = columns.map((name) => fieldNameMap.get(name) ?? name);
|
|
186
|
+
const explicitName = getAttributeStringArg(attr, ["name", "map"]);
|
|
187
|
+
uniqueConstraints.push({
|
|
188
|
+
name: buildUniqueName(tableName, resolvedColumns, explicitName),
|
|
189
|
+
columns: resolvedColumns,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
for (const field of model.fields) {
|
|
193
|
+
if (!isDataField(field))
|
|
194
|
+
continue;
|
|
195
|
+
if (!getAttribute(field, "@unique"))
|
|
196
|
+
continue;
|
|
197
|
+
const columnName = getColumnName(field);
|
|
198
|
+
const constraintName = buildUniqueName(tableName, [columnName]);
|
|
199
|
+
if (!uniqueConstraints.some((constraint) => constraint.name === constraintName)) {
|
|
200
|
+
uniqueConstraints.push({ name: constraintName, columns: [columnName] });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const indexes = [];
|
|
204
|
+
const indexAttrs = model.attributes.filter((attr) => attr.decl.$refText === "@@index");
|
|
205
|
+
for (const attr of indexAttrs) {
|
|
206
|
+
const columns = getAttributeArrayRefs(attr, "fields");
|
|
207
|
+
if (!columns || columns.length === 0)
|
|
208
|
+
continue;
|
|
209
|
+
const resolvedColumns = columns.map((name) => fieldNameMap.get(name) ?? name);
|
|
210
|
+
const explicitName = getAttributeStringArg(attr, ["name", "map"]);
|
|
211
|
+
indexes.push({
|
|
212
|
+
name: buildIndexName(tableName, resolvedColumns, explicitName),
|
|
213
|
+
columns: resolvedColumns,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const foreignKeys = [];
|
|
217
|
+
for (const field of model.fields) {
|
|
218
|
+
if (!isDataField(field))
|
|
219
|
+
continue;
|
|
220
|
+
const relation = getRelationFieldNames(field);
|
|
221
|
+
if (!relation)
|
|
222
|
+
continue;
|
|
223
|
+
const refModel = field.type.reference?.ref;
|
|
224
|
+
if (!refModel || !isDataModel(refModel))
|
|
225
|
+
continue;
|
|
226
|
+
const referencedTable = getTableName(refModel);
|
|
227
|
+
const referencedFieldMap = buildFieldNameMap(refModel);
|
|
228
|
+
const referencedColumnNames = relation.references.map((name) => referencedFieldMap.get(name) ?? name);
|
|
229
|
+
const columnNames = relation.fields.map((name) => fieldNameMap.get(name) ?? name);
|
|
230
|
+
foreignKeys.push({
|
|
231
|
+
name: buildForeignKeyName(tableName, columnNames, referencedTable, referencedColumnNames, relation.mapName),
|
|
232
|
+
columns: columnNames,
|
|
233
|
+
referencedTable,
|
|
234
|
+
referencedColumns: referencedColumnNames,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const sortedColumns = columns.sort((a, b) => a.name.localeCompare(b.name));
|
|
238
|
+
return {
|
|
239
|
+
name: tableName,
|
|
240
|
+
columns: sortedColumns,
|
|
241
|
+
primaryKey,
|
|
242
|
+
uniqueConstraints: uniqueConstraints.sort((a, b) => a.name.localeCompare(b.name)),
|
|
243
|
+
indexes: indexes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
244
|
+
foreignKeys: foreignKeys.sort((a, b) => a.name.localeCompare(b.name)),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
export async function generateSchemaSnapshot(schemaPath) {
|
|
248
|
+
const loadResult = await loadDocument(schemaPath);
|
|
249
|
+
if (!loadResult.success) {
|
|
250
|
+
const messages = loadResult.errors.map((error) => String(error)).join("\n");
|
|
251
|
+
throw new Error(`Failed to load schema:\n${messages}`);
|
|
252
|
+
}
|
|
253
|
+
const dataModels = loadResult.model.declarations.filter(isDataModel);
|
|
254
|
+
const tables = dataModels
|
|
255
|
+
.map((model) => parseModel(model))
|
|
256
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
257
|
+
return { tables };
|
|
258
|
+
}
|
|
259
|
+
export function createSnapshot(schema) {
|
|
260
|
+
return {
|
|
261
|
+
version: 2,
|
|
262
|
+
createdAt: new Date().toISOString(),
|
|
263
|
+
schema,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL Compiler - Generates raw SQL from schema operations using Kysely's compile()
|
|
3
|
+
*
|
|
4
|
+
* Uses Kysely with DummyDriver to compile schema operations to dialect-specific SQL
|
|
5
|
+
* without requiring a database connection.
|
|
6
|
+
*/
|
|
7
|
+
import type { KyselyDialect } from "./kysely-adapter.js";
|
|
8
|
+
import type { SchemaTable, SchemaColumn } from "../schema/snapshot.js";
|
|
9
|
+
export interface SqlMigration {
|
|
10
|
+
up: string[];
|
|
11
|
+
down: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface CompileSqlOptions {
|
|
14
|
+
dialect: KyselyDialect;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compile a CREATE TABLE statement to SQL
|
|
18
|
+
*/
|
|
19
|
+
export declare function compileCreateTable(model: SchemaTable, options: CompileSqlOptions): string;
|
|
20
|
+
/**
|
|
21
|
+
* Compile a DROP TABLE statement to SQL
|
|
22
|
+
*/
|
|
23
|
+
export declare function compileDropTable(tableName: string, options: CompileSqlOptions): string;
|
|
24
|
+
/**
|
|
25
|
+
* Compile an ADD COLUMN statement to SQL
|
|
26
|
+
*/
|
|
27
|
+
export declare function compileAddColumn(tableName: string, column: SchemaColumn, options: CompileSqlOptions): string;
|
|
28
|
+
/**
|
|
29
|
+
* Compile a DROP COLUMN statement to SQL
|
|
30
|
+
*/
|
|
31
|
+
export declare function compileDropColumn(tableName: string, columnName: string, options: CompileSqlOptions): string;
|
|
32
|
+
/**
|
|
33
|
+
* Compile a RENAME TABLE statement to SQL
|
|
34
|
+
*/
|
|
35
|
+
export declare function compileRenameTable(fromName: string, toName: string, options: CompileSqlOptions): string;
|
|
36
|
+
/**
|
|
37
|
+
* Compile a RENAME COLUMN statement to SQL
|
|
38
|
+
*/
|
|
39
|
+
export declare function compileRenameColumn(tableName: string, fromName: string, toName: string, options: CompileSqlOptions): string;
|
|
40
|
+
/**
|
|
41
|
+
* Compile a CREATE INDEX statement to SQL
|
|
42
|
+
*/
|
|
43
|
+
export declare function compileCreateIndex(tableName: string, indexName: string, columns: string[], options: CompileSqlOptions): string;
|
|
44
|
+
/**
|
|
45
|
+
* Compile a DROP INDEX statement to SQL
|
|
46
|
+
*/
|
|
47
|
+
export declare function compileDropIndex(indexName: string, options: CompileSqlOptions): string;
|
|
48
|
+
/**
|
|
49
|
+
* Compile an ADD CONSTRAINT (unique) statement to SQL
|
|
50
|
+
*/
|
|
51
|
+
export declare function compileAddUniqueConstraint(tableName: string, constraintName: string, columns: string[], options: CompileSqlOptions): string;
|
|
52
|
+
/**
|
|
53
|
+
* Compile a DROP CONSTRAINT statement to SQL
|
|
54
|
+
*/
|
|
55
|
+
export declare function compileDropConstraint(tableName: string, constraintName: string, options: CompileSqlOptions): string;
|
|
56
|
+
/**
|
|
57
|
+
* Compile an ADD FOREIGN KEY CONSTRAINT statement to SQL
|
|
58
|
+
*/
|
|
59
|
+
export declare function compileAddForeignKeyConstraint(tableName: string, constraintName: string, columns: string[], referencedTable: string, referencedColumns: string[], options: CompileSqlOptions): string;
|
|
60
|
+
/**
|
|
61
|
+
* Compile an ADD PRIMARY KEY CONSTRAINT statement to SQL
|
|
62
|
+
*/
|
|
63
|
+
export declare function compileAddPrimaryKeyConstraint(tableName: string, constraintName: string, columns: string[], options: CompileSqlOptions): string;
|
|
64
|
+
/**
|
|
65
|
+
* Compile ALTER COLUMN statements for type/nullability/default changes
|
|
66
|
+
*/
|
|
67
|
+
export declare function compileAlterColumn(tableName: string, columnName: string, changes: {
|
|
68
|
+
setType?: string;
|
|
69
|
+
setNotNull?: boolean;
|
|
70
|
+
dropNotNull?: boolean;
|
|
71
|
+
setDefault?: string | number | boolean;
|
|
72
|
+
dropDefault?: boolean;
|
|
73
|
+
}, options: CompileSqlOptions): string[];
|
|
74
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/sql/compiler.ts"],"names":[],"mappings":"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"}
|