@opendatalabs/vana-sdk 0.1.0-alpha.761813a → 0.1.0-alpha.8eb4e46
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 +10 -10
- package/dist/chains.browser.cjs.map +1 -1
- package/dist/chains.browser.js.map +1 -1
- package/dist/chains.cjs.map +1 -1
- package/dist/chains.js.map +1 -1
- package/dist/chains.node.cjs.map +1 -1
- package/dist/chains.node.js.map +1 -1
- package/dist/index.browser.d.ts +906 -269
- package/dist/index.browser.js +639 -530
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +643 -536
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.cts +907 -270
- package/dist/index.node.d.ts +907 -270
- package/dist/index.node.js +638 -530
- package/dist/index.node.js.map +1 -1
- package/dist/platform.browser.js +1 -57
- package/dist/platform.browser.js.map +1 -1
- package/dist/platform.cjs +1 -57
- package/dist/platform.cjs.map +1 -1
- package/dist/platform.js +1 -57
- package/dist/platform.js.map +1 -1
- package/dist/platform.node.cjs +1 -57
- package/dist/platform.node.cjs.map +1 -1
- package/dist/platform.node.js +1 -57
- package/dist/platform.node.js.map +1 -1
- package/package.json +10 -2
package/dist/index.node.js
CHANGED
|
@@ -251,65 +251,9 @@ var init_browser = __esm({
|
|
|
251
251
|
try {
|
|
252
252
|
const eccrypto2 = await import("eccrypto-js");
|
|
253
253
|
const uncompressedKey = processWalletPublicKey(publicKey);
|
|
254
|
-
const iv = Buffer.from([
|
|
255
|
-
1,
|
|
256
|
-
2,
|
|
257
|
-
3,
|
|
258
|
-
4,
|
|
259
|
-
5,
|
|
260
|
-
6,
|
|
261
|
-
7,
|
|
262
|
-
8,
|
|
263
|
-
9,
|
|
264
|
-
10,
|
|
265
|
-
11,
|
|
266
|
-
12,
|
|
267
|
-
13,
|
|
268
|
-
14,
|
|
269
|
-
15,
|
|
270
|
-
16
|
|
271
|
-
]);
|
|
272
|
-
const ephemeralKey = Buffer.from([
|
|
273
|
-
17,
|
|
274
|
-
34,
|
|
275
|
-
51,
|
|
276
|
-
68,
|
|
277
|
-
85,
|
|
278
|
-
102,
|
|
279
|
-
119,
|
|
280
|
-
136,
|
|
281
|
-
153,
|
|
282
|
-
170,
|
|
283
|
-
187,
|
|
284
|
-
204,
|
|
285
|
-
221,
|
|
286
|
-
238,
|
|
287
|
-
255,
|
|
288
|
-
0,
|
|
289
|
-
16,
|
|
290
|
-
32,
|
|
291
|
-
48,
|
|
292
|
-
64,
|
|
293
|
-
80,
|
|
294
|
-
96,
|
|
295
|
-
112,
|
|
296
|
-
128,
|
|
297
|
-
144,
|
|
298
|
-
160,
|
|
299
|
-
176,
|
|
300
|
-
192,
|
|
301
|
-
208,
|
|
302
|
-
224,
|
|
303
|
-
240,
|
|
304
|
-
0
|
|
305
|
-
]);
|
|
306
254
|
const encryptedBuffer = await eccrypto2.encrypt(
|
|
307
255
|
uncompressedKey,
|
|
308
|
-
Buffer.from(data)
|
|
309
|
-
{
|
|
310
|
-
iv,
|
|
311
|
-
ephemPrivateKey: ephemeralKey
|
|
312
|
-
}
|
|
256
|
+
Buffer.from(data)
|
|
313
257
|
);
|
|
314
258
|
const result = Buffer.concat([
|
|
315
259
|
encryptedBuffer.iv,
|
|
@@ -678,6 +622,9 @@ function isWalletConfig(config) {
|
|
|
678
622
|
function isChainConfig(config) {
|
|
679
623
|
return "chainId" in config && !("walletClient" in config);
|
|
680
624
|
}
|
|
625
|
+
function hasStorageConfig(config) {
|
|
626
|
+
return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
|
|
627
|
+
}
|
|
681
628
|
|
|
682
629
|
// src/types/chains.ts
|
|
683
630
|
function isVanaChainId(chainId) {
|
|
@@ -973,55 +920,6 @@ function isReplicateAPIResponse(value) {
|
|
|
973
920
|
obj.status
|
|
974
921
|
);
|
|
975
922
|
}
|
|
976
|
-
function isIdentityServerOutput(value) {
|
|
977
|
-
console.debug("\u{1F50D} Type Guard: Checking value:", value);
|
|
978
|
-
console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
|
|
979
|
-
if (typeof value !== "object" || value === null) {
|
|
980
|
-
console.debug("\u{1F50D} Type Guard: Failed - not object or null");
|
|
981
|
-
return false;
|
|
982
|
-
}
|
|
983
|
-
const obj = value;
|
|
984
|
-
console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
|
|
985
|
-
console.debug(
|
|
986
|
-
"\u{1F50D} Type Guard: user_address:",
|
|
987
|
-
obj.user_address,
|
|
988
|
-
typeof obj.user_address
|
|
989
|
-
);
|
|
990
|
-
if (typeof obj.user_address !== "string") {
|
|
991
|
-
console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
|
|
992
|
-
return false;
|
|
993
|
-
}
|
|
994
|
-
console.debug(
|
|
995
|
-
"\u{1F50D} Type Guard: personal_server:",
|
|
996
|
-
obj.personal_server,
|
|
997
|
-
typeof obj.personal_server
|
|
998
|
-
);
|
|
999
|
-
if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
|
|
1000
|
-
console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
|
|
1001
|
-
return false;
|
|
1002
|
-
}
|
|
1003
|
-
const personalServer = obj.personal_server;
|
|
1004
|
-
console.debug(
|
|
1005
|
-
"\u{1F50D} Type Guard: Personal server keys:",
|
|
1006
|
-
Object.keys(personalServer)
|
|
1007
|
-
);
|
|
1008
|
-
console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
|
|
1009
|
-
console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
|
|
1010
|
-
const hasAddress = "address" in personalServer;
|
|
1011
|
-
const hasPublicKey = "public_key" in personalServer;
|
|
1012
|
-
console.debug(
|
|
1013
|
-
"\u{1F50D} Type Guard: Has address:",
|
|
1014
|
-
hasAddress,
|
|
1015
|
-
"Has public_key:",
|
|
1016
|
-
hasPublicKey
|
|
1017
|
-
);
|
|
1018
|
-
return hasAddress && hasPublicKey;
|
|
1019
|
-
}
|
|
1020
|
-
function isPersonalServerOutput(value) {
|
|
1021
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1022
|
-
const obj = value;
|
|
1023
|
-
return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
|
|
1024
|
-
}
|
|
1025
923
|
function isAPIResponse(value) {
|
|
1026
924
|
if (typeof value !== "object" || value === null) return false;
|
|
1027
925
|
const obj = value;
|
|
@@ -33614,7 +33512,7 @@ function getAbi(contract) {
|
|
|
33614
33512
|
import { keccak256, toHex } from "viem";
|
|
33615
33513
|
function createGrantFile(params) {
|
|
33616
33514
|
const grantFile = {
|
|
33617
|
-
grantee: params.
|
|
33515
|
+
grantee: params.grantee,
|
|
33618
33516
|
operation: params.operation,
|
|
33619
33517
|
parameters: params.parameters
|
|
33620
33518
|
};
|
|
@@ -34034,7 +33932,7 @@ var PermissionsController = class {
|
|
|
34034
33932
|
* @example
|
|
34035
33933
|
* ```typescript
|
|
34036
33934
|
* const txHash = await vana.permissions.grant({
|
|
34037
|
-
*
|
|
33935
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34038
33936
|
* operation: "llm_inference",
|
|
34039
33937
|
* parameters: {
|
|
34040
33938
|
* model: "gpt-4",
|
|
@@ -34063,7 +33961,7 @@ var PermissionsController = class {
|
|
|
34063
33961
|
* @example
|
|
34064
33962
|
* ```typescript
|
|
34065
33963
|
* const { preview, confirm } = await vana.permissions.prepareGrant({
|
|
34066
|
-
*
|
|
33964
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34067
33965
|
* operation: "llm_inference",
|
|
34068
33966
|
* files: [1, 2, 3],
|
|
34069
33967
|
* parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
|
|
@@ -34112,9 +34010,13 @@ var PermissionsController = class {
|
|
|
34112
34010
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
34113
34011
|
if (!grantUrl) {
|
|
34114
34012
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
34115
|
-
|
|
34116
|
-
|
|
34117
|
-
|
|
34013
|
+
if (this.context.validateStorageRequired) {
|
|
34014
|
+
this.context.validateStorageRequired();
|
|
34015
|
+
} else {
|
|
34016
|
+
throw new Error(
|
|
34017
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
34018
|
+
);
|
|
34019
|
+
}
|
|
34118
34020
|
}
|
|
34119
34021
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
34120
34022
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -34138,7 +34040,7 @@ var PermissionsController = class {
|
|
|
34138
34040
|
grantUrl
|
|
34139
34041
|
);
|
|
34140
34042
|
const typedData = await this.composePermissionGrantMessage({
|
|
34141
|
-
|
|
34043
|
+
grantee: params.grantee,
|
|
34142
34044
|
operation: params.operation,
|
|
34143
34045
|
// Placeholder - real data is in IPFS
|
|
34144
34046
|
files: params.files,
|
|
@@ -34185,7 +34087,7 @@ var PermissionsController = class {
|
|
|
34185
34087
|
* @example
|
|
34186
34088
|
* ```typescript
|
|
34187
34089
|
* const { typedData, signature } = await vana.permissions.createAndSign({
|
|
34188
|
-
*
|
|
34090
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34189
34091
|
* operation: "data_analysis",
|
|
34190
34092
|
* parameters: { analysisType: "sentiment" },
|
|
34191
34093
|
* });
|
|
@@ -34201,9 +34103,13 @@ var PermissionsController = class {
|
|
|
34201
34103
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
34202
34104
|
if (!grantUrl) {
|
|
34203
34105
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
34204
|
-
|
|
34205
|
-
|
|
34206
|
-
|
|
34106
|
+
if (this.context.validateStorageRequired) {
|
|
34107
|
+
this.context.validateStorageRequired();
|
|
34108
|
+
} else {
|
|
34109
|
+
throw new Error(
|
|
34110
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
34111
|
+
);
|
|
34112
|
+
}
|
|
34207
34113
|
}
|
|
34208
34114
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
34209
34115
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -34227,7 +34133,7 @@ var PermissionsController = class {
|
|
|
34227
34133
|
grantUrl
|
|
34228
34134
|
);
|
|
34229
34135
|
const typedData = await this.composePermissionGrantMessage({
|
|
34230
|
-
|
|
34136
|
+
grantee: params.grantee,
|
|
34231
34137
|
operation: params.operation,
|
|
34232
34138
|
// Placeholder - real data is in IPFS
|
|
34233
34139
|
files: params.files,
|
|
@@ -34578,7 +34484,7 @@ var PermissionsController = class {
|
|
|
34578
34484
|
* Composes the EIP-712 typed data for PermissionGrant (new simplified format).
|
|
34579
34485
|
*
|
|
34580
34486
|
* @param params - The parameters for composing the permission grant message
|
|
34581
|
-
* @param params.
|
|
34487
|
+
* @param params.grantee - The recipient address for the permission grant
|
|
34582
34488
|
* @param params.operation - The type of operation being granted permission for
|
|
34583
34489
|
* @param params.files - Array of file IDs that the permission applies to
|
|
34584
34490
|
* @param params.grantUrl - URL where the grant details are stored
|
|
@@ -34671,36 +34577,45 @@ var PermissionsController = class {
|
|
|
34671
34577
|
return addresses[0];
|
|
34672
34578
|
}
|
|
34673
34579
|
/**
|
|
34674
|
-
*
|
|
34580
|
+
* Gets on-chain permission grant data without expensive off-chain resolution.
|
|
34675
34581
|
*
|
|
34676
34582
|
* @remarks
|
|
34677
|
-
* This method
|
|
34678
|
-
*
|
|
34679
|
-
*
|
|
34680
|
-
*
|
|
34681
|
-
*
|
|
34682
|
-
*
|
|
34683
|
-
*
|
|
34684
|
-
*
|
|
34685
|
-
*
|
|
34686
|
-
*
|
|
34583
|
+
* This method provides a fast, performance-focused way to retrieve permission grants
|
|
34584
|
+
* by querying only the subgraph without making expensive IPFS or individual contract calls.
|
|
34585
|
+
* It eliminates the N+1 query problem of the legacy `getUserPermissions()` method.
|
|
34586
|
+
*
|
|
34587
|
+
* The returned data contains all on-chain information but does NOT include resolved
|
|
34588
|
+
* operation details, parameters, or file IDs. Use `retrieveGrantFile()` separately
|
|
34589
|
+
* for specific grants when detailed data is needed.
|
|
34590
|
+
*
|
|
34591
|
+
* **Performance**: Completes in ~100-500ms regardless of permission count.
|
|
34592
|
+
* **Reliability**: Single point of failure (subgraph) with clear RPC fallback path.
|
|
34593
|
+
*
|
|
34594
|
+
* @param options - Options for retrieving permissions (limit, subgraph URL)
|
|
34595
|
+
* @returns A Promise that resolves to an array of `OnChainPermissionGrant` objects
|
|
34596
|
+
* @throws {BlockchainError} When subgraph query fails
|
|
34597
|
+
* @throws {NetworkError} When network requests fail
|
|
34687
34598
|
* @example
|
|
34688
34599
|
* ```typescript
|
|
34689
|
-
* // Get all
|
|
34690
|
-
* const
|
|
34600
|
+
* // Fast: Get all on-chain permission data
|
|
34601
|
+
* const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
|
|
34691
34602
|
*
|
|
34692
|
-
*
|
|
34693
|
-
*
|
|
34603
|
+
* // Display in UI immediately
|
|
34604
|
+
* grants.forEach(grant => {
|
|
34605
|
+
* console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
|
|
34694
34606
|
* });
|
|
34695
34607
|
*
|
|
34696
|
-
* //
|
|
34697
|
-
* const
|
|
34608
|
+
* // Lazy load detailed data for specific permission when user clicks
|
|
34609
|
+
* const grantFile = await retrieveGrantFile(grants[0].grantUrl);
|
|
34610
|
+
* console.log(`Operation: ${grantFile.operation}`);
|
|
34611
|
+
* console.log(`Parameters:`, grantFile.parameters);
|
|
34698
34612
|
* ```
|
|
34699
34613
|
*/
|
|
34700
|
-
async
|
|
34614
|
+
async getUserPermissionGrantsOnChain(options = {}) {
|
|
34615
|
+
const { limit = 50, subgraphUrl } = options;
|
|
34701
34616
|
try {
|
|
34702
34617
|
const userAddress = await this.getUserAddress();
|
|
34703
|
-
const graphqlEndpoint =
|
|
34618
|
+
const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
|
|
34704
34619
|
if (!graphqlEndpoint) {
|
|
34705
34620
|
throw new BlockchainError(
|
|
34706
34621
|
"subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
|
|
@@ -34722,16 +34637,6 @@ var PermissionsController = class {
|
|
|
34722
34637
|
}
|
|
34723
34638
|
}
|
|
34724
34639
|
`;
|
|
34725
|
-
console.info("Query:", query);
|
|
34726
|
-
console.info(
|
|
34727
|
-
"Body:",
|
|
34728
|
-
JSON.stringify({
|
|
34729
|
-
query,
|
|
34730
|
-
variables: {
|
|
34731
|
-
userId: userAddress.toLowerCase()
|
|
34732
|
-
}
|
|
34733
|
-
})
|
|
34734
|
-
);
|
|
34735
34640
|
const response = await fetch(graphqlEndpoint, {
|
|
34736
34641
|
method: "POST",
|
|
34737
34642
|
headers: {
|
|
@@ -34750,7 +34655,6 @@ var PermissionsController = class {
|
|
|
34750
34655
|
);
|
|
34751
34656
|
}
|
|
34752
34657
|
const result = await response.json();
|
|
34753
|
-
console.info("Result:", result);
|
|
34754
34658
|
if (result.errors) {
|
|
34755
34659
|
throw new BlockchainError(
|
|
34756
34660
|
`Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
|
|
@@ -34758,64 +34662,32 @@ var PermissionsController = class {
|
|
|
34758
34662
|
}
|
|
34759
34663
|
const userData = result.data?.user;
|
|
34760
34664
|
if (!userData || !userData.permissions?.length) {
|
|
34761
|
-
console.warn("No permissions found for user:", userAddress);
|
|
34762
34665
|
return [];
|
|
34763
34666
|
}
|
|
34764
|
-
const
|
|
34765
|
-
|
|
34766
|
-
|
|
34767
|
-
|
|
34768
|
-
|
|
34769
|
-
|
|
34770
|
-
|
|
34771
|
-
|
|
34772
|
-
|
|
34773
|
-
|
|
34774
|
-
|
|
34775
|
-
|
|
34776
|
-
|
|
34777
|
-
|
|
34778
|
-
} catch {
|
|
34779
|
-
}
|
|
34780
|
-
try {
|
|
34781
|
-
const fileIds = await this.getPermissionFileIds(
|
|
34782
|
-
BigInt(permission.id)
|
|
34783
|
-
);
|
|
34784
|
-
files = fileIds.map((id) => Number(id));
|
|
34785
|
-
} catch {
|
|
34786
|
-
}
|
|
34787
|
-
userPermissions.push({
|
|
34788
|
-
id: BigInt(permission.id),
|
|
34789
|
-
files,
|
|
34790
|
-
operation: operation || "",
|
|
34791
|
-
parameters: parameters || {},
|
|
34792
|
-
grant: permission.grant,
|
|
34793
|
-
grantor: userAddress.toLowerCase(),
|
|
34794
|
-
// Current user is the grantor
|
|
34795
|
-
grantee: granteeAddress || userAddress,
|
|
34796
|
-
// Application that received permission
|
|
34797
|
-
active: true,
|
|
34798
|
-
// Default to active if not specified
|
|
34799
|
-
grantedAt: Number(permission.addedAtBlock),
|
|
34800
|
-
nonce: Number(permission.nonce)
|
|
34801
|
-
});
|
|
34802
|
-
} catch (error) {
|
|
34803
|
-
console.error(
|
|
34804
|
-
"SDK Error: Failed to process permission:",
|
|
34805
|
-
permission.id,
|
|
34806
|
-
error
|
|
34807
|
-
);
|
|
34808
|
-
}
|
|
34809
|
-
}
|
|
34810
|
-
return userPermissions.sort((a, b) => {
|
|
34667
|
+
const onChainGrants = userData.permissions.slice(0, limit).map((permission) => ({
|
|
34668
|
+
id: BigInt(permission.id),
|
|
34669
|
+
grantUrl: permission.grant,
|
|
34670
|
+
grantSignature: permission.grantSignature,
|
|
34671
|
+
grantHash: permission.grantHash,
|
|
34672
|
+
nonce: BigInt(permission.nonce),
|
|
34673
|
+
addedAtBlock: BigInt(permission.addedAtBlock),
|
|
34674
|
+
addedAtTimestamp: BigInt(permission.addedAtTimestamp || "0"),
|
|
34675
|
+
transactionHash: permission.transactionHash || "",
|
|
34676
|
+
grantor: userAddress,
|
|
34677
|
+
active: true
|
|
34678
|
+
// TODO: Add revocation status from subgraph when available
|
|
34679
|
+
}));
|
|
34680
|
+
return onChainGrants.sort((a, b) => {
|
|
34811
34681
|
if (a.id < b.id) return 1;
|
|
34812
34682
|
if (a.id > b.id) return -1;
|
|
34813
34683
|
return 0;
|
|
34814
34684
|
});
|
|
34815
34685
|
} catch (error) {
|
|
34816
|
-
|
|
34686
|
+
if (error instanceof BlockchainError || error instanceof NetworkError) {
|
|
34687
|
+
throw error;
|
|
34688
|
+
}
|
|
34817
34689
|
throw new BlockchainError(
|
|
34818
|
-
`Failed to fetch user
|
|
34690
|
+
`Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
34819
34691
|
);
|
|
34820
34692
|
}
|
|
34821
34693
|
}
|
|
@@ -35577,32 +35449,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
|
|
|
35577
35449
|
throw new Error(`Failed to decrypt with private key: ${error}`);
|
|
35578
35450
|
}
|
|
35579
35451
|
}
|
|
35580
|
-
async function
|
|
35452
|
+
async function encryptBlobWithSignedKey(data, key, platformAdapter) {
|
|
35581
35453
|
try {
|
|
35582
35454
|
const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
|
|
35583
35455
|
const dataArray = new Uint8Array(dataBuffer);
|
|
35584
35456
|
const encrypted = await platformAdapter.crypto.encryptWithPassword(
|
|
35585
35457
|
dataArray,
|
|
35586
|
-
|
|
35458
|
+
key
|
|
35587
35459
|
);
|
|
35588
35460
|
return new Blob([encrypted], {
|
|
35589
35461
|
type: "application/octet-stream"
|
|
35590
35462
|
});
|
|
35591
35463
|
} catch (error) {
|
|
35592
|
-
throw new Error(`Failed to encrypt
|
|
35593
|
-
}
|
|
35594
|
-
}
|
|
35595
|
-
async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
|
|
35596
|
-
try {
|
|
35597
|
-
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35598
|
-
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35599
|
-
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35600
|
-
encryptedArray,
|
|
35601
|
-
walletSignature
|
|
35602
|
-
);
|
|
35603
|
-
return new Blob([decrypted], { type: "text/plain" });
|
|
35604
|
-
} catch (error) {
|
|
35605
|
-
throw new Error(`Failed to decrypt user data: ${error}`);
|
|
35464
|
+
throw new Error(`Failed to encrypt data: ${error}`);
|
|
35606
35465
|
}
|
|
35607
35466
|
}
|
|
35608
35467
|
async function generateEncryptionKeyPair(platformAdapter) {
|
|
@@ -35619,60 +35478,25 @@ async function generatePGPKeyPair(platformAdapter, options) {
|
|
|
35619
35478
|
throw new Error(`Failed to generate PGP key pair: ${error}`);
|
|
35620
35479
|
}
|
|
35621
35480
|
}
|
|
35481
|
+
async function decryptBlobWithSignedKey(encryptedData, key, platformAdapter) {
|
|
35482
|
+
try {
|
|
35483
|
+
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35484
|
+
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35485
|
+
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35486
|
+
encryptedArray,
|
|
35487
|
+
key
|
|
35488
|
+
);
|
|
35489
|
+
return new Blob([decrypted], { type: "text/plain" });
|
|
35490
|
+
} catch (error) {
|
|
35491
|
+
throw new Error(`Failed to decrypt data: ${error}`);
|
|
35492
|
+
}
|
|
35493
|
+
}
|
|
35622
35494
|
|
|
35623
35495
|
// src/controllers/data.ts
|
|
35624
35496
|
var DataController = class {
|
|
35625
35497
|
constructor(context) {
|
|
35626
35498
|
this.context = context;
|
|
35627
35499
|
}
|
|
35628
|
-
/**
|
|
35629
|
-
* Uploads user data with automatic encryption and blockchain registration.
|
|
35630
|
-
*
|
|
35631
|
-
* @remarks
|
|
35632
|
-
* This is the primary method for uploading user data to the Vana network. It handles
|
|
35633
|
-
* the complete workflow including content normalization, schema validation, encryption,
|
|
35634
|
-
* storage upload, permission granting, and blockchain registration.
|
|
35635
|
-
*
|
|
35636
|
-
* The method automatically:
|
|
35637
|
-
* - Normalizes input content to a Blob
|
|
35638
|
-
* - Validates data against schema if provided
|
|
35639
|
-
* - Generates encryption keys and encrypts the data
|
|
35640
|
-
* - Uploads to the configured storage provider
|
|
35641
|
-
* - Grants permissions to specified applications
|
|
35642
|
-
* - Registers the file on the blockchain
|
|
35643
|
-
*
|
|
35644
|
-
* @param params - Upload parameters including content, filename, schema, and permissions
|
|
35645
|
-
* @returns Promise resolving to upload results with file ID and transaction hash
|
|
35646
|
-
* @throws {Error} When wallet is not connected or storage is not configured
|
|
35647
|
-
* @throws {SchemaValidationError} When schema validation fails
|
|
35648
|
-
* @throws {Error} When upload or blockchain registration fails
|
|
35649
|
-
* @example
|
|
35650
|
-
* ```typescript
|
|
35651
|
-
* // Basic file upload
|
|
35652
|
-
* const result = await vana.data.upload({
|
|
35653
|
-
* content: "My personal data",
|
|
35654
|
-
* filename: "diary.txt"
|
|
35655
|
-
* });
|
|
35656
|
-
*
|
|
35657
|
-
* // Upload with schema validation
|
|
35658
|
-
* const result = await vana.data.upload({
|
|
35659
|
-
* content: { name: "John", age: 30 },
|
|
35660
|
-
* filename: "profile.json",
|
|
35661
|
-
* schemaId: 1
|
|
35662
|
-
* });
|
|
35663
|
-
*
|
|
35664
|
-
* // Upload with permissions
|
|
35665
|
-
* const result = await vana.data.upload({
|
|
35666
|
-
* content: "Data for AI analysis",
|
|
35667
|
-
* filename: "analysis.txt",
|
|
35668
|
-
* permissions: [{
|
|
35669
|
-
* to: "0x1234...",
|
|
35670
|
-
* operation: "llm_inference",
|
|
35671
|
-
* parameters: { model: "gpt-4" }
|
|
35672
|
-
* }]
|
|
35673
|
-
* });
|
|
35674
|
-
* ```
|
|
35675
|
-
*/
|
|
35676
35500
|
async upload(params) {
|
|
35677
35501
|
const {
|
|
35678
35502
|
content,
|
|
@@ -35680,7 +35504,8 @@ var DataController = class {
|
|
|
35680
35504
|
schemaId,
|
|
35681
35505
|
permissions = [],
|
|
35682
35506
|
encrypt: encrypt3 = true,
|
|
35683
|
-
providerName
|
|
35507
|
+
providerName,
|
|
35508
|
+
owner
|
|
35684
35509
|
} = params;
|
|
35685
35510
|
try {
|
|
35686
35511
|
let blob;
|
|
@@ -35737,23 +35562,28 @@ var DataController = class {
|
|
|
35737
35562
|
this.context.walletClient,
|
|
35738
35563
|
DEFAULT_ENCRYPTION_SEED
|
|
35739
35564
|
);
|
|
35740
|
-
finalBlob = await
|
|
35565
|
+
finalBlob = await encryptBlobWithSignedKey(
|
|
35741
35566
|
blob,
|
|
35742
35567
|
encryptionKey,
|
|
35743
35568
|
this.context.platform
|
|
35744
35569
|
);
|
|
35745
35570
|
}
|
|
35746
35571
|
if (!this.context.storageManager) {
|
|
35747
|
-
|
|
35748
|
-
|
|
35749
|
-
|
|
35572
|
+
if (this.context.validateStorageRequired) {
|
|
35573
|
+
this.context.validateStorageRequired();
|
|
35574
|
+
throw new Error("Storage validation failed");
|
|
35575
|
+
} else {
|
|
35576
|
+
throw new Error(
|
|
35577
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
35578
|
+
);
|
|
35579
|
+
}
|
|
35750
35580
|
}
|
|
35751
35581
|
const uploadResult = await this.context.storageManager.upload(
|
|
35752
35582
|
finalBlob,
|
|
35753
35583
|
filename,
|
|
35754
35584
|
providerName
|
|
35755
35585
|
);
|
|
35756
|
-
const userAddress = await this.getUserAddress();
|
|
35586
|
+
const userAddress = owner || await this.getUserAddress();
|
|
35757
35587
|
let encryptedPermissions = [];
|
|
35758
35588
|
if (permissions.length > 0 && encrypt3) {
|
|
35759
35589
|
const userEncryptionKey = await generateEncryptionKey(
|
|
@@ -35765,7 +35595,7 @@ var DataController = class {
|
|
|
35765
35595
|
const publicKey = permission.publicKey;
|
|
35766
35596
|
if (!publicKey) {
|
|
35767
35597
|
throw new Error(
|
|
35768
|
-
`Public key required for permission to ${permission.
|
|
35598
|
+
`Public key required for permission to ${permission.grantee} when encryption is enabled`
|
|
35769
35599
|
);
|
|
35770
35600
|
}
|
|
35771
35601
|
const encryptedKey = await encryptWithWalletPublicKey(
|
|
@@ -35774,7 +35604,7 @@ var DataController = class {
|
|
|
35774
35604
|
this.context.platform
|
|
35775
35605
|
);
|
|
35776
35606
|
return {
|
|
35777
|
-
account: permission.
|
|
35607
|
+
account: permission.grantee,
|
|
35778
35608
|
key: encryptedKey
|
|
35779
35609
|
};
|
|
35780
35610
|
})
|
|
@@ -35787,7 +35617,8 @@ var DataController = class {
|
|
|
35787
35617
|
url: uploadResult.url,
|
|
35788
35618
|
userAddress,
|
|
35789
35619
|
permissions: encryptedPermissions,
|
|
35790
|
-
schemaId: schemaId || 0
|
|
35620
|
+
schemaId: schemaId || 0,
|
|
35621
|
+
ownerAddress: owner
|
|
35791
35622
|
}
|
|
35792
35623
|
);
|
|
35793
35624
|
} else if (this.context.relayerCallbacks?.submitFileAddition) {
|
|
@@ -35823,6 +35654,142 @@ var DataController = class {
|
|
|
35823
35654
|
);
|
|
35824
35655
|
}
|
|
35825
35656
|
}
|
|
35657
|
+
/**
|
|
35658
|
+
* Decrypts a file owned by the user using their wallet signature.
|
|
35659
|
+
*
|
|
35660
|
+
* @remarks
|
|
35661
|
+
* This is the high-level convenience method for decrypting user files, serving as the
|
|
35662
|
+
* symmetrical counterpart to the `upload` method. It handles the complete decryption
|
|
35663
|
+
* workflow including key generation, URL protocol detection, content fetching, and
|
|
35664
|
+
* decryption.
|
|
35665
|
+
*
|
|
35666
|
+
* The method automatically:
|
|
35667
|
+
* - Generates the decryption key from the user's wallet signature
|
|
35668
|
+
* - Determines the appropriate fetch method based on the file URL protocol
|
|
35669
|
+
* - Fetches the encrypted content from IPFS or standard HTTP URLs
|
|
35670
|
+
* - Decrypts the content using the generated key
|
|
35671
|
+
*
|
|
35672
|
+
* For IPFS URLs, the method uses gateway fallback for improved reliability. For
|
|
35673
|
+
* standard HTTP URLs, it uses a simple fetch. If you need custom authentication
|
|
35674
|
+
* headers or specific gateway configurations, use the low-level primitives directly.
|
|
35675
|
+
*
|
|
35676
|
+
* @param file - The user file to decrypt (typically from getUserFiles)
|
|
35677
|
+
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
35678
|
+
* @returns Promise resolving to the decrypted file content as a Blob
|
|
35679
|
+
* @throws {Error} When the wallet is not connected
|
|
35680
|
+
* @throws {Error} When fetching the encrypted content fails
|
|
35681
|
+
* @throws {Error} When decryption fails (wrong key or corrupted data)
|
|
35682
|
+
* @example
|
|
35683
|
+
* ```typescript
|
|
35684
|
+
* // Basic file decryption
|
|
35685
|
+
* const files = await vana.data.getUserFiles({ owner: userAddress });
|
|
35686
|
+
* const decryptedBlob = await vana.data.decryptFile(files[0]);
|
|
35687
|
+
*
|
|
35688
|
+
* // Convert to text
|
|
35689
|
+
* const text = await decryptedBlob.text();
|
|
35690
|
+
* console.log('Decrypted content:', text);
|
|
35691
|
+
*
|
|
35692
|
+
* // Convert to JSON
|
|
35693
|
+
* const json = JSON.parse(await decryptedBlob.text());
|
|
35694
|
+
* console.log('Decrypted data:', json);
|
|
35695
|
+
*
|
|
35696
|
+
* // With custom encryption seed
|
|
35697
|
+
* const decryptedBlob = await vana.data.decryptFile(
|
|
35698
|
+
* files[0],
|
|
35699
|
+
* "My custom encryption seed"
|
|
35700
|
+
* );
|
|
35701
|
+
*
|
|
35702
|
+
* // Save to file (in Node.js)
|
|
35703
|
+
* const buffer = await decryptedBlob.arrayBuffer();
|
|
35704
|
+
* fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
|
|
35705
|
+
* ```
|
|
35706
|
+
*/
|
|
35707
|
+
async decryptFile(file, encryptionSeed) {
|
|
35708
|
+
try {
|
|
35709
|
+
const encryptionKey = await generateEncryptionKey(
|
|
35710
|
+
this.context.walletClient,
|
|
35711
|
+
encryptionSeed || DEFAULT_ENCRYPTION_SEED
|
|
35712
|
+
);
|
|
35713
|
+
let encryptedBlob;
|
|
35714
|
+
try {
|
|
35715
|
+
if (file.url.startsWith("ipfs://")) {
|
|
35716
|
+
encryptedBlob = await this.fetchFromIPFS(file.url);
|
|
35717
|
+
} else {
|
|
35718
|
+
encryptedBlob = await this.fetch(file.url);
|
|
35719
|
+
}
|
|
35720
|
+
} catch (fetchError) {
|
|
35721
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
|
|
35722
|
+
if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
|
|
35723
|
+
throw new Error(
|
|
35724
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35725
|
+
);
|
|
35726
|
+
} else if (errorMessage.includes("Empty response")) {
|
|
35727
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35728
|
+
} else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
|
|
35729
|
+
throw new Error(
|
|
35730
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35731
|
+
);
|
|
35732
|
+
} else if (errorMessage.includes("HTTP error!")) {
|
|
35733
|
+
const statusMatch = errorMessage.match(/status: (\d+)/);
|
|
35734
|
+
const status = statusMatch ? statusMatch[1] : "unknown";
|
|
35735
|
+
if (status === "500") {
|
|
35736
|
+
throw new Error(
|
|
35737
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35738
|
+
);
|
|
35739
|
+
} else if (status === "403") {
|
|
35740
|
+
throw new Error(
|
|
35741
|
+
"Access denied. You may not have permission to access this file"
|
|
35742
|
+
);
|
|
35743
|
+
} else if (status === "404") {
|
|
35744
|
+
throw new Error(
|
|
35745
|
+
"File not found: The encrypted file is no longer available at the stored URL."
|
|
35746
|
+
);
|
|
35747
|
+
} else {
|
|
35748
|
+
throw new Error(
|
|
35749
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35750
|
+
);
|
|
35751
|
+
}
|
|
35752
|
+
}
|
|
35753
|
+
throw fetchError;
|
|
35754
|
+
}
|
|
35755
|
+
if (encryptedBlob.size === 0) {
|
|
35756
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35757
|
+
}
|
|
35758
|
+
let decryptedBlob;
|
|
35759
|
+
try {
|
|
35760
|
+
decryptedBlob = await decryptBlobWithSignedKey(
|
|
35761
|
+
encryptedBlob,
|
|
35762
|
+
encryptionKey,
|
|
35763
|
+
this.context.platform
|
|
35764
|
+
);
|
|
35765
|
+
} catch (decryptError) {
|
|
35766
|
+
const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
|
|
35767
|
+
if (errorMessage.includes("not a valid OpenPGP message")) {
|
|
35768
|
+
throw new Error(
|
|
35769
|
+
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
|
|
35770
|
+
);
|
|
35771
|
+
} else if (errorMessage.includes("Session key decryption failed")) {
|
|
35772
|
+
throw new Error("Wrong encryption key");
|
|
35773
|
+
} else if (errorMessage.includes("Error decrypting message")) {
|
|
35774
|
+
throw new Error("Wrong encryption key");
|
|
35775
|
+
} else if (errorMessage.includes("File not found")) {
|
|
35776
|
+
throw new Error(
|
|
35777
|
+
"File not found: The encrypted file is no longer available"
|
|
35778
|
+
);
|
|
35779
|
+
} else {
|
|
35780
|
+
throw decryptError;
|
|
35781
|
+
}
|
|
35782
|
+
}
|
|
35783
|
+
return decryptedBlob;
|
|
35784
|
+
} catch (error) {
|
|
35785
|
+
if (error instanceof Error && (error.message.includes("Network error:") || error.message.includes("Invalid file format:") || error.message.includes("Wrong encryption key") || error.message.includes("Access denied") || error.message.includes("File not found:") || error.message.includes("File is empty"))) {
|
|
35786
|
+
throw error;
|
|
35787
|
+
}
|
|
35788
|
+
throw new Error(
|
|
35789
|
+
`Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35790
|
+
);
|
|
35791
|
+
}
|
|
35792
|
+
}
|
|
35826
35793
|
/**
|
|
35827
35794
|
* Retrieves all data files owned by a specific user address.
|
|
35828
35795
|
*
|
|
@@ -36573,81 +36540,6 @@ var DataController = class {
|
|
|
36573
36540
|
);
|
|
36574
36541
|
}
|
|
36575
36542
|
}
|
|
36576
|
-
/**
|
|
36577
|
-
* Decrypts a file that was encrypted using the Vana protocol.
|
|
36578
|
-
*
|
|
36579
|
-
* @param file - The UserFile object containing the file URL and metadata
|
|
36580
|
-
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
36581
|
-
* @returns Promise resolving to the decrypted file as a Blob
|
|
36582
|
-
*
|
|
36583
|
-
* This method handles the complete flow of:
|
|
36584
|
-
* 1. Generating the encryption key from the user's wallet signature
|
|
36585
|
-
* 2. Fetching the encrypted file from the stored URL
|
|
36586
|
-
* 3. Decrypting the file using the canonical Vana decryption method
|
|
36587
|
-
*/
|
|
36588
|
-
async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
|
|
36589
|
-
try {
|
|
36590
|
-
const encryptionKey = await generateEncryptionKey(
|
|
36591
|
-
this.context.walletClient,
|
|
36592
|
-
encryptionSeed
|
|
36593
|
-
);
|
|
36594
|
-
const fetchUrl = this.convertToDownloadUrl(file.url);
|
|
36595
|
-
console.debug(
|
|
36596
|
-
`Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
|
|
36597
|
-
);
|
|
36598
|
-
const response = await fetch(fetchUrl);
|
|
36599
|
-
if (!response.ok) {
|
|
36600
|
-
if (response.status === 404) {
|
|
36601
|
-
throw new Error(
|
|
36602
|
-
"File not found. The encrypted file may have been moved or deleted."
|
|
36603
|
-
);
|
|
36604
|
-
} else if (response.status === 403) {
|
|
36605
|
-
throw new Error(
|
|
36606
|
-
"Access denied. You may not have permission to access this file."
|
|
36607
|
-
);
|
|
36608
|
-
} else {
|
|
36609
|
-
throw new Error(
|
|
36610
|
-
`Network error: ${response.status} ${response.statusText}`
|
|
36611
|
-
);
|
|
36612
|
-
}
|
|
36613
|
-
}
|
|
36614
|
-
const encryptedBlob = await response.blob();
|
|
36615
|
-
console.debug(
|
|
36616
|
-
`Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
|
|
36617
|
-
);
|
|
36618
|
-
if (encryptedBlob.size === 0) {
|
|
36619
|
-
throw new Error("File is empty or could not be retrieved.");
|
|
36620
|
-
}
|
|
36621
|
-
const decryptedBlob = await decryptUserData(
|
|
36622
|
-
encryptedBlob,
|
|
36623
|
-
encryptionKey,
|
|
36624
|
-
this.context.platform
|
|
36625
|
-
);
|
|
36626
|
-
return decryptedBlob;
|
|
36627
|
-
} catch (error) {
|
|
36628
|
-
console.error("Failed to decrypt file:", error);
|
|
36629
|
-
if (error instanceof Error) {
|
|
36630
|
-
if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
|
|
36631
|
-
throw new Error(
|
|
36632
|
-
"Wrong encryption key. This file may have been encrypted with a different wallet or encryption seed. Try using the same wallet that originally encrypted this file."
|
|
36633
|
-
);
|
|
36634
|
-
} else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
|
|
36635
|
-
throw new Error(
|
|
36636
|
-
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
36637
|
-
);
|
|
36638
|
-
} else if (error.message.includes("File not found")) {
|
|
36639
|
-
throw new Error(
|
|
36640
|
-
"File not found: The encrypted file is no longer available at the stored URL."
|
|
36641
|
-
);
|
|
36642
|
-
} else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
|
|
36643
|
-
throw new Error(
|
|
36644
|
-
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
|
|
36645
|
-
);
|
|
36646
|
-
}
|
|
36647
|
-
}
|
|
36648
|
-
throw error;
|
|
36649
|
-
}
|
|
36650
|
-
}
|
|
36651
36543
|
/**
|
|
36652
36544
|
* Registers a file URL directly on the blockchain with a schema ID.
|
|
36653
36545
|
*
|
|
@@ -36708,26 +36600,6 @@ var DataController = class {
|
|
|
36708
36600
|
);
|
|
36709
36601
|
}
|
|
36710
36602
|
}
|
|
36711
|
-
/**
|
|
36712
|
-
* Converts IPFS and Google Drive URLs to direct download URLs for fetching.
|
|
36713
|
-
*
|
|
36714
|
-
* @param url - The URL to convert to a direct download URL
|
|
36715
|
-
* @returns The converted direct download URL or the original URL if not a special URL
|
|
36716
|
-
*/
|
|
36717
|
-
convertToDownloadUrl(url) {
|
|
36718
|
-
if (url.startsWith("ipfs://")) {
|
|
36719
|
-
const hash = url.replace("ipfs://", "");
|
|
36720
|
-
return `https://ipfs.io/ipfs/${hash}`;
|
|
36721
|
-
}
|
|
36722
|
-
if (url.includes("drive.google.com/file/d/")) {
|
|
36723
|
-
const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
|
|
36724
|
-
if (fileIdMatch) {
|
|
36725
|
-
const fileId = fileIdMatch[1];
|
|
36726
|
-
return `https://drive.google.com/uc?id=${fileId}&export=download`;
|
|
36727
|
-
}
|
|
36728
|
-
}
|
|
36729
|
-
return url;
|
|
36730
|
-
}
|
|
36731
36603
|
/**
|
|
36732
36604
|
* Gets the user's address from the wallet client.
|
|
36733
36605
|
*
|
|
@@ -37221,7 +37093,7 @@ var DataController = class {
|
|
|
37221
37093
|
this.context.walletClient,
|
|
37222
37094
|
DEFAULT_ENCRYPTION_SEED
|
|
37223
37095
|
);
|
|
37224
|
-
const encryptedData = await
|
|
37096
|
+
const encryptedData = await encryptBlobWithSignedKey(
|
|
37225
37097
|
data,
|
|
37226
37098
|
userEncryptionKey,
|
|
37227
37099
|
this.context.platform
|
|
@@ -37385,12 +37257,13 @@ var DataController = class {
|
|
|
37385
37257
|
privateKey,
|
|
37386
37258
|
this.context.platform
|
|
37387
37259
|
);
|
|
37388
|
-
|
|
37389
|
-
if (
|
|
37390
|
-
|
|
37260
|
+
let encryptedData;
|
|
37261
|
+
if (file.url.startsWith("ipfs://")) {
|
|
37262
|
+
encryptedData = await this.fetchFromIPFS(file.url);
|
|
37263
|
+
} else {
|
|
37264
|
+
encryptedData = await this.fetch(file.url);
|
|
37391
37265
|
}
|
|
37392
|
-
const
|
|
37393
|
-
const decryptedData = await decryptUserData(
|
|
37266
|
+
const decryptedData = await decryptBlobWithSignedKey(
|
|
37394
37267
|
encryptedData,
|
|
37395
37268
|
userEncryptionKey,
|
|
37396
37269
|
this.context.platform
|
|
@@ -37403,6 +37276,162 @@ var DataController = class {
|
|
|
37403
37276
|
);
|
|
37404
37277
|
}
|
|
37405
37278
|
}
|
|
37279
|
+
/**
|
|
37280
|
+
* Simple network-agnostic fetch utility for retrieving file content.
|
|
37281
|
+
*
|
|
37282
|
+
* @remarks
|
|
37283
|
+
* This is a thin wrapper around the global fetch API that returns the response as a Blob.
|
|
37284
|
+
* It provides a consistent interface for fetching encrypted content before decryption.
|
|
37285
|
+
* For IPFS URLs, consider using fetchFromIPFS for better reliability.
|
|
37286
|
+
*
|
|
37287
|
+
* @param url - The URL to fetch content from
|
|
37288
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37289
|
+
* @throws {Error} When the fetch fails or returns a non-ok response
|
|
37290
|
+
*
|
|
37291
|
+
* @example
|
|
37292
|
+
* ```typescript
|
|
37293
|
+
* // Fetch and decrypt a file
|
|
37294
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37295
|
+
* const encryptedBlob = await vana.data.fetch(file.url);
|
|
37296
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37297
|
+
*
|
|
37298
|
+
* // With custom headers for authentication
|
|
37299
|
+
* const response = await fetch(file.url, {
|
|
37300
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
37301
|
+
* });
|
|
37302
|
+
* const encryptedBlob = await response.blob();
|
|
37303
|
+
* ```
|
|
37304
|
+
*/
|
|
37305
|
+
async fetch(url) {
|
|
37306
|
+
try {
|
|
37307
|
+
const response = await fetch(url);
|
|
37308
|
+
if (!response.ok) {
|
|
37309
|
+
throw new Error(
|
|
37310
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37311
|
+
);
|
|
37312
|
+
}
|
|
37313
|
+
const blob = await response.blob();
|
|
37314
|
+
if (blob.size === 0) {
|
|
37315
|
+
throw new Error("Empty response");
|
|
37316
|
+
}
|
|
37317
|
+
return blob;
|
|
37318
|
+
} catch (error) {
|
|
37319
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
37320
|
+
throw new Error(
|
|
37321
|
+
`Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
|
|
37322
|
+
);
|
|
37323
|
+
}
|
|
37324
|
+
throw error;
|
|
37325
|
+
}
|
|
37326
|
+
}
|
|
37327
|
+
/**
|
|
37328
|
+
* Specialized IPFS fetcher with gateway fallback mechanism.
|
|
37329
|
+
*
|
|
37330
|
+
* @remarks
|
|
37331
|
+
* This method provides robust IPFS content fetching by trying multiple gateways
|
|
37332
|
+
* in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
|
|
37333
|
+
*
|
|
37334
|
+
* The default gateway list includes public gateways, but you should provide
|
|
37335
|
+
* your own gateways for production use to ensure reliability and privacy.
|
|
37336
|
+
*
|
|
37337
|
+
* @param url - The IPFS URL (ipfs://...) or CID to fetch
|
|
37338
|
+
* @param options - Optional configuration
|
|
37339
|
+
* @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
|
|
37340
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37341
|
+
* @throws {Error} When all gateways fail to fetch the content
|
|
37342
|
+
*
|
|
37343
|
+
* @example
|
|
37344
|
+
* ```typescript
|
|
37345
|
+
* // Fetch from IPFS with custom gateways
|
|
37346
|
+
* const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
|
|
37347
|
+
* gateways: [
|
|
37348
|
+
* 'https://my-private-gateway.com/ipfs/',
|
|
37349
|
+
* 'https://dweb.link/ipfs/',
|
|
37350
|
+
* 'https://ipfs.io/ipfs/'
|
|
37351
|
+
* ]
|
|
37352
|
+
* });
|
|
37353
|
+
*
|
|
37354
|
+
* // Decrypt the fetched content
|
|
37355
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37356
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37357
|
+
*
|
|
37358
|
+
* // With raw CID
|
|
37359
|
+
* const blob = await vana.data.fetchFromIPFS('QmXxx...', {
|
|
37360
|
+
* gateways: ['https://ipfs.io/ipfs/']
|
|
37361
|
+
* });
|
|
37362
|
+
* ```
|
|
37363
|
+
*/
|
|
37364
|
+
async fetchFromIPFS(url, options) {
|
|
37365
|
+
const defaultGateways = [
|
|
37366
|
+
"https://dweb.link/ipfs/",
|
|
37367
|
+
"https://ipfs.io/ipfs/"
|
|
37368
|
+
];
|
|
37369
|
+
const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
|
|
37370
|
+
let cid;
|
|
37371
|
+
if (url.startsWith("ipfs://")) {
|
|
37372
|
+
cid = url.replace("ipfs://", "");
|
|
37373
|
+
} else if (url.startsWith("Qm") || url.startsWith("bafy")) {
|
|
37374
|
+
cid = url;
|
|
37375
|
+
} else {
|
|
37376
|
+
throw new Error(
|
|
37377
|
+
`Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
|
|
37378
|
+
);
|
|
37379
|
+
}
|
|
37380
|
+
const errors = [];
|
|
37381
|
+
for (let i = 0; i < gateways.length; i++) {
|
|
37382
|
+
const gateway = gateways[i];
|
|
37383
|
+
const isLastGateway = i === gateways.length - 1;
|
|
37384
|
+
const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
|
|
37385
|
+
try {
|
|
37386
|
+
console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
|
|
37387
|
+
const response = await fetch(gatewayUrl);
|
|
37388
|
+
if (response.ok) {
|
|
37389
|
+
const blob = await response.blob();
|
|
37390
|
+
if (blob.size > 0) {
|
|
37391
|
+
console.debug(`Successfully fetched from gateway: ${gateway}`);
|
|
37392
|
+
return blob;
|
|
37393
|
+
} else {
|
|
37394
|
+
if (isLastGateway) {
|
|
37395
|
+
throw new Error("Empty response");
|
|
37396
|
+
}
|
|
37397
|
+
errors.push({
|
|
37398
|
+
gateway,
|
|
37399
|
+
error: "Empty response"
|
|
37400
|
+
});
|
|
37401
|
+
}
|
|
37402
|
+
} else {
|
|
37403
|
+
if (isLastGateway) {
|
|
37404
|
+
if (response.status === 403) {
|
|
37405
|
+
throw new Error(`HTTP error! status: 403 ${response.statusText}`);
|
|
37406
|
+
} else if (response.status === 404) {
|
|
37407
|
+
throw new Error(`HTTP error! status: 404 ${response.statusText}`);
|
|
37408
|
+
} else {
|
|
37409
|
+
throw new Error(
|
|
37410
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37411
|
+
);
|
|
37412
|
+
}
|
|
37413
|
+
}
|
|
37414
|
+
errors.push({
|
|
37415
|
+
gateway,
|
|
37416
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
37417
|
+
});
|
|
37418
|
+
}
|
|
37419
|
+
} catch (error) {
|
|
37420
|
+
if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
|
|
37421
|
+
throw error;
|
|
37422
|
+
}
|
|
37423
|
+
errors.push({
|
|
37424
|
+
gateway,
|
|
37425
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
37426
|
+
});
|
|
37427
|
+
}
|
|
37428
|
+
}
|
|
37429
|
+
const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
|
|
37430
|
+
throw new Error(
|
|
37431
|
+
`Failed to fetch IPFS content ${cid} from all gateways:
|
|
37432
|
+
${errorDetails}`
|
|
37433
|
+
);
|
|
37434
|
+
}
|
|
37406
37435
|
/**
|
|
37407
37436
|
* Validates a data schema against the Vana meta-schema.
|
|
37408
37437
|
*
|
|
@@ -37591,9 +37620,14 @@ var SchemaController = class {
|
|
|
37591
37620
|
};
|
|
37592
37621
|
validateDataSchema(dataSchema);
|
|
37593
37622
|
if (!this.context.storageManager) {
|
|
37594
|
-
|
|
37595
|
-
|
|
37596
|
-
|
|
37623
|
+
if (this.context.validateStorageRequired) {
|
|
37624
|
+
this.context.validateStorageRequired();
|
|
37625
|
+
throw new Error("Storage validation failed");
|
|
37626
|
+
} else {
|
|
37627
|
+
throw new Error(
|
|
37628
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
37629
|
+
);
|
|
37630
|
+
}
|
|
37597
37631
|
}
|
|
37598
37632
|
const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
|
|
37599
37633
|
type: "application/json"
|
|
@@ -37865,6 +37899,7 @@ var ServerController = class {
|
|
|
37865
37899
|
}
|
|
37866
37900
|
const serverResponse = await response.json();
|
|
37867
37901
|
return {
|
|
37902
|
+
kind: serverResponse.personal_server.kind,
|
|
37868
37903
|
address: serverResponse.personal_server.address,
|
|
37869
37904
|
public_key: serverResponse.personal_server.public_key,
|
|
37870
37905
|
base_url: this.PERSONAL_SERVER_BASE_URL || "",
|
|
@@ -39478,175 +39513,151 @@ var PinataStorage = class {
|
|
|
39478
39513
|
}
|
|
39479
39514
|
};
|
|
39480
39515
|
|
|
39481
|
-
// src/storage/providers/
|
|
39482
|
-
var
|
|
39483
|
-
constructor(
|
|
39484
|
-
this.
|
|
39485
|
-
if (!
|
|
39486
|
-
throw new
|
|
39487
|
-
"
|
|
39488
|
-
"MISSING_UPLOAD_URL",
|
|
39489
|
-
"server-proxy"
|
|
39490
|
-
);
|
|
39491
|
-
}
|
|
39492
|
-
if (!config.downloadUrl) {
|
|
39493
|
-
throw new StorageError(
|
|
39494
|
-
"Download URL is required",
|
|
39495
|
-
"MISSING_DOWNLOAD_URL",
|
|
39496
|
-
"server-proxy"
|
|
39516
|
+
// src/storage/providers/callback-storage.ts
|
|
39517
|
+
var CallbackStorage = class {
|
|
39518
|
+
constructor(callbacks) {
|
|
39519
|
+
this.callbacks = callbacks;
|
|
39520
|
+
if (!callbacks.upload || !callbacks.download) {
|
|
39521
|
+
throw new Error(
|
|
39522
|
+
"CallbackStorage requires both upload and download callbacks"
|
|
39497
39523
|
);
|
|
39498
39524
|
}
|
|
39499
39525
|
}
|
|
39500
39526
|
/**
|
|
39501
|
-
*
|
|
39527
|
+
* Upload a file using the provided callback
|
|
39502
39528
|
*
|
|
39503
|
-
* @
|
|
39504
|
-
*
|
|
39505
|
-
*
|
|
39506
|
-
* and must return a JSON response with `success: true` and an `identifier` field.
|
|
39507
|
-
*
|
|
39508
|
-
* @param file - The file to upload
|
|
39509
|
-
* @param filename - Optional custom filename
|
|
39510
|
-
* @returns Promise that resolves to the server-provided identifier
|
|
39511
|
-
* @throws {StorageError} When the upload fails or server returns an error
|
|
39512
|
-
*
|
|
39513
|
-
* @example
|
|
39514
|
-
* ```typescript
|
|
39515
|
-
* const identifier = await serverStorage.upload(fileBlob, { name: "report.pdf" });
|
|
39516
|
-
* console.log("File uploaded with identifier:", identifier);
|
|
39517
|
-
* ```
|
|
39529
|
+
* @param file - The blob to upload
|
|
39530
|
+
* @param filename - Optional filename for the upload
|
|
39531
|
+
* @returns The upload result with URL and metadata
|
|
39518
39532
|
*/
|
|
39519
39533
|
async upload(file, filename) {
|
|
39520
39534
|
try {
|
|
39521
|
-
const
|
|
39522
|
-
|
|
39523
|
-
if (filename) {
|
|
39524
|
-
formData.append("name", filename);
|
|
39525
|
-
}
|
|
39526
|
-
const response = await fetch(this.config.uploadUrl, {
|
|
39527
|
-
method: "POST",
|
|
39528
|
-
body: formData
|
|
39529
|
-
});
|
|
39530
|
-
if (!response.ok) {
|
|
39531
|
-
const _errorText = await response.text();
|
|
39532
|
-
throw new StorageError(
|
|
39533
|
-
`Server upload failed: ${response.status} ${response.statusText}`,
|
|
39534
|
-
"UPLOAD_FAILED",
|
|
39535
|
-
"server-proxy"
|
|
39536
|
-
);
|
|
39537
|
-
}
|
|
39538
|
-
const result = await response.json();
|
|
39539
|
-
if (!result.success) {
|
|
39535
|
+
const result = await this.callbacks.upload(file, filename);
|
|
39536
|
+
if (!result.url || result.url.trim() === "") {
|
|
39540
39537
|
throw new StorageError(
|
|
39541
|
-
|
|
39542
|
-
"
|
|
39543
|
-
"
|
|
39538
|
+
"Upload callback returned invalid result: missing or empty url",
|
|
39539
|
+
"INVALID_UPLOAD_RESULT",
|
|
39540
|
+
"callback-storage"
|
|
39544
39541
|
);
|
|
39545
39542
|
}
|
|
39546
|
-
|
|
39547
|
-
throw new StorageError(
|
|
39548
|
-
"Server upload succeeded but no identifier returned",
|
|
39549
|
-
"NO_IDENTIFIER_RETURNED",
|
|
39550
|
-
"server-proxy"
|
|
39551
|
-
);
|
|
39552
|
-
}
|
|
39553
|
-
return {
|
|
39554
|
-
url: result.url || result.identifier,
|
|
39555
|
-
size: file.size,
|
|
39556
|
-
contentType: file.type || "application/octet-stream"
|
|
39557
|
-
};
|
|
39543
|
+
return result;
|
|
39558
39544
|
} catch (error) {
|
|
39559
39545
|
if (error instanceof StorageError) {
|
|
39560
39546
|
throw error;
|
|
39561
39547
|
}
|
|
39562
39548
|
throw new StorageError(
|
|
39563
|
-
`
|
|
39549
|
+
`Upload failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39564
39550
|
"UPLOAD_ERROR",
|
|
39565
|
-
"
|
|
39551
|
+
"callback-storage",
|
|
39552
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39566
39553
|
);
|
|
39567
39554
|
}
|
|
39568
39555
|
}
|
|
39569
39556
|
/**
|
|
39570
|
-
*
|
|
39557
|
+
* Download a file using the provided callback
|
|
39571
39558
|
*
|
|
39572
|
-
* @
|
|
39573
|
-
*
|
|
39574
|
-
* Your server is responsible for retrieving the file from your storage backend
|
|
39575
|
-
* and returning the file content as a blob response.
|
|
39576
|
-
*
|
|
39577
|
-
* @param url - The server-provided URL or identifier from upload
|
|
39578
|
-
* @returns Promise that resolves to the downloaded file content
|
|
39579
|
-
* @throws {StorageError} When the download fails or file is not found
|
|
39580
|
-
*
|
|
39581
|
-
* @example
|
|
39582
|
-
* ```typescript
|
|
39583
|
-
* const fileBlob = await serverStorage.download("file-123");
|
|
39584
|
-
* const url = URL.createObjectURL(fileBlob);
|
|
39585
|
-
* ```
|
|
39559
|
+
* @param url - The URL or identifier to download
|
|
39560
|
+
* @returns The downloaded blob
|
|
39586
39561
|
*/
|
|
39587
39562
|
async download(url) {
|
|
39588
39563
|
try {
|
|
39589
|
-
const identifier = this.
|
|
39590
|
-
const
|
|
39591
|
-
|
|
39592
|
-
headers: {
|
|
39593
|
-
"Content-Type": "application/json"
|
|
39594
|
-
},
|
|
39595
|
-
body: JSON.stringify({ identifier })
|
|
39596
|
-
});
|
|
39597
|
-
if (!response.ok) {
|
|
39598
|
-
const _errorText = await response.text();
|
|
39564
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39565
|
+
const blob = await this.callbacks.download(identifier);
|
|
39566
|
+
if (!(blob instanceof Blob)) {
|
|
39599
39567
|
throw new StorageError(
|
|
39600
|
-
|
|
39601
|
-
"
|
|
39602
|
-
"
|
|
39568
|
+
"Download callback returned invalid result: expected Blob",
|
|
39569
|
+
"INVALID_DOWNLOAD_RESULT",
|
|
39570
|
+
"callback-storage"
|
|
39603
39571
|
);
|
|
39604
39572
|
}
|
|
39605
|
-
return
|
|
39573
|
+
return blob;
|
|
39606
39574
|
} catch (error) {
|
|
39607
39575
|
if (error instanceof StorageError) {
|
|
39608
39576
|
throw error;
|
|
39609
39577
|
}
|
|
39610
39578
|
throw new StorageError(
|
|
39611
|
-
`
|
|
39579
|
+
`Download failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39612
39580
|
"DOWNLOAD_ERROR",
|
|
39613
|
-
"
|
|
39581
|
+
"callback-storage",
|
|
39582
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39614
39583
|
);
|
|
39615
39584
|
}
|
|
39616
39585
|
}
|
|
39617
|
-
|
|
39618
|
-
|
|
39619
|
-
|
|
39620
|
-
|
|
39621
|
-
|
|
39622
|
-
|
|
39623
|
-
|
|
39624
|
-
|
|
39625
|
-
|
|
39626
|
-
|
|
39627
|
-
|
|
39628
|
-
|
|
39629
|
-
|
|
39586
|
+
/**
|
|
39587
|
+
* List files using the provided callback (if available)
|
|
39588
|
+
*
|
|
39589
|
+
* @param options - Optional list options including filters and pagination
|
|
39590
|
+
* @returns Array of storage files
|
|
39591
|
+
*/
|
|
39592
|
+
async list(options) {
|
|
39593
|
+
if (!this.callbacks.list) {
|
|
39594
|
+
throw new StorageError(
|
|
39595
|
+
"List operation not supported - no list callback provided",
|
|
39596
|
+
"NOT_SUPPORTED",
|
|
39597
|
+
"callback-storage"
|
|
39598
|
+
);
|
|
39599
|
+
}
|
|
39600
|
+
try {
|
|
39601
|
+
const result = await this.callbacks.list(options?.namePattern, options);
|
|
39602
|
+
return result.items.map((item, index) => ({
|
|
39603
|
+
id: item.identifier,
|
|
39604
|
+
name: item.identifier.split("/").pop() || `file-${index}`,
|
|
39605
|
+
url: item.identifier,
|
|
39606
|
+
size: item.size || 0,
|
|
39607
|
+
contentType: "application/octet-stream",
|
|
39608
|
+
createdAt: item.lastModified || /* @__PURE__ */ new Date(),
|
|
39609
|
+
metadata: item.metadata
|
|
39610
|
+
}));
|
|
39611
|
+
} catch (error) {
|
|
39612
|
+
throw new StorageError(
|
|
39613
|
+
`List failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39614
|
+
"LIST_ERROR",
|
|
39615
|
+
"callback-storage",
|
|
39616
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39617
|
+
);
|
|
39618
|
+
}
|
|
39630
39619
|
}
|
|
39631
39620
|
/**
|
|
39632
|
-
*
|
|
39621
|
+
* Delete a file using the provided callback (if available)
|
|
39633
39622
|
*
|
|
39634
|
-
* @param url - URL or identifier
|
|
39635
|
-
* @returns
|
|
39623
|
+
* @param url - The URL or identifier to delete
|
|
39624
|
+
* @returns True if deletion succeeded
|
|
39636
39625
|
*/
|
|
39637
|
-
|
|
39638
|
-
|
|
39626
|
+
async delete(url) {
|
|
39627
|
+
if (!this.callbacks.delete) {
|
|
39628
|
+
throw new StorageError(
|
|
39629
|
+
"Delete operation not supported - no delete callback provided",
|
|
39630
|
+
"NOT_SUPPORTED",
|
|
39631
|
+
"callback-storage"
|
|
39632
|
+
);
|
|
39633
|
+
}
|
|
39634
|
+
try {
|
|
39635
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39636
|
+
return await this.callbacks.delete(identifier);
|
|
39637
|
+
} catch (error) {
|
|
39638
|
+
throw new StorageError(
|
|
39639
|
+
`Delete failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39640
|
+
"DELETE_ERROR",
|
|
39641
|
+
"callback-storage",
|
|
39642
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39643
|
+
);
|
|
39644
|
+
}
|
|
39639
39645
|
}
|
|
39646
|
+
/**
|
|
39647
|
+
* Get provider configuration
|
|
39648
|
+
*
|
|
39649
|
+
* @returns Provider configuration metadata
|
|
39650
|
+
*/
|
|
39640
39651
|
getConfig() {
|
|
39641
39652
|
return {
|
|
39642
|
-
name: "
|
|
39643
|
-
type: "
|
|
39653
|
+
name: "callback-storage",
|
|
39654
|
+
type: "callback",
|
|
39644
39655
|
requiresAuth: false,
|
|
39645
39656
|
features: {
|
|
39646
39657
|
upload: true,
|
|
39647
39658
|
download: true,
|
|
39648
|
-
list:
|
|
39649
|
-
delete:
|
|
39659
|
+
list: !!this.callbacks.list,
|
|
39660
|
+
delete: !!this.callbacks.delete
|
|
39650
39661
|
}
|
|
39651
39662
|
};
|
|
39652
39663
|
}
|
|
@@ -39876,6 +39887,47 @@ function getAllChains() {
|
|
|
39876
39887
|
}
|
|
39877
39888
|
|
|
39878
39889
|
// src/core.ts
|
|
39890
|
+
var VanaCoreFactory = class {
|
|
39891
|
+
/**
|
|
39892
|
+
* Creates a VanaCore instance that enforces storage requirements at compile time.
|
|
39893
|
+
* Use this factory when you know you'll need storage-dependent operations.
|
|
39894
|
+
*
|
|
39895
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39896
|
+
* @param config - Configuration that includes required storage providers
|
|
39897
|
+
* @returns VanaCore instance with storage validation
|
|
39898
|
+
* @example
|
|
39899
|
+
* ```typescript
|
|
39900
|
+
* const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
|
|
39901
|
+
* walletClient: myWalletClient,
|
|
39902
|
+
* storage: {
|
|
39903
|
+
* providers: { ipfs: new IPFSStorage() },
|
|
39904
|
+
* defaultProvider: 'ipfs'
|
|
39905
|
+
* }
|
|
39906
|
+
* });
|
|
39907
|
+
* ```
|
|
39908
|
+
*/
|
|
39909
|
+
static createWithStorage(platform, config) {
|
|
39910
|
+
const core = new VanaCore(platform, config);
|
|
39911
|
+
return core;
|
|
39912
|
+
}
|
|
39913
|
+
/**
|
|
39914
|
+
* Creates a VanaCore instance without storage requirements.
|
|
39915
|
+
* Storage-dependent operations will fail at runtime if not configured.
|
|
39916
|
+
*
|
|
39917
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39918
|
+
* @param config - Basic configuration without required storage
|
|
39919
|
+
* @returns VanaCore instance
|
|
39920
|
+
* @example
|
|
39921
|
+
* ```typescript
|
|
39922
|
+
* const vanaCore = VanaCoreFactory.create(platformAdapter, {
|
|
39923
|
+
* walletClient: myWalletClient
|
|
39924
|
+
* });
|
|
39925
|
+
* ```
|
|
39926
|
+
*/
|
|
39927
|
+
static create(platform, config) {
|
|
39928
|
+
return new VanaCore(platform, config);
|
|
39929
|
+
}
|
|
39930
|
+
};
|
|
39879
39931
|
var VanaCore = class {
|
|
39880
39932
|
/** Manages gasless data access permissions and trusted server registry. */
|
|
39881
39933
|
permissions;
|
|
@@ -39891,12 +39943,18 @@ var VanaCore = class {
|
|
|
39891
39943
|
platform;
|
|
39892
39944
|
relayerCallbacks;
|
|
39893
39945
|
storageManager;
|
|
39946
|
+
hasRequiredStorage;
|
|
39947
|
+
ipfsGateways;
|
|
39894
39948
|
/**
|
|
39895
39949
|
* Initializes a new VanaCore client instance with the provided configuration.
|
|
39896
39950
|
*
|
|
39897
39951
|
* @remarks
|
|
39898
39952
|
* The constructor validates the configuration, initializes storage providers if configured,
|
|
39899
39953
|
* creates wallet and public clients, and sets up all SDK controllers with shared context.
|
|
39954
|
+
*
|
|
39955
|
+
* IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
|
|
39956
|
+
* Methods that require storage will throw runtime errors if storage is not configured.
|
|
39957
|
+
*
|
|
39900
39958
|
* @param platform - The platform adapter for environment-specific operations
|
|
39901
39959
|
* @param config - The configuration object specifying wallet or chain settings
|
|
39902
39960
|
* @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
|
|
@@ -39912,6 +39970,8 @@ var VanaCore = class {
|
|
|
39912
39970
|
this.platform = platform;
|
|
39913
39971
|
this.validateConfig(config);
|
|
39914
39972
|
this.relayerCallbacks = config.relayerCallbacks;
|
|
39973
|
+
this.ipfsGateways = config.ipfsGateways;
|
|
39974
|
+
this.hasRequiredStorage = hasStorageConfig(config);
|
|
39915
39975
|
if (config.storage?.providers) {
|
|
39916
39976
|
this.storageManager = new StorageManager();
|
|
39917
39977
|
for (const [name, provider] of Object.entries(config.storage.providers)) {
|
|
@@ -39966,8 +40026,11 @@ var VanaCore = class {
|
|
|
39966
40026
|
relayerCallbacks: this.relayerCallbacks,
|
|
39967
40027
|
storageManager: this.storageManager,
|
|
39968
40028
|
subgraphUrl,
|
|
39969
|
-
platform: this.platform
|
|
40029
|
+
platform: this.platform,
|
|
39970
40030
|
// Pass the platform adapter to controllers
|
|
40031
|
+
validateStorageRequired: this.validateStorageRequired.bind(this),
|
|
40032
|
+
hasStorage: this.hasStorage.bind(this),
|
|
40033
|
+
ipfsGateways: this.ipfsGateways
|
|
39971
40034
|
};
|
|
39972
40035
|
this.permissions = new PermissionsController(sharedContext);
|
|
39973
40036
|
this.data = new DataController(sharedContext);
|
|
@@ -39975,6 +40038,58 @@ var VanaCore = class {
|
|
|
39975
40038
|
this.server = new ServerController(sharedContext);
|
|
39976
40039
|
this.protocol = new ProtocolController(sharedContext);
|
|
39977
40040
|
}
|
|
40041
|
+
/**
|
|
40042
|
+
* Validates that storage is available for storage-dependent operations.
|
|
40043
|
+
* This method enforces the fail-fast principle by checking storage availability
|
|
40044
|
+
* at method call time rather than during expensive operations.
|
|
40045
|
+
*
|
|
40046
|
+
* @throws {InvalidConfigurationError} When storage is required but not configured
|
|
40047
|
+
* @example
|
|
40048
|
+
* ```typescript
|
|
40049
|
+
* // This will throw if storage is not configured
|
|
40050
|
+
* vana.validateStorageRequired();
|
|
40051
|
+
* await vana.data.uploadFile(file); // Safe to proceed
|
|
40052
|
+
* ```
|
|
40053
|
+
*/
|
|
40054
|
+
validateStorageRequired() {
|
|
40055
|
+
if (!this.hasRequiredStorage) {
|
|
40056
|
+
throw new InvalidConfigurationError(
|
|
40057
|
+
"Storage configuration is required for this operation. Please configure storage providers in VanaConfig.storage, provide a relayerCallbacks.storeGrantFile implementation, or pass pre-stored URLs to avoid this dependency. \n\nFor better type safety, consider using VanaCoreFactory.createWithStorage() with VanaConfigWithStorage to catch this error at compile time."
|
|
40058
|
+
);
|
|
40059
|
+
}
|
|
40060
|
+
}
|
|
40061
|
+
/**
|
|
40062
|
+
* Checks whether storage is configured without throwing an error.
|
|
40063
|
+
*
|
|
40064
|
+
* @returns True if storage is properly configured
|
|
40065
|
+
* @example
|
|
40066
|
+
* ```typescript
|
|
40067
|
+
* if (vana.hasStorage()) {
|
|
40068
|
+
* await vana.data.uploadFile(file);
|
|
40069
|
+
* } else {
|
|
40070
|
+
* console.warn('Storage not configured - using pre-stored URLs only');
|
|
40071
|
+
* }
|
|
40072
|
+
* ```
|
|
40073
|
+
*/
|
|
40074
|
+
hasStorage() {
|
|
40075
|
+
return this.hasRequiredStorage;
|
|
40076
|
+
}
|
|
40077
|
+
/**
|
|
40078
|
+
* Type guard to check if this instance has storage enabled at compile time.
|
|
40079
|
+
* Use this when you need TypeScript to understand that storage is available.
|
|
40080
|
+
*
|
|
40081
|
+
* @returns True if storage is configured, with type narrowing
|
|
40082
|
+
* @example
|
|
40083
|
+
* ```typescript
|
|
40084
|
+
* if (vana.isStorageEnabled()) {
|
|
40085
|
+
* // TypeScript knows storage is available here
|
|
40086
|
+
* await vana.data.uploadFile(file);
|
|
40087
|
+
* }
|
|
40088
|
+
* ```
|
|
40089
|
+
*/
|
|
40090
|
+
isStorageEnabled() {
|
|
40091
|
+
return this.hasRequiredStorage;
|
|
40092
|
+
}
|
|
39978
40093
|
/**
|
|
39979
40094
|
* Validates the provided configuration object against all requirements.
|
|
39980
40095
|
*
|
|
@@ -40163,23 +40278,23 @@ var VanaCore = class {
|
|
|
40163
40278
|
return this.platform;
|
|
40164
40279
|
}
|
|
40165
40280
|
/**
|
|
40166
|
-
* Encrypts
|
|
40281
|
+
* Encrypts data using the Vana protocol standard encryption.
|
|
40167
40282
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
40168
40283
|
*
|
|
40169
40284
|
* @param data The data to encrypt (string or Blob)
|
|
40170
|
-
* @param
|
|
40285
|
+
* @param key The key to use as encryption key
|
|
40171
40286
|
* @returns The encrypted data as Blob
|
|
40172
40287
|
* @example
|
|
40173
40288
|
* ```typescript
|
|
40174
40289
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
40175
|
-
* const encrypted = await vana.
|
|
40290
|
+
* const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
|
|
40176
40291
|
* ```
|
|
40177
40292
|
*/
|
|
40178
|
-
async
|
|
40179
|
-
return
|
|
40293
|
+
async encryptBlob(data, key) {
|
|
40294
|
+
return encryptBlobWithSignedKey(data, key, this.platform);
|
|
40180
40295
|
}
|
|
40181
40296
|
/**
|
|
40182
|
-
* Decrypts
|
|
40297
|
+
* Decrypts data that was encrypted using the Vana protocol.
|
|
40183
40298
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
40184
40299
|
*
|
|
40185
40300
|
* @param encryptedData The encrypted data (string or Blob)
|
|
@@ -40188,12 +40303,16 @@ var VanaCore = class {
|
|
|
40188
40303
|
* @example
|
|
40189
40304
|
* ```typescript
|
|
40190
40305
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
40191
|
-
* const decrypted = await vana.
|
|
40306
|
+
* const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
|
|
40192
40307
|
* const text = await decrypted.text();
|
|
40193
40308
|
* ```
|
|
40194
40309
|
*/
|
|
40195
|
-
async
|
|
40196
|
-
return
|
|
40310
|
+
async decryptBlob(encryptedData, walletSignature) {
|
|
40311
|
+
return decryptBlobWithSignedKey(
|
|
40312
|
+
encryptedData,
|
|
40313
|
+
walletSignature,
|
|
40314
|
+
this.platform
|
|
40315
|
+
);
|
|
40197
40316
|
}
|
|
40198
40317
|
};
|
|
40199
40318
|
|
|
@@ -40224,7 +40343,7 @@ function createValidatedGrant(params) {
|
|
|
40224
40343
|
try {
|
|
40225
40344
|
validateGrant(grantFile, {
|
|
40226
40345
|
schema: true,
|
|
40227
|
-
grantee: params.
|
|
40346
|
+
grantee: params.grantee,
|
|
40228
40347
|
operation: params.operation
|
|
40229
40348
|
});
|
|
40230
40349
|
} catch (error) {
|
|
@@ -41087,31 +41206,22 @@ var ApiClient = class {
|
|
|
41087
41206
|
};
|
|
41088
41207
|
|
|
41089
41208
|
// src/index.node.ts
|
|
41090
|
-
var
|
|
41091
|
-
/**
|
|
41092
|
-
* Creates a Vana SDK instance configured for Node.js environments.
|
|
41093
|
-
*
|
|
41094
|
-
* @param config - SDK configuration object (wallet client or chain config)
|
|
41095
|
-
* @example
|
|
41096
|
-
* ```typescript
|
|
41097
|
-
* // With wallet client
|
|
41098
|
-
* const vana = new Vana({ walletClient });
|
|
41099
|
-
*
|
|
41100
|
-
* // With chain configuration
|
|
41101
|
-
* const vana = new Vana({ chainId: 14800, account });
|
|
41102
|
-
* ```
|
|
41103
|
-
*/
|
|
41209
|
+
var VanaNodeImpl = class extends VanaCore {
|
|
41104
41210
|
constructor(config) {
|
|
41105
41211
|
super(new NodePlatformAdapter(), config);
|
|
41106
41212
|
}
|
|
41107
41213
|
};
|
|
41108
|
-
|
|
41214
|
+
function Vana(config) {
|
|
41215
|
+
return new VanaNodeImpl(config);
|
|
41216
|
+
}
|
|
41217
|
+
var index_node_default = Vana;
|
|
41109
41218
|
export {
|
|
41110
41219
|
ApiClient,
|
|
41111
41220
|
AsyncQueue,
|
|
41112
41221
|
BaseController,
|
|
41113
41222
|
BlockchainError,
|
|
41114
41223
|
BrowserPlatformAdapter,
|
|
41224
|
+
CallbackStorage,
|
|
41115
41225
|
CircuitBreaker,
|
|
41116
41226
|
ContractFactory,
|
|
41117
41227
|
ContractNotFoundError,
|
|
@@ -41146,16 +41256,16 @@ export {
|
|
|
41146
41256
|
SchemaValidator,
|
|
41147
41257
|
SerializationError,
|
|
41148
41258
|
ServerController,
|
|
41149
|
-
ServerProxyStorage,
|
|
41150
41259
|
ServerUrlMismatchError,
|
|
41151
41260
|
SignatureError,
|
|
41152
41261
|
StorageError,
|
|
41153
41262
|
StorageManager,
|
|
41154
41263
|
UserRejectedRequestError,
|
|
41155
|
-
|
|
41264
|
+
Vana,
|
|
41156
41265
|
VanaCore,
|
|
41266
|
+
VanaCoreFactory,
|
|
41157
41267
|
VanaError,
|
|
41158
|
-
|
|
41268
|
+
VanaNodeImpl,
|
|
41159
41269
|
__contractCache,
|
|
41160
41270
|
chains,
|
|
41161
41271
|
checkGrantAccess,
|
|
@@ -41170,13 +41280,13 @@ export {
|
|
|
41170
41280
|
createPlatformAdapterFor,
|
|
41171
41281
|
createPlatformAdapterSafe,
|
|
41172
41282
|
createValidatedGrant,
|
|
41173
|
-
|
|
41283
|
+
decryptBlobWithSignedKey,
|
|
41174
41284
|
decryptWithPrivateKey,
|
|
41175
41285
|
decryptWithWalletPrivateKey,
|
|
41176
41286
|
index_node_default as default,
|
|
41177
41287
|
detectPlatform,
|
|
41288
|
+
encryptBlobWithSignedKey,
|
|
41178
41289
|
encryptFileKey,
|
|
41179
|
-
encryptUserData,
|
|
41180
41290
|
encryptWithWalletPublicKey,
|
|
41181
41291
|
extractIpfsHash,
|
|
41182
41292
|
fetchAndValidateSchema,
|
|
@@ -41201,9 +41311,7 @@ export {
|
|
|
41201
41311
|
handleRelayerRequest,
|
|
41202
41312
|
isAPIResponse,
|
|
41203
41313
|
isGrantExpired,
|
|
41204
|
-
isIdentityServerOutput,
|
|
41205
41314
|
isIpfsUrl,
|
|
41206
|
-
isPersonalServerOutput,
|
|
41207
41315
|
isPlatformSupported,
|
|
41208
41316
|
isReplicateAPIResponse,
|
|
41209
41317
|
moksha,
|