@aztec/blob-client 3.0.0-nightly.20251223

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 (104) hide show
  1. package/README.md +62 -0
  2. package/dest/archive/blobscan_archive_client.d.ts +147 -0
  3. package/dest/archive/blobscan_archive_client.d.ts.map +1 -0
  4. package/dest/archive/blobscan_archive_client.js +141 -0
  5. package/dest/archive/config.d.ts +7 -0
  6. package/dest/archive/config.d.ts.map +1 -0
  7. package/dest/archive/config.js +11 -0
  8. package/dest/archive/factory.d.ts +4 -0
  9. package/dest/archive/factory.d.ts.map +1 -0
  10. package/dest/archive/factory.js +7 -0
  11. package/dest/archive/index.d.ts +3 -0
  12. package/dest/archive/index.d.ts.map +1 -0
  13. package/dest/archive/index.js +2 -0
  14. package/dest/archive/instrumentation.d.ts +11 -0
  15. package/dest/archive/instrumentation.d.ts.map +1 -0
  16. package/dest/archive/instrumentation.js +33 -0
  17. package/dest/archive/interface.d.ts +13 -0
  18. package/dest/archive/interface.d.ts.map +1 -0
  19. package/dest/archive/interface.js +1 -0
  20. package/dest/blobstore/blob_store_test_suite.d.ts +3 -0
  21. package/dest/blobstore/blob_store_test_suite.d.ts.map +1 -0
  22. package/dest/blobstore/blob_store_test_suite.js +164 -0
  23. package/dest/blobstore/index.d.ts +3 -0
  24. package/dest/blobstore/index.d.ts.map +1 -0
  25. package/dest/blobstore/index.js +2 -0
  26. package/dest/blobstore/interface.d.ts +12 -0
  27. package/dest/blobstore/interface.d.ts.map +1 -0
  28. package/dest/blobstore/interface.js +1 -0
  29. package/dest/blobstore/memory_blob_store.d.ts +8 -0
  30. package/dest/blobstore/memory_blob_store.d.ts.map +1 -0
  31. package/dest/blobstore/memory_blob_store.js +24 -0
  32. package/dest/client/bin/index.d.ts +3 -0
  33. package/dest/client/bin/index.d.ts.map +1 -0
  34. package/dest/client/bin/index.js +30 -0
  35. package/dest/client/config.d.ts +50 -0
  36. package/dest/client/config.d.ts.map +1 -0
  37. package/dest/client/config.js +55 -0
  38. package/dest/client/factory.d.ts +39 -0
  39. package/dest/client/factory.d.ts.map +1 -0
  40. package/dest/client/factory.js +53 -0
  41. package/dest/client/http.d.ts +63 -0
  42. package/dest/client/http.d.ts.map +1 -0
  43. package/dest/client/http.js +536 -0
  44. package/dest/client/index.d.ts +6 -0
  45. package/dest/client/index.d.ts.map +1 -0
  46. package/dest/client/index.js +5 -0
  47. package/dest/client/interface.d.ts +23 -0
  48. package/dest/client/interface.d.ts.map +1 -0
  49. package/dest/client/interface.js +1 -0
  50. package/dest/client/local.d.ts +12 -0
  51. package/dest/client/local.d.ts.map +1 -0
  52. package/dest/client/local.js +18 -0
  53. package/dest/client/tests.d.ts +11 -0
  54. package/dest/client/tests.d.ts.map +1 -0
  55. package/dest/client/tests.js +65 -0
  56. package/dest/encoding/index.d.ts +15 -0
  57. package/dest/encoding/index.d.ts.map +1 -0
  58. package/dest/encoding/index.js +19 -0
  59. package/dest/filestore/factory.d.ts +50 -0
  60. package/dest/filestore/factory.d.ts.map +1 -0
  61. package/dest/filestore/factory.js +67 -0
  62. package/dest/filestore/filestore_blob_client.d.ts +56 -0
  63. package/dest/filestore/filestore_blob_client.d.ts.map +1 -0
  64. package/dest/filestore/filestore_blob_client.js +99 -0
  65. package/dest/filestore/index.d.ts +3 -0
  66. package/dest/filestore/index.d.ts.map +1 -0
  67. package/dest/filestore/index.js +2 -0
  68. package/dest/types/api.d.ts +65 -0
  69. package/dest/types/api.d.ts.map +1 -0
  70. package/dest/types/api.js +22 -0
  71. package/dest/types/blob_with_index.d.ts +25 -0
  72. package/dest/types/blob_with_index.d.ts.map +1 -0
  73. package/dest/types/blob_with_index.js +43 -0
  74. package/dest/types/index.d.ts +2 -0
  75. package/dest/types/index.d.ts.map +1 -0
  76. package/dest/types/index.js +1 -0
  77. package/package.json +95 -0
  78. package/src/archive/blobscan_archive_client.ts +178 -0
  79. package/src/archive/config.ts +14 -0
  80. package/src/archive/factory.ts +11 -0
  81. package/src/archive/fixtures/blobscan_get_blob_data.json +1 -0
  82. package/src/archive/fixtures/blobscan_get_block.json +56 -0
  83. package/src/archive/index.ts +2 -0
  84. package/src/archive/instrumentation.ts +41 -0
  85. package/src/archive/interface.ts +9 -0
  86. package/src/blobstore/blob_store_test_suite.ts +137 -0
  87. package/src/blobstore/index.ts +2 -0
  88. package/src/blobstore/interface.ts +12 -0
  89. package/src/blobstore/memory_blob_store.ts +31 -0
  90. package/src/client/bin/index.ts +35 -0
  91. package/src/client/config.ts +117 -0
  92. package/src/client/factory.ts +88 -0
  93. package/src/client/http.ts +620 -0
  94. package/src/client/index.ts +5 -0
  95. package/src/client/interface.ts +30 -0
  96. package/src/client/local.ts +32 -0
  97. package/src/client/tests.ts +78 -0
  98. package/src/encoding/index.ts +21 -0
  99. package/src/filestore/factory.ts +145 -0
  100. package/src/filestore/filestore_blob_client.ts +129 -0
  101. package/src/filestore/index.ts +2 -0
  102. package/src/types/api.ts +50 -0
  103. package/src/types/blob_with_index.ts +48 -0
  104. package/src/types/index.ts +1 -0
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ ## Blob Client
2
+
3
+ A client library for storing and retrieving blob data from various sources.
4
+
5
+ ## When is this used?
6
+
7
+ The blob client is used by nodes to store and retrieve blob data that accompanies `propose` transactions on L1.
8
+
9
+ ### Why?
10
+
11
+ Blob data is only available in the L1 consensus layer for a limited period (~3 weeks). The blob client provides a unified interface for:
12
+ - Fetching blobs from L1 consensus layer (beacon nodes)
13
+ - Storing/retrieving blobs from file stores (local files, S3, GCS)
14
+ - Caching blobs locally for faster access
15
+
16
+ ### How?
17
+
18
+ The blob client supports multiple blob sources:
19
+ 1. **File Store**: Local filesystem (`file://`), S3 (`s3://`), or GCS (`gs://`)
20
+ 2. **L1 Consensus**: Beacon node API for recent blobs
21
+ 3. **Archive**: Blobscan API for historical blobs
22
+
23
+ ### Configurations
24
+
25
+ **File Store URLs** (`BLOB_FILE_STORE_URLS`):
26
+ Comma-separated list of URLs to read blobs from. Tried in order until blobs are found.
27
+
28
+ **File Store Upload URL** (`BLOB_FILE_STORE_UPLOAD_URL`):
29
+ URL for uploading blobs to a file store.
30
+
31
+ **L1 Consensus Host URLs** (`L1_CONSENSUS_HOST_URLS`):
32
+ Beacon node URLs for fetching recent blobs directly from L1.
33
+
34
+ **Archive API URL** (`BLOB_SINK_ARCHIVE_API_URL`):
35
+ Blobscan or similar archive API for historical blob data.
36
+
37
+ ### Example Usage
38
+
39
+ ```typescript
40
+ import { createBlobClient, createBlobClientWithFileStores } from '@aztec/blob-client/client';
41
+
42
+ // Simple client with L1 consensus and archive sources
43
+ const client = createBlobClient({
44
+ l1ConsensusHostUrls: ['https://beacon.example.com'],
45
+ archiveApiUrl: 'https://api.blobscan.com',
46
+ });
47
+
48
+ // Client with file store support
49
+ const clientWithFileStore = await createBlobClientWithFileStores({
50
+ l1ChainId: 1,
51
+ rollupVersion: 1,
52
+ l1Contracts: { rollupAddress: { toString: () => '0x...' } },
53
+ blobFileStoreUrls: ['s3://bucket/blobs', 'file:///local/blobs'],
54
+ blobFileStoreUploadUrl: 's3://bucket/blobs',
55
+ });
56
+
57
+ // Fetch blobs
58
+ const blobs = await client.getBlobSidecar(blockHash, blobHashes);
59
+
60
+ // Upload blobs to file store
61
+ await client.sendBlobsToFilestore(blobs);
62
+ ```
@@ -0,0 +1,147 @@
1
+ import type { BlobJson } from '@aztec/blob-lib/types';
2
+ import { type TelemetryClient } from '@aztec/telemetry-client';
3
+ import { z } from 'zod';
4
+ import type { BlobArchiveClient } from './interface.js';
5
+ export declare const BlobscanBlockResponseSchema: z.ZodEffects<z.ZodObject<{
6
+ hash: z.ZodString;
7
+ slot: z.ZodNumber;
8
+ number: z.ZodNumber;
9
+ transactions: z.ZodArray<z.ZodObject<{
10
+ hash: z.ZodString;
11
+ blobs: z.ZodArray<z.ZodObject<{
12
+ versionedHash: z.ZodString;
13
+ data: z.ZodString;
14
+ commitment: z.ZodString;
15
+ proof: z.ZodString;
16
+ size: z.ZodNumber;
17
+ index: z.ZodOptional<z.ZodNumber>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ versionedHash: string;
20
+ data: string;
21
+ commitment: string;
22
+ proof: string;
23
+ size: number;
24
+ index?: number | undefined;
25
+ }, {
26
+ versionedHash: string;
27
+ data: string;
28
+ commitment: string;
29
+ proof: string;
30
+ size: number;
31
+ index?: number | undefined;
32
+ }>, "many">;
33
+ }, "strip", z.ZodTypeAny, {
34
+ hash: string;
35
+ blobs: {
36
+ versionedHash: string;
37
+ data: string;
38
+ commitment: string;
39
+ proof: string;
40
+ size: number;
41
+ index?: number | undefined;
42
+ }[];
43
+ }, {
44
+ hash: string;
45
+ blobs: {
46
+ versionedHash: string;
47
+ data: string;
48
+ commitment: string;
49
+ proof: string;
50
+ size: number;
51
+ index?: number | undefined;
52
+ }[];
53
+ }>, "many">;
54
+ }, "strip", z.ZodTypeAny, {
55
+ hash: string;
56
+ slot: number;
57
+ number: number;
58
+ transactions: {
59
+ hash: string;
60
+ blobs: {
61
+ versionedHash: string;
62
+ data: string;
63
+ commitment: string;
64
+ proof: string;
65
+ size: number;
66
+ index?: number | undefined;
67
+ }[];
68
+ }[];
69
+ }, {
70
+ hash: string;
71
+ slot: number;
72
+ number: number;
73
+ transactions: {
74
+ hash: string;
75
+ blobs: {
76
+ versionedHash: string;
77
+ data: string;
78
+ commitment: string;
79
+ proof: string;
80
+ size: number;
81
+ index?: number | undefined;
82
+ }[];
83
+ }[];
84
+ }>, {
85
+ blob: string;
86
+ kzg_commitment: string;
87
+ index: string;
88
+ }[], {
89
+ hash: string;
90
+ slot: number;
91
+ number: number;
92
+ transactions: {
93
+ hash: string;
94
+ blobs: {
95
+ versionedHash: string;
96
+ data: string;
97
+ commitment: string;
98
+ proof: string;
99
+ size: number;
100
+ index?: number | undefined;
101
+ }[];
102
+ }[];
103
+ }>;
104
+ export declare const BlobscanBlocksResponseSchema: z.ZodObject<{
105
+ blocks: z.ZodArray<z.ZodObject<{
106
+ hash: z.ZodString;
107
+ slot: z.ZodNumber;
108
+ number: z.ZodNumber;
109
+ }, "strip", z.ZodTypeAny, {
110
+ hash: string;
111
+ slot: number;
112
+ number: number;
113
+ }, {
114
+ hash: string;
115
+ slot: number;
116
+ number: number;
117
+ }>, "many">;
118
+ }, "strip", z.ZodTypeAny, {
119
+ blocks: {
120
+ hash: string;
121
+ slot: number;
122
+ number: number;
123
+ }[];
124
+ }, {
125
+ blocks: {
126
+ hash: string;
127
+ slot: number;
128
+ number: number;
129
+ }[];
130
+ }>;
131
+ export declare class BlobscanArchiveClient implements BlobArchiveClient {
132
+ private readonly logger;
133
+ private readonly fetchOpts;
134
+ private readonly fetch;
135
+ private readonly baseUrl;
136
+ private instrumentation;
137
+ constructor(baseUrl: string, telemetry?: TelemetryClient);
138
+ getLatestBlock(): Promise<{
139
+ hash: string;
140
+ number: number;
141
+ slot: number;
142
+ }>;
143
+ getBaseUrl(): string;
144
+ getBlobsFromBlock(blockId: string): Promise<BlobJson[] | undefined>;
145
+ getBlobData(id: string): Promise<Buffer | undefined>;
146
+ }
147
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYnNjYW5fYXJjaGl2ZV9jbGllbnQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcmNoaXZlL2Jsb2JzY2FuX2FyY2hpdmVfY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBSXRELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQUVuRixPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBR3hCLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEQsZUFBTyxNQUFNLDJCQUEyQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFpQ3ZDLENBQUM7QUFHRixlQUFPLE1BQU0sNEJBQTRCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQVF2QyxDQUFDO0FBRUgscUJBQWEscUJBQXNCLFlBQVcsaUJBQWlCO0lBQzdELE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUF1RDtJQUM5RSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBK0M7SUFDekUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBU3BCO0lBRUYsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQU07SUFFOUIsT0FBTyxDQUFDLGVBQWUsQ0FBbUM7SUFFMUQsWUFBWSxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsR0FBRSxlQUFzQyxFQVU3RTtJQUVZLGNBQWMsSUFBSSxPQUFPLENBQUM7UUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDO1FBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQztRQUFDLElBQUksRUFBRSxNQUFNLENBQUE7S0FBRSxDQUFDLENBMkJyRjtJQUVNLFVBQVUsSUFBSSxNQUFNLENBRTFCO0lBRVksaUJBQWlCLENBQUMsT0FBTyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLEdBQUcsU0FBUyxDQUFDLENBNEIvRTtJQUVZLFdBQVcsQ0FBQyxFQUFFLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFDLENBMEJoRTtDQUNGIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blobscan_archive_client.d.ts","sourceRoot":"","sources":["../../src/archive/blobscan_archive_client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEnF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCvC,CAAC;AAGF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;EAQvC,CAAC;AAEH,qBAAa,qBAAsB,YAAW,iBAAiB;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuD;IAC9E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+C;IACzE,OAAO,CAAC,QAAQ,CAAC,KAAK,CASpB;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAE9B,OAAO,CAAC,eAAe,CAAmC;IAE1D,YAAY,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,eAAsC,EAU7E;IAEY,cAAc,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CA2BrF;IAEM,UAAU,IAAI,MAAM,CAE1B;IAEY,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CA4B/E;IAEY,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA0BhE;CACF"}
@@ -0,0 +1,141 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import { makeBackoff, retry } from '@aztec/foundation/retry';
3
+ import { schemas, zodFor } from '@aztec/foundation/schemas';
4
+ import { getTelemetryClient } from '@aztec/telemetry-client';
5
+ import { z } from 'zod';
6
+ import { BlobArchiveClientInstrumentation } from './instrumentation.js';
7
+ export const BlobscanBlockResponseSchema = zodFor()(z.object({
8
+ hash: z.string(),
9
+ slot: z.number().int(),
10
+ number: z.number().int(),
11
+ transactions: z.array(z.object({
12
+ hash: z.string(),
13
+ blobs: z.array(z.object({
14
+ versionedHash: z.string(),
15
+ data: z.string(),
16
+ commitment: z.string(),
17
+ proof: z.string(),
18
+ size: z.number().int(),
19
+ index: z.number().int().optional()
20
+ }))
21
+ }))
22
+ }).transform((data)=>data.transactions.flatMap((tx)=>tx.blobs.map((blob)=>({
23
+ blob: blob.data,
24
+ // eslint-disable-next-line camelcase
25
+ kzg_commitment: blob.commitment
26
+ }))).map((blob, index)=>({
27
+ ...blob,
28
+ index: index.toString()
29
+ }))));
30
+ // Response from https://api.blobscan.com/blocks?sort=desc&type=canonical
31
+ export const BlobscanBlocksResponseSchema = z.object({
32
+ blocks: z.array(z.object({
33
+ hash: z.string(),
34
+ slot: z.number().int(),
35
+ number: z.number().int()
36
+ }))
37
+ });
38
+ export class BlobscanArchiveClient {
39
+ logger = createLogger('blob-client:blobscan-archive-client');
40
+ fetchOpts = {
41
+ headers: {
42
+ accept: 'application/json'
43
+ }
44
+ };
45
+ fetch = async (...args)=>{
46
+ return await retry(()=>fetch(...args), // eslint-disable-next-line @typescript-eslint/no-base-to-string
47
+ `Fetching ${args[0]}`, makeBackoff([
48
+ 1,
49
+ 1,
50
+ 3
51
+ ]), this.logger, /*failSilently=*/ false);
52
+ };
53
+ baseUrl;
54
+ instrumentation;
55
+ constructor(baseUrl, telemetry = getTelemetryClient()){
56
+ this.baseUrl = new URL(baseUrl);
57
+ if (this.baseUrl.protocol !== 'https:') {
58
+ throw new TypeError('BaseURL must be secure: ' + baseUrl);
59
+ }
60
+ this.instrumentation = new BlobArchiveClientInstrumentation(telemetry, this.baseUrl.origin, 'BlobscanArchiveClient');
61
+ }
62
+ async getLatestBlock() {
63
+ const url = new URL('blocks', this.baseUrl);
64
+ url.searchParams.set('sort', 'desc');
65
+ url.searchParams.set('type', 'canonical');
66
+ url.searchParams.set('p', '1');
67
+ url.searchParams.set('ps', '1');
68
+ this.logger.trace(`Fetching latest block from ${url.href}`);
69
+ const response = await this.fetch(url, this.fetchOpts);
70
+ if (response.status !== 200) {
71
+ throw new Error(`Failed to fetch latest block: ${response.statusText} (${response.status})`, {
72
+ cause: {
73
+ httpResponse: {
74
+ status: response.status,
75
+ body: await response.text().catch(()=>'Failed to read response body')
76
+ }
77
+ }
78
+ });
79
+ }
80
+ const parsed = await response.json().then((data)=>BlobscanBlocksResponseSchema.parse(data));
81
+ if (parsed.blocks.length === 0) {
82
+ throw new Error(`No blocks found at ${this.baseUrl}`);
83
+ }
84
+ return parsed.blocks[0];
85
+ }
86
+ getBaseUrl() {
87
+ return this.baseUrl.href;
88
+ }
89
+ async getBlobsFromBlock(blockId) {
90
+ const url = new URL(`blocks/${blockId}`, this.baseUrl);
91
+ url.searchParams.set('type', 'canonical');
92
+ url.searchParams.set('expand', 'blob,blob_data');
93
+ this.logger.trace(`Fetching blobs for block ${blockId} from ${url.href}`);
94
+ const response = await this.fetch(url, this.fetchOpts);
95
+ this.instrumentation.incRequest('blocks', response.status);
96
+ if (response.status === 404) {
97
+ this.logger.debug(`No blobs found for block ${blockId} at ${this.baseUrl}`);
98
+ return undefined;
99
+ } else if (response.status !== 200) {
100
+ throw new Error(`Failed to fetch blobs for block ${blockId}: ${response.statusText} (${response.status})`, {
101
+ cause: {
102
+ httpResponse: {
103
+ status: response.status,
104
+ body: await response.text().catch(()=>'Failed to read response body')
105
+ }
106
+ }
107
+ });
108
+ } else {
109
+ const result = await response.json().then((data)=>BlobscanBlockResponseSchema.parse(data));
110
+ this.logger.debug(`Fetched ${result.length} blobs for block ${blockId} from ${this.baseUrl}`);
111
+ this.instrumentation.incRetrievedBlobs(result.length);
112
+ return result;
113
+ }
114
+ }
115
+ async getBlobData(id) {
116
+ const url = new URL(`blobs/${id}/data`, this.baseUrl);
117
+ const response = await this.fetch(url, this.fetchOpts);
118
+ this.instrumentation.incRequest('blobs', response.status);
119
+ if (response.status === 404) {
120
+ return undefined;
121
+ } else if (response.status !== 200) {
122
+ throw new Error(`Failed to fetch blob data for blob ${id}: ${response.statusText} (${response.status})`, {
123
+ cause: {
124
+ httpResponse: {
125
+ status: response.status,
126
+ body: await response.text().catch((err)=>{
127
+ this.logger.warn('Failed to read response body', err);
128
+ return '';
129
+ })
130
+ }
131
+ }
132
+ });
133
+ } else {
134
+ return await response.json().then((data)=>{
135
+ const blob = schemas.BufferHex.parse(data);
136
+ this.instrumentation.incRetrievedBlobs(1);
137
+ return blob;
138
+ });
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,7 @@
1
+ import { type L1ReaderConfig } from '@aztec/ethereum/l1-reader';
2
+ import { type ConfigMappingsType } from '@aztec/foundation/config';
3
+ export type BlobArchiveApiConfig = {
4
+ archiveApiUrl?: string;
5
+ } & Partial<Pick<L1ReaderConfig, 'l1ChainId'>>;
6
+ export declare const blobArchiveApiConfigMappings: ConfigMappingsType<BlobArchiveApiConfig>;
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXJjaGl2ZS9jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssY0FBYyxFQUEwQixNQUFNLDJCQUEyQixDQUFDO0FBQ3hGLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUFzQixNQUFNLDBCQUEwQixDQUFDO0FBRXZGLE1BQU0sTUFBTSxvQkFBb0IsR0FBRztJQUNqQyxhQUFhLENBQUMsRUFBRSxNQUFNLENBQUM7Q0FDeEIsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBRS9DLGVBQU8sTUFBTSw0QkFBNEIsRUFBRSxrQkFBa0IsQ0FBQyxvQkFBb0IsQ0FNakYsQ0FBQyJ9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/archive/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,KAAK,kBAAkB,EAAsB,MAAM,0BAA0B,CAAC;AAEvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AAE/C,eAAO,MAAM,4BAA4B,EAAE,kBAAkB,CAAC,oBAAoB,CAMjF,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { l1ReaderConfigMappings } from '@aztec/ethereum/l1-reader';
2
+ import { pickConfigMappings } from '@aztec/foundation/config';
3
+ export const blobArchiveApiConfigMappings = {
4
+ archiveApiUrl: {
5
+ env: 'BLOB_SINK_ARCHIVE_API_URL',
6
+ description: 'The URL of the archive API'
7
+ },
8
+ ...pickConfigMappings(l1ReaderConfigMappings, [
9
+ 'l1ChainId'
10
+ ])
11
+ };
@@ -0,0 +1,4 @@
1
+ import type { BlobClientConfig } from '../client/config.js';
2
+ import type { BlobArchiveClient } from './interface.js';
3
+ export declare function createBlobArchiveClient(config: BlobClientConfig): BlobArchiveClient | undefined;
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FyY2hpdmUvZmFjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTVELE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEQsd0JBQWdCLHVCQUF1QixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsR0FBRyxpQkFBaUIsR0FBRyxTQUFTLENBTS9GIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/archive/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,SAAS,CAM/F"}
@@ -0,0 +1,7 @@
1
+ import { BlobscanArchiveClient } from './blobscan_archive_client.js';
2
+ export function createBlobArchiveClient(config) {
3
+ if (config.archiveApiUrl) {
4
+ return new BlobscanArchiveClient(config.archiveApiUrl);
5
+ }
6
+ return undefined;
7
+ }
@@ -0,0 +1,3 @@
1
+ export * from './interface.js';
2
+ export * from './factory.js';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcmNoaXZlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxjQUFjLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/archive/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './interface.js';
2
+ export * from './factory.js';
@@ -0,0 +1,11 @@
1
+ import { type TelemetryClient } from '@aztec/telemetry-client';
2
+ export declare class BlobArchiveClientInstrumentation {
3
+ private httpHost;
4
+ private blockRequestCounter;
5
+ private blobRequestCounter;
6
+ private retrievedBlobs;
7
+ constructor(client: TelemetryClient, httpHost: string, name: string);
8
+ incRequest(type: 'blocks' | 'blobs', status: number): void;
9
+ incRetrievedBlobs(count: number): void;
10
+ }
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdHJ1bWVudGF0aW9uLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXJjaGl2ZS9pbnN0cnVtZW50YXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUF1QixLQUFLLGVBQWUsRUFBaUMsTUFBTSx5QkFBeUIsQ0FBQztBQUVuSCxxQkFBYSxnQ0FBZ0M7SUFPekMsT0FBTyxDQUFDLFFBQVE7SUFObEIsT0FBTyxDQUFDLG1CQUFtQixDQUFnQjtJQUMzQyxPQUFPLENBQUMsa0JBQWtCLENBQWdCO0lBQzFDLE9BQU8sQ0FBQyxjQUFjLENBQWdCO0lBRXRDLFlBQ0UsTUFBTSxFQUFFLGVBQWUsRUFDZixRQUFRLEVBQUUsTUFBTSxFQUN4QixJQUFJLEVBQUUsTUFBTSxFQWlCYjtJQUVELFVBQVUsQ0FBQyxJQUFJLEVBQUUsUUFBUSxHQUFHLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQU1sRDtJQUVELGlCQUFpQixDQUFDLEtBQUssRUFBRSxNQUFNLFFBRTlCO0NBQ0YifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/archive/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAiC,MAAM,yBAAyB,CAAC;AAEnH,qBAAa,gCAAgC;IAOzC,OAAO,CAAC,QAAQ;IANlB,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,cAAc,CAAgB;IAEtC,YACE,MAAM,EAAE,eAAe,EACf,QAAQ,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,EAiBb;IAED,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,QAMlD;IAED,iBAAiB,CAAC,KAAK,EAAE,MAAM,QAE9B;CACF"}
@@ -0,0 +1,33 @@
1
+ import { Attributes, Metrics, ValueType } from '@aztec/telemetry-client';
2
+ export class BlobArchiveClientInstrumentation {
3
+ httpHost;
4
+ blockRequestCounter;
5
+ blobRequestCounter;
6
+ retrievedBlobs;
7
+ constructor(client, httpHost, name){
8
+ this.httpHost = httpHost;
9
+ const meter = client.getMeter(name);
10
+ this.blockRequestCounter = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOCK_REQUEST_COUNT, {
11
+ description: 'Number of requests made to retrieve blocks from the blob archive',
12
+ valueType: ValueType.INT
13
+ });
14
+ this.blobRequestCounter = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOB_REQUEST_COUNT, {
15
+ description: 'Number of requests made to retrieve blobs from the blob archive',
16
+ valueType: ValueType.INT
17
+ });
18
+ this.retrievedBlobs = meter.createUpDownCounter(Metrics.BLOB_SINK_ARCHIVE_BLOB_COUNT, {
19
+ description: 'Number of blobs retrieved from the blob archive',
20
+ valueType: ValueType.INT
21
+ });
22
+ }
23
+ incRequest(type, status) {
24
+ const counter = type === 'blocks' ? this.blockRequestCounter : this.blobRequestCounter;
25
+ counter.add(1, {
26
+ [Attributes.HTTP_RESPONSE_STATUS_CODE]: status,
27
+ [Attributes.HTTP_REQUEST_HOST]: this.httpHost
28
+ });
29
+ }
30
+ incRetrievedBlobs(count) {
31
+ this.retrievedBlobs.add(count);
32
+ }
33
+ }
@@ -0,0 +1,13 @@
1
+ import type { BlobJson } from '@aztec/blob-lib/types';
2
+ /** Interface to an blob archiving service. */
3
+ export interface BlobArchiveClient {
4
+ getBlobData(id: string): Promise<Buffer | undefined>;
5
+ getBlobsFromBlock(blockId: string): Promise<BlobJson[] | undefined>;
6
+ getLatestBlock(): Promise<{
7
+ hash: string;
8
+ number: number;
9
+ slot: number;
10
+ }>;
11
+ getBaseUrl(): string;
12
+ }
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXJjaGl2ZS9pbnRlcmZhY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFdEQsOENBQThDO0FBQzlDLE1BQU0sV0FBVyxpQkFBaUI7SUFDaEMsV0FBVyxDQUFDLEVBQUUsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsQ0FBQztJQUNyRCxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxTQUFTLENBQUMsQ0FBQztJQUNwRSxjQUFjLElBQUksT0FBTyxDQUFDO1FBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQztRQUFDLE1BQU0sRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsTUFBTSxDQUFBO0tBQUUsQ0FBQyxDQUFDO0lBQzFFLFVBQVUsSUFBSSxNQUFNLENBQUM7Q0FDdEIifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/archive/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CAAC;IACpE,cAAc,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,UAAU,IAAI,MAAM,CAAC;CACtB"}
@@ -0,0 +1 @@
1
+ /** Interface to an blob archiving service. */ export { };
@@ -0,0 +1,3 @@
1
+ import type { BlobStore } from './interface.js';
2
+ export declare function describeBlobStore(getBlobStore: () => Promise<BlobStore>): void;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYl9zdG9yZV90ZXN0X3N1aXRlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmxvYnN0b3JlL2Jsb2Jfc3RvcmVfdGVzdF9zdWl0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVoRCx3QkFBZ0IsaUJBQWlCLENBQUMsWUFBWSxFQUFFLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQWtJdkUifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob_store_test_suite.d.ts","sourceRoot":"","sources":["../../src/blobstore/blob_store_test_suite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,QAkIvE"}
@@ -0,0 +1,164 @@
1
+ import { Blob } from '@aztec/blob-lib';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { BlobWithIndex } from '../types/index.js';
4
+ export function describeBlobStore(getBlobStore) {
5
+ let blobStore;
6
+ beforeEach(async ()=>{
7
+ blobStore = await getBlobStore();
8
+ });
9
+ it('should store and retrieve a blob by hash', async ()=>{
10
+ // Create a test blob with random fields
11
+ const testFields = [
12
+ Fr.random(),
13
+ Fr.random(),
14
+ Fr.random()
15
+ ];
16
+ const blob = Blob.fromFields(testFields);
17
+ const blobWithIndex = new BlobWithIndex(blob, 0);
18
+ const blobHash = blob.getEthVersionedBlobHash();
19
+ // Store the blob
20
+ await blobStore.addBlobs([
21
+ blobWithIndex
22
+ ]);
23
+ // Retrieve the blob by hash
24
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
25
+ blobHash
26
+ ]);
27
+ // Verify the blob was retrieved and matches
28
+ expect(retrievedBlobs.length).toBe(1);
29
+ expect(retrievedBlobs[0].blob).toEqual(blob);
30
+ });
31
+ it('should handle multiple blobs stored and retrieved by their hashes', async ()=>{
32
+ // Create two different blobs
33
+ const blob1 = Blob.fromFields([
34
+ Fr.random(),
35
+ Fr.random()
36
+ ]);
37
+ const blob2 = Blob.fromFields([
38
+ Fr.random(),
39
+ Fr.random(),
40
+ Fr.random()
41
+ ]);
42
+ const blobWithIndex1 = new BlobWithIndex(blob1, 0);
43
+ const blobWithIndex2 = new BlobWithIndex(blob2, 1);
44
+ const blobHash1 = blob1.getEthVersionedBlobHash();
45
+ const blobHash2 = blob2.getEthVersionedBlobHash();
46
+ // Store both blobs
47
+ await blobStore.addBlobs([
48
+ blobWithIndex1,
49
+ blobWithIndex2
50
+ ]);
51
+ // Retrieve and verify both blobs
52
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
53
+ blobHash1,
54
+ blobHash2
55
+ ]);
56
+ expect(retrievedBlobs.length).toBe(2);
57
+ expect(retrievedBlobs[0].blob).toEqual(blob1);
58
+ expect(retrievedBlobs[1].blob).toEqual(blob2);
59
+ });
60
+ it('should return empty array for non-existent blob hash', async ()=>{
61
+ // Create a random hash that doesn't exist
62
+ const nonExistentHash = Buffer.alloc(32);
63
+ nonExistentHash.fill(0xff);
64
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
65
+ nonExistentHash
66
+ ]);
67
+ expect(retrievedBlobs).toEqual([]);
68
+ });
69
+ it('should handle storing blobs with different indices', async ()=>{
70
+ // Create blobs with different indices
71
+ const blob1 = Blob.fromFields([
72
+ Fr.random()
73
+ ]);
74
+ const blob2 = Blob.fromFields([
75
+ Fr.random()
76
+ ]);
77
+ const blobWithIndex1 = new BlobWithIndex(blob1, 0);
78
+ const blobWithIndex2 = new BlobWithIndex(blob2, 1);
79
+ await blobStore.addBlobs([
80
+ blobWithIndex1,
81
+ blobWithIndex2
82
+ ]);
83
+ const blobHash1 = blob1.getEthVersionedBlobHash();
84
+ const blobHash2 = blob2.getEthVersionedBlobHash();
85
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
86
+ blobHash1,
87
+ blobHash2
88
+ ]);
89
+ expect(retrievedBlobs[0].index).toBe(0);
90
+ expect(retrievedBlobs[1].index).toBe(1);
91
+ });
92
+ it('should handle retrieving subset of stored blobs', async ()=>{
93
+ // Store multiple blobs
94
+ const blob1 = Blob.fromFields([
95
+ Fr.random()
96
+ ]);
97
+ const blob2 = Blob.fromFields([
98
+ Fr.random()
99
+ ]);
100
+ const blob3 = Blob.fromFields([
101
+ Fr.random()
102
+ ]);
103
+ await blobStore.addBlobs([
104
+ new BlobWithIndex(blob1, 0),
105
+ new BlobWithIndex(blob2, 1),
106
+ new BlobWithIndex(blob3, 2)
107
+ ]);
108
+ // Retrieve only some of them
109
+ const blobHash1 = blob1.getEthVersionedBlobHash();
110
+ const blobHash3 = blob3.getEthVersionedBlobHash();
111
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
112
+ blobHash1,
113
+ blobHash3
114
+ ]);
115
+ expect(retrievedBlobs.length).toBe(2);
116
+ expect(retrievedBlobs[0].blob).toEqual(blob1);
117
+ expect(retrievedBlobs[1].blob).toEqual(blob3);
118
+ });
119
+ it('should handle duplicate blob hashes in request', async ()=>{
120
+ const blob = Blob.fromFields([
121
+ Fr.random()
122
+ ]);
123
+ const blobWithIndex = new BlobWithIndex(blob, 0);
124
+ const blobHash = blob.getEthVersionedBlobHash();
125
+ await blobStore.addBlobs([
126
+ blobWithIndex
127
+ ]);
128
+ // Request the same blob hash multiple times
129
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
130
+ blobHash,
131
+ blobHash
132
+ ]);
133
+ // Implementation may return duplicates or deduplicate - both are valid
134
+ expect(retrievedBlobs.length).toBeGreaterThanOrEqual(1);
135
+ expect(retrievedBlobs[0].blob).toEqual(blob);
136
+ });
137
+ it('should overwrite blob when storing with same hash', async ()=>{
138
+ // Create two blobs that will have the same hash (same content)
139
+ const fields = [
140
+ Fr.random(),
141
+ Fr.random()
142
+ ];
143
+ const blob1 = Blob.fromFields(fields);
144
+ const blob2 = Blob.fromFields(fields);
145
+ // Store with different indices
146
+ const blobWithIndex1 = new BlobWithIndex(blob1, 0);
147
+ const blobWithIndex2 = new BlobWithIndex(blob2, 5);
148
+ const blobHash = blob1.getEthVersionedBlobHash();
149
+ // Store first blob
150
+ await blobStore.addBlobs([
151
+ blobWithIndex1
152
+ ]);
153
+ // Overwrite with second blob (same hash, different index)
154
+ await blobStore.addBlobs([
155
+ blobWithIndex2
156
+ ]);
157
+ // Retrieve and verify it's the second blob (with index 5)
158
+ const retrievedBlobs = await blobStore.getBlobsByHashes([
159
+ blobHash
160
+ ]);
161
+ expect(retrievedBlobs.length).toBe(1);
162
+ expect(retrievedBlobs[0].index).toBe(5);
163
+ });
164
+ }
@@ -0,0 +1,3 @@
1
+ export * from './memory_blob_store.js';
2
+ export * from './interface.js';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ibG9ic3RvcmUvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLGdCQUFnQixDQUFDIn0=