@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.
Files changed (63) hide show
  1. package/README.md +60 -0
  2. package/dist/browser/index.d.ts +10 -0
  3. package/dist/browser/index.mjs +55 -0
  4. package/dist/chunk-7P6ASYW6.mjs +9 -0
  5. package/dist/chunk-A4IG6GSE.mjs +21 -0
  6. package/dist/chunk-D2FERD4A.mjs +39 -0
  7. package/dist/chunk-DBTBUKCW.mjs +235 -0
  8. package/dist/chunk-E3QOKRQ4.mjs +50 -0
  9. package/dist/chunk-EFE5Y7IE.mjs +331 -0
  10. package/dist/chunk-GGYTHP5F.mjs +84 -0
  11. package/dist/chunk-I6NG5GNL.mjs +8 -0
  12. package/dist/chunk-IHTPXUYI.mjs +0 -0
  13. package/dist/chunk-MWDW4ROU.mjs +0 -0
  14. package/dist/chunk-NPM7WXK3.mjs +49 -0
  15. package/dist/chunk-OWIIKLFI.mjs +43 -0
  16. package/dist/chunk-PZPMURE4.mjs +0 -0
  17. package/dist/chunk-QEMIORTL.mjs +9 -0
  18. package/dist/chunk-VLLCOFH7.mjs +73 -0
  19. package/dist/chunk-WVWL2OPB.mjs +0 -0
  20. package/dist/chunk-XZMYTH4K.mjs +90 -0
  21. package/dist/chunk-YV67F5NY.mjs +38 -0
  22. package/dist/chunk-ZHXCVRZX.mjs +0 -0
  23. package/dist/core/ShelbyClient.d.ts +34 -0
  24. package/dist/core/ShelbyClient.mjs +8 -0
  25. package/dist/core/blobs.d.ts +52 -0
  26. package/dist/core/blobs.mjs +7 -0
  27. package/dist/core/commitments.d.ts +102 -0
  28. package/dist/core/commitments.mjs +11 -0
  29. package/dist/core/constants.d.ts +26 -0
  30. package/dist/core/constants.mjs +27 -0
  31. package/dist/core/index.d.ts +10 -0
  32. package/dist/core/index.mjs +55 -0
  33. package/dist/core/layout.d.ts +41 -0
  34. package/dist/core/layout.mjs +14 -0
  35. package/dist/core/promises.d.ts +10 -0
  36. package/dist/core/promises.mjs +7 -0
  37. package/dist/core/types/blobs.d.ts +132 -0
  38. package/dist/core/types/blobs.mjs +1 -0
  39. package/dist/core/types/encodings.d.ts +19 -0
  40. package/dist/core/types/encodings.mjs +1 -0
  41. package/dist/core/types/index.d.ts +5 -0
  42. package/dist/core/types/index.mjs +3 -0
  43. package/dist/node/clients/ShelbyBlobClient.d.ts +215 -0
  44. package/dist/node/clients/ShelbyBlobClient.mjs +19 -0
  45. package/dist/node/clients/ShelbyNodeClient.d.ts +95 -0
  46. package/dist/node/clients/ShelbyNodeClient.mjs +22 -0
  47. package/dist/node/clients/ShelbyRPCClient.d.ts +72 -0
  48. package/dist/node/clients/ShelbyRPCClient.mjs +17 -0
  49. package/dist/node/clients/index.d.ts +12 -0
  50. package/dist/node/clients/index.mjs +29 -0
  51. package/dist/node/commitments.d.ts +35 -0
  52. package/dist/node/commitments.mjs +18 -0
  53. package/dist/node/erasure.d.ts +12 -0
  54. package/dist/node/erasure.mjs +7 -0
  55. package/dist/node/index.d.ts +18 -0
  56. package/dist/node/index.mjs +85 -0
  57. package/dist/node/readableUtil.d.ts +2 -0
  58. package/dist/node/readableUtil.mjs +9 -0
  59. package/dist/node/testUtil.d.ts +1 -0
  60. package/dist/node/testUtil.mjs +7 -0
  61. package/dist/readableUtil-BW_7enT-.d.ts +12 -0
  62. package/dist/testUtil-BnxAchIN.d.ts +8 -0
  63. 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,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
@@ -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
+ };