@aztec/blob-client 3.0.0-nightly.20260105 → 3.0.0-nightly.20260106

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/README.md CHANGED
@@ -34,6 +34,15 @@ Beacon node URLs for fetching recent blobs directly from L1.
34
34
  **Archive API URL** (`BLOB_SINK_ARCHIVE_API_URL`):
35
35
  Blobscan or similar archive API for historical blob data.
36
36
 
37
+ ### File Store Connectivity Testing
38
+
39
+ All file stores (S3, GCS, HTTP, local) test connectivity by checking if a well-known healthcheck file (`.healthcheck`) exists. This approach was chosen because:
40
+
41
+ 1. **HTTP compatibility**: For HTTP-based file stores, requesting a known file is the only reliable way to verify connectivity
42
+ 2. **Uniform behavior**: Using the same healthcheck mechanism across all store types ensures consistent behavior and simplifies testing
43
+
44
+ When uploading is enabled, the sequencer uploads the healthcheck file on startup and then periodically re-uploads it (by default every 60 minutes) to ensure it remains available. This guards against accidental deletion, storage pruning, or other failures that might remove the file.
45
+
37
46
  ### Example Usage
38
47
 
39
48
  ```typescript
@@ -36,6 +36,10 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
36
36
  * URL for uploading blobs to filestore (s3://, gs://, file://)
37
37
  */
38
38
  blobFileStoreUploadUrl?: string;
39
+ /**
40
+ * Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
41
+ */
42
+ blobHealthcheckUploadIntervalMinutes?: number;
39
43
  }
40
44
  export declare const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig>;
41
45
  /**
@@ -47,4 +51,4 @@ export declare function getBlobClientConfigFromEnv(): BlobClientConfig;
47
51
  * Returns whether the given blob client config has any remote sources defined.
48
52
  */
49
53
  export declare function hasRemoteBlobSources(config?: BlobClientConfig): boolean;
50
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsS0FBSyxrQkFBa0IsRUFDdkIsV0FBVyxFQUdaLE1BQU0sMEJBQTBCLENBQUM7QUFFbEMsT0FBTyxFQUFFLEtBQUssb0JBQW9CLEVBQWdDLE1BQU0sc0JBQXNCLENBQUM7QUFFL0Y7O0dBRUc7QUFDSCxNQUFNLFdBQVcsZ0JBQWlCLFNBQVEsb0JBQW9CO0lBQzVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFckI7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBRS9COztPQUVHO0lBQ0gsc0JBQXNCLENBQUMsRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztJQUUvQzs7T0FFRztJQUNILDRCQUE0QixDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFeEM7O09BRUc7SUFDSCxpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUUzQjs7T0FFRztJQUNILHFCQUFxQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUU3Qjs7T0FFRztJQUNILHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQ2pDO0FBRUQsZUFBTyxNQUFNLHVCQUF1QixFQUFFLGtCQUFrQixDQUFDLGdCQUFnQixDQStDeEUsQ0FBQztBQUVGOzs7R0FHRztBQUNILHdCQUFnQiwwQkFBMEIsSUFBSSxnQkFBZ0IsQ0FFN0Q7QUFFRDs7R0FFRztBQUNILHdCQUFnQixvQkFBb0IsQ0FBQyxNQUFNLEdBQUUsZ0JBQXFCLEdBQUcsT0FBTyxDQUUzRSJ9
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsS0FBSyxrQkFBa0IsRUFDdkIsV0FBVyxFQUdaLE1BQU0sMEJBQTBCLENBQUM7QUFFbEMsT0FBTyxFQUFFLEtBQUssb0JBQW9CLEVBQWdDLE1BQU0sc0JBQXNCLENBQUM7QUFFL0Y7O0dBRUc7QUFDSCxNQUFNLFdBQVcsZ0JBQWlCLFNBQVEsb0JBQW9CO0lBQzVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFckI7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBRS9COztPQUVHO0lBQ0gsc0JBQXNCLENBQUMsRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztJQUUvQzs7T0FFRztJQUNILDRCQUE0QixDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFeEM7O09BRUc7SUFDSCxpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUUzQjs7T0FFRztJQUNILHFCQUFxQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUU3Qjs7T0FFRztJQUNILHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsb0NBQW9DLENBQUMsRUFBRSxNQUFNLENBQUM7Q0FDL0M7QUFFRCxlQUFPLE1BQU0sdUJBQXVCLEVBQUUsa0JBQWtCLENBQUMsZ0JBQWdCLENBb0R4RSxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsd0JBQWdCLDBCQUEwQixJQUFJLGdCQUFnQixDQUU3RDtBQUVEOztHQUVHO0FBQ0gsd0JBQWdCLG9CQUFvQixDQUFDLE1BQU0sR0FBRSxnQkFBcUIsR0FBRyxPQUFPLENBRTNFIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,oBAAoB,EAAgC,MAAM,sBAAsB,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IAE/C;;OAEG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,gBAAgB,CA+CxE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,gBAAqB,GAAG,OAAO,CAE3E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,oBAAoB,EAAgC,MAAM,sBAAsB,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IAE/C;;OAEG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;OAEG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAC;CAC/C;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,gBAAgB,CAoDxE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,gBAAqB,GAAG,OAAO,CAE3E"}
@@ -40,6 +40,11 @@ export const blobClientConfigMapping = {
40
40
  env: 'BLOB_FILE_STORE_UPLOAD_URL',
41
41
  description: 'URL for uploading blobs to filestore (s3://, gs://, file://)'
42
42
  },
43
+ blobHealthcheckUploadIntervalMinutes: {
44
+ env: 'BLOB_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES',
45
+ description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
46
+ parseEnv: (val)=>val ? +val : undefined
47
+ },
43
48
  ...blobArchiveApiConfigMappings
44
49
  };
45
50
  /**
@@ -30,10 +30,11 @@ export interface BlobClientWithFileStoresConfig extends BlobClientConfig {
30
30
  * 2. Creating read-only FileStore clients
31
31
  * 3. Creating a writable FileStore client for uploads
32
32
  * 4. Creating the BlobClient with these dependencies
33
+ * 5. Starting the client (uploads initial healthcheck file if upload client is configured)
33
34
  *
34
35
  * @param config - Configuration containing blob client settings and chain metadata
35
36
  * @param logger - Optional logger for the blob client
36
- * @returns A BlobClientInterface configured with file store support
37
+ * @returns A BlobClientInterface configured with file store support, already started
37
38
  */
38
39
  export declare function createBlobClientWithFileStores(config: BlobClientWithFileStoresConfig, logger?: Logger): Promise<BlobClientInterface>;
39
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQVFsRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxLQUFLLGdCQUFnQixFQUF3QixNQUFNLGFBQWEsQ0FBQztBQUUxRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRzFELE1BQU0sV0FBVyxvQkFBb0I7SUFDbkMsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2hCLDBDQUEwQztJQUMxQyxnQkFBZ0IsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLENBQUM7SUFDekMsMkNBQTJDO0lBQzNDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7Q0FDN0M7QUFFRCx3QkFBZ0IsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLEVBQUUsb0JBQW9CLEdBQUcsbUJBQW1CLENBbUI1RztBQUVEOzs7R0FHRztBQUNILE1BQU0sV0FBVyw4QkFBK0IsU0FBUSxnQkFBZ0I7SUFDdEUsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLFdBQVcsRUFBRTtRQUFFLGFBQWEsRUFBRTtZQUFFLFFBQVEsSUFBSSxNQUFNLENBQUE7U0FBRSxDQUFBO0tBQUUsQ0FBQztDQUN4RDtBQUVEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsd0JBQXNCLDhCQUE4QixDQUNsRCxNQUFNLEVBQUUsOEJBQThCLEVBQ3RDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxPQUFPLENBQUMsbUJBQW1CLENBQUMsQ0FtQjlCIn0=
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQVFsRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxLQUFLLGdCQUFnQixFQUF3QixNQUFNLGFBQWEsQ0FBQztBQUUxRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRzFELE1BQU0sV0FBVyxvQkFBb0I7SUFDbkMsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2hCLDBDQUEwQztJQUMxQyxnQkFBZ0IsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLENBQUM7SUFDekMsMkNBQTJDO0lBQzNDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7Q0FDN0M7QUFFRCx3QkFBZ0IsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLEVBQUUsb0JBQW9CLEdBQUcsbUJBQW1CLENBbUI1RztBQUVEOzs7R0FHRztBQUNILE1BQU0sV0FBVyw4QkFBK0IsU0FBUSxnQkFBZ0I7SUFDdEUsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLFdBQVcsRUFBRTtRQUFFLGFBQWEsRUFBRTtZQUFFLFFBQVEsSUFBSSxNQUFNLENBQUE7U0FBRSxDQUFBO0tBQUUsQ0FBQztDQUN4RDtBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILHdCQUFzQiw4QkFBOEIsQ0FDbEQsTUFBTSxFQUFFLDhCQUE4QixFQUN0QyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBdUI5QiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACzC,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;CAC7C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,mBAAmB,CAmB5G;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE;QAAE,aAAa,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,8BAA8B,EACtC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAmB9B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACzC,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;CAC7C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,mBAAmB,CAmB5G;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE;QAAE,aAAa,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,8BAA8B,EACtC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAuB9B"}
@@ -30,10 +30,11 @@ export function createBlobClient(config, deps) {
30
30
  * 2. Creating read-only FileStore clients
31
31
  * 3. Creating a writable FileStore client for uploads
32
32
  * 4. Creating the BlobClient with these dependencies
33
+ * 5. Starting the client (uploads initial healthcheck file if upload client is configured)
33
34
  *
34
35
  * @param config - Configuration containing blob client settings and chain metadata
35
36
  * @param logger - Optional logger for the blob client
36
- * @returns A BlobClientInterface configured with file store support
37
+ * @returns A BlobClientInterface configured with file store support, already started
37
38
  */ export async function createBlobClientWithFileStores(config, logger) {
38
39
  const log = logger ?? createLogger('blob-client');
39
40
  const fileStoreMetadata = {
@@ -45,9 +46,11 @@ export function createBlobClient(config, deps) {
45
46
  createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, fileStoreMetadata, log),
46
47
  createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, fileStoreMetadata, log)
47
48
  ]);
48
- return createBlobClient(config, {
49
+ const client = createBlobClient(config, {
49
50
  logger: log,
50
51
  fileStoreClients,
51
52
  fileStoreUploadClient
52
53
  });
54
+ await client.start?.();
55
+ return client;
53
56
  }
@@ -13,6 +13,7 @@ export declare class HttpBlobClient implements BlobClientInterface {
13
13
  protected readonly fileStoreClients: FileStoreBlobClient[];
14
14
  protected readonly fileStoreUploadClient: FileStoreBlobClient | undefined;
15
15
  private disabled;
16
+ private healthcheckUploadIntervalId?;
16
17
  constructor(config?: BlobClientConfig, opts?: {
17
18
  logger?: Logger;
18
19
  archiveClient?: BlobArchiveClient;
@@ -59,5 +60,18 @@ export declare class HttpBlobClient implements BlobClientInterface {
59
60
  private getSlotNumber;
60
61
  /** @internal - exposed for testing */
61
62
  getArchiveClient(): BlobArchiveClient | undefined;
63
+ /**
64
+ * Start the blob client.
65
+ * Uploads the initial healthcheck file (awaited) and starts periodic uploads.
66
+ */
67
+ start(): Promise<void>;
68
+ /**
69
+ * Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
70
+ */
71
+ private startPeriodicHealthcheckUpload;
72
+ /**
73
+ * Stop the blob client, clearing any periodic tasks.
74
+ */
75
+ stop(): void;
62
76
  }
63
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBWXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVh2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBRXpCLFlBQ0UsTUFBTSxDQUFDLEVBQUUsZ0JBQWdCLEVBQ1IsSUFBSSxHQUFFO1FBQ3JCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUNoQixhQUFhLENBQUMsRUFBRSxpQkFBaUIsQ0FBQztRQUNsQyxnQkFBZ0IsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLENBQUM7UUFDekMscUJBQXFCLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztRQUM1Qyx5RUFBeUU7UUFDekUsY0FBYyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssSUFBSSxDQUFDO0tBQ3JDLEVBd0JQO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLHNCQUFzQjtJQVU5Qjs7Ozs7T0FLRztJQUNJLFdBQVcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxHQUFHLElBQUksQ0FHdkM7SUFFWSxXQUFXLGtCQW9FdkI7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQW1CakU7SUFFRDs7Ozs7Ozs7Ozs7Ozs7T0FjRztJQUNVLGNBQWMsQ0FDekIsU0FBUyxFQUFFLEtBQUssTUFBTSxFQUFFLEVBQ3hCLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFDcEIsSUFBSSxDQUFDLEVBQUUscUJBQXFCLEdBQzNCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQTZJakI7WUFRYSxhQUFhO0lBc0NkLGtCQUFrQixDQUM3QixPQUFPLEVBQUUsTUFBTSxFQUNmLGVBQWUsRUFBRSxNQUFNLEdBQUcsTUFBTSxFQUNoQyxVQUFVLEdBQUUsTUFBTSxFQUFPLEVBQ3pCLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FHakI7SUFFWSxnQkFBZ0IsQ0FDM0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsb0JBQW9CLENBQUMsRUFBRSxNQUFNLEdBQzVCLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQXNDckI7SUFFRCxPQUFPLENBQUMsaUJBQWlCO1lBWVgsbUJBQW1CO1lBbUNuQixhQUFhO0lBNkQzQixzQ0FBc0M7SUFDL0IsZ0JBQWdCLElBQUksaUJBQWlCLEdBQUcsU0FBUyxDQUV2RDtDQUNGIn0=
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBYXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVp2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBQ3pCLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQUFpQjtJQUVyRCxZQUNFLE1BQU0sQ0FBQyxFQUFFLGdCQUFnQixFQUNSLElBQUksR0FBRTtRQUNyQixNQUFNLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEIsYUFBYSxDQUFDLEVBQUUsaUJBQWlCLENBQUM7UUFDbEMsZ0JBQWdCLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxDQUFDO1FBQ3pDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7UUFDNUMseUVBQXlFO1FBQ3pFLGNBQWMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLElBQUksQ0FBQztLQUNyQyxFQXdCUDtJQUVEOzs7T0FHRztJQUNILE9BQU8sQ0FBQyxzQkFBc0I7SUFVOUI7Ozs7O09BS0c7SUFDSSxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sR0FBRyxJQUFJLENBR3ZDO0lBRVksV0FBVyxrQkFvRXZCO0lBRVksb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FtQmpFO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDVSxjQUFjLENBQ3pCLFNBQVMsRUFBRSxLQUFLLE1BQU0sRUFBRSxFQUN4QixVQUFVLEVBQUUsTUFBTSxFQUFFLEVBQ3BCLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUMzQixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0E2SWpCO1lBUWEsYUFBYTtJQXNDZCxrQkFBa0IsQ0FDN0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsVUFBVSxHQUFFLE1BQU0sRUFBTyxFQUN6QixvQkFBb0IsQ0FBQyxFQUFFLE1BQU0sR0FDNUIsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBR2pCO0lBRVksZ0JBQWdCLENBQzNCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsZUFBZSxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQ2hDLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FzQ3JCO0lBRUQsT0FBTyxDQUFDLGlCQUFpQjtZQVlYLG1CQUFtQjtZQW1DbkIsYUFBYTtJQTZEM0Isc0NBQXNDO0lBQy9CLGdCQUFnQixJQUFJLGlCQUFpQixHQUFHLFNBQVMsQ0FFdkQ7SUFFRDs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVNsQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLDhCQUE4QjtJQVd0Qzs7T0FFRztJQUNJLElBQUksSUFBSSxJQUFJLENBS2xCO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;IAYtD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IAC3D,SAAS,CAAC,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE1E,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACE,MAAM,CAAC,EAAE,gBAAgB,EACR,IAAI,GAAE;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,iBAAiB,CAAC;QAClC,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;QACzC,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;QAC5C,yEAAyE;QACzE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;KACrC,EAwBP;IAED;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAU9B;;;;;OAKG;IACI,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGvC;IAEY,WAAW,kBAoEvB;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBjE;IAED;;;;;;;;;;;;;;OAcG;IACU,cAAc,CACzB,SAAS,EAAE,KAAK,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,CA6IjB;YAQa,aAAa;IAsCd,kBAAkB,CAC7B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,UAAU,GAAE,MAAM,EAAO,EACzB,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,IAAI,EAAE,CAAC,CAGjB;IAEY,gBAAgB,CAC3B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCrB;IAED,OAAO,CAAC,iBAAiB;YAYX,mBAAmB;YAmCnB,aAAa;IA6D3B,sCAAsC;IAC/B,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;CACF"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;IAatD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IAC3D,SAAS,CAAC,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE1E,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,2BAA2B,CAAC,CAAiB;IAErD,YACE,MAAM,CAAC,EAAE,gBAAgB,EACR,IAAI,GAAE;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,iBAAiB,CAAC;QAClC,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;QACzC,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;QAC5C,yEAAyE;QACzE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;KACrC,EAwBP;IAED;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAU9B;;;;;OAKG;IACI,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGvC;IAEY,WAAW,kBAoEvB;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBjE;IAED;;;;;;;;;;;;;;OAcG;IACU,cAAc,CACzB,SAAS,EAAE,KAAK,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,CA6IjB;YAQa,aAAa;IAsCd,kBAAkB,CAC7B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,UAAU,GAAE,MAAM,EAAO,EACzB,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,IAAI,EAAE,CAAC,CAGjB;IAEY,gBAAgB,CAC3B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCrB;IAED,OAAO,CAAC,iBAAiB;YAYX,mBAAmB;YAmCnB,aAAa;IA6D3B,sCAAsC;IAC/B,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;IAED;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CASlC;IAED;;OAEG;IACH,OAAO,CAAC,8BAA8B;IAWtC;;OAEG;IACI,IAAI,IAAI,IAAI,CAKlB;CACF"}
@@ -5,6 +5,7 @@ import { makeBackoff, retry } from '@aztec/foundation/retry';
5
5
  import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
6
6
  import { createPublicClient, fallback, http } from 'viem';
7
7
  import { createBlobArchiveClient } from '../archive/factory.js';
8
+ import { DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES } from '../filestore/healthcheck.js';
8
9
  import { getBlobClientConfigFromEnv } from './config.js';
9
10
  export class HttpBlobClient {
10
11
  opts;
@@ -15,6 +16,7 @@ export class HttpBlobClient {
15
16
  fileStoreClients;
16
17
  fileStoreUploadClient;
17
18
  disabled;
19
+ healthcheckUploadIntervalId;
18
20
  constructor(config, opts = {}){
19
21
  this.opts = opts;
20
22
  this.disabled = false;
@@ -474,6 +476,35 @@ export class HttpBlobClient {
474
476
  /** @internal - exposed for testing */ getArchiveClient() {
475
477
  return this.archiveClient;
476
478
  }
479
+ /**
480
+ * Start the blob client.
481
+ * Uploads the initial healthcheck file (awaited) and starts periodic uploads.
482
+ */ async start() {
483
+ if (!this.fileStoreUploadClient) {
484
+ return;
485
+ }
486
+ await this.fileStoreUploadClient.uploadHealthcheck();
487
+ this.log.debug('Initial healthcheck file uploaded');
488
+ this.startPeriodicHealthcheckUpload();
489
+ }
490
+ /**
491
+ * Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
492
+ */ startPeriodicHealthcheckUpload() {
493
+ const intervalMs = (this.config.blobHealthcheckUploadIntervalMinutes ?? DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES) * 60 * 1000;
494
+ this.healthcheckUploadIntervalId = setInterval(()=>{
495
+ void this.fileStoreUploadClient.uploadHealthcheck().catch((err)=>{
496
+ this.log.warn('Failed to upload periodic healthcheck file', err);
497
+ });
498
+ }, intervalMs);
499
+ }
500
+ /**
501
+ * Stop the blob client, clearing any periodic tasks.
502
+ */ stop() {
503
+ if (this.healthcheckUploadIntervalId) {
504
+ clearInterval(this.healthcheckUploadIntervalId);
505
+ this.healthcheckUploadIntervalId = undefined;
506
+ }
507
+ }
477
508
  }
478
509
  function parseBlobJsonsFromResponse(response, logger) {
479
510
  try {
@@ -16,7 +16,11 @@ export interface BlobClientInterface {
16
16
  sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
17
17
  /** Fetches the given blob sidecars by block hash and blob hashes. */
18
18
  getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
19
+ /** Starts the blob client (e.g., uploads healthcheck file if not exists). */
20
+ start?(): Promise<void>;
19
21
  /** Tests all configured blob sources and logs whether they are reachable or not. */
20
22
  testSources(): Promise<void>;
23
+ /** Stops the blob client, clearing any periodic tasks. */
24
+ stop?(): void;
21
25
  }
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLG9GQUFvRjtJQUNwRixXQUFXLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQzlCIn0=
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLDZFQUE2RTtJQUM3RSxLQUFLLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsb0ZBQW9GO0lBQ3BGLFdBQVcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDN0IsMERBQTBEO0lBQzFELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQztDQUNmIn0=
@@ -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;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,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;CACf"}
@@ -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": "3.0.0-nightly.20260105",
3
+ "version": "3.0.0-nightly.20260106",
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": "3.0.0-nightly.20260105",
60
- "@aztec/ethereum": "3.0.0-nightly.20260105",
61
- "@aztec/foundation": "3.0.0-nightly.20260105",
62
- "@aztec/kv-store": "3.0.0-nightly.20260105",
63
- "@aztec/stdlib": "3.0.0-nightly.20260105",
64
- "@aztec/telemetry-client": "3.0.0-nightly.20260105",
59
+ "@aztec/blob-lib": "3.0.0-nightly.20260106",
60
+ "@aztec/ethereum": "3.0.0-nightly.20260106",
61
+ "@aztec/foundation": "3.0.0-nightly.20260106",
62
+ "@aztec/kv-store": "3.0.0-nightly.20260106",
63
+ "@aztec/stdlib": "3.0.0-nightly.20260106",
64
+ "@aztec/telemetry-client": "3.0.0-nightly.20260106",
65
65
  "express": "^4.21.2",
66
66
  "snappy": "^7.2.2",
67
67
  "source-map-support": "^0.5.21",
@@ -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
  }
@@ -9,6 +9,7 @@ import { type RpcBlock, createPublicClient, fallback, http } from 'viem';
9
9
  import { createBlobArchiveClient } from '../archive/factory.js';
10
10
  import type { BlobArchiveClient } from '../archive/interface.js';
11
11
  import type { FileStoreBlobClient } from '../filestore/filestore_blob_client.js';
12
+ import { DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES } from '../filestore/healthcheck.js';
12
13
  import { type BlobClientConfig, getBlobClientConfigFromEnv } from './config.js';
13
14
  import type { BlobClientInterface, GetBlobSidecarOptions } from './interface.js';
14
15
 
@@ -21,6 +22,7 @@ export class HttpBlobClient implements BlobClientInterface {
21
22
  protected readonly fileStoreUploadClient: FileStoreBlobClient | undefined;
22
23
 
23
24
  private disabled = false;
25
+ private healthcheckUploadIntervalId?: NodeJS.Timeout;
24
26
 
25
27
  constructor(
26
28
  config?: BlobClientConfig,
@@ -545,6 +547,45 @@ export class HttpBlobClient implements BlobClientInterface {
545
547
  public getArchiveClient(): BlobArchiveClient | undefined {
546
548
  return this.archiveClient;
547
549
  }
550
+
551
+ /**
552
+ * Start the blob client.
553
+ * Uploads the initial healthcheck file (awaited) and starts periodic uploads.
554
+ */
555
+ public async start(): Promise<void> {
556
+ if (!this.fileStoreUploadClient) {
557
+ return;
558
+ }
559
+
560
+ await this.fileStoreUploadClient.uploadHealthcheck();
561
+ this.log.debug('Initial healthcheck file uploaded');
562
+
563
+ this.startPeriodicHealthcheckUpload();
564
+ }
565
+
566
+ /**
567
+ * Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
568
+ */
569
+ private startPeriodicHealthcheckUpload(): void {
570
+ const intervalMs =
571
+ (this.config.blobHealthcheckUploadIntervalMinutes ?? DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES) * 60 * 1000;
572
+
573
+ this.healthcheckUploadIntervalId = setInterval(() => {
574
+ void this.fileStoreUploadClient!.uploadHealthcheck().catch(err => {
575
+ this.log.warn('Failed to upload periodic healthcheck file', err);
576
+ });
577
+ }, intervalMs);
578
+ }
579
+
580
+ /**
581
+ * Stop the blob client, clearing any periodic tasks.
582
+ */
583
+ public stop(): void {
584
+ if (this.healthcheckUploadIntervalId) {
585
+ clearInterval(this.healthcheckUploadIntervalId);
586
+ this.healthcheckUploadIntervalId = undefined;
587
+ }
588
+ }
548
589
  }
549
590
 
550
591
  function parseBlobJsonsFromResponse(response: any, logger: Logger): BlobJson[] {
@@ -18,6 +18,10 @@ export interface BlobClientInterface {
18
18
  sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
19
19
  /** Fetches the given blob sidecars by block hash and blob hashes. */
20
20
  getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
21
+ /** Starts the blob client (e.g., uploads healthcheck file if not exists). */
22
+ start?(): Promise<void>;
21
23
  /** Tests all configured blob sources and logs whether they are reachable or not. */
22
24
  testSources(): Promise<void>;
25
+ /** Stops the blob client, clearing any periodic tasks. */
26
+ stop?(): void;
23
27
  }
@@ -3,6 +3,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import type { FileStore, ReadOnlyFileStore } from '@aztec/stdlib/file-store';
4
4
 
5
5
  import { inboundTransform, outboundTransform } from '../encoding/index.js';
6
+ import { HEALTHCHECK_CONTENT, HEALTHCHECK_FILENAME } from './healthcheck.js';
6
7
 
7
8
  /**
8
9
  * A blob client that uses a FileStore (S3/GCS/local) as the data source.
@@ -27,6 +28,14 @@ export class FileStoreBlobClient {
27
28
  return `${this.basePath}/blobs/${versionedBlobHash}.data`;
28
29
  }
29
30
 
31
+ /**
32
+ * Get the path for the healthcheck file.
33
+ * Format: basePath/.healthcheck
34
+ */
35
+ private healthcheckPath(): string {
36
+ return `${this.basePath}/${HEALTHCHECK_FILENAME}`;
37
+ }
38
+
30
39
  /**
31
40
  * Fetch blobs by their versioned hashes.
32
41
  * @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
@@ -104,12 +113,31 @@ export class FileStoreBlobClient {
104
113
  }
105
114
 
106
115
  /**
107
- * Test if the filestore connection is working.
116
+ * Test if the filestore connection is working by checking for healthcheck file.
117
+ * The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
118
+ * This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
119
+ */
120
+ async testConnection(): Promise<boolean> {
121
+ try {
122
+ return await this.store.exists(this.healthcheckPath());
123
+ } catch (err: any) {
124
+ this.log.warn(`Connection test failed: ${err?.message ?? String(err)}`);
125
+ return false;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Upload the healthcheck file if it doesn't already exist.
131
+ * This enables read-only clients (HTTP) to verify connectivity.
108
132
  */
109
- testConnection(): Promise<boolean> {
110
- // This implementation will be improved in a separate PR
111
- // Currently underlying filestore implementations do not expose an easy way to test connectivitiy
112
- return Promise.resolve(true);
133
+ async uploadHealthcheck(): Promise<void> {
134
+ if (!this.isWritable()) {
135
+ this.log.trace('Cannot upload healthcheck: store is read-only');
136
+ return;
137
+ }
138
+ const path = this.healthcheckPath();
139
+ await (this.store as FileStore).save(path, Buffer.from(HEALTHCHECK_CONTENT));
140
+ this.log.debug(`Uploaded healthcheck file to ${path}`);
113
141
  }
114
142
 
115
143
  /**
@@ -0,0 +1,5 @@
1
+ /** Constants for healthcheck file used to test file store connectivity. */
2
+
3
+ export const HEALTHCHECK_FILENAME = '.healthcheck';
4
+ export const HEALTHCHECK_CONTENT = 'ok';
5
+ export const DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES = 60; // 1 hour