@tenonhq/sincronia-core 0.0.71 → 0.0.73

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.
@@ -358,7 +358,7 @@ async function watchAllScopesCommand(args) {
358
358
  }
359
359
  // Start dashboard unless --no-dashboard flag is set
360
360
  if (!args.noDashboard) {
361
- dashboardProcess = startDashboardProcess();
361
+ dashboardProcess = startDashboardProcess(args.port);
362
362
  }
363
363
  // Import and start the multi-scope watcher
364
364
  const { startMultiScopeWatching } = await Promise.resolve().then(() => __importStar(require("./MultiScopeWatcher")));
@@ -383,7 +383,7 @@ async function watchAllScopesCommand(args) {
383
383
  throw error;
384
384
  }
385
385
  }
386
- function startDashboardProcess() {
386
+ function startDashboardProcess(portOverride) {
387
387
  var serverPath;
388
388
  try {
389
389
  serverPath = require.resolve("@tenonhq/sincronia-dashboard/server.js");
@@ -392,11 +392,11 @@ function startDashboardProcess() {
392
392
  Logger_1.logger.warn("Dashboard package not installed. Run: npm install @tenonhq/sincronia-dashboard");
393
393
  return null;
394
394
  }
395
- var port = process.env.DASHBOARD_PORT || "3456";
395
+ var port = portOverride ? String(portOverride) : (process.env.DASHBOARD_PORT || "3456");
396
396
  var server = (0, child_process_1.spawn)("node", [serverPath], {
397
397
  cwd: process.cwd(),
398
398
  stdio: "ignore",
399
- env: { ...process.env },
399
+ env: { ...process.env, DASHBOARD_PORT: port },
400
400
  detached: false,
401
401
  });
402
402
  server.on("error", function (err) {
package/dist/appUtils.js CHANGED
@@ -72,10 +72,7 @@ const processFilesInManRec = async (recPath, rec, forceWrite) => {
72
72
  }, null, 2)
73
73
  };
74
74
  await fileWrite(metadataFile, recPath);
75
- const regularPromises = rec.files.map((file) => {
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) => {
@@ -97,16 +94,12 @@ const processRecsInManTable = async (tablePath, table, forceWrite, onRecordProce
97
94
  .map(recKeyToPath)
98
95
  .map(fUtils.createDirRecursively);
99
96
  await Promise.all(recPathPromises);
100
- const filePromises = recKeys.reduce((acc, recKey) => {
101
- return [
102
- ...acc,
103
- processFilesInManRec(recKeyToPath(recKey), records[recKey], forceWrite).then(function () {
104
- if (onRecordProcessed)
105
- onRecordProcessed();
106
- }),
107
- ];
108
- }, []);
109
- 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
+ });
110
103
  };
111
104
  const countRecordsInTables = (tables) => {
112
105
  return Object.keys(tables).reduce(function (sum, tableName) {
@@ -116,10 +109,9 @@ const countRecordsInTables = (tables) => {
116
109
  const processTablesInManifest = async (tables, forceWrite, sourcePath, onRecordProcessed) => {
117
110
  var basePath = sourcePath || ConfigManager.getSourcePath();
118
111
  const tableNames = Object.keys(tables);
119
- const tablePromises = tableNames.map((tableName) => {
112
+ await (0, genericUtils_1.processBatched)(tableNames, constants_1.CONCURRENCY_TABLES, function (tableName) {
120
113
  return processRecsInManTable(path_1.default.join(basePath, tableName), tables[tableName], forceWrite, onRecordProcessed);
121
114
  });
122
- await Promise.all(tablePromises);
123
115
  };
124
116
  const processManifest = async (manifest, forceWrite = false, sourcePath) => {
125
117
  const tableCount = Object.keys(manifest.tables).length;
@@ -367,7 +359,7 @@ const pushFiles = async (recs) => {
367
359
  Logger_1.logger.info(`Update set routing active: ${activeScopes}`);
368
360
  }
369
361
  const tick = getProgTick(Logger_1.logger.getLogLevel(), recs.length * 2) || (() => { });
370
- const pushResultPromises = recs.map(async (rec) => {
362
+ const results = await (0, genericUtils_1.allSettledBatched)(recs, constants_1.CONCURRENCY_PUSH, async function (rec) {
371
363
  const fieldNames = Object.keys(rec.fields);
372
364
  const firstField = rec.fields[fieldNames[0]];
373
365
  const recSummary = (0, exports.summarizeRecord)(rec.table, firstField.name);
@@ -382,8 +374,7 @@ const pushFiles = async (recs) => {
382
374
  tick();
383
375
  return pushRes;
384
376
  });
385
- const results = await Promise.allSettled(pushResultPromises);
386
- return results.map((result) => {
377
+ return results.map(function (result) {
387
378
  if (result.status === "fulfilled") {
388
379
  return result.value;
389
380
  }
@@ -431,20 +422,18 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
431
422
  const sourcePath = ConfigManager.getSourcePath();
432
423
  const buildPath = ConfigManager.getBuildPath();
433
424
  const fieldNames = Object.keys(fields);
434
- const writePromises = fieldNames.map(async (field) => {
435
- const fieldCtx = fields[field];
436
- const srcFilePath = fieldCtx.filePath;
437
- const relativePath = path_1.default.relative(sourcePath, srcFilePath);
438
- const relPathNoExt = relativePath.split(".").slice(0, -1).join();
439
- const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField);
440
- const relPathNewExt = `${relPathNoExt}.${buildExt}`;
441
- const buildFilePath = path_1.default.join(buildPath, relPathNewExt);
442
- await fUtils.createDirRecursively(path_1.default.dirname(buildFilePath));
443
- const writeResult = await fUtils.writeFileForce(buildFilePath, buildRes.builtRec[fieldCtx.targetField]);
444
- return writeResult;
445
- });
446
425
  try {
447
- await Promise.all(writePromises);
426
+ await (0, genericUtils_1.processBatched)(fieldNames, constants_1.CONCURRENCY_FILES, async function (field) {
427
+ const fieldCtx = fields[field];
428
+ const srcFilePath = fieldCtx.filePath;
429
+ const relativePath = path_1.default.relative(sourcePath, srcFilePath);
430
+ const relPathNoExt = relativePath.split(".").slice(0, -1).join();
431
+ const buildExt = fUtils.getBuildExt(fieldCtx.tableName, fieldCtx.name, fieldCtx.targetField);
432
+ const relPathNewExt = `${relPathNoExt}.${buildExt}`;
433
+ const buildFilePath = path_1.default.join(buildPath, relPathNewExt);
434
+ await fUtils.createDirRecursively(path_1.default.dirname(buildFilePath));
435
+ await fUtils.writeFileForce(buildFilePath, buildRes.builtRec[fieldCtx.targetField]);
436
+ });
448
437
  return { success: true, message: `${recSummary} built successfully` };
449
438
  }
450
439
  catch (e) {
@@ -456,7 +445,7 @@ const writeBuildFile = async (preBuild, buildRes, summary) => {
456
445
  };
457
446
  const buildFiles = async (fileList) => {
458
447
  const tick = getProgTick(Logger_1.logger.getLogLevel(), fileList.length * 2) || (() => { });
459
- const buildPromises = fileList.map(async (rec) => {
448
+ const results = await (0, genericUtils_1.allSettledBatched)(fileList, constants_1.CONCURRENCY_BUILD, async function (rec) {
460
449
  const { fields, table } = rec;
461
450
  const fieldNames = Object.keys(fields);
462
451
  const recSummary = (0, exports.summarizeRecord)(table, fields[fieldNames[0]].name);
@@ -471,8 +460,7 @@ const buildFiles = async (fileList) => {
471
460
  tick();
472
461
  return writeRes;
473
462
  });
474
- const results = await Promise.allSettled(buildPromises);
475
- return results.map((result) => {
463
+ return results.map(function (result) {
476
464
  if (result.status === "fulfilled") {
477
465
  return result.value;
478
466
  }
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) => {
@@ -286,7 +291,19 @@ async function initCommands() {
286
291
  });
287
292
  return cmdArgs;
288
293
  }, updateSetCommands_1.showCurrentScopeCommand)
289
- .command("dashboard", "Launch the Update Set Dashboard web UI", sharedOptions, dashboardCommand_1.dashboardCommand)
294
+ .command("dashboard", "Launch the Update Set Dashboard web UI", (cmdArgs) => {
295
+ cmdArgs.options({
296
+ ...sharedOptions,
297
+ port: {
298
+ alias: "p",
299
+ type: "number",
300
+ describe: "Dashboard port (default: DASHBOARD_PORT env or 3456)",
301
+ },
302
+ });
303
+ return cmdArgs;
304
+ }, async (args) => {
305
+ await (0, dashboardCommand_1.dashboardCommand)(args);
306
+ })
290
307
  .command("schema <subcommand>", "Manage ServiceNow table schemas (subcommands: pull)", (cmdArgs) => {
291
308
  cmdArgs.positional("subcommand", {
292
309
  describe: "Schema subcommand to run",
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;
@@ -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 port = process.env.DASHBOARD_PORT || "3456";
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) => {
@@ -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
- const SKIP_PACKAGES = new Set(["sincronia-core", "sincronia-types"]);
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.
@@ -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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/sincronia-core",
3
- "version": "0.0.71",
3
+ "version": "0.0.73",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",