@famgia/omnify-atlas 0.0.5 → 0.0.6
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/index.cjs +290 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +157 -15
- package/dist/index.d.ts +157 -15
- package/dist/index.js +284 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -25,8 +25,10 @@ __export(index_exports, {
|
|
|
25
25
|
addMigrationRecord: () => addMigrationRecord,
|
|
26
26
|
applySchema: () => applySchema,
|
|
27
27
|
buildSchemaHashes: () => buildSchemaHashes,
|
|
28
|
+
buildSchemaSnapshots: () => buildSchemaSnapshots,
|
|
28
29
|
checkAtlasVersion: () => checkAtlasVersion,
|
|
29
30
|
compareSchemas: () => compareSchemas,
|
|
31
|
+
compareSchemasDeep: () => compareSchemasDeep,
|
|
30
32
|
computeHash: () => computeHash,
|
|
31
33
|
computeSchemaHash: () => computeSchemaHash,
|
|
32
34
|
createEmptyLockFile: () => createEmptyLockFile,
|
|
@@ -39,15 +41,19 @@ __export(index_exports, {
|
|
|
39
41
|
getPrimaryKeyType: () => getPrimaryKeyType,
|
|
40
42
|
getTimestampType: () => getTimestampType,
|
|
41
43
|
hasBlockingIssues: () => hasBlockingIssues,
|
|
44
|
+
isLockFileV2: () => isLockFileV2,
|
|
42
45
|
mapPropertyToSql: () => mapPropertyToSql,
|
|
43
46
|
parseDiffOutput: () => parseDiffOutput,
|
|
44
47
|
previewSchemaChanges: () => previewSchemaChanges,
|
|
45
48
|
propertyNameToColumnName: () => propertyNameToColumnName,
|
|
49
|
+
propertyToSnapshot: () => propertyToSnapshot,
|
|
46
50
|
readLockFile: () => readLockFile,
|
|
47
51
|
renderHcl: () => renderHcl,
|
|
48
52
|
runAtlasDiff: () => runAtlasDiff,
|
|
49
53
|
schemaNameToTableName: () => schemaNameToTableName,
|
|
54
|
+
schemaToSnapshot: () => schemaToSnapshot,
|
|
50
55
|
updateLockFile: () => updateLockFile,
|
|
56
|
+
updateLockFileV1: () => updateLockFileV1,
|
|
51
57
|
validateHcl: () => validateHcl,
|
|
52
58
|
writeLockFile: () => writeLockFile
|
|
53
59
|
});
|
|
@@ -57,7 +63,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
57
63
|
var import_node_crypto = require("crypto");
|
|
58
64
|
var import_promises = require("fs/promises");
|
|
59
65
|
var LOCK_FILE_NAME = ".omnify.lock";
|
|
60
|
-
var LOCK_FILE_VERSION =
|
|
66
|
+
var LOCK_FILE_VERSION = 2;
|
|
61
67
|
function computeHash(content) {
|
|
62
68
|
return (0, import_node_crypto.createHash)("sha256").update(content, "utf8").digest("hex");
|
|
63
69
|
}
|
|
@@ -80,13 +86,73 @@ function createEmptyLockFile(driver) {
|
|
|
80
86
|
migrations: []
|
|
81
87
|
};
|
|
82
88
|
}
|
|
89
|
+
function propertyToSnapshot(property) {
|
|
90
|
+
const prop = property;
|
|
91
|
+
return {
|
|
92
|
+
type: prop.type,
|
|
93
|
+
nullable: prop.nullable,
|
|
94
|
+
unique: prop.unique,
|
|
95
|
+
default: prop.default,
|
|
96
|
+
length: prop.length,
|
|
97
|
+
unsigned: prop.unsigned,
|
|
98
|
+
enum: prop.enum,
|
|
99
|
+
relation: prop.relation,
|
|
100
|
+
target: prop.target,
|
|
101
|
+
onDelete: prop.onDelete,
|
|
102
|
+
onUpdate: prop.onUpdate,
|
|
103
|
+
mappedBy: prop.mappedBy,
|
|
104
|
+
joinTable: prop.joinTable,
|
|
105
|
+
// renamedFrom is kept in snapshot for comparison (rename detection),
|
|
106
|
+
// but will be stripped when writing to lock file.
|
|
107
|
+
renamedFrom: prop.renamedFrom
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function schemaToSnapshot(schema, hash, modifiedAt) {
|
|
111
|
+
const properties = {};
|
|
112
|
+
if (schema.properties) {
|
|
113
|
+
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
114
|
+
properties[name] = propertyToSnapshot(prop);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const opts = schema.options;
|
|
118
|
+
let indexes;
|
|
119
|
+
if (opts?.indexes && opts.indexes.length > 0) {
|
|
120
|
+
indexes = opts.indexes.map((idx) => ({
|
|
121
|
+
columns: idx.columns,
|
|
122
|
+
unique: idx.unique ?? false,
|
|
123
|
+
name: idx.name
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
let uniqueConstraints;
|
|
127
|
+
if (opts?.unique) {
|
|
128
|
+
uniqueConstraints = Array.isArray(opts.unique[0]) ? opts.unique : [opts.unique];
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
name: schema.name,
|
|
132
|
+
kind: schema.kind ?? "object",
|
|
133
|
+
hash,
|
|
134
|
+
relativePath: schema.relativePath,
|
|
135
|
+
modifiedAt,
|
|
136
|
+
properties,
|
|
137
|
+
primaryKeyType: opts?.primaryKeyType,
|
|
138
|
+
timestamps: opts?.timestamps,
|
|
139
|
+
softDelete: opts?.softDelete,
|
|
140
|
+
indexes,
|
|
141
|
+
uniqueConstraints,
|
|
142
|
+
values: schema.values
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function isLockFileV2(lockFile) {
|
|
146
|
+
return lockFile.version === 2;
|
|
147
|
+
}
|
|
83
148
|
async function readLockFile(lockFilePath) {
|
|
84
149
|
try {
|
|
85
150
|
const content = await (0, import_promises.readFile)(lockFilePath, "utf8");
|
|
86
151
|
const parsed = JSON.parse(content);
|
|
87
|
-
|
|
152
|
+
const lockFile = parsed;
|
|
153
|
+
if (lockFile.version !== 1 && lockFile.version !== 2) {
|
|
88
154
|
throw new Error(
|
|
89
|
-
`Lock file version mismatch: expected
|
|
155
|
+
`Lock file version mismatch: expected 1 or 2, got ${lockFile.version}`
|
|
90
156
|
);
|
|
91
157
|
}
|
|
92
158
|
return parsed;
|
|
@@ -121,6 +187,136 @@ async function buildSchemaHashes(schemas) {
|
|
|
121
187
|
}
|
|
122
188
|
return hashes;
|
|
123
189
|
}
|
|
190
|
+
async function buildSchemaSnapshots(schemas) {
|
|
191
|
+
const snapshots = {};
|
|
192
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
193
|
+
const hash = computeSchemaHash(schema);
|
|
194
|
+
let modifiedAt;
|
|
195
|
+
try {
|
|
196
|
+
const stats = await (0, import_promises.stat)(schema.filePath);
|
|
197
|
+
modifiedAt = stats.mtime.toISOString();
|
|
198
|
+
} catch {
|
|
199
|
+
modifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
200
|
+
}
|
|
201
|
+
snapshots[name] = schemaToSnapshot(schema, hash, modifiedAt);
|
|
202
|
+
}
|
|
203
|
+
return snapshots;
|
|
204
|
+
}
|
|
205
|
+
function diffPropertySnapshots(prev, curr) {
|
|
206
|
+
const modifications = [];
|
|
207
|
+
if (prev.type !== curr.type) modifications.push("type");
|
|
208
|
+
if (prev.nullable !== curr.nullable) modifications.push("nullable");
|
|
209
|
+
if (prev.unique !== curr.unique) modifications.push("unique");
|
|
210
|
+
if (JSON.stringify(prev.default) !== JSON.stringify(curr.default)) modifications.push("default");
|
|
211
|
+
if (prev.length !== curr.length) modifications.push("length");
|
|
212
|
+
if (prev.unsigned !== curr.unsigned) modifications.push("unsigned");
|
|
213
|
+
if (JSON.stringify(prev.enum) !== JSON.stringify(curr.enum)) modifications.push("enum");
|
|
214
|
+
if (prev.relation !== curr.relation) modifications.push("relation");
|
|
215
|
+
if (prev.target !== curr.target) modifications.push("target");
|
|
216
|
+
if (prev.onDelete !== curr.onDelete) modifications.push("onDelete");
|
|
217
|
+
if (prev.onUpdate !== curr.onUpdate) modifications.push("onUpdate");
|
|
218
|
+
if (prev.mappedBy !== curr.mappedBy) modifications.push("mappedBy");
|
|
219
|
+
return modifications;
|
|
220
|
+
}
|
|
221
|
+
function diffIndexes(prev, curr) {
|
|
222
|
+
const changes = [];
|
|
223
|
+
const prevIndexes = prev ?? [];
|
|
224
|
+
const currIndexes = curr ?? [];
|
|
225
|
+
const indexKey = (idx) => `${idx.columns.join(",")}:${idx.unique}`;
|
|
226
|
+
const prevKeys = new Map(prevIndexes.map((idx) => [indexKey(idx), idx]));
|
|
227
|
+
const currKeys = new Map(currIndexes.map((idx) => [indexKey(idx), idx]));
|
|
228
|
+
for (const [key, idx] of currKeys) {
|
|
229
|
+
if (!prevKeys.has(key)) {
|
|
230
|
+
changes.push({ changeType: "added", index: idx });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const [key, idx] of prevKeys) {
|
|
234
|
+
if (!currKeys.has(key)) {
|
|
235
|
+
changes.push({ changeType: "removed", index: idx });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return changes;
|
|
239
|
+
}
|
|
240
|
+
function diffSchemaSnapshots(prev, curr) {
|
|
241
|
+
const columnChanges = [];
|
|
242
|
+
const prevProps = prev.properties;
|
|
243
|
+
const currProps = curr.properties;
|
|
244
|
+
const prevNames = new Set(Object.keys(prevProps));
|
|
245
|
+
const currNames = new Set(Object.keys(currProps));
|
|
246
|
+
const renamedNewNames = /* @__PURE__ */ new Set();
|
|
247
|
+
const renamedOldNames = /* @__PURE__ */ new Set();
|
|
248
|
+
for (const [name, currProp] of Object.entries(currProps)) {
|
|
249
|
+
if (currProp.renamedFrom && prevProps[currProp.renamedFrom]) {
|
|
250
|
+
const prevProp = prevProps[currProp.renamedFrom];
|
|
251
|
+
const mods = diffPropertySnapshots(prevProp, currProp);
|
|
252
|
+
const filteredMods = mods.filter((m) => m !== "renamedFrom");
|
|
253
|
+
columnChanges.push({
|
|
254
|
+
column: name,
|
|
255
|
+
changeType: "renamed",
|
|
256
|
+
previousColumn: currProp.renamedFrom,
|
|
257
|
+
previousDef: prevProp,
|
|
258
|
+
currentDef: currProp,
|
|
259
|
+
modifications: filteredMods.length > 0 ? filteredMods : void 0
|
|
260
|
+
});
|
|
261
|
+
renamedNewNames.add(name);
|
|
262
|
+
renamedOldNames.add(currProp.renamedFrom);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const name of currNames) {
|
|
266
|
+
if (!prevNames.has(name) && !renamedNewNames.has(name)) {
|
|
267
|
+
columnChanges.push({
|
|
268
|
+
column: name,
|
|
269
|
+
changeType: "added",
|
|
270
|
+
currentDef: currProps[name]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const name of prevNames) {
|
|
275
|
+
if (!currNames.has(name) && !renamedOldNames.has(name)) {
|
|
276
|
+
columnChanges.push({
|
|
277
|
+
column: name,
|
|
278
|
+
changeType: "removed",
|
|
279
|
+
previousDef: prevProps[name]
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const name of currNames) {
|
|
284
|
+
if (prevNames.has(name) && !renamedNewNames.has(name)) {
|
|
285
|
+
const prevProp = prevProps[name];
|
|
286
|
+
const currProp = currProps[name];
|
|
287
|
+
const mods = diffPropertySnapshots(prevProp, currProp);
|
|
288
|
+
if (mods.length > 0) {
|
|
289
|
+
columnChanges.push({
|
|
290
|
+
column: name,
|
|
291
|
+
changeType: "modified",
|
|
292
|
+
previousDef: prevProp,
|
|
293
|
+
currentDef: currProp,
|
|
294
|
+
modifications: mods
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const indexChanges = diffIndexes(prev.indexes, curr.indexes);
|
|
300
|
+
const optionChanges = {};
|
|
301
|
+
let hasOptionChanges = false;
|
|
302
|
+
if (prev.timestamps !== curr.timestamps) {
|
|
303
|
+
optionChanges.timestamps = { from: prev.timestamps, to: curr.timestamps };
|
|
304
|
+
hasOptionChanges = true;
|
|
305
|
+
}
|
|
306
|
+
if (prev.softDelete !== curr.softDelete) {
|
|
307
|
+
optionChanges.softDelete = { from: prev.softDelete, to: curr.softDelete };
|
|
308
|
+
hasOptionChanges = true;
|
|
309
|
+
}
|
|
310
|
+
if (prev.primaryKeyType !== curr.primaryKeyType) {
|
|
311
|
+
optionChanges.primaryKeyType = { from: prev.primaryKeyType, to: curr.primaryKeyType };
|
|
312
|
+
hasOptionChanges = true;
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
columnChanges: columnChanges.length > 0 ? columnChanges : void 0,
|
|
316
|
+
indexChanges: indexChanges.length > 0 ? indexChanges : void 0,
|
|
317
|
+
optionChanges: hasOptionChanges ? optionChanges : void 0
|
|
318
|
+
};
|
|
319
|
+
}
|
|
124
320
|
function compareSchemas(currentHashes, lockFile) {
|
|
125
321
|
const changes = [];
|
|
126
322
|
const unchanged = [];
|
|
@@ -175,11 +371,95 @@ function compareSchemas(currentHashes, lockFile) {
|
|
|
175
371
|
unchanged
|
|
176
372
|
};
|
|
177
373
|
}
|
|
178
|
-
function
|
|
374
|
+
function compareSchemasDeep(currentSnapshots, lockFile) {
|
|
375
|
+
const changes = [];
|
|
376
|
+
const unchanged = [];
|
|
377
|
+
const previousSnapshots = lockFile?.schemas ?? {};
|
|
378
|
+
const previousNames = new Set(Object.keys(previousSnapshots));
|
|
379
|
+
const currentNames = new Set(Object.keys(currentSnapshots));
|
|
380
|
+
for (const name of currentNames) {
|
|
381
|
+
if (!previousNames.has(name)) {
|
|
382
|
+
const current = currentSnapshots[name];
|
|
383
|
+
if (current) {
|
|
384
|
+
changes.push({
|
|
385
|
+
schemaName: name,
|
|
386
|
+
changeType: "added",
|
|
387
|
+
currentHash: current.hash
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
for (const name of previousNames) {
|
|
393
|
+
if (!currentNames.has(name)) {
|
|
394
|
+
const previous = previousSnapshots[name];
|
|
395
|
+
if (previous) {
|
|
396
|
+
changes.push({
|
|
397
|
+
schemaName: name,
|
|
398
|
+
changeType: "removed",
|
|
399
|
+
previousHash: previous.hash
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const name of currentNames) {
|
|
405
|
+
if (previousNames.has(name)) {
|
|
406
|
+
const current = currentSnapshots[name];
|
|
407
|
+
const previous = previousSnapshots[name];
|
|
408
|
+
if (current.hash !== previous.hash) {
|
|
409
|
+
const diff = diffSchemaSnapshots(previous, current);
|
|
410
|
+
changes.push({
|
|
411
|
+
schemaName: name,
|
|
412
|
+
changeType: "modified",
|
|
413
|
+
previousHash: previous.hash,
|
|
414
|
+
currentHash: current.hash,
|
|
415
|
+
...diff
|
|
416
|
+
});
|
|
417
|
+
} else {
|
|
418
|
+
unchanged.push(name);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
hasChanges: changes.length > 0,
|
|
424
|
+
changes,
|
|
425
|
+
unchanged
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function stripTransientInfo(snapshots) {
|
|
429
|
+
const result = {};
|
|
430
|
+
for (const [schemaName, snapshot] of Object.entries(snapshots)) {
|
|
431
|
+
if (!snapshot.properties) {
|
|
432
|
+
result[schemaName] = snapshot;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const cleanProperties = {};
|
|
436
|
+
for (const [propName, prop] of Object.entries(snapshot.properties)) {
|
|
437
|
+
const { renamedFrom, ...rest } = prop;
|
|
438
|
+
cleanProperties[propName] = rest;
|
|
439
|
+
}
|
|
440
|
+
result[schemaName] = {
|
|
441
|
+
...snapshot,
|
|
442
|
+
properties: cleanProperties
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
function updateLockFile(existingLockFile, currentSnapshots, driver) {
|
|
448
|
+
const cleanSnapshots = stripTransientInfo(currentSnapshots);
|
|
179
449
|
return {
|
|
180
450
|
version: LOCK_FILE_VERSION,
|
|
181
451
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
182
452
|
driver,
|
|
453
|
+
schemas: cleanSnapshots,
|
|
454
|
+
migrations: existingLockFile?.migrations ?? [],
|
|
455
|
+
hclChecksum: existingLockFile?.hclChecksum
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function updateLockFileV1(existingLockFile, currentHashes, driver) {
|
|
459
|
+
return {
|
|
460
|
+
version: 1,
|
|
461
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
462
|
+
driver,
|
|
183
463
|
schemas: currentHashes,
|
|
184
464
|
migrations: existingLockFile?.migrations ?? [],
|
|
185
465
|
hclChecksum: existingLockFile?.hclChecksum
|
|
@@ -1360,8 +1640,10 @@ function hasBlockingIssues(_preview) {
|
|
|
1360
1640
|
addMigrationRecord,
|
|
1361
1641
|
applySchema,
|
|
1362
1642
|
buildSchemaHashes,
|
|
1643
|
+
buildSchemaSnapshots,
|
|
1363
1644
|
checkAtlasVersion,
|
|
1364
1645
|
compareSchemas,
|
|
1646
|
+
compareSchemasDeep,
|
|
1365
1647
|
computeHash,
|
|
1366
1648
|
computeSchemaHash,
|
|
1367
1649
|
createEmptyLockFile,
|
|
@@ -1374,15 +1656,19 @@ function hasBlockingIssues(_preview) {
|
|
|
1374
1656
|
getPrimaryKeyType,
|
|
1375
1657
|
getTimestampType,
|
|
1376
1658
|
hasBlockingIssues,
|
|
1659
|
+
isLockFileV2,
|
|
1377
1660
|
mapPropertyToSql,
|
|
1378
1661
|
parseDiffOutput,
|
|
1379
1662
|
previewSchemaChanges,
|
|
1380
1663
|
propertyNameToColumnName,
|
|
1664
|
+
propertyToSnapshot,
|
|
1381
1665
|
readLockFile,
|
|
1382
1666
|
renderHcl,
|
|
1383
1667
|
runAtlasDiff,
|
|
1384
1668
|
schemaNameToTableName,
|
|
1669
|
+
schemaToSnapshot,
|
|
1385
1670
|
updateLockFile,
|
|
1671
|
+
updateLockFileV1,
|
|
1386
1672
|
validateHcl,
|
|
1387
1673
|
writeLockFile
|
|
1388
1674
|
});
|