@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.browser.js
CHANGED
|
@@ -141,65 +141,9 @@ var init_browser = __esm({
|
|
|
141
141
|
try {
|
|
142
142
|
const eccrypto = await import("eccrypto-js");
|
|
143
143
|
const uncompressedKey = processWalletPublicKey(publicKey);
|
|
144
|
-
const iv = Buffer.from([
|
|
145
|
-
1,
|
|
146
|
-
2,
|
|
147
|
-
3,
|
|
148
|
-
4,
|
|
149
|
-
5,
|
|
150
|
-
6,
|
|
151
|
-
7,
|
|
152
|
-
8,
|
|
153
|
-
9,
|
|
154
|
-
10,
|
|
155
|
-
11,
|
|
156
|
-
12,
|
|
157
|
-
13,
|
|
158
|
-
14,
|
|
159
|
-
15,
|
|
160
|
-
16
|
|
161
|
-
]);
|
|
162
|
-
const ephemeralKey = Buffer.from([
|
|
163
|
-
17,
|
|
164
|
-
34,
|
|
165
|
-
51,
|
|
166
|
-
68,
|
|
167
|
-
85,
|
|
168
|
-
102,
|
|
169
|
-
119,
|
|
170
|
-
136,
|
|
171
|
-
153,
|
|
172
|
-
170,
|
|
173
|
-
187,
|
|
174
|
-
204,
|
|
175
|
-
221,
|
|
176
|
-
238,
|
|
177
|
-
255,
|
|
178
|
-
0,
|
|
179
|
-
16,
|
|
180
|
-
32,
|
|
181
|
-
48,
|
|
182
|
-
64,
|
|
183
|
-
80,
|
|
184
|
-
96,
|
|
185
|
-
112,
|
|
186
|
-
128,
|
|
187
|
-
144,
|
|
188
|
-
160,
|
|
189
|
-
176,
|
|
190
|
-
192,
|
|
191
|
-
208,
|
|
192
|
-
224,
|
|
193
|
-
240,
|
|
194
|
-
0
|
|
195
|
-
]);
|
|
196
144
|
const encryptedBuffer = await eccrypto.encrypt(
|
|
197
145
|
uncompressedKey,
|
|
198
|
-
Buffer.from(data)
|
|
199
|
-
{
|
|
200
|
-
iv,
|
|
201
|
-
ephemPrivateKey: ephemeralKey
|
|
202
|
-
}
|
|
146
|
+
Buffer.from(data)
|
|
203
147
|
);
|
|
204
148
|
const result = Buffer.concat([
|
|
205
149
|
encryptedBuffer.iv,
|
|
@@ -446,6 +390,9 @@ function isWalletConfig(config) {
|
|
|
446
390
|
function isChainConfig(config) {
|
|
447
391
|
return "chainId" in config && !("walletClient" in config);
|
|
448
392
|
}
|
|
393
|
+
function hasStorageConfig(config) {
|
|
394
|
+
return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
|
|
395
|
+
}
|
|
449
396
|
|
|
450
397
|
// src/types/chains.ts
|
|
451
398
|
function isVanaChainId(chainId) {
|
|
@@ -741,55 +688,6 @@ function isReplicateAPIResponse(value) {
|
|
|
741
688
|
obj.status
|
|
742
689
|
);
|
|
743
690
|
}
|
|
744
|
-
function isIdentityServerOutput(value) {
|
|
745
|
-
console.debug("\u{1F50D} Type Guard: Checking value:", value);
|
|
746
|
-
console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
|
|
747
|
-
if (typeof value !== "object" || value === null) {
|
|
748
|
-
console.debug("\u{1F50D} Type Guard: Failed - not object or null");
|
|
749
|
-
return false;
|
|
750
|
-
}
|
|
751
|
-
const obj = value;
|
|
752
|
-
console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
|
|
753
|
-
console.debug(
|
|
754
|
-
"\u{1F50D} Type Guard: user_address:",
|
|
755
|
-
obj.user_address,
|
|
756
|
-
typeof obj.user_address
|
|
757
|
-
);
|
|
758
|
-
if (typeof obj.user_address !== "string") {
|
|
759
|
-
console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
|
|
760
|
-
return false;
|
|
761
|
-
}
|
|
762
|
-
console.debug(
|
|
763
|
-
"\u{1F50D} Type Guard: personal_server:",
|
|
764
|
-
obj.personal_server,
|
|
765
|
-
typeof obj.personal_server
|
|
766
|
-
);
|
|
767
|
-
if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
|
|
768
|
-
console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
|
|
769
|
-
return false;
|
|
770
|
-
}
|
|
771
|
-
const personalServer = obj.personal_server;
|
|
772
|
-
console.debug(
|
|
773
|
-
"\u{1F50D} Type Guard: Personal server keys:",
|
|
774
|
-
Object.keys(personalServer)
|
|
775
|
-
);
|
|
776
|
-
console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
|
|
777
|
-
console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
|
|
778
|
-
const hasAddress = "address" in personalServer;
|
|
779
|
-
const hasPublicKey = "public_key" in personalServer;
|
|
780
|
-
console.debug(
|
|
781
|
-
"\u{1F50D} Type Guard: Has address:",
|
|
782
|
-
hasAddress,
|
|
783
|
-
"Has public_key:",
|
|
784
|
-
hasPublicKey
|
|
785
|
-
);
|
|
786
|
-
return hasAddress && hasPublicKey;
|
|
787
|
-
}
|
|
788
|
-
function isPersonalServerOutput(value) {
|
|
789
|
-
if (typeof value !== "object" || value === null) return false;
|
|
790
|
-
const obj = value;
|
|
791
|
-
return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
|
|
792
|
-
}
|
|
793
691
|
function isAPIResponse(value) {
|
|
794
692
|
if (typeof value !== "object" || value === null) return false;
|
|
795
693
|
const obj = value;
|
|
@@ -33382,7 +33280,7 @@ function getAbi(contract) {
|
|
|
33382
33280
|
import { keccak256, toHex } from "viem";
|
|
33383
33281
|
function createGrantFile(params) {
|
|
33384
33282
|
const grantFile = {
|
|
33385
|
-
grantee: params.
|
|
33283
|
+
grantee: params.grantee,
|
|
33386
33284
|
operation: params.operation,
|
|
33387
33285
|
parameters: params.parameters
|
|
33388
33286
|
};
|
|
@@ -33802,7 +33700,7 @@ var PermissionsController = class {
|
|
|
33802
33700
|
* @example
|
|
33803
33701
|
* ```typescript
|
|
33804
33702
|
* const txHash = await vana.permissions.grant({
|
|
33805
|
-
*
|
|
33703
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
33806
33704
|
* operation: "llm_inference",
|
|
33807
33705
|
* parameters: {
|
|
33808
33706
|
* model: "gpt-4",
|
|
@@ -33831,7 +33729,7 @@ var PermissionsController = class {
|
|
|
33831
33729
|
* @example
|
|
33832
33730
|
* ```typescript
|
|
33833
33731
|
* const { preview, confirm } = await vana.permissions.prepareGrant({
|
|
33834
|
-
*
|
|
33732
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
33835
33733
|
* operation: "llm_inference",
|
|
33836
33734
|
* files: [1, 2, 3],
|
|
33837
33735
|
* parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
|
|
@@ -33880,9 +33778,13 @@ var PermissionsController = class {
|
|
|
33880
33778
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
33881
33779
|
if (!grantUrl) {
|
|
33882
33780
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
33883
|
-
|
|
33884
|
-
|
|
33885
|
-
|
|
33781
|
+
if (this.context.validateStorageRequired) {
|
|
33782
|
+
this.context.validateStorageRequired();
|
|
33783
|
+
} else {
|
|
33784
|
+
throw new Error(
|
|
33785
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
33786
|
+
);
|
|
33787
|
+
}
|
|
33886
33788
|
}
|
|
33887
33789
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
33888
33790
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -33906,7 +33808,7 @@ var PermissionsController = class {
|
|
|
33906
33808
|
grantUrl
|
|
33907
33809
|
);
|
|
33908
33810
|
const typedData = await this.composePermissionGrantMessage({
|
|
33909
|
-
|
|
33811
|
+
grantee: params.grantee,
|
|
33910
33812
|
operation: params.operation,
|
|
33911
33813
|
// Placeholder - real data is in IPFS
|
|
33912
33814
|
files: params.files,
|
|
@@ -33953,7 +33855,7 @@ var PermissionsController = class {
|
|
|
33953
33855
|
* @example
|
|
33954
33856
|
* ```typescript
|
|
33955
33857
|
* const { typedData, signature } = await vana.permissions.createAndSign({
|
|
33956
|
-
*
|
|
33858
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
33957
33859
|
* operation: "data_analysis",
|
|
33958
33860
|
* parameters: { analysisType: "sentiment" },
|
|
33959
33861
|
* });
|
|
@@ -33969,9 +33871,13 @@ var PermissionsController = class {
|
|
|
33969
33871
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
33970
33872
|
if (!grantUrl) {
|
|
33971
33873
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
33972
|
-
|
|
33973
|
-
|
|
33974
|
-
|
|
33874
|
+
if (this.context.validateStorageRequired) {
|
|
33875
|
+
this.context.validateStorageRequired();
|
|
33876
|
+
} else {
|
|
33877
|
+
throw new Error(
|
|
33878
|
+
"No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
|
|
33879
|
+
);
|
|
33880
|
+
}
|
|
33975
33881
|
}
|
|
33976
33882
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
33977
33883
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -33995,7 +33901,7 @@ var PermissionsController = class {
|
|
|
33995
33901
|
grantUrl
|
|
33996
33902
|
);
|
|
33997
33903
|
const typedData = await this.composePermissionGrantMessage({
|
|
33998
|
-
|
|
33904
|
+
grantee: params.grantee,
|
|
33999
33905
|
operation: params.operation,
|
|
34000
33906
|
// Placeholder - real data is in IPFS
|
|
34001
33907
|
files: params.files,
|
|
@@ -34346,7 +34252,7 @@ var PermissionsController = class {
|
|
|
34346
34252
|
* Composes the EIP-712 typed data for PermissionGrant (new simplified format).
|
|
34347
34253
|
*
|
|
34348
34254
|
* @param params - The parameters for composing the permission grant message
|
|
34349
|
-
* @param params.
|
|
34255
|
+
* @param params.grantee - The recipient address for the permission grant
|
|
34350
34256
|
* @param params.operation - The type of operation being granted permission for
|
|
34351
34257
|
* @param params.files - Array of file IDs that the permission applies to
|
|
34352
34258
|
* @param params.grantUrl - URL where the grant details are stored
|
|
@@ -34439,36 +34345,45 @@ var PermissionsController = class {
|
|
|
34439
34345
|
return addresses[0];
|
|
34440
34346
|
}
|
|
34441
34347
|
/**
|
|
34442
|
-
*
|
|
34348
|
+
* Gets on-chain permission grant data without expensive off-chain resolution.
|
|
34443
34349
|
*
|
|
34444
34350
|
* @remarks
|
|
34445
|
-
* This method
|
|
34446
|
-
*
|
|
34447
|
-
*
|
|
34448
|
-
*
|
|
34449
|
-
*
|
|
34450
|
-
*
|
|
34451
|
-
*
|
|
34452
|
-
*
|
|
34453
|
-
*
|
|
34454
|
-
*
|
|
34351
|
+
* This method provides a fast, performance-focused way to retrieve permission grants
|
|
34352
|
+
* by querying only the subgraph without making expensive IPFS or individual contract calls.
|
|
34353
|
+
* It eliminates the N+1 query problem of the legacy `getUserPermissions()` method.
|
|
34354
|
+
*
|
|
34355
|
+
* The returned data contains all on-chain information but does NOT include resolved
|
|
34356
|
+
* operation details, parameters, or file IDs. Use `retrieveGrantFile()` separately
|
|
34357
|
+
* for specific grants when detailed data is needed.
|
|
34358
|
+
*
|
|
34359
|
+
* **Performance**: Completes in ~100-500ms regardless of permission count.
|
|
34360
|
+
* **Reliability**: Single point of failure (subgraph) with clear RPC fallback path.
|
|
34361
|
+
*
|
|
34362
|
+
* @param options - Options for retrieving permissions (limit, subgraph URL)
|
|
34363
|
+
* @returns A Promise that resolves to an array of `OnChainPermissionGrant` objects
|
|
34364
|
+
* @throws {BlockchainError} When subgraph query fails
|
|
34365
|
+
* @throws {NetworkError} When network requests fail
|
|
34455
34366
|
* @example
|
|
34456
34367
|
* ```typescript
|
|
34457
|
-
* // Get all
|
|
34458
|
-
* const
|
|
34368
|
+
* // Fast: Get all on-chain permission data
|
|
34369
|
+
* const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
|
|
34459
34370
|
*
|
|
34460
|
-
*
|
|
34461
|
-
*
|
|
34371
|
+
* // Display in UI immediately
|
|
34372
|
+
* grants.forEach(grant => {
|
|
34373
|
+
* console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
|
|
34462
34374
|
* });
|
|
34463
34375
|
*
|
|
34464
|
-
* //
|
|
34465
|
-
* const
|
|
34376
|
+
* // Lazy load detailed data for specific permission when user clicks
|
|
34377
|
+
* const grantFile = await retrieveGrantFile(grants[0].grantUrl);
|
|
34378
|
+
* console.log(`Operation: ${grantFile.operation}`);
|
|
34379
|
+
* console.log(`Parameters:`, grantFile.parameters);
|
|
34466
34380
|
* ```
|
|
34467
34381
|
*/
|
|
34468
|
-
async
|
|
34382
|
+
async getUserPermissionGrantsOnChain(options = {}) {
|
|
34383
|
+
const { limit = 50, subgraphUrl } = options;
|
|
34469
34384
|
try {
|
|
34470
34385
|
const userAddress = await this.getUserAddress();
|
|
34471
|
-
const graphqlEndpoint =
|
|
34386
|
+
const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
|
|
34472
34387
|
if (!graphqlEndpoint) {
|
|
34473
34388
|
throw new BlockchainError(
|
|
34474
34389
|
"subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
|
|
@@ -34490,16 +34405,6 @@ var PermissionsController = class {
|
|
|
34490
34405
|
}
|
|
34491
34406
|
}
|
|
34492
34407
|
`;
|
|
34493
|
-
console.info("Query:", query);
|
|
34494
|
-
console.info(
|
|
34495
|
-
"Body:",
|
|
34496
|
-
JSON.stringify({
|
|
34497
|
-
query,
|
|
34498
|
-
variables: {
|
|
34499
|
-
userId: userAddress.toLowerCase()
|
|
34500
|
-
}
|
|
34501
|
-
})
|
|
34502
|
-
);
|
|
34503
34408
|
const response = await fetch(graphqlEndpoint, {
|
|
34504
34409
|
method: "POST",
|
|
34505
34410
|
headers: {
|
|
@@ -34518,7 +34423,6 @@ var PermissionsController = class {
|
|
|
34518
34423
|
);
|
|
34519
34424
|
}
|
|
34520
34425
|
const result = await response.json();
|
|
34521
|
-
console.info("Result:", result);
|
|
34522
34426
|
if (result.errors) {
|
|
34523
34427
|
throw new BlockchainError(
|
|
34524
34428
|
`Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
|
|
@@ -34526,64 +34430,32 @@ var PermissionsController = class {
|
|
|
34526
34430
|
}
|
|
34527
34431
|
const userData = result.data?.user;
|
|
34528
34432
|
if (!userData || !userData.permissions?.length) {
|
|
34529
|
-
console.warn("No permissions found for user:", userAddress);
|
|
34530
34433
|
return [];
|
|
34531
34434
|
}
|
|
34532
|
-
const
|
|
34533
|
-
|
|
34534
|
-
|
|
34535
|
-
|
|
34536
|
-
|
|
34537
|
-
|
|
34538
|
-
|
|
34539
|
-
|
|
34540
|
-
|
|
34541
|
-
|
|
34542
|
-
|
|
34543
|
-
|
|
34544
|
-
|
|
34545
|
-
|
|
34546
|
-
} catch {
|
|
34547
|
-
}
|
|
34548
|
-
try {
|
|
34549
|
-
const fileIds = await this.getPermissionFileIds(
|
|
34550
|
-
BigInt(permission.id)
|
|
34551
|
-
);
|
|
34552
|
-
files = fileIds.map((id) => Number(id));
|
|
34553
|
-
} catch {
|
|
34554
|
-
}
|
|
34555
|
-
userPermissions.push({
|
|
34556
|
-
id: BigInt(permission.id),
|
|
34557
|
-
files,
|
|
34558
|
-
operation: operation || "",
|
|
34559
|
-
parameters: parameters || {},
|
|
34560
|
-
grant: permission.grant,
|
|
34561
|
-
grantor: userAddress.toLowerCase(),
|
|
34562
|
-
// Current user is the grantor
|
|
34563
|
-
grantee: granteeAddress || userAddress,
|
|
34564
|
-
// Application that received permission
|
|
34565
|
-
active: true,
|
|
34566
|
-
// Default to active if not specified
|
|
34567
|
-
grantedAt: Number(permission.addedAtBlock),
|
|
34568
|
-
nonce: Number(permission.nonce)
|
|
34569
|
-
});
|
|
34570
|
-
} catch (error) {
|
|
34571
|
-
console.error(
|
|
34572
|
-
"SDK Error: Failed to process permission:",
|
|
34573
|
-
permission.id,
|
|
34574
|
-
error
|
|
34575
|
-
);
|
|
34576
|
-
}
|
|
34577
|
-
}
|
|
34578
|
-
return userPermissions.sort((a, b) => {
|
|
34435
|
+
const onChainGrants = userData.permissions.slice(0, limit).map((permission) => ({
|
|
34436
|
+
id: BigInt(permission.id),
|
|
34437
|
+
grantUrl: permission.grant,
|
|
34438
|
+
grantSignature: permission.grantSignature,
|
|
34439
|
+
grantHash: permission.grantHash,
|
|
34440
|
+
nonce: BigInt(permission.nonce),
|
|
34441
|
+
addedAtBlock: BigInt(permission.addedAtBlock),
|
|
34442
|
+
addedAtTimestamp: BigInt(permission.addedAtTimestamp || "0"),
|
|
34443
|
+
transactionHash: permission.transactionHash || "",
|
|
34444
|
+
grantor: userAddress,
|
|
34445
|
+
active: true
|
|
34446
|
+
// TODO: Add revocation status from subgraph when available
|
|
34447
|
+
}));
|
|
34448
|
+
return onChainGrants.sort((a, b) => {
|
|
34579
34449
|
if (a.id < b.id) return 1;
|
|
34580
34450
|
if (a.id > b.id) return -1;
|
|
34581
34451
|
return 0;
|
|
34582
34452
|
});
|
|
34583
34453
|
} catch (error) {
|
|
34584
|
-
|
|
34454
|
+
if (error instanceof BlockchainError || error instanceof NetworkError) {
|
|
34455
|
+
throw error;
|
|
34456
|
+
}
|
|
34585
34457
|
throw new BlockchainError(
|
|
34586
|
-
`Failed to fetch user
|
|
34458
|
+
`Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
34587
34459
|
);
|
|
34588
34460
|
}
|
|
34589
34461
|
}
|
|
@@ -35345,32 +35217,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
|
|
|
35345
35217
|
throw new Error(`Failed to decrypt with private key: ${error}`);
|
|
35346
35218
|
}
|
|
35347
35219
|
}
|
|
35348
|
-
async function
|
|
35220
|
+
async function encryptBlobWithSignedKey(data, key, platformAdapter) {
|
|
35349
35221
|
try {
|
|
35350
35222
|
const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
|
|
35351
35223
|
const dataArray = new Uint8Array(dataBuffer);
|
|
35352
35224
|
const encrypted = await platformAdapter.crypto.encryptWithPassword(
|
|
35353
35225
|
dataArray,
|
|
35354
|
-
|
|
35226
|
+
key
|
|
35355
35227
|
);
|
|
35356
35228
|
return new Blob([encrypted], {
|
|
35357
35229
|
type: "application/octet-stream"
|
|
35358
35230
|
});
|
|
35359
35231
|
} catch (error) {
|
|
35360
|
-
throw new Error(`Failed to encrypt
|
|
35361
|
-
}
|
|
35362
|
-
}
|
|
35363
|
-
async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
|
|
35364
|
-
try {
|
|
35365
|
-
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35366
|
-
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35367
|
-
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35368
|
-
encryptedArray,
|
|
35369
|
-
walletSignature
|
|
35370
|
-
);
|
|
35371
|
-
return new Blob([decrypted], { type: "text/plain" });
|
|
35372
|
-
} catch (error) {
|
|
35373
|
-
throw new Error(`Failed to decrypt user data: ${error}`);
|
|
35232
|
+
throw new Error(`Failed to encrypt data: ${error}`);
|
|
35374
35233
|
}
|
|
35375
35234
|
}
|
|
35376
35235
|
async function generateEncryptionKeyPair(platformAdapter) {
|
|
@@ -35387,60 +35246,25 @@ async function generatePGPKeyPair(platformAdapter, options) {
|
|
|
35387
35246
|
throw new Error(`Failed to generate PGP key pair: ${error}`);
|
|
35388
35247
|
}
|
|
35389
35248
|
}
|
|
35249
|
+
async function decryptBlobWithSignedKey(encryptedData, key, platformAdapter) {
|
|
35250
|
+
try {
|
|
35251
|
+
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
35252
|
+
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
35253
|
+
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
35254
|
+
encryptedArray,
|
|
35255
|
+
key
|
|
35256
|
+
);
|
|
35257
|
+
return new Blob([decrypted], { type: "text/plain" });
|
|
35258
|
+
} catch (error) {
|
|
35259
|
+
throw new Error(`Failed to decrypt data: ${error}`);
|
|
35260
|
+
}
|
|
35261
|
+
}
|
|
35390
35262
|
|
|
35391
35263
|
// src/controllers/data.ts
|
|
35392
35264
|
var DataController = class {
|
|
35393
35265
|
constructor(context) {
|
|
35394
35266
|
this.context = context;
|
|
35395
35267
|
}
|
|
35396
|
-
/**
|
|
35397
|
-
* Uploads user data with automatic encryption and blockchain registration.
|
|
35398
|
-
*
|
|
35399
|
-
* @remarks
|
|
35400
|
-
* This is the primary method for uploading user data to the Vana network. It handles
|
|
35401
|
-
* the complete workflow including content normalization, schema validation, encryption,
|
|
35402
|
-
* storage upload, permission granting, and blockchain registration.
|
|
35403
|
-
*
|
|
35404
|
-
* The method automatically:
|
|
35405
|
-
* - Normalizes input content to a Blob
|
|
35406
|
-
* - Validates data against schema if provided
|
|
35407
|
-
* - Generates encryption keys and encrypts the data
|
|
35408
|
-
* - Uploads to the configured storage provider
|
|
35409
|
-
* - Grants permissions to specified applications
|
|
35410
|
-
* - Registers the file on the blockchain
|
|
35411
|
-
*
|
|
35412
|
-
* @param params - Upload parameters including content, filename, schema, and permissions
|
|
35413
|
-
* @returns Promise resolving to upload results with file ID and transaction hash
|
|
35414
|
-
* @throws {Error} When wallet is not connected or storage is not configured
|
|
35415
|
-
* @throws {SchemaValidationError} When schema validation fails
|
|
35416
|
-
* @throws {Error} When upload or blockchain registration fails
|
|
35417
|
-
* @example
|
|
35418
|
-
* ```typescript
|
|
35419
|
-
* // Basic file upload
|
|
35420
|
-
* const result = await vana.data.upload({
|
|
35421
|
-
* content: "My personal data",
|
|
35422
|
-
* filename: "diary.txt"
|
|
35423
|
-
* });
|
|
35424
|
-
*
|
|
35425
|
-
* // Upload with schema validation
|
|
35426
|
-
* const result = await vana.data.upload({
|
|
35427
|
-
* content: { name: "John", age: 30 },
|
|
35428
|
-
* filename: "profile.json",
|
|
35429
|
-
* schemaId: 1
|
|
35430
|
-
* });
|
|
35431
|
-
*
|
|
35432
|
-
* // Upload with permissions
|
|
35433
|
-
* const result = await vana.data.upload({
|
|
35434
|
-
* content: "Data for AI analysis",
|
|
35435
|
-
* filename: "analysis.txt",
|
|
35436
|
-
* permissions: [{
|
|
35437
|
-
* to: "0x1234...",
|
|
35438
|
-
* operation: "llm_inference",
|
|
35439
|
-
* parameters: { model: "gpt-4" }
|
|
35440
|
-
* }]
|
|
35441
|
-
* });
|
|
35442
|
-
* ```
|
|
35443
|
-
*/
|
|
35444
35268
|
async upload(params) {
|
|
35445
35269
|
const {
|
|
35446
35270
|
content,
|
|
@@ -35448,7 +35272,8 @@ var DataController = class {
|
|
|
35448
35272
|
schemaId,
|
|
35449
35273
|
permissions = [],
|
|
35450
35274
|
encrypt: encrypt2 = true,
|
|
35451
|
-
providerName
|
|
35275
|
+
providerName,
|
|
35276
|
+
owner
|
|
35452
35277
|
} = params;
|
|
35453
35278
|
try {
|
|
35454
35279
|
let blob;
|
|
@@ -35505,23 +35330,28 @@ var DataController = class {
|
|
|
35505
35330
|
this.context.walletClient,
|
|
35506
35331
|
DEFAULT_ENCRYPTION_SEED
|
|
35507
35332
|
);
|
|
35508
|
-
finalBlob = await
|
|
35333
|
+
finalBlob = await encryptBlobWithSignedKey(
|
|
35509
35334
|
blob,
|
|
35510
35335
|
encryptionKey,
|
|
35511
35336
|
this.context.platform
|
|
35512
35337
|
);
|
|
35513
35338
|
}
|
|
35514
35339
|
if (!this.context.storageManager) {
|
|
35515
|
-
|
|
35516
|
-
|
|
35517
|
-
|
|
35340
|
+
if (this.context.validateStorageRequired) {
|
|
35341
|
+
this.context.validateStorageRequired();
|
|
35342
|
+
throw new Error("Storage validation failed");
|
|
35343
|
+
} else {
|
|
35344
|
+
throw new Error(
|
|
35345
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
35346
|
+
);
|
|
35347
|
+
}
|
|
35518
35348
|
}
|
|
35519
35349
|
const uploadResult = await this.context.storageManager.upload(
|
|
35520
35350
|
finalBlob,
|
|
35521
35351
|
filename,
|
|
35522
35352
|
providerName
|
|
35523
35353
|
);
|
|
35524
|
-
const userAddress = await this.getUserAddress();
|
|
35354
|
+
const userAddress = owner || await this.getUserAddress();
|
|
35525
35355
|
let encryptedPermissions = [];
|
|
35526
35356
|
if (permissions.length > 0 && encrypt2) {
|
|
35527
35357
|
const userEncryptionKey = await generateEncryptionKey(
|
|
@@ -35533,7 +35363,7 @@ var DataController = class {
|
|
|
35533
35363
|
const publicKey = permission.publicKey;
|
|
35534
35364
|
if (!publicKey) {
|
|
35535
35365
|
throw new Error(
|
|
35536
|
-
`Public key required for permission to ${permission.
|
|
35366
|
+
`Public key required for permission to ${permission.grantee} when encryption is enabled`
|
|
35537
35367
|
);
|
|
35538
35368
|
}
|
|
35539
35369
|
const encryptedKey = await encryptWithWalletPublicKey(
|
|
@@ -35542,7 +35372,7 @@ var DataController = class {
|
|
|
35542
35372
|
this.context.platform
|
|
35543
35373
|
);
|
|
35544
35374
|
return {
|
|
35545
|
-
account: permission.
|
|
35375
|
+
account: permission.grantee,
|
|
35546
35376
|
key: encryptedKey
|
|
35547
35377
|
};
|
|
35548
35378
|
})
|
|
@@ -35555,7 +35385,8 @@ var DataController = class {
|
|
|
35555
35385
|
url: uploadResult.url,
|
|
35556
35386
|
userAddress,
|
|
35557
35387
|
permissions: encryptedPermissions,
|
|
35558
|
-
schemaId: schemaId || 0
|
|
35388
|
+
schemaId: schemaId || 0,
|
|
35389
|
+
ownerAddress: owner
|
|
35559
35390
|
}
|
|
35560
35391
|
);
|
|
35561
35392
|
} else if (this.context.relayerCallbacks?.submitFileAddition) {
|
|
@@ -35591,6 +35422,142 @@ var DataController = class {
|
|
|
35591
35422
|
);
|
|
35592
35423
|
}
|
|
35593
35424
|
}
|
|
35425
|
+
/**
|
|
35426
|
+
* Decrypts a file owned by the user using their wallet signature.
|
|
35427
|
+
*
|
|
35428
|
+
* @remarks
|
|
35429
|
+
* This is the high-level convenience method for decrypting user files, serving as the
|
|
35430
|
+
* symmetrical counterpart to the `upload` method. It handles the complete decryption
|
|
35431
|
+
* workflow including key generation, URL protocol detection, content fetching, and
|
|
35432
|
+
* decryption.
|
|
35433
|
+
*
|
|
35434
|
+
* The method automatically:
|
|
35435
|
+
* - Generates the decryption key from the user's wallet signature
|
|
35436
|
+
* - Determines the appropriate fetch method based on the file URL protocol
|
|
35437
|
+
* - Fetches the encrypted content from IPFS or standard HTTP URLs
|
|
35438
|
+
* - Decrypts the content using the generated key
|
|
35439
|
+
*
|
|
35440
|
+
* For IPFS URLs, the method uses gateway fallback for improved reliability. For
|
|
35441
|
+
* standard HTTP URLs, it uses a simple fetch. If you need custom authentication
|
|
35442
|
+
* headers or specific gateway configurations, use the low-level primitives directly.
|
|
35443
|
+
*
|
|
35444
|
+
* @param file - The user file to decrypt (typically from getUserFiles)
|
|
35445
|
+
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
35446
|
+
* @returns Promise resolving to the decrypted file content as a Blob
|
|
35447
|
+
* @throws {Error} When the wallet is not connected
|
|
35448
|
+
* @throws {Error} When fetching the encrypted content fails
|
|
35449
|
+
* @throws {Error} When decryption fails (wrong key or corrupted data)
|
|
35450
|
+
* @example
|
|
35451
|
+
* ```typescript
|
|
35452
|
+
* // Basic file decryption
|
|
35453
|
+
* const files = await vana.data.getUserFiles({ owner: userAddress });
|
|
35454
|
+
* const decryptedBlob = await vana.data.decryptFile(files[0]);
|
|
35455
|
+
*
|
|
35456
|
+
* // Convert to text
|
|
35457
|
+
* const text = await decryptedBlob.text();
|
|
35458
|
+
* console.log('Decrypted content:', text);
|
|
35459
|
+
*
|
|
35460
|
+
* // Convert to JSON
|
|
35461
|
+
* const json = JSON.parse(await decryptedBlob.text());
|
|
35462
|
+
* console.log('Decrypted data:', json);
|
|
35463
|
+
*
|
|
35464
|
+
* // With custom encryption seed
|
|
35465
|
+
* const decryptedBlob = await vana.data.decryptFile(
|
|
35466
|
+
* files[0],
|
|
35467
|
+
* "My custom encryption seed"
|
|
35468
|
+
* );
|
|
35469
|
+
*
|
|
35470
|
+
* // Save to file (in Node.js)
|
|
35471
|
+
* const buffer = await decryptedBlob.arrayBuffer();
|
|
35472
|
+
* fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
|
|
35473
|
+
* ```
|
|
35474
|
+
*/
|
|
35475
|
+
async decryptFile(file, encryptionSeed) {
|
|
35476
|
+
try {
|
|
35477
|
+
const encryptionKey = await generateEncryptionKey(
|
|
35478
|
+
this.context.walletClient,
|
|
35479
|
+
encryptionSeed || DEFAULT_ENCRYPTION_SEED
|
|
35480
|
+
);
|
|
35481
|
+
let encryptedBlob;
|
|
35482
|
+
try {
|
|
35483
|
+
if (file.url.startsWith("ipfs://")) {
|
|
35484
|
+
encryptedBlob = await this.fetchFromIPFS(file.url);
|
|
35485
|
+
} else {
|
|
35486
|
+
encryptedBlob = await this.fetch(file.url);
|
|
35487
|
+
}
|
|
35488
|
+
} catch (fetchError) {
|
|
35489
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
|
|
35490
|
+
if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
|
|
35491
|
+
throw new Error(
|
|
35492
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35493
|
+
);
|
|
35494
|
+
} else if (errorMessage.includes("Empty response")) {
|
|
35495
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35496
|
+
} else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
|
|
35497
|
+
throw new Error(
|
|
35498
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35499
|
+
);
|
|
35500
|
+
} else if (errorMessage.includes("HTTP error!")) {
|
|
35501
|
+
const statusMatch = errorMessage.match(/status: (\d+)/);
|
|
35502
|
+
const status = statusMatch ? statusMatch[1] : "unknown";
|
|
35503
|
+
if (status === "500") {
|
|
35504
|
+
throw new Error(
|
|
35505
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35506
|
+
);
|
|
35507
|
+
} else if (status === "403") {
|
|
35508
|
+
throw new Error(
|
|
35509
|
+
"Access denied. You may not have permission to access this file"
|
|
35510
|
+
);
|
|
35511
|
+
} else if (status === "404") {
|
|
35512
|
+
throw new Error(
|
|
35513
|
+
"File not found: The encrypted file is no longer available at the stored URL."
|
|
35514
|
+
);
|
|
35515
|
+
} else {
|
|
35516
|
+
throw new Error(
|
|
35517
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35518
|
+
);
|
|
35519
|
+
}
|
|
35520
|
+
}
|
|
35521
|
+
throw fetchError;
|
|
35522
|
+
}
|
|
35523
|
+
if (encryptedBlob.size === 0) {
|
|
35524
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35525
|
+
}
|
|
35526
|
+
let decryptedBlob;
|
|
35527
|
+
try {
|
|
35528
|
+
decryptedBlob = await decryptBlobWithSignedKey(
|
|
35529
|
+
encryptedBlob,
|
|
35530
|
+
encryptionKey,
|
|
35531
|
+
this.context.platform
|
|
35532
|
+
);
|
|
35533
|
+
} catch (decryptError) {
|
|
35534
|
+
const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
|
|
35535
|
+
if (errorMessage.includes("not a valid OpenPGP message")) {
|
|
35536
|
+
throw new Error(
|
|
35537
|
+
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
|
|
35538
|
+
);
|
|
35539
|
+
} else if (errorMessage.includes("Session key decryption failed")) {
|
|
35540
|
+
throw new Error("Wrong encryption key");
|
|
35541
|
+
} else if (errorMessage.includes("Error decrypting message")) {
|
|
35542
|
+
throw new Error("Wrong encryption key");
|
|
35543
|
+
} else if (errorMessage.includes("File not found")) {
|
|
35544
|
+
throw new Error(
|
|
35545
|
+
"File not found: The encrypted file is no longer available"
|
|
35546
|
+
);
|
|
35547
|
+
} else {
|
|
35548
|
+
throw decryptError;
|
|
35549
|
+
}
|
|
35550
|
+
}
|
|
35551
|
+
return decryptedBlob;
|
|
35552
|
+
} catch (error) {
|
|
35553
|
+
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"))) {
|
|
35554
|
+
throw error;
|
|
35555
|
+
}
|
|
35556
|
+
throw new Error(
|
|
35557
|
+
`Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35558
|
+
);
|
|
35559
|
+
}
|
|
35560
|
+
}
|
|
35594
35561
|
/**
|
|
35595
35562
|
* Retrieves all data files owned by a specific user address.
|
|
35596
35563
|
*
|
|
@@ -36341,81 +36308,6 @@ var DataController = class {
|
|
|
36341
36308
|
);
|
|
36342
36309
|
}
|
|
36343
36310
|
}
|
|
36344
|
-
/**
|
|
36345
|
-
* Decrypts a file that was encrypted using the Vana protocol.
|
|
36346
|
-
*
|
|
36347
|
-
* @param file - The UserFile object containing the file URL and metadata
|
|
36348
|
-
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
36349
|
-
* @returns Promise resolving to the decrypted file as a Blob
|
|
36350
|
-
*
|
|
36351
|
-
* This method handles the complete flow of:
|
|
36352
|
-
* 1. Generating the encryption key from the user's wallet signature
|
|
36353
|
-
* 2. Fetching the encrypted file from the stored URL
|
|
36354
|
-
* 3. Decrypting the file using the canonical Vana decryption method
|
|
36355
|
-
*/
|
|
36356
|
-
async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
|
|
36357
|
-
try {
|
|
36358
|
-
const encryptionKey = await generateEncryptionKey(
|
|
36359
|
-
this.context.walletClient,
|
|
36360
|
-
encryptionSeed
|
|
36361
|
-
);
|
|
36362
|
-
const fetchUrl = this.convertToDownloadUrl(file.url);
|
|
36363
|
-
console.debug(
|
|
36364
|
-
`Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
|
|
36365
|
-
);
|
|
36366
|
-
const response = await fetch(fetchUrl);
|
|
36367
|
-
if (!response.ok) {
|
|
36368
|
-
if (response.status === 404) {
|
|
36369
|
-
throw new Error(
|
|
36370
|
-
"File not found. The encrypted file may have been moved or deleted."
|
|
36371
|
-
);
|
|
36372
|
-
} else if (response.status === 403) {
|
|
36373
|
-
throw new Error(
|
|
36374
|
-
"Access denied. You may not have permission to access this file."
|
|
36375
|
-
);
|
|
36376
|
-
} else {
|
|
36377
|
-
throw new Error(
|
|
36378
|
-
`Network error: ${response.status} ${response.statusText}`
|
|
36379
|
-
);
|
|
36380
|
-
}
|
|
36381
|
-
}
|
|
36382
|
-
const encryptedBlob = await response.blob();
|
|
36383
|
-
console.debug(
|
|
36384
|
-
`Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
|
|
36385
|
-
);
|
|
36386
|
-
if (encryptedBlob.size === 0) {
|
|
36387
|
-
throw new Error("File is empty or could not be retrieved.");
|
|
36388
|
-
}
|
|
36389
|
-
const decryptedBlob = await decryptUserData(
|
|
36390
|
-
encryptedBlob,
|
|
36391
|
-
encryptionKey,
|
|
36392
|
-
this.context.platform
|
|
36393
|
-
);
|
|
36394
|
-
return decryptedBlob;
|
|
36395
|
-
} catch (error) {
|
|
36396
|
-
console.error("Failed to decrypt file:", error);
|
|
36397
|
-
if (error instanceof Error) {
|
|
36398
|
-
if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
|
|
36399
|
-
throw new Error(
|
|
36400
|
-
"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."
|
|
36401
|
-
);
|
|
36402
|
-
} else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
|
|
36403
|
-
throw new Error(
|
|
36404
|
-
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
36405
|
-
);
|
|
36406
|
-
} else if (error.message.includes("File not found")) {
|
|
36407
|
-
throw new Error(
|
|
36408
|
-
"File not found: The encrypted file is no longer available at the stored URL."
|
|
36409
|
-
);
|
|
36410
|
-
} else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
|
|
36411
|
-
throw new Error(
|
|
36412
|
-
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
|
|
36413
|
-
);
|
|
36414
|
-
}
|
|
36415
|
-
}
|
|
36416
|
-
throw error;
|
|
36417
|
-
}
|
|
36418
|
-
}
|
|
36419
36311
|
/**
|
|
36420
36312
|
* Registers a file URL directly on the blockchain with a schema ID.
|
|
36421
36313
|
*
|
|
@@ -36476,26 +36368,6 @@ var DataController = class {
|
|
|
36476
36368
|
);
|
|
36477
36369
|
}
|
|
36478
36370
|
}
|
|
36479
|
-
/**
|
|
36480
|
-
* Converts IPFS and Google Drive URLs to direct download URLs for fetching.
|
|
36481
|
-
*
|
|
36482
|
-
* @param url - The URL to convert to a direct download URL
|
|
36483
|
-
* @returns The converted direct download URL or the original URL if not a special URL
|
|
36484
|
-
*/
|
|
36485
|
-
convertToDownloadUrl(url) {
|
|
36486
|
-
if (url.startsWith("ipfs://")) {
|
|
36487
|
-
const hash = url.replace("ipfs://", "");
|
|
36488
|
-
return `https://ipfs.io/ipfs/${hash}`;
|
|
36489
|
-
}
|
|
36490
|
-
if (url.includes("drive.google.com/file/d/")) {
|
|
36491
|
-
const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
|
|
36492
|
-
if (fileIdMatch) {
|
|
36493
|
-
const fileId = fileIdMatch[1];
|
|
36494
|
-
return `https://drive.google.com/uc?id=${fileId}&export=download`;
|
|
36495
|
-
}
|
|
36496
|
-
}
|
|
36497
|
-
return url;
|
|
36498
|
-
}
|
|
36499
36371
|
/**
|
|
36500
36372
|
* Gets the user's address from the wallet client.
|
|
36501
36373
|
*
|
|
@@ -36989,7 +36861,7 @@ var DataController = class {
|
|
|
36989
36861
|
this.context.walletClient,
|
|
36990
36862
|
DEFAULT_ENCRYPTION_SEED
|
|
36991
36863
|
);
|
|
36992
|
-
const encryptedData = await
|
|
36864
|
+
const encryptedData = await encryptBlobWithSignedKey(
|
|
36993
36865
|
data,
|
|
36994
36866
|
userEncryptionKey,
|
|
36995
36867
|
this.context.platform
|
|
@@ -37153,12 +37025,13 @@ var DataController = class {
|
|
|
37153
37025
|
privateKey,
|
|
37154
37026
|
this.context.platform
|
|
37155
37027
|
);
|
|
37156
|
-
|
|
37157
|
-
if (
|
|
37158
|
-
|
|
37028
|
+
let encryptedData;
|
|
37029
|
+
if (file.url.startsWith("ipfs://")) {
|
|
37030
|
+
encryptedData = await this.fetchFromIPFS(file.url);
|
|
37031
|
+
} else {
|
|
37032
|
+
encryptedData = await this.fetch(file.url);
|
|
37159
37033
|
}
|
|
37160
|
-
const
|
|
37161
|
-
const decryptedData = await decryptUserData(
|
|
37034
|
+
const decryptedData = await decryptBlobWithSignedKey(
|
|
37162
37035
|
encryptedData,
|
|
37163
37036
|
userEncryptionKey,
|
|
37164
37037
|
this.context.platform
|
|
@@ -37171,6 +37044,162 @@ var DataController = class {
|
|
|
37171
37044
|
);
|
|
37172
37045
|
}
|
|
37173
37046
|
}
|
|
37047
|
+
/**
|
|
37048
|
+
* Simple network-agnostic fetch utility for retrieving file content.
|
|
37049
|
+
*
|
|
37050
|
+
* @remarks
|
|
37051
|
+
* This is a thin wrapper around the global fetch API that returns the response as a Blob.
|
|
37052
|
+
* It provides a consistent interface for fetching encrypted content before decryption.
|
|
37053
|
+
* For IPFS URLs, consider using fetchFromIPFS for better reliability.
|
|
37054
|
+
*
|
|
37055
|
+
* @param url - The URL to fetch content from
|
|
37056
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37057
|
+
* @throws {Error} When the fetch fails or returns a non-ok response
|
|
37058
|
+
*
|
|
37059
|
+
* @example
|
|
37060
|
+
* ```typescript
|
|
37061
|
+
* // Fetch and decrypt a file
|
|
37062
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37063
|
+
* const encryptedBlob = await vana.data.fetch(file.url);
|
|
37064
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37065
|
+
*
|
|
37066
|
+
* // With custom headers for authentication
|
|
37067
|
+
* const response = await fetch(file.url, {
|
|
37068
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
37069
|
+
* });
|
|
37070
|
+
* const encryptedBlob = await response.blob();
|
|
37071
|
+
* ```
|
|
37072
|
+
*/
|
|
37073
|
+
async fetch(url) {
|
|
37074
|
+
try {
|
|
37075
|
+
const response = await fetch(url);
|
|
37076
|
+
if (!response.ok) {
|
|
37077
|
+
throw new Error(
|
|
37078
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37079
|
+
);
|
|
37080
|
+
}
|
|
37081
|
+
const blob = await response.blob();
|
|
37082
|
+
if (blob.size === 0) {
|
|
37083
|
+
throw new Error("Empty response");
|
|
37084
|
+
}
|
|
37085
|
+
return blob;
|
|
37086
|
+
} catch (error) {
|
|
37087
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
37088
|
+
throw new Error(
|
|
37089
|
+
`Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
|
|
37090
|
+
);
|
|
37091
|
+
}
|
|
37092
|
+
throw error;
|
|
37093
|
+
}
|
|
37094
|
+
}
|
|
37095
|
+
/**
|
|
37096
|
+
* Specialized IPFS fetcher with gateway fallback mechanism.
|
|
37097
|
+
*
|
|
37098
|
+
* @remarks
|
|
37099
|
+
* This method provides robust IPFS content fetching by trying multiple gateways
|
|
37100
|
+
* in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
|
|
37101
|
+
*
|
|
37102
|
+
* The default gateway list includes public gateways, but you should provide
|
|
37103
|
+
* your own gateways for production use to ensure reliability and privacy.
|
|
37104
|
+
*
|
|
37105
|
+
* @param url - The IPFS URL (ipfs://...) or CID to fetch
|
|
37106
|
+
* @param options - Optional configuration
|
|
37107
|
+
* @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
|
|
37108
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37109
|
+
* @throws {Error} When all gateways fail to fetch the content
|
|
37110
|
+
*
|
|
37111
|
+
* @example
|
|
37112
|
+
* ```typescript
|
|
37113
|
+
* // Fetch from IPFS with custom gateways
|
|
37114
|
+
* const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
|
|
37115
|
+
* gateways: [
|
|
37116
|
+
* 'https://my-private-gateway.com/ipfs/',
|
|
37117
|
+
* 'https://dweb.link/ipfs/',
|
|
37118
|
+
* 'https://ipfs.io/ipfs/'
|
|
37119
|
+
* ]
|
|
37120
|
+
* });
|
|
37121
|
+
*
|
|
37122
|
+
* // Decrypt the fetched content
|
|
37123
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37124
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37125
|
+
*
|
|
37126
|
+
* // With raw CID
|
|
37127
|
+
* const blob = await vana.data.fetchFromIPFS('QmXxx...', {
|
|
37128
|
+
* gateways: ['https://ipfs.io/ipfs/']
|
|
37129
|
+
* });
|
|
37130
|
+
* ```
|
|
37131
|
+
*/
|
|
37132
|
+
async fetchFromIPFS(url, options) {
|
|
37133
|
+
const defaultGateways = [
|
|
37134
|
+
"https://dweb.link/ipfs/",
|
|
37135
|
+
"https://ipfs.io/ipfs/"
|
|
37136
|
+
];
|
|
37137
|
+
const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
|
|
37138
|
+
let cid;
|
|
37139
|
+
if (url.startsWith("ipfs://")) {
|
|
37140
|
+
cid = url.replace("ipfs://", "");
|
|
37141
|
+
} else if (url.startsWith("Qm") || url.startsWith("bafy")) {
|
|
37142
|
+
cid = url;
|
|
37143
|
+
} else {
|
|
37144
|
+
throw new Error(
|
|
37145
|
+
`Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
|
|
37146
|
+
);
|
|
37147
|
+
}
|
|
37148
|
+
const errors = [];
|
|
37149
|
+
for (let i = 0; i < gateways.length; i++) {
|
|
37150
|
+
const gateway = gateways[i];
|
|
37151
|
+
const isLastGateway = i === gateways.length - 1;
|
|
37152
|
+
const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
|
|
37153
|
+
try {
|
|
37154
|
+
console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
|
|
37155
|
+
const response = await fetch(gatewayUrl);
|
|
37156
|
+
if (response.ok) {
|
|
37157
|
+
const blob = await response.blob();
|
|
37158
|
+
if (blob.size > 0) {
|
|
37159
|
+
console.debug(`Successfully fetched from gateway: ${gateway}`);
|
|
37160
|
+
return blob;
|
|
37161
|
+
} else {
|
|
37162
|
+
if (isLastGateway) {
|
|
37163
|
+
throw new Error("Empty response");
|
|
37164
|
+
}
|
|
37165
|
+
errors.push({
|
|
37166
|
+
gateway,
|
|
37167
|
+
error: "Empty response"
|
|
37168
|
+
});
|
|
37169
|
+
}
|
|
37170
|
+
} else {
|
|
37171
|
+
if (isLastGateway) {
|
|
37172
|
+
if (response.status === 403) {
|
|
37173
|
+
throw new Error(`HTTP error! status: 403 ${response.statusText}`);
|
|
37174
|
+
} else if (response.status === 404) {
|
|
37175
|
+
throw new Error(`HTTP error! status: 404 ${response.statusText}`);
|
|
37176
|
+
} else {
|
|
37177
|
+
throw new Error(
|
|
37178
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37179
|
+
);
|
|
37180
|
+
}
|
|
37181
|
+
}
|
|
37182
|
+
errors.push({
|
|
37183
|
+
gateway,
|
|
37184
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
37185
|
+
});
|
|
37186
|
+
}
|
|
37187
|
+
} catch (error) {
|
|
37188
|
+
if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
|
|
37189
|
+
throw error;
|
|
37190
|
+
}
|
|
37191
|
+
errors.push({
|
|
37192
|
+
gateway,
|
|
37193
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
37194
|
+
});
|
|
37195
|
+
}
|
|
37196
|
+
}
|
|
37197
|
+
const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
|
|
37198
|
+
throw new Error(
|
|
37199
|
+
`Failed to fetch IPFS content ${cid} from all gateways:
|
|
37200
|
+
${errorDetails}`
|
|
37201
|
+
);
|
|
37202
|
+
}
|
|
37174
37203
|
/**
|
|
37175
37204
|
* Validates a data schema against the Vana meta-schema.
|
|
37176
37205
|
*
|
|
@@ -37359,9 +37388,14 @@ var SchemaController = class {
|
|
|
37359
37388
|
};
|
|
37360
37389
|
validateDataSchema(dataSchema);
|
|
37361
37390
|
if (!this.context.storageManager) {
|
|
37362
|
-
|
|
37363
|
-
|
|
37364
|
-
|
|
37391
|
+
if (this.context.validateStorageRequired) {
|
|
37392
|
+
this.context.validateStorageRequired();
|
|
37393
|
+
throw new Error("Storage validation failed");
|
|
37394
|
+
} else {
|
|
37395
|
+
throw new Error(
|
|
37396
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
37397
|
+
);
|
|
37398
|
+
}
|
|
37365
37399
|
}
|
|
37366
37400
|
const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
|
|
37367
37401
|
type: "application/json"
|
|
@@ -37633,6 +37667,7 @@ var ServerController = class {
|
|
|
37633
37667
|
}
|
|
37634
37668
|
const serverResponse = await response.json();
|
|
37635
37669
|
return {
|
|
37670
|
+
kind: serverResponse.personal_server.kind,
|
|
37636
37671
|
address: serverResponse.personal_server.address,
|
|
37637
37672
|
public_key: serverResponse.personal_server.public_key,
|
|
37638
37673
|
base_url: this.PERSONAL_SERVER_BASE_URL || "",
|
|
@@ -39246,175 +39281,151 @@ var PinataStorage = class {
|
|
|
39246
39281
|
}
|
|
39247
39282
|
};
|
|
39248
39283
|
|
|
39249
|
-
// src/storage/providers/
|
|
39250
|
-
var
|
|
39251
|
-
constructor(
|
|
39252
|
-
this.
|
|
39253
|
-
if (!
|
|
39254
|
-
throw new
|
|
39255
|
-
"
|
|
39256
|
-
"MISSING_UPLOAD_URL",
|
|
39257
|
-
"server-proxy"
|
|
39258
|
-
);
|
|
39259
|
-
}
|
|
39260
|
-
if (!config.downloadUrl) {
|
|
39261
|
-
throw new StorageError(
|
|
39262
|
-
"Download URL is required",
|
|
39263
|
-
"MISSING_DOWNLOAD_URL",
|
|
39264
|
-
"server-proxy"
|
|
39284
|
+
// src/storage/providers/callback-storage.ts
|
|
39285
|
+
var CallbackStorage = class {
|
|
39286
|
+
constructor(callbacks) {
|
|
39287
|
+
this.callbacks = callbacks;
|
|
39288
|
+
if (!callbacks.upload || !callbacks.download) {
|
|
39289
|
+
throw new Error(
|
|
39290
|
+
"CallbackStorage requires both upload and download callbacks"
|
|
39265
39291
|
);
|
|
39266
39292
|
}
|
|
39267
39293
|
}
|
|
39268
39294
|
/**
|
|
39269
|
-
*
|
|
39295
|
+
* Upload a file using the provided callback
|
|
39270
39296
|
*
|
|
39271
|
-
* @
|
|
39272
|
-
*
|
|
39273
|
-
*
|
|
39274
|
-
* and must return a JSON response with `success: true` and an `identifier` field.
|
|
39275
|
-
*
|
|
39276
|
-
* @param file - The file to upload
|
|
39277
|
-
* @param filename - Optional custom filename
|
|
39278
|
-
* @returns Promise that resolves to the server-provided identifier
|
|
39279
|
-
* @throws {StorageError} When the upload fails or server returns an error
|
|
39280
|
-
*
|
|
39281
|
-
* @example
|
|
39282
|
-
* ```typescript
|
|
39283
|
-
* const identifier = await serverStorage.upload(fileBlob, { name: "report.pdf" });
|
|
39284
|
-
* console.log("File uploaded with identifier:", identifier);
|
|
39285
|
-
* ```
|
|
39297
|
+
* @param file - The blob to upload
|
|
39298
|
+
* @param filename - Optional filename for the upload
|
|
39299
|
+
* @returns The upload result with URL and metadata
|
|
39286
39300
|
*/
|
|
39287
39301
|
async upload(file, filename) {
|
|
39288
39302
|
try {
|
|
39289
|
-
const
|
|
39290
|
-
|
|
39291
|
-
if (filename) {
|
|
39292
|
-
formData.append("name", filename);
|
|
39293
|
-
}
|
|
39294
|
-
const response = await fetch(this.config.uploadUrl, {
|
|
39295
|
-
method: "POST",
|
|
39296
|
-
body: formData
|
|
39297
|
-
});
|
|
39298
|
-
if (!response.ok) {
|
|
39299
|
-
const _errorText = await response.text();
|
|
39300
|
-
throw new StorageError(
|
|
39301
|
-
`Server upload failed: ${response.status} ${response.statusText}`,
|
|
39302
|
-
"UPLOAD_FAILED",
|
|
39303
|
-
"server-proxy"
|
|
39304
|
-
);
|
|
39305
|
-
}
|
|
39306
|
-
const result = await response.json();
|
|
39307
|
-
if (!result.success) {
|
|
39303
|
+
const result = await this.callbacks.upload(file, filename);
|
|
39304
|
+
if (!result.url || result.url.trim() === "") {
|
|
39308
39305
|
throw new StorageError(
|
|
39309
|
-
|
|
39310
|
-
"
|
|
39311
|
-
"
|
|
39306
|
+
"Upload callback returned invalid result: missing or empty url",
|
|
39307
|
+
"INVALID_UPLOAD_RESULT",
|
|
39308
|
+
"callback-storage"
|
|
39312
39309
|
);
|
|
39313
39310
|
}
|
|
39314
|
-
|
|
39315
|
-
throw new StorageError(
|
|
39316
|
-
"Server upload succeeded but no identifier returned",
|
|
39317
|
-
"NO_IDENTIFIER_RETURNED",
|
|
39318
|
-
"server-proxy"
|
|
39319
|
-
);
|
|
39320
|
-
}
|
|
39321
|
-
return {
|
|
39322
|
-
url: result.url || result.identifier,
|
|
39323
|
-
size: file.size,
|
|
39324
|
-
contentType: file.type || "application/octet-stream"
|
|
39325
|
-
};
|
|
39311
|
+
return result;
|
|
39326
39312
|
} catch (error) {
|
|
39327
39313
|
if (error instanceof StorageError) {
|
|
39328
39314
|
throw error;
|
|
39329
39315
|
}
|
|
39330
39316
|
throw new StorageError(
|
|
39331
|
-
`
|
|
39317
|
+
`Upload failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39332
39318
|
"UPLOAD_ERROR",
|
|
39333
|
-
"
|
|
39319
|
+
"callback-storage",
|
|
39320
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39334
39321
|
);
|
|
39335
39322
|
}
|
|
39336
39323
|
}
|
|
39337
39324
|
/**
|
|
39338
|
-
*
|
|
39325
|
+
* Download a file using the provided callback
|
|
39339
39326
|
*
|
|
39340
|
-
* @
|
|
39341
|
-
*
|
|
39342
|
-
* Your server is responsible for retrieving the file from your storage backend
|
|
39343
|
-
* and returning the file content as a blob response.
|
|
39344
|
-
*
|
|
39345
|
-
* @param url - The server-provided URL or identifier from upload
|
|
39346
|
-
* @returns Promise that resolves to the downloaded file content
|
|
39347
|
-
* @throws {StorageError} When the download fails or file is not found
|
|
39348
|
-
*
|
|
39349
|
-
* @example
|
|
39350
|
-
* ```typescript
|
|
39351
|
-
* const fileBlob = await serverStorage.download("file-123");
|
|
39352
|
-
* const url = URL.createObjectURL(fileBlob);
|
|
39353
|
-
* ```
|
|
39327
|
+
* @param url - The URL or identifier to download
|
|
39328
|
+
* @returns The downloaded blob
|
|
39354
39329
|
*/
|
|
39355
39330
|
async download(url) {
|
|
39356
39331
|
try {
|
|
39357
|
-
const identifier = this.
|
|
39358
|
-
const
|
|
39359
|
-
|
|
39360
|
-
headers: {
|
|
39361
|
-
"Content-Type": "application/json"
|
|
39362
|
-
},
|
|
39363
|
-
body: JSON.stringify({ identifier })
|
|
39364
|
-
});
|
|
39365
|
-
if (!response.ok) {
|
|
39366
|
-
const _errorText = await response.text();
|
|
39332
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39333
|
+
const blob = await this.callbacks.download(identifier);
|
|
39334
|
+
if (!(blob instanceof Blob)) {
|
|
39367
39335
|
throw new StorageError(
|
|
39368
|
-
|
|
39369
|
-
"
|
|
39370
|
-
"
|
|
39336
|
+
"Download callback returned invalid result: expected Blob",
|
|
39337
|
+
"INVALID_DOWNLOAD_RESULT",
|
|
39338
|
+
"callback-storage"
|
|
39371
39339
|
);
|
|
39372
39340
|
}
|
|
39373
|
-
return
|
|
39341
|
+
return blob;
|
|
39374
39342
|
} catch (error) {
|
|
39375
39343
|
if (error instanceof StorageError) {
|
|
39376
39344
|
throw error;
|
|
39377
39345
|
}
|
|
39378
39346
|
throw new StorageError(
|
|
39379
|
-
`
|
|
39347
|
+
`Download failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39380
39348
|
"DOWNLOAD_ERROR",
|
|
39381
|
-
"
|
|
39349
|
+
"callback-storage",
|
|
39350
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39382
39351
|
);
|
|
39383
39352
|
}
|
|
39384
39353
|
}
|
|
39385
|
-
|
|
39386
|
-
|
|
39387
|
-
|
|
39388
|
-
|
|
39389
|
-
|
|
39390
|
-
|
|
39391
|
-
|
|
39392
|
-
|
|
39393
|
-
|
|
39394
|
-
|
|
39395
|
-
|
|
39396
|
-
|
|
39397
|
-
|
|
39354
|
+
/**
|
|
39355
|
+
* List files using the provided callback (if available)
|
|
39356
|
+
*
|
|
39357
|
+
* @param options - Optional list options including filters and pagination
|
|
39358
|
+
* @returns Array of storage files
|
|
39359
|
+
*/
|
|
39360
|
+
async list(options) {
|
|
39361
|
+
if (!this.callbacks.list) {
|
|
39362
|
+
throw new StorageError(
|
|
39363
|
+
"List operation not supported - no list callback provided",
|
|
39364
|
+
"NOT_SUPPORTED",
|
|
39365
|
+
"callback-storage"
|
|
39366
|
+
);
|
|
39367
|
+
}
|
|
39368
|
+
try {
|
|
39369
|
+
const result = await this.callbacks.list(options?.namePattern, options);
|
|
39370
|
+
return result.items.map((item, index) => ({
|
|
39371
|
+
id: item.identifier,
|
|
39372
|
+
name: item.identifier.split("/").pop() || `file-${index}`,
|
|
39373
|
+
url: item.identifier,
|
|
39374
|
+
size: item.size || 0,
|
|
39375
|
+
contentType: "application/octet-stream",
|
|
39376
|
+
createdAt: item.lastModified || /* @__PURE__ */ new Date(),
|
|
39377
|
+
metadata: item.metadata
|
|
39378
|
+
}));
|
|
39379
|
+
} catch (error) {
|
|
39380
|
+
throw new StorageError(
|
|
39381
|
+
`List failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39382
|
+
"LIST_ERROR",
|
|
39383
|
+
"callback-storage",
|
|
39384
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39385
|
+
);
|
|
39386
|
+
}
|
|
39398
39387
|
}
|
|
39399
39388
|
/**
|
|
39400
|
-
*
|
|
39389
|
+
* Delete a file using the provided callback (if available)
|
|
39401
39390
|
*
|
|
39402
|
-
* @param url - URL or identifier
|
|
39403
|
-
* @returns
|
|
39391
|
+
* @param url - The URL or identifier to delete
|
|
39392
|
+
* @returns True if deletion succeeded
|
|
39404
39393
|
*/
|
|
39405
|
-
|
|
39406
|
-
|
|
39394
|
+
async delete(url) {
|
|
39395
|
+
if (!this.callbacks.delete) {
|
|
39396
|
+
throw new StorageError(
|
|
39397
|
+
"Delete operation not supported - no delete callback provided",
|
|
39398
|
+
"NOT_SUPPORTED",
|
|
39399
|
+
"callback-storage"
|
|
39400
|
+
);
|
|
39401
|
+
}
|
|
39402
|
+
try {
|
|
39403
|
+
const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
|
|
39404
|
+
return await this.callbacks.delete(identifier);
|
|
39405
|
+
} catch (error) {
|
|
39406
|
+
throw new StorageError(
|
|
39407
|
+
`Delete failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
39408
|
+
"DELETE_ERROR",
|
|
39409
|
+
"callback-storage",
|
|
39410
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
39411
|
+
);
|
|
39412
|
+
}
|
|
39407
39413
|
}
|
|
39414
|
+
/**
|
|
39415
|
+
* Get provider configuration
|
|
39416
|
+
*
|
|
39417
|
+
* @returns Provider configuration metadata
|
|
39418
|
+
*/
|
|
39408
39419
|
getConfig() {
|
|
39409
39420
|
return {
|
|
39410
|
-
name: "
|
|
39411
|
-
type: "
|
|
39421
|
+
name: "callback-storage",
|
|
39422
|
+
type: "callback",
|
|
39412
39423
|
requiresAuth: false,
|
|
39413
39424
|
features: {
|
|
39414
39425
|
upload: true,
|
|
39415
39426
|
download: true,
|
|
39416
|
-
list:
|
|
39417
|
-
delete:
|
|
39427
|
+
list: !!this.callbacks.list,
|
|
39428
|
+
delete: !!this.callbacks.delete
|
|
39418
39429
|
}
|
|
39419
39430
|
};
|
|
39420
39431
|
}
|
|
@@ -39646,6 +39657,47 @@ function getAllChains() {
|
|
|
39646
39657
|
}
|
|
39647
39658
|
|
|
39648
39659
|
// src/core.ts
|
|
39660
|
+
var VanaCoreFactory = class {
|
|
39661
|
+
/**
|
|
39662
|
+
* Creates a VanaCore instance that enforces storage requirements at compile time.
|
|
39663
|
+
* Use this factory when you know you'll need storage-dependent operations.
|
|
39664
|
+
*
|
|
39665
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39666
|
+
* @param config - Configuration that includes required storage providers
|
|
39667
|
+
* @returns VanaCore instance with storage validation
|
|
39668
|
+
* @example
|
|
39669
|
+
* ```typescript
|
|
39670
|
+
* const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
|
|
39671
|
+
* walletClient: myWalletClient,
|
|
39672
|
+
* storage: {
|
|
39673
|
+
* providers: { ipfs: new IPFSStorage() },
|
|
39674
|
+
* defaultProvider: 'ipfs'
|
|
39675
|
+
* }
|
|
39676
|
+
* });
|
|
39677
|
+
* ```
|
|
39678
|
+
*/
|
|
39679
|
+
static createWithStorage(platform, config) {
|
|
39680
|
+
const core = new VanaCore(platform, config);
|
|
39681
|
+
return core;
|
|
39682
|
+
}
|
|
39683
|
+
/**
|
|
39684
|
+
* Creates a VanaCore instance without storage requirements.
|
|
39685
|
+
* Storage-dependent operations will fail at runtime if not configured.
|
|
39686
|
+
*
|
|
39687
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39688
|
+
* @param config - Basic configuration without required storage
|
|
39689
|
+
* @returns VanaCore instance
|
|
39690
|
+
* @example
|
|
39691
|
+
* ```typescript
|
|
39692
|
+
* const vanaCore = VanaCoreFactory.create(platformAdapter, {
|
|
39693
|
+
* walletClient: myWalletClient
|
|
39694
|
+
* });
|
|
39695
|
+
* ```
|
|
39696
|
+
*/
|
|
39697
|
+
static create(platform, config) {
|
|
39698
|
+
return new VanaCore(platform, config);
|
|
39699
|
+
}
|
|
39700
|
+
};
|
|
39649
39701
|
var VanaCore = class {
|
|
39650
39702
|
/**
|
|
39651
39703
|
* Initializes a new VanaCore client instance with the provided configuration.
|
|
@@ -39653,6 +39705,10 @@ var VanaCore = class {
|
|
|
39653
39705
|
* @remarks
|
|
39654
39706
|
* The constructor validates the configuration, initializes storage providers if configured,
|
|
39655
39707
|
* creates wallet and public clients, and sets up all SDK controllers with shared context.
|
|
39708
|
+
*
|
|
39709
|
+
* IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
|
|
39710
|
+
* Methods that require storage will throw runtime errors if storage is not configured.
|
|
39711
|
+
*
|
|
39656
39712
|
* @param platform - The platform adapter for environment-specific operations
|
|
39657
39713
|
* @param config - The configuration object specifying wallet or chain settings
|
|
39658
39714
|
* @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
|
|
@@ -39679,9 +39735,13 @@ var VanaCore = class {
|
|
|
39679
39735
|
__publicField(this, "platform");
|
|
39680
39736
|
__publicField(this, "relayerCallbacks");
|
|
39681
39737
|
__publicField(this, "storageManager");
|
|
39738
|
+
__publicField(this, "hasRequiredStorage");
|
|
39739
|
+
__publicField(this, "ipfsGateways");
|
|
39682
39740
|
this.platform = platform;
|
|
39683
39741
|
this.validateConfig(config);
|
|
39684
39742
|
this.relayerCallbacks = config.relayerCallbacks;
|
|
39743
|
+
this.ipfsGateways = config.ipfsGateways;
|
|
39744
|
+
this.hasRequiredStorage = hasStorageConfig(config);
|
|
39685
39745
|
if (config.storage?.providers) {
|
|
39686
39746
|
this.storageManager = new StorageManager();
|
|
39687
39747
|
for (const [name, provider] of Object.entries(config.storage.providers)) {
|
|
@@ -39736,8 +39796,11 @@ var VanaCore = class {
|
|
|
39736
39796
|
relayerCallbacks: this.relayerCallbacks,
|
|
39737
39797
|
storageManager: this.storageManager,
|
|
39738
39798
|
subgraphUrl,
|
|
39739
|
-
platform: this.platform
|
|
39799
|
+
platform: this.platform,
|
|
39740
39800
|
// Pass the platform adapter to controllers
|
|
39801
|
+
validateStorageRequired: this.validateStorageRequired.bind(this),
|
|
39802
|
+
hasStorage: this.hasStorage.bind(this),
|
|
39803
|
+
ipfsGateways: this.ipfsGateways
|
|
39741
39804
|
};
|
|
39742
39805
|
this.permissions = new PermissionsController(sharedContext);
|
|
39743
39806
|
this.data = new DataController(sharedContext);
|
|
@@ -39745,6 +39808,58 @@ var VanaCore = class {
|
|
|
39745
39808
|
this.server = new ServerController(sharedContext);
|
|
39746
39809
|
this.protocol = new ProtocolController(sharedContext);
|
|
39747
39810
|
}
|
|
39811
|
+
/**
|
|
39812
|
+
* Validates that storage is available for storage-dependent operations.
|
|
39813
|
+
* This method enforces the fail-fast principle by checking storage availability
|
|
39814
|
+
* at method call time rather than during expensive operations.
|
|
39815
|
+
*
|
|
39816
|
+
* @throws {InvalidConfigurationError} When storage is required but not configured
|
|
39817
|
+
* @example
|
|
39818
|
+
* ```typescript
|
|
39819
|
+
* // This will throw if storage is not configured
|
|
39820
|
+
* vana.validateStorageRequired();
|
|
39821
|
+
* await vana.data.uploadFile(file); // Safe to proceed
|
|
39822
|
+
* ```
|
|
39823
|
+
*/
|
|
39824
|
+
validateStorageRequired() {
|
|
39825
|
+
if (!this.hasRequiredStorage) {
|
|
39826
|
+
throw new InvalidConfigurationError(
|
|
39827
|
+
"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."
|
|
39828
|
+
);
|
|
39829
|
+
}
|
|
39830
|
+
}
|
|
39831
|
+
/**
|
|
39832
|
+
* Checks whether storage is configured without throwing an error.
|
|
39833
|
+
*
|
|
39834
|
+
* @returns True if storage is properly configured
|
|
39835
|
+
* @example
|
|
39836
|
+
* ```typescript
|
|
39837
|
+
* if (vana.hasStorage()) {
|
|
39838
|
+
* await vana.data.uploadFile(file);
|
|
39839
|
+
* } else {
|
|
39840
|
+
* console.warn('Storage not configured - using pre-stored URLs only');
|
|
39841
|
+
* }
|
|
39842
|
+
* ```
|
|
39843
|
+
*/
|
|
39844
|
+
hasStorage() {
|
|
39845
|
+
return this.hasRequiredStorage;
|
|
39846
|
+
}
|
|
39847
|
+
/**
|
|
39848
|
+
* Type guard to check if this instance has storage enabled at compile time.
|
|
39849
|
+
* Use this when you need TypeScript to understand that storage is available.
|
|
39850
|
+
*
|
|
39851
|
+
* @returns True if storage is configured, with type narrowing
|
|
39852
|
+
* @example
|
|
39853
|
+
* ```typescript
|
|
39854
|
+
* if (vana.isStorageEnabled()) {
|
|
39855
|
+
* // TypeScript knows storage is available here
|
|
39856
|
+
* await vana.data.uploadFile(file);
|
|
39857
|
+
* }
|
|
39858
|
+
* ```
|
|
39859
|
+
*/
|
|
39860
|
+
isStorageEnabled() {
|
|
39861
|
+
return this.hasRequiredStorage;
|
|
39862
|
+
}
|
|
39748
39863
|
/**
|
|
39749
39864
|
* Validates the provided configuration object against all requirements.
|
|
39750
39865
|
*
|
|
@@ -39933,23 +40048,23 @@ var VanaCore = class {
|
|
|
39933
40048
|
return this.platform;
|
|
39934
40049
|
}
|
|
39935
40050
|
/**
|
|
39936
|
-
* Encrypts
|
|
40051
|
+
* Encrypts data using the Vana protocol standard encryption.
|
|
39937
40052
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
39938
40053
|
*
|
|
39939
40054
|
* @param data The data to encrypt (string or Blob)
|
|
39940
|
-
* @param
|
|
40055
|
+
* @param key The key to use as encryption key
|
|
39941
40056
|
* @returns The encrypted data as Blob
|
|
39942
40057
|
* @example
|
|
39943
40058
|
* ```typescript
|
|
39944
40059
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
39945
|
-
* const encrypted = await vana.
|
|
40060
|
+
* const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
|
|
39946
40061
|
* ```
|
|
39947
40062
|
*/
|
|
39948
|
-
async
|
|
39949
|
-
return
|
|
40063
|
+
async encryptBlob(data, key) {
|
|
40064
|
+
return encryptBlobWithSignedKey(data, key, this.platform);
|
|
39950
40065
|
}
|
|
39951
40066
|
/**
|
|
39952
|
-
* Decrypts
|
|
40067
|
+
* Decrypts data that was encrypted using the Vana protocol.
|
|
39953
40068
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
39954
40069
|
*
|
|
39955
40070
|
* @param encryptedData The encrypted data (string or Blob)
|
|
@@ -39958,12 +40073,16 @@ var VanaCore = class {
|
|
|
39958
40073
|
* @example
|
|
39959
40074
|
* ```typescript
|
|
39960
40075
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
39961
|
-
* const decrypted = await vana.
|
|
40076
|
+
* const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
|
|
39962
40077
|
* const text = await decrypted.text();
|
|
39963
40078
|
* ```
|
|
39964
40079
|
*/
|
|
39965
|
-
async
|
|
39966
|
-
return
|
|
40080
|
+
async decryptBlob(encryptedData, walletSignature) {
|
|
40081
|
+
return decryptBlobWithSignedKey(
|
|
40082
|
+
encryptedData,
|
|
40083
|
+
walletSignature,
|
|
40084
|
+
this.platform
|
|
40085
|
+
);
|
|
39967
40086
|
}
|
|
39968
40087
|
};
|
|
39969
40088
|
|
|
@@ -39994,7 +40113,7 @@ function createValidatedGrant(params) {
|
|
|
39994
40113
|
try {
|
|
39995
40114
|
validateGrant(grantFile, {
|
|
39996
40115
|
schema: true,
|
|
39997
|
-
grantee: params.
|
|
40116
|
+
grantee: params.grantee,
|
|
39998
40117
|
operation: params.operation
|
|
39999
40118
|
});
|
|
40000
40119
|
} catch (error) {
|
|
@@ -40751,31 +40870,22 @@ var ApiClient = class {
|
|
|
40751
40870
|
};
|
|
40752
40871
|
|
|
40753
40872
|
// src/index.browser.ts
|
|
40754
|
-
var
|
|
40755
|
-
/**
|
|
40756
|
-
* Creates a Vana SDK instance configured for browser environments.
|
|
40757
|
-
*
|
|
40758
|
-
* @param config - SDK configuration object (wallet client or chain config)
|
|
40759
|
-
* @example
|
|
40760
|
-
* ```typescript
|
|
40761
|
-
* // With wallet client
|
|
40762
|
-
* const vana = new Vana({ walletClient });
|
|
40763
|
-
*
|
|
40764
|
-
* // With chain configuration
|
|
40765
|
-
* const vana = new Vana({ chainId: 14800, account });
|
|
40766
|
-
* ```
|
|
40767
|
-
*/
|
|
40873
|
+
var VanaBrowserImpl = class extends VanaCore {
|
|
40768
40874
|
constructor(config) {
|
|
40769
40875
|
super(new BrowserPlatformAdapter(), config);
|
|
40770
40876
|
}
|
|
40771
40877
|
};
|
|
40772
|
-
|
|
40878
|
+
function Vana(config) {
|
|
40879
|
+
return new VanaBrowserImpl(config);
|
|
40880
|
+
}
|
|
40881
|
+
var index_browser_default = Vana;
|
|
40773
40882
|
export {
|
|
40774
40883
|
ApiClient,
|
|
40775
40884
|
AsyncQueue,
|
|
40776
40885
|
BaseController,
|
|
40777
40886
|
BlockchainError,
|
|
40778
40887
|
BrowserPlatformAdapter,
|
|
40888
|
+
CallbackStorage,
|
|
40779
40889
|
CircuitBreaker,
|
|
40780
40890
|
ContractFactory,
|
|
40781
40891
|
ContractNotFoundError,
|
|
@@ -40808,14 +40918,15 @@ export {
|
|
|
40808
40918
|
SchemaValidator,
|
|
40809
40919
|
SerializationError,
|
|
40810
40920
|
ServerController,
|
|
40811
|
-
ServerProxyStorage,
|
|
40812
40921
|
ServerUrlMismatchError,
|
|
40813
40922
|
SignatureError,
|
|
40814
40923
|
StorageError,
|
|
40815
40924
|
StorageManager,
|
|
40816
40925
|
UserRejectedRequestError,
|
|
40817
|
-
|
|
40818
|
-
|
|
40926
|
+
Vana,
|
|
40927
|
+
VanaBrowserImpl,
|
|
40928
|
+
VanaCore,
|
|
40929
|
+
VanaCoreFactory,
|
|
40819
40930
|
VanaError,
|
|
40820
40931
|
__contractCache,
|
|
40821
40932
|
chains,
|
|
@@ -40828,13 +40939,13 @@ export {
|
|
|
40828
40939
|
createGrantFile,
|
|
40829
40940
|
createPlatformAdapterSafe,
|
|
40830
40941
|
createValidatedGrant,
|
|
40831
|
-
|
|
40942
|
+
decryptBlobWithSignedKey,
|
|
40832
40943
|
decryptWithPrivateKey,
|
|
40833
40944
|
decryptWithWalletPrivateKey,
|
|
40834
40945
|
index_browser_default as default,
|
|
40835
40946
|
detectPlatform,
|
|
40947
|
+
encryptBlobWithSignedKey,
|
|
40836
40948
|
encryptFileKey,
|
|
40837
|
-
encryptUserData,
|
|
40838
40949
|
encryptWithWalletPublicKey,
|
|
40839
40950
|
extractIpfsHash,
|
|
40840
40951
|
fetchAndValidateSchema,
|
|
@@ -40858,9 +40969,7 @@ export {
|
|
|
40858
40969
|
getPlatformCapabilities,
|
|
40859
40970
|
isAPIResponse,
|
|
40860
40971
|
isGrantExpired,
|
|
40861
|
-
isIdentityServerOutput,
|
|
40862
40972
|
isIpfsUrl,
|
|
40863
|
-
isPersonalServerOutput,
|
|
40864
40973
|
isPlatformSupported,
|
|
40865
40974
|
isReplicateAPIResponse,
|
|
40866
40975
|
moksha,
|