@tenonhq/sincronia-core 0.0.72 → 0.0.74
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/FileUtils.js +30 -5
- package/dist/allScopesCommands.js +27 -6
- package/dist/appUtils.js +98 -122
- package/dist/commander.js +18 -7
- package/dist/commands.js +142 -143
- package/dist/config.js +32 -0
- package/dist/constants.js +6 -1
- package/dist/createRecordCommand.js +1 -1
- package/dist/dashboardCommand.js +3 -3
- package/dist/deleteRecordCommand.js +1 -1
- package/dist/genericUtils.js +44 -0
- package/dist/initSystem/discovery.js +10 -1
- package/dist/initSystem/orchestrator.js +33 -0
- package/dist/logMessages.js +0 -10
- package/dist/tests/discovery.test.js +11 -0
- package/package.json +1 -1
package/dist/FileUtils.js
CHANGED
|
@@ -138,12 +138,20 @@ const getFileExtension = (filePath) => {
|
|
|
138
138
|
return "";
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
|
-
const getBuildExt = (table, recordName, field) => {
|
|
142
|
-
|
|
141
|
+
const getBuildExt = (table, recordName, field, scope) => {
|
|
142
|
+
var manifest = ConfigManager.getManifest();
|
|
143
143
|
if (!manifest) {
|
|
144
144
|
throw new Error("Failed to retrieve manifest");
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
var resolvedManifest = manifest;
|
|
147
|
+
if (scope && ConfigManager.isMultiScopeManifest(manifest)) {
|
|
148
|
+
var scopeMan = ConfigManager.resolveManifestForScope(manifest, scope);
|
|
149
|
+
if (!scopeMan) {
|
|
150
|
+
throw new Error("Failed to find scope " + scope + " in manifest");
|
|
151
|
+
}
|
|
152
|
+
resolvedManifest = scopeMan;
|
|
153
|
+
}
|
|
154
|
+
const files = resolvedManifest.tables[table].records[recordName].files;
|
|
147
155
|
const file = files.find((f) => f.name === field);
|
|
148
156
|
if (!file) {
|
|
149
157
|
throw new Error("Unable to find file");
|
|
@@ -163,11 +171,28 @@ const getFileContextFromPath = (filePath) => {
|
|
|
163
171
|
.split(path_1.default.sep)
|
|
164
172
|
.slice(-2);
|
|
165
173
|
const targetField = getTargetFieldFromPath(filePath, tableName, ext);
|
|
166
|
-
|
|
174
|
+
var manifest = ConfigManager.getManifest();
|
|
167
175
|
if (!manifest) {
|
|
168
176
|
throw new Error("No manifest has been loaded!");
|
|
169
177
|
}
|
|
170
|
-
|
|
178
|
+
var scope;
|
|
179
|
+
var tables;
|
|
180
|
+
if (ConfigManager.isMultiScopeManifest(manifest)) {
|
|
181
|
+
var detectedScope = ConfigManager.resolveScopeFromPath(filePath);
|
|
182
|
+
if (!detectedScope) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
var scopeMan = ConfigManager.resolveManifestForScope(manifest, detectedScope);
|
|
186
|
+
if (!scopeMan) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
scope = scopeMan.scope || detectedScope;
|
|
190
|
+
tables = scopeMan.tables;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
scope = manifest.scope;
|
|
194
|
+
tables = manifest.tables;
|
|
195
|
+
}
|
|
171
196
|
try {
|
|
172
197
|
const { records } = tables[tableName];
|
|
173
198
|
const record = records[recordName];
|
|
@@ -32,18 +32,23 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.initScopesCommand = initScopesCommand;
|
|
37
40
|
exports.watchAllScopesCommand = watchAllScopesCommand;
|
|
38
41
|
const Logger_1 = require("./Logger");
|
|
39
42
|
const FileLogger_1 = require("./FileLogger");
|
|
40
43
|
const ConfigManager = __importStar(require("./config"));
|
|
44
|
+
const AppUtils = __importStar(require("./appUtils"));
|
|
41
45
|
const wizard_1 = require("./wizard");
|
|
42
46
|
const snClient_1 = require("./snClient");
|
|
43
47
|
const commands_1 = require("./commands");
|
|
44
48
|
const path = __importStar(require("path"));
|
|
45
49
|
const fs = __importStar(require("fs"));
|
|
46
50
|
const child_process_1 = require("child_process");
|
|
51
|
+
const progress_1 = __importDefault(require("progress"));
|
|
47
52
|
const fsp = fs.promises;
|
|
48
53
|
// Custom function to process manifest with specific source directory
|
|
49
54
|
async function processManifestForScope(manifest, sourceDirectory, forceWrite = false) {
|
|
@@ -58,7 +63,20 @@ async function processManifestForScope(manifest, sourceDirectory, forceWrite = f
|
|
|
58
63
|
}
|
|
59
64
|
const tables = manifest.tables || {};
|
|
60
65
|
const tableNames = Object.keys(tables);
|
|
61
|
-
|
|
66
|
+
var totalRecords = 0;
|
|
67
|
+
for (var tn = 0; tn < tableNames.length; tn++) {
|
|
68
|
+
totalRecords += Object.keys(tables[tableNames[tn]].records || {}).length;
|
|
69
|
+
}
|
|
70
|
+
var scopeLabel = sourceDirectory.split(path.sep).pop() || "scope";
|
|
71
|
+
var progBar = null;
|
|
72
|
+
if (Logger_1.logger.getLogLevel() === "info" && totalRecords > 0) {
|
|
73
|
+
progBar = new progress_1.default(":scope :bar :current/:total (:percent)", {
|
|
74
|
+
total: totalRecords,
|
|
75
|
+
width: 40,
|
|
76
|
+
complete: "=",
|
|
77
|
+
incomplete: "-",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
62
80
|
for (const tableName of tableNames) {
|
|
63
81
|
const tableRecords = tables[tableName];
|
|
64
82
|
const tablePath = path.join(sourceDirectory, tableName);
|
|
@@ -102,9 +120,10 @@ async function processManifestForScope(manifest, sourceDirectory, forceWrite = f
|
|
|
102
120
|
Logger_1.logger.error("Failed to write metadata: " + metadataFilePath);
|
|
103
121
|
}
|
|
104
122
|
}
|
|
123
|
+
if (progBar)
|
|
124
|
+
progBar.tick({ scope: scopeLabel });
|
|
105
125
|
}
|
|
106
126
|
}
|
|
107
|
-
Logger_1.logger.info("Files written to " + sourceDirectory);
|
|
108
127
|
}
|
|
109
128
|
catch (error) {
|
|
110
129
|
Logger_1.logger.error("Error processing files for " + sourceDirectory + ": " + error);
|
|
@@ -175,6 +194,8 @@ async function processScope(scopeName, scopeConfig, apiDelay = 0) {
|
|
|
175
194
|
else {
|
|
176
195
|
Logger_1.logger.warn("No _tables whitelist defined — writing ALL tables for " + scopeName);
|
|
177
196
|
}
|
|
197
|
+
// Normalize record keys from sys_id to display name
|
|
198
|
+
AppUtils.normalizeManifestKeys(manifest);
|
|
178
199
|
// Build the missing files structure from the filtered manifest
|
|
179
200
|
var manifestTables = manifest.tables || {};
|
|
180
201
|
var allMissingFiles = {};
|
|
@@ -340,7 +361,7 @@ async function watchAllScopesCommand(args) {
|
|
|
340
361
|
}
|
|
341
362
|
// Start dashboard unless --no-dashboard flag is set
|
|
342
363
|
if (!args.noDashboard) {
|
|
343
|
-
dashboardProcess = startDashboardProcess();
|
|
364
|
+
dashboardProcess = startDashboardProcess(args.port);
|
|
344
365
|
}
|
|
345
366
|
// Import and start the multi-scope watcher
|
|
346
367
|
const { startMultiScopeWatching } = await Promise.resolve().then(() => __importStar(require("./MultiScopeWatcher")));
|
|
@@ -365,7 +386,7 @@ async function watchAllScopesCommand(args) {
|
|
|
365
386
|
throw error;
|
|
366
387
|
}
|
|
367
388
|
}
|
|
368
|
-
function startDashboardProcess() {
|
|
389
|
+
function startDashboardProcess(portOverride) {
|
|
369
390
|
var serverPath;
|
|
370
391
|
try {
|
|
371
392
|
serverPath = require.resolve("@tenonhq/sincronia-dashboard/server.js");
|
|
@@ -374,11 +395,11 @@ function startDashboardProcess() {
|
|
|
374
395
|
Logger_1.logger.warn("Dashboard package not installed. Run: npm install @tenonhq/sincronia-dashboard");
|
|
375
396
|
return null;
|
|
376
397
|
}
|
|
377
|
-
var port = process.env.DASHBOARD_PORT || "3456";
|
|
398
|
+
var port = portOverride ? String(portOverride) : (process.env.DASHBOARD_PORT || "3456");
|
|
378
399
|
var server = (0, child_process_1.spawn)("node", [serverPath], {
|
|
379
400
|
cwd: process.cwd(),
|
|
380
401
|
stdio: "ignore",
|
|
381
|
-
env: { ...process.env },
|
|
402
|
+
env: { ...process.env, DASHBOARD_PORT: port },
|
|
382
403
|
detached: false,
|
|
383
404
|
});
|
|
384
405
|
server.on("error", function (err) {
|
package/dist/appUtils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.
|
|
39
|
+
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = void 0;
|
|
40
40
|
const path_1 = __importDefault(require("path"));
|
|
41
41
|
const fs_1 = __importDefault(require("fs"));
|
|
42
42
|
const progress_1 = __importDefault(require("progress"));
|
|
@@ -72,10 +72,7 @@ const processFilesInManRec = async (recPath, rec, forceWrite) => {
|
|
|
72
72
|
}, null, 2)
|
|
73
73
|
};
|
|
74
74
|
await fileWrite(metadataFile, recPath);
|
|
75
|
-
const
|
|
76
|
-
return fileWrite(file, recPath);
|
|
77
|
-
});
|
|
78
|
-
const writeResults = await Promise.allSettled(regularPromises);
|
|
75
|
+
const writeResults = await (0, genericUtils_1.allSettledBatched)(rec.files, constants_1.CONCURRENCY_FILES, function (file) { return fileWrite(file, recPath); });
|
|
79
76
|
const writeFailures = writeResults.filter((r) => r.status === "rejected");
|
|
80
77
|
if (writeFailures.length > 0) {
|
|
81
78
|
writeFailures.forEach((f) => {
|
|
@@ -89,7 +86,7 @@ const processFilesInManRec = async (recPath, rec, forceWrite) => {
|
|
|
89
86
|
return fileCopy;
|
|
90
87
|
});
|
|
91
88
|
};
|
|
92
|
-
const processRecsInManTable = async (tablePath, table, forceWrite) => {
|
|
89
|
+
const processRecsInManTable = async (tablePath, table, forceWrite, onRecordProcessed) => {
|
|
93
90
|
const { records } = table;
|
|
94
91
|
const recKeys = Object.keys(records);
|
|
95
92
|
const recKeyToPath = (key) => path_1.default.join(tablePath, records[key].name);
|
|
@@ -97,26 +94,62 @@ const processRecsInManTable = async (tablePath, table, forceWrite) => {
|
|
|
97
94
|
.map(recKeyToPath)
|
|
98
95
|
.map(fUtils.createDirRecursively);
|
|
99
96
|
await Promise.all(recPathPromises);
|
|
100
|
-
|
|
101
|
-
return [
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
return Promise.all(filePromises);
|
|
97
|
+
await (0, genericUtils_1.processBatched)(recKeys, constants_1.CONCURRENCY_RECORDS, function (recKey) {
|
|
98
|
+
return processFilesInManRec(recKeyToPath(recKey), records[recKey], forceWrite).then(function () {
|
|
99
|
+
if (onRecordProcessed)
|
|
100
|
+
onRecordProcessed();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
107
103
|
};
|
|
108
|
-
const
|
|
104
|
+
const countRecordsInTables = (tables) => {
|
|
105
|
+
return Object.keys(tables).reduce(function (sum, tableName) {
|
|
106
|
+
return sum + Object.keys(tables[tableName].records).length;
|
|
107
|
+
}, 0);
|
|
108
|
+
};
|
|
109
|
+
const processTablesInManifest = async (tables, forceWrite, sourcePath, onRecordProcessed) => {
|
|
109
110
|
var basePath = sourcePath || ConfigManager.getSourcePath();
|
|
110
111
|
const tableNames = Object.keys(tables);
|
|
111
|
-
|
|
112
|
-
return processRecsInManTable(path_1.default.join(basePath, tableName), tables[tableName], forceWrite);
|
|
112
|
+
await (0, genericUtils_1.processBatched)(tableNames, constants_1.CONCURRENCY_TABLES, function (tableName) {
|
|
113
|
+
return processRecsInManTable(path_1.default.join(basePath, tableName), tables[tableName], forceWrite, onRecordProcessed);
|
|
113
114
|
});
|
|
114
|
-
await Promise.all(tablePromises);
|
|
115
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Re-keys manifest records from sys_id to record.name (display value).
|
|
118
|
+
* Some ServiceNow tables return records keyed by sys_id instead of display name.
|
|
119
|
+
* This ensures consistent naming for directories and manifest lookups.
|
|
120
|
+
*/
|
|
121
|
+
const normalizeManifestKeys = (manifest) => {
|
|
122
|
+
var tables = manifest.tables || {};
|
|
123
|
+
var tableNames = Object.keys(tables);
|
|
124
|
+
for (var i = 0; i < tableNames.length; i++) {
|
|
125
|
+
var tableName = tableNames[i];
|
|
126
|
+
var records = tables[tableName].records || {};
|
|
127
|
+
var recordKeys = Object.keys(records);
|
|
128
|
+
var normalized = {};
|
|
129
|
+
for (var j = 0; j < recordKeys.length; j++) {
|
|
130
|
+
var key = recordKeys[j];
|
|
131
|
+
var record = records[key];
|
|
132
|
+
var displayKey = record.name || key;
|
|
133
|
+
// Handle duplicate display names by appending sys_id suffix
|
|
134
|
+
if (normalized[displayKey]) {
|
|
135
|
+
displayKey = displayKey + " (" + record.sys_id.substring(0, 8) + ")";
|
|
136
|
+
}
|
|
137
|
+
normalized[displayKey] = record;
|
|
138
|
+
}
|
|
139
|
+
tables[tableName].records = normalized;
|
|
140
|
+
}
|
|
141
|
+
return manifest;
|
|
142
|
+
};
|
|
143
|
+
exports.normalizeManifestKeys = normalizeManifestKeys;
|
|
116
144
|
const processManifest = async (manifest, forceWrite = false, sourcePath) => {
|
|
117
145
|
const tableCount = Object.keys(manifest.tables).length;
|
|
118
146
|
FileLogger_1.fileLogger.debug("Processing manifest: " + (manifest.scope || "legacy") + " (" + tableCount + " tables)");
|
|
119
|
-
|
|
147
|
+
var recordCount = countRecordsInTables(manifest.tables);
|
|
148
|
+
var progress = createScopeProgress(Logger_1.logger.getLogLevel(), {
|
|
149
|
+
scope: manifest.scope || "default",
|
|
150
|
+
total: recordCount,
|
|
151
|
+
});
|
|
152
|
+
await processTablesInManifest(manifest.tables, forceWrite, sourcePath, progress.tick);
|
|
120
153
|
if (manifest.scope) {
|
|
121
154
|
await fUtils.writeScopeManifest(manifest.scope, manifest);
|
|
122
155
|
}
|
|
@@ -137,20 +170,20 @@ const syncManifest = async (scope) => {
|
|
|
137
170
|
const config = ConfigManager.getConfig();
|
|
138
171
|
// Resolve scope-specific source directory
|
|
139
172
|
var scopeSourcePath = ConfigManager.getSourcePathForScope(scope);
|
|
140
|
-
const newManifest = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config));
|
|
173
|
+
const newManifest = (0, exports.normalizeManifestKeys)(await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config)));
|
|
141
174
|
const refreshTableCount = Object.keys(newManifest.tables).length;
|
|
142
175
|
FileLogger_1.fileLogger.debug("Refreshed manifest for " + scope + ": " + refreshTableCount + " tables");
|
|
143
176
|
await fUtils.writeScopeManifest(scope, newManifest);
|
|
144
177
|
await (0, exports.processMissingFiles)(newManifest, scopeSourcePath);
|
|
145
178
|
// Update the in-memory manifest for this scope
|
|
146
|
-
if (
|
|
179
|
+
if (ConfigManager.isMultiScopeManifest(curManifest)) {
|
|
147
180
|
curManifest[scope] = newManifest;
|
|
148
181
|
ConfigManager.updateManifest(curManifest);
|
|
149
182
|
}
|
|
150
183
|
}
|
|
151
184
|
else {
|
|
152
185
|
// Sync all scopes if manifest has multiple scopes
|
|
153
|
-
if (
|
|
186
|
+
if (ConfigManager.isMultiScopeManifest(curManifest)) {
|
|
154
187
|
// Multiple scopes detected
|
|
155
188
|
for (const scopeName of Object.keys(curManifest)) {
|
|
156
189
|
await (0, exports.syncManifest)(scopeName);
|
|
@@ -245,13 +278,18 @@ const processMissingFiles = async (newManifest, sourcePath) => {
|
|
|
245
278
|
try {
|
|
246
279
|
const missing = await (0, exports.findMissingFiles)(newManifest, sourcePath);
|
|
247
280
|
const missingTableCount = Object.keys(missing).length;
|
|
248
|
-
if (missingTableCount
|
|
249
|
-
|
|
250
|
-
|
|
281
|
+
if (missingTableCount === 0)
|
|
282
|
+
return;
|
|
283
|
+
FileLogger_1.fileLogger.debug("Downloading missing files from " + missingTableCount + " tables");
|
|
251
284
|
const { tableOptions = {} } = ConfigManager.getConfig();
|
|
252
285
|
const client = (0, snClient_1.defaultClient)();
|
|
253
286
|
const filesToProcess = await (0, snClient_1.unwrapSNResponse)(client.getMissingFiles(missing, tableOptions));
|
|
254
|
-
|
|
287
|
+
var recordCount = countRecordsInTables(filesToProcess);
|
|
288
|
+
var progress = createScopeProgress(Logger_1.logger.getLogLevel(), {
|
|
289
|
+
scope: newManifest.scope || "default",
|
|
290
|
+
total: recordCount,
|
|
291
|
+
});
|
|
292
|
+
await processTablesInManifest(filesToProcess, false, sourcePath, progress.tick);
|
|
255
293
|
}
|
|
256
294
|
catch (e) {
|
|
257
295
|
throw e;
|
|
@@ -349,7 +387,7 @@ const pushFiles = async (recs) => {
|
|
|
349
387
|
Logger_1.logger.info(`Update set routing active: ${activeScopes}`);
|
|
350
388
|
}
|
|
351
389
|
const tick = getProgTick(Logger_1.logger.getLogLevel(), recs.length * 2) || (() => { });
|
|
352
|
-
const
|
|
390
|
+
const results = await (0, genericUtils_1.allSettledBatched)(recs, constants_1.CONCURRENCY_PUSH, async function (rec) {
|
|
353
391
|
const fieldNames = Object.keys(rec.fields);
|
|
354
392
|
const firstField = rec.fields[fieldNames[0]];
|
|
355
393
|
const recSummary = (0, exports.summarizeRecord)(rec.table, firstField.name);
|
|
@@ -364,8 +402,7 @@ const pushFiles = async (recs) => {
|
|
|
364
402
|
tick();
|
|
365
403
|
return pushRes;
|
|
366
404
|
});
|
|
367
|
-
|
|
368
|
-
return results.map((result) => {
|
|
405
|
+
return results.map(function (result) {
|
|
369
406
|
if (result.status === "fulfilled") {
|
|
370
407
|
return result.value;
|
|
371
408
|
}
|
|
@@ -375,6 +412,25 @@ const pushFiles = async (recs) => {
|
|
|
375
412
|
exports.pushFiles = pushFiles;
|
|
376
413
|
const summarizeRecord = (table, recDescriptor) => `${table} > ${recDescriptor}`;
|
|
377
414
|
exports.summarizeRecord = summarizeRecord;
|
|
415
|
+
const createScopeProgress = (logLevel, options) => {
|
|
416
|
+
if (logLevel !== "info" || options.total === 0) {
|
|
417
|
+
return { tick: function () { }, setTotal: function () { } };
|
|
418
|
+
}
|
|
419
|
+
var progBar = new progress_1.default(":scope :bar :current/:total (:percent)", {
|
|
420
|
+
total: options.total,
|
|
421
|
+
width: 40,
|
|
422
|
+
complete: "=",
|
|
423
|
+
incomplete: "-",
|
|
424
|
+
});
|
|
425
|
+
return {
|
|
426
|
+
tick: function () {
|
|
427
|
+
progBar.tick({ scope: options.scope });
|
|
428
|
+
},
|
|
429
|
+
setTotal: function (n) {
|
|
430
|
+
progBar.total = n;
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
};
|
|
378
434
|
const getProgTick = (logLevel, total) => {
|
|
379
435
|
if (logLevel === "info") {
|
|
380
436
|
const progBar = new progress_1.default(":bar (:percent)", {
|
|
@@ -399,7 +455,7 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
|
|
|
399
455
|
const srcFilePath = fieldCtx.filePath;
|
|
400
456
|
const relativePath = path_1.default.relative(sourcePath, srcFilePath);
|
|
401
457
|
const relPathNoExt = relativePath.split(".").slice(0, -1).join();
|
|
402
|
-
const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField);
|
|
458
|
+
const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField, fieldCtx.scope);
|
|
403
459
|
const relPathNewExt = `${relPathNoExt}.${buildExt}`;
|
|
404
460
|
const buildFilePath = path_1.default.join(buildPath, relPathNewExt);
|
|
405
461
|
await fUtils.createDirRecursively(path_1.default.dirname(buildFilePath));
|
|
@@ -407,7 +463,17 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
|
|
|
407
463
|
return writeResult;
|
|
408
464
|
});
|
|
409
465
|
try {
|
|
410
|
-
await
|
|
466
|
+
await (0, genericUtils_1.processBatched)(fieldNames, constants_1.CONCURRENCY_FILES, async function (field) {
|
|
467
|
+
const fieldCtx = fields[field];
|
|
468
|
+
const srcFilePath = fieldCtx.filePath;
|
|
469
|
+
const relativePath = path_1.default.relative(sourcePath, srcFilePath);
|
|
470
|
+
const relPathNoExt = relativePath.split(".").slice(0, -1).join();
|
|
471
|
+
const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField);
|
|
472
|
+
const relPathNewExt = `${relPathNoExt}.${buildExt}`;
|
|
473
|
+
const buildFilePath = path_1.default.join(buildPath, relPathNewExt);
|
|
474
|
+
await fUtils.createDirRecursively(path_1.default.dirname(buildFilePath));
|
|
475
|
+
await fUtils.writeFileForce(buildFilePath, buildRes.builtRec[fieldCtx.targetField]);
|
|
476
|
+
});
|
|
411
477
|
return { success: true, message: `${recSummary} built successfully` };
|
|
412
478
|
}
|
|
413
479
|
catch (e) {
|
|
@@ -419,7 +485,7 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
|
|
|
419
485
|
};
|
|
420
486
|
const buildFiles = async (fileList) => {
|
|
421
487
|
const tick = getProgTick(Logger_1.logger.getLogLevel(), fileList.length * 2) || (() => { });
|
|
422
|
-
const
|
|
488
|
+
const results = await (0, genericUtils_1.allSettledBatched)(fileList, constants_1.CONCURRENCY_BUILD, async function (rec) {
|
|
423
489
|
const { fields, table } = rec;
|
|
424
490
|
const fieldNames = Object.keys(fields);
|
|
425
491
|
const recSummary = (0, exports.summarizeRecord)(table, fields[fieldNames[0]].name);
|
|
@@ -434,8 +500,7 @@ const buildFiles = async (fileList) => {
|
|
|
434
500
|
tick();
|
|
435
501
|
return writeRes;
|
|
436
502
|
});
|
|
437
|
-
|
|
438
|
-
return results.map((result) => {
|
|
503
|
+
return results.map(function (result) {
|
|
439
504
|
if (result.status === "fulfilled") {
|
|
440
505
|
return result.value;
|
|
441
506
|
}
|
|
@@ -507,92 +572,3 @@ const createAndAssignUpdateSet = async (updateSetName = "", scope) => {
|
|
|
507
572
|
};
|
|
508
573
|
};
|
|
509
574
|
exports.createAndAssignUpdateSet = createAndAssignUpdateSet;
|
|
510
|
-
const checkScope = async (swap) => {
|
|
511
|
-
try {
|
|
512
|
-
const man = ConfigManager.getManifest();
|
|
513
|
-
if (man) {
|
|
514
|
-
// Detect multi-scope manifest (keys are scope names, no top-level .scope)
|
|
515
|
-
var isMultiScope = typeof man === "object" && !man.scope && !man.tables;
|
|
516
|
-
if (isMultiScope) {
|
|
517
|
-
// Multi-scope: session scope just needs to match any configured scope
|
|
518
|
-
var configuredScopes = Object.keys(man);
|
|
519
|
-
var client = (0, snClient_1.defaultClient)();
|
|
520
|
-
var scopeObj;
|
|
521
|
-
try {
|
|
522
|
-
scopeObj = await (0, snClient_1.unwrapSNResponse)(client.getCurrentScope());
|
|
523
|
-
}
|
|
524
|
-
catch (scopeErr) {
|
|
525
|
-
Logger_1.logger.info("Scope check endpoint unavailable, assuming match for multi-scope project");
|
|
526
|
-
return { match: true, sessionScope: configuredScopes[0], manifestScope: configuredScopes[0] };
|
|
527
|
-
}
|
|
528
|
-
var sessionScope = scopeObj.scope;
|
|
529
|
-
var scopeMatch = configuredScopes.indexOf(sessionScope) !== -1;
|
|
530
|
-
Logger_1.logger.info("Current scope: " + sessionScope + ", Configured scopes: " + configuredScopes.join(", "));
|
|
531
|
-
if (scopeMatch) {
|
|
532
|
-
return { match: true, sessionScope: sessionScope, manifestScope: sessionScope };
|
|
533
|
-
}
|
|
534
|
-
else if (swap && configuredScopes.length > 0) {
|
|
535
|
-
Logger_1.logger.info("Current scope (" + sessionScope + ") not in configured scopes. Swapping to " + configuredScopes[0] + "...\n");
|
|
536
|
-
var swappedScopeObj = await (0, exports.swapScope)(configuredScopes[0]);
|
|
537
|
-
return {
|
|
538
|
-
match: configuredScopes.indexOf(swappedScopeObj.scope) !== -1,
|
|
539
|
-
sessionScope: swappedScopeObj.scope,
|
|
540
|
-
manifestScope: configuredScopes[0],
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
return { match: false, sessionScope: sessionScope, manifestScope: configuredScopes.join(", ") };
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
// Single-scope manifest
|
|
548
|
-
var singleClient = (0, snClient_1.defaultClient)();
|
|
549
|
-
var singleScopeObj;
|
|
550
|
-
try {
|
|
551
|
-
singleScopeObj = await (0, snClient_1.unwrapSNResponse)(singleClient.getCurrentScope());
|
|
552
|
-
}
|
|
553
|
-
catch (scopeErr) {
|
|
554
|
-
// getCurrentScope endpoint may not exist on this instance — skip scope check
|
|
555
|
-
Logger_1.logger.info(`Scope check endpoint unavailable, assuming scope match for: ${man.scope}`);
|
|
556
|
-
return {
|
|
557
|
-
match: true,
|
|
558
|
-
sessionScope: man.scope,
|
|
559
|
-
manifestScope: man.scope,
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
Logger_1.logger.info(`Current scope: ${singleScopeObj.scope}, Manifest scope: ${man.scope}`);
|
|
563
|
-
if (singleScopeObj.scope === man.scope) {
|
|
564
|
-
return {
|
|
565
|
-
match: true,
|
|
566
|
-
sessionScope: singleScopeObj.scope,
|
|
567
|
-
manifestScope: man.scope,
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
else if (swap) {
|
|
571
|
-
Logger_1.logger.info(`Current scope (${singleScopeObj.scope}) does not match manifest scope (${man.scope}). Swapping...\n`);
|
|
572
|
-
var singleSwapped = await (0, exports.swapScope)(man.scope);
|
|
573
|
-
return {
|
|
574
|
-
match: singleSwapped.scope === man.scope,
|
|
575
|
-
sessionScope: singleSwapped.scope,
|
|
576
|
-
manifestScope: man.scope,
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
return {
|
|
581
|
-
match: false,
|
|
582
|
-
sessionScope: singleScopeObj.scope,
|
|
583
|
-
manifestScope: man.scope,
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
//first time case
|
|
588
|
-
return {
|
|
589
|
-
match: true,
|
|
590
|
-
sessionScope: "",
|
|
591
|
-
manifestScope: "",
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
catch (e) {
|
|
595
|
-
throw e;
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
exports.checkScope = checkScope;
|
package/dist/commander.js
CHANGED
|
@@ -30,6 +30,11 @@ async function initCommands() {
|
|
|
30
30
|
default: false,
|
|
31
31
|
describe: "Skip launching the dashboard web UI",
|
|
32
32
|
},
|
|
33
|
+
port: {
|
|
34
|
+
alias: "p",
|
|
35
|
+
type: "number",
|
|
36
|
+
describe: "Dashboard port (default: DASHBOARD_PORT env or 3456)",
|
|
37
|
+
},
|
|
33
38
|
});
|
|
34
39
|
return cmdArgs;
|
|
35
40
|
}, async (args) => {
|
|
@@ -45,12 +50,6 @@ async function initCommands() {
|
|
|
45
50
|
default: "",
|
|
46
51
|
describe: "Specify branch to do git diff against",
|
|
47
52
|
},
|
|
48
|
-
scopeSwap: {
|
|
49
|
-
alias: "ss",
|
|
50
|
-
type: "boolean",
|
|
51
|
-
default: false,
|
|
52
|
-
describe: "Will auto-swap to the correct scope for the files being pushed",
|
|
53
|
-
},
|
|
54
53
|
updateSet: {
|
|
55
54
|
alias: "us",
|
|
56
55
|
type: "string",
|
|
@@ -286,7 +285,19 @@ async function initCommands() {
|
|
|
286
285
|
});
|
|
287
286
|
return cmdArgs;
|
|
288
287
|
}, updateSetCommands_1.showCurrentScopeCommand)
|
|
289
|
-
.command("dashboard", "Launch the Update Set Dashboard web UI",
|
|
288
|
+
.command("dashboard", "Launch the Update Set Dashboard web UI", (cmdArgs) => {
|
|
289
|
+
cmdArgs.options({
|
|
290
|
+
...sharedOptions,
|
|
291
|
+
port: {
|
|
292
|
+
alias: "p",
|
|
293
|
+
type: "number",
|
|
294
|
+
describe: "Dashboard port (default: DASHBOARD_PORT env or 3456)",
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
return cmdArgs;
|
|
298
|
+
}, async (args) => {
|
|
299
|
+
await (0, dashboardCommand_1.dashboardCommand)(args);
|
|
300
|
+
})
|
|
290
301
|
.command("schema <subcommand>", "Manage ServiceNow table schemas (subcommands: pull)", (cmdArgs) => {
|
|
291
302
|
cmdArgs.positional("subcommand", {
|
|
292
303
|
describe: "Schema subcommand to run",
|
package/dist/commands.js
CHANGED
|
@@ -37,7 +37,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.setLogLevel = setLogLevel;
|
|
40
|
-
exports.devCommand = devCommand;
|
|
41
40
|
exports.refreshCommand = refreshCommand;
|
|
42
41
|
exports.pushCommand = pushCommand;
|
|
43
42
|
exports.downloadCommand = downloadCommand;
|
|
@@ -46,7 +45,6 @@ exports.buildCommand = buildCommand;
|
|
|
46
45
|
exports.deployCommand = deployCommand;
|
|
47
46
|
exports.statusCommand = statusCommand;
|
|
48
47
|
const ConfigManager = __importStar(require("./config"));
|
|
49
|
-
const Watcher_1 = require("./Watcher");
|
|
50
48
|
const AppUtils = __importStar(require("./appUtils"));
|
|
51
49
|
const orchestrator_1 = require("./initSystem/orchestrator");
|
|
52
50
|
const Logger_1 = require("./Logger");
|
|
@@ -56,135 +54,108 @@ const snClient_1 = require("./snClient");
|
|
|
56
54
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
57
55
|
const gitUtils_1 = require("./gitUtils");
|
|
58
56
|
const FileUtils_1 = require("./FileUtils");
|
|
59
|
-
|
|
57
|
+
function setLogLevel(args) {
|
|
58
|
+
Logger_1.logger.setLogLevel(args.logLevel);
|
|
59
|
+
}
|
|
60
|
+
async function refreshCommand(args, log = true) {
|
|
61
|
+
setLogLevel(args);
|
|
60
62
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
else {
|
|
68
|
-
await successFunc();
|
|
69
|
-
}
|
|
63
|
+
if (!log)
|
|
64
|
+
setLogLevel({ logLevel: "warn" });
|
|
65
|
+
FileLogger_1.fileLogger.debug("Syncing manifest from instance");
|
|
66
|
+
await AppUtils.syncManifest();
|
|
67
|
+
Logger_1.logger.success("Refresh complete!");
|
|
68
|
+
setLogLevel(args);
|
|
70
69
|
}
|
|
71
70
|
catch (e) {
|
|
72
|
-
|
|
73
|
-
// Throw exception to register this as an error
|
|
74
|
-
process.exit(1);
|
|
71
|
+
throw e;
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
|
-
function
|
|
78
|
-
Logger_1.logger.setLogLevel(args.logLevel);
|
|
79
|
-
}
|
|
80
|
-
async function devCommand(args) {
|
|
74
|
+
async function pushCommand(args) {
|
|
81
75
|
setLogLevel(args);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
76
|
+
try {
|
|
77
|
+
const { updateSet, ci: skipPrompt, target, diff } = args;
|
|
78
|
+
let encodedPaths;
|
|
79
|
+
if (target !== undefined && target !== "")
|
|
80
|
+
encodedPaths = target;
|
|
81
|
+
else
|
|
82
|
+
encodedPaths = await (0, gitUtils_1.gitDiffToEncodedPaths)(diff);
|
|
83
|
+
const fileList = await AppUtils.getAppFileList(encodedPaths);
|
|
84
|
+
const instance = process.env.SN_INSTANCE || "unknown";
|
|
85
|
+
Logger_1.logger.info(fileList.length + " files to push to " + instance);
|
|
86
|
+
if (!skipPrompt) {
|
|
87
|
+
const targetServer = process.env.SN_INSTANCE;
|
|
88
|
+
if (!targetServer) {
|
|
89
|
+
Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const answers = await inquirer_1.default.prompt([
|
|
93
|
+
{
|
|
94
|
+
type: "confirm",
|
|
95
|
+
name: "confirmed",
|
|
96
|
+
message: "Pushing will overwrite code in your instance. Are you sure?",
|
|
97
|
+
default: false,
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
100
|
+
if (!answers["confirmed"])
|
|
101
|
+
return;
|
|
92
102
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
// Handle --clickup flag: resolve ClickUp task into an update set name
|
|
104
|
+
let resolvedUpdateSet = updateSet;
|
|
105
|
+
if (!resolvedUpdateSet && args.clickup) {
|
|
106
|
+
try {
|
|
107
|
+
const { resolveClickUpForPush } = await Promise.resolve().then(() => __importStar(require("./clickupPushHelper")));
|
|
108
|
+
resolvedUpdateSet = await resolveClickUpForPush(args.clickup);
|
|
109
|
+
}
|
|
110
|
+
catch (cuErr) {
|
|
111
|
+
if (cuErr instanceof Error)
|
|
112
|
+
Logger_1.logger.error(cuErr.message);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
105
115
|
}
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
// Extract scope from file list and auto-swap to it
|
|
117
|
+
var pushScope;
|
|
118
|
+
if (fileList.length > 0) {
|
|
119
|
+
var fieldKeys = Object.keys(fileList[0].fields);
|
|
120
|
+
if (fieldKeys.length > 0) {
|
|
121
|
+
pushScope = fileList[0].fields[fieldKeys[0]].scope;
|
|
122
|
+
}
|
|
108
123
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const fileList = await AppUtils.getAppFileList(encodedPaths);
|
|
122
|
-
const instance = process.env.SN_INSTANCE || "unknown";
|
|
123
|
-
Logger_1.logger.info(fileList.length + " files to push to " + instance);
|
|
124
|
+
if (pushScope) {
|
|
125
|
+
try {
|
|
126
|
+
var client = (0, snClient_1.defaultClient)();
|
|
127
|
+
await client.changeScope(pushScope);
|
|
128
|
+
Logger_1.logger.info("Switched to scope: " + pushScope);
|
|
129
|
+
}
|
|
130
|
+
catch (scopeErr) {
|
|
131
|
+
Logger_1.logger.warn("Could not auto-switch scope: " + scopeErr);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Does not create update set if updateSetName is blank
|
|
135
|
+
if (resolvedUpdateSet) {
|
|
124
136
|
if (!skipPrompt) {
|
|
125
|
-
|
|
126
|
-
if (!targetServer) {
|
|
127
|
-
Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const answers = await inquirer_1.default.prompt([
|
|
137
|
+
let answers = await inquirer_1.default.prompt([
|
|
131
138
|
{
|
|
132
139
|
type: "confirm",
|
|
133
140
|
name: "confirmed",
|
|
134
|
-
message: "
|
|
141
|
+
message: `A new Update Set "${resolvedUpdateSet}" will be created for these pushed changes. Do you want to proceed?`,
|
|
135
142
|
default: false,
|
|
136
143
|
},
|
|
137
144
|
]);
|
|
138
|
-
if (!answers["confirmed"])
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
// Handle --clickup flag: resolve ClickUp task into an update set name
|
|
142
|
-
let resolvedUpdateSet = updateSet;
|
|
143
|
-
if (!resolvedUpdateSet && args.clickup) {
|
|
144
|
-
try {
|
|
145
|
-
const { resolveClickUpForPush } = await Promise.resolve().then(() => __importStar(require("./clickupPushHelper")));
|
|
146
|
-
resolvedUpdateSet = await resolveClickUpForPush(args.clickup);
|
|
147
|
-
}
|
|
148
|
-
catch (cuErr) {
|
|
149
|
-
if (cuErr instanceof Error)
|
|
150
|
-
Logger_1.logger.error(cuErr.message);
|
|
151
|
-
process.exit(1);
|
|
145
|
+
if (!answers["confirmed"]) {
|
|
146
|
+
process.exit(0);
|
|
152
147
|
}
|
|
153
148
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (fileList.length > 0) {
|
|
157
|
-
var fieldKeys = Object.keys(fileList[0].fields);
|
|
158
|
-
if (fieldKeys.length > 0) {
|
|
159
|
-
pushScope = fileList[0].fields[fieldKeys[0]].scope;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Does not create update set if updateSetName is blank
|
|
163
|
-
if (resolvedUpdateSet) {
|
|
164
|
-
if (!skipPrompt) {
|
|
165
|
-
let answers = await inquirer_1.default.prompt([
|
|
166
|
-
{
|
|
167
|
-
type: "confirm",
|
|
168
|
-
name: "confirmed",
|
|
169
|
-
message: `A new Update Set "${resolvedUpdateSet}" will be created for these pushed changes. Do you want to proceed?`,
|
|
170
|
-
default: false,
|
|
171
|
-
},
|
|
172
|
-
]);
|
|
173
|
-
if (!answers["confirmed"]) {
|
|
174
|
-
process.exit(0);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const newUpdateSet = await AppUtils.createAndAssignUpdateSet(resolvedUpdateSet, pushScope);
|
|
178
|
-
Logger_1.logger.debug(`New Update Set Created(${newUpdateSet.name}) sys_id:${newUpdateSet.id}`);
|
|
179
|
-
}
|
|
180
|
-
const pushResults = await AppUtils.pushFiles(fileList);
|
|
181
|
-
(0, logMessages_1.logPushResults)(pushResults);
|
|
182
|
-
}
|
|
183
|
-
catch (e) {
|
|
184
|
-
Logger_1.logger.getInternalLogger().error(e);
|
|
185
|
-
process.exit(1);
|
|
149
|
+
const newUpdateSet = await AppUtils.createAndAssignUpdateSet(resolvedUpdateSet, pushScope);
|
|
150
|
+
Logger_1.logger.debug(`New Update Set Created(${newUpdateSet.name}) sys_id:${newUpdateSet.id}`);
|
|
186
151
|
}
|
|
187
|
-
|
|
152
|
+
const pushResults = await AppUtils.pushFiles(fileList);
|
|
153
|
+
(0, logMessages_1.logPushResults)(pushResults);
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
Logger_1.logger.getInternalLogger().error(e);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
188
159
|
}
|
|
189
160
|
async function downloadCommand(args) {
|
|
190
161
|
setLogLevel(args);
|
|
@@ -233,7 +204,6 @@ async function downloadCommand(args) {
|
|
|
233
204
|
});
|
|
234
205
|
Logger_1.logger.info("Received " + tableCount + " tables, " + recordCount + " records");
|
|
235
206
|
FileLogger_1.fileLogger.debug("Download manifest: " + tableCount + " tables, " + recordCount + " records");
|
|
236
|
-
Logger_1.logger.info("Writing files to disk...");
|
|
237
207
|
await AppUtils.processManifest(man, true);
|
|
238
208
|
Logger_1.logger.success("Download complete — " + recordCount + " records written");
|
|
239
209
|
}
|
|
@@ -285,43 +255,72 @@ async function getDeployPaths() {
|
|
|
285
255
|
}
|
|
286
256
|
async function deployCommand(args) {
|
|
287
257
|
setLogLevel(args);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const { confirmed } = await inquirer_1.default.prompt([
|
|
296
|
-
{
|
|
297
|
-
type: "confirm",
|
|
298
|
-
name: "confirmed",
|
|
299
|
-
message: "Deploying will overwrite code in your instance. Are you sure?",
|
|
300
|
-
default: false,
|
|
301
|
-
},
|
|
302
|
-
]);
|
|
303
|
-
if (!confirmed) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
const paths = await getDeployPaths();
|
|
307
|
-
Logger_1.logger.silly(`${paths.length} paths found...`);
|
|
308
|
-
Logger_1.logger.silly(JSON.stringify(paths, null, 2));
|
|
309
|
-
const appFileList = await AppUtils.getAppFileList(paths);
|
|
310
|
-
const pushResults = await AppUtils.pushFiles(appFileList);
|
|
311
|
-
(0, logMessages_1.logPushResults)(pushResults);
|
|
258
|
+
try {
|
|
259
|
+
const targetServer = process.env.SN_INSTANCE || "";
|
|
260
|
+
if (!targetServer) {
|
|
261
|
+
Logger_1.logger.error("No SN_INSTANCE configured. Set it in your .env file.");
|
|
262
|
+
return;
|
|
312
263
|
}
|
|
313
|
-
|
|
314
|
-
|
|
264
|
+
const { confirmed } = await inquirer_1.default.prompt([
|
|
265
|
+
{
|
|
266
|
+
type: "confirm",
|
|
267
|
+
name: "confirmed",
|
|
268
|
+
message: "Deploying will overwrite code in your instance. Are you sure?",
|
|
269
|
+
default: false,
|
|
270
|
+
},
|
|
271
|
+
]);
|
|
272
|
+
if (!confirmed) {
|
|
273
|
+
return;
|
|
315
274
|
}
|
|
316
|
-
|
|
275
|
+
const paths = await getDeployPaths();
|
|
276
|
+
Logger_1.logger.silly(`${paths.length} paths found...`);
|
|
277
|
+
Logger_1.logger.silly(JSON.stringify(paths, null, 2));
|
|
278
|
+
const appFileList = await AppUtils.getAppFileList(paths);
|
|
279
|
+
// Auto-swap to scope detected from files
|
|
280
|
+
if (appFileList.length > 0) {
|
|
281
|
+
var fieldKeys = Object.keys(appFileList[0].fields);
|
|
282
|
+
if (fieldKeys.length > 0) {
|
|
283
|
+
var deployScope = appFileList[0].fields[fieldKeys[0]].scope;
|
|
284
|
+
if (deployScope) {
|
|
285
|
+
try {
|
|
286
|
+
var client = (0, snClient_1.defaultClient)();
|
|
287
|
+
await client.changeScope(deployScope);
|
|
288
|
+
Logger_1.logger.info("Switched to scope: " + deployScope);
|
|
289
|
+
}
|
|
290
|
+
catch (scopeErr) {
|
|
291
|
+
Logger_1.logger.warn("Could not auto-switch scope: " + scopeErr);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const pushResults = await AppUtils.pushFiles(appFileList);
|
|
297
|
+
(0, logMessages_1.logPushResults)(pushResults);
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
throw e;
|
|
301
|
+
}
|
|
317
302
|
}
|
|
318
303
|
async function statusCommand() {
|
|
319
304
|
try {
|
|
320
305
|
const client = (0, snClient_1.defaultClient)();
|
|
306
|
+
var config = ConfigManager.getConfig();
|
|
321
307
|
let scopeObj = await (0, snClient_1.unwrapSNResponse)(client.getCurrentScope());
|
|
322
|
-
Logger_1.logger.info("Instance:
|
|
323
|
-
Logger_1.logger.info("
|
|
324
|
-
Logger_1.logger.info("
|
|
308
|
+
Logger_1.logger.info("Instance: " + (process.env.SN_INSTANCE || "not set"));
|
|
309
|
+
Logger_1.logger.info("User: " + (process.env.SN_USER || "not set"));
|
|
310
|
+
Logger_1.logger.info("Active scope: " + scopeObj.scope);
|
|
311
|
+
if (config.scopes) {
|
|
312
|
+
var scopeNames = Object.keys(config.scopes);
|
|
313
|
+
Logger_1.logger.info("\nConfigured scopes (" + scopeNames.length + "):");
|
|
314
|
+
for (var i = 0; i < scopeNames.length; i++) {
|
|
315
|
+
var scopeName = scopeNames[i];
|
|
316
|
+
var scopeConf = config.scopes[scopeName];
|
|
317
|
+
var srcDir = (typeof scopeConf === "object" && scopeConf.sourceDirectory)
|
|
318
|
+
? scopeConf.sourceDirectory
|
|
319
|
+
: "src/" + scopeName;
|
|
320
|
+
var marker = scopeName === scopeObj.scope ? " (active)" : "";
|
|
321
|
+
Logger_1.logger.info(" " + scopeName + marker + " — " + srcDir);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
325
324
|
}
|
|
326
325
|
catch (e) {
|
|
327
326
|
throw e;
|
package/dist/config.js
CHANGED
|
@@ -54,6 +54,9 @@ exports.getRefresh = getRefresh;
|
|
|
54
54
|
exports.getDefaultConfigFile = getDefaultConfigFile;
|
|
55
55
|
exports.loadScopeManifest = loadScopeManifest;
|
|
56
56
|
exports.updateManifest = updateManifest;
|
|
57
|
+
exports.isMultiScopeManifest = isMultiScopeManifest;
|
|
58
|
+
exports.resolveManifestForScope = resolveManifestForScope;
|
|
59
|
+
exports.resolveScopeFromPath = resolveScopeFromPath;
|
|
57
60
|
exports.isDirectiveKey = isDirectiveKey;
|
|
58
61
|
exports.stripDirectiveKeys = stripDirectiveKeys;
|
|
59
62
|
exports.resolveConfigForScope = resolveConfigForScope;
|
|
@@ -290,6 +293,35 @@ async function loadScopeManifest(scope) {
|
|
|
290
293
|
function updateManifest(man) {
|
|
291
294
|
manifest = man;
|
|
292
295
|
}
|
|
296
|
+
function isMultiScopeManifest(man) {
|
|
297
|
+
return typeof man === "object" && man !== null && !man.scope && !man.tables;
|
|
298
|
+
}
|
|
299
|
+
function resolveManifestForScope(man, scope) {
|
|
300
|
+
if (!isMultiScopeManifest(man)) {
|
|
301
|
+
if (!scope || man.scope === scope) {
|
|
302
|
+
return man;
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
var scopeMan = man[scope];
|
|
307
|
+
if (scopeMan && scopeMan.tables) {
|
|
308
|
+
return scopeMan;
|
|
309
|
+
}
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
function resolveScopeFromPath(filePath) {
|
|
313
|
+
var cfg = getConfig();
|
|
314
|
+
if (!cfg.scopes)
|
|
315
|
+
return undefined;
|
|
316
|
+
var scopeNames = Object.keys(cfg.scopes);
|
|
317
|
+
for (var i = 0; i < scopeNames.length; i++) {
|
|
318
|
+
var scopeSourcePath = getSourcePathForScope(scopeNames[i]);
|
|
319
|
+
if (filePath.indexOf(scopeSourcePath) === 0) {
|
|
320
|
+
return scopeNames[i];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
293
325
|
async function loadConfigPath(pth) {
|
|
294
326
|
if (!pth) {
|
|
295
327
|
pth = process.cwd();
|
package/dist/constants.js
CHANGED
|
@@ -3,8 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.PUSH_RETRY_LIMIT = exports.PUSH_RETRY_WAIT = exports.PATH_DELIMITER = void 0;
|
|
6
|
+
exports.CONCURRENCY_BUILD = exports.CONCURRENCY_PUSH = exports.CONCURRENCY_FILES = exports.CONCURRENCY_RECORDS = exports.CONCURRENCY_TABLES = exports.PUSH_RETRY_LIMIT = exports.PUSH_RETRY_WAIT = exports.PATH_DELIMITER = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
exports.PATH_DELIMITER = `${path_1.default.delimiter}${path_1.default.delimiter}`;
|
|
9
9
|
exports.PUSH_RETRY_WAIT = 3000;
|
|
10
10
|
exports.PUSH_RETRY_LIMIT = 3;
|
|
11
|
+
exports.CONCURRENCY_TABLES = 2;
|
|
12
|
+
exports.CONCURRENCY_RECORDS = 5;
|
|
13
|
+
exports.CONCURRENCY_FILES = 10;
|
|
14
|
+
exports.CONCURRENCY_PUSH = 3;
|
|
15
|
+
exports.CONCURRENCY_BUILD = 5;
|
|
@@ -102,7 +102,7 @@ async function resolveScope(args) {
|
|
|
102
102
|
// Try to get scope from current manifest
|
|
103
103
|
try {
|
|
104
104
|
var manifest = ConfigManager.getManifest();
|
|
105
|
-
if (manifest && manifest.scope) {
|
|
105
|
+
if (manifest && !ConfigManager.isMultiScopeManifest(manifest) && manifest.scope) {
|
|
106
106
|
return manifest.scope;
|
|
107
107
|
}
|
|
108
108
|
}
|
package/dist/dashboardCommand.js
CHANGED
|
@@ -13,16 +13,16 @@ async function dashboardCommand(args) {
|
|
|
13
13
|
catch (e) {
|
|
14
14
|
throw new Error("Dashboard package not installed. Run: npm install @tenonhq/sincronia-dashboard");
|
|
15
15
|
}
|
|
16
|
+
var port = args.port ? String(args.port) : (process.env.DASHBOARD_PORT || "3456");
|
|
16
17
|
Logger_1.logger.info("Starting Update Set Dashboard...");
|
|
17
18
|
const server = (0, child_process_1.spawn)("node", [serverPath], {
|
|
18
19
|
cwd: process.cwd(),
|
|
19
20
|
stdio: "inherit",
|
|
20
|
-
env: { ...process.env },
|
|
21
|
+
env: { ...process.env, DASHBOARD_PORT: port },
|
|
21
22
|
});
|
|
22
23
|
// Open browser after a short delay
|
|
23
24
|
setTimeout(() => {
|
|
24
|
-
const
|
|
25
|
-
const url = `http://localhost:${port}`;
|
|
25
|
+
const url = "http://localhost:" + port;
|
|
26
26
|
(0, child_process_1.spawn)("open", [url]);
|
|
27
27
|
}, 1000);
|
|
28
28
|
server.on("close", (code) => {
|
|
@@ -57,7 +57,7 @@ async function resolveScope(args) {
|
|
|
57
57
|
// Try to get scope from current manifest
|
|
58
58
|
try {
|
|
59
59
|
var manifest = ConfigManager.getManifest();
|
|
60
|
-
if (manifest && manifest.scope) {
|
|
60
|
+
if (manifest && !ConfigManager.isMultiScopeManifest(manifest) && manifest.scope) {
|
|
61
61
|
return manifest.scope;
|
|
62
62
|
}
|
|
63
63
|
}
|
package/dist/genericUtils.js
CHANGED
|
@@ -41,6 +41,8 @@ exports.parseFileNameParams = parseFileNameParams;
|
|
|
41
41
|
exports.getParsedFilesPayload = getParsedFilesPayload;
|
|
42
42
|
exports.wait = wait;
|
|
43
43
|
exports.chunkArr = chunkArr;
|
|
44
|
+
exports.processBatched = processBatched;
|
|
45
|
+
exports.allSettledBatched = allSettledBatched;
|
|
44
46
|
const path_1 = __importDefault(require("path"));
|
|
45
47
|
const ConfigManager = __importStar(require("./config"));
|
|
46
48
|
async function _getConfigFromPath(params) {
|
|
@@ -125,6 +127,48 @@ function chunkArr(arr, chunkSize) {
|
|
|
125
127
|
}
|
|
126
128
|
return chunks;
|
|
127
129
|
}
|
|
130
|
+
async function processBatched(items, concurrency, fn) {
|
|
131
|
+
var results = new Array(items.length);
|
|
132
|
+
var index = 0;
|
|
133
|
+
async function runNext() {
|
|
134
|
+
while (index < items.length) {
|
|
135
|
+
var currentIndex = index;
|
|
136
|
+
index++;
|
|
137
|
+
results[currentIndex] = await fn(items[currentIndex]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
var workers = [];
|
|
141
|
+
var workerCount = Math.min(concurrency, items.length);
|
|
142
|
+
for (var i = 0; i < workerCount; i++) {
|
|
143
|
+
workers.push(runNext());
|
|
144
|
+
}
|
|
145
|
+
await Promise.all(workers);
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
async function allSettledBatched(items, concurrency, fn) {
|
|
149
|
+
var results = new Array(items.length);
|
|
150
|
+
var index = 0;
|
|
151
|
+
async function runNext() {
|
|
152
|
+
while (index < items.length) {
|
|
153
|
+
var currentIndex = index;
|
|
154
|
+
index++;
|
|
155
|
+
try {
|
|
156
|
+
var value = await fn(items[currentIndex]);
|
|
157
|
+
results[currentIndex] = { status: "fulfilled", value: value };
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
results[currentIndex] = { status: "rejected", reason: e };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
var workers = [];
|
|
165
|
+
var workerCount = Math.min(concurrency, items.length);
|
|
166
|
+
for (var i = 0; i < workerCount; i++) {
|
|
167
|
+
workers.push(runNext());
|
|
168
|
+
}
|
|
169
|
+
await Promise.all(workers);
|
|
170
|
+
return results;
|
|
171
|
+
}
|
|
128
172
|
const allSettled = (promises) => {
|
|
129
173
|
return Promise.all(promises.map((prom) => prom
|
|
130
174
|
.then((value) => ({
|
|
@@ -37,7 +37,16 @@ exports.discoverPlugins = discoverPlugins;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const Logger_1 = require("../Logger");
|
|
40
|
-
|
|
40
|
+
// Skip packages that are not init plugins:
|
|
41
|
+
// - core/types: the discovery system itself
|
|
42
|
+
// - dashboard: starts Express server on require (side effect)
|
|
43
|
+
// - schema: library imported directly by schemaCommand, not a plugin
|
|
44
|
+
const SKIP_PACKAGES = new Set([
|
|
45
|
+
"sincronia-core",
|
|
46
|
+
"sincronia-types",
|
|
47
|
+
"sincronia-dashboard",
|
|
48
|
+
"sincronia-schema",
|
|
49
|
+
]);
|
|
41
50
|
const MAX_PARENT_DEPTH = 3;
|
|
42
51
|
/**
|
|
43
52
|
* @description Scans node_modules for @tenonhq/sincronia-* packages that export a sincPlugin.
|
|
@@ -173,6 +173,34 @@ async function runLoginPhase(plugin, context) {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
+
async function runConfigPhase(context) {
|
|
177
|
+
if (context.hasConfig) {
|
|
178
|
+
var answer = await inquirer_1.default.prompt([{
|
|
179
|
+
type: "list",
|
|
180
|
+
name: "configAction",
|
|
181
|
+
message: "Existing config found. Would you like to update it or use the current one?",
|
|
182
|
+
choices: [
|
|
183
|
+
{ name: "Use current config", value: "keep" },
|
|
184
|
+
{ name: "Update config", value: "update" },
|
|
185
|
+
],
|
|
186
|
+
}]);
|
|
187
|
+
if (answer.configAction === "keep") {
|
|
188
|
+
Logger_1.logger.info(chalk_1.default.green(" ✓ Using existing sinc.config.js"));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// TODO: Future config wizard steps:
|
|
193
|
+
// 1. Scopes — multi-select from available scopes
|
|
194
|
+
// 2. Tables — multi-select with search (inquirer-autocomplete)
|
|
195
|
+
// 3. Fields for selected tables
|
|
196
|
+
// 4. Special scope tables
|
|
197
|
+
// 5. Special scope fields for tables
|
|
198
|
+
Logger_1.logger.info("");
|
|
199
|
+
Logger_1.logger.info(chalk_1.default.magenta(" 🎬 Coming soon to a terminal near you!"));
|
|
200
|
+
Logger_1.logger.info(chalk_1.default.dim(" The config wizard is still in development — stay tuned."));
|
|
201
|
+
Logger_1.logger.info(chalk_1.default.dim(" For now, " + (context.hasConfig ? "we'll keep your current config." : "we'll set you up with the defaults.")));
|
|
202
|
+
Logger_1.logger.info("");
|
|
203
|
+
}
|
|
176
204
|
async function runConfigurePhase(plugin, context) {
|
|
177
205
|
const hooks = plugin.configure;
|
|
178
206
|
if (!hooks || hooks.length === 0)
|
|
@@ -239,6 +267,11 @@ async function runInit(options) {
|
|
|
239
267
|
}
|
|
240
268
|
// 5. Save env vars after login
|
|
241
269
|
saveEnvVars(context, selectedPlugins);
|
|
270
|
+
// 5.5 Config phase
|
|
271
|
+
Logger_1.logger.info("");
|
|
272
|
+
Logger_1.logger.info(chalk_1.default.bold(" ── Config " + "─".repeat(29)));
|
|
273
|
+
Logger_1.logger.info("");
|
|
274
|
+
await runConfigPhase(context);
|
|
242
275
|
// 6. Configure phase
|
|
243
276
|
Logger_1.logger.info("");
|
|
244
277
|
Logger_1.logger.info(chalk_1.default.bold(" ── Configure " + "─".repeat(26)));
|
package/dist/logMessages.js
CHANGED
|
@@ -4,21 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.logBuildResults = exports.logPushResults = exports.log = void 0;
|
|
7
|
-
exports.scopeCheckMessage = scopeCheckMessage;
|
|
8
|
-
exports.devModeLog = devModeLog;
|
|
9
7
|
exports.logFilePush = logFilePush;
|
|
10
8
|
exports.logDeploy = logDeploy;
|
|
11
9
|
const Logger_1 = require("./Logger");
|
|
12
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
11
|
exports.log = console.log;
|
|
14
|
-
function scopeCheckMessage(scopeCheck) {
|
|
15
|
-
let sScope = chalk_1.default.blue(scopeCheck.sessionScope);
|
|
16
|
-
let mScope = chalk_1.default.blue(scopeCheck.manifestScope);
|
|
17
|
-
Logger_1.logger.error("Scope mismatch: your session is " + sScope + " but this project targets " + mScope + ". Switch scopes in ServiceNow to continue.");
|
|
18
|
-
}
|
|
19
|
-
function devModeLog() {
|
|
20
|
-
Logger_1.logger.info(`Dev mode started! Watching for changes...[${chalk_1.default.red("Press CTRL-C to Stop")}]\n`);
|
|
21
|
-
}
|
|
22
12
|
function parseError(err) {
|
|
23
13
|
return `${err.name}:
|
|
24
14
|
${err.message}
|
|
@@ -53,4 +53,15 @@ describe("discoverPlugins", function () {
|
|
|
53
53
|
const names = plugins.map(function (p) { return p.name; });
|
|
54
54
|
expect(names).not.toContain("sass-plugin");
|
|
55
55
|
});
|
|
56
|
+
it("skips sincronia-dashboard and sincronia-schema", function () {
|
|
57
|
+
Logger_1.logger.warn.mockClear();
|
|
58
|
+
mockReaddirSync.mockReturnValue([
|
|
59
|
+
"sincronia-dashboard",
|
|
60
|
+
"sincronia-schema",
|
|
61
|
+
]);
|
|
62
|
+
const plugins = (0, discovery_1.discoverPlugins)();
|
|
63
|
+
expect(plugins).toEqual([]);
|
|
64
|
+
// No require() attempted, so no warn from load failure
|
|
65
|
+
expect(Logger_1.logger.warn).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
56
67
|
});
|