retold-data-service 2.0.13 → 2.0.16

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.
Files changed (52) hide show
  1. package/.claude/launch.json +11 -0
  2. package/bin/retold-data-service-clone.js +286 -0
  3. package/package.json +18 -9
  4. package/source/Retold-Data-Service.js +275 -73
  5. package/source/services/Retold-Data-Service-ConnectionManager.js +277 -0
  6. package/source/services/Retold-Data-Service-MeadowEndpoints.js +217 -0
  7. package/source/services/Retold-Data-Service-ModelManager.js +335 -0
  8. package/source/services/data-cloner/DataCloner-Command-Connection.js +138 -0
  9. package/source/services/data-cloner/DataCloner-Command-Headless.js +357 -0
  10. package/source/services/data-cloner/DataCloner-Command-Schema.js +367 -0
  11. package/source/services/data-cloner/DataCloner-Command-Session.js +229 -0
  12. package/source/services/data-cloner/DataCloner-Command-Sync.js +491 -0
  13. package/source/services/data-cloner/DataCloner-Command-WebUI.js +40 -0
  14. package/source/services/data-cloner/DataCloner-ProviderRegistry.js +20 -0
  15. package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +751 -0
  16. package/source/services/data-cloner/data-cloner-web.html +2706 -0
  17. package/source/services/integration-telemetry/IntegrationTelemetry-Command-Dashboard.js +60 -0
  18. package/source/services/integration-telemetry/IntegrationTelemetry-Command-Integrations.js +132 -0
  19. package/source/services/integration-telemetry/IntegrationTelemetry-Command-Runs.js +93 -0
  20. package/source/services/integration-telemetry/IntegrationTelemetry-StorageProvider-Base.js +116 -0
  21. package/source/services/integration-telemetry/IntegrationTelemetry-StorageProvider-Bibliograph.js +495 -0
  22. package/source/services/integration-telemetry/Retold-Data-Service-IntegrationTelemetry.js +224 -0
  23. package/source/services/meadow-integration/MeadowIntegration-Command-CSVCheck.js +85 -0
  24. package/source/services/meadow-integration/MeadowIntegration-Command-CSVTransform.js +180 -0
  25. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionIntersect.js +153 -0
  26. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionPush.js +190 -0
  27. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToArray.js +113 -0
  28. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToCSV.js +211 -0
  29. package/source/services/meadow-integration/MeadowIntegration-Command-EntityFromTabularFolder.js +244 -0
  30. package/source/services/meadow-integration/MeadowIntegration-Command-JSONArrayTransform.js +213 -0
  31. package/source/services/meadow-integration/MeadowIntegration-Command-TSVCheck.js +80 -0
  32. package/source/services/meadow-integration/MeadowIntegration-Command-TSVTransform.js +166 -0
  33. package/source/services/meadow-integration/Retold-Data-Service-MeadowIntegration.js +113 -0
  34. package/source/services/migration-manager/MigrationManager-Command-Connections.js +220 -0
  35. package/source/services/migration-manager/MigrationManager-Command-DiffMigrate.js +169 -0
  36. package/source/services/migration-manager/MigrationManager-Command-Schemas.js +532 -0
  37. package/source/services/migration-manager/MigrationManager-Command-WebUI.js +123 -0
  38. package/source/services/migration-manager/Retold-Data-Service-MigrationManager.js +357 -0
  39. package/source/services/stricture/Retold-Data-Service-Stricture.js +303 -0
  40. package/source/services/stricture/Stricture-Command-Compile.js +39 -0
  41. package/source/services/stricture/Stricture-Command-Generate-AuthorizationChart.js +14 -0
  42. package/source/services/stricture/Stricture-Command-Generate-DictionaryCSV.js +14 -0
  43. package/source/services/stricture/Stricture-Command-Generate-LaTeX.js +14 -0
  44. package/source/services/stricture/Stricture-Command-Generate-Markdown.js +14 -0
  45. package/source/services/stricture/Stricture-Command-Generate-Meadow.js +14 -0
  46. package/source/services/stricture/Stricture-Command-Generate-ModelGraph.js +14 -0
  47. package/source/services/stricture/Stricture-Command-Generate-MySQL.js +14 -0
  48. package/source/services/stricture/Stricture-Command-Generate-MySQLMigrate.js +14 -0
  49. package/source/services/stricture/Stricture-Command-Generate-Pict.js +14 -0
  50. package/source/services/stricture/Stricture-Command-Generate-TestObjectContainers.js +14 -0
  51. package/test/RetoldDataService_tests.js +161 -1
  52. package/debug/data/books.csv +0 -10001
@@ -0,0 +1,532 @@
1
+ /**
2
+ * MigrationManager Command - Schema Management
3
+ *
4
+ * Routes for schema CRUD, compilation, visualization, and file management.
5
+ *
6
+ * GET /api/schemas
7
+ * GET /api/schemas/:name
8
+ * GET /api/schemas/:name/ddl
9
+ * PUT /api/schemas/:name/ddl
10
+ * GET /api/schemas/:name/files
11
+ * GET /api/schemas/:name/file/:filepath
12
+ * PUT /api/schemas/:name/file/:filepath
13
+ * POST /api/schemas/:name/compile
14
+ * GET /api/schemas/:name/visualize
15
+ * GET /api/schemas/:name/meadow-packages
16
+ */
17
+ const libFs = require('fs');
18
+ const libPath = require('path');
19
+
20
+ module.exports = function(pMigrationService, pOratorServiceServer)
21
+ {
22
+ let tmpSchemaLibrary = pMigrationService._schemaLibrary;
23
+ let tmpStrictureAdapter = pMigrationService._strictureAdapter;
24
+ let tmpSchemaVisualizer = pMigrationService._schemaVisualizer;
25
+ let tmpFlowDataBuilder = pMigrationService._flowDataBuilder;
26
+ let tmpPrefix = pMigrationService.routePrefix;
27
+
28
+ // GET /api/schemas — list all schemas with compiled status
29
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas',
30
+ (pRequest, pResponse, fNext) =>
31
+ {
32
+ try
33
+ {
34
+ let tmpNames = tmpSchemaLibrary.listSchemas();
35
+ let tmpSchemas = [];
36
+
37
+ for (let i = 0; i < tmpNames.length; i++)
38
+ {
39
+ let tmpEntry = tmpSchemaLibrary.getSchema(tmpNames[i]);
40
+ tmpSchemas.push(
41
+ {
42
+ Name: tmpEntry.Name,
43
+ IsCompiled: !!tmpEntry.CompiledSchema,
44
+ LastCompiled: tmpEntry.LastCompiled || null,
45
+ TableCount: (tmpEntry.CompiledSchema && tmpEntry.CompiledSchema.Tables)
46
+ ? (Array.isArray(tmpEntry.CompiledSchema.Tables)
47
+ ? tmpEntry.CompiledSchema.Tables.length
48
+ : Object.keys(tmpEntry.CompiledSchema.Tables).length)
49
+ : 0,
50
+ HasSourceFile: !!tmpEntry.SourceFilePath
51
+ });
52
+ }
53
+
54
+ pResponse.send({ Success: true, Schemas: tmpSchemas });
55
+ }
56
+ catch (pError)
57
+ {
58
+ pResponse.send(500, { Success: false, Error: pError.message });
59
+ }
60
+ return fNext();
61
+ });
62
+
63
+ // GET /api/schemas/:name — full schema detail
64
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name',
65
+ (pRequest, pResponse, fNext) =>
66
+ {
67
+ try
68
+ {
69
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
70
+
71
+ if (!tmpEntry)
72
+ {
73
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
74
+ return fNext();
75
+ }
76
+
77
+ pResponse.send(
78
+ {
79
+ Success: true,
80
+ Schema:
81
+ {
82
+ Name: tmpEntry.Name,
83
+ DDL: tmpEntry.DDL,
84
+ CompiledSchema: tmpEntry.CompiledSchema,
85
+ MeadowPackages: tmpEntry.MeadowPackages,
86
+ IsCompiled: !!tmpEntry.CompiledSchema,
87
+ LastCompiled: tmpEntry.LastCompiled || null,
88
+ HasSourceFile: !!tmpEntry.SourceFilePath
89
+ }
90
+ });
91
+ }
92
+ catch (pError)
93
+ {
94
+ pResponse.send(500, { Success: false, Error: pError.message });
95
+ }
96
+ return fNext();
97
+ });
98
+
99
+ // GET /api/schemas/:name/ddl — raw DDL text
100
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/ddl',
101
+ (pRequest, pResponse, fNext) =>
102
+ {
103
+ try
104
+ {
105
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
106
+
107
+ if (!tmpEntry)
108
+ {
109
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
110
+ return fNext();
111
+ }
112
+
113
+ pResponse.send({ Success: true, Name: tmpEntry.Name, DDL: tmpEntry.DDL });
114
+ }
115
+ catch (pError)
116
+ {
117
+ pResponse.send(500, { Success: false, Error: pError.message });
118
+ }
119
+ return fNext();
120
+ });
121
+
122
+ // PUT /api/schemas/:name/ddl — update DDL text
123
+ pOratorServiceServer.put(tmpPrefix + '/api/schemas/:name/ddl',
124
+ (pRequest, pResponse, fNext) =>
125
+ {
126
+ try
127
+ {
128
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
129
+
130
+ if (!tmpEntry)
131
+ {
132
+ // Create a new schema entry if it doesn't exist
133
+ tmpEntry = tmpSchemaLibrary.addSchema(pRequest.params.name, '');
134
+ }
135
+
136
+ let tmpDDL = '';
137
+ if (pRequest.body && typeof (pRequest.body) === 'object' && pRequest.body.DDL !== undefined)
138
+ {
139
+ tmpDDL = pRequest.body.DDL;
140
+ }
141
+ else if (typeof (pRequest.body) === 'string')
142
+ {
143
+ tmpDDL = pRequest.body;
144
+ }
145
+
146
+ tmpEntry.DDL = tmpDDL;
147
+ // Clear compiled state when DDL changes
148
+ tmpEntry.CompiledSchema = null;
149
+ tmpEntry.MeadowPackages = null;
150
+ tmpEntry.LastCompiled = null;
151
+
152
+ // Also write back to the source file if it exists
153
+ if (tmpEntry.SourceFilePath)
154
+ {
155
+ try
156
+ {
157
+ libFs.writeFileSync(tmpEntry.SourceFilePath, tmpDDL, 'utf8');
158
+ }
159
+ catch (pWriteError)
160
+ {
161
+ pMigrationService._MM.log.warn(`Failed to write back to source file [${tmpEntry.SourceFilePath}]: ${pWriteError.message}`);
162
+ }
163
+ }
164
+
165
+ pResponse.send({ Success: true, Name: tmpEntry.Name });
166
+ }
167
+ catch (pError)
168
+ {
169
+ pResponse.send(500, { Success: false, Error: pError.message });
170
+ }
171
+ return fNext();
172
+ });
173
+
174
+ // GET /api/schemas/:name/files — discover all DDL files (main + includes)
175
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/files',
176
+ (pRequest, pResponse, fNext) =>
177
+ {
178
+ try
179
+ {
180
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
181
+
182
+ if (!tmpEntry)
183
+ {
184
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
185
+ return fNext();
186
+ }
187
+
188
+ let tmpModelPath = pMigrationService.modelPath;
189
+
190
+ if (tmpEntry.SourceFilePath && tmpModelPath)
191
+ {
192
+ let tmpFiles = pMigrationService.discoverIncludedFiles(tmpEntry.SourceFilePath, tmpModelPath);
193
+
194
+ pResponse.send(
195
+ {
196
+ Success: true,
197
+ Name: tmpEntry.Name,
198
+ HasSourceFile: true,
199
+ Files: tmpFiles.map(function(pFile)
200
+ {
201
+ let tmpLines = pFile.Content.split('\n');
202
+ let tmpTableCount = 0;
203
+ for (let k = 0; k < tmpLines.length; k++)
204
+ {
205
+ if (tmpLines[k].trimStart().charAt(0) === '!')
206
+ {
207
+ tmpTableCount++;
208
+ }
209
+ }
210
+ return {
211
+ RelativePath: pFile.RelativePath,
212
+ Content: pFile.Content,
213
+ IsMain: pFile.IsMain,
214
+ IncludedBy: pFile.IncludedBy || null,
215
+ Depth: pFile.Depth || 0,
216
+ Bytes: Buffer.byteLength(pFile.Content, 'utf8'),
217
+ LineCount: tmpLines.length,
218
+ TableCount: tmpTableCount
219
+ };
220
+ })
221
+ });
222
+ }
223
+ else
224
+ {
225
+ // Programmatic schema — return a single virtual file entry
226
+ let tmpDDL = tmpEntry.DDL || '';
227
+ let tmpDDLLines = tmpDDL.split('\n');
228
+ let tmpDDLTableCount = 0;
229
+ for (let k = 0; k < tmpDDLLines.length; k++)
230
+ {
231
+ if (tmpDDLLines[k].trimStart().charAt(0) === '!')
232
+ {
233
+ tmpDDLTableCount++;
234
+ }
235
+ }
236
+ pResponse.send(
237
+ {
238
+ Success: true,
239
+ Name: tmpEntry.Name,
240
+ HasSourceFile: false,
241
+ Files:
242
+ [
243
+ {
244
+ RelativePath: tmpEntry.Name + '.ddl',
245
+ Content: tmpDDL,
246
+ IsMain: true,
247
+ IncludedBy: null,
248
+ Depth: 0,
249
+ Bytes: Buffer.byteLength(tmpDDL, 'utf8'),
250
+ LineCount: tmpDDLLines.length,
251
+ TableCount: tmpDDLTableCount
252
+ }
253
+ ]
254
+ });
255
+ }
256
+ }
257
+ catch (pError)
258
+ {
259
+ pResponse.send(500, { Success: false, Error: pError.message });
260
+ }
261
+ return fNext();
262
+ });
263
+
264
+ // GET /api/schemas/:name/file/:filepath — read a specific child file
265
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/file/:filepath',
266
+ (pRequest, pResponse, fNext) =>
267
+ {
268
+ try
269
+ {
270
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
271
+
272
+ if (!tmpEntry)
273
+ {
274
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
275
+ return fNext();
276
+ }
277
+
278
+ if (!tmpEntry.SourceFilePath)
279
+ {
280
+ pResponse.send({ Success: true, Content: tmpEntry.DDL || '' });
281
+ return fNext();
282
+ }
283
+
284
+ let tmpModelPath = pMigrationService.modelPath;
285
+ let tmpRelPath = decodeURIComponent(pRequest.params.filepath);
286
+ let tmpAbsPath = libPath.resolve(tmpModelPath, tmpRelPath);
287
+ let tmpAbsBase = libPath.resolve(tmpModelPath);
288
+
289
+ // Prevent directory traversal
290
+ if (!tmpAbsPath.startsWith(tmpAbsBase))
291
+ {
292
+ pResponse.send(403, { Success: false, Error: 'Path outside model directory.' });
293
+ return fNext();
294
+ }
295
+
296
+ let tmpContent = libFs.readFileSync(tmpAbsPath, 'utf8');
297
+ pResponse.send({ Success: true, RelativePath: tmpRelPath, Content: tmpContent });
298
+ }
299
+ catch (pError)
300
+ {
301
+ pResponse.send(500, { Success: false, Error: pError.message });
302
+ }
303
+ return fNext();
304
+ });
305
+
306
+ // PUT /api/schemas/:name/file/:filepath — write updated content to a specific child file
307
+ pOratorServiceServer.put(tmpPrefix + '/api/schemas/:name/file/:filepath',
308
+ (pRequest, pResponse, fNext) =>
309
+ {
310
+ try
311
+ {
312
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
313
+
314
+ if (!tmpEntry)
315
+ {
316
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
317
+ return fNext();
318
+ }
319
+
320
+ let tmpContent = '';
321
+ if (pRequest.body && typeof (pRequest.body) === 'object' && pRequest.body.Content !== undefined)
322
+ {
323
+ tmpContent = pRequest.body.Content;
324
+ }
325
+ else if (typeof (pRequest.body) === 'string')
326
+ {
327
+ tmpContent = pRequest.body;
328
+ }
329
+
330
+ if (!tmpEntry.SourceFilePath)
331
+ {
332
+ tmpEntry.DDL = tmpContent;
333
+ tmpEntry.CompiledSchema = null;
334
+ tmpEntry.MeadowPackages = null;
335
+ tmpEntry.LastCompiled = null;
336
+ pResponse.send({ Success: true });
337
+ return fNext();
338
+ }
339
+
340
+ let tmpModelPath = pMigrationService.modelPath;
341
+ let tmpRelPath = decodeURIComponent(pRequest.params.filepath);
342
+ let tmpAbsPath = libPath.resolve(tmpModelPath, tmpRelPath);
343
+ let tmpAbsBase = libPath.resolve(tmpModelPath);
344
+
345
+ // Prevent directory traversal
346
+ if (!tmpAbsPath.startsWith(tmpAbsBase))
347
+ {
348
+ pResponse.send(403, { Success: false, Error: 'Path outside model directory.' });
349
+ return fNext();
350
+ }
351
+
352
+ libFs.writeFileSync(tmpAbsPath, tmpContent, 'utf8');
353
+
354
+ // If this is the main file, also update the schema entry DDL
355
+ if (tmpAbsPath === libPath.resolve(tmpEntry.SourceFilePath))
356
+ {
357
+ tmpEntry.DDL = tmpContent;
358
+ }
359
+
360
+ // Clear compiled state since a file changed
361
+ tmpEntry.CompiledSchema = null;
362
+ tmpEntry.MeadowPackages = null;
363
+ tmpEntry.LastCompiled = null;
364
+
365
+ pResponse.send({ Success: true, RelativePath: tmpRelPath });
366
+ }
367
+ catch (pError)
368
+ {
369
+ pResponse.send(500, { Success: false, Error: pError.message });
370
+ }
371
+ return fNext();
372
+ });
373
+
374
+ // POST /api/schemas/:name/compile — compile DDL
375
+ pOratorServiceServer.post(tmpPrefix + '/api/schemas/:name/compile',
376
+ (pRequest, pResponse, fNext) =>
377
+ {
378
+ try
379
+ {
380
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
381
+
382
+ if (!tmpEntry)
383
+ {
384
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
385
+ return fNext();
386
+ }
387
+
388
+ if (!tmpEntry.DDL)
389
+ {
390
+ pResponse.send(400, { Success: false, Error: 'Schema has no DDL text to compile.' });
391
+ return fNext();
392
+ }
393
+
394
+ let tmpCompileHandler = (pError, pCompiledSchema, pMeadowPackages) =>
395
+ {
396
+ if (pError)
397
+ {
398
+ pResponse.send(500, { Success: false, Error: `Compilation failed: ${pError.message || pError}` });
399
+ return fNext();
400
+ }
401
+
402
+ tmpEntry.CompiledSchema = pCompiledSchema;
403
+ tmpEntry.MeadowPackages = pMeadowPackages;
404
+ tmpEntry.LastCompiled = new Date().toJSON();
405
+
406
+ pResponse.send(
407
+ {
408
+ Success: true,
409
+ Name: tmpEntry.Name,
410
+ CompiledSchema: tmpEntry.CompiledSchema,
411
+ MeadowPackages: tmpEntry.MeadowPackages,
412
+ LastCompiled: tmpEntry.LastCompiled
413
+ });
414
+ return fNext();
415
+ };
416
+
417
+ if (tmpEntry.SourceFilePath)
418
+ {
419
+ // Re-read the main file in case it was edited via the file API
420
+ try
421
+ {
422
+ tmpEntry.DDL = libFs.readFileSync(tmpEntry.SourceFilePath, 'utf8');
423
+ }
424
+ catch (pReadError)
425
+ {
426
+ // If we can't re-read, use the existing DDL
427
+ }
428
+
429
+ tmpStrictureAdapter.compileFileAndGenerate(tmpEntry.SourceFilePath, tmpCompileHandler);
430
+ }
431
+ else
432
+ {
433
+ tmpStrictureAdapter.compileAndGenerate(tmpEntry.DDL, tmpCompileHandler);
434
+ }
435
+ }
436
+ catch (pError)
437
+ {
438
+ pResponse.send(500, { Success: false, Error: pError.message });
439
+ return fNext();
440
+ }
441
+ });
442
+
443
+ // GET /api/schemas/:name/visualize — visualization data
444
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/visualize',
445
+ (pRequest, pResponse, fNext) =>
446
+ {
447
+ try
448
+ {
449
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
450
+
451
+ if (!tmpEntry)
452
+ {
453
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
454
+ return fNext();
455
+ }
456
+
457
+ if (!tmpEntry.CompiledSchema)
458
+ {
459
+ pResponse.send(400, { Success: false, Error: 'Schema has not been compiled yet.' });
460
+ return fNext();
461
+ }
462
+
463
+ let tmpTableList = tmpSchemaVisualizer.generateTableList(tmpEntry.CompiledSchema);
464
+ let tmpASCIIDiagram = tmpSchemaVisualizer.generateASCIIDiagram(tmpEntry.CompiledSchema);
465
+ let tmpRelationshipMap = tmpSchemaVisualizer.generateRelationshipMap(tmpEntry.CompiledSchema);
466
+
467
+ let tmpTableDetails = [];
468
+ let tmpTables = Array.isArray(tmpEntry.CompiledSchema.Tables)
469
+ ? tmpEntry.CompiledSchema.Tables
470
+ : Object.values(tmpEntry.CompiledSchema.Tables || {});
471
+
472
+ for (let i = 0; i < tmpTables.length; i++)
473
+ {
474
+ tmpTableDetails.push(tmpSchemaVisualizer.generateTableDetail(tmpTables[i]));
475
+ }
476
+
477
+ let tmpFlowData = tmpFlowDataBuilder.buildFlowData(tmpEntry.CompiledSchema);
478
+
479
+ pResponse.send(
480
+ {
481
+ Success: true,
482
+ Name: tmpEntry.Name,
483
+ TableList: tmpTableList,
484
+ ASCIIDiagram: tmpASCIIDiagram,
485
+ RelationshipMap: tmpRelationshipMap,
486
+ TableDetails: tmpTableDetails,
487
+ FlowData: tmpFlowData
488
+ });
489
+ }
490
+ catch (pError)
491
+ {
492
+ pResponse.send(500, { Success: false, Error: pError.message });
493
+ }
494
+ return fNext();
495
+ });
496
+
497
+ // GET /api/schemas/:name/meadow-packages — Meadow package JSON
498
+ pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/meadow-packages',
499
+ (pRequest, pResponse, fNext) =>
500
+ {
501
+ try
502
+ {
503
+ let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
504
+
505
+ if (!tmpEntry)
506
+ {
507
+ pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
508
+ return fNext();
509
+ }
510
+
511
+ if (!tmpEntry.CompiledSchema)
512
+ {
513
+ pResponse.send(400, { Success: false, Error: 'Schema has not been compiled yet.' });
514
+ return fNext();
515
+ }
516
+
517
+ let tmpPackages = tmpEntry.MeadowPackages || tmpStrictureAdapter.generateMeadowPackages(tmpEntry.CompiledSchema);
518
+
519
+ pResponse.send(
520
+ {
521
+ Success: true,
522
+ Name: tmpEntry.Name,
523
+ MeadowPackages: tmpPackages
524
+ });
525
+ }
526
+ catch (pError)
527
+ {
528
+ pResponse.send(500, { Success: false, Error: pError.message });
529
+ }
530
+ return fNext();
531
+ });
532
+ };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * MigrationManager Command - Web UI
3
+ *
4
+ * Serves the migration manager web UI HTML and static JS library files.
5
+ * Library files are resolved from meadow-migrationmanager's node_modules
6
+ * at runtime, matching the standalone server's approach.
7
+ *
8
+ * When a RoutePrefix is configured (e.g. '/meadow-migrationmanager'),
9
+ * the HTML is patched at serve time to inject window.MIGRATION_MANAGER_BASE
10
+ * and rewrite script src paths so the web UI resolves all resources
11
+ * under the prefix.
12
+ *
13
+ * GET {prefix}/ (index.html)
14
+ * GET {prefix}/lib/codejar.js (CodeJar editor)
15
+ * GET {prefix}/lib/pict.min.js (Pict browser bundle)
16
+ * GET {prefix}/lib/pict-section-flow.min.js (Flow diagram visualization)
17
+ */
18
+ const libFs = require('fs');
19
+ const libPath = require('path');
20
+
21
+ // Resolve paths relative to the meadow-migrationmanager package root
22
+ const _MigrationManagerBase = libPath.dirname(require.resolve('meadow-migrationmanager/package.json'));
23
+
24
+ module.exports = function(pMigrationService, pOratorServiceServer)
25
+ {
26
+ let tmpPrefix = pMigrationService.routePrefix;
27
+
28
+ // GET {prefix}/lib/codejar.js — serve CodeJar as a global (strip ES module export)
29
+ let tmpCodeJarPath = libPath.join(_MigrationManagerBase, 'node_modules', 'codejar', 'dist', 'codejar.js');
30
+ pOratorServiceServer.get(tmpPrefix + '/lib/codejar.js',
31
+ (pRequest, pResponse, fNext) =>
32
+ {
33
+ try
34
+ {
35
+ let tmpSource = libFs.readFileSync(tmpCodeJarPath, 'utf8');
36
+ // Strip the ES module `export ` keyword so CodeJar becomes a global function
37
+ tmpSource = tmpSource.replace('export function CodeJar', 'function CodeJar');
38
+ pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
39
+ pResponse.write(tmpSource);
40
+ pResponse.end();
41
+ }
42
+ catch (pError)
43
+ {
44
+ pResponse.send(500, { Success: false, Error: 'Failed to load CodeJar.' });
45
+ }
46
+ return fNext();
47
+ });
48
+
49
+ // GET {prefix}/lib/pict.min.js — serve Pict browser bundle
50
+ let tmpPictPath = libPath.join(_MigrationManagerBase, 'node_modules', 'pict', 'dist', 'pict.min.js');
51
+ pOratorServiceServer.get(tmpPrefix + '/lib/pict.min.js',
52
+ (pRequest, pResponse, fNext) =>
53
+ {
54
+ try
55
+ {
56
+ let tmpSource = libFs.readFileSync(tmpPictPath, 'utf8');
57
+ pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
58
+ pResponse.write(tmpSource);
59
+ pResponse.end();
60
+ }
61
+ catch (pError)
62
+ {
63
+ pResponse.send(500, { Success: false, Error: 'Failed to load Pict.' });
64
+ }
65
+ return fNext();
66
+ });
67
+
68
+ // GET {prefix}/lib/pict-section-flow.min.js — serve pict-section-flow browser bundle
69
+ let tmpFlowPath = libPath.join(_MigrationManagerBase, 'node_modules', 'pict-section-flow', 'dist', 'pict-section-flow.min.js');
70
+ pOratorServiceServer.get(tmpPrefix + '/lib/pict-section-flow.min.js',
71
+ (pRequest, pResponse, fNext) =>
72
+ {
73
+ try
74
+ {
75
+ let tmpSource = libFs.readFileSync(tmpFlowPath, 'utf8');
76
+ pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
77
+ pResponse.write(tmpSource);
78
+ pResponse.end();
79
+ }
80
+ catch (pError)
81
+ {
82
+ pResponse.send(500, { Success: false, Error: 'Failed to load pict-section-flow.' });
83
+ }
84
+ return fNext();
85
+ });
86
+
87
+ // GET {prefix}/ — serve the web UI HTML
88
+ let tmpHTMLPath = libPath.join(_MigrationManagerBase, 'source', 'web', 'index.html');
89
+
90
+ let fServeHTML = (pRequest, pResponse, fNext) =>
91
+ {
92
+ try
93
+ {
94
+ let tmpHTML = libFs.readFileSync(tmpHTMLPath, 'utf8');
95
+
96
+ // When a route prefix is set, inject the base path
97
+ // so the web UI's api() function and script src tags
98
+ // resolve to the correct prefixed URLs.
99
+ if (tmpPrefix)
100
+ {
101
+ tmpHTML = tmpHTML.replace(/src="\/lib\//g, 'src="' + tmpPrefix + '/lib/');
102
+ tmpHTML = tmpHTML.replace('<script src=', '<script>window.MIGRATION_MANAGER_BASE=\'' + tmpPrefix + '\';</script>\n<script src=');
103
+ }
104
+
105
+ pResponse.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
106
+ pResponse.write(tmpHTML);
107
+ pResponse.end();
108
+ }
109
+ catch (pError)
110
+ {
111
+ pResponse.send(500, { Success: false, Error: 'Failed to load web UI.' });
112
+ }
113
+ return fNext();
114
+ };
115
+
116
+ pOratorServiceServer.get(tmpPrefix + '/', fServeHTML);
117
+
118
+ // Also serve without trailing slash when a prefix is active
119
+ if (tmpPrefix)
120
+ {
121
+ pOratorServiceServer.get(tmpPrefix, fServeHTML);
122
+ }
123
+ };