@dexterai/connect 0.17.0 → 0.19.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 CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">@dexterai/connect</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Sign in with Dexter passkey sign-in for any app. Tap your face, you're in: a non-custodial Dexter Wallet and its live USD balance, in one component.</strong>
8
+ <strong>Sign in with Dexter. One passkey tap gives any website a non-custodial Dexter Wallet, and gives any server an offline-verifiable session.</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -18,23 +18,31 @@
18
18
 
19
19
  ## What this is
20
20
 
21
- A React connector that adds **"Sign in with Dexter"** to any app. One
22
- `<SignInWithDexter/>` button runs a discoverable passkey ceremony the user
23
- taps their face and you get back a session plus their non-custodial **Dexter
24
- Wallet** (address + live USD balance). No password, no seed phrase, no
25
- extension.
21
+ A `<SignInWithDexter/>` button runs a discoverable passkey ceremony. The user
22
+ taps their face, and your app gets back a session plus their **Dexter
23
+ Wallet**: address, live USD balance, and the rails for an agent to spend from
24
+ it under on-chain limits. The user holds their own keys; only their passkey
25
+ moves funds, enforced on-chain. Composes
26
+ [`@dexterai/vault`](https://www.npmjs.com/package/@dexterai/vault).
26
27
 
27
- The user holds their own keys. Nothing here is custodial — only the user's
28
- passkey moves funds, enforced on-chain. Composes
29
- [`@dexterai/vault`](https://www.npmjs.com/package/@dexterai/vault); the only
30
- peer is React.
28
+ Four entry points cover the whole flow:
29
+
30
+ | Entry point | Runs in | What it gives you |
31
+ |---|---|---|
32
+ | `@dexterai/connect` | browser | framework-free core: `passkeyLogin`, `createWallet`, `continueWithDexter`, the wallet store, agent-spend controls |
33
+ | `@dexterai/connect/react` | browser | `<SignInWithDexter/>`, the branded wallet kit, hooks |
34
+ | `@dexterai/connect/server` | Node 18+, Workers, Vercel edge | `verifyDexterSession`: offline session verification |
35
+ | `@dexterai/connect/worldid` | browser | `<VerifyPersonhood/>` World ID proof-of-personhood button |
31
36
 
32
37
  ## Install
33
38
 
34
39
  ```bash
35
- npm install @dexterai/connect react
40
+ npm install @dexterai/connect @dexterai/vault react
36
41
  ```
37
42
 
43
+ `@solana/web3.js` and `@worldcoin/idkit` are optional peers: the first for
44
+ agent-spend signing, the second only if you use the World ID button.
45
+
38
46
  ## Quick start
39
47
 
40
48
  ```tsx
@@ -52,39 +60,128 @@ function Header() {
52
60
  }
53
61
  ```
54
62
 
55
- Signed out, it renders a **Sign in with Dexter** button. On success it becomes
56
- a compact chip — the Dexter Wallet address + **"$X.XX available."**
63
+ Signed out, it renders a **Sign in with Dexter** button. Connected, it becomes
64
+ the wallet chip: address plus **"$X.XX available."**
57
65
 
58
- ## Hook (full control)
66
+ ## Works on any website
67
+
68
+ The ceremony is not limited to dexter.cash. On a foreign origin, `passkeyLogin`
69
+ opens a hosted popup on `dexter.cash/connect`, runs the ceremony there, and
70
+ posts the result back to your page (origin-checked and nonce-bound on both
71
+ sides). The default `transport: 'auto'` picks the right mode; `'popup'` and
72
+ `'inline'` force it.
73
+
74
+ ```ts
75
+ import { passkeyLogin } from '@dexterai/connect';
76
+
77
+ const { session, vault } = await passkeyLogin({ transport: 'auto' });
78
+ ```
59
79
 
60
- For your own UI, use the hook directly:
80
+ ## Verify the session on your server
81
+
82
+ ```ts
83
+ import { createDexterClient } from '@dexterai/connect/server';
84
+
85
+ const dexter = createDexterClient(); // parameterized on (iss, jwksUrl); defaults to Dexter's issuer
86
+
87
+ export async function handler(req: Request) {
88
+ const auth = await dexter.authenticateRequest(req);
89
+ if (!auth.isSignedIn) return new Response('unauthorized', { status: 401 });
90
+ auth.sub; // stable user id
91
+ auth.vaultAddress; // the Dexter Wallet address, from the signed dexter claim
92
+ auth.claims; // full verified JWT payload
93
+ }
94
+ ```
95
+
96
+ Verification is a local ES256 signature check against a cached JWKS. The first
97
+ call fetches the key set; every later call is pure local crypto with zero
98
+ network (measured at ~0.6ms). The algorithm list is pinned to ES256, and
99
+ issuer plus audience are always checked. `verifyDexterSession(token, options)`
100
+ does the same for a bare token string, and `jwtKey` accepts a public JWK for
101
+ fully networkless deployments.
102
+
103
+ ## Hook (full control)
61
104
 
62
105
  ```tsx
63
106
  import { useSignInWithDexter } from '@dexterai/connect/react';
64
107
 
65
108
  const c = useSignInWithDexter();
66
109
  await c.signIn(); // run the passkey ceremony
67
- c.status; // idle pending done error
110
+ c.status; // idle -> pending -> done -> error
68
111
  c.vaultAddress; // the Dexter Wallet address (base58)
69
112
  c.usdcBalance; // USD available (via Dexter's RPC), or null
70
113
  c.disconnect();
71
114
  ```
72
115
 
73
- ## What `useSignInWithDexter()` gives you
74
-
75
116
  | Field | What it is |
76
117
  |---|---|
77
118
  | `signIn()` / `disconnect()` | run the passkey ceremony / clear state |
78
- | `status` / `isVaultConnected` | `idlependingdoneerror` / connected flag |
119
+ | `status` / `isVaultConnected` | `idle->pending->done->error` / connected flag |
79
120
  | `session` | auth session tokens (camelCase) |
80
121
  | `vaultAddress` / `vaultPda` | the Dexter Wallet address / PDA |
81
122
  | `usdcBalance` / `refreshBalance()` | USD available, best-effort via Dexter's RPC |
82
123
  | `vault` / `credentialId` / `error` | raw vault payload / credential id / typed error |
83
124
 
84
- ## Exports
125
+ ## The wallet kit
126
+
127
+ Branded, presentational pieces that share one implementation across every
128
+ Dexter surface, themed with `--dx-*` CSS variables: `DexterButton` (and
129
+ `DexterMark`) for any action that should look like Dexter, `DexterWalletChip`
130
+ as the header trigger, `DexterWalletMenu` for manage / save / start-fresh, and
131
+ the `useDexterWallet` + `useIdentity` hooks to drive them.
132
+
133
+ ## Agent spend
134
+
135
+ The control surface for letting an agent spend from the connected wallet:
136
+
137
+ ```ts
138
+ import {
139
+ assembleAgentSpendStatus, // honest two-mode status read
140
+ enableAgentSpend, // the on switch
141
+ revokeAgentSpend, // the off switch
142
+ createPasskeySigner, // @dexterai/vault guest signer for x402 / tab flows
143
+ } from '@dexterai/connect';
144
+ ```
145
+
146
+ The verbs are framework-free and take `apiOrigin` as a parameter; the SDK
147
+ reads no environment variables.
148
+
149
+ ## World ID
150
+
151
+ ```tsx
152
+ import { VerifyPersonhood } from '@dexterai/connect/worldid';
153
+
154
+ <VerifyPersonhood onSuccess={(proof) => sendToYourVerifier(proof)} />
155
+ ```
85
156
 
86
- - `@dexterai/connect` framework-free: `passkeyLogin()`, `ConnectError`, types.
87
- - `@dexterai/connect/react` — `<SignInWithDexter/>`, `useSignInWithDexter()`.
157
+ `useVerifyPersonhood` is the headless version. Requires the optional
158
+ `@worldcoin/idkit` peer.
159
+
160
+ ## Wallet lifecycle
161
+
162
+ - `createWallet` mints a brand-new named passkey + vault; `passkeyLogin` signs
163
+ an existing one in; `continueWithDexter` resumes a known wallet.
164
+ - The **wallet store** (`getActiveHandle`, `listWallets`, `switchWallet`,
165
+ `ejectActiveWallet`, `forgetWallet`, `subscribeWallet`) is the canonical
166
+ owner of the active-wallet handle. Read and write through it rather than
167
+ touching localStorage.
168
+ - The **WebAuthn Signal API** helpers (`renamePasskey`, `prunePasskey`,
169
+ `syncAcceptedPasskeys`, `passkeySignalSupport`) keep the OS keychain in sync
170
+ where the browser supports it.
171
+ - `resolveIdentity` combines the wallet handle with whatever account token you
172
+ pass in; `ceremonyPhaseLabel` gives shared copy for connecting-step UI;
173
+ `createAnonServerPolicy` builds the anonymous server policy for the signer.
174
+
175
+ ## Peer dependencies
176
+
177
+ | Peer | Required | Why |
178
+ |---|---|---|
179
+ | `react` >=18 | yes | the `/react` and `/worldid` surfaces |
180
+ | `@dexterai/vault` >=0.30 | yes | signer + agent-spend message builders (0.30 matches the deployed program's account layout) |
181
+ | `@solana/web3.js` | optional | agent-spend signing paths |
182
+ | `@worldcoin/idkit` | optional | only for `/worldid` |
183
+
184
+ The `/server` entry has none of these peers; it depends only on `jose`.
88
185
 
89
186
  ## License
90
187
 
@@ -124,6 +124,24 @@ function onStorageEvent(e) {
124
124
  }
125
125
  var ACTIVE_WALLET_STORAGE_KEY = ACTIVE_HANDLE_KEY;
126
126
 
127
+ // src/policy.ts
128
+ var SESSION_TTL_30D = "2592000";
129
+ function usdToAtomic(input) {
130
+ const cleaned = input.trim().replace(/^\$/, "").replace(/,/g, "");
131
+ if (!/^\d+(\.\d{1,6})?$/.test(cleaned)) return null;
132
+ const [whole, frac = ""] = cleaned.split(".");
133
+ try {
134
+ return BigInt(whole) * 1000000n + BigInt((frac + "000000").slice(0, 6));
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+ function authoredPolicy(usdInput) {
140
+ const atomic = usdToAtomic(usdInput);
141
+ if (atomic === null || atomic <= 0n) return null;
142
+ return { spendLimitAtomic: atomic.toString(), sessionTtlSeconds: SESSION_TTL_30D };
143
+ }
144
+
127
145
  // src/enroll.ts
128
146
  import { startRegistration } from "@simplewebauthn/browser";
129
147
 
@@ -218,11 +236,13 @@ var DEFAULT_RP_ID = "dexter.cash";
218
236
  var DEFAULT_WALLET_NAME = "Dexter Wallet";
219
237
  async function createWallet(config = {}) {
220
238
  if (shouldUsePopup(config.transport)) {
221
- return openCeremonyPopup("create", {
239
+ const result = await openCeremonyPopup("create", {
222
240
  connectHost: config.connectHost,
223
241
  name: config.name,
224
242
  apiBase: config.apiBase
225
243
  });
244
+ setActiveHandle(result.handle, config.name, result.credentialId);
245
+ return result;
226
246
  }
227
247
  if (typeof navigator === "undefined" || !navigator.credentials) {
228
248
  throw new ConnectError("webauthn_unsupported", "WebAuthn unavailable in this environment");
@@ -247,7 +267,12 @@ async function createWallet(config = {}) {
247
267
  config.onPhase?.("verifying");
248
268
  const enrolled = await submitEnrollComplete(apiBase, regResponse);
249
269
  config.onPhase?.("finalizing");
250
- const init = await initializeVault(apiBase, enrolled.userHandle, enrolled.credentialId);
270
+ const init = await initializeVault(
271
+ apiBase,
272
+ enrolled.userHandle,
273
+ enrolled.credentialId,
274
+ config.spendPolicy
275
+ );
251
276
  setActiveHandle(enrolled.userHandle, name, enrolled.credentialId);
252
277
  return {
253
278
  handle: enrolled.userHandle,
@@ -288,11 +313,16 @@ async function submitEnrollComplete(apiBase, response) {
288
313
  if (!res.ok) throw new ConnectError(await readErrorCode(res), `enroll/complete ${res.status}`);
289
314
  return await res.json();
290
315
  }
291
- async function initializeVault(apiBase, userHandle, credentialId) {
316
+ async function initializeVault(apiBase, userHandle, credentialId, spendPolicy) {
317
+ const body = { userHandle, credentialId, coolingOffSeconds: 0 };
318
+ if (spendPolicy) {
319
+ body.spendLimitAtomic = spendPolicy.spendLimitAtomic;
320
+ body.sessionTtlSeconds = SESSION_TTL_30D;
321
+ }
292
322
  const res = await fetch(`${apiBase}/api/passkey-vault-anon/initialize`, {
293
323
  method: "POST",
294
324
  headers: { "content-type": "application/json" },
295
- body: JSON.stringify({ userHandle, credentialId, coolingOffSeconds: 0 })
325
+ body: JSON.stringify(body)
296
326
  });
297
327
  if (!res.ok) throw new ConnectError(await readErrorCode(res), `initialize ${res.status}`);
298
328
  return await res.json();
@@ -312,10 +342,14 @@ var DEFAULT_API_BASE2 = "https://api.dexter.cash";
312
342
  var ANON_SIGN_BASE = "/api/passkey-anon/sign";
313
343
  async function passkeyLogin(config = {}, onPhase) {
314
344
  if (shouldUsePopup(config.transport)) {
315
- return openCeremonyPopup("signin", {
345
+ const result2 = await openCeremonyPopup("signin", {
316
346
  connectHost: config.connectHost,
317
347
  apiBase: config.apiBase
318
348
  });
349
+ if (result2.vault) {
350
+ setActiveHandle(result2.vault.userHandle, void 0, result2.vault.credentialId);
351
+ }
352
+ return result2;
319
353
  }
320
354
  if (!browserSupportsWebAuthn()) {
321
355
  throw new ConnectError("webauthn_unsupported", "WebAuthn unavailable in this environment");
@@ -331,15 +365,25 @@ async function passkeyLogin(config = {}, onPhase) {
331
365
  throw new ConnectError("webauthn_failed", err instanceof Error ? err.message : String(err));
332
366
  }
333
367
  onPhase?.("verifying");
334
- return submitLogin(apiBase, response);
368
+ const result = await submitLogin(apiBase, response);
369
+ if (result.vault) {
370
+ setActiveHandle(result.vault.userHandle, void 0, result.vault.credentialId);
371
+ }
372
+ return result;
335
373
  }
336
374
  async function continueWithDexter(config = {}, onPhase) {
337
375
  if (shouldUsePopup(config.transport)) {
338
- return openCeremonyPopup("continue", {
376
+ const result2 = await openCeremonyPopup("continue", {
339
377
  connectHost: config.connectHost,
340
378
  name: config.name,
341
379
  apiBase: config.apiBase
342
380
  });
381
+ if (result2.kind === "create") {
382
+ setActiveHandle(result2.handle, config.name, result2.credentialId);
383
+ } else if (result2.vault) {
384
+ setActiveHandle(result2.vault.userHandle, void 0, result2.vault.credentialId);
385
+ }
386
+ return result2;
343
387
  }
344
388
  if (getActiveHandle()) {
345
389
  const result2 = await passkeyLogin(config, onPhase);
@@ -623,6 +667,9 @@ export {
623
667
  forgetWallet,
624
668
  subscribe,
625
669
  ACTIVE_WALLET_STORAGE_KEY,
670
+ SESSION_TTL_30D,
671
+ usdToAtomic,
672
+ authoredPolicy,
626
673
  createWallet,
627
674
  passkeyLogin,
628
675
  continueWithDexter,
package/dist/index.d.ts CHANGED
@@ -1,33 +1,7 @@
1
- import { C as ConnectVault, D as DexterConnectConfig, a as CeremonyPhase, S as SignInResult, I as IdentityKind } from './signals-BaXobUfB.js';
2
- export { A as ACTIVE_WALLET_STORAGE_KEY, b as ConnectError, c as IdentityInput, P as PasskeyLoginTokens, d as PasskeySignalSupport, R as ResolvedIdentity, e as StoredWallet, f as ejectActiveWallet, g as forgetWallet, h as getActiveHandle, i as getCredentialId, l as listWallets, p as passkeySignalSupport, j as prunePasskey, r as renamePasskey, k as resolveIdentity, s as setActiveHandle, m as subscribeWallet, n as switchWallet, o as syncAcceptedPasskeys } from './signals-BaXobUfB.js';
1
+ import { S as SignInResult, C as CreateWalletResult, a as CreateWalletConfig, b as CeremonyPhase, D as DexterConnectConfig, c as ConnectVault, I as IdentityKind } from './signals-qc4rDyQ6.js';
2
+ export { A as ACTIVE_WALLET_STORAGE_KEY, d as ConnectError, e as IdentityInput, P as PasskeyLoginTokens, f as PasskeySignalSupport, R as ResolvedIdentity, g as SESSION_TTL_30D, h as SpendPolicy, i as StoredWallet, j as authoredPolicy, k as createWallet, l as ejectActiveWallet, m as forgetWallet, n as getActiveHandle, o as getCredentialId, p as listWallets, q as passkeySignalSupport, r as prunePasskey, s as renamePasskey, t as resolveIdentity, u as setActiveHandle, v as subscribeWallet, w as switchWallet, x as syncAcceptedPasskeys, y as usdToAtomic } from './signals-qc4rDyQ6.js';
3
3
  import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
4
4
 
5
- interface CreateWalletConfig extends DexterConnectConfig {
6
- /** Label for the passkey in the OS keychain AND the wallet roster. Set at
7
- * creation — the only moment naming is guaranteed to stick. Default "Dexter Wallet". */
8
- name?: string;
9
- /** RP id for the new credential. Default "dexter.cash". */
10
- rpId?: string;
11
- /** Called as the ceremony progresses, for live "connecting steps" UI:
12
- * challenge → passkey → verifying → finalizing. */
13
- onPhase?: (phase: CeremonyPhase) => void;
14
- }
15
- interface CreateWalletResult {
16
- /** Server-minted 16-byte user handle, base64url — the vault identity. */
17
- handle: string;
18
- /** base64url credential id of the new passkey. */
19
- credentialId: string;
20
- /** The freshly initialized vault (swig not yet deployed; deploys lazily). */
21
- vault: ConnectVault;
22
- }
23
- /**
24
- * Mint a brand-new Dexter wallet (passkey + vault) and make it the active wallet.
25
- *
26
- * One passkey approval. Throws ConnectError on any failed leg (the `code` is the
27
- * server's error string, or webauthn_failed / no_credential for the ceremony).
28
- */
29
- declare function createWallet(config?: CreateWalletConfig): Promise<CreateWalletResult>;
30
-
31
5
  /**
32
6
  * "Sign in with Dexter" — the discoverable-credential login ceremony.
33
7
  *
@@ -247,4 +221,4 @@ interface EnableAgentSpendResult {
247
221
  */
248
222
  declare function enableAgentSpend(id: AgentSpendIdentity, vaultPda: string, apiOrigin: string, credentialId?: string | null): Promise<EnableAgentSpendResult>;
249
223
 
250
- export { AgentSpendError, type AgentSpendIdentity, type AgentSpendStatus, type AgentSpendTab, type AnonChallengeResult, type AnonServerPolicy, type AutomaticAgentSpend, CeremonyPhase, ConnectVault, type ContinueResult, type CreateWalletConfig, type CreateWalletResult, DexterConnectConfig, type EnableAgentSpendResult, IdentityKind, type RawAgentSpendSession, type RawAgentSpendStatus, type RevokeAgentSpendResult, SignInResult, assembleAgentSpendStatus, ceremonyPhaseLabel, continueWithDexter, createAnonServerPolicy, createPasskeySigner, createWallet, describeAgentSpendError, enableAgentSpend, passkeyLogin, revokeAgentSpend };
224
+ export { AgentSpendError, type AgentSpendIdentity, type AgentSpendStatus, type AgentSpendTab, type AnonChallengeResult, type AnonServerPolicy, type AutomaticAgentSpend, CeremonyPhase, ConnectVault, type ContinueResult, CreateWalletConfig, CreateWalletResult, DexterConnectConfig, type EnableAgentSpendResult, IdentityKind, type RawAgentSpendSession, type RawAgentSpendStatus, type RevokeAgentSpendResult, SignInResult, assembleAgentSpendStatus, ceremonyPhaseLabel, continueWithDexter, createAnonServerPolicy, createPasskeySigner, describeAgentSpendError, enableAgentSpend, passkeyLogin, revokeAgentSpend };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  ACTIVE_WALLET_STORAGE_KEY,
3
3
  ConnectError,
4
+ SESSION_TTL_30D,
5
+ authoredPolicy,
4
6
  base64urlToBase64,
5
7
  bytesToBase64url,
6
8
  ceremonyPhaseLabel,
@@ -21,8 +23,9 @@ import {
21
23
  setActiveHandle,
22
24
  subscribe,
23
25
  switchWallet,
24
- syncAcceptedPasskeys
25
- } from "./chunk-Y22JFT53.js";
26
+ syncAcceptedPasskeys,
27
+ usdToAtomic
28
+ } from "./chunk-2QZ5QLGI.js";
26
29
 
27
30
  // src/agentSpend.ts
28
31
  import { PublicKey } from "@solana/web3.js";
@@ -175,7 +178,9 @@ export {
175
178
  ACTIVE_WALLET_STORAGE_KEY,
176
179
  AgentSpendError,
177
180
  ConnectError,
181
+ SESSION_TTL_30D,
178
182
  assembleAgentSpendStatus,
183
+ authoredPolicy,
179
184
  ceremonyPhaseLabel,
180
185
  continueWithDexter,
181
186
  createAnonServerPolicy,
@@ -197,5 +202,6 @@ export {
197
202
  setActiveHandle,
198
203
  subscribe as subscribeWallet,
199
204
  switchWallet,
200
- syncAcceptedPasskeys
205
+ syncAcceptedPasskeys,
206
+ usdToAtomic
201
207
  };
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
2
- import { a as CeremonyPhase, S as SignInResult, P as PasskeyLoginTokens, C as ConnectVault, b as ConnectError, e as StoredWallet, d as PasskeySignalSupport, R as ResolvedIdentity } from './signals-BaXobUfB.js';
2
+ import { b as CeremonyPhase, S as SignInResult, P as PasskeyLoginTokens, c as ConnectVault, d as ConnectError, i as StoredWallet, f as PasskeySignalSupport, R as ResolvedIdentity, C as CreateWalletResult } from './signals-qc4rDyQ6.js';
3
3
  import { ReactElement, ReactNode } from 'react';
4
4
 
5
5
  type ConnectStatus = 'idle' | 'pending' | 'done' | 'error';
@@ -168,4 +168,34 @@ interface UseIdentityConfig {
168
168
  }
169
169
  declare function useIdentity({ accountToken }: UseIdentityConfig): ResolvedIdentity;
170
170
 
171
- export { type ConnectStatus, DexterButton, type DexterButtonProps, DexterMark, DexterWalletChip, type DexterWalletChipProps, DexterWalletMenu, type DexterWalletMenuProps, SignInWithDexter, type SignInWithDexterProps, type UseDexterWallet, type UseIdentityConfig, type UseSignInWithDexter, type UseSignInWithDexterConfig, useDexterWallet, useIdentity, useSignInWithDexter };
171
+ interface AllowanceChipsProps {
172
+ /** The authored USD amount as a raw string, or null when nothing is chosen.
173
+ * NONE selected initially → pass null. Preset chips echo '5' / '20' / '50';
174
+ * Custom echoes whatever the user types. */
175
+ value: string | null;
176
+ /** Fired with the raw USD string (or null when Custom is opened empty). */
177
+ onChange: (usd: string | null) => void;
178
+ /** Extra className composed after the brand classes. */
179
+ className?: string;
180
+ }
181
+ /** The consent-at-birth allowance chips. */
182
+ declare function AllowanceChips(props: AllowanceChipsProps): ReactElement;
183
+
184
+ interface CreateWalletPanelProps {
185
+ /** Fired with the minted wallet the moment creation succeeds. */
186
+ onCreated?: (result: CreateWalletResult) => void;
187
+ /** Fired with the typed error if the ceremony fails. */
188
+ onError?: (error: ConnectError) => void;
189
+ /** dexter-api base. Default https://api.dexter.cash (createWallet's default). */
190
+ apiBase?: string;
191
+ /** Where the WebAuthn ceremony runs. Default 'auto' (createWallet's default). */
192
+ transport?: 'auto' | 'popup' | 'inline';
193
+ /** Render the optional "Name your wallet" field. Default true. */
194
+ showName?: boolean;
195
+ /** Extra className composed after the brand classes. */
196
+ className?: string;
197
+ }
198
+ /** The turnkey consent-at-birth create panel. */
199
+ declare function CreateWalletPanel(props: CreateWalletPanelProps): ReactElement;
200
+
201
+ export { AllowanceChips, type AllowanceChipsProps, type ConnectStatus, CreateWalletPanel, type CreateWalletPanelProps, DexterButton, type DexterButtonProps, DexterMark, DexterWalletChip, type DexterWalletChipProps, DexterWalletMenu, type DexterWalletMenuProps, SignInWithDexter, type SignInWithDexterProps, type UseDexterWallet, type UseIdentityConfig, type UseSignInWithDexter, type UseSignInWithDexterConfig, useDexterWallet, useIdentity, useSignInWithDexter };
package/dist/react.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  ConnectError,
3
+ authoredPolicy,
3
4
  ceremonyPhaseLabel,
4
5
  createPasskeySigner,
6
+ createWallet,
5
7
  ejectActiveWallet,
6
8
  getActiveHandle,
7
9
  getCredentialId,
@@ -14,7 +16,7 @@ import {
14
16
  setActiveHandle,
15
17
  subscribe,
16
18
  switchWallet
17
- } from "./chunk-Y22JFT53.js";
19
+ } from "./chunk-2QZ5QLGI.js";
18
20
  import {
19
21
  DexterButton,
20
22
  DexterMark,
@@ -368,7 +370,223 @@ function useIdentity({ accountToken }) {
368
370
  [accountToken, activeHandle]
369
371
  );
370
372
  }
373
+
374
+ // src/AllowanceChips.tsx
375
+ import { useEffect as useEffect6, useState as useState4 } from "react";
376
+
377
+ // src/consentStyles.ts
378
+ var STYLE_ID2 = "dexter-connect-consent-styles";
379
+ var CONSENT_CSS = `
380
+ .dx-allow{
381
+ --dx-ember:#f26c18; --dx-ember-2:#ba3a00; --dx-fg:#fff4ea; --dx-radius:0px;
382
+ display:flex; flex-wrap:wrap; gap:8px; align-items:center;
383
+ }
384
+ .dx-allow__chip{
385
+ display:inline-flex; align-items:center; justify-content:center; padding:9px 15px;
386
+ font:inherit; font-weight:600; font-size:.78rem; letter-spacing:.12em; text-transform:uppercase;
387
+ font-variant-numeric:tabular-nums; cursor:pointer; background:transparent; color:inherit;
388
+ border:1px solid color-mix(in srgb,var(--dx-ember) 45%,transparent);
389
+ border-radius:var(--dx-radius,0px); -webkit-tap-highlight-color:transparent;
390
+ transition:filter .16s ease, box-shadow .16s ease, border-color .16s ease, background .16s ease, color .16s ease;
391
+ }
392
+ .dx-allow__chip:hover{ border-color:color-mix(in srgb,var(--dx-ember) 70%,transparent); filter:brightness(1.05); }
393
+ .dx-allow__chip:focus-visible{ outline:none; box-shadow:0 0 0 3px color-mix(in srgb,var(--dx-ember) 34%,transparent); }
394
+ .dx-allow__chip--active{
395
+ background:linear-gradient(135deg,var(--dx-ember,#f26c18),var(--dx-ember-2,#ba3a00));
396
+ color:var(--dx-fg,#fff4ea); border-color:color-mix(in srgb,var(--dx-ember) 55%,transparent);
397
+ box-shadow:0 10px 22px color-mix(in srgb,var(--dx-ember) 22%,transparent);
398
+ }
399
+ .dx-allow__input{
400
+ flex:1 1 130px; min-width:120px; padding:9px 13px; font:inherit; font-size:.9rem;
401
+ font-variant-numeric:tabular-nums; color:inherit; background:transparent;
402
+ border:1px solid color-mix(in srgb,var(--dx-ember) 45%,transparent);
403
+ border-radius:var(--dx-radius,0px); -webkit-tap-highlight-color:transparent;
404
+ transition:border-color .16s ease, box-shadow .16s ease;
405
+ }
406
+ .dx-allow__input:focus, .dx-allow__input:focus-visible{
407
+ outline:none; border-color:color-mix(in srgb,var(--dx-ember) 70%,transparent);
408
+ box-shadow:0 0 0 3px color-mix(in srgb,var(--dx-ember) 30%,transparent);
409
+ }
410
+ .dx-allow__input::placeholder{ color:currentColor; opacity:.5; letter-spacing:.02em; text-transform:none; }
411
+
412
+ .dx-cwp{
413
+ --dx-ember:#f26c18; --dx-ember-2:#ba3a00; --dx-fg:#fff4ea; --dx-radius:0px;
414
+ display:flex; flex-direction:column; gap:16px; font:inherit;
415
+ }
416
+ .dx-cwp__field{ display:flex; flex-direction:column; gap:8px; }
417
+ .dx-cwp__label{ font-size:.7rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase; opacity:.62; }
418
+ .dx-cwp__name{
419
+ padding:10px 13px; font:inherit; font-size:.95rem; color:inherit; background:transparent;
420
+ border:1px solid color-mix(in srgb,currentColor 22%,transparent); border-radius:var(--dx-radius,0px);
421
+ -webkit-tap-highlight-color:transparent; transition:border-color .16s ease, box-shadow .16s ease;
422
+ }
423
+ .dx-cwp__name:focus, .dx-cwp__name:focus-visible{
424
+ outline:none; border-color:color-mix(in srgb,var(--dx-ember) 60%,transparent);
425
+ box-shadow:0 0 0 3px color-mix(in srgb,var(--dx-ember) 26%,transparent);
426
+ }
427
+ .dx-cwp__name::placeholder{ color:currentColor; opacity:.42; }
428
+ .dx-cwp__fine{ font-size:.76rem; line-height:1.5; opacity:.66; }
429
+ .dx-cwp__err{ font-size:.78rem; line-height:1.45; color:#e5552e; }
430
+ `;
431
+ function ensureConsentStyles() {
432
+ if (typeof document === "undefined") return;
433
+ if (document.getElementById(STYLE_ID2)) return;
434
+ const el = document.createElement("style");
435
+ el.id = STYLE_ID2;
436
+ el.textContent = CONSENT_CSS;
437
+ document.head.appendChild(el);
438
+ }
439
+ ensureConsentStyles();
440
+
441
+ // src/AllowanceChips.tsx
442
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
443
+ var PRESETS = [
444
+ { label: "$5", num: "5" },
445
+ { label: "$20", num: "20" },
446
+ { label: "$50", num: "50" }
447
+ ];
448
+ var PRESET_NUMS = PRESETS.map((p) => p.num);
449
+ var isPreset = (v) => v !== null && PRESET_NUMS.includes(v);
450
+ function AllowanceChips(props) {
451
+ const { value, onChange, className } = props;
452
+ useEffect6(ensureConsentStyles, []);
453
+ const [customOpen, setCustomOpen] = useState4(false);
454
+ const customActive = customOpen || value !== null && !isPreset(value);
455
+ const selectPreset = (num) => {
456
+ setCustomOpen(false);
457
+ onChange(num);
458
+ };
459
+ const selectCustom = () => {
460
+ setCustomOpen(true);
461
+ onChange(isPreset(value) ? null : value);
462
+ };
463
+ const onChipKeyDown = (e, select) => {
464
+ if (e.key === "Enter" || e.key === " " || e.key === "Spacebar") {
465
+ e.preventDefault();
466
+ select();
467
+ }
468
+ };
469
+ return /* @__PURE__ */ jsxs4("div", { className: cx("dx-allow", className), role: "radiogroup", "aria-label": "Monthly agent allowance", children: [
470
+ PRESETS.map(({ label, num }) => {
471
+ const checked = !customActive && value === num;
472
+ return /* @__PURE__ */ jsx4(
473
+ "div",
474
+ {
475
+ role: "radio",
476
+ "aria-checked": checked,
477
+ tabIndex: 0,
478
+ className: cx("dx-allow__chip", checked && "dx-allow__chip--active"),
479
+ onClick: () => selectPreset(num),
480
+ onKeyDown: (e) => onChipKeyDown(e, () => selectPreset(num)),
481
+ children: label
482
+ },
483
+ num
484
+ );
485
+ }),
486
+ /* @__PURE__ */ jsx4(
487
+ "div",
488
+ {
489
+ role: "radio",
490
+ "aria-checked": customActive,
491
+ tabIndex: 0,
492
+ className: cx("dx-allow__chip", customActive && "dx-allow__chip--active"),
493
+ onClick: selectCustom,
494
+ onKeyDown: (e) => onChipKeyDown(e, selectCustom),
495
+ children: "Custom"
496
+ }
497
+ ),
498
+ customActive && /* @__PURE__ */ jsx4(
499
+ "input",
500
+ {
501
+ className: "dx-allow__input",
502
+ inputMode: "decimal",
503
+ placeholder: "$ amount",
504
+ "aria-label": "Custom monthly allowance in USD",
505
+ value: value ?? "",
506
+ autoFocus: true,
507
+ onChange: (e) => onChange(e.target.value)
508
+ }
509
+ )
510
+ ] });
511
+ }
512
+
513
+ // src/CreateWalletPanel.tsx
514
+ import { useEffect as useEffect7, useState as useState5 } from "react";
515
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
516
+ var FINE_PRINT = "Your number, your tap. Agents can never spend past it, and you can revoke any time.";
517
+ function CreateWalletPanel(props) {
518
+ const { onCreated, onError, apiBase, transport, showName = true, className } = props;
519
+ useEffect7(ensureConsentStyles, []);
520
+ useEffect7(ensureDexterButtonStyles, []);
521
+ const [name, setName] = useState5("");
522
+ const [value, setValue] = useState5(null);
523
+ const [running, setRunning] = useState5(false);
524
+ const [phase, setPhase] = useState5(null);
525
+ const [error, setError] = useState5(null);
526
+ const policy = authoredPolicy(value ?? "");
527
+ const handleCreate = async () => {
528
+ if (running) return;
529
+ if (!policy) return;
530
+ setError(null);
531
+ setRunning(true);
532
+ setPhase(null);
533
+ try {
534
+ const result = await createWallet({
535
+ name: name.trim() || "Dexter Wallet",
536
+ spendPolicy: policy,
537
+ apiBase,
538
+ transport,
539
+ onPhase: setPhase
540
+ });
541
+ onCreated?.(result);
542
+ } catch (e) {
543
+ const err = e instanceof ConnectError ? e : new ConnectError("create_failed", e instanceof Error ? e.message : String(e));
544
+ setError(err);
545
+ onError?.(err);
546
+ } finally {
547
+ setRunning(false);
548
+ setPhase(null);
549
+ }
550
+ };
551
+ return /* @__PURE__ */ jsxs5("div", { className: cx("dx-cwp", className), children: [
552
+ showName && /* @__PURE__ */ jsxs5("div", { className: "dx-cwp__field", children: [
553
+ /* @__PURE__ */ jsx5("label", { className: "dx-cwp__label", htmlFor: "dx-cwp-name", children: "Name your wallet" }),
554
+ /* @__PURE__ */ jsx5(
555
+ "input",
556
+ {
557
+ id: "dx-cwp-name",
558
+ className: "dx-cwp__name",
559
+ maxLength: 40,
560
+ placeholder: "Dexter Wallet",
561
+ value: name,
562
+ disabled: running,
563
+ onChange: (e) => setName(e.target.value)
564
+ }
565
+ )
566
+ ] }),
567
+ /* @__PURE__ */ jsxs5("div", { className: "dx-cwp__field", children: [
568
+ /* @__PURE__ */ jsx5("span", { className: "dx-cwp__label", children: "What agents may spend, per 30 days" }),
569
+ /* @__PURE__ */ jsx5(AllowanceChips, { value, onChange: setValue })
570
+ ] }),
571
+ /* @__PURE__ */ jsx5("p", { className: "dx-cwp__fine", children: FINE_PRINT }),
572
+ error && /* @__PURE__ */ jsx5("div", { className: "dx-cwp__err", role: "alert", children: error.message || error.code }),
573
+ /* @__PURE__ */ jsx5(
574
+ DexterButton,
575
+ {
576
+ block: true,
577
+ className: "dx-cwp__cta",
578
+ loading: running,
579
+ loadingLabel: phase ? ceremonyPhaseLabel(phase) : "Creating\u2026",
580
+ disabled: !policy,
581
+ onClick: handleCreate,
582
+ children: error ? "Retry" : "Create your Dexter Wallet"
583
+ }
584
+ )
585
+ ] });
586
+ }
371
587
  export {
588
+ AllowanceChips,
589
+ CreateWalletPanel,
372
590
  DexterButton,
373
591
  DexterMark,
374
592
  DexterWalletChip,
@@ -0,0 +1,72 @@
1
+ import { JWTPayload, JWK, JSONWebKeySet } from 'jose';
2
+
3
+ /**
4
+ * @dexterai/connect/server — offline Dexter session verification.
5
+ *
6
+ * The server half of Sign in with Dexter (CONTRACT-dexter-session-token.md):
7
+ * a local ES256 signature check against the published JWKS — no call to
8
+ * Supabase or dexter-api on the hot path, so it runs on Node and edge
9
+ * runtimes alike. Pre-hook tokens (no `dexter` claim) verify as signed-in
10
+ * with `vaultAddress: null`; once the access-token hook is enabled the
11
+ * claim appears with no code change here.
12
+ *
13
+ * Phase 1 issuer is Supabase (CONTRACT §3); everything is parameterized on
14
+ * (iss, jwksUrl) so the Phase-2 sovereign cutover is a config flip.
15
+ */
16
+
17
+ declare const DEFAULT_ISS = "https://qdgumpoqnthrjfmqziwm.supabase.co/auth/v1";
18
+ declare const DEFAULT_AUDIENCE = "authenticated";
19
+ /** The namespaced claim sealed into the token by the access-token hook. */
20
+ interface DexterClaim {
21
+ ver: number;
22
+ /** Swig state address (base58) — the canonical Dexter Wallet identity. */
23
+ vault: string;
24
+ /** 16-byte passkey handle, base64url; absent on rows without one. */
25
+ userHandle?: string;
26
+ agentGrant?: unknown;
27
+ }
28
+ type VerifyFailureReason = 'no_token' | 'invalid' | 'expired' | 'issuer_mismatch' | 'audience_mismatch';
29
+ type DexterSession = {
30
+ isSignedIn: true;
31
+ sub: string;
32
+ vaultAddress: string | null;
33
+ userHandle: string | null;
34
+ agentGrant: unknown;
35
+ sessionId: string | null;
36
+ aal: string | null;
37
+ claims: JWTPayload & {
38
+ dexter?: DexterClaim;
39
+ };
40
+ } | {
41
+ isSignedIn: false;
42
+ reason: VerifyFailureReason;
43
+ };
44
+ interface VerifyOptions {
45
+ /** Expected issuer. Phase 1 default: the Dexter Supabase project. */
46
+ iss?: string;
47
+ /** JWKS location; defaults to `${iss}/.well-known/jwks.json`. */
48
+ jwksUrl?: string;
49
+ /**
50
+ * Public key(s) for fully networkless verification (a JWK or a JWKS).
51
+ * When omitted, the JWKS is fetched once and cached in-instance.
52
+ */
53
+ jwtKey?: JWK | JSONWebKeySet;
54
+ /** Expected audience. Default: Supabase's `authenticated`. */
55
+ audience?: string;
56
+ }
57
+ /** A fetch-API Request or anything with a node-style headers bag. */
58
+ type RequestLike = Request | {
59
+ headers: Record<string, string | string[] | undefined>;
60
+ };
61
+ interface DexterClient {
62
+ verifyDexterSession(token: string): Promise<DexterSession>;
63
+ authenticateRequest(req: RequestLike): Promise<DexterSession>;
64
+ }
65
+ declare function createDexterClient(options?: VerifyOptions): DexterClient;
66
+ /**
67
+ * One-off verification. For servers verifying many requests against a
68
+ * remote JWKS, create a client once instead so the key set caches.
69
+ */
70
+ declare function verifyDexterSession(token: string, options?: VerifyOptions): Promise<DexterSession>;
71
+
72
+ export { DEFAULT_AUDIENCE, DEFAULT_ISS, type DexterClaim, type DexterClient, type DexterSession, type RequestLike, type VerifyFailureReason, type VerifyOptions, createDexterClient, verifyDexterSession };
package/dist/server.js ADDED
@@ -0,0 +1,90 @@
1
+ // src/server.ts
2
+ import {
3
+ createLocalJWKSet,
4
+ createRemoteJWKSet,
5
+ jwtVerify,
6
+ errors as joseErrors
7
+ } from "jose";
8
+ var DEFAULT_ISS = "https://qdgumpoqnthrjfmqziwm.supabase.co/auth/v1";
9
+ var DEFAULT_AUDIENCE = "authenticated";
10
+ function buildGetKey(opts) {
11
+ if (opts.jwtKey) {
12
+ const set = "keys" in opts.jwtKey ? opts.jwtKey : { keys: [opts.jwtKey] };
13
+ return createLocalJWKSet(set);
14
+ }
15
+ const iss = opts.iss ?? DEFAULT_ISS;
16
+ const url = new URL(opts.jwksUrl ?? `${iss}/.well-known/jwks.json`);
17
+ return createRemoteJWKSet(url, {
18
+ cacheMaxAge: 6e5,
19
+ cooldownDuration: 3e4,
20
+ timeoutDuration: 5e3
21
+ });
22
+ }
23
+ function failureReason(err) {
24
+ if (err instanceof joseErrors.JWTExpired) return "expired";
25
+ if (err instanceof joseErrors.JWTClaimValidationFailed) {
26
+ if (err.claim === "iss") return "issuer_mismatch";
27
+ if (err.claim === "aud") return "audience_mismatch";
28
+ if (err.claim === "exp") return "expired";
29
+ }
30
+ return "invalid";
31
+ }
32
+ function bearerFrom(req) {
33
+ let raw;
34
+ const headers = req.headers;
35
+ if (headers && typeof headers.get === "function") {
36
+ raw = headers.get("authorization");
37
+ } else {
38
+ const bag = headers;
39
+ raw = bag.authorization ?? bag.Authorization;
40
+ }
41
+ const value = Array.isArray(raw) ? raw[0] : raw;
42
+ if (!value) return null;
43
+ const match = /^Bearer\s+(.+)$/i.exec(value);
44
+ return match ? match[1] : null;
45
+ }
46
+ function createDexterClient(options = {}) {
47
+ const iss = options.iss ?? DEFAULT_ISS;
48
+ const audience = options.audience ?? DEFAULT_AUDIENCE;
49
+ const getKey = buildGetKey(options);
50
+ async function verify(token) {
51
+ try {
52
+ const { payload } = await jwtVerify(token, getKey, {
53
+ issuer: iss,
54
+ audience,
55
+ algorithms: ["ES256"]
56
+ // pinned — defeats alg-confusion / alg:none
57
+ });
58
+ const dexter = payload.dexter;
59
+ return {
60
+ isSignedIn: true,
61
+ sub: payload.sub ?? "",
62
+ vaultAddress: dexter?.vault ?? null,
63
+ userHandle: dexter?.userHandle ?? null,
64
+ agentGrant: dexter?.agentGrant ?? null,
65
+ sessionId: payload.session_id ?? null,
66
+ aal: payload.aal ?? null,
67
+ claims: payload
68
+ };
69
+ } catch (err) {
70
+ return { isSignedIn: false, reason: failureReason(err) };
71
+ }
72
+ }
73
+ return {
74
+ verifyDexterSession: verify,
75
+ async authenticateRequest(req) {
76
+ const token = bearerFrom(req);
77
+ if (!token) return { isSignedIn: false, reason: "no_token" };
78
+ return verify(token);
79
+ }
80
+ };
81
+ }
82
+ function verifyDexterSession(token, options = {}) {
83
+ return createDexterClient(options).verifyDexterSession(token);
84
+ }
85
+ export {
86
+ DEFAULT_AUDIENCE,
87
+ DEFAULT_ISS,
88
+ createDexterClient,
89
+ verifyDexterSession
90
+ };
@@ -60,6 +60,55 @@ declare class ConnectError extends Error {
60
60
  constructor(code: string, message?: string);
61
61
  }
62
62
 
63
+ /** Consent-at-birth allowance (Branch rulings 2026-07-02/03).
64
+ * The user authors the number; zero is not consent; TTL is fixed 30d and
65
+ * never user-editable; no caller may invent a default. */
66
+ declare const SESSION_TTL_30D = "2592000";
67
+ interface SpendPolicy {
68
+ /** Role-2 allowance, atomic USDC (6dp), decimal string. User-authored. */
69
+ spendLimitAtomic: string;
70
+ /** Fixed 30d. Present for wire compatibility; always SESSION_TTL_30D. */
71
+ sessionTtlSeconds: string;
72
+ }
73
+ /** Parse user-entered USD ("5", "$20", "1,000", "20.5") to atomic USDC.
74
+ * Null on anything invalid — callers must not invent a fallback. */
75
+ declare function usdToAtomic(input: string): bigint | null;
76
+ /** Null when invalid or zero (zero is not consent). */
77
+ declare function authoredPolicy(usdInput: string): SpendPolicy | null;
78
+
79
+ interface CreateWalletConfig extends DexterConnectConfig {
80
+ /** Label for the passkey in the OS keychain AND the wallet roster. Set at
81
+ * creation — the only moment naming is guaranteed to stick. Default "Dexter Wallet". */
82
+ name?: string;
83
+ /** RP id for the new credential. Default "dexter.cash". */
84
+ rpId?: string;
85
+ /** Consent-at-birth allowance the user authored at creation (chips $5/$20/$50
86
+ * or Custom; zero is not consent; build it with authoredPolicy()). When
87
+ * present it rides the /initialize body so the number becomes the server-side
88
+ * write-once consent record. The TTL is ruled fixed 30d — whatever the object
89
+ * carries, the wire always sends SESSION_TTL_30D. Absent → no policy authored
90
+ * (the vault initializes without one; nothing invents a default). */
91
+ spendPolicy?: SpendPolicy;
92
+ /** Called as the ceremony progresses, for live "connecting steps" UI:
93
+ * challenge → passkey → verifying → finalizing. */
94
+ onPhase?: (phase: CeremonyPhase) => void;
95
+ }
96
+ interface CreateWalletResult {
97
+ /** Server-minted 16-byte user handle, base64url — the vault identity. */
98
+ handle: string;
99
+ /** base64url credential id of the new passkey. */
100
+ credentialId: string;
101
+ /** The freshly initialized vault (swig not yet deployed; deploys lazily). */
102
+ vault: ConnectVault;
103
+ }
104
+ /**
105
+ * Mint a brand-new Dexter wallet (passkey + vault) and make it the active wallet.
106
+ *
107
+ * One passkey approval. Throws ConnectError on any failed leg (the `code` is the
108
+ * server's error string, or webauthn_failed / no_credential for the ceremony).
109
+ */
110
+ declare function createWallet(config?: CreateWalletConfig): Promise<CreateWalletResult>;
111
+
63
112
  type IdentityKind = 'passkey-vault' | 'account' | 'none';
64
113
  interface IdentityInput {
65
114
  /** An account session token (e.g. Supabase access_token) when present, else null. */
@@ -180,4 +229,4 @@ declare function syncAcceptedPasskeys(args: {
180
229
  rpId?: string;
181
230
  }): Promise<boolean>;
182
231
 
183
- export { ACTIVE_WALLET_STORAGE_KEY as A, type ConnectVault as C, type DexterConnectConfig as D, type IdentityKind as I, type PasskeyLoginTokens as P, type ResolvedIdentity as R, type SignInResult as S, type CeremonyPhase as a, ConnectError as b, type IdentityInput as c, type PasskeySignalSupport as d, type StoredWallet as e, ejectActiveWallet as f, forgetWallet as g, getActiveHandle as h, getCredentialId as i, prunePasskey as j, resolveIdentity as k, listWallets as l, subscribe as m, switchWallet as n, syncAcceptedPasskeys as o, passkeySignalSupport as p, renamePasskey as r, setActiveHandle as s };
232
+ export { ACTIVE_WALLET_STORAGE_KEY as A, type CreateWalletResult as C, type DexterConnectConfig as D, type IdentityKind as I, type PasskeyLoginTokens as P, type ResolvedIdentity as R, type SignInResult as S, type CreateWalletConfig as a, type CeremonyPhase as b, type ConnectVault as c, ConnectError as d, type IdentityInput as e, type PasskeySignalSupport as f, SESSION_TTL_30D as g, type SpendPolicy as h, type StoredWallet as i, authoredPolicy as j, createWallet as k, ejectActiveWallet as l, forgetWallet as m, getActiveHandle as n, getCredentialId as o, listWallets as p, passkeySignalSupport as q, prunePasskey as r, renamePasskey as s, resolveIdentity as t, setActiveHandle as u, subscribe as v, switchWallet as w, syncAcceptedPasskeys as x, usdToAtomic as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/connect",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Sign in with Dexter — passkey connector. Composes @dexterai/vault.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -15,6 +15,10 @@
15
15
  "./worldid": {
16
16
  "types": "./dist/worldid.d.ts",
17
17
  "import": "./dist/worldid.js"
18
+ },
19
+ "./server": {
20
+ "types": "./dist/server.d.ts",
21
+ "import": "./dist/server.js"
18
22
  }
19
23
  },
20
24
  "files": [
@@ -26,7 +30,7 @@
26
30
  "typecheck": "tsc --noEmit"
27
31
  },
28
32
  "peerDependencies": {
29
- "@dexterai/vault": ">=0.22",
33
+ "@dexterai/vault": ">=0.30.0",
30
34
  "@solana/web3.js": "^1.98.0",
31
35
  "@worldcoin/idkit": "^4.1.8",
32
36
  "react": ">=18"
@@ -40,16 +44,19 @@
40
44
  }
41
45
  },
42
46
  "devDependencies": {
43
- "@dexterai/vault": "^0.24.0",
47
+ "@dexterai/vault": "^0.30.0",
44
48
  "@solana/web3.js": "^1.98.4",
45
49
  "@types/react": "^19.1.12",
50
+ "@types/react-dom": "^19.2.3",
46
51
  "@worldcoin/idkit": "^4.1.8",
52
+ "happy-dom": "^15.11.7",
47
53
  "react": "^19.2.5",
48
54
  "tsup": "^8.5.0",
49
55
  "typescript": "^5.6.2",
50
56
  "vitest": "^4.1.9"
51
57
  },
52
58
  "dependencies": {
53
- "@simplewebauthn/browser": "^13.3.0"
59
+ "@simplewebauthn/browser": "^13.3.0",
60
+ "jose": "^6.2.3"
54
61
  }
55
62
  }