@opendatalabs/vana-sdk 3.13.0 → 3.13.1
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 +7 -6
- package/dist/direct/endpoints.cjs +2 -1
- package/dist/direct/endpoints.cjs.map +1 -1
- package/dist/direct/endpoints.js +2 -1
- package/dist/direct/endpoints.js.map +1 -1
- package/dist/direct/types.cjs.map +1 -1
- package/dist/direct/types.d.ts +2 -1
- package/dist/direct/types.js.map +1 -1
- package/dist/index.browser.js +54 -9
- package/dist/index.browser.js.map +3 -3
- package/dist/index.node.cjs +54 -9
- package/dist/index.node.cjs.map +3 -3
- package/dist/index.node.js +54 -9
- package/dist/index.node.js.map +3 -3
- package/dist/protocol/networks.cjs +43 -0
- package/dist/protocol/networks.cjs.map +1 -0
- package/dist/protocol/networks.d.ts +11 -0
- package/dist/protocol/networks.js +18 -0
- package/dist/protocol/networks.js.map +1 -0
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/providers/vana-storage.cjs +40 -9
- package/dist/storage/providers/vana-storage.cjs.map +1 -1
- package/dist/storage/providers/vana-storage.d.ts +22 -7
- package/dist/storage/providers/vana-storage.js +43 -9
- package/dist/storage/providers/vana-storage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var networks_exports = {};
|
|
20
|
+
__export(networks_exports, {
|
|
21
|
+
getProtocolNetworkChainId: () => getProtocolNetworkChainId,
|
|
22
|
+
isProtocolNetwork: () => isProtocolNetwork
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(networks_exports);
|
|
25
|
+
const PROTOCOL_NETWORK_CHAIN_IDS = {
|
|
26
|
+
mainnet: 1480,
|
|
27
|
+
moksha: 14800
|
|
28
|
+
};
|
|
29
|
+
function isProtocolNetwork(value) {
|
|
30
|
+
return typeof value === "string" && Object.hasOwn(PROTOCOL_NETWORK_CHAIN_IDS, value);
|
|
31
|
+
}
|
|
32
|
+
function getProtocolNetworkChainId(network) {
|
|
33
|
+
if (!isProtocolNetwork(network)) {
|
|
34
|
+
throw new Error(`Unsupported Vana protocol network: ${String(network)}`);
|
|
35
|
+
}
|
|
36
|
+
return PROTOCOL_NETWORK_CHAIN_IDS[network];
|
|
37
|
+
}
|
|
38
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
39
|
+
0 && (module.exports = {
|
|
40
|
+
getProtocolNetworkChainId,
|
|
41
|
+
isProtocolNetwork
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=networks.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/networks.ts"],"sourcesContent":["/**\n * Vana protocol networks known to this SDK release.\n *\n * The network name is the ergonomic public API; the chain ID is the protocol\n * namespace used by chain-scoped services such as storage and escrow.\n *\n * @category Protocol\n */\nexport type ProtocolNetwork = \"mainnet\" | \"moksha\";\n\nconst PROTOCOL_NETWORK_CHAIN_IDS: Record<ProtocolNetwork, number> = {\n mainnet: 1480,\n moksha: 14800,\n};\n\nexport function isProtocolNetwork(value: unknown): value is ProtocolNetwork {\n return (\n typeof value === \"string\" &&\n Object.hasOwn(PROTOCOL_NETWORK_CHAIN_IDS, value)\n );\n}\n\nexport function getProtocolNetworkChainId(network: ProtocolNetwork): number {\n if (!isProtocolNetwork(network)) {\n throw new Error(`Unsupported Vana protocol network: ${String(network)}`);\n }\n return PROTOCOL_NETWORK_CHAIN_IDS[network];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,MAAM,6BAA8D;AAAA,EAClE,SAAS;AAAA,EACT,QAAQ;AACV;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,SACE,OAAO,UAAU,YACjB,OAAO,OAAO,4BAA4B,KAAK;AAEnD;AAEO,SAAS,0BAA0B,SAAkC;AAC1E,MAAI,CAAC,kBAAkB,OAAO,GAAG;AAC/B,UAAM,IAAI,MAAM,sCAAsC,OAAO,OAAO,CAAC,EAAE;AAAA,EACzE;AACA,SAAO,2BAA2B,OAAO;AAC3C;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vana protocol networks known to this SDK release.
|
|
3
|
+
*
|
|
4
|
+
* The network name is the ergonomic public API; the chain ID is the protocol
|
|
5
|
+
* namespace used by chain-scoped services such as storage and escrow.
|
|
6
|
+
*
|
|
7
|
+
* @category Protocol
|
|
8
|
+
*/
|
|
9
|
+
export type ProtocolNetwork = "mainnet" | "moksha";
|
|
10
|
+
export declare function isProtocolNetwork(value: unknown): value is ProtocolNetwork;
|
|
11
|
+
export declare function getProtocolNetworkChainId(network: ProtocolNetwork): number;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const PROTOCOL_NETWORK_CHAIN_IDS = {
|
|
2
|
+
mainnet: 1480,
|
|
3
|
+
moksha: 14800
|
|
4
|
+
};
|
|
5
|
+
function isProtocolNetwork(value) {
|
|
6
|
+
return typeof value === "string" && Object.hasOwn(PROTOCOL_NETWORK_CHAIN_IDS, value);
|
|
7
|
+
}
|
|
8
|
+
function getProtocolNetworkChainId(network) {
|
|
9
|
+
if (!isProtocolNetwork(network)) {
|
|
10
|
+
throw new Error(`Unsupported Vana protocol network: ${String(network)}`);
|
|
11
|
+
}
|
|
12
|
+
return PROTOCOL_NETWORK_CHAIN_IDS[network];
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getProtocolNetworkChainId,
|
|
16
|
+
isProtocolNetwork
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=networks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/networks.ts"],"sourcesContent":["/**\n * Vana protocol networks known to this SDK release.\n *\n * The network name is the ergonomic public API; the chain ID is the protocol\n * namespace used by chain-scoped services such as storage and escrow.\n *\n * @category Protocol\n */\nexport type ProtocolNetwork = \"mainnet\" | \"moksha\";\n\nconst PROTOCOL_NETWORK_CHAIN_IDS: Record<ProtocolNetwork, number> = {\n mainnet: 1480,\n moksha: 14800,\n};\n\nexport function isProtocolNetwork(value: unknown): value is ProtocolNetwork {\n return (\n typeof value === \"string\" &&\n Object.hasOwn(PROTOCOL_NETWORK_CHAIN_IDS, value)\n );\n}\n\nexport function getProtocolNetworkChainId(network: ProtocolNetwork): number {\n if (!isProtocolNetwork(network)) {\n throw new Error(`Unsupported Vana protocol network: ${String(network)}`);\n }\n return PROTOCOL_NETWORK_CHAIN_IDS[network];\n}\n"],"mappings":"AAUA,MAAM,6BAA8D;AAAA,EAClE,SAAS;AAAA,EACT,QAAQ;AACV;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,SACE,OAAO,UAAU,YACjB,OAAO,OAAO,4BAA4B,KAAK;AAEnD;AAEO,SAAS,0BAA0B,SAAkC;AAC1E,MAAI,CAAC,kBAAkB,OAAO,GAAG;AAC/B,UAAM,IAAI,MAAM,sCAAsC,OAAO,OAAO,CAAC,EAAE;AAAA,EACzE;AACA,SAAO,2BAA2B,OAAO;AAC3C;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage/index.ts"],"sourcesContent":["/**\n * Storage API for Vana SDK\n *\n * Provides unified interface for different storage providers\n * to upload, download, and manage user data files.\n *\n * ## Storage Provider Decision Tree\n *\n * Choose your storage provider based on your needs:\n *\n * **Default backend hosted by ODL?**\n * - ✅ Use `VanaStorage` - storage.vana.org with Web3Signed auth.\n *\n * **Need full CRUD operations and metadata?**\n * - ✅ Use `PinataStorage` - Managed IPFS with listing, deletion, and rich metadata\n *\n * **Want to use your own IPFS infrastructure?**\n * - ✅ Use `IpfsStorage.forInfura()` - Connect to Infura IPFS service\n * - ✅ Use `IpfsStorage.forLocalNode()` - Connect to local IPFS node\n * - ✅ Use `new IpfsStorage()` - Connect to any IPFS-compatible service\n *\n * **Want flexible callback-based storage?**\n * - ✅ Use `CallbackStorage` - Implement storage via custom callbacks (HTTP, WebSocket, etc.)\n *\n * **Need Google Drive integration?**\n * - ✅ Use `GoogleDriveStorage` - Direct Google Drive API with folder management\n *\n * @example\n * ```typescript\n * // Managed IPFS with full features\n * const pinata = new PinataStorage({ jwt: \"your-jwt\" });\n *\n * // Standard IPFS with Infura\n * const ipfs = IpfsStorage.forInfura({ projectId: \"...\", projectSecret: \"...\" });\n *\n * // Callback-based storage (flexible)\n * const storage = new CallbackStorage({\n * async upload(blob, filename) {\n * // Your custom upload logic\n * const response = await fetch('/api/upload', { method: 'POST', body: blob });\n * const data = await response.json();\n * return { url: data.url, size: blob.size, contentType: blob.type };\n * },\n * async download(identifier) {\n * // Your custom download logic\n * const response = await fetch(`/api/download/${identifier}`);\n * return response.blob();\n * }\n * });\n * ```\n */\n\n// Re-export storage types from types module to avoid circular dependencies\nexport type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n StorageProviderConfig,\n} from \"../types/storage\";\n\nexport { StorageError } from \"../types/storage\";\n\n// Export default Vana storage factory\nexport { createVanaStorageProvider } from \"./default\";\nexport type { VanaStorageProviderOptions } from \"./default\";\n\n// Export storage providers\nexport { R2Storage } from \"./providers/r2\";\nexport type { R2Config } from \"./providers/r2\";\nexport { VanaStorage } from \"./providers/vana-storage\";\nexport type {\n VanaStorageConfig,\n VanaStorageSigner,\n} from \"./providers/vana-storage\";\nexport { GoogleDriveStorage } from \"./providers/google-drive\";\nexport { DropboxStorage } from \"./providers/dropbox\";\nexport { IpfsStorage } from \"./providers/ipfs\";\nexport { PinataStorage } from \"./providers/pinata\";\nexport { CallbackStorage } from \"./providers/callback-storage\";\n\n// Export storage manager\nexport { StorageManager } from \"./manager\";\n\n// Export storage callback types\nexport type {\n StorageCallbacks,\n StorageDownloadOptions,\n StorageListResult,\n} from \"../types/config\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DA,qBAA6B;AAG7B,qBAA0C;AAI1C,gBAA0B;AAE1B,0BAA4B;
|
|
1
|
+
{"version":3,"sources":["../../src/storage/index.ts"],"sourcesContent":["/**\n * Storage API for Vana SDK\n *\n * Provides unified interface for different storage providers\n * to upload, download, and manage user data files.\n *\n * ## Storage Provider Decision Tree\n *\n * Choose your storage provider based on your needs:\n *\n * **Default backend hosted by ODL?**\n * - ✅ Use `VanaStorage` - storage.vana.org with Web3Signed auth.\n *\n * **Need full CRUD operations and metadata?**\n * - ✅ Use `PinataStorage` - Managed IPFS with listing, deletion, and rich metadata\n *\n * **Want to use your own IPFS infrastructure?**\n * - ✅ Use `IpfsStorage.forInfura()` - Connect to Infura IPFS service\n * - ✅ Use `IpfsStorage.forLocalNode()` - Connect to local IPFS node\n * - ✅ Use `new IpfsStorage()` - Connect to any IPFS-compatible service\n *\n * **Want flexible callback-based storage?**\n * - ✅ Use `CallbackStorage` - Implement storage via custom callbacks (HTTP, WebSocket, etc.)\n *\n * **Need Google Drive integration?**\n * - ✅ Use `GoogleDriveStorage` - Direct Google Drive API with folder management\n *\n * @example\n * ```typescript\n * // Managed IPFS with full features\n * const pinata = new PinataStorage({ jwt: \"your-jwt\" });\n *\n * // Standard IPFS with Infura\n * const ipfs = IpfsStorage.forInfura({ projectId: \"...\", projectSecret: \"...\" });\n *\n * // Callback-based storage (flexible)\n * const storage = new CallbackStorage({\n * async upload(blob, filename) {\n * // Your custom upload logic\n * const response = await fetch('/api/upload', { method: 'POST', body: blob });\n * const data = await response.json();\n * return { url: data.url, size: blob.size, contentType: blob.type };\n * },\n * async download(identifier) {\n * // Your custom download logic\n * const response = await fetch(`/api/download/${identifier}`);\n * return response.blob();\n * }\n * });\n * ```\n */\n\n// Re-export storage types from types module to avoid circular dependencies\nexport type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n StorageProviderConfig,\n} from \"../types/storage\";\n\nexport { StorageError } from \"../types/storage\";\n\n// Export default Vana storage factory\nexport { createVanaStorageProvider } from \"./default\";\nexport type { VanaStorageProviderOptions } from \"./default\";\n\n// Export storage providers\nexport { R2Storage } from \"./providers/r2\";\nexport type { R2Config } from \"./providers/r2\";\nexport { VanaStorage } from \"./providers/vana-storage\";\nexport type {\n VanaStorageConfig,\n VanaStorageSigner,\n} from \"./providers/vana-storage\";\nexport type { ProtocolNetwork } from \"../protocol/networks\";\nexport { GoogleDriveStorage } from \"./providers/google-drive\";\nexport { DropboxStorage } from \"./providers/dropbox\";\nexport { IpfsStorage } from \"./providers/ipfs\";\nexport { PinataStorage } from \"./providers/pinata\";\nexport { CallbackStorage } from \"./providers/callback-storage\";\n\n// Export storage manager\nexport { StorageManager } from \"./manager\";\n\n// Export storage callback types\nexport type {\n StorageCallbacks,\n StorageDownloadOptions,\n StorageListResult,\n} from \"../types/config\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DA,qBAA6B;AAG7B,qBAA0C;AAI1C,gBAA0B;AAE1B,0BAA4B;AAM5B,0BAAmC;AACnC,qBAA+B;AAC/B,kBAA4B;AAC5B,oBAA8B;AAC9B,8BAAgC;AAGhC,qBAA+B;","names":[]}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export { R2Storage } from "./providers/r2";
|
|
|
57
57
|
export type { R2Config } from "./providers/r2";
|
|
58
58
|
export { VanaStorage } from "./providers/vana-storage";
|
|
59
59
|
export type { VanaStorageConfig, VanaStorageSigner, } from "./providers/vana-storage";
|
|
60
|
+
export type { ProtocolNetwork } from "../protocol/networks";
|
|
60
61
|
export { GoogleDriveStorage } from "./providers/google-drive";
|
|
61
62
|
export { DropboxStorage } from "./providers/dropbox";
|
|
62
63
|
export { IpfsStorage } from "./providers/ipfs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage/index.ts"],"sourcesContent":["/**\n * Storage API for Vana SDK\n *\n * Provides unified interface for different storage providers\n * to upload, download, and manage user data files.\n *\n * ## Storage Provider Decision Tree\n *\n * Choose your storage provider based on your needs:\n *\n * **Default backend hosted by ODL?**\n * - ✅ Use `VanaStorage` - storage.vana.org with Web3Signed auth.\n *\n * **Need full CRUD operations and metadata?**\n * - ✅ Use `PinataStorage` - Managed IPFS with listing, deletion, and rich metadata\n *\n * **Want to use your own IPFS infrastructure?**\n * - ✅ Use `IpfsStorage.forInfura()` - Connect to Infura IPFS service\n * - ✅ Use `IpfsStorage.forLocalNode()` - Connect to local IPFS node\n * - ✅ Use `new IpfsStorage()` - Connect to any IPFS-compatible service\n *\n * **Want flexible callback-based storage?**\n * - ✅ Use `CallbackStorage` - Implement storage via custom callbacks (HTTP, WebSocket, etc.)\n *\n * **Need Google Drive integration?**\n * - ✅ Use `GoogleDriveStorage` - Direct Google Drive API with folder management\n *\n * @example\n * ```typescript\n * // Managed IPFS with full features\n * const pinata = new PinataStorage({ jwt: \"your-jwt\" });\n *\n * // Standard IPFS with Infura\n * const ipfs = IpfsStorage.forInfura({ projectId: \"...\", projectSecret: \"...\" });\n *\n * // Callback-based storage (flexible)\n * const storage = new CallbackStorage({\n * async upload(blob, filename) {\n * // Your custom upload logic\n * const response = await fetch('/api/upload', { method: 'POST', body: blob });\n * const data = await response.json();\n * return { url: data.url, size: blob.size, contentType: blob.type };\n * },\n * async download(identifier) {\n * // Your custom download logic\n * const response = await fetch(`/api/download/${identifier}`);\n * return response.blob();\n * }\n * });\n * ```\n */\n\n// Re-export storage types from types module to avoid circular dependencies\nexport type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n StorageProviderConfig,\n} from \"../types/storage\";\n\nexport { StorageError } from \"../types/storage\";\n\n// Export default Vana storage factory\nexport { createVanaStorageProvider } from \"./default\";\nexport type { VanaStorageProviderOptions } from \"./default\";\n\n// Export storage providers\nexport { R2Storage } from \"./providers/r2\";\nexport type { R2Config } from \"./providers/r2\";\nexport { VanaStorage } from \"./providers/vana-storage\";\nexport type {\n VanaStorageConfig,\n VanaStorageSigner,\n} from \"./providers/vana-storage\";\nexport { GoogleDriveStorage } from \"./providers/google-drive\";\nexport { DropboxStorage } from \"./providers/dropbox\";\nexport { IpfsStorage } from \"./providers/ipfs\";\nexport { PinataStorage } from \"./providers/pinata\";\nexport { CallbackStorage } from \"./providers/callback-storage\";\n\n// Export storage manager\nexport { StorageManager } from \"./manager\";\n\n// Export storage callback types\nexport type {\n StorageCallbacks,\n StorageDownloadOptions,\n StorageListResult,\n} from \"../types/config\";\n"],"mappings":"AA6DA,SAAS,oBAAoB;AAG7B,SAAS,iCAAiC;AAI1C,SAAS,iBAAiB;AAE1B,SAAS,mBAAmB;
|
|
1
|
+
{"version":3,"sources":["../../src/storage/index.ts"],"sourcesContent":["/**\n * Storage API for Vana SDK\n *\n * Provides unified interface for different storage providers\n * to upload, download, and manage user data files.\n *\n * ## Storage Provider Decision Tree\n *\n * Choose your storage provider based on your needs:\n *\n * **Default backend hosted by ODL?**\n * - ✅ Use `VanaStorage` - storage.vana.org with Web3Signed auth.\n *\n * **Need full CRUD operations and metadata?**\n * - ✅ Use `PinataStorage` - Managed IPFS with listing, deletion, and rich metadata\n *\n * **Want to use your own IPFS infrastructure?**\n * - ✅ Use `IpfsStorage.forInfura()` - Connect to Infura IPFS service\n * - ✅ Use `IpfsStorage.forLocalNode()` - Connect to local IPFS node\n * - ✅ Use `new IpfsStorage()` - Connect to any IPFS-compatible service\n *\n * **Want flexible callback-based storage?**\n * - ✅ Use `CallbackStorage` - Implement storage via custom callbacks (HTTP, WebSocket, etc.)\n *\n * **Need Google Drive integration?**\n * - ✅ Use `GoogleDriveStorage` - Direct Google Drive API with folder management\n *\n * @example\n * ```typescript\n * // Managed IPFS with full features\n * const pinata = new PinataStorage({ jwt: \"your-jwt\" });\n *\n * // Standard IPFS with Infura\n * const ipfs = IpfsStorage.forInfura({ projectId: \"...\", projectSecret: \"...\" });\n *\n * // Callback-based storage (flexible)\n * const storage = new CallbackStorage({\n * async upload(blob, filename) {\n * // Your custom upload logic\n * const response = await fetch('/api/upload', { method: 'POST', body: blob });\n * const data = await response.json();\n * return { url: data.url, size: blob.size, contentType: blob.type };\n * },\n * async download(identifier) {\n * // Your custom download logic\n * const response = await fetch(`/api/download/${identifier}`);\n * return response.blob();\n * }\n * });\n * ```\n */\n\n// Re-export storage types from types module to avoid circular dependencies\nexport type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n StorageProviderConfig,\n} from \"../types/storage\";\n\nexport { StorageError } from \"../types/storage\";\n\n// Export default Vana storage factory\nexport { createVanaStorageProvider } from \"./default\";\nexport type { VanaStorageProviderOptions } from \"./default\";\n\n// Export storage providers\nexport { R2Storage } from \"./providers/r2\";\nexport type { R2Config } from \"./providers/r2\";\nexport { VanaStorage } from \"./providers/vana-storage\";\nexport type {\n VanaStorageConfig,\n VanaStorageSigner,\n} from \"./providers/vana-storage\";\nexport type { ProtocolNetwork } from \"../protocol/networks\";\nexport { GoogleDriveStorage } from \"./providers/google-drive\";\nexport { DropboxStorage } from \"./providers/dropbox\";\nexport { IpfsStorage } from \"./providers/ipfs\";\nexport { PinataStorage } from \"./providers/pinata\";\nexport { CallbackStorage } from \"./providers/callback-storage\";\n\n// Export storage manager\nexport { StorageManager } from \"./manager\";\n\n// Export storage callback types\nexport type {\n StorageCallbacks,\n StorageDownloadOptions,\n StorageListResult,\n} from \"../types/config\";\n"],"mappings":"AA6DA,SAAS,oBAAoB;AAG7B,SAAS,iCAAiC;AAI1C,SAAS,iBAAiB;AAE1B,SAAS,mBAAmB;AAM5B,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,uBAAuB;AAGhC,SAAS,sBAAsB;","names":[]}
|
|
@@ -23,6 +23,7 @@ __export(vana_storage_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(vana_storage_exports);
|
|
24
24
|
var import__ = require("../index");
|
|
25
25
|
var import_web3_signed_builder = require("../../auth/web3-signed-builder");
|
|
26
|
+
var import_networks = require("../../protocol/networks");
|
|
26
27
|
const DEFAULT_ENDPOINT = "https://storage.vana.org";
|
|
27
28
|
const LEGACY_BLOB_PATH_PREFIX = "/v1/blobs";
|
|
28
29
|
const DEFAULT_TOKEN_TTL_SECONDS = 300;
|
|
@@ -30,6 +31,7 @@ const MAX_UPLOAD_ATTEMPTS = 4;
|
|
|
30
31
|
const MAX_RATE_LIMIT_DELAY_MS = 3e4;
|
|
31
32
|
class VanaStorage {
|
|
32
33
|
endpoint;
|
|
34
|
+
network;
|
|
33
35
|
chainId;
|
|
34
36
|
blobPathPrefix;
|
|
35
37
|
signer;
|
|
@@ -44,14 +46,9 @@ class VanaStorage {
|
|
|
44
46
|
);
|
|
45
47
|
}
|
|
46
48
|
this.endpoint = (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"INVALID_CHAIN_ID",
|
|
51
|
-
"vana-storage"
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
this.chainId = config.chainId;
|
|
49
|
+
const resolved = resolveStorageNamespace(config);
|
|
50
|
+
this.network = resolved.network;
|
|
51
|
+
this.chainId = resolved.chainId;
|
|
55
52
|
this.blobPathPrefix = this.chainId !== void 0 ? `/v1/chains/${this.chainId}/blobs` : LEGACY_BLOB_PATH_PREFIX;
|
|
56
53
|
this.signer = config.signer;
|
|
57
54
|
this.ownerAddress = (config.ownerAddress ?? config.signer.address).toLowerCase();
|
|
@@ -251,17 +248,51 @@ class VanaStorage {
|
|
|
251
248
|
}
|
|
252
249
|
if (route.chainId !== this.chainId) {
|
|
253
250
|
throw new import__.StorageError(
|
|
254
|
-
`URL chainId '${route.chainId ?? "legacy"}' does not match provider
|
|
251
|
+
`URL chainId '${route.chainId ?? "legacy"}' does not match provider namespace '${this.namespaceDescription()}'`,
|
|
255
252
|
"INVALID_URL",
|
|
256
253
|
"vana-storage"
|
|
257
254
|
);
|
|
258
255
|
}
|
|
259
256
|
return parsed.pathname;
|
|
260
257
|
}
|
|
258
|
+
namespaceDescription() {
|
|
259
|
+
if (this.network !== void 0) {
|
|
260
|
+
return `${this.network} (${this.chainId})`;
|
|
261
|
+
}
|
|
262
|
+
return this.chainId === void 0 ? "legacy" : String(this.chainId);
|
|
263
|
+
}
|
|
261
264
|
}
|
|
262
265
|
function isValidChainId(value) {
|
|
263
266
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
264
267
|
}
|
|
268
|
+
function resolveStorageNamespace(config) {
|
|
269
|
+
if (config.network !== void 0 && !(0, import_networks.isProtocolNetwork)(config.network)) {
|
|
270
|
+
throw new import__.StorageError(
|
|
271
|
+
`Unsupported vana-storage network '${String(config.network)}'`,
|
|
272
|
+
"INVALID_NETWORK",
|
|
273
|
+
"vana-storage"
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
if (config.chainId !== void 0 && !isValidChainId(config.chainId)) {
|
|
277
|
+
throw new import__.StorageError(
|
|
278
|
+
`Unsupported vana-storage chainId '${String(config.chainId)}'`,
|
|
279
|
+
"INVALID_CHAIN_ID",
|
|
280
|
+
"vana-storage"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (config.network === void 0) {
|
|
284
|
+
return { chainId: config.chainId };
|
|
285
|
+
}
|
|
286
|
+
const networkChainId = (0, import_networks.getProtocolNetworkChainId)(config.network);
|
|
287
|
+
if (config.chainId !== void 0 && config.chainId !== networkChainId) {
|
|
288
|
+
throw new import__.StorageError(
|
|
289
|
+
`vana-storage network '${config.network}' resolves to chainId '${networkChainId}', not '${config.chainId}'`,
|
|
290
|
+
"INVALID_CHAIN_ID",
|
|
291
|
+
"vana-storage"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return { chainId: networkChainId, network: config.network };
|
|
295
|
+
}
|
|
265
296
|
function parseBlobPath(pathname) {
|
|
266
297
|
const segments = pathname.split("/").filter((s) => s.length > 0);
|
|
267
298
|
const isTraversal = (s) => s === "." || s === "..";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage/providers/vana-storage.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\nimport {\n buildWeb3SignedHeader,\n type Web3SignedSignFn,\n} from \"../../auth/web3-signed-builder\";\n\nconst DEFAULT_ENDPOINT = \"https://storage.vana.org\";\nconst LEGACY_BLOB_PATH_PREFIX = \"/v1/blobs\";\nconst DEFAULT_TOKEN_TTL_SECONDS = 300;\nconst MAX_UPLOAD_ATTEMPTS = 4;\nconst MAX_RATE_LIMIT_DELAY_MS = 30_000;\n\n/**\n * Wallet-style signer used by {@link VanaStorage} to authenticate every\n * request. For Personal Server flows this can be a registered server wallet\n * signing requests for the owner's storage namespace.\n *\n * @category Storage\n */\nexport interface VanaStorageSigner {\n /** EIP-191 address (`0x...`). */\n address: `0x${string}`;\n /** EIP-191 personal_sign callback (e.g. viem `account.signMessage`). */\n signMessage: Web3SignedSignFn;\n}\n\n/**\n * Configuration for {@link VanaStorage}.\n *\n * @category Storage\n */\nexport interface VanaStorageConfig {\n /**\n * Base URL of the vana-storage Worker. Defaults to `https://storage.vana.org`.\n *\n * This selects the storage endpoint and is independent of {@link chainId}.\n */\n endpoint?: string;\n /**\n * Numeric chain ID (e.g. `1480` for Vana mainnet, `14800` for Moksha) that\n * scopes blob paths.\n *\n * When set, uploads use chain-scoped routes\n * (`/v1/chains/{chainId}/blobs/...`) so data for different chains under the\n * same owner/scope/timestamp never collides. Reads and deletes must use the\n * same chain-scoped namespace; legacy blobs should be migrated or re-collected\n * rather than read through an ambiguous fallback. When omitted, the provider\n * preserves the legacy `/v1/blobs/...` routes and behavior.\n *\n * Chain ID is orthogonal to {@link endpoint}: `endpoint` picks the storage\n * host, `chainId` picks the chain namespace within that host.\n */\n chainId?: number;\n /**\n * Wallet signer used to authenticate writes and reads.\n */\n signer: VanaStorageSigner;\n /**\n * Owner namespace under which blobs are stored. Defaults to the signer address.\n */\n ownerAddress?: `0x${string}`;\n /**\n * Optional `fetch` implementation. Defaults to the global `fetch`.\n * Useful for tests and for environments that need a custom HTTP client.\n */\n fetchImpl?: typeof fetch;\n}\n\ninterface VanaStorageUploadResponse {\n key: string;\n url: string;\n etag: string;\n size: number;\n}\n\n/**\n * Storage provider that talks to the vana-storage Worker\n * (`https://storage.vana.org` by default). All requests are authenticated\n * with Web3Signed headers signed by the configured wallet.\n *\n * @remarks\n * Filenames passed to {@link VanaStorage.upload} must be of the form\n * `\"{scope}/{collectedAt}\"` (e.g. `\"instagram.profile/2026-05-08T20:00:00.000Z\"`).\n * The owner address is prepended automatically to produce the canonical\n * blob path `/v1/blobs/{owner}/{scope}/{collectedAt}`.\n *\n * When {@link VanaStorageConfig.chainId} is set, paths are chain-scoped as\n * `/v1/chains/{chainId}/blobs/{owner}/{scope}/{collectedAt}` so different chains\n * never collide on the same host. The Web3Signed audience remains the endpoint\n * origin regardless of chain ID.\n *\n * @category Storage\n *\n * @example\n * ```typescript\n * import { privateKeyToAccount } from \"viem/accounts\";\n * import { VanaStorage } from \"@opendatalabs/vana-sdk/node\";\n *\n * const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);\n * const storage = new VanaStorage({\n * signer: {\n * address: account.address,\n * signMessage: (msg) => account.signMessage({ message: msg }),\n * },\n * });\n *\n * const result = await storage.upload(\n * new Blob([ciphertext]),\n * \"instagram.profile/2026-05-08T20:00:00.000Z\",\n * );\n * ```\n */\nexport class VanaStorage implements StorageProvider {\n private readonly endpoint: string;\n private readonly chainId?: number;\n private readonly blobPathPrefix: string;\n private readonly signer: VanaStorageSigner;\n private readonly ownerAddress: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(config: VanaStorageConfig) {\n if (!config?.signer?.address || !config?.signer?.signMessage) {\n throw new StorageError(\n \"VanaStorage requires a signer with address and signMessage\",\n \"MISSING_SIGNER\",\n \"vana-storage\",\n );\n }\n this.endpoint = (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n if (config.chainId !== undefined && !isValidChainId(config.chainId)) {\n throw new StorageError(\n `Unsupported vana-storage chainId '${String(config.chainId)}'`,\n \"INVALID_CHAIN_ID\",\n \"vana-storage\",\n );\n }\n this.chainId = config.chainId;\n this.blobPathPrefix =\n this.chainId !== undefined\n ? `/v1/chains/${this.chainId}/blobs`\n : LEGACY_BLOB_PATH_PREFIX;\n this.signer = config.signer;\n this.ownerAddress = (\n config.ownerAddress ?? config.signer.address\n ).toLowerCase();\n this.fetchImpl = config.fetchImpl ?? globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Upload an encrypted blob to vana-storage.\n *\n * @param file - The blob to upload.\n * @param filename - Required relative key in the form `\"{scope}/{collectedAt}\"`.\n * The owner address is prepended automatically.\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n if (!filename) {\n throw new StorageError(\n \"VanaStorage.upload requires a filename of the form '{scope}/{collectedAt}'\",\n \"MISSING_FILENAME\",\n \"vana-storage\",\n );\n }\n\n const subpath = encodeRelativePath(filename);\n const path = `${this.blobPathPrefix}/${this.ownerAddress}/${subpath}`;\n const body = new Uint8Array(await file.arrayBuffer());\n const contentType =\n file.type !== \"\" ? file.type : \"application/octet-stream\";\n\n const header = await this.signRequest(\"PUT\", path, body);\n\n let response: Response | null = null;\n let responseText = \"\";\n for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt++) {\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"PUT\",\n headers: {\n authorization: header,\n \"content-type\": contentType,\n },\n body,\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage upload network error: ${describe(cause)}`,\n \"UPLOAD_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (response.ok) {\n break;\n }\n\n responseText = await safeText(response);\n if (response.status === 429 && attempt < MAX_UPLOAD_ATTEMPTS) {\n const delayMs = retryDelayMs(response, responseText);\n if (delayMs > 0) {\n await sleep(delayMs);\n }\n continue;\n }\n\n throw new StorageError(\n `vana-storage upload failed: ${response.status} ${response.statusText} - ${responseText}`,\n \"UPLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n if (!response?.ok) {\n throw new StorageError(\n `vana-storage upload failed after ${MAX_UPLOAD_ATTEMPTS} attempts - ${responseText}`,\n \"UPLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n const result = (await response.json()) as VanaStorageUploadResponse;\n return {\n url: result.url,\n size: result.size,\n contentType,\n metadata: { key: result.key, etag: result.etag },\n };\n }\n\n /**\n * Download a blob by URL. The URL must point at a path under this\n * provider's endpoint.\n */\n async download(url: string): Promise<Blob> {\n const path = this.pathFromUrl(url);\n const header = await this.signRequest(\"GET\", path);\n\n let response: Response;\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"GET\",\n headers: { authorization: header },\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage download network error: ${describe(cause)}`,\n \"DOWNLOAD_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (!response.ok) {\n throw new StorageError(\n `vana-storage download failed: ${response.status} ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n return await response.blob();\n }\n\n /**\n * Listing is not supported by vana-storage — file discovery is handled by\n * the Gateway DataRegistry, not the storage layer.\n */\n async list(_options?: StorageListOptions): Promise<StorageFile[]> {\n throw new StorageError(\n \"list is not supported by vana-storage; query the Gateway DataRegistry instead\",\n \"NOT_IMPLEMENTED\",\n \"vana-storage\",\n );\n }\n\n async delete(url: string): Promise<boolean> {\n const path = this.pathFromUrl(url);\n const header = await this.signRequest(\"DELETE\", path);\n\n let response: Response;\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"DELETE\",\n headers: { authorization: header },\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage delete network error: ${describe(cause)}`,\n \"DELETE_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (response.status === 404) return false;\n if (!response.ok) {\n throw new StorageError(\n `vana-storage delete failed: ${response.status} ${response.statusText}`,\n \"DELETE_FAILED\",\n \"vana-storage\",\n );\n }\n return true;\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"vana-storage\",\n type: \"vana-storage\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: false,\n delete: true,\n },\n };\n }\n\n private async signRequest(\n method: \"GET\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: Uint8Array,\n ): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n return buildWeb3SignedHeader({\n signMessage: this.signer.signMessage,\n aud: this.endpoint,\n method,\n uri: path,\n iat: now,\n exp: now + DEFAULT_TOKEN_TTL_SECONDS,\n ...(body !== undefined && body.length > 0 && { body }),\n });\n }\n\n private pathFromUrl(url: string): string {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new StorageError(\n `Invalid URL: ${url}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n const expectedHost = new URL(this.endpoint).host;\n if (parsed.host !== expectedHost) {\n throw new StorageError(\n `URL host '${parsed.host}' does not match storage endpoint '${expectedHost}'`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n // Restrict to a blob path for this owner so a caller cannot induce this\n // wallet to sign arbitrary same-host paths.\n const route = parseBlobPath(parsed.pathname);\n if (!route || route.owner.toLowerCase() !== this.ownerAddress) {\n throw new StorageError(\n `URL path '${parsed.pathname}' must be ${this.blobPathPrefix}/${this.ownerAddress}/{scope}/{collectedAt}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n // A provider must only sign requests for its configured namespace. Chain\n // mode is strict: old legacy blobs should be migrated or re-collected\n // rather than read through a second ambiguous path.\n if (route.chainId !== this.chainId) {\n throw new StorageError(\n `URL chainId '${route.chainId ?? \"legacy\"}' does not match provider chainId '${this.chainId ?? \"legacy\"}'`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n return parsed.pathname;\n }\n}\n\ninterface ParsedBlobRoute {\n chainId?: number;\n owner: string;\n scope: string;\n collectedAt: string;\n}\n\nfunction isValidChainId(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value > 0;\n}\n\n/**\n * Parse a storage blob pathname into structured route info, or `null` when the\n * path is neither the legacy nor the chain-scoped blob shape. Traversal\n * segments (`.` / `..`) are rejected as invalid.\n */\nfunction parseBlobPath(pathname: string): ParsedBlobRoute | null {\n const segments = pathname.split(\"/\").filter((s) => s.length > 0);\n const isTraversal = (s: string): boolean => s === \".\" || s === \"..\";\n\n // Legacy: /v1/blobs/{owner}/{scope}/{collectedAt}\n if (\n segments.length === 5 &&\n segments[0] === \"v1\" &&\n segments[1] === \"blobs\"\n ) {\n const [, , owner, scope, collectedAt] = segments as [\n string,\n string,\n string,\n string,\n string,\n ];\n if (isTraversal(scope) || isTraversal(collectedAt)) return null;\n return { owner, scope, collectedAt };\n }\n\n // Chain-scoped: /v1/chains/{chainId}/blobs/{owner}/{scope}/{collectedAt}\n if (\n segments.length === 7 &&\n segments[0] === \"v1\" &&\n segments[1] === \"chains\" &&\n segments[3] === \"blobs\"\n ) {\n const [, , chainIdSegment, , owner, scope, collectedAt] = segments as [\n string,\n string,\n string,\n string,\n string,\n string,\n string,\n ];\n if (!/^[0-9]+$/.test(chainIdSegment)) return null;\n const chainId = Number(chainIdSegment);\n if (!isValidChainId(chainId)) return null;\n if (isTraversal(scope) || isTraversal(collectedAt)) return null;\n return { chainId, owner, scope, collectedAt };\n }\n\n return null;\n}\n\nfunction encodeRelativePath(filename: string): string {\n const parts = filename.split(\"/\");\n if (\n parts.length !== 2 ||\n parts.some((p) => p.length === 0 || p === \".\" || p === \"..\")\n ) {\n throw new StorageError(\n `filename must be exactly '{scope}/{collectedAt}' with non-empty segments, got '${filename}'`,\n \"INVALID_FILENAME\",\n \"vana-storage\",\n );\n }\n return parts.map((p) => encodeURIComponent(p)).join(\"/\");\n}\n\nfunction describe(value: unknown): string {\n if (value instanceof Error) return value.message;\n return String(value);\n}\n\nfunction retryDelayMs(response: Response, responseText: string): number {\n const headerDelayMs = parseRetryAfterHeaderMs(\n response.headers.get(\"retry-after\"),\n );\n if (headerDelayMs !== null) {\n return clampRateLimitDelay(headerDelayMs);\n }\n\n return clampRateLimitDelay(parseRetryAfterBodyMs(responseText) ?? 0);\n}\n\nfunction parseRetryAfterHeaderMs(value: string | null): number | null {\n if (!value) return null;\n\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return seconds * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (Number.isFinite(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return null;\n}\n\nfunction parseRetryAfterBodyMs(responseText: string): number | null {\n if (!responseText) return null;\n\n try {\n const parsed = JSON.parse(responseText) as { retryAfter?: unknown };\n const seconds = Number(parsed.retryAfter);\n return Number.isFinite(seconds) ? seconds * 1000 : null;\n } catch {\n return null;\n }\n}\n\nfunction clampRateLimitDelay(delayMs: number): number {\n return Math.min(Math.max(0, delayMs), MAX_RATE_LIMIT_DELAY_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function safeText(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/storage/providers/vana-storage.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\nimport {\n buildWeb3SignedHeader,\n type Web3SignedSignFn,\n} from \"../../auth/web3-signed-builder\";\nimport {\n getProtocolNetworkChainId,\n isProtocolNetwork,\n type ProtocolNetwork,\n} from \"../../protocol/networks\";\n\nconst DEFAULT_ENDPOINT = \"https://storage.vana.org\";\nconst LEGACY_BLOB_PATH_PREFIX = \"/v1/blobs\";\nconst DEFAULT_TOKEN_TTL_SECONDS = 300;\nconst MAX_UPLOAD_ATTEMPTS = 4;\nconst MAX_RATE_LIMIT_DELAY_MS = 30_000;\n\n/**\n * Wallet-style signer used by {@link VanaStorage} to authenticate every\n * request. For Personal Server flows this can be a registered server wallet\n * signing requests for the owner's storage namespace.\n *\n * @category Storage\n */\nexport interface VanaStorageSigner {\n /** EIP-191 address (`0x...`). */\n address: `0x${string}`;\n /** EIP-191 personal_sign callback (e.g. viem `account.signMessage`). */\n signMessage: Web3SignedSignFn;\n}\n\n/**\n * Configuration for {@link VanaStorage}.\n *\n * @category Storage\n */\nexport interface VanaStorageConfig {\n /**\n * Base URL of the vana-storage Worker. Defaults to `https://storage.vana.org`.\n *\n * This selects the storage endpoint and is independent of {@link network} /\n * {@link chainId}.\n */\n endpoint?: string;\n /**\n * Named Vana protocol network that scopes blob paths. Prefer this for known\n * Vana networks because it keeps callers on the SDK's typed network registry.\n *\n * When set, uploads use chain-scoped routes derived from the network's chain\n * ID (`/v1/chains/1480/...` for mainnet, `/v1/chains/14800/...` for Moksha).\n */\n network?: ProtocolNetwork;\n /**\n * Explicit numeric chain ID for custom or future networks not yet represented\n * by {@link ProtocolNetwork}.\n *\n * When set, uploads use chain-scoped routes\n * (`/v1/chains/{chainId}/blobs/...`) so data for different chains under the\n * same owner/scope/timestamp never collides. Reads and deletes must use the\n * same chain-scoped namespace; legacy blobs should be migrated or re-collected\n * rather than read through an ambiguous fallback. When omitted, the provider\n * preserves the legacy `/v1/blobs/...` routes and behavior.\n *\n * `chainId` and {@link network} are mutually consistent ways to select the\n * namespace. If both are provided, they must resolve to the same chain ID.\n * `endpoint` remains orthogonal: it picks the storage host, not the protocol\n * namespace.\n */\n chainId?: number;\n /**\n * Wallet signer used to authenticate writes and reads.\n */\n signer: VanaStorageSigner;\n /**\n * Owner namespace under which blobs are stored. Defaults to the signer address.\n */\n ownerAddress?: `0x${string}`;\n /**\n * Optional `fetch` implementation. Defaults to the global `fetch`.\n * Useful for tests and for environments that need a custom HTTP client.\n */\n fetchImpl?: typeof fetch;\n}\n\ninterface VanaStorageUploadResponse {\n key: string;\n url: string;\n etag: string;\n size: number;\n}\n\n/**\n * Storage provider that talks to the vana-storage Worker\n * (`https://storage.vana.org` by default). All requests are authenticated\n * with Web3Signed headers signed by the configured wallet.\n *\n * @remarks\n * Filenames passed to {@link VanaStorage.upload} must be of the form\n * `\"{scope}/{collectedAt}\"` (e.g. `\"instagram.profile/2026-05-08T20:00:00.000Z\"`).\n * The owner address is prepended automatically to produce the canonical\n * blob path `/v1/blobs/{owner}/{scope}/{collectedAt}`.\n *\n * When {@link VanaStorageConfig.network} or {@link VanaStorageConfig.chainId}\n * is set, paths are chain-scoped as\n * `/v1/chains/{chainId}/blobs/{owner}/{scope}/{collectedAt}` so different chains\n * never collide on the same host. The Web3Signed audience remains the endpoint\n * origin regardless of network.\n *\n * @category Storage\n *\n * @example\n * ```typescript\n * import { privateKeyToAccount } from \"viem/accounts\";\n * import { VanaStorage } from \"@opendatalabs/vana-sdk/node\";\n *\n * const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);\n * const storage = new VanaStorage({\n * signer: {\n * address: account.address,\n * signMessage: (msg) => account.signMessage({ message: msg }),\n * },\n * });\n *\n * const result = await storage.upload(\n * new Blob([ciphertext]),\n * \"instagram.profile/2026-05-08T20:00:00.000Z\",\n * );\n * ```\n */\nexport class VanaStorage implements StorageProvider {\n private readonly endpoint: string;\n private readonly network?: ProtocolNetwork;\n private readonly chainId?: number;\n private readonly blobPathPrefix: string;\n private readonly signer: VanaStorageSigner;\n private readonly ownerAddress: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(config: VanaStorageConfig) {\n if (!config?.signer?.address || !config?.signer?.signMessage) {\n throw new StorageError(\n \"VanaStorage requires a signer with address and signMessage\",\n \"MISSING_SIGNER\",\n \"vana-storage\",\n );\n }\n this.endpoint = (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n const resolved = resolveStorageNamespace(config);\n this.network = resolved.network;\n this.chainId = resolved.chainId;\n this.blobPathPrefix =\n this.chainId !== undefined\n ? `/v1/chains/${this.chainId}/blobs`\n : LEGACY_BLOB_PATH_PREFIX;\n this.signer = config.signer;\n this.ownerAddress = (\n config.ownerAddress ?? config.signer.address\n ).toLowerCase();\n this.fetchImpl = config.fetchImpl ?? globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Upload an encrypted blob to vana-storage.\n *\n * @param file - The blob to upload.\n * @param filename - Required relative key in the form `\"{scope}/{collectedAt}\"`.\n * The owner address is prepended automatically.\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n if (!filename) {\n throw new StorageError(\n \"VanaStorage.upload requires a filename of the form '{scope}/{collectedAt}'\",\n \"MISSING_FILENAME\",\n \"vana-storage\",\n );\n }\n\n const subpath = encodeRelativePath(filename);\n const path = `${this.blobPathPrefix}/${this.ownerAddress}/${subpath}`;\n const body = new Uint8Array(await file.arrayBuffer());\n const contentType =\n file.type !== \"\" ? file.type : \"application/octet-stream\";\n\n const header = await this.signRequest(\"PUT\", path, body);\n\n let response: Response | null = null;\n let responseText = \"\";\n for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt++) {\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"PUT\",\n headers: {\n authorization: header,\n \"content-type\": contentType,\n },\n body,\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage upload network error: ${describe(cause)}`,\n \"UPLOAD_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (response.ok) {\n break;\n }\n\n responseText = await safeText(response);\n if (response.status === 429 && attempt < MAX_UPLOAD_ATTEMPTS) {\n const delayMs = retryDelayMs(response, responseText);\n if (delayMs > 0) {\n await sleep(delayMs);\n }\n continue;\n }\n\n throw new StorageError(\n `vana-storage upload failed: ${response.status} ${response.statusText} - ${responseText}`,\n \"UPLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n if (!response?.ok) {\n throw new StorageError(\n `vana-storage upload failed after ${MAX_UPLOAD_ATTEMPTS} attempts - ${responseText}`,\n \"UPLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n const result = (await response.json()) as VanaStorageUploadResponse;\n return {\n url: result.url,\n size: result.size,\n contentType,\n metadata: { key: result.key, etag: result.etag },\n };\n }\n\n /**\n * Download a blob by URL. The URL must point at a path under this\n * provider's endpoint.\n */\n async download(url: string): Promise<Blob> {\n const path = this.pathFromUrl(url);\n const header = await this.signRequest(\"GET\", path);\n\n let response: Response;\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"GET\",\n headers: { authorization: header },\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage download network error: ${describe(cause)}`,\n \"DOWNLOAD_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (!response.ok) {\n throw new StorageError(\n `vana-storage download failed: ${response.status} ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"vana-storage\",\n );\n }\n\n return await response.blob();\n }\n\n /**\n * Listing is not supported by vana-storage — file discovery is handled by\n * the Gateway DataRegistry, not the storage layer.\n */\n async list(_options?: StorageListOptions): Promise<StorageFile[]> {\n throw new StorageError(\n \"list is not supported by vana-storage; query the Gateway DataRegistry instead\",\n \"NOT_IMPLEMENTED\",\n \"vana-storage\",\n );\n }\n\n async delete(url: string): Promise<boolean> {\n const path = this.pathFromUrl(url);\n const header = await this.signRequest(\"DELETE\", path);\n\n let response: Response;\n try {\n response = await this.fetchImpl(`${this.endpoint}${path}`, {\n method: \"DELETE\",\n headers: { authorization: header },\n });\n } catch (cause) {\n throw new StorageError(\n `vana-storage delete network error: ${describe(cause)}`,\n \"DELETE_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (response.status === 404) return false;\n if (!response.ok) {\n throw new StorageError(\n `vana-storage delete failed: ${response.status} ${response.statusText}`,\n \"DELETE_FAILED\",\n \"vana-storage\",\n );\n }\n return true;\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"vana-storage\",\n type: \"vana-storage\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: false,\n delete: true,\n },\n };\n }\n\n private async signRequest(\n method: \"GET\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: Uint8Array,\n ): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n return buildWeb3SignedHeader({\n signMessage: this.signer.signMessage,\n aud: this.endpoint,\n method,\n uri: path,\n iat: now,\n exp: now + DEFAULT_TOKEN_TTL_SECONDS,\n ...(body !== undefined && body.length > 0 && { body }),\n });\n }\n\n private pathFromUrl(url: string): string {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new StorageError(\n `Invalid URL: ${url}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n const expectedHost = new URL(this.endpoint).host;\n if (parsed.host !== expectedHost) {\n throw new StorageError(\n `URL host '${parsed.host}' does not match storage endpoint '${expectedHost}'`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n // Restrict to a blob path for this owner so a caller cannot induce this\n // wallet to sign arbitrary same-host paths.\n const route = parseBlobPath(parsed.pathname);\n if (!route || route.owner.toLowerCase() !== this.ownerAddress) {\n throw new StorageError(\n `URL path '${parsed.pathname}' must be ${this.blobPathPrefix}/${this.ownerAddress}/{scope}/{collectedAt}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n // A provider must only sign requests for its configured namespace. Chain\n // mode is strict: old legacy blobs should be migrated or re-collected\n // rather than read through a second ambiguous path.\n if (route.chainId !== this.chainId) {\n throw new StorageError(\n `URL chainId '${route.chainId ?? \"legacy\"}' does not match provider namespace '${this.namespaceDescription()}'`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n return parsed.pathname;\n }\n\n private namespaceDescription(): string {\n if (this.network !== undefined) {\n return `${this.network} (${this.chainId})`;\n }\n return this.chainId === undefined ? \"legacy\" : String(this.chainId);\n }\n}\n\ninterface ParsedBlobRoute {\n chainId?: number;\n owner: string;\n scope: string;\n collectedAt: string;\n}\n\nfunction isValidChainId(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value > 0;\n}\n\nfunction resolveStorageNamespace(\n config: Pick<VanaStorageConfig, \"network\" | \"chainId\">,\n): {\n chainId?: number;\n network?: ProtocolNetwork;\n} {\n if (config.network !== undefined && !isProtocolNetwork(config.network)) {\n throw new StorageError(\n `Unsupported vana-storage network '${String(config.network)}'`,\n \"INVALID_NETWORK\",\n \"vana-storage\",\n );\n }\n\n if (config.chainId !== undefined && !isValidChainId(config.chainId)) {\n throw new StorageError(\n `Unsupported vana-storage chainId '${String(config.chainId)}'`,\n \"INVALID_CHAIN_ID\",\n \"vana-storage\",\n );\n }\n\n if (config.network === undefined) {\n return { chainId: config.chainId };\n }\n\n const networkChainId = getProtocolNetworkChainId(config.network);\n if (config.chainId !== undefined && config.chainId !== networkChainId) {\n throw new StorageError(\n `vana-storage network '${config.network}' resolves to chainId '${networkChainId}', not '${config.chainId}'`,\n \"INVALID_CHAIN_ID\",\n \"vana-storage\",\n );\n }\n\n return { chainId: networkChainId, network: config.network };\n}\n\n/**\n * Parse a storage blob pathname into structured route info, or `null` when the\n * path is neither the legacy nor the chain-scoped blob shape. Traversal\n * segments (`.` / `..`) are rejected as invalid.\n */\nfunction parseBlobPath(pathname: string): ParsedBlobRoute | null {\n const segments = pathname.split(\"/\").filter((s) => s.length > 0);\n const isTraversal = (s: string): boolean => s === \".\" || s === \"..\";\n\n // Legacy: /v1/blobs/{owner}/{scope}/{collectedAt}\n if (\n segments.length === 5 &&\n segments[0] === \"v1\" &&\n segments[1] === \"blobs\"\n ) {\n const [, , owner, scope, collectedAt] = segments as [\n string,\n string,\n string,\n string,\n string,\n ];\n if (isTraversal(scope) || isTraversal(collectedAt)) return null;\n return { owner, scope, collectedAt };\n }\n\n // Chain-scoped: /v1/chains/{chainId}/blobs/{owner}/{scope}/{collectedAt}\n if (\n segments.length === 7 &&\n segments[0] === \"v1\" &&\n segments[1] === \"chains\" &&\n segments[3] === \"blobs\"\n ) {\n const [, , chainIdSegment, , owner, scope, collectedAt] = segments as [\n string,\n string,\n string,\n string,\n string,\n string,\n string,\n ];\n if (!/^[0-9]+$/.test(chainIdSegment)) return null;\n const chainId = Number(chainIdSegment);\n if (!isValidChainId(chainId)) return null;\n if (isTraversal(scope) || isTraversal(collectedAt)) return null;\n return { chainId, owner, scope, collectedAt };\n }\n\n return null;\n}\n\nfunction encodeRelativePath(filename: string): string {\n const parts = filename.split(\"/\");\n if (\n parts.length !== 2 ||\n parts.some((p) => p.length === 0 || p === \".\" || p === \"..\")\n ) {\n throw new StorageError(\n `filename must be exactly '{scope}/{collectedAt}' with non-empty segments, got '${filename}'`,\n \"INVALID_FILENAME\",\n \"vana-storage\",\n );\n }\n return parts.map((p) => encodeURIComponent(p)).join(\"/\");\n}\n\nfunction describe(value: unknown): string {\n if (value instanceof Error) return value.message;\n return String(value);\n}\n\nfunction retryDelayMs(response: Response, responseText: string): number {\n const headerDelayMs = parseRetryAfterHeaderMs(\n response.headers.get(\"retry-after\"),\n );\n if (headerDelayMs !== null) {\n return clampRateLimitDelay(headerDelayMs);\n }\n\n return clampRateLimitDelay(parseRetryAfterBodyMs(responseText) ?? 0);\n}\n\nfunction parseRetryAfterHeaderMs(value: string | null): number | null {\n if (!value) return null;\n\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return seconds * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (Number.isFinite(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return null;\n}\n\nfunction parseRetryAfterBodyMs(responseText: string): number | null {\n if (!responseText) return null;\n\n try {\n const parsed = JSON.parse(responseText) as { retryAfter?: unknown };\n const seconds = Number(parsed.retryAfter);\n return Number.isFinite(seconds) ? seconds * 1000 : null;\n } catch {\n return null;\n }\n}\n\nfunction clampRateLimitDelay(delayMs: number): number {\n return Math.min(Math.max(0, delayMs), MAX_RATE_LIMIT_DELAY_MS);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function safeText(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type StorageProvider, type StorageUploadResult, type StorageFile, type StorageListOptions, type StorageProviderConfig } from "../index";
|
|
2
2
|
import { type Web3SignedSignFn } from "../../auth/web3-signed-builder";
|
|
3
|
+
import { type ProtocolNetwork } from "../../protocol/networks";
|
|
3
4
|
/**
|
|
4
5
|
* Wallet-style signer used by {@link VanaStorage} to authenticate every
|
|
5
6
|
* request. For Personal Server flows this can be a registered server wallet
|
|
@@ -22,12 +23,21 @@ export interface VanaStorageConfig {
|
|
|
22
23
|
/**
|
|
23
24
|
* Base URL of the vana-storage Worker. Defaults to `https://storage.vana.org`.
|
|
24
25
|
*
|
|
25
|
-
* This selects the storage endpoint and is independent of {@link
|
|
26
|
+
* This selects the storage endpoint and is independent of {@link network} /
|
|
27
|
+
* {@link chainId}.
|
|
26
28
|
*/
|
|
27
29
|
endpoint?: string;
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
+
* Named Vana protocol network that scopes blob paths. Prefer this for known
|
|
32
|
+
* Vana networks because it keeps callers on the SDK's typed network registry.
|
|
33
|
+
*
|
|
34
|
+
* When set, uploads use chain-scoped routes derived from the network's chain
|
|
35
|
+
* ID (`/v1/chains/1480/...` for mainnet, `/v1/chains/14800/...` for Moksha).
|
|
36
|
+
*/
|
|
37
|
+
network?: ProtocolNetwork;
|
|
38
|
+
/**
|
|
39
|
+
* Explicit numeric chain ID for custom or future networks not yet represented
|
|
40
|
+
* by {@link ProtocolNetwork}.
|
|
31
41
|
*
|
|
32
42
|
* When set, uploads use chain-scoped routes
|
|
33
43
|
* (`/v1/chains/{chainId}/blobs/...`) so data for different chains under the
|
|
@@ -36,8 +46,10 @@ export interface VanaStorageConfig {
|
|
|
36
46
|
* rather than read through an ambiguous fallback. When omitted, the provider
|
|
37
47
|
* preserves the legacy `/v1/blobs/...` routes and behavior.
|
|
38
48
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
49
|
+
* `chainId` and {@link network} are mutually consistent ways to select the
|
|
50
|
+
* namespace. If both are provided, they must resolve to the same chain ID.
|
|
51
|
+
* `endpoint` remains orthogonal: it picks the storage host, not the protocol
|
|
52
|
+
* namespace.
|
|
41
53
|
*/
|
|
42
54
|
chainId?: number;
|
|
43
55
|
/**
|
|
@@ -65,10 +77,11 @@ export interface VanaStorageConfig {
|
|
|
65
77
|
* The owner address is prepended automatically to produce the canonical
|
|
66
78
|
* blob path `/v1/blobs/{owner}/{scope}/{collectedAt}`.
|
|
67
79
|
*
|
|
68
|
-
* When {@link VanaStorageConfig.
|
|
80
|
+
* When {@link VanaStorageConfig.network} or {@link VanaStorageConfig.chainId}
|
|
81
|
+
* is set, paths are chain-scoped as
|
|
69
82
|
* `/v1/chains/{chainId}/blobs/{owner}/{scope}/{collectedAt}` so different chains
|
|
70
83
|
* never collide on the same host. The Web3Signed audience remains the endpoint
|
|
71
|
-
* origin regardless of
|
|
84
|
+
* origin regardless of network.
|
|
72
85
|
*
|
|
73
86
|
* @category Storage
|
|
74
87
|
*
|
|
@@ -93,6 +106,7 @@ export interface VanaStorageConfig {
|
|
|
93
106
|
*/
|
|
94
107
|
export declare class VanaStorage implements StorageProvider {
|
|
95
108
|
private readonly endpoint;
|
|
109
|
+
private readonly network?;
|
|
96
110
|
private readonly chainId?;
|
|
97
111
|
private readonly blobPathPrefix;
|
|
98
112
|
private readonly signer;
|
|
@@ -121,4 +135,5 @@ export declare class VanaStorage implements StorageProvider {
|
|
|
121
135
|
getConfig(): StorageProviderConfig;
|
|
122
136
|
private signRequest;
|
|
123
137
|
private pathFromUrl;
|
|
138
|
+
private namespaceDescription;
|
|
124
139
|
}
|
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
buildWeb3SignedHeader
|
|
6
6
|
} from "../../auth/web3-signed-builder.js";
|
|
7
|
+
import {
|
|
8
|
+
getProtocolNetworkChainId,
|
|
9
|
+
isProtocolNetwork
|
|
10
|
+
} from "../../protocol/networks.js";
|
|
7
11
|
const DEFAULT_ENDPOINT = "https://storage.vana.org";
|
|
8
12
|
const LEGACY_BLOB_PATH_PREFIX = "/v1/blobs";
|
|
9
13
|
const DEFAULT_TOKEN_TTL_SECONDS = 300;
|
|
@@ -11,6 +15,7 @@ const MAX_UPLOAD_ATTEMPTS = 4;
|
|
|
11
15
|
const MAX_RATE_LIMIT_DELAY_MS = 3e4;
|
|
12
16
|
class VanaStorage {
|
|
13
17
|
endpoint;
|
|
18
|
+
network;
|
|
14
19
|
chainId;
|
|
15
20
|
blobPathPrefix;
|
|
16
21
|
signer;
|
|
@@ -25,14 +30,9 @@ class VanaStorage {
|
|
|
25
30
|
);
|
|
26
31
|
}
|
|
27
32
|
this.endpoint = (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"INVALID_CHAIN_ID",
|
|
32
|
-
"vana-storage"
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
this.chainId = config.chainId;
|
|
33
|
+
const resolved = resolveStorageNamespace(config);
|
|
34
|
+
this.network = resolved.network;
|
|
35
|
+
this.chainId = resolved.chainId;
|
|
36
36
|
this.blobPathPrefix = this.chainId !== void 0 ? `/v1/chains/${this.chainId}/blobs` : LEGACY_BLOB_PATH_PREFIX;
|
|
37
37
|
this.signer = config.signer;
|
|
38
38
|
this.ownerAddress = (config.ownerAddress ?? config.signer.address).toLowerCase();
|
|
@@ -232,17 +232,51 @@ class VanaStorage {
|
|
|
232
232
|
}
|
|
233
233
|
if (route.chainId !== this.chainId) {
|
|
234
234
|
throw new StorageError(
|
|
235
|
-
`URL chainId '${route.chainId ?? "legacy"}' does not match provider
|
|
235
|
+
`URL chainId '${route.chainId ?? "legacy"}' does not match provider namespace '${this.namespaceDescription()}'`,
|
|
236
236
|
"INVALID_URL",
|
|
237
237
|
"vana-storage"
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
return parsed.pathname;
|
|
241
241
|
}
|
|
242
|
+
namespaceDescription() {
|
|
243
|
+
if (this.network !== void 0) {
|
|
244
|
+
return `${this.network} (${this.chainId})`;
|
|
245
|
+
}
|
|
246
|
+
return this.chainId === void 0 ? "legacy" : String(this.chainId);
|
|
247
|
+
}
|
|
242
248
|
}
|
|
243
249
|
function isValidChainId(value) {
|
|
244
250
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
245
251
|
}
|
|
252
|
+
function resolveStorageNamespace(config) {
|
|
253
|
+
if (config.network !== void 0 && !isProtocolNetwork(config.network)) {
|
|
254
|
+
throw new StorageError(
|
|
255
|
+
`Unsupported vana-storage network '${String(config.network)}'`,
|
|
256
|
+
"INVALID_NETWORK",
|
|
257
|
+
"vana-storage"
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
if (config.chainId !== void 0 && !isValidChainId(config.chainId)) {
|
|
261
|
+
throw new StorageError(
|
|
262
|
+
`Unsupported vana-storage chainId '${String(config.chainId)}'`,
|
|
263
|
+
"INVALID_CHAIN_ID",
|
|
264
|
+
"vana-storage"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (config.network === void 0) {
|
|
268
|
+
return { chainId: config.chainId };
|
|
269
|
+
}
|
|
270
|
+
const networkChainId = getProtocolNetworkChainId(config.network);
|
|
271
|
+
if (config.chainId !== void 0 && config.chainId !== networkChainId) {
|
|
272
|
+
throw new StorageError(
|
|
273
|
+
`vana-storage network '${config.network}' resolves to chainId '${networkChainId}', not '${config.chainId}'`,
|
|
274
|
+
"INVALID_CHAIN_ID",
|
|
275
|
+
"vana-storage"
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return { chainId: networkChainId, network: config.network };
|
|
279
|
+
}
|
|
246
280
|
function parseBlobPath(pathname) {
|
|
247
281
|
const segments = pathname.split("/").filter((s) => s.length > 0);
|
|
248
282
|
const isTraversal = (s) => s === "." || s === "..";
|