@aztec/blob-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.04852196a

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.
Files changed (39) hide show
  1. package/README.md +10 -1
  2. package/dest/archive/config.js +1 -1
  3. package/dest/archive/instrumentation.d.ts +1 -1
  4. package/dest/archive/instrumentation.d.ts.map +1 -1
  5. package/dest/archive/instrumentation.js +13 -13
  6. package/dest/blobstore/blob_store_test_suite.js +9 -9
  7. package/dest/client/config.d.ts +5 -1
  8. package/dest/client/config.d.ts.map +1 -1
  9. package/dest/client/config.js +5 -0
  10. package/dest/client/factory.d.ts +3 -2
  11. package/dest/client/factory.d.ts.map +1 -1
  12. package/dest/client/factory.js +5 -2
  13. package/dest/client/http.d.ts +24 -2
  14. package/dest/client/http.d.ts.map +1 -1
  15. package/dest/client/http.js +133 -47
  16. package/dest/client/interface.d.ts +18 -1
  17. package/dest/client/interface.d.ts.map +1 -1
  18. package/dest/client/local.d.ts +3 -1
  19. package/dest/client/local.d.ts.map +1 -1
  20. package/dest/client/local.js +3 -0
  21. package/dest/client/tests.js +3 -3
  22. package/dest/filestore/filestore_blob_client.d.ts +14 -2
  23. package/dest/filestore/filestore_blob_client.d.ts.map +1 -1
  24. package/dest/filestore/filestore_blob_client.js +29 -5
  25. package/dest/filestore/healthcheck.d.ts +5 -0
  26. package/dest/filestore/healthcheck.d.ts.map +1 -0
  27. package/dest/filestore/healthcheck.js +3 -0
  28. package/package.json +8 -8
  29. package/src/archive/config.ts +1 -1
  30. package/src/archive/instrumentation.ts +22 -13
  31. package/src/blobstore/blob_store_test_suite.ts +9 -9
  32. package/src/client/config.ts +10 -0
  33. package/src/client/factory.ts +7 -2
  34. package/src/client/http.ts +175 -41
  35. package/src/client/interface.ts +17 -0
  36. package/src/client/local.ts +5 -0
  37. package/src/client/tests.ts +2 -2
  38. package/src/filestore/filestore_blob_client.ts +33 -5
  39. package/src/filestore/healthcheck.ts +5 -0
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,6EAA6E;IAC7E,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,IAAI,CAAC,IAAI,IAAI,CAAC;IACd,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC;CACtB"}
@@ -7,5 +7,7 @@ export declare class LocalBlobClient implements BlobClientInterface {
7
7
  testSources(): Promise<void>;
8
8
  sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
9
9
  getBlobSidecar(_blockId: string, blobHashes: Buffer[], _opts?: GetBlobSidecarOptions): Promise<Blob[]>;
10
+ /** Returns true if this client can upload blobs. Always true for local client. */
11
+ canUpload(): boolean;
10
12
  }
11
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvbG9jYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDdkQsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxlQUFnQixZQUFXLG1CQUFtQjtJQUN6RCxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBWTtJQUV0QyxZQUFZLFNBQVMsRUFBRSxTQUFTLEVBRS9CO0lBRU0sV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbEM7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUdqRTtJQUVNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxxQkFBcUIsR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FFNUc7Q0FDRiJ9
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvbG9jYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDdkQsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxlQUFnQixZQUFXLG1CQUFtQjtJQUN6RCxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBWTtJQUV0QyxZQUFZLFNBQVMsRUFBRSxTQUFTLEVBRS9CO0lBRU0sV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbEM7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUdqRTtJQUVNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxxQkFBcUIsR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FFNUc7SUFFRCxrRkFBa0Y7SUFDM0UsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,YAAY,SAAS,EAAE,SAAS,EAE/B;IAEM,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;IAEM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAE5G;CACF"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,YAAY,SAAS,EAAE,SAAS,EAE/B;IAEM,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;IAEM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAE5G;IAED,kFAAkF;IAC3E,SAAS,IAAI,OAAO,CAE1B;CACF"}
@@ -13,4 +13,7 @@ export class LocalBlobClient {
13
13
  getBlobSidecar(_blockId, blobHashes, _opts) {
14
14
  return this.blobStore.getBlobsByHashes(blobHashes);
15
15
  }
16
+ /** Returns true if this client can upload blobs. Always true for local client. */ canUpload() {
17
+ return true;
18
+ }
16
19
  }
@@ -17,7 +17,7 @@ import { makeRandomBlob } from '@aztec/blob-lib/testing';
17
17
  await cleanup();
18
18
  });
19
19
  it('should send and retrieve blobs by hash', async ()=>{
20
- const blob = makeRandomBlob(5);
20
+ const blob = await makeRandomBlob(5);
21
21
  const blobHash = blob.getEthVersionedBlobHash();
22
22
  await client.sendBlobsToFilestore([
23
23
  blob
@@ -29,9 +29,9 @@ import { makeRandomBlob } from '@aztec/blob-lib/testing';
29
29
  expect(retrievedBlobs[0]).toEqual(blob);
30
30
  });
31
31
  it('should handle multiple blobs', async ()=>{
32
- const blobs = Array.from({
32
+ const blobs = await Promise.all(Array.from({
33
33
  length: 3
34
- }, ()=>makeRandomBlob(7));
34
+ }, ()=>makeRandomBlob(7)));
35
35
  const blobHashes = blobs.map((blob)=>blob.getEthVersionedBlobHash());
36
36
  await client.sendBlobsToFilestore(blobs);
37
37
  const retrievedBlobs = await client.getBlobSidecar(blockId, blobHashes);
@@ -15,6 +15,11 @@ export declare class FileStoreBlobClient {
15
15
  * Format: basePath/blobs/{versionedBlobHash}.data
16
16
  */
17
17
  private blobPath;
18
+ /**
19
+ * Get the path for the healthcheck file.
20
+ * Format: basePath/.healthcheck
21
+ */
22
+ private healthcheckPath;
18
23
  /**
19
24
  * Fetch blobs by their versioned hashes.
20
25
  * @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
@@ -44,12 +49,19 @@ export declare class FileStoreBlobClient {
44
49
  */
45
50
  getBaseUrl(): string;
46
51
  /**
47
- * Test if the filestore connection is working.
52
+ * Test if the filestore connection is working by checking for healthcheck file.
53
+ * The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
54
+ * This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
48
55
  */
49
56
  testConnection(): Promise<boolean>;
57
+ /**
58
+ * Upload the healthcheck file if it doesn't already exist.
59
+ * This enables read-only clients (HTTP) to verify connectivity.
60
+ */
61
+ uploadHealthcheck(): Promise<void>;
50
62
  /**
51
63
  * Check if the store supports write operations.
52
64
  */
53
65
  private isWritable;
54
66
  }
55
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXN0b3JlX2Jsb2JfY2xpZW50LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmlsZXN0b3JlL2ZpbGVzdG9yZV9ibG9iX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssUUFBUSxFQUErQixNQUFNLGlCQUFpQixDQUFDO0FBQ25GLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUk3RTs7O0dBR0c7QUFDSCxxQkFBYSxtQkFBbUI7SUFJNUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUTtJQUozQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBUztJQUU3QixZQUNtQixLQUFLLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxFQUNwQyxRQUFRLEVBQUUsTUFBTSxFQUNqQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBR2hCO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLFFBQVE7SUFJaEI7Ozs7T0FJRztJQUNHLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FtQmhFO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLGlCQUFpQixFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRWxEO0lBRUQ7Ozs7O09BS0c7SUFDRyxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxZQUFZLFVBQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBa0I3RDtJQUVEOzs7O09BSUc7SUFDRyxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLFlBQVksVUFBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFakU7SUFFRDs7T0FFRztJQUNILFVBQVUsSUFBSSxNQUFNLENBRW5CO0lBRUQ7O09BRUc7SUFDSCxjQUFjLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUlqQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLFVBQVU7Q0FHbkIifQ==
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXN0b3JlX2Jsb2JfY2xpZW50LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmlsZXN0b3JlL2ZpbGVzdG9yZV9ibG9iX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssUUFBUSxFQUErQixNQUFNLGlCQUFpQixDQUFDO0FBQ25GLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUs3RTs7O0dBR0c7QUFDSCxxQkFBYSxtQkFBbUI7SUFJNUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUTtJQUozQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBUztJQUU3QixZQUNtQixLQUFLLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxFQUNwQyxRQUFRLEVBQUUsTUFBTSxFQUNqQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBR2hCO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLFFBQVE7SUFJaEI7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLGVBQWU7SUFJdkI7Ozs7T0FJRztJQUNHLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FtQmhFO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLGlCQUFpQixFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRWxEO0lBRUQ7Ozs7O09BS0c7SUFDRyxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxZQUFZLFVBQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBa0I3RDtJQUVEOzs7O09BSUc7SUFDRyxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLFlBQVksVUFBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFakU7SUFFRDs7T0FFRztJQUNILFVBQVUsSUFBSSxNQUFNLENBRW5CO0lBRUQ7Ozs7T0FJRztJQUNHLGNBQWMsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLENBT3ZDO0lBRUQ7OztPQUdHO0lBQ0csaUJBQWlCLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVF2QztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLFVBQVU7Q0FHbkIifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"filestore_blob_client.d.ts","sourceRoot":"","sources":["../../src/filestore/filestore_blob_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAI7E;;;GAGG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,YACmB,KAAK,EAAE,iBAAiB,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,EACjC,MAAM,CAAC,EAAE,MAAM,EAGhB;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;;OAIG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAmBhE;IAED;;;OAGG;IACH,MAAM,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElD;IAED;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB7D;IAED;;;;OAIG;IACG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAIjC;IAED;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
1
+ {"version":3,"file":"filestore_blob_client.d.ts","sourceRoot":"","sources":["../../src/filestore/filestore_blob_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAK7E;;;GAGG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,YACmB,KAAK,EAAE,iBAAiB,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,EACjC,MAAM,CAAC,EAAE,MAAM,EAGhB;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;OAIG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAmBhE;IAED;;;OAGG;IACH,MAAM,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElD;IAED;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB7D;IAED;;;;OAIG;IACG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;IAED;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAOvC;IAED;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQvC;IAED;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
@@ -1,6 +1,7 @@
1
1
  import { computeEthVersionedBlobHash } from '@aztec/blob-lib';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
  import { inboundTransform, outboundTransform } from '../encoding/index.js';
4
+ import { HEALTHCHECK_CONTENT, HEALTHCHECK_FILENAME } from './healthcheck.js';
4
5
  /**
5
6
  * A blob client that uses a FileStore (S3/GCS/local) as the data source.
6
7
  * Blobs are stored as JSON files keyed by their versioned blob hash.
@@ -20,6 +21,12 @@ import { inboundTransform, outboundTransform } from '../encoding/index.js';
20
21
  return `${this.basePath}/blobs/${versionedBlobHash}.data`;
21
22
  }
22
23
  /**
24
+ * Get the path for the healthcheck file.
25
+ * Format: basePath/.healthcheck
26
+ */ healthcheckPath() {
27
+ return `${this.basePath}/${HEALTHCHECK_FILENAME}`;
28
+ }
29
+ /**
23
30
  * Fetch blobs by their versioned hashes.
24
31
  * @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
25
32
  * @returns Array of BlobJson objects for found blobs
@@ -77,11 +84,28 @@ import { inboundTransform, outboundTransform } from '../encoding/index.js';
77
84
  return this.basePath;
78
85
  }
79
86
  /**
80
- * Test if the filestore connection is working.
81
- */ testConnection() {
82
- // This implementation will be improved in a separate PR
83
- // Currently underlying filestore implementations do not expose an easy way to test connectivitiy
84
- return Promise.resolve(true);
87
+ * Test if the filestore connection is working by checking for healthcheck file.
88
+ * The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
89
+ * This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
90
+ */ async testConnection() {
91
+ try {
92
+ return await this.store.exists(this.healthcheckPath());
93
+ } catch (err) {
94
+ this.log.warn(`Connection test failed: ${err?.message ?? String(err)}`);
95
+ return false;
96
+ }
97
+ }
98
+ /**
99
+ * Upload the healthcheck file if it doesn't already exist.
100
+ * This enables read-only clients (HTTP) to verify connectivity.
101
+ */ async uploadHealthcheck() {
102
+ if (!this.isWritable()) {
103
+ this.log.trace('Cannot upload healthcheck: store is read-only');
104
+ return;
105
+ }
106
+ const path = this.healthcheckPath();
107
+ await this.store.save(path, Buffer.from(HEALTHCHECK_CONTENT));
108
+ this.log.debug(`Uploaded healthcheck file to ${path}`);
85
109
  }
86
110
  /**
87
111
  * Check if the store supports write operations.
@@ -0,0 +1,5 @@
1
+ /** Constants for healthcheck file used to test file store connectivity. */
2
+ export declare const HEALTHCHECK_FILENAME = ".healthcheck";
3
+ export declare const HEALTHCHECK_CONTENT = "ok";
4
+ export declare const DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES = 60;
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhbHRoY2hlY2suZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9maWxlc3RvcmUvaGVhbHRoY2hlY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkVBQTJFO0FBRTNFLGVBQU8sTUFBTSxvQkFBb0IsaUJBQWlCLENBQUM7QUFDbkQsZUFBTyxNQUFNLG1CQUFtQixPQUFPLENBQUM7QUFDeEMsZUFBTyxNQUFNLDJDQUEyQyxLQUFLLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthcheck.d.ts","sourceRoot":"","sources":["../../src/filestore/healthcheck.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AACnD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,2CAA2C,KAAK,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Constants for healthcheck file used to test file store connectivity. */ export const HEALTHCHECK_FILENAME = '.healthcheck';
2
+ export const HEALTHCHECK_CONTENT = 'ok';
3
+ export const DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES = 60; // 1 hour
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/blob-client",
3
- "version": "0.0.1-commit.03f7ef2",
3
+ "version": "0.0.1-commit.04852196a",
4
4
  "type": "module",
5
5
  "bin": "./dest/client/bin/index.js",
6
6
  "exports": {
@@ -56,12 +56,12 @@
56
56
  ]
57
57
  },
58
58
  "dependencies": {
59
- "@aztec/blob-lib": "0.0.1-commit.03f7ef2",
60
- "@aztec/ethereum": "0.0.1-commit.03f7ef2",
61
- "@aztec/foundation": "0.0.1-commit.03f7ef2",
62
- "@aztec/kv-store": "0.0.1-commit.03f7ef2",
63
- "@aztec/stdlib": "0.0.1-commit.03f7ef2",
64
- "@aztec/telemetry-client": "0.0.1-commit.03f7ef2",
59
+ "@aztec/blob-lib": "0.0.1-commit.04852196a",
60
+ "@aztec/ethereum": "0.0.1-commit.04852196a",
61
+ "@aztec/foundation": "0.0.1-commit.04852196a",
62
+ "@aztec/kv-store": "0.0.1-commit.04852196a",
63
+ "@aztec/stdlib": "0.0.1-commit.04852196a",
64
+ "@aztec/telemetry-client": "0.0.1-commit.04852196a",
65
65
  "express": "^4.21.2",
66
66
  "snappy": "^7.2.2",
67
67
  "source-map-support": "^0.5.21",
@@ -75,7 +75,7 @@
75
75
  "@types/node": "^22.15.17",
76
76
  "@types/source-map-support": "^0.5.10",
77
77
  "@types/supertest": "^6.0.2",
78
- "@typescript/native-preview": "7.0.0-dev.20251126.1",
78
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
79
79
  "jest": "^30.0.0",
80
80
  "jest-mock-extended": "^4.0.0",
81
81
  "supertest": "^7.0.0",
@@ -7,7 +7,7 @@ export type BlobArchiveApiConfig = {
7
7
 
8
8
  export const blobArchiveApiConfigMappings: ConfigMappingsType<BlobArchiveApiConfig> = {
9
9
  archiveApiUrl: {
10
- env: 'BLOB_SINK_ARCHIVE_API_URL',
10
+ env: 'BLOB_ARCHIVE_API_URL',
11
11
  description: 'The URL of the archive API',
12
12
  },
13
13
  ...pickConfigMappings(l1ReaderConfigMappings, ['l1ChainId']),
@@ -1,4 +1,10 @@
1
- import { Attributes, Metrics, type TelemetryClient, type UpDownCounter, ValueType } from '@aztec/telemetry-client';
1
+ import {
2
+ Attributes,
3
+ Metrics,
4
+ type TelemetryClient,
5
+ type UpDownCounter,
6
+ createUpDownCounterWithDefault,
7
+ } from '@aztec/telemetry-client';
2
8
 
3
9
  export class BlobArchiveClientInstrumentation {
4
10
  private blockRequestCounter: UpDownCounter;
@@ -11,20 +17,23 @@ export class BlobArchiveClientInstrumentation {
11
17
  name: string,
12
18
  ) {
13
19
  const meter = client.getMeter(name);
14
- this.blockRequestCounter = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOCK_REQUEST_COUNT, {
15
- description: 'Number of requests made to retrieve blocks from the blob archive',
16
- valueType: ValueType.INT,
17
- });
20
+ const requestAttrs = {
21
+ [Attributes.HTTP_RESPONSE_STATUS_CODE]: [200, 404],
22
+ [Attributes.HTTP_REQUEST_HOST]: [httpHost],
23
+ };
24
+ this.blockRequestCounter = createUpDownCounterWithDefault(
25
+ meter,
26
+ Metrics.BLOB_SINK_ARCHIVE_BLOCK_REQUEST_COUNT,
27
+ requestAttrs,
28
+ );
18
29
 
19
- this.blobRequestCounter = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOB_REQUEST_COUNT, {
20
- description: 'Number of requests made to retrieve blobs from the blob archive',
21
- valueType: ValueType.INT,
22
- });
30
+ this.blobRequestCounter = createUpDownCounterWithDefault(
31
+ meter,
32
+ Metrics.BLOB_SINK_ARCHIVE_BLOB_REQUEST_COUNT,
33
+ requestAttrs,
34
+ );
23
35
 
24
- this.retrievedBlobs = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOB_COUNT, {
25
- description: 'Number of blobs retrieved from the blob archive',
26
- valueType: ValueType.INT,
27
- });
36
+ this.retrievedBlobs = createUpDownCounterWithDefault(meter, Metrics.BLOB_SINK_ARCHIVE_BLOB_COUNT);
28
37
  }
29
38
 
30
39
  incRequest(type: 'blocks' | 'blobs', status: number) {
@@ -13,7 +13,7 @@ export function describeBlobStore(getBlobStore: () => Promise<BlobStore>) {
13
13
  it('should store and retrieve a blob by hash', async () => {
14
14
  // Create a test blob with random fields
15
15
  const testFields = [Fr.random(), Fr.random(), Fr.random()];
16
- const blob = Blob.fromFields(testFields);
16
+ const blob = await Blob.fromFields(testFields);
17
17
  const blobHash = blob.getEthVersionedBlobHash();
18
18
 
19
19
  // Store the blob
@@ -29,8 +29,8 @@ export function describeBlobStore(getBlobStore: () => Promise<BlobStore>) {
29
29
 
30
30
  it('should handle multiple blobs stored and retrieved by their hashes', async () => {
31
31
  // Create two different blobs
32
- const blob1 = Blob.fromFields([Fr.random(), Fr.random()]);
33
- const blob2 = Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]);
32
+ const blob1 = await Blob.fromFields([Fr.random(), Fr.random()]);
33
+ const blob2 = await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]);
34
34
 
35
35
  const blobHash1 = blob1.getEthVersionedBlobHash();
36
36
  const blobHash2 = blob2.getEthVersionedBlobHash();
@@ -57,9 +57,9 @@ export function describeBlobStore(getBlobStore: () => Promise<BlobStore>) {
57
57
 
58
58
  it('should handle retrieving subset of stored blobs', async () => {
59
59
  // Store multiple blobs
60
- const blob1 = Blob.fromFields([Fr.random()]);
61
- const blob2 = Blob.fromFields([Fr.random()]);
62
- const blob3 = Blob.fromFields([Fr.random()]);
60
+ const blob1 = await Blob.fromFields([Fr.random()]);
61
+ const blob2 = await Blob.fromFields([Fr.random()]);
62
+ const blob3 = await Blob.fromFields([Fr.random()]);
63
63
 
64
64
  await blobStore.addBlobs([blob1, blob2, blob3]);
65
65
 
@@ -75,7 +75,7 @@ export function describeBlobStore(getBlobStore: () => Promise<BlobStore>) {
75
75
  });
76
76
 
77
77
  it('should handle duplicate blob hashes in request', async () => {
78
- const blob = Blob.fromFields([Fr.random()]);
78
+ const blob = await Blob.fromFields([Fr.random()]);
79
79
  const blobHash = blob.getEthVersionedBlobHash();
80
80
 
81
81
  await blobStore.addBlobs([blob]);
@@ -91,8 +91,8 @@ export function describeBlobStore(getBlobStore: () => Promise<BlobStore>) {
91
91
  it('should overwrite blob when storing with same hash', async () => {
92
92
  // Create two blobs that will have the same hash (same content)
93
93
  const fields = [Fr.random(), Fr.random()];
94
- const blob1 = Blob.fromFields(fields);
95
- const blob2 = Blob.fromFields(fields);
94
+ const blob1 = await Blob.fromFields(fields);
95
+ const blob2 = await Blob.fromFields(fields);
96
96
 
97
97
  const blobHash = blob1.getEthVersionedBlobHash();
98
98
 
@@ -50,6 +50,11 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
50
50
  * URL for uploading blobs to filestore (s3://, gs://, file://)
51
51
  */
52
52
  blobFileStoreUploadUrl?: string;
53
+
54
+ /**
55
+ * Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
56
+ */
57
+ blobHealthcheckUploadIntervalMinutes?: number;
53
58
  }
54
59
 
55
60
  export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
@@ -98,6 +103,11 @@ export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
98
103
  env: 'BLOB_FILE_STORE_UPLOAD_URL',
99
104
  description: 'URL for uploading blobs to filestore (s3://, gs://, file://)',
100
105
  },
106
+ blobHealthcheckUploadIntervalMinutes: {
107
+ env: 'BLOB_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES',
108
+ description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
109
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
110
+ },
101
111
  ...blobArchiveApiConfigMappings,
102
112
  };
103
113
 
@@ -58,10 +58,11 @@ export interface BlobClientWithFileStoresConfig extends BlobClientConfig {
58
58
  * 2. Creating read-only FileStore clients
59
59
  * 3. Creating a writable FileStore client for uploads
60
60
  * 4. Creating the BlobClient with these dependencies
61
+ * 5. Starting the client (uploads initial healthcheck file if upload client is configured)
61
62
  *
62
63
  * @param config - Configuration containing blob client settings and chain metadata
63
64
  * @param logger - Optional logger for the blob client
64
- * @returns A BlobClientInterface configured with file store support
65
+ * @returns A BlobClientInterface configured with file store support, already started
65
66
  */
66
67
  export async function createBlobClientWithFileStores(
67
68
  config: BlobClientWithFileStoresConfig,
@@ -80,9 +81,13 @@ export async function createBlobClientWithFileStores(
80
81
  createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, fileStoreMetadata, log),
81
82
  ]);
82
83
 
83
- return createBlobClient(config, {
84
+ const client = createBlobClient(config, {
84
85
  logger: log,
85
86
  fileStoreClients,
86
87
  fileStoreUploadClient,
87
88
  });
89
+
90
+ await client.start?.();
91
+
92
+ return client;
88
93
  }