@tokenflight/swap 0.2.0 → 0.3.0-rc.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.
@@ -0,0 +1 @@
1
+ var e=null;function t(t){e=t}function n(){return e}export{t as n,n as t};
@@ -0,0 +1 @@
1
+ var e=1,t=0;function n(e,n){return{type:`tf:${e}`,protocolVersion:1,seq:++t,payload:n}}function r(e){if(typeof e!=`object`||!e)return null;let t=e;return typeof t.type!=`string`||!t.type.startsWith(`tf:`)||t.protocolVersion!==1||typeof t.seq!=`number`||!(`payload`in t)?null:e}function i(e,t){return e.type===t}var a=new Set;function o(){return crypto.randomUUID()}function s(e){if(a.has(e))return!1;if(a.add(e),a.size>1e4){let e=[...a];for(let t=0;t<5e3;t++)a.delete(e[t])}return!0}var c=[[/user (rejected|denied|cancelled|refused)/i,`USER_REJECTED`],[/chain.*switch|switch.*chain|network.*change/i,`CHAIN_SWITCH_FAILED`],[/insufficient.*funds|not enough/i,`INSUFFICIENT_FUNDS`],[/timed?\s*out|timeout/i,`TIMEOUT`]];function l(e){for(let[t,n]of c)if(t.test(e))return n;return`UNKNOWN`}function u(e){if(e instanceof Error){let t=e,n=t.code;return{code:typeof n==`string`?n:l(e.message),message:e.message,details:t.details}}if(typeof e==`string`)return{code:l(e),message:e};if(typeof e==`object`&&e){let t=e;return{code:typeof t.code==`string`?t.code:`UNKNOWN`,message:typeof t.message==`string`?t.message:String(e),details:t.details}}return{code:`UNKNOWN`,message:String(e)}}function d(e){let t=Error(e.message);return t.code=e.code,e.details!==void 0&&(t.details=e.details),t}var f,p=`https://embed.tokenflight.ai/widget`,m=15e3,h=500,g=class e{constructor(e){if(this.listeners=new Map,this.ready=!1,this.destroyed=!1,this.readyTimer=null,this.popup=null,this.popupPollTimer=null,this.popupOrderId=null,this.popupMessageHandler=null,this.handleMessage=e=>{if(this.destroyed||e.origin!==this.widgetOrigin||e.source!==this.iframe.contentWindow)return;let t=r(e.data);t&&(i(t,`tf:ready`)?this.handleReady(t.payload.version,t.payload.protocolVersion):i(t,`tf:signRequest`)?this.handleSignRequest(t.payload.requestId,t.payload.action,t.payload.timeoutMs):i(t,`tf:connectWallet`)?this.handleConnectWallet(t.payload.chainType):i(t,`tf:disconnectWallet`)?this.handleDisconnectWallet():i(t,`tf:event`)?this.handleWidgetEvent(t.payload.name,t.payload.data):i(t,`tf:resize`)?this.handleResize(t.payload.height):i(t,`tf:preOpenPopup`)?this.handlePreOpenPopup():i(t,`tf:navigatePopup`)&&this.navigatePopup(t.payload.url,t.payload.orderId))},this.options=e,this.wallet=e.wallet,typeof e.container==`string`){let t=document.querySelector(e.container);if(!t)throw Error(`TokenFlightBridge: container "${e.container}" not found`);this.container=t}else this.container=e.container;let t=e.iframeSrc??p,n=typeof window<`u`?window.location.href:void 0;this.widgetOrigin=new URL(t,n).origin,this.iframe=document.createElement(`iframe`),this.iframe.src=t,this.iframe.style.display=`block`,this.iframe.style.width=`100%`,this.iframe.style.height=`600px`,this.iframe.style.minWidth=`375px`,this.iframe.style.colorScheme=`normal`,this.iframe.style.borderRadius=`var(--tf-radius-xl, 20px)`,this.iframe.style.overflow=`hidden`,e.config.noBackground||(this.iframe.style.background=`var(--tf-bg, #0d0f14)`),e.config.noBorder?this.iframe.style.border=`none`:(this.iframe.style.border=`1px solid var(--tf-border, #252a38)`,this.iframe.style.boxShadow=`var(--tf-shadow-lg, none)`),this.iframe.setAttribute(`allow`,`camera;microphone;payment;clipboard-write`),this.iframe.setAttribute(`title`,`TokenFlight Widget`),this.container.appendChild(this.iframe),window.addEventListener(`message`,this.handleMessage),this.walletHandler=e=>{this.sendWalletState(e.type)},this.subscribeWalletEvents();let a=e.readyTimeout??m;this.readyTimer=setTimeout(()=>{!this.ready&&!this.destroyed&&e.onLoadError?.(Error(`TokenFlightBridge: widget did not send tf:ready within timeout`))},a)}setConfig(e){this.postToIframe(n(`setConfig`,e)),e.noBackground!==void 0&&(this.iframe.style.background=e.noBackground?``:`var(--tf-bg, #0d0f14)`),e.noBorder!==void 0&&(this.iframe.style.border=e.noBorder?`none`:`1px solid var(--tf-border, #252a38)`,this.iframe.style.boxShadow=e.noBorder?`none`:`var(--tf-shadow-lg, none)`)}setStyle(e){this.postToIframe(n(`setStyle`,e))}setWallet(e){this.unsubscribeWalletEvents(),this.wallet=e,this.subscribeWalletEvents(),this.sendWalletState()}on(e,t){let n=this.listeners.get(e);n||(n=new Set,this.listeners.set(e,n)),n.add(t)}off(e,t){this.listeners.get(e)?.delete(t)}destroy(){this.destroyed||(this.destroyed=!0,this.readyTimer&&(clearTimeout(this.readyTimer),this.readyTimer=null),window.removeEventListener(`message`,this.handleMessage),this.unsubscribeWalletEvents(),this.closePopup(),this.iframe.remove(),this.listeners.clear())}async handleReady(e,t){if(t!==1){this.options.onLoadError?.(Error(`TokenFlightBridge: protocol version mismatch (bridge=1, widget=${t})`));return}this.ready=!0,this.readyTimer&&(clearTimeout(this.readyTimer),this.readyTimer=null);let r=this.wallet?.isConnected()??!1,i=null;if(r&&this.wallet)try{i=await this.wallet.getAddress()}catch{}this.postToIframe(n(`init`,{config:this.options.config,style:this.options.style,walletAddress:i,connected:r})),this.options.onReady?.(e),this.emit(`ready`,{version:e})}async handleSignRequest(e,t,r){if(!this.wallet){this.postToIframe(n(`signResult`,{requestId:e,error:{code:`ADAPTER_ERROR`,message:`No wallet adapter configured`}}));return}try{let r=await this.wallet.executeWalletAction(t);this.postToIframe(n(`signResult`,{requestId:e,result:r}))}catch(t){this.postToIframe(n(`signResult`,{requestId:e,error:u(t)}))}}async handleConnectWallet(e){if(this.wallet)try{await this.wallet.connect(e)}catch{}}async handleDisconnectWallet(){if(this.wallet)try{await this.wallet.disconnect()}catch{}}handleWidgetEvent(e,t){let n=_[e];n&&this.emit(n,t)}handleResize(e){this.iframe.style.height=`${e}px`,this.emit(`resize`,{height:e})}handlePreOpenPopup(){this.closePopup(),this.popup=window.open(`about:blank`,`tf-payment`,`width=500,height=700`)}navigatePopup(e,t){if(!this.popup||this.popup.closed){this.postToIframe(n(`fiatPopupClosed`,{orderId:t}));return}try{let r=new URL(e);if(r.protocol!==`https:`&&r.protocol!==`http:`){this.postToIframe(n(`fiatPopupClosed`,{orderId:t}));return}}catch{this.postToIframe(n(`fiatPopupClosed`,{orderId:t}));return}this.popup.location.href=e,this.popupOrderId=t,this.installPopupMessageHandler(),this.popupPollTimer=setInterval(()=>{this.popup?.closed&&(this.closePopup(),this.postToIframe(n(`fiatPopupClosed`,{orderId:this.popupOrderId??t})),this.popupOrderId=null)},h)}installPopupMessageHandler(){if(this.popupMessageHandler)return;let e,t=this.options.config.fiatApiEndpoint;if(!t)return;try{e=new URL(t).origin}catch{return}let r=new Set([`widget_ready`,`order_completed`,`order_failed`,`widget_close`]);this.popupMessageHandler=t=>{if(this.destroyed||t.origin!==e)return;let i=t.data;if(!i||i.type!==`FIAT_WIDGET_EVENT`||typeof i.event!=`string`||!r.has(i.event))return;let a=this.popupOrderId;a&&(typeof i.orderId==`string`&&i.orderId!==a||this.postToIframe(n(`fiatProviderEvent`,{orderId:a,event:i.event,provider:typeof i.provider==`string`?i.provider:void 0,data:i.data??void 0})))},window.addEventListener(`message`,this.popupMessageHandler)}closePopup(){this.popupPollTimer&&(clearInterval(this.popupPollTimer),this.popupPollTimer=null),this.popupMessageHandler&&(window.removeEventListener(`message`,this.popupMessageHandler),this.popupMessageHandler=null),this.popup&&!this.popup.closed&&this.popup.close(),this.popup=null}subscribeWalletEvents(){if(this.wallet)for(let t of e.WALLET_EVENTS)this.wallet.on(t,this.walletHandler)}unsubscribeWalletEvents(){if(this.wallet)for(let t of e.WALLET_EVENTS)this.wallet.off(t,this.walletHandler)}async sendWalletState(e){if(!this.wallet)return;let t=this.wallet.isConnected(),r=null;if(t)try{r=await this.wallet.getAddress()}catch{}this.postToIframe(n(`walletState`,{connected:t,address:r,event:e}))}postToIframe(e){this.destroyed||this.iframe.contentWindow?.postMessage(e,this.widgetOrigin)}emit(e,t){let n=this.listeners.get(e);if(n)for(let e of n)try{e(t)}catch{}}};f=g,f.WALLET_EVENTS=[`connect`,`disconnect`,`chainChanged`,`accountsChanged`];var _={swapComplete:`swapComplete`,error:`error`,fiatOrderCreated:`fiatOrderCreated`,fiatOrderCompleted:`fiatOrderCompleted`};function v(e){return new g(e)}export{o as a,r as c,n as i,u as l,v as n,d as o,s as r,i as s,g as t,e as u};
@@ -0,0 +1,597 @@
1
+ export declare interface BridgeEventMap {
2
+ ready: {
3
+ version: string;
4
+ };
5
+ swapComplete: {
6
+ orderId: string;
7
+ txHash?: string;
8
+ };
9
+ error: {
10
+ code: string;
11
+ message: string;
12
+ details?: unknown;
13
+ };
14
+ fiatOrderCreated: {
15
+ orderId: string;
16
+ widgetUrl: string;
17
+ };
18
+ fiatOrderCompleted: {
19
+ orderId: string;
20
+ status: string;
21
+ txHash?: string;
22
+ };
23
+ resize: {
24
+ height: number;
25
+ };
26
+ }
27
+
28
+ export declare type BridgeEventName = keyof BridgeEventMap;
29
+
30
+ export declare interface BridgeOptions {
31
+ /** Mount point — CSS selector or HTMLElement. */
32
+ container: string | HTMLElement;
33
+ /** Wallet adapter running on the host page. */
34
+ wallet?: IWalletAdapter;
35
+ /** Widget configuration. */
36
+ config: WidgetConfig;
37
+ /** Style overrides (CSS variables, custom CSS, remote stylesheet). */
38
+ style?: StyleOverrides;
39
+ /** Iframe source URL. Defaults to latest hosted widget. */
40
+ iframeSrc?: string;
41
+ /** Called when the widget iframe fails to load. */
42
+ onLoadError?: (error: Error) => void;
43
+ /** Called when the widget iframe is ready. */
44
+ onReady?: (version: string) => void;
45
+ /** Timeout in ms for waiting for tf:ready (default: 15000). */
46
+ readyTimeout?: number;
47
+ }
48
+
49
+ /** Chain type for multi-chain support */
50
+ declare type ChainType = "evm" | "solana";
51
+
52
+ /**
53
+ * Consume a request ID (one-time use). Returns `true` if the ID was valid
54
+ * and not yet consumed, `false` if already consumed or unknown.
55
+ */
56
+ export declare function consumeRequestId(id: string): boolean;
57
+
58
+ /**
59
+ * Create a typed bridge message envelope.
60
+ *
61
+ * ```ts
62
+ * const msg = createMessage("init", { config, walletAddress });
63
+ * // → { type: "tf:init", protocolVersion: 1, seq: 1, payload: { ... } }
64
+ * ```
65
+ */
66
+ export declare function createMessage<T extends string, P>(type: T, payload: P): TfMessageEnvelope<T, P>;
67
+
68
+ /** Generate a unique request ID using crypto.randomUUID(). */
69
+ export declare function createRequestId(): string;
70
+
71
+ /**
72
+ * Create a new TokenFlight bridge instance.
73
+ *
74
+ * ```ts
75
+ * const bridge = createTokenFlightBridge({
76
+ * container: '#widget',
77
+ * wallet: adapter,
78
+ * config: { theme: 'dark', fromToken: '...' },
79
+ * });
80
+ * ```
81
+ */
82
+ export declare function createTokenFlightBridge(options: BridgeOptions): TokenFlightBridge;
83
+
84
+ /**
85
+ * Custom color overrides — keys are CSS variable names.
86
+ * Typed keys provide autocomplete; arbitrary `--tf-*` strings are also accepted.
87
+ *
88
+ * ```ts
89
+ * customColors: {
90
+ * "--tf-primary": "#FF6B00",
91
+ * "--tf-bg": "#1A1A2E",
92
+ * "--tf-button-radius": "4px",
93
+ * "--tf-widget-max-width": "480px",
94
+ * }
95
+ * ```
96
+ */
97
+ declare type CustomColors = Partial<Record<TfCssVar, string>> & Record<string, string>;
98
+
99
+ /** Reconstruct an Error from a SerializedError. */
100
+ export declare function deserializeError(err: SerializedError): Error & {
101
+ code: string;
102
+ };
103
+
104
+ /** EVM wallet action via EIP-1193 */
105
+ declare interface EvmWalletAction {
106
+ type: "eip1193_request";
107
+ chainId: number;
108
+ method: string;
109
+ params: unknown[];
110
+ }
111
+
112
+ /**
113
+ * Iframe-embed bridge integration for the fiat popup. When the widget is
114
+ * embedded as an iframe via `@tokenflight/swap/bridge`, the host page owns the
115
+ * payment popup (to avoid 3-level iframe nesting through the wrapper page).
116
+ * The widget delegates `window.open` to the host and consumes popup-closed
117
+ * notifications + forwarded provider events via callback registries.
118
+ */
119
+ declare interface FiatIframeBridge {
120
+ /** Synchronously ask the host to pre-open a blank popup in the user-gesture chain. */
121
+ requestPreOpenPopup: () => void;
122
+ /** Ask the host to navigate the pre-opened popup to the wrapper URL. */
123
+ requestNavigatePopup: (url: string, orderId: string) => void;
124
+ /**
125
+ * Subscribe to popup-closed notifications forwarded from the host bridge.
126
+ * Returns an unsubscribe function. The handler receives the orderId the host
127
+ * was tracking; checkout state filters out stale closures by comparing it
128
+ * against the current `orderId()` signal.
129
+ */
130
+ onPopupClosed: (handler: (closedOrderId: string) => void) => () => void;
131
+ /**
132
+ * Subscribe to provider terminal events forwarded from the host bridge.
133
+ * Returns an unsubscribe function. The host installs its own
134
+ * `FIAT_WIDGET_EVENT` listener (origin-validated against `fiatApiEndpoint`)
135
+ * and forwards matched events here so the widget's `paymentOutcome` signal
136
+ * can be driven exactly the same way as in non-iframe mode.
137
+ */
138
+ onProviderEvent: (handler: (event: ForwardedFiatProviderEvent) => void) => () => void;
139
+ }
140
+
141
+ /** Payload of a forwarded provider event (extracted from the message envelope). */
142
+ declare type FiatProviderEventPayload = TfFiatProviderEventMessage["payload"];
143
+
144
+ /**
145
+ * Forwarded provider terminal event payload — mirrors the postMessage event
146
+ * the wrapper page would dispatch directly in non-iframe mode. The host bridge
147
+ * unpacks `FIAT_WIDGET_EVENT` postMessages on its own window and re-broadcasts
148
+ * them as this shape via `tf:fiatProviderEvent`.
149
+ */
150
+ declare interface ForwardedFiatProviderEvent {
151
+ orderId: string;
152
+ event: "widget_ready" | "order_completed" | "order_failed" | "widget_close";
153
+ provider?: string;
154
+ data?: Record<string, unknown>;
155
+ }
156
+
157
+ /**
158
+ * Get the host page origin from the referrer or document.referrer.
159
+ * Returns null if not in iframe mode or origin cannot be determined.
160
+ */
161
+ export declare function getHostOrigin(): string | null;
162
+
163
+ /** Union of all Host → iframe messages. */
164
+ export declare type HostToIframeMessage = TfInitMessage | TfSetConfigMessage | TfSetStyleMessage | TfWalletStateMessage | TfSignResultMessage | TfFiatPopupClosedMessage | TfFiatProviderEventMessage;
165
+
166
+ /**
167
+ * Wallet adapter that runs inside the widget iframe and proxies all
168
+ * wallet operations to the host page's real adapter via postMessage.
169
+ */
170
+ export declare class IframeBridgeWalletAdapter implements IWalletAdapter {
171
+ readonly name = "IframeBridge";
172
+ readonly supportedActionTypes: WalletActionType[];
173
+ private connected;
174
+ private address;
175
+ private chainType;
176
+ private lastWalletSeq;
177
+ private readonly hostOrigin;
178
+ private readonly eventListeners;
179
+ private readonly pendingRequests;
180
+ private pendingConnect;
181
+ private pendingDisconnect;
182
+ private destroyed;
183
+ constructor(hostOrigin: string);
184
+ connect(chainType?: ChainType): Promise<void>;
185
+ disconnect(): Promise<void>;
186
+ isConnected(_chainType?: ChainType): boolean;
187
+ getAddress(_chainType?: ChainType): Promise<string | null>;
188
+ executeWalletAction(action: WalletAction): Promise<WalletActionResult>;
189
+ on(event: WalletEventType, handler: (event: WalletEvent) => void): void;
190
+ off(event: WalletEventType, handler: (event: WalletEvent) => void): void;
191
+ destroy(): void;
192
+ /** Set initial wallet state from tf:init payload. */
193
+ setInitialState(connected: boolean, address: string | null, chainType?: ChainType): void;
194
+ private handleMessage;
195
+ private handleWalletState;
196
+ private handleSignResult;
197
+ private postToHost;
198
+ private emit;
199
+ }
200
+
201
+ /**
202
+ * Runs inside the widget iframe. Listens for postMessage from the host Bridge
203
+ * and orchestrates config application and wallet adapter setup.
204
+ *
205
+ * Returns the IframeBridgeWalletAdapter that should be passed to the widget
206
+ * as its wallet adapter.
207
+ */
208
+ export declare class IframeReceiver {
209
+ readonly walletAdapter: IframeBridgeWalletAdapter;
210
+ private readonly hostOrigin;
211
+ private readonly callbacks;
212
+ private destroyed;
213
+ private resizeObserver;
214
+ private lastReportedHeight;
215
+ /** Subscribers to forwarded popup-closed events (set by useFiatCheckout in bridge mode). */
216
+ private readonly popupClosedHandlers;
217
+ /** Subscribers to forwarded provider terminal events (also useFiatCheckout). */
218
+ private readonly providerEventHandlers;
219
+ constructor(hostOrigin: string, callbacks: IframeReceiverCallbacks);
220
+ /**
221
+ * Returns the {@link FiatIframeBridge} accessor object that
222
+ * `useFiatCheckout` consumes. Methods are bound to this receiver instance.
223
+ */
224
+ getFiatBridge(): FiatIframeBridge;
225
+ /** Start observing widget root for height changes → tf:resize. */
226
+ observeResize(element: HTMLElement): void;
227
+ /** Emit a widget event to the host (e.g. swapComplete). */
228
+ emitEvent(name: string, data: unknown): void;
229
+ /** Request the host to pre-open a popup (fiat flow). */
230
+ requestPreOpenPopup(): void;
231
+ /** Request the host to navigate the popup to a URL (fiat flow). */
232
+ requestNavigatePopup(url: string, orderId: string): void;
233
+ /** Clean up all listeners and observers. */
234
+ destroy(): void;
235
+ private handleMessage;
236
+ private handleInit;
237
+ private postToHost;
238
+ }
239
+
240
+ export declare interface IframeReceiverCallbacks {
241
+ /** Called on tf:init — apply initial config to the widget. */
242
+ onInit: (config: WidgetConfig, style?: StyleOverrides) => void;
243
+ /** Called on tf:setConfig — apply config update. */
244
+ onConfigUpdate: (config: Partial<WidgetConfig>) => void;
245
+ /** Called on tf:setStyle — apply style overrides. */
246
+ onStyleUpdate: (style: StyleOverrides) => void;
247
+ /** Called on tf:fiatPopupClosed — used by fiat checkout in iframe mode. */
248
+ onFiatPopupClosed?: (orderId: string) => void;
249
+ /**
250
+ * Called on tf:fiatProviderEvent — host bridge forwards a provider terminal
251
+ * event from the popup wrapper page so the widget can drive `paymentOutcome`.
252
+ */
253
+ onFiatProviderEvent?: (event: FiatProviderEventPayload) => void;
254
+ }
255
+
256
+ /** Union of all iframe → Host messages. */
257
+ export declare type IframeToHostMessage = TfReadyMessage | TfSignRequestMessage | TfConnectWalletMessage | TfDisconnectWalletMessage | TfEventMessage | TfResizeMessage | TfPreOpenPopupMessage | TfNavigatePopupMessage;
258
+
259
+ /** Check if the current window is running inside an iframe. */
260
+ export declare function isIframeMode(): boolean;
261
+
262
+ /**
263
+ * Type guard: check if a message has a specific type.
264
+ */
265
+ export declare function isMessageType<T extends TfBridgeMessage["type"]>(msg: TfBridgeMessage, type: T): msg is Extract<TfBridgeMessage, {
266
+ type: T;
267
+ }>;
268
+
269
+ /** Wallet adapter interface that all adapters must implement */
270
+ declare interface IWalletAdapter {
271
+ /** Human-readable name */
272
+ readonly name: string;
273
+ /** Icon URL */
274
+ readonly icon?: string;
275
+ /** Supported action types */
276
+ readonly supportedActionTypes: WalletActionType[];
277
+ /** Optional: supported chain IDs — when set, the widget only shows tokens on these chains */
278
+ readonly supportedChainIds?: number[];
279
+ /** Connect the wallet */
280
+ connect(chainType?: ChainType): Promise<void>;
281
+ /** Disconnect the wallet */
282
+ disconnect(): Promise<void>;
283
+ /** Check if connected */
284
+ isConnected(chainType?: ChainType): boolean;
285
+ /** Get the current address */
286
+ getAddress(chainType?: ChainType): Promise<string | null>;
287
+ /** Execute a wallet action */
288
+ executeWalletAction(action: WalletAction): Promise<WalletActionResult>;
289
+ /** Optional: sign a message */
290
+ signMessage?(message: string, chainType?: ChainType): Promise<string>;
291
+ /** Optional: open the wallet's native account/connected modal */
292
+ openAccountModal?(): Promise<void>;
293
+ /** Optional: clean up internal subscriptions and watchers */
294
+ destroy?(): void;
295
+ /** Subscribe to wallet events */
296
+ on(event: WalletEventType, handler: (event: WalletEvent) => void): void;
297
+ /** Unsubscribe from wallet events */
298
+ off(event: WalletEventType, handler: (event: WalletEvent) => void): void;
299
+ }
300
+
301
+ /**
302
+ * Validate and parse a raw postMessage data object as a bridge message.
303
+ * Returns `null` if the data is not a valid bridge message.
304
+ */
305
+ export declare function parseMessage(data: unknown): TfBridgeMessage | null;
306
+
307
+ export declare interface SerializedError {
308
+ code: string;
309
+ message: string;
310
+ details?: unknown;
311
+ }
312
+
313
+ /** Serialize any error value into a structured object safe for postMessage. */
314
+ export declare function serializeError(err: unknown): SerializedError;
315
+
316
+ /** Solana sign and send transaction action */
317
+ declare interface SolanaSignAndSendAction {
318
+ type: "solana_signAndSendTransaction";
319
+ transaction: string;
320
+ }
321
+
322
+ /** Solana sign transaction action */
323
+ declare interface SolanaSignTransactionAction {
324
+ type: "solana_signTransaction";
325
+ transaction: string;
326
+ }
327
+
328
+ export declare interface StyleOverrides {
329
+ /** CSS custom property overrides (only --tf-* keys accepted). */
330
+ vars?: Partial<Record<TfCssVar, string>> & Record<string, string>;
331
+ /** Raw CSS string injected into <style> inside the widget Shadow DOM.
332
+ * Use [part="..."] selectors. @import rules are stripped. Max 50 KB. */
333
+ css?: string;
334
+ /** URL to an external stylesheet loaded via <link> inside the widget.
335
+ * Must be HTTPS. */
336
+ cssUrl?: string;
337
+ }
338
+
339
+ /** Supported locale identifiers. Accepts any string for forward-compat; known values get bundled translations. */
340
+ declare type SupportedLocale = "en-US" | "zh-CN" | "zh-TW" | "ja-JP" | "ko-KR" | (string & {});
341
+
342
+ /** Payment methods available in swap widget */
343
+ declare type SwapPayMethod = "crypto" | "card";
344
+
345
+ /** Current protocol version. Bumped on breaking message format changes. */
346
+ export declare const TF_PROTOCOL_VERSION = 1;
347
+
348
+ /** Union of all bridge messages (both directions). */
349
+ export declare type TfBridgeMessage = HostToIframeMessage | IframeToHostMessage;
350
+
351
+ /** Request host to trigger wallet connection. */
352
+ export declare type TfConnectWalletMessage = TfMessageEnvelope<"connectWallet", {
353
+ chainType?: ChainType;
354
+ }>;
355
+
356
+ /** All first-party CSS custom properties exposed for theming. */
357
+ declare type TfCssVar = "--tf-bg" | "--tf-bg-secondary" | "--tf-bg-elevated" | "--tf-surface" | "--tf-surface-hover" | "--tf-input-bg" | "--tf-glass" | "--tf-text" | "--tf-text-secondary" | "--tf-text-tertiary" | "--tf-text-on-primary" | "--tf-border" | "--tf-border-light" | "--tf-primary" | "--tf-primary-alpha" | "--tf-primary-light" | "--tf-primary-glow" | "--tf-success" | "--tf-success-bg" | "--tf-error" | "--tf-error-bg" | "--tf-error-alpha" | "--tf-warning" | "--tf-warning-bg" | "--tf-shadow" | "--tf-shadow-lg" | "--tf-skeleton" | "--tf-radius-xs" | "--tf-radius-sm" | "--tf-radius" | "--tf-radius-lg" | "--tf-radius-xl" | "--tf-button-radius" | "--tf-widget-max-width" | "--tf-font-family" | "--tf-font-family-mono" | "--tf-font-size" | "--tf-line-height" | "--tf-font-sm" | "--tf-font-base" | "--tf-font-md" | "--tf-font-lg" | "--tf-font-xl" | "--tf-font-heading" | "--tf-font-amount" | "--tf-font-amount-lg" | "--tf-font-amount-sm";
358
+
359
+ /** Request host to disconnect wallet. */
360
+ export declare type TfDisconnectWalletMessage = TfMessageEnvelope<"disconnectWallet", Record<string, never>>;
361
+
362
+ /** Widget events (swapComplete, error, etc.). */
363
+ export declare type TfEventMessage = TfMessageEnvelope<"event", {
364
+ name: string;
365
+ data: unknown;
366
+ }>;
367
+
368
+ /** Notify widget that the fiat popup was closed by the user. */
369
+ export declare type TfFiatPopupClosedMessage = TfMessageEnvelope<"fiatPopupClosed", {
370
+ orderId: string;
371
+ }>;
372
+
373
+ /**
374
+ * Forward a fiat provider terminal event from the host bridge to the widget.
375
+ *
376
+ * In iframe-embed mode the payment popup is opened by the host page, so the
377
+ * provider's wrapper script (`fiat.hyperstream.dev`) posts `FIAT_WIDGET_EVENT`
378
+ * messages to the host's `window.opener` — never to the widget iframe. The
379
+ * host bridge listens for those events (origin-validated against the widget
380
+ * config's `fiatApiEndpoint`, filtered by the active popup's orderId) and
381
+ * forwards them as this message so `useFiatCheckout` can drive the
382
+ * `paymentOutcome` signal exactly the same way it does in non-iframe mode.
383
+ */
384
+ export declare type TfFiatProviderEventMessage = TfMessageEnvelope<"fiatProviderEvent", {
385
+ orderId: string;
386
+ /** Provider event name as forwarded by the wrapper bridge. */
387
+ event: "widget_ready" | "order_completed" | "order_failed" | "widget_close";
388
+ /** Provider id (e.g. "transak"). */
389
+ provider?: string;
390
+ /** Optional event-specific payload (failedReason, providerStatus, etc.). */
391
+ data?: Record<string, unknown>;
392
+ }>;
393
+
394
+ /** Initial config + wallet state, sent after tf:ready. */
395
+ export declare type TfInitMessage = TfMessageEnvelope<"init", {
396
+ config: WidgetConfig;
397
+ style?: StyleOverrides;
398
+ walletAddress?: string | null;
399
+ chainType?: ChainType;
400
+ connected?: boolean;
401
+ }>;
402
+
403
+ export declare interface TfMessageEnvelope<T extends string = string, P = unknown> {
404
+ type: `tf:${T}`;
405
+ protocolVersion: typeof TF_PROTOCOL_VERSION;
406
+ /** Monotonically increasing per sender — used for ordering & staleness detection. */
407
+ seq: number;
408
+ payload: P;
409
+ }
410
+
411
+ /** Request host to navigate the pre-opened popup to a fiat payment URL. */
412
+ export declare type TfNavigatePopupMessage = TfMessageEnvelope<"navigatePopup", {
413
+ url: string;
414
+ orderId: string;
415
+ }>;
416
+
417
+ /** Request host to pre-open a blank popup (must be in user gesture context). */
418
+ export declare type TfPreOpenPopupMessage = TfMessageEnvelope<"preOpenPopup", Record<string, never>>;
419
+
420
+ /** Widget loaded and ready. */
421
+ export declare type TfReadyMessage = TfMessageEnvelope<"ready", {
422
+ version: string;
423
+ protocolVersion: number;
424
+ }>;
425
+
426
+ /** Suggested iframe height for responsive layout. */
427
+ export declare type TfResizeMessage = TfMessageEnvelope<"resize", {
428
+ height: number;
429
+ }>;
430
+
431
+ /** Dynamic config update. */
432
+ export declare type TfSetConfigMessage = TfMessageEnvelope<"setConfig", Partial<WidgetConfig>>;
433
+
434
+ /** Dynamic style update. */
435
+ export declare type TfSetStyleMessage = TfMessageEnvelope<"setStyle", StyleOverrides>;
436
+
437
+ /** Request host to sign a transaction. */
438
+ export declare type TfSignRequestMessage = TfMessageEnvelope<"signRequest", {
439
+ requestId: string;
440
+ action: WalletAction;
441
+ timeoutMs?: number;
442
+ }>;
443
+
444
+ /** Response to a signing request. */
445
+ export declare type TfSignResultMessage = TfMessageEnvelope<"signResult", {
446
+ requestId: string;
447
+ result?: WalletActionResult;
448
+ error?: SerializedError;
449
+ }>;
450
+
451
+ /** Wallet state change (connect, disconnect, address change). */
452
+ export declare type TfWalletStateMessage = TfMessageEnvelope<"walletState", {
453
+ connected: boolean;
454
+ address: string | null;
455
+ chainType?: ChainType;
456
+ event?: WalletEventType;
457
+ }>;
458
+
459
+ /** Visual theme mode. */
460
+ declare type Theme = "light" | "dark" | "auto";
461
+
462
+ /**
463
+ * Bridge that connects a host page's wallet adapter to a TokenFlight widget
464
+ * running inside an iframe via postMessage.
465
+ *
466
+ * ```ts
467
+ * const bridge = createTokenFlightBridge({
468
+ * container: '#swap-widget',
469
+ * wallet: adapter,
470
+ * config: { fromToken: '...', toToken: '...' },
471
+ * });
472
+ * bridge.on('swapComplete', (data) => { ... });
473
+ * bridge.destroy();
474
+ * ```
475
+ */
476
+ export declare class TokenFlightBridge {
477
+ readonly iframe: HTMLIFrameElement;
478
+ private readonly container;
479
+ private wallet;
480
+ private readonly options;
481
+ private readonly listeners;
482
+ private readonly walletHandler;
483
+ private widgetOrigin;
484
+ private ready;
485
+ private destroyed;
486
+ private readyTimer;
487
+ private popup;
488
+ private popupPollTimer;
489
+ private popupOrderId;
490
+ /** Window listener forwarding FIAT_WIDGET_EVENT messages → tf:fiatProviderEvent. */
491
+ private popupMessageHandler;
492
+ constructor(options: BridgeOptions);
493
+ /** Update widget configuration without iframe reload. */
494
+ setConfig(config: Partial<WidgetConfig>): void;
495
+ /** Update style overrides. */
496
+ setStyle(style: StyleOverrides): void;
497
+ /** Set or replace the wallet adapter (supports deferred setup). */
498
+ setWallet(adapter: IWalletAdapter | undefined): void;
499
+ /** Subscribe to a widget event. */
500
+ on<K extends BridgeEventName>(event: K, handler: (data: BridgeEventMap[K]) => void): void;
501
+ /** Unsubscribe from a widget event. */
502
+ off<K extends BridgeEventName>(event: K, handler: (data: BridgeEventMap[K]) => void): void;
503
+ /** Remove iframe, clean up listeners, close popup if open. */
504
+ destroy(): void;
505
+ private handleMessage;
506
+ private handleReady;
507
+ private handleSignRequest;
508
+ private handleConnectWallet;
509
+ private handleDisconnectWallet;
510
+ private handleWidgetEvent;
511
+ private handleResize;
512
+ private handlePreOpenPopup;
513
+ /** Called internally when the widget wants to navigate the pre-opened popup. */
514
+ private navigatePopup;
515
+ /**
516
+ * Subscribe to provider terminal events arriving on the host window from
517
+ * the popup's wrapper page. Origin-validated against the widget config's
518
+ * `fiatApiEndpoint`; filtered by the active `popupOrderId`. Drops messages
519
+ * that don't match the expected `FiatWidgetEvent` shape.
520
+ */
521
+ private installPopupMessageHandler;
522
+ private closePopup;
523
+ private static readonly WALLET_EVENTS;
524
+ private subscribeWalletEvents;
525
+ private unsubscribeWalletEvents;
526
+ private sendWalletState;
527
+ private postToIframe;
528
+ private emit;
529
+ }
530
+
531
+ /**
532
+ * Flexible token identifier supporting:
533
+ * - Direct object: { chainId: 1, address: "0x..." }
534
+ * - CAIP-10 string: "eip155:1:0x..."
535
+ * - JSON string: '{"chainId":1,"address":"0x..."}'
536
+ */
537
+ declare type TokenIdentifier = string | TokenTarget;
538
+
539
+ /** Token target as chain + address pair */
540
+ declare interface TokenTarget {
541
+ chainId: number;
542
+ address: string;
543
+ }
544
+
545
+ /** Union of all wallet action types */
546
+ declare type WalletAction = EvmWalletAction | SolanaSignTransactionAction | SolanaSignAndSendAction;
547
+
548
+ /** Result of executing a wallet action */
549
+ declare interface WalletActionResult {
550
+ success: boolean;
551
+ data?: unknown;
552
+ error?: string;
553
+ txHash?: string;
554
+ }
555
+
556
+ /** Wallet action types */
557
+ declare type WalletActionType = "eip1193_request" | "solana_signTransaction" | "solana_signAndSendTransaction";
558
+
559
+ /** Well-known error codes for wallet operations. */
560
+ export declare type WalletErrorCode = "USER_REJECTED" | "CHAIN_SWITCH_FAILED" | "INSUFFICIENT_FUNDS" | "TIMEOUT" | "ADAPTER_ERROR" | "UNKNOWN";
561
+
562
+ /** Wallet event payload */
563
+ declare interface WalletEvent {
564
+ type: WalletEventType;
565
+ data?: unknown;
566
+ }
567
+
568
+ /** Wallet event types */
569
+ declare type WalletEventType = "connect" | "disconnect" | "chainChanged" | "accountsChanged";
570
+
571
+ export declare interface WidgetConfig {
572
+ apiEndpoint?: string;
573
+ fiatApiEndpoint?: string;
574
+ theme?: Theme;
575
+ locale?: SupportedLocale;
576
+ customColors?: CustomColors;
577
+ fromToken?: TokenIdentifier;
578
+ toToken?: TokenIdentifier | TokenIdentifier[];
579
+ tradeType?: "EXACT_INPUT" | "EXACT_OUTPUT";
580
+ amount?: string;
581
+ recipient?: string;
582
+ methods?: SwapPayMethod[];
583
+ defaultPayMethod?: SwapPayMethod;
584
+ fiatCurrency?: string;
585
+ fromTokens?: TokenIdentifier[];
586
+ toTokens?: TokenIdentifier[];
587
+ titleText?: string;
588
+ titleImageUrl?: string;
589
+ hideTitle?: boolean;
590
+ hidePoweredBy?: boolean;
591
+ noBackground?: boolean;
592
+ noBorder?: boolean;
593
+ lockFromToken?: boolean;
594
+ lockToToken?: boolean;
595
+ }
596
+
597
+ export { }
package/dist/bridge.js ADDED
@@ -0,0 +1 @@
1
+ import{n as e}from"./active-bridge-DTyKObda.js";import{a as t,c as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./bridge-C9uFKAdS.js";var d=12e4,f=6e4,p=class{constructor(e){this.name=`IframeBridge`,this.supportedActionTypes=[`eip1193_request`,`solana_signTransaction`,`solana_signAndSendTransaction`],this.connected=!1,this.address=null,this.lastWalletSeq=0,this.eventListeners=new Map,this.pendingRequests=new Map,this.pendingConnect=null,this.pendingDisconnect=null,this.destroyed=!1,this.handleMessage=e=>{if(this.destroyed||e.origin!==this.hostOrigin)return;let t=n(e.data);t&&(c(t,`tf:walletState`)?this.handleWalletState(t.payload,t.seq):c(t,`tf:signResult`)&&this.handleSignResult(t.payload.requestId,t.payload.result,t.payload.error))},this.hostOrigin=e,window.addEventListener(`message`,this.handleMessage)}async connect(e){return this.postToHost(r(`connectWallet`,{chainType:e})),new Promise((e,t)=>{this.pendingConnect={resolve:e,reject:t},setTimeout(()=>{this.pendingConnect&&(this.pendingConnect.reject(Error(`Wallet connection timed out`)),this.pendingConnect=null)},f)})}async disconnect(){return this.postToHost(r(`disconnectWallet`,{})),new Promise((e,t)=>{this.pendingDisconnect={resolve:e,reject:t},setTimeout(()=>{this.pendingDisconnect&&(this.pendingDisconnect.reject(Error(`Wallet disconnect timed out`)),this.pendingDisconnect=null)},f)})}isConnected(e){return this.connected}async getAddress(e){return this.address}async executeWalletAction(e){let n=t();return new Promise((t,i)=>{let a=setTimeout(()=>{this.pendingRequests.delete(n),i(Error(`Wallet action timed out`))},d);this.pendingRequests.set(n,{resolve:t,reject:i,timer:a}),this.postToHost(r(`signRequest`,{requestId:n,action:e}))})}on(e,t){let n=this.eventListeners.get(e);n||(n=new Set,this.eventListeners.set(e,n)),n.add(t)}off(e,t){this.eventListeners.get(e)?.delete(t)}destroy(){if(!this.destroyed){this.destroyed=!0,window.removeEventListener(`message`,this.handleMessage);for(let[,e]of this.pendingRequests)clearTimeout(e.timer),e.reject(Error(`Bridge destroyed`));this.pendingRequests.clear(),this.pendingConnect&&(this.pendingConnect.reject(Error(`Bridge destroyed`)),this.pendingConnect=null),this.pendingDisconnect&&(this.pendingDisconnect.reject(Error(`Bridge destroyed`)),this.pendingDisconnect=null),this.eventListeners.clear()}}setInitialState(e,t,n){this.connected=e,this.address=t,this.chainType=n}handleWalletState(e,t){if(t<=this.lastWalletSeq)return;this.lastWalletSeq=t;let n=this.connected;this.connected=e.connected,this.address=e.address,e.chainType&&(this.chainType=e.chainType),e.connected&&this.pendingConnect&&(this.pendingConnect.resolve(),this.pendingConnect=null),!e.connected&&this.pendingDisconnect&&(this.pendingDisconnect.resolve(),this.pendingDisconnect=null),e.event?this.emit(e.event,{type:e.event,data:e}):e.connected&&!n?this.emit(`connect`,{type:`connect`,data:e}):!e.connected&&n?this.emit(`disconnect`,{type:`disconnect`,data:e}):e.connected&&this.emit(`accountsChanged`,{type:`accountsChanged`,data:e})}handleSignResult(e,t,n){let r=this.pendingRequests.get(e);r&&(this.pendingRequests.delete(e),clearTimeout(r.timer),n?r.reject(o(n)):t?r.resolve(t):r.reject(Error(`Invalid sign result: missing both result and error`)))}postToHost(e){this.destroyed||typeof window>`u`||!window.parent||window.parent.postMessage(e,this.hostOrigin)}emit(e,t){let n=this.eventListeners.get(e);if(n)for(let e of n)try{e(t)}catch{}}},m=`__TF_VERSION__`,h=class{constructor(t,i){this.destroyed=!1,this.resizeObserver=null,this.lastReportedHeight=0,this.popupClosedHandlers=new Set,this.providerEventHandlers=new Set,this.handleMessage=e=>{if(this.destroyed||e.origin!==this.hostOrigin)return;let t=n(e.data);if(t){if(c(t,`tf:init`))this.handleInit(t.payload);else if(c(t,`tf:setConfig`))this.callbacks.onConfigUpdate(t.payload);else if(c(t,`tf:setStyle`))this.callbacks.onStyleUpdate(t.payload);else if(c(t,`tf:fiatPopupClosed`)){let e=t.payload.orderId;this.callbacks.onFiatPopupClosed?.(e);for(let t of this.popupClosedHandlers)t(e)}else if(c(t,`tf:fiatProviderEvent`)){let e=t.payload;this.callbacks.onFiatProviderEvent?.(e);for(let t of this.providerEventHandlers)t(e)}}},this.hostOrigin=t,this.callbacks=i,this.walletAdapter=new p(t),window.addEventListener(`message`,this.handleMessage),e(this.getFiatBridge()),this.postToHost(r(`ready`,{version:m,protocolVersion:1}))}getFiatBridge(){return{requestPreOpenPopup:()=>this.requestPreOpenPopup(),requestNavigatePopup:(e,t)=>this.requestNavigatePopup(e,t),onPopupClosed:e=>(this.popupClosedHandlers.add(e),()=>this.popupClosedHandlers.delete(e)),onProviderEvent:e=>(this.providerEventHandlers.add(e),()=>this.providerEventHandlers.delete(e))}}observeResize(e){this.resizeObserver=new ResizeObserver(e=>{let t=e[0];if(!t)return;let n=Math.ceil(t.borderBoxSize?.[0]?.blockSize??t.contentRect.height);Math.abs(n-this.lastReportedHeight)<2||(this.lastReportedHeight=n,this.postToHost(r(`resize`,{height:n})))}),this.resizeObserver.observe(e)}emitEvent(e,t){this.postToHost(r(`event`,{name:e,data:t}))}requestPreOpenPopup(){this.postToHost(r(`preOpenPopup`,{}))}requestNavigatePopup(e,t){this.postToHost(r(`navigatePopup`,{url:e,orderId:t}))}destroy(){this.destroyed||(this.destroyed=!0,window.removeEventListener(`message`,this.handleMessage),this.walletAdapter.destroy(),this.popupClosedHandlers.clear(),this.providerEventHandlers.clear(),e(null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null))}handleInit(e){this.walletAdapter.setInitialState(e.connected??!1,e.walletAddress??null,e.chainType),this.callbacks.onInit(e.config,e.style)}postToHost(e){this.destroyed||window.parent.postMessage(e,this.hostOrigin)}};function g(){try{return window!==window.top}catch{return!0}}function _(){if(!g())return null;try{if(document.referrer)return new URL(document.referrer).origin}catch{}return null}export{p as IframeBridgeWalletAdapter,h as IframeReceiver,u as TF_PROTOCOL_VERSION,l as TokenFlightBridge,s as consumeRequestId,r as createMessage,t as createRequestId,a as createTokenFlightBridge,o as deserializeError,_ as getHostOrigin,g as isIframeMode,c as isMessageType,n as parseMessage,i as serializeError};