{"version":3,"sources":["../../src/direct/access-request-client.ts"],"sourcesContent":["/**\n * Default client for the Vana Account access-request API.\n *\n * @remarks\n * Calls the Vana Account endpoints that issue `dcr_*` ids and approval URLs and\n * report request status. Inject a custom {@link AccessRequestClient} on the\n * controller to point at a different deployment; pass `fetchFn` to supply a test\n * double for the HTTP layer.\n *\n * @category Direct\n * @module direct/access-request-client\n */\n\nimport type {\n  AccessRequest,\n  AccessRequestClient,\n  AccessRequestDelivery,\n  AccessRequestQuestion,\n  AccessRequestStatus,\n  AccessRequestStatusValue,\n  DirectEnv,\n} from \"./types\";\nimport { normalizeMobileContinuationUrl } from \"./types\";\nimport type { Web3SignedSignFn } from \"../auth/web3-signed-builder\";\nimport { parseScope, type ParsedScope } from \"../protocol/scopes\";\nimport { DirectConfigError } from \"./errors\";\n\n/** Minimal `fetch` signature so the client is testable without a global fetch. */\nexport type FetchLike = (\n  input: string,\n  init?: {\n    method?: string;\n    headers?: Record<string, string>;\n    body?: string;\n  },\n) => Promise<{\n  ok: boolean;\n  status: number;\n  statusText: string;\n  json(): Promise<unknown>;\n  text(): Promise<string>;\n}>;\n\n/** Options for {@link createDefaultAccessRequestClient}. */\nexport interface DefaultAccessRequestClientOptions {\n  /** Base URL of the Vana Account access-request API. */\n  baseUrl: string;\n  /** Base URL the user is sent to for approval. */\n  approvalBaseUrl: string;\n  /**\n   * Target environment. Pins the allowed mobile continuation link host\n   * (`open.vana.org` for production, `open-dev.vana.org` for dev). When omitted,\n   * both canonical hosts pass the structural continuation-URL check.\n   */\n  env?: DirectEnv;\n  /** `fetch` implementation. Defaults to the global `fetch`. */\n  fetchFn?: FetchLike;\n  /** App identity address used for direct access-request authentication. */\n  appAddress?: string;\n  /** EIP-191 signer for direct access-request authentication. */\n  signMessage?: Web3SignedSignFn;\n  /** Clock source used for signed request timestamps. */\n  now?: () => number;\n  /**\n   * Create the signed DCR idempotency key used when a create call omits one.\n   * Called once per create. Injectable for deterministic tests.\n   */\n  createIdempotencyKey?: () => string;\n}\n\nconst VALID_STATUSES: readonly AccessRequestStatusValue[] = [\n  \"pending\",\n  \"approved\",\n  \"ready_for_read\",\n  \"completed\",\n  \"denied\",\n  \"expired\",\n];\n\nfunction normalizeStatus(value: unknown): AccessRequestStatusValue {\n  return VALID_STATUSES.includes(value as AccessRequestStatusValue)\n    ? (value as AccessRequestStatusValue)\n    : \"pending\";\n}\n\nfunction normalizeDelivery(value: unknown): AccessRequestDelivery | undefined {\n  return value === \"enclave\" || value === \"personal_server\" ? value : undefined;\n}\n\nfunction normalizeNetwork(value: unknown): AccessRequest[\"network\"] {\n  return value === \"mainnet\" || value === \"moksha\" ? value : undefined;\n}\n\nfunction normalizeExpiresAt(value: unknown): string | undefined {\n  return typeof value === \"string\" && Number.isFinite(Date.parse(value))\n    ? value\n    : undefined;\n}\n\nfunction defaultCreateIdempotencyKey(): string {\n  if (typeof globalThis.crypto?.randomUUID !== \"function\") {\n    throw new Error(\n      \"Secure randomUUID is unavailable. Pass createIdempotencyKey to createDefaultAccessRequestClient.\",\n    );\n  }\n  return globalThis.crypto.randomUUID();\n}\n\nfunction stripTrailingSlash(url: string): string {\n  return url.replace(/\\/+$/, \"\");\n}\n\nconst DIRECT_ACCESS_REQUEST_MESSAGE_PREFIX = \"Vana Direct Access Request v1\";\n\ninterface DirectAccessRequestAuthInput {\n  body: string;\n  method: string;\n  path: string;\n  timestamp: string;\n}\n\nexport function buildDirectAccessRequestAuthMessage(\n  input: DirectAccessRequestAuthInput,\n): string {\n  return [\n    DIRECT_ACCESS_REQUEST_MESSAGE_PREFIX,\n    `method:${input.method.toUpperCase()}`,\n    `path:${input.path}`,\n    `timestamp:${input.timestamp}`,\n    `body:${input.body}`,\n  ].join(\"\\n\");\n}\n\nasync function buildDirectAccessRequestHeaders(\n  options: DefaultAccessRequestClientOptions,\n  input: Omit<DirectAccessRequestAuthInput, \"timestamp\">,\n): Promise<Record<string, string>> {\n  if (!options.appAddress && !options.signMessage) {\n    return {};\n  }\n  if (!options.appAddress || !options.signMessage) {\n    throw new Error(\n      \"Direct access-request authentication requires both `appAddress` and `signMessage`.\",\n    );\n  }\n\n  const timestamp = String(options.now?.() ?? Date.now());\n  const signature = await options.signMessage(\n    buildDirectAccessRequestAuthMessage({ ...input, timestamp }),\n  );\n\n  return {\n    \"X-Vana-App-Address\": options.appAddress,\n    \"X-Vana-App-Signature\": signature,\n    \"X-Vana-App-Timestamp\": timestamp,\n  };\n}\n\n/**\n * Build an approval URL for a request id, matching the documented format\n * (`{app}/data-connection-requests/{requestId}?mode=page`).\n *\n * @param approvalBaseUrl - Base URL of the Vana approval app.\n * @param requestId - The `dcr_*` request id.\n * @returns The full approval URL.\n */\nexport function buildApprovalUrl(\n  approvalBaseUrl: string,\n  requestId: string,\n): string {\n  return `${stripTrailingSlash(approvalBaseUrl)}/data-connection-requests/${encodeURIComponent(\n    requestId,\n  )}?mode=page`;\n}\n\n/** The `recompute` values the question contract defines today. */\nconst RECOMPUTE_VALUES: readonly string[] = [\"snapshot\", \"on-change\"];\n\nfunction parseConcreteScope(field: string, value: unknown): ParsedScope {\n  if (typeof value !== \"string\") {\n    throw new DirectConfigError(`${field} must be a string`, { field });\n  }\n  try {\n    return parseScope(value);\n  } catch {\n    throw new DirectConfigError(\n      `${field} \"${value}\" is not a concrete scope. Use {source}.{category}[.{subcategory}] with no wildcard and no operation prefix.`,\n      { field, value },\n    );\n  }\n}\n\n/**\n * Validate the derivative questions on a create input against the request\n * scope entries.\n *\n * @remarks\n * Client-side mirror of the access-request service rules so builders fail\n * fast, before the create request is signed and sent; the service remains\n * authoritative. Rules: 1 to 4 questions; every `derivedScope` and every\n * `sourceScope` is a concrete scope (wildcards rejected); 1 to 16 source\n * scopes per question with no duplicates and none equal to the derived scope;\n * the first dot-segment of the derived scope differs from the first\n * dot-segment of every source scope; the derived scope appears verbatim in\n * `scopes` as a bare read entry; no two questions share a derived scope; the\n * question text is 1 to 4000 characters after trimming; `recompute`, when\n * present, is `\"snapshot\"` or `\"on-change\"`.\n *\n * @param questions - The `questions` array from the create input.\n * @param scopes - The request's grant scope entries, verbatim.\n * @throws {DirectConfigError} - When any rule is violated. The message names\n * the offending question index and field.\n */\nexport function validateAccessRequestQuestions(\n  questions: readonly AccessRequestQuestion[],\n  scopes: readonly string[],\n): void {\n  if (questions.length === 0 || questions.length > 4) {\n    throw new DirectConfigError(\n      `questions must contain 1 to 4 entries when present, got ${questions.length}. Omit the field to send no questions.`,\n      { count: questions.length },\n    );\n  }\n  const seenDerived = new Set<string>();\n  questions.forEach((question, index) => {\n    const label = `questions[${index}]`;\n    const derived = parseConcreteScope(\n      `${label}.derivedScope`,\n      question.derivedScope,\n    );\n    if (seenDerived.has(question.derivedScope)) {\n      throw new DirectConfigError(\n        `${label}.derivedScope \"${question.derivedScope}\" is already used by an earlier question. Each question must target its own derived scope.`,\n        { derivedScope: question.derivedScope },\n      );\n    }\n    seenDerived.add(question.derivedScope);\n    // The bare entry (no operation prefix) is what makes the answer readable\n    // by the app: `write:coach.weekly` alone would not grant the read back.\n    if (!scopes.includes(question.derivedScope)) {\n      throw new DirectConfigError(\n        `${label}.derivedScope \"${question.derivedScope}\" must also appear in scopes as a bare read entry, so the app can read the answer it asked for.`,\n        { derivedScope: question.derivedScope, scopes: [...scopes] },\n      );\n    }\n    if (\n      question.sourceScopes.length === 0 ||\n      question.sourceScopes.length > 16\n    ) {\n      throw new DirectConfigError(\n        `${label}.sourceScopes must contain 1 to 16 entries, got ${question.sourceScopes.length}.`,\n        { count: question.sourceScopes.length },\n      );\n    }\n    const seenSources = new Set<string>();\n    for (const sourceScope of question.sourceScopes) {\n      const source = parseConcreteScope(`${label}.sourceScopes`, sourceScope);\n      if (seenSources.has(sourceScope)) {\n        throw new DirectConfigError(\n          `${label}.sourceScopes contains \"${sourceScope}\" more than once. Deduplicate the source scopes.`,\n          { sourceScope },\n        );\n      }\n      seenSources.add(sourceScope);\n      if (sourceScope === question.derivedScope) {\n        throw new DirectConfigError(\n          `${label}.sourceScopes must not contain the derived scope \"${question.derivedScope}\".`,\n          { sourceScope },\n        );\n      }\n      if (source.source === derived.source) {\n        throw new DirectConfigError(\n          `${label}.derivedScope \"${question.derivedScope}\" must not share its first dot-segment \"${derived.source}\" with source scope \"${sourceScope}\". Name the derived scope under the app's own namespace.`,\n          { derivedScope: question.derivedScope, sourceScope },\n        );\n      }\n    }\n    if (typeof question.question !== \"string\") {\n      throw new DirectConfigError(`${label}.question must be a string`, {\n        field: `${label}.question`,\n      });\n    }\n    const trimmedLength = question.question.trim().length;\n    if (trimmedLength === 0 || trimmedLength > 4000) {\n      throw new DirectConfigError(\n        `${label}.question must be 1 to 4000 characters after trimming, got ${trimmedLength}.`,\n        { length: trimmedLength },\n      );\n    }\n    if (\n      question.recompute !== undefined &&\n      !RECOMPUTE_VALUES.includes(question.recompute)\n    ) {\n      throw new DirectConfigError(\n        `${label}.recompute must be \"snapshot\" or \"on-change\" when present, got \"${String(question.recompute)}\".`,\n        { recompute: question.recompute },\n      );\n    }\n  });\n}\n\n/**\n * Create the default {@link AccessRequestClient} for the Vana Account\n * access-request API.\n *\n * @param options - Base URLs and an optional `fetch` implementation.\n * @returns An {@link AccessRequestClient} backed by HTTP calls.\n */\nexport function createDefaultAccessRequestClient(\n  options: DefaultAccessRequestClientOptions,\n): AccessRequestClient {\n  const fetchFn = options.fetchFn ?? (globalThis.fetch as FetchLike);\n  if (!fetchFn) {\n    throw new Error(\n      \"No fetch implementation available. Pass `fetchFn` to createDefaultAccessRequestClient.\",\n    );\n  }\n  const base = stripTrailingSlash(options.baseUrl);\n\n  return {\n    async createAccessRequest(input): Promise<AccessRequest> {\n      if (input.questions !== undefined) {\n        validateAccessRequestQuestions(input.questions, input.scopes);\n      }\n      const path = \"/api/data-connection-requests\";\n      // Every call is an independent logical create, so it gets its own key.\n      // The client cannot tell two look-alike creates apart — one shared backend\n      // controller serves many users with the same app, scopes, and returnUrl —\n      // so deriving a key from the input would let the service deduplicate two\n      // users onto a single DCR. Retrying an uncertain create is the caller's\n      // decision: pass the same `idempotencyKey` back in.\n      const idempotencyKey =\n        input.idempotencyKey ??\n        (options.createIdempotencyKey ?? defaultCreateIdempotencyKey)();\n      const body = JSON.stringify({\n        appAddress: input.appAddress,\n        app: input.app,\n        source: input.source,\n        scopes: input.scopes,\n        returnUrl: input.returnUrl,\n        network: input.network,\n        ...(input.foregroundDelivery !== undefined\n          ? { foregroundDelivery: input.foregroundDelivery }\n          : {}),\n        ...(input.questions !== undefined\n          ? { questions: input.questions }\n          : {}),\n        idempotencyKey,\n      });\n      const res = await fetchFn(`${base}${path}`, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          ...(await buildDirectAccessRequestHeaders(options, {\n            body,\n            method: \"POST\",\n            path,\n          })),\n        },\n        body,\n      });\n      if (!res.ok) {\n        throw new Error(\n          `Access request service error: ${res.status} ${res.statusText}`,\n        );\n      }\n      const responseBody = (await res.json()) as {\n        requestId?: string;\n        id?: string;\n        approvalUrl?: string;\n        appAddress?: string;\n        network?: unknown;\n        expiresAt?: unknown;\n        mobileContinuationUrl?: unknown;\n      };\n      const requestId = responseBody.requestId ?? responseBody.id;\n      if (!requestId) {\n        throw new Error(\"Access request service returned no requestId\");\n      }\n      return {\n        requestId,\n        approvalUrl:\n          responseBody.approvalUrl ??\n          buildApprovalUrl(options.approvalBaseUrl, requestId),\n        appAddress: responseBody.appAddress ?? input.appAddress,\n        network: normalizeNetwork(responseBody.network),\n        expiresAt: normalizeExpiresAt(responseBody.expiresAt),\n        mobileContinuationUrl: normalizeMobileContinuationUrl(\n          responseBody.mobileContinuationUrl,\n          options.env,\n        ),\n      };\n    },\n\n    async getAccessRequestStatus(\n      requestId: string,\n    ): Promise<AccessRequestStatus> {\n      const path = `/api/data-connection-requests/${encodeURIComponent(requestId)}`;\n      const res = await fetchFn(`${base}${path}`, {\n        method: \"GET\",\n        headers: await buildDirectAccessRequestHeaders(options, {\n          body: \"\",\n          method: \"GET\",\n          path,\n        }),\n      });\n      if (!res.ok) {\n        throw new Error(\n          `Access request service error: ${res.status} ${res.statusText}`,\n        );\n      }\n      const body = (await res.json()) as {\n        status?: string;\n        delivery?: unknown;\n        personalServerUrl?: string;\n        grantId?: string;\n        scope?: string;\n        mobileContinuationUrl?: unknown;\n        scopes?: string[];\n      };\n      // `scopes` is the full approved set; `scope` is the first of them, kept\n      // for callers (and deployments) that predate the array.\n      const scopes =\n        body.scopes && body.scopes.length > 0\n          ? body.scopes\n          : body.scope\n            ? [body.scope]\n            : undefined;\n      return {\n        status: normalizeStatus(body.status),\n        delivery: normalizeDelivery(body.delivery),\n        personalServerUrl: body.personalServerUrl,\n        grantId: body.grantId,\n        scope: body.scope ?? scopes?.[0],\n        scopes,\n        mobileContinuationUrl: normalizeMobileContinuationUrl(\n          body.mobileContinuationUrl,\n          options.env,\n        ),\n      };\n    },\n\n    async acknowledgeRead(requestId: string): Promise<void> {\n      const path = `/api/data-connection-requests/${encodeURIComponent(requestId)}/consumer-ack`;\n      const res = await fetchFn(`${base}${path}`, {\n        method: \"POST\",\n        headers: await buildDirectAccessRequestHeaders(options, {\n          body: \"\",\n          method: \"POST\",\n          path,\n        }),\n      });\n      if (!res.ok) {\n        throw new Error(\n          `Access request ack service error: ${res.status} ${res.statusText}`,\n        );\n      }\n    },\n  };\n}\n"],"mappings":"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","names":[]}