@ab-org/predicate-market-sdk 0.0.1

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.
Files changed (86) hide show
  1. package/README.md +246 -0
  2. package/dist/auth/autoReconnect.d.ts +11 -0
  3. package/dist/auth/autoReconnect.js +36 -0
  4. package/dist/auth/bundledConfig.d.ts +2 -0
  5. package/dist/auth/bundledConfig.js +19 -0
  6. package/dist/auth/config.d.ts +29 -0
  7. package/dist/auth/config.js +53 -0
  8. package/dist/auth/google.d.ts +43 -0
  9. package/dist/auth/google.js +147 -0
  10. package/dist/auth/twitter.d.ts +7 -0
  11. package/dist/auth/twitter.js +94 -0
  12. package/dist/constants/chains.d.ts +22 -0
  13. package/dist/constants/chains.js +23 -0
  14. package/dist/index.d.ts +19 -0
  15. package/dist/index.js +19 -0
  16. package/dist/modules/api.d.ts +144 -0
  17. package/dist/modules/api.js +93 -0
  18. package/dist/modules/balanceQuery.d.ts +20 -0
  19. package/dist/modules/balanceQuery.js +58 -0
  20. package/dist/modules/deposit.d.ts +31 -0
  21. package/dist/modules/deposit.js +57 -0
  22. package/dist/modules/marketData.d.ts +8 -0
  23. package/dist/modules/marketData.js +113 -0
  24. package/dist/modules/withdraw.d.ts +31 -0
  25. package/dist/modules/withdraw.js +60 -0
  26. package/dist/modules/withdrawExecutor.d.ts +47 -0
  27. package/dist/modules/withdrawExecutor.js +208 -0
  28. package/dist/policyAdapter.d.ts +11 -0
  29. package/dist/policyAdapter.js +38 -0
  30. package/dist/types.d.ts +62 -0
  31. package/dist/types.js +1 -0
  32. package/dist/ui/DepositModal.d.ts +36 -0
  33. package/dist/ui/DepositModal.js +326 -0
  34. package/dist/ui/SignInModal.d.ts +22 -0
  35. package/dist/ui/SignInModal.js +74 -0
  36. package/dist/ui/SignInModal.sections.d.ts +33 -0
  37. package/dist/ui/SignInModal.sections.js +45 -0
  38. package/dist/ui/SignInModal.shared.d.ts +15 -0
  39. package/dist/ui/SignInModal.shared.js +87 -0
  40. package/dist/ui/WalletSelectionModal.d.ts +14 -0
  41. package/dist/ui/WalletSelectionModal.js +54 -0
  42. package/dist/ui/WithdrawModal.d.ts +47 -0
  43. package/dist/ui/WithdrawModal.js +528 -0
  44. package/dist/ui/components/CloseButton.d.ts +4 -0
  45. package/dist/ui/components/CloseButton.js +15 -0
  46. package/dist/ui/components/Countdown.d.ts +16 -0
  47. package/dist/ui/components/Countdown.js +42 -0
  48. package/dist/ui/components/DepositDetailsPanel.d.ts +8 -0
  49. package/dist/ui/components/DepositDetailsPanel.js +117 -0
  50. package/dist/ui/components/DropdownField.d.ts +19 -0
  51. package/dist/ui/components/DropdownField.js +81 -0
  52. package/dist/ui/components/Field.d.ts +10 -0
  53. package/dist/ui/components/Field.js +21 -0
  54. package/dist/ui/components/LoginRequiredOverlay.d.ts +6 -0
  55. package/dist/ui/components/LoginRequiredOverlay.js +31 -0
  56. package/dist/ui/components/ModalCard.d.ts +9 -0
  57. package/dist/ui/components/ModalCard.js +14 -0
  58. package/dist/ui/components/ModalFrame.d.ts +9 -0
  59. package/dist/ui/components/ModalFrame.js +18 -0
  60. package/dist/ui/components/PrimaryButton.d.ts +2 -0
  61. package/dist/ui/components/PrimaryButton.js +14 -0
  62. package/dist/ui/components/QRCodePanel.d.ts +4 -0
  63. package/dist/ui/components/QRCodePanel.js +43 -0
  64. package/dist/ui/components/Select.d.ts +12 -0
  65. package/dist/ui/components/Select.js +29 -0
  66. package/dist/ui/components/StepIndicator.d.ts +7 -0
  67. package/dist/ui/components/StepIndicator.js +35 -0
  68. package/dist/ui/components/Success.d.ts +1 -0
  69. package/dist/ui/components/Success.js +4 -0
  70. package/dist/ui/components/Toast.d.ts +8 -0
  71. package/dist/ui/components/Toast.js +51 -0
  72. package/dist/ui/hooks/useSession.d.ts +2 -0
  73. package/dist/ui/hooks/useSession.js +10 -0
  74. package/dist/ui/signInTypes.d.ts +25 -0
  75. package/dist/ui/signInTypes.js +1 -0
  76. package/dist/ui/theme.d.ts +31 -0
  77. package/dist/ui/theme.js +31 -0
  78. package/dist/ui/useSignInModalController.d.ts +25 -0
  79. package/dist/ui/useSignInModalController.js +173 -0
  80. package/dist/utils/env.d.ts +1 -0
  81. package/dist/utils/env.js +61 -0
  82. package/dist/utils/explorer.d.ts +3 -0
  83. package/dist/utils/explorer.js +47 -0
  84. package/dist/walletUtils.d.ts +3 -0
  85. package/dist/walletUtils.js +3 -0
  86. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # @ab-org/predicate-market-sdk
2
+
3
+ Prediction-market specific helpers built on top of `@ab-org/sdk-core`.
4
+
5
+ ## Key features
6
+ - Built-in wallet connection modal (social + plugin wallets)
7
+ - Connection state via `createWalletConnectController()` and `createAccountController()` from `@ab-org/sdk-core`
8
+ - Unified smart-wallet session metadata with capability policy support
9
+ - High-level execution helpers via `createWalletExecutionController()`
10
+ - Deposit / withdraw controllers with USD1-first defaults
11
+ - **Dynamic token & chain lists** via `MarketDataProvider` (backend-driven, mock included)
12
+ - **Quote / slippage API** — fetch real-time pricing before confirming deposit or withdraw
13
+ - Predicate-market policy adapter for deposit / withdraw / trade flows
14
+
15
+ ## Installation
16
+ ```bash
17
+ npm install @ab-org/sdk-core @ab-org/predicate-market-sdk
18
+ ```
19
+
20
+ ## Quick start
21
+ ```tsx
22
+ import {
23
+ WalletConnector,
24
+ MetaMaskProvider,
25
+ createAccountController,
26
+ createWalletConnectController,
27
+ createWalletExecutionController,
28
+ } from "@ab-org/sdk-core";
29
+ import {
30
+ createDepositController,
31
+ createWithdrawController,
32
+ createMockMarketDataProvider,
33
+ createPredicateMarketPolicyAdapter,
34
+ } from "@ab-org/predicate-market-sdk";
35
+
36
+ const connector = new WalletConnector([
37
+ new MetaMaskProvider(),
38
+ // new CubistSocialProvider(cubistClient)
39
+ ]);
40
+
41
+ const walletConnect = createWalletConnectController({ connector, defaultAdapterId: "metamask" });
42
+ const account = createAccountController();
43
+ const execution = createWalletExecutionController();
44
+
45
+ // Use the mock provider until real backend endpoints are ready
46
+ const marketData = createMockMarketDataProvider();
47
+
48
+ const deposit = createDepositController(custodyAdapter, marketData);
49
+ const withdraw = createWithdrawController(custodyAdapter, marketData);
50
+ const policy = createPredicateMarketPolicyAdapter({
51
+ appId: "prediction-market",
52
+ origin: window.location.origin,
53
+ });
54
+ ```
55
+
56
+ ## Connect wallet
57
+ ```tsx
58
+ <button onClick={() => (walletConnect.isConnected ? walletConnect.disconnect() : walletConnect.openModal())}>
59
+ {walletConnect.isConnected ? "Disconnect" : "Connect Wallet"}
60
+ </button>
61
+ ```
62
+
63
+ ## Configure SignInModal
64
+
65
+ The SDK ships with **bundled auth config** (Google client id, Twitter client id, CubeSigner env/org) so you can call `initSDK` with only `signIn` (and optionally `twitterRedirectUri` for your origin). Override via env (`NEXT_PUBLIC_*`) or by passing options to `initSDK`.
66
+
67
+ ```tsx
68
+ import { initSDK, SignInModal } from "@ab-org/predicate-market-sdk";
69
+
70
+ initSDK({
71
+ // optional: override redirect for Twitter (origin-dependent)
72
+ twitterRedirectUri: typeof window !== "undefined" ? `${window.location.origin}/auth/twitter-callback` : undefined,
73
+ signIn: {
74
+ socialProviders: [
75
+ { id: "google", label: "Continue with Google" },
76
+ { id: "x", label: "Continue with X" },
77
+ ],
78
+ wallets: [
79
+ { id: "metamask", name: "MetaMask" },
80
+ { id: "okx", name: "OKX Wallet" },
81
+ { id: "phantom", name: "Phantom" },
82
+ ],
83
+ initialVisibleCount: 4,
84
+ },
85
+ });
86
+
87
+ <SignInModal />;
88
+ ```
89
+
90
+ Rules:
91
+ - `socialProviders: undefined` uses built-in defaults (`google`, `x`)
92
+ - `socialProviders: []` hides all social buttons
93
+ - known social ids like `google` and `x` automatically reuse built-in icons unless you override `icon`
94
+ - `wallets: undefined` uses the built-in wallet registry
95
+ - `wallets: []` hides all wallet buttons
96
+ - known wallet ids automatically reuse built-in metadata like `installUrl` and detected `installed` state unless you override them
97
+ - component props still override `initSDK({ signIn })` on a per-modal basis
98
+
99
+ ## Address & balance
100
+ ```tsx
101
+ if (!account.isConnected) return <p>Please connect wallet</p>;
102
+ return (
103
+ <div>
104
+ Address: {account.address?.slice(0, 6)}...{account.address?.slice(-4)}
105
+ <br />
106
+ Balance: {account.balance?.formatted} {account.balance?.symbol}
107
+ </div>
108
+ );
109
+ ```
110
+
111
+ ## Market data — token / chain lists & quotes
112
+
113
+ Both `deposit` and `withdraw` controllers expose market-data helpers
114
+ that delegate to the injected `MarketDataProvider`:
115
+
116
+ ```ts
117
+ // 1. Fetch available tokens
118
+ const tokens = await deposit.fetchTokens();
119
+ // → [{ symbol: "USDT", name: "Tether", decimals: 6, … }, …]
120
+
121
+ // 2. After the user picks a token, fetch the chains it supports
122
+ const chains = await deposit.fetchChains("USDT");
123
+ // → [{ id: "ETH", name: "Ethereum", estimatedTime: "~15 min" }, …]
124
+
125
+ // 3. After user picks token + chain, fetch the deposit address (backend-provided)
126
+ const { address, minimumDeposit } = await deposit.fetchDepositAddress("USDT", "ETH");
127
+ // QR code is generated client-side from the address — no backend QR needed
128
+
129
+ // 4. After user enters amount, fetch a quote
130
+ const quote = await deposit.fetchQuote("USDT", "ETH", "100");
131
+ // → { quoteId, estimatedAmount, slippage, fee, feeToken, exchangeRate, expiresAt }
132
+ ```
133
+
134
+ The same token / chain / quote API is available on the withdraw controller:
135
+
136
+ ```ts
137
+ const tokens = await withdraw.fetchTokens();
138
+ const chains = await withdraw.fetchChains("USDT");
139
+ const quote = await withdraw.fetchQuote("USDT", "ETH", "200");
140
+ ```
141
+
142
+ ### Implementing a real `MarketDataProvider`
143
+
144
+ Replace `createMockMarketDataProvider()` with your own implementation:
145
+
146
+ ```ts
147
+ import type { MarketDataProvider } from "@ab-org/predicate-market-sdk";
148
+
149
+ const realMarketData: MarketDataProvider = {
150
+ async getSupportedTokens(direction) {
151
+ const res = await fetch(`/api/market/tokens?direction=${direction}`);
152
+ return res.json();
153
+ },
154
+ async getSupportedChains(token, direction) {
155
+ const res = await fetch(`/api/market/chains?token=${token}&direction=${direction}`);
156
+ return res.json();
157
+ },
158
+ async getQuote(request) {
159
+ const res = await fetch("/api/market/quote", {
160
+ method: "POST",
161
+ body: JSON.stringify(request),
162
+ });
163
+ return res.json();
164
+ },
165
+ async getDepositAddress(token, chain) {
166
+ const res = await fetch(`/api/market/deposit-address?token=${token}&chain=${chain}`);
167
+ return res.json();
168
+ },
169
+ };
170
+ ```
171
+
172
+ ## Deposit modal
173
+ ```tsx
174
+ <button
175
+ onClick={() =>
176
+ deposit.open({
177
+ preferredToken: "USDC",
178
+ preferredChain: "ETH",
179
+ onStatusChange: (status) => console.log("deposit status", status),
180
+ })
181
+ }
182
+ >
183
+ Deposit
184
+ </button>
185
+ ```
186
+
187
+ ## Smart-wallet session model
188
+ Social login and injected wallets now converge on the same `WalletSession` shape.
189
+
190
+ ```ts
191
+ const account = createAccountController();
192
+
193
+ console.log(account.session?.walletType); // "injected" | "smart"
194
+ console.log(account.chainContext?.walletChainDescriptor.label);
195
+ console.log(account.capabilities); // provider capability matrix
196
+ console.log(account.capabilityPolicy); // optional scoped session policy
197
+ ```
198
+
199
+ ## Trading helpers
200
+ ```ts
201
+ import { encodeFunctionData, erc20Abi } from "viem";
202
+
203
+ async function approveSpender(spender: string, value: bigint) {
204
+ if (!account.address) return;
205
+
206
+ const callData = encodeFunctionData({
207
+ abi: erc20Abi,
208
+ functionName: "approve",
209
+ args: [spender, value],
210
+ });
211
+
212
+ await execution.sendTransaction({
213
+ from: account.address,
214
+ to: tokenAddress,
215
+ data: callData,
216
+ });
217
+ }
218
+ ```
219
+
220
+ Use `execution.signTransaction()`, `execution.signMessage()`, and `execution.signTypedData()` instead of building raw EIP-1193 requests in app code.
221
+
222
+ ## Capability policies
223
+ ```ts
224
+ const depositPolicy = policy.deposit("USDC", "ETH", "1000000");
225
+ const withdrawPolicy = policy.withdraw("USD1", "AB_CORE", "500000");
226
+ const tradePolicy = policy.trade("ETH", ["eth_sendTransaction", "eth_signTypedData_v4"]);
227
+ ```
228
+
229
+ These policies can be passed into your smart-wallet authorization flow so app actions stay scoped by chain, method, token, and amount.
230
+
231
+ ## Withdraw modal
232
+ ```tsx
233
+ <button
234
+ onClick={() =>
235
+ withdraw.open({
236
+ defaultToken: "USD1",
237
+ defaultChain: "AB_CORE",
238
+ targetAddress: "0x...",
239
+ defaultAmount: "100",
240
+ onStatusChange: (status) => console.log("withdraw", status),
241
+ })
242
+ }
243
+ >
244
+ Withdraw
245
+ </button>
246
+ ```
@@ -0,0 +1,11 @@
1
+ import { type WalletSession } from "@ab-org/sdk-core";
2
+ /**
3
+ * Attempt to silently reconnect a wallet whose session was restored
4
+ * from localStorage cache. Creates a temporary `WalletConnector` with
5
+ * the default adapter registry (injected wallets + Cubist if configured)
6
+ * and delegates to `WalletConnector.tryAutoReconnect()`.
7
+ *
8
+ * Safe to call multiple times — concurrent calls share the same promise.
9
+ * Returns the fresh session on success, or `null` if reconnection failed.
10
+ */
11
+ export declare function tryAutoReconnect(): Promise<WalletSession | null>;
@@ -0,0 +1,36 @@
1
+ import { sessionStore, WalletConnector, createDefaultInjectedWalletRegistry, CubistSocialProvider, } from "@ab-org/sdk-core";
2
+ import { getSDKConfig } from "./config.js";
3
+ let pending = null;
4
+ /**
5
+ * Attempt to silently reconnect a wallet whose session was restored
6
+ * from localStorage cache. Creates a temporary `WalletConnector` with
7
+ * the default adapter registry (injected wallets + Cubist if configured)
8
+ * and delegates to `WalletConnector.tryAutoReconnect()`.
9
+ *
10
+ * Safe to call multiple times — concurrent calls share the same promise.
11
+ * Returns the fresh session on success, or `null` if reconnection failed.
12
+ */
13
+ export function tryAutoReconnect() {
14
+ const session = sessionStore.getState().session;
15
+ if (!session)
16
+ return Promise.resolve(null);
17
+ if (pending)
18
+ return pending;
19
+ pending = doAutoReconnect().finally(() => {
20
+ pending = null;
21
+ });
22
+ return pending;
23
+ }
24
+ async function doAutoReconnect() {
25
+ const config = getSDKConfig();
26
+ const registry = createDefaultInjectedWalletRegistry();
27
+ const adapters = [...registry.map((r) => r.provider)];
28
+ if (config.cubeSigner) {
29
+ adapters.push(new CubistSocialProvider({
30
+ ...config.cubeSigner,
31
+ defaultSessionPolicy: config.signIn?.sessionPolicy ?? config.cubeSigner.defaultSessionPolicy,
32
+ }));
33
+ }
34
+ const connector = new WalletConnector(adapters);
35
+ return connector.tryAutoReconnect();
36
+ }
@@ -0,0 +1,2 @@
1
+ import type { SDKConfig } from "./config.js";
2
+ export declare const BUNDLED_AUTH_CONFIG: Partial<SDKConfig>;
@@ -0,0 +1,19 @@
1
+ import { getEnv } from "../utils/env.js";
2
+ function readOptionalEnv(key) {
3
+ const value = getEnv(key);
4
+ return value === "" ? undefined : value;
5
+ }
6
+ const relayOrigin = readOptionalEnv("RELAY_ORIGIN");
7
+ const cubeEnv = readOptionalEnv("CUBE_SIGNER_ENV");
8
+ const cubeOrgId = readOptionalEnv("CUBE_SIGNER_ORG_ID");
9
+ export const BUNDLED_AUTH_CONFIG = {
10
+ googleClientId: readOptionalEnv("GOOGLE_CLIENT_ID"),
11
+ twitterClientId: readOptionalEnv("X_CLIENT_ID"),
12
+ twitterRedirectUri: relayOrigin ? `${relayOrigin}/auth/twitter-callback` : undefined,
13
+ cubeSigner: cubeEnv && cubeOrgId
14
+ ? {
15
+ env: cubeEnv,
16
+ orgId: cubeOrgId,
17
+ }
18
+ : undefined,
19
+ };
@@ -0,0 +1,29 @@
1
+ import type { CubeSignerConfig } from "@ab-org/sdk-core";
2
+ import type { SignInUiConfig } from "../ui/signInTypes.js";
3
+ export interface SDKConfig {
4
+ googleClientId?: string;
5
+ twitterClientId?: string;
6
+ twitterRedirectUri?: string;
7
+ cubeSigner?: CubeSignerConfig;
8
+ signIn?: SignInUiConfig;
9
+ }
10
+ /**
11
+ * Fixed auth config: built-in bundled defaults (see bundledConfig.ts) with optional
12
+ * overrides from environment (server / `NEXT_PUBLIC_*` client). Used as defaults in initSDK.
13
+ */
14
+ export declare function getFixedAuthConfig(): Partial<SDKConfig>;
15
+ /**
16
+ * Config for initializing the predicate SDK. Defaults come from bundled config
17
+ * (and env overrides); you may override any of them here. Optional top-level
18
+ * `registerUser` is merged into `cubeSigner.oidcLoginHooks` when cubeSigner is present.
19
+ */
20
+ export interface PredicateSDKConfig extends Partial<SDKConfig> {
21
+ /**
22
+ * Optional hook to register the user on your backend when CubeSigner proof
23
+ * indicates the user is not initialized. Used when cubeSigner is available
24
+ * (from env or override).
25
+ */
26
+ registerUser?: (oidcToken: string) => Promise<void>;
27
+ }
28
+ export declare function initSDK(config?: PredicateSDKConfig): void;
29
+ export declare function getSDKConfig(): Readonly<SDKConfig>;
@@ -0,0 +1,53 @@
1
+ import { getEnv } from "../utils/env.js";
2
+ import { BUNDLED_AUTH_CONFIG } from "./bundledConfig.js";
3
+ import { tryAutoReconnect } from "./autoReconnect.js";
4
+ let sdkConfig = {};
5
+ function readOptionalEnv(key) {
6
+ const value = getEnv(key);
7
+ return value === "" ? undefined : value;
8
+ }
9
+ /**
10
+ * Fixed auth config: built-in bundled defaults (see bundledConfig.ts) with optional
11
+ * overrides from environment (server / `NEXT_PUBLIC_*` client). Used as defaults in initSDK.
12
+ */
13
+ export function getFixedAuthConfig() {
14
+ const envGoogle = readOptionalEnv("GOOGLE_CLIENT_ID");
15
+ const envTwitter = readOptionalEnv("X_CLIENT_ID");
16
+ const relayOrigin = readOptionalEnv("RELAY_ORIGIN");
17
+ const envRedirect = relayOrigin ? `${relayOrigin}/auth/twitter-callback` : undefined;
18
+ const cubeEnv = readOptionalEnv("CUBE_SIGNER_ENV");
19
+ const cubeOrgId = readOptionalEnv("CUBE_SIGNER_ORG_ID");
20
+ const cubeSignerFromEnv = cubeEnv && cubeOrgId
21
+ ? { env: cubeEnv, orgId: cubeOrgId }
22
+ : undefined;
23
+ return {
24
+ googleClientId: envGoogle || BUNDLED_AUTH_CONFIG.googleClientId,
25
+ twitterClientId: envTwitter || BUNDLED_AUTH_CONFIG.twitterClientId,
26
+ twitterRedirectUri: envRedirect || BUNDLED_AUTH_CONFIG.twitterRedirectUri,
27
+ cubeSigner: cubeSignerFromEnv ?? BUNDLED_AUTH_CONFIG.cubeSigner,
28
+ };
29
+ }
30
+ export function initSDK(config = {}) {
31
+ const { registerUser, ...rest } = config;
32
+ const fixed = getFixedAuthConfig();
33
+ const merged = {
34
+ ...sdkConfig,
35
+ ...fixed,
36
+ ...rest,
37
+ cubeSigner: rest.cubeSigner ?? fixed.cubeSigner,
38
+ };
39
+ if (registerUser != null && merged.cubeSigner) {
40
+ merged.cubeSigner = {
41
+ ...merged.cubeSigner,
42
+ oidcLoginHooks: {
43
+ ...merged.cubeSigner.oidcLoginHooks,
44
+ registerUser,
45
+ },
46
+ };
47
+ }
48
+ sdkConfig = merged;
49
+ tryAutoReconnect().catch(() => { });
50
+ }
51
+ export function getSDKConfig() {
52
+ return sdkConfig;
53
+ }
@@ -0,0 +1,43 @@
1
+ declare global {
2
+ interface Window {
3
+ google?: {
4
+ accounts: {
5
+ id: {
6
+ initialize(config: GISInitConfig): void;
7
+ prompt(momentListener?: (notification: GISPromptMoment) => void): void;
8
+ renderButton(parent: HTMLElement, options: GISButtonConfig): void;
9
+ revoke(hint: string, callback?: () => void): void;
10
+ };
11
+ };
12
+ };
13
+ }
14
+ }
15
+ interface GISInitConfig {
16
+ client_id: string;
17
+ callback: (response: GISCredentialResponse) => void;
18
+ auto_select?: boolean;
19
+ cancel_on_tap_outside?: boolean;
20
+ use_fedcm_for_prompt?: boolean;
21
+ }
22
+ interface GISCredentialResponse {
23
+ credential: string;
24
+ }
25
+ interface GISPromptMoment {
26
+ isSkippedMoment(): boolean;
27
+ isDismissedMoment(): boolean;
28
+ isNotDisplayed(): boolean;
29
+ getDismissedReason(): string;
30
+ }
31
+ interface GISButtonConfig {
32
+ type?: "standard" | "icon";
33
+ size?: "large" | "medium" | "small";
34
+ }
35
+ export interface GoogleCredential {
36
+ idToken: string;
37
+ email?: string;
38
+ name?: string;
39
+ picture?: string;
40
+ }
41
+ export declare function isFedCMSupported(): boolean;
42
+ export declare function signInWithGoogle(clientId: string): Promise<GoogleCredential>;
43
+ export {};
@@ -0,0 +1,147 @@
1
+ const GIS_URL = "https://accounts.google.com/gsi/client";
2
+ let loadPromise = null;
3
+ let currentCredentialHandler = null;
4
+ let initializedKey = null;
5
+ function loadGIS() {
6
+ if (loadPromise)
7
+ return loadPromise;
8
+ if (window.google?.accounts?.id) {
9
+ loadPromise = Promise.resolve();
10
+ return loadPromise;
11
+ }
12
+ loadPromise = new Promise((resolve, reject) => {
13
+ const script = document.createElement("script");
14
+ script.src = GIS_URL;
15
+ script.async = true;
16
+ script.defer = true;
17
+ script.onload = () => resolve();
18
+ script.onerror = () => reject(new Error("Failed to load Google Identity Services"));
19
+ document.head.appendChild(script);
20
+ });
21
+ return loadPromise;
22
+ }
23
+ function decodeJwtPayload(token) {
24
+ const parts = token.split(".");
25
+ if (parts.length !== 3)
26
+ throw new Error("Invalid JWT");
27
+ const payload = parts[1];
28
+ if (!payload)
29
+ throw new Error("Invalid JWT payload");
30
+ let padded = payload.replace(/-/g, "+").replace(/_/g, "/");
31
+ while (padded.length % 4 !== 0)
32
+ padded += "=";
33
+ return JSON.parse(atob(padded));
34
+ }
35
+ export function isFedCMSupported() {
36
+ if (typeof window === "undefined" || typeof navigator === "undefined")
37
+ return false;
38
+ if (!window.isSecureContext)
39
+ return false;
40
+ return typeof navigator.credentials?.get === "function";
41
+ }
42
+ export async function signInWithGoogle(clientId) {
43
+ await loadGIS();
44
+ const gid = window.google?.accounts?.id;
45
+ if (!gid)
46
+ throw new Error("Google Identity Services not available");
47
+ const SIGN_IN_TIMEOUT_MS = 90000;
48
+ return new Promise((resolve, reject) => {
49
+ let settled = false;
50
+ let fallbackContainer = null;
51
+ const cleanupFallback = () => {
52
+ if (!fallbackContainer)
53
+ return;
54
+ fallbackContainer.remove();
55
+ fallbackContainer = null;
56
+ };
57
+ const finish = (fn) => {
58
+ if (settled)
59
+ return;
60
+ settled = true;
61
+ clearTimeout(timeoutId);
62
+ cleanupFallback();
63
+ fn();
64
+ };
65
+ const timeoutId = setTimeout(() => {
66
+ finish(() => reject(new Error("Google sign-in timed out")));
67
+ }, SIGN_IN_TIMEOUT_MS);
68
+ const handleCredential = (response) => {
69
+ finish(() => {
70
+ try {
71
+ const payload = decodeJwtPayload(response.credential);
72
+ resolve({
73
+ idToken: response.credential,
74
+ email: payload.email,
75
+ name: payload.name,
76
+ picture: payload.picture,
77
+ });
78
+ }
79
+ catch {
80
+ resolve({ idToken: response.credential });
81
+ }
82
+ });
83
+ };
84
+ const useFedcmForPrompt = isFedCMSupported();
85
+ const initKey = `${clientId}\0${useFedcmForPrompt ? "1" : "0"}`;
86
+ currentCredentialHandler = handleCredential;
87
+ if (initializedKey !== initKey) {
88
+ initializedKey = initKey;
89
+ gid.initialize({
90
+ client_id: clientId,
91
+ callback: (response) => currentCredentialHandler?.(response),
92
+ auto_select: false,
93
+ cancel_on_tap_outside: true,
94
+ use_fedcm_for_prompt: useFedcmForPrompt,
95
+ });
96
+ }
97
+ const triggerFallback = () => {
98
+ if (settled)
99
+ return;
100
+ fallbackContainer = document.createElement("div");
101
+ Object.assign(fallbackContainer.style, {
102
+ position: "fixed",
103
+ top: "-9999px",
104
+ left: "-9999px",
105
+ opacity: "0",
106
+ pointerEvents: "none",
107
+ });
108
+ document.body.appendChild(fallbackContainer);
109
+ gid.renderButton(fallbackContainer, { type: "standard", size: "large" });
110
+ const tryClickButton = (attemptsLeft) => {
111
+ requestAnimationFrame(() => {
112
+ const btn = fallbackContainer?.querySelector('[role="button"]') ??
113
+ fallbackContainer?.querySelector("div[style]");
114
+ if (btn) {
115
+ Object.assign(fallbackContainer.style, { pointerEvents: "auto" });
116
+ btn.click();
117
+ return;
118
+ }
119
+ if (attemptsLeft > 0) {
120
+ tryClickButton(attemptsLeft - 1);
121
+ return;
122
+ }
123
+ finish(() => reject(new Error("Google sign-in unavailable")));
124
+ });
125
+ };
126
+ tryClickButton(8);
127
+ };
128
+ gid.prompt((moment) => {
129
+ if (settled)
130
+ return;
131
+ if (moment.isDismissedMoment()) {
132
+ const reason = moment.getDismissedReason();
133
+ if (reason === "credential_returned")
134
+ return;
135
+ finish(() => reject(new Error(`Google sign-in dismissed: ${reason}`)));
136
+ return;
137
+ }
138
+ if (moment.isSkippedMoment()) {
139
+ triggerFallback();
140
+ return;
141
+ }
142
+ if (moment.isNotDisplayed()) {
143
+ triggerFallback();
144
+ }
145
+ });
146
+ });
147
+ }
@@ -0,0 +1,7 @@
1
+ export interface TwitterAuthResult {
2
+ code: string;
3
+ codeVerifier: string;
4
+ state: string;
5
+ }
6
+ export declare function notifyTwitterCallback(): void;
7
+ export declare function signInWithTwitter(clientId: string, redirectUri: string): Promise<TwitterAuthResult>;