@rhinestone/1auth 0.6.9 → 0.7.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.
@@ -1,10 +1,11 @@
1
- import { P as PasskeyProviderConfig, S as SponsorshipConfig, aa as ThemeConfig, a6 as GetAssetsOptions, a9 as AssetsResponse, v as AuthWithModalOptions, A as AuthResult, L as LoginWithModalOptions, w as CreateAccountWithModalOptions, y as ConnectResult, ad as CheckConsentOptions, ae as CheckConsentResult, af as RequestConsentOptions, ag as RequestConsentResult, ah as GrantPermissionsOptions, ai as GrantPermissionsResult, aj as ListSessionGrantsOptions, ak as ListSessionGrantsResult, z as AuthenticateOptions, B as AuthenticateResult, m as SigningRequestOptions, n as SigningResult, Y as SendIntentOptions, i as SendIntentResult, aq as SendBatchIntentOptions, ar as SendBatchIntentResult, a3 as IntentHistoryOptions, a5 as IntentHistoryResult, F as SignMessageOptions, G as SignMessageResult, J as SignTypedDataOptions, K as SignTypedDataResult, E as EmbedOptions, t as PasskeyCredential } from './types-1BMD1PSH.mjs';
1
+ import { P as PasskeyProviderConfig, S as SponsorshipConfig, aa as ThemeConfig, a6 as GetAssetsOptions, a9 as AssetsResponse, v as AuthWithModalOptions, A as AuthResult, L as LoginWithModalOptions, w as CreateAccountWithModalOptions, y as ConnectResult, ad as CheckConsentOptions, ae as CheckConsentResult, af as RequestConsentOptions, ag as RequestConsentResult, ah as GrantPermissionsOptions, ai as GrantPermissionsResult, aj as ListSessionGrantsOptions, ak as ListSessionGrantsResult, z as AuthenticateOptions, B as AuthenticateResult, m as SigningRequestOptions, n as SigningResult, Y as SendIntentOptions, i as SendIntentResult, aq as SendBatchIntentOptions, ar as SendBatchIntentResult, a3 as IntentHistoryOptions, a5 as IntentHistoryResult, F as SignMessageOptions, G as SignMessageResult, J as SignTypedDataOptions, K as SignTypedDataResult, E as EmbedOptions, t as PasskeyCredential } from './types-U_dwxbtS.mjs';
2
2
 
3
3
  declare class OneAuthClient {
4
4
  private config;
5
5
  private theme;
6
6
  private sponsorship;
7
7
  private eoaDialogState;
8
+ private prewarmState;
8
9
  private eoaSerialQueue;
9
10
  /**
10
11
  * Create a new OneAuthClient.
@@ -586,6 +587,52 @@ declare class OneAuthClient {
586
587
  * @param url - Any URL whose origin should be preconnected to.
587
588
  */
588
589
  private injectPreconnect;
590
+ /**
591
+ * Warm the dialog ahead of the first user interaction.
592
+ *
593
+ * `preconnect` (run in the constructor) only warms DNS/TLS. This goes
594
+ * further: it loads the dialog into a hidden, off-screen iframe so the
595
+ * browser downloads and parses the dialog HTML + JS bundle and the font, and
596
+ * keeps the cross-origin connection alive. When the user later opens a real
597
+ * auth/sign dialog, the fresh iframe serves those bytes from cache over the
598
+ * warm connection and paints far sooner — so the SDK preload overlay is shown
599
+ * only briefly (or imperceptibly) instead of covering a full cold load.
600
+ *
601
+ * It loads a dedicated `/dialog/warm` route, NOT a real flow route. That
602
+ * matters for two reasons:
603
+ * - **No side effects.** The real `/dialog/auth` route runs the signup
604
+ * flow on mount (identity probe → `POST /api/auth/register?step=start`),
605
+ * which writes an `AuthChallenge` row and counts against the register
606
+ * rate limiter. Warming must never create backend auth state for a user
607
+ * who hasn't acted, so the warm route renders nothing and runs no flow.
608
+ * - **No message cross-talk.** The warm route posts no `PASSKEY_READY` /
609
+ * `PASSKEY_RENDERED`, so a slow warm can never satisfy a visible modal's
610
+ * readiness listener and drive it to `PASSKEY_INIT` at the wrong moment.
611
+ * The warm route still pulls the shared Next.js framework + main + dialog
612
+ * layout chunks (the bulk of every flow's bundle), so the real open is fast.
613
+ *
614
+ * Best called on a *likely-intent* signal — `pointerenter`/`focus` of your
615
+ * "Sign in" / "Pay" button — rather than on page load, so visitors who never
616
+ * authenticate don't pay for a cross-origin iframe. Pass `prewarm: true` in
617
+ * the client config to have the SDK schedule this once on an idle callback.
618
+ *
619
+ * Idempotent: repeated calls return the same in-flight/settled promise.
620
+ * Resolves `true` once the hidden iframe's document has loaded, `false` on
621
+ * timeout or failure. Never throws and never blocks a real dialog open — a
622
+ * failed prewarm just means the next open does a normal (cold) load.
623
+ *
624
+ * @returns Whether the dialog became warm.
625
+ */
626
+ prewarm(): Promise<boolean>;
627
+ /**
628
+ * Tear down the hidden prewarm iframe created by {@link prewarm}.
629
+ *
630
+ * The HTTP cache that prewarm populated survives teardown, so a subsequent
631
+ * open is still bundle-warm; this only releases the parked frame (and its
632
+ * keep-alive connection). Call it when auth is no longer likely on the
633
+ * current view. Safe to call when nothing is warmed.
634
+ */
635
+ destroyPrewarm(): void;
589
636
  /**
590
637
  * Wait for the dialog iframe to signal ready, but defer sending `PASSKEY_INIT`
591
638
  * until the caller decides the time is right.
@@ -894,17 +941,17 @@ declare class OneAuthClient {
894
941
  /**
895
942
  * Create and open a full-viewport `<dialog>` containing a passkey iframe.
896
943
  *
897
- * The SDK owns only the hidden outer shell; all visible UI (backdrop,
898
- * card, animations, close button) is rendered by the passkey app inside the
899
- * iframe. This approach means design changes can be shipped server-side
900
- * without updating SDK consumers.
944
+ * The SDK owns the browser `<dialog>` plus a minimal generic preload shell
945
+ * that appears immediately on click. The passkey iframe owns all branded and
946
+ * origin-specific UI (TitleBar, trust icon, action cards); the shell is
947
+ * removed as soon as the iframe reports that its centered card has painted.
901
948
  *
902
949
  * Lifecycle:
903
- * 1. A themed "Loading…" overlay is injected and shown immediately while the
904
- * iframe loads, matching the exact visual structure of the passkey app's
905
- * TitleBar + IndeterminateLoader so the transition is seamless.
906
- * 2. When the iframe sends `PASSKEY_RENDERED`, the overlay fades out and
907
- * the iframe becomes fully visible.
950
+ * 1. A themed generic preload shell is injected and shown immediately
951
+ * while the iframe loads. It intentionally contains no TitleBar or
952
+ * origin chrome so that UI has a single implementation in apps/passkey.
953
+ * 2. When the iframe sends `PASSKEY_RENDERED`, the overlay is removed and
954
+ * the iframe becomes fully visible in the same frame.
908
955
  * 3. The returned `cleanup` function tears down all event listeners,
909
956
  * disconnects the MutationObserver, closes the `<dialog>`, and removes
910
957
  * it from the DOM. It is idempotent — safe to call multiple times.
@@ -1,7 +1,7 @@
1
- import { P as PasskeyProviderConfig, S as SponsorshipConfig, H as HeadlessIntentOptions, a as HeadlessPrepareResult, b as HeadlessSubmitOptions, c as HeadlessSubmitResult, d as HeadlessIntentStatusResult, I as InstallSmartSessionOptions, e as InstallSmartSessionResult } from './types-1BMD1PSH.mjs';
2
- export { f as SessionKeyHandle, g as SmartSessionEnableRequest } from './types-1BMD1PSH.mjs';
3
- import 'viem';
4
- import '@rhinestone/sdk';
1
+ import { P as PasskeyProviderConfig, S as SponsorshipConfig, H as HeadlessIntentOptions, a as HeadlessPrepareResult, b as HeadlessSubmitOptions, c as HeadlessSubmitResult, d as HeadlessIntentStatusResult, I as InstallSmartSessionOptions, e as InstallSmartSessionResult, f as SessionKeyHandle } from './types-U_dwxbtS.mjs';
2
+ export { g as SmartSessionEnableRequest } from './types-U_dwxbtS.mjs';
3
+ import * as _rhinestone_sdk from '@rhinestone/sdk';
4
+ import { Hex, Address } from 'viem';
5
5
 
6
6
  /**
7
7
  * Headless 1auth client.
@@ -87,4 +87,23 @@ declare class OneAuthHeadlessClient {
87
87
  installSmartSession(options: InstallSmartSessionOptions): Promise<InstallSmartSessionResult>;
88
88
  }
89
89
 
90
- export { HeadlessIntentOptions, HeadlessIntentStatusResult, HeadlessPrepareResult, HeadlessSubmitOptions, HeadlessSubmitResult, InstallSmartSessionOptions, InstallSmartSessionResult, OneAuthHeadlessClient };
90
+ interface BuildSmartSessionHeadlessSignaturesOptions {
91
+ /** ECDSA private key for the locally-held SmartSession signer. */
92
+ privateKey: Hex;
93
+ /** Smart account address the SmartSession is scoped to. */
94
+ accountAddress: Address;
95
+ /** Persisted handle returned by `grantPermissions` / SmartSession install. */
96
+ sessionKeyHandle: SessionKeyHandle;
97
+ /** Prepared headless intent returned by `OneAuthHeadlessClient.prepareIntent`. */
98
+ prepared: HeadlessPrepareResult;
99
+ }
100
+ /**
101
+ * Builds SmartSession signatures for `OneAuthHeadlessClient.submitIntent`.
102
+ *
103
+ * This intentionally delegates signing and SmartSession signature packing to
104
+ * `@rhinestone/sdk`. 1auth only rebuilds the persisted session descriptor and
105
+ * supplies the SDK-native `SignData` returned by the orchestrator quote.
106
+ */
107
+ declare function buildSmartSessionHeadlessSignatures(options: BuildSmartSessionHeadlessSignaturesOptions): Promise<_rhinestone_sdk.SignedIntentData>;
108
+
109
+ export { type BuildSmartSessionHeadlessSignaturesOptions, HeadlessIntentOptions, HeadlessIntentStatusResult, HeadlessPrepareResult, HeadlessSubmitOptions, HeadlessSubmitResult, InstallSmartSessionOptions, InstallSmartSessionResult, OneAuthHeadlessClient, SessionKeyHandle, buildSmartSessionHeadlessSignatures };
@@ -1,7 +1,7 @@
1
- import { P as PasskeyProviderConfig, S as SponsorshipConfig, H as HeadlessIntentOptions, a as HeadlessPrepareResult, b as HeadlessSubmitOptions, c as HeadlessSubmitResult, d as HeadlessIntentStatusResult, I as InstallSmartSessionOptions, e as InstallSmartSessionResult } from './types-1BMD1PSH.js';
2
- export { f as SessionKeyHandle, g as SmartSessionEnableRequest } from './types-1BMD1PSH.js';
3
- import 'viem';
4
- import '@rhinestone/sdk';
1
+ import { P as PasskeyProviderConfig, S as SponsorshipConfig, H as HeadlessIntentOptions, a as HeadlessPrepareResult, b as HeadlessSubmitOptions, c as HeadlessSubmitResult, d as HeadlessIntentStatusResult, I as InstallSmartSessionOptions, e as InstallSmartSessionResult, f as SessionKeyHandle } from './types-U_dwxbtS.js';
2
+ export { g as SmartSessionEnableRequest } from './types-U_dwxbtS.js';
3
+ import * as _rhinestone_sdk from '@rhinestone/sdk';
4
+ import { Hex, Address } from 'viem';
5
5
 
6
6
  /**
7
7
  * Headless 1auth client.
@@ -87,4 +87,23 @@ declare class OneAuthHeadlessClient {
87
87
  installSmartSession(options: InstallSmartSessionOptions): Promise<InstallSmartSessionResult>;
88
88
  }
89
89
 
90
- export { HeadlessIntentOptions, HeadlessIntentStatusResult, HeadlessPrepareResult, HeadlessSubmitOptions, HeadlessSubmitResult, InstallSmartSessionOptions, InstallSmartSessionResult, OneAuthHeadlessClient };
90
+ interface BuildSmartSessionHeadlessSignaturesOptions {
91
+ /** ECDSA private key for the locally-held SmartSession signer. */
92
+ privateKey: Hex;
93
+ /** Smart account address the SmartSession is scoped to. */
94
+ accountAddress: Address;
95
+ /** Persisted handle returned by `grantPermissions` / SmartSession install. */
96
+ sessionKeyHandle: SessionKeyHandle;
97
+ /** Prepared headless intent returned by `OneAuthHeadlessClient.prepareIntent`. */
98
+ prepared: HeadlessPrepareResult;
99
+ }
100
+ /**
101
+ * Builds SmartSession signatures for `OneAuthHeadlessClient.submitIntent`.
102
+ *
103
+ * This intentionally delegates signing and SmartSession signature packing to
104
+ * `@rhinestone/sdk`. 1auth only rebuilds the persisted session descriptor and
105
+ * supplies the SDK-native `SignData` returned by the orchestrator quote.
106
+ */
107
+ declare function buildSmartSessionHeadlessSignatures(options: BuildSmartSessionHeadlessSignaturesOptions): Promise<_rhinestone_sdk.SignedIntentData>;
108
+
109
+ export { type BuildSmartSessionHeadlessSignaturesOptions, HeadlessIntentOptions, HeadlessIntentStatusResult, HeadlessPrepareResult, HeadlessSubmitOptions, HeadlessSubmitResult, InstallSmartSessionOptions, InstallSmartSessionResult, OneAuthHeadlessClient, SessionKeyHandle, buildSmartSessionHeadlessSignatures };
package/dist/headless.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,12 +17,21 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/headless.ts
21
31
  var headless_exports = {};
22
32
  __export(headless_exports, {
23
- OneAuthHeadlessClient: () => OneAuthHeadlessClient
33
+ OneAuthHeadlessClient: () => OneAuthHeadlessClient,
34
+ buildSmartSessionHeadlessSignatures: () => buildSmartSessionHeadlessSignatures
24
35
  });
25
36
  module.exports = __toCommonJS(headless_exports);
26
37
 
@@ -320,8 +331,137 @@ var OneAuthHeadlessClient = class {
320
331
  return await response.json();
321
332
  }
322
333
  };
334
+
335
+ // src/smart-session-signatures.ts
336
+ var import_accounts = require("viem/accounts");
337
+ var import_sdk = require("@rhinestone/sdk");
338
+ var import_smart_sessions = require("@rhinestone/sdk/smart-sessions");
339
+
340
+ // src/registry.ts
341
+ var import_viem = require("viem");
342
+ var viemChains = __toESM(require("viem/chains"));
343
+ var import_shared_configs = require("@rhinestone/shared-configs");
344
+ var env = typeof process !== "undefined" ? process.env : {};
345
+ var VIEM_CHAIN_BY_ID = /* @__PURE__ */ new Map();
346
+ for (const value of Object.values(viemChains)) {
347
+ if (typeof value !== "object" || value === null || !("id" in value) || !("name" in value)) continue;
348
+ const chain = value;
349
+ const existing = VIEM_CHAIN_BY_ID.get(chain.id);
350
+ if (!existing || existing.testnet && !chain.testnet) {
351
+ VIEM_CHAIN_BY_ID.set(chain.id, chain);
352
+ }
353
+ }
354
+ function isEvmRegistryEntry(entry) {
355
+ return entry?.vmType === "evm";
356
+ }
357
+ var REGISTRY_CHAIN_IDS = Object.entries(import_shared_configs.chainRegistry).filter(([, entry]) => isEvmRegistryEntry(entry)).map(([id]) => Number(id));
358
+ var SUPPORTED_CHAIN_IDS = new Set(REGISTRY_CHAIN_IDS);
359
+ function getChainById(chainId) {
360
+ if (!SUPPORTED_CHAIN_IDS.has(chainId)) {
361
+ throw new Error(`Unsupported chain ID: ${chainId}`);
362
+ }
363
+ const chain = VIEM_CHAIN_BY_ID.get(chainId);
364
+ if (!chain) {
365
+ throw new Error(`Unsupported chain ID: ${chainId}`);
366
+ }
367
+ return chain;
368
+ }
369
+
370
+ // src/smart-session-signatures.ts
371
+ function reviveBigIntStrings(value) {
372
+ if (Array.isArray(value)) return value.map((entry) => reviveBigIntStrings(entry));
373
+ if (value && typeof value === "object") {
374
+ return Object.fromEntries(
375
+ Object.entries(value).map(([key, entry]) => [key, reviveBigIntStrings(entry)])
376
+ );
377
+ }
378
+ if (typeof value === "string" && value.startsWith("__bigint:")) {
379
+ return BigInt(value.slice("__bigint:".length));
380
+ }
381
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
382
+ return value;
383
+ }
384
+ function parsePreparedSignData(value) {
385
+ const prepared = JSON.parse(value, (_key, nested) => reviveBigIntStrings(nested));
386
+ const signData = prepared.quotes?.best?.signData;
387
+ if (!signData) {
388
+ throw new Error("Prepared headless intent is missing quotes.best.signData");
389
+ }
390
+ return signData;
391
+ }
392
+ function chainIdFromSignDataDomain(domainChainId) {
393
+ if (typeof domainChainId === "number" && Number.isSafeInteger(domainChainId)) {
394
+ return domainChainId;
395
+ }
396
+ if (typeof domainChainId === "bigint") return Number(domainChainId);
397
+ if (typeof domainChainId === "string" && /^\d+$/.test(domainChainId)) {
398
+ return Number(domainChainId);
399
+ }
400
+ return void 0;
401
+ }
402
+ function buildSessionForChain(args) {
403
+ return (0, import_smart_sessions.toSession)(
404
+ {
405
+ chain: getChainById(args.chainId),
406
+ owners: { type: "ecdsa", accounts: [(0, import_accounts.privateKeyToAccount)(args.privateKey)] },
407
+ permissions: reviveBigIntStrings(args.handle.permissions ?? []),
408
+ crossChainPermits: reviveBigIntStrings(args.handle.crossChainPermits ?? [])
409
+ },
410
+ // The production SmartSession module is deployed on testnets too. Never opt
411
+ // into SDK dev contracts for 1auth headless permissions.
412
+ { useDevContracts: false }
413
+ );
414
+ }
415
+ function buildSessionSigners(args) {
416
+ const chainIds = /* @__PURE__ */ new Set([args.prepared.targetChain]);
417
+ for (const typedData of args.signData.origin) {
418
+ const chainId = chainIdFromSignDataDomain(typedData.domain?.chainId);
419
+ if (chainId) chainIds.add(chainId);
420
+ }
421
+ const targetExecutionChainId = chainIdFromSignDataDomain(
422
+ args.signData.targetExecution?.domain?.chainId
423
+ );
424
+ if (targetExecutionChainId) chainIds.add(targetExecutionChainId);
425
+ return {
426
+ type: "experimental_session",
427
+ sessions: Object.fromEntries(
428
+ [...chainIds].map((chainId) => [
429
+ chainId,
430
+ {
431
+ session: buildSessionForChain({
432
+ privateKey: args.privateKey,
433
+ handle: args.handle,
434
+ chainId
435
+ })
436
+ }
437
+ ])
438
+ )
439
+ };
440
+ }
441
+ async function buildSmartSessionHeadlessSignatures(options) {
442
+ const signData = parsePreparedSignData(options.prepared.intentOp);
443
+ const account = await (0, import_sdk.createRhinestoneAccount)({
444
+ initData: { address: options.accountAddress },
445
+ owners: {
446
+ type: "ecdsa",
447
+ accounts: [(0, import_accounts.privateKeyToAccount)(options.privateKey)]
448
+ },
449
+ experimental_sessions: { enabled: true }
450
+ });
451
+ return account.signIntent(
452
+ signData,
453
+ getChainById(options.prepared.targetChain),
454
+ buildSessionSigners({
455
+ privateKey: options.privateKey,
456
+ handle: options.sessionKeyHandle,
457
+ prepared: options.prepared,
458
+ signData
459
+ })
460
+ );
461
+ }
323
462
  // Annotate the CommonJS export names for ESM import in node:
324
463
  0 && (module.exports = {
325
- OneAuthHeadlessClient
464
+ OneAuthHeadlessClient,
465
+ buildSmartSessionHeadlessSignatures
326
466
  });
327
467
  //# sourceMappingURL=headless.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/headless.ts","../src/sponsorship-fetch.ts","../src/headless-client.ts"],"sourcesContent":["/**\n * Public entry point for the headless 1auth SDK.\n *\n * Exposes {@link OneAuthHeadlessClient} plus the request/response types\n * an integrating app needs when using its own ERC-7579 validator (e.g.\n * the experimental SmartSession from `@rhinestone/sdk`) to sign intents\n * locally instead of going through the passkey dialog.\n */\n\nexport { OneAuthHeadlessClient } from \"./headless-client\";\n\nexport type {\n HeadlessIntentStatusResult,\n HeadlessIntentOptions,\n HeadlessPrepareResult,\n HeadlessSubmitOptions,\n HeadlessSubmitResult,\n InstallSmartSessionOptions,\n InstallSmartSessionResult,\n SessionKeyHandle,\n SmartSessionEnableRequest,\n} from \"./types\";\n","/**\n * Shared helpers for fetching sponsorship tokens (access + extension) from an\n * integrator's backend endpoints.\n *\n * The integrator's token routes return their failure reason in the JSON body\n * (`{ error: \"createSponsorshipSigner: missing env var(s): ...\" }`). Surfacing\n * only the HTTP status — \"Failed to fetch access token (500 ...)\" — hides that\n * reason and turns every misconfiguration into the same opaque dialog error.\n * These helpers read the body and fold the server's message into the thrown\n * Error so the actual cause (missing env var, bad JWK, expired session) is\n * visible at the call site and in the dialog.\n */\n\n/**\n * Pull a human-readable reason out of a failed token response. Tries the JSON\n * `error`/`message` fields first, then falls back to a short text snippet, then\n * to the HTTP status text. Never throws — diagnostics must not mask the original\n * failure with a parse error.\n */\nasync function extractServerReason(response: Response): Promise<string> {\n try {\n // Clone so a JSON parse failure can still fall back to reading text.\n const text = await response.clone().text();\n if (!text) return response.statusText;\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const reason = data.error ?? data.message;\n if (typeof reason === \"string\" && reason.trim()) return reason;\n } catch {\n // Body wasn't JSON — fall through to the raw text snippet.\n }\n // Cap the snippet so an HTML error page doesn't flood the message.\n return text.slice(0, 200);\n } catch {\n return response.statusText;\n }\n}\n\n/**\n * Build an Error for a failed token fetch that includes the server's reason.\n * `label` names the token kind, e.g. \"access token\" or \"extension token\".\n */\nexport async function tokenFetchError(\n label: string,\n response: Response,\n): Promise<Error> {\n const reason = await extractServerReason(response);\n const suffix = reason ? `: ${reason}` : \"\";\n return new Error(\n `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`,\n );\n}\n","/**\n * Headless 1auth client.\n *\n * Used by integrators that hold their own ECDSA signer in localStorage\n * and produce signatures for an ERC-7579 validator other than the\n * passkey one (typically `@rhinestone/sdk` experimental SmartSession).\n *\n * Responsibilities:\n * - Fetch sponsorship JWTs from the app's own backend.\n * - Call `/api/intent/prepare?signingMode=headless` to get an\n * orchestrator-quoted `intentOp` + `digestResult`.\n * - Forward pre-encoded validator-prefixed signatures to the new\n * `/api/intent/headless-execute` route.\n * - Drive the existing passkey dialog (via {@link OneAuthClient}) for\n * the one-time SmartSession install ceremony.\n *\n * 1auth itself stays ignorant of session keys — the on-chain\n * SmartSession validator is the only authority over what a session key\n * may sign for. The host app persists `{ sessionKeyAddress, permissionId,\n * accountAddress, permissions }` in its own storage.\n */\n\nimport type {\n HeadlessIntentOptions,\n HeadlessIntentStatusResult,\n HeadlessPrepareResult,\n HeadlessSubmitOptions,\n HeadlessSubmitResult,\n InstallSmartSessionOptions,\n InstallSmartSessionResult,\n IntentTokenRequest,\n PasskeyProviderConfig,\n SponsorshipCallbackConfig,\n SponsorshipConfig,\n} from \"./types\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\n\nconst DEFAULT_PROVIDER_URL = \"https://passkey.1auth.box\";\n\nconst MISSING_APP_CREDENTIALS_MESSAGE =\n \"No sponsorship configured on OneAuthHeadlessClient. Every user intent must carry the app's JWT — pass `sponsorship: { accessToken, getExtensionToken }` (or URL form) to the constructor.\";\n\ntype AccessTokenFetchResult =\n | { ok: true; accessToken: string }\n | { ok: false; code?: \"MISSING_APP_CREDENTIALS\"; message: string };\n\ntype ExtensionTokenFetchResult =\n | { ok: true; extensionToken: string | undefined }\n | { ok: false; message: string };\n\n/**\n * Mirrors `normalizeSponsorship` in `client.ts`. Kept local so the\n * headless entry point doesn't pull in the whole OneAuthClient module\n * graph for callers that only need the headless surface.\n */\nfunction normalizeSponsorship(\n config: SponsorshipConfig | undefined,\n): SponsorshipCallbackConfig | null {\n if (!config) return null;\n if (\"accessToken\" in config && \"getExtensionToken\" in config) {\n return config;\n }\n const { accessTokenUrl, extensionTokenUrl } = config;\n return {\n accessToken: async () => {\n const response = await fetch(accessTokenUrl, { credentials: \"include\" });\n if (!response.ok) {\n throw await tokenFetchError(\"access token\", response);\n }\n const data = (await response.json()) as { token?: string };\n if (!data.token) {\n throw new Error(\"Access token response missing `token` field\");\n }\n return data.token;\n },\n getExtensionToken: async (intentOp: string) => {\n const response = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (!response.ok) {\n throw await tokenFetchError(\"extension token\", response);\n }\n const data = (await response.json()) as { token?: string };\n if (!data.token) {\n throw new Error(\"Extension token response missing `token` field\");\n }\n return data.token;\n },\n };\n}\n\nexport class OneAuthHeadlessClient {\n private providerUrl: string;\n private clientId?: string;\n private sponsorship: SponsorshipCallbackConfig | null;\n\n constructor(config: PasskeyProviderConfig) {\n this.providerUrl = config.providerUrl || DEFAULT_PROVIDER_URL;\n this.clientId = config.clientId;\n this.sponsorship = normalizeSponsorship(config.sponsorship);\n }\n\n setSponsorship(sponsorship: SponsorshipConfig | undefined): void {\n this.sponsorship = normalizeSponsorship(sponsorship);\n }\n\n // ---------------------------------------------------------------------------\n // Sponsorship JWT fetchers\n // ---------------------------------------------------------------------------\n\n private async fetchAccessToken(): Promise<AccessTokenFetchResult> {\n if (!this.sponsorship) {\n return {\n ok: false,\n code: \"MISSING_APP_CREDENTIALS\",\n message: MISSING_APP_CREDENTIALS_MESSAGE,\n };\n }\n try {\n const accessToken = await this.sponsorship.accessToken();\n return { ok: true, accessToken };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n private async fetchExtensionToken(\n intentOp: string,\n sponsor: boolean,\n ): Promise<ExtensionTokenFetchResult> {\n if (!sponsor) return { ok: true, extensionToken: undefined };\n if (!this.sponsorship) {\n return { ok: false, message: MISSING_APP_CREDENTIALS_MESSAGE };\n }\n try {\n const token = await this.sponsorship.getExtensionToken(intentOp);\n return { ok: true, extensionToken: token };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Intent prepare / submit\n // ---------------------------------------------------------------------------\n\n /**\n * Quote an intent without opening any dialog. Returns the orchestrator's\n * `intentOp` plus the digest the caller must sign with their own\n * validator (e.g. SmartSession). The caller is responsible for\n * encoding signatures and submitting via {@link submitIntent}.\n */\n async prepareIntent(options: HeadlessIntentOptions): Promise<HeadlessPrepareResult> {\n if (!options.username && !options.accountAddress) {\n throw new Error(\"prepareIntent requires either `username` or `accountAddress`\");\n }\n\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(`Sponsorship access token fetch failed: ${accessTokenResult.message}`);\n }\n\n // bigint → string conversion for token requests; matches what the\n // dialog flow does before serializing requestBody.\n const tokenRequests = options.tokenRequests?.map((tr: IntentTokenRequest) => ({\n token: tr.token,\n amount: tr.amount.toString(),\n }));\n\n const body = {\n username: options.username,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n calls: options.calls,\n tokenRequests,\n sourceAssets: options.sourceAssets,\n sourceChainId: options.sourceChainId,\n sessionKeyHandle: options.sessionKeyHandle,\n clientId: this.clientId,\n signingMode: \"headless\" as const,\n auth: { accessToken: accessTokenResult.accessToken },\n };\n\n const response = await fetch(`${this.providerUrl}/api/intent/prepare`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { error?: string };\n throw new Error(errorData.error || `Prepare failed (${response.status})`);\n }\n\n return (await response.json()) as HeadlessPrepareResult;\n }\n\n /**\n * Forward a pre-signed intent to the orchestrator. The caller has\n * already encoded validator-prefixed origin + destination signatures\n * (one origin signature per intent element); 1auth treats them as\n * opaque bytes.\n *\n * If `sponsor` is omitted or `true`, an extension token is fetched\n * just-in-time and bound to the intent. Pass `sponsor: false` for\n * self-paying intents.\n */\n async submitIntent(options: HeadlessSubmitOptions): Promise<HeadlessSubmitResult> {\n const sponsor = options.sponsor ?? true;\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(`Sponsorship access token fetch failed: ${accessTokenResult.message}`);\n }\n\n const extensionResult = await this.fetchExtensionToken(options.intentOp, sponsor);\n if (!extensionResult.ok) {\n throw new Error(`Sponsorship extension token fetch failed: ${extensionResult.message}`);\n }\n\n const body = {\n intentOp: options.intentOp,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n calls: options.calls,\n expiresAt: options.expiresAt,\n originSignatures: options.originSignatures,\n destinationSignature: options.destinationSignature,\n targetExecutionSignature: options.targetExecutionSignature,\n auth: {\n accessToken: accessTokenResult.accessToken,\n ...(extensionResult.extensionToken && { extensionToken: extensionResult.extensionToken }),\n },\n };\n\n const response = await fetch(`${this.providerUrl}/api/intent/headless-execute`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { error?: string };\n throw new Error(errorData.error || `Headless execute failed (${response.status})`);\n }\n\n return (await response.json()) as HeadlessSubmitResult;\n }\n\n // ---------------------------------------------------------------------------\n // Status / wait — proxies to the existing routes (no headless variant needed).\n // ---------------------------------------------------------------------------\n\n /**\n * Fetch the latest known status for a submitted headless intent.\n * This is a non-blocking read and may return `pending` before a\n * solver has produced a fill transaction hash.\n */\n async getIntentStatus(intentId: string): Promise<HeadlessIntentStatusResult> {\n const response = await fetch(`${this.providerUrl}/api/intent/status/${intentId}`);\n if (!response.ok) {\n throw new Error(`Status fetch failed (${response.status})`);\n }\n return (await response.json()) as HeadlessIntentStatusResult;\n }\n\n /**\n * Wait for a submitted headless intent to produce a status update and,\n * when available, the final fill transaction hash. The wait endpoint\n * needs the opaque `transactionResult` returned by submit because 1auth\n * does not persist that SDK object in its database.\n */\n async waitForIntent(\n intentId: string,\n transactionResult: unknown,\n ): Promise<HeadlessIntentStatusResult> {\n if (!transactionResult) {\n throw new Error(\"waitForIntent requires submitResult.transactionResult\");\n }\n\n const response = await fetch(`${this.providerUrl}/api/intent/wait/${intentId}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ transactionResult }),\n });\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n details?: string;\n };\n throw new Error(\n errorData.details || errorData.error || `Wait fetch failed (${response.status})`,\n );\n }\n return (await response.json()) as HeadlessIntentStatusResult;\n }\n\n // ---------------------------------------------------------------------------\n // SmartSession install (one-time, passkey-signed)\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the SmartSession install call(s) for `sessionKeyAddress`\n * with the supplied `permissions`.\n *\n * Returns `{ install, sessionKeyHandle }`:\n *\n * - `install` is a `{ targetChain, calls }` pair the caller feeds\n * straight into `OneAuthClient.sendIntent({ targetChain, calls })`\n * so the user passkey-signs the install via the existing dialog.\n * - `sessionKeyHandle` contains the deterministic `permissionId`\n * plus metadata to persist in localStorage. Persist this BEFORE\n * submitting the install so a refresh during signing doesn't\n * lose the handle.\n *\n * If the validator is already installed on the user's account,\n * `install.calls` is empty and `install.alreadyInstalled` is true.\n * The caller should still persist `sessionKeyHandle` — the on-chain\n * session enabling happens at first headless use via SmartSession's\n * ENABLE-mode signature wrapping (see follow-up).\n */\n async installSmartSession(\n options: InstallSmartSessionOptions,\n ): Promise<InstallSmartSessionResult> {\n if (!options.username && !options.accountAddress) {\n throw new Error(\n \"installSmartSession requires either `username` or `accountAddress`\",\n );\n }\n\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(\n `Sponsorship access token fetch failed: ${accessTokenResult.message}`,\n );\n }\n\n const body = {\n username: options.username,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n sessionKeyAddress: options.sessionKeyAddress,\n permissions: options.permissions,\n validUntil: options.validUntil,\n validAfter: options.validAfter,\n maxUses: options.maxUses,\n enableSessionSignature: options.enableSessionSignature,\n label: options.label,\n auth: { accessToken: accessTokenResult.accessToken },\n };\n\n const response = await fetch(`${this.providerUrl}/api/sessions/install`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(errorData.error || `Install failed (${response.status})`);\n }\n\n return (await response.json()) as InstallSmartSessionResult;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBA,eAAe,oBAAoB,UAAqC;AACtE,MAAI;AAEF,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,QAAI,CAAC,KAAM,QAAO,SAAS;AAC3B,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,YAAM,SAAS,KAAK,SAAS,KAAK;AAClC,UAAI,OAAO,WAAW,YAAY,OAAO,KAAK,EAAG,QAAO;AAAA,IAC1D,QAAQ;AAAA,IAER;AAEA,WAAO,KAAK,MAAM,GAAG,GAAG;AAAA,EAC1B,QAAQ;AACN,WAAO,SAAS;AAAA,EAClB;AACF;AAMA,eAAsB,gBACpB,OACA,UACgB;AAChB,QAAM,SAAS,MAAM,oBAAoB,QAAQ;AACjD,QAAM,SAAS,SAAS,KAAK,MAAM,KAAK;AACxC,SAAO,IAAI;AAAA,IACT,mBAAmB,KAAK,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,IAAI,MAAM;AAAA,EAC/E;AACF;;;ACdA,IAAM,uBAAuB;AAE7B,IAAM,kCACJ;AAeF,SAAS,qBACP,QACkC;AAClC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,iBAAiB,UAAU,uBAAuB,QAAQ;AAC5D,WAAO;AAAA,EACT;AACA,QAAM,EAAE,gBAAgB,kBAAkB,IAAI;AAC9C,SAAO;AAAA,IACL,aAAa,YAAY;AACvB,YAAM,WAAW,MAAM,MAAM,gBAAgB,EAAE,aAAa,UAAU,CAAC;AACvE,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,MAAM,gBAAgB,gBAAgB,QAAQ;AAAA,MACtD;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAI,CAAC,KAAK,OAAO;AACf,cAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AACA,aAAO,KAAK;AAAA,IACd;AAAA,IACA,mBAAmB,OAAO,aAAqB;AAC7C,YAAM,WAAW,MAAM,MAAM,mBAAmB;AAAA,QAC9C,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,MACnC,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,MAAM,gBAAgB,mBAAmB,QAAQ;AAAA,MACzD;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAI,CAAC,KAAK,OAAO;AACf,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;AAEO,IAAM,wBAAN,MAA4B;AAAA,EAKjC,YAAY,QAA+B;AACzC,SAAK,cAAc,OAAO,eAAe;AACzC,SAAK,WAAW,OAAO;AACvB,SAAK,cAAc,qBAAqB,OAAO,WAAW;AAAA,EAC5D;AAAA,EAEA,eAAe,aAAkD;AAC/D,SAAK,cAAc,qBAAqB,WAAW;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBAAoD;AAChE,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AACA,QAAI;AACF,YAAM,cAAc,MAAM,KAAK,YAAY,YAAY;AACvD,aAAO,EAAE,IAAI,MAAM,YAAY;AAAA,IACjC,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,oBACZ,UACA,SACoC;AACpC,QAAI,CAAC,QAAS,QAAO,EAAE,IAAI,MAAM,gBAAgB,OAAU;AAC3D,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO,EAAE,IAAI,OAAO,SAAS,gCAAgC;AAAA,IAC/D;AACA,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,YAAY,kBAAkB,QAAQ;AAC/D,aAAO,EAAE,IAAI,MAAM,gBAAgB,MAAM;AAAA,IAC3C,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,cAAc,SAAgE;AAClF,QAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,gBAAgB;AAChD,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAEA,UAAM,oBAAoB,MAAM,KAAK,iBAAiB;AACtD,QAAI,CAAC,kBAAkB,IAAI;AACzB,YAAM,IAAI,MAAM,0CAA0C,kBAAkB,OAAO,EAAE;AAAA,IACvF;AAIA,UAAM,gBAAgB,QAAQ,eAAe,IAAI,CAAC,QAA4B;AAAA,MAC5E,OAAO,GAAG;AAAA,MACV,QAAQ,GAAG,OAAO,SAAS;AAAA,IAC7B,EAAE;AAEF,UAAM,OAAO;AAAA,MACX,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf;AAAA,MACA,cAAc,QAAQ;AAAA,MACtB,eAAe,QAAQ;AAAA,MACvB,kBAAkB,QAAQ;AAAA,MAC1B,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,MAAM,EAAE,aAAa,kBAAkB,YAAY;AAAA,IACrD;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,uBAAuB;AAAA,MACrE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACzD,YAAM,IAAI,MAAM,UAAU,SAAS,mBAAmB,SAAS,MAAM,GAAG;AAAA,IAC1E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,aAAa,SAA+D;AAChF,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,oBAAoB,MAAM,KAAK,iBAAiB;AACtD,QAAI,CAAC,kBAAkB,IAAI;AACzB,YAAM,IAAI,MAAM,0CAA0C,kBAAkB,OAAO,EAAE;AAAA,IACvF;AAEA,UAAM,kBAAkB,MAAM,KAAK,oBAAoB,QAAQ,UAAU,OAAO;AAChF,QAAI,CAAC,gBAAgB,IAAI;AACvB,YAAM,IAAI,MAAM,6CAA6C,gBAAgB,OAAO,EAAE;AAAA,IACxF;AAEA,UAAM,OAAO;AAAA,MACX,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf,WAAW,QAAQ;AAAA,MACnB,kBAAkB,QAAQ;AAAA,MAC1B,sBAAsB,QAAQ;AAAA,MAC9B,0BAA0B,QAAQ;AAAA,MAClC,MAAM;AAAA,QACJ,aAAa,kBAAkB;AAAA,QAC/B,GAAI,gBAAgB,kBAAkB,EAAE,gBAAgB,gBAAgB,eAAe;AAAA,MACzF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,gCAAgC;AAAA,MAC9E,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACzD,YAAM,IAAI,MAAM,UAAU,SAAS,4BAA4B,SAAS,MAAM,GAAG;AAAA,IACnF;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,gBAAgB,UAAuD;AAC3E,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,sBAAsB,QAAQ,EAAE;AAChF,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,GAAG;AAAA,IAC5D;AACA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cACJ,UACA,mBACqC;AACrC,QAAI,CAAC,mBAAmB;AACtB,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,oBAAoB,QAAQ,IAAI;AAAA,MAC9E,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,kBAAkB,CAAC;AAAA,IAC5C,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAIzD,YAAM,IAAI;AAAA,QACR,UAAU,WAAW,UAAU,SAAS,sBAAsB,SAAS,MAAM;AAAA,MAC/E;AAAA,IACF;AACA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,oBACJ,SACoC;AACpC,QAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,gBAAgB;AAChD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,oBAAoB,MAAM,KAAK,iBAAiB;AACtD,QAAI,CAAC,kBAAkB,IAAI;AACzB,YAAM,IAAI;AAAA,QACR,0CAA0C,kBAAkB,OAAO;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,OAAO;AAAA,MACX,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,aAAa,QAAQ;AAAA,MACrB,mBAAmB,QAAQ;AAAA,MAC3B,aAAa,QAAQ;AAAA,MACrB,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,SAAS,QAAQ;AAAA,MACjB,wBAAwB,QAAQ;AAAA,MAChC,OAAO,QAAQ;AAAA,MACf,MAAM,EAAE,aAAa,kBAAkB,YAAY;AAAA,IACrD;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,yBAAyB;AAAA,MACvE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAGzD,YAAM,IAAI,MAAM,UAAU,SAAS,mBAAmB,SAAS,MAAM,GAAG;AAAA,IAC1E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/headless.ts","../src/sponsorship-fetch.ts","../src/headless-client.ts","../src/smart-session-signatures.ts","../src/registry.ts"],"sourcesContent":["/**\n * Public entry point for the headless 1auth SDK.\n *\n * Exposes {@link OneAuthHeadlessClient} plus the request/response types\n * an integrating app needs when using its own ERC-7579 validator (e.g.\n * the experimental SmartSession from `@rhinestone/sdk`) to sign intents\n * locally instead of going through the passkey dialog.\n */\n\nexport { OneAuthHeadlessClient } from \"./headless-client\";\nexport {\n buildSmartSessionHeadlessSignatures,\n type BuildSmartSessionHeadlessSignaturesOptions,\n} from \"./smart-session-signatures\";\n\nexport type {\n HeadlessIntentStatusResult,\n HeadlessIntentOptions,\n HeadlessPrepareResult,\n HeadlessSubmitOptions,\n HeadlessSubmitResult,\n InstallSmartSessionOptions,\n InstallSmartSessionResult,\n SessionKeyHandle,\n SmartSessionEnableRequest,\n} from \"./types\";\n","/**\n * Shared helpers for fetching sponsorship tokens (access + extension) from an\n * integrator's backend endpoints.\n *\n * The integrator's token routes return their failure reason in the JSON body\n * (`{ error: \"createSponsorshipSigner: missing env var(s): ...\" }`). Surfacing\n * only the HTTP status — \"Failed to fetch access token (500 ...)\" — hides that\n * reason and turns every misconfiguration into the same opaque dialog error.\n * These helpers read the body and fold the server's message into the thrown\n * Error so the actual cause (missing env var, bad JWK, expired session) is\n * visible at the call site and in the dialog.\n */\n\n/**\n * Pull a human-readable reason out of a failed token response. Tries the JSON\n * `error`/`message` fields first, then falls back to a short text snippet, then\n * to the HTTP status text. Never throws — diagnostics must not mask the original\n * failure with a parse error.\n */\nasync function extractServerReason(response: Response): Promise<string> {\n try {\n // Clone so a JSON parse failure can still fall back to reading text.\n const text = await response.clone().text();\n if (!text) return response.statusText;\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const reason = data.error ?? data.message;\n if (typeof reason === \"string\" && reason.trim()) return reason;\n } catch {\n // Body wasn't JSON — fall through to the raw text snippet.\n }\n // Cap the snippet so an HTML error page doesn't flood the message.\n return text.slice(0, 200);\n } catch {\n return response.statusText;\n }\n}\n\n/**\n * Build an Error for a failed token fetch that includes the server's reason.\n * `label` names the token kind, e.g. \"access token\" or \"extension token\".\n */\nexport async function tokenFetchError(\n label: string,\n response: Response,\n): Promise<Error> {\n const reason = await extractServerReason(response);\n const suffix = reason ? `: ${reason}` : \"\";\n return new Error(\n `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`,\n );\n}\n","/**\n * Headless 1auth client.\n *\n * Used by integrators that hold their own ECDSA signer in localStorage\n * and produce signatures for an ERC-7579 validator other than the\n * passkey one (typically `@rhinestone/sdk` experimental SmartSession).\n *\n * Responsibilities:\n * - Fetch sponsorship JWTs from the app's own backend.\n * - Call `/api/intent/prepare?signingMode=headless` to get an\n * orchestrator-quoted `intentOp` + `digestResult`.\n * - Forward pre-encoded validator-prefixed signatures to the new\n * `/api/intent/headless-execute` route.\n * - Drive the existing passkey dialog (via {@link OneAuthClient}) for\n * the one-time SmartSession install ceremony.\n *\n * 1auth itself stays ignorant of session keys — the on-chain\n * SmartSession validator is the only authority over what a session key\n * may sign for. The host app persists `{ sessionKeyAddress, permissionId,\n * accountAddress, permissions }` in its own storage.\n */\n\nimport type {\n HeadlessIntentOptions,\n HeadlessIntentStatusResult,\n HeadlessPrepareResult,\n HeadlessSubmitOptions,\n HeadlessSubmitResult,\n InstallSmartSessionOptions,\n InstallSmartSessionResult,\n IntentTokenRequest,\n PasskeyProviderConfig,\n SponsorshipCallbackConfig,\n SponsorshipConfig,\n} from \"./types\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\n\nconst DEFAULT_PROVIDER_URL = \"https://passkey.1auth.box\";\n\nconst MISSING_APP_CREDENTIALS_MESSAGE =\n \"No sponsorship configured on OneAuthHeadlessClient. Every user intent must carry the app's JWT — pass `sponsorship: { accessToken, getExtensionToken }` (or URL form) to the constructor.\";\n\ntype AccessTokenFetchResult =\n | { ok: true; accessToken: string }\n | { ok: false; code?: \"MISSING_APP_CREDENTIALS\"; message: string };\n\ntype ExtensionTokenFetchResult =\n | { ok: true; extensionToken: string | undefined }\n | { ok: false; message: string };\n\n/**\n * Mirrors `normalizeSponsorship` in `client.ts`. Kept local so the\n * headless entry point doesn't pull in the whole OneAuthClient module\n * graph for callers that only need the headless surface.\n */\nfunction normalizeSponsorship(\n config: SponsorshipConfig | undefined,\n): SponsorshipCallbackConfig | null {\n if (!config) return null;\n if (\"accessToken\" in config && \"getExtensionToken\" in config) {\n return config;\n }\n const { accessTokenUrl, extensionTokenUrl } = config;\n return {\n accessToken: async () => {\n const response = await fetch(accessTokenUrl, { credentials: \"include\" });\n if (!response.ok) {\n throw await tokenFetchError(\"access token\", response);\n }\n const data = (await response.json()) as { token?: string };\n if (!data.token) {\n throw new Error(\"Access token response missing `token` field\");\n }\n return data.token;\n },\n getExtensionToken: async (intentOp: string) => {\n const response = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (!response.ok) {\n throw await tokenFetchError(\"extension token\", response);\n }\n const data = (await response.json()) as { token?: string };\n if (!data.token) {\n throw new Error(\"Extension token response missing `token` field\");\n }\n return data.token;\n },\n };\n}\n\nexport class OneAuthHeadlessClient {\n private providerUrl: string;\n private clientId?: string;\n private sponsorship: SponsorshipCallbackConfig | null;\n\n constructor(config: PasskeyProviderConfig) {\n this.providerUrl = config.providerUrl || DEFAULT_PROVIDER_URL;\n this.clientId = config.clientId;\n this.sponsorship = normalizeSponsorship(config.sponsorship);\n }\n\n setSponsorship(sponsorship: SponsorshipConfig | undefined): void {\n this.sponsorship = normalizeSponsorship(sponsorship);\n }\n\n // ---------------------------------------------------------------------------\n // Sponsorship JWT fetchers\n // ---------------------------------------------------------------------------\n\n private async fetchAccessToken(): Promise<AccessTokenFetchResult> {\n if (!this.sponsorship) {\n return {\n ok: false,\n code: \"MISSING_APP_CREDENTIALS\",\n message: MISSING_APP_CREDENTIALS_MESSAGE,\n };\n }\n try {\n const accessToken = await this.sponsorship.accessToken();\n return { ok: true, accessToken };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n private async fetchExtensionToken(\n intentOp: string,\n sponsor: boolean,\n ): Promise<ExtensionTokenFetchResult> {\n if (!sponsor) return { ok: true, extensionToken: undefined };\n if (!this.sponsorship) {\n return { ok: false, message: MISSING_APP_CREDENTIALS_MESSAGE };\n }\n try {\n const token = await this.sponsorship.getExtensionToken(intentOp);\n return { ok: true, extensionToken: token };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Intent prepare / submit\n // ---------------------------------------------------------------------------\n\n /**\n * Quote an intent without opening any dialog. Returns the orchestrator's\n * `intentOp` plus the digest the caller must sign with their own\n * validator (e.g. SmartSession). The caller is responsible for\n * encoding signatures and submitting via {@link submitIntent}.\n */\n async prepareIntent(options: HeadlessIntentOptions): Promise<HeadlessPrepareResult> {\n if (!options.username && !options.accountAddress) {\n throw new Error(\"prepareIntent requires either `username` or `accountAddress`\");\n }\n\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(`Sponsorship access token fetch failed: ${accessTokenResult.message}`);\n }\n\n // bigint → string conversion for token requests; matches what the\n // dialog flow does before serializing requestBody.\n const tokenRequests = options.tokenRequests?.map((tr: IntentTokenRequest) => ({\n token: tr.token,\n amount: tr.amount.toString(),\n }));\n\n const body = {\n username: options.username,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n calls: options.calls,\n tokenRequests,\n sourceAssets: options.sourceAssets,\n sourceChainId: options.sourceChainId,\n sessionKeyHandle: options.sessionKeyHandle,\n clientId: this.clientId,\n signingMode: \"headless\" as const,\n auth: { accessToken: accessTokenResult.accessToken },\n };\n\n const response = await fetch(`${this.providerUrl}/api/intent/prepare`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { error?: string };\n throw new Error(errorData.error || `Prepare failed (${response.status})`);\n }\n\n return (await response.json()) as HeadlessPrepareResult;\n }\n\n /**\n * Forward a pre-signed intent to the orchestrator. The caller has\n * already encoded validator-prefixed origin + destination signatures\n * (one origin signature per intent element); 1auth treats them as\n * opaque bytes.\n *\n * If `sponsor` is omitted or `true`, an extension token is fetched\n * just-in-time and bound to the intent. Pass `sponsor: false` for\n * self-paying intents.\n */\n async submitIntent(options: HeadlessSubmitOptions): Promise<HeadlessSubmitResult> {\n const sponsor = options.sponsor ?? true;\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(`Sponsorship access token fetch failed: ${accessTokenResult.message}`);\n }\n\n const extensionResult = await this.fetchExtensionToken(options.intentOp, sponsor);\n if (!extensionResult.ok) {\n throw new Error(`Sponsorship extension token fetch failed: ${extensionResult.message}`);\n }\n\n const body = {\n intentOp: options.intentOp,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n calls: options.calls,\n expiresAt: options.expiresAt,\n originSignatures: options.originSignatures,\n destinationSignature: options.destinationSignature,\n targetExecutionSignature: options.targetExecutionSignature,\n auth: {\n accessToken: accessTokenResult.accessToken,\n ...(extensionResult.extensionToken && { extensionToken: extensionResult.extensionToken }),\n },\n };\n\n const response = await fetch(`${this.providerUrl}/api/intent/headless-execute`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { error?: string };\n throw new Error(errorData.error || `Headless execute failed (${response.status})`);\n }\n\n return (await response.json()) as HeadlessSubmitResult;\n }\n\n // ---------------------------------------------------------------------------\n // Status / wait — proxies to the existing routes (no headless variant needed).\n // ---------------------------------------------------------------------------\n\n /**\n * Fetch the latest known status for a submitted headless intent.\n * This is a non-blocking read and may return `pending` before a\n * solver has produced a fill transaction hash.\n */\n async getIntentStatus(intentId: string): Promise<HeadlessIntentStatusResult> {\n const response = await fetch(`${this.providerUrl}/api/intent/status/${intentId}`);\n if (!response.ok) {\n throw new Error(`Status fetch failed (${response.status})`);\n }\n return (await response.json()) as HeadlessIntentStatusResult;\n }\n\n /**\n * Wait for a submitted headless intent to produce a status update and,\n * when available, the final fill transaction hash. The wait endpoint\n * needs the opaque `transactionResult` returned by submit because 1auth\n * does not persist that SDK object in its database.\n */\n async waitForIntent(\n intentId: string,\n transactionResult: unknown,\n ): Promise<HeadlessIntentStatusResult> {\n if (!transactionResult) {\n throw new Error(\"waitForIntent requires submitResult.transactionResult\");\n }\n\n const response = await fetch(`${this.providerUrl}/api/intent/wait/${intentId}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ transactionResult }),\n });\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n details?: string;\n };\n throw new Error(\n errorData.details || errorData.error || `Wait fetch failed (${response.status})`,\n );\n }\n return (await response.json()) as HeadlessIntentStatusResult;\n }\n\n // ---------------------------------------------------------------------------\n // SmartSession install (one-time, passkey-signed)\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the SmartSession install call(s) for `sessionKeyAddress`\n * with the supplied `permissions`.\n *\n * Returns `{ install, sessionKeyHandle }`:\n *\n * - `install` is a `{ targetChain, calls }` pair the caller feeds\n * straight into `OneAuthClient.sendIntent({ targetChain, calls })`\n * so the user passkey-signs the install via the existing dialog.\n * - `sessionKeyHandle` contains the deterministic `permissionId`\n * plus metadata to persist in localStorage. Persist this BEFORE\n * submitting the install so a refresh during signing doesn't\n * lose the handle.\n *\n * If the validator is already installed on the user's account,\n * `install.calls` is empty and `install.alreadyInstalled` is true.\n * The caller should still persist `sessionKeyHandle` — the on-chain\n * session enabling happens at first headless use via SmartSession's\n * ENABLE-mode signature wrapping (see follow-up).\n */\n async installSmartSession(\n options: InstallSmartSessionOptions,\n ): Promise<InstallSmartSessionResult> {\n if (!options.username && !options.accountAddress) {\n throw new Error(\n \"installSmartSession requires either `username` or `accountAddress`\",\n );\n }\n\n const accessTokenResult = await this.fetchAccessToken();\n if (!accessTokenResult.ok) {\n throw new Error(\n `Sponsorship access token fetch failed: ${accessTokenResult.message}`,\n );\n }\n\n const body = {\n username: options.username,\n accountAddress: options.accountAddress,\n targetChain: options.targetChain,\n sessionKeyAddress: options.sessionKeyAddress,\n permissions: options.permissions,\n validUntil: options.validUntil,\n validAfter: options.validAfter,\n maxUses: options.maxUses,\n enableSessionSignature: options.enableSessionSignature,\n label: options.label,\n auth: { accessToken: accessTokenResult.accessToken },\n };\n\n const response = await fetch(`${this.providerUrl}/api/sessions/install`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(errorData.error || `Install failed (${response.status})`);\n }\n\n return (await response.json()) as InstallSmartSessionResult;\n }\n}\n","import { type Address, type Hex } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport {\n createRhinestoneAccount,\n type Session,\n type SignData,\n type SignerSet,\n} from \"@rhinestone/sdk\";\nimport { toSession } from \"@rhinestone/sdk/smart-sessions\";\nimport { getChainById } from \"./registry\";\nimport type { HeadlessPrepareResult, SessionKeyHandle } from \"./types\";\n\n/** Restores bigint sentinel strings emitted by 1auth's JSON transport. */\nfunction reviveBigIntStrings<T>(value: T): T {\n if (Array.isArray(value)) return value.map((entry) => reviveBigIntStrings(entry)) as T;\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [key, reviveBigIntStrings(entry)]),\n ) as T;\n }\n if (typeof value === \"string\" && value.startsWith(\"__bigint:\")) {\n return BigInt(value.slice(\"__bigint:\".length)) as T;\n }\n if (typeof value === \"string\" && /^\\d+$/.test(value)) return BigInt(value) as T;\n return value;\n}\n\n/** Parses the prepared transaction enough to recover the SDK-native SignData. */\nfunction parsePreparedSignData(value: string): SignData {\n const prepared = JSON.parse(value, (_key, nested) => reviveBigIntStrings(nested)) as {\n quotes?: { best?: { signData?: SignData } };\n };\n const signData = prepared.quotes?.best?.signData;\n if (!signData) {\n throw new Error(\"Prepared headless intent is missing quotes.best.signData\");\n }\n return signData;\n}\n\n/** Reads the numeric EIP-712 chain id from a typed-data domain. */\nfunction chainIdFromSignDataDomain(domainChainId: unknown): number | undefined {\n if (typeof domainChainId === \"number\" && Number.isSafeInteger(domainChainId)) {\n return domainChainId;\n }\n if (typeof domainChainId === \"bigint\") return Number(domainChainId);\n if (typeof domainChainId === \"string\" && /^\\d+$/.test(domainChainId)) {\n return Number(domainChainId);\n }\n return undefined;\n}\n\n/** Rebuilds the SDK SmartSession object from the persisted grant handle. */\nfunction buildSessionForChain(args: {\n privateKey: Hex;\n handle: SessionKeyHandle;\n chainId: number;\n}): Session {\n return toSession(\n {\n chain: getChainById(args.chainId),\n owners: { type: \"ecdsa\", accounts: [privateKeyToAccount(args.privateKey)] },\n permissions: reviveBigIntStrings(args.handle.permissions ?? []),\n crossChainPermits: reviveBigIntStrings(args.handle.crossChainPermits ?? []),\n },\n // The production SmartSession module is deployed on testnets too. Never opt\n // into SDK dev contracts for 1auth headless permissions.\n { useDevContracts: false },\n );\n}\n\n/** Builds a per-chain SmartSession signer set for every chain in SignData. */\nfunction buildSessionSigners(args: {\n privateKey: Hex;\n handle: SessionKeyHandle;\n prepared: HeadlessPrepareResult;\n signData: SignData;\n}): SignerSet {\n const chainIds = new Set<number>([args.prepared.targetChain]);\n for (const typedData of args.signData.origin) {\n const chainId = chainIdFromSignDataDomain(typedData.domain?.chainId);\n if (chainId) chainIds.add(chainId);\n }\n const targetExecutionChainId = chainIdFromSignDataDomain(\n args.signData.targetExecution?.domain?.chainId,\n );\n if (targetExecutionChainId) chainIds.add(targetExecutionChainId);\n\n return {\n type: \"experimental_session\",\n sessions: Object.fromEntries(\n [...chainIds].map((chainId) => [\n chainId,\n {\n session: buildSessionForChain({\n privateKey: args.privateKey,\n handle: args.handle,\n chainId,\n }),\n },\n ]),\n ),\n };\n}\n\nexport interface BuildSmartSessionHeadlessSignaturesOptions {\n /** ECDSA private key for the locally-held SmartSession signer. */\n privateKey: Hex;\n /** Smart account address the SmartSession is scoped to. */\n accountAddress: Address;\n /** Persisted handle returned by `grantPermissions` / SmartSession install. */\n sessionKeyHandle: SessionKeyHandle;\n /** Prepared headless intent returned by `OneAuthHeadlessClient.prepareIntent`. */\n prepared: HeadlessPrepareResult;\n}\n\n/**\n * Builds SmartSession signatures for `OneAuthHeadlessClient.submitIntent`.\n *\n * This intentionally delegates signing and SmartSession signature packing to\n * `@rhinestone/sdk`. 1auth only rebuilds the persisted session descriptor and\n * supplies the SDK-native `SignData` returned by the orchestrator quote.\n */\nexport async function buildSmartSessionHeadlessSignatures(\n options: BuildSmartSessionHeadlessSignaturesOptions,\n) {\n const signData = parsePreparedSignData(options.prepared.intentOp);\n const account = await createRhinestoneAccount({\n initData: { address: options.accountAddress },\n owners: {\n type: \"ecdsa\",\n accounts: [privateKeyToAccount(options.privateKey)],\n },\n experimental_sessions: { enabled: true },\n });\n\n return account.signIntent(\n signData,\n getChainById(options.prepared.targetChain),\n buildSessionSigners({\n privateKey: options.privateKey,\n handle: options.sessionKeyHandle,\n prepared: options.prepared,\n signData,\n }),\n );\n}\n","/**\n * @file Chain and token registry for the 1auth SDK.\n *\n * Wraps `@rhinestone/shared-configs` chain/token data and combines it with\n * viem's chain definitions to expose a filtered, testnet-aware registry.\n * Consumers can look up supported chains and tokens, resolve token addresses\n * by symbol, and retrieve chain metadata such as explorer URLs and default RPC\n * endpoints.\n *\n * Testnet inclusion is controlled by the `includeTestnets` option on each\n * function or, as a project-wide default, by the environment variables\n * `NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS` or `ORCHESTRATOR_USE_TESTNETS`.\n */\n\nimport type { Address, Chain } from \"viem\";\nimport { isAddress } from \"viem\";\nimport * as viemChains from \"viem/chains\";\nimport { chainRegistry } from \"@rhinestone/shared-configs\";\n\nexport type TokenConfig = {\n symbol: string;\n address: Address;\n decimals: number;\n};\n\nexport type ChainFilterOptions = {\n includeTestnets?: boolean;\n chainIds?: number[];\n};\n\ntype ChainRegistryEntry = (typeof chainRegistry)[keyof typeof chainRegistry];\n\nconst env: Record<string, string | undefined> =\n typeof process !== \"undefined\" ? process.env : {};\n\n// Build a chain-ID-to-Chain map from viem, resolving collisions by preferring\n// mainnet definitions over testnets. viem exports duplicate numeric IDs (e.g.\n// zoraTestnet and hyperEvm both use 999); a naive Map would silently keep\n// whichever entry happens to appear last in iteration order, which may be the\n// testnet. The loop below only overwrites an existing entry when the incoming\n// chain is NOT a testnet and the existing one IS, ensuring the production chain\n// always wins while still falling back to a testnet entry when no mainnet\n// definition exists for a given ID.\nconst VIEM_CHAIN_BY_ID = new Map<number, Chain>();\nfor (const value of Object.values(viemChains)) {\n if (typeof value !== \"object\" || value === null || !(\"id\" in value) || !(\"name\" in value)) continue;\n const chain = value as Chain;\n const existing = VIEM_CHAIN_BY_ID.get(chain.id);\n if (!existing || (existing.testnet && !chain.testnet)) {\n VIEM_CHAIN_BY_ID.set(chain.id, chain);\n }\n}\n\nfunction isEvmRegistryEntry(\n entry: ChainRegistryEntry | undefined,\n): entry is ChainRegistryEntry & { vmType: \"evm\" } {\n return entry?.vmType === \"evm\";\n}\n\nfunction getEvmRegistryEntry(\n chainId: number,\n): (ChainRegistryEntry & { vmType: \"evm\" }) | undefined {\n const entry = chainRegistry[String(chainId) as keyof typeof chainRegistry];\n return isEvmRegistryEntry(entry) ? entry : undefined;\n}\n\n// The shared-configs registry includes non-EVM chains (Solana, Tron). Filter\n// to EVM entries before exposing supported chains — downstream consumers\n// (viem clients, wallet_getCapabilities, RPC proxy) only handle eip155 chains.\nconst REGISTRY_CHAIN_IDS = Object.entries(chainRegistry)\n .filter(([, entry]) => isEvmRegistryEntry(entry))\n .map(([id]) => Number(id));\nconst SUPPORTED_CHAIN_IDS = new Set(REGISTRY_CHAIN_IDS);\n\nfunction tokensForChain(chainId: number): TokenConfig[] {\n const entry = getEvmRegistryEntry(chainId);\n if (!entry) return [];\n return entry.tokens.map((token) => ({\n symbol: token.symbol,\n address: token.address as Address,\n decimals: token.decimals,\n }));\n}\n\n/** Treat any 20-byte hex string as an address; checksum casing is optional for user inputs. */\nfunction isAddressInput(value: string): value is Address {\n return isAddress(value, { strict: false });\n}\n\n/**\n * Parse a string environment variable into a boolean, returning `undefined`\n * when the value is absent or not a recognised truthy/falsy literal.\n *\n * Accepted truthy values: `\"true\"`, `\"1\"`.\n * Accepted falsy values: `\"false\"`, `\"0\"`.\n */\nfunction parseBool(value?: string): boolean | undefined {\n if (value === \"true\" || value === \"1\") return true;\n if (value === \"false\" || value === \"0\") return false;\n return undefined;\n}\n\n/**\n * Determine whether testnet chains should be included in registry results.\n *\n * Precedence (highest to lowest):\n * 1. `explicit` argument — a caller-supplied boolean always wins.\n * 2. `NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS` env var — checked first so\n * Next.js browser bundles (which only expose `NEXT_PUBLIC_*` variables)\n * can configure testnet mode without a server-side env var.\n * 3. `ORCHESTRATOR_USE_TESTNETS` env var — server-side / Node.js fallback.\n * 4. Defaults to `false` (testnets excluded) when none of the above is set.\n */\nfunction resolveIncludeTestnets(explicit?: boolean): boolean {\n if (explicit !== undefined) return explicit;\n const envValue =\n parseBool(env.NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS) ??\n parseBool(env.ORCHESTRATOR_USE_TESTNETS);\n return envValue ?? false;\n}\n\nfunction applyChainFilters(chainIds: number[], options?: ChainFilterOptions): number[] {\n const includeTestnets = resolveIncludeTestnets(options?.includeTestnets);\n const allowlist = options?.chainIds;\n let filtered = chainIds;\n\n if (!includeTestnets) {\n filtered = filtered.filter((chainId) => !isTestnet(chainId));\n }\n\n if (allowlist) {\n const allowed = new Set(allowlist);\n filtered = filtered.filter((chainId) => allowed.has(chainId));\n }\n\n return filtered;\n}\n\nexport function getSupportedChainIds(options?: ChainFilterOptions): number[] {\n return applyChainFilters(REGISTRY_CHAIN_IDS, options);\n}\n\nexport function getSupportedChains(options?: ChainFilterOptions): Chain[] {\n return getSupportedChainIds(options)\n .map((chainId) => VIEM_CHAIN_BY_ID.get(chainId))\n .filter((chain): chain is Chain => Boolean(chain));\n}\n\nexport function getAllSupportedChainsAndTokens(options?: ChainFilterOptions): Array<{\n chainId: number;\n tokens: TokenConfig[];\n}> {\n return getSupportedChainIds(options).map((chainId) => ({\n chainId,\n tokens: tokensForChain(chainId),\n }));\n}\n\nexport function getChainById(chainId: number): Chain {\n if (!SUPPORTED_CHAIN_IDS.has(chainId)) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n const chain = VIEM_CHAIN_BY_ID.get(chainId);\n if (!chain) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n return chain;\n}\n\nexport function getChainName(chainId: number): string {\n try {\n return getChainById(chainId).name;\n } catch {\n return `Chain ${chainId}`;\n }\n}\n\nexport function getChainExplorerUrl(chainId: number): string | undefined {\n try {\n return getChainById(chainId).blockExplorers?.default?.url;\n } catch {\n return undefined;\n }\n}\n\nexport function getChainRpcUrl(chainId: number): string | undefined {\n try {\n const chain = getChainById(chainId);\n return chain.rpcUrls?.default?.http?.[0] || chain.rpcUrls?.public?.http?.[0];\n } catch {\n return undefined;\n }\n}\n\nexport function getSupportedTokens(chainId: number): TokenConfig[] {\n return tokensForChain(chainId);\n}\n\nexport function getSupportedTokenSymbols(chainId: number): string[] {\n return getSupportedTokens(chainId).map((token) => token.symbol);\n}\n\nexport function getTokenAddress(symbolOrAddress: string, chainId: number): Address {\n if (isAddressInput(symbolOrAddress)) {\n return symbolOrAddress;\n }\n const match = getSupportedTokens(chainId).find(\n (t) => t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()\n );\n if (!match) {\n throw new Error(`Unsupported token \"${symbolOrAddress}\" on chain ${chainId}`);\n }\n return match.address;\n}\n\nexport function getTokenDecimals(symbolOrAddress: string, chainId: number): number {\n const match = getSupportedTokens(chainId).find(\n (t) =>\n isAddressInput(symbolOrAddress)\n ? t.address.toLowerCase() === symbolOrAddress.toLowerCase()\n : t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()\n );\n if (!match && isAddressInput(symbolOrAddress) && SUPPORTED_CHAIN_IDS.has(chainId)) {\n return 18;\n }\n if (!match) {\n throw new Error(`Unsupported token \"${symbolOrAddress}\" on chain ${chainId}`);\n }\n return match.decimals;\n}\n\nexport function resolveTokenAddress(token: string, chainId: number): Address {\n if (isAddressInput(token)) {\n return token;\n }\n return getTokenAddress(token, chainId);\n}\n\nexport function isTestnet(chainId: number): boolean {\n try {\n return getChainById(chainId).testnet ?? false;\n } catch {\n return false;\n }\n}\n\nexport function getTokenSymbol(tokenAddress: Address, chainId: number): string {\n const token = getSupportedTokens(chainId).find(\n (entry) => entry.address.toLowerCase() === tokenAddress.toLowerCase()\n );\n if (token) {\n return token.symbol;\n }\n if (!SUPPORTED_CHAIN_IDS.has(chainId)) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n return \"Token\";\n}\n\nexport function isTokenAddressSupported(tokenAddress: Address, chainId: number): boolean {\n return isAddressInput(tokenAddress) && SUPPORTED_CHAIN_IDS.has(chainId);\n}\n"],"mappings":"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","names":[]}
package/dist/headless.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  tokenFetchError
3
3
  } from "./chunk-IIACVHR3.mjs";
4
+ import {
5
+ getChainById
6
+ } from "./chunk-GUAI55LL.mjs";
4
7
 
5
8
  // src/headless-client.ts
6
9
  var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
@@ -274,7 +277,106 @@ var OneAuthHeadlessClient = class {
274
277
  return await response.json();
275
278
  }
276
279
  };
280
+
281
+ // src/smart-session-signatures.ts
282
+ import { privateKeyToAccount } from "viem/accounts";
283
+ import {
284
+ createRhinestoneAccount
285
+ } from "@rhinestone/sdk";
286
+ import { toSession } from "@rhinestone/sdk/smart-sessions";
287
+ function reviveBigIntStrings(value) {
288
+ if (Array.isArray(value)) return value.map((entry) => reviveBigIntStrings(entry));
289
+ if (value && typeof value === "object") {
290
+ return Object.fromEntries(
291
+ Object.entries(value).map(([key, entry]) => [key, reviveBigIntStrings(entry)])
292
+ );
293
+ }
294
+ if (typeof value === "string" && value.startsWith("__bigint:")) {
295
+ return BigInt(value.slice("__bigint:".length));
296
+ }
297
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
298
+ return value;
299
+ }
300
+ function parsePreparedSignData(value) {
301
+ const prepared = JSON.parse(value, (_key, nested) => reviveBigIntStrings(nested));
302
+ const signData = prepared.quotes?.best?.signData;
303
+ if (!signData) {
304
+ throw new Error("Prepared headless intent is missing quotes.best.signData");
305
+ }
306
+ return signData;
307
+ }
308
+ function chainIdFromSignDataDomain(domainChainId) {
309
+ if (typeof domainChainId === "number" && Number.isSafeInteger(domainChainId)) {
310
+ return domainChainId;
311
+ }
312
+ if (typeof domainChainId === "bigint") return Number(domainChainId);
313
+ if (typeof domainChainId === "string" && /^\d+$/.test(domainChainId)) {
314
+ return Number(domainChainId);
315
+ }
316
+ return void 0;
317
+ }
318
+ function buildSessionForChain(args) {
319
+ return toSession(
320
+ {
321
+ chain: getChainById(args.chainId),
322
+ owners: { type: "ecdsa", accounts: [privateKeyToAccount(args.privateKey)] },
323
+ permissions: reviveBigIntStrings(args.handle.permissions ?? []),
324
+ crossChainPermits: reviveBigIntStrings(args.handle.crossChainPermits ?? [])
325
+ },
326
+ // The production SmartSession module is deployed on testnets too. Never opt
327
+ // into SDK dev contracts for 1auth headless permissions.
328
+ { useDevContracts: false }
329
+ );
330
+ }
331
+ function buildSessionSigners(args) {
332
+ const chainIds = /* @__PURE__ */ new Set([args.prepared.targetChain]);
333
+ for (const typedData of args.signData.origin) {
334
+ const chainId = chainIdFromSignDataDomain(typedData.domain?.chainId);
335
+ if (chainId) chainIds.add(chainId);
336
+ }
337
+ const targetExecutionChainId = chainIdFromSignDataDomain(
338
+ args.signData.targetExecution?.domain?.chainId
339
+ );
340
+ if (targetExecutionChainId) chainIds.add(targetExecutionChainId);
341
+ return {
342
+ type: "experimental_session",
343
+ sessions: Object.fromEntries(
344
+ [...chainIds].map((chainId) => [
345
+ chainId,
346
+ {
347
+ session: buildSessionForChain({
348
+ privateKey: args.privateKey,
349
+ handle: args.handle,
350
+ chainId
351
+ })
352
+ }
353
+ ])
354
+ )
355
+ };
356
+ }
357
+ async function buildSmartSessionHeadlessSignatures(options) {
358
+ const signData = parsePreparedSignData(options.prepared.intentOp);
359
+ const account = await createRhinestoneAccount({
360
+ initData: { address: options.accountAddress },
361
+ owners: {
362
+ type: "ecdsa",
363
+ accounts: [privateKeyToAccount(options.privateKey)]
364
+ },
365
+ experimental_sessions: { enabled: true }
366
+ });
367
+ return account.signIntent(
368
+ signData,
369
+ getChainById(options.prepared.targetChain),
370
+ buildSessionSigners({
371
+ privateKey: options.privateKey,
372
+ handle: options.sessionKeyHandle,
373
+ prepared: options.prepared,
374
+ signData
375
+ })
376
+ );
377
+ }
277
378
  export {
278
- OneAuthHeadlessClient
379
+ OneAuthHeadlessClient,
380
+ buildSmartSessionHeadlessSignatures
279
381
  };
280
382
  //# sourceMappingURL=headless.mjs.map