@opendatalabs/vana-sdk 3.4.1 → 3.5.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.
Files changed (37) hide show
  1. package/dist/account/personal-server-lite-owner-binding.cjs +3 -3
  2. package/dist/account/personal-server-lite-owner-binding.cjs.map +1 -1
  3. package/dist/account/personal-server-lite-owner-binding.d.ts +2 -2
  4. package/dist/account/personal-server-lite-owner-binding.js +1 -1
  5. package/dist/account/personal-server-lite-owner-binding.js.map +1 -1
  6. package/dist/index.browser.d.ts +2 -1
  7. package/dist/index.browser.js +208 -23
  8. package/dist/index.browser.js.map +4 -4
  9. package/dist/index.node.cjs +213 -23
  10. package/dist/index.node.cjs.map +4 -4
  11. package/dist/index.node.d.ts +2 -1
  12. package/dist/index.node.js +208 -23
  13. package/dist/index.node.js.map +4 -4
  14. package/dist/{protocol/personal-server-lite-owner-binding.cjs → personal-server-lite/owner-binding.cjs} +5 -5
  15. package/dist/personal-server-lite/owner-binding.cjs.map +1 -0
  16. package/dist/{protocol/personal-server-lite-owner-binding.d.ts → personal-server-lite/owner-binding.d.ts} +3 -3
  17. package/dist/{protocol/personal-server-lite-owner-binding.js → personal-server-lite/owner-binding.js} +2 -2
  18. package/dist/personal-server-lite/owner-binding.js.map +1 -0
  19. package/dist/protocol/escrow.cjs +146 -0
  20. package/dist/protocol/escrow.cjs.map +1 -0
  21. package/dist/protocol/escrow.d.ts +336 -0
  22. package/dist/protocol/escrow.js +118 -0
  23. package/dist/protocol/escrow.js.map +1 -0
  24. package/dist/protocol/escrow.test.d.ts +1 -0
  25. package/dist/protocol/personal-server-registration.cjs +16 -4
  26. package/dist/protocol/personal-server-registration.cjs.map +1 -1
  27. package/dist/protocol/personal-server-registration.d.ts +5 -2
  28. package/dist/protocol/personal-server-registration.js +16 -4
  29. package/dist/protocol/personal-server-registration.js.map +1 -1
  30. package/dist/storage/providers/vana-storage.cjs +75 -17
  31. package/dist/storage/providers/vana-storage.cjs.map +1 -1
  32. package/dist/storage/providers/vana-storage.js +75 -17
  33. package/dist/storage/providers/vana-storage.js.map +1 -1
  34. package/package.json +1 -1
  35. package/dist/protocol/personal-server-lite-owner-binding.cjs.map +0 -1
  36. package/dist/protocol/personal-server-lite-owner-binding.js.map +0 -1
  37. /package/dist/{protocol/personal-server-lite-owner-binding.test.d.ts → personal-server-lite/owner-binding.test.d.ts} +0 -0
@@ -7,6 +7,8 @@ import {
7
7
  const DEFAULT_ENDPOINT = "https://storage.vana.org";
8
8
  const BLOB_PATH_PREFIX = "/v1/blobs";
9
9
  const DEFAULT_TOKEN_TTL_SECONDS = 300;
10
+ const MAX_UPLOAD_ATTEMPTS = 4;
11
+ const MAX_RATE_LIMIT_DELAY_MS = 3e4;
10
12
  class VanaStorage {
11
13
  endpoint;
12
14
  signer;
@@ -45,27 +47,46 @@ class VanaStorage {
45
47
  const body = new Uint8Array(await file.arrayBuffer());
46
48
  const contentType = file.type !== "" ? file.type : "application/octet-stream";
47
49
  const header = await this.signRequest("PUT", path, body);
48
- let response;
49
- try {
50
- response = await this.fetchImpl(`${this.endpoint}${path}`, {
51
- method: "PUT",
52
- headers: {
53
- authorization: header,
54
- "content-type": contentType
55
- },
56
- body
57
- });
58
- } catch (cause) {
50
+ let response = null;
51
+ let responseText = "";
52
+ for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt++) {
53
+ try {
54
+ response = await this.fetchImpl(`${this.endpoint}${path}`, {
55
+ method: "PUT",
56
+ headers: {
57
+ authorization: header,
58
+ "content-type": contentType
59
+ },
60
+ body
61
+ });
62
+ } catch (cause) {
63
+ throw new StorageError(
64
+ `vana-storage upload network error: ${describe(cause)}`,
65
+ "UPLOAD_ERROR",
66
+ "vana-storage",
67
+ { cause: cause instanceof Error ? cause : void 0 }
68
+ );
69
+ }
70
+ if (response.ok) {
71
+ break;
72
+ }
73
+ responseText = await safeText(response);
74
+ if (response.status === 429 && attempt < MAX_UPLOAD_ATTEMPTS) {
75
+ const delayMs = retryDelayMs(response, responseText);
76
+ if (delayMs > 0) {
77
+ await sleep(delayMs);
78
+ }
79
+ continue;
80
+ }
59
81
  throw new StorageError(
60
- `vana-storage upload network error: ${describe(cause)}`,
61
- "UPLOAD_ERROR",
62
- "vana-storage",
63
- { cause: cause instanceof Error ? cause : void 0 }
82
+ `vana-storage upload failed: ${response.status} ${response.statusText} - ${responseText}`,
83
+ "UPLOAD_FAILED",
84
+ "vana-storage"
64
85
  );
65
86
  }
66
- if (!response.ok) {
87
+ if (!response?.ok) {
67
88
  throw new StorageError(
68
- `vana-storage upload failed: ${response.status} ${response.statusText} - ${await safeText(response)}`,
89
+ `vana-storage upload failed after ${MAX_UPLOAD_ATTEMPTS} attempts - ${responseText}`,
69
90
  "UPLOAD_FAILED",
70
91
  "vana-storage"
71
92
  );
@@ -218,6 +239,43 @@ function describe(value) {
218
239
  if (value instanceof Error) return value.message;
219
240
  return String(value);
220
241
  }
242
+ function retryDelayMs(response, responseText) {
243
+ const headerDelayMs = parseRetryAfterHeaderMs(
244
+ response.headers.get("retry-after")
245
+ );
246
+ if (headerDelayMs !== null) {
247
+ return clampRateLimitDelay(headerDelayMs);
248
+ }
249
+ return clampRateLimitDelay(parseRetryAfterBodyMs(responseText) ?? 0);
250
+ }
251
+ function parseRetryAfterHeaderMs(value) {
252
+ if (!value) return null;
253
+ const seconds = Number(value);
254
+ if (Number.isFinite(seconds)) {
255
+ return seconds * 1e3;
256
+ }
257
+ const dateMs = Date.parse(value);
258
+ if (Number.isFinite(dateMs)) {
259
+ return Math.max(0, dateMs - Date.now());
260
+ }
261
+ return null;
262
+ }
263
+ function parseRetryAfterBodyMs(responseText) {
264
+ if (!responseText) return null;
265
+ try {
266
+ const parsed = JSON.parse(responseText);
267
+ const seconds = Number(parsed.retryAfter);
268
+ return Number.isFinite(seconds) ? seconds * 1e3 : null;
269
+ } catch {
270
+ return null;
271
+ }
272
+ }
273
+ function clampRateLimitDelay(delayMs) {
274
+ return Math.min(Math.max(0, delayMs), MAX_RATE_LIMIT_DELAY_MS);
275
+ }
276
+ function sleep(ms) {
277
+ return new Promise((resolve) => setTimeout(resolve, ms));
278
+ }
221
279
  async function safeText(response) {
222
280
  try {
223
281
  return await response.text();
@@ -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 BLOB_PATH_PREFIX = \"/v1/blobs\";\nconst DEFAULT_TOKEN_TTL_SECONDS = 300;\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 endpoint?: string;\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 * @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 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 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 = `${BLOB_PATH_PREFIX}/${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;\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 throw new StorageError(\n `vana-storage upload failed: ${response.status} ${response.statusText} - ${await safeText(response)}`,\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 /v1/blobs/{owner}/{scope}/{collectedAt} so a caller\n // cannot induce this wallet to sign arbitrary same-host paths.\n const segments = parsed.pathname.split(\"/\").filter((s) => s.length > 0);\n const isTraversal = (s: string): boolean => s === \".\" || s === \"..\";\n const valid =\n segments.length === 5 &&\n segments[0] === \"v1\" &&\n segments[1] === \"blobs\" &&\n segments[2]?.toLowerCase() === this.ownerAddress &&\n segments[3] !== undefined &&\n !isTraversal(segments[3]) &&\n segments[4] !== undefined &&\n !isTraversal(segments[4]);\n if (!valid) {\n throw new StorageError(\n `URL path '${parsed.pathname}' must be /v1/blobs/${this.ownerAddress}/{scope}/{collectedAt}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n return parsed.pathname;\n }\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\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\";\n\nconst DEFAULT_ENDPOINT = \"https://storage.vana.org\";\nconst 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 endpoint?: string;\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 * @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 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 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 = `${BLOB_PATH_PREFIX}/${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 /v1/blobs/{owner}/{scope}/{collectedAt} so a caller\n // cannot induce this wallet to sign arbitrary same-host paths.\n const segments = parsed.pathname.split(\"/\").filter((s) => s.length > 0);\n const isTraversal = (s: string): boolean => s === \".\" || s === \"..\";\n const valid =\n segments.length === 5 &&\n segments[0] === \"v1\" &&\n segments[1] === \"blobs\" &&\n segments[2]?.toLowerCase() === this.ownerAddress &&\n segments[3] !== undefined &&\n !isTraversal(segments[3]) &&\n segments[4] !== undefined &&\n !isTraversal(segments[4]);\n if (!valid) {\n throw new StorageError(\n `URL path '${parsed.pathname}' must be /v1/blobs/${this.ownerAddress}/{scope}/{collectedAt}`,\n \"INVALID_URL\",\n \"vana-storage\",\n );\n }\n return parsed.pathname;\n }\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/vana-sdk",
3
- "version": "3.4.1",
3
+ "version": "3.5.1",
4
4
  "description": "A TypeScript library for interacting with Vana Network smart contracts.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/protocol/personal-server-lite-owner-binding.ts"],"sourcesContent":["/**\n * PS Lite owner-binding message and signing helpers.\n *\n * PS Lite uses this replayable personal-sign message as a wallet-owned input\n * for opening the user's local encrypted runtime. This is intentionally\n * separate from Personal Server registration, which is EIP-712 typed data.\n *\n * @category Protocol\n */\n\nimport {\n isAddress,\n type Account,\n type Address,\n type Hex,\n type SignableMessage,\n} from \"viem\";\n\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION = \"vana.account.v1\";\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE = \"ps-lite-owner\";\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX =\n `${PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION}:${PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE}:` as const;\n\nexport type PersonalServerLiteOwnerBindingPurpose =\n typeof PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE;\n\nexport type PersonalServerLiteOwnerBindingMessage =\n `${typeof PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX}${Lowercase<Address>}`;\n\nexport interface PersonalServerLiteOwnerBindingSigner {\n address: Address;\n signMessage(input: {\n message: PersonalServerLiteOwnerBindingMessage;\n }): Promise<Hex> | Hex;\n}\n\nexport interface ViemPersonalServerLiteOwnerBindingWalletClient {\n account?: Account | Address | null;\n signMessage(input: {\n account?: Account | Address;\n message: SignableMessage;\n }): Promise<Hex>;\n}\n\nexport type ViemPersonalServerLiteOwnerBindingSignerSource =\n | PersonalServerLiteOwnerBindingSigner\n | ViemPersonalServerLiteOwnerBindingWalletClient;\n\nexport interface BuildPersonalServerLiteOwnerBindingSignatureInput {\n signer: PersonalServerLiteOwnerBindingSigner;\n}\n\nexport interface PersonalServerLiteOwnerBindingSignature {\n signature: Hex;\n signerAddress: Address;\n message: PersonalServerLiteOwnerBindingMessage;\n purpose: PersonalServerLiteOwnerBindingPurpose;\n}\n\nfunction assertAddress(value: Address, name: string): void {\n if (!isAddress(value)) {\n throw new Error(`${name} must be a valid EVM address`);\n }\n}\n\nfunction getAccountAddress(\n account: Account | Address | null | undefined,\n): Address | undefined {\n if (!account) {\n return undefined;\n }\n\n return typeof account === \"string\" ? account : account.address;\n}\n\nfunction isPersonalServerLiteOwnerBindingSigner(\n source: ViemPersonalServerLiteOwnerBindingSignerSource,\n): source is PersonalServerLiteOwnerBindingSigner {\n return \"address\" in source && typeof source.signMessage === \"function\";\n}\n\nexport function buildPersonalServerLiteOwnerBindingMessage(\n ownerAddress: Address,\n): PersonalServerLiteOwnerBindingMessage {\n assertAddress(ownerAddress, \"ownerAddress\");\n return `${PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX}${ownerAddress.toLowerCase()}` as PersonalServerLiteOwnerBindingMessage;\n}\n\nexport function createViemPersonalServerLiteOwnerBindingSigner(\n source: ViemPersonalServerLiteOwnerBindingSignerSource,\n options: { account?: Account | Address } = {},\n): PersonalServerLiteOwnerBindingSigner {\n if (isPersonalServerLiteOwnerBindingSigner(source)) {\n return source;\n }\n\n const accountAddress =\n getAccountAddress(options.account) ?? getAccountAddress(source.account);\n\n if (accountAddress) {\n return {\n address: accountAddress,\n signMessage: ({ message }) =>\n source.signMessage({\n account: options.account ?? source.account ?? accountAddress,\n message,\n }),\n };\n }\n\n throw new Error(\n \"Viem wallet client requires an account option or account property\",\n );\n}\n\nexport async function buildPersonalServerLiteOwnerBindingSignature(\n input: BuildPersonalServerLiteOwnerBindingSignatureInput,\n): Promise<PersonalServerLiteOwnerBindingSignature> {\n const message = buildPersonalServerLiteOwnerBindingMessage(\n input.signer.address,\n );\n const signature = await input.signer.signMessage({ message });\n\n return {\n signature,\n signerAddress: input.signer.address,\n message,\n purpose: PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE,\n };\n}\n\nexport const signPersonalServerLiteOwnerBinding =\n buildPersonalServerLiteOwnerBindingSignature;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,kBAMO;AAEA,MAAM,6CAA6C;AACnD,MAAM,6CAA6C;AACnD,MAAM,4CACX,GAAG,0CAA0C,IAAI,0CAA0C;AAsC7F,SAAS,cAAc,OAAgB,MAAoB;AACzD,MAAI,KAAC,uBAAU,KAAK,GAAG;AACrB,UAAM,IAAI,MAAM,GAAG,IAAI,8BAA8B;AAAA,EACvD;AACF;AAEA,SAAS,kBACP,SACqB;AACrB,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,YAAY,WAAW,UAAU,QAAQ;AACzD;AAEA,SAAS,uCACP,QACgD;AAChD,SAAO,aAAa,UAAU,OAAO,OAAO,gBAAgB;AAC9D;AAEO,SAAS,2CACd,cACuC;AACvC,gBAAc,cAAc,cAAc;AAC1C,SAAO,GAAG,yCAAyC,GAAG,aAAa,YAAY,CAAC;AAClF;AAEO,SAAS,+CACd,QACA,UAA2C,CAAC,GACN;AACtC,MAAI,uCAAuC,MAAM,GAAG;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,iBACJ,kBAAkB,QAAQ,OAAO,KAAK,kBAAkB,OAAO,OAAO;AAExE,MAAI,gBAAgB;AAClB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,aAAa,CAAC,EAAE,QAAQ,MACtB,OAAO,YAAY;AAAA,QACjB,SAAS,QAAQ,WAAW,OAAO,WAAW;AAAA,QAC9C;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,6CACpB,OACkD;AAClD,QAAM,UAAU;AAAA,IACd,MAAM,OAAO;AAAA,EACf;AACA,QAAM,YAAY,MAAM,MAAM,OAAO,YAAY,EAAE,QAAQ,CAAC;AAE5D,SAAO;AAAA,IACL;AAAA,IACA,eAAe,MAAM,OAAO;AAAA,IAC5B;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEO,MAAM,qCACX;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/protocol/personal-server-lite-owner-binding.ts"],"sourcesContent":["/**\n * PS Lite owner-binding message and signing helpers.\n *\n * PS Lite uses this replayable personal-sign message as a wallet-owned input\n * for opening the user's local encrypted runtime. This is intentionally\n * separate from Personal Server registration, which is EIP-712 typed data.\n *\n * @category Protocol\n */\n\nimport {\n isAddress,\n type Account,\n type Address,\n type Hex,\n type SignableMessage,\n} from \"viem\";\n\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION = \"vana.account.v1\";\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE = \"ps-lite-owner\";\nexport const PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX =\n `${PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION}:${PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE}:` as const;\n\nexport type PersonalServerLiteOwnerBindingPurpose =\n typeof PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE;\n\nexport type PersonalServerLiteOwnerBindingMessage =\n `${typeof PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX}${Lowercase<Address>}`;\n\nexport interface PersonalServerLiteOwnerBindingSigner {\n address: Address;\n signMessage(input: {\n message: PersonalServerLiteOwnerBindingMessage;\n }): Promise<Hex> | Hex;\n}\n\nexport interface ViemPersonalServerLiteOwnerBindingWalletClient {\n account?: Account | Address | null;\n signMessage(input: {\n account?: Account | Address;\n message: SignableMessage;\n }): Promise<Hex>;\n}\n\nexport type ViemPersonalServerLiteOwnerBindingSignerSource =\n | PersonalServerLiteOwnerBindingSigner\n | ViemPersonalServerLiteOwnerBindingWalletClient;\n\nexport interface BuildPersonalServerLiteOwnerBindingSignatureInput {\n signer: PersonalServerLiteOwnerBindingSigner;\n}\n\nexport interface PersonalServerLiteOwnerBindingSignature {\n signature: Hex;\n signerAddress: Address;\n message: PersonalServerLiteOwnerBindingMessage;\n purpose: PersonalServerLiteOwnerBindingPurpose;\n}\n\nfunction assertAddress(value: Address, name: string): void {\n if (!isAddress(value)) {\n throw new Error(`${name} must be a valid EVM address`);\n }\n}\n\nfunction getAccountAddress(\n account: Account | Address | null | undefined,\n): Address | undefined {\n if (!account) {\n return undefined;\n }\n\n return typeof account === \"string\" ? account : account.address;\n}\n\nfunction isPersonalServerLiteOwnerBindingSigner(\n source: ViemPersonalServerLiteOwnerBindingSignerSource,\n): source is PersonalServerLiteOwnerBindingSigner {\n return \"address\" in source && typeof source.signMessage === \"function\";\n}\n\nexport function buildPersonalServerLiteOwnerBindingMessage(\n ownerAddress: Address,\n): PersonalServerLiteOwnerBindingMessage {\n assertAddress(ownerAddress, \"ownerAddress\");\n return `${PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX}${ownerAddress.toLowerCase()}` as PersonalServerLiteOwnerBindingMessage;\n}\n\nexport function createViemPersonalServerLiteOwnerBindingSigner(\n source: ViemPersonalServerLiteOwnerBindingSignerSource,\n options: { account?: Account | Address } = {},\n): PersonalServerLiteOwnerBindingSigner {\n if (isPersonalServerLiteOwnerBindingSigner(source)) {\n return source;\n }\n\n const accountAddress =\n getAccountAddress(options.account) ?? getAccountAddress(source.account);\n\n if (accountAddress) {\n return {\n address: accountAddress,\n signMessage: ({ message }) =>\n source.signMessage({\n account: options.account ?? source.account ?? accountAddress,\n message,\n }),\n };\n }\n\n throw new Error(\n \"Viem wallet client requires an account option or account property\",\n );\n}\n\nexport async function buildPersonalServerLiteOwnerBindingSignature(\n input: BuildPersonalServerLiteOwnerBindingSignatureInput,\n): Promise<PersonalServerLiteOwnerBindingSignature> {\n const message = buildPersonalServerLiteOwnerBindingMessage(\n input.signer.address,\n );\n const signature = await input.signer.signMessage({ message });\n\n return {\n signature,\n signerAddress: input.signer.address,\n message,\n purpose: PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE,\n };\n}\n\nexport const signPersonalServerLiteOwnerBinding =\n buildPersonalServerLiteOwnerBindingSignature;\n"],"mappings":"AAUA;AAAA,EACE;AAAA,OAKK;AAEA,MAAM,6CAA6C;AACnD,MAAM,6CAA6C;AACnD,MAAM,4CACX,GAAG,0CAA0C,IAAI,0CAA0C;AAsC7F,SAAS,cAAc,OAAgB,MAAoB;AACzD,MAAI,CAAC,UAAU,KAAK,GAAG;AACrB,UAAM,IAAI,MAAM,GAAG,IAAI,8BAA8B;AAAA,EACvD;AACF;AAEA,SAAS,kBACP,SACqB;AACrB,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,YAAY,WAAW,UAAU,QAAQ;AACzD;AAEA,SAAS,uCACP,QACgD;AAChD,SAAO,aAAa,UAAU,OAAO,OAAO,gBAAgB;AAC9D;AAEO,SAAS,2CACd,cACuC;AACvC,gBAAc,cAAc,cAAc;AAC1C,SAAO,GAAG,yCAAyC,GAAG,aAAa,YAAY,CAAC;AAClF;AAEO,SAAS,+CACd,QACA,UAA2C,CAAC,GACN;AACtC,MAAI,uCAAuC,MAAM,GAAG;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,iBACJ,kBAAkB,QAAQ,OAAO,KAAK,kBAAkB,OAAO,OAAO;AAExE,MAAI,gBAAgB;AAClB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,aAAa,CAAC,EAAE,QAAQ,MACtB,OAAO,YAAY;AAAA,QACjB,SAAS,QAAQ,WAAW,OAAO,WAAW;AAAA,QAC9C;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,6CACpB,OACkD;AAClD,QAAM,UAAU;AAAA,IACd,MAAM,OAAO;AAAA,EACf;AACA,QAAM,YAAY,MAAM,MAAM,OAAO,YAAY,EAAE,QAAQ,CAAC;AAE5D,SAAO;AAAA,IACL;AAAA,IACA,eAAe,MAAM,OAAO;AAAA,IAC5B;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEO,MAAM,qCACX;","names":[]}