@aztec/blob-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.04d373f

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 (45) hide show
  1. package/README.md +10 -1
  2. package/dest/archive/blobscan_archive_client.d.ts +8 -92
  3. package/dest/archive/blobscan_archive_client.d.ts.map +1 -1
  4. package/dest/archive/config.js +1 -1
  5. package/dest/archive/instrumentation.d.ts +1 -1
  6. package/dest/archive/instrumentation.d.ts.map +1 -1
  7. package/dest/archive/instrumentation.js +13 -13
  8. package/dest/blobstore/blob_store_test_suite.js +9 -9
  9. package/dest/client/config.d.ts +11 -1
  10. package/dest/client/config.d.ts.map +1 -1
  11. package/dest/client/config.js +22 -2
  12. package/dest/client/factory.d.ts +5 -6
  13. package/dest/client/factory.d.ts.map +1 -1
  14. package/dest/client/factory.js +13 -4
  15. package/dest/client/http.d.ts +34 -10
  16. package/dest/client/http.d.ts.map +1 -1
  17. package/dest/client/http.js +304 -139
  18. package/dest/client/interface.d.ts +19 -4
  19. package/dest/client/interface.d.ts.map +1 -1
  20. package/dest/client/local.d.ts +3 -1
  21. package/dest/client/local.d.ts.map +1 -1
  22. package/dest/client/local.js +3 -0
  23. package/dest/client/tests.js +3 -3
  24. package/dest/filestore/factory.d.ts +5 -4
  25. package/dest/filestore/factory.d.ts.map +1 -1
  26. package/dest/filestore/factory.js +4 -4
  27. package/dest/filestore/filestore_blob_client.d.ts +14 -2
  28. package/dest/filestore/filestore_blob_client.d.ts.map +1 -1
  29. package/dest/filestore/filestore_blob_client.js +29 -5
  30. package/dest/filestore/healthcheck.d.ts +5 -0
  31. package/dest/filestore/healthcheck.d.ts.map +1 -0
  32. package/dest/filestore/healthcheck.js +3 -0
  33. package/package.json +9 -9
  34. package/src/archive/config.ts +1 -1
  35. package/src/archive/instrumentation.ts +22 -13
  36. package/src/blobstore/blob_store_test_suite.ts +9 -9
  37. package/src/client/config.ts +36 -1
  38. package/src/client/factory.ts +17 -5
  39. package/src/client/http.ts +357 -134
  40. package/src/client/interface.ts +18 -3
  41. package/src/client/local.ts +5 -0
  42. package/src/client/tests.ts +2 -2
  43. package/src/filestore/factory.ts +7 -2
  44. package/src/filestore/filestore_blob_client.ts +33 -5
  45. package/src/filestore/healthcheck.ts +5 -0
@@ -5,18 +5,33 @@ import type { Blob } from '@aztec/blob-lib';
5
5
  export interface GetBlobSidecarOptions {
6
6
  /**
7
7
  * True if the archiver is catching up (historical sync), false if near tip.
8
- * This affects source ordering:
9
- * - Historical: FileStore first (data should exist), then L1 consensus, then archive (eg. blobscan)
10
- * - Near tip: FileStore first with no retries (data should exist), L1 consensus second (freshest data), then FileStore with retries, then archive (eg. blobscan)
8
+ * Historical sync uses a shorter retry backoff since blobs should already exist.
11
9
  */
12
10
  isHistoricalSync?: boolean;
11
+ /**
12
+ * The parent beacon block root for the L1 block containing the blobs.
13
+ * If provided, skips the eth_getBlockByHash execution RPC call inside getSlotNumber.
14
+ */
15
+ parentBeaconBlockRoot?: string;
16
+ /**
17
+ * The timestamp of the L1 execution block containing the blobs.
18
+ * When provided alongside a cached beacon genesis config (fetched at startup), allows computing
19
+ * the beacon slot directly via timestamp math, skipping the beacon headers network call entirely.
20
+ */
21
+ l1BlockTimestamp?: bigint;
13
22
  }
14
23
  export interface BlobClientInterface {
15
24
  /** Sends the given blobs to the filestore, to be indexed by blob hash. */
16
25
  sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
17
26
  /** Fetches the given blob sidecars by block hash and blob hashes. */
18
27
  getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
28
+ /** Starts the blob client (e.g., uploads healthcheck file if not exists). */
29
+ start?(): Promise<void>;
19
30
  /** Tests all configured blob sources and logs whether they are reachable or not. */
20
31
  testSources(): Promise<void>;
32
+ /** Stops the blob client, clearing any periodic tasks. */
33
+ stop?(): void;
34
+ /** Returns true if this client can upload blobs to filestore. */
35
+ canUpload(): boolean;
21
36
  }
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLG9GQUFvRjtJQUNwRixXQUFXLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQzlCIn0=
37
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7OztPQUdHO0lBQ0gsZ0JBQWdCLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDM0I7OztPQUdHO0lBQ0gscUJBQXFCLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDL0I7Ozs7T0FJRztJQUNILGdCQUFnQixDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQzNCO0FBRUQsTUFBTSxXQUFXLG1CQUFtQjtJQUNsQywwRUFBMEU7SUFDMUUsb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0RCxxRUFBcUU7SUFDckUsY0FBYyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxDQUFDLEVBQUUsTUFBTSxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUUscUJBQXFCLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdEcsNkVBQTZFO0lBQzdFLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN4QixvRkFBb0Y7SUFDcEYsV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3QiwwREFBMEQ7SUFDMUQsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2QsaUVBQWlFO0lBQ2pFLFNBQVMsSUFBSSxPQUFPLENBQUM7Q0FDdEIifQ==
@@ -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;;;OAGG;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);
@@ -1,4 +1,5 @@
1
1
  import { type Logger } from '@aztec/foundation/log';
2
+ import { type HttpFileStoreOptions } from '@aztec/stdlib/file-store';
2
3
  import { FileStoreBlobClient } from './filestore_blob_client.js';
3
4
  /**
4
5
  * Metadata required to construct the base path for blob storage.
@@ -25,8 +26,8 @@ export declare function makeBlobBasePath(metadata: BlobFileStoreMetadata): strin
25
26
  * @param logger - Optional logger
26
27
  * @returns A FileStoreBlobClient for reading blobs, or undefined if storeUrl is undefined
27
28
  */
28
- export declare function createReadOnlyFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient>;
29
- export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient | undefined>;
29
+ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient>;
30
+ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient | undefined>;
30
31
  /**
31
32
  * Creates multiple read-only FileStoreBlobClients from an array of URLs.
32
33
  *
@@ -35,7 +36,7 @@ export declare function createReadOnlyFileStoreBlobClient(storeUrl: string | und
35
36
  * @param logger - Optional logger
36
37
  * @returns Array of FileStoreBlobClients (excludes any that failed to create)
37
38
  */
38
- export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient[]>;
39
+ export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] | undefined, metadata: BlobFileStoreMetadata, logger?: Logger, httpOptions?: HttpFileStoreOptions): Promise<FileStoreBlobClient[]>;
39
40
  /**
40
41
  * Creates a writable FileStoreBlobClient for uploading blobs.
41
42
  * Note: https:// URLs are not supported for upload, only s3://, gs://, or file://.
@@ -47,4 +48,4 @@ export declare function createReadOnlyFileStoreBlobClients(storeUrls: string[] |
47
48
  */
48
49
  export declare function createWritableFileStoreBlobClient(storeUrl: string, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient>;
49
50
  export declare function createWritableFileStoreBlobClient(storeUrl: string | undefined, metadata: BlobFileStoreMetadata, logger?: Logger): Promise<FileStoreBlobClient | undefined>;
50
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGVzdG9yZS9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQVFsRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVqRTs7O0dBR0c7QUFDSCxNQUFNLFdBQVcscUJBQXFCO0lBQ3BDLHNCQUFzQjtJQUN0QixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLHlCQUF5QjtJQUN6QixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLDhEQUE4RDtJQUM5RCxhQUFhLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxxQkFBcUIsR0FBRyxNQUFNLENBS3hFO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDO0FBbUI1Qzs7Ozs7OztHQU9HO0FBQ0gsd0JBQXNCLGtDQUFrQyxDQUN0RCxTQUFTLEVBQUUsTUFBTSxFQUFFLEdBQUcsU0FBUyxFQUMvQixRQUFRLEVBQUUscUJBQXFCLEVBQy9CLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQW9CaEM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDIn0=
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGVzdG9yZS9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEVBRUwsS0FBSyxvQkFBb0IsRUFJMUIsTUFBTSwwQkFBMEIsQ0FBQztBQUVsQyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVqRTs7O0dBR0c7QUFDSCxNQUFNLFdBQVcscUJBQXFCO0lBQ3BDLHNCQUFzQjtJQUN0QixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLHlCQUF5QjtJQUN6QixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLDhEQUE4RDtJQUM5RCxhQUFhLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxxQkFBcUIsR0FBRyxNQUFNLENBS3hFO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEVBQ2YsV0FBVyxDQUFDLEVBQUUsb0JBQW9CLEdBQ2pDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0FBQ2hDLHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sR0FBRyxTQUFTLEVBQzVCLFFBQVEsRUFBRSxxQkFBcUIsRUFDL0IsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUNmLFdBQVcsQ0FBQyxFQUFFLG9CQUFvQixHQUNqQyxPQUFPLENBQUMsbUJBQW1CLEdBQUcsU0FBUyxDQUFDLENBQUM7QUFvQjVDOzs7Ozs7O0dBT0c7QUFDSCx3QkFBc0Isa0NBQWtDLENBQ3RELFNBQVMsRUFBRSxNQUFNLEVBQUUsR0FBRyxTQUFTLEVBQy9CLFFBQVEsRUFBRSxxQkFBcUIsRUFDL0IsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUNmLFdBQVcsQ0FBQyxFQUFFLG9CQUFvQixHQUNqQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQW9CaEM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILHdCQUFzQixpQ0FBaUMsQ0FDckQsUUFBUSxFQUFFLE1BQU0sRUFDaEIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDaEMsd0JBQXNCLGlDQUFpQyxDQUNyRCxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFDNUIsUUFBUSxFQUFFLHFCQUFxQixFQUMvQixNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQUFDIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/filestore/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;AAmB5C;;;;;;;GAOG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAoBhC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/filestore/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;AAoB5C;;;;;;;GAOG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAoBhC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAChC,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC"}
@@ -10,7 +10,7 @@ import { FileStoreBlobClient } from './filestore_blob_client.js';
10
10
  const normalizedAddress = rollupAddress.toLowerCase().replace(/^0x/, '');
11
11
  return `aztec-${l1ChainId}-${rollupVersion}-0x${normalizedAddress}`;
12
12
  }
13
- export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logger) {
13
+ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logger, httpOptions) {
14
14
  if (!storeUrl) {
15
15
  return undefined;
16
16
  }
@@ -20,7 +20,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
20
20
  storeUrl,
21
21
  basePath
22
22
  });
23
- const store = await createReadOnlyFileStore(storeUrl, log);
23
+ const store = await createReadOnlyFileStore(storeUrl, log, httpOptions);
24
24
  return new FileStoreBlobClient(store, basePath, log);
25
25
  }
26
26
  /**
@@ -30,7 +30,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
30
30
  * @param metadata - Chain metadata for constructing the base path
31
31
  * @param logger - Optional logger
32
32
  * @returns Array of FileStoreBlobClients (excludes any that failed to create)
33
- */ export async function createReadOnlyFileStoreBlobClients(storeUrls, metadata, logger) {
33
+ */ export async function createReadOnlyFileStoreBlobClients(storeUrls, metadata, logger, httpOptions) {
34
34
  if (!storeUrls || storeUrls.length === 0) {
35
35
  return [];
36
36
  }
@@ -38,7 +38,7 @@ export async function createReadOnlyFileStoreBlobClient(storeUrl, metadata, logg
38
38
  const clients = [];
39
39
  for (const storeUrl of storeUrls){
40
40
  try {
41
- const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log);
41
+ const client = await createReadOnlyFileStoreBlobClient(storeUrl, metadata, log, httpOptions);
42
42
  if (client) {
43
43
  clients.push(client);
44
44
  }
@@ -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.04d373f",
4
4
  "type": "module",
5
5
  "bin": "./dest/client/bin/index.js",
6
6
  "exports": {
@@ -56,18 +56,18 @@
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.04d373f",
60
+ "@aztec/ethereum": "0.0.1-commit.04d373f",
61
+ "@aztec/foundation": "0.0.1-commit.04d373f",
62
+ "@aztec/kv-store": "0.0.1-commit.04d373f",
63
+ "@aztec/stdlib": "0.0.1-commit.04d373f",
64
+ "@aztec/telemetry-client": "0.0.1-commit.04d373f",
65
65
  "express": "^4.21.2",
66
66
  "snappy": "^7.2.2",
67
67
  "source-map-support": "^0.5.21",
68
68
  "tslib": "^2.4.0",
69
69
  "viem": "npm:@aztec/viem@2.38.2",
70
- "zod": "^3.23.8"
70
+ "zod": "^4"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@jest/globals": "^30.0.0",
@@ -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
 
@@ -3,6 +3,7 @@ import {
3
3
  SecretValue,
4
4
  booleanConfigHelper,
5
5
  getConfigFromMappings,
6
+ optionalNumberConfigHelper,
6
7
  } from '@aztec/foundation/config';
7
8
 
8
9
  import { type BlobArchiveApiConfig, blobArchiveApiConfigMappings } from '../archive/config.js';
@@ -50,6 +51,20 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
50
51
  * URL for uploading blobs to filestore (s3://, gs://, file://)
51
52
  */
52
53
  blobFileStoreUploadUrl?: string;
54
+
55
+ /**
56
+ * Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
57
+ */
58
+ blobHealthcheckUploadIntervalMinutes?: number;
59
+
60
+ /** Timeout for HTTP requests to the L1 RPC node in ms. */
61
+ l1HttpTimeoutMS?: number;
62
+
63
+ /** Whether to prefer filestores over consensus clients when fetching blobs. Default: false (consensus first). */
64
+ blobPreferFilestores?: boolean;
65
+
66
+ /** Timeout in ms for HTTP requests to the blob file store. Default: 10000 (10s). */
67
+ blobFileStoreTimeoutMs?: number;
53
68
  }
54
69
 
55
70
  export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
@@ -78,7 +93,7 @@ export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
78
93
  blobSinkMapSizeKb: {
79
94
  env: 'BLOB_SINK_MAP_SIZE_KB',
80
95
  description: 'The maximum possible size of the blob sink DB in KB. Overwrites the general dataStoreMapSizeKb.',
81
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
96
+ ...optionalNumberConfigHelper(),
82
97
  },
83
98
  blobAllowEmptySources: {
84
99
  env: 'BLOB_ALLOW_EMPTY_SOURCES',
@@ -98,6 +113,26 @@ export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
98
113
  env: 'BLOB_FILE_STORE_UPLOAD_URL',
99
114
  description: 'URL for uploading blobs to filestore (s3://, gs://, file://)',
100
115
  },
116
+ blobHealthcheckUploadIntervalMinutes: {
117
+ env: 'BLOB_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES',
118
+ description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
119
+ ...optionalNumberConfigHelper(),
120
+ },
121
+ l1HttpTimeoutMS: {
122
+ env: 'ETHEREUM_HTTP_TIMEOUT_MS',
123
+ description: 'Timeout for HTTP requests to the L1 RPC node in ms.',
124
+ ...optionalNumberConfigHelper(),
125
+ },
126
+ blobPreferFilestores: {
127
+ env: 'BLOB_PREFER_FILESTORES',
128
+ description: 'Whether to prefer filestores over consensus clients when fetching blobs. Default: false.',
129
+ ...booleanConfigHelper(false),
130
+ },
131
+ blobFileStoreTimeoutMs: {
132
+ env: 'BLOB_FILE_STORE_TIMEOUT_MS',
133
+ description: 'Timeout in ms for HTTP requests to the blob file store. Default: 10000 (10s).',
134
+ ...optionalNumberConfigHelper(),
135
+ },
101
136
  ...blobArchiveApiConfigMappings,
102
137
  };
103
138
 
@@ -48,7 +48,7 @@ export function createBlobClient(config?: BlobClientConfig, deps?: CreateBlobCli
48
48
  export interface BlobClientWithFileStoresConfig extends BlobClientConfig {
49
49
  l1ChainId: number;
50
50
  rollupVersion: number;
51
- l1Contracts: { rollupAddress: { toString(): string } };
51
+ rollupAddress: { toString(): string };
52
52
  }
53
53
 
54
54
  /**
@@ -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,
@@ -72,17 +73,28 @@ export async function createBlobClientWithFileStores(
72
73
  const fileStoreMetadata: BlobFileStoreMetadata = {
73
74
  l1ChainId: config.l1ChainId,
74
75
  rollupVersion: config.rollupVersion,
75
- rollupAddress: config.l1Contracts.rollupAddress.toString(),
76
+ rollupAddress: config.rollupAddress.toString(),
77
+ };
78
+
79
+ // Disable internal retries for blob file stores — retry logic is handled by HttpBlobClient.
80
+ // Set a configurable timeout (default 10s) to avoid hanging on slow stores.
81
+ const httpOptions = {
82
+ retryBackoff: [] as number[],
83
+ timeoutMs: config.blobFileStoreTimeoutMs ?? 10_000,
76
84
  };
77
85
 
78
86
  const [fileStoreClients, fileStoreUploadClient] = await Promise.all([
79
- createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, fileStoreMetadata, log),
87
+ createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, fileStoreMetadata, log, httpOptions),
80
88
  createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, fileStoreMetadata, log),
81
89
  ]);
82
90
 
83
- return createBlobClient(config, {
91
+ const client = createBlobClient(config, {
84
92
  logger: log,
85
93
  fileStoreClients,
86
94
  fileStoreUploadClient,
87
95
  });
96
+
97
+ await client.start?.();
98
+
99
+ return client;
88
100
  }