@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.js CHANGED
@@ -2361,6 +2361,9 @@ var FileDatabase = class _FileDatabase {
2361
2361
  currentRecord = 0;
2362
2362
  hasReadFirstPage = false;
2363
2363
  lastFileData = null;
2364
+ /** Cache of the last JSON file parsed during paginated reads (avoid re-parse per page). */
2365
+ readFileCache = null;
2366
+ // { filePath: string, data: any[] } | null
2364
2367
  metadata;
2365
2368
  // Synopsis calculation functions
2366
2369
  fileSynopsisFunction = null;
@@ -3127,7 +3130,6 @@ var FileDatabase = class _FileDatabase {
3127
3130
  let effectivePageSize;
3128
3131
  if (nextPage && this.hasReadFirstPage) {
3129
3132
  effectivePageSize = pageSize || this.pageSize;
3130
- this.currentRecord += effectivePageSize;
3131
3133
  } else if (!nextPage) {
3132
3134
  effectivePageSize = pageSize !== void 0 ? pageSize : this.metadata.totalRecords;
3133
3135
  this.currentRecord = 0;
@@ -3156,8 +3158,14 @@ var FileDatabase = class _FileDatabase {
3156
3158
  const file = this.metadata.files[i];
3157
3159
  const filePath = path3.join(this.getDestinationPath(this.currentVersion || void 0), file.fileName);
3158
3160
  try {
3159
- const rawData = await fs3.promises.readFile(filePath, "utf8");
3160
- const fileData = deserializeData(rawData, this.metadata.dataType);
3161
+ let fileData;
3162
+ if (this.readFileCache?.filePath === filePath && Array.isArray(this.readFileCache.data)) {
3163
+ fileData = this.readFileCache.data;
3164
+ } else {
3165
+ const rawData = await fs3.promises.readFile(filePath, "utf8");
3166
+ fileData = deserializeData(rawData, this.metadata.dataType);
3167
+ this.readFileCache = { filePath, data: fileData };
3168
+ }
3161
3169
  let startIndex = 0;
3162
3170
  if (i === currentFileIndex) {
3163
3171
  startIndex = this.currentRecord - cumulativeRecords;
@@ -3171,6 +3179,7 @@ var FileDatabase = class _FileDatabase {
3171
3179
  throw new FileDatabaseError(`Failed to read file ${file.fileName}: ${error.message}`);
3172
3180
  }
3173
3181
  }
3182
+ this.currentRecord += recordsRead;
3174
3183
  if (result.length > 0) {
3175
3184
  if (nextPage || pageSize !== void 0 && pageSize < this.metadata.totalRecords) {
3176
3185
  this.hasReadFirstPage = true;
@@ -3184,6 +3193,7 @@ var FileDatabase = class _FileDatabase {
3184
3193
  setStartRecord(startRecord) {
3185
3194
  this.currentRecord = startRecord - 1;
3186
3195
  this.hasReadFirstPage = false;
3196
+ this.readFileCache = null;
3187
3197
  }
3188
3198
  /**
3189
3199
  * Reset read pagination state
@@ -3191,6 +3201,7 @@ var FileDatabase = class _FileDatabase {
3191
3201
  resetPagination() {
3192
3202
  this.currentRecord = 0;
3193
3203
  this.hasReadFirstPage = false;
3204
+ this.readFileCache = null;
3194
3205
  }
3195
3206
  /**
3196
3207
  * List filenames in the table directory.
@@ -3936,6 +3947,41 @@ var Db = class _Db {
3936
3947
  }
3937
3948
  return stack.pop();
3938
3949
  };
3950
+ const isTimeoutError = (error) => error?.name === "KnexTimeoutError" || /query timeout|timeout exceeded/i.test(String(error?.message ?? ""));
3951
+ const logQueryEntry = (query, start, { error } = {}) => {
3952
+ let executionTimeMs;
3953
+ if (start) {
3954
+ const [seconds, nanoseconds] = process.hrtime(start);
3955
+ executionTimeMs = (seconds * 1e3 + nanoseconds / 1e6).toFixed(2);
3956
+ } else if (error?.timeout != null) {
3957
+ executionTimeMs = Number(error.timeout).toFixed(2);
3958
+ } else {
3959
+ executionTimeMs = "?";
3960
+ }
3961
+ let status = "ok";
3962
+ if (error) {
3963
+ status = isTimeoutError(error) ? "timeout" : "error";
3964
+ }
3965
+ const logEntry = {
3966
+ sql: query?.sql,
3967
+ bindings: query?.bindings || [],
3968
+ executionTimeMs,
3969
+ status
3970
+ };
3971
+ if (error) {
3972
+ logEntry.error = error.message ?? String(error);
3973
+ }
3974
+ this.queriesLog.push(logEntry);
3975
+ const suffix = error ? ` | ${status}: ${logEntry.error}` : "";
3976
+ this.logger.debug?.(
3977
+ `[Db] Query: ${query?.sql} | Duration: ${executionTimeMs}ms${suffix}`
3978
+ );
3979
+ };
3980
+ const handleQueryError = (error, query) => {
3981
+ const start = popStart(query);
3982
+ logQueryEntry(query, start, { error });
3983
+ this.logger.error?.(`[Db] Query failed: ${query?.sql}`, error);
3984
+ };
3939
3985
  this.knexInstance.on("query", (query) => {
3940
3986
  pushStart(query);
3941
3987
  });
@@ -3944,20 +3990,31 @@ var Db = class _Db {
3944
3990
  if (!start) {
3945
3991
  return;
3946
3992
  }
3947
- const [seconds, nanoseconds] = process.hrtime(start);
3948
- const executionTimeMs = (seconds * 1e3 + nanoseconds / 1e6).toFixed(2);
3949
- const logEntry = {
3950
- sql: query.sql,
3951
- bindings: query.bindings || [],
3952
- executionTimeMs
3953
- };
3954
- this.queriesLog.push(logEntry);
3955
- this.logger.debug?.(`[Db] Query: ${query.sql} | Duration: ${executionTimeMs}ms`);
3993
+ logQueryEntry(query, start);
3956
3994
  });
3957
3995
  this.knexInstance.on("query-error", (error, query) => {
3958
- popStart(query);
3959
- this.logger.error?.(`[Db] Query failed: ${query.sql}`, error);
3996
+ handleQueryError(error, query);
3960
3997
  });
3998
+ const hookBuilder = (builder) => {
3999
+ if (!builder || builder.__dbProfilerHooked) {
4000
+ return;
4001
+ }
4002
+ builder.__dbProfilerHooked = true;
4003
+ builder.on("query", pushStart);
4004
+ builder.on("query-error", handleQueryError);
4005
+ };
4006
+ const client = this.knexInstance.client;
4007
+ if (client && !client.__dbProfilerPatched) {
4008
+ client.__dbProfilerPatched = true;
4009
+ if (typeof client.queryBuilder === "function") {
4010
+ const origQueryBuilder = client.queryBuilder.bind(client);
4011
+ client.queryBuilder = (...args) => {
4012
+ const builder = origQueryBuilder(...args);
4013
+ hookBuilder(builder);
4014
+ return builder;
4015
+ };
4016
+ }
4017
+ }
3961
4018
  }
3962
4019
  getQueryLog() {
3963
4020
  return [...this.queriesLog];