@nmakarov/cli-toolkit 0.39.0 → 0.43.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.cjs CHANGED
@@ -2534,6 +2534,9 @@ var FileDatabase = class _FileDatabase {
2534
2534
  currentRecord = 0;
2535
2535
  hasReadFirstPage = false;
2536
2536
  lastFileData = null;
2537
+ /** Cache of the last JSON file parsed during paginated reads (avoid re-parse per page). */
2538
+ readFileCache = null;
2539
+ // { filePath: string, data: any[] } | null
2537
2540
  metadata;
2538
2541
  // Synopsis calculation functions
2539
2542
  fileSynopsisFunction = null;
@@ -3300,7 +3303,6 @@ var FileDatabase = class _FileDatabase {
3300
3303
  let effectivePageSize;
3301
3304
  if (nextPage && this.hasReadFirstPage) {
3302
3305
  effectivePageSize = pageSize || this.pageSize;
3303
- this.currentRecord += effectivePageSize;
3304
3306
  } else if (!nextPage) {
3305
3307
  effectivePageSize = pageSize !== void 0 ? pageSize : this.metadata.totalRecords;
3306
3308
  this.currentRecord = 0;
@@ -3329,8 +3331,14 @@ var FileDatabase = class _FileDatabase {
3329
3331
  const file = this.metadata.files[i];
3330
3332
  const filePath = import_path4.default.join(this.getDestinationPath(this.currentVersion || void 0), file.fileName);
3331
3333
  try {
3332
- const rawData = await import_fs4.default.promises.readFile(filePath, "utf8");
3333
- const fileData = deserializeData(rawData, this.metadata.dataType);
3334
+ let fileData;
3335
+ if (this.readFileCache?.filePath === filePath && Array.isArray(this.readFileCache.data)) {
3336
+ fileData = this.readFileCache.data;
3337
+ } else {
3338
+ const rawData = await import_fs4.default.promises.readFile(filePath, "utf8");
3339
+ fileData = deserializeData(rawData, this.metadata.dataType);
3340
+ this.readFileCache = { filePath, data: fileData };
3341
+ }
3334
3342
  let startIndex = 0;
3335
3343
  if (i === currentFileIndex) {
3336
3344
  startIndex = this.currentRecord - cumulativeRecords;
@@ -3344,6 +3352,7 @@ var FileDatabase = class _FileDatabase {
3344
3352
  throw new FileDatabaseError(`Failed to read file ${file.fileName}: ${error.message}`);
3345
3353
  }
3346
3354
  }
3355
+ this.currentRecord += recordsRead;
3347
3356
  if (result.length > 0) {
3348
3357
  if (nextPage || pageSize !== void 0 && pageSize < this.metadata.totalRecords) {
3349
3358
  this.hasReadFirstPage = true;
@@ -3357,6 +3366,7 @@ var FileDatabase = class _FileDatabase {
3357
3366
  setStartRecord(startRecord) {
3358
3367
  this.currentRecord = startRecord - 1;
3359
3368
  this.hasReadFirstPage = false;
3369
+ this.readFileCache = null;
3360
3370
  }
3361
3371
  /**
3362
3372
  * Reset read pagination state
@@ -3364,6 +3374,7 @@ var FileDatabase = class _FileDatabase {
3364
3374
  resetPagination() {
3365
3375
  this.currentRecord = 0;
3366
3376
  this.hasReadFirstPage = false;
3377
+ this.readFileCache = null;
3367
3378
  }
3368
3379
  /**
3369
3380
  * List filenames in the table directory.
@@ -4109,6 +4120,41 @@ var Db = class _Db {
4109
4120
  }
4110
4121
  return stack.pop();
4111
4122
  };
4123
+ const isTimeoutError = (error) => error?.name === "KnexTimeoutError" || /query timeout|timeout exceeded/i.test(String(error?.message ?? ""));
4124
+ const logQueryEntry = (query, start, { error } = {}) => {
4125
+ let executionTimeMs;
4126
+ if (start) {
4127
+ const [seconds, nanoseconds] = process.hrtime(start);
4128
+ executionTimeMs = (seconds * 1e3 + nanoseconds / 1e6).toFixed(2);
4129
+ } else if (error?.timeout != null) {
4130
+ executionTimeMs = Number(error.timeout).toFixed(2);
4131
+ } else {
4132
+ executionTimeMs = "?";
4133
+ }
4134
+ let status = "ok";
4135
+ if (error) {
4136
+ status = isTimeoutError(error) ? "timeout" : "error";
4137
+ }
4138
+ const logEntry = {
4139
+ sql: query?.sql,
4140
+ bindings: query?.bindings || [],
4141
+ executionTimeMs,
4142
+ status
4143
+ };
4144
+ if (error) {
4145
+ logEntry.error = error.message ?? String(error);
4146
+ }
4147
+ this.queriesLog.push(logEntry);
4148
+ const suffix = error ? ` | ${status}: ${logEntry.error}` : "";
4149
+ this.logger.debug?.(
4150
+ `[Db] Query: ${query?.sql} | Duration: ${executionTimeMs}ms${suffix}`
4151
+ );
4152
+ };
4153
+ const handleQueryError = (error, query) => {
4154
+ const start = popStart(query);
4155
+ logQueryEntry(query, start, { error });
4156
+ this.logger.error?.(`[Db] Query failed: ${query?.sql}`, error);
4157
+ };
4112
4158
  this.knexInstance.on("query", (query) => {
4113
4159
  pushStart(query);
4114
4160
  });
@@ -4117,20 +4163,31 @@ var Db = class _Db {
4117
4163
  if (!start) {
4118
4164
  return;
4119
4165
  }
4120
- const [seconds, nanoseconds] = process.hrtime(start);
4121
- const executionTimeMs = (seconds * 1e3 + nanoseconds / 1e6).toFixed(2);
4122
- const logEntry = {
4123
- sql: query.sql,
4124
- bindings: query.bindings || [],
4125
- executionTimeMs
4126
- };
4127
- this.queriesLog.push(logEntry);
4128
- this.logger.debug?.(`[Db] Query: ${query.sql} | Duration: ${executionTimeMs}ms`);
4166
+ logQueryEntry(query, start);
4129
4167
  });
4130
4168
  this.knexInstance.on("query-error", (error, query) => {
4131
- popStart(query);
4132
- this.logger.error?.(`[Db] Query failed: ${query.sql}`, error);
4169
+ handleQueryError(error, query);
4133
4170
  });
4171
+ const hookBuilder = (builder) => {
4172
+ if (!builder || builder.__dbProfilerHooked) {
4173
+ return;
4174
+ }
4175
+ builder.__dbProfilerHooked = true;
4176
+ builder.on("query", pushStart);
4177
+ builder.on("query-error", handleQueryError);
4178
+ };
4179
+ const client = this.knexInstance.client;
4180
+ if (client && !client.__dbProfilerPatched) {
4181
+ client.__dbProfilerPatched = true;
4182
+ if (typeof client.queryBuilder === "function") {
4183
+ const origQueryBuilder = client.queryBuilder.bind(client);
4184
+ client.queryBuilder = (...args) => {
4185
+ const builder = origQueryBuilder(...args);
4186
+ hookBuilder(builder);
4187
+ return builder;
4188
+ };
4189
+ }
4190
+ }
4134
4191
  }
4135
4192
  getQueryLog() {
4136
4193
  return [...this.queriesLog];