decane-connect-kit 2.2.4 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +332 -129
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +250 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -387,7 +387,7 @@ interface SignTypedDataRequest {
|
|
|
387
387
|
message: Record<string, unknown>;
|
|
388
388
|
primaryType?: string;
|
|
389
389
|
}
|
|
390
|
-
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
|
|
390
|
+
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking';
|
|
391
391
|
interface DecaneConnect {
|
|
392
392
|
connect(): Promise<ConnectResult>;
|
|
393
393
|
connectWithGoogle(): Promise<ConnectResult>;
|
|
@@ -730,6 +730,12 @@ interface SocialAuthState {
|
|
|
730
730
|
* user, so signing back in costs one passkey prompt and nothing else.
|
|
731
731
|
*/
|
|
732
732
|
canUsePasskey: boolean;
|
|
733
|
+
/**
|
|
734
|
+
* Long-running work in progress, so the UI can show a status screen instead
|
|
735
|
+
* of appearing frozen. 'creating' = first-time key generation (several
|
|
736
|
+
* seconds); 'unlocking' = reopening the TEE session for a returning user.
|
|
737
|
+
*/
|
|
738
|
+
phase: "creating" | "unlocking" | null;
|
|
733
739
|
}
|
|
734
740
|
interface DecaneKitProps {
|
|
735
741
|
config?: DecaneConfig;
|
|
@@ -752,6 +758,9 @@ interface SocialWalletModalProps {
|
|
|
752
758
|
authMethods?: Array<"google" | "email">;
|
|
753
759
|
customAuthLabel?: string;
|
|
754
760
|
onCustomAuthSignIn?: () => Promise<void>;
|
|
761
|
+
canUsePasskey?: boolean;
|
|
762
|
+
passkeyAddress?: string | null;
|
|
763
|
+
onPasskeySignIn?: () => Promise<unknown>;
|
|
755
764
|
onRotateShares: (opts: {
|
|
756
765
|
password: string;
|
|
757
766
|
passwordHint?: string;
|
|
@@ -763,7 +772,7 @@ interface SocialWalletModalProps {
|
|
|
763
772
|
rpcUrls?: Record<string, string>;
|
|
764
773
|
chains?: string[];
|
|
765
774
|
}
|
|
766
|
-
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
775
|
+
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
767
776
|
|
|
768
777
|
declare function DecaneMark({ size, muted }: {
|
|
769
778
|
size?: number;
|
|
@@ -787,6 +796,9 @@ declare function useSocialAuth(): {
|
|
|
787
796
|
solana?: string;
|
|
788
797
|
} | null;
|
|
789
798
|
sessionExpiresAt: number | null;
|
|
799
|
+
canUsePasskey: boolean;
|
|
800
|
+
phase: "creating" | "unlocking" | null;
|
|
801
|
+
isCreatingWallet: boolean;
|
|
790
802
|
needsReconnect: boolean;
|
|
791
803
|
googleLoading: boolean;
|
|
792
804
|
emailLoading: boolean;
|
|
@@ -804,6 +816,7 @@ declare function useSocialAuth(): {
|
|
|
804
816
|
providerId?: string;
|
|
805
817
|
}) => Promise<void>;
|
|
806
818
|
signInWithCustomAuth: (() => Promise<void>) | undefined;
|
|
819
|
+
signInWithPasskey: () => Promise<void>;
|
|
807
820
|
unlock: () => Promise<void>;
|
|
808
821
|
disconnect: () => Promise<void>;
|
|
809
822
|
};
|
|
@@ -817,6 +830,8 @@ declare function useSocialWallet(): {
|
|
|
817
830
|
} | null;
|
|
818
831
|
sessionExpiresAt: number | null;
|
|
819
832
|
needsReconnect: boolean;
|
|
833
|
+
phase: "creating" | "unlocking" | null;
|
|
834
|
+
isCreatingWallet: boolean;
|
|
820
835
|
modalOpen: boolean;
|
|
821
836
|
openModal: () => void;
|
|
822
837
|
closeModal: () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -387,7 +387,7 @@ interface SignTypedDataRequest {
|
|
|
387
387
|
message: Record<string, unknown>;
|
|
388
388
|
primaryType?: string;
|
|
389
389
|
}
|
|
390
|
-
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
|
|
390
|
+
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking';
|
|
391
391
|
interface DecaneConnect {
|
|
392
392
|
connect(): Promise<ConnectResult>;
|
|
393
393
|
connectWithGoogle(): Promise<ConnectResult>;
|
|
@@ -730,6 +730,12 @@ interface SocialAuthState {
|
|
|
730
730
|
* user, so signing back in costs one passkey prompt and nothing else.
|
|
731
731
|
*/
|
|
732
732
|
canUsePasskey: boolean;
|
|
733
|
+
/**
|
|
734
|
+
* Long-running work in progress, so the UI can show a status screen instead
|
|
735
|
+
* of appearing frozen. 'creating' = first-time key generation (several
|
|
736
|
+
* seconds); 'unlocking' = reopening the TEE session for a returning user.
|
|
737
|
+
*/
|
|
738
|
+
phase: "creating" | "unlocking" | null;
|
|
733
739
|
}
|
|
734
740
|
interface DecaneKitProps {
|
|
735
741
|
config?: DecaneConfig;
|
|
@@ -752,6 +758,9 @@ interface SocialWalletModalProps {
|
|
|
752
758
|
authMethods?: Array<"google" | "email">;
|
|
753
759
|
customAuthLabel?: string;
|
|
754
760
|
onCustomAuthSignIn?: () => Promise<void>;
|
|
761
|
+
canUsePasskey?: boolean;
|
|
762
|
+
passkeyAddress?: string | null;
|
|
763
|
+
onPasskeySignIn?: () => Promise<unknown>;
|
|
755
764
|
onRotateShares: (opts: {
|
|
756
765
|
password: string;
|
|
757
766
|
passwordHint?: string;
|
|
@@ -763,7 +772,7 @@ interface SocialWalletModalProps {
|
|
|
763
772
|
rpcUrls?: Record<string, string>;
|
|
764
773
|
chains?: string[];
|
|
765
774
|
}
|
|
766
|
-
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
775
|
+
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
767
776
|
|
|
768
777
|
declare function DecaneMark({ size, muted }: {
|
|
769
778
|
size?: number;
|
|
@@ -787,6 +796,9 @@ declare function useSocialAuth(): {
|
|
|
787
796
|
solana?: string;
|
|
788
797
|
} | null;
|
|
789
798
|
sessionExpiresAt: number | null;
|
|
799
|
+
canUsePasskey: boolean;
|
|
800
|
+
phase: "creating" | "unlocking" | null;
|
|
801
|
+
isCreatingWallet: boolean;
|
|
790
802
|
needsReconnect: boolean;
|
|
791
803
|
googleLoading: boolean;
|
|
792
804
|
emailLoading: boolean;
|
|
@@ -804,6 +816,7 @@ declare function useSocialAuth(): {
|
|
|
804
816
|
providerId?: string;
|
|
805
817
|
}) => Promise<void>;
|
|
806
818
|
signInWithCustomAuth: (() => Promise<void>) | undefined;
|
|
819
|
+
signInWithPasskey: () => Promise<void>;
|
|
807
820
|
unlock: () => Promise<void>;
|
|
808
821
|
disconnect: () => Promise<void>;
|
|
809
822
|
};
|
|
@@ -817,6 +830,8 @@ declare function useSocialWallet(): {
|
|
|
817
830
|
} | null;
|
|
818
831
|
sessionExpiresAt: number | null;
|
|
819
832
|
needsReconnect: boolean;
|
|
833
|
+
phase: "creating" | "unlocking" | null;
|
|
834
|
+
isCreatingWallet: boolean;
|
|
820
835
|
modalOpen: boolean;
|
|
821
836
|
openModal: () => void;
|
|
822
837
|
closeModal: () => void;
|
package/dist/index.js
CHANGED
|
@@ -2548,9 +2548,6 @@ function validatePin(pin) {
|
|
|
2548
2548
|
// src/social/crypto/passkey.ts
|
|
2549
2549
|
init_errors();
|
|
2550
2550
|
import { startRegistration, startAuthentication as startAuthentication2 } from "@simplewebauthn/browser";
|
|
2551
|
-
function b64u(u) {
|
|
2552
|
-
return btoa(String.fromCharCode(...u)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
2553
|
-
}
|
|
2554
2551
|
function fromB64u(s) {
|
|
2555
2552
|
return Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
|
|
2556
2553
|
}
|
|
@@ -2585,7 +2582,7 @@ async function authenticateWithPasskey(opts) {
|
|
|
2585
2582
|
...opts.optionsJSON,
|
|
2586
2583
|
extensions: {
|
|
2587
2584
|
...opts.optionsJSON.extensions,
|
|
2588
|
-
prf: { eval: { first:
|
|
2585
|
+
prf: { eval: { first: buf3(opts.prfSalt) } }
|
|
2589
2586
|
}
|
|
2590
2587
|
};
|
|
2591
2588
|
let response;
|
|
@@ -3268,6 +3265,7 @@ async function createDecaneConnect(config) {
|
|
|
3268
3265
|
}
|
|
3269
3266
|
async function passkeyLogin(stored) {
|
|
3270
3267
|
const userId = stored.userId;
|
|
3268
|
+
events.emit("wallet-unlocking");
|
|
3271
3269
|
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3272
3270
|
if (!options) {
|
|
3273
3271
|
throw new NewDeviceError();
|
|
@@ -3301,8 +3299,10 @@ async function createDecaneConnect(config) {
|
|
|
3301
3299
|
async function handleAuthResult(result) {
|
|
3302
3300
|
let addresses;
|
|
3303
3301
|
if (!result.hasShare) {
|
|
3302
|
+
events.emit("wallet-creating");
|
|
3304
3303
|
addresses = await firstTimeSetup(result.userId, result.jwt);
|
|
3305
3304
|
} else {
|
|
3305
|
+
events.emit("wallet-unlocking");
|
|
3306
3306
|
addresses = await returningUserUnlock(result.userId, result.jwt);
|
|
3307
3307
|
}
|
|
3308
3308
|
auth = { jwt: result.jwt, userId: result.userId, addresses };
|
|
@@ -3641,8 +3641,8 @@ async function createDecaneConnect(config) {
|
|
|
3641
3641
|
function bufferToB64u(buf6) {
|
|
3642
3642
|
return btoa(String.fromCharCode(...buf6)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
3643
3643
|
}
|
|
3644
|
-
function b64uToBuffer(
|
|
3645
|
-
const b644 =
|
|
3644
|
+
function b64uToBuffer(b64u) {
|
|
3645
|
+
const b644 = b64u.replace(/-/g, "+").replace(/_/g, "/");
|
|
3646
3646
|
return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
|
|
3647
3647
|
}
|
|
3648
3648
|
function toHex(bytes) {
|
|
@@ -4817,7 +4817,10 @@ function SignInView({
|
|
|
4817
4817
|
authMethods,
|
|
4818
4818
|
resumeHint,
|
|
4819
4819
|
customAuthLabel,
|
|
4820
|
-
onCustomAuthSignIn
|
|
4820
|
+
onCustomAuthSignIn,
|
|
4821
|
+
canUsePasskey,
|
|
4822
|
+
passkeyAddress,
|
|
4823
|
+
onPasskeySignIn
|
|
4821
4824
|
}) {
|
|
4822
4825
|
const [googleLoading, setGoogleLoading] = useState2(false);
|
|
4823
4826
|
const [email, setEmail] = useState2("");
|
|
@@ -4825,7 +4828,25 @@ function SignInView({
|
|
|
4825
4828
|
const [emailSent, setEmailSent] = useState2(false);
|
|
4826
4829
|
const [emailLoading, setEmailLoading] = useState2(false);
|
|
4827
4830
|
const [customLoading, setCustomLoading] = useState2(false);
|
|
4831
|
+
const [passkeyLoading, setPasskeyLoading] = useState2(false);
|
|
4832
|
+
const [showOtherMethods, setShowOtherMethods] = useState2(false);
|
|
4828
4833
|
const [error, setError] = useState2("");
|
|
4834
|
+
const passkeyFirst = !!canUsePasskey && !!onPasskeySignIn && !showOtherMethods;
|
|
4835
|
+
async function handlePasskey() {
|
|
4836
|
+
if (!onPasskeySignIn) return;
|
|
4837
|
+
setError("");
|
|
4838
|
+
setPasskeyLoading(true);
|
|
4839
|
+
try {
|
|
4840
|
+
await onPasskeySignIn();
|
|
4841
|
+
} catch (e) {
|
|
4842
|
+
const err = e;
|
|
4843
|
+
if (err?.name !== "UserCancelledError") {
|
|
4844
|
+
setError(err?.message ?? "Passkey sign-in failed");
|
|
4845
|
+
setShowOtherMethods(true);
|
|
4846
|
+
}
|
|
4847
|
+
setPasskeyLoading(false);
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4829
4850
|
async function handleGoogle() {
|
|
4830
4851
|
setError("");
|
|
4831
4852
|
setGoogleLoading(true);
|
|
@@ -4885,7 +4906,47 @@ function SignInView({
|
|
|
4885
4906
|
fontFamily: "inherit",
|
|
4886
4907
|
boxSizing: "border-box"
|
|
4887
4908
|
};
|
|
4888
|
-
return /* @__PURE__ */ React3.createElement("div", { style: { padding: "0 16px 16px" } }, resumeHint && /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 sign in again to resume ", resumeHint),
|
|
4909
|
+
return /* @__PURE__ */ React3.createElement("div", { style: { padding: "0 16px 16px" } }, resumeHint && !passkeyFirst && /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 sign in again to resume ", resumeHint), passkeyFirst && /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted)", marginBottom: 12, textAlign: "center" } }, "Welcome back \u2014 unlock to resume", passkeyAddress ? ` ${passkeyAddress.slice(0, 6)}\u2026${passkeyAddress.slice(-4)}` : ""), /* @__PURE__ */ React3.createElement(
|
|
4910
|
+
"button",
|
|
4911
|
+
{
|
|
4912
|
+
onClick: handlePasskey,
|
|
4913
|
+
disabled: passkeyLoading,
|
|
4914
|
+
style: {
|
|
4915
|
+
width: "100%",
|
|
4916
|
+
padding: "11px 0",
|
|
4917
|
+
background: "var(--dc-accent)",
|
|
4918
|
+
color: "var(--dc-accent-text)",
|
|
4919
|
+
border: "none",
|
|
4920
|
+
borderRadius: 10,
|
|
4921
|
+
fontSize: 13,
|
|
4922
|
+
fontWeight: 700,
|
|
4923
|
+
cursor: passkeyLoading ? "not-allowed" : "pointer",
|
|
4924
|
+
opacity: passkeyLoading ? 0.7 : 1,
|
|
4925
|
+
fontFamily: "inherit",
|
|
4926
|
+
display: "flex",
|
|
4927
|
+
alignItems: "center",
|
|
4928
|
+
justifyContent: "center",
|
|
4929
|
+
gap: 7
|
|
4930
|
+
}
|
|
4931
|
+
},
|
|
4932
|
+
passkeyLoading ? "Verifying\u2026" : "\u{1F513} Continue with passkey"
|
|
4933
|
+
), /* @__PURE__ */ React3.createElement(
|
|
4934
|
+
"button",
|
|
4935
|
+
{
|
|
4936
|
+
onClick: () => setShowOtherMethods(true),
|
|
4937
|
+
style: {
|
|
4938
|
+
width: "100%",
|
|
4939
|
+
marginTop: 10,
|
|
4940
|
+
background: "none",
|
|
4941
|
+
border: "none",
|
|
4942
|
+
color: "var(--dc-text-muted)",
|
|
4943
|
+
fontSize: 12,
|
|
4944
|
+
cursor: "pointer",
|
|
4945
|
+
fontFamily: "inherit"
|
|
4946
|
+
}
|
|
4947
|
+
},
|
|
4948
|
+
"Use email or Google instead"
|
|
4949
|
+
), error && /* @__PURE__ */ React3.createElement("div", { style: { marginTop: 10, fontSize: 12, color: "var(--dc-danger, #ff5533)" } }, error)), !passkeyFirst && authMethods.includes("google") && /* @__PURE__ */ React3.createElement(
|
|
4889
4950
|
"button",
|
|
4890
4951
|
{
|
|
4891
4952
|
onClick: handleGoogle,
|
|
@@ -4906,7 +4967,7 @@ function SignInView({
|
|
|
4906
4967
|
}
|
|
4907
4968
|
},
|
|
4908
4969
|
googleLoading ? "Redirecting\u2026" : "Continue with Google"
|
|
4909
|
-
), authMethods.includes("email") && /* @__PURE__ */ React3.createElement(React3.Fragment, null, authMethods.includes("google") && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), !emailSent ? /* @__PURE__ */ React3.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React3.createElement(
|
|
4970
|
+
), !passkeyFirst && authMethods.includes("email") && /* @__PURE__ */ React3.createElement(React3.Fragment, null, authMethods.includes("google") && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), !emailSent ? /* @__PURE__ */ React3.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React3.createElement(
|
|
4910
4971
|
"input",
|
|
4911
4972
|
{
|
|
4912
4973
|
type: "email",
|
|
@@ -4945,7 +5006,7 @@ function SignInView({
|
|
|
4945
5006
|
fontWeight: 700,
|
|
4946
5007
|
cursor: "pointer",
|
|
4947
5008
|
fontFamily: "inherit"
|
|
4948
|
-
} }, emailLoading ? "\u2026" : "Verify"))), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React3.createElement(React3.Fragment, null, (authMethods.includes("google") || authMethods.includes("email")) && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), /* @__PURE__ */ React3.createElement(
|
|
5009
|
+
} }, emailLoading ? "\u2026" : "Verify"))), !passkeyFirst && customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React3.createElement(React3.Fragment, null, (authMethods.includes("google") || authMethods.includes("email")) && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), /* @__PURE__ */ React3.createElement(
|
|
4949
5010
|
"button",
|
|
4950
5011
|
{
|
|
4951
5012
|
onClick: handleCustomAuth,
|
|
@@ -5084,6 +5145,9 @@ function SocialWalletModal({
|
|
|
5084
5145
|
authMethods = ["google", "email"],
|
|
5085
5146
|
customAuthLabel,
|
|
5086
5147
|
onCustomAuthSignIn,
|
|
5148
|
+
canUsePasskey,
|
|
5149
|
+
passkeyAddress,
|
|
5150
|
+
onPasskeySignIn,
|
|
5087
5151
|
onRotateShares,
|
|
5088
5152
|
onExportPortableBackup,
|
|
5089
5153
|
rpcUrls,
|
|
@@ -5214,7 +5278,10 @@ function SocialWalletModal({
|
|
|
5214
5278
|
authMethods,
|
|
5215
5279
|
resumeHint: socialAuth.needsReconnect ? "your wallet" : void 0,
|
|
5216
5280
|
customAuthLabel,
|
|
5217
|
-
onCustomAuthSignIn
|
|
5281
|
+
onCustomAuthSignIn,
|
|
5282
|
+
canUsePasskey,
|
|
5283
|
+
passkeyAddress,
|
|
5284
|
+
onPasskeySignIn
|
|
5218
5285
|
}
|
|
5219
5286
|
) : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", { style: {
|
|
5220
5287
|
margin: "0 16px 14px",
|
|
@@ -5301,21 +5368,103 @@ function SocialWalletModal({
|
|
|
5301
5368
|
}
|
|
5302
5369
|
|
|
5303
5370
|
// src/ui/context.tsx
|
|
5304
|
-
import
|
|
5371
|
+
import React5, {
|
|
5305
5372
|
createContext,
|
|
5306
5373
|
useCallback as useCallback3,
|
|
5307
5374
|
useContext,
|
|
5308
|
-
useEffect as
|
|
5375
|
+
useEffect as useEffect4,
|
|
5309
5376
|
useRef,
|
|
5310
5377
|
useState as useState3
|
|
5311
5378
|
} from "react";
|
|
5379
|
+
|
|
5380
|
+
// src/ui/StatusOverlay.tsx
|
|
5381
|
+
import React4, { useEffect as useEffect3 } from "react";
|
|
5382
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
5383
|
+
function injectTokens3(theme) {
|
|
5384
|
+
if (typeof document === "undefined") return;
|
|
5385
|
+
const resolved = theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : theme;
|
|
5386
|
+
let el = document.getElementById("decane-tokens");
|
|
5387
|
+
if (!el) {
|
|
5388
|
+
el = document.createElement("style");
|
|
5389
|
+
el.id = "decane-tokens";
|
|
5390
|
+
document.head.appendChild(el);
|
|
5391
|
+
}
|
|
5392
|
+
el.textContent = `:root { ${CSS_VARS[resolved]} }`;
|
|
5393
|
+
}
|
|
5394
|
+
function WalletStatusOverlay({
|
|
5395
|
+
phase,
|
|
5396
|
+
theme = "auto"
|
|
5397
|
+
}) {
|
|
5398
|
+
useEffect3(() => {
|
|
5399
|
+
if (!phase) return;
|
|
5400
|
+
injectTokens3(theme);
|
|
5401
|
+
if (theme !== "auto") return;
|
|
5402
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5403
|
+
const h = () => injectTokens3("auto");
|
|
5404
|
+
mq.addEventListener("change", h);
|
|
5405
|
+
return () => mq.removeEventListener("change", h);
|
|
5406
|
+
}, [phase, theme]);
|
|
5407
|
+
if (!phase || typeof document === "undefined") return null;
|
|
5408
|
+
const title = phase === "creating" ? "Creating your wallet" : "Unlocking your wallet";
|
|
5409
|
+
const sub = phase === "creating" ? "Generating your keys and splitting them across three shares so no single party ever holds your wallet. This takes a few seconds \u2014 please keep this tab open." : "Reopening your secure session\u2026";
|
|
5410
|
+
return createPortal3(
|
|
5411
|
+
/* @__PURE__ */ React4.createElement(
|
|
5412
|
+
"div",
|
|
5413
|
+
{
|
|
5414
|
+
role: "dialog",
|
|
5415
|
+
"aria-modal": "true",
|
|
5416
|
+
"aria-label": title,
|
|
5417
|
+
style: {
|
|
5418
|
+
position: "fixed",
|
|
5419
|
+
inset: 0,
|
|
5420
|
+
zIndex: 1e4,
|
|
5421
|
+
display: "flex",
|
|
5422
|
+
alignItems: "center",
|
|
5423
|
+
justifyContent: "center",
|
|
5424
|
+
padding: 16,
|
|
5425
|
+
fontFamily: "var(--dc-font-sans, system-ui, sans-serif)"
|
|
5426
|
+
}
|
|
5427
|
+
},
|
|
5428
|
+
/* @__PURE__ */ React4.createElement("div", { style: {
|
|
5429
|
+
position: "absolute",
|
|
5430
|
+
inset: 0,
|
|
5431
|
+
background: "var(--dc-bg-overlay, rgba(0,0,0,0.75))",
|
|
5432
|
+
backdropFilter: "blur(8px)",
|
|
5433
|
+
WebkitBackdropFilter: "blur(8px)"
|
|
5434
|
+
} }),
|
|
5435
|
+
/* @__PURE__ */ React4.createElement("div", { style: {
|
|
5436
|
+
position: "relative",
|
|
5437
|
+
background: "var(--dc-bg, #0D0D0B)",
|
|
5438
|
+
border: "1px solid var(--dc-border, #3D3C39)",
|
|
5439
|
+
borderRadius: 20,
|
|
5440
|
+
width: "100%",
|
|
5441
|
+
maxWidth: 340,
|
|
5442
|
+
padding: "30px 26px",
|
|
5443
|
+
textAlign: "center",
|
|
5444
|
+
boxShadow: "0 24px 64px rgba(0,0,0,0.6)"
|
|
5445
|
+
} }, /* @__PURE__ */ React4.createElement("div", { style: { display: "flex", justifyContent: "center", marginBottom: 18 } }, /* @__PURE__ */ React4.createElement("div", { style: { position: "relative", width: 60, height: 60, display: "flex", alignItems: "center", justifyContent: "center" } }, /* @__PURE__ */ React4.createElement("span", { style: {
|
|
5446
|
+
position: "absolute",
|
|
5447
|
+
inset: 0,
|
|
5448
|
+
borderRadius: "50%",
|
|
5449
|
+
border: "2px solid var(--dc-border, #3D3C39)",
|
|
5450
|
+
borderTopColor: "var(--dc-accent, #F5C800)",
|
|
5451
|
+
animation: "dc-status-spin 0.8s linear infinite"
|
|
5452
|
+
} }), /* @__PURE__ */ React4.createElement(DecaneMark, { size: 32 }))), /* @__PURE__ */ React4.createElement("div", { style: { fontSize: 16, fontWeight: 800, color: "var(--dc-text, #F9F9F7)", letterSpacing: "-0.02em", marginBottom: 7 } }, title), /* @__PURE__ */ React4.createElement("div", { style: { fontSize: 12, color: "var(--dc-text-muted, #8A8884)", lineHeight: 1.55 } }, sub), /* @__PURE__ */ React4.createElement("div", { style: { marginTop: 20, paddingTop: 14, borderTop: "1px solid var(--dc-border, #3D3C39)", display: "flex", alignItems: "center", justifyContent: "center", gap: 6 } }, /* @__PURE__ */ React4.createElement(DecaneMark, { size: 13, muted: true }), /* @__PURE__ */ React4.createElement(DecaneWordmark, { size: 11, muted: true }))),
|
|
5453
|
+
/* @__PURE__ */ React4.createElement("style", null, `@keyframes dc-status-spin { to { transform: rotate(360deg); } }`)
|
|
5454
|
+
),
|
|
5455
|
+
document.body
|
|
5456
|
+
);
|
|
5457
|
+
}
|
|
5458
|
+
|
|
5459
|
+
// src/ui/context.tsx
|
|
5312
5460
|
var DEFAULT_SOCIAL = {
|
|
5313
5461
|
addresses: null,
|
|
5314
5462
|
isNewUser: false,
|
|
5315
5463
|
isUnlocked: false,
|
|
5316
5464
|
sessionExpiresAt: null,
|
|
5317
5465
|
needsReconnect: false,
|
|
5318
|
-
canUsePasskey: false
|
|
5466
|
+
canUsePasskey: false,
|
|
5467
|
+
phase: null
|
|
5319
5468
|
};
|
|
5320
5469
|
var DecaneContext = createContext(null);
|
|
5321
5470
|
function hasSocial(mode) {
|
|
@@ -5352,7 +5501,7 @@ function DecaneKit({ config, children }) {
|
|
|
5352
5501
|
const closeSelector = useCallback3(() => setSelectorOpen(false), []);
|
|
5353
5502
|
const openSocialModal = useCallback3(() => setSocialModalOpen(true), []);
|
|
5354
5503
|
const closeSocialModal = useCallback3(() => setSocialModalOpen(false), []);
|
|
5355
|
-
|
|
5504
|
+
useEffect4(() => {
|
|
5356
5505
|
if (!socialAuth.isUnlocked) return;
|
|
5357
5506
|
const id = setInterval(() => {
|
|
5358
5507
|
const sdk = socialRef.current;
|
|
@@ -5365,7 +5514,7 @@ function DecaneKit({ config, children }) {
|
|
|
5365
5514
|
}, 1e4);
|
|
5366
5515
|
return () => clearInterval(id);
|
|
5367
5516
|
}, [socialAuth.isUnlocked]);
|
|
5368
|
-
|
|
5517
|
+
useEffect4(() => {
|
|
5369
5518
|
if (!hasSocial(mode) || !config?.social || socialInitialized.current) return;
|
|
5370
5519
|
socialInitialized.current = true;
|
|
5371
5520
|
const {
|
|
@@ -5393,6 +5542,9 @@ function DecaneKit({ config, children }) {
|
|
|
5393
5542
|
() => setSocialAuth((p) => ({ ...p, isUnlocked: false, sessionExpiresAt: null }))
|
|
5394
5543
|
);
|
|
5395
5544
|
sdk.on("disconnected", () => setSocialAuth(DEFAULT_SOCIAL));
|
|
5545
|
+
sdk.on("wallet-creating", () => setSocialAuth((p) => ({ ...p, phase: "creating" })));
|
|
5546
|
+
sdk.on("wallet-unlocking", () => setSocialAuth((p) => ({ ...p, phase: "unlocking" })));
|
|
5547
|
+
sdk.on("connected", () => setSocialAuth((p) => ({ ...p, phase: null })));
|
|
5396
5548
|
const saved = sdk.getAddresses();
|
|
5397
5549
|
if (saved) {
|
|
5398
5550
|
setSocialAuth((p) => ({ ...p, addresses: saved, isUnlocked: false, needsReconnect: true }));
|
|
@@ -5401,7 +5553,10 @@ function DecaneKit({ config, children }) {
|
|
|
5401
5553
|
});
|
|
5402
5554
|
const params = new URLSearchParams(window.location.search);
|
|
5403
5555
|
if (params.has("decane_jwt") || params.has("decane_error")) {
|
|
5404
|
-
sdk.connectWithGoogle().then(applyResult).catch(
|
|
5556
|
+
sdk.connectWithGoogle().then(applyResult).catch((e) => {
|
|
5557
|
+
setSocialAuth((p) => p.phase ? { ...p, phase: null } : p);
|
|
5558
|
+
console.error(e);
|
|
5559
|
+
});
|
|
5405
5560
|
}
|
|
5406
5561
|
}).catch(console.error);
|
|
5407
5562
|
}, []);
|
|
@@ -5413,19 +5568,26 @@ function DecaneKit({ config, children }) {
|
|
|
5413
5568
|
isUnlocked: sdk?.isUnlocked() ?? false,
|
|
5414
5569
|
sessionExpiresAt: sdk?.sessionExpiresAt() ?? null,
|
|
5415
5570
|
needsReconnect: false,
|
|
5416
|
-
canUsePasskey: false
|
|
5571
|
+
canUsePasskey: false,
|
|
5572
|
+
phase: null
|
|
5417
5573
|
});
|
|
5418
5574
|
sdk?.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
5419
5575
|
});
|
|
5420
5576
|
}
|
|
5577
|
+
const clearPhase = useCallback3(() => setSocialAuth((p) => p.phase ? { ...p, phase: null } : p), []);
|
|
5421
5578
|
const signInWithPasskey = useCallback3(async () => {
|
|
5422
5579
|
const sdk = socialRef.current;
|
|
5423
5580
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5581
|
+
try {
|
|
5582
|
+
const result = await sdk.signInWithPasskey();
|
|
5583
|
+
applyResult(result);
|
|
5584
|
+
setSelectorOpen(false);
|
|
5585
|
+
return result;
|
|
5586
|
+
} catch (e) {
|
|
5587
|
+
clearPhase();
|
|
5588
|
+
throw e;
|
|
5589
|
+
}
|
|
5590
|
+
}, [clearPhase]);
|
|
5429
5591
|
const connectWithGoogle = useCallback3(async () => {
|
|
5430
5592
|
const sdk = socialRef.current;
|
|
5431
5593
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
@@ -5439,17 +5601,27 @@ function DecaneKit({ config, children }) {
|
|
|
5439
5601
|
const verifyEmailCode2 = useCallback3(async (email, code) => {
|
|
5440
5602
|
const sdk = socialRef.current;
|
|
5441
5603
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5604
|
+
try {
|
|
5605
|
+
const result = await sdk.verifyEmailCode(email, code);
|
|
5606
|
+
applyResult(result);
|
|
5607
|
+
setSelectorOpen(false);
|
|
5608
|
+
} catch (e) {
|
|
5609
|
+
clearPhase();
|
|
5610
|
+
throw e;
|
|
5611
|
+
}
|
|
5612
|
+
}, [clearPhase]);
|
|
5446
5613
|
const connectWithToken = useCallback3(async (token, opts) => {
|
|
5447
5614
|
const sdk = socialRef.current;
|
|
5448
5615
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5616
|
+
try {
|
|
5617
|
+
const result = await sdk.connectWithToken(token, opts);
|
|
5618
|
+
applyResult(result);
|
|
5619
|
+
setSelectorOpen(false);
|
|
5620
|
+
} catch (e) {
|
|
5621
|
+
clearPhase();
|
|
5622
|
+
throw e;
|
|
5623
|
+
}
|
|
5624
|
+
}, [clearPhase]);
|
|
5453
5625
|
const customAuthConfig = config?.social?.customAuth;
|
|
5454
5626
|
const connectWithCustomAuth = useCallback3(async () => {
|
|
5455
5627
|
if (!customAuthConfig) throw new Error("Custom auth not configured");
|
|
@@ -5459,14 +5631,21 @@ function DecaneKit({ config, children }) {
|
|
|
5459
5631
|
const socialUnlock = useCallback3(async () => {
|
|
5460
5632
|
const sdk = socialRef.current;
|
|
5461
5633
|
if (!sdk) return;
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5634
|
+
setSocialAuth((p) => ({ ...p, phase: "unlocking" }));
|
|
5635
|
+
try {
|
|
5636
|
+
await sdk.unlock();
|
|
5637
|
+
setSocialAuth((p) => ({
|
|
5638
|
+
...p,
|
|
5639
|
+
isUnlocked: sdk.isUnlocked(),
|
|
5640
|
+
sessionExpiresAt: sdk.sessionExpiresAt(),
|
|
5641
|
+
needsReconnect: false,
|
|
5642
|
+
phase: null
|
|
5643
|
+
}));
|
|
5644
|
+
} catch (e) {
|
|
5645
|
+
clearPhase();
|
|
5646
|
+
throw e;
|
|
5647
|
+
}
|
|
5648
|
+
}, [clearPhase]);
|
|
5470
5649
|
const socialDisconnect = useCallback3(async () => {
|
|
5471
5650
|
const sdk = socialRef.current;
|
|
5472
5651
|
if (!sdk) return;
|
|
@@ -5535,7 +5714,7 @@ function DecaneKit({ config, children }) {
|
|
|
5535
5714
|
[]
|
|
5536
5715
|
);
|
|
5537
5716
|
const networks = deriveNetworks(mode, config?.networks);
|
|
5538
|
-
return /* @__PURE__ */
|
|
5717
|
+
return /* @__PURE__ */ React5.createElement(
|
|
5539
5718
|
DecaneContext.Provider,
|
|
5540
5719
|
{
|
|
5541
5720
|
value: {
|
|
@@ -5573,7 +5752,7 @@ function DecaneKit({ config, children }) {
|
|
|
5573
5752
|
}
|
|
5574
5753
|
},
|
|
5575
5754
|
children,
|
|
5576
|
-
/* @__PURE__ */
|
|
5755
|
+
/* @__PURE__ */ React5.createElement(
|
|
5577
5756
|
WalletSelector,
|
|
5578
5757
|
{
|
|
5579
5758
|
provider: walletRef.current,
|
|
@@ -5594,7 +5773,7 @@ function DecaneKit({ config, children }) {
|
|
|
5594
5773
|
onCustomAuthSignIn: customAuthConfig ? connectWithCustomAuth : void 0
|
|
5595
5774
|
}
|
|
5596
5775
|
),
|
|
5597
|
-
/* @__PURE__ */
|
|
5776
|
+
/* @__PURE__ */ React5.createElement(
|
|
5598
5777
|
SocialWalletModal,
|
|
5599
5778
|
{
|
|
5600
5779
|
open: socialModalOpen,
|
|
@@ -5609,12 +5788,16 @@ function DecaneKit({ config, children }) {
|
|
|
5609
5788
|
onEmailStart: connectWithEmail,
|
|
5610
5789
|
onEmailVerify: verifyEmailCode2,
|
|
5611
5790
|
authMethods: config?.social?.authMethods ?? ["google", "email"],
|
|
5791
|
+
canUsePasskey: socialAuth.canUsePasskey,
|
|
5792
|
+
passkeyAddress: socialAuth.addresses?.evm ?? socialAuth.addresses?.solana ?? null,
|
|
5793
|
+
onPasskeySignIn: signInWithPasskey,
|
|
5612
5794
|
onRotateShares: socialRotateShares,
|
|
5613
5795
|
onExportPortableBackup: socialExportPortableBackup,
|
|
5614
5796
|
rpcUrls: config?.social?.rpcUrls,
|
|
5615
5797
|
chains: config?.social?.chains
|
|
5616
5798
|
}
|
|
5617
|
-
)
|
|
5799
|
+
),
|
|
5800
|
+
/* @__PURE__ */ React5.createElement(WalletStatusOverlay, { phase: socialAuth.phase, theme })
|
|
5618
5801
|
);
|
|
5619
5802
|
}
|
|
5620
5803
|
function useDecaneContext() {
|
|
@@ -5628,7 +5811,7 @@ function useDecaneContext() {
|
|
|
5628
5811
|
}
|
|
5629
5812
|
|
|
5630
5813
|
// src/ui/hooks.ts
|
|
5631
|
-
import { useState as useState4, useEffect as
|
|
5814
|
+
import { useState as useState4, useEffect as useEffect5, useCallback as useCallback4 } from "react";
|
|
5632
5815
|
function useSocialAuth() {
|
|
5633
5816
|
const {
|
|
5634
5817
|
socialAuth,
|
|
@@ -5638,6 +5821,7 @@ function useSocialAuth() {
|
|
|
5638
5821
|
connectWithToken,
|
|
5639
5822
|
customAuthLabel,
|
|
5640
5823
|
connectWithCustomAuth,
|
|
5824
|
+
signInWithPasskey: ctxSignInWithPasskey,
|
|
5641
5825
|
socialUnlock,
|
|
5642
5826
|
socialDisconnect
|
|
5643
5827
|
} = useDecaneContext();
|
|
@@ -5706,6 +5890,15 @@ function useSocialAuth() {
|
|
|
5706
5890
|
setCustomLoading(false);
|
|
5707
5891
|
}
|
|
5708
5892
|
}, [connectWithCustomAuth]);
|
|
5893
|
+
const signInWithPasskey = useCallback4(async () => {
|
|
5894
|
+
setError(null);
|
|
5895
|
+
try {
|
|
5896
|
+
await ctxSignInWithPasskey();
|
|
5897
|
+
} catch (e) {
|
|
5898
|
+
setError(e.message);
|
|
5899
|
+
throw e;
|
|
5900
|
+
}
|
|
5901
|
+
}, [ctxSignInWithPasskey]);
|
|
5709
5902
|
const unlock = useCallback4(async () => {
|
|
5710
5903
|
setError(null);
|
|
5711
5904
|
try {
|
|
@@ -5730,6 +5923,11 @@ function useSocialAuth() {
|
|
|
5730
5923
|
isNewUser: socialAuth.isNewUser,
|
|
5731
5924
|
addresses: socialAuth.addresses,
|
|
5732
5925
|
sessionExpiresAt: socialAuth.sessionExpiresAt,
|
|
5926
|
+
// True when this device can skip the full sign-in and resume with a passkey.
|
|
5927
|
+
canUsePasskey: socialAuth.canUsePasskey,
|
|
5928
|
+
// Long-running work in progress, for driving your own status UI.
|
|
5929
|
+
phase: socialAuth.phase,
|
|
5930
|
+
isCreatingWallet: socialAuth.phase === "creating",
|
|
5733
5931
|
// True when addresses are known (from a previous visit) but there's no
|
|
5734
5932
|
// live session — unlock() will always throw in this state (every
|
|
5735
5933
|
// unlock requires a fresh JWT, and JWTs are never persisted). Route
|
|
@@ -5747,6 +5945,7 @@ function useSocialAuth() {
|
|
|
5747
5945
|
confirmEmailCode,
|
|
5748
5946
|
signInWithToken,
|
|
5749
5947
|
signInWithCustomAuth: connectWithCustomAuth ? signInWithCustomAuth : void 0,
|
|
5948
|
+
signInWithPasskey,
|
|
5750
5949
|
unlock,
|
|
5751
5950
|
disconnect
|
|
5752
5951
|
};
|
|
@@ -5860,6 +6059,10 @@ function useSocialWallet() {
|
|
|
5860
6059
|
// unlock requires a fresh JWT, and JWTs are never persisted). Route
|
|
5861
6060
|
// this into a real reconnect (signInWithGoogle / sendEmailCode), not unlock().
|
|
5862
6061
|
needsReconnect: socialAuth.needsReconnect,
|
|
6062
|
+
// Long-running work in progress ('creating' | 'unlocking' | null), for
|
|
6063
|
+
// driving your own status UI in addition to the SDK's built-in overlay.
|
|
6064
|
+
phase: socialAuth.phase,
|
|
6065
|
+
isCreatingWallet: socialAuth.phase === "creating",
|
|
5863
6066
|
// Modal
|
|
5864
6067
|
modalOpen: socialModalOpen,
|
|
5865
6068
|
openModal: openSocialModal,
|
|
@@ -5908,7 +6111,7 @@ function useSocialBalance(chain) {
|
|
|
5908
6111
|
setLoading(false);
|
|
5909
6112
|
}
|
|
5910
6113
|
}, [socialGetBalance, chain, socialAuth.addresses]);
|
|
5911
|
-
|
|
6114
|
+
useEffect5(() => {
|
|
5912
6115
|
let cancelled = false;
|
|
5913
6116
|
if (!socialAuth.addresses) {
|
|
5914
6117
|
setBalance(null);
|
|
@@ -5940,7 +6143,7 @@ function toDecaneError(err) {
|
|
|
5940
6143
|
function useDecane() {
|
|
5941
6144
|
const { provider, theme, setTheme } = useDecaneContext();
|
|
5942
6145
|
const [, forceUpdate] = useState4(0);
|
|
5943
|
-
|
|
6146
|
+
useEffect5(() => {
|
|
5944
6147
|
const refresh = () => forceUpdate((n) => n + 1);
|
|
5945
6148
|
const events = [
|
|
5946
6149
|
"connect",
|
|
@@ -5982,7 +6185,7 @@ function useChainSwitch() {
|
|
|
5982
6185
|
const [, forceUpdate] = useState4(0);
|
|
5983
6186
|
const [loading, setLoading] = useState4(false);
|
|
5984
6187
|
const [error, setError] = useState4(null);
|
|
5985
|
-
|
|
6188
|
+
useEffect5(() => {
|
|
5986
6189
|
const refresh = () => forceUpdate((n) => n + 1);
|
|
5987
6190
|
provider.on("connect", refresh);
|
|
5988
6191
|
provider.on("disconnect", refresh);
|