@opendatalabs/vana-sdk 3.17.0 → 3.18.0

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.
Files changed (47) hide show
  1. package/dist/direct/personal-server-read.cjs +20 -1
  2. package/dist/direct/personal-server-read.cjs.map +1 -1
  3. package/dist/direct/personal-server-read.js +23 -1
  4. package/dist/direct/personal-server-read.js.map +1 -1
  5. package/dist/errors.cjs +27 -0
  6. package/dist/errors.cjs.map +1 -1
  7. package/dist/errors.d.ts +67 -0
  8. package/dist/errors.js +24 -0
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.browser.d.ts +2 -1
  11. package/dist/index.browser.js +724 -309
  12. package/dist/index.browser.js.map +4 -4
  13. package/dist/index.node.cjs +736 -311
  14. package/dist/index.node.cjs.map +4 -4
  15. package/dist/index.node.d.ts +2 -1
  16. package/dist/index.node.js +724 -309
  17. package/dist/index.node.js.map +4 -4
  18. package/dist/protocol/data-point-deletion.cjs +220 -0
  19. package/dist/protocol/data-point-deletion.cjs.map +1 -0
  20. package/dist/protocol/data-point-deletion.d.ts +180 -0
  21. package/dist/protocol/data-point-deletion.js +193 -0
  22. package/dist/protocol/data-point-deletion.js.map +1 -0
  23. package/dist/protocol/data-point-deletion.test.d.ts +1 -0
  24. package/dist/protocol/gateway.cjs +145 -32
  25. package/dist/protocol/gateway.cjs.map +1 -1
  26. package/dist/protocol/gateway.d.ts +42 -3
  27. package/dist/protocol/gateway.js +156 -32
  28. package/dist/protocol/gateway.js.map +1 -1
  29. package/dist/protocol/personal-server-data.cjs +20 -1
  30. package/dist/protocol/personal-server-data.cjs.map +1 -1
  31. package/dist/protocol/personal-server-data.js +23 -1
  32. package/dist/protocol/personal-server-data.js.map +1 -1
  33. package/dist/storage/index.cjs.map +1 -1
  34. package/dist/storage/index.d.ts +1 -1
  35. package/dist/storage/index.js.map +1 -1
  36. package/dist/storage/providers/vana-storage.cjs +66 -0
  37. package/dist/storage/providers/vana-storage.cjs.map +1 -1
  38. package/dist/storage/providers/vana-storage.d.ts +26 -0
  39. package/dist/storage/providers/vana-storage.js +66 -0
  40. package/dist/storage/providers/vana-storage.js.map +1 -1
  41. package/dist/utils/response-body.cjs +46 -0
  42. package/dist/utils/response-body.cjs.map +1 -0
  43. package/dist/utils/response-body.d.ts +26 -0
  44. package/dist/utils/response-body.js +20 -0
  45. package/dist/utils/response-body.js.map +1 -0
  46. package/dist/utils/response-body.test.d.ts +1 -0
  47. package/package.json +1 -1
@@ -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\";\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
+ {"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\";\nimport { readJsonObject } from \"../../utils/response-body\";\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 * Response of {@link VanaStorage.deleteScope} -- the worker's\n * `DELETE /:owner/:scope` body. `count` is the number of blobs removed (0 is\n * a success: nothing was stored under that scope).\n *\n * @category Storage\n */\nexport interface VanaStorageScopeDeleteResult {\n deleted: boolean;\n scope: string;\n count: number;\n totalBytes: 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 /**\n * Delete every version's blob under `(owner, scope)` --\n * `DELETE {prefix}/{owner}/{scope}` on vana-storage, signed with the same\n * Web3Signed header as uploads (aud = endpoint origin, empty bodyHash).\n * The worker accepts the owner's own signature or a personal server the\n * owner registered with the gateway.\n *\n * @param ownerAddress - Must equal the provider's configured owner; a\n * mismatch throws before anything is signed so this wallet can never be\n * induced to sign a delete for another namespace.\n * @param scope - The scope segment, e.g. `\"instagram.profile\"`.\n */\n async deleteScope(\n ownerAddress: `0x${string}`,\n scope: string,\n ): Promise<VanaStorageScopeDeleteResult> {\n if (ownerAddress.toLowerCase() !== this.ownerAddress) {\n throw new StorageError(\n `deleteScope owner '${ownerAddress}' does not match the configured owner '${this.ownerAddress}'`,\n \"INVALID_OWNER\",\n \"vana-storage\",\n );\n }\n if (\n scope.length === 0 ||\n scope.includes(\"/\") ||\n isTraversalSegment(scope)\n ) {\n throw new StorageError(\n `scope must be a single non-empty path segment, got '${scope}'`,\n \"INVALID_SCOPE\",\n \"vana-storage\",\n );\n }\n\n const path = `${this.blobPathPrefix}/${this.ownerAddress}/${encodeURIComponent(scope)}`;\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 scope delete network error: ${describe(cause)}`,\n \"DELETE_ERROR\",\n \"vana-storage\",\n { cause: cause instanceof Error ? cause : undefined },\n );\n }\n\n if (!response.ok) {\n const responseText = await safeText(response);\n throw new StorageError(\n `vana-storage scope delete failed: ${response.status} ${response.statusText} - ${responseText}`,\n \"DELETE_FAILED\",\n \"vana-storage\",\n );\n }\n\n // A 2xx is the success signal; the body is informational. Tolerate an\n // empty 204, a non-JSON body, or a JSON `null`/array, and only trust\n // fields that carry the documented type.\n const body = await readJsonObject(response);\n return {\n deleted: typeof body[\"deleted\"] === \"boolean\" ? body[\"deleted\"] : true,\n scope: typeof body[\"scope\"] === \"string\" ? body[\"scope\"] : scope,\n count: nonNegativeInteger(body[\"count\"]) ?? 0,\n totalBytes: nonNegativeInteger(body[\"totalBytes\"]) ?? 0,\n };\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 isTraversalSegment(segment: string): boolean {\n return segment === \".\" || segment === \"..\";\n}\n\nfunction nonNegativeInteger(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0\n ? value\n : undefined;\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":"AAAA;AAAA,EACE;AAAA,OAMK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,sBAAsB;AAE/B,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;AAChC,MAAM,4BAA4B;AAClC,MAAM,sBAAsB;AAC5B,MAAM,0BAA0B;AAgIzB,MAAM,YAAuC;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAA2B;AACrC,QAAI,CAAC,QAAQ,QAAQ,WAAW,CAAC,QAAQ,QAAQ,aAAa;AAC5D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,YAAY,OAAO,YAAY,kBAAkB,QAAQ,QAAQ,EAAE;AACxE,UAAM,WAAW,wBAAwB,MAAM;AAC/C,SAAK,UAAU,SAAS;AACxB,SAAK,UAAU,SAAS;AACxB,SAAK,iBACH,KAAK,YAAY,SACb,cAAc,KAAK,OAAO,WAC1B;AACN,SAAK,SAAS,OAAO;AACrB,SAAK,gBACH,OAAO,gBAAgB,OAAO,OAAO,SACrC,YAAY;AACd,SAAK,YAAY,OAAO,aAAa,WAAW,MAAM,KAAK,UAAU;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAO,MAAY,UAAiD;AACxE,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,mBAAmB,QAAQ;AAC3C,UAAM,OAAO,GAAG,KAAK,cAAc,IAAI,KAAK,YAAY,IAAI,OAAO;AACnE,UAAM,OAAO,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;AACpD,UAAM,cACJ,KAAK,SAAS,KAAK,KAAK,OAAO;AAEjC,UAAM,SAAS,MAAM,KAAK,YAAY,OAAO,MAAM,IAAI;AAEvD,QAAI,WAA4B;AAChC,QAAI,eAAe;AACnB,aAAS,UAAU,GAAG,WAAW,qBAAqB,WAAW;AAC/D,UAAI;AACF,mBAAW,MAAM,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,UACzD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe;AAAA,YACf,gBAAgB;AAAA,UAClB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,SAAS,OAAO;AACd,cAAM,IAAI;AAAA,UACR,sCAAsC,SAAS,KAAK,CAAC;AAAA,UACrD;AAAA,UACA;AAAA,UACA,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,OAAU;AAAA,QACtD;AAAA,MACF;AAEA,UAAI,SAAS,IAAI;AACf;AAAA,MACF;AAEA,qBAAe,MAAM,SAAS,QAAQ;AACtC,UAAI,SAAS,WAAW,OAAO,UAAU,qBAAqB;AAC5D,cAAM,UAAU,aAAa,UAAU,YAAY;AACnD,YAAI,UAAU,GAAG;AACf,gBAAM,MAAM,OAAO;AAAA,QACrB;AACA;AAAA,MACF;AAEA,YAAM,IAAI;AAAA,QACR,+BAA+B,SAAS,MAAM,IAAI,SAAS,UAAU,MAAM,YAAY;AAAA,QACvF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,IAAI;AACjB,YAAM,IAAI;AAAA,QACR,oCAAoC,mBAAmB,eAAe,YAAY;AAAA,QAClF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAU,MAAM,SAAS,KAAK;AACpC,WAAO;AAAA,MACL,KAAK,OAAO;AAAA,MACZ,MAAM,OAAO;AAAA,MACb;AAAA,MACA,UAAU,EAAE,KAAK,OAAO,KAAK,MAAM,OAAO,KAAK;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAAS,KAA4B;AACzC,UAAM,OAAO,KAAK,YAAY,GAAG;AACjC,UAAM,SAAS,MAAM,KAAK,YAAY,OAAO,IAAI;AAEjD,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,OAAO;AAAA,MACnC,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,wCAAwC,SAAS,KAAK,CAAC;AAAA,QACvD;AAAA,QACA;AAAA,QACA,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,OAAU;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,iCAAiC,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,QACvE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAK,UAAuD;AAChE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,UAAM,OAAO,KAAK,YAAY,GAAG;AACjC,UAAM,SAAS,MAAM,KAAK,YAAY,UAAU,IAAI;AAEpD,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,OAAO;AAAA,MACnC,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,sCAAsC,SAAS,KAAK,CAAC;AAAA,QACrD;AAAA,QACA;AAAA,QACA,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,OAAU;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,SAAS,WAAW,IAAK,QAAO;AACpC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,+BAA+B,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,QACrE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,YACJ,cACA,OACuC;AACvC,QAAI,aAAa,YAAY,MAAM,KAAK,cAAc;AACpD,YAAM,IAAI;AAAA,QACR,sBAAsB,YAAY,0CAA0C,KAAK,YAAY;AAAA,QAC7F;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QACE,MAAM,WAAW,KACjB,MAAM,SAAS,GAAG,KAClB,mBAAmB,KAAK,GACxB;AACA,YAAM,IAAI;AAAA,QACR,uDAAuD,KAAK;AAAA,QAC5D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,GAAG,KAAK,cAAc,IAAI,KAAK,YAAY,IAAI,mBAAmB,KAAK,CAAC;AACrF,UAAM,SAAS,MAAM,KAAK,YAAY,UAAU,IAAI;AAEpD,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,OAAO;AAAA,MACnC,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,4CAA4C,SAAS,KAAK,CAAC;AAAA,QAC3D;AAAA,QACA;AAAA,QACA,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,OAAU;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,eAAe,MAAM,SAAS,QAAQ;AAC5C,YAAM,IAAI;AAAA,QACR,qCAAqC,SAAS,MAAM,IAAI,SAAS,UAAU,MAAM,YAAY;AAAA,QAC7F;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAKA,UAAM,OAAO,MAAM,eAAe,QAAQ;AAC1C,WAAO;AAAA,MACL,SAAS,OAAO,KAAK,SAAS,MAAM,YAAY,KAAK,SAAS,IAAI;AAAA,MAClE,OAAO,OAAO,KAAK,OAAO,MAAM,WAAW,KAAK,OAAO,IAAI;AAAA,MAC3D,OAAO,mBAAmB,KAAK,OAAO,CAAC,KAAK;AAAA,MAC5C,YAAY,mBAAmB,KAAK,YAAY,CAAC,KAAK;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,YAAmC;AACjC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,cAAc;AAAA,MACd,UAAU;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,YACZ,QACA,MACA,MACiB;AACjB,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,WAAO,sBAAsB;AAAA,MAC3B,aAAa,KAAK,OAAO;AAAA,MACzB,KAAK,KAAK;AAAA,MACV;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,MAAM;AAAA,MACX,GAAI,SAAS,UAAa,KAAK,SAAS,KAAK,EAAE,KAAK;AAAA,IACtD,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,KAAqB;AACvC,QAAI;AACJ,QAAI;AACF,eAAS,IAAI,IAAI,GAAG;AAAA,IACtB,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,gBAAgB,GAAG;AAAA,QACnB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,eAAe,IAAI,IAAI,KAAK,QAAQ,EAAE;AAC5C,QAAI,OAAO,SAAS,cAAc;AAChC,YAAM,IAAI;AAAA,QACR,aAAa,OAAO,IAAI,sCAAsC,YAAY;AAAA,QAC1E;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,cAAc,OAAO,QAAQ;AAC3C,QAAI,CAAC,SAAS,MAAM,MAAM,YAAY,MAAM,KAAK,cAAc;AAC7D,YAAM,IAAI;AAAA,QACR,aAAa,OAAO,QAAQ,aAAa,KAAK,cAAc,IAAI,KAAK,YAAY;AAAA,QACjF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAIA,QAAI,MAAM,YAAY,KAAK,SAAS;AAClC,YAAM,IAAI;AAAA,QACR,gBAAgB,MAAM,WAAW,QAAQ,wCAAwC,KAAK,qBAAqB,CAAC;AAAA,QAC5G;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,OAAO;AAAA,EAChB;AAAA,EAEQ,uBAA+B;AACrC,QAAI,KAAK,YAAY,QAAW;AAC9B,aAAO,GAAG,KAAK,OAAO,KAAK,KAAK,OAAO;AAAA,IACzC;AACA,WAAO,KAAK,YAAY,SAAY,WAAW,OAAO,KAAK,OAAO;AAAA,EACpE;AACF;AASA,SAAS,eAAe,OAAiC;AACvD,SAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ;AACzE;AAEA,SAAS,wBACP,QAIA;AACA,MAAI,OAAO,YAAY,UAAa,CAAC,kBAAkB,OAAO,OAAO,GAAG;AACtE,UAAM,IAAI;AAAA,MACR,qCAAqC,OAAO,OAAO,OAAO,CAAC;AAAA,MAC3D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,YAAY,UAAa,CAAC,eAAe,OAAO,OAAO,GAAG;AACnE,UAAM,IAAI;AAAA,MACR,qCAAqC,OAAO,OAAO,OAAO,CAAC;AAAA,MAC3D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,YAAY,QAAW;AAChC,WAAO,EAAE,SAAS,OAAO,QAAQ;AAAA,EACnC;AAEA,QAAM,iBAAiB,0BAA0B,OAAO,OAAO;AAC/D,MAAI,OAAO,YAAY,UAAa,OAAO,YAAY,gBAAgB;AACrE,UAAM,IAAI;AAAA,MACR,yBAAyB,OAAO,OAAO,0BAA0B,cAAc,WAAW,OAAO,OAAO;AAAA,MACxG;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,gBAAgB,SAAS,OAAO,QAAQ;AAC5D;AAOA,SAAS,cAAc,UAA0C;AAC/D,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC/D,QAAM,cAAc,CAAC,MAAuB,MAAM,OAAO,MAAM;AAG/D,MACE,SAAS,WAAW,KACpB,SAAS,CAAC,MAAM,QAChB,SAAS,CAAC,MAAM,SAChB;AACA,UAAM,CAAC,EAAE,EAAE,OAAO,OAAO,WAAW,IAAI;AAOxC,QAAI,YAAY,KAAK,KAAK,YAAY,WAAW,EAAG,QAAO;AAC3D,WAAO,EAAE,OAAO,OAAO,YAAY;AAAA,EACrC;AAGA,MACE,SAAS,WAAW,KACpB,SAAS,CAAC,MAAM,QAChB,SAAS,CAAC,MAAM,YAChB,SAAS,CAAC,MAAM,SAChB;AACA,UAAM,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,OAAO,WAAW,IAAI;AAS1D,QAAI,CAAC,WAAW,KAAK,cAAc,EAAG,QAAO;AAC7C,UAAM,UAAU,OAAO,cAAc;AACrC,QAAI,CAAC,eAAe,OAAO,EAAG,QAAO;AACrC,QAAI,YAAY,KAAK,KAAK,YAAY,WAAW,EAAG,QAAO;AAC3D,WAAO,EAAE,SAAS,OAAO,OAAO,YAAY;AAAA,EAC9C;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,UAA0B;AACpD,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,MACE,MAAM,WAAW,KACjB,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,MAAM,OAAO,MAAM,IAAI,GAC3D;AACA,UAAM,IAAI;AAAA,MACR,kFAAkF,QAAQ;AAAA,MAC1F;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,IAAI,CAAC,MAAM,mBAAmB,CAAC,CAAC,EAAE,KAAK,GAAG;AACzD;AAEA,SAAS,mBAAmB,SAA0B;AACpD,SAAO,YAAY,OAAO,YAAY;AACxC;AAEA,SAAS,mBAAmB,OAAoC;AAC9D,SAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,IACpE,QACA;AACN;AAEA,SAAS,SAAS,OAAwB;AACxC,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,aAAa,UAAoB,cAA8B;AACtE,QAAM,gBAAgB;AAAA,IACpB,SAAS,QAAQ,IAAI,aAAa;AAAA,EACpC;AACA,MAAI,kBAAkB,MAAM;AAC1B,WAAO,oBAAoB,aAAa;AAAA,EAC1C;AAEA,SAAO,oBAAoB,sBAAsB,YAAY,KAAK,CAAC;AACrE;AAEA,SAAS,wBAAwB,OAAqC;AACpE,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,WAAO,UAAU;AAAA,EACnB;AAEA,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,MAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,WAAO,KAAK,IAAI,GAAG,SAAS,KAAK,IAAI,CAAC;AAAA,EACxC;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,cAAqC;AAClE,MAAI,CAAC,aAAc,QAAO;AAE1B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,YAAY;AACtC,UAAM,UAAU,OAAO,OAAO,UAAU;AACxC,WAAO,OAAO,SAAS,OAAO,IAAI,UAAU,MAAO;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,SAAyB;AACpD,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,GAAG,uBAAuB;AAC/D;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,eAAe,SAAS,UAAqC;AAC3D,MAAI;AACF,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,46 @@
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 response_body_exports = {};
20
+ __export(response_body_exports, {
21
+ isPlainObject: () => isPlainObject,
22
+ readJsonObject: () => readJsonObject,
23
+ readJsonValue: () => readJsonValue
24
+ });
25
+ module.exports = __toCommonJS(response_body_exports);
26
+ function isPlainObject(value) {
27
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28
+ }
29
+ async function readJsonValue(res) {
30
+ try {
31
+ return await res.json();
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ async function readJsonObject(res) {
37
+ const value = await readJsonValue(res);
38
+ return isPlainObject(value) ? value : {};
39
+ }
40
+ // Annotate the CommonJS export names for ESM import in node:
41
+ 0 && (module.exports = {
42
+ isPlainObject,
43
+ readJsonObject,
44
+ readJsonValue
45
+ });
46
+ //# sourceMappingURL=response-body.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/response-body.ts"],"sourcesContent":["/**\n * Tolerant JSON body readers for `fetch` responses.\n *\n * A response body can legitimately be empty (204), non-JSON (an HTML error\n * page from a proxy), or JSON that is not an object (`null`, an array, a\n * string). Every SDK code path that indexes into a parsed body must go\n * through these helpers so those shapes degrade to \"no fields\" instead of a\n * `TypeError` that bypasses the typed error the caller expects.\n *\n * @internal\n */\n\n/** The one method the readers need, so custom fetch shims qualify too. */\nexport type JsonBodySource = Pick<Response, \"json\">;\n\n/** True for a non-null, non-array object -- the only shape safe to index. */\nexport function isPlainObject(\n value: unknown,\n): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse the body as JSON. Resolves `null` when the body is empty or not\n * valid JSON. Never throws.\n */\nexport async function readJsonValue(res: JsonBodySource): Promise<unknown> {\n try {\n return (await res.json()) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse the body as JSON and return it only when it is a plain object;\n * every other shape (empty, non-JSON, `null`, array, primitive) becomes `{}`.\n * Never throws.\n */\nexport async function readJsonObject(\n res: JsonBodySource,\n): Promise<Record<string, unknown>> {\n const value = await readJsonValue(res);\n return isPlainObject(value) ? value : {};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,SAAS,cACd,OACkC;AAClC,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAMA,eAAsB,cAAc,KAAuC;AACzE,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,eACpB,KACkC;AAClC,QAAM,QAAQ,MAAM,cAAc,GAAG;AACrC,SAAO,cAAc,KAAK,IAAI,QAAQ,CAAC;AACzC;","names":[]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Tolerant JSON body readers for `fetch` responses.
3
+ *
4
+ * A response body can legitimately be empty (204), non-JSON (an HTML error
5
+ * page from a proxy), or JSON that is not an object (`null`, an array, a
6
+ * string). Every SDK code path that indexes into a parsed body must go
7
+ * through these helpers so those shapes degrade to "no fields" instead of a
8
+ * `TypeError` that bypasses the typed error the caller expects.
9
+ *
10
+ * @internal
11
+ */
12
+ /** The one method the readers need, so custom fetch shims qualify too. */
13
+ export type JsonBodySource = Pick<Response, "json">;
14
+ /** True for a non-null, non-array object -- the only shape safe to index. */
15
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
16
+ /**
17
+ * Parse the body as JSON. Resolves `null` when the body is empty or not
18
+ * valid JSON. Never throws.
19
+ */
20
+ export declare function readJsonValue(res: JsonBodySource): Promise<unknown>;
21
+ /**
22
+ * Parse the body as JSON and return it only when it is a plain object;
23
+ * every other shape (empty, non-JSON, `null`, array, primitive) becomes `{}`.
24
+ * Never throws.
25
+ */
26
+ export declare function readJsonObject(res: JsonBodySource): Promise<Record<string, unknown>>;
@@ -0,0 +1,20 @@
1
+ function isPlainObject(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ async function readJsonValue(res) {
5
+ try {
6
+ return await res.json();
7
+ } catch {
8
+ return null;
9
+ }
10
+ }
11
+ async function readJsonObject(res) {
12
+ const value = await readJsonValue(res);
13
+ return isPlainObject(value) ? value : {};
14
+ }
15
+ export {
16
+ isPlainObject,
17
+ readJsonObject,
18
+ readJsonValue
19
+ };
20
+ //# sourceMappingURL=response-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/response-body.ts"],"sourcesContent":["/**\n * Tolerant JSON body readers for `fetch` responses.\n *\n * A response body can legitimately be empty (204), non-JSON (an HTML error\n * page from a proxy), or JSON that is not an object (`null`, an array, a\n * string). Every SDK code path that indexes into a parsed body must go\n * through these helpers so those shapes degrade to \"no fields\" instead of a\n * `TypeError` that bypasses the typed error the caller expects.\n *\n * @internal\n */\n\n/** The one method the readers need, so custom fetch shims qualify too. */\nexport type JsonBodySource = Pick<Response, \"json\">;\n\n/** True for a non-null, non-array object -- the only shape safe to index. */\nexport function isPlainObject(\n value: unknown,\n): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse the body as JSON. Resolves `null` when the body is empty or not\n * valid JSON. Never throws.\n */\nexport async function readJsonValue(res: JsonBodySource): Promise<unknown> {\n try {\n return (await res.json()) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse the body as JSON and return it only when it is a plain object;\n * every other shape (empty, non-JSON, `null`, array, primitive) becomes `{}`.\n * Never throws.\n */\nexport async function readJsonObject(\n res: JsonBodySource,\n): Promise<Record<string, unknown>> {\n const value = await readJsonValue(res);\n return isPlainObject(value) ? value : {};\n}\n"],"mappings":"AAgBO,SAAS,cACd,OACkC;AAClC,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAMA,eAAsB,cAAc,KAAuC;AACzE,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,eACpB,KACkC;AAClC,QAAM,QAAQ,MAAM,cAAc,GAAG;AACrC,SAAO,cAAc,KAAK,IAAI,QAAQ,CAAC;AACzC;","names":[]}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/vana-sdk",
3
- "version": "3.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "A TypeScript library for interacting with Vana Network smart contracts.",
5
5
  "publishConfig": {
6
6
  "access": "public"