@cowprotocol/widget-lib 2.0.0 → 2.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 (48) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +27 -4
  3. package/src/IframeCowEventEmitter.d.ts +10 -0
  4. package/src/IframeCowEventEmitter.js +35 -0
  5. package/src/IframeCowEventEmitter.js.map +1 -0
  6. package/src/IframeSafeSdkBridge.d.ts +8 -0
  7. package/src/IframeSafeSdkBridge.js +53 -0
  8. package/src/IframeSafeSdkBridge.js.map +1 -0
  9. package/src/cowSwapWidget.d.ts +18 -0
  10. package/src/cowSwapWidget.js +189 -0
  11. package/src/cowSwapWidget.js.map +1 -0
  12. package/src/flexibleConfig.d.ts +5 -0
  13. package/src/flexibleConfig.js +30 -0
  14. package/src/flexibleConfig.js.map +1 -0
  15. package/src/index.d.ts +7 -0
  16. package/src/index.js +14 -0
  17. package/src/index.js.map +1 -0
  18. package/src/resolveFlexibleConfigValues.d.ts +2 -0
  19. package/src/resolveFlexibleConfigValues.js +14 -0
  20. package/src/resolveFlexibleConfigValues.js.map +1 -0
  21. package/src/themeUtils.d.ts +2 -0
  22. package/src/themeUtils.js +7 -0
  23. package/src/themeUtils.js.map +1 -0
  24. package/src/types.d.ts +365 -0
  25. package/src/types.js +42 -0
  26. package/src/types.js.map +1 -0
  27. package/src/urlUtils.d.ts +4 -0
  28. package/src/urlUtils.js +55 -0
  29. package/src/urlUtils.js.map +1 -0
  30. package/src/widgetIframeTransport.d.ts +3 -0
  31. package/src/widgetIframeTransport.js +6 -0
  32. package/src/widgetIframeTransport.js.map +1 -0
  33. package/jest.config.ts +0 -10
  34. package/project.json +0 -51
  35. package/src/IframeCowEventEmitter.ts +0 -47
  36. package/src/IframeSafeSdkBridge.ts +0 -77
  37. package/src/cowSwapWidget.ts +0 -259
  38. package/src/flexibleConfig.ts +0 -39
  39. package/src/index.ts +0 -7
  40. package/src/resolveFlexibleConfigValues.ts +0 -14
  41. package/src/themeUtils.ts +0 -7
  42. package/src/types.ts +0 -458
  43. package/src/urlUtils.spec.ts +0 -88
  44. package/src/urlUtils.ts +0 -66
  45. package/src/widgetIframeTransport.ts +0 -5
  46. package/tsconfig.json +0 -22
  47. package/tsconfig.lib.json +0 -10
  48. package/tsconfig.spec.json +0 -9
@@ -1,259 +0,0 @@
1
- import { CowWidgetEventListeners } from '@cowprotocol/events'
2
- import { IframeRpcProviderBridge } from '@cowprotocol/iframe-transport'
3
-
4
- import { IframeCowEventEmitter } from './IframeCowEventEmitter'
5
- import { IframeSafeSdkBridge } from './IframeSafeSdkBridge'
6
- import {
7
- CowSwapWidgetParams,
8
- CowSwapWidgetProps,
9
- EthereumProvider,
10
- WidgetMethodsEmit,
11
- WidgetMethodsListen,
12
- WindowListener,
13
- } from './types'
14
- import { buildWidgetPath, buildWidgetUrl, buildWidgetUrlQuery } from './urlUtils'
15
- import { widgetIframeTransport } from './widgetIframeTransport'
16
-
17
- const DEFAULT_HEIGHT = '640px'
18
- const DEFAULT_WIDTH = '450px'
19
-
20
- /**
21
- * Reference: IframeResizer (apps/cowswap-frontend/src/modules/injectedWidget/updaters/IframeResizer.ts)
22
- * Sometimes MutationObserver doesn't trigger when the height of the widget changes and the widget displays with a scrollbar.
23
- * To avoid this we add a threshold to the height.
24
- * 20px
25
- */
26
- const HEIGHT_THRESHOLD = 20
27
-
28
- const noopHandler: CowSwapWidgetHandler = {
29
- updateParams: () => void 0,
30
- updateListeners: () => void 0,
31
- updateProvider: () => void 0,
32
- destroy: () => void 0,
33
- }
34
-
35
- /**
36
- * Callback function signature for updating the CoW Swap Widget.
37
- */
38
- export interface CowSwapWidgetHandler {
39
- updateParams: (params: CowSwapWidgetParams) => void
40
- updateListeners: (newListeners?: CowWidgetEventListeners) => void
41
- updateProvider: (newProvider?: EthereumProvider) => void
42
- destroy: () => void
43
- }
44
-
45
- /**
46
- * Generates and injects a CoW Swap Widget into the provided container.
47
- * @param container - The HTML element to inject the widget into.
48
- * @param params - Parameters for configuring the widget.
49
- * @returns A callback function to update the widget with new settings.
50
- */
51
- export function createCowSwapWidget(container: HTMLElement, props: CowSwapWidgetProps): CowSwapWidgetHandler {
52
- const { params, provider: providerAux, listeners } = props
53
- let provider = providerAux
54
- let currentParams = params
55
-
56
- if (typeof window === 'undefined') return noopHandler
57
-
58
- // 1. Create a brand new iframe
59
- const iframe = createIframe(params)
60
-
61
- // 2. Clear the content (delete any previous iFrame if it exists)
62
- container.innerHTML = ''
63
- container.appendChild(iframe)
64
-
65
- const { contentWindow: iframeWindow } = iframe
66
- if (!iframeWindow) {
67
- console.error('Iframe does not contain a window', iframe)
68
- throw new Error('Iframe does not contain a window!')
69
- }
70
-
71
- // 3. Send appCode (once the widget posts the ACTIVATE message)
72
- const windowListeners: WindowListener[] = []
73
- windowListeners.push(sendAppCodeOnActivation(iframeWindow, params.appCode))
74
-
75
- // 4. Handle widget height changes
76
- windowListeners.push(...listenToHeightChanges(iframe, params.height, params.maxHeight))
77
-
78
- // 5. Intercept deeplinks navigation in the iframe
79
- windowListeners.push(interceptDeepLinks())
80
-
81
- // 6. Handle and forward widget events to the listeners
82
- const iFrameCowEventEmitter = new IframeCowEventEmitter(window, listeners)
83
-
84
- // 7. Wire up the iframeRpcProviderBridge with the provider (so RPC calls flow back and forth)
85
- let iframeRpcProviderBridge = updateProvider(iframeWindow, null, provider)
86
-
87
- // 8. Schedule the uploading of the params, once the iframe is loaded
88
- iframe.addEventListener('load', () => updateParams(iframeWindow, currentParams, provider))
89
-
90
- // 9. Listen for messages from the iframe
91
- const iframeSafeSdkBridge = new IframeSafeSdkBridge(window, iframeWindow)
92
-
93
- // 10. Return the handler, so the widget, listeners, and provider can be updated
94
- return {
95
- updateParams: (newParams: CowSwapWidgetParams) => {
96
- currentParams = newParams
97
- updateParams(iframeWindow, currentParams, provider)
98
- },
99
- updateListeners: (newListeners?: CowWidgetEventListeners) => iFrameCowEventEmitter.updateListeners(newListeners),
100
- updateProvider: (newProvider) => {
101
- provider = newProvider
102
- iframeRpcProviderBridge = updateProvider(iframeWindow, iframeRpcProviderBridge, newProvider)
103
- },
104
-
105
- destroy: () => {
106
- // Disconnect rpc provider and unsubscribe to events
107
- iframeRpcProviderBridge.disconnect()
108
- // Stop listening for cow events
109
- iFrameCowEventEmitter.stopListeningIframe()
110
-
111
- // Disconnect all listeners
112
- windowListeners.forEach((listener) => window.removeEventListener('message', listener))
113
-
114
- // Stop listening for SDK messages
115
- iframeSafeSdkBridge.stopListening()
116
-
117
- // Destroy the iframe
118
- container.removeChild(iframe)
119
- },
120
- }
121
- }
122
-
123
- /**
124
- * Update the provider for the iframeRpcProviderBridge.
125
- *
126
- * It will disconnect from the previous provider and connect to the new one.
127
- *
128
- * @param iframe iframe window
129
- * @param iframeRpcProviderBridge iframe RPC manager
130
- * @param newProvider new provider
131
- *
132
- * @returns the iframeRpcProviderBridge
133
- */
134
- function updateProvider(
135
- iframe: Window,
136
- iframeRpcProviderBridge: IframeRpcProviderBridge | null,
137
- newProvider?: EthereumProvider,
138
- ): IframeRpcProviderBridge {
139
- // Disconnect from the previous provider bridge
140
- if (iframeRpcProviderBridge) {
141
- iframeRpcProviderBridge.disconnect()
142
- }
143
-
144
- const providerBridge = iframeRpcProviderBridge || new IframeRpcProviderBridge(iframe)
145
-
146
- // Connect to the new provider
147
- if (newProvider) {
148
- providerBridge.onConnect(newProvider)
149
- }
150
-
151
- return providerBridge
152
- }
153
-
154
- /**
155
- * Creates an iframe element for the CoW Swap Widget based on provided parameters and settings.
156
- * @param params - Parameters for the widget.
157
- * @returns The generated HTMLIFrameElement.
158
- */
159
- function createIframe(params: CowSwapWidgetParams): HTMLIFrameElement {
160
- const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = params
161
-
162
- const iframe = document.createElement('iframe')
163
-
164
- iframe.src = buildWidgetUrl(params)
165
- iframe.width = width
166
- iframe.height = height
167
- iframe.style.border = '0'
168
- iframe.allow = 'clipboard-read; clipboard-write'
169
-
170
- return iframe
171
- }
172
-
173
- /**
174
- * Updates the CoW Swap Widget based on the new settings provided.
175
- * @param params - New params for the widget.
176
- * @param contentWindow - Window object of the widget's iframe.
177
- */
178
- function updateParams(
179
- contentWindow: Window,
180
- params: CowSwapWidgetParams,
181
- provider: EthereumProvider | undefined,
182
- ): void {
183
- const hasProvider = !!provider
184
-
185
- const pathname = buildWidgetPath(params)
186
- const search = buildWidgetUrlQuery(params).toString()
187
-
188
- // Omit theme from appParams
189
- const { theme: _theme, ...appParams } = params
190
-
191
- widgetIframeTransport.postMessageToWindow(contentWindow, WidgetMethodsListen.UPDATE_PARAMS, {
192
- urlParams: {
193
- pathname,
194
- search,
195
- },
196
- appParams,
197
- hasProvider,
198
- })
199
- }
200
-
201
- /**
202
- * Sends appCode to the contentWindow of the widget once the widget is activated.
203
- *
204
- * @param contentWindow - Window object of the widget's iframe.
205
- * @param appCode - A unique identifier for the app.
206
- */
207
- function sendAppCodeOnActivation(
208
- contentWindow: Window,
209
- appCode: string | undefined,
210
- ): (payload: MessageEvent<unknown>) => void {
211
- return widgetIframeTransport.listenToMessageFromWindow(window, WidgetMethodsEmit.ACTIVATE, () => {
212
- // Update the appData
213
- widgetIframeTransport.postMessageToWindow(contentWindow, WidgetMethodsListen.UPDATE_APP_DATA, {
214
- metaData: appCode ? { appCode } : undefined,
215
- })
216
- })
217
- }
218
-
219
- /**
220
- * Since deeplinks are not supported in iframes, this function intercepts the window.open calls from the widget and opens
221
- */
222
- function interceptDeepLinks(): (payload: MessageEvent<unknown>) => void {
223
- return widgetIframeTransport.listenToMessageFromWindow(
224
- window,
225
- WidgetMethodsEmit.INTERCEPT_WINDOW_OPEN,
226
- ({ href, rel, target }) => {
227
- const url = href.toString()
228
-
229
- if (!url.startsWith('http') && url.match(/^[a-zA-Z0-9]+:\/\//)) {
230
- window.open(url, target, rel)
231
- return
232
- }
233
- },
234
- )
235
- }
236
-
237
- /**
238
- * Listens for iframeHeight emitted by the widget, and applies dynamic height adjustments to the widget's iframe.
239
- *
240
- * @param iframe - The HTMLIFrameElement of the widget.
241
- * @param defaultHeight - Default height for the widget.
242
- * @param maxHeight - Maximum height for the widget.
243
- */
244
- function listenToHeightChanges(
245
- iframe: HTMLIFrameElement,
246
- defaultHeight = DEFAULT_HEIGHT,
247
- maxHeight?: number,
248
- ): WindowListener[] {
249
- return [
250
- widgetIframeTransport.listenToMessageFromWindow(window, WidgetMethodsEmit.UPDATE_HEIGHT, (data) => {
251
- const newHeight = data.height ? data.height + HEIGHT_THRESHOLD : undefined
252
-
253
- iframe.style.height = newHeight ? `${maxHeight ? Math.min(newHeight, maxHeight) : newHeight}px` : defaultHeight
254
- }),
255
- widgetIframeTransport.listenToMessageFromWindow(window, WidgetMethodsEmit.SET_FULL_HEIGHT, ({ isUpToSmall }) => {
256
- iframe.style.height = isUpToSmall ? defaultHeight : `${maxHeight || document.body.offsetHeight}px`
257
- }),
258
- ]
259
- }
@@ -1,39 +0,0 @@
1
- import type { SupportedChainId } from '@cowprotocol/cow-sdk'
2
-
3
- import { FlexibleConfig, PerNetworkConfig, PerTradeTypeConfig, TradeType } from './types'
4
-
5
- const TradeTypes = Object.values(TradeType)
6
-
7
- export function resolveFlexibleConfig<T>(
8
- config: FlexibleConfig<T>,
9
- chainId: SupportedChainId,
10
- tradeType: TradeType,
11
- ): T | undefined {
12
- if (isPerTradeTypeConfig(config)) {
13
- const value = config[tradeType]
14
-
15
- return isPerNetworkConfig(value) ? value[chainId] : value
16
- }
17
-
18
- if (isPerNetworkConfig(config)) {
19
- const value = config[chainId]
20
-
21
- return isPerTradeTypeConfig(value) ? value[tradeType] : value
22
- }
23
-
24
- return config as T
25
- }
26
-
27
- export function isPerTradeTypeConfig<T>(config: FlexibleConfig<T>): config is PerTradeTypeConfig<T> {
28
- if (typeof config !== 'object') return false
29
-
30
- return Object.keys(config as object).every((key) => TradeTypes.includes(key as TradeType))
31
- }
32
-
33
- const D_REGEX = /^\d+$/
34
-
35
- export function isPerNetworkConfig<T>(config: FlexibleConfig<T>): config is PerNetworkConfig<T> {
36
- if (typeof config !== 'object') return false
37
-
38
- return Object.keys(config as object).every((key) => D_REGEX.test(key))
39
- }
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export { createCowSwapWidget } from './cowSwapWidget'
2
- export { widgetIframeTransport } from './widgetIframeTransport'
3
- export type { CowSwapWidgetHandler } from './cowSwapWidget'
4
- export * from './types'
5
- export * from './themeUtils'
6
- export * from './flexibleConfig'
7
- export { resolveFlexibleConfigValues } from './resolveFlexibleConfigValues'
@@ -1,14 +0,0 @@
1
- import { isPerNetworkConfig, isPerTradeTypeConfig } from './flexibleConfig'
2
- import { FlexibleConfig } from './types'
3
-
4
- export function resolveFlexibleConfigValues<T>(config: FlexibleConfig<T>): T[] {
5
- if (isPerTradeTypeConfig(config)) {
6
- return Object.values(config).flatMap((value) => (isPerNetworkConfig(value) ? Object.values(value) : [value]))
7
- }
8
-
9
- if (isPerNetworkConfig(config)) {
10
- return Object.values(config).flatMap((value) => (isPerTradeTypeConfig(value) ? Object.values(value) : [value]))
11
- }
12
-
13
- return [config] as T[]
14
- }
package/src/themeUtils.ts DELETED
@@ -1,7 +0,0 @@
1
- import { CowSwapTheme, CowSwapWidgetPalette } from './types'
2
-
3
- export function isCowSwapWidgetPalette(
4
- palette: CowSwapTheme | CowSwapWidgetPalette | undefined,
5
- ): palette is CowSwapWidgetPalette {
6
- return Boolean(palette && typeof palette === 'object')
7
- }