@erikey/react 0.3.2 → 0.3.4

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.d.mts CHANGED
@@ -2190,8 +2190,10 @@ type DashboardClient = ReturnType<typeof createDashboardClient>;
2190
2190
  * - Bearer token auth for cross-origin iframes (Sandpack)
2191
2191
  * - localStorage token storage when cookies are blocked
2192
2192
  *
2193
- * This replaces 300+ lines of custom fetch code with better-auth client,
2194
- * enabling signIn.social(), useSession(), and all better-auth features.
2193
+ * Architecture:
2194
+ * - Uses better-auth's native reactivity (nanostores + useSyncExternalStore)
2195
+ * - Hooks into fetchOptions.onSuccess to store tokens for cross-origin
2196
+ * - No Proxy wrapper - uses better-auth's useSession directly
2195
2197
  */
2196
2198
  interface AuthClientConfig {
2197
2199
  /**
@@ -2208,9 +2210,9 @@ interface AuthClientConfig {
2208
2210
  /**
2209
2211
  * Create an auth client for end-user authentication
2210
2212
  *
2211
- * Wraps better-auth/react with Erikey-specific enhancements.
2212
- * Automatically detects cross-origin iframes (Sandpack) and uses
2213
- * Bearer tokens instead of cookies.
2213
+ * Uses better-auth/react with Erikey-specific configuration.
2214
+ * Automatically detects cross-origin contexts (Sandpack, deployed sites)
2215
+ * and uses Bearer tokens instead of cookies.
2214
2216
  *
2215
2217
  * @example Basic usage
2216
2218
  * ```tsx
@@ -2227,7 +2229,7 @@ interface AuthClientConfig {
2227
2229
  * // Social OAuth
2228
2230
  * await auth.signIn.social({ provider: 'google' });
2229
2231
  *
2230
- * // Reactive session hook (React)
2232
+ * // Reactive session hook (React) - uses better-auth's nanostores
2231
2233
  * const { data: session, isPending } = auth.useSession();
2232
2234
  *
2233
2235
  * // Sign out
package/dist/index.mjs CHANGED
@@ -1611,9 +1611,6 @@ function createDashboardClient(config) {
1611
1611
  });
1612
1612
  }
1613
1613
 
1614
- // src/auth-client.ts
1615
- import { useState, useEffect, useCallback as useCallback2 } from "react";
1616
-
1617
1614
  // src/lib/cross-origin-auth.ts
1618
1615
  function shouldUseBearerAuth(authApiUrl) {
1619
1616
  if (typeof window === "undefined") {
@@ -1662,10 +1659,6 @@ function clearToken(projectId) {
1662
1659
  function createAuthClient2(config) {
1663
1660
  const { projectId, baseUrl = "https://auth.erikey.com" } = config;
1664
1661
  const useBearerAuth = shouldUseBearerAuth(baseUrl);
1665
- const sessionListeners = /* @__PURE__ */ new Set();
1666
- const notifySessionChange = () => {
1667
- sessionListeners.forEach((listener) => listener());
1668
- };
1669
1662
  const fetchOptions = {
1670
1663
  // Always send project ID header for multi-tenant routing
1671
1664
  headers: {
@@ -1677,131 +1670,40 @@ function createAuthClient2(config) {
1677
1670
  type: "Bearer",
1678
1671
  token: () => getStoredToken(projectId) || ""
1679
1672
  }
1673
+ },
1674
+ // Hook into successful responses to handle token storage
1675
+ onSuccess: async (context) => {
1676
+ if (!useBearerAuth) return;
1677
+ const url = context.response?.url || "";
1678
+ const path = new URL(url, baseUrl).pathname;
1679
+ const token = context.data?.token;
1680
+ if (token && (path.includes("/sign-in") || path.includes("/sign-up"))) {
1681
+ const session = {
1682
+ id: context.data?.session?.id || "session",
1683
+ token,
1684
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
1685
+ };
1686
+ storeToken(projectId, session);
1687
+ }
1688
+ if (path.includes("/sign-out")) {
1689
+ clearToken(projectId);
1690
+ }
1691
+ },
1692
+ // Handle errors - clear token on auth errors
1693
+ onError: async (context) => {
1694
+ if (!useBearerAuth) return;
1695
+ if (context.response?.status === 401) {
1696
+ clearToken(projectId);
1697
+ }
1680
1698
  }
1681
1699
  };
1682
1700
  const client = createAuthClient({
1683
1701
  baseURL: baseUrl,
1684
1702
  fetchOptions,
1685
- // For same-origin, include cookies
1703
+ // For same-origin, include cookies (cross-origin uses Bearer from fetchOptions.auth)
1686
1704
  ...!useBearerAuth && { credentials: "include" }
1687
1705
  });
1688
- if (!useBearerAuth) {
1689
- return client;
1690
- }
1691
- return new Proxy({}, {
1692
- get(_target, prop) {
1693
- if (typeof prop === "symbol") {
1694
- return client[prop];
1695
- }
1696
- const mergeHeaders = (args) => {
1697
- const [data, ...rest] = args;
1698
- if (!data || typeof data !== "object") return args;
1699
- const existingHeaders = data.fetchOptions?.headers || {};
1700
- const mergedData = {
1701
- ...data,
1702
- fetchOptions: {
1703
- ...data.fetchOptions,
1704
- headers: {
1705
- "X-Project-Id": projectId,
1706
- ...existingHeaders
1707
- }
1708
- }
1709
- };
1710
- return [mergedData, ...rest];
1711
- };
1712
- if (prop === "signIn") {
1713
- return new Proxy(client.signIn, {
1714
- get(_signInTarget, signInProp) {
1715
- if (signInProp === "email") {
1716
- return async (...args) => {
1717
- const mergedArgs = mergeHeaders(args);
1718
- const result = await client.signIn.email(...mergedArgs);
1719
- const token = result.data?.token;
1720
- if (token) {
1721
- const session = {
1722
- id: result.data?.session?.id || "session",
1723
- token,
1724
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
1725
- };
1726
- storeToken(projectId, session);
1727
- notifySessionChange();
1728
- }
1729
- return result;
1730
- };
1731
- }
1732
- return client.signIn[signInProp];
1733
- }
1734
- });
1735
- }
1736
- if (prop === "signUp") {
1737
- return new Proxy(client.signUp, {
1738
- get(_signUpTarget, signUpProp) {
1739
- if (signUpProp === "email") {
1740
- return async (...args) => {
1741
- const mergedArgs = mergeHeaders(args);
1742
- const result = await client.signUp.email(...mergedArgs);
1743
- const token = result.data?.token;
1744
- if (token) {
1745
- const session = {
1746
- id: result.data?.session?.id || "session",
1747
- token,
1748
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
1749
- };
1750
- storeToken(projectId, session);
1751
- notifySessionChange();
1752
- }
1753
- return result;
1754
- };
1755
- }
1756
- return client.signUp[signUpProp];
1757
- }
1758
- });
1759
- }
1760
- if (prop === "signOut") {
1761
- return async (...args) => {
1762
- clearToken(projectId);
1763
- const result = await client.signOut(...args);
1764
- notifySessionChange();
1765
- return result;
1766
- };
1767
- }
1768
- if (prop === "useSession") {
1769
- return function useSession() {
1770
- const [data, setData] = useState(null);
1771
- const [isPending, setIsPending] = useState(true);
1772
- const [error, setError] = useState(null);
1773
- const refetch = useCallback2(async () => {
1774
- setIsPending(true);
1775
- try {
1776
- const result = await client.getSession();
1777
- if (result.error) {
1778
- setError(result.error);
1779
- setData(null);
1780
- } else {
1781
- setData(result.data);
1782
- setError(null);
1783
- }
1784
- } catch (e) {
1785
- setError(e);
1786
- setData(null);
1787
- }
1788
- setIsPending(false);
1789
- }, []);
1790
- useEffect(() => {
1791
- refetch();
1792
- }, [refetch]);
1793
- useEffect(() => {
1794
- sessionListeners.add(refetch);
1795
- return () => {
1796
- sessionListeners.delete(refetch);
1797
- };
1798
- }, [refetch]);
1799
- return { data, isPending, error, refetch };
1800
- };
1801
- }
1802
- return client[prop];
1803
- }
1804
- });
1706
+ return client;
1805
1707
  }
1806
1708
 
1807
1709
  // src/kv-client.ts