{"version":3,"sources":["../../src/direct/types.ts"],"sourcesContent":["import type { EscrowAccessRecord } from \"../protocol/escrow\";\nimport type { ProtocolNetwork } from \"../protocol/networks\";\n\n/**\n * Shared types for the Direct Data Controller and the browser connect helper.\n *\n * @remarks\n * These types describe the \"two-tab\" Data Portability flow documented in the\n * builder guide: a backend controller creates an access request, the browser\n * opens Vana for user approval, and the backend reads the approved data from\n * the user's Personal Server (handling 402 Payment Required).\n *\n * @category Direct\n * @module direct/types\n */\n\n/**\n * Target environment for a {@link DirectDataController}.\n *\n * - `\"production\"` — Vana mainnet stack (default service URLs).\n * - `\"dev\"` — Vana internal dev stack. Use only when testing against\n *   Vana's dev infrastructure.\n */\nexport type DirectEnv = \"dev\" | \"production\";\n\n/**\n * Vana network used for chain-aware Direct defaults.\n *\n * - `\"mainnet\"` — Vana mainnet (`chainId` 1480).\n * - `\"moksha\"` — Moksha testnet (`chainId` 14800).\n */\nexport type DirectNetwork = ProtocolNetwork;\n\n/**\n * App identity advertised to users during approval and attributed in Builder\n * League activity reports.\n */\nexport interface DirectAppConfig {\n  /** Stable, human-readable app id (e.g. `\"notes-lens\"`). */\n  id: string;\n  /** Display name shown to the user in the Vana approval UI. */\n  name: string;\n  /** Public homepage URL for the app. */\n  homepageUrl: string;\n}\n\n/**\n * Resolved app identity: the configured {@link DirectAppConfig} plus the app's\n * derived on-chain address (the address to fund and inspect).\n */\nexport interface AppIdentity extends DirectAppConfig {\n  /** The app's `0x`-prefixed on-chain address (derived from `appPrivateKey`). */\n  address: string;\n}\n\n/** One-time HTTPS callback used to deliver a foreground mobile Direct read. */\nexport interface ForegroundDelivery {\n  /** Fixed, same-origin consumer callback URL. */\n  url: string;\n  /** High-entropy bearer capability, generated and retained by the consumer. */\n  token: string;\n}\n\n/**\n * One derivative question carried on an access request.\n *\n * @remarks\n * A question asks the user's Personal Server to compute an answer scope\n * (`derivedScope`) from source scopes the app never reads. On approval, the\n * Vana app registers the question on the Personal Server as the owner and the\n * grant covers only the derived scope, so the raw sources stay private to the\n * user. The SDK validates each question before the create request is signed\n * (see `validateAccessRequestQuestions`); the access-request service remains\n * authoritative and re-validates server-side.\n */\nexport interface AccessRequestQuestion {\n  /**\n   * Concrete scope the computed answer is written to and read from (no\n   * wildcards, no operation prefix). It must also appear verbatim in the\n   * request `scopes` as a bare read entry, and its first dot-segment must\n   * differ from the first dot-segment of every entry in\n   * {@link AccessRequestQuestion.sourceScopes}.\n   */\n  derivedScope: string;\n  /**\n   * Concrete scopes the answer is computed from: 1 to 16 entries, no\n   * duplicates, none equal to {@link AccessRequestQuestion.derivedScope}.\n   * These scopes are never granted to the app.\n   */\n  sourceScopes: string[];\n  /**\n   * Natural-language question the Personal Server answers from the source\n   * scopes. Must be 1 to 4000 characters after trimming.\n   */\n  question: string;\n  /**\n   * When the Personal Server recomputes the answer. `\"snapshot\"` computes\n   * once at registration; `\"on-change\"` also recomputes when a source scope\n   * changes. When omitted, the server-side default applies.\n   */\n  recompute?: \"snapshot\" | \"on-change\";\n}\n\n/**\n * Resolved service URLs and chain id for a given {@link DirectEnv}.\n *\n * @remarks\n * Centralizes the per-environment base URLs the controller talks to. Each can\n * be overridden via {@link DirectDataControllerConfig.endpoints} when pointing\n * at a non-standard deployment.\n */\nexport interface DirectServiceEndpoints {\n  /** Vana chain id for this environment (1480 mainnet, 14800 moksha). */\n  chainId: number;\n  /** Base URL of the Vana Account access-request API that issues `dcr_*` ids. */\n  accessRequestBaseUrl: string;\n  /** Base URL users are sent to for approval (the Vana app). */\n  approvalAppBaseUrl: string;\n  /** Base URL of the DP RPC escrow gateway used to settle `402 Payment Required`. */\n  escrowGatewayUrl: string;\n}\n\n/** Result of {@link DirectDataController.createAccessRequest}. */\nexport interface AccessRequest {\n  /** Opaque request id (e.g. `\"dcr_123\"`). */\n  requestId: string;\n  /** URL the browser opens so the user can approve the requested scopes. */\n  approvalUrl: string;\n  /** On-chain address of the (registered or reused) app. */\n  appAddress: string;\n  /** Protocol network echoed by the access-request service. */\n  network?: DirectNetwork;\n  /** Authoritative ISO-8601 expiry for the access request. */\n  expiresAt?: string;\n  /**\n   * HTTPS continuation URL for a deep Direct request on a mobile browser.\n   *\n   * @remarks\n   * Present only for a server-classified deep Direct DCR while it remains\n   * pending, and only when Mobile continuation is enabled. It is an ordinary\n   * `https://open[-dev].vana.org/continue#<ticket>` link the mobile UI renders\n   * as a primary \"Open Vana\" tap — iOS Universal Links / Android App Links\n   * deliver it to Vana Mobile, and its web fallback recovers an absent app. The\n   * SDK never launches it automatically and owns no persistence.\n   */\n  mobileContinuationUrl?: string;\n}\n\n/** Canonical mobile continuation link host per {@link DirectEnv}. */\nconst MOBILE_CONTINUATION_HOSTS: Record<DirectEnv, string> = {\n  production: \"open.vana.org\",\n  dev: \"open-dev.vana.org\",\n};\n\n/** URL-fragment-safe opaque ticket: no separators, query, or scheme chars. */\nconst MOBILE_CONTINUATION_TICKET = /^[A-Za-z0-9._~-]+$/;\n\n/**\n * @internal Strictly validate a mobile HTTPS continuation URL at the SDK\n * boundary.\n *\n * @remarks\n * Accepts only `https://open[-dev].vana.org/continue#<ticket>` with exactly one\n * well-formed opaque fragment ticket and no user info, port, or query. When\n * `env` is supplied only that environment's host is allowed; otherwise both\n * canonical hosts are accepted for structural (defense-in-depth) validation.\n *\n * @param value - The candidate URL from a create or status response.\n * @param env - Optional environment to pin the allowed host to.\n * @returns The canonical URL string, or `undefined` when it fails validation.\n */\nexport function normalizeMobileContinuationUrl(\n  value: unknown,\n  env?: DirectEnv,\n): string | undefined {\n  if (typeof value !== \"string\" || value.length === 0) return undefined;\n  let url: URL;\n  try {\n    url = new URL(value);\n  } catch {\n    return undefined;\n  }\n  if (url.protocol !== \"https:\") return undefined;\n  const allowedHosts = env\n    ? [MOBILE_CONTINUATION_HOSTS[env]]\n    : Object.values(MOBILE_CONTINUATION_HOSTS);\n  if (!allowedHosts.includes(url.hostname)) return undefined;\n  if (url.pathname !== \"/continue\") return undefined;\n  if (url.username !== \"\" || url.password !== \"\") return undefined;\n  if (url.port !== \"\") return undefined;\n  if (url.search !== \"\") return undefined;\n  const ticket = url.hash.startsWith(\"#\") ? url.hash.slice(1) : \"\";\n  if (!MOBILE_CONTINUATION_TICKET.test(ticket)) return undefined;\n  return url.toString();\n}\n\n/**\n * Lifecycle status of an access request.\n *\n * @remarks\n * - `\"pending\"` — created, awaiting user approval.\n * - `\"approved\"` / `\"ready_for_read\"` — the grant exists and the Personal\n *   Server is reachable; the data is read-ready (see {@link DirectDataController.readApprovedData}).\n * - `\"completed\"` — the app has already read the data and acknowledged it, so\n *   the DCR is terminal. A `\"completed\"` request is **not** read-ready — the\n *   browser Personal Server may no longer be serving it.\n * - `\"denied\"` / `\"expired\"` — terminal, no data was delivered.\n */\nexport type AccessRequestStatusValue =\n  | \"pending\"\n  | \"approved\"\n  | \"ready_for_read\"\n  | \"completed\"\n  | \"denied\"\n  | \"expired\";\n\n/** Delivery path reported once an access request is ready. */\nexport type AccessRequestDelivery = \"enclave\" | \"personal_server\";\n\n/** Result of {@link DirectDataController.getAccessRequestStatus}. */\nexport interface AccessRequestStatus {\n  /** Current lifecycle status of the request. */\n  status: AccessRequestStatusValue;\n  /**\n   * Present once ready. `\"enclave\"` reads through the Gateway jobs API\n   * (`protocol/jobs` client), with no `personalServerUrl`.\n   */\n  delivery?: AccessRequestDelivery;\n  /** Personal Server base URL — present once data is ready to read. */\n  personalServerUrl?: string;\n  /** Grant id covering the approved scope — present once data is ready to read. */\n  grantId?: string;\n  /**\n   * The first approved scope — present once data is ready to read.\n   *\n   * @remarks\n   * Kept for backwards compatibility. A request can approve many scopes; read\n   * {@link AccessRequestStatus.scopes} to see all of them.\n   */\n  scope?: string;\n  /**\n   * Fresh HTTPS mobile continuation URL, returned only while the deep Direct\n   * DCR is still pending. Its embedded ticket may rotate between polls.\n   */\n  mobileContinuationUrl?: string;\n  /**\n   * Every scope the user approved on this request — present once data is ready\n   * to read.\n   *\n   * @remarks\n   * A grant is keyed by `(user, app)` and carries a list of scopes, so a single\n   * approval can cover several. Against an older Vana Account deployment that\n   * only returns `scope`, this falls back to `[scope]`.\n   */\n  scopes?: string[];\n}\n\n/** Result of {@link DirectDataController.readApprovedData}. */\nexport interface ApprovedDataResult<T = unknown> {\n  /** The scope the data was read for. */\n  scope: string;\n  /** The decoded payload returned by the Personal Server. */\n  data: T;\n  /**\n   * Shape-validated but unauthenticated payment metadata echoed by the\n   * Personal Server. Use for display/debugging, not accounting proof.\n   */\n  payment?: DirectPaymentResponseMetadata;\n}\n\n/**\n * Result of {@link DirectDataController.readApprovedData} across every approved\n * scope.\n *\n * @remarks\n * Successes and failures are reported side by side rather than as a thrown\n * error, because each scope read settles its own fee: throwing on the third\n * scope would discard data the app has already paid for. Check `errors` before\n * treating the read as complete.\n */\nexport interface MultiScopeDataResult<T = unknown> {\n  /** Scopes that read successfully, keyed by scope. */\n  results: Record<string, ApprovedDataResult<T>>;\n  /** Scopes that failed, keyed by scope. Empty when every scope read. */\n  errors: Record<string, Error>;\n}\n\n/**\n * Client for the Vana Account access-request API — the service that turns a\n * registered app + scopes into a `dcr_*` id and approval URL.\n *\n * @remarks\n * The controller uses a default client against the Vana Account endpoints. You\n * can inject your own implementation to point at a custom deployment or to\n * supply a test double.\n */\nexport interface AccessRequestClient {\n  /**\n   * Create an access request for the given app + scopes.\n   *\n   * @param input - App identity, source, scopes, network, and the post-approval return URL.\n   * @returns The created {@link AccessRequest}.\n   */\n  createAccessRequest(input: {\n    appAddress: string;\n    app: DirectAppConfig;\n    source: string;\n    scopes: string[];\n    returnUrl: string;\n    /** Vana protocol network for this request (`\"mainnet\"` or `\"moksha\"`). */\n    network: DirectNetwork;\n    /** Optional foreground mobile delivery callback. */\n    foregroundDelivery?: ForegroundDelivery;\n    /**\n     * Derivative questions to carry on the request (1 to 4). When present,\n     * the array is validated client-side and serialized into the signed\n     * create body verbatim. See {@link AccessRequestQuestion}.\n     */\n    questions?: AccessRequestQuestion[];\n    /**\n     * Optional retry key. The default client generates a fresh key per call\n     * when omitted; pass a stable key to retry a create whose response was\n     * lost without risking a duplicate DCR.\n     */\n    idempotencyKey?: string;\n  }): Promise<AccessRequest>;\n\n  /**\n   * Fetch the current status of a previously created access request.\n   *\n   * @param requestId - The `dcr_*` id returned by {@link AccessRequestClient.createAccessRequest}.\n   * @returns The current {@link AccessRequestStatus}.\n   */\n  getAccessRequestStatus(requestId: string): Promise<AccessRequestStatus>;\n\n  /**\n   * Acknowledge that the app successfully read the approved data.\n   *\n   * @remarks\n   * Direct Vana Web DCRs remain in `ready_for_read` while the browser Personal\n   * Server is serving the app. After a successful Personal Server read, the\n   * controller calls this hook so Vana Web can mark the request completed and\n   * close/redirect the approval tab.\n   *\n   * Optional so injected clients from older SDK integrations keep compiling;\n   * the default HTTP client implements it.\n   */\n  acknowledgeRead?(requestId: string): Promise<void>;\n}\n\n/**\n * Op-type vocabulary used by the DPv2 escrow payment surface.\n *\n * @remarks\n * These are the operations the gateway prices and settles via\n * `POST /v1/escrow/pay` (`opType` field of the `GenericPayment` message). A\n * direct data read settles the {@link DirectOpType.DataAccess} op for the\n * approved grant; the other op types are listed here for completeness and to\n * give builders a typed vocabulary when inspecting fee breakdowns.\n *\n * GenericPayment uses `\"grant\"` for legacy grant lifecycle payments and\n * `\"data_access\"` for standalone receipt-bound reads.\n */\nexport const DirectOpType = {\n  GrantRegistration: \"grant_registration\",\n  DataAccess: \"data_access\",\n  DataRegistration: \"data_registration\",\n  ServerRegistration: \"server_registration\",\n  BuilderRegistration: \"builder_registration\",\n} as const;\n\n/** A direct-flow op type (see {@link DirectOpType}). */\nexport type DirectOpTypeValue =\n  (typeof DirectOpType)[keyof typeof DirectOpType];\n\n/**\n * What a Personal Server `402 Payment Required` tells the controller is owed for\n * a data read.\n *\n * @remarks\n * The PS read 402 body identifies the challenged operation and amount/asset.\n * The controller settles it via the DPv2 escrow gateway (`/v1/escrow/pay`). The\n * full unmodified body is preserved under\n * {@link PersonalServerPaymentRequired.raw}.\n */\nexport interface PersonalServerPaymentRequired {\n  /** Grant id authorizing the Personal Server read. */\n  grantId: string;\n  /** X402 network advertised by the Personal Server challenge. */\n  network?: string;\n  /** Payment nonce requested by the 402 challenge. */\n  paymentNonce?: string;\n  /** Data-access receipt carrying a signature for the gateway to verify. */\n  accessRecord?: EscrowAccessRecord;\n  /** Asset address owed (zero address = native VANA). */\n  asset: string;\n  /** Amount owed, as a decimal base-unit string (preserves uint256 precision). */\n  amount: string;\n  /** The full, unmodified 402 response body. */\n  raw: unknown;\n}\n\n/** A validated legacy grant payment challenge. */\nexport interface PersonalServerGrantPaymentOperation extends PersonalServerPaymentRequired {\n  /** Escrow operation discriminator. */\n  opType: \"grant\";\n  /** Grant id settled by the escrow payment. */\n  opId: string;\n}\n\n/** A validated receipt-bound data-access payment challenge. */\nexport interface PersonalServerDataAccessPaymentOperation extends PersonalServerPaymentRequired {\n  /** Escrow operation discriminator. */\n  opType: \"data_access\";\n  /** Access-record id settled by the escrow payment. */\n  opId: string;\n  /** Complete receipt whose signature is verified later by the gateway. */\n  accessRecord: EscrowAccessRecord;\n  /** Positive uint256 nonce supplied by the Personal Server challenge. */\n  paymentNonce: string;\n}\n\n/**\n * A Personal Server payment challenge whose escrow operation has been\n * validated.\n *\n * @remarks\n * Validation here is structural and binds operation ids to their receipt. It\n * does not cryptographically verify the receipt signature; the Personal\n * Server and Data Gateway perform that verification.\n */\nexport type PersonalServerPaymentOperation =\n  | PersonalServerGrantPaymentOperation\n  | PersonalServerDataAccessPaymentOperation;\n\n/** Shape-validated payment response returned directly by the escrow gateway. */\nexport interface DirectPaymentReceipt {\n  /** Op type settled (the gateway `opType`, e.g. `\"grant\"`). */\n  opType: string;\n  /** Op id settled (a grant id or access-record id). */\n  opId: string;\n  /** Asset paid in (zero address = native VANA). */\n  asset: string;\n  /** Total amount paid, as a decimal base-unit string. */\n  amount: string;\n  /** Payment nonce used for this settlement. */\n  paymentNonce: string;\n  /** Fee breakdown reported by the gateway (registration vs data-access fee). */\n  breakdown: DirectFeeBreakdown;\n  /** ISO timestamp the gateway recorded the payment. */\n  paidAt: string;\n}\n\n/**\n * Untrusted payment response metadata echoed by a Personal Server.\n *\n * @remarks\n * The SDK validates every field before exposing this shape, but the response\n * header is not signed by the gateway. Use it for display and debugging only,\n * never as accounting proof that a payment occurred.\n */\nexport type DirectPaymentResponseMetadata = DirectPaymentReceipt;\n\n/**\n * Per-op fee breakdown reported by the gateway.\n *\n * @remarks\n * Mirrors the escrow {@link PaymentBreakdown}: a one-time registration fee plus\n * the per-read data-access fee, and whether this settlement covered the\n * registration fee.\n */\nexport interface DirectFeeBreakdown {\n  /** One-time registration fee for the op, as a decimal base-unit string. */\n  registrationFee: string;\n  /** Per-read data-access fee, as a decimal base-unit string. */\n  dataAccessFee: string;\n  /** True when this settlement paid the registration fee. */\n  registrationPaid: boolean;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqJA,MAAM,4BAAuD;AAAA,EAC3D,YAAY;AAAA,EACZ,KAAK;AACP;AAGA,MAAM,6BAA6B;AAgB5B,SAAS,+BACd,OACA,KACoB;AACpB,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAAG,QAAO;AAC5D,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,KAAK;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,IAAI,aAAa,SAAU,QAAO;AACtC,QAAM,eAAe,MACjB,CAAC,0BAA0B,GAAG,CAAC,IAC/B,OAAO,OAAO,yBAAyB;AAC3C,MAAI,CAAC,aAAa,SAAS,IAAI,QAAQ,EAAG,QAAO;AACjD,MAAI,IAAI,aAAa,YAAa,QAAO;AACzC,MAAI,IAAI,aAAa,MAAM,IAAI,aAAa,GAAI,QAAO;AACvD,MAAI,IAAI,SAAS,GAAI,QAAO;AAC5B,MAAI,IAAI,WAAW,GAAI,QAAO;AAC9B,QAAM,SAAS,IAAI,KAAK,WAAW,GAAG,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI;AAC9D,MAAI,CAAC,2BAA2B,KAAK,MAAM,EAAG,QAAO;AACrD,SAAO,IAAI,SAAS;AACtB;AAyKO,MAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;","names":[]}