@opendatalabs/vana-sdk 3.16.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 (72) hide show
  1. package/README.md +107 -0
  2. package/dist/direct/personal-server-read.cjs +20 -1
  3. package/dist/direct/personal-server-read.cjs.map +1 -1
  4. package/dist/direct/personal-server-read.js +23 -1
  5. package/dist/direct/personal-server-read.js.map +1 -1
  6. package/dist/errors.cjs +121 -2
  7. package/dist/errors.cjs.map +1 -1
  8. package/dist/errors.d.ts +190 -0
  9. package/dist/errors.js +106 -1
  10. package/dist/errors.js.map +1 -1
  11. package/dist/index.browser.d.ts +5 -1
  12. package/dist/index.browser.js +1449 -58
  13. package/dist/index.browser.js.map +4 -4
  14. package/dist/index.node.cjs +1501 -59
  15. package/dist/index.node.cjs.map +4 -4
  16. package/dist/index.node.d.ts +5 -1
  17. package/dist/index.node.js +1449 -58
  18. package/dist/index.node.js.map +4 -4
  19. package/dist/protocol/data-point-deletion.cjs +220 -0
  20. package/dist/protocol/data-point-deletion.cjs.map +1 -0
  21. package/dist/protocol/data-point-deletion.d.ts +180 -0
  22. package/dist/protocol/data-point-deletion.js +193 -0
  23. package/dist/protocol/data-point-deletion.js.map +1 -0
  24. package/dist/protocol/data-point-deletion.test.d.ts +1 -0
  25. package/dist/protocol/gateway.cjs +145 -32
  26. package/dist/protocol/gateway.cjs.map +1 -1
  27. package/dist/protocol/gateway.d.ts +42 -3
  28. package/dist/protocol/gateway.js +156 -32
  29. package/dist/protocol/gateway.js.map +1 -1
  30. package/dist/protocol/lineage.cjs +287 -0
  31. package/dist/protocol/lineage.cjs.map +1 -0
  32. package/dist/protocol/lineage.d.ts +228 -0
  33. package/dist/protocol/lineage.js +258 -0
  34. package/dist/protocol/lineage.js.map +1 -0
  35. package/dist/protocol/lineage.test.d.ts +1 -0
  36. package/dist/protocol/personal-server-data.cjs +20 -1
  37. package/dist/protocol/personal-server-data.cjs.map +1 -1
  38. package/dist/protocol/personal-server-data.js +23 -1
  39. package/dist/protocol/personal-server-data.js.map +1 -1
  40. package/dist/protocol/personal-server-error-body.cjs +57 -0
  41. package/dist/protocol/personal-server-error-body.cjs.map +1 -0
  42. package/dist/protocol/personal-server-error-body.d.ts +18 -0
  43. package/dist/protocol/personal-server-error-body.js +32 -0
  44. package/dist/protocol/personal-server-error-body.js.map +1 -0
  45. package/dist/protocol/personal-server-write.cjs +623 -0
  46. package/dist/protocol/personal-server-write.cjs.map +1 -0
  47. package/dist/protocol/personal-server-write.d.ts +284 -0
  48. package/dist/protocol/personal-server-write.js +601 -0
  49. package/dist/protocol/personal-server-write.js.map +1 -0
  50. package/dist/protocol/personal-server-write.test.d.ts +1 -0
  51. package/dist/protocol/write-signer.cjs +67 -0
  52. package/dist/protocol/write-signer.cjs.map +1 -0
  53. package/dist/protocol/write-signer.d.ts +59 -0
  54. package/dist/protocol/write-signer.js +43 -0
  55. package/dist/protocol/write-signer.js.map +1 -0
  56. package/dist/protocol/write-signer.test.d.ts +1 -0
  57. package/dist/storage/index.cjs.map +1 -1
  58. package/dist/storage/index.d.ts +1 -1
  59. package/dist/storage/index.js.map +1 -1
  60. package/dist/storage/providers/vana-storage.cjs +66 -0
  61. package/dist/storage/providers/vana-storage.cjs.map +1 -1
  62. package/dist/storage/providers/vana-storage.d.ts +26 -0
  63. package/dist/storage/providers/vana-storage.js +66 -0
  64. package/dist/storage/providers/vana-storage.js.map +1 -1
  65. package/dist/tests/mock-personal-server.d.ts +127 -0
  66. package/dist/utils/response-body.cjs +46 -0
  67. package/dist/utils/response-body.cjs.map +1 -0
  68. package/dist/utils/response-body.d.ts +26 -0
  69. package/dist/utils/response-body.js +20 -0
  70. package/dist/utils/response-body.js.map +1 -0
  71. package/dist/utils/response-body.test.d.ts +1 -0
  72. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/protocol/personal-server-write.ts"],"sourcesContent":["/**\n * Builder-side client for the Personal Server Write API.\n *\n * @remarks\n * A builder holding a write-grant (a grant whose scope entries carry the\n * `write:` prefix, see {@link formatScopeEntry}) writes into a user's Personal\n * Server in two steps:\n *\n * 1. {@link openWriteSession}: `POST /v1/write/session` with a Web3Signed\n * handshake that carries the grant id as a signed claim. The Personal\n * Server verifies the builder key against the grant and mints a\n * short-lived bearer token bound to `{ builder, grantId }`.\n * 2. {@link writeData}: `POST /v1/data/:scope` with that bearer plus an\n * `X-Vana-Write-Signature` proof, a second Web3Signed signature over the\n * representation the Personal Server will store, again carrying the grant\n * id as a signed claim. The Personal Server stores the proof with the\n * record under the reserved `$writtenBy` key so anyone holding the record\n * can verify who wrote it.\n *\n * What the proof covers: a JSON write signs the request body, which must be\n * the compact `JSON.stringify` form (the server rejects anything else with\n * `WRITE_BODY_NOT_CANONICAL`); a binary write signs the `$binary` record the\n * server stores for the bytes and their representation headers\n * ({@link binaryWriteSignedBytes}), not the raw bytes. Every proof is\n * single-use: a retry after a lost response signs a fresh proof.\n *\n * Derivatives name their sources through `lineage` (see\n * {@link deriveDataPointId}): for a JSON write the ids are the top-level\n * `lineage` field of the body (inside the signed bytes); for a binary write\n * they are the `lineage` field of the `X-Vana-Metadata` JSON (inside the\n * signed `$binary` record). The server validates them and mirrors them under\n * the reserved `$lineage` key. Callers never send `$writtenBy` or `$lineage`.\n *\n * @category Protocol\n */\n\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport { bytesToHex, isAddress, type Address, type Hex } from \"viem\";\nimport { z } from \"zod\";\nimport { buildWeb3SignedHeader } from \"../auth/web3-signed-builder\";\nimport {\n type PersonalServerWriteError,\n WriteConflictError,\n WriteForbiddenError,\n WriteLineageError,\n WriteRejectedError,\n WriteRequestError,\n WriteSessionError,\n WriteSessionExpiredError,\n WriteTransportError,\n WriteUnauthorizedError,\n} from \"../errors\";\nimport { toBase64 } from \"../utils/encoding\";\nimport { IngestResponseSchema, type IngestResponse } from \"./data-file\";\nimport {\n assertDerivedScopeNaming,\n deriveDataPointId,\n isDataPointId,\n} from \"./lineage\";\nimport {\n isRecord,\n readPersonalServerErrorBody,\n} from \"./personal-server-error-body\";\nimport { scopeMatchesPattern } from \"./scopes\";\nimport {\n resolveWriteSigner,\n type ResolveWriteSignerOptions,\n type WriteSigner,\n type WriteSignerSource,\n} from \"./write-signer\";\n\n/** Path of the write-session handshake. */\nexport const WRITE_SESSION_PATH = \"/v1/write/session\";\n/** Header carrying the builder's per-write payload proof. */\nexport const WRITE_SIGNATURE_HEADER = \"X-Vana-Write-Signature\";\n/** Header carrying caller metadata (and `lineage`) for a binary write. */\nexport const WRITE_METADATA_HEADER = \"X-Vana-Metadata\";\n/** Field the lineage source ids travel in (body for JSON, metadata for binary). */\nexport const LINEAGE_FIELD = \"lineage\";\n/** The most sources one record may cite. */\nexport const MAX_LINEAGE_SOURCES = 256;\n/** Header carrying the filename of a binary write (printable ASCII names). */\nexport const WRITE_FILENAME_HEADER = \"X-Filename\";\n/**\n * Header carrying a filename the `X-Filename` header cannot: the Personal\n * Server percent-decodes `filename*=UTF-8''...` (RFC 5987).\n */\nexport const WRITE_CONTENT_DISPOSITION_HEADER = \"Content-Disposition\";\n/** Reserved record key the Personal Server stamps builder attribution into. */\nexport const WRITER_ATTRIBUTION_KEY = \"$writtenBy\";\n/** Reserved record key the Personal Server stamps lineage sources into. */\nexport const LINEAGE_KEY = \"$lineage\";\n/** Record keys a builder must never send. */\nexport const RESERVED_WRITE_KEYS: readonly string[] = [\n WRITER_ATTRIBUTION_KEY,\n LINEAGE_KEY,\n];\n\n/**\n * Transport-level retry knobs shared by {@link openWriteSession} and\n * {@link writeData}.\n *\n * @remarks\n * Applies only when `fetch` **throws** (connection reset, DNS, a relay drop).\n * Every attempt signs a fresh proof, because the Personal Server consumes a\n * proof the moment it accepts it. A received HTTP response is never retried:\n * a 4xx/5xx is surfaced as a typed error.\n */\nexport interface WriteTransportRetryOptions {\n /** Total attempts including the first (default 3). `1` disables retries. */\n attempts?: number;\n /** Delay before the first retry (ms); doubles per retry (default 1_000). */\n initialDelayMs?: number;\n}\n\n/** An open write session: the bearer token plus what it was minted for. */\nexport interface WriteSession {\n /** Personal Server origin, without a trailing slash. */\n personalServerUrl: string;\n /** Web3Signed audience every proof under this session is addressed to. */\n audience: string;\n /** The write-grant the session is bound to. */\n grantId: string;\n /** The bearer token (`vana_write_...`). */\n accessToken: string;\n /** Unix milliseconds after which the token is no longer accepted. */\n expiresAt: number;\n /** Write patterns (prefix stripped) the session may write into. */\n writeScopes: readonly string[];\n /** The builder key the session was opened with; signs every write proof. */\n signer: WriteSigner;\n}\n\nexport interface OpenWriteSessionParams extends ResolveWriteSignerOptions {\n /** Personal Server origin, e.g. `https://ps.example.com`. */\n personalServerUrl: string;\n /** Builder key: a viem `LocalAccount`, `WalletClient`, or `{ signMessage }`. */\n signer: WriteSignerSource;\n /** The write-grant issued to the builder. */\n grantId: string;\n /** Web3Signed audience; defaults to `personalServerUrl`. */\n audience?: string;\n /** `fetch` to use; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** Extra request headers. */\n headers?: HeadersInit;\n retry?: WriteTransportRetryOptions;\n}\n\n/** A lineage source: a data point id, or the pair it is derived from. */\nexport type LineageSource = Hex | { ownerAddress: Address; scope: string };\n\n/** Bytes to store as an unstructured (binary) record. */\nexport interface WriteBinaryPayload {\n bytes: Uint8Array;\n /** Media type, e.g. `application/pdf`. Parameters are dropped when stored. */\n contentType: string;\n /**\n * Stored with the record. Sent as `X-Filename` when it is printable ASCII,\n * otherwise as `Content-Disposition: attachment; filename*=UTF-8''...`,\n * which the Personal Server decodes back to the same string. Must not have\n * leading or trailing whitespace (HTTP would strip it and the signed\n * representation would no longer match).\n */\n filename?: string;\n}\n\ninterface WriteDataBaseParams {\n session: WriteSession;\n /**\n * The scope to write into; must match one of the session's write patterns.\n * A derived scope must not share its first dot-segment with any source's\n * scope (the server rejects it with `LINEAGE_SCOPE_UNDER_SOURCE_PREFIX`):\n * put derivatives in the app's own namespace.\n */\n scope: string;\n /**\n * The data points this record was derived from: ids\n * ({@link deriveDataPointId}) or `{ ownerAddress, scope }` pairs the SDK\n * derives the id from. Distinct, at most {@link MAX_LINEAGE_SOURCES}, all\n * belonging to the same owner as the target scope, never the record's own\n * id. Sent lowercased as the record's `lineage` field, inside the signed\n * bytes. `[]` is an explicit root statement and is sent; absent or `null`\n * makes no statement. Pairs also let the SDK apply the naming rule\n * ({@link assertDerivedScopeNaming}) before anything is signed.\n */\n lineage?: readonly LineageSource[] | null;\n /** `fetch` to use; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** Extra request headers. */\n headers?: HeadersInit;\n retry?: WriteTransportRetryOptions;\n}\n\n/**\n * A JSON write: `data` is stored as the record (plus `lineage` when given).\n * Anything else to store goes inside `data`; `lineage` and reserved keys are\n * not accepted in it.\n */\nexport interface WriteJsonDataParams extends WriteDataBaseParams {\n data: Record<string, unknown>;\n binary?: never;\n metadata?: never;\n}\n\n/** A binary write: the bytes are stored as a `$binary` record. */\nexport interface WriteBinaryDataParams extends WriteDataBaseParams {\n binary: WriteBinaryPayload;\n data?: never;\n /**\n * Caller metadata stored with the record (`X-Vana-Metadata`, part of the\n * signed `$binary` record). Must not contain `lineage` (use the option) or\n * a reserved key.\n */\n metadata?: Record<string, unknown>;\n}\n\nexport type WriteDataParams = WriteJsonDataParams | WriteBinaryDataParams;\n\nconst WriteDataResultSchema = IngestResponseSchema.extend({\n // Present when the write carried lineage: the validated, lowercased ids.\n lineage: z.object({ sources: z.array(z.string()) }).optional(),\n});\n\n/** The Personal Server's ingest answer, plus the accepted lineage if any. */\nexport type WriteDataResult = IngestResponse & {\n lineage?: { sources: Hex[] };\n};\n\nconst WriteSessionResponseSchema = z.object({\n access_token: z.string().min(1),\n token_type: z.string(),\n expires_in: z.number().nonnegative(),\n scope: z.string(),\n});\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nfunction resolveFetch(fetchFn: typeof fetch | undefined): typeof fetch {\n const resolved = fetchFn ?? globalThis.fetch;\n if (resolved === undefined) {\n throw new WriteRequestError(\"No fetch implementation available\");\n }\n return resolved;\n}\n\nfunction dataPath(scope: string): string {\n return `/v1/data/${encodeURIComponent(scope)}`;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction finiteOr(value: number | undefined, fallback: number): number {\n return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * The Personal Server consumes every proof it accepts, and a Web3Signed\n * payload is fully determined by `{ aud, method, uri, bodyHash, grantId, iat,\n * exp }`, so two proofs for the same request signed within one second would\n * be byte-identical and the second rejected as a replay. Remember the\n * highest `iat` issued per request identity in this process and bump past\n * it when a second proof for the same identity falls inside the same second.\n *\n * A mark is kept for as long as the server can still remember the proof it\n * guards (its lifetime plus the verifier's clock skew), so a proof is never\n * re-issued while it could still be rejected as a replay: not by a burst,\n * and not by a wall clock stepping backwards (an identical request after a\n * step back waits for the clock instead of reusing the mark). Marks are\n * bucketed by their `iat` second, so pruning (once per second) only touches\n * the buckets that fell out of the retention window: the work per proof is\n * amortised constant and the map is bounded by the distinct requests signed\n * in the window.\n */\nconst issuedProofIats = new Map<string, number>();\n/** `iat` second -> identities whose current mark is that second. */\nconst issuedProofBuckets = new Map<number, Set<string>>();\nlet issuedProofIatsPrunedAtSec = 0;\n/** `buildWeb3SignedHeader`'s default `exp - iat`. */\nconst WEB3_SIGNED_PROOF_LIFETIME_SECONDS = 300;\n/** The verifier's tolerated clock skew (`verifyWeb3Signed`). */\nconst WEB3_SIGNED_CLOCK_SKEW_SECONDS = 60;\nconst PROOF_IAT_RETENTION_SECONDS =\n WEB3_SIGNED_PROOF_LIFETIME_SECONDS + WEB3_SIGNED_CLOCK_SKEW_SECONDS;\n/**\n * How far ahead of the clock a bumped `iat` may run when the proof is sent.\n * The verifier tolerates 60 s of skew. A burst of identical requests that\n * would need to run further ahead waits for the clock instead, so a proof is\n * never repeated; sustained identical requests are throttled to one per\n * second after a burst of this many.\n */\nconst PROOF_IAT_MAX_AHEAD_SECONDS = 30;\n\nfunction pruneIssuedProofIats(nowSec: number): void {\n if (issuedProofIatsPrunedAtSec === nowSec) return;\n issuedProofIatsPrunedAtSec = nowSec;\n const cutoff = nowSec - PROOF_IAT_RETENTION_SECONDS;\n // There is at most one bucket per second in the window, so this walk is\n // bounded by the window length, not by the number of marks.\n for (const [sec, keys] of issuedProofBuckets) {\n if (sec >= cutoff) continue;\n for (const key of keys) issuedProofIats.delete(key);\n issuedProofBuckets.delete(sec);\n }\n}\n\nfunction setIssuedProofIat(key: string, iat: number, previous?: number): void {\n if (previous !== undefined) {\n const bucket = issuedProofBuckets.get(previous);\n bucket?.delete(key);\n if (bucket?.size === 0) issuedProofBuckets.delete(previous);\n }\n issuedProofIats.set(key, iat);\n let bucket = issuedProofBuckets.get(iat);\n if (bucket === undefined) {\n bucket = new Set();\n issuedProofBuckets.set(iat, bucket);\n }\n bucket.add(key);\n}\n\n/**\n * Reserve the next `iat` for a request identity. The reservation is made\n * synchronously so concurrent callers never share a value; the returned\n * promise only waits when the reserved `iat` is further ahead of the clock\n * than {@link PROOF_IAT_MAX_AHEAD_SECONDS}.\n */\nfunction nextProofIat(proofKey: string): Promise<number> {\n const nowSec = Math.floor(Date.now() / 1000);\n pruneIssuedProofIats(nowSec);\n const last = issuedProofIats.get(proofKey);\n const iat = last === undefined ? nowSec : Math.max(nowSec, last + 1);\n setIssuedProofIat(proofKey, iat, last);\n const waitSec = iat - nowSec - PROOF_IAT_MAX_AHEAD_SECONDS;\n if (waitSec <= 0) return Promise.resolve(iat);\n return sleep(waitSec * 1000).then(() => iat);\n}\n\nfunction proofKeyFor(parts: {\n aud: string;\n method: string;\n uri: string;\n grantId: string;\n signedBytes?: Uint8Array;\n}): string {\n // A digest, so a retained mark costs a fixed amount of memory whatever the\n // request looked like.\n return bytesToHex(\n sha256(\n new TextEncoder().encode(\n JSON.stringify([\n parts.aud,\n parts.method,\n parts.uri,\n parts.grantId,\n parts.signedBytes ? bytesToHex(sha256(parts.signedBytes)) : \"\",\n ]),\n ),\n ),\n );\n}\n\n/**\n * Send a request, re-signing it on every attempt. Only a thrown `fetch` is\n * retried; the proof builder and any received response are never retried.\n */\nasync function sendWithFreshProof(\n label: string,\n fetchFn: typeof fetch,\n options: WriteTransportRetryOptions | undefined,\n proofKey: string,\n build: (iat: number) => Promise<{ url: string; init: RequestInit }>,\n): Promise<Response> {\n const attempts = Math.max(1, Math.floor(finiteOr(options?.attempts, 3)));\n let delayMs = Math.max(0, finiteOr(options?.initialDelayMs, 1_000));\n let lastError: unknown;\n for (let attempt = 0; attempt < attempts; attempt++) {\n const { url, init } = await build(await nextProofIat(proofKey));\n try {\n return await fetchFn(url, init);\n } catch (err) {\n lastError = err;\n }\n if (attempt < attempts - 1) {\n await sleep(delayMs);\n delayMs *= 2;\n }\n }\n throw new WriteTransportError(\n `${label} failed after ${attempts} attempt(s): ${errorMessage(lastError)}`,\n attempts,\n lastError,\n );\n}\n\n/**\n * Open a write session against a Personal Server.\n *\n * @remarks\n * Sends `POST /v1/write/session` with a Web3Signed `Authorization` header\n * whose signed claims carry `grantId`. The handshake proof is single-use on\n * the server; a transport retry signs a new one.\n *\n * @returns The session to pass to {@link writeData}.\n * @throws {WriteSessionError} When the Personal Server refuses the handshake\n * (`errorCode` names why: `UNREGISTERED_BUILDER`, `GRANT_REQUIRED`,\n * `GRANT_REVOKED`, `SCOPE_MISMATCH` for a grant without write entries,\n * `INVALID_SIGNATURE` when the key is not the grantee,\n * `GRANT_OWNER_MISMATCH`, ...).\n * @throws {WriteTransportError} When `fetch` threw on every attempt.\n * @throws {WriteRequestError} When the signer is unusable.\n */\nexport async function openWriteSession(\n params: OpenWriteSessionParams,\n): Promise<WriteSession> {\n const fetchFn = resolveFetch(params.fetch);\n const personalServerUrl = normalizeBaseUrl(params.personalServerUrl);\n const audience = params.audience ?? personalServerUrl;\n const signer = resolveWriteSigner(params.signer, { account: params.account });\n\n const response = await sendWithFreshProof(\n \"Write session handshake\",\n fetchFn,\n params.retry,\n proofKeyFor({\n aud: audience,\n method: \"POST\",\n uri: WRITE_SESSION_PATH,\n grantId: params.grantId,\n }),\n async (iat) => {\n const headers = new Headers(params.headers);\n headers.set(\n \"Authorization\",\n await buildWeb3SignedHeader({\n signMessage: signer.signMessage,\n aud: audience,\n method: \"POST\",\n uri: WRITE_SESSION_PATH,\n grantId: params.grantId,\n iat,\n }),\n );\n return {\n url: `${personalServerUrl}${WRITE_SESSION_PATH}`,\n init: { method: \"POST\", headers },\n };\n },\n );\n // Read the clock before parsing so `expiresAt` never overstates the\n // remaining lifetime.\n const mintedAt = Date.now();\n\n if (!response.ok) {\n const { errorCode, message, details } =\n await readPersonalServerErrorBody(response);\n throw new WriteSessionError(\n message ??\n `Write session handshake failed: ${response.status} ${response.statusText}`,\n response.status,\n errorCode,\n details,\n );\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch (err) {\n throw new WriteSessionError(\n \"Write session response is not JSON\",\n response.status,\n null,\n { cause: errorMessage(err) },\n );\n }\n const parsed = WriteSessionResponseSchema.safeParse(body);\n if (!parsed.success) {\n throw new WriteSessionError(\n \"Write session response is not a session\",\n response.status,\n null,\n { issues: parsed.error.issues },\n );\n }\n if (parsed.data.token_type.toLowerCase() !== \"bearer\") {\n throw new WriteSessionError(\n `Write session token type is not Bearer: ${parsed.data.token_type}`,\n response.status,\n );\n }\n\n return {\n personalServerUrl,\n audience,\n grantId: params.grantId,\n accessToken: parsed.data.access_token,\n expiresAt: mintedAt + parsed.data.expires_in * 1000,\n writeScopes: parsed.data.scope.split(\" \").filter((s) => s.length > 0),\n signer,\n };\n}\n\n/** Every field {@link writeData} relies on, checked before any is used. */\nfunction isWriteSession(value: unknown): value is WriteSession {\n if (!isRecord(value)) return false;\n const signer: unknown = value.signer;\n return (\n typeof value.personalServerUrl === \"string\" &&\n typeof value.audience === \"string\" &&\n typeof value.grantId === \"string\" &&\n typeof value.accessToken === \"string\" &&\n value.accessToken.length > 0 &&\n typeof value.expiresAt === \"number\" &&\n Number.isFinite(value.expiresAt) &&\n Array.isArray(value.writeScopes) &&\n value.writeScopes.every((scope) => typeof scope === \"string\") &&\n isRecord(signer) &&\n typeof signer.signMessage === \"function\"\n );\n}\n\n/** `true` when one of the session's write patterns covers `scope`. */\nexport function sessionCoversScope(\n session: Pick<WriteSession, \"writeScopes\">,\n scope: string,\n): boolean {\n if (\n !isRecord(session) ||\n !Array.isArray(session.writeScopes) ||\n !session.writeScopes.every((pattern) => typeof pattern === \"string\")\n ) {\n throw new WriteRequestError(\"session must come from openWriteSession\");\n }\n return session.writeScopes.some((pattern) =>\n scopeMatchesPattern(scope, pattern),\n );\n}\n\n/**\n * The media type the Personal Server stores for a binary write: the\n * `Content-Type` minus its parameters, `application/octet-stream` when blank.\n */\nexport function normalizeBinaryMimeType(contentType: string | null): string {\n if (!contentType) return \"application/octet-stream\";\n return contentType.split(\";\")[0].trim() || \"application/octet-stream\";\n}\n\n/**\n * How the Personal Server reads an `X-Vana-Metadata` header: JSON when it\n * parses, the raw string otherwise, `undefined` when absent or blank.\n */\nexport function parseWriteMetadataHeader(value: string | null): unknown {\n if (value === null) return undefined;\n const trimmed = value.trim();\n if (trimmed === \"\") return undefined;\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\n/**\n * Encode a metadata object for the `X-Vana-Metadata` header. Non-ASCII\n * characters are `\\uXXXX`-escaped so the value is header-safe everywhere;\n * it parses back to the same object.\n */\nexport function encodeWriteMetadataHeader(\n metadata: Record<string, unknown>,\n): string {\n let json: string;\n try {\n json = JSON.stringify(metadata);\n } catch (err) {\n throw new WriteRequestError(\n `metadata is not JSON-serialisable: ${errorMessage(err)}`,\n );\n }\n if (typeof json !== \"string\") {\n throw new WriteRequestError(\"metadata must serialise to JSON\");\n }\n return json.replace(\n /[\\u007f-\\uffff]/g,\n (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, \"0\")}`,\n );\n}\n\nexport interface BinaryWriteSignedBytesInput {\n /** The raw body bytes the write sends. */\n bytes: Uint8Array;\n /** The `Content-Type` header the write sends (parameters are ignored). */\n contentType: string;\n /** The `X-Filename` header value the write sends, if any. */\n filename?: string;\n /** The exact `X-Vana-Metadata` header value the write sends, if any. */\n metadataHeader?: string;\n}\n\n/**\n * The bytes a builder signs for a binary write: the compact JSON of the\n * `$binary` record the Personal Server stores for these headers and bytes\n * (`personal-server-ts` `binaryWriteSignedBytes`, mirrored field for field).\n *\n * @returns UTF-8 bytes of the stored record's compact JSON.\n */\nexport function binaryWriteSignedBytes(\n input: BinaryWriteSignedBytesInput,\n): Uint8Array {\n const metadata = parseWriteMetadataHeader(input.metadataHeader ?? null);\n const record: Record<string, unknown> = {\n $binary: true,\n mimeType: normalizeBinaryMimeType(input.contentType),\n ...(input.filename ? { filename: input.filename } : {}),\n sizeBytes: input.bytes.length,\n contentHash: bytesToHex(sha256(input.bytes)),\n encoding: \"base64\",\n content: toBase64(input.bytes),\n ...(metadata !== undefined ? { metadata } : {}),\n };\n return new TextEncoder().encode(JSON.stringify(record));\n}\n\nconst PRINTABLE_ASCII = /^[\\x20-\\x7e]*$/;\n\n/**\n * Carry a filename the way the Personal Server reads it back verbatim:\n * `X-Filename` for printable ASCII, RFC 5987 `filename*` otherwise (a raw\n * non-ASCII header value is rejected by `fetch` or mangled in transit).\n */\nfunction setFilenameHeader(headers: Headers, filename: string): void {\n if (PRINTABLE_ASCII.test(filename)) {\n headers.set(WRITE_FILENAME_HEADER, filename);\n return;\n }\n headers.set(\n WRITE_CONTENT_DISPOSITION_HEADER,\n `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`,\n );\n}\n\nfunction assertNoReservedKeys(\n value: Record<string, unknown>,\n where: string,\n): void {\n for (const key of RESERVED_WRITE_KEYS) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n throw new WriteRequestError(\n `${where} must not contain the reserved ${key} key; the Personal Server stamps it`,\n { key },\n );\n }\n }\n}\n\nfunction isLineagePair(\n value: unknown,\n): value is { ownerAddress: Address; scope: string } {\n return (\n isRecord(value) &&\n typeof value.ownerAddress === \"string\" &&\n typeof value.scope === \"string\"\n );\n}\n\n/**\n * Validate and lowercase the sources the way the Personal Server will\n * (distinct, at most {@link MAX_LINEAGE_SOURCES}, never the record's own id)\n * and apply the naming rule to every source given as a pair. `[]` is kept:\n * it is an explicit root statement.\n */\nfunction normalizeLineage(lineage: unknown, derivedScope: string): Hex[] {\n if (!Array.isArray(lineage)) {\n throw new WriteRequestError(\"lineage must be an array of data point ids\");\n }\n if (lineage.length > MAX_LINEAGE_SOURCES) {\n throw new WriteRequestError(\n `lineage lists ${lineage.length} sources; the maximum is ${MAX_LINEAGE_SOURCES}`,\n { max: MAX_LINEAGE_SOURCES, count: lineage.length },\n );\n }\n const seen = new Set<string>();\n const sources: Hex[] = [];\n const sourceScopes: string[] = [];\n const ownIds = new Set<string>();\n for (const entry of lineage as unknown[]) {\n let id: Hex;\n if (isLineagePair(entry)) {\n if (!isAddress(entry.ownerAddress, { strict: false })) {\n throw new WriteRequestError(\n \"lineage source ownerAddress must be an EVM address\",\n { ownerAddress: entry.ownerAddress },\n );\n }\n if (entry.scope.length === 0) {\n throw new WriteRequestError(\"lineage source scope is required\");\n }\n id = deriveDataPointId(entry.ownerAddress, entry.scope);\n sourceScopes.push(entry.scope);\n ownIds.add(deriveDataPointId(entry.ownerAddress, derivedScope));\n } else if (isDataPointId(entry)) {\n id = entry;\n } else {\n throw new WriteRequestError(\n \"lineage entries must be 32-byte hex data point ids or { ownerAddress, scope } pairs\",\n { entry },\n );\n }\n const normalized = id.toLowerCase() as Hex;\n if (seen.has(normalized)) {\n throw new WriteRequestError(\"lineage must not repeat a data point id\", {\n dataPointId: normalized,\n });\n }\n seen.add(normalized);\n sources.push(normalized);\n }\n for (const own of ownIds) {\n if (seen.has(own)) {\n throw new WriteRequestError(\n \"lineage must not cite the record's own data point\",\n { dataPointId: own, scope: derivedScope },\n );\n }\n }\n assertDerivedScopeNaming(derivedScope, sourceScopes);\n return sources;\n}\n\nfunction assertNoLineageField(\n value: Record<string, unknown>,\n where: string,\n): void {\n if (Object.prototype.hasOwnProperty.call(value, LINEAGE_FIELD)) {\n throw new WriteRequestError(\n `${where}.${LINEAGE_FIELD} is reserved; pass sources through the lineage option`,\n );\n }\n}\n\n/** The `X-Vana-Metadata` header for a binary write, or `undefined`. */\nfunction buildMetadataHeader(\n metadata: Record<string, unknown> | undefined,\n sources: Hex[] | undefined,\n): string | undefined {\n if (metadata !== undefined) {\n if (!isRecord(metadata)) {\n throw new WriteRequestError(\"metadata must be a plain object\");\n }\n assertNoReservedKeys(metadata, \"metadata\");\n assertNoLineageField(metadata, \"metadata\");\n }\n if (metadata === undefined && sources === undefined) return undefined;\n return encodeWriteMetadataHeader({\n ...(metadata ?? {}),\n ...(sources !== undefined ? { [LINEAGE_FIELD]: sources } : {}),\n });\n}\n\ninterface PreparedWrite {\n body: Uint8Array;\n /** What the proof's `bodyHash` commits to. */\n signedBytes: Uint8Array;\n contentType: string;\n filename?: string;\n metadataHeader?: string;\n}\n\nfunction prepareWrite(params: WriteDataParams): PreparedWrite {\n if (params.binary !== undefined && params.data !== undefined) {\n throw new WriteRequestError(\"Pass either data or binary, not both\");\n }\n // Absent or null lineage makes no statement: send nothing. Anything else\n // (`[]` included: an explicit root) is validated before it is touched,\n // since callers may be untyped.\n const rawLineage: unknown = params.lineage;\n const sources =\n rawLineage === undefined || rawLineage === null\n ? undefined\n : normalizeLineage(rawLineage, params.scope);\n\n if (params.binary !== undefined) {\n const { bytes, contentType, filename } = params.binary;\n if (!(bytes instanceof Uint8Array)) {\n throw new WriteRequestError(\"binary.bytes must be a Uint8Array\");\n }\n if (typeof contentType !== \"string\" || contentType.trim() === \"\") {\n throw new WriteRequestError(\"binary.contentType is required\");\n }\n if (filename !== undefined) {\n if (typeof filename !== \"string\") {\n throw new WriteRequestError(\"binary.filename must be a string\");\n }\n if (filename !== filename.trim()) {\n throw new WriteRequestError(\n \"binary.filename must not have leading or trailing whitespace\",\n );\n }\n }\n const metadataHeader = buildMetadataHeader(params.metadata, sources);\n return {\n body: bytes,\n signedBytes: binaryWriteSignedBytes({\n bytes,\n contentType,\n filename,\n metadataHeader,\n }),\n contentType,\n filename,\n metadataHeader,\n };\n }\n\n if (params.data === undefined) {\n throw new WriteRequestError(\"Pass data (a JSON object) or binary\");\n }\n if (!isRecord(params.data)) {\n throw new WriteRequestError(\"data must be a plain JSON object\");\n }\n if (params.metadata !== undefined) {\n throw new WriteRequestError(\n \"metadata applies to binary writes; put fields to store inside data\",\n );\n }\n assertNoReservedKeys(params.data, \"data\");\n assertNoLineageField(params.data, \"data\");\n const record =\n sources === undefined\n ? params.data\n : { ...params.data, [LINEAGE_FIELD]: sources };\n let text: string;\n try {\n // Compact JSON is the contract: the server re-serialises the parsed\n // record and requires it to match the signed bytes.\n text = JSON.stringify(record);\n } catch (err) {\n throw new WriteRequestError(\n `data is not JSON-serialisable: ${errorMessage(err)}`,\n );\n }\n if (typeof text !== \"string\" || !text.startsWith(\"{\")) {\n throw new WriteRequestError(\"data must serialise to a JSON object\");\n }\n const body = new TextEncoder().encode(text);\n return {\n body,\n signedBytes: body,\n contentType: \"application/json\",\n };\n}\n\nasync function writeErrorFromResponse(\n response: Response,\n): Promise<PersonalServerWriteError> {\n const { errorCode, message, details } =\n await readPersonalServerErrorBody(response);\n const text =\n message ??\n `Personal Server write failed: ${response.status} ${response.statusText}`;\n // Lineage rejections span 400 / 422 / 502; the code is the discriminator.\n if (response.status === 422 || errorCode?.startsWith(\"LINEAGE_\")) {\n return new WriteLineageError(text, response.status, errorCode, details);\n }\n switch (response.status) {\n case 401:\n return new WriteUnauthorizedError(text, errorCode, details);\n case 403:\n return new WriteForbiddenError(text, errorCode, details);\n case 409:\n return new WriteConflictError(text, errorCode, details);\n default:\n return new WriteRejectedError(text, response.status, errorCode, details);\n }\n}\n\n/**\n * Write one record into a scope under an open write session.\n *\n * @remarks\n * Sends `POST /v1/data/:scope` with `Authorization: Bearer <session token>`\n * and `X-Vana-Write-Signature`, a Web3Signed proof over the stored\n * representation carrying the session's `grantId` as a signed claim. JSON\n * writes send `data` as compact JSON with `Content-Type: application/json`;\n * binary writes send the bytes with their `Content-Type`, `X-Filename`, and\n * sign {@link binaryWriteSignedBytes}. `lineage` is the record's top-level\n * `lineage` field for JSON and the `lineage` field of `X-Vana-Metadata` for\n * binary, so the proof covers it either way.\n *\n * @returns The ingest answer (`scope`, `collectedAt`, `status`, and\n * `lineage.sources` when the write carried lineage).\n * @throws {WriteRequestError} Before sending: no payload, a reserved key, a\n * malformed lineage id, or non-object data.\n * @throws {WriteSessionExpiredError} Before sending: the session token has\n * passed its lifetime.\n * @throws {WriteUnauthorizedError} 401 (proof or session rejected).\n * @throws {WriteForbiddenError} 403 (grant no longer authorises the write).\n * @throws {WriteConflictError} 409.\n * @throws {WriteLineageError} Any `LINEAGE_*` rejection: 422\n * `LINEAGE_SOURCE_UNKNOWN` (`details.unknown`), 400 `LINEAGE_INVALID` /\n * `LINEAGE_SCOPE_UNDER_SOURCE_PREFIX`, 502 `LINEAGE_SOURCE_LOOKUP_FAILED`.\n * @throws {WriteRejectedError} Any other non-2xx.\n * @throws {WriteTransportError} `fetch` threw on every attempt.\n */\nexport async function writeData(\n params: WriteDataParams,\n): Promise<WriteDataResult> {\n const { session } = params;\n if (!isWriteSession(session)) {\n throw new WriteRequestError(\"session must come from openWriteSession\");\n }\n const fetchFn = resolveFetch(params.fetch);\n if (typeof params.scope !== \"string\" || params.scope.length === 0) {\n throw new WriteRequestError(\"scope is required\");\n }\n const prepared = prepareWrite(params);\n if (Date.now() >= session.expiresAt) {\n throw new WriteSessionExpiredError(\n \"Write session has expired; open a new session\",\n { grantId: session.grantId, expiresAt: session.expiresAt },\n );\n }\n\n const path = dataPath(params.scope);\n const response = await sendWithFreshProof(\n `Write to ${params.scope}`,\n fetchFn,\n params.retry,\n proofKeyFor({\n aud: session.audience,\n method: \"POST\",\n uri: path,\n grantId: session.grantId,\n signedBytes: prepared.signedBytes,\n }),\n async (iat) => {\n const headers = new Headers(params.headers);\n headers.set(\"Content-Type\", prepared.contentType);\n headers.set(\"Authorization\", `Bearer ${session.accessToken}`);\n if (prepared.filename) {\n setFilenameHeader(headers, prepared.filename);\n }\n if (prepared.metadataHeader !== undefined) {\n headers.set(WRITE_METADATA_HEADER, prepared.metadataHeader);\n }\n headers.set(\n WRITE_SIGNATURE_HEADER,\n await buildWeb3SignedHeader({\n signMessage: session.signer.signMessage,\n aud: session.audience,\n method: \"POST\",\n uri: path,\n body: prepared.signedBytes,\n grantId: session.grantId,\n iat,\n }),\n );\n return {\n url: `${session.personalServerUrl}${path}`,\n init: {\n method: \"POST\",\n headers,\n body: prepared.body as unknown as BodyInit,\n },\n };\n },\n );\n\n if (!response.ok) {\n throw await writeErrorFromResponse(response);\n }\n let body: unknown;\n try {\n body = await response.json();\n } catch (err) {\n throw new WriteRejectedError(\n \"Personal Server write response is not JSON\",\n response.status,\n null,\n { cause: errorMessage(err) },\n );\n }\n const parsed = WriteDataResultSchema.safeParse(body);\n if (!parsed.success) {\n throw new WriteRejectedError(\n \"Personal Server write response is not an ingest result\",\n response.status,\n null,\n { issues: parsed.error.issues },\n );\n }\n return parsed.data as WriteDataResult;\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown\n ? Omit<T, K>\n : never;\n\nexport type WritePersonalServerDataParams = Omit<\n OpenWriteSessionParams,\n \"fetch\" | \"headers\" | \"retry\"\n> &\n DistributiveOmit<WriteDataParams, \"session\">;\n\n/** {@link writePersonalServerData}'s answer: the ingest result plus the session it opened. */\nexport interface WritePersonalServerDataResult extends WriteDataResult {\n /** Reuse for further writes until `expiresAt`. */\n session: WriteSession;\n}\n\n/**\n * Open a write session and write one record in a single call.\n *\n * @remarks\n * Equivalent to {@link openWriteSession} followed by {@link writeData}. The\n * session is returned so further writes can reuse it; opening one per write\n * is correct but costs an extra signature and round-trip each time.\n *\n * @throws Everything {@link openWriteSession} and {@link writeData} throw.\n */\nexport async function writePersonalServerData(\n params: WritePersonalServerDataParams,\n): Promise<WritePersonalServerDataResult> {\n const session = await openWriteSession({\n personalServerUrl: params.personalServerUrl,\n signer: params.signer,\n grantId: params.grantId,\n account: params.account,\n audience: params.audience,\n fetch: params.fetch,\n headers: params.headers,\n retry: params.retry,\n });\n const writeParams = { ...params, session } as WriteDataParams;\n const result = await writeData(writeParams);\n return { ...result, session };\n}\n"],"mappings":"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","names":["bucket"]}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Builder-side client for the Personal Server Write API.
3
+ *
4
+ * @remarks
5
+ * A builder holding a write-grant (a grant whose scope entries carry the
6
+ * `write:` prefix, see {@link formatScopeEntry}) writes into a user's Personal
7
+ * Server in two steps:
8
+ *
9
+ * 1. {@link openWriteSession}: `POST /v1/write/session` with a Web3Signed
10
+ * handshake that carries the grant id as a signed claim. The Personal
11
+ * Server verifies the builder key against the grant and mints a
12
+ * short-lived bearer token bound to `{ builder, grantId }`.
13
+ * 2. {@link writeData}: `POST /v1/data/:scope` with that bearer plus an
14
+ * `X-Vana-Write-Signature` proof, a second Web3Signed signature over the
15
+ * representation the Personal Server will store, again carrying the grant
16
+ * id as a signed claim. The Personal Server stores the proof with the
17
+ * record under the reserved `$writtenBy` key so anyone holding the record
18
+ * can verify who wrote it.
19
+ *
20
+ * What the proof covers: a JSON write signs the request body, which must be
21
+ * the compact `JSON.stringify` form (the server rejects anything else with
22
+ * `WRITE_BODY_NOT_CANONICAL`); a binary write signs the `$binary` record the
23
+ * server stores for the bytes and their representation headers
24
+ * ({@link binaryWriteSignedBytes}), not the raw bytes. Every proof is
25
+ * single-use: a retry after a lost response signs a fresh proof.
26
+ *
27
+ * Derivatives name their sources through `lineage` (see
28
+ * {@link deriveDataPointId}): for a JSON write the ids are the top-level
29
+ * `lineage` field of the body (inside the signed bytes); for a binary write
30
+ * they are the `lineage` field of the `X-Vana-Metadata` JSON (inside the
31
+ * signed `$binary` record). The server validates them and mirrors them under
32
+ * the reserved `$lineage` key. Callers never send `$writtenBy` or `$lineage`.
33
+ *
34
+ * @category Protocol
35
+ */
36
+ import { type Address, type Hex } from "viem";
37
+ import { type IngestResponse } from "./data-file.js";
38
+ import { type ResolveWriteSignerOptions, type WriteSigner, type WriteSignerSource } from "./write-signer.js";
39
+ /** Path of the write-session handshake. */
40
+ export declare const WRITE_SESSION_PATH = "/v1/write/session";
41
+ /** Header carrying the builder's per-write payload proof. */
42
+ export declare const WRITE_SIGNATURE_HEADER = "X-Vana-Write-Signature";
43
+ /** Header carrying caller metadata (and `lineage`) for a binary write. */
44
+ export declare const WRITE_METADATA_HEADER = "X-Vana-Metadata";
45
+ /** Field the lineage source ids travel in (body for JSON, metadata for binary). */
46
+ export declare const LINEAGE_FIELD = "lineage";
47
+ /** The most sources one record may cite. */
48
+ export declare const MAX_LINEAGE_SOURCES = 256;
49
+ /** Header carrying the filename of a binary write (printable ASCII names). */
50
+ export declare const WRITE_FILENAME_HEADER = "X-Filename";
51
+ /**
52
+ * Header carrying a filename the `X-Filename` header cannot: the Personal
53
+ * Server percent-decodes `filename*=UTF-8''...` (RFC 5987).
54
+ */
55
+ export declare const WRITE_CONTENT_DISPOSITION_HEADER = "Content-Disposition";
56
+ /** Reserved record key the Personal Server stamps builder attribution into. */
57
+ export declare const WRITER_ATTRIBUTION_KEY = "$writtenBy";
58
+ /** Reserved record key the Personal Server stamps lineage sources into. */
59
+ export declare const LINEAGE_KEY = "$lineage";
60
+ /** Record keys a builder must never send. */
61
+ export declare const RESERVED_WRITE_KEYS: readonly string[];
62
+ /**
63
+ * Transport-level retry knobs shared by {@link openWriteSession} and
64
+ * {@link writeData}.
65
+ *
66
+ * @remarks
67
+ * Applies only when `fetch` **throws** (connection reset, DNS, a relay drop).
68
+ * Every attempt signs a fresh proof, because the Personal Server consumes a
69
+ * proof the moment it accepts it. A received HTTP response is never retried:
70
+ * a 4xx/5xx is surfaced as a typed error.
71
+ */
72
+ export interface WriteTransportRetryOptions {
73
+ /** Total attempts including the first (default 3). `1` disables retries. */
74
+ attempts?: number;
75
+ /** Delay before the first retry (ms); doubles per retry (default 1_000). */
76
+ initialDelayMs?: number;
77
+ }
78
+ /** An open write session: the bearer token plus what it was minted for. */
79
+ export interface WriteSession {
80
+ /** Personal Server origin, without a trailing slash. */
81
+ personalServerUrl: string;
82
+ /** Web3Signed audience every proof under this session is addressed to. */
83
+ audience: string;
84
+ /** The write-grant the session is bound to. */
85
+ grantId: string;
86
+ /** The bearer token (`vana_write_...`). */
87
+ accessToken: string;
88
+ /** Unix milliseconds after which the token is no longer accepted. */
89
+ expiresAt: number;
90
+ /** Write patterns (prefix stripped) the session may write into. */
91
+ writeScopes: readonly string[];
92
+ /** The builder key the session was opened with; signs every write proof. */
93
+ signer: WriteSigner;
94
+ }
95
+ export interface OpenWriteSessionParams extends ResolveWriteSignerOptions {
96
+ /** Personal Server origin, e.g. `https://ps.example.com`. */
97
+ personalServerUrl: string;
98
+ /** Builder key: a viem `LocalAccount`, `WalletClient`, or `{ signMessage }`. */
99
+ signer: WriteSignerSource;
100
+ /** The write-grant issued to the builder. */
101
+ grantId: string;
102
+ /** Web3Signed audience; defaults to `personalServerUrl`. */
103
+ audience?: string;
104
+ /** `fetch` to use; defaults to `globalThis.fetch`. */
105
+ fetch?: typeof fetch;
106
+ /** Extra request headers. */
107
+ headers?: HeadersInit;
108
+ retry?: WriteTransportRetryOptions;
109
+ }
110
+ /** A lineage source: a data point id, or the pair it is derived from. */
111
+ export type LineageSource = Hex | {
112
+ ownerAddress: Address;
113
+ scope: string;
114
+ };
115
+ /** Bytes to store as an unstructured (binary) record. */
116
+ export interface WriteBinaryPayload {
117
+ bytes: Uint8Array;
118
+ /** Media type, e.g. `application/pdf`. Parameters are dropped when stored. */
119
+ contentType: string;
120
+ /**
121
+ * Stored with the record. Sent as `X-Filename` when it is printable ASCII,
122
+ * otherwise as `Content-Disposition: attachment; filename*=UTF-8''...`,
123
+ * which the Personal Server decodes back to the same string. Must not have
124
+ * leading or trailing whitespace (HTTP would strip it and the signed
125
+ * representation would no longer match).
126
+ */
127
+ filename?: string;
128
+ }
129
+ interface WriteDataBaseParams {
130
+ session: WriteSession;
131
+ /**
132
+ * The scope to write into; must match one of the session's write patterns.
133
+ * A derived scope must not share its first dot-segment with any source's
134
+ * scope (the server rejects it with `LINEAGE_SCOPE_UNDER_SOURCE_PREFIX`):
135
+ * put derivatives in the app's own namespace.
136
+ */
137
+ scope: string;
138
+ /**
139
+ * The data points this record was derived from: ids
140
+ * ({@link deriveDataPointId}) or `{ ownerAddress, scope }` pairs the SDK
141
+ * derives the id from. Distinct, at most {@link MAX_LINEAGE_SOURCES}, all
142
+ * belonging to the same owner as the target scope, never the record's own
143
+ * id. Sent lowercased as the record's `lineage` field, inside the signed
144
+ * bytes. `[]` is an explicit root statement and is sent; absent or `null`
145
+ * makes no statement. Pairs also let the SDK apply the naming rule
146
+ * ({@link assertDerivedScopeNaming}) before anything is signed.
147
+ */
148
+ lineage?: readonly LineageSource[] | null;
149
+ /** `fetch` to use; defaults to `globalThis.fetch`. */
150
+ fetch?: typeof fetch;
151
+ /** Extra request headers. */
152
+ headers?: HeadersInit;
153
+ retry?: WriteTransportRetryOptions;
154
+ }
155
+ /**
156
+ * A JSON write: `data` is stored as the record (plus `lineage` when given).
157
+ * Anything else to store goes inside `data`; `lineage` and reserved keys are
158
+ * not accepted in it.
159
+ */
160
+ export interface WriteJsonDataParams extends WriteDataBaseParams {
161
+ data: Record<string, unknown>;
162
+ binary?: never;
163
+ metadata?: never;
164
+ }
165
+ /** A binary write: the bytes are stored as a `$binary` record. */
166
+ export interface WriteBinaryDataParams extends WriteDataBaseParams {
167
+ binary: WriteBinaryPayload;
168
+ data?: never;
169
+ /**
170
+ * Caller metadata stored with the record (`X-Vana-Metadata`, part of the
171
+ * signed `$binary` record). Must not contain `lineage` (use the option) or
172
+ * a reserved key.
173
+ */
174
+ metadata?: Record<string, unknown>;
175
+ }
176
+ export type WriteDataParams = WriteJsonDataParams | WriteBinaryDataParams;
177
+ /** The Personal Server's ingest answer, plus the accepted lineage if any. */
178
+ export type WriteDataResult = IngestResponse & {
179
+ lineage?: {
180
+ sources: Hex[];
181
+ };
182
+ };
183
+ /**
184
+ * Open a write session against a Personal Server.
185
+ *
186
+ * @remarks
187
+ * Sends `POST /v1/write/session` with a Web3Signed `Authorization` header
188
+ * whose signed claims carry `grantId`. The handshake proof is single-use on
189
+ * the server; a transport retry signs a new one.
190
+ *
191
+ * @returns The session to pass to {@link writeData}.
192
+ * @throws {WriteSessionError} When the Personal Server refuses the handshake
193
+ * (`errorCode` names why: `UNREGISTERED_BUILDER`, `GRANT_REQUIRED`,
194
+ * `GRANT_REVOKED`, `SCOPE_MISMATCH` for a grant without write entries,
195
+ * `INVALID_SIGNATURE` when the key is not the grantee,
196
+ * `GRANT_OWNER_MISMATCH`, ...).
197
+ * @throws {WriteTransportError} When `fetch` threw on every attempt.
198
+ * @throws {WriteRequestError} When the signer is unusable.
199
+ */
200
+ export declare function openWriteSession(params: OpenWriteSessionParams): Promise<WriteSession>;
201
+ /** `true` when one of the session's write patterns covers `scope`. */
202
+ export declare function sessionCoversScope(session: Pick<WriteSession, "writeScopes">, scope: string): boolean;
203
+ /**
204
+ * The media type the Personal Server stores for a binary write: the
205
+ * `Content-Type` minus its parameters, `application/octet-stream` when blank.
206
+ */
207
+ export declare function normalizeBinaryMimeType(contentType: string | null): string;
208
+ /**
209
+ * How the Personal Server reads an `X-Vana-Metadata` header: JSON when it
210
+ * parses, the raw string otherwise, `undefined` when absent or blank.
211
+ */
212
+ export declare function parseWriteMetadataHeader(value: string | null): unknown;
213
+ /**
214
+ * Encode a metadata object for the `X-Vana-Metadata` header. Non-ASCII
215
+ * characters are `\uXXXX`-escaped so the value is header-safe everywhere;
216
+ * it parses back to the same object.
217
+ */
218
+ export declare function encodeWriteMetadataHeader(metadata: Record<string, unknown>): string;
219
+ export interface BinaryWriteSignedBytesInput {
220
+ /** The raw body bytes the write sends. */
221
+ bytes: Uint8Array;
222
+ /** The `Content-Type` header the write sends (parameters are ignored). */
223
+ contentType: string;
224
+ /** The `X-Filename` header value the write sends, if any. */
225
+ filename?: string;
226
+ /** The exact `X-Vana-Metadata` header value the write sends, if any. */
227
+ metadataHeader?: string;
228
+ }
229
+ /**
230
+ * The bytes a builder signs for a binary write: the compact JSON of the
231
+ * `$binary` record the Personal Server stores for these headers and bytes
232
+ * (`personal-server-ts` `binaryWriteSignedBytes`, mirrored field for field).
233
+ *
234
+ * @returns UTF-8 bytes of the stored record's compact JSON.
235
+ */
236
+ export declare function binaryWriteSignedBytes(input: BinaryWriteSignedBytesInput): Uint8Array;
237
+ /**
238
+ * Write one record into a scope under an open write session.
239
+ *
240
+ * @remarks
241
+ * Sends `POST /v1/data/:scope` with `Authorization: Bearer <session token>`
242
+ * and `X-Vana-Write-Signature`, a Web3Signed proof over the stored
243
+ * representation carrying the session's `grantId` as a signed claim. JSON
244
+ * writes send `data` as compact JSON with `Content-Type: application/json`;
245
+ * binary writes send the bytes with their `Content-Type`, `X-Filename`, and
246
+ * sign {@link binaryWriteSignedBytes}. `lineage` is the record's top-level
247
+ * `lineage` field for JSON and the `lineage` field of `X-Vana-Metadata` for
248
+ * binary, so the proof covers it either way.
249
+ *
250
+ * @returns The ingest answer (`scope`, `collectedAt`, `status`, and
251
+ * `lineage.sources` when the write carried lineage).
252
+ * @throws {WriteRequestError} Before sending: no payload, a reserved key, a
253
+ * malformed lineage id, or non-object data.
254
+ * @throws {WriteSessionExpiredError} Before sending: the session token has
255
+ * passed its lifetime.
256
+ * @throws {WriteUnauthorizedError} 401 (proof or session rejected).
257
+ * @throws {WriteForbiddenError} 403 (grant no longer authorises the write).
258
+ * @throws {WriteConflictError} 409.
259
+ * @throws {WriteLineageError} Any `LINEAGE_*` rejection: 422
260
+ * `LINEAGE_SOURCE_UNKNOWN` (`details.unknown`), 400 `LINEAGE_INVALID` /
261
+ * `LINEAGE_SCOPE_UNDER_SOURCE_PREFIX`, 502 `LINEAGE_SOURCE_LOOKUP_FAILED`.
262
+ * @throws {WriteRejectedError} Any other non-2xx.
263
+ * @throws {WriteTransportError} `fetch` threw on every attempt.
264
+ */
265
+ export declare function writeData(params: WriteDataParams): Promise<WriteDataResult>;
266
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
267
+ export type WritePersonalServerDataParams = Omit<OpenWriteSessionParams, "fetch" | "headers" | "retry"> & DistributiveOmit<WriteDataParams, "session">;
268
+ /** {@link writePersonalServerData}'s answer: the ingest result plus the session it opened. */
269
+ export interface WritePersonalServerDataResult extends WriteDataResult {
270
+ /** Reuse for further writes until `expiresAt`. */
271
+ session: WriteSession;
272
+ }
273
+ /**
274
+ * Open a write session and write one record in a single call.
275
+ *
276
+ * @remarks
277
+ * Equivalent to {@link openWriteSession} followed by {@link writeData}. The
278
+ * session is returned so further writes can reuse it; opening one per write
279
+ * is correct but costs an extra signature and round-trip each time.
280
+ *
281
+ * @throws Everything {@link openWriteSession} and {@link writeData} throw.
282
+ */
283
+ export declare function writePersonalServerData(params: WritePersonalServerDataParams): Promise<WritePersonalServerDataResult>;
284
+ export {};