@shelby-protocol/sdk 0.0.8 → 0.1.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/dist/browser/index.d.ts +13 -7
- package/dist/browser/index.mjs +102 -49
- package/dist/{chunk-HFGEQP5N.mjs → chunk-3ZL3FSNA.mjs} +4 -4
- package/dist/chunk-4MG4XGY4.mjs +91 -0
- package/dist/chunk-66GI734H.mjs +493 -0
- package/dist/chunk-AABBONAF.mjs +34 -0
- package/dist/{chunk-BBOG5JSX.mjs → chunk-AGRRYZWV.mjs} +215 -44
- package/dist/chunk-CQ6QPIZK.mjs +37 -0
- package/dist/{chunk-LGNWAXBG.mjs → chunk-DI2K6OUG.mjs} +1 -1
- package/dist/{chunk-SEXQTDX6.mjs → chunk-FLLOQZVD.mjs} +4 -0
- package/dist/chunk-IE6LYVIA.mjs +26 -0
- package/dist/{chunk-WJKSPJSS.mjs → chunk-KJ24NKPH.mjs} +46 -0
- package/dist/{chunk-ZPW742E7.mjs → chunk-LZSIZJYR.mjs} +3 -1
- package/dist/chunk-MSCUDBMH.mjs +83 -0
- package/dist/{chunk-CPNZAQVY.mjs → chunk-OMZOR2ZF.mjs} +2 -2
- package/dist/chunk-QQ57OGQ2.mjs +0 -0
- package/dist/{chunk-XWAPNLU6.mjs → chunk-RLRI2533.mjs} +4 -2
- package/dist/{chunk-NLPIHQ7K.mjs → chunk-UCDAABAS.mjs} +1 -1
- package/dist/{chunk-GY5DCVVL.mjs → chunk-W5NRGZEP.mjs} +1 -1
- package/dist/{chunk-MGAN2SBA.mjs → chunk-W6YL46DT.mjs} +28 -14
- package/dist/{chunk-CTGCK3H2.mjs → chunk-YZXIPUVQ.mjs} +32 -8
- package/dist/{chunk-QRGZJBAG.mjs → chunk-ZAM2EUVN.mjs} +165 -36
- package/dist/core/chunk.mjs +1 -1
- package/dist/core/clients/ShelbyBlobClient.d.ts +160 -11
- package/dist/core/clients/ShelbyBlobClient.mjs +11 -9
- package/dist/core/clients/ShelbyClient.d.ts +14 -3
- package/dist/core/clients/ShelbyClient.mjs +15 -13
- package/dist/core/clients/ShelbyClientConfig.d.ts +7 -2
- package/dist/core/clients/ShelbyMicropaymentChannelClient.d.ts +349 -0
- package/dist/core/clients/ShelbyMicropaymentChannelClient.mjs +16 -0
- package/dist/core/clients/ShelbyPlacementGroupClient.d.ts +73 -0
- package/dist/core/clients/ShelbyPlacementGroupClient.mjs +11 -0
- package/dist/core/clients/ShelbyRPCClient.d.ts +28 -4
- package/dist/core/clients/ShelbyRPCClient.mjs +9 -7
- package/dist/core/clients/index.d.ts +7 -3
- package/dist/core/clients/index.mjs +33 -17
- package/dist/core/clients/utils.d.ts +54 -0
- package/dist/core/clients/utils.mjs +1 -1
- package/dist/core/commitments.d.ts +5 -1
- package/dist/core/commitments.mjs +4 -2
- package/dist/core/constants.d.ts +4 -1
- package/dist/core/constants.mjs +3 -1
- package/dist/core/erasure/clay-codes.mjs +2 -2
- package/dist/core/erasure/constants.d.ts +5 -1
- package/dist/core/erasure/constants.mjs +3 -1
- package/dist/core/erasure/default.mjs +3 -3
- package/dist/core/erasure/index.d.ts +1 -1
- package/dist/core/erasure/index.mjs +8 -6
- package/dist/core/errors.d.ts +58 -0
- package/dist/core/errors.mjs +15 -0
- package/dist/core/index.d.ts +13 -7
- package/dist/core/index.mjs +102 -49
- package/dist/core/layout.mjs +2 -2
- package/dist/core/operations/generated/sdk.d.ts +207 -17
- package/dist/core/operations/generated/sdk.mjs +7 -1
- package/dist/core/operations/generated/types.d.ts +908 -0
- package/dist/core/operations/generated/types.mjs +63 -0
- package/dist/core/operations/index.d.ts +9 -3
- package/dist/core/operations/index.mjs +10 -4
- package/dist/core/rpc-responses.d.ts +69 -0
- package/dist/core/rpc-responses.mjs +15 -0
- package/dist/core/types/blobs.d.ts +7 -3
- package/dist/core/types/index.d.ts +3 -1
- package/dist/core/types/index.mjs +12 -2
- package/dist/core/types/payments.d.ts +94 -0
- package/dist/core/types/payments.mjs +9 -0
- package/dist/core/types/placement_groups.d.ts +30 -1
- package/dist/core/types/placement_groups.mjs +1 -0
- package/dist/core/types/storage_providers.d.ts +32 -2
- package/dist/node/clients/ShelbyMetadataClient.mjs +3 -3
- package/dist/node/clients/ShelbyNodeClient.d.ts +4 -3
- package/dist/node/clients/ShelbyNodeClient.mjs +16 -14
- package/dist/node/clients/index.d.ts +4 -3
- package/dist/node/clients/index.mjs +19 -17
- package/dist/node/index.d.ts +13 -7
- package/dist/node/index.mjs +106 -53
- package/package.json +3 -2
- package/dist/chunk-RBFWGDMY.mjs +0 -30
- /package/dist/{chunk-DJJD2AXO.mjs → chunk-AD2G3QYD.mjs} +0 -0
- /package/dist/{chunk-MWDW4ROU.mjs → chunk-EM67QTMR.mjs} +0 -0
- /package/dist/{chunk-RNXGC54D.mjs → chunk-FGUK6IBA.mjs} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getShelbyIndexerClient
|
|
3
|
+
} from "./chunk-3ZL3FSNA.mjs";
|
|
4
|
+
|
|
5
|
+
// src/core/clients/ShelbyPlacementGroupClient.ts
|
|
6
|
+
var ShelbyPlacementGroupClient = class {
|
|
7
|
+
indexer;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new ShelbyPlacementGroupClient.
|
|
10
|
+
*
|
|
11
|
+
* @param config - The client configuration object.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const pgClient = new ShelbyPlacementGroupClient({
|
|
16
|
+
* network: Network.SHELBYNET,
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.indexer = getShelbyIndexerClient(config);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Retrieves placement group slots from the indexer.
|
|
25
|
+
*
|
|
26
|
+
* @param params.where (optional) - The where clause to filter slots by.
|
|
27
|
+
* @param params.pagination (optional) - The pagination options.
|
|
28
|
+
* @param params.orderBy (optional) - The order by clause to sort slots by.
|
|
29
|
+
* @returns The placement group slots that match the filter.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Get all active slots
|
|
34
|
+
* const slots = await client.getPlacementGroupSlots({
|
|
35
|
+
* where: { status: { _eq: "active" } },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
async getPlacementGroupSlots(params) {
|
|
40
|
+
const { limit, offset } = params.pagination ?? {};
|
|
41
|
+
const { orderBy, where } = params;
|
|
42
|
+
const { placement_group_slots } = await this.indexer.getPlacementGroupSlots(
|
|
43
|
+
{
|
|
44
|
+
where,
|
|
45
|
+
limit,
|
|
46
|
+
offset,
|
|
47
|
+
orderBy
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
return placement_group_slots.map(
|
|
51
|
+
(slot) => ({
|
|
52
|
+
placementGroup: slot.placement_group,
|
|
53
|
+
slotIndex: Number(slot.slot_index),
|
|
54
|
+
storageProvider: slot.storage_provider,
|
|
55
|
+
status: slot.status,
|
|
56
|
+
updatedAt: Number(slot.updated_at)
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves the total count of placement group slots from the indexer.
|
|
62
|
+
*
|
|
63
|
+
* @param params.where (optional) - The where clause to filter slots by.
|
|
64
|
+
* @returns The count of placement group slots that match the filter.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Get count of active slots
|
|
69
|
+
* const count = await client.getPlacementGroupSlotsCount({
|
|
70
|
+
* where: { status: { _eq: "active" } },
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
async getPlacementGroupSlotsCount(params) {
|
|
75
|
+
const { where } = params;
|
|
76
|
+
const { placement_group_slots_aggregate } = await this.indexer.getPlacementGroupSlotsCount({ where });
|
|
77
|
+
return placement_group_slots_aggregate?.aggregate?.count ?? 0;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
ShelbyPlacementGroupClient
|
|
83
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClayErasureCodingProvider
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UCDAABAS.mjs";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_CHUNK_SIZE_BYTES
|
|
6
6
|
} from "./chunk-67F5YZ25.mjs";
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
DEFAULT_ERASURE_D,
|
|
9
9
|
DEFAULT_ERASURE_K,
|
|
10
10
|
DEFAULT_ERASURE_N
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LZSIZJYR.mjs";
|
|
12
12
|
|
|
13
13
|
// src/core/erasure/default.ts
|
|
14
14
|
var defaultProviderPromise;
|
|
File without changes
|
|
@@ -9,11 +9,12 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_ERASURE_K,
|
|
11
11
|
DEFAULT_ERASURE_M
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LZSIZJYR.mjs";
|
|
13
13
|
|
|
14
14
|
// src/core/commitments.ts
|
|
15
15
|
import { Hex } from "@aptos-labs/ts-sdk";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
+
var COMMITMENT_SCHEMA_VERSION = "1.3";
|
|
17
18
|
var ChunksetCommitmentSchema = z.object({
|
|
18
19
|
// Chunkset root (vector commitment of child chunks)
|
|
19
20
|
chunkset_root: z.string(),
|
|
@@ -143,7 +144,7 @@ async function generateCommitments(provider, fullData, onChunk, options) {
|
|
|
143
144
|
chunksetCommitmentHashes.push(h);
|
|
144
145
|
}
|
|
145
146
|
return {
|
|
146
|
-
schema_version:
|
|
147
|
+
schema_version: COMMITMENT_SCHEMA_VERSION,
|
|
147
148
|
raw_data_size: rawDataSize,
|
|
148
149
|
blob_merkle_root: (await generateMerkleRoot(chunksetCommitmentHashes)).toString(),
|
|
149
150
|
chunkset_commitments: chunksetCommitments
|
|
@@ -159,6 +160,7 @@ function validatePrePaddedChunkset(chunkset, expectedSize, chunksetIdx) {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
export {
|
|
163
|
+
COMMITMENT_SCHEMA_VERSION,
|
|
162
164
|
ChunksetCommitmentSchema,
|
|
163
165
|
expectedTotalChunksets,
|
|
164
166
|
BlobCommitmentsSchema,
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ShelbyRPCClient
|
|
3
|
-
} from "./chunk-QRGZJBAG.mjs";
|
|
4
1
|
import {
|
|
5
2
|
ShelbyBlobClient
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
getAptosConfig
|
|
9
|
-
} from "./chunk-RBFWGDMY.mjs";
|
|
3
|
+
} from "./chunk-AGRRYZWV.mjs";
|
|
10
4
|
import {
|
|
11
5
|
ClayErasureCodingProvider
|
|
12
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UCDAABAS.mjs";
|
|
7
|
+
import {
|
|
8
|
+
ShelbyRPCClient
|
|
9
|
+
} from "./chunk-ZAM2EUVN.mjs";
|
|
10
|
+
import {
|
|
11
|
+
getAptosConfig
|
|
12
|
+
} from "./chunk-AABBONAF.mjs";
|
|
13
13
|
import {
|
|
14
14
|
createBlobKey
|
|
15
15
|
} from "./chunk-OTBLZL2S.mjs";
|
|
16
16
|
import {
|
|
17
17
|
generateCommitments
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-RLRI2533.mjs";
|
|
19
19
|
import {
|
|
20
20
|
NetworkToShelbyRPCBaseUrl
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-FLLOQZVD.mjs";
|
|
22
22
|
|
|
23
23
|
// src/core/clients/ShelbyClient.ts
|
|
24
24
|
import {
|
|
@@ -109,6 +109,11 @@ var ShelbyClient = class {
|
|
|
109
109
|
* This method handles the complete upload flow including commitment generation,
|
|
110
110
|
* blockchain registration, and storage upload.
|
|
111
111
|
*
|
|
112
|
+
* Note: This method accepts only `Uint8Array` and buffers the entire blob in memory.
|
|
113
|
+
* For streaming uploads of large files (e.g. >2 GiB), orchestrate the steps manually
|
|
114
|
+
* using `generateCommitments()`, `coordination.registerBlob()`, and `rpc.putBlob()`
|
|
115
|
+
* with a `ReadableStream`.
|
|
116
|
+
*
|
|
112
117
|
* @param params.blobData - The raw data to upload as a Uint8Array.
|
|
113
118
|
* @param params.signer - The account that signs and pays for the transaction.
|
|
114
119
|
* @param params.blobName - The name/path of the blob (e.g. "folder/file.txt").
|
|
@@ -145,7 +150,8 @@ var ShelbyClient = class {
|
|
|
145
150
|
blobName: params.blobName,
|
|
146
151
|
blobMerkleRoot: blobCommitments.blob_merkle_root,
|
|
147
152
|
size: params.blobData.length,
|
|
148
|
-
expirationMicros: params.expirationMicros
|
|
153
|
+
expirationMicros: params.expirationMicros,
|
|
154
|
+
options: params.options
|
|
149
155
|
});
|
|
150
156
|
await this.coordination.aptos.waitForTransaction({
|
|
151
157
|
transactionHash: pendingRegisterBlobTransaction.hash
|
|
@@ -162,6 +168,11 @@ var ShelbyClient = class {
|
|
|
162
168
|
* This method handles the complete upload flow including commitment generation,
|
|
163
169
|
* blockchain registration, and storage upload.
|
|
164
170
|
*
|
|
171
|
+
* Note: This method accepts only `Uint8Array` and buffers each blob in memory.
|
|
172
|
+
* For streaming uploads of large files, orchestrate the steps manually using
|
|
173
|
+
* `generateCommitments()`, `coordination.registerBlob()`, and `rpc.putBlob()`
|
|
174
|
+
* with a `ReadableStream`.
|
|
175
|
+
*
|
|
165
176
|
* @param params.blobs - The blobs to upload.
|
|
166
177
|
* @param params.blobs.blobData - The raw data to upload as a Uint8Array.
|
|
167
178
|
* @param params.blobs.blobName - The name/path of the blob (e.g. "folder/file.txt").
|
|
@@ -279,7 +290,8 @@ var ShelbyClient = class {
|
|
|
279
290
|
async fundAccountWithShelbyUSD(params) {
|
|
280
291
|
const { address, amount } = params;
|
|
281
292
|
try {
|
|
282
|
-
const faucet = this.config.faucet ?? "https://faucet.shelbynet.shelby.xyz/fund?asset=shelbyusd";
|
|
293
|
+
const faucet = this.config.faucet?.baseUrl ?? "https://faucet.shelbynet.shelby.xyz/fund?asset=shelbyusd";
|
|
294
|
+
const authToken = this.config.faucet?.authToken;
|
|
283
295
|
const response = await fetch(`${faucet}`, {
|
|
284
296
|
method: "POST",
|
|
285
297
|
body: JSON.stringify({
|
|
@@ -287,11 +299,13 @@ var ShelbyClient = class {
|
|
|
287
299
|
amount
|
|
288
300
|
}),
|
|
289
301
|
headers: {
|
|
290
|
-
"Content-Type": "application/json"
|
|
302
|
+
"Content-Type": "application/json",
|
|
303
|
+
...authToken ? { Authorization: `Bearer ${authToken}` } : {}
|
|
291
304
|
}
|
|
292
305
|
});
|
|
293
306
|
if (!response.ok) {
|
|
294
|
-
|
|
307
|
+
const errorBody = await response.text();
|
|
308
|
+
throw new Error(`Failed to fund account: ${errorBody}`);
|
|
295
309
|
}
|
|
296
310
|
const json = await response.json();
|
|
297
311
|
const res = await this.aptos.waitForTransaction({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAptosConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AABBONAF.mjs";
|
|
4
4
|
import {
|
|
5
5
|
SHELBY_DEPLOYER
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FLLOQZVD.mjs";
|
|
7
7
|
|
|
8
8
|
// src/node/clients/ShelbyMetadataClient.ts
|
|
9
9
|
import {
|
|
@@ -11,6 +11,29 @@ import {
|
|
|
11
11
|
Aptos,
|
|
12
12
|
Hex
|
|
13
13
|
} from "@aptos-labs/ts-sdk";
|
|
14
|
+
function parseStorageProviderState(raw) {
|
|
15
|
+
switch (raw.variant) {
|
|
16
|
+
case "Active":
|
|
17
|
+
return {
|
|
18
|
+
variant: "Active",
|
|
19
|
+
quota: raw.Active.quota.value,
|
|
20
|
+
stakeAtStartOfStakingEpoch: raw.Active.stake_at_start_of_staking_epoch,
|
|
21
|
+
faulty: raw.Active.faulty,
|
|
22
|
+
leaving: raw.Active.leaving
|
|
23
|
+
};
|
|
24
|
+
case "Waitlisted":
|
|
25
|
+
return {
|
|
26
|
+
variant: "Waitlisted"
|
|
27
|
+
};
|
|
28
|
+
case "Frozen":
|
|
29
|
+
return {
|
|
30
|
+
variant: "Frozen",
|
|
31
|
+
frozenReason: raw.Frozen.frozen_reason,
|
|
32
|
+
frozenFrom: raw.Frozen.frozen_from,
|
|
33
|
+
frozenTill: raw.Frozen.frozen_till
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
14
37
|
var ShelbyMetadataClient = class {
|
|
15
38
|
aptos;
|
|
16
39
|
deployer;
|
|
@@ -45,7 +68,7 @@ var ShelbyMetadataClient = class {
|
|
|
45
68
|
try {
|
|
46
69
|
const rawMetadata = await this.aptos.view({
|
|
47
70
|
payload: {
|
|
48
|
-
function: `${this.deployer.toString()}::
|
|
71
|
+
function: `${this.deployer.toString()}::storage_provider_registry::get_all_storage_providers`,
|
|
49
72
|
functionArguments: []
|
|
50
73
|
}
|
|
51
74
|
});
|
|
@@ -55,7 +78,8 @@ var ShelbyMetadataClient = class {
|
|
|
55
78
|
ipAddress: provider.ip_address,
|
|
56
79
|
port: provider.port,
|
|
57
80
|
blsPublicKey: Hex.fromHexInput(provider.bls_public_key).toUint8Array(),
|
|
58
|
-
failureDomain: provider.failure_domain
|
|
81
|
+
failureDomain: provider.failure_domain,
|
|
82
|
+
state: parseStorageProviderState(provider.state)
|
|
59
83
|
}));
|
|
60
84
|
} catch (error) {
|
|
61
85
|
if (error instanceof Error && // Depending on the network, the error message may show up differently.
|
|
@@ -79,14 +103,14 @@ var ShelbyMetadataClient = class {
|
|
|
79
103
|
try {
|
|
80
104
|
const pgSizeMetadata = await this.aptos.view({
|
|
81
105
|
payload: {
|
|
82
|
-
function: `${this.deployer.toString()}::
|
|
106
|
+
function: `${this.deployer.toString()}::placement_group_registry::get_number_of_placement_groups`,
|
|
83
107
|
functionArguments: []
|
|
84
108
|
}
|
|
85
109
|
});
|
|
86
110
|
const finalPlacementGroupIndex = pgSizeMetadata[0] - 1;
|
|
87
111
|
const addressMetadataArray = await this.aptos.view({
|
|
88
112
|
payload: {
|
|
89
|
-
function: `${this.deployer.toString()}::
|
|
113
|
+
function: `${this.deployer.toString()}::placement_group_registry::get_placement_group_addresses`,
|
|
90
114
|
functionArguments: [0, finalPlacementGroupIndex]
|
|
91
115
|
}
|
|
92
116
|
});
|
|
@@ -114,14 +138,14 @@ var ShelbyMetadataClient = class {
|
|
|
114
138
|
try {
|
|
115
139
|
const sliceSizeMetadata = await this.aptos.view({
|
|
116
140
|
payload: {
|
|
117
|
-
function: `${this.deployer.toString()}::
|
|
141
|
+
function: `${this.deployer.toString()}::slice_registry::get_number_of_slices`,
|
|
118
142
|
functionArguments: []
|
|
119
143
|
}
|
|
120
144
|
});
|
|
121
145
|
const finalSliceIndex = sliceSizeMetadata[0] - 1;
|
|
122
146
|
const addressMetadataArray = await this.aptos.view({
|
|
123
147
|
payload: {
|
|
124
|
-
function: `${this.deployer.toString()}::
|
|
148
|
+
function: `${this.deployer.toString()}::slice_registry::get_slice_addresses`,
|
|
125
149
|
functionArguments: [0, finalSliceIndex]
|
|
126
150
|
}
|
|
127
151
|
});
|
|
@@ -1,24 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StaleChannelStateError
|
|
3
|
+
} from "./chunk-4MG4XGY4.mjs";
|
|
4
|
+
import {
|
|
5
|
+
getShelbyIndexerClient
|
|
6
|
+
} from "./chunk-3ZL3FSNA.mjs";
|
|
7
|
+
import {
|
|
8
|
+
BlobNameSchema
|
|
9
|
+
} from "./chunk-W5NRGZEP.mjs";
|
|
1
10
|
import {
|
|
2
11
|
sleep
|
|
3
12
|
} from "./chunk-I6NG5GNL.mjs";
|
|
4
13
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
14
|
+
StaleMicropaymentErrorResponseSchema,
|
|
15
|
+
StartMultipartUploadResponseSchema
|
|
16
|
+
} from "./chunk-IE6LYVIA.mjs";
|
|
7
17
|
import {
|
|
8
|
-
buildRequestUrl
|
|
18
|
+
buildRequestUrl,
|
|
19
|
+
readInChunks
|
|
9
20
|
} from "./chunk-4JZO2D7T.mjs";
|
|
10
21
|
import {
|
|
11
22
|
NetworkToShelbyRPCBaseUrl
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
BlobNameSchema
|
|
15
|
-
} from "./chunk-GY5DCVVL.mjs";
|
|
23
|
+
} from "./chunk-FLLOQZVD.mjs";
|
|
16
24
|
|
|
17
25
|
// src/core/clients/ShelbyRPCClient.ts
|
|
18
26
|
import { AccountAddress } from "@aptos-labs/ts-sdk";
|
|
27
|
+
var MICROPAYMENT_HEADER = "X-Shelby-Micropayment";
|
|
19
28
|
function encodeURIComponentKeepSlashes(str) {
|
|
20
29
|
return encodeURIComponent(str).replace(/%2F/g, "/");
|
|
21
30
|
}
|
|
31
|
+
function validateTotalBytes(totalBytes) {
|
|
32
|
+
if (!Number.isInteger(totalBytes) || totalBytes < 0) {
|
|
33
|
+
throw new Error("totalBytes must be a non-negative integer");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function getErrorCode(error) {
|
|
37
|
+
if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
|
|
38
|
+
return error.code;
|
|
39
|
+
}
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
const match = error.message.match(/\bE[A-Z0-9_]+\b/);
|
|
42
|
+
return match?.[0];
|
|
43
|
+
}
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
22
46
|
var ShelbyRPCClient = class {
|
|
23
47
|
baseUrl;
|
|
24
48
|
apiKey;
|
|
@@ -47,30 +71,53 @@ var ShelbyRPCClient = class {
|
|
|
47
71
|
}
|
|
48
72
|
async #uploadPart(uploadId, partIdx, partData) {
|
|
49
73
|
const nRetries = 5;
|
|
74
|
+
let lastResponse;
|
|
75
|
+
let lastError;
|
|
76
|
+
const partUrl = buildRequestUrl(
|
|
77
|
+
`/v1/multipart-uploads/${uploadId}/parts/${partIdx}`,
|
|
78
|
+
this.baseUrl
|
|
79
|
+
);
|
|
50
80
|
for (let i = 0; i < nRetries; ++i) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`/v1/multipart-uploads/${uploadId}/parts/${partIdx}`,
|
|
54
|
-
this.baseUrl
|
|
55
|
-
),
|
|
56
|
-
{
|
|
81
|
+
try {
|
|
82
|
+
lastResponse = await fetch(partUrl, {
|
|
57
83
|
method: "PUT",
|
|
58
84
|
headers: {
|
|
59
85
|
"Content-Type": "application/octet-stream",
|
|
60
86
|
...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
61
87
|
},
|
|
62
88
|
body: partData
|
|
89
|
+
});
|
|
90
|
+
lastError = void 0;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
lastError = error;
|
|
93
|
+
if (i < nRetries - 1) {
|
|
94
|
+
const delay = 2 ** i * 100;
|
|
95
|
+
await sleep(delay);
|
|
96
|
+
continue;
|
|
63
97
|
}
|
|
64
|
-
|
|
65
|
-
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (lastResponse.ok) return;
|
|
66
101
|
if (i < nRetries - 1) {
|
|
67
102
|
const delay = 2 ** i * 100;
|
|
68
103
|
await sleep(delay);
|
|
69
104
|
}
|
|
70
105
|
}
|
|
71
|
-
|
|
106
|
+
if (lastError !== void 0) {
|
|
107
|
+
const errorCode = getErrorCode(lastError);
|
|
108
|
+
const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Failed to upload part ${partIdx} for multipart upload ${uploadId} after ${nRetries} attempts. The connection to the Shelby RPC endpoint was interrupted while sending data${errorCode ? ` (${errorCode})` : ""}. Endpoint: ${partUrl.toString()}, partBytes: ${partData.length}. Last error: ${errorMessage}`,
|
|
111
|
+
{ cause: lastError }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const errorBody = await lastResponse?.text().catch(() => "");
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Failed to upload part ${partIdx} for multipart upload ${uploadId} after ${nRetries} attempts. status: ${lastResponse?.status}, body: ${errorBody}`
|
|
117
|
+
);
|
|
72
118
|
}
|
|
73
|
-
async #putBlobMultipart(account, blobName, blobData, partSize = 5 * 1024 * 1024) {
|
|
119
|
+
async #putBlobMultipart(account, blobName, blobData, totalBytes, partSize = 5 * 1024 * 1024, onProgress) {
|
|
120
|
+
validateTotalBytes(totalBytes);
|
|
74
121
|
const startResponse = await fetch(
|
|
75
122
|
buildRequestUrl("/v1/multipart-uploads", this.baseUrl),
|
|
76
123
|
{
|
|
@@ -96,14 +143,38 @@ var ShelbyRPCClient = class {
|
|
|
96
143
|
`Failed to start multipart upload! status: ${startResponse.status}, body: ${errorBodyText}`
|
|
97
144
|
);
|
|
98
145
|
}
|
|
99
|
-
const { uploadId } =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
146
|
+
const { uploadId } = StartMultipartUploadResponseSchema.parse(
|
|
147
|
+
await startResponse.json()
|
|
148
|
+
);
|
|
149
|
+
const totalParts = Math.ceil(totalBytes / partSize);
|
|
150
|
+
let uploadedBytes = 0;
|
|
151
|
+
for await (const [partIdx, partData] of readInChunks(blobData, partSize)) {
|
|
105
152
|
await this.#uploadPart(uploadId, partIdx, partData);
|
|
153
|
+
uploadedBytes += partData.length;
|
|
154
|
+
onProgress?.({
|
|
155
|
+
phase: "uploading",
|
|
156
|
+
partIdx,
|
|
157
|
+
totalParts,
|
|
158
|
+
partBytes: partData.length,
|
|
159
|
+
uploadedBytes,
|
|
160
|
+
totalBytes
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (uploadedBytes !== totalBytes) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Uploaded bytes (${uploadedBytes}) did not match declared totalBytes (${totalBytes})`
|
|
166
|
+
);
|
|
106
167
|
}
|
|
168
|
+
const finalPartIdx = totalParts > 0 ? totalParts - 1 : 0;
|
|
169
|
+
onProgress?.({
|
|
170
|
+
phase: "finalizing",
|
|
171
|
+
partIdx: finalPartIdx,
|
|
172
|
+
totalParts,
|
|
173
|
+
// no part uploaded in this phase
|
|
174
|
+
partBytes: 0,
|
|
175
|
+
uploadedBytes: totalBytes,
|
|
176
|
+
totalBytes
|
|
177
|
+
});
|
|
107
178
|
const completeResponse = await fetch(
|
|
108
179
|
buildRequestUrl(
|
|
109
180
|
`/v1/multipart-uploads/${uploadId}/complete`,
|
|
@@ -135,7 +206,8 @@ var ShelbyRPCClient = class {
|
|
|
135
206
|
*
|
|
136
207
|
* @param params.account - The account that owns the blob.
|
|
137
208
|
* @param params.blobName - The name/path of the blob (e.g. "folder/file.txt").
|
|
138
|
-
* @param params.blobData - The raw blob data as a Uint8Array.
|
|
209
|
+
* @param params.blobData - The raw blob data as a Uint8Array or ReadableStream.
|
|
210
|
+
* @param params.totalBytes - Total byte length. Required for streams; optional for Uint8Array.
|
|
139
211
|
*
|
|
140
212
|
* @example
|
|
141
213
|
* ```typescript
|
|
@@ -144,19 +216,38 @@ var ShelbyRPCClient = class {
|
|
|
144
216
|
* await client.putBlob({
|
|
145
217
|
* account: AccountAddress.from("0x1"),
|
|
146
218
|
* blobName: "greetings/hello.txt",
|
|
147
|
-
* blobData
|
|
219
|
+
* blobData,
|
|
148
220
|
* });
|
|
149
221
|
* ```
|
|
150
222
|
*/
|
|
151
223
|
async putBlob(params) {
|
|
152
224
|
BlobNameSchema.parse(params.blobName);
|
|
225
|
+
let totalBytes;
|
|
226
|
+
if (params.blobData instanceof Uint8Array) {
|
|
227
|
+
totalBytes = params.totalBytes ?? params.blobData.length;
|
|
228
|
+
if (totalBytes !== params.blobData.length) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
"totalBytes must match blobData.length when blobData is a Uint8Array"
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
if (params.totalBytes === void 0) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
"totalBytes is required when blobData is a ReadableStream"
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
totalBytes = params.totalBytes;
|
|
240
|
+
}
|
|
241
|
+
validateTotalBytes(totalBytes);
|
|
153
242
|
await this.#putBlobMultipart(
|
|
154
243
|
params.account,
|
|
155
244
|
params.blobName,
|
|
156
|
-
params.blobData
|
|
245
|
+
params.blobData,
|
|
246
|
+
totalBytes,
|
|
247
|
+
void 0,
|
|
248
|
+
params.onProgress
|
|
157
249
|
);
|
|
158
250
|
}
|
|
159
|
-
// FIXME make this possible to stream in put ^^^
|
|
160
251
|
/**
|
|
161
252
|
* Downloads a blob from the Shelby RPC node.
|
|
162
253
|
* Returns a streaming response with validation to ensure data integrity.
|
|
@@ -166,10 +257,12 @@ var ShelbyRPCClient = class {
|
|
|
166
257
|
* @param params.range - Optional byte range for partial downloads.
|
|
167
258
|
* @param params.range.start - Starting byte position (inclusive).
|
|
168
259
|
* @param params.range.end - Ending byte position (inclusive, optional).
|
|
260
|
+
* @param params.micropayment - Optional micropayment to attach to the request.
|
|
169
261
|
*
|
|
170
262
|
* @returns A ShelbyBlob object containing the account, name, readable stream, and content length.
|
|
171
263
|
*
|
|
172
264
|
* @throws Error if the download fails or content length doesn't match.
|
|
265
|
+
* @throws StaleChannelStateError if the micropayment is stale (server has newer state).
|
|
173
266
|
*
|
|
174
267
|
* @example
|
|
175
268
|
* ```typescript
|
|
@@ -185,6 +278,13 @@ var ShelbyRPCClient = class {
|
|
|
185
278
|
* blobName: "large-file.bin",
|
|
186
279
|
* range: { start: 100, end: 199 }
|
|
187
280
|
* });
|
|
281
|
+
*
|
|
282
|
+
* // Download with micropayment
|
|
283
|
+
* const blob = await client.getBlob({
|
|
284
|
+
* account: AccountAddress.from("0x1"),
|
|
285
|
+
* blobName: "documents/report.pdf",
|
|
286
|
+
* micropayment: senderBuiltMicropayment
|
|
287
|
+
* });
|
|
188
288
|
* ```
|
|
189
289
|
*/
|
|
190
290
|
async getBlob(params) {
|
|
@@ -195,9 +295,8 @@ var ShelbyRPCClient = class {
|
|
|
195
295
|
)}`,
|
|
196
296
|
this.baseUrl
|
|
197
297
|
);
|
|
198
|
-
const
|
|
298
|
+
const headers = new Headers();
|
|
199
299
|
if (params.range !== void 0) {
|
|
200
|
-
const headers = new Headers();
|
|
201
300
|
const { start, end } = params.range;
|
|
202
301
|
if (end === void 0) {
|
|
203
302
|
headers.set("Range", `bytes=${start}-`);
|
|
@@ -207,15 +306,45 @@ var ShelbyRPCClient = class {
|
|
|
207
306
|
}
|
|
208
307
|
headers.set("Range", `bytes=${start}-${end}`);
|
|
209
308
|
}
|
|
210
|
-
requestInit.headers = headers;
|
|
211
309
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
310
|
+
if (this.apiKey) {
|
|
311
|
+
headers.set("Authorization", `Bearer ${this.apiKey}`);
|
|
312
|
+
}
|
|
313
|
+
if (params.micropayment) {
|
|
314
|
+
const bytes = params.micropayment.bcsToBytes();
|
|
315
|
+
const binaryString = Array.from(
|
|
316
|
+
bytes,
|
|
317
|
+
(byte) => String.fromCharCode(byte)
|
|
318
|
+
).join("");
|
|
319
|
+
headers.set(MICROPAYMENT_HEADER, btoa(binaryString));
|
|
320
|
+
}
|
|
321
|
+
const response = await fetch(url, { headers });
|
|
322
|
+
if (response.status === 409) {
|
|
323
|
+
let json;
|
|
324
|
+
try {
|
|
325
|
+
json = await response.json();
|
|
326
|
+
} catch {
|
|
327
|
+
throw new Error(
|
|
328
|
+
`Failed to download blob: ${response.status} ${response.statusText}`
|
|
329
|
+
);
|
|
217
330
|
}
|
|
218
|
-
|
|
331
|
+
const parseResult = StaleMicropaymentErrorResponseSchema.safeParse(json);
|
|
332
|
+
if (!parseResult.success) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
`Failed to download blob: ${response.status} ${response.statusText}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
const errorBody = parseResult.data;
|
|
338
|
+
if (errorBody.storedMicropayment) {
|
|
339
|
+
throw StaleChannelStateError.fromBase64(
|
|
340
|
+
errorBody.storedMicropayment,
|
|
341
|
+
errorBody.error
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
throw new Error(
|
|
345
|
+
errorBody.error ?? `Failed to download blob: ${response.status} ${response.statusText}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
219
348
|
if (!response.ok) {
|
|
220
349
|
throw new Error(
|
|
221
350
|
`Failed to download blob: ${response.status} ${response.statusText}`
|
package/dist/core/chunk.mjs
CHANGED