@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.cjs
CHANGED
|
@@ -273,65 +273,9 @@ var init_browser = __esm({
|
|
|
273
273
|
try {
|
|
274
274
|
const eccrypto2 = await import("eccrypto-js");
|
|
275
275
|
const uncompressedKey = processWalletPublicKey(publicKey);
|
|
276
|
-
const iv = Buffer.from([
|
|
277
|
-
1,
|
|
278
|
-
2,
|
|
279
|
-
3,
|
|
280
|
-
4,
|
|
281
|
-
5,
|
|
282
|
-
6,
|
|
283
|
-
7,
|
|
284
|
-
8,
|
|
285
|
-
9,
|
|
286
|
-
10,
|
|
287
|
-
11,
|
|
288
|
-
12,
|
|
289
|
-
13,
|
|
290
|
-
14,
|
|
291
|
-
15,
|
|
292
|
-
16
|
|
293
|
-
]);
|
|
294
|
-
const ephemeralKey = Buffer.from([
|
|
295
|
-
17,
|
|
296
|
-
34,
|
|
297
|
-
51,
|
|
298
|
-
68,
|
|
299
|
-
85,
|
|
300
|
-
102,
|
|
301
|
-
119,
|
|
302
|
-
136,
|
|
303
|
-
153,
|
|
304
|
-
170,
|
|
305
|
-
187,
|
|
306
|
-
204,
|
|
307
|
-
221,
|
|
308
|
-
238,
|
|
309
|
-
255,
|
|
310
|
-
0,
|
|
311
|
-
16,
|
|
312
|
-
32,
|
|
313
|
-
48,
|
|
314
|
-
64,
|
|
315
|
-
80,
|
|
316
|
-
96,
|
|
317
|
-
112,
|
|
318
|
-
128,
|
|
319
|
-
144,
|
|
320
|
-
160,
|
|
321
|
-
176,
|
|
322
|
-
192,
|
|
323
|
-
208,
|
|
324
|
-
224,
|
|
325
|
-
240,
|
|
326
|
-
0
|
|
327
|
-
]);
|
|
328
276
|
const encryptedBuffer = await eccrypto2.encrypt(
|
|
329
277
|
uncompressedKey,
|
|
330
|
-
Buffer.from(data)
|
|
331
|
-
{
|
|
332
|
-
iv,
|
|
333
|
-
ephemPrivateKey: ephemeralKey
|
|
334
|
-
}
|
|
278
|
+
Buffer.from(data)
|
|
335
279
|
);
|
|
336
280
|
const result = Buffer.concat([
|
|
337
281
|
encryptedBuffer.iv,
|
|
@@ -469,6 +413,7 @@ __export(index_node_exports, {
|
|
|
469
413
|
BaseController: () => BaseController,
|
|
470
414
|
BlockchainError: () => BlockchainError,
|
|
471
415
|
BrowserPlatformAdapter: () => BrowserPlatformAdapter,
|
|
416
|
+
CallbackStorage: () => CallbackStorage,
|
|
472
417
|
CircuitBreaker: () => CircuitBreaker,
|
|
473
418
|
ContractFactory: () => ContractFactory,
|
|
474
419
|
ContractNotFoundError: () => ContractNotFoundError,
|
|
@@ -503,16 +448,16 @@ __export(index_node_exports, {
|
|
|
503
448
|
SchemaValidator: () => SchemaValidator,
|
|
504
449
|
SerializationError: () => SerializationError,
|
|
505
450
|
ServerController: () => ServerController,
|
|
506
|
-
ServerProxyStorage: () => ServerProxyStorage,
|
|
507
451
|
ServerUrlMismatchError: () => ServerUrlMismatchError,
|
|
508
452
|
SignatureError: () => SignatureError,
|
|
509
453
|
StorageError: () => StorageError,
|
|
510
454
|
StorageManager: () => StorageManager,
|
|
511
455
|
UserRejectedRequestError: () => UserRejectedRequestError,
|
|
512
|
-
Vana: () =>
|
|
456
|
+
Vana: () => Vana,
|
|
513
457
|
VanaCore: () => VanaCore,
|
|
458
|
+
VanaCoreFactory: () => VanaCoreFactory,
|
|
514
459
|
VanaError: () => VanaError,
|
|
515
|
-
|
|
460
|
+
VanaNodeImpl: () => VanaNodeImpl,
|
|
516
461
|
__contractCache: () => __contractCache,
|
|
517
462
|
chains: () => chains,
|
|
518
463
|
checkGrantAccess: () => checkGrantAccess,
|
|
@@ -527,13 +472,13 @@ __export(index_node_exports, {
|
|
|
527
472
|
createPlatformAdapterFor: () => createPlatformAdapterFor,
|
|
528
473
|
createPlatformAdapterSafe: () => createPlatformAdapterSafe,
|
|
529
474
|
createValidatedGrant: () => createValidatedGrant,
|
|
530
|
-
|
|
475
|
+
decryptBlobWithSignedKey: () => decryptBlobWithSignedKey,
|
|
531
476
|
decryptWithPrivateKey: () => decryptWithPrivateKey,
|
|
532
477
|
decryptWithWalletPrivateKey: () => decryptWithWalletPrivateKey,
|
|
533
478
|
default: () => index_node_default,
|
|
534
479
|
detectPlatform: () => detectPlatform,
|
|
480
|
+
encryptBlobWithSignedKey: () => encryptBlobWithSignedKey,
|
|
535
481
|
encryptFileKey: () => encryptFileKey,
|
|
536
|
-
encryptUserData: () => encryptUserData,
|
|
537
482
|
encryptWithWalletPublicKey: () => encryptWithWalletPublicKey,
|
|
538
483
|
extractIpfsHash: () => extractIpfsHash,
|
|
539
484
|
fetchAndValidateSchema: () => fetchAndValidateSchema,
|
|
@@ -558,9 +503,7 @@ __export(index_node_exports, {
|
|
|
558
503
|
handleRelayerRequest: () => handleRelayerRequest,
|
|
559
504
|
isAPIResponse: () => isAPIResponse,
|
|
560
505
|
isGrantExpired: () => isGrantExpired,
|
|
561
|
-
isIdentityServerOutput: () => isIdentityServerOutput,
|
|
562
506
|
isIpfsUrl: () => isIpfsUrl,
|
|
563
|
-
isPersonalServerOutput: () => isPersonalServerOutput,
|
|
564
507
|
isPlatformSupported: () => isPlatformSupported,
|
|
565
508
|
isReplicateAPIResponse: () => isReplicateAPIResponse,
|
|
566
509
|
moksha: () => moksha,
|
|
@@ -823,6 +766,9 @@ function isWalletConfig(config) {
|
|
|
823
766
|
function isChainConfig(config) {
|
|
824
767
|
return "chainId" in config && !("walletClient" in config);
|
|
825
768
|
}
|
|
769
|
+
function hasStorageConfig(config) {
|
|
770
|
+
return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
|
|
771
|
+
}
|
|
826
772
|
|
|
827
773
|
// src/types/chains.ts
|
|
828
774
|
function isVanaChainId(chainId) {
|
|
@@ -1118,55 +1064,6 @@ function isReplicateAPIResponse(value) {
|
|
|
1118
1064
|
obj.status
|
|
1119
1065
|
);
|
|
1120
1066
|
}
|
|
1121
|
-
function isIdentityServerOutput(value) {
|
|
1122
|
-
console.debug("\u{1F50D} Type Guard: Checking value:", value);
|
|
1123
|
-
console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
|
|
1124
|
-
if (typeof value !== "object" || value === null) {
|
|
1125
|
-
console.debug("\u{1F50D} Type Guard: Failed - not object or null");
|
|
1126
|
-
return false;
|
|
1127
|
-
}
|
|
1128
|
-
const obj = value;
|
|
1129
|
-
console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
|
|
1130
|
-
console.debug(
|
|
1131
|
-
"\u{1F50D} Type Guard: user_address:",
|
|
1132
|
-
obj.user_address,
|
|
1133
|
-
typeof obj.user_address
|
|
1134
|
-
);
|
|
1135
|
-
if (typeof obj.user_address !== "string") {
|
|
1136
|
-
console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
console.debug(
|
|
1140
|
-
"\u{1F50D} Type Guard: personal_server:",
|
|
1141
|
-
obj.personal_server,
|
|
1142
|
-
typeof obj.personal_server
|
|
1143
|
-
);
|
|
1144
|
-
if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
|
|
1145
|
-
console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
|
|
1146
|
-
return false;
|
|
1147
|
-
}
|
|
1148
|
-
const personalServer = obj.personal_server;
|
|
1149
|
-
console.debug(
|
|
1150
|
-
"\u{1F50D} Type Guard: Personal server keys:",
|
|
1151
|
-
Object.keys(personalServer)
|
|
1152
|
-
);
|
|
1153
|
-
console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
|
|
1154
|
-
console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
|
|
1155
|
-
const hasAddress = "address" in personalServer;
|
|
1156
|
-
const hasPublicKey = "public_key" in personalServer;
|
|
1157
|
-
console.debug(
|
|
1158
|
-
"\u{1F50D} Type Guard: Has address:",
|
|
1159
|
-
hasAddress,
|
|
1160
|
-
"Has public_key:",
|
|
1161
|
-
hasPublicKey
|
|
1162
|
-
);
|
|
1163
|
-
return hasAddress && hasPublicKey;
|
|
1164
|
-
}
|
|
1165
|
-
function isPersonalServerOutput(value) {
|
|
1166
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1167
|
-
const obj = value;
|
|
1168
|
-
return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
|
|
1169
|
-
}
|
|
1170
1067
|
function isAPIResponse(value) {
|
|
1171
1068
|
if (typeof value !== "object" || value === null) return false;
|
|
1172
1069
|
const obj = value;
|
|
@@ -33759,7 +33656,7 @@ function getAbi(contract) {
|
|
|
33759
33656
|
var import_viem = require("viem");
|
|
33760
33657
|
function createGrantFile(params) {
|
|
33761
33658
|
const grantFile = {
|
|
33762
|
-
grantee: params.
|
|
33659
|
+
grantee: params.grantee,
|
|
33763
33660
|
operation: params.operation,
|
|
33764
33661
|
parameters: params.parameters
|
|
33765
33662
|
};
|
|
@@ -34179,7 +34076,7 @@ var PermissionsController = class {
|
|
|
34179
34076
|
* @example
|
|
34180
34077
|
* ```typescript
|
|
34181
34078
|
* const txHash = await vana.permissions.grant({
|
|
34182
|
-
*
|
|
34079
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34183
34080
|
* operation: "llm_inference",
|
|
34184
34081
|
* parameters: {
|
|
34185
34082
|
* model: "gpt-4",
|
|
@@ -34208,7 +34105,7 @@ var PermissionsController = class {
|
|
|
34208
34105
|
* @example
|
|
34209
34106
|
* ```typescript
|
|
34210
34107
|
* const { preview, confirm } = await vana.permissions.prepareGrant({
|
|
34211
|
-
*
|
|
34108
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34212
34109
|
* operation: "llm_inference",
|
|
34213
34110
|
* files: [1, 2, 3],
|
|
34214
34111
|
* parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
|
|
@@ -34257,9 +34154,13 @@ var PermissionsController = class {
|
|
|
34257
34154
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
34258
34155
|
if (!grantUrl) {
|
|
34259
34156
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
34260
|
-
|
|
34261
|
-
|
|
34262
|
-
|
|
34157
|
+
if (this.context.validateStorageRequired) {
|
|
34158
|
+
this.context.validateStorageRequired();
|
|
34159
|
+
} else {
|
|
34160
|
+
throw new Error(
|
|
34161
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
34162
|
+
);
|
|
34163
|
+
}
|
|
34263
34164
|
}
|
|
34264
34165
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
34265
34166
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -34283,7 +34184,7 @@ var PermissionsController = class {
|
|
|
34283
34184
|
grantUrl
|
|
34284
34185
|
);
|
|
34285
34186
|
const typedData = await this.composePermissionGrantMessage({
|
|
34286
|
-
|
|
34187
|
+
grantee: params.grantee,
|
|
34287
34188
|
operation: params.operation,
|
|
34288
34189
|
// Placeholder - real data is in IPFS
|
|
34289
34190
|
files: params.files,
|
|
@@ -34330,7 +34231,7 @@ var PermissionsController = class {
|
|
|
34330
34231
|
* @example
|
|
34331
34232
|
* ```typescript
|
|
34332
34233
|
* const { typedData, signature } = await vana.permissions.createAndSign({
|
|
34333
|
-
*
|
|
34234
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34334
34235
|
* operation: "data_analysis",
|
|
34335
34236
|
* parameters: { analysisType: "sentiment" },
|
|
34336
34237
|
* });
|
|
@@ -34346,9 +34247,13 @@ var PermissionsController = class {
|
|
|
34346
34247
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
34347
34248
|
if (!grantUrl) {
|
|
34348
34249
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
34349
|
-
|
|
34350
|
-
|
|
34351
|
-
|
|
34250
|
+
if (this.context.validateStorageRequired) {
|
|
34251
|
+
this.context.validateStorageRequired();
|
|
34252
|
+
} else {
|
|
34253
|
+
throw new Error(
|
|
34254
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
34255
|
+
);
|
|
34256
|
+
}
|
|
34352
34257
|
}
|
|
34353
34258
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
34354
34259
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -34372,7 +34277,7 @@ var PermissionsController = class {
|
|
|
34372
34277
|
grantUrl
|
|
34373
34278
|
);
|
|
34374
34279
|
const typedData = await this.composePermissionGrantMessage({
|
|
34375
|
-
|
|
34280
|
+
grantee: params.grantee,
|
|
34376
34281
|
operation: params.operation,
|
|
34377
34282
|
// Placeholder - real data is in IPFS
|
|
34378
34283
|
files: params.files,
|
|
@@ -34723,7 +34628,7 @@ var PermissionsController = class {
|
|
|
34723
34628
|
* Composes the EIP-712 typed data for PermissionGrant (new simplified format).
|
|
34724
34629
|
*
|
|
34725
34630
|
* @param params - The parameters for composing the permission grant message
|
|
34726
|
-
* @param params.
|
|
34631
|
+
* @param params.grantee - The recipient address for the permission grant
|
|
34727
34632
|
* @param params.operation - The type of operation being granted permission for
|
|
34728
34633
|
* @param params.files - Array of file IDs that the permission applies to
|
|
34729
34634
|
* @param params.grantUrl - URL where the grant details are stored
|
|
@@ -34816,36 +34721,45 @@ var PermissionsController = class {
|
|
|
34816
34721
|
return addresses[0];
|
|
34817
34722
|
}
|
|
34818
34723
|
/**
|
|
34819
|
-
*
|
|
34724
|
+
* Gets on-chain permission grant data without expensive off-chain resolution.
|
|
34820
34725
|
*
|
|
34821
34726
|
* @remarks
|
|
34822
|
-
* This method
|
|
34823
|
-
*
|
|
34824
|
-
*
|
|
34825
|
-
*
|
|
34826
|
-
*
|
|
34827
|
-
*
|
|
34828
|
-
*
|
|
34829
|
-
*
|
|
34830
|
-
*
|
|
34831
|
-
*
|
|
34727
|
+
* This method provides a fast, performance-focused way to retrieve permission grants
|
|
34728
|
+
* by querying only the subgraph without making expensive IPFS or individual contract calls.
|
|
34729
|
+
* It eliminates the N+1 query problem of the legacy `getUserPermissions()` method.
|
|
34730
|
+
*
|
|
34731
|
+
* The returned data contains all on-chain information but does NOT include resolved
|
|
34732
|
+
* operation details, parameters, or file IDs. Use `retrieveGrantFile()` separately
|
|
34733
|
+
* for specific grants when detailed data is needed.
|
|
34734
|
+
*
|
|
34735
|
+
* **Performance**: Completes in ~100-500ms regardless of permission count.
|
|
34736
|
+
* **Reliability**: Single point of failure (subgraph) with clear RPC fallback path.
|
|
34737
|
+
*
|
|
34738
|
+
* @param options - Options for retrieving permissions (limit, subgraph URL)
|
|
34739
|
+
* @returns A Promise that resolves to an array of `OnChainPermissionGrant` objects
|
|
34740
|
+
* @throws {BlockchainError} When subgraph query fails
|
|
34741
|
+
* @throws {NetworkError} When network requests fail
|
|
34832
34742
|
* @example
|
|
34833
34743
|
* ```typescript
|
|
34834
|
-
* // Get all
|
|
34835
|
-
* const
|
|
34744
|
+
* // Fast: Get all on-chain permission data
|
|
34745
|
+
* const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
|
|
34836
34746
|
*
|
|
34837
|
-
*
|
|
34838
|
-
*
|
|
34747
|
+
* // Display in UI immediately
|
|
34748
|
+
* grants.forEach(grant => {
|
|
34749
|
+
* console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
|
|
34839
34750
|
* });
|
|
34840
34751
|
*
|
|
34841
|
-
* //
|
|
34842
|
-
* const
|
|
34752
|
+
* // Lazy load detailed data for specific permission when user clicks
|
|
34753
|
+
* const grantFile = await retrieveGrantFile(grants[0].grantUrl);
|
|
34754
|
+
* console.log(`Operation: ${grantFile.operation}`);
|
|
34755
|
+
* console.log(`Parameters:`, grantFile.parameters);
|
|
34843
34756
|
* ```
|
|
34844
34757
|
*/
|
|
34845
|
-
async
|
|
34758
|
+
async getUserPermissionGrantsOnChain(options = {}) {
|
|
34759
|
+
const { limit = 50, subgraphUrl } = options;
|
|
34846
34760
|
try {
|
|
34847
34761
|
const userAddress = await this.getUserAddress();
|
|
34848
|
-
const graphqlEndpoint =
|
|
34762
|
+
const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
|
|
34849
34763
|
if (!graphqlEndpoint) {
|
|
34850
34764
|
throw new BlockchainError(
|
|
34851
34765
|
"subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
|
|
@@ -34867,16 +34781,6 @@ var PermissionsController = class {
|
|
|
34867
34781
|
}
|
|
34868
34782
|
}
|
|
34869
34783
|
`;
|
|
34870
|
-
console.info("Query:", query);
|
|
34871
|
-
console.info(
|
|
34872
|
-
"Body:",
|
|
34873
|
-
JSON.stringify({
|
|
34874
|
-
query,
|
|
34875
|
-
variables: {
|
|
34876
|
-
userId: userAddress.toLowerCase()
|
|
34877
|
-
}
|
|
34878
|
-
})
|
|
34879
|
-
);
|
|
34880
34784
|
const response = await fetch(graphqlEndpoint, {
|
|
34881
34785
|
method: "POST",
|
|
34882
34786
|
headers: {
|
|
@@ -34895,7 +34799,6 @@ var PermissionsController = class {
|
|
|
34895
34799
|
);
|
|
34896
34800
|
}
|
|
34897
34801
|
const result = await response.json();
|
|
34898
|
-
console.info("Result:", result);
|
|
34899
34802
|
if (result.errors) {
|
|
34900
34803
|
throw new BlockchainError(
|
|
34901
34804
|
`Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
|
|
@@ -34903,64 +34806,32 @@ var PermissionsController = class {
|
|
|
34903
34806
|
}
|
|
34904
34807
|
const userData = result.data?.user;
|
|
34905
34808
|
if (!userData || !userData.permissions?.length) {
|
|
34906
|
-
console.warn("No permissions found for user:", userAddress);
|
|
34907
34809
|
return [];
|
|
34908
34810
|
}
|
|
34909
|
-
const
|
|
34910
|
-
|
|
34911
|
-
|
|
34912
|
-
|
|
34913
|
-
|
|
34914
|
-
|
|
34915
|
-
|
|
34916
|
-
|
|
34917
|
-
|
|
34918
|
-
|
|
34919
|
-
|
|
34920
|
-
|
|
34921
|
-
|
|
34922
|
-
|
|
34923
|
-
} catch {
|
|
34924
|
-
}
|
|
34925
|
-
try {
|
|
34926
|
-
const fileIds = await this.getPermissionFileIds(
|
|
34927
|
-
BigInt(permission.id)
|
|
34928
|
-
);
|
|
34929
|
-
files = fileIds.map((id) => Number(id));
|
|
34930
|
-
} catch {
|
|
34931
|
-
}
|
|
34932
|
-
userPermissions.push({
|
|
34933
|
-
id: BigInt(permission.id),
|
|
34934
|
-
files,
|
|
34935
|
-
operation: operation || "",
|
|
34936
|
-
parameters: parameters || {},
|
|
34937
|
-
grant: permission.grant,
|
|
34938
|
-
grantor: userAddress.toLowerCase(),
|
|
34939
|
-
// Current user is the grantor
|
|
34940
|
-
grantee: granteeAddress || userAddress,
|
|
34941
|
-
// Application that received permission
|
|
34942
|
-
active: true,
|
|
34943
|
-
// Default to active if not specified
|
|
34944
|
-
grantedAt: Number(permission.addedAtBlock),
|
|
34945
|
-
nonce: Number(permission.nonce)
|
|
34946
|
-
});
|
|
34947
|
-
} catch (error) {
|
|
34948
|
-
console.error(
|
|
34949
|
-
"SDK Error: Failed to process permission:",
|
|
34950
|
-
permission.id,
|
|
34951
|
-
error
|
|
34952
|
-
);
|
|
34953
|
-
}
|
|
34954
|
-
}
|
|
34955
|
-
return userPermissions.sort((a, b) => {
|
|
34811
|
+
const onChainGrants = userData.permissions.slice(0, limit).map((permission) => ({
|
|
34812
|
+
id: BigInt(permission.id),
|
|
34813
|
+
grantUrl: permission.grant,
|
|
34814
|
+
grantSignature: permission.grantSignature,
|
|
34815
|
+
grantHash: permission.grantHash,
|
|
34816
|
+
nonce: BigInt(permission.nonce),
|
|
34817
|
+
addedAtBlock: BigInt(permission.addedAtBlock),
|
|
34818
|
+
addedAtTimestamp: BigInt(permission.addedAtTimestamp || "0"),
|
|
34819
|
+
transactionHash: permission.transactionHash || "",
|
|
34820
|
+
grantor: userAddress,
|
|
34821
|
+
active: true
|
|
34822
|
+
// TODO: Add revocation status from subgraph when available
|
|
34823
|
+
}));
|
|
34824
|
+
return onChainGrants.sort((a, b) => {
|
|
34956
34825
|
if (a.id < b.id) return 1;
|
|
34957
34826
|
if (a.id > b.id) return -1;
|
|
34958
34827
|
return 0;
|
|
34959
34828
|
});
|
|
34960
34829
|
} catch (error) {
|
|
34961
|
-
|
|
34830
|
+
if (error instanceof BlockchainError || error instanceof NetworkError) {
|
|
34831
|
+
throw error;
|
|
34832
|
+
}
|
|
34962
34833
|
throw new BlockchainError(
|
|
34963
|
-
`Failed to fetch user
|
|
34834
|
+
`Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
34964
34835
|
);
|
|
34965
34836
|
}
|
|
34966
34837
|
}
|
|
@@ -35722,32 +35593,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
|
|
|
35722
35593
|
throw new Error(`Failed to decrypt with private key: ${error}`);
|
|
35723
35594
|
}
|
|
35724
35595
|
}
|
|
35725
|
-
async function
|
|
35596
|
+
async function encryptBlobWithSignedKey(data, key, platformAdapter) {
|
|
35726
35597
|
try {
|
|
35727
35598
|
const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
|
|
35728
35599
|
const dataArray = new Uint8Array(dataBuffer);
|
|
35729
35600
|
const encrypted = await platformAdapter.crypto.encryptWithPassword(
|
|
35730
35601
|
dataArray,
|
|
35731
|
-
|
|
35602
|
+
key
|
|
35732
35603
|
);
|
|
35733
35604
|
return new Blob([encrypted], {
|
|
35734
35605
|
type: "application/octet-stream"
|
|
35735
35606
|
});
|
|
35736
35607
|
} catch (error) {
|
|
35737
|
-
throw new Error(`Failed to encrypt
|
|
35738
|
-
}
|
|
35739
|
-
}
|
|
35740
|
-
async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
|
|
35741
|
-
try {
|
|
35742
|
-
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35743
|
-
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35744
|
-
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35745
|
-
encryptedArray,
|
|
35746
|
-
walletSignature
|
|
35747
|
-
);
|
|
35748
|
-
return new Blob([decrypted], { type: "text/plain" });
|
|
35749
|
-
} catch (error) {
|
|
35750
|
-
throw new Error(`Failed to decrypt user data: ${error}`);
|
|
35608
|
+
throw new Error(`Failed to encrypt data: ${error}`);
|
|
35751
35609
|
}
|
|
35752
35610
|
}
|
|
35753
35611
|
async function generateEncryptionKeyPair(platformAdapter) {
|
|
@@ -35764,60 +35622,25 @@ async function generatePGPKeyPair(platformAdapter, options) {
|
|
|
35764
35622
|
throw new Error(`Failed to generate PGP key pair: ${error}`);
|
|
35765
35623
|
}
|
|
35766
35624
|
}
|
|
35625
|
+
async function decryptBlobWithSignedKey(encryptedData, key, platformAdapter) {
|
|
35626
|
+
try {
|
|
35627
|
+
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35628
|
+
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35629
|
+
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35630
|
+
encryptedArray,
|
|
35631
|
+
key
|
|
35632
|
+
);
|
|
35633
|
+
return new Blob([decrypted], { type: "text/plain" });
|
|
35634
|
+
} catch (error) {
|
|
35635
|
+
throw new Error(`Failed to decrypt data: ${error}`);
|
|
35636
|
+
}
|
|
35637
|
+
}
|
|
35767
35638
|
|
|
35768
35639
|
// src/controllers/data.ts
|
|
35769
35640
|
var DataController = class {
|
|
35770
35641
|
constructor(context) {
|
|
35771
35642
|
this.context = context;
|
|
35772
35643
|
}
|
|
35773
|
-
/**
|
|
35774
|
-
* Uploads user data with automatic encryption and blockchain registration.
|
|
35775
|
-
*
|
|
35776
|
-
* @remarks
|
|
35777
|
-
* This is the primary method for uploading user data to the Vana network. It handles
|
|
35778
|
-
* the complete workflow including content normalization, schema validation, encryption,
|
|
35779
|
-
* storage upload, permission granting, and blockchain registration.
|
|
35780
|
-
*
|
|
35781
|
-
* The method automatically:
|
|
35782
|
-
* - Normalizes input content to a Blob
|
|
35783
|
-
* - Validates data against schema if provided
|
|
35784
|
-
* - Generates encryption keys and encrypts the data
|
|
35785
|
-
* - Uploads to the configured storage provider
|
|
35786
|
-
* - Grants permissions to specified applications
|
|
35787
|
-
* - Registers the file on the blockchain
|
|
35788
|
-
*
|
|
35789
|
-
* @param params - Upload parameters including content, filename, schema, and permissions
|
|
35790
|
-
* @returns Promise resolving to upload results with file ID and transaction hash
|
|
35791
|
-
* @throws {Error} When wallet is not connected or storage is not configured
|
|
35792
|
-
* @throws {SchemaValidationError} When schema validation fails
|
|
35793
|
-
* @throws {Error} When upload or blockchain registration fails
|
|
35794
|
-
* @example
|
|
35795
|
-
* ```typescript
|
|
35796
|
-
* // Basic file upload
|
|
35797
|
-
* const result = await vana.data.upload({
|
|
35798
|
-
* content: "My personal data",
|
|
35799
|
-
* filename: "diary.txt"
|
|
35800
|
-
* });
|
|
35801
|
-
*
|
|
35802
|
-
* // Upload with schema validation
|
|
35803
|
-
* const result = await vana.data.upload({
|
|
35804
|
-
* content: { name: "John", age: 30 },
|
|
35805
|
-
* filename: "profile.json",
|
|
35806
|
-
* schemaId: 1
|
|
35807
|
-
* });
|
|
35808
|
-
*
|
|
35809
|
-
* // Upload with permissions
|
|
35810
|
-
* const result = await vana.data.upload({
|
|
35811
|
-
* content: "Data for AI analysis",
|
|
35812
|
-
* filename: "analysis.txt",
|
|
35813
|
-
* permissions: [{
|
|
35814
|
-
* to: "0x1234...",
|
|
35815
|
-
* operation: "llm_inference",
|
|
35816
|
-
* parameters: { model: "gpt-4" }
|
|
35817
|
-
* }]
|
|
35818
|
-
* });
|
|
35819
|
-
* ```
|
|
35820
|
-
*/
|
|
35821
35644
|
async upload(params) {
|
|
35822
35645
|
const {
|
|
35823
35646
|
content,
|
|
@@ -35825,7 +35648,8 @@ var DataController = class {
|
|
|
35825
35648
|
schemaId,
|
|
35826
35649
|
permissions = [],
|
|
35827
35650
|
encrypt: encrypt3 = true,
|
|
35828
|
-
providerName
|
|
35651
|
+
providerName,
|
|
35652
|
+
owner
|
|
35829
35653
|
} = params;
|
|
35830
35654
|
try {
|
|
35831
35655
|
let blob;
|
|
@@ -35882,23 +35706,28 @@ var DataController = class {
|
|
|
35882
35706
|
this.context.walletClient,
|
|
35883
35707
|
DEFAULT_ENCRYPTION_SEED
|
|
35884
35708
|
);
|
|
35885
|
-
finalBlob = await
|
|
35709
|
+
finalBlob = await encryptBlobWithSignedKey(
|
|
35886
35710
|
blob,
|
|
35887
35711
|
encryptionKey,
|
|
35888
35712
|
this.context.platform
|
|
35889
35713
|
);
|
|
35890
35714
|
}
|
|
35891
35715
|
if (!this.context.storageManager) {
|
|
35892
|
-
|
|
35893
|
-
|
|
35894
|
-
|
|
35716
|
+
if (this.context.validateStorageRequired) {
|
|
35717
|
+
this.context.validateStorageRequired();
|
|
35718
|
+
throw new Error("Storage validation failed");
|
|
35719
|
+
} else {
|
|
35720
|
+
throw new Error(
|
|
35721
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
35722
|
+
);
|
|
35723
|
+
}
|
|
35895
35724
|
}
|
|
35896
35725
|
const uploadResult = await this.context.storageManager.upload(
|
|
35897
35726
|
finalBlob,
|
|
35898
35727
|
filename,
|
|
35899
35728
|
providerName
|
|
35900
35729
|
);
|
|
35901
|
-
const userAddress = await this.getUserAddress();
|
|
35730
|
+
const userAddress = owner || await this.getUserAddress();
|
|
35902
35731
|
let encryptedPermissions = [];
|
|
35903
35732
|
if (permissions.length > 0 && encrypt3) {
|
|
35904
35733
|
const userEncryptionKey = await generateEncryptionKey(
|
|
@@ -35910,7 +35739,7 @@ var DataController = class {
|
|
|
35910
35739
|
const publicKey = permission.publicKey;
|
|
35911
35740
|
if (!publicKey) {
|
|
35912
35741
|
throw new Error(
|
|
35913
|
-
`Public key required for permission to ${permission.
|
|
35742
|
+
`Public key required for permission to ${permission.grantee} when encryption is enabled`
|
|
35914
35743
|
);
|
|
35915
35744
|
}
|
|
35916
35745
|
const encryptedKey = await encryptWithWalletPublicKey(
|
|
@@ -35919,7 +35748,7 @@ var DataController = class {
|
|
|
35919
35748
|
this.context.platform
|
|
35920
35749
|
);
|
|
35921
35750
|
return {
|
|
35922
|
-
account: permission.
|
|
35751
|
+
account: permission.grantee,
|
|
35923
35752
|
key: encryptedKey
|
|
35924
35753
|
};
|
|
35925
35754
|
})
|
|
@@ -35932,7 +35761,8 @@ var DataController = class {
|
|
|
35932
35761
|
url: uploadResult.url,
|
|
35933
35762
|
userAddress,
|
|
35934
35763
|
permissions: encryptedPermissions,
|
|
35935
|
-
schemaId: schemaId || 0
|
|
35764
|
+
schemaId: schemaId || 0,
|
|
35765
|
+
ownerAddress: owner
|
|
35936
35766
|
}
|
|
35937
35767
|
);
|
|
35938
35768
|
} else if (this.context.relayerCallbacks?.submitFileAddition) {
|
|
@@ -35968,6 +35798,142 @@ var DataController = class {
|
|
|
35968
35798
|
);
|
|
35969
35799
|
}
|
|
35970
35800
|
}
|
|
35801
|
+
/**
|
|
35802
|
+
* Decrypts a file owned by the user using their wallet signature.
|
|
35803
|
+
*
|
|
35804
|
+
* @remarks
|
|
35805
|
+
* This is the high-level convenience method for decrypting user files, serving as the
|
|
35806
|
+
* symmetrical counterpart to the `upload` method. It handles the complete decryption
|
|
35807
|
+
* workflow including key generation, URL protocol detection, content fetching, and
|
|
35808
|
+
* decryption.
|
|
35809
|
+
*
|
|
35810
|
+
* The method automatically:
|
|
35811
|
+
* - Generates the decryption key from the user's wallet signature
|
|
35812
|
+
* - Determines the appropriate fetch method based on the file URL protocol
|
|
35813
|
+
* - Fetches the encrypted content from IPFS or standard HTTP URLs
|
|
35814
|
+
* - Decrypts the content using the generated key
|
|
35815
|
+
*
|
|
35816
|
+
* For IPFS URLs, the method uses gateway fallback for improved reliability. For
|
|
35817
|
+
* standard HTTP URLs, it uses a simple fetch. If you need custom authentication
|
|
35818
|
+
* headers or specific gateway configurations, use the low-level primitives directly.
|
|
35819
|
+
*
|
|
35820
|
+
* @param file - The user file to decrypt (typically from getUserFiles)
|
|
35821
|
+
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
35822
|
+
* @returns Promise resolving to the decrypted file content as a Blob
|
|
35823
|
+
* @throws {Error} When the wallet is not connected
|
|
35824
|
+
* @throws {Error} When fetching the encrypted content fails
|
|
35825
|
+
* @throws {Error} When decryption fails (wrong key or corrupted data)
|
|
35826
|
+
* @example
|
|
35827
|
+
* ```typescript
|
|
35828
|
+
* // Basic file decryption
|
|
35829
|
+
* const files = await vana.data.getUserFiles({ owner: userAddress });
|
|
35830
|
+
* const decryptedBlob = await vana.data.decryptFile(files[0]);
|
|
35831
|
+
*
|
|
35832
|
+
* // Convert to text
|
|
35833
|
+
* const text = await decryptedBlob.text();
|
|
35834
|
+
* console.log('Decrypted content:', text);
|
|
35835
|
+
*
|
|
35836
|
+
* // Convert to JSON
|
|
35837
|
+
* const json = JSON.parse(await decryptedBlob.text());
|
|
35838
|
+
* console.log('Decrypted data:', json);
|
|
35839
|
+
*
|
|
35840
|
+
* // With custom encryption seed
|
|
35841
|
+
* const decryptedBlob = await vana.data.decryptFile(
|
|
35842
|
+
* files[0],
|
|
35843
|
+
* "My custom encryption seed"
|
|
35844
|
+
* );
|
|
35845
|
+
*
|
|
35846
|
+
* // Save to file (in Node.js)
|
|
35847
|
+
* const buffer = await decryptedBlob.arrayBuffer();
|
|
35848
|
+
* fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
|
|
35849
|
+
* ```
|
|
35850
|
+
*/
|
|
35851
|
+
async decryptFile(file, encryptionSeed) {
|
|
35852
|
+
try {
|
|
35853
|
+
const encryptionKey = await generateEncryptionKey(
|
|
35854
|
+
this.context.walletClient,
|
|
35855
|
+
encryptionSeed || DEFAULT_ENCRYPTION_SEED
|
|
35856
|
+
);
|
|
35857
|
+
let encryptedBlob;
|
|
35858
|
+
try {
|
|
35859
|
+
if (file.url.startsWith("ipfs://")) {
|
|
35860
|
+
encryptedBlob = await this.fetchFromIPFS(file.url);
|
|
35861
|
+
} else {
|
|
35862
|
+
encryptedBlob = await this.fetch(file.url);
|
|
35863
|
+
}
|
|
35864
|
+
} catch (fetchError) {
|
|
35865
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
|
|
35866
|
+
if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
|
|
35867
|
+
throw new Error(
|
|
35868
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35869
|
+
);
|
|
35870
|
+
} else if (errorMessage.includes("Empty response")) {
|
|
35871
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35872
|
+
} else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
|
|
35873
|
+
throw new Error(
|
|
35874
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35875
|
+
);
|
|
35876
|
+
} else if (errorMessage.includes("HTTP error!")) {
|
|
35877
|
+
const statusMatch = errorMessage.match(/status: (\d+)/);
|
|
35878
|
+
const status = statusMatch ? statusMatch[1] : "unknown";
|
|
35879
|
+
if (status === "500") {
|
|
35880
|
+
throw new Error(
|
|
35881
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35882
|
+
);
|
|
35883
|
+
} else if (status === "403") {
|
|
35884
|
+
throw new Error(
|
|
35885
|
+
"Access denied. You may not have permission to access this file"
|
|
35886
|
+
);
|
|
35887
|
+
} else if (status === "404") {
|
|
35888
|
+
throw new Error(
|
|
35889
|
+
"File not found: The encrypted file is no longer available at the stored URL."
|
|
35890
|
+
);
|
|
35891
|
+
} else {
|
|
35892
|
+
throw new Error(
|
|
35893
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35894
|
+
);
|
|
35895
|
+
}
|
|
35896
|
+
}
|
|
35897
|
+
throw fetchError;
|
|
35898
|
+
}
|
|
35899
|
+
if (encryptedBlob.size === 0) {
|
|
35900
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35901
|
+
}
|
|
35902
|
+
let decryptedBlob;
|
|
35903
|
+
try {
|
|
35904
|
+
decryptedBlob = await decryptBlobWithSignedKey(
|
|
35905
|
+
encryptedBlob,
|
|
35906
|
+
encryptionKey,
|
|
35907
|
+
this.context.platform
|
|
35908
|
+
);
|
|
35909
|
+
} catch (decryptError) {
|
|
35910
|
+
const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
|
|
35911
|
+
if (errorMessage.includes("not a valid OpenPGP message")) {
|
|
35912
|
+
throw new Error(
|
|
35913
|
+
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
|
|
35914
|
+
);
|
|
35915
|
+
} else if (errorMessage.includes("Session key decryption failed")) {
|
|
35916
|
+
throw new Error("Wrong encryption key");
|
|
35917
|
+
} else if (errorMessage.includes("Error decrypting message")) {
|
|
35918
|
+
throw new Error("Wrong encryption key");
|
|
35919
|
+
} else if (errorMessage.includes("File not found")) {
|
|
35920
|
+
throw new Error(
|
|
35921
|
+
"File not found: The encrypted file is no longer available"
|
|
35922
|
+
);
|
|
35923
|
+
} else {
|
|
35924
|
+
throw decryptError;
|
|
35925
|
+
}
|
|
35926
|
+
}
|
|
35927
|
+
return decryptedBlob;
|
|
35928
|
+
} catch (error) {
|
|
35929
|
+
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"))) {
|
|
35930
|
+
throw error;
|
|
35931
|
+
}
|
|
35932
|
+
throw new Error(
|
|
35933
|
+
`Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35934
|
+
);
|
|
35935
|
+
}
|
|
35936
|
+
}
|
|
35971
35937
|
/**
|
|
35972
35938
|
* Retrieves all data files owned by a specific user address.
|
|
35973
35939
|
*
|
|
@@ -36718,81 +36684,6 @@ var DataController = class {
|
|
|
36718
36684
|
);
|
|
36719
36685
|
}
|
|
36720
36686
|
}
|
|
36721
|
-
/**
|
|
36722
|
-
* Decrypts a file that was encrypted using the Vana protocol.
|
|
36723
|
-
*
|
|
36724
|
-
* @param file - The UserFile object containing the file URL and metadata
|
|
36725
|
-
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
36726
|
-
* @returns Promise resolving to the decrypted file as a Blob
|
|
36727
|
-
*
|
|
36728
|
-
* This method handles the complete flow of:
|
|
36729
|
-
* 1. Generating the encryption key from the user's wallet signature
|
|
36730
|
-
* 2. Fetching the encrypted file from the stored URL
|
|
36731
|
-
* 3. Decrypting the file using the canonical Vana decryption method
|
|
36732
|
-
*/
|
|
36733
|
-
async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
|
|
36734
|
-
try {
|
|
36735
|
-
const encryptionKey = await generateEncryptionKey(
|
|
36736
|
-
this.context.walletClient,
|
|
36737
|
-
encryptionSeed
|
|
36738
|
-
);
|
|
36739
|
-
const fetchUrl = this.convertToDownloadUrl(file.url);
|
|
36740
|
-
console.debug(
|
|
36741
|
-
`Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
|
|
36742
|
-
);
|
|
36743
|
-
const response = await fetch(fetchUrl);
|
|
36744
|
-
if (!response.ok) {
|
|
36745
|
-
if (response.status === 404) {
|
|
36746
|
-
throw new Error(
|
|
36747
|
-
"File not found. The encrypted file may have been moved or deleted."
|
|
36748
|
-
);
|
|
36749
|
-
} else if (response.status === 403) {
|
|
36750
|
-
throw new Error(
|
|
36751
|
-
"Access denied. You may not have permission to access this file."
|
|
36752
|
-
);
|
|
36753
|
-
} else {
|
|
36754
|
-
throw new Error(
|
|
36755
|
-
`Network error: ${response.status} ${response.statusText}`
|
|
36756
|
-
);
|
|
36757
|
-
}
|
|
36758
|
-
}
|
|
36759
|
-
const encryptedBlob = await response.blob();
|
|
36760
|
-
console.debug(
|
|
36761
|
-
`Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
|
|
36762
|
-
);
|
|
36763
|
-
if (encryptedBlob.size === 0) {
|
|
36764
|
-
throw new Error("File is empty or could not be retrieved.");
|
|
36765
|
-
}
|
|
36766
|
-
const decryptedBlob = await decryptUserData(
|
|
36767
|
-
encryptedBlob,
|
|
36768
|
-
encryptionKey,
|
|
36769
|
-
this.context.platform
|
|
36770
|
-
);
|
|
36771
|
-
return decryptedBlob;
|
|
36772
|
-
} catch (error) {
|
|
36773
|
-
console.error("Failed to decrypt file:", error);
|
|
36774
|
-
if (error instanceof Error) {
|
|
36775
|
-
if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
|
|
36776
|
-
throw new Error(
|
|
36777
|
-
"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."
|
|
36778
|
-
);
|
|
36779
|
-
} else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
|
|
36780
|
-
throw new Error(
|
|
36781
|
-
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
36782
|
-
);
|
|
36783
|
-
} else if (error.message.includes("File not found")) {
|
|
36784
|
-
throw new Error(
|
|
36785
|
-
"File not found: The encrypted file is no longer available at the stored URL."
|
|
36786
|
-
);
|
|
36787
|
-
} else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
|
|
36788
|
-
throw new Error(
|
|
36789
|
-
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
|
|
36790
|
-
);
|
|
36791
|
-
}
|
|
36792
|
-
}
|
|
36793
|
-
throw error;
|
|
36794
|
-
}
|
|
36795
|
-
}
|
|
36796
36687
|
/**
|
|
36797
36688
|
* Registers a file URL directly on the blockchain with a schema ID.
|
|
36798
36689
|
*
|
|
@@ -36853,26 +36744,6 @@ var DataController = class {
|
|
|
36853
36744
|
);
|
|
36854
36745
|
}
|
|
36855
36746
|
}
|
|
36856
|
-
/**
|
|
36857
|
-
* Converts IPFS and Google Drive URLs to direct download URLs for fetching.
|
|
36858
|
-
*
|
|
36859
|
-
* @param url - The URL to convert to a direct download URL
|
|
36860
|
-
* @returns The converted direct download URL or the original URL if not a special URL
|
|
36861
|
-
*/
|
|
36862
|
-
convertToDownloadUrl(url) {
|
|
36863
|
-
if (url.startsWith("ipfs://")) {
|
|
36864
|
-
const hash = url.replace("ipfs://", "");
|
|
36865
|
-
return `https://ipfs.io/ipfs/${hash}`;
|
|
36866
|
-
}
|
|
36867
|
-
if (url.includes("drive.google.com/file/d/")) {
|
|
36868
|
-
const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
|
|
36869
|
-
if (fileIdMatch) {
|
|
36870
|
-
const fileId = fileIdMatch[1];
|
|
36871
|
-
return `https://drive.google.com/uc?id=${fileId}&export=download`;
|
|
36872
|
-
}
|
|
36873
|
-
}
|
|
36874
|
-
return url;
|
|
36875
|
-
}
|
|
36876
36747
|
/**
|
|
36877
36748
|
* Gets the user's address from the wallet client.
|
|
36878
36749
|
*
|
|
@@ -37366,7 +37237,7 @@ var DataController = class {
|
|
|
37366
37237
|
this.context.walletClient,
|
|
37367
37238
|
DEFAULT_ENCRYPTION_SEED
|
|
37368
37239
|
);
|
|
37369
|
-
const encryptedData = await
|
|
37240
|
+
const encryptedData = await encryptBlobWithSignedKey(
|
|
37370
37241
|
data,
|
|
37371
37242
|
userEncryptionKey,
|
|
37372
37243
|
this.context.platform
|
|
@@ -37530,12 +37401,13 @@ var DataController = class {
|
|
|
37530
37401
|
privateKey,
|
|
37531
37402
|
this.context.platform
|
|
37532
37403
|
);
|
|
37533
|
-
|
|
37534
|
-
if (
|
|
37535
|
-
|
|
37404
|
+
let encryptedData;
|
|
37405
|
+
if (file.url.startsWith("ipfs://")) {
|
|
37406
|
+
encryptedData = await this.fetchFromIPFS(file.url);
|
|
37407
|
+
} else {
|
|
37408
|
+
encryptedData = await this.fetch(file.url);
|
|
37536
37409
|
}
|
|
37537
|
-
const
|
|
37538
|
-
const decryptedData = await decryptUserData(
|
|
37410
|
+
const decryptedData = await decryptBlobWithSignedKey(
|
|
37539
37411
|
encryptedData,
|
|
37540
37412
|
userEncryptionKey,
|
|
37541
37413
|
this.context.platform
|
|
@@ -37548,6 +37420,162 @@ var DataController = class {
|
|
|
37548
37420
|
);
|
|
37549
37421
|
}
|
|
37550
37422
|
}
|
|
37423
|
+
/**
|
|
37424
|
+
* Simple network-agnostic fetch utility for retrieving file content.
|
|
37425
|
+
*
|
|
37426
|
+
* @remarks
|
|
37427
|
+
* This is a thin wrapper around the global fetch API that returns the response as a Blob.
|
|
37428
|
+
* It provides a consistent interface for fetching encrypted content before decryption.
|
|
37429
|
+
* For IPFS URLs, consider using fetchFromIPFS for better reliability.
|
|
37430
|
+
*
|
|
37431
|
+
* @param url - The URL to fetch content from
|
|
37432
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37433
|
+
* @throws {Error} When the fetch fails or returns a non-ok response
|
|
37434
|
+
*
|
|
37435
|
+
* @example
|
|
37436
|
+
* ```typescript
|
|
37437
|
+
* // Fetch and decrypt a file
|
|
37438
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37439
|
+
* const encryptedBlob = await vana.data.fetch(file.url);
|
|
37440
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37441
|
+
*
|
|
37442
|
+
* // With custom headers for authentication
|
|
37443
|
+
* const response = await fetch(file.url, {
|
|
37444
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
37445
|
+
* });
|
|
37446
|
+
* const encryptedBlob = await response.blob();
|
|
37447
|
+
* ```
|
|
37448
|
+
*/
|
|
37449
|
+
async fetch(url) {
|
|
37450
|
+
try {
|
|
37451
|
+
const response = await fetch(url);
|
|
37452
|
+
if (!response.ok) {
|
|
37453
|
+
throw new Error(
|
|
37454
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37455
|
+
);
|
|
37456
|
+
}
|
|
37457
|
+
const blob = await response.blob();
|
|
37458
|
+
if (blob.size === 0) {
|
|
37459
|
+
throw new Error("Empty response");
|
|
37460
|
+
}
|
|
37461
|
+
return blob;
|
|
37462
|
+
} catch (error) {
|
|
37463
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
37464
|
+
throw new Error(
|
|
37465
|
+
`Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
|
|
37466
|
+
);
|
|
37467
|
+
}
|
|
37468
|
+
throw error;
|
|
37469
|
+
}
|
|
37470
|
+
}
|
|
37471
|
+
/**
|
|
37472
|
+
* Specialized IPFS fetcher with gateway fallback mechanism.
|
|
37473
|
+
*
|
|
37474
|
+
* @remarks
|
|
37475
|
+
* This method provides robust IPFS content fetching by trying multiple gateways
|
|
37476
|
+
* in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
|
|
37477
|
+
*
|
|
37478
|
+
* The default gateway list includes public gateways, but you should provide
|
|
37479
|
+
* your own gateways for production use to ensure reliability and privacy.
|
|
37480
|
+
*
|
|
37481
|
+
* @param url - The IPFS URL (ipfs://...) or CID to fetch
|
|
37482
|
+
* @param options - Optional configuration
|
|
37483
|
+
* @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
|
|
37484
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37485
|
+
* @throws {Error} When all gateways fail to fetch the content
|
|
37486
|
+
*
|
|
37487
|
+
* @example
|
|
37488
|
+
* ```typescript
|
|
37489
|
+
* // Fetch from IPFS with custom gateways
|
|
37490
|
+
* const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
|
|
37491
|
+
* gateways: [
|
|
37492
|
+
* 'https://my-private-gateway.com/ipfs/',
|
|
37493
|
+
* 'https://dweb.link/ipfs/',
|
|
37494
|
+
* 'https://ipfs.io/ipfs/'
|
|
37495
|
+
* ]
|
|
37496
|
+
* });
|
|
37497
|
+
*
|
|
37498
|
+
* // Decrypt the fetched content
|
|
37499
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37500
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37501
|
+
*
|
|
37502
|
+
* // With raw CID
|
|
37503
|
+
* const blob = await vana.data.fetchFromIPFS('QmXxx...', {
|
|
37504
|
+
* gateways: ['https://ipfs.io/ipfs/']
|
|
37505
|
+
* });
|
|
37506
|
+
* ```
|
|
37507
|
+
*/
|
|
37508
|
+
async fetchFromIPFS(url, options) {
|
|
37509
|
+
const defaultGateways = [
|
|
37510
|
+
"https://dweb.link/ipfs/",
|
|
37511
|
+
"https://ipfs.io/ipfs/"
|
|
37512
|
+
];
|
|
37513
|
+
const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
|
|
37514
|
+
let cid;
|
|
37515
|
+
if (url.startsWith("ipfs://")) {
|
|
37516
|
+
cid = url.replace("ipfs://", "");
|
|
37517
|
+
} else if (url.startsWith("Qm") || url.startsWith("bafy")) {
|
|
37518
|
+
cid = url;
|
|
37519
|
+
} else {
|
|
37520
|
+
throw new Error(
|
|
37521
|
+
`Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
|
|
37522
|
+
);
|
|
37523
|
+
}
|
|
37524
|
+
const errors = [];
|
|
37525
|
+
for (let i = 0; i < gateways.length; i++) {
|
|
37526
|
+
const gateway = gateways[i];
|
|
37527
|
+
const isLastGateway = i === gateways.length - 1;
|
|
37528
|
+
const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
|
|
37529
|
+
try {
|
|
37530
|
+
console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
|
|
37531
|
+
const response = await fetch(gatewayUrl);
|
|
37532
|
+
if (response.ok) {
|
|
37533
|
+
const blob = await response.blob();
|
|
37534
|
+
if (blob.size > 0) {
|
|
37535
|
+
console.debug(`Successfully fetched from gateway: ${gateway}`);
|
|
37536
|
+
return blob;
|
|
37537
|
+
} else {
|
|
37538
|
+
if (isLastGateway) {
|
|
37539
|
+
throw new Error("Empty response");
|
|
37540
|
+
}
|
|
37541
|
+
errors.push({
|
|
37542
|
+
gateway,
|
|
37543
|
+
error: "Empty response"
|
|
37544
|
+
});
|
|
37545
|
+
}
|
|
37546
|
+
} else {
|
|
37547
|
+
if (isLastGateway) {
|
|
37548
|
+
if (response.status === 403) {
|
|
37549
|
+
throw new Error(`HTTP error! status: 403 ${response.statusText}`);
|
|
37550
|
+
} else if (response.status === 404) {
|
|
37551
|
+
throw new Error(`HTTP error! status: 404 ${response.statusText}`);
|
|
37552
|
+
} else {
|
|
37553
|
+
throw new Error(
|
|
37554
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37555
|
+
);
|
|
37556
|
+
}
|
|
37557
|
+
}
|
|
37558
|
+
errors.push({
|
|
37559
|
+
gateway,
|
|
37560
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
37561
|
+
});
|
|
37562
|
+
}
|
|
37563
|
+
} catch (error) {
|
|
37564
|
+
if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
|
|
37565
|
+
throw error;
|
|
37566
|
+
}
|
|
37567
|
+
errors.push({
|
|
37568
|
+
gateway,
|
|
37569
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
37570
|
+
});
|
|
37571
|
+
}
|
|
37572
|
+
}
|
|
37573
|
+
const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
|
|
37574
|
+
throw new Error(
|
|
37575
|
+
`Failed to fetch IPFS content ${cid} from all gateways:
|
|
37576
|
+
${errorDetails}`
|
|
37577
|
+
);
|
|
37578
|
+
}
|
|
37551
37579
|
/**
|
|
37552
37580
|
* Validates a data schema against the Vana meta-schema.
|
|
37553
37581
|
*
|
|
@@ -37736,9 +37764,14 @@ var SchemaController = class {
|
|
|
37736
37764
|
};
|
|
37737
37765
|
validateDataSchema(dataSchema);
|
|
37738
37766
|
if (!this.context.storageManager) {
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37767
|
+
if (this.context.validateStorageRequired) {
|
|
37768
|
+
this.context.validateStorageRequired();
|
|
37769
|
+
throw new Error("Storage validation failed");
|
|
37770
|
+
} else {
|
|
37771
|
+
throw new Error(
|
|
37772
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
37773
|
+
);
|
|
37774
|
+
}
|
|
37742
37775
|
}
|
|
37743
37776
|
const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
|
|
37744
37777
|
type: "application/json"
|
|
@@ -38010,6 +38043,7 @@ var ServerController = class {
|
|
|
38010
38043
|
}
|
|
38011
38044
|
const serverResponse = await response.json();
|
|
38012
38045
|
return {
|
|
38046
|
+
kind: serverResponse.personal_server.kind,
|
|
38013
38047
|
address: serverResponse.personal_server.address,
|
|
38014
38048
|
public_key: serverResponse.personal_server.public_key,
|
|
38015
38049
|
base_url: this.PERSONAL_SERVER_BASE_URL || "",
|
|
@@ -39617,175 +39651,151 @@ var PinataStorage = class {
|
|
|
39617
39651
|
}
|
|
39618
39652
|
};
|
|
39619
39653
|
|
|
39620
|
-
// src/storage/providers/
|
|
39621
|
-
var
|
|
39622
|
-
constructor(
|
|
39623
|
-
this.
|
|
39624
|
-
if (!
|
|
39625
|
-
throw new
|
|
39626
|
-
"
|
|
39627
|
-
"MISSING_UPLOAD_URL",
|
|
39628
|
-
"server-proxy"
|
|
39629
|
-
);
|
|
39630
|
-
}
|
|
39631
|
-
if (!config.downloadUrl) {
|
|
39632
|
-
throw new StorageError(
|
|
39633
|
-
"Download URL is required",
|
|
39634
|
-
"MISSING_DOWNLOAD_URL",
|
|
39635
|
-
"server-proxy"
|
|
39654
|
+
// src/storage/providers/callback-storage.ts
|
|
39655
|
+
var CallbackStorage = class {
|
|
39656
|
+
constructor(callbacks) {
|
|
39657
|
+
this.callbacks = callbacks;
|
|
39658
|
+
if (!callbacks.upload || !callbacks.download) {
|
|
39659
|
+
throw new Error(
|
|
39660
|
+
"CallbackStorage requires both upload and download callbacks"
|
|
39636
39661
|
);
|
|
39637
39662
|
}
|
|
39638
39663
|
}
|
|
39639
39664
|
/**
|
|
39640
|
-
*
|
|
39665
|
+
* Upload a file using the provided callback
|
|
39641
39666
|
*
|
|
39642
|
-
* @
|
|
39643
|
-
*
|
|
39644
|
-
*
|
|
39645
|
-
* and must return a JSON response with `success: true` and an `identifier` field.
|
|
39646
|
-
*
|
|
39647
|
-
* @param file - The file to upload
|
|
39648
|
-
* @param filename - Optional custom filename
|
|
39649
|
-
* @returns Promise that resolves to the server-provided identifier
|
|
39650
|
-
* @throws {StorageError} When the upload fails or server returns an error
|
|
39651
|
-
*
|
|
39652
|
-
* @example
|
|
39653
|
-
* ```typescript
|
|
39654
|
-
* const identifier = await serverStorage.upload(fileBlob, { name: "report.pdf" });
|
|
39655
|
-
* console.log("File uploaded with identifier:", identifier);
|
|
39656
|
-
* ```
|
|
39667
|
+
* @param file - The blob to upload
|
|
39668
|
+
* @param filename - Optional filename for the upload
|
|
39669
|
+
* @returns The upload result with URL and metadata
|
|
39657
39670
|
*/
|
|
39658
39671
|
async upload(file, filename) {
|
|
39659
39672
|
try {
|
|
39660
|
-
const
|
|
39661
|
-
|
|
39662
|
-
if (filename) {
|
|
39663
|
-
formData.append("name", filename);
|
|
39664
|
-
}
|
|
39665
|
-
const response = await fetch(this.config.uploadUrl, {
|
|
39666
|
-
method: "POST",
|
|
39667
|
-
body: formData
|
|
39668
|
-
});
|
|
39669
|
-
if (!response.ok) {
|
|
39670
|
-
const _errorText = await response.text();
|
|
39671
|
-
throw new StorageError(
|
|
39672
|
-
`Server upload failed: ${response.status} ${response.statusText}`,
|
|
39673
|
-
"UPLOAD_FAILED",
|
|
39674
|
-
"server-proxy"
|
|
39675
|
-
);
|
|
39676
|
-
}
|
|
39677
|
-
const result = await response.json();
|
|
39678
|
-
if (!result.success) {
|
|
39673
|
+
const result = await this.callbacks.upload(file, filename);
|
|
39674
|
+
if (!result.url || result.url.trim() === "") {
|
|
39679
39675
|
throw new StorageError(
|
|
39680
|
-
|
|
39681
|
-
"
|
|
39682
|
-
"
|
|
39676
|
+
"Upload callback returned invalid result: missing or empty url",
|
|
39677
|
+
"INVALID_UPLOAD_RESULT",
|
|
39678
|
+
"callback-storage"
|
|
39683
39679
|
);
|
|
39684
39680
|
}
|
|
39685
|
-
|
|
39686
|
-
throw new StorageError(
|
|
39687
|
-
"Server upload succeeded but no identifier returned",
|
|
39688
|
-
"NO_IDENTIFIER_RETURNED",
|
|
39689
|
-
"server-proxy"
|
|
39690
|
-
);
|
|
39691
|
-
}
|
|
39692
|
-
return {
|
|
39693
|
-
url: result.url || result.identifier,
|
|
39694
|
-
size: file.size,
|
|
39695
|
-
contentType: file.type || "application/octet-stream"
|
|
39696
|
-
};
|
|
39681
|
+
return result;
|
|
39697
39682
|
} catch (error) {
|
|
39698
39683
|
if (error instanceof StorageError) {
|
|
39699
39684
|
throw error;
|
|
39700
39685
|
}
|
|
39701
39686
|
throw new StorageError(
|
|
39702
|
-
`
|
|
39687
|
+
`Upload failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39703
39688
|
"UPLOAD_ERROR",
|
|
39704
|
-
"
|
|
39689
|
+
"callback-storage",
|
|
39690
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39705
39691
|
);
|
|
39706
39692
|
}
|
|
39707
39693
|
}
|
|
39708
39694
|
/**
|
|
39709
|
-
*
|
|
39695
|
+
* Download a file using the provided callback
|
|
39710
39696
|
*
|
|
39711
|
-
* @
|
|
39712
|
-
*
|
|
39713
|
-
* Your server is responsible for retrieving the file from your storage backend
|
|
39714
|
-
* and returning the file content as a blob response.
|
|
39715
|
-
*
|
|
39716
|
-
* @param url - The server-provided URL or identifier from upload
|
|
39717
|
-
* @returns Promise that resolves to the downloaded file content
|
|
39718
|
-
* @throws {StorageError} When the download fails or file is not found
|
|
39719
|
-
*
|
|
39720
|
-
* @example
|
|
39721
|
-
* ```typescript
|
|
39722
|
-
* const fileBlob = await serverStorage.download("file-123");
|
|
39723
|
-
* const url = URL.createObjectURL(fileBlob);
|
|
39724
|
-
* ```
|
|
39697
|
+
* @param url - The URL or identifier to download
|
|
39698
|
+
* @returns The downloaded blob
|
|
39725
39699
|
*/
|
|
39726
39700
|
async download(url) {
|
|
39727
39701
|
try {
|
|
39728
|
-
const identifier = this.
|
|
39729
|
-
const
|
|
39730
|
-
|
|
39731
|
-
headers: {
|
|
39732
|
-
"Content-Type": "application/json"
|
|
39733
|
-
},
|
|
39734
|
-
body: JSON.stringify({ identifier })
|
|
39735
|
-
});
|
|
39736
|
-
if (!response.ok) {
|
|
39737
|
-
const _errorText = await response.text();
|
|
39702
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39703
|
+
const blob = await this.callbacks.download(identifier);
|
|
39704
|
+
if (!(blob instanceof Blob)) {
|
|
39738
39705
|
throw new StorageError(
|
|
39739
|
-
|
|
39740
|
-
"
|
|
39741
|
-
"
|
|
39706
|
+
"Download callback returned invalid result: expected Blob",
|
|
39707
|
+
"INVALID_DOWNLOAD_RESULT",
|
|
39708
|
+
"callback-storage"
|
|
39742
39709
|
);
|
|
39743
39710
|
}
|
|
39744
|
-
return
|
|
39711
|
+
return blob;
|
|
39745
39712
|
} catch (error) {
|
|
39746
39713
|
if (error instanceof StorageError) {
|
|
39747
39714
|
throw error;
|
|
39748
39715
|
}
|
|
39749
39716
|
throw new StorageError(
|
|
39750
|
-
`
|
|
39717
|
+
`Download failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39751
39718
|
"DOWNLOAD_ERROR",
|
|
39752
|
-
"
|
|
39719
|
+
"callback-storage",
|
|
39720
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39753
39721
|
);
|
|
39754
39722
|
}
|
|
39755
39723
|
}
|
|
39756
|
-
|
|
39757
|
-
|
|
39758
|
-
|
|
39759
|
-
|
|
39760
|
-
|
|
39761
|
-
|
|
39762
|
-
|
|
39763
|
-
|
|
39764
|
-
|
|
39765
|
-
|
|
39766
|
-
|
|
39767
|
-
|
|
39768
|
-
|
|
39724
|
+
/**
|
|
39725
|
+
* List files using the provided callback (if available)
|
|
39726
|
+
*
|
|
39727
|
+
* @param options - Optional list options including filters and pagination
|
|
39728
|
+
* @returns Array of storage files
|
|
39729
|
+
*/
|
|
39730
|
+
async list(options) {
|
|
39731
|
+
if (!this.callbacks.list) {
|
|
39732
|
+
throw new StorageError(
|
|
39733
|
+
"List operation not supported - no list callback provided",
|
|
39734
|
+
"NOT_SUPPORTED",
|
|
39735
|
+
"callback-storage"
|
|
39736
|
+
);
|
|
39737
|
+
}
|
|
39738
|
+
try {
|
|
39739
|
+
const result = await this.callbacks.list(options?.namePattern, options);
|
|
39740
|
+
return result.items.map((item, index) => ({
|
|
39741
|
+
id: item.identifier,
|
|
39742
|
+
name: item.identifier.split("/").pop() || `file-${index}`,
|
|
39743
|
+
url: item.identifier,
|
|
39744
|
+
size: item.size || 0,
|
|
39745
|
+
contentType: "application/octet-stream",
|
|
39746
|
+
createdAt: item.lastModified || /* @__PURE__ */ new Date(),
|
|
39747
|
+
metadata: item.metadata
|
|
39748
|
+
}));
|
|
39749
|
+
} catch (error) {
|
|
39750
|
+
throw new StorageError(
|
|
39751
|
+
`List failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39752
|
+
"LIST_ERROR",
|
|
39753
|
+
"callback-storage",
|
|
39754
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39755
|
+
);
|
|
39756
|
+
}
|
|
39769
39757
|
}
|
|
39770
39758
|
/**
|
|
39771
|
-
*
|
|
39759
|
+
* Delete a file using the provided callback (if available)
|
|
39772
39760
|
*
|
|
39773
|
-
* @param url - URL or identifier
|
|
39774
|
-
* @returns
|
|
39761
|
+
* @param url - The URL or identifier to delete
|
|
39762
|
+
* @returns True if deletion succeeded
|
|
39775
39763
|
*/
|
|
39776
|
-
|
|
39777
|
-
|
|
39764
|
+
async delete(url) {
|
|
39765
|
+
if (!this.callbacks.delete) {
|
|
39766
|
+
throw new StorageError(
|
|
39767
|
+
"Delete operation not supported - no delete callback provided",
|
|
39768
|
+
"NOT_SUPPORTED",
|
|
39769
|
+
"callback-storage"
|
|
39770
|
+
);
|
|
39771
|
+
}
|
|
39772
|
+
try {
|
|
39773
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39774
|
+
return await this.callbacks.delete(identifier);
|
|
39775
|
+
} catch (error) {
|
|
39776
|
+
throw new StorageError(
|
|
39777
|
+
`Delete failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39778
|
+
"DELETE_ERROR",
|
|
39779
|
+
"callback-storage",
|
|
39780
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39781
|
+
);
|
|
39782
|
+
}
|
|
39778
39783
|
}
|
|
39784
|
+
/**
|
|
39785
|
+
* Get provider configuration
|
|
39786
|
+
*
|
|
39787
|
+
* @returns Provider configuration metadata
|
|
39788
|
+
*/
|
|
39779
39789
|
getConfig() {
|
|
39780
39790
|
return {
|
|
39781
|
-
name: "
|
|
39782
|
-
type: "
|
|
39791
|
+
name: "callback-storage",
|
|
39792
|
+
type: "callback",
|
|
39783
39793
|
requiresAuth: false,
|
|
39784
39794
|
features: {
|
|
39785
39795
|
upload: true,
|
|
39786
39796
|
download: true,
|
|
39787
|
-
list:
|
|
39788
|
-
delete:
|
|
39797
|
+
list: !!this.callbacks.list,
|
|
39798
|
+
delete: !!this.callbacks.delete
|
|
39789
39799
|
}
|
|
39790
39800
|
};
|
|
39791
39801
|
}
|
|
@@ -40015,6 +40025,47 @@ function getAllChains() {
|
|
|
40015
40025
|
}
|
|
40016
40026
|
|
|
40017
40027
|
// src/core.ts
|
|
40028
|
+
var VanaCoreFactory = class {
|
|
40029
|
+
/**
|
|
40030
|
+
* Creates a VanaCore instance that enforces storage requirements at compile time.
|
|
40031
|
+
* Use this factory when you know you'll need storage-dependent operations.
|
|
40032
|
+
*
|
|
40033
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
40034
|
+
* @param config - Configuration that includes required storage providers
|
|
40035
|
+
* @returns VanaCore instance with storage validation
|
|
40036
|
+
* @example
|
|
40037
|
+
* ```typescript
|
|
40038
|
+
* const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
|
|
40039
|
+
* walletClient: myWalletClient,
|
|
40040
|
+
* storage: {
|
|
40041
|
+
* providers: { ipfs: new IPFSStorage() },
|
|
40042
|
+
* defaultProvider: 'ipfs'
|
|
40043
|
+
* }
|
|
40044
|
+
* });
|
|
40045
|
+
* ```
|
|
40046
|
+
*/
|
|
40047
|
+
static createWithStorage(platform, config) {
|
|
40048
|
+
const core = new VanaCore(platform, config);
|
|
40049
|
+
return core;
|
|
40050
|
+
}
|
|
40051
|
+
/**
|
|
40052
|
+
* Creates a VanaCore instance without storage requirements.
|
|
40053
|
+
* Storage-dependent operations will fail at runtime if not configured.
|
|
40054
|
+
*
|
|
40055
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
40056
|
+
* @param config - Basic configuration without required storage
|
|
40057
|
+
* @returns VanaCore instance
|
|
40058
|
+
* @example
|
|
40059
|
+
* ```typescript
|
|
40060
|
+
* const vanaCore = VanaCoreFactory.create(platformAdapter, {
|
|
40061
|
+
* walletClient: myWalletClient
|
|
40062
|
+
* });
|
|
40063
|
+
* ```
|
|
40064
|
+
*/
|
|
40065
|
+
static create(platform, config) {
|
|
40066
|
+
return new VanaCore(platform, config);
|
|
40067
|
+
}
|
|
40068
|
+
};
|
|
40018
40069
|
var VanaCore = class {
|
|
40019
40070
|
/** Manages gasless data access permissions and trusted server registry. */
|
|
40020
40071
|
permissions;
|
|
@@ -40030,12 +40081,18 @@ var VanaCore = class {
|
|
|
40030
40081
|
platform;
|
|
40031
40082
|
relayerCallbacks;
|
|
40032
40083
|
storageManager;
|
|
40084
|
+
hasRequiredStorage;
|
|
40085
|
+
ipfsGateways;
|
|
40033
40086
|
/**
|
|
40034
40087
|
* Initializes a new VanaCore client instance with the provided configuration.
|
|
40035
40088
|
*
|
|
40036
40089
|
* @remarks
|
|
40037
40090
|
* The constructor validates the configuration, initializes storage providers if configured,
|
|
40038
40091
|
* creates wallet and public clients, and sets up all SDK controllers with shared context.
|
|
40092
|
+
*
|
|
40093
|
+
* IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
|
|
40094
|
+
* Methods that require storage will throw runtime errors if storage is not configured.
|
|
40095
|
+
*
|
|
40039
40096
|
* @param platform - The platform adapter for environment-specific operations
|
|
40040
40097
|
* @param config - The configuration object specifying wallet or chain settings
|
|
40041
40098
|
* @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
|
|
@@ -40051,6 +40108,8 @@ var VanaCore = class {
|
|
|
40051
40108
|
this.platform = platform;
|
|
40052
40109
|
this.validateConfig(config);
|
|
40053
40110
|
this.relayerCallbacks = config.relayerCallbacks;
|
|
40111
|
+
this.ipfsGateways = config.ipfsGateways;
|
|
40112
|
+
this.hasRequiredStorage = hasStorageConfig(config);
|
|
40054
40113
|
if (config.storage?.providers) {
|
|
40055
40114
|
this.storageManager = new StorageManager();
|
|
40056
40115
|
for (const [name, provider] of Object.entries(config.storage.providers)) {
|
|
@@ -40105,8 +40164,11 @@ var VanaCore = class {
|
|
|
40105
40164
|
relayerCallbacks: this.relayerCallbacks,
|
|
40106
40165
|
storageManager: this.storageManager,
|
|
40107
40166
|
subgraphUrl,
|
|
40108
|
-
platform: this.platform
|
|
40167
|
+
platform: this.platform,
|
|
40109
40168
|
// Pass the platform adapter to controllers
|
|
40169
|
+
validateStorageRequired: this.validateStorageRequired.bind(this),
|
|
40170
|
+
hasStorage: this.hasStorage.bind(this),
|
|
40171
|
+
ipfsGateways: this.ipfsGateways
|
|
40110
40172
|
};
|
|
40111
40173
|
this.permissions = new PermissionsController(sharedContext);
|
|
40112
40174
|
this.data = new DataController(sharedContext);
|
|
@@ -40114,6 +40176,58 @@ var VanaCore = class {
|
|
|
40114
40176
|
this.server = new ServerController(sharedContext);
|
|
40115
40177
|
this.protocol = new ProtocolController(sharedContext);
|
|
40116
40178
|
}
|
|
40179
|
+
/**
|
|
40180
|
+
* Validates that storage is available for storage-dependent operations.
|
|
40181
|
+
* This method enforces the fail-fast principle by checking storage availability
|
|
40182
|
+
* at method call time rather than during expensive operations.
|
|
40183
|
+
*
|
|
40184
|
+
* @throws {InvalidConfigurationError} When storage is required but not configured
|
|
40185
|
+
* @example
|
|
40186
|
+
* ```typescript
|
|
40187
|
+
* // This will throw if storage is not configured
|
|
40188
|
+
* vana.validateStorageRequired();
|
|
40189
|
+
* await vana.data.uploadFile(file); // Safe to proceed
|
|
40190
|
+
* ```
|
|
40191
|
+
*/
|
|
40192
|
+
validateStorageRequired() {
|
|
40193
|
+
if (!this.hasRequiredStorage) {
|
|
40194
|
+
throw new InvalidConfigurationError(
|
|
40195
|
+
"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."
|
|
40196
|
+
);
|
|
40197
|
+
}
|
|
40198
|
+
}
|
|
40199
|
+
/**
|
|
40200
|
+
* Checks whether storage is configured without throwing an error.
|
|
40201
|
+
*
|
|
40202
|
+
* @returns True if storage is properly configured
|
|
40203
|
+
* @example
|
|
40204
|
+
* ```typescript
|
|
40205
|
+
* if (vana.hasStorage()) {
|
|
40206
|
+
* await vana.data.uploadFile(file);
|
|
40207
|
+
* } else {
|
|
40208
|
+
* console.warn('Storage not configured - using pre-stored URLs only');
|
|
40209
|
+
* }
|
|
40210
|
+
* ```
|
|
40211
|
+
*/
|
|
40212
|
+
hasStorage() {
|
|
40213
|
+
return this.hasRequiredStorage;
|
|
40214
|
+
}
|
|
40215
|
+
/**
|
|
40216
|
+
* Type guard to check if this instance has storage enabled at compile time.
|
|
40217
|
+
* Use this when you need TypeScript to understand that storage is available.
|
|
40218
|
+
*
|
|
40219
|
+
* @returns True if storage is configured, with type narrowing
|
|
40220
|
+
* @example
|
|
40221
|
+
* ```typescript
|
|
40222
|
+
* if (vana.isStorageEnabled()) {
|
|
40223
|
+
* // TypeScript knows storage is available here
|
|
40224
|
+
* await vana.data.uploadFile(file);
|
|
40225
|
+
* }
|
|
40226
|
+
* ```
|
|
40227
|
+
*/
|
|
40228
|
+
isStorageEnabled() {
|
|
40229
|
+
return this.hasRequiredStorage;
|
|
40230
|
+
}
|
|
40117
40231
|
/**
|
|
40118
40232
|
* Validates the provided configuration object against all requirements.
|
|
40119
40233
|
*
|
|
@@ -40302,23 +40416,23 @@ var VanaCore = class {
|
|
|
40302
40416
|
return this.platform;
|
|
40303
40417
|
}
|
|
40304
40418
|
/**
|
|
40305
|
-
* Encrypts
|
|
40419
|
+
* Encrypts data using the Vana protocol standard encryption.
|
|
40306
40420
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
40307
40421
|
*
|
|
40308
40422
|
* @param data The data to encrypt (string or Blob)
|
|
40309
|
-
* @param
|
|
40423
|
+
* @param key The key to use as encryption key
|
|
40310
40424
|
* @returns The encrypted data as Blob
|
|
40311
40425
|
* @example
|
|
40312
40426
|
* ```typescript
|
|
40313
40427
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
40314
|
-
* const encrypted = await vana.
|
|
40428
|
+
* const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
|
|
40315
40429
|
* ```
|
|
40316
40430
|
*/
|
|
40317
|
-
async
|
|
40318
|
-
return
|
|
40431
|
+
async encryptBlob(data, key) {
|
|
40432
|
+
return encryptBlobWithSignedKey(data, key, this.platform);
|
|
40319
40433
|
}
|
|
40320
40434
|
/**
|
|
40321
|
-
* Decrypts
|
|
40435
|
+
* Decrypts data that was encrypted using the Vana protocol.
|
|
40322
40436
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
40323
40437
|
*
|
|
40324
40438
|
* @param encryptedData The encrypted data (string or Blob)
|
|
@@ -40327,12 +40441,16 @@ var VanaCore = class {
|
|
|
40327
40441
|
* @example
|
|
40328
40442
|
* ```typescript
|
|
40329
40443
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
40330
|
-
* const decrypted = await vana.
|
|
40444
|
+
* const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
|
|
40331
40445
|
* const text = await decrypted.text();
|
|
40332
40446
|
* ```
|
|
40333
40447
|
*/
|
|
40334
|
-
async
|
|
40335
|
-
return
|
|
40448
|
+
async decryptBlob(encryptedData, walletSignature) {
|
|
40449
|
+
return decryptBlobWithSignedKey(
|
|
40450
|
+
encryptedData,
|
|
40451
|
+
walletSignature,
|
|
40452
|
+
this.platform
|
|
40453
|
+
);
|
|
40336
40454
|
}
|
|
40337
40455
|
};
|
|
40338
40456
|
|
|
@@ -40363,7 +40481,7 @@ function createValidatedGrant(params) {
|
|
|
40363
40481
|
try {
|
|
40364
40482
|
validateGrant(grantFile, {
|
|
40365
40483
|
schema: true,
|
|
40366
|
-
grantee: params.
|
|
40484
|
+
grantee: params.grantee,
|
|
40367
40485
|
operation: params.operation
|
|
40368
40486
|
});
|
|
40369
40487
|
} catch (error) {
|
|
@@ -41226,25 +41344,15 @@ var ApiClient = class {
|
|
|
41226
41344
|
};
|
|
41227
41345
|
|
|
41228
41346
|
// src/index.node.ts
|
|
41229
|
-
var
|
|
41230
|
-
/**
|
|
41231
|
-
* Creates a Vana SDK instance configured for Node.js environments.
|
|
41232
|
-
*
|
|
41233
|
-
* @param config - SDK configuration object (wallet client or chain config)
|
|
41234
|
-
* @example
|
|
41235
|
-
* ```typescript
|
|
41236
|
-
* // With wallet client
|
|
41237
|
-
* const vana = new Vana({ walletClient });
|
|
41238
|
-
*
|
|
41239
|
-
* // With chain configuration
|
|
41240
|
-
* const vana = new Vana({ chainId: 14800, account });
|
|
41241
|
-
* ```
|
|
41242
|
-
*/
|
|
41347
|
+
var VanaNodeImpl = class extends VanaCore {
|
|
41243
41348
|
constructor(config) {
|
|
41244
41349
|
super(new NodePlatformAdapter(), config);
|
|
41245
41350
|
}
|
|
41246
41351
|
};
|
|
41247
|
-
|
|
41352
|
+
function Vana(config) {
|
|
41353
|
+
return new VanaNodeImpl(config);
|
|
41354
|
+
}
|
|
41355
|
+
var index_node_default = Vana;
|
|
41248
41356
|
// Annotate the CommonJS export names for ESM import in node:
|
|
41249
41357
|
0 && (module.exports = {
|
|
41250
41358
|
ApiClient,
|
|
@@ -41252,6 +41360,7 @@ var index_node_default = VanaNode;
|
|
|
41252
41360
|
BaseController,
|
|
41253
41361
|
BlockchainError,
|
|
41254
41362
|
BrowserPlatformAdapter,
|
|
41363
|
+
CallbackStorage,
|
|
41255
41364
|
CircuitBreaker,
|
|
41256
41365
|
ContractFactory,
|
|
41257
41366
|
ContractNotFoundError,
|
|
@@ -41286,7 +41395,6 @@ var index_node_default = VanaNode;
|
|
|
41286
41395
|
SchemaValidator,
|
|
41287
41396
|
SerializationError,
|
|
41288
41397
|
ServerController,
|
|
41289
|
-
ServerProxyStorage,
|
|
41290
41398
|
ServerUrlMismatchError,
|
|
41291
41399
|
SignatureError,
|
|
41292
41400
|
StorageError,
|
|
@@ -41294,8 +41402,9 @@ var index_node_default = VanaNode;
|
|
|
41294
41402
|
UserRejectedRequestError,
|
|
41295
41403
|
Vana,
|
|
41296
41404
|
VanaCore,
|
|
41405
|
+
VanaCoreFactory,
|
|
41297
41406
|
VanaError,
|
|
41298
|
-
|
|
41407
|
+
VanaNodeImpl,
|
|
41299
41408
|
__contractCache,
|
|
41300
41409
|
chains,
|
|
41301
41410
|
checkGrantAccess,
|
|
@@ -41310,12 +41419,12 @@ var index_node_default = VanaNode;
|
|
|
41310
41419
|
createPlatformAdapterFor,
|
|
41311
41420
|
createPlatformAdapterSafe,
|
|
41312
41421
|
createValidatedGrant,
|
|
41313
|
-
|
|
41422
|
+
decryptBlobWithSignedKey,
|
|
41314
41423
|
decryptWithPrivateKey,
|
|
41315
41424
|
decryptWithWalletPrivateKey,
|
|
41316
41425
|
detectPlatform,
|
|
41426
|
+
encryptBlobWithSignedKey,
|
|
41317
41427
|
encryptFileKey,
|
|
41318
|
-
encryptUserData,
|
|
41319
41428
|
encryptWithWalletPublicKey,
|
|
41320
41429
|
extractIpfsHash,
|
|
41321
41430
|
fetchAndValidateSchema,
|
|
@@ -41340,9 +41449,7 @@ var index_node_default = VanaNode;
|
|
|
41340
41449
|
handleRelayerRequest,
|
|
41341
41450
|
isAPIResponse,
|
|
41342
41451
|
isGrantExpired,
|
|
41343
|
-
isIdentityServerOutput,
|
|
41344
41452
|
isIpfsUrl,
|
|
41345
|
-
isPersonalServerOutput,
|
|
41346
41453
|
isPlatformSupported,
|
|
41347
41454
|
isReplicateAPIResponse,
|
|
41348
41455
|
moksha,
|