@dexterai/connect 0.12.0 → 0.14.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.
@@ -8,6 +8,122 @@ var ConnectError = class extends Error {
8
8
  }
9
9
  };
10
10
 
11
+ // src/walletStore.ts
12
+ var ACTIVE_HANDLE_KEY = "dexter:passkey:userHandle";
13
+ var ROSTER_KEY = "dexter:passkey:wallets";
14
+ var listeners = /* @__PURE__ */ new Set();
15
+ function hasStorage() {
16
+ try {
17
+ return typeof window !== "undefined" && !!window.localStorage;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ function readRoster() {
23
+ if (!hasStorage()) return [];
24
+ try {
25
+ const raw = window.localStorage.getItem(ROSTER_KEY);
26
+ if (!raw) return [];
27
+ const parsed = JSON.parse(raw);
28
+ if (!Array.isArray(parsed)) return [];
29
+ return parsed.filter(
30
+ (w) => !!w && typeof w === "object" && typeof w.handle === "string"
31
+ );
32
+ } catch {
33
+ return [];
34
+ }
35
+ }
36
+ function writeRoster(wallets) {
37
+ if (!hasStorage()) return;
38
+ try {
39
+ window.localStorage.setItem(ROSTER_KEY, JSON.stringify(wallets));
40
+ } catch {
41
+ }
42
+ }
43
+ function emit() {
44
+ for (const l of listeners) {
45
+ try {
46
+ l();
47
+ } catch {
48
+ }
49
+ }
50
+ }
51
+ function getActiveHandle() {
52
+ if (!hasStorage()) return null;
53
+ try {
54
+ return window.localStorage.getItem(ACTIVE_HANDLE_KEY);
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function setActiveHandle(handle, label, credentialId) {
60
+ if (!hasStorage() || !handle) return;
61
+ try {
62
+ window.localStorage.setItem(ACTIVE_HANDLE_KEY, handle);
63
+ } catch {
64
+ return;
65
+ }
66
+ const roster = readRoster();
67
+ const existing = roster.find((w) => w.handle === handle);
68
+ const now = Date.now();
69
+ if (existing) {
70
+ existing.lastUsedAt = now;
71
+ if (label !== void 0) existing.label = label;
72
+ if (credentialId !== void 0) existing.credentialId = credentialId;
73
+ } else {
74
+ roster.push({ handle, label, credentialId, lastUsedAt: now });
75
+ }
76
+ writeRoster(roster);
77
+ emit();
78
+ }
79
+ function getCredentialId(handle) {
80
+ return readRoster().find((w) => w.handle === handle)?.credentialId;
81
+ }
82
+ function ejectActiveWallet(opts) {
83
+ if (!hasStorage()) return;
84
+ const current = getActiveHandle();
85
+ try {
86
+ window.localStorage.removeItem(ACTIVE_HANDLE_KEY);
87
+ } catch {
88
+ }
89
+ if (opts?.forget && current) {
90
+ writeRoster(readRoster().filter((w) => w.handle !== current));
91
+ }
92
+ emit();
93
+ }
94
+ function listWallets() {
95
+ return readRoster().sort((a, b) => (b.lastUsedAt ?? 0) - (a.lastUsedAt ?? 0));
96
+ }
97
+ function switchWallet(handle) {
98
+ if (!readRoster().some((w) => w.handle === handle)) return false;
99
+ setActiveHandle(handle);
100
+ return true;
101
+ }
102
+ function forgetWallet(handle) {
103
+ if (getActiveHandle() === handle) {
104
+ ejectActiveWallet({ forget: true });
105
+ return;
106
+ }
107
+ writeRoster(readRoster().filter((w) => w.handle !== handle));
108
+ emit();
109
+ }
110
+ function subscribe(listener) {
111
+ listeners.add(listener);
112
+ if (hasStorage() && listeners.size === 1) {
113
+ window.addEventListener("storage", onStorageEvent);
114
+ }
115
+ return () => {
116
+ listeners.delete(listener);
117
+ if (hasStorage() && listeners.size === 0) {
118
+ window.removeEventListener("storage", onStorageEvent);
119
+ }
120
+ };
121
+ }
122
+ function onStorageEvent(e) {
123
+ if (e.key === ACTIVE_HANDLE_KEY || e.key === ROSTER_KEY || e.key === null) emit();
124
+ }
125
+ var ACTIVE_WALLET_STORAGE_KEY = ACTIVE_HANDLE_KEY;
126
+
11
127
  // src/base64.ts
12
128
  function base64ToBytes(s) {
13
129
  const bin = atob(s);
@@ -145,8 +261,145 @@ function makeNonce() {
145
261
  return `rid-${new URL(location.href).searchParams.get("v") ?? ""}-${typeof performance !== "undefined" ? performance.now() : 0}`;
146
262
  }
147
263
 
148
- // src/relay.ts
264
+ // src/enroll.ts
149
265
  var DEFAULT_API_BASE = "https://api.dexter.cash";
266
+ var DEFAULT_RP_ID = "dexter.cash";
267
+ var DEFAULT_WALLET_NAME = "Dexter Wallet";
268
+ async function createWallet(config = {}) {
269
+ if (shouldUsePopup(config.transport)) {
270
+ return openCeremonyPopup("create", {
271
+ connectHost: config.connectHost,
272
+ name: config.name,
273
+ apiBase: config.apiBase
274
+ });
275
+ }
276
+ if (typeof navigator === "undefined" || !navigator.credentials) {
277
+ throw new ConnectError("webauthn_unsupported", "WebAuthn unavailable in this environment");
278
+ }
279
+ const apiBase = (config.apiBase ?? DEFAULT_API_BASE).replace(/\/$/, "");
280
+ const rpId = config.rpId ?? DEFAULT_RP_ID;
281
+ const name = config.name && config.name.trim() || DEFAULT_WALLET_NAME;
282
+ config.onPhase?.("challenge");
283
+ const options = await fetchEnrollChallenge(apiBase);
284
+ config.onPhase?.("passkey");
285
+ const credential = await createCredential(options, name, rpId);
286
+ config.onPhase?.("verifying");
287
+ const enrolled = await submitEnrollComplete(apiBase, credential);
288
+ config.onPhase?.("finalizing");
289
+ const init = await initializeVault(apiBase, enrolled.userHandle, enrolled.credentialId);
290
+ setActiveHandle(enrolled.userHandle, name, enrolled.credentialId);
291
+ return {
292
+ handle: enrolled.userHandle,
293
+ credentialId: enrolled.credentialId,
294
+ vault: {
295
+ vaultPda: init.vaultPda,
296
+ swigAddress: init.swigStateAddress,
297
+ // FAIL SAFE: never invent a receive address — null until the server returns
298
+ // one (depositing to the config PDA would strand funds).
299
+ receiveAddress: init.receiveAddress ?? null,
300
+ usdcAta: null,
301
+ // swig not deployed yet (counterfactual pattern)
302
+ publicKey: enrolled.publicKey,
303
+ userHandle: enrolled.userHandle,
304
+ credentialId: enrolled.credentialId
305
+ }
306
+ };
307
+ }
308
+ async function fetchEnrollChallenge(apiBase) {
309
+ const res = await fetch(`${apiBase}/api/passkey-anon/enroll/challenge`, {
310
+ method: "POST",
311
+ headers: { "content-type": "application/json" },
312
+ body: "{}"
313
+ });
314
+ if (!res.ok) throw new ConnectError("enroll_challenge_failed", `enroll/challenge ${res.status}`);
315
+ const data = await res.json();
316
+ if (!data?.options?.challenge) {
317
+ throw new ConnectError("enroll_challenge_malformed", "no creation options in response");
318
+ }
319
+ return data.options;
320
+ }
321
+ async function createCredential(options, name, rpId) {
322
+ let credential;
323
+ try {
324
+ credential = await navigator.credentials.create({
325
+ publicKey: buildCreationOptions(options, name, rpId)
326
+ });
327
+ } catch (err) {
328
+ throw new ConnectError("webauthn_failed", err instanceof Error ? err.message : String(err));
329
+ }
330
+ if (!credential || credential.type !== "public-key") {
331
+ throw new ConnectError("no_credential", "authenticator returned no credential");
332
+ }
333
+ return credential;
334
+ }
335
+ async function submitEnrollComplete(apiBase, credential) {
336
+ const attestation = credential.response;
337
+ const credentialJson = {
338
+ id: credential.id,
339
+ rawId: bytesToBase64url(new Uint8Array(credential.rawId)),
340
+ type: credential.type,
341
+ response: {
342
+ attestationObject: bytesToBase64url(new Uint8Array(attestation.attestationObject)),
343
+ clientDataJSON: bytesToBase64url(new Uint8Array(attestation.clientDataJSON)),
344
+ transports: typeof attestation.getTransports === "function" ? attestation.getTransports() : []
345
+ },
346
+ clientExtensionResults: credential.getClientExtensionResults?.() ?? {},
347
+ authenticatorAttachment: credential.authenticatorAttachment ?? null
348
+ };
349
+ const res = await fetch(`${apiBase}/api/passkey-anon/enroll/complete`, {
350
+ method: "POST",
351
+ headers: { "content-type": "application/json" },
352
+ body: JSON.stringify({ credential: credentialJson })
353
+ });
354
+ if (!res.ok) throw new ConnectError(await readErrorCode(res), `enroll/complete ${res.status}`);
355
+ return await res.json();
356
+ }
357
+ async function initializeVault(apiBase, userHandle, credentialId) {
358
+ const res = await fetch(`${apiBase}/api/passkey-vault-anon/initialize`, {
359
+ method: "POST",
360
+ headers: { "content-type": "application/json" },
361
+ body: JSON.stringify({ userHandle, credentialId, coolingOffSeconds: 0 })
362
+ });
363
+ if (!res.ok) throw new ConnectError(await readErrorCode(res), `initialize ${res.status}`);
364
+ return await res.json();
365
+ }
366
+ function toBuf(b64url) {
367
+ return base64urlToBytes(b64url).buffer.slice(0);
368
+ }
369
+ function buildCreationOptions(o, name, rpId) {
370
+ return {
371
+ // rp.name = the site shown in the keychain; user.name/displayName = the
372
+ // wallet label the user sees. We override the server's user.name (a raw,
373
+ // unreadable handle) with the chosen wallet name.
374
+ rp: { id: o.rp.id ?? rpId, name: "Dexter" },
375
+ user: {
376
+ id: toBuf(o.user.id),
377
+ name,
378
+ displayName: name
379
+ },
380
+ challenge: toBuf(o.challenge),
381
+ pubKeyCredParams: o.pubKeyCredParams,
382
+ timeout: o.timeout,
383
+ excludeCredentials: o.excludeCredentials?.map((c) => ({
384
+ id: toBuf(c.id),
385
+ type: c.type,
386
+ transports: c.transports
387
+ })),
388
+ authenticatorSelection: o.authenticatorSelection,
389
+ attestation: o.attestation
390
+ };
391
+ }
392
+ async function readErrorCode(res) {
393
+ try {
394
+ const body = await res.json();
395
+ if (body?.error) return body.error;
396
+ } catch {
397
+ }
398
+ return `http_${res.status}`;
399
+ }
400
+
401
+ // src/relay.ts
402
+ var DEFAULT_API_BASE2 = "https://api.dexter.cash";
150
403
  var ANON_SIGN_BASE = "/api/passkey-anon/sign";
151
404
  async function passkeyLogin(config = {}, onPhase) {
152
405
  if (shouldUsePopup(config.transport)) {
@@ -155,7 +408,7 @@ async function passkeyLogin(config = {}, onPhase) {
155
408
  apiBase: config.apiBase
156
409
  });
157
410
  }
158
- const apiBase = (config.apiBase ?? DEFAULT_API_BASE).replace(/\/$/, "");
411
+ const apiBase = (config.apiBase ?? DEFAULT_API_BASE2).replace(/\/$/, "");
159
412
  onPhase?.("challenge");
160
413
  const options = await fetchLoginChallenge(apiBase);
161
414
  onPhase?.("passkey");
@@ -163,6 +416,21 @@ async function passkeyLogin(config = {}, onPhase) {
163
416
  onPhase?.("verifying");
164
417
  return submitLogin(apiBase, credential);
165
418
  }
419
+ async function continueWithDexter(config = {}, onPhase) {
420
+ if (shouldUsePopup(config.transport)) {
421
+ return openCeremonyPopup("continue", {
422
+ connectHost: config.connectHost,
423
+ name: config.name,
424
+ apiBase: config.apiBase
425
+ });
426
+ }
427
+ if (getActiveHandle()) {
428
+ const result2 = await passkeyLogin(config, onPhase);
429
+ return { kind: "signin", ...result2 };
430
+ }
431
+ const result = await createWallet({ ...config, onPhase });
432
+ return { kind: "create", ...result };
433
+ }
166
434
  async function fetchLoginChallenge(apiBase) {
167
435
  const res = await fetch(`${apiBase}${ANON_SIGN_BASE}/login-challenge`, {
168
436
  method: "POST",
@@ -221,7 +489,7 @@ async function submitLogin(apiBase, credential) {
221
489
  body: JSON.stringify({ credential: credentialJson })
222
490
  });
223
491
  if (!res.ok) {
224
- throw new ConnectError(await readErrorCode(res), `passkey-login ${res.status}`);
492
+ throw new ConnectError(await readErrorCode2(res), `passkey-login ${res.status}`);
225
493
  }
226
494
  const data = await res.json();
227
495
  const session = {
@@ -233,7 +501,7 @@ async function submitLogin(apiBase, credential) {
233
501
  };
234
502
  return data.vault ? { session, vault: data.vault } : { session };
235
503
  }
236
- async function readErrorCode(res) {
504
+ async function readErrorCode2(res) {
237
505
  try {
238
506
  const body = await res.json();
239
507
  if (body?.error) return body.error;
@@ -243,9 +511,9 @@ async function readErrorCode(res) {
243
511
  }
244
512
 
245
513
  // src/anon-policy.ts
246
- var DEFAULT_API_BASE2 = "https://api.dexter.cash";
514
+ var DEFAULT_API_BASE3 = "https://api.dexter.cash";
247
515
  var ANON_SIGN_BASE2 = "/api/passkey-anon/sign";
248
- function createAnonServerPolicy(apiBase = DEFAULT_API_BASE2) {
516
+ function createAnonServerPolicy(apiBase = DEFAULT_API_BASE3) {
249
517
  const base = apiBase.replace(/\/$/, "");
250
518
  return {
251
519
  async issueChallenge({ userHandle, operationHash }) {
@@ -257,7 +525,7 @@ function createAnonServerPolicy(apiBase = DEFAULT_API_BASE2) {
257
525
  operationHash: bytesToBase64url(operationHash)
258
526
  })
259
527
  });
260
- if (!res.ok) throw new ConnectError(await readErrorCode2(res), `challenge ${res.status}`);
528
+ if (!res.ok) throw new ConnectError(await readErrorCode3(res), `challenge ${res.status}`);
261
529
  const data = await res.json();
262
530
  const options = data?.options;
263
531
  if (!options?.challenge) {
@@ -294,7 +562,7 @@ function createAnonServerPolicy(apiBase = DEFAULT_API_BASE2) {
294
562
  headers: { "content-type": "application/json" },
295
563
  body: JSON.stringify({ credential, userHandle: bytesToBase64url(userHandle) })
296
564
  });
297
- if (!res.ok) throw new ConnectError(await readErrorCode2(res), `verify ${res.status}`);
565
+ if (!res.ok) throw new ConnectError(await readErrorCode3(res), `verify ${res.status}`);
298
566
  const data = await res.json();
299
567
  if (data?.verified === false) {
300
568
  throw new ConnectError("verification_failed", "server returned verified=false");
@@ -302,7 +570,7 @@ function createAnonServerPolicy(apiBase = DEFAULT_API_BASE2) {
302
570
  }
303
571
  };
304
572
  }
305
- async function readErrorCode2(res) {
573
+ async function readErrorCode3(res) {
306
574
  try {
307
575
  const body = await res.json();
308
576
  if (body?.error) return body.error;
@@ -336,122 +604,6 @@ function resolveIdentity(input) {
336
604
  return { kind, userHandle, accountToken, hasPasskeyVault, hasAccount, hasWallet };
337
605
  }
338
606
 
339
- // src/walletStore.ts
340
- var ACTIVE_HANDLE_KEY = "dexter:passkey:userHandle";
341
- var ROSTER_KEY = "dexter:passkey:wallets";
342
- var listeners = /* @__PURE__ */ new Set();
343
- function hasStorage() {
344
- try {
345
- return typeof window !== "undefined" && !!window.localStorage;
346
- } catch {
347
- return false;
348
- }
349
- }
350
- function readRoster() {
351
- if (!hasStorage()) return [];
352
- try {
353
- const raw = window.localStorage.getItem(ROSTER_KEY);
354
- if (!raw) return [];
355
- const parsed = JSON.parse(raw);
356
- if (!Array.isArray(parsed)) return [];
357
- return parsed.filter(
358
- (w) => !!w && typeof w === "object" && typeof w.handle === "string"
359
- );
360
- } catch {
361
- return [];
362
- }
363
- }
364
- function writeRoster(wallets) {
365
- if (!hasStorage()) return;
366
- try {
367
- window.localStorage.setItem(ROSTER_KEY, JSON.stringify(wallets));
368
- } catch {
369
- }
370
- }
371
- function emit() {
372
- for (const l of listeners) {
373
- try {
374
- l();
375
- } catch {
376
- }
377
- }
378
- }
379
- function getActiveHandle() {
380
- if (!hasStorage()) return null;
381
- try {
382
- return window.localStorage.getItem(ACTIVE_HANDLE_KEY);
383
- } catch {
384
- return null;
385
- }
386
- }
387
- function setActiveHandle(handle, label, credentialId) {
388
- if (!hasStorage() || !handle) return;
389
- try {
390
- window.localStorage.setItem(ACTIVE_HANDLE_KEY, handle);
391
- } catch {
392
- return;
393
- }
394
- const roster = readRoster();
395
- const existing = roster.find((w) => w.handle === handle);
396
- const now = Date.now();
397
- if (existing) {
398
- existing.lastUsedAt = now;
399
- if (label !== void 0) existing.label = label;
400
- if (credentialId !== void 0) existing.credentialId = credentialId;
401
- } else {
402
- roster.push({ handle, label, credentialId, lastUsedAt: now });
403
- }
404
- writeRoster(roster);
405
- emit();
406
- }
407
- function getCredentialId(handle) {
408
- return readRoster().find((w) => w.handle === handle)?.credentialId;
409
- }
410
- function ejectActiveWallet(opts) {
411
- if (!hasStorage()) return;
412
- const current = getActiveHandle();
413
- try {
414
- window.localStorage.removeItem(ACTIVE_HANDLE_KEY);
415
- } catch {
416
- }
417
- if (opts?.forget && current) {
418
- writeRoster(readRoster().filter((w) => w.handle !== current));
419
- }
420
- emit();
421
- }
422
- function listWallets() {
423
- return readRoster().sort((a, b) => (b.lastUsedAt ?? 0) - (a.lastUsedAt ?? 0));
424
- }
425
- function switchWallet(handle) {
426
- if (!readRoster().some((w) => w.handle === handle)) return false;
427
- setActiveHandle(handle);
428
- return true;
429
- }
430
- function forgetWallet(handle) {
431
- if (getActiveHandle() === handle) {
432
- ejectActiveWallet({ forget: true });
433
- return;
434
- }
435
- writeRoster(readRoster().filter((w) => w.handle !== handle));
436
- emit();
437
- }
438
- function subscribe(listener) {
439
- listeners.add(listener);
440
- if (hasStorage() && listeners.size === 1) {
441
- window.addEventListener("storage", onStorageEvent);
442
- }
443
- return () => {
444
- listeners.delete(listener);
445
- if (hasStorage() && listeners.size === 0) {
446
- window.removeEventListener("storage", onStorageEvent);
447
- }
448
- };
449
- }
450
- function onStorageEvent(e) {
451
- if (e.key === ACTIVE_HANDLE_KEY || e.key === ROSTER_KEY || e.key === null) emit();
452
- }
453
- var ACTIVE_WALLET_STORAGE_KEY = ACTIVE_HANDLE_KEY;
454
-
455
607
  // src/phase.ts
456
608
  var PHASE_LABEL = {
457
609
  challenge: "Preparing\u2026",
@@ -525,14 +677,6 @@ async function syncAcceptedPasskeys(args) {
525
677
 
526
678
  export {
527
679
  ConnectError,
528
- base64urlToBytes,
529
- bytesToBase64url,
530
- shouldUsePopup,
531
- openCeremonyPopup,
532
- passkeyLogin,
533
- createAnonServerPolicy,
534
- createPasskeySigner,
535
- resolveIdentity,
536
680
  getActiveHandle,
537
681
  setActiveHandle,
538
682
  getCredentialId,
@@ -542,6 +686,12 @@ export {
542
686
  forgetWallet,
543
687
  subscribe,
544
688
  ACTIVE_WALLET_STORAGE_KEY,
689
+ createWallet,
690
+ passkeyLogin,
691
+ continueWithDexter,
692
+ createAnonServerPolicy,
693
+ createPasskeySigner,
694
+ resolveIdentity,
545
695
  ceremonyPhaseLabel,
546
696
  passkeySignalSupport,
547
697
  renamePasskey,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,33 @@
1
- import { D as DexterConnectConfig, C as CeremonyPhase, S as SignInResult, a as ConnectVault } from './signals-DM7IQdq6.js';
2
- export { A as ACTIVE_WALLET_STORAGE_KEY, b as ConnectError, I as IdentityInput, c as IdentityKind, 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-DM7IQdq6.js';
1
+ import { C as ConnectVault, D as DexterConnectConfig, a as CeremonyPhase, S as SignInResult } from './signals-BuP-7inZ.js';
2
+ export { A as ACTIVE_WALLET_STORAGE_KEY, b as ConnectError, I as IdentityInput, c as IdentityKind, 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-BuP-7inZ.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
+
5
31
  /**
6
32
  * "Sign in with Dexter" — the discoverable-credential login ceremony.
7
33
  *
@@ -16,6 +42,12 @@ import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
16
42
  * Supabase session, so the Supabase-gated router would 401.
17
43
  */
18
44
  declare function passkeyLogin(config?: DexterConnectConfig, onPhase?: (phase: CeremonyPhase) => void): Promise<SignInResult>;
45
+ type ContinueResult = ({
46
+ kind: 'signin';
47
+ } & SignInResult) | ({
48
+ kind: 'create';
49
+ } & CreateWalletResult);
50
+ declare function continueWithDexter(config?: CreateWalletConfig, onPhase?: (phase: CeremonyPhase) => void): Promise<ContinueResult>;
19
51
 
20
52
  /** What `issueChallenge` returns to the SDK signer. */
21
53
  interface AnonChallengeResult {
@@ -76,32 +108,6 @@ declare function createPasskeySigner(vault: ConnectVault, apiBase?: string, opts
76
108
  __assertion?: AssertionLike;
77
109
  }): DexterApiBrowserPasskeySigner;
78
110
 
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
- /** Called as the ceremony progresses, for live "connecting steps" UI:
86
- * challenge → passkey → verifying → finalizing. */
87
- onPhase?: (phase: CeremonyPhase) => void;
88
- }
89
- interface CreateWalletResult {
90
- /** Server-minted 16-byte user handle, base64url — the vault identity. */
91
- handle: string;
92
- /** base64url credential id of the new passkey. */
93
- credentialId: string;
94
- /** The freshly initialized vault (swig not yet deployed; deploys lazily). */
95
- vault: ConnectVault;
96
- }
97
- /**
98
- * Mint a brand-new Dexter wallet (passkey + vault) and make it the active wallet.
99
- *
100
- * One passkey approval. Throws ConnectError on any failed leg (the `code` is the
101
- * server's error string, or webauthn_failed / no_credential for the ceremony).
102
- */
103
- declare function createWallet(config?: CreateWalletConfig): Promise<CreateWalletResult>;
104
-
105
111
  /**
106
112
  * Human-readable label for a ceremony phase — the live "connecting step" copy.
107
113
  * ONE source of truth so sign-in (SignInWithDexter) and create (consumer setup
@@ -110,4 +116,4 @@ declare function createWallet(config?: CreateWalletConfig): Promise<CreateWallet
110
116
  */
111
117
  declare function ceremonyPhaseLabel(phase: CeremonyPhase): string;
112
118
 
113
- export { type AnonChallengeResult, type AnonServerPolicy, CeremonyPhase, ConnectVault, type CreateWalletConfig, type CreateWalletResult, DexterConnectConfig, SignInResult, ceremonyPhaseLabel, createAnonServerPolicy, createPasskeySigner, createWallet, passkeyLogin };
119
+ export { type AnonChallengeResult, type AnonServerPolicy, CeremonyPhase, ConnectVault, type ContinueResult, type CreateWalletConfig, type CreateWalletResult, DexterConnectConfig, SignInResult, ceremonyPhaseLabel, continueWithDexter, createAnonServerPolicy, createPasskeySigner, createWallet, passkeyLogin };
package/dist/index.js CHANGED
@@ -1,169 +1,31 @@
1
1
  import {
2
2
  ACTIVE_WALLET_STORAGE_KEY,
3
3
  ConnectError,
4
- base64urlToBytes,
5
- bytesToBase64url,
6
4
  ceremonyPhaseLabel,
5
+ continueWithDexter,
7
6
  createAnonServerPolicy,
8
7
  createPasskeySigner,
8
+ createWallet,
9
9
  ejectActiveWallet,
10
10
  forgetWallet,
11
11
  getActiveHandle,
12
12
  getCredentialId,
13
13
  listWallets,
14
- openCeremonyPopup,
15
14
  passkeyLogin,
16
15
  passkeySignalSupport,
17
16
  prunePasskey,
18
17
  renamePasskey,
19
18
  resolveIdentity,
20
19
  setActiveHandle,
21
- shouldUsePopup,
22
20
  subscribe,
23
21
  switchWallet,
24
22
  syncAcceptedPasskeys
25
- } from "./chunk-6NIGGPVI.js";
26
-
27
- // src/enroll.ts
28
- var DEFAULT_API_BASE = "https://api.dexter.cash";
29
- var DEFAULT_RP_ID = "dexter.cash";
30
- var DEFAULT_WALLET_NAME = "Dexter Wallet";
31
- async function createWallet(config = {}) {
32
- if (shouldUsePopup(config.transport)) {
33
- return openCeremonyPopup("create", {
34
- connectHost: config.connectHost,
35
- name: config.name,
36
- apiBase: config.apiBase
37
- });
38
- }
39
- if (typeof navigator === "undefined" || !navigator.credentials) {
40
- throw new ConnectError("webauthn_unsupported", "WebAuthn unavailable in this environment");
41
- }
42
- const apiBase = (config.apiBase ?? DEFAULT_API_BASE).replace(/\/$/, "");
43
- const rpId = config.rpId ?? DEFAULT_RP_ID;
44
- const name = config.name && config.name.trim() || DEFAULT_WALLET_NAME;
45
- config.onPhase?.("challenge");
46
- const options = await fetchEnrollChallenge(apiBase);
47
- config.onPhase?.("passkey");
48
- const credential = await createCredential(options, name, rpId);
49
- config.onPhase?.("verifying");
50
- const enrolled = await submitEnrollComplete(apiBase, credential);
51
- config.onPhase?.("finalizing");
52
- const init = await initializeVault(apiBase, enrolled.userHandle, enrolled.credentialId);
53
- setActiveHandle(enrolled.userHandle, name, enrolled.credentialId);
54
- return {
55
- handle: enrolled.userHandle,
56
- credentialId: enrolled.credentialId,
57
- vault: {
58
- vaultPda: init.vaultPda,
59
- swigAddress: init.swigStateAddress,
60
- // FAIL SAFE: never invent a receive address — null until the server returns
61
- // one (depositing to the config PDA would strand funds).
62
- receiveAddress: init.receiveAddress ?? null,
63
- usdcAta: null,
64
- // swig not deployed yet (counterfactual pattern)
65
- publicKey: enrolled.publicKey,
66
- userHandle: enrolled.userHandle,
67
- credentialId: enrolled.credentialId
68
- }
69
- };
70
- }
71
- async function fetchEnrollChallenge(apiBase) {
72
- const res = await fetch(`${apiBase}/api/passkey-anon/enroll/challenge`, {
73
- method: "POST",
74
- headers: { "content-type": "application/json" },
75
- body: "{}"
76
- });
77
- if (!res.ok) throw new ConnectError("enroll_challenge_failed", `enroll/challenge ${res.status}`);
78
- const data = await res.json();
79
- if (!data?.options?.challenge) {
80
- throw new ConnectError("enroll_challenge_malformed", "no creation options in response");
81
- }
82
- return data.options;
83
- }
84
- async function createCredential(options, name, rpId) {
85
- let credential;
86
- try {
87
- credential = await navigator.credentials.create({
88
- publicKey: buildCreationOptions(options, name, rpId)
89
- });
90
- } catch (err) {
91
- throw new ConnectError("webauthn_failed", err instanceof Error ? err.message : String(err));
92
- }
93
- if (!credential || credential.type !== "public-key") {
94
- throw new ConnectError("no_credential", "authenticator returned no credential");
95
- }
96
- return credential;
97
- }
98
- async function submitEnrollComplete(apiBase, credential) {
99
- const attestation = credential.response;
100
- const credentialJson = {
101
- id: credential.id,
102
- rawId: bytesToBase64url(new Uint8Array(credential.rawId)),
103
- type: credential.type,
104
- response: {
105
- attestationObject: bytesToBase64url(new Uint8Array(attestation.attestationObject)),
106
- clientDataJSON: bytesToBase64url(new Uint8Array(attestation.clientDataJSON)),
107
- transports: typeof attestation.getTransports === "function" ? attestation.getTransports() : []
108
- },
109
- clientExtensionResults: credential.getClientExtensionResults?.() ?? {},
110
- authenticatorAttachment: credential.authenticatorAttachment ?? null
111
- };
112
- const res = await fetch(`${apiBase}/api/passkey-anon/enroll/complete`, {
113
- method: "POST",
114
- headers: { "content-type": "application/json" },
115
- body: JSON.stringify({ credential: credentialJson })
116
- });
117
- if (!res.ok) throw new ConnectError(await readErrorCode(res), `enroll/complete ${res.status}`);
118
- return await res.json();
119
- }
120
- async function initializeVault(apiBase, userHandle, credentialId) {
121
- const res = await fetch(`${apiBase}/api/passkey-vault-anon/initialize`, {
122
- method: "POST",
123
- headers: { "content-type": "application/json" },
124
- body: JSON.stringify({ userHandle, credentialId, coolingOffSeconds: 0 })
125
- });
126
- if (!res.ok) throw new ConnectError(await readErrorCode(res), `initialize ${res.status}`);
127
- return await res.json();
128
- }
129
- function toBuf(b64url) {
130
- return base64urlToBytes(b64url).buffer.slice(0);
131
- }
132
- function buildCreationOptions(o, name, rpId) {
133
- return {
134
- // rp.name = the site shown in the keychain; user.name/displayName = the
135
- // wallet label the user sees. We override the server's user.name (a raw,
136
- // unreadable handle) with the chosen wallet name.
137
- rp: { id: o.rp.id ?? rpId, name: "Dexter" },
138
- user: {
139
- id: toBuf(o.user.id),
140
- name,
141
- displayName: name
142
- },
143
- challenge: toBuf(o.challenge),
144
- pubKeyCredParams: o.pubKeyCredParams,
145
- timeout: o.timeout,
146
- excludeCredentials: o.excludeCredentials?.map((c) => ({
147
- id: toBuf(c.id),
148
- type: c.type,
149
- transports: c.transports
150
- })),
151
- authenticatorSelection: o.authenticatorSelection,
152
- attestation: o.attestation
153
- };
154
- }
155
- async function readErrorCode(res) {
156
- try {
157
- const body = await res.json();
158
- if (body?.error) return body.error;
159
- } catch {
160
- }
161
- return `http_${res.status}`;
162
- }
23
+ } from "./chunk-IUZBHWTP.js";
163
24
  export {
164
25
  ACTIVE_WALLET_STORAGE_KEY,
165
26
  ConnectError,
166
27
  ceremonyPhaseLabel,
28
+ continueWithDexter,
167
29
  createAnonServerPolicy,
168
30
  createPasskeySigner,
169
31
  createWallet,
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
2
- import { C as CeremonyPhase, S as SignInResult, P as PasskeyLoginTokens, a as ConnectVault, b as ConnectError, e as StoredWallet, d as PasskeySignalSupport, R as ResolvedIdentity } from './signals-DM7IQdq6.js';
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-BuP-7inZ.js';
3
3
  import { ReactElement, ReactNode } from 'react';
4
4
 
5
5
  type ConnectStatus = 'idle' | 'pending' | 'done' | 'error';
@@ -124,6 +124,43 @@ interface UseDexterWallet {
124
124
  }
125
125
  declare function useDexterWallet(): UseDexterWallet;
126
126
 
127
+ interface DexterWalletChipProps {
128
+ /** Connected (a wallet/account is present) → avatar + label; else signed-out label only. */
129
+ connected: boolean;
130
+ /** What to show: account name, wallet label, or the signed-out prompt (e.g. "Log in"). */
131
+ label: string;
132
+ /** Avatar image (e.g. a linked X avatar); falls back to the initial. */
133
+ avatarUrl?: string | null;
134
+ /** Single-character avatar fallback (defaults to the first letter of `label`). */
135
+ avatarInitial?: string;
136
+ onClick?: () => void;
137
+ /** Extra className composed after the brand classes. */
138
+ className?: string;
139
+ /** Reflected to aria-expanded (when the chip toggles a menu). */
140
+ ariaExpanded?: boolean;
141
+ }
142
+ /** The branded wallet/account chip. Wire it to open your menu via `onClick`. */
143
+ declare function DexterWalletChip(props: DexterWalletChipProps): ReactElement;
144
+
145
+ interface DexterWalletMenuProps {
146
+ /** The wallet's display name (nickname, else "Dexter Wallet"). */
147
+ walletLabel: string;
148
+ /** Avatar initial (defaults to the first letter of walletLabel). */
149
+ avatarInitial?: string;
150
+ /** "Manage wallet" → the consumer's wallet page. Row hidden if omitted. */
151
+ onManageWallet?: () => void;
152
+ /** "Start fresh" → the consumer ejects/resets (guard + perform on its side). Row hidden if omitted. */
153
+ onStartFresh?: () => void;
154
+ /** Sign-in/save UI revealed when "Save your wallet" is tapped (e.g. <SignInWithDexter/>). Row hidden if omitted. */
155
+ saveSlot?: ReactNode;
156
+ /** Short hint shown above the save UI. */
157
+ saveHint?: string;
158
+ /** Extra className composed after the brand classes. */
159
+ className?: string;
160
+ }
161
+ /** The branded wallet dropdown. */
162
+ declare function DexterWalletMenu(props: DexterWalletMenuProps): ReactElement;
163
+
127
164
  interface UseIdentityConfig {
128
165
  /** The account session token (e.g. a Supabase access_token), or null when the
129
166
  * user has no account session. The SDK is auth-agnostic — pass your own. */
@@ -131,4 +168,4 @@ interface UseIdentityConfig {
131
168
  }
132
169
  declare function useIdentity({ accountToken }: UseIdentityConfig): ResolvedIdentity;
133
170
 
134
- export { type ConnectStatus, DexterButton, type DexterButtonProps, DexterMark, SignInWithDexter, type SignInWithDexterProps, type UseDexterWallet, type UseIdentityConfig, type UseSignInWithDexter, type UseSignInWithDexterConfig, useDexterWallet, useIdentity, useSignInWithDexter };
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 };
package/dist/react.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  setActiveHandle,
15
15
  subscribe,
16
16
  switchWallet
17
- } from "./chunk-6NIGGPVI.js";
17
+ } from "./chunk-IUZBHWTP.js";
18
18
 
19
19
  // src/useSignInWithDexter.ts
20
20
  import { useCallback, useEffect, useMemo, useState } from "react";
@@ -323,6 +323,136 @@ function useDexterWallet() {
323
323
  };
324
324
  }
325
325
 
326
+ // src/DexterWalletChip.tsx
327
+ import { useEffect as useEffect5 } from "react";
328
+
329
+ // src/walletKitStyles.ts
330
+ var STYLE_ID2 = "dexter-connect-wallet-kit-styles";
331
+ var WALLET_KIT_CSS = `
332
+ .dx-wchip{
333
+ --dx-ember:#f26c18; --dx-ember-2:#ba3a00; --dx-fg:#fff4ea; --dx-radius:0px;
334
+ display:inline-flex; align-items:center; gap:9px; max-width:240px;
335
+ padding:6px 12px 6px 7px; font:inherit; font-weight:600; font-size:.74rem;
336
+ letter-spacing:.1em; text-transform:uppercase; cursor:pointer; background:transparent;
337
+ color:inherit; border:1px solid color-mix(in srgb,var(--dx-ember) 42%,transparent);
338
+ border-radius:var(--dx-radius); -webkit-tap-highlight-color:transparent;
339
+ transition:filter .16s ease, box-shadow .16s ease, border-color .16s ease;
340
+ }
341
+ .dx-wchip:hover{ filter:brightness(1.08); box-shadow:0 8px 18px color-mix(in srgb,var(--dx-ember) 18%,transparent); }
342
+ .dx-wchip:focus-visible{ outline:none; box-shadow:0 0 0 3px color-mix(in srgb,var(--dx-ember) 34%,transparent); }
343
+ .dx-wchip--signedout{ border-color:color-mix(in srgb,currentColor 22%,transparent); padding-left:12px; }
344
+ .dx-wchip__avatar{
345
+ width:22px; height:22px; flex-shrink:0; display:inline-flex; align-items:center; justify-content:center;
346
+ border-radius:50%; overflow:hidden; font-size:.72rem; font-weight:700; letter-spacing:0; line-height:1;
347
+ background:linear-gradient(135deg,var(--dx-ember),var(--dx-ember-2)); color:var(--dx-fg);
348
+ }
349
+ .dx-wchip__avatar img{ width:100%; height:100%; object-fit:cover; }
350
+ .dx-wchip__label{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
351
+
352
+ .dx-wmenu{ --dx-ember:#f26c18; --dx-ember-2:#ba3a00; --dx-fg:#fff4ea; display:flex; flex-direction:column; min-width:248px; }
353
+ .dx-wmenu__id{ display:flex; align-items:center; gap:10px; padding:13px 15px; border-bottom:1px solid color-mix(in srgb,currentColor 12%,transparent); }
354
+ .dx-wmenu__avatar{ width:32px; height:32px; flex-shrink:0; display:inline-flex; align-items:center; justify-content:center; border-radius:50%; font-weight:700; font-size:.85rem; line-height:1; background:linear-gradient(135deg,var(--dx-ember),var(--dx-ember-2)); color:var(--dx-fg); }
355
+ .dx-wmenu__meta{ display:flex; flex-direction:column; gap:2px; min-width:0; }
356
+ .dx-wmenu__name{ font-weight:700; font-size:.9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
357
+ .dx-wmenu__sub{ font-size:.66rem; letter-spacing:.12em; text-transform:uppercase; opacity:.55; }
358
+ .dx-wmenu__list{ display:flex; flex-direction:column; padding:6px; }
359
+ .dx-wmenu__item{ display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; padding:10px 12px; font:inherit; font-size:.82rem; font-weight:600; text-align:left; background:transparent; border:none; color:inherit; cursor:pointer; transition:background .12s ease; }
360
+ .dx-wmenu__item:hover{ background:color-mix(in srgb,var(--dx-ember) 10%,transparent); }
361
+ .dx-wmenu__item--danger{ color:#e5552e; }
362
+ .dx-wmenu__icon{ opacity:.45; font-size:.9rem; }
363
+ .dx-wmenu__back{ border-bottom:1px solid color-mix(in srgb,currentColor 10%,transparent); opacity:.85; }
364
+ .dx-wmenu__save{ display:flex; flex-direction:column; gap:9px; padding:13px 15px; }
365
+ .dx-wmenu__savehint{ font-size:.78rem; line-height:1.4; opacity:.7; }
366
+ `;
367
+ function ensureWalletKitStyles() {
368
+ if (typeof document === "undefined") return;
369
+ if (document.getElementById(STYLE_ID2)) return;
370
+ const el = document.createElement("style");
371
+ el.id = STYLE_ID2;
372
+ el.textContent = WALLET_KIT_CSS;
373
+ document.head.appendChild(el);
374
+ }
375
+ ensureWalletKitStyles();
376
+
377
+ // src/DexterWalletChip.tsx
378
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
379
+ function DexterWalletChip(props) {
380
+ const { connected, label, avatarUrl, avatarInitial, onClick, className, ariaExpanded } = props;
381
+ useEffect5(ensureWalletKitStyles, []);
382
+ const initial = (avatarInitial ?? label.trim().charAt(0) ?? "D").toUpperCase();
383
+ return /* @__PURE__ */ jsxs3(
384
+ "button",
385
+ {
386
+ type: "button",
387
+ className: cx("dx-wchip", !connected && "dx-wchip--signedout", className),
388
+ onClick,
389
+ "aria-expanded": ariaExpanded,
390
+ children: [
391
+ connected ? /* @__PURE__ */ jsx3("span", { className: "dx-wchip__avatar", "aria-hidden": "true", children: avatarUrl ? /* @__PURE__ */ jsx3("img", { src: avatarUrl, alt: "" }) : initial }) : null,
392
+ /* @__PURE__ */ jsx3("span", { className: "dx-wchip__label", children: label })
393
+ ]
394
+ }
395
+ );
396
+ }
397
+
398
+ // src/DexterWalletMenu.tsx
399
+ import { useEffect as useEffect6, useState as useState3 } from "react";
400
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
401
+ function DexterWalletMenu(props) {
402
+ const { walletLabel, avatarInitial, onManageWallet, onStartFresh, saveSlot, saveHint, className } = props;
403
+ useEffect6(ensureWalletKitStyles, []);
404
+ const [showSave, setShowSave] = useState3(false);
405
+ const initial = (avatarInitial ?? walletLabel.trim().charAt(0) ?? "D").toUpperCase();
406
+ if (showSave && saveSlot) {
407
+ return /* @__PURE__ */ jsxs4("div", { className: cx("dx-wmenu", className), children: [
408
+ /* @__PURE__ */ jsx4(
409
+ "button",
410
+ {
411
+ type: "button",
412
+ className: "dx-wmenu__item dx-wmenu__back",
413
+ onClick: () => setShowSave(false),
414
+ children: /* @__PURE__ */ jsx4("span", { children: "\u2190 Back to wallet" })
415
+ }
416
+ ),
417
+ /* @__PURE__ */ jsxs4("div", { className: "dx-wmenu__save", children: [
418
+ saveHint ? /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__savehint", children: saveHint }) : null,
419
+ saveSlot
420
+ ] })
421
+ ] });
422
+ }
423
+ return /* @__PURE__ */ jsxs4("div", { className: cx("dx-wmenu", className), children: [
424
+ /* @__PURE__ */ jsxs4("div", { className: "dx-wmenu__id", children: [
425
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__avatar", "aria-hidden": "true", children: initial }),
426
+ /* @__PURE__ */ jsxs4("span", { className: "dx-wmenu__meta", children: [
427
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__name", children: walletLabel }),
428
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__sub", children: "Your Dexter Wallet" })
429
+ ] })
430
+ ] }),
431
+ /* @__PURE__ */ jsxs4("div", { className: "dx-wmenu__list", children: [
432
+ onManageWallet ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "dx-wmenu__item", onClick: onManageWallet, children: [
433
+ /* @__PURE__ */ jsx4("span", { children: "Manage wallet" }),
434
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__icon", "aria-hidden": "true", children: "\u2197" })
435
+ ] }) : null,
436
+ saveSlot ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "dx-wmenu__item", onClick: () => setShowSave(true), children: [
437
+ /* @__PURE__ */ jsx4("span", { children: "Save your wallet" }),
438
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__icon", "aria-hidden": "true", children: "\u2197" })
439
+ ] }) : null,
440
+ onStartFresh ? /* @__PURE__ */ jsxs4(
441
+ "button",
442
+ {
443
+ type: "button",
444
+ className: "dx-wmenu__item dx-wmenu__item--danger",
445
+ onClick: onStartFresh,
446
+ children: [
447
+ /* @__PURE__ */ jsx4("span", { children: "Start fresh" }),
448
+ /* @__PURE__ */ jsx4("span", { className: "dx-wmenu__icon", "aria-hidden": "true", children: "\u27F5" })
449
+ ]
450
+ }
451
+ ) : null
452
+ ] })
453
+ ] });
454
+ }
455
+
326
456
  // src/useIdentity.ts
327
457
  import { useMemo as useMemo2 } from "react";
328
458
  function useIdentity({ accountToken }) {
@@ -338,6 +468,8 @@ function useIdentity({ accountToken }) {
338
468
  export {
339
469
  DexterButton,
340
470
  DexterMark,
471
+ DexterWalletChip,
472
+ DexterWalletMenu,
341
473
  SignInWithDexter,
342
474
  useDexterWallet,
343
475
  useIdentity,
@@ -180,4 +180,4 @@ declare function syncAcceptedPasskeys(args: {
180
180
  rpId?: string;
181
181
  }): Promise<boolean>;
182
182
 
183
- export { ACTIVE_WALLET_STORAGE_KEY as A, type CeremonyPhase as C, type DexterConnectConfig as D, type IdentityInput as I, type PasskeyLoginTokens as P, type ResolvedIdentity as R, type SignInResult as S, type ConnectVault as a, ConnectError as b, type IdentityKind 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 };
183
+ export { ACTIVE_WALLET_STORAGE_KEY as A, type ConnectVault as C, type DexterConnectConfig as D, type IdentityInput as I, type PasskeyLoginTokens as P, type ResolvedIdentity as R, type SignInResult as S, type CeremonyPhase as a, ConnectError as b, type IdentityKind 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/connect",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Sign in with Dexter — passkey connector. Composes @dexterai/vault.",
5
5
  "type": "module",
6
6
  "exports": {