@swype-org/react-sdk 0.1.9 → 0.1.11

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 CHANGED
@@ -178,8 +178,9 @@ async function fetchChains(apiBaseUrl, token) {
178
178
  const data = await res.json();
179
179
  return data.items;
180
180
  }
181
- async function fetchAccounts(apiBaseUrl, token) {
182
- const res = await fetch(`${apiBaseUrl}/v1/accounts`, {
181
+ async function fetchAccounts(apiBaseUrl, token, credentialId) {
182
+ const params = new URLSearchParams({ credentialId });
183
+ const res = await fetch(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
183
184
  headers: { Authorization: `Bearer ${token}` }
184
185
  });
185
186
  if (!res.ok) await throwApiError(res);
@@ -189,6 +190,7 @@ async function fetchAccounts(apiBaseUrl, token) {
189
190
  async function createTransfer(apiBaseUrl, token, params) {
190
191
  const body = {
191
192
  id: crypto.randomUUID(),
193
+ credentialId: params.credentialId,
192
194
  sources: [{ [params.sourceType]: params.sourceId }],
193
195
  destinations: [
194
196
  {
@@ -561,6 +563,27 @@ async function getWalletClient(config, parameters = {}) {
561
563
  const client = await getConnectorClient(config, parameters);
562
564
  return client.extend(viem.walletActions);
563
565
  }
566
+
567
+ // src/passkeyRpId.ts
568
+ function normalizeConfiguredDomain(value) {
569
+ return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
570
+ }
571
+ function resolveRootDomainFromHostname(hostname) {
572
+ const trimmedHostname = hostname.trim().toLowerCase();
573
+ if (!trimmedHostname) {
574
+ return "localhost";
575
+ }
576
+ if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
577
+ return trimmedHostname;
578
+ }
579
+ const parts = trimmedHostname.split(".").filter(Boolean);
580
+ if (parts.length < 2) {
581
+ return trimmedHostname;
582
+ }
583
+ return parts.slice(-2).join(".");
584
+ }
585
+
586
+ // src/hooks.ts
564
587
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
565
588
  var WALLET_CLIENT_POLL_MS = 200;
566
589
  var ACTION_POLL_INTERVAL_MS = 500;
@@ -611,10 +634,10 @@ function readEnvValue(name) {
611
634
  function resolvePasskeyRpId() {
612
635
  const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("SWYPE_DOMAIN");
613
636
  if (configuredDomain) {
614
- return configuredDomain.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
637
+ return normalizeConfiguredDomain(configuredDomain);
615
638
  }
616
639
  if (typeof window !== "undefined") {
617
- return window.location.hostname;
640
+ return resolveRootDomainFromHostname(window.location.hostname);
618
641
  }
619
642
  return "localhost";
620
643
  }
@@ -1934,6 +1957,7 @@ function AdvancedSettings({
1934
1957
  )
1935
1958
  ] });
1936
1959
  }
1960
+ var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
1937
1961
  function isMobile() {
1938
1962
  if (typeof navigator === "undefined") return false;
1939
1963
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
@@ -2037,8 +2061,13 @@ function SwypePayment({
2037
2061
  const [transfer, setTransfer] = react.useState(null);
2038
2062
  const [creatingTransfer, setCreatingTransfer] = react.useState(false);
2039
2063
  const [registeringPasskey, setRegisteringPasskey] = react.useState(false);
2064
+ const [activeCredentialId, setActiveCredentialId] = react.useState(() => {
2065
+ if (typeof window === "undefined") return null;
2066
+ return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
2067
+ });
2040
2068
  const [mobileFlow, setMobileFlow] = react.useState(false);
2041
2069
  const pollingTransferIdRef = react.useRef(null);
2070
+ const mobileSigningTransferIdRef = react.useRef(null);
2042
2071
  const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
2043
2072
  const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
2044
2073
  const initializedSelectSourceActionRef = react.useRef(null);
@@ -2061,7 +2090,7 @@ function SwypePayment({
2061
2090
  if (!token || cancelled) return;
2062
2091
  const { config } = await fetchUserConfig(apiBaseUrl, token);
2063
2092
  if (cancelled) return;
2064
- if (!config.passkey) {
2093
+ if (!config.passkey || !activeCredentialId) {
2065
2094
  setStep("register-passkey");
2066
2095
  } else if (depositAmount != null && depositAmount > 0) {
2067
2096
  setStep("ready");
@@ -2082,7 +2111,7 @@ function SwypePayment({
2082
2111
  return () => {
2083
2112
  cancelled = true;
2084
2113
  };
2085
- }, [ready, authenticated, step, depositAmount, apiBaseUrl, getAccessToken]);
2114
+ }, [ready, authenticated, step, depositAmount, apiBaseUrl, getAccessToken, activeCredentialId]);
2086
2115
  const loadingDataRef = react.useRef(false);
2087
2116
  react.useEffect(() => {
2088
2117
  if (!authenticated) return;
@@ -2093,11 +2122,15 @@ function SwypePayment({
2093
2122
  setLoadingData(true);
2094
2123
  setError(null);
2095
2124
  try {
2125
+ if (!activeCredentialId) {
2126
+ setStep("register-passkey");
2127
+ return;
2128
+ }
2096
2129
  const token = await getAccessToken();
2097
2130
  if (!token) throw new Error("Not authenticated");
2098
2131
  const [prov, accts, chn] = await Promise.all([
2099
2132
  fetchProviders(apiBaseUrl, token),
2100
- fetchAccounts(apiBaseUrl, token),
2133
+ fetchAccounts(apiBaseUrl, token, activeCredentialId),
2101
2134
  fetchChains(apiBaseUrl, token)
2102
2135
  ]);
2103
2136
  if (cancelled) return;
@@ -2129,7 +2162,7 @@ function SwypePayment({
2129
2162
  cancelled = true;
2130
2163
  loadingDataRef.current = false;
2131
2164
  };
2132
- }, [authenticated, accounts.length, apiBaseUrl, getAccessToken]);
2165
+ }, [authenticated, accounts.length, apiBaseUrl, getAccessToken, activeCredentialId]);
2133
2166
  react.useEffect(() => {
2134
2167
  if (!polling.transfer) return;
2135
2168
  if (polling.transfer.status === "COMPLETED") {
@@ -2142,6 +2175,27 @@ function SwypePayment({
2142
2175
  setError("Transfer failed.");
2143
2176
  }
2144
2177
  }, [polling.transfer, onComplete]);
2178
+ react.useEffect(() => {
2179
+ if (!mobileFlow) return;
2180
+ const polledTransfer = polling.transfer;
2181
+ if (!polledTransfer) return;
2182
+ if (polledTransfer.status !== "AUTHORIZED") return;
2183
+ if (transferSigning.signing) return;
2184
+ if (mobileSigningTransferIdRef.current === polledTransfer.id) return;
2185
+ mobileSigningTransferIdRef.current = polledTransfer.id;
2186
+ const sign = async () => {
2187
+ try {
2188
+ const signedTransfer = await transferSigning.signTransfer(polledTransfer.id);
2189
+ setTransfer(signedTransfer);
2190
+ } catch (err) {
2191
+ mobileSigningTransferIdRef.current = null;
2192
+ const msg = err instanceof Error ? err.message : "Failed to sign transfer";
2193
+ setError(msg);
2194
+ onError?.(msg);
2195
+ }
2196
+ };
2197
+ void sign();
2198
+ }, [mobileFlow, polling.transfer, transferSigning, onError]);
2145
2199
  react.useEffect(() => {
2146
2200
  if (!mobileFlow || !polling.isPolling) return;
2147
2201
  const handleVisibility = () => {
@@ -2203,6 +2257,11 @@ function SwypePayment({
2203
2257
  setError("No account or provider selected.");
2204
2258
  return;
2205
2259
  }
2260
+ if (!activeCredentialId) {
2261
+ setError("Create a passkey on this device before continuing.");
2262
+ setStep("register-passkey");
2263
+ return;
2264
+ }
2206
2265
  setStep("processing");
2207
2266
  setError(null);
2208
2267
  setCreatingTransfer(true);
@@ -2238,6 +2297,7 @@ function SwypePayment({
2238
2297
  }
2239
2298
  }
2240
2299
  const t = await createTransfer(apiBaseUrl, token, {
2300
+ credentialId: activeCredentialId,
2241
2301
  sourceType: effectiveSourceType,
2242
2302
  sourceId: effectiveSourceId,
2243
2303
  destination,
@@ -2271,6 +2331,7 @@ function SwypePayment({
2271
2331
  amount,
2272
2332
  sourceId,
2273
2333
  sourceType,
2334
+ activeCredentialId,
2274
2335
  destination,
2275
2336
  apiBaseUrl,
2276
2337
  getAccessToken,
@@ -2288,6 +2349,7 @@ function SwypePayment({
2288
2349
  setAmount(depositAmount != null ? depositAmount.toString() : "");
2289
2350
  setMobileFlow(false);
2290
2351
  pollingTransferIdRef.current = null;
2352
+ mobileSigningTransferIdRef.current = null;
2291
2353
  setConnectingNewAccount(false);
2292
2354
  setSelectedWalletId(null);
2293
2355
  setAdvancedSettings({ asset: null, chain: null });
@@ -2468,6 +2530,8 @@ function SwypePayment({
2468
2530
  if (!token) throw new Error("Not authenticated");
2469
2531
  const { credentialId, publicKey } = await createPasskeyCredential("Swype User");
2470
2532
  await registerPasskey(apiBaseUrl, token, credentialId, publicKey);
2533
+ setActiveCredentialId(credentialId);
2534
+ window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
2471
2535
  if (depositAmount != null && depositAmount > 0) {
2472
2536
  setStep("ready");
2473
2537
  } else {