@nmakarov/cli-toolkit 0.61.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2452,8 +2452,13 @@ var FileDatabase = class _FileDatabase {
2452
2452
  this.currentVersionFolder = await ensurePath(this.getDestinationPath(), version);
2453
2453
  }
2454
2454
  /**
2455
- * Create a new version folder with comprehensive timestamp logic
2456
- * Only works in versioned mode
2455
+ * Create a new version folder named with the current UTC second.
2456
+ * Only works in versioned mode.
2457
+ *
2458
+ * Clash protection: if that name already exists (two writers in the same
2459
+ * second, or wall clock behind the latest version), advance +1s until free.
2460
+ * Do NOT always derive from max(existing)+1s — that made successive harvests
2461
+ * hours apart still land one second apart on disk.
2457
2462
  */
2458
2463
  async makeNewVersion() {
2459
2464
  if (!this.versioned) {
@@ -2461,19 +2466,22 @@ var FileDatabase = class _FileDatabase {
2461
2466
  }
2462
2467
  this.metadata = this.getDefaultMetadata();
2463
2468
  const existingVersions = await this.getVersions();
2464
- let versionName;
2469
+ const existingSet = new Set(existingVersions);
2470
+ let candidateMs = Date.now();
2465
2471
  if (existingVersions.length > 0) {
2466
- const maxTimestamp = existingVersions.reduce((max, version) => {
2467
- const versionDate = new Date(version.replace("Z", ""));
2468
- const maxDate2 = new Date(max.replace("Z", ""));
2469
- return versionDate > maxDate2 ? version : max;
2470
- });
2471
- const maxDate = new Date(maxTimestamp.replace("Z", ""));
2472
- const nextDate = new Date(maxDate.getTime() + 1e3);
2473
- versionName = nextDate.toISOString().split(".")[0] + "Z";
2474
- } else {
2475
- const now = /* @__PURE__ */ new Date();
2476
- versionName = now.toISOString().split(".")[0] + "Z";
2472
+ let maxMs = 0;
2473
+ for (const version of existingVersions) {
2474
+ const ms = new Date(version).getTime();
2475
+ if (Number.isFinite(ms) && ms > maxMs) maxMs = ms;
2476
+ }
2477
+ if (candidateMs <= maxMs) {
2478
+ candidateMs = maxMs + 1e3;
2479
+ }
2480
+ }
2481
+ let versionName = new Date(candidateMs).toISOString().split(".")[0] + "Z";
2482
+ while (existingSet.has(versionName)) {
2483
+ candidateMs += 1e3;
2484
+ versionName = new Date(candidateMs).toISOString().split(".")[0] + "Z";
2477
2485
  }
2478
2486
  await this.setCurrentVersion(versionName);
2479
2487
  this.currentFileNumber = 0;
@@ -3503,13 +3511,15 @@ var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
3503
3511
  "57P02",
3504
3512
  "57P03"
3505
3513
  ]);
3506
- var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET|Timeout acquiring a connection/i;
3514
+ var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET/i;
3507
3515
  var Db = class _Db {
3508
3516
  static async init(context, options = {}) {
3509
3517
  const buildConfig = async () => {
3510
3518
  const defs = {
3511
3519
  dbName: "string",
3512
- dbProfile: "boolean default false"
3520
+ dbProfile: "boolean default false",
3521
+ /** Cap knex pool size (default 10). Size ≈ expected concurrency + headroom. */
3522
+ dbPoolMax: "number"
3513
3523
  };
3514
3524
  const discovered = context?.params?.getAllForModule?.("db", defs) ?? {};
3515
3525
  const merged = { ...discovered, ...options };
@@ -3568,12 +3578,23 @@ var Db = class _Db {
3568
3578
  context?.args?.env,
3569
3579
  merged.name
3570
3580
  );
3581
+ const poolMaxRaw = merged.dbPoolMax ?? merged.pool?.max;
3582
+ const poolMax = Number(poolMaxRaw);
3583
+ const pool = {
3584
+ ...KNEX_DEFAULTS.pool,
3585
+ ...merged.pool && typeof merged.pool === "object" ? merged.pool : {}
3586
+ };
3587
+ if (Number.isFinite(poolMax) && poolMax >= 1) {
3588
+ pool.max = Math.floor(poolMax);
3589
+ }
3571
3590
  return {
3572
3591
  ...KNEX_DEFAULTS,
3573
3592
  connectionString: dbConnectionString,
3574
3593
  name: displayName,
3575
3594
  profile: !!dbProfile,
3576
- logger: context.logger
3595
+ logger: context.logger,
3596
+ pool,
3597
+ ...merged.acquireConnectionTimeout != null ? { acquireConnectionTimeout: merged.acquireConnectionTimeout } : {}
3577
3598
  };
3578
3599
  };
3579
3600
  const config2 = context?.params?.runWithModuleAsync ? await context.params.runWithModuleAsync("db", buildConfig) : await buildConfig();
@@ -5026,6 +5047,9 @@ var Logger = class _Logger {
5026
5047
  const message = this.inspectChunks([operation, ...chunks]);
5027
5048
  this.out({ level: "response", message });
5028
5049
  }
5050
+ /**
5051
+ * @returns {boolean} true when the progress line was emitted (not throttled)
5052
+ */
5029
5053
  progress(message, opts) {
5030
5054
  const { prefix, count, total } = opts;
5031
5055
  const paddedTotal = String(total).length;
@@ -5059,7 +5083,9 @@ var Logger = class _Logger {
5059
5083
  if (this.options.progressThrottle && prefix) {
5060
5084
  this.lastProgressTimes[prefix] = Date.now();
5061
5085
  }
5086
+ return true;
5062
5087
  }
5088
+ return false;
5063
5089
  }
5064
5090
  shouldOutputProgress(prefix, count, total) {
5065
5091
  if (!this.options.progressThrottle) {
@@ -6899,6 +6925,34 @@ async function updateTaskProgress(context, tasksTable, taskId, progress) {
6899
6925
  progress: typeof progress === "string" ? progress : JSON.stringify(progress)
6900
6926
  });
6901
6927
  }
6928
+ function createTaskProgressReporter(context, tasksTable, taskId) {
6929
+ let pump = null;
6930
+ let pending = void 0;
6931
+ let hasPending = false;
6932
+ return (progress) => {
6933
+ pending = progress;
6934
+ hasPending = true;
6935
+ if (pump) return pump;
6936
+ pump = (async () => {
6937
+ try {
6938
+ while (hasPending) {
6939
+ hasPending = false;
6940
+ const value = pending;
6941
+ try {
6942
+ await updateTaskProgress(context, tasksTable, taskId, value);
6943
+ } catch (err) {
6944
+ context.logger?.warn?.(
6945
+ `[tasks] progress update failed for ${taskId}: ${err?.message ?? err}`
6946
+ );
6947
+ }
6948
+ }
6949
+ } finally {
6950
+ pump = null;
6951
+ }
6952
+ })();
6953
+ return pump;
6954
+ };
6955
+ }
6902
6956
 
6903
6957
  // src/tasks/servicesRegistry.js
6904
6958
  function getDb2(context) {
@@ -8750,7 +8804,8 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
8750
8804
  try {
8751
8805
  taskInstance = new TaskClass(context, row);
8752
8806
  runningTaskInstances.set(row.id, taskInstance);
8753
- const runResult = await taskInstance.run((progress) => updateTaskProgress(context, tasksTable, row.id, progress));
8807
+ const reportProgress = createTaskProgressReporter(context, tasksTable, row.id);
8808
+ const runResult = await taskInstance.run(reportProgress);
8754
8809
  success = !!runResult?.success;
8755
8810
  results = runResult?.results ?? null;
8756
8811
  } catch (error) {
@@ -9297,6 +9352,7 @@ export {
9297
9352
  controlLaneTaskNames,
9298
9353
  convertPattern,
9299
9354
  createRelease,
9355
+ createTaskProgressReporter,
9300
9356
  defaultFileSynopsisFunction,
9301
9357
  defaultTasksRegistry,
9302
9358
  defaultVersionSynopsisFunction,