@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.
@@ -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: () => VanaNode,
456
+ Vana: () => Vana,
513
457
  VanaCore: () => VanaCore,
458
+ VanaCoreFactory: () => VanaCoreFactory,
514
459
  VanaError: () => VanaError,
515
- VanaNode: () => VanaNode,
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
- decryptUserData: () => decryptUserData,
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.to,
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
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
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
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
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
- throw new Error(
34261
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
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
- to: params.to,
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
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
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
- throw new Error(
34350
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
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
- to: params.to,
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.to - The recipient address for the permission grant
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
- * Retrieves all permissions granted by the current user using subgraph queries.
34724
+ * Gets on-chain permission grant data without expensive off-chain resolution.
34820
34725
  *
34821
34726
  * @remarks
34822
- * This method queries the Vana subgraph to find permissions directly granted by the user
34823
- * using the Permission entity. It efficiently handles millions of permissions by leveraging
34824
- * indexed subgraph data instead of scanning contract logs. The method fetches complete
34825
- * grant files from IPFS to provide detailed permission information including operation
34826
- * parameters and grantee details.
34827
- * @param params - Optional query parameters
34828
- * @param params.limit - Maximum number of permissions to return (default: 50)
34829
- * @param params.subgraphUrl - Optional subgraph URL to override the default endpoint
34830
- * @returns A Promise that resolves to an array of `GrantedPermission` objects
34831
- * @throws {BlockchainError} When subgraph is unavailable or returns invalid data
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 permissions granted by current user
34835
- * const permissions = await vana.permissions.getUserPermissions();
34744
+ * // Fast: Get all on-chain permission data
34745
+ * const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
34836
34746
  *
34837
- * permissions.forEach(permission => {
34838
- * console.log(`Granted ${permission.operation} to ${permission.grantee}`);
34747
+ * // Display in UI immediately
34748
+ * grants.forEach(grant => {
34749
+ * console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
34839
34750
  * });
34840
34751
  *
34841
- * // Limit results
34842
- * const recent = await vana.permissions.getUserPermissions({ limit: 10 });
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 getUserPermissions(params) {
34758
+ async getUserPermissionGrantsOnChain(options = {}) {
34759
+ const { limit = 50, subgraphUrl } = options;
34846
34760
  try {
34847
34761
  const userAddress = await this.getUserAddress();
34848
- const graphqlEndpoint = params?.subgraphUrl || this.context.subgraphUrl;
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 userPermissions = [];
34910
- const limit = params?.limit || 50;
34911
- const permissionsToProcess = userData.permissions.slice(0, limit);
34912
- for (const permission of permissionsToProcess) {
34913
- try {
34914
- let operation;
34915
- let files = [];
34916
- let parameters;
34917
- let granteeAddress;
34918
- try {
34919
- const grantFile = await retrieveGrantFile(permission.grant);
34920
- operation = grantFile.operation;
34921
- parameters = grantFile.parameters;
34922
- granteeAddress = grantFile.grantee;
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
- console.error("Failed to fetch user permissions:", error);
34830
+ if (error instanceof BlockchainError || error instanceof NetworkError) {
34831
+ throw error;
34832
+ }
34962
34833
  throw new BlockchainError(
34963
- `Failed to fetch user permissions: ${error instanceof Error ? error.message : "Unknown error"}`
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 encryptUserData(data, walletSignature, platformAdapter) {
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
- walletSignature
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 user data: ${error}`);
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 encryptUserData(
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
- throw new Error(
35893
- "Storage manager not configured. Please provide storage providers in VanaConfig."
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.to}`
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.to,
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 encryptUserData(
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
- const response = await fetch(this.convertToDownloadUrl(file.url));
37534
- if (!response.ok) {
37535
- throw new Error(`Failed to download file: ${response.statusText}`);
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 encryptedData = await response.blob();
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
- throw new Error(
37740
- "Storage manager not configured. Please provide storage providers in VanaConfig."
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/server-proxy.ts
39621
- var ServerProxyStorage = class {
39622
- constructor(config) {
39623
- this.config = config;
39624
- if (!config.uploadUrl) {
39625
- throw new StorageError(
39626
- "Upload URL is required",
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
- * Uploads a file through your server endpoint
39665
+ * Upload a file using the provided callback
39641
39666
  *
39642
- * @remarks
39643
- * This method sends the file to your configured upload endpoint via FormData.
39644
- * Your server is responsible for handling the actual storage implementation
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 formData = new FormData();
39661
- formData.append("file", file);
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
- `Upload failed: ${result.error || "Unknown server error"}`,
39681
- "UPLOAD_FAILED",
39682
- "server-proxy"
39676
+ "Upload callback returned invalid result: missing or empty url",
39677
+ "INVALID_UPLOAD_RESULT",
39678
+ "callback-storage"
39683
39679
  );
39684
39680
  }
39685
- if (!result.identifier) {
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
- `Server proxy upload error: ${error instanceof Error ? error.message : "Unknown error"}`,
39687
+ `Upload failed: ${error instanceof Error ? error.message : String(error)}`,
39703
39688
  "UPLOAD_ERROR",
39704
- "server-proxy"
39689
+ "callback-storage",
39690
+ { cause: error instanceof Error ? error : void 0 }
39705
39691
  );
39706
39692
  }
39707
39693
  }
39708
39694
  /**
39709
- * Downloads a file through your server endpoint
39695
+ * Download a file using the provided callback
39710
39696
  *
39711
- * @remarks
39712
- * This method sends the identifier to your configured download endpoint via POST request.
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.extractIdentifierFromUrl(url);
39729
- const response = await fetch(this.config.downloadUrl, {
39730
- method: "POST",
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
- `Server download failed: ${response.status} ${response.statusText}`,
39740
- "DOWNLOAD_FAILED",
39741
- "server-proxy"
39706
+ "Download callback returned invalid result: expected Blob",
39707
+ "INVALID_DOWNLOAD_RESULT",
39708
+ "callback-storage"
39742
39709
  );
39743
39710
  }
39744
- return await response.blob();
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
- `Server proxy download error: ${error instanceof Error ? error.message : "Unknown error"}`,
39717
+ `Download failed: ${error instanceof Error ? error.message : String(error)}`,
39751
39718
  "DOWNLOAD_ERROR",
39752
- "server-proxy"
39719
+ "callback-storage",
39720
+ { cause: error instanceof Error ? error : void 0 }
39753
39721
  );
39754
39722
  }
39755
39723
  }
39756
- async list(_options) {
39757
- throw new StorageError(
39758
- "List operation is not supported by server proxy storage",
39759
- "LIST_NOT_SUPPORTED",
39760
- "server-proxy"
39761
- );
39762
- }
39763
- async delete(_url) {
39764
- throw new StorageError(
39765
- "Delete operation is not supported by server proxy storage",
39766
- "DELETE_NOT_SUPPORTED",
39767
- "server-proxy"
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
- * Extract identifier from URL or return as-is
39759
+ * Delete a file using the provided callback (if available)
39772
39760
  *
39773
- * @param url - URL or identifier string
39774
- * @returns identifier string
39761
+ * @param url - The URL or identifier to delete
39762
+ * @returns True if deletion succeeded
39775
39763
  */
39776
- extractIdentifierFromUrl(url) {
39777
- return url;
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: "Server Proxy",
39782
- type: "server-proxy",
39791
+ name: "callback-storage",
39792
+ type: "callback",
39783
39793
  requiresAuth: false,
39784
39794
  features: {
39785
39795
  upload: true,
39786
39796
  download: true,
39787
- list: false,
39788
- delete: false
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 user data using the Vana protocol standard encryption.
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 walletSignature The wallet signature to use as encryption key
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.encryptUserData("sensitive data", encryptionKey);
40428
+ * const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
40315
40429
  * ```
40316
40430
  */
40317
- async encryptUserData(data, walletSignature) {
40318
- return encryptUserData(data, walletSignature, this.platform);
40431
+ async encryptBlob(data, key) {
40432
+ return encryptBlobWithSignedKey(data, key, this.platform);
40319
40433
  }
40320
40434
  /**
40321
- * Decrypts user data using the Vana protocol standard decryption.
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.decryptUserData(encryptedData, encryptionKey);
40444
+ * const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
40331
40445
  * const text = await decrypted.text();
40332
40446
  * ```
40333
40447
  */
40334
- async decryptUserData(encryptedData, walletSignature) {
40335
- return decryptUserData(encryptedData, walletSignature, this.platform);
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.to,
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 VanaNode = class extends VanaCore {
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
- var index_node_default = VanaNode;
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
- VanaNode,
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
- decryptUserData,
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,