querysub 0.453.0 → 0.454.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.
@@ -22,7 +22,8 @@
22
22
  "mcp__node-debugger__waitForPause",
23
23
  "mcp__node-debugger__resume",
24
24
  "mcp__node-debugger__listBreakpoints",
25
- "mcp__node-debugger__removeBreakpoint"
25
+ "mcp__node-debugger__removeBreakpoint",
26
+ "mcp__hottest__runTest"
26
27
  ]
27
28
  }
28
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.453.0",
3
+ "version": "0.454.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -509,7 +509,10 @@ export class ArchivesBackblaze {
509
509
  private last503Reset = 0;
510
510
  // IMPORTANT! We must always CATCH AROUND the apiRetryLogic, NEVER inside of fnc. Otherwise we won't
511
511
  // be able to recreate the auth token.
512
+ // `context` is a short label (verb + file path) included in every retry/error log so a stuck
513
+ // silent-retry loop is identifiable from the logs.
512
514
  private async apiRetryLogic<T>(
515
+ context: string,
513
516
  fnc: (api: B2Api) => Promise<T>,
514
517
  retries = 3
515
518
  ): Promise<T> {
@@ -519,33 +522,33 @@ export class ArchivesBackblaze {
519
522
  } catch (err: any) {
520
523
  if (retries <= 0) throw err;
521
524
 
522
- // If it's a 503 and it's been a minute since we last reset, then Wait and reset.
525
+ // If it's a 503 and it's been a minute since we last reset, then Wait and reset.
523
526
  if (
524
527
  (err.stack.includes(`"status": 503`)
525
528
  || err.stack.includes(`"service_unavailable"`)
526
529
  || err.stack.includes(`"internal_error"`)
527
530
  || err.stack.includes(`ENOBUFS`)
528
531
  ) && Date.now() - this.last503Reset > 60 * 1000) {
529
- console.error("503 error, waiting a minute and resetting: " + err.message);
530
- this.log("503 error, waiting a minute and resetting: " + err.message);
532
+ console.error(`[${context}] 503 error, waiting and resetting: ${err.message}`);
533
+ this.log(`[${context}] 503 error, waiting and resetting: ${err.message}`);
531
534
  await delay(10 * 1000);
532
- // We check again in case, and in the very likely case that this is being run in parallel, we only want to reset once.
535
+ // We check again in case, and in the very likely case that this is being run in parallel, we only want to reset once.
533
536
  if (Date.now() - this.last503Reset > 60 * 1000) {
534
- this.log("Resetting getAPI and getBucketAPI: " + err.message);
537
+ this.log(`[${context}] Resetting getAPI and getBucketAPI: ${err.message}`);
535
538
  this.last503Reset = Date.now();
536
539
  getAPI.reset();
537
540
  this.getBucketAPI.reset();
538
541
  }
539
- return this.apiRetryLogic(fnc, retries - 1);
542
+ return this.apiRetryLogic(context, fnc, retries - 1);
540
543
  }
541
544
 
542
545
  // If the error is that the authorization token is invalid, reset getBucketAPI and getAPI
543
546
  // If the error is that the bucket isn't found, reset getBucketAPI
544
547
  if (err.stack.includes(`"expired_auth_token"`)) {
545
- this.log("Authorization token expired");
548
+ this.log(`[${context}] Authorization token expired`);
546
549
  getAPI.reset();
547
550
  this.getBucketAPI.reset();
548
- return this.apiRetryLogic(fnc, retries - 1);
551
+ return this.apiRetryLogic(context, fnc, retries - 1);
549
552
  }
550
553
 
551
554
  if (
@@ -560,10 +563,10 @@ export class ArchivesBackblaze {
560
563
  || err.stack.includes(`ECONNREFUSED`)
561
564
  || err.stack.includes(`ENOBUFS`)
562
565
  ) {
563
- console.error("Retrying in 5s: " + err.message);
564
- this.log(err.message + " retrying in 5s");
566
+ console.error(`[${context}] Retrying in 5s: ${err.message}`);
567
+ this.log(`[${context}] ${err.message} retrying in 5s`);
565
568
  await delay(5000);
566
- return this.apiRetryLogic(fnc, retries - 1);
569
+ return this.apiRetryLogic(context, fnc, retries - 1);
567
570
  }
568
571
 
569
572
  if (err.stack.includes(`getaddrinfo ENOTFOUND`)) {
@@ -579,10 +582,11 @@ export class ArchivesBackblaze {
579
582
  resolve(addresses);
580
583
  });
581
584
  });
582
- console.error(`getaddrinfo ENOTFOUND ${hostname}`, { lookupAddresses, resolveAddresses, apiUrl: api.apiUrl, fullError: err.stack });
585
+ console.error(`[${context}] getaddrinfo ENOTFOUND ${hostname}`, { lookupAddresses, resolveAddresses, apiUrl: api.apiUrl, fullError: err.stack });
583
586
  }
584
587
 
585
588
  // TODO: Handle if the bucket is deleted?
589
+ console.error(`[${context}] giving up after ${3 - retries + 1} attempts: ${err.stack ?? err}`);
586
590
  throw err;
587
591
  }
588
592
  }
@@ -597,7 +601,7 @@ export class ArchivesBackblaze {
597
601
  setTimeout(downloadPoll, 5000);
598
602
  };
599
603
  setTimeout(downloadPoll, 5000);
600
- let result = await this.apiRetryLogic(async (api) => {
604
+ let result = await this.apiRetryLogic(`get ${fileName}`, async (api) => {
601
605
  let range = config?.range;
602
606
  if (range) {
603
607
  let fileInfo = await this.getInfo(fileName);
@@ -649,7 +653,7 @@ export class ArchivesBackblaze {
649
653
  public async set(fileName: string, data: Buffer): Promise<void> {
650
654
  this.log(`backblaze upload (${formatNumber(data.length)}B) ${fileName}`);
651
655
  let f = fileName;
652
- await this.apiRetryLogic(async (api) => {
656
+ await this.apiRetryLogic(`uploadFile ${fileName}`, async (api) => {
653
657
  await api.uploadFile({ bucketId: this.bucketId, fileName, data: data, });
654
658
  });
655
659
  let existsChecks = 30;
@@ -677,7 +681,7 @@ export class ArchivesBackblaze {
677
681
  public async del(fileName: string): Promise<void> {
678
682
  this.log(`backblaze delete ${fileName}`);
679
683
  try {
680
- await this.apiRetryLogic(async (api) => {
684
+ await this.apiRetryLogic(`hideFile ${fileName}`, async (api) => {
681
685
  await api.hideFile({ bucketId: this.bucketId, fileName: fileName });
682
686
  });
683
687
  } catch (e: any) {
@@ -746,7 +750,7 @@ export class ArchivesBackblaze {
746
750
  dataQueue.unshift(data, secondData);
747
751
 
748
752
 
749
- let uploadInfo = await this.apiRetryLogic(async (api) => {
753
+ let uploadInfo = await this.apiRetryLogic(`startLargeFile ${fileName}`, async (api) => {
750
754
  return await api.startLargeFile({
751
755
  bucketId: this.bucketId,
752
756
  fileName: fileName,
@@ -755,7 +759,7 @@ export class ArchivesBackblaze {
755
759
  });
756
760
  });
757
761
  onError.push(async () => {
758
- await this.apiRetryLogic(async (api) => {
762
+ await this.apiRetryLogic(`cancelLargeFile ${fileName}`, async (api) => {
759
763
  await api.cancelLargeFile({ fileId: uploadInfo.fileId });
760
764
  });
761
765
  });
@@ -792,7 +796,7 @@ export class ArchivesBackblaze {
792
796
  sha1.update(data);
793
797
  let sha1Hex = sha1.digest("hex");
794
798
  partSha1Array.push(sha1Hex);
795
- await this.apiRetryLogic(async (api) => {
799
+ await this.apiRetryLogic(`uploadPart#${partNumber} ${fileName}`, async (api) => {
796
800
  if (!data) throw new Error("Impossible, data is undefined");
797
801
 
798
802
  let timeStr = formatTime(Date.now() - time);
@@ -818,7 +822,7 @@ export class ArchivesBackblaze {
818
822
  }
819
823
  this.log(`Finished uploading large file uploaded ${green(formatNumber(totalBytes))}B`);
820
824
 
821
- await this.apiRetryLogic(async (api) => {
825
+ await this.apiRetryLogic(`finishLargeFile ${fileName}`, async (api) => {
822
826
  await api.finishLargeFile({
823
827
  fileId: uploadInfo.fileId,
824
828
  partSha1Array: partSha1Array,
@@ -838,7 +842,7 @@ export class ArchivesBackblaze {
838
842
  }
839
843
 
840
844
  public async getInfo(fileName: string): Promise<{ writeTime: number; size: number; } | undefined> {
841
- return await this.apiRetryLogic(async (api) => {
845
+ return await this.apiRetryLogic(`getInfo ${fileName}`, async (api) => {
842
846
  try {
843
847
  // NOTE: Apparently, there's no other way to do this, as the file name does not equal the file ID, and git file info requires the file ID.
844
848
  let info = await api.listFileNames({ bucketId: this.bucketId, prefix: fileName, maxFileCount: 10 });
@@ -868,7 +872,7 @@ export class ArchivesBackblaze {
868
872
  return result.map(x => x.path);
869
873
  }
870
874
  public async findInfo(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<{ path: string; createTime: number; size: number; }[]> {
871
- return await this.apiRetryLogic(async (api) => {
875
+ return await this.apiRetryLogic(`findInfo ${prefix}`, async (api) => {
872
876
  if (!config?.shallow && config?.type === "folders") {
873
877
  let allFiles = await this.findInfo(prefix);
874
878
  let allFolders = new Map<string, { path: string; createTime: number; size: number }>();
@@ -935,7 +939,7 @@ export class ArchivesBackblaze {
935
939
  if (target instanceof ArchivesBackblaze) {
936
940
  let targetBucketId = target.bucketId;
937
941
  if (targetBucketId === this.bucketId && path === targetPath) return;
938
- await this.apiRetryLogic(async (api) => {
942
+ await this.apiRetryLogic(`move ${path} -> ${targetPath}`, async (api) => {
939
943
  // Ugh... listing the file name sucks, but... I guess it's still better than
940
944
  // downloading and re-uploading the entire file.
941
945
  let info = await api.listFileNames({ bucketId: this.bucketId, prefix: path, maxFileCount: 10 });
@@ -974,7 +978,7 @@ export class ArchivesBackblaze {
974
978
  }
975
979
 
976
980
  public async getURL(path: string) {
977
- return await this.apiRetryLogic(async (api) => {
981
+ return await this.apiRetryLogic(`getURL ${path}`, async (api) => {
978
982
  if (path.startsWith("/")) {
979
983
  path = path.slice(1);
980
984
  }
@@ -996,7 +1000,7 @@ export class ArchivesBackblaze {
996
1000
  fileNamePrefix: string;
997
1001
  authorizationToken: string;
998
1002
  }> {
999
- return await this.apiRetryLogic(async (api) => {
1003
+ return await this.apiRetryLogic(`getDownloadAuthorization ${config.fileNamePrefix ?? ""}`, async (api) => {
1000
1004
  return await api.getDownloadAuthorization({
1001
1005
  bucketId: this.bucketId,
1002
1006
  fileNamePrefix: config.fileNamePrefix ?? "",
@@ -208,11 +208,12 @@ export class PathValueArchives {
208
208
  let fullPath = pathIdentifier + "/" + file;
209
209
  console.log(`Write archive file ${fullPath}, with size ${formatNumber(data.byteLength)}B, and count ${formatNumber(values.length)}`);
210
210
  try {
211
+
211
212
  await retryFunctional(() => archives().set(fullPath, data), {
212
213
  maxRetries: 10,
213
214
  minDelay: 1000,
214
215
  maxDelay: 5000,
215
- });
216
+ })();
216
217
  } catch (e) {
217
218
  console.error(`Error writing archive file ${fullPath}. THIS IS BAD! WE ARE SHUTTING DOWN SO THIS ERROR IS LOUDER! WHAT'S THE POINT OF A DB THAT DOESN'T SAVE DATA!: ${(e as Error).stack ?? e}`);
218
219
  await delay(5000);
@@ -420,10 +421,10 @@ export class PathValueArchives {
420
421
  skipValues: config.skipValues,
421
422
  });
422
423
  if (dataValues.length !== decodedObj.valueCount) {
423
- console.error(`Bad archive data file at ${config.path}, Decoded count ${formatNumber(decodedObj.valueCount)} !== count in file name ${formatNumber(dataValues.length)} (${decodedObj.valueCount} !== ${dataValues.length})`);
424
+ console.warn(`Bad archive data file at ${config.path}, Decoded count ${formatNumber(decodedObj.valueCount)} !== count in file name ${formatNumber(dataValues.length)} (${decodedObj.valueCount} !== ${dataValues.length})`);
424
425
  }
425
426
  } catch (e: any) {
426
- console.log(red(`Bad archive data file at ${config.path}, error: ${e.stack}`));
427
+ console.warn(`Bad archive data file at ${config.path}, error: ${e.stack}`);
427
428
  }
428
429
  let rawCount = dataValues.length;
429
430