@nmakarov/cli-toolkit 0.40.0 → 0.44.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
@@ -2283,23 +2283,18 @@ function defaultFileSynopsisFunction(fileEntry, data) {
2283
2283
  const timestamps = [];
2284
2284
  const statusCounts = {};
2285
2285
  for (const item of data) {
2286
- let ts = null;
2287
- let status = null;
2286
+ if (!item || typeof item !== "object") continue;
2288
2287
  for (const [key, value] of Object.entries(item)) {
2289
2288
  const k = key.toLowerCase();
2290
- if (k === "modificationtimestamp") {
2291
- ts = new Date(value).getTime();
2289
+ if (k.endsWith("modificationtimestamp") && value != null && value !== "") {
2290
+ const ts = new Date(value).getTime();
2291
+ if (!Number.isNaN(ts)) timestamps.push(ts);
2292
2292
  }
2293
- if (k === "standardstatus") {
2294
- status = value;
2293
+ if (k === "standardstatus" && value != null && value !== "") {
2294
+ const status = String(value);
2295
+ statusCounts[status] = (statusCounts[status] || 0) + 1;
2295
2296
  }
2296
2297
  }
2297
- if (ts && !isNaN(ts)) {
2298
- timestamps.push(ts);
2299
- }
2300
- if (status !== null && status !== void 0) {
2301
- statusCounts[status] = (statusCounts[status] || 0) + 1;
2302
- }
2303
2298
  }
2304
2299
  const result = { ...fileEntry };
2305
2300
  if (timestamps.length) {
@@ -2318,27 +2313,38 @@ function defaultVersionSynopsisFunction(metadata) {
2318
2313
  const timestamps = [];
2319
2314
  const statusCounts = {};
2320
2315
  for (const file of metadata.files) {
2321
- if (file.minModificationTimestamp) {
2316
+ if (file?.minModificationTimestamp) {
2322
2317
  const minTs = new Date(file.minModificationTimestamp).getTime();
2323
- if (!isNaN(minTs)) timestamps.push(minTs);
2318
+ if (!Number.isNaN(minTs)) timestamps.push(minTs);
2324
2319
  }
2325
- if (file.maxModificationTimestamp) {
2320
+ if (file?.maxModificationTimestamp) {
2326
2321
  const maxTs = new Date(file.maxModificationTimestamp).getTime();
2327
- if (!isNaN(maxTs)) timestamps.push(maxTs);
2322
+ if (!Number.isNaN(maxTs)) timestamps.push(maxTs);
2328
2323
  }
2329
- if (file.StandardStatuses && typeof file.StandardStatuses === "object") {
2324
+ if (file?.StandardStatuses && typeof file.StandardStatuses === "object") {
2330
2325
  for (const [status, count] of Object.entries(file.StandardStatuses)) {
2331
- statusCounts[status] = (statusCounts[status] || 0) + count;
2326
+ statusCounts[status] = (statusCounts[status] || 0) + Number(count || 0);
2332
2327
  }
2333
2328
  }
2334
2329
  }
2335
2330
  const result = { ...metadata };
2331
+ const synopsis = {
2332
+ ...metadata.synopsis && typeof metadata.synopsis === "object" ? metadata.synopsis : {}
2333
+ };
2336
2334
  if (timestamps.length) {
2337
- result.minModificationTimestamp = new Date(Math.min(...timestamps)).toISOString();
2338
- result.maxModificationTimestamp = new Date(Math.max(...timestamps)).toISOString();
2335
+ const minIso = new Date(Math.min(...timestamps)).toISOString();
2336
+ const maxIso = new Date(Math.max(...timestamps)).toISOString();
2337
+ result.minModificationTimestamp = minIso;
2338
+ result.maxModificationTimestamp = maxIso;
2339
+ synopsis.minModificationTimestamp = minIso;
2340
+ synopsis.maxModificationTimestamp = maxIso;
2339
2341
  }
2340
2342
  if (Object.keys(statusCounts).length > 0) {
2341
2343
  result.StandardStatuses = statusCounts;
2344
+ synopsis.StandardStatuses = statusCounts;
2345
+ }
2346
+ if (Object.keys(synopsis).length) {
2347
+ result.synopsis = synopsis;
2342
2348
  }
2343
2349
  return result;
2344
2350
  }
@@ -2361,6 +2367,9 @@ var FileDatabase = class _FileDatabase {
2361
2367
  currentRecord = 0;
2362
2368
  hasReadFirstPage = false;
2363
2369
  lastFileData = null;
2370
+ /** Cache of the last JSON file parsed during paginated reads (avoid re-parse per page). */
2371
+ readFileCache = null;
2372
+ // { filePath: string, data: any[] } | null
2364
2373
  metadata;
2365
2374
  // Synopsis calculation functions
2366
2375
  fileSynopsisFunction = null;
@@ -3127,7 +3136,6 @@ var FileDatabase = class _FileDatabase {
3127
3136
  let effectivePageSize;
3128
3137
  if (nextPage && this.hasReadFirstPage) {
3129
3138
  effectivePageSize = pageSize || this.pageSize;
3130
- this.currentRecord += effectivePageSize;
3131
3139
  } else if (!nextPage) {
3132
3140
  effectivePageSize = pageSize !== void 0 ? pageSize : this.metadata.totalRecords;
3133
3141
  this.currentRecord = 0;
@@ -3156,8 +3164,14 @@ var FileDatabase = class _FileDatabase {
3156
3164
  const file = this.metadata.files[i];
3157
3165
  const filePath = path3.join(this.getDestinationPath(this.currentVersion || void 0), file.fileName);
3158
3166
  try {
3159
- const rawData = await fs3.promises.readFile(filePath, "utf8");
3160
- const fileData = deserializeData(rawData, this.metadata.dataType);
3167
+ let fileData;
3168
+ if (this.readFileCache?.filePath === filePath && Array.isArray(this.readFileCache.data)) {
3169
+ fileData = this.readFileCache.data;
3170
+ } else {
3171
+ const rawData = await fs3.promises.readFile(filePath, "utf8");
3172
+ fileData = deserializeData(rawData, this.metadata.dataType);
3173
+ this.readFileCache = { filePath, data: fileData };
3174
+ }
3161
3175
  let startIndex = 0;
3162
3176
  if (i === currentFileIndex) {
3163
3177
  startIndex = this.currentRecord - cumulativeRecords;
@@ -3171,6 +3185,7 @@ var FileDatabase = class _FileDatabase {
3171
3185
  throw new FileDatabaseError(`Failed to read file ${file.fileName}: ${error.message}`);
3172
3186
  }
3173
3187
  }
3188
+ this.currentRecord += recordsRead;
3174
3189
  if (result.length > 0) {
3175
3190
  if (nextPage || pageSize !== void 0 && pageSize < this.metadata.totalRecords) {
3176
3191
  this.hasReadFirstPage = true;
@@ -3184,6 +3199,7 @@ var FileDatabase = class _FileDatabase {
3184
3199
  setStartRecord(startRecord) {
3185
3200
  this.currentRecord = startRecord - 1;
3186
3201
  this.hasReadFirstPage = false;
3202
+ this.readFileCache = null;
3187
3203
  }
3188
3204
  /**
3189
3205
  * Reset read pagination state
@@ -3191,6 +3207,7 @@ var FileDatabase = class _FileDatabase {
3191
3207
  resetPagination() {
3192
3208
  this.currentRecord = 0;
3193
3209
  this.hasReadFirstPage = false;
3210
+ this.readFileCache = null;
3194
3211
  }
3195
3212
  /**
3196
3213
  * List filenames in the table directory.
@@ -6310,22 +6327,46 @@ async function ensureTaskTables(context, options = {}) {
6310
6327
  }
6311
6328
  }
6312
6329
  const { actions } = await ensureSchema(db, spec, { dryRun, logger: context.logger });
6330
+ const legacyDrops = await dropLegacyTaskNameColumn(db, [tasksTable, historyTable], {
6331
+ dryRun,
6332
+ log,
6333
+ label
6334
+ });
6335
+ const allActions = [...actions, ...legacyDrops];
6313
6336
  if (dryRun) {
6314
- if (actions.length === 0) {
6337
+ if (allActions.length === 0) {
6315
6338
  log.info?.(`[tasks-schema] dryRun \u2014 ${label}: queue "${queueName}" already up to date; no DDL`);
6316
6339
  } else {
6317
6340
  log.info?.(
6318
- `[tasks-schema] dryRun \u2014 ${label}: would run ${actions.length} statement(s) for queue "${queueName}":`
6341
+ `[tasks-schema] dryRun \u2014 ${label}: would run ${allActions.length} statement(s) for queue "${queueName}":`
6319
6342
  );
6320
- for (const s of actions) log.info?.(` - ${s}`);
6343
+ for (const s of allActions) log.info?.(` - ${s}`);
6321
6344
  }
6322
- } else if (actions.length > 0) {
6345
+ } else if (allActions.length > 0) {
6323
6346
  log.info?.(
6324
- `[tasks-schema] ${label}: applied ${actions.length} DDL statement(s) for queue "${queueName}"`
6347
+ `[tasks-schema] ${label}: applied ${allActions.length} DDL statement(s) for queue "${queueName}"`
6325
6348
  );
6326
6349
  }
6327
6350
  }
6328
6351
  }
6352
+ async function dropLegacyTaskNameColumn(db, tableNames, { dryRun, log, label }) {
6353
+ const actions = [];
6354
+ for (const table of tableNames) {
6355
+ if (!await db.tableExists(table).catch(() => false)) continue;
6356
+ const hasTask = await db.schema.hasColumn(table, "task");
6357
+ const hasName = await db.schema.hasColumn(table, "name");
6358
+ if (!hasTask || !hasName) continue;
6359
+ const sql = `ALTER TABLE "${table}" DROP COLUMN IF EXISTS "task"`;
6360
+ actions.push(sql);
6361
+ if (dryRun) {
6362
+ log?.info?.(`[tasks-schema] dryRun \u2014 ${label}: ${sql}`);
6363
+ } else {
6364
+ await db.raw(sql);
6365
+ log?.info?.(`[tasks-schema] ${label}: dropped legacy column ${table}.task`);
6366
+ }
6367
+ }
6368
+ return actions;
6369
+ }
6329
6370
  async function enqueueTask(context, options) {
6330
6371
  const db = getDb(context);
6331
6372
  const queueName = options.queueName ?? "tasks";
@@ -6342,7 +6383,7 @@ async function enqueueTask(context, options) {
6342
6383
  } else if (schedule) {
6343
6384
  nextRunAt = nextTimeMatch(schedule, /* @__PURE__ */ new Date());
6344
6385
  }
6345
- await db(tasksTable).insert({
6386
+ const row = {
6346
6387
  id,
6347
6388
  name,
6348
6389
  params: toJsonColumn(options.params ?? null),
@@ -6356,9 +6397,21 @@ async function enqueueTask(context, options) {
6356
6397
  server_name: options.serverName ?? null,
6357
6398
  status: "idle",
6358
6399
  status_changed_at: db.fn.now()
6359
- });
6400
+ };
6401
+ if (await tableHasLegacyTaskColumn(db, tasksTable)) {
6402
+ row.task = name;
6403
+ }
6404
+ await db(tasksTable).insert(row);
6360
6405
  return id;
6361
6406
  }
6407
+ var legacyTaskColumnCache = /* @__PURE__ */ new Map();
6408
+ async function tableHasLegacyTaskColumn(db, tableName) {
6409
+ const key = `${db?.config?.name ?? "db"}:${tableName}`;
6410
+ if (!legacyTaskColumnCache.has(key)) {
6411
+ legacyTaskColumnCache.set(key, await db.schema.hasColumn(tableName, "task"));
6412
+ }
6413
+ return legacyTaskColumnCache.get(key);
6414
+ }
6362
6415
  async function updateTaskProgress(context, tasksTable, taskId, progress) {
6363
6416
  const db = getDb(context);
6364
6417
  await db(tasksTable).where({ id: taskId }).update({