@shelby-protocol/sdk 0.0.1-experimental.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/browser/index.d.ts +10 -0
- package/dist/browser/index.mjs +55 -0
- package/dist/chunk-7P6ASYW6.mjs +9 -0
- package/dist/chunk-A4IG6GSE.mjs +21 -0
- package/dist/chunk-D2FERD4A.mjs +39 -0
- package/dist/chunk-DBTBUKCW.mjs +235 -0
- package/dist/chunk-E3QOKRQ4.mjs +50 -0
- package/dist/chunk-EFE5Y7IE.mjs +331 -0
- package/dist/chunk-GGYTHP5F.mjs +84 -0
- package/dist/chunk-I6NG5GNL.mjs +8 -0
- package/dist/chunk-IHTPXUYI.mjs +0 -0
- package/dist/chunk-MWDW4ROU.mjs +0 -0
- package/dist/chunk-NPM7WXK3.mjs +49 -0
- package/dist/chunk-OWIIKLFI.mjs +43 -0
- package/dist/chunk-PZPMURE4.mjs +0 -0
- package/dist/chunk-QEMIORTL.mjs +9 -0
- package/dist/chunk-VLLCOFH7.mjs +73 -0
- package/dist/chunk-WVWL2OPB.mjs +0 -0
- package/dist/chunk-XZMYTH4K.mjs +90 -0
- package/dist/chunk-YV67F5NY.mjs +38 -0
- package/dist/chunk-ZHXCVRZX.mjs +0 -0
- package/dist/core/ShelbyClient.d.ts +34 -0
- package/dist/core/ShelbyClient.mjs +8 -0
- package/dist/core/blobs.d.ts +52 -0
- package/dist/core/blobs.mjs +7 -0
- package/dist/core/commitments.d.ts +102 -0
- package/dist/core/commitments.mjs +11 -0
- package/dist/core/constants.d.ts +26 -0
- package/dist/core/constants.mjs +27 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.mjs +55 -0
- package/dist/core/layout.d.ts +41 -0
- package/dist/core/layout.mjs +14 -0
- package/dist/core/promises.d.ts +10 -0
- package/dist/core/promises.mjs +7 -0
- package/dist/core/types/blobs.d.ts +132 -0
- package/dist/core/types/blobs.mjs +1 -0
- package/dist/core/types/encodings.d.ts +19 -0
- package/dist/core/types/encodings.mjs +1 -0
- package/dist/core/types/index.d.ts +5 -0
- package/dist/core/types/index.mjs +3 -0
- package/dist/node/clients/ShelbyBlobClient.d.ts +215 -0
- package/dist/node/clients/ShelbyBlobClient.mjs +19 -0
- package/dist/node/clients/ShelbyNodeClient.d.ts +95 -0
- package/dist/node/clients/ShelbyNodeClient.mjs +22 -0
- package/dist/node/clients/ShelbyRPCClient.d.ts +72 -0
- package/dist/node/clients/ShelbyRPCClient.mjs +17 -0
- package/dist/node/clients/index.d.ts +12 -0
- package/dist/node/clients/index.mjs +29 -0
- package/dist/node/commitments.d.ts +35 -0
- package/dist/node/commitments.mjs +18 -0
- package/dist/node/erasure.d.ts +12 -0
- package/dist/node/erasure.mjs +7 -0
- package/dist/node/index.d.ts +18 -0
- package/dist/node/index.mjs +85 -0
- package/dist/node/readableUtil.d.ts +2 -0
- package/dist/node/readableUtil.mjs +9 -0
- package/dist/node/testUtil.d.ts +1 -0
- package/dist/node/testUtil.mjs +7 -0
- package/dist/readableUtil-BW_7enT-.d.ts +12 -0
- package/dist/testUtil-BnxAchIN.d.ts +8 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @shelby-protocol/sdk
|
|
2
|
+
|
|
3
|
+
A TypeScript SDK providing core encoding and decoding utilities for both Node.js and browser environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Installing the shelby sdk
|
|
9
|
+
pnpm install @shelby-protocol/sdk @aptos-labs/ts-sdk
|
|
10
|
+
|
|
11
|
+
# If you plan to use the Node.js entrypoint, also install its peer dependencies:
|
|
12
|
+
pnpm install fs-extra glob node-fetch yaml
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Depending on your runtime environment, import from one of the following entrypoints:
|
|
18
|
+
|
|
19
|
+
### Node.js
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { ShelbyNodeClient } from '@shelby-protocol/sdk/node';
|
|
23
|
+
|
|
24
|
+
// Your code here...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Browser
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { ShelbyBlob } from '@shelby-protocol/sdk/browser';
|
|
31
|
+
|
|
32
|
+
// Your code here...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Peer Dependencies
|
|
36
|
+
|
|
37
|
+
- `@aptos-labs/ts-sdk`
|
|
38
|
+
|
|
39
|
+
When using the **Node.js** entrypoint (`@shelby-protocol/sdk/node`), make sure you have the following installed in your project:
|
|
40
|
+
|
|
41
|
+
- `fs-extra`
|
|
42
|
+
- `glob`
|
|
43
|
+
- `node-fetch`
|
|
44
|
+
- `yaml`
|
|
45
|
+
|
|
46
|
+
These are marked as **peerDependencies** and are **optional** for browser usage.
|
|
47
|
+
|
|
48
|
+
## Scripts
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm run build # compile TypeScript to dist/
|
|
52
|
+
npm run test # watch mode with Vitest
|
|
53
|
+
npm run test:once # single-run tests
|
|
54
|
+
npm run lint # run biome diagnostics
|
|
55
|
+
npm run fmt # auto-format with biome
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
> Please import the entrypoint that matches your environment (`/node`, `/browser`, or the root) to ensure you get the correct implementation and avoid missing-module errors.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { BlobCommitments, BlobCommitmentsSchema, ChunksetCommitment, ChunksetCommitmentSchema } from '../core/commitments.js';
|
|
2
|
+
export { BlobName, BlobNameSchema, ChunkKey, allChunksForBlob, roundSize } from '../core/layout.js';
|
|
3
|
+
export { CHUNKSET_SIZE_BYTES, DEFAULT_PROJECT_DESCRIPTION, DEFAULT_PROJECT_NAME, DEFAULT_SHELBY_BASE_URL, ERASURE_K, ERASURE_M, SHELBY_DEPLOYER, TOKEN_DEPLOYER, TOKEN_OBJECT_ADDRESS, getChunkSizeBytes, getChunksetSizeBytes } from '../core/constants.js';
|
|
4
|
+
export { BlobChunk, BlobEncoding, BlobMetadata, ChunkLocation, ClayEncoding, PendingChunkLocation, SignedChunkCommitment, StoredChunkLocation } from '../core/types/blobs.js';
|
|
5
|
+
export { EncodingOptions } from '../core/types/encodings.js';
|
|
6
|
+
export { ShelbyBlob, createBlobKey } from '../core/blobs.js';
|
|
7
|
+
export { ShelbyClient, ShelbyClientConfig } from '../core/ShelbyClient.js';
|
|
8
|
+
import 'zod';
|
|
9
|
+
import '@aptos-labs/ts-sdk';
|
|
10
|
+
import 'node:stream';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import "../chunk-PZPMURE4.mjs";
|
|
2
|
+
import "../chunk-MWDW4ROU.mjs";
|
|
3
|
+
import "../chunk-ZHXCVRZX.mjs";
|
|
4
|
+
import "../chunk-IHTPXUYI.mjs";
|
|
5
|
+
import {
|
|
6
|
+
ShelbyClient
|
|
7
|
+
} from "../chunk-YV67F5NY.mjs";
|
|
8
|
+
import {
|
|
9
|
+
createBlobKey
|
|
10
|
+
} from "../chunk-QEMIORTL.mjs";
|
|
11
|
+
import {
|
|
12
|
+
BlobCommitmentsSchema,
|
|
13
|
+
ChunksetCommitmentSchema
|
|
14
|
+
} from "../chunk-NPM7WXK3.mjs";
|
|
15
|
+
import {
|
|
16
|
+
BlobNameSchema,
|
|
17
|
+
ChunkKey,
|
|
18
|
+
allChunksForBlob,
|
|
19
|
+
roundSize
|
|
20
|
+
} from "../chunk-GGYTHP5F.mjs";
|
|
21
|
+
import {
|
|
22
|
+
CHUNKSET_SIZE_BYTES,
|
|
23
|
+
DEFAULT_PROJECT_DESCRIPTION,
|
|
24
|
+
DEFAULT_PROJECT_NAME,
|
|
25
|
+
DEFAULT_SHELBY_BASE_URL,
|
|
26
|
+
ERASURE_K,
|
|
27
|
+
ERASURE_M,
|
|
28
|
+
SHELBY_DEPLOYER,
|
|
29
|
+
TOKEN_DEPLOYER,
|
|
30
|
+
TOKEN_OBJECT_ADDRESS,
|
|
31
|
+
getChunkSizeBytes,
|
|
32
|
+
getChunksetSizeBytes
|
|
33
|
+
} from "../chunk-D2FERD4A.mjs";
|
|
34
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
35
|
+
export {
|
|
36
|
+
BlobCommitmentsSchema,
|
|
37
|
+
BlobNameSchema,
|
|
38
|
+
CHUNKSET_SIZE_BYTES,
|
|
39
|
+
ChunkKey,
|
|
40
|
+
ChunksetCommitmentSchema,
|
|
41
|
+
DEFAULT_PROJECT_DESCRIPTION,
|
|
42
|
+
DEFAULT_PROJECT_NAME,
|
|
43
|
+
DEFAULT_SHELBY_BASE_URL,
|
|
44
|
+
ERASURE_K,
|
|
45
|
+
ERASURE_M,
|
|
46
|
+
SHELBY_DEPLOYER,
|
|
47
|
+
ShelbyClient,
|
|
48
|
+
TOKEN_DEPLOYER,
|
|
49
|
+
TOKEN_OBJECT_ADDRESS,
|
|
50
|
+
allChunksForBlob,
|
|
51
|
+
createBlobKey,
|
|
52
|
+
getChunkSizeBytes,
|
|
53
|
+
getChunksetSizeBytes,
|
|
54
|
+
roundSize
|
|
55
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-7P6ASYW6.mjs";
|
|
4
|
+
|
|
5
|
+
// src/node/testUtil.ts
|
|
6
|
+
var testUtil_exports = {};
|
|
7
|
+
__export(testUtil_exports, {
|
|
8
|
+
makeChunk: () => makeChunk
|
|
9
|
+
});
|
|
10
|
+
function makeChunk(n) {
|
|
11
|
+
const c = Buffer.alloc(n);
|
|
12
|
+
for (let i = 0; i < n; ++i) {
|
|
13
|
+
c[i] = i % 256;
|
|
14
|
+
}
|
|
15
|
+
return c;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
makeChunk,
|
|
20
|
+
testUtil_exports
|
|
21
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/core/constants.ts
|
|
2
|
+
var DEFAULT_PROJECT_NAME = "shelby";
|
|
3
|
+
var DEFAULT_PROJECT_DESCRIPTION = "High performance, decentralized storage";
|
|
4
|
+
var DEFAULT_SHELBY_BASE_URL = "https://api.shelby.dev";
|
|
5
|
+
var SHELBY_DEPLOYER = "0xc63d6a5efb0080a6029403131715bd4971e1149f7cc099aac69bb0069b3ddbf5";
|
|
6
|
+
var TOKEN_DEPLOYER = "0x33009e852be7f93762dd0bf303383c2cb2c5cab7a30d8238ca5f9f177ae75124";
|
|
7
|
+
var TOKEN_OBJECT_ADDRESS = "0x249f5c642a63885ff88a5113b3ba0079840af5a1357706f8c7f3bfc5dd12511f";
|
|
8
|
+
var ERASURE_K = 2;
|
|
9
|
+
var ERASURE_M = 1;
|
|
10
|
+
var CHUNKSET_SIZE_BYTES = 10 * 1024 * 1024;
|
|
11
|
+
function computeChunkSizeBytes(k, chunksetSize) {
|
|
12
|
+
const ret = chunksetSize / k;
|
|
13
|
+
if (!Number.isInteger(ret)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`ChunksetSize and erasure coding number of chunks are incompatible: ${chunksetSize}/${k} is not an integer`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return ret;
|
|
19
|
+
}
|
|
20
|
+
function getChunksetSizeBytes() {
|
|
21
|
+
return CHUNKSET_SIZE_BYTES;
|
|
22
|
+
}
|
|
23
|
+
function getChunkSizeBytes() {
|
|
24
|
+
return computeChunkSizeBytes(ERASURE_K, getChunksetSizeBytes());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
DEFAULT_PROJECT_NAME,
|
|
29
|
+
DEFAULT_PROJECT_DESCRIPTION,
|
|
30
|
+
DEFAULT_SHELBY_BASE_URL,
|
|
31
|
+
SHELBY_DEPLOYER,
|
|
32
|
+
TOKEN_DEPLOYER,
|
|
33
|
+
TOKEN_OBJECT_ADDRESS,
|
|
34
|
+
ERASURE_K,
|
|
35
|
+
ERASURE_M,
|
|
36
|
+
CHUNKSET_SIZE_BYTES,
|
|
37
|
+
getChunksetSizeBytes,
|
|
38
|
+
getChunkSizeBytes
|
|
39
|
+
};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ShelbyClient
|
|
3
|
+
} from "./chunk-YV67F5NY.mjs";
|
|
4
|
+
import {
|
|
5
|
+
BlobNameSchema
|
|
6
|
+
} from "./chunk-GGYTHP5F.mjs";
|
|
7
|
+
import {
|
|
8
|
+
sleep
|
|
9
|
+
} from "./chunk-I6NG5GNL.mjs";
|
|
10
|
+
|
|
11
|
+
// src/node/clients/ShelbyRPCClient.ts
|
|
12
|
+
import { Readable, Transform } from "stream";
|
|
13
|
+
import { AccountAddress } from "@aptos-labs/ts-sdk";
|
|
14
|
+
function encodeURIComponentKeepSlashes(str) {
|
|
15
|
+
return encodeURIComponent(str).replace(/%2F/g, "/");
|
|
16
|
+
}
|
|
17
|
+
var ShelbyRPCClient = class extends ShelbyClient {
|
|
18
|
+
/**
|
|
19
|
+
* The ShelbyRPCClient is used to interact with the Shelby RPC node. This
|
|
20
|
+
* includes functions like uploading blobs after they have been committed to the
|
|
21
|
+
* blockchain and downloading blobs.
|
|
22
|
+
*
|
|
23
|
+
* @param config.aptos.config - The Aptos config.
|
|
24
|
+
* @param config.shelby.baseUrl - The base URL of the Shelby RPC node.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET }));
|
|
29
|
+
* const client = new ShelbyRPCClient({ aptos, shelby: { baseUrl: "https://api.shelby.dev" } });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
// biome-ignore lint/complexity/noUselessConstructor: Add JSDoc
|
|
33
|
+
constructor(config) {
|
|
34
|
+
super(config);
|
|
35
|
+
}
|
|
36
|
+
async #uploadPart(uploadId, partIdx, partData) {
|
|
37
|
+
const nRetries = 5;
|
|
38
|
+
for (let i = 0; i < nRetries; ++i) {
|
|
39
|
+
const partResponse = await fetch(
|
|
40
|
+
`${this.baseUrl}/api/v1/multipartUpload/uploadPart/${uploadId}/${partIdx}`,
|
|
41
|
+
{
|
|
42
|
+
method: "PUT",
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/octet-stream"
|
|
45
|
+
},
|
|
46
|
+
body: partData
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
if (partResponse.ok) return;
|
|
50
|
+
if (i < nRetries - 1) {
|
|
51
|
+
const delay = 2 ** i * 100;
|
|
52
|
+
await sleep(delay);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Failed to upload part ${partIdx}.`);
|
|
56
|
+
}
|
|
57
|
+
async #putBlobMultipart(account, blobName, blobData, partSize = 5 * 1024 * 1024) {
|
|
58
|
+
const startResponse = await fetch(
|
|
59
|
+
`${this.baseUrl}/api/v1/multipartUpload/start`,
|
|
60
|
+
{
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
"Content-Type": "application/json"
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
rawAccount: account.toString(),
|
|
67
|
+
rawBlobName: blobName,
|
|
68
|
+
rawPartSize: partSize
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
if (!startResponse.ok) {
|
|
73
|
+
let errorBodyText = "Could not read error body";
|
|
74
|
+
try {
|
|
75
|
+
errorBodyText = await startResponse.text();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
}
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Failed to start multipart upload! status: ${startResponse.status}, body: ${errorBodyText}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const { uploadId } = await startResponse.json();
|
|
83
|
+
const totalParts = Math.ceil(blobData.length / partSize);
|
|
84
|
+
for (let partIdx = 0; partIdx < totalParts; partIdx++) {
|
|
85
|
+
const start = partIdx * partSize;
|
|
86
|
+
const end = Math.min(start + partSize, blobData.length);
|
|
87
|
+
const partData = blobData.subarray(start, end);
|
|
88
|
+
await this.#uploadPart(uploadId, partIdx, partData);
|
|
89
|
+
}
|
|
90
|
+
const completeResponse = await fetch(
|
|
91
|
+
`${this.baseUrl}/api/v1/multipartUpload/complete/${uploadId}`,
|
|
92
|
+
{
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
if (!completeResponse.ok) {
|
|
100
|
+
let errorBodyText = "Could not read error body";
|
|
101
|
+
try {
|
|
102
|
+
errorBodyText = await completeResponse.text();
|
|
103
|
+
} catch (e) {
|
|
104
|
+
}
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Failed to complete multipart upload! status: ${completeResponse.status}, body: ${errorBodyText}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Uploads blob data to the Shelby RPC node to be stored and confirmed by storage providers. This should be performed after the blob
|
|
112
|
+
* commitments have been written to the blockchain.
|
|
113
|
+
*
|
|
114
|
+
* @param params.account - The account namespace the blob is stored in (e.g. "0x1")
|
|
115
|
+
* @param params.blobName - The name of the blob (e.g. "foo/bar")
|
|
116
|
+
* @param params.blobData - The data to upload.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const blobData = Buffer.from("Hello, world!");
|
|
121
|
+
*
|
|
122
|
+
* await client.putBlob({ account, blobName: "foo/bar.txt", blobData });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
async putBlob(params) {
|
|
126
|
+
BlobNameSchema.parse(params.blobName);
|
|
127
|
+
await this.#putBlobMultipart(
|
|
128
|
+
params.account,
|
|
129
|
+
params.blobName,
|
|
130
|
+
params.blobData
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
// FIXME make this possible to stream in put ^^^
|
|
134
|
+
/**
|
|
135
|
+
* Downloads a blob from the Shelby RPC node.
|
|
136
|
+
*
|
|
137
|
+
* @param params.account - The account namespace the blob is stored in (e.g. "0x1")
|
|
138
|
+
* @param params.blobName - The name of the blob (e.g. "foo/bar")
|
|
139
|
+
* @param params.range - The range of the blob to download.
|
|
140
|
+
*
|
|
141
|
+
* @returns A `ShelbyBlob` object containing the blob data.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const blob = await client.getBlob({
|
|
146
|
+
* account,
|
|
147
|
+
* blobName: "foo/bar.txt",
|
|
148
|
+
* });
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
async getBlob(params) {
|
|
152
|
+
BlobNameSchema.parse(params.blobName);
|
|
153
|
+
const url = `${this.baseUrl}/storage/${params.account.toString()}/${encodeURIComponentKeepSlashes(params.blobName)}`;
|
|
154
|
+
const requestInit = {};
|
|
155
|
+
if (params.range !== void 0) {
|
|
156
|
+
const headers = new Headers();
|
|
157
|
+
const { start, end } = params.range;
|
|
158
|
+
if (end === void 0) {
|
|
159
|
+
headers.set("Range", `bytes=${start}-`);
|
|
160
|
+
} else {
|
|
161
|
+
if (end < start) {
|
|
162
|
+
throw new Error("Range end cannot be less than start.");
|
|
163
|
+
}
|
|
164
|
+
headers.set("Range", `bytes=${start}-${end}`);
|
|
165
|
+
}
|
|
166
|
+
requestInit.headers = headers;
|
|
167
|
+
}
|
|
168
|
+
const response = await fetch(url, requestInit);
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Failed to download blob: ${response.status} ${response.statusText}`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
if (!response.body) {
|
|
175
|
+
throw new Error("Response body is null");
|
|
176
|
+
}
|
|
177
|
+
const contentLengthHeader = response.headers.get("content-length");
|
|
178
|
+
if (contentLengthHeader === null) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"Response did not have content-length header, which is required"
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const expectedContentLength = Number.parseInt(contentLengthHeader, 10);
|
|
184
|
+
if (Number.isNaN(expectedContentLength)) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`Invalid content-length header received: ${contentLengthHeader}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
let bytesReceived = 0;
|
|
190
|
+
const reader = response.body.getReader();
|
|
191
|
+
const sourceStream = new Readable({
|
|
192
|
+
async read() {
|
|
193
|
+
try {
|
|
194
|
+
const { done, value } = await reader.read();
|
|
195
|
+
if (done) {
|
|
196
|
+
this.push(null);
|
|
197
|
+
} else {
|
|
198
|
+
this.push(Buffer.from(value));
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
this.destroy(error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
const validationTransform = new Transform({
|
|
206
|
+
transform(chunk, encoding, callback) {
|
|
207
|
+
bytesReceived += chunk.length;
|
|
208
|
+
callback(null, chunk);
|
|
209
|
+
},
|
|
210
|
+
flush(callback) {
|
|
211
|
+
if (bytesReceived !== expectedContentLength) {
|
|
212
|
+
callback(
|
|
213
|
+
new Error(
|
|
214
|
+
`Downloaded data size (${bytesReceived} bytes) does not match content-length header (${expectedContentLength} bytes). This might indicate a partial or corrupted download.`
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
} else {
|
|
218
|
+
callback();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
sourceStream.pipe(validationTransform);
|
|
223
|
+
return {
|
|
224
|
+
account: AccountAddress.from(params.account),
|
|
225
|
+
name: params.blobName,
|
|
226
|
+
readable: response.body,
|
|
227
|
+
stream: validationTransform,
|
|
228
|
+
contentLength: expectedContentLength
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export {
|
|
234
|
+
ShelbyRPCClient
|
|
235
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-7P6ASYW6.mjs";
|
|
4
|
+
|
|
5
|
+
// src/node/readableUtil.ts
|
|
6
|
+
var readableUtil_exports = {};
|
|
7
|
+
__export(readableUtil_exports, {
|
|
8
|
+
readInChunks: () => readInChunks,
|
|
9
|
+
zeroPadBuffer: () => zeroPadBuffer
|
|
10
|
+
});
|
|
11
|
+
async function* readInChunks(r, chunkSize) {
|
|
12
|
+
let buffer = Buffer.alloc(chunkSize);
|
|
13
|
+
let bufferWriteOffset = 0;
|
|
14
|
+
let idx = 0;
|
|
15
|
+
for await (const readableChunk of r) {
|
|
16
|
+
let bufferRemain = chunkSize - bufferWriteOffset;
|
|
17
|
+
let st = 0;
|
|
18
|
+
let ed = Math.min(readableChunk.length, st + bufferRemain);
|
|
19
|
+
while (st < ed) {
|
|
20
|
+
const slice = readableChunk.slice(st, ed);
|
|
21
|
+
slice.copy(buffer, bufferWriteOffset);
|
|
22
|
+
bufferWriteOffset += slice.length;
|
|
23
|
+
if (bufferWriteOffset >= chunkSize) {
|
|
24
|
+
yield [idx++, buffer];
|
|
25
|
+
buffer = Buffer.alloc(chunkSize);
|
|
26
|
+
bufferWriteOffset = 0;
|
|
27
|
+
}
|
|
28
|
+
bufferRemain = chunkSize - bufferWriteOffset;
|
|
29
|
+
st = ed;
|
|
30
|
+
ed = Math.min(readableChunk.length, st + bufferRemain);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (bufferWriteOffset > 0) {
|
|
34
|
+
yield [idx++, buffer.subarray(0, bufferWriteOffset)];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function zeroPadBuffer(buffer, desiredLength) {
|
|
38
|
+
if (buffer.length >= desiredLength) {
|
|
39
|
+
return buffer;
|
|
40
|
+
}
|
|
41
|
+
const paddedBuffer = Buffer.alloc(desiredLength);
|
|
42
|
+
buffer.copy(paddedBuffer);
|
|
43
|
+
return paddedBuffer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
readInChunks,
|
|
48
|
+
zeroPadBuffer,
|
|
49
|
+
readableUtil_exports
|
|
50
|
+
};
|