masterrecord 0.2.5 → 0.2.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/Migrations/cli.js
CHANGED
|
@@ -21,6 +21,19 @@ var Migration = require('./migrations');
|
|
|
21
21
|
var globSearch = require("glob");
|
|
22
22
|
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
23
23
|
|
|
24
|
+
// Extract numeric timestamp from migration filename (e.g., 1737999999999_name_migration.js)
|
|
25
|
+
function __getMigrationTimestamp(file){
|
|
26
|
+
try{
|
|
27
|
+
const base = path.basename(file);
|
|
28
|
+
const match = /^([0-9]{10,})_/i.exec(base);
|
|
29
|
+
if(match){ return Number(match[1]); }
|
|
30
|
+
const stat = fs.statSync(file);
|
|
31
|
+
return stat.mtimeMs || 0;
|
|
32
|
+
}catch(_){
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
|
|
25
38
|
const [,, ...args] = process.argv
|
|
26
39
|
|
|
@@ -45,6 +58,10 @@ program.option('-V', 'output the version');
|
|
|
45
58
|
var executedLocation = process.cwd();
|
|
46
59
|
// find context file from main folder location
|
|
47
60
|
var contextInstance = migration.findContext(executedLocation, contextFileName);
|
|
61
|
+
if(!contextInstance){
|
|
62
|
+
console.log(`Error - Cannot read or find Context file '${contextFileName}.js' in '${executedLocation}'.`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
48
65
|
var snap = {
|
|
49
66
|
file : contextInstance.fileLocation,
|
|
50
67
|
executedLocation : executedLocation,
|
|
@@ -103,23 +120,41 @@ program.option('-V', 'output the version');
|
|
|
103
120
|
// find context file from main folder location
|
|
104
121
|
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
105
122
|
var files = globSearch.sync(search, executedLocation);
|
|
106
|
-
var file = files[0];
|
|
107
|
-
if(file){
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
var file = files && files[0];
|
|
124
|
+
if(!file){
|
|
125
|
+
console.log(`Error - Cannot read or find Context snapshot '${contextFileName}_contextSnapShot.json' in '${executedLocation}'. Run 'masterrecord enable-migrations ${contextFileName}'.`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
var contextSnapshot = null;
|
|
129
|
+
try{
|
|
130
|
+
contextSnapshot = require(file);
|
|
131
|
+
}catch(_){
|
|
132
|
+
console.log(`Error - Cannot read context snapshot at '${file}'.`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let ContextCtor;
|
|
137
|
+
try{
|
|
138
|
+
ContextCtor = require(contextSnapshot.contextLocation);
|
|
139
|
+
}catch(_){
|
|
140
|
+
console.log(`Error - Cannot load Context file at '${contextSnapshot.contextLocation}'.`);
|
|
141
|
+
return;
|
|
119
142
|
}
|
|
120
|
-
|
|
121
|
-
|
|
143
|
+
let contextInstance;
|
|
144
|
+
try{
|
|
145
|
+
contextInstance = new ContextCtor();
|
|
146
|
+
}catch(_){
|
|
147
|
+
console.log(`Error - Failed to construct Context from '${contextSnapshot.contextLocation}'.`);
|
|
148
|
+
return;
|
|
122
149
|
}
|
|
150
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
151
|
+
var newEntity = migration.template(name, contextSnapshot.schema, cleanEntities);
|
|
152
|
+
var migrationDate = Date.now();
|
|
153
|
+
var outputFile = `${contextSnapshot.migrationFolder}/${migrationDate}_${name}_migration.js`
|
|
154
|
+
fs.writeFile(outputFile, newEntity, 'utf8', function (err) {
|
|
155
|
+
if (err) return console.log("--- Error running cammand, re-run command add-migration ---- ", err);
|
|
156
|
+
});
|
|
157
|
+
console.log(`${name} migration file created`);
|
|
123
158
|
}catch (e){
|
|
124
159
|
console.log("Error - Cannot read or find file ", e);
|
|
125
160
|
}
|
|
@@ -142,25 +177,20 @@ program.option('-V', 'output the version');
|
|
|
142
177
|
var contextSnapshot = require(file);
|
|
143
178
|
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
144
179
|
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
145
|
-
if( migrationFiles){
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
/** ENd of BUG */
|
|
153
|
-
|
|
154
|
-
var mFile = mFiles[mFiles.length -1];
|
|
180
|
+
if( migrationFiles && migrationFiles.length){
|
|
181
|
+
// sort by timestamp prefix or file mtime as fallback
|
|
182
|
+
var mFiles = migrationFiles.slice().sort(function(a, b){
|
|
183
|
+
return __getMigrationTimestamp(a) - __getMigrationTimestamp(b);
|
|
184
|
+
});
|
|
185
|
+
var mFile = mFiles[mFiles.length -1];
|
|
155
186
|
|
|
156
187
|
var migrationProjectFile = require(mFile);
|
|
157
188
|
var context = require(contextSnapshot.contextLocation);
|
|
158
189
|
var contextInstance = new context();
|
|
159
190
|
var newMigrationProjectInstance = new migrationProjectFile(context);
|
|
160
191
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
192
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
193
|
+
var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
|
|
164
194
|
newMigrationProjectInstance.up(tableObj);
|
|
165
195
|
|
|
166
196
|
var snap = {
|
|
@@ -201,51 +231,62 @@ program.option('-V', 'output the version');
|
|
|
201
231
|
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
202
232
|
var files = globSearch.sync(search, executedLocation);
|
|
203
233
|
var file = files[0];
|
|
204
|
-
if(file){
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
var file = mFiles[i];
|
|
219
|
-
var migrationProjectFile = require(file);
|
|
220
|
-
|
|
221
|
-
var contextInstance = new context();
|
|
222
|
-
var newMigrationProjectInstance = new migrationProjectFile(context);
|
|
223
|
-
var tableObj = migration.buildUpObject(contextSnapshot.schema, contextInstance.__entities);
|
|
224
|
-
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
225
|
-
newMigrationProjectInstance.up(tableObj);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
var snap = {
|
|
230
|
-
file : contextSnapshot.contextLocation,
|
|
231
|
-
executedLocation : executedLocation,
|
|
232
|
-
context : contextInstance,
|
|
233
|
-
contextEntities : cleanEntities,
|
|
234
|
-
contextFileName: contextFileName
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
migration.createSnapShot(snap);
|
|
238
|
-
console.log("database updated");
|
|
239
|
-
}
|
|
240
|
-
else{
|
|
234
|
+
if(!file){
|
|
235
|
+
console.log(`Error - Cannot read or find Context snapshot '${contextFileName}_contextSnapShot.json' in '${executedLocation}'.`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
var contextSnapshot;
|
|
239
|
+
try{
|
|
240
|
+
contextSnapshot = require(file);
|
|
241
|
+
}catch(_){
|
|
242
|
+
console.log(`Error - Cannot read context snapshot at '${file}'.`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
246
|
+
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
247
|
+
if(!(migrationFiles && migrationFiles.length)){
|
|
241
248
|
console.log("Error - Cannot read or find migration file");
|
|
242
|
-
|
|
243
|
-
|
|
249
|
+
return;
|
|
244
250
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
// organize by time using filename timestamp or file mtime
|
|
252
|
+
var mFiles = migrationFiles.slice().sort(function(a, b){
|
|
253
|
+
return __getMigrationTimestamp(a) - __getMigrationTimestamp(b);
|
|
254
|
+
});
|
|
255
|
+
let ContextCtor;
|
|
256
|
+
try{
|
|
257
|
+
ContextCtor = require(contextSnapshot.contextLocation);
|
|
258
|
+
}catch(_){
|
|
259
|
+
console.log(`Error - Cannot load Context file at '${contextSnapshot.contextLocation}'.`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
var contextInstance;
|
|
263
|
+
try{
|
|
264
|
+
contextInstance = new ContextCtor();
|
|
265
|
+
}catch(_){
|
|
266
|
+
console.log(`Error - Failed to construct Context from '${contextSnapshot.contextLocation}'.`);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
270
|
+
for (let i = 0; i < mFiles.length; i++) {
|
|
271
|
+
var migFile = mFiles[i];
|
|
272
|
+
var migrationProjectFile = require(migFile);
|
|
273
|
+
var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
|
|
274
|
+
var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
|
|
275
|
+
newMigrationProjectInstance.up(tableObj);
|
|
276
|
+
}
|
|
277
|
+
var snap = {
|
|
278
|
+
file : contextSnapshot.contextLocation,
|
|
279
|
+
executedLocation : executedLocation,
|
|
280
|
+
context : contextInstance,
|
|
281
|
+
contextEntities : cleanEntities,
|
|
282
|
+
contextFileName: contextFileName
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
migration.createSnapShot(snap);
|
|
286
|
+
console.log("database updated");
|
|
287
|
+
|
|
288
|
+
}
|
|
289
|
+
catch (e){
|
|
249
290
|
console.log("Error - Cannot read or find file ", e);
|
|
250
291
|
}
|
|
251
292
|
});
|
|
@@ -261,16 +302,27 @@ program.option('-V', 'output the version');
|
|
|
261
302
|
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
262
303
|
var files = globSearch.sync(search, executedLocation);
|
|
263
304
|
var file = files[0];
|
|
264
|
-
if(file){
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
305
|
+
if(!file){
|
|
306
|
+
console.log(`Error - Cannot read or find Context snapshot '${contextFileName}_contextSnapShot.json' in '${executedLocation}'.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
var contextSnapshot;
|
|
310
|
+
try{
|
|
311
|
+
contextSnapshot = require(file);
|
|
312
|
+
}catch(_){
|
|
313
|
+
console.log(`Error - Cannot read context snapshot at '${file}'.`);
|
|
314
|
+
return;
|
|
271
315
|
}
|
|
272
|
-
|
|
273
|
-
|
|
316
|
+
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
317
|
+
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
318
|
+
if(!(migrationFiles && migrationFiles.length)){
|
|
319
|
+
console.log("No migration files found.");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
var sorted = migrationFiles.slice().sort((a,b) => __getMigrationTimestamp(a) - __getMigrationTimestamp(b));
|
|
323
|
+
// Print relative names for readability
|
|
324
|
+
for(const f of sorted){
|
|
325
|
+
console.log(path.basename(f));
|
|
274
326
|
}
|
|
275
327
|
});
|
|
276
328
|
|
|
@@ -281,10 +333,102 @@ program.option('-V', 'output the version');
|
|
|
281
333
|
.description('Apply pending migrations to database - down method call')
|
|
282
334
|
.action(function(migrationFileName){
|
|
283
335
|
// this will call all the down methods until it gets to the one your looking for. First it needs to validate that there is such a file.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
336
|
+
var executedLocation = process.cwd();
|
|
337
|
+
var migration = new Migration();
|
|
338
|
+
try{
|
|
339
|
+
// Accept either a bare filename or a path; normalize to basename
|
|
340
|
+
var targetName = path.basename(migrationFileName);
|
|
341
|
+
|
|
342
|
+
// Locate the target migration file anywhere under the current folder
|
|
343
|
+
var searchTarget = `${executedLocation}/**/${targetName}`;
|
|
344
|
+
var targetMatches = globSearch.sync(searchTarget, executedLocation);
|
|
345
|
+
if(!(targetMatches && targetMatches.length)){
|
|
346
|
+
console.log(`Error - Cannot read or find migration file '${targetName}' in '${executedLocation}'.`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
var targetFilePath = targetMatches[0];
|
|
350
|
+
var migrationFolder = path.dirname(targetFilePath);
|
|
351
|
+
|
|
352
|
+
// Find the context snapshot within the same migrations folder
|
|
353
|
+
var snapshotMatches = globSearch.sync(`${migrationFolder}/**/*_contextSnapShot.json`, migrationFolder);
|
|
354
|
+
var snapshotFile = snapshotMatches && snapshotMatches[0];
|
|
355
|
+
if(!snapshotFile){
|
|
356
|
+
console.log("Error - Cannot read or find Context snapshot in migration folder.");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
287
359
|
|
|
360
|
+
var contextSnapshot;
|
|
361
|
+
try{
|
|
362
|
+
contextSnapshot = require(snapshotFile);
|
|
363
|
+
}catch(_){
|
|
364
|
+
console.log(`Error - Cannot read context snapshot at '${snapshotFile}'.`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Get all migration files in this folder
|
|
369
|
+
var allMigrationFiles = globSearch.sync(`${migrationFolder}/**/*_migration.js`, migrationFolder);
|
|
370
|
+
if(!(allMigrationFiles && allMigrationFiles.length)){
|
|
371
|
+
console.log("Error - Cannot read or find migration file");
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Sort chronologically
|
|
376
|
+
var sorted = allMigrationFiles.slice().sort(function(a, b){
|
|
377
|
+
return __getMigrationTimestamp(a) - __getMigrationTimestamp(b);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Find target index by basename match
|
|
381
|
+
var targetIndex = sorted.findIndex(function(f){ return path.basename(f) === targetName; });
|
|
382
|
+
if(targetIndex === -1){
|
|
383
|
+
console.log(`Error - Target migration '${targetName}' not found.`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Prepare context and table object
|
|
388
|
+
let ContextCtor;
|
|
389
|
+
try{
|
|
390
|
+
ContextCtor = require(contextSnapshot.contextLocation);
|
|
391
|
+
}catch(_){
|
|
392
|
+
console.log(`Error - Cannot load Context file at '${contextSnapshot.contextLocation}'.`);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
var contextInstance;
|
|
396
|
+
try{
|
|
397
|
+
contextInstance = new ContextCtor();
|
|
398
|
+
}catch(_){
|
|
399
|
+
console.log(`Error - Failed to construct Context from '${contextSnapshot.contextLocation}'.`);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
403
|
+
var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
|
|
404
|
+
|
|
405
|
+
// Roll back (down) all migrations newer than the target (i.e., strictly after targetIndex)
|
|
406
|
+
for (var i = sorted.length - 1; i > targetIndex; i--) {
|
|
407
|
+
var migFile = sorted[i];
|
|
408
|
+
var MigCtor = require(migFile);
|
|
409
|
+
var migInstance = new MigCtor(ContextCtor);
|
|
410
|
+
if(typeof migInstance.down === 'function'){
|
|
411
|
+
migInstance.down(tableObj);
|
|
412
|
+
} else {
|
|
413
|
+
console.log(`Warning - Migration '${path.basename(migFile)}' has no down method; skipping.`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Update snapshot
|
|
418
|
+
var snap = {
|
|
419
|
+
file : contextSnapshot.contextLocation,
|
|
420
|
+
executedLocation : executedLocation,
|
|
421
|
+
context : contextInstance,
|
|
422
|
+
contextEntities : cleanEntities,
|
|
423
|
+
contextFileName: path.basename(snapshotFile).replace('_contextSnapShot.json','')
|
|
424
|
+
}
|
|
425
|
+
migration.createSnapShot(snap);
|
|
426
|
+
console.log("database updated");
|
|
427
|
+
|
|
428
|
+
}catch (e){
|
|
429
|
+
console.log("Error - Cannot read or find file ", e);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
288
432
|
|
|
289
433
|
|
|
290
434
|
program.parse(process.argv);
|
|
@@ -8,10 +8,17 @@ class migrationMySQLQuery {
|
|
|
8
8
|
var columnList = [];
|
|
9
9
|
for (var key in table) {
|
|
10
10
|
if(typeof table[key] === "object"){
|
|
11
|
-
|
|
11
|
+
var col = table[key];
|
|
12
|
+
// Skip relationship-only fields
|
|
13
|
+
if(col.type === 'hasOne' || col.type === 'hasMany' || col.type === 'hasManyThrough'){
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
// Map belongsTo to its foreignKey name
|
|
17
|
+
var name = (col.relationshipType === 'belongsTo' && col.foreignKey) ? col.foreignKey : col.name;
|
|
18
|
+
columnList.push(name);
|
|
12
19
|
}
|
|
13
20
|
}
|
|
14
|
-
return columnList.join(',')
|
|
21
|
+
return columnList.join(',');
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
#columnMapping(table){
|
|
@@ -8,10 +8,17 @@ class migrationSQLiteQuery {
|
|
|
8
8
|
var columnList = [];
|
|
9
9
|
for (var key in table) {
|
|
10
10
|
if(typeof table[key] === "object"){
|
|
11
|
-
|
|
11
|
+
var col = table[key];
|
|
12
|
+
// Skip relationship-only fields
|
|
13
|
+
if(col.type === 'hasOne' || col.type === 'hasMany' || col.type === 'hasManyThrough'){
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
// Map belongsTo to its foreignKey name
|
|
17
|
+
var name = (col.relationshipType === 'belongsTo' && col.foreignKey) ? col.foreignKey : col.name;
|
|
18
|
+
columnList.push(name);
|
|
12
19
|
}
|
|
13
20
|
}
|
|
14
|
-
return columnList.join(',')
|
|
21
|
+
return columnList.join(',');
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
#columnMapping(table){
|
|
@@ -134,7 +141,12 @@ class migrationSQLiteQuery {
|
|
|
134
141
|
var queryVar = "";
|
|
135
142
|
for (var key in table) {
|
|
136
143
|
if(typeof table[key] === "object"){
|
|
137
|
-
|
|
144
|
+
var col = table[key];
|
|
145
|
+
// Skip relationship-only fields
|
|
146
|
+
if(col.type === 'hasOne' || col.type === 'hasMany' || col.type === 'hasManyThrough'){
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
queryVar += `${this.#columnMapping(col)}, `;
|
|
138
150
|
}
|
|
139
151
|
}
|
|
140
152
|
|
package/Migrations/migrations.js
CHANGED
|
@@ -167,7 +167,10 @@ class Migrations{
|
|
|
167
167
|
findContext(executedLocation, contextFileName){
|
|
168
168
|
var search = `${executedLocation}/**/*${contextFileName}.js`
|
|
169
169
|
var files = globSearch.sync(search, executedLocation);
|
|
170
|
-
var file = files[0];
|
|
170
|
+
var file = files && files[0];
|
|
171
|
+
if(!file){
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
171
174
|
var context = require(file);
|
|
172
175
|
return {
|
|
173
176
|
context : context,
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"app-root-path": "^3.1.0",
|
|
10
10
|
"better-sqlite3": "^12.4.1"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.2.
|
|
12
|
+
"version": "0.2.7",
|
|
13
13
|
"description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
|
|
14
14
|
"homepage": "https://github.com/Tailor/MasterRecord#readme",
|
|
15
15
|
"repository": {
|