@opendatalabs/vana-sdk 3.14.1 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -5
- package/dist/direct/access-request-client.cjs +31 -3
- package/dist/direct/access-request-client.cjs.map +1 -1
- package/dist/direct/access-request-client.d.ts +12 -1
- package/dist/direct/access-request-client.js +31 -3
- package/dist/direct/access-request-client.js.map +1 -1
- package/dist/direct/connect-flow.cjs +63 -6
- package/dist/direct/connect-flow.cjs.map +1 -1
- package/dist/direct/connect-flow.d.ts +30 -4
- package/dist/direct/connect-flow.js +63 -6
- package/dist/direct/connect-flow.js.map +1 -1
- package/dist/direct/controller.cjs +4 -1
- package/dist/direct/controller.cjs.map +1 -1
- package/dist/direct/controller.d.ts +11 -3
- package/dist/direct/controller.js +4 -1
- package/dist/direct/controller.js.map +1 -1
- package/dist/direct/types.cjs +28 -2
- package/dist/direct/types.cjs.map +1 -1
- package/dist/direct/types.d.ts +51 -0
- package/dist/direct/types.js +26 -1
- package/dist/direct/types.js.map +1 -1
- package/dist/direct/use-direct-vana-connect.cjs +3 -0
- package/dist/direct/use-direct-vana-connect.cjs.map +1 -1
- package/dist/direct/use-direct-vana-connect.js +3 -0
- package/dist/direct/use-direct-vana-connect.js.map +1 -1
- package/dist/direct/use-direct-vana-connect.test.d.ts +1 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +167 -2
- package/dist/index.browser.js.map +3 -3
- package/dist/index.node.cjs +175 -2
- package/dist/index.node.cjs.map +3 -3
- package/dist/index.node.d.ts +2 -1
- package/dist/index.node.js +167 -2
- package/dist/index.node.js.map +3 -3
- package/dist/protocol/gateway.cjs +16 -2
- package/dist/protocol/gateway.cjs.map +1 -1
- package/dist/protocol/gateway.d.ts +2 -0
- package/dist/protocol/gateway.js +16 -2
- package/dist/protocol/gateway.js.map +1 -1
- package/dist/protocol/scope-actions.cjs +185 -0
- package/dist/protocol/scope-actions.cjs.map +1 -0
- package/dist/protocol/scope-actions.d.ts +145 -0
- package/dist/protocol/scope-actions.js +154 -0
- package/dist/protocol/scope-actions.js.map +1 -0
- package/dist/protocol/scope-actions.test.d.ts +1 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +9 -2
- package/dist/react.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/direct/types.cjs
CHANGED
|
@@ -18,9 +18,34 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var types_exports = {};
|
|
20
20
|
__export(types_exports, {
|
|
21
|
-
DirectOpType: () => DirectOpType
|
|
21
|
+
DirectOpType: () => DirectOpType,
|
|
22
|
+
normalizeMobileContinuationUrl: () => normalizeMobileContinuationUrl
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(types_exports);
|
|
25
|
+
const MOBILE_CONTINUATION_HOSTS = {
|
|
26
|
+
production: "open.vana.org",
|
|
27
|
+
dev: "open-dev.vana.org"
|
|
28
|
+
};
|
|
29
|
+
const MOBILE_CONTINUATION_TICKET = /^[A-Za-z0-9._~-]+$/;
|
|
30
|
+
function normalizeMobileContinuationUrl(value, env) {
|
|
31
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
32
|
+
let url;
|
|
33
|
+
try {
|
|
34
|
+
url = new URL(value);
|
|
35
|
+
} catch {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
if (url.protocol !== "https:") return void 0;
|
|
39
|
+
const allowedHosts = env ? [MOBILE_CONTINUATION_HOSTS[env]] : Object.values(MOBILE_CONTINUATION_HOSTS);
|
|
40
|
+
if (!allowedHosts.includes(url.hostname)) return void 0;
|
|
41
|
+
if (url.pathname !== "/continue") return void 0;
|
|
42
|
+
if (url.username !== "" || url.password !== "") return void 0;
|
|
43
|
+
if (url.port !== "") return void 0;
|
|
44
|
+
if (url.search !== "") return void 0;
|
|
45
|
+
const ticket = url.hash.startsWith("#") ? url.hash.slice(1) : "";
|
|
46
|
+
if (!MOBILE_CONTINUATION_TICKET.test(ticket)) return void 0;
|
|
47
|
+
return url.toString();
|
|
48
|
+
}
|
|
24
49
|
const DirectOpType = {
|
|
25
50
|
GrantRegistration: "grant_registration",
|
|
26
51
|
DataAccess: "data_access",
|
|
@@ -30,6 +55,7 @@ const DirectOpType = {
|
|
|
30
55
|
};
|
|
31
56
|
// Annotate the CommonJS export names for ESM import in node:
|
|
32
57
|
0 && (module.exports = {
|
|
33
|
-
DirectOpType
|
|
58
|
+
DirectOpType,
|
|
59
|
+
normalizeMobileContinuationUrl
|
|
34
60
|
});
|
|
35
61
|
//# sourceMappingURL=types.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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/**\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}\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/** Result of {@link DirectDataController.getAccessRequestStatus}. */\nexport interface AccessRequestStatus {\n /** Current lifecycle status of the request. */\n status: AccessRequestStatusValue;\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 * 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 }): 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;AAgOO,MAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;","names":[]}
|
|
1
|
+
{"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 * 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/** Result of {@link DirectDataController.getAccessRequestStatus}. */\nexport interface AccessRequestStatus {\n /** Current lifecycle status of the request. */\n status: AccessRequestStatusValue;\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 * 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;AA6GA,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;AA2JO,MAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;","names":[]}
|
package/dist/direct/types.d.ts
CHANGED
|
@@ -47,6 +47,13 @@ export interface AppIdentity extends DirectAppConfig {
|
|
|
47
47
|
/** The app's `0x`-prefixed on-chain address (derived from `appPrivateKey`). */
|
|
48
48
|
address: string;
|
|
49
49
|
}
|
|
50
|
+
/** One-time HTTPS callback used to deliver a foreground mobile Direct read. */
|
|
51
|
+
export interface ForegroundDelivery {
|
|
52
|
+
/** Fixed, same-origin consumer callback URL. */
|
|
53
|
+
url: string;
|
|
54
|
+
/** High-entropy bearer capability, generated and retained by the consumer. */
|
|
55
|
+
token: string;
|
|
56
|
+
}
|
|
50
57
|
/**
|
|
51
58
|
* Resolved service URLs and chain id for a given {@link DirectEnv}.
|
|
52
59
|
*
|
|
@@ -73,7 +80,38 @@ export interface AccessRequest {
|
|
|
73
80
|
approvalUrl: string;
|
|
74
81
|
/** On-chain address of the (registered or reused) app. */
|
|
75
82
|
appAddress: string;
|
|
83
|
+
/** Protocol network echoed by the access-request service. */
|
|
84
|
+
network?: DirectNetwork;
|
|
85
|
+
/** Authoritative ISO-8601 expiry for the access request. */
|
|
86
|
+
expiresAt?: string;
|
|
87
|
+
/**
|
|
88
|
+
* HTTPS continuation URL for a deep Direct request on a mobile browser.
|
|
89
|
+
*
|
|
90
|
+
* @remarks
|
|
91
|
+
* Present only for a server-classified deep Direct DCR while it remains
|
|
92
|
+
* pending, and only when Mobile continuation is enabled. It is an ordinary
|
|
93
|
+
* `https://open[-dev].vana.org/continue#<ticket>` link the mobile UI renders
|
|
94
|
+
* as a primary "Open Vana" tap — iOS Universal Links / Android App Links
|
|
95
|
+
* deliver it to Vana Mobile, and its web fallback recovers an absent app. The
|
|
96
|
+
* SDK never launches it automatically and owns no persistence.
|
|
97
|
+
*/
|
|
98
|
+
mobileContinuationUrl?: string;
|
|
76
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* @internal Strictly validate a mobile HTTPS continuation URL at the SDK
|
|
102
|
+
* boundary.
|
|
103
|
+
*
|
|
104
|
+
* @remarks
|
|
105
|
+
* Accepts only `https://open[-dev].vana.org/continue#<ticket>` with exactly one
|
|
106
|
+
* well-formed opaque fragment ticket and no user info, port, or query. When
|
|
107
|
+
* `env` is supplied only that environment's host is allowed; otherwise both
|
|
108
|
+
* canonical hosts are accepted for structural (defense-in-depth) validation.
|
|
109
|
+
*
|
|
110
|
+
* @param value - The candidate URL from a create or status response.
|
|
111
|
+
* @param env - Optional environment to pin the allowed host to.
|
|
112
|
+
* @returns The canonical URL string, or `undefined` when it fails validation.
|
|
113
|
+
*/
|
|
114
|
+
export declare function normalizeMobileContinuationUrl(value: unknown, env?: DirectEnv): string | undefined;
|
|
77
115
|
/**
|
|
78
116
|
* Lifecycle status of an access request.
|
|
79
117
|
*
|
|
@@ -103,6 +141,11 @@ export interface AccessRequestStatus {
|
|
|
103
141
|
* {@link AccessRequestStatus.scopes} to see all of them.
|
|
104
142
|
*/
|
|
105
143
|
scope?: string;
|
|
144
|
+
/**
|
|
145
|
+
* Fresh HTTPS mobile continuation URL, returned only while the deep Direct
|
|
146
|
+
* DCR is still pending. Its embedded ticket may rotate between polls.
|
|
147
|
+
*/
|
|
148
|
+
mobileContinuationUrl?: string;
|
|
106
149
|
/**
|
|
107
150
|
* Every scope the user approved on this request — present once data is ready
|
|
108
151
|
* to read.
|
|
@@ -166,6 +209,14 @@ export interface AccessRequestClient {
|
|
|
166
209
|
returnUrl: string;
|
|
167
210
|
/** Vana protocol network for this request (`"mainnet"` or `"moksha"`). */
|
|
168
211
|
network: DirectNetwork;
|
|
212
|
+
/** Optional foreground mobile delivery callback. */
|
|
213
|
+
foregroundDelivery?: ForegroundDelivery;
|
|
214
|
+
/**
|
|
215
|
+
* Optional retry key. The default client generates a fresh key per call
|
|
216
|
+
* when omitted; pass a stable key to retry a create whose response was
|
|
217
|
+
* lost without risking a duplicate DCR.
|
|
218
|
+
*/
|
|
219
|
+
idempotencyKey?: string;
|
|
169
220
|
}): Promise<AccessRequest>;
|
|
170
221
|
/**
|
|
171
222
|
* Fetch the current status of a previously created access request.
|
package/dist/direct/types.js
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
const MOBILE_CONTINUATION_HOSTS = {
|
|
2
|
+
production: "open.vana.org",
|
|
3
|
+
dev: "open-dev.vana.org"
|
|
4
|
+
};
|
|
5
|
+
const MOBILE_CONTINUATION_TICKET = /^[A-Za-z0-9._~-]+$/;
|
|
6
|
+
function normalizeMobileContinuationUrl(value, env) {
|
|
7
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
url = new URL(value);
|
|
11
|
+
} catch {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
if (url.protocol !== "https:") return void 0;
|
|
15
|
+
const allowedHosts = env ? [MOBILE_CONTINUATION_HOSTS[env]] : Object.values(MOBILE_CONTINUATION_HOSTS);
|
|
16
|
+
if (!allowedHosts.includes(url.hostname)) return void 0;
|
|
17
|
+
if (url.pathname !== "/continue") return void 0;
|
|
18
|
+
if (url.username !== "" || url.password !== "") return void 0;
|
|
19
|
+
if (url.port !== "") return void 0;
|
|
20
|
+
if (url.search !== "") return void 0;
|
|
21
|
+
const ticket = url.hash.startsWith("#") ? url.hash.slice(1) : "";
|
|
22
|
+
if (!MOBILE_CONTINUATION_TICKET.test(ticket)) return void 0;
|
|
23
|
+
return url.toString();
|
|
24
|
+
}
|
|
1
25
|
const DirectOpType = {
|
|
2
26
|
GrantRegistration: "grant_registration",
|
|
3
27
|
DataAccess: "data_access",
|
|
@@ -6,6 +30,7 @@ const DirectOpType = {
|
|
|
6
30
|
BuilderRegistration: "builder_registration"
|
|
7
31
|
};
|
|
8
32
|
export {
|
|
9
|
-
DirectOpType
|
|
33
|
+
DirectOpType,
|
|
34
|
+
normalizeMobileContinuationUrl
|
|
10
35
|
};
|
|
11
36
|
//# sourceMappingURL=types.js.map
|
package/dist/direct/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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/**\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}\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/** Result of {@link DirectDataController.getAccessRequestStatus}. */\nexport interface AccessRequestStatus {\n /** Current lifecycle status of the request. */\n status: AccessRequestStatusValue;\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 * 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 }): 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":"AAgOO,MAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;","names":[]}
|
|
1
|
+
{"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 * 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/** Result of {@link DirectDataController.getAccessRequestStatus}. */\nexport interface AccessRequestStatus {\n /** Current lifecycle status of the request. */\n status: AccessRequestStatusValue;\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 * 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":"AA6GA,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;AA2JO,MAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/direct/use-direct-vana-connect.ts"],"sourcesContent":["/**\n * React hook for the browser side of the direct Data Portability flow.\n *\n * @remarks\n * `useDirectVanaConnect` is a thin `useSyncExternalStore` binding over the\n * framework-agnostic {@link createDirectConnectFlow} store. The browser never\n * sees the app private key and never chooses scopes — it only calls the app's\n * own backend routes via the injected transports.\n *\n * This module is browser-safe and imports nothing Node-only. `react` is a peer\n * dependency.\n *\n * @category Direct\n * @module direct/use-direct-vana-connect\n */\n\nimport { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport {\n createDirectConnectFlow,\n type DirectConnectOptions,\n type DirectConnectState,\n type DirectConnectTransports,\n} from \"./connect-flow\";\n\n/** Options for {@link useDirectVanaConnect}: transports plus flow tunables. */\nexport type UseDirectVanaConnectOptions<T = unknown> =\n DirectConnectTransports<T> & DirectConnectOptions;\n\n/** Return value of {@link useDirectVanaConnect}. */\nexport interface UseDirectVanaConnectResult<T = unknown> {\n /** Current flow state (`state.type` is `\"idle\"` until `start()` is called). */\n state: DirectConnectState<T>;\n /** Begin the connect flow (create request, open Vana, poll, read). */\n start: () => void;\n /** Reset back to `idle` and cancel any in-flight polling. */\n reset: () => void;\n}\n\n/**\n * Drive the two-tab connect flow from a React component.\n *\n * @param options - The `createRequest`/`getStatus`/`readResult` transports plus\n * optional polling/timeout tunables.\n * @returns `{ state, start, reset }`.\n *\n * @example\n * ```tsx\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * return <button disabled={connect.state.type !== \"idle\"} onClick={connect.start}>Connect</button>;\n * ```\n */\nexport function useDirectVanaConnect<T = unknown>(\n options: UseDirectVanaConnectOptions<T>,\n): UseDirectVanaConnectResult<T> {\n // Keep the latest options in a ref so the store reads current callbacks\n // without being recreated on every render.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const flow = useMemo(\n () =>\n createDirectConnectFlow<T>(\n {\n createRequest: () => optionsRef.current.createRequest(),\n getStatus: (id) => optionsRef.current.getStatus(id),\n readResult: (id) => optionsRef.current.readResult(id),\n },\n {\n get pollIntervalMs() {\n return optionsRef.current.pollIntervalMs;\n },\n get timeoutMs() {\n return optionsRef.current.timeoutMs;\n },\n get openApprovalWindow() {\n return optionsRef.current.openApprovalWindow;\n },\n },\n ),\n // Created once per component instance; callbacks are read via optionsRef.\n [],\n );\n\n const state = useSyncExternalStore(\n flow.subscribe,\n flow.getState,\n flow.getState,\n );\n\n const start = useCallback(() => {\n void flow.start();\n }, [flow]);\n\n const reset = useCallback(() => {\n flow.reset();\n }, [flow]);\n\n return { state, start, reset };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,mBAAmE;AACnE,0BAKO;AAiCA,SAAS,qBACd,SAC+B;AAG/B,QAAM,iBAAa,qBAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,WAAO;AAAA,IACX,UACE;AAAA,MACE;AAAA,QACE,eAAe,MAAM,WAAW,QAAQ,cAAc;AAAA,QACtD,WAAW,CAAC,OAAO,WAAW,QAAQ,UAAU,EAAE;AAAA,QAClD,YAAY,CAAC,OAAO,WAAW,QAAQ,WAAW,EAAE;AAAA,MACtD;AAAA,MACA;AAAA,QACE,IAAI,iBAAiB;AACnB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,YAAY;AACd,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,qBAAqB;AACvB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAEF,CAAC;AAAA,EACH;AAEA,QAAM,YAAQ;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,QAAM,YAAQ,0BAAY,MAAM;AAC9B,SAAK,KAAK,MAAM;AAAA,EAClB,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,YAAQ,0BAAY,MAAM;AAC9B,SAAK,MAAM;AAAA,EACb,GAAG,CAAC,IAAI,CAAC;AAET,SAAO,EAAE,OAAO,OAAO,MAAM;AAC/B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/direct/use-direct-vana-connect.ts"],"sourcesContent":["/**\n * React hook for the browser side of the direct Data Portability flow.\n *\n * @remarks\n * `useDirectVanaConnect` is a thin `useSyncExternalStore` binding over the\n * framework-agnostic {@link createDirectConnectFlow} store. The browser never\n * sees the app private key and never chooses scopes — it only calls the app's\n * own backend routes via the injected transports.\n *\n * This module is browser-safe and imports nothing Node-only. `react` is a peer\n * dependency.\n *\n * @category Direct\n * @module direct/use-direct-vana-connect\n */\n\nimport { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport {\n createDirectConnectFlow,\n type DirectConnectOptions,\n type DirectConnectState,\n type DirectConnectTransports,\n} from \"./connect-flow\";\n\n/** Options for {@link useDirectVanaConnect}: transports plus flow tunables. */\nexport type UseDirectVanaConnectOptions<T = unknown> =\n DirectConnectTransports<T> & DirectConnectOptions;\n\n/** Return value of {@link useDirectVanaConnect}. */\nexport interface UseDirectVanaConnectResult<T = unknown> {\n /** Current flow state (`state.type` is `\"idle\"` until `start()` is called). */\n state: DirectConnectState<T>;\n /** Begin the connect flow (create request, open Vana, poll, read). */\n start: () => void;\n /** Reset back to `idle` and cancel any in-flight polling. */\n reset: () => void;\n}\n\n/**\n * Drive the two-tab connect flow from a React component.\n *\n * @param options - The `createRequest`/`getStatus`/`readResult` transports plus\n * optional polling/timeout tunables.\n * @returns `{ state, start, reset }`.\n *\n * @example\n * ```tsx\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * return <button disabled={connect.state.type !== \"idle\"} onClick={connect.start}>Connect</button>;\n * ```\n */\nexport function useDirectVanaConnect<T = unknown>(\n options: UseDirectVanaConnectOptions<T>,\n): UseDirectVanaConnectResult<T> {\n // Keep the latest options in a ref so the store reads current callbacks\n // without being recreated on every render.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const flow = useMemo(\n () =>\n createDirectConnectFlow<T>(\n {\n createRequest: () => optionsRef.current.createRequest(),\n getStatus: (id) => optionsRef.current.getStatus(id),\n readResult: (id) => optionsRef.current.readResult(id),\n },\n {\n get pollIntervalMs() {\n return optionsRef.current.pollIntervalMs;\n },\n get timeoutMs() {\n return optionsRef.current.timeoutMs;\n },\n get openApprovalWindow() {\n return optionsRef.current.openApprovalWindow;\n },\n get browserPlatformPolicy() {\n return optionsRef.current.browserPlatformPolicy;\n },\n },\n ),\n // Created once per component instance; callbacks are read via optionsRef.\n [],\n );\n\n const state = useSyncExternalStore(\n flow.subscribe,\n flow.getState,\n flow.getState,\n );\n\n const start = useCallback(() => {\n void flow.start();\n }, [flow]);\n\n const reset = useCallback(() => {\n flow.reset();\n }, [flow]);\n\n return { state, start, reset };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,mBAAmE;AACnE,0BAKO;AAiCA,SAAS,qBACd,SAC+B;AAG/B,QAAM,iBAAa,qBAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,WAAO;AAAA,IACX,UACE;AAAA,MACE;AAAA,QACE,eAAe,MAAM,WAAW,QAAQ,cAAc;AAAA,QACtD,WAAW,CAAC,OAAO,WAAW,QAAQ,UAAU,EAAE;AAAA,QAClD,YAAY,CAAC,OAAO,WAAW,QAAQ,WAAW,EAAE;AAAA,MACtD;AAAA,MACA;AAAA,QACE,IAAI,iBAAiB;AACnB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,YAAY;AACd,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,qBAAqB;AACvB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,wBAAwB;AAC1B,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAEF,CAAC;AAAA,EACH;AAEA,QAAM,YAAQ;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,QAAM,YAAQ,0BAAY,MAAM;AAC9B,SAAK,KAAK,MAAM;AAAA,EAClB,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,YAAQ,0BAAY,MAAM;AAC9B,SAAK,MAAM;AAAA,EACb,GAAG,CAAC,IAAI,CAAC;AAET,SAAO,EAAE,OAAO,OAAO,MAAM;AAC/B;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/direct/use-direct-vana-connect.ts"],"sourcesContent":["/**\n * React hook for the browser side of the direct Data Portability flow.\n *\n * @remarks\n * `useDirectVanaConnect` is a thin `useSyncExternalStore` binding over the\n * framework-agnostic {@link createDirectConnectFlow} store. The browser never\n * sees the app private key and never chooses scopes — it only calls the app's\n * own backend routes via the injected transports.\n *\n * This module is browser-safe and imports nothing Node-only. `react` is a peer\n * dependency.\n *\n * @category Direct\n * @module direct/use-direct-vana-connect\n */\n\nimport { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport {\n createDirectConnectFlow,\n type DirectConnectOptions,\n type DirectConnectState,\n type DirectConnectTransports,\n} from \"./connect-flow\";\n\n/** Options for {@link useDirectVanaConnect}: transports plus flow tunables. */\nexport type UseDirectVanaConnectOptions<T = unknown> =\n DirectConnectTransports<T> & DirectConnectOptions;\n\n/** Return value of {@link useDirectVanaConnect}. */\nexport interface UseDirectVanaConnectResult<T = unknown> {\n /** Current flow state (`state.type` is `\"idle\"` until `start()` is called). */\n state: DirectConnectState<T>;\n /** Begin the connect flow (create request, open Vana, poll, read). */\n start: () => void;\n /** Reset back to `idle` and cancel any in-flight polling. */\n reset: () => void;\n}\n\n/**\n * Drive the two-tab connect flow from a React component.\n *\n * @param options - The `createRequest`/`getStatus`/`readResult` transports plus\n * optional polling/timeout tunables.\n * @returns `{ state, start, reset }`.\n *\n * @example\n * ```tsx\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * return <button disabled={connect.state.type !== \"idle\"} onClick={connect.start}>Connect</button>;\n * ```\n */\nexport function useDirectVanaConnect<T = unknown>(\n options: UseDirectVanaConnectOptions<T>,\n): UseDirectVanaConnectResult<T> {\n // Keep the latest options in a ref so the store reads current callbacks\n // without being recreated on every render.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const flow = useMemo(\n () =>\n createDirectConnectFlow<T>(\n {\n createRequest: () => optionsRef.current.createRequest(),\n getStatus: (id) => optionsRef.current.getStatus(id),\n readResult: (id) => optionsRef.current.readResult(id),\n },\n {\n get pollIntervalMs() {\n return optionsRef.current.pollIntervalMs;\n },\n get timeoutMs() {\n return optionsRef.current.timeoutMs;\n },\n get openApprovalWindow() {\n return optionsRef.current.openApprovalWindow;\n },\n },\n ),\n // Created once per component instance; callbacks are read via optionsRef.\n [],\n );\n\n const state = useSyncExternalStore(\n flow.subscribe,\n flow.getState,\n flow.getState,\n );\n\n const start = useCallback(() => {\n void flow.start();\n }, [flow]);\n\n const reset = useCallback(() => {\n flow.reset();\n }, [flow]);\n\n return { state, start, reset };\n}\n"],"mappings":"AAgBA,SAAS,aAAa,SAAS,QAAQ,4BAA4B;AACnE;AAAA,EACE;AAAA,OAIK;AAiCA,SAAS,qBACd,SAC+B;AAG/B,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,OAAO;AAAA,IACX,MACE;AAAA,MACE;AAAA,QACE,eAAe,MAAM,WAAW,QAAQ,cAAc;AAAA,QACtD,WAAW,CAAC,OAAO,WAAW,QAAQ,UAAU,EAAE;AAAA,QAClD,YAAY,CAAC,OAAO,WAAW,QAAQ,WAAW,EAAE;AAAA,MACtD;AAAA,MACA;AAAA,QACE,IAAI,iBAAiB;AACnB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,YAAY;AACd,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,qBAAqB;AACvB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAEF,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,QAAM,QAAQ,YAAY,MAAM;AAC9B,SAAK,KAAK,MAAM;AAAA,EAClB,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,QAAQ,YAAY,MAAM;AAC9B,SAAK,MAAM;AAAA,EACb,GAAG,CAAC,IAAI,CAAC;AAET,SAAO,EAAE,OAAO,OAAO,MAAM;AAC/B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/direct/use-direct-vana-connect.ts"],"sourcesContent":["/**\n * React hook for the browser side of the direct Data Portability flow.\n *\n * @remarks\n * `useDirectVanaConnect` is a thin `useSyncExternalStore` binding over the\n * framework-agnostic {@link createDirectConnectFlow} store. The browser never\n * sees the app private key and never chooses scopes — it only calls the app's\n * own backend routes via the injected transports.\n *\n * This module is browser-safe and imports nothing Node-only. `react` is a peer\n * dependency.\n *\n * @category Direct\n * @module direct/use-direct-vana-connect\n */\n\nimport { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport {\n createDirectConnectFlow,\n type DirectConnectOptions,\n type DirectConnectState,\n type DirectConnectTransports,\n} from \"./connect-flow\";\n\n/** Options for {@link useDirectVanaConnect}: transports plus flow tunables. */\nexport type UseDirectVanaConnectOptions<T = unknown> =\n DirectConnectTransports<T> & DirectConnectOptions;\n\n/** Return value of {@link useDirectVanaConnect}. */\nexport interface UseDirectVanaConnectResult<T = unknown> {\n /** Current flow state (`state.type` is `\"idle\"` until `start()` is called). */\n state: DirectConnectState<T>;\n /** Begin the connect flow (create request, open Vana, poll, read). */\n start: () => void;\n /** Reset back to `idle` and cancel any in-flight polling. */\n reset: () => void;\n}\n\n/**\n * Drive the two-tab connect flow from a React component.\n *\n * @param options - The `createRequest`/`getStatus`/`readResult` transports plus\n * optional polling/timeout tunables.\n * @returns `{ state, start, reset }`.\n *\n * @example\n * ```tsx\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * return <button disabled={connect.state.type !== \"idle\"} onClick={connect.start}>Connect</button>;\n * ```\n */\nexport function useDirectVanaConnect<T = unknown>(\n options: UseDirectVanaConnectOptions<T>,\n): UseDirectVanaConnectResult<T> {\n // Keep the latest options in a ref so the store reads current callbacks\n // without being recreated on every render.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const flow = useMemo(\n () =>\n createDirectConnectFlow<T>(\n {\n createRequest: () => optionsRef.current.createRequest(),\n getStatus: (id) => optionsRef.current.getStatus(id),\n readResult: (id) => optionsRef.current.readResult(id),\n },\n {\n get pollIntervalMs() {\n return optionsRef.current.pollIntervalMs;\n },\n get timeoutMs() {\n return optionsRef.current.timeoutMs;\n },\n get openApprovalWindow() {\n return optionsRef.current.openApprovalWindow;\n },\n get browserPlatformPolicy() {\n return optionsRef.current.browserPlatformPolicy;\n },\n },\n ),\n // Created once per component instance; callbacks are read via optionsRef.\n [],\n );\n\n const state = useSyncExternalStore(\n flow.subscribe,\n flow.getState,\n flow.getState,\n );\n\n const start = useCallback(() => {\n void flow.start();\n }, [flow]);\n\n const reset = useCallback(() => {\n flow.reset();\n }, [flow]);\n\n return { state, start, reset };\n}\n"],"mappings":"AAgBA,SAAS,aAAa,SAAS,QAAQ,4BAA4B;AACnE;AAAA,EACE;AAAA,OAIK;AAiCA,SAAS,qBACd,SAC+B;AAG/B,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,OAAO;AAAA,IACX,MACE;AAAA,MACE;AAAA,QACE,eAAe,MAAM,WAAW,QAAQ,cAAc;AAAA,QACtD,WAAW,CAAC,OAAO,WAAW,QAAQ,UAAU,EAAE;AAAA,QAClD,YAAY,CAAC,OAAO,WAAW,QAAQ,WAAW,EAAE;AAAA,MACtD;AAAA,MACA;AAAA,QACE,IAAI,iBAAiB;AACnB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,YAAY;AACd,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,qBAAqB;AACvB,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,QACA,IAAI,wBAAwB;AAC1B,iBAAO,WAAW,QAAQ;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAEF,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,QAAM,QAAQ,YAAY,MAAM;AAC9B,SAAK,KAAK,MAAM;AAAA,EAClB,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,QAAQ,YAAY,MAAM;AAC9B,SAAK,MAAM;AAAA,EACb,GAAG,CAAC,IAAI,CAAC;AAET,SAAO,EAAE,OAAO,OAAO,MAAM;AAC/B;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export { escrowContractAddress, encodeDepositNativeData, encodeDepositTokenData,
|
|
|
47
47
|
export { DATA_REGISTRY_STATUS_ABI, DataPointStatus, dataRegistryContractAddress, encodeSetDataPointStatusData, buildSetDataPointStatusRequest, buildMarkDataPointUnavailableRequest, type SetDataPointStatusInput, type DataPointStatusTransactionRequest, } from "./protocol/data-point-status.js";
|
|
48
48
|
export { personalServerDataReadPath, buildPersonalServerDataReadRequest, readPersonalServerData, type BuildPersonalServerDataReadRequestParams, type ReadPersonalServerDataParams, } from "./protocol/personal-server-data.js";
|
|
49
49
|
export { ScopeSchema, parseScope, scopeToPathSegments, scopeMatchesPattern, scopeCoveredByGrant, type Scope, type ParsedScope, } from "./protocol/scopes.js";
|
|
50
|
+
export { SCOPE_ACTIONS, InvalidScopeEntryError, parseScopeEntry, formatScopeEntry, grantPermissions, permissionsToScopes, tryGrantPermissions, hasAction, type ScopeAction, type ParsedScopeEntry, type GrantPermission, } from "./protocol/scope-actions.js";
|
|
50
51
|
export { DataFileEnvelopeSchema, createDataFileEnvelope, IngestResponseSchema, type DataFileEnvelope, type IngestResponse, } from "./protocol/data-file.js";
|
|
51
52
|
export { createGatewayClient, type GatewayEnvelope, type GatewayProof, type Builder, type Schema, type ServerInfo, type OwnerServerRecord, type OwnerServersResult, type GatewayGrantFee, type GatewayGrantStatus, type GatewayGrantResponse, type GrantListItem, type DataPointRecord, type DataPointListResult, type ListDataPointsOptions, type RegisterServerParams, type RegisterServerResult, type RegisterBuilderParams, type RegisterBuilderResult, type RegisterDataPointParams, type RegisterDataPointResult, type CreateGrantParams, type RevokeGrantParams, type AccessRecord, type PayForOperationParams, type PayForOperationResult, type SettleOpType, type SettleItem, type SettlePromoteResult, type SettleReconcileItem, type SettleParams, type SettleResult, type GatewayClient, } from "./protocol/gateway.js";
|
|
52
53
|
export { createEscrowGatewayClient, genericPaymentDomain, GENERIC_PAYMENT_TYPES, ESCROW_DEPOSIT_ABI, NATIVE_ASSET_ADDRESS, type GenericPaymentMessage, type EscrowBalanceEntry, type EscrowBalanceResult, type EscrowBalanceSyncResult, type DepositSubmissionResult, type PaymentBreakdown, type EscrowPayResult, type SubmitDepositParams, type PayForOpParams, type EscrowGatewayClient, type SubmittedDepositEntry, type FinalizedDepositEntry, type FailedDepositEntry, } from "./protocol/escrow.js";
|
package/dist/index.browser.js
CHANGED
|
@@ -32947,7 +32947,161 @@ function scopeCoveredByGrant(requestedScope, grantedScopes) {
|
|
|
32947
32947
|
);
|
|
32948
32948
|
}
|
|
32949
32949
|
|
|
32950
|
+
// src/protocol/scope-actions.ts
|
|
32951
|
+
var SCOPE_ACTIONS = ["read", "write"];
|
|
32952
|
+
var InvalidScopeEntryError = class extends Error {
|
|
32953
|
+
/** The offending entry, verbatim (unknown because it may not be a string). */
|
|
32954
|
+
entry;
|
|
32955
|
+
constructor(entry, reason) {
|
|
32956
|
+
super(`Invalid scope entry ${describeValue(entry)}: ${reason}`);
|
|
32957
|
+
this.name = "InvalidScopeEntryError";
|
|
32958
|
+
this.entry = entry;
|
|
32959
|
+
}
|
|
32960
|
+
};
|
|
32961
|
+
var OPERATION_SEPARATOR = ":";
|
|
32962
|
+
function describeValue(value) {
|
|
32963
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
32964
|
+
if (value === null) return "null";
|
|
32965
|
+
return `[${typeof value}]`;
|
|
32966
|
+
}
|
|
32967
|
+
var OPERATION_BY_PREFIX = {
|
|
32968
|
+
write: "write"
|
|
32969
|
+
};
|
|
32970
|
+
function assertScopePart(entry, scope) {
|
|
32971
|
+
if (scope.length === 0) {
|
|
32972
|
+
throw new InvalidScopeEntryError(entry, "scope part is empty");
|
|
32973
|
+
}
|
|
32974
|
+
if (scope.includes(OPERATION_SEPARATOR)) {
|
|
32975
|
+
throw new InvalidScopeEntryError(
|
|
32976
|
+
entry,
|
|
32977
|
+
`scope part must not contain "${OPERATION_SEPARATOR}"`
|
|
32978
|
+
);
|
|
32979
|
+
}
|
|
32980
|
+
}
|
|
32981
|
+
function parseScopeEntry(entry) {
|
|
32982
|
+
const raw = entry;
|
|
32983
|
+
if (typeof raw !== "string") {
|
|
32984
|
+
throw new InvalidScopeEntryError(raw, "entry must be a string");
|
|
32985
|
+
}
|
|
32986
|
+
const separatorIndex = entry.indexOf(OPERATION_SEPARATOR);
|
|
32987
|
+
if (separatorIndex === -1) {
|
|
32988
|
+
assertScopePart(entry, entry);
|
|
32989
|
+
return { scope: entry, action: "read" };
|
|
32990
|
+
}
|
|
32991
|
+
const prefix = entry.slice(0, separatorIndex);
|
|
32992
|
+
const scope = entry.slice(separatorIndex + 1);
|
|
32993
|
+
const action = Object.hasOwn(OPERATION_BY_PREFIX, prefix) ? OPERATION_BY_PREFIX[prefix] : void 0;
|
|
32994
|
+
if (action === void 0) {
|
|
32995
|
+
throw new InvalidScopeEntryError(
|
|
32996
|
+
entry,
|
|
32997
|
+
`unknown operation "${prefix}" (known: ${Object.keys(OPERATION_BY_PREFIX).join(", ")}; read has no prefix)`
|
|
32998
|
+
);
|
|
32999
|
+
}
|
|
33000
|
+
assertScopePart(entry, scope);
|
|
33001
|
+
return { scope, action };
|
|
33002
|
+
}
|
|
33003
|
+
function formatScopeEntry(parsed) {
|
|
33004
|
+
const { scope, action } = parsed;
|
|
33005
|
+
assertScopePart(scope, scope);
|
|
33006
|
+
if (action === "read") return scope;
|
|
33007
|
+
const prefix = Object.entries(OPERATION_BY_PREFIX).find(
|
|
33008
|
+
([, candidate]) => candidate === action
|
|
33009
|
+
)?.[0];
|
|
33010
|
+
if (prefix === void 0) {
|
|
33011
|
+
throw new InvalidScopeEntryError(
|
|
33012
|
+
scope,
|
|
33013
|
+
`unknown action ${describeValue(action)} (known: ${SCOPE_ACTIONS.join(", ")})`
|
|
33014
|
+
);
|
|
33015
|
+
}
|
|
33016
|
+
return `${prefix}${OPERATION_SEPARATOR}${scope}`;
|
|
33017
|
+
}
|
|
33018
|
+
function compareScopes(a, b) {
|
|
33019
|
+
if (a < b) return -1;
|
|
33020
|
+
if (a > b) return 1;
|
|
33021
|
+
return 0;
|
|
33022
|
+
}
|
|
33023
|
+
function sortActions(actions) {
|
|
33024
|
+
const present = new Set(actions);
|
|
33025
|
+
return SCOPE_ACTIONS.filter((action) => present.has(action));
|
|
33026
|
+
}
|
|
33027
|
+
function grantPermissions(scopes) {
|
|
33028
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
33029
|
+
for (const entry of scopes) {
|
|
33030
|
+
const { scope, action } = parseScopeEntry(entry);
|
|
33031
|
+
let actions = byScope.get(scope);
|
|
33032
|
+
if (actions === void 0) {
|
|
33033
|
+
actions = /* @__PURE__ */ new Set();
|
|
33034
|
+
byScope.set(scope, actions);
|
|
33035
|
+
}
|
|
33036
|
+
actions.add(action);
|
|
33037
|
+
}
|
|
33038
|
+
return [...byScope.keys()].sort(compareScopes).map((scope) => ({
|
|
33039
|
+
scope,
|
|
33040
|
+
actions: sortActions(byScope.get(scope) ?? [])
|
|
33041
|
+
}));
|
|
33042
|
+
}
|
|
33043
|
+
function permissionsToScopes(permissions) {
|
|
33044
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
33045
|
+
for (const { scope, actions } of permissions) {
|
|
33046
|
+
let merged = byScope.get(scope);
|
|
33047
|
+
if (merged === void 0) {
|
|
33048
|
+
merged = /* @__PURE__ */ new Set();
|
|
33049
|
+
byScope.set(scope, merged);
|
|
33050
|
+
}
|
|
33051
|
+
for (const action of actions) {
|
|
33052
|
+
if (!SCOPE_ACTIONS.includes(action)) {
|
|
33053
|
+
throw new InvalidScopeEntryError(
|
|
33054
|
+
scope,
|
|
33055
|
+
`unknown action ${describeValue(action)} (known: ${SCOPE_ACTIONS.join(", ")})`
|
|
33056
|
+
);
|
|
33057
|
+
}
|
|
33058
|
+
merged.add(action);
|
|
33059
|
+
}
|
|
33060
|
+
}
|
|
33061
|
+
const entries = [];
|
|
33062
|
+
for (const scope of [...byScope.keys()].sort(compareScopes)) {
|
|
33063
|
+
for (const action of sortActions(byScope.get(scope) ?? [])) {
|
|
33064
|
+
entries.push(formatScopeEntry({ scope, action }));
|
|
33065
|
+
}
|
|
33066
|
+
}
|
|
33067
|
+
return entries;
|
|
33068
|
+
}
|
|
33069
|
+
function hasAction(scopes, scope, action) {
|
|
33070
|
+
if (scope.includes(OPERATION_SEPARATOR)) return false;
|
|
33071
|
+
for (const entry of scopes) {
|
|
33072
|
+
let parsed;
|
|
33073
|
+
try {
|
|
33074
|
+
parsed = parseScopeEntry(entry);
|
|
33075
|
+
} catch (error) {
|
|
33076
|
+
if (error instanceof InvalidScopeEntryError) continue;
|
|
33077
|
+
throw error;
|
|
33078
|
+
}
|
|
33079
|
+
if (parsed.action === action && scopeMatchesPattern(scope, parsed.scope)) {
|
|
33080
|
+
return true;
|
|
33081
|
+
}
|
|
33082
|
+
}
|
|
33083
|
+
return false;
|
|
33084
|
+
}
|
|
33085
|
+
function tryGrantPermissions(scopes) {
|
|
33086
|
+
try {
|
|
33087
|
+
return grantPermissions(scopes);
|
|
33088
|
+
} catch (error) {
|
|
33089
|
+
if (error instanceof InvalidScopeEntryError) return void 0;
|
|
33090
|
+
throw error;
|
|
33091
|
+
}
|
|
33092
|
+
}
|
|
33093
|
+
|
|
32950
33094
|
// src/protocol/gateway.ts
|
|
33095
|
+
function withGrantPermissions(grant) {
|
|
33096
|
+
const stripped = { ...grant };
|
|
33097
|
+
delete stripped.permissions;
|
|
33098
|
+
const scopes = stripped.scopes;
|
|
33099
|
+
if (!Array.isArray(scopes)) {
|
|
33100
|
+
return stripped;
|
|
33101
|
+
}
|
|
33102
|
+
const permissions = tryGrantPermissions(scopes);
|
|
33103
|
+
return permissions === void 0 ? stripped : { ...stripped, permissions };
|
|
33104
|
+
}
|
|
32951
33105
|
function createGatewayClient(baseUrl) {
|
|
32952
33106
|
const base = baseUrl.replace(/\/+$/, "");
|
|
32953
33107
|
async function unwrapEnvelope(res) {
|
|
@@ -32977,7 +33131,9 @@ function createGatewayClient(baseUrl) {
|
|
|
32977
33131
|
if (!res.ok) {
|
|
32978
33132
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
32979
33133
|
}
|
|
32980
|
-
return
|
|
33134
|
+
return withGrantPermissions(
|
|
33135
|
+
await unwrapEnvelope(res)
|
|
33136
|
+
);
|
|
32981
33137
|
},
|
|
32982
33138
|
async listGrantsByUser(userAddress) {
|
|
32983
33139
|
const res = await fetch(`${base}/v1/grants?user=${userAddress}`);
|
|
@@ -32985,7 +33141,8 @@ function createGatewayClient(baseUrl) {
|
|
|
32985
33141
|
if (!res.ok) {
|
|
32986
33142
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
32987
33143
|
}
|
|
32988
|
-
|
|
33144
|
+
const grants = await unwrapEnvelope(res);
|
|
33145
|
+
return grants.map(withGrantPermissions);
|
|
32989
33146
|
},
|
|
32990
33147
|
async getSchemaForScope(scope) {
|
|
32991
33148
|
const res = await fetch(`${base}/v1/schemas?scope=${scope}`);
|
|
@@ -33451,6 +33608,7 @@ export {
|
|
|
33451
33608
|
InMemoryTokenStore,
|
|
33452
33609
|
IngestResponseSchema,
|
|
33453
33610
|
InvalidConfigurationError,
|
|
33611
|
+
InvalidScopeEntryError,
|
|
33454
33612
|
InvalidSignatureError,
|
|
33455
33613
|
IpfsStorage,
|
|
33456
33614
|
MASTER_KEY_MESSAGE,
|
|
@@ -33476,6 +33634,7 @@ export {
|
|
|
33476
33634
|
REGISTRATION_KIND_FOR_OP,
|
|
33477
33635
|
ReadOnlyError,
|
|
33478
33636
|
RelayerError,
|
|
33637
|
+
SCOPE_ACTIONS,
|
|
33479
33638
|
SERVER_REGISTRATION_TYPES,
|
|
33480
33639
|
ScopeSchema,
|
|
33481
33640
|
SerializationError,
|
|
@@ -33525,6 +33684,7 @@ export {
|
|
|
33525
33684
|
encryptWithPassword,
|
|
33526
33685
|
escrowContractAddress,
|
|
33527
33686
|
escrowPaymentDomain,
|
|
33687
|
+
formatScopeEntry,
|
|
33528
33688
|
generatePkceVerifier,
|
|
33529
33689
|
genericPaymentDomain,
|
|
33530
33690
|
getAbi,
|
|
@@ -33537,8 +33697,10 @@ export {
|
|
|
33537
33697
|
getOpFee,
|
|
33538
33698
|
getPlatformCapabilities,
|
|
33539
33699
|
getServiceEndpoints,
|
|
33700
|
+
grantPermissions,
|
|
33540
33701
|
grantRegistrationDomain,
|
|
33541
33702
|
grantRevocationDomain,
|
|
33703
|
+
hasAction,
|
|
33542
33704
|
isDataPortabilityGatewayConfig,
|
|
33543
33705
|
isECIESEncrypted,
|
|
33544
33706
|
isPlatformSupported,
|
|
@@ -33548,7 +33710,9 @@ export {
|
|
|
33548
33710
|
mokshaTestnet2 as mokshaTestnet,
|
|
33549
33711
|
parsePSError,
|
|
33550
33712
|
parseScope,
|
|
33713
|
+
parseScopeEntry,
|
|
33551
33714
|
parseWeb3SignedHeader,
|
|
33715
|
+
permissionsToScopes,
|
|
33552
33716
|
personalServerDataReadPath,
|
|
33553
33717
|
personalServerRegistrationDomain,
|
|
33554
33718
|
readPersonalServerData,
|
|
@@ -33562,6 +33726,7 @@ export {
|
|
|
33562
33726
|
signPersonalServerLiteOwnerBinding,
|
|
33563
33727
|
signPersonalServerLiteOwnerBindingWithAccountClient,
|
|
33564
33728
|
signPersonalServerRegistrationWithAccount,
|
|
33729
|
+
tryGrantPermissions,
|
|
33565
33730
|
vanaMainnet2 as vanaMainnet,
|
|
33566
33731
|
verifyGrantRegistration,
|
|
33567
33732
|
verifyPkceChallenge,
|