@opendatalabs/vana-sdk 3.13.3 → 3.13.4-pr.184.b3b57a3
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.
- package/dist/account/personal-server-lite-owner-binding.d.ts +1 -1
- package/dist/account/personal-server-registration.d.ts +1 -1
- package/dist/auth/errors.d.ts +1 -1
- package/dist/auth/oauth-client.d.ts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/chains/index.d.ts +2 -2
- package/dist/chains.browser.d.ts +2 -2
- package/dist/chains.d.ts +2 -2
- package/dist/chains.node.d.ts +2 -2
- package/dist/contracts/contractController.d.ts +4 -4
- package/dist/core/client.d.ts +1 -1
- package/dist/crypto/ecies/base.d.ts +1 -1
- package/dist/crypto/ecies/browser.d.ts +1 -1
- package/dist/crypto/ecies/index.d.ts +2 -2
- package/dist/crypto/ecies/node.d.ts +1 -1
- package/dist/crypto/services/WalletKeyEncryptionService.d.ts +1 -1
- package/dist/direct/access-request-client.d.ts +2 -2
- package/dist/direct/connect-flow.d.ts +1 -1
- package/dist/direct/controller.cjs.map +1 -1
- package/dist/direct/controller.d.ts +8 -8
- package/dist/direct/controller.js.map +1 -1
- package/dist/direct/endpoints.d.ts +1 -1
- package/dist/direct/errors.d.ts +1 -1
- package/dist/direct/escrow-payment.cjs +147 -12
- package/dist/direct/escrow-payment.cjs.map +1 -1
- package/dist/direct/escrow-payment.d.ts +65 -12
- package/dist/direct/escrow-payment.js +143 -12
- package/dist/direct/escrow-payment.js.map +1 -1
- package/dist/direct/personal-server-read.cjs +164 -23
- package/dist/direct/personal-server-read.cjs.map +1 -1
- package/dist/direct/personal-server-read.d.ts +28 -18
- package/dist/direct/personal-server-read.js +167 -25
- package/dist/direct/personal-server-read.js.map +1 -1
- package/dist/direct/types.cjs.map +1 -1
- package/dist/direct/types.d.ts +48 -20
- package/dist/direct/types.js.map +1 -1
- package/dist/direct/use-direct-vana-connect.d.ts +1 -1
- package/dist/generated/abi/index.d.ts +25 -25
- package/dist/index.browser.d.ts +44 -44
- package/dist/index.browser.js.map +2 -2
- package/dist/index.node.cjs +492 -0
- package/dist/index.node.cjs.map +4 -4
- package/dist/index.node.d.ts +48 -45
- package/dist/index.node.js +480 -0
- package/dist/index.node.js.map +4 -4
- package/dist/node.d.ts +1 -1
- package/dist/platform/browser-only.d.ts +3 -3
- package/dist/platform/browser-safe.d.ts +3 -3
- package/dist/platform/browser.d.ts +1 -1
- package/dist/platform/index.d.ts +5 -5
- package/dist/platform/node.d.ts +1 -1
- package/dist/platform/ports/openpgp-port.d.ts +1 -1
- package/dist/platform/utils.d.ts +1 -1
- package/dist/platform.browser.d.ts +4 -4
- package/dist/platform.d.ts +5 -5
- package/dist/platform.node.d.ts +5 -5
- package/dist/protocol/data-point-status.d.ts +1 -1
- package/dist/protocol/eip712.cjs.map +1 -1
- package/dist/protocol/eip712.js.map +1 -1
- package/dist/protocol/escrow-deposit.d.ts +1 -1
- package/dist/protocol/escrow.cjs.map +1 -1
- package/dist/protocol/escrow.d.ts +11 -4
- package/dist/protocol/escrow.js.map +1 -1
- package/dist/protocol/fee-registry.d.ts +1 -1
- package/dist/protocol/gateway.cjs.map +1 -1
- package/dist/protocol/gateway.js.map +1 -1
- package/dist/protocol/grants.d.ts +1 -1
- package/dist/protocol/personal-server-data.d.ts +2 -2
- package/dist/protocol/personal-server-registration.d.ts +2 -2
- package/dist/react.d.ts +3 -3
- package/dist/server.cjs +10 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts +8 -8
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -1
- package/dist/session-relay/client.d.ts +1 -1
- package/dist/session-relay/endpoints.d.ts +1 -1
- package/dist/session-relay/errors.d.ts +1 -1
- package/dist/session-relay/index.d.ts +5 -5
- package/dist/session-relay/signing.d.ts +1 -1
- package/dist/session-relay/types.d.ts +1 -1
- package/dist/session-relay.d.ts +5 -5
- package/dist/storage/default.d.ts +2 -2
- package/dist/storage/index.d.ts +16 -16
- package/dist/storage/manager.d.ts +1 -1
- package/dist/storage/providers/callback-storage.d.ts +2 -2
- package/dist/storage/providers/dropbox.d.ts +1 -1
- package/dist/storage/providers/google-drive.d.ts +1 -1
- package/dist/storage/providers/ipfs.d.ts +1 -1
- package/dist/storage/providers/pinata.d.ts +1 -1
- package/dist/storage/providers/r2.d.ts +1 -1
- package/dist/storage/providers/vana-storage.d.ts +3 -3
- package/dist/types/config.d.ts +1 -1
- package/dist/types/index.d.ts +6 -6
- package/dist/types.d.ts +1 -1
- package/package.json +13 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @category Account
|
|
9
9
|
*/
|
|
10
10
|
import type { Address, Hex } from "viem";
|
|
11
|
-
import { buildPersonalServerLiteOwnerBindingMessage, type PersonalServerLiteOwnerBindingSignature } from "../personal-server-lite/owner-binding";
|
|
11
|
+
import { buildPersonalServerLiteOwnerBindingMessage, type PersonalServerLiteOwnerBindingSignature } from "../personal-server-lite/owner-binding.js";
|
|
12
12
|
export interface AccountPersonalServerLiteOwnerBindingClient {
|
|
13
13
|
getAddress(): Promise<Address | null> | Address | null;
|
|
14
14
|
signMessage(input: {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @category Account
|
|
9
9
|
*/
|
|
10
10
|
import { type Address } from "viem";
|
|
11
|
-
import { type BuildPersonalServerRegistrationTypedDataInput, type PersonalServerRegistrationSignature, type PersonalServerRegistrationSigner, type PersonalServerRegistrationTypedData } from "../protocol/personal-server-registration";
|
|
11
|
+
import { type BuildPersonalServerRegistrationTypedDataInput, type PersonalServerRegistrationSignature, type PersonalServerRegistrationSigner, type PersonalServerRegistrationTypedData } from "../protocol/personal-server-registration.js";
|
|
12
12
|
export declare const ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT: "personal_server.server_registration.v1";
|
|
13
13
|
export type AccountPersonalServerRegistrationIntent = typeof ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT;
|
|
14
14
|
export type AccountPersonalServerRegistrationStatus = "signed" | "confirmation_required" | "fallback_required";
|
package/dist/auth/errors.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @category Error Handling
|
|
10
10
|
*/
|
|
11
|
-
import { VanaError } from "../errors";
|
|
11
|
+
import { VanaError } from "../errors.js";
|
|
12
12
|
/** Thrown when an Authorization header is missing or empty. */
|
|
13
13
|
export declare class MissingAuthError extends VanaError {
|
|
14
14
|
readonly details?: Record<string, unknown> | undefined;
|
package/dist/browser.d.ts
CHANGED
package/dist/chains/index.d.ts
CHANGED
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
* @category Chains
|
|
31
31
|
* @module chains
|
|
32
32
|
*/
|
|
33
|
-
export type { VanaChainConfig } from "./definitions";
|
|
34
|
-
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./definitions";
|
|
33
|
+
export type { VanaChainConfig } from "./definitions.js";
|
|
34
|
+
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./definitions.js";
|
package/dist/chains.browser.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* This is identical to the base chains export since chain configurations
|
|
5
5
|
* are environment-agnostic.
|
|
6
6
|
*/
|
|
7
|
-
export type { VanaChainConfig } from "./chains/definitions";
|
|
8
|
-
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions";
|
|
7
|
+
export type { VanaChainConfig } from "./chains/definitions.js";
|
|
8
|
+
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions.js";
|
package/dist/chains.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* This module exports only chain configurations without any platform-specific
|
|
5
5
|
* dependencies, making it safe for server-side rendering.
|
|
6
6
|
*/
|
|
7
|
-
export type { VanaChainConfig } from "./chains/definitions";
|
|
8
|
-
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions";
|
|
7
|
+
export type { VanaChainConfig } from "./chains/definitions.js";
|
|
8
|
+
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions.js";
|
package/dist/chains.node.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* This is identical to the base chains export since chain configurations
|
|
5
5
|
* are environment-agnostic.
|
|
6
6
|
*/
|
|
7
|
-
export type { VanaChainConfig } from "./chains/definitions";
|
|
8
|
-
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions";
|
|
7
|
+
export type { VanaChainConfig } from "./chains/definitions.js";
|
|
8
|
+
export { vanaMainnet, moksha, mokshaTestnet, getChainConfig, getAllChains, } from "./chains/definitions.js";
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Abi } from "abitype";
|
|
13
13
|
import { type GetContractReturnType, type PublicClient, type WalletClient } from "viem";
|
|
14
|
-
import { type ContractAbis, type VanaContract } from "../generated/abi";
|
|
15
|
-
import type { VanaChainId, ContractInfo } from "../types/index";
|
|
16
|
-
import { type VanaContractAddress } from "../generated/addresses";
|
|
17
|
-
import { createClient } from "../core/client";
|
|
14
|
+
import { type ContractAbis, type VanaContract } from "../generated/abi/index.js";
|
|
15
|
+
import type { VanaChainId, ContractInfo } from "../types/index.js";
|
|
16
|
+
import { type VanaContractAddress } from "../generated/addresses.js";
|
|
17
|
+
import { createClient } from "../core/client.js";
|
|
18
18
|
type AddressedVanaContract = Extract<VanaContract, VanaContractAddress>;
|
|
19
19
|
export declare const contractCacheForTesting: Map<string, {
|
|
20
20
|
address: `0x${string}`;
|
package/dist/core/client.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses native browser crypto APIs and @noble/secp256k1 for elliptic curve operations.
|
|
6
6
|
* This implementation is polyfill-free and works in all modern browsers.
|
|
7
7
|
*/
|
|
8
|
-
import { BaseECIESUint8 } from "./base";
|
|
8
|
+
import { BaseECIESUint8 } from "./base.js";
|
|
9
9
|
/**
|
|
10
10
|
* Browser-specific ECIES provider using @noble/secp256k1
|
|
11
11
|
*
|
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @category Cryptography
|
|
14
14
|
*/
|
|
15
|
-
export type { ECIESProvider, ECIESEncrypted, ECIESOptions } from "./interface";
|
|
16
|
-
export { ECIESError, isECIESEncrypted, serializeECIES, deserializeECIES, } from "./interface";
|
|
15
|
+
export type { ECIESProvider, ECIESEncrypted, ECIESOptions } from "./interface.js";
|
|
16
|
+
export { ECIESError, isECIESEncrypted, serializeECIES, deserializeECIES, } from "./interface.js";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Uses Node.js crypto module for hashing and AES operations.
|
|
7
7
|
* Provides Uint8Array-only interface with no Buffer exposure.
|
|
8
8
|
*/
|
|
9
|
-
import { BaseECIESUint8 } from "./base";
|
|
9
|
+
import { BaseECIESUint8 } from "./base.js";
|
|
10
10
|
/**
|
|
11
11
|
* Node.js-specific ECIES provider using native secp256k1
|
|
12
12
|
*
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @category Cryptography
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
|
-
import type { ECIESProvider, ECIESEncrypted } from "../ecies/interface";
|
|
12
|
+
import type { ECIESProvider, ECIESEncrypted } from "../ecies/interface.js";
|
|
13
13
|
export interface WalletKeyEncryptionServiceConfig {
|
|
14
14
|
/** ECIES provider for encryption/decryption */
|
|
15
15
|
eciesProvider: ECIESProvider;
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* @category Direct
|
|
11
11
|
* @module direct/access-request-client
|
|
12
12
|
*/
|
|
13
|
-
import type { AccessRequestClient } from "./types";
|
|
14
|
-
import type { Web3SignedSignFn } from "../auth/web3-signed-builder";
|
|
13
|
+
import type { AccessRequestClient } from "./types.js";
|
|
14
|
+
import type { Web3SignedSignFn } from "../auth/web3-signed-builder.js";
|
|
15
15
|
/** Minimal `fetch` signature so the client is testable without a global fetch. */
|
|
16
16
|
export type FetchLike = (input: string, init?: {
|
|
17
17
|
method?: string;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @category Direct
|
|
13
13
|
* @module direct/connect-flow
|
|
14
14
|
*/
|
|
15
|
-
import type { AccessRequest, AccessRequestStatus, ApprovedDataResult } from "./types";
|
|
15
|
+
import type { AccessRequest, AccessRequestStatus, ApprovedDataResult } from "./types.js";
|
|
16
16
|
/**
|
|
17
17
|
* Caller-supplied transports. These typically `fetch` the app's own backend
|
|
18
18
|
* routes, which in turn delegate to a {@link DirectDataController}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/direct/controller.ts"],"sourcesContent":["/**\n * Direct Data Controller — the server-side facade for the two-tab Data\n * Portability flow.\n *\n * @remarks\n * One controller owns an app's private key, source, scopes, app identity, and\n * payment flow. It exposes the three methods the builder guide documents:\n *\n * - {@link DirectDataController.createAccessRequest} — start an approval request.\n * - {@link DirectDataController.getAccessRequestStatus} — poll while the Vana tab is open.\n * - {@link DirectDataController.readApprovedData} — read from the Personal Server,\n * handling 402 Payment Required.\n *\n * Access requests are created through the Vana Account access-request API; the\n * Personal Server read uses Web3Signed auth; and payment uses the DPv2 escrow\n * surface (`protocol/escrow`) — when a read returns `402`, the controller signs\n * a `GenericPayment` with the app key, settles it through the escrow gateway,\n * and retries.\n *\n * @category Direct\n * @module direct/controller\n */\n\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex } from \"viem\";\nimport type { Web3SignedSignFn } from \"../auth/web3-signed-builder\";\nimport { parseScope } from \"../protocol/scopes\";\nimport { createEscrowGatewayClient } from \"../protocol/escrow\";\nimport { CONTRACTS } from \"../generated/addresses\";\nimport {\n createDefaultAccessRequestClient,\n type FetchLike,\n} from \"./access-request-client\";\nimport {\n getDirectDefaultNetwork,\n getDirectEndpoints,\n getDirectNetworkChainId,\n} from \"./endpoints\";\nimport { AccessNotApprovedError, DirectConfigError } from \"./errors\";\nimport {\n type EscrowPaymentConfig,\n type SignTypedDataFn,\n} from \"./escrow-payment\";\nimport {\n readPersonalServerData,\n type PersonalServerFetch,\n type PersonalServerTransportRetryOptions,\n} from \"./personal-server-read\";\nimport type {\n AccessRequest,\n AccessRequestClient,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n AppIdentity,\n DirectAppConfig,\n DirectEnv,\n DirectNetwork,\n DirectServiceEndpoints,\n} from \"./types\";\n\n/** Configuration for {@link createDirectDataController}. */\nexport interface DirectDataControllerConfig {\n /** Target environment. Defaults to `\"production\"`. */\n env?: DirectEnv;\n /**\n * Target Vana network for chain-aware defaults. Defaults to the selected\n * environment's historical network (`mainnet` for production, `moksha` for\n * dev). Use `network: \"moksha\"` with the default production env for\n * production app/API URLs on testnet.\n */\n network?: DirectNetwork;\n /**\n * The app private key (`0x`-prefixed, 32 bytes). Server-side only — this key\n * is the app's on-chain identity and is never exposed to the browser.\n */\n appPrivateKey?: string;\n /**\n * @deprecated Use {@link DirectDataControllerConfig.appPrivateKey}. Accepted as\n * a backwards-compatible alias; if both are set, `appPrivateKey` wins.\n */\n builderPrivateKey?: string;\n /** App identity advertised during approval. */\n app: DirectAppConfig;\n /** Data source key (e.g. `\"icloud_notes\"`). */\n source: string;\n /** Scopes to request (e.g. `[\"icloud_notes.notes\"]`). At least one required. */\n scopes: string[];\n /**\n * Override the resolved service endpoints (partial). Useful for pointing at a\n * non-standard deployment.\n */\n endpoints?: Partial<DirectServiceEndpoints>;\n /**\n * Client for the Vana Account access-request API. Defaults to a client against\n * the resolved Vana Account endpoints; inject your own to point at a custom\n * deployment or to supply a test double.\n */\n accessRequestClient?: AccessRequestClient;\n /**\n * Escrow settlement config used when a Personal Server read returns `402`.\n *\n * @remarks\n * Wires the DPv2 escrow gateway (`protocol/escrow`). The controller supplies\n * the EIP-712 `signTypedData` from the app key automatically.\n *\n * When omitted (or partially omitted), the SDK derives defaults from the\n * per-network endpoints table and the contract registry:\n * - `client` defaults to a gateway client at `endpoints.escrowGatewayUrl`\n * - `escrowContract` defaults to `CONTRACTS.DataPortabilityEscrow.addresses[chainId]`\n * - `chainId` defaults to the controller's resolved chain id\n *\n * Provide this field only to override a specific default.\n */\n escrow?: Partial<DirectEscrowConfig>;\n /** `fetch` used by the default access-request client. Defaults to `globalThis.fetch`. */\n fetchFn?: FetchLike;\n /** `fetch` used for the Personal Server read. Defaults to `globalThis.fetch`. */\n personalServerFetch?: PersonalServerFetch;\n /**\n * Transport-retry knobs for the Personal Server read\n * ({@link PersonalServerTransportRetryOptions}). Defaults to 3 attempts with\n * exponential backoff. Retries fire only when fetch throws (the browser-PS\n * relay reconnect window), never on a received HTTP status, and never\n * re-sign a payment.\n */\n personalServerTransportRetry?: PersonalServerTransportRetryOptions;\n}\n\n/**\n * Controller-level escrow config — the {@link EscrowPaymentConfig} minus the\n * `signTypedData` and `chainId` the controller injects itself.\n */\nexport interface DirectEscrowConfig extends Omit<\n EscrowPaymentConfig,\n \"signTypedData\" | \"chainId\"\n> {\n /**\n * Chain id for the EIP-712 domain. Defaults to the controller's environment\n * (1480 for mainnet, 14800 for moksha).\n */\n chainId?: number;\n}\n\n/**\n * Server-side controller for the direct Data Portability flow.\n *\n * @typeParam T - Shape of the data returned by {@link DirectDataController.readApprovedData}.\n */\nexport interface DirectDataController {\n /** The on-chain address of the app, derived from `appPrivateKey`. */\n readonly appAddress: string;\n\n /**\n * The app's on-chain address — the address to fund and inspect in the Builder\n * activity report. Equivalent to {@link DirectDataController.appAddress}.\n *\n * @returns The app's `0x`-prefixed address.\n */\n getAppAddress(): string;\n\n /**\n * The app's full identity: its configured id/name/homepage plus the derived\n * on-chain address. Useful for telling builders which app address to fund or\n * look up.\n *\n * @returns `{ id, name, homepageUrl, address }`.\n */\n getAppIdentity(): AppIdentity;\n\n /**\n * Create an access request the user can approve.\n *\n * @param input - The post-approval return URL.\n * @returns `{ requestId, approvalUrl, appAddress }`.\n */\n createAccessRequest(input: { returnUrl: string }): Promise<AccessRequest>;\n\n /**\n * Fetch the current status of an access request.\n *\n * @param requestId - The `dcr_*` id from {@link DirectDataController.createAccessRequest}.\n * @returns `{ status, personalServerUrl?, grantId?, scope? }`.\n */\n getAccessRequestStatus(requestId: string): Promise<AccessRequestStatus>;\n\n /**\n * Read the approved data from the user's Personal Server.\n *\n * @remarks\n * Resolves the request to its grant + Personal Server and performs a Web3Signed\n * read. Hides the `402 Payment Required` flow by default: if a read needs\n * payment, it signs the Personal Server's payment challenge, retries with\n * `X-PAYMENT`, and attaches a {@link DirectPaymentReceipt} under `payment`\n * when the Personal Server returns one. After a successful read, the\n * controller acknowledges the DCR so Vana Web can close/redirect the approval\n * tab.\n *\n * @param input - The `dcr_*` request id to read.\n * @returns `{ scope, data, payment? }`.\n * @throws {@link AccessNotApprovedError} if the request is not approved.\n * @throws {@link PaymentRequiredError} if payment is required but unsettled.\n */\n readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>>;\n}\n\nfunction isHexPrivateKey(value: string): value is Hex {\n return /^0x[0-9a-fA-F]{64}$/.test(value);\n}\n\n// A DCR is read-ready only while the grant exists and the Personal Server is\n// still serving it: `approved` (durable PS) or `ready_for_read` (browser PS).\n// `completed` is terminal — the app already read and acknowledged, and the\n// browser PS may be gone — so it is deliberately excluded here.\nfunction isReadReadyStatus(status: AccessRequestStatusValue): boolean {\n return status === \"approved\" || status === \"ready_for_read\";\n}\n\n/**\n * Create a {@link DirectDataController}.\n *\n * @param config - Controller configuration (env, key, app identity, source, scopes).\n * @returns A ready-to-use controller.\n * @throws {@link DirectConfigError} when the key or scopes are invalid.\n */\nexport function createDirectDataController(\n config: DirectDataControllerConfig,\n): DirectDataController {\n // `appPrivateKey` is the documented field; `builderPrivateKey` is a\n // deprecated alias kept for backwards compatibility.\n const privateKey = config.appPrivateKey ?? config.builderPrivateKey;\n if (!privateKey || !isHexPrivateKey(privateKey)) {\n throw new DirectConfigError(\n \"appPrivateKey must be a 0x-prefixed 32-byte hex string\",\n );\n }\n if (!config.scopes || config.scopes.length === 0) {\n throw new DirectConfigError(\"At least one scope is required\");\n }\n // Validate scopes eagerly so misconfiguration fails at construction.\n for (const scope of config.scopes) {\n parseScope(scope);\n }\n\n const env: DirectEnv = config.env ?? \"production\";\n const network: DirectNetwork = config.network ?? getDirectDefaultNetwork(env);\n const defaultEndpoints = getDirectEndpoints(env);\n const chainId = config.endpoints?.chainId ?? getDirectNetworkChainId(network);\n const endpoints: DirectServiceEndpoints = {\n ...defaultEndpoints,\n ...config.endpoints,\n chainId,\n };\n\n const account = privateKeyToAccount(privateKey as Hex);\n const signMessage: Web3SignedSignFn = (message: string) =>\n account.signMessage({ message });\n // viem's account.signTypedData satisfies the structural SignTypedDataFn used\n // by the escrow GenericPayment signer.\n const signTypedData = account.signTypedData as unknown as SignTypedDataFn;\n const accessRequestClient: AccessRequestClient =\n config.accessRequestClient ??\n createDefaultAccessRequestClient({\n baseUrl: endpoints.accessRequestBaseUrl,\n approvalBaseUrl: endpoints.approvalAppBaseUrl,\n fetchFn: config.fetchFn,\n appAddress: account.address,\n signMessage,\n });\n\n // Build the escrow payment config, defaulting from the per-network endpoints\n // table and the contract registry when `config.escrow` is omitted or partial.\n const escrowChainId = config.escrow?.chainId ?? chainId;\n const defaultEscrowContract =\n CONTRACTS.DataPortabilityEscrow.addresses[\n escrowChainId as keyof typeof CONTRACTS.DataPortabilityEscrow.addresses\n ] ?? undefined;\n if (!config.escrow?.escrowContract && !defaultEscrowContract) {\n throw new DirectConfigError(\n `No DataPortabilityEscrow address found in the registry for chainId ${escrowChainId}. ` +\n `Provide an explicit escrow.escrowContract in the controller config.`,\n );\n }\n const escrow: EscrowPaymentConfig = {\n client:\n config.escrow?.client ??\n createEscrowGatewayClient(endpoints.escrowGatewayUrl),\n escrowContract:\n config.escrow?.escrowContract ?? (defaultEscrowContract as `0x${string}`),\n chainId: escrowChainId,\n nonceSource: config.escrow?.nonceSource,\n signTypedData,\n };\n\n return {\n appAddress: account.address,\n\n getAppAddress(): string {\n return account.address;\n },\n\n getAppIdentity(): AppIdentity {\n return {\n id: config.app.id,\n name: config.app.name,\n homepageUrl: config.app.homepageUrl,\n address: account.address,\n };\n },\n\n async createAccessRequest(input): Promise<AccessRequest> {\n return accessRequestClient.createAccessRequest({\n appAddress: account.address,\n app: config.app,\n source: config.source,\n scopes: config.scopes,\n returnUrl: input.returnUrl,\n network,\n });\n },\n\n async getAccessRequestStatus(\n requestId: string,\n ): Promise<AccessRequestStatus> {\n return accessRequestClient.getAccessRequestStatus(requestId);\n },\n\n async readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>> {\n const status = await accessRequestClient.getAccessRequestStatus(\n input.requestId,\n );\n if (\n !isReadReadyStatus(status.status) ||\n !status.personalServerUrl ||\n !status.grantId ||\n !status.scope\n ) {\n throw new AccessNotApprovedError(\n \"Request is not approved or is missing grantId/scope/personalServerUrl\",\n {\n requestId: input.requestId,\n status: status.status,\n hasPersonalServerUrl: Boolean(status.personalServerUrl),\n hasGrantId: Boolean(status.grantId),\n hasScope: Boolean(status.scope),\n },\n );\n }\n\n const result = await readPersonalServerData({\n personalServerUrl: status.personalServerUrl,\n scope: status.scope,\n grantId: status.grantId,\n payerAddress: account.address,\n signMessage,\n escrow,\n fetchFn: config.personalServerFetch,\n transportRetry: config.personalServerTransportRetry,\n });\n try {\n await accessRequestClient.acknowledgeRead?.(input.requestId);\n } catch {\n // The read already succeeded; ack only drives Vana Web completion UX.\n }\n\n return {\n scope: status.scope,\n data: result.data as T,\n payment: result.payment,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/direct/controller.ts"],"sourcesContent":["/**\n * Direct Data Controller — the server-side facade for the two-tab Data\n * Portability flow.\n *\n * @remarks\n * One controller owns an app's private key, source, scopes, app identity, and\n * payment flow. It exposes the three methods the builder guide documents:\n *\n * - {@link DirectDataController.createAccessRequest} — start an approval request.\n * - {@link DirectDataController.getAccessRequestStatus} — poll while the Vana tab is open.\n * - {@link DirectDataController.readApprovedData} — read from the Personal Server,\n * handling 402 Payment Required.\n *\n * Access requests are created through the Vana Account access-request API; the\n * Personal Server read uses Web3Signed auth; and payment uses the DPv2 escrow\n * surface (`protocol/escrow`) — when a read returns `402`, the controller signs\n * a `GenericPayment` with the app key, settles it through the escrow gateway,\n * and retries.\n *\n * @category Direct\n * @module direct/controller\n */\n\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex } from \"viem\";\nimport type { Web3SignedSignFn } from \"../auth/web3-signed-builder\";\nimport { parseScope } from \"../protocol/scopes\";\nimport { createEscrowGatewayClient } from \"../protocol/escrow\";\nimport { CONTRACTS } from \"../generated/addresses\";\nimport {\n createDefaultAccessRequestClient,\n type FetchLike,\n} from \"./access-request-client\";\nimport {\n getDirectDefaultNetwork,\n getDirectEndpoints,\n getDirectNetworkChainId,\n} from \"./endpoints\";\nimport { AccessNotApprovedError, DirectConfigError } from \"./errors\";\nimport {\n type EscrowPaymentConfig,\n type SignTypedDataFn,\n} from \"./escrow-payment\";\nimport {\n readPersonalServerData,\n type PersonalServerFetch,\n type PersonalServerTransportRetryOptions,\n} from \"./personal-server-read\";\nimport type {\n AccessRequest,\n AccessRequestClient,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n AppIdentity,\n DirectAppConfig,\n DirectEnv,\n DirectNetwork,\n DirectPaymentResponseMetadata,\n DirectServiceEndpoints,\n} from \"./types\";\n\n/** Configuration for {@link createDirectDataController}. */\nexport interface DirectDataControllerConfig {\n /** Target environment. Defaults to `\"production\"`. */\n env?: DirectEnv;\n /**\n * Target Vana network for chain-aware defaults. Defaults to the selected\n * environment's historical network (`mainnet` for production, `moksha` for\n * dev). Use `network: \"moksha\"` with the default production env for\n * production app/API URLs on testnet.\n */\n network?: DirectNetwork;\n /**\n * The app private key (`0x`-prefixed, 32 bytes). Server-side only — this key\n * is the app's on-chain identity and is never exposed to the browser.\n */\n appPrivateKey?: string;\n /**\n * @deprecated Use {@link DirectDataControllerConfig.appPrivateKey}. Accepted as\n * a backwards-compatible alias; if both are set, `appPrivateKey` wins.\n */\n builderPrivateKey?: string;\n /** App identity advertised during approval. */\n app: DirectAppConfig;\n /** Data source key (e.g. `\"icloud_notes\"`). */\n source: string;\n /** Scopes to request (e.g. `[\"icloud_notes.notes\"]`). At least one required. */\n scopes: string[];\n /**\n * Override the resolved service endpoints (partial). Useful for pointing at a\n * non-standard deployment.\n */\n endpoints?: Partial<DirectServiceEndpoints>;\n /**\n * Client for the Vana Account access-request API. Defaults to a client against\n * the resolved Vana Account endpoints; inject your own to point at a custom\n * deployment or to supply a test double.\n */\n accessRequestClient?: AccessRequestClient;\n /**\n * Escrow settlement config used when a Personal Server read returns `402`.\n *\n * @remarks\n * Wires the DPv2 escrow gateway (`protocol/escrow`). The controller supplies\n * the EIP-712 `signTypedData` from the app key automatically.\n *\n * When omitted (or partially omitted), the SDK derives defaults from the\n * per-network endpoints table and the contract registry:\n * - `client` defaults to a gateway client at `endpoints.escrowGatewayUrl`\n * - `escrowContract` defaults to `CONTRACTS.DataPortabilityEscrow.addresses[chainId]`\n * - `chainId` defaults to the controller's resolved chain id\n *\n * Provide this field only to override a specific default.\n */\n escrow?: Partial<DirectEscrowConfig>;\n /** `fetch` used by the default access-request client. Defaults to `globalThis.fetch`. */\n fetchFn?: FetchLike;\n /** `fetch` used for the Personal Server read. Defaults to `globalThis.fetch`. */\n personalServerFetch?: PersonalServerFetch;\n /**\n * Transport-retry knobs for the Personal Server read\n * ({@link PersonalServerTransportRetryOptions}). Defaults to 3 attempts with\n * exponential backoff. Retries fire only when fetch throws (the browser-PS\n * relay reconnect window), never on a received HTTP status, and never\n * re-sign a payment.\n */\n personalServerTransportRetry?: PersonalServerTransportRetryOptions;\n}\n\n/**\n * Controller-level escrow config — the {@link EscrowPaymentConfig} minus the\n * `signTypedData` and `chainId` the controller injects itself.\n */\nexport interface DirectEscrowConfig extends Omit<\n EscrowPaymentConfig,\n \"signTypedData\" | \"chainId\"\n> {\n /**\n * Chain id for the EIP-712 domain. Defaults to the controller's environment\n * (1480 for mainnet, 14800 for moksha).\n */\n chainId?: number;\n}\n\n/**\n * Server-side controller for the direct Data Portability flow.\n *\n * @typeParam T - Shape of the data returned by {@link DirectDataController.readApprovedData}.\n */\nexport interface DirectDataController {\n /** The on-chain address of the app, derived from `appPrivateKey`. */\n readonly appAddress: string;\n\n /**\n * The app's on-chain address — the address to fund and inspect in the Builder\n * activity report. Equivalent to {@link DirectDataController.appAddress}.\n *\n * @returns The app's `0x`-prefixed address.\n */\n getAppAddress(): string;\n\n /**\n * The app's full identity: its configured id/name/homepage plus the derived\n * on-chain address. Useful for telling builders which app address to fund or\n * look up.\n *\n * @returns `{ id, name, homepageUrl, address }`.\n */\n getAppIdentity(): AppIdentity;\n\n /**\n * Create an access request the user can approve.\n *\n * @param input - The post-approval return URL.\n * @returns `{ requestId, approvalUrl, appAddress }`.\n */\n createAccessRequest(input: { returnUrl: string }): Promise<AccessRequest>;\n\n /**\n * Fetch the current status of an access request.\n *\n * @param requestId - The `dcr_*` id from {@link DirectDataController.createAccessRequest}.\n * @returns `{ status, personalServerUrl?, grantId?, scope? }`.\n */\n getAccessRequestStatus(requestId: string): Promise<AccessRequestStatus>;\n\n /**\n * Read the approved data from the user's Personal Server.\n *\n * @remarks\n * Resolves the request to its grant + Personal Server and performs a Web3Signed\n * read. Hides the `402 Payment Required` flow by default: if a read needs\n * payment, it signs the Personal Server's payment challenge, retries with\n * `X-PAYMENT`, and attaches shape-validated but unauthenticated\n * {@link DirectPaymentResponseMetadata} under `payment` when the Personal\n * Server returns it. After a successful read, the controller acknowledges\n * the DCR so Vana Web can close/redirect the approval tab.\n *\n * @param input - The `dcr_*` request id to read.\n * @returns `{ scope, data, payment? }`.\n * @throws {@link AccessNotApprovedError} if the request is not approved.\n * @throws {@link PaymentRequiredError} if payment is required but unsettled.\n */\n readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>>;\n}\n\nfunction isHexPrivateKey(value: string): value is Hex {\n return /^0x[0-9a-fA-F]{64}$/.test(value);\n}\n\n// A DCR is read-ready only while the grant exists and the Personal Server is\n// still serving it: `approved` (durable PS) or `ready_for_read` (browser PS).\n// `completed` is terminal — the app already read and acknowledged, and the\n// browser PS may be gone — so it is deliberately excluded here.\nfunction isReadReadyStatus(status: AccessRequestStatusValue): boolean {\n return status === \"approved\" || status === \"ready_for_read\";\n}\n\n/**\n * Create a {@link DirectDataController}.\n *\n * @param config - Controller configuration (env, key, app identity, source, scopes).\n * @returns A ready-to-use controller.\n * @throws {@link DirectConfigError} when the key or scopes are invalid.\n */\nexport function createDirectDataController(\n config: DirectDataControllerConfig,\n): DirectDataController {\n // `appPrivateKey` is the documented field; `builderPrivateKey` is a\n // deprecated alias kept for backwards compatibility.\n const privateKey = config.appPrivateKey ?? config.builderPrivateKey;\n if (!privateKey || !isHexPrivateKey(privateKey)) {\n throw new DirectConfigError(\n \"appPrivateKey must be a 0x-prefixed 32-byte hex string\",\n );\n }\n if (!config.scopes || config.scopes.length === 0) {\n throw new DirectConfigError(\"At least one scope is required\");\n }\n // Validate scopes eagerly so misconfiguration fails at construction.\n for (const scope of config.scopes) {\n parseScope(scope);\n }\n\n const env: DirectEnv = config.env ?? \"production\";\n const network: DirectNetwork = config.network ?? getDirectDefaultNetwork(env);\n const defaultEndpoints = getDirectEndpoints(env);\n const chainId = config.endpoints?.chainId ?? getDirectNetworkChainId(network);\n const endpoints: DirectServiceEndpoints = {\n ...defaultEndpoints,\n ...config.endpoints,\n chainId,\n };\n\n const account = privateKeyToAccount(privateKey as Hex);\n const signMessage: Web3SignedSignFn = (message: string) =>\n account.signMessage({ message });\n // viem's account.signTypedData satisfies the structural SignTypedDataFn used\n // by the escrow GenericPayment signer.\n const signTypedData = account.signTypedData as unknown as SignTypedDataFn;\n const accessRequestClient: AccessRequestClient =\n config.accessRequestClient ??\n createDefaultAccessRequestClient({\n baseUrl: endpoints.accessRequestBaseUrl,\n approvalBaseUrl: endpoints.approvalAppBaseUrl,\n fetchFn: config.fetchFn,\n appAddress: account.address,\n signMessage,\n });\n\n // Build the escrow payment config, defaulting from the per-network endpoints\n // table and the contract registry when `config.escrow` is omitted or partial.\n const escrowChainId = config.escrow?.chainId ?? chainId;\n const defaultEscrowContract =\n CONTRACTS.DataPortabilityEscrow.addresses[\n escrowChainId as keyof typeof CONTRACTS.DataPortabilityEscrow.addresses\n ] ?? undefined;\n if (!config.escrow?.escrowContract && !defaultEscrowContract) {\n throw new DirectConfigError(\n `No DataPortabilityEscrow address found in the registry for chainId ${escrowChainId}. ` +\n `Provide an explicit escrow.escrowContract in the controller config.`,\n );\n }\n const escrow: EscrowPaymentConfig = {\n client:\n config.escrow?.client ??\n createEscrowGatewayClient(endpoints.escrowGatewayUrl),\n escrowContract:\n config.escrow?.escrowContract ?? (defaultEscrowContract as `0x${string}`),\n chainId: escrowChainId,\n nonceSource: config.escrow?.nonceSource,\n signTypedData,\n };\n\n return {\n appAddress: account.address,\n\n getAppAddress(): string {\n return account.address;\n },\n\n getAppIdentity(): AppIdentity {\n return {\n id: config.app.id,\n name: config.app.name,\n homepageUrl: config.app.homepageUrl,\n address: account.address,\n };\n },\n\n async createAccessRequest(input): Promise<AccessRequest> {\n return accessRequestClient.createAccessRequest({\n appAddress: account.address,\n app: config.app,\n source: config.source,\n scopes: config.scopes,\n returnUrl: input.returnUrl,\n network,\n });\n },\n\n async getAccessRequestStatus(\n requestId: string,\n ): Promise<AccessRequestStatus> {\n return accessRequestClient.getAccessRequestStatus(requestId);\n },\n\n async readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>> {\n const status = await accessRequestClient.getAccessRequestStatus(\n input.requestId,\n );\n if (\n !isReadReadyStatus(status.status) ||\n !status.personalServerUrl ||\n !status.grantId ||\n !status.scope\n ) {\n throw new AccessNotApprovedError(\n \"Request is not approved or is missing grantId/scope/personalServerUrl\",\n {\n requestId: input.requestId,\n status: status.status,\n hasPersonalServerUrl: Boolean(status.personalServerUrl),\n hasGrantId: Boolean(status.grantId),\n hasScope: Boolean(status.scope),\n },\n );\n }\n\n const result = await readPersonalServerData({\n personalServerUrl: status.personalServerUrl,\n scope: status.scope,\n grantId: status.grantId,\n payerAddress: account.address,\n signMessage,\n escrow,\n fetchFn: config.personalServerFetch,\n transportRetry: config.personalServerTransportRetry,\n });\n try {\n await accessRequestClient.acknowledgeRead?.(input.requestId);\n } catch {\n // The read already succeeded; ack only drives Vana Web completion UX.\n }\n\n return {\n scope: status.scope,\n data: result.data as T,\n payment: result.payment,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
* @category Direct
|
|
21
21
|
* @module direct/controller
|
|
22
22
|
*/
|
|
23
|
-
import { type FetchLike } from "./access-request-client";
|
|
24
|
-
import { type EscrowPaymentConfig } from "./escrow-payment";
|
|
25
|
-
import { type PersonalServerFetch, type PersonalServerTransportRetryOptions } from "./personal-server-read";
|
|
26
|
-
import type { AccessRequest, AccessRequestClient, AccessRequestStatus, ApprovedDataResult, AppIdentity, DirectAppConfig, DirectEnv, DirectNetwork, DirectServiceEndpoints } from "./types";
|
|
23
|
+
import { type FetchLike } from "./access-request-client.js";
|
|
24
|
+
import { type EscrowPaymentConfig } from "./escrow-payment.js";
|
|
25
|
+
import { type PersonalServerFetch, type PersonalServerTransportRetryOptions } from "./personal-server-read.js";
|
|
26
|
+
import type { AccessRequest, AccessRequestClient, AccessRequestStatus, ApprovedDataResult, AppIdentity, DirectAppConfig, DirectEnv, DirectNetwork, DirectServiceEndpoints } from "./types.js";
|
|
27
27
|
/** Configuration for {@link createDirectDataController}. */
|
|
28
28
|
export interface DirectDataControllerConfig {
|
|
29
29
|
/** Target environment. Defaults to `"production"`. */
|
|
@@ -148,10 +148,10 @@ export interface DirectDataController {
|
|
|
148
148
|
* Resolves the request to its grant + Personal Server and performs a Web3Signed
|
|
149
149
|
* read. Hides the `402 Payment Required` flow by default: if a read needs
|
|
150
150
|
* payment, it signs the Personal Server's payment challenge, retries with
|
|
151
|
-
* `X-PAYMENT`, and attaches
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* tab.
|
|
151
|
+
* `X-PAYMENT`, and attaches shape-validated but unauthenticated
|
|
152
|
+
* {@link DirectPaymentResponseMetadata} under `payment` when the Personal
|
|
153
|
+
* Server returns it. After a successful read, the controller acknowledges
|
|
154
|
+
* the DCR so Vana Web can close/redirect the approval tab.
|
|
155
155
|
*
|
|
156
156
|
* @param input - The `dcr_*` request id to read.
|
|
157
157
|
* @returns `{ scope, data, payment? }`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/direct/controller.ts"],"sourcesContent":["/**\n * Direct Data Controller — the server-side facade for the two-tab Data\n * Portability flow.\n *\n * @remarks\n * One controller owns an app's private key, source, scopes, app identity, and\n * payment flow. It exposes the three methods the builder guide documents:\n *\n * - {@link DirectDataController.createAccessRequest} — start an approval request.\n * - {@link DirectDataController.getAccessRequestStatus} — poll while the Vana tab is open.\n * - {@link DirectDataController.readApprovedData} — read from the Personal Server,\n * handling 402 Payment Required.\n *\n * Access requests are created through the Vana Account access-request API; the\n * Personal Server read uses Web3Signed auth; and payment uses the DPv2 escrow\n * surface (`protocol/escrow`) — when a read returns `402`, the controller signs\n * a `GenericPayment` with the app key, settles it through the escrow gateway,\n * and retries.\n *\n * @category Direct\n * @module direct/controller\n */\n\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex } from \"viem\";\nimport type { Web3SignedSignFn } from \"../auth/web3-signed-builder\";\nimport { parseScope } from \"../protocol/scopes\";\nimport { createEscrowGatewayClient } from \"../protocol/escrow\";\nimport { CONTRACTS } from \"../generated/addresses\";\nimport {\n createDefaultAccessRequestClient,\n type FetchLike,\n} from \"./access-request-client\";\nimport {\n getDirectDefaultNetwork,\n getDirectEndpoints,\n getDirectNetworkChainId,\n} from \"./endpoints\";\nimport { AccessNotApprovedError, DirectConfigError } from \"./errors\";\nimport {\n type EscrowPaymentConfig,\n type SignTypedDataFn,\n} from \"./escrow-payment\";\nimport {\n readPersonalServerData,\n type PersonalServerFetch,\n type PersonalServerTransportRetryOptions,\n} from \"./personal-server-read\";\nimport type {\n AccessRequest,\n AccessRequestClient,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n AppIdentity,\n DirectAppConfig,\n DirectEnv,\n DirectNetwork,\n DirectServiceEndpoints,\n} from \"./types\";\n\n/** Configuration for {@link createDirectDataController}. */\nexport interface DirectDataControllerConfig {\n /** Target environment. Defaults to `\"production\"`. */\n env?: DirectEnv;\n /**\n * Target Vana network for chain-aware defaults. Defaults to the selected\n * environment's historical network (`mainnet` for production, `moksha` for\n * dev). Use `network: \"moksha\"` with the default production env for\n * production app/API URLs on testnet.\n */\n network?: DirectNetwork;\n /**\n * The app private key (`0x`-prefixed, 32 bytes). Server-side only — this key\n * is the app's on-chain identity and is never exposed to the browser.\n */\n appPrivateKey?: string;\n /**\n * @deprecated Use {@link DirectDataControllerConfig.appPrivateKey}. Accepted as\n * a backwards-compatible alias; if both are set, `appPrivateKey` wins.\n */\n builderPrivateKey?: string;\n /** App identity advertised during approval. */\n app: DirectAppConfig;\n /** Data source key (e.g. `\"icloud_notes\"`). */\n source: string;\n /** Scopes to request (e.g. `[\"icloud_notes.notes\"]`). At least one required. */\n scopes: string[];\n /**\n * Override the resolved service endpoints (partial). Useful for pointing at a\n * non-standard deployment.\n */\n endpoints?: Partial<DirectServiceEndpoints>;\n /**\n * Client for the Vana Account access-request API. Defaults to a client against\n * the resolved Vana Account endpoints; inject your own to point at a custom\n * deployment or to supply a test double.\n */\n accessRequestClient?: AccessRequestClient;\n /**\n * Escrow settlement config used when a Personal Server read returns `402`.\n *\n * @remarks\n * Wires the DPv2 escrow gateway (`protocol/escrow`). The controller supplies\n * the EIP-712 `signTypedData` from the app key automatically.\n *\n * When omitted (or partially omitted), the SDK derives defaults from the\n * per-network endpoints table and the contract registry:\n * - `client` defaults to a gateway client at `endpoints.escrowGatewayUrl`\n * - `escrowContract` defaults to `CONTRACTS.DataPortabilityEscrow.addresses[chainId]`\n * - `chainId` defaults to the controller's resolved chain id\n *\n * Provide this field only to override a specific default.\n */\n escrow?: Partial<DirectEscrowConfig>;\n /** `fetch` used by the default access-request client. Defaults to `globalThis.fetch`. */\n fetchFn?: FetchLike;\n /** `fetch` used for the Personal Server read. Defaults to `globalThis.fetch`. */\n personalServerFetch?: PersonalServerFetch;\n /**\n * Transport-retry knobs for the Personal Server read\n * ({@link PersonalServerTransportRetryOptions}). Defaults to 3 attempts with\n * exponential backoff. Retries fire only when fetch throws (the browser-PS\n * relay reconnect window), never on a received HTTP status, and never\n * re-sign a payment.\n */\n personalServerTransportRetry?: PersonalServerTransportRetryOptions;\n}\n\n/**\n * Controller-level escrow config — the {@link EscrowPaymentConfig} minus the\n * `signTypedData` and `chainId` the controller injects itself.\n */\nexport interface DirectEscrowConfig extends Omit<\n EscrowPaymentConfig,\n \"signTypedData\" | \"chainId\"\n> {\n /**\n * Chain id for the EIP-712 domain. Defaults to the controller's environment\n * (1480 for mainnet, 14800 for moksha).\n */\n chainId?: number;\n}\n\n/**\n * Server-side controller for the direct Data Portability flow.\n *\n * @typeParam T - Shape of the data returned by {@link DirectDataController.readApprovedData}.\n */\nexport interface DirectDataController {\n /** The on-chain address of the app, derived from `appPrivateKey`. */\n readonly appAddress: string;\n\n /**\n * The app's on-chain address — the address to fund and inspect in the Builder\n * activity report. Equivalent to {@link DirectDataController.appAddress}.\n *\n * @returns The app's `0x`-prefixed address.\n */\n getAppAddress(): string;\n\n /**\n * The app's full identity: its configured id/name/homepage plus the derived\n * on-chain address. Useful for telling builders which app address to fund or\n * look up.\n *\n * @returns `{ id, name, homepageUrl, address }`.\n */\n getAppIdentity(): AppIdentity;\n\n /**\n * Create an access request the user can approve.\n *\n * @param input - The post-approval return URL.\n * @returns `{ requestId, approvalUrl, appAddress }`.\n */\n createAccessRequest(input: { returnUrl: string }): Promise<AccessRequest>;\n\n /**\n * Fetch the current status of an access request.\n *\n * @param requestId - The `dcr_*` id from {@link DirectDataController.createAccessRequest}.\n * @returns `{ status, personalServerUrl?, grantId?, scope? }`.\n */\n getAccessRequestStatus(requestId: string): Promise<AccessRequestStatus>;\n\n /**\n * Read the approved data from the user's Personal Server.\n *\n * @remarks\n * Resolves the request to its grant + Personal Server and performs a Web3Signed\n * read. Hides the `402 Payment Required` flow by default: if a read needs\n * payment, it signs the Personal Server's payment challenge, retries with\n * `X-PAYMENT`, and attaches a {@link DirectPaymentReceipt} under `payment`\n * when the Personal Server returns one. After a successful read, the\n * controller acknowledges the DCR so Vana Web can close/redirect the approval\n * tab.\n *\n * @param input - The `dcr_*` request id to read.\n * @returns `{ scope, data, payment? }`.\n * @throws {@link AccessNotApprovedError} if the request is not approved.\n * @throws {@link PaymentRequiredError} if payment is required but unsettled.\n */\n readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>>;\n}\n\nfunction isHexPrivateKey(value: string): value is Hex {\n return /^0x[0-9a-fA-F]{64}$/.test(value);\n}\n\n// A DCR is read-ready only while the grant exists and the Personal Server is\n// still serving it: `approved` (durable PS) or `ready_for_read` (browser PS).\n// `completed` is terminal — the app already read and acknowledged, and the\n// browser PS may be gone — so it is deliberately excluded here.\nfunction isReadReadyStatus(status: AccessRequestStatusValue): boolean {\n return status === \"approved\" || status === \"ready_for_read\";\n}\n\n/**\n * Create a {@link DirectDataController}.\n *\n * @param config - Controller configuration (env, key, app identity, source, scopes).\n * @returns A ready-to-use controller.\n * @throws {@link DirectConfigError} when the key or scopes are invalid.\n */\nexport function createDirectDataController(\n config: DirectDataControllerConfig,\n): DirectDataController {\n // `appPrivateKey` is the documented field; `builderPrivateKey` is a\n // deprecated alias kept for backwards compatibility.\n const privateKey = config.appPrivateKey ?? config.builderPrivateKey;\n if (!privateKey || !isHexPrivateKey(privateKey)) {\n throw new DirectConfigError(\n \"appPrivateKey must be a 0x-prefixed 32-byte hex string\",\n );\n }\n if (!config.scopes || config.scopes.length === 0) {\n throw new DirectConfigError(\"At least one scope is required\");\n }\n // Validate scopes eagerly so misconfiguration fails at construction.\n for (const scope of config.scopes) {\n parseScope(scope);\n }\n\n const env: DirectEnv = config.env ?? \"production\";\n const network: DirectNetwork = config.network ?? getDirectDefaultNetwork(env);\n const defaultEndpoints = getDirectEndpoints(env);\n const chainId = config.endpoints?.chainId ?? getDirectNetworkChainId(network);\n const endpoints: DirectServiceEndpoints = {\n ...defaultEndpoints,\n ...config.endpoints,\n chainId,\n };\n\n const account = privateKeyToAccount(privateKey as Hex);\n const signMessage: Web3SignedSignFn = (message: string) =>\n account.signMessage({ message });\n // viem's account.signTypedData satisfies the structural SignTypedDataFn used\n // by the escrow GenericPayment signer.\n const signTypedData = account.signTypedData as unknown as SignTypedDataFn;\n const accessRequestClient: AccessRequestClient =\n config.accessRequestClient ??\n createDefaultAccessRequestClient({\n baseUrl: endpoints.accessRequestBaseUrl,\n approvalBaseUrl: endpoints.approvalAppBaseUrl,\n fetchFn: config.fetchFn,\n appAddress: account.address,\n signMessage,\n });\n\n // Build the escrow payment config, defaulting from the per-network endpoints\n // table and the contract registry when `config.escrow` is omitted or partial.\n const escrowChainId = config.escrow?.chainId ?? chainId;\n const defaultEscrowContract =\n CONTRACTS.DataPortabilityEscrow.addresses[\n escrowChainId as keyof typeof CONTRACTS.DataPortabilityEscrow.addresses\n ] ?? undefined;\n if (!config.escrow?.escrowContract && !defaultEscrowContract) {\n throw new DirectConfigError(\n `No DataPortabilityEscrow address found in the registry for chainId ${escrowChainId}. ` +\n `Provide an explicit escrow.escrowContract in the controller config.`,\n );\n }\n const escrow: EscrowPaymentConfig = {\n client:\n config.escrow?.client ??\n createEscrowGatewayClient(endpoints.escrowGatewayUrl),\n escrowContract:\n config.escrow?.escrowContract ?? (defaultEscrowContract as `0x${string}`),\n chainId: escrowChainId,\n nonceSource: config.escrow?.nonceSource,\n signTypedData,\n };\n\n return {\n appAddress: account.address,\n\n getAppAddress(): string {\n return account.address;\n },\n\n getAppIdentity(): AppIdentity {\n return {\n id: config.app.id,\n name: config.app.name,\n homepageUrl: config.app.homepageUrl,\n address: account.address,\n };\n },\n\n async createAccessRequest(input): Promise<AccessRequest> {\n return accessRequestClient.createAccessRequest({\n appAddress: account.address,\n app: config.app,\n source: config.source,\n scopes: config.scopes,\n returnUrl: input.returnUrl,\n network,\n });\n },\n\n async getAccessRequestStatus(\n requestId: string,\n ): Promise<AccessRequestStatus> {\n return accessRequestClient.getAccessRequestStatus(requestId);\n },\n\n async readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>> {\n const status = await accessRequestClient.getAccessRequestStatus(\n input.requestId,\n );\n if (\n !isReadReadyStatus(status.status) ||\n !status.personalServerUrl ||\n !status.grantId ||\n !status.scope\n ) {\n throw new AccessNotApprovedError(\n \"Request is not approved or is missing grantId/scope/personalServerUrl\",\n {\n requestId: input.requestId,\n status: status.status,\n hasPersonalServerUrl: Boolean(status.personalServerUrl),\n hasGrantId: Boolean(status.grantId),\n hasScope: Boolean(status.scope),\n },\n );\n }\n\n const result = await readPersonalServerData({\n personalServerUrl: status.personalServerUrl,\n scope: status.scope,\n grantId: status.grantId,\n payerAddress: account.address,\n signMessage,\n escrow,\n fetchFn: config.personalServerFetch,\n transportRetry: config.personalServerTransportRetry,\n });\n try {\n await accessRequestClient.acknowledgeRead?.(input.requestId);\n } catch {\n // The read already succeeded; ack only drives Vana Web completion UX.\n }\n\n return {\n scope: status.scope,\n data: result.data as T,\n payment: result.payment,\n };\n },\n };\n}\n"],"mappings":"AAuBA,SAAS,2BAA2B;AAGpC,SAAS,kBAAkB;AAC3B,SAAS,iCAAiC;AAC1C,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,wBAAwB,yBAAyB;AAK1D;AAAA,EACE;AAAA,OAGK;AAiKP,SAAS,gBAAgB,OAA6B;AACpD,SAAO,sBAAsB,KAAK,KAAK;AACzC;AAMA,SAAS,kBAAkB,QAA2C;AACpE,SAAO,WAAW,cAAc,WAAW;AAC7C;AASO,SAAS,2BACd,QACsB;AAGtB,QAAM,aAAa,OAAO,iBAAiB,OAAO;AAClD,MAAI,CAAC,cAAc,CAAC,gBAAgB,UAAU,GAAG;AAC/C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,GAAG;AAChD,UAAM,IAAI,kBAAkB,gCAAgC;AAAA,EAC9D;AAEA,aAAW,SAAS,OAAO,QAAQ;AACjC,eAAW,KAAK;AAAA,EAClB;AAEA,QAAM,MAAiB,OAAO,OAAO;AACrC,QAAM,UAAyB,OAAO,WAAW,wBAAwB,GAAG;AAC5E,QAAM,mBAAmB,mBAAmB,GAAG;AAC/C,QAAM,UAAU,OAAO,WAAW,WAAW,wBAAwB,OAAO;AAC5E,QAAM,YAAoC;AAAA,IACxC,GAAG;AAAA,IACH,GAAG,OAAO;AAAA,IACV;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,UAAiB;AACrD,QAAM,cAAgC,CAAC,YACrC,QAAQ,YAAY,EAAE,QAAQ,CAAC;AAGjC,QAAM,gBAAgB,QAAQ;AAC9B,QAAM,sBACJ,OAAO,uBACP,iCAAiC;AAAA,IAC/B,SAAS,UAAU;AAAA,IACnB,iBAAiB,UAAU;AAAA,IAC3B,SAAS,OAAO;AAAA,IAChB,YAAY,QAAQ;AAAA,IACpB;AAAA,EACF,CAAC;AAIH,QAAM,gBAAgB,OAAO,QAAQ,WAAW;AAChD,QAAM,wBACJ,UAAU,sBAAsB,UAC9B,aACF,KAAK;AACP,MAAI,CAAC,OAAO,QAAQ,kBAAkB,CAAC,uBAAuB;AAC5D,UAAM,IAAI;AAAA,MACR,sEAAsE,aAAa;AAAA,IAErF;AAAA,EACF;AACA,QAAM,SAA8B;AAAA,IAClC,QACE,OAAO,QAAQ,UACf,0BAA0B,UAAU,gBAAgB;AAAA,IACtD,gBACE,OAAO,QAAQ,kBAAmB;AAAA,IACpC,SAAS;AAAA,IACT,aAAa,OAAO,QAAQ;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY,QAAQ;AAAA,IAEpB,gBAAwB;AACtB,aAAO,QAAQ;AAAA,IACjB;AAAA,IAEA,iBAA8B;AAC5B,aAAO;AAAA,QACL,IAAI,OAAO,IAAI;AAAA,QACf,MAAM,OAAO,IAAI;AAAA,QACjB,aAAa,OAAO,IAAI;AAAA,QACxB,SAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,IAEA,MAAM,oBAAoB,OAA+B;AACvD,aAAO,oBAAoB,oBAAoB;AAAA,QAC7C,YAAY,QAAQ;AAAA,QACpB,KAAK,OAAO;AAAA,QACZ,QAAQ,OAAO;AAAA,QACf,QAAQ,OAAO;AAAA,QACf,WAAW,MAAM;AAAA,QACjB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,uBACJ,WAC8B;AAC9B,aAAO,oBAAoB,uBAAuB,SAAS;AAAA,IAC7D;AAAA,IAEA,MAAM,iBAA8B,OAED;AACjC,YAAM,SAAS,MAAM,oBAAoB;AAAA,QACvC,MAAM;AAAA,MACR;AACA,UACE,CAAC,kBAAkB,OAAO,MAAM,KAChC,CAAC,OAAO,qBACR,CAAC,OAAO,WACR,CAAC,OAAO,OACR;AACA,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,YACE,WAAW,MAAM;AAAA,YACjB,QAAQ,OAAO;AAAA,YACf,sBAAsB,QAAQ,OAAO,iBAAiB;AAAA,YACtD,YAAY,QAAQ,OAAO,OAAO;AAAA,YAClC,UAAU,QAAQ,OAAO,KAAK;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,uBAAuB;AAAA,QAC1C,mBAAmB,OAAO;AAAA,QAC1B,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,cAAc,QAAQ;AAAA,QACtB;AAAA,QACA;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,gBAAgB,OAAO;AAAA,MACzB,CAAC;AACD,UAAI;AACF,cAAM,oBAAoB,kBAAkB,MAAM,SAAS;AAAA,MAC7D,QAAQ;AAAA,MAER;AAEA,aAAO;AAAA,QACL,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,SAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/direct/controller.ts"],"sourcesContent":["/**\n * Direct Data Controller — the server-side facade for the two-tab Data\n * Portability flow.\n *\n * @remarks\n * One controller owns an app's private key, source, scopes, app identity, and\n * payment flow. It exposes the three methods the builder guide documents:\n *\n * - {@link DirectDataController.createAccessRequest} — start an approval request.\n * - {@link DirectDataController.getAccessRequestStatus} — poll while the Vana tab is open.\n * - {@link DirectDataController.readApprovedData} — read from the Personal Server,\n * handling 402 Payment Required.\n *\n * Access requests are created through the Vana Account access-request API; the\n * Personal Server read uses Web3Signed auth; and payment uses the DPv2 escrow\n * surface (`protocol/escrow`) — when a read returns `402`, the controller signs\n * a `GenericPayment` with the app key, settles it through the escrow gateway,\n * and retries.\n *\n * @category Direct\n * @module direct/controller\n */\n\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex } from \"viem\";\nimport type { Web3SignedSignFn } from \"../auth/web3-signed-builder\";\nimport { parseScope } from \"../protocol/scopes\";\nimport { createEscrowGatewayClient } from \"../protocol/escrow\";\nimport { CONTRACTS } from \"../generated/addresses\";\nimport {\n createDefaultAccessRequestClient,\n type FetchLike,\n} from \"./access-request-client\";\nimport {\n getDirectDefaultNetwork,\n getDirectEndpoints,\n getDirectNetworkChainId,\n} from \"./endpoints\";\nimport { AccessNotApprovedError, DirectConfigError } from \"./errors\";\nimport {\n type EscrowPaymentConfig,\n type SignTypedDataFn,\n} from \"./escrow-payment\";\nimport {\n readPersonalServerData,\n type PersonalServerFetch,\n type PersonalServerTransportRetryOptions,\n} from \"./personal-server-read\";\nimport type {\n AccessRequest,\n AccessRequestClient,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n AppIdentity,\n DirectAppConfig,\n DirectEnv,\n DirectNetwork,\n DirectPaymentResponseMetadata,\n DirectServiceEndpoints,\n} from \"./types\";\n\n/** Configuration for {@link createDirectDataController}. */\nexport interface DirectDataControllerConfig {\n /** Target environment. Defaults to `\"production\"`. */\n env?: DirectEnv;\n /**\n * Target Vana network for chain-aware defaults. Defaults to the selected\n * environment's historical network (`mainnet` for production, `moksha` for\n * dev). Use `network: \"moksha\"` with the default production env for\n * production app/API URLs on testnet.\n */\n network?: DirectNetwork;\n /**\n * The app private key (`0x`-prefixed, 32 bytes). Server-side only — this key\n * is the app's on-chain identity and is never exposed to the browser.\n */\n appPrivateKey?: string;\n /**\n * @deprecated Use {@link DirectDataControllerConfig.appPrivateKey}. Accepted as\n * a backwards-compatible alias; if both are set, `appPrivateKey` wins.\n */\n builderPrivateKey?: string;\n /** App identity advertised during approval. */\n app: DirectAppConfig;\n /** Data source key (e.g. `\"icloud_notes\"`). */\n source: string;\n /** Scopes to request (e.g. `[\"icloud_notes.notes\"]`). At least one required. */\n scopes: string[];\n /**\n * Override the resolved service endpoints (partial). Useful for pointing at a\n * non-standard deployment.\n */\n endpoints?: Partial<DirectServiceEndpoints>;\n /**\n * Client for the Vana Account access-request API. Defaults to a client against\n * the resolved Vana Account endpoints; inject your own to point at a custom\n * deployment or to supply a test double.\n */\n accessRequestClient?: AccessRequestClient;\n /**\n * Escrow settlement config used when a Personal Server read returns `402`.\n *\n * @remarks\n * Wires the DPv2 escrow gateway (`protocol/escrow`). The controller supplies\n * the EIP-712 `signTypedData` from the app key automatically.\n *\n * When omitted (or partially omitted), the SDK derives defaults from the\n * per-network endpoints table and the contract registry:\n * - `client` defaults to a gateway client at `endpoints.escrowGatewayUrl`\n * - `escrowContract` defaults to `CONTRACTS.DataPortabilityEscrow.addresses[chainId]`\n * - `chainId` defaults to the controller's resolved chain id\n *\n * Provide this field only to override a specific default.\n */\n escrow?: Partial<DirectEscrowConfig>;\n /** `fetch` used by the default access-request client. Defaults to `globalThis.fetch`. */\n fetchFn?: FetchLike;\n /** `fetch` used for the Personal Server read. Defaults to `globalThis.fetch`. */\n personalServerFetch?: PersonalServerFetch;\n /**\n * Transport-retry knobs for the Personal Server read\n * ({@link PersonalServerTransportRetryOptions}). Defaults to 3 attempts with\n * exponential backoff. Retries fire only when fetch throws (the browser-PS\n * relay reconnect window), never on a received HTTP status, and never\n * re-sign a payment.\n */\n personalServerTransportRetry?: PersonalServerTransportRetryOptions;\n}\n\n/**\n * Controller-level escrow config — the {@link EscrowPaymentConfig} minus the\n * `signTypedData` and `chainId` the controller injects itself.\n */\nexport interface DirectEscrowConfig extends Omit<\n EscrowPaymentConfig,\n \"signTypedData\" | \"chainId\"\n> {\n /**\n * Chain id for the EIP-712 domain. Defaults to the controller's environment\n * (1480 for mainnet, 14800 for moksha).\n */\n chainId?: number;\n}\n\n/**\n * Server-side controller for the direct Data Portability flow.\n *\n * @typeParam T - Shape of the data returned by {@link DirectDataController.readApprovedData}.\n */\nexport interface DirectDataController {\n /** The on-chain address of the app, derived from `appPrivateKey`. */\n readonly appAddress: string;\n\n /**\n * The app's on-chain address — the address to fund and inspect in the Builder\n * activity report. Equivalent to {@link DirectDataController.appAddress}.\n *\n * @returns The app's `0x`-prefixed address.\n */\n getAppAddress(): string;\n\n /**\n * The app's full identity: its configured id/name/homepage plus the derived\n * on-chain address. Useful for telling builders which app address to fund or\n * look up.\n *\n * @returns `{ id, name, homepageUrl, address }`.\n */\n getAppIdentity(): AppIdentity;\n\n /**\n * Create an access request the user can approve.\n *\n * @param input - The post-approval return URL.\n * @returns `{ requestId, approvalUrl, appAddress }`.\n */\n createAccessRequest(input: { returnUrl: string }): Promise<AccessRequest>;\n\n /**\n * Fetch the current status of an access request.\n *\n * @param requestId - The `dcr_*` id from {@link DirectDataController.createAccessRequest}.\n * @returns `{ status, personalServerUrl?, grantId?, scope? }`.\n */\n getAccessRequestStatus(requestId: string): Promise<AccessRequestStatus>;\n\n /**\n * Read the approved data from the user's Personal Server.\n *\n * @remarks\n * Resolves the request to its grant + Personal Server and performs a Web3Signed\n * read. Hides the `402 Payment Required` flow by default: if a read needs\n * payment, it signs the Personal Server's payment challenge, retries with\n * `X-PAYMENT`, and attaches shape-validated but unauthenticated\n * {@link DirectPaymentResponseMetadata} under `payment` when the Personal\n * Server returns it. After a successful read, the controller acknowledges\n * the DCR so Vana Web can close/redirect the approval tab.\n *\n * @param input - The `dcr_*` request id to read.\n * @returns `{ scope, data, payment? }`.\n * @throws {@link AccessNotApprovedError} if the request is not approved.\n * @throws {@link PaymentRequiredError} if payment is required but unsettled.\n */\n readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>>;\n}\n\nfunction isHexPrivateKey(value: string): value is Hex {\n return /^0x[0-9a-fA-F]{64}$/.test(value);\n}\n\n// A DCR is read-ready only while the grant exists and the Personal Server is\n// still serving it: `approved` (durable PS) or `ready_for_read` (browser PS).\n// `completed` is terminal — the app already read and acknowledged, and the\n// browser PS may be gone — so it is deliberately excluded here.\nfunction isReadReadyStatus(status: AccessRequestStatusValue): boolean {\n return status === \"approved\" || status === \"ready_for_read\";\n}\n\n/**\n * Create a {@link DirectDataController}.\n *\n * @param config - Controller configuration (env, key, app identity, source, scopes).\n * @returns A ready-to-use controller.\n * @throws {@link DirectConfigError} when the key or scopes are invalid.\n */\nexport function createDirectDataController(\n config: DirectDataControllerConfig,\n): DirectDataController {\n // `appPrivateKey` is the documented field; `builderPrivateKey` is a\n // deprecated alias kept for backwards compatibility.\n const privateKey = config.appPrivateKey ?? config.builderPrivateKey;\n if (!privateKey || !isHexPrivateKey(privateKey)) {\n throw new DirectConfigError(\n \"appPrivateKey must be a 0x-prefixed 32-byte hex string\",\n );\n }\n if (!config.scopes || config.scopes.length === 0) {\n throw new DirectConfigError(\"At least one scope is required\");\n }\n // Validate scopes eagerly so misconfiguration fails at construction.\n for (const scope of config.scopes) {\n parseScope(scope);\n }\n\n const env: DirectEnv = config.env ?? \"production\";\n const network: DirectNetwork = config.network ?? getDirectDefaultNetwork(env);\n const defaultEndpoints = getDirectEndpoints(env);\n const chainId = config.endpoints?.chainId ?? getDirectNetworkChainId(network);\n const endpoints: DirectServiceEndpoints = {\n ...defaultEndpoints,\n ...config.endpoints,\n chainId,\n };\n\n const account = privateKeyToAccount(privateKey as Hex);\n const signMessage: Web3SignedSignFn = (message: string) =>\n account.signMessage({ message });\n // viem's account.signTypedData satisfies the structural SignTypedDataFn used\n // by the escrow GenericPayment signer.\n const signTypedData = account.signTypedData as unknown as SignTypedDataFn;\n const accessRequestClient: AccessRequestClient =\n config.accessRequestClient ??\n createDefaultAccessRequestClient({\n baseUrl: endpoints.accessRequestBaseUrl,\n approvalBaseUrl: endpoints.approvalAppBaseUrl,\n fetchFn: config.fetchFn,\n appAddress: account.address,\n signMessage,\n });\n\n // Build the escrow payment config, defaulting from the per-network endpoints\n // table and the contract registry when `config.escrow` is omitted or partial.\n const escrowChainId = config.escrow?.chainId ?? chainId;\n const defaultEscrowContract =\n CONTRACTS.DataPortabilityEscrow.addresses[\n escrowChainId as keyof typeof CONTRACTS.DataPortabilityEscrow.addresses\n ] ?? undefined;\n if (!config.escrow?.escrowContract && !defaultEscrowContract) {\n throw new DirectConfigError(\n `No DataPortabilityEscrow address found in the registry for chainId ${escrowChainId}. ` +\n `Provide an explicit escrow.escrowContract in the controller config.`,\n );\n }\n const escrow: EscrowPaymentConfig = {\n client:\n config.escrow?.client ??\n createEscrowGatewayClient(endpoints.escrowGatewayUrl),\n escrowContract:\n config.escrow?.escrowContract ?? (defaultEscrowContract as `0x${string}`),\n chainId: escrowChainId,\n nonceSource: config.escrow?.nonceSource,\n signTypedData,\n };\n\n return {\n appAddress: account.address,\n\n getAppAddress(): string {\n return account.address;\n },\n\n getAppIdentity(): AppIdentity {\n return {\n id: config.app.id,\n name: config.app.name,\n homepageUrl: config.app.homepageUrl,\n address: account.address,\n };\n },\n\n async createAccessRequest(input): Promise<AccessRequest> {\n return accessRequestClient.createAccessRequest({\n appAddress: account.address,\n app: config.app,\n source: config.source,\n scopes: config.scopes,\n returnUrl: input.returnUrl,\n network,\n });\n },\n\n async getAccessRequestStatus(\n requestId: string,\n ): Promise<AccessRequestStatus> {\n return accessRequestClient.getAccessRequestStatus(requestId);\n },\n\n async readApprovedData<T = unknown>(input: {\n requestId: string;\n }): Promise<ApprovedDataResult<T>> {\n const status = await accessRequestClient.getAccessRequestStatus(\n input.requestId,\n );\n if (\n !isReadReadyStatus(status.status) ||\n !status.personalServerUrl ||\n !status.grantId ||\n !status.scope\n ) {\n throw new AccessNotApprovedError(\n \"Request is not approved or is missing grantId/scope/personalServerUrl\",\n {\n requestId: input.requestId,\n status: status.status,\n hasPersonalServerUrl: Boolean(status.personalServerUrl),\n hasGrantId: Boolean(status.grantId),\n hasScope: Boolean(status.scope),\n },\n );\n }\n\n const result = await readPersonalServerData({\n personalServerUrl: status.personalServerUrl,\n scope: status.scope,\n grantId: status.grantId,\n payerAddress: account.address,\n signMessage,\n escrow,\n fetchFn: config.personalServerFetch,\n transportRetry: config.personalServerTransportRetry,\n });\n try {\n await accessRequestClient.acknowledgeRead?.(input.requestId);\n } catch {\n // The read already succeeded; ack only drives Vana Web completion UX.\n }\n\n return {\n scope: status.scope,\n data: result.data as T,\n payment: result.payment,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @category Direct
|
|
10
10
|
* @module direct/endpoints
|
|
11
11
|
*/
|
|
12
|
-
import type { DirectEnv, DirectNetwork, DirectServiceEndpoints } from "./types";
|
|
12
|
+
import type { DirectEnv, DirectNetwork, DirectServiceEndpoints } from "./types.js";
|
|
13
13
|
/** Production (mainnet) service URLs. */
|
|
14
14
|
export declare const PRODUCTION_ENDPOINTS: DirectServiceEndpoints;
|
|
15
15
|
/** Internal dev stack service URLs. */
|
package/dist/direct/errors.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @category Direct
|
|
10
10
|
* @module direct/errors
|
|
11
11
|
*/
|
|
12
|
-
import { VanaError } from "../errors";
|
|
12
|
+
import { VanaError } from "../errors.js";
|
|
13
13
|
/** Thrown when configuration passed to {@link createDirectDataController} is invalid. */
|
|
14
14
|
export declare class DirectConfigError extends VanaError {
|
|
15
15
|
readonly details?: Record<string, unknown> | undefined;
|