@yuno-payments/dashboard-embed-sdk 1.2.0 → 1.4.0-BETA.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.
- package/README.md +71 -28
- package/dist/index.d.ts +28 -7
- package/dist/index.js +96 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ destroyDashboard();
|
|
|
74
74
|
| `testMode` | `boolean` | No | When set, the dashboard mounts in test/sandbox mode (`true`) or live mode (`false`). Omit to inherit the dashboard's own default. See [Test mode](#test-mode) |
|
|
75
75
|
| `onReady` | `() => void` | No | Callback invoked when the dashboard is fully loaded and authenticated |
|
|
76
76
|
| `onSessionExpired` | `() => void` | No | Callback invoked when the embedded session has expired. The host should re-authenticate and call `setToken(newToken)` to resume. |
|
|
77
|
-
| `
|
|
77
|
+
| `events` | `DashboardEvents` | No | Business events the dashboard hands off to the host, grouped by MFE (domain) and action — e.g. `events.connections.created`, `events.checkout.published`. Providing a handler also signals that the host owns that action: the dashboard skips its own UI and lets the host decide what happens next. See [Embed events](#embed-events). |
|
|
78
78
|
| `loading` | `HTMLElement` | No | Custom loading overlay element. If omitted, a default spinner is shown |
|
|
79
79
|
| `autoHeight` | `boolean` | No | When `true`, the iframe is resized to match the dashboard content height (no inner scroll). Default `false` (iframe fills its container at `100%`). See [Auto height](#auto-height) |
|
|
80
80
|
|
|
@@ -133,15 +133,31 @@ interface DashboardTheme {
|
|
|
133
133
|
styles?: string; // Custom CSS injected into the dashboard
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
type
|
|
136
|
+
type EmbedEventStatus = "loading" | "success" | "error";
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
interface ConnectionCreatedPayload {
|
|
139
|
+
connectionCode: string;
|
|
140
|
+
providerId: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface ConnectionCreatedEvent {
|
|
144
|
+
status: EmbedEventStatus;
|
|
145
|
+
payload?: ConnectionCreatedPayload;
|
|
146
|
+
}
|
|
139
147
|
|
|
140
148
|
interface CheckoutPublishedEvent {
|
|
141
|
-
|
|
142
|
-
status: CheckoutPublishedStatus;
|
|
149
|
+
status: EmbedEventStatus;
|
|
143
150
|
payload?: unknown; // The configuration object that was published
|
|
144
151
|
}
|
|
152
|
+
|
|
153
|
+
interface DashboardEvents {
|
|
154
|
+
connections?: {
|
|
155
|
+
created?: (event: ConnectionCreatedEvent) => void | Promise<void>;
|
|
156
|
+
};
|
|
157
|
+
checkout?: {
|
|
158
|
+
published?: (event: CheckoutPublishedEvent) => void | Promise<void>;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
145
161
|
```
|
|
146
162
|
|
|
147
163
|
## Session timeouts
|
|
@@ -171,40 +187,53 @@ const sdk = initDashboard({
|
|
|
171
187
|
})
|
|
172
188
|
```
|
|
173
189
|
|
|
174
|
-
##
|
|
190
|
+
## Embed events
|
|
175
191
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
The embedded dashboard hands business events off to the host through the
|
|
193
|
+
`events` config, grouped by MFE (`domain`) and `action`. Register a handler only
|
|
194
|
+
for what you care about:
|
|
179
195
|
|
|
180
196
|
```ts
|
|
181
197
|
const sdk = initDashboard({
|
|
182
198
|
baseUrl: "https://dashboard.y.uno",
|
|
183
199
|
container: document.getElementById("dashboard")!,
|
|
184
200
|
token: initialToken,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
events: {
|
|
202
|
+
connections: {
|
|
203
|
+
created: (event) => {
|
|
204
|
+
// event.status is "loading" | "success" | "error"
|
|
205
|
+
// event.payload is { connectionCode, providerId } on success
|
|
206
|
+
if (event.status === "success") {
|
|
207
|
+
router.push(`/integrations/${event.payload!.connectionCode}/done`)
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
checkout: {
|
|
212
|
+
published: (event) => {
|
|
213
|
+
if (event.status === "success") {
|
|
214
|
+
console.log("Checkout published", event.payload)
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
},
|
|
200
218
|
},
|
|
201
219
|
})
|
|
202
220
|
```
|
|
203
221
|
|
|
204
|
-
The
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
### The host owns the moment
|
|
223
|
+
|
|
224
|
+
Providing a handler is also a signal. On init the SDK announces your registered
|
|
225
|
+
handlers to the dashboard, so the dashboard knows the host wants to own those
|
|
226
|
+
moments. For **connection creation**, this means: when a connection is created
|
|
227
|
+
in the embedded dashboard **and** you registered `events.connections.created`,
|
|
228
|
+
the dashboard does **not** show its own success screen or redirect — it hands
|
|
229
|
+
off to your handler and lets you route the user. If you do **not** register the
|
|
230
|
+
handler, the dashboard keeps its built-in success screen unchanged.
|
|
231
|
+
|
|
232
|
+
### Status lifecycle
|
|
233
|
+
|
|
234
|
+
Handlers fire once per state transition: first `status: "loading"` when the
|
|
235
|
+
action starts, then `"success"` or `"error"` once the dashboard's call resolves.
|
|
236
|
+
`event.payload` is only meaningful on `"success"`.
|
|
208
237
|
|
|
209
238
|
## Test mode
|
|
210
239
|
|
|
@@ -262,6 +291,20 @@ content resizes; the SDK applies that height to the iframe and its wrapper. Mess
|
|
|
262
291
|
accepted only from the configured `baseUrl` origin. Your container must allow vertical growth
|
|
263
292
|
(avoid a fixed `height` / `overflow: hidden`) for the effect to be visible.
|
|
264
293
|
|
|
294
|
+
## Overlays (modals & drawers)
|
|
295
|
+
|
|
296
|
+
An embedded overlay is positioned relative to **its iframe**, not the host page — so a
|
|
297
|
+
modal would appear centered to the iframe panel, not the browser viewport. To fix this,
|
|
298
|
+
the embedded dashboard signals the SDK when an overlay opens or closes
|
|
299
|
+
(`embed-event` with `domain: "ui"`, `action: "overlay-open" | "overlay-close"`), and the
|
|
300
|
+
**SDK expands the iframe to the full viewport** (`position: fixed; inset: 0`, top
|
|
301
|
+
`z-index`) for as long as any overlay is open, then restores it. This is automatic — no
|
|
302
|
+
host code required. Signals are ref-counted, so stacked overlays are handled correctly.
|
|
303
|
+
|
|
304
|
+
**Host/iframe contract:** while an overlay is open the iframe covers the whole viewport.
|
|
305
|
+
Ensure no host element needs to sit above it during that time (the SDK uses a maximal
|
|
306
|
+
`z-index`). Messages are accepted only from the configured `baseUrl` origin.
|
|
307
|
+
|
|
265
308
|
## Migrating from 0.x to 1.0
|
|
266
309
|
|
|
267
310
|
`1.0.0` drops the `Yuno` brand prefix from the public API so the SDK reads cleanly
|
package/dist/index.d.ts
CHANGED
|
@@ -37,13 +37,27 @@ interface DashboardTheme {
|
|
|
37
37
|
mode?: "light" | "dark";
|
|
38
38
|
styles?: string;
|
|
39
39
|
}
|
|
40
|
-
type
|
|
41
|
-
|
|
40
|
+
type EmbedEventStatus = "loading" | "success" | "error";
|
|
41
|
+
interface ConnectionCreatedPayload {
|
|
42
|
+
connectionCode: string;
|
|
43
|
+
providerId: string;
|
|
44
|
+
}
|
|
45
|
+
interface ConnectionCreatedEvent {
|
|
46
|
+
status: EmbedEventStatus;
|
|
47
|
+
payload?: ConnectionCreatedPayload;
|
|
48
|
+
}
|
|
42
49
|
interface CheckoutPublishedEvent {
|
|
43
|
-
|
|
44
|
-
status: CheckoutPublishedStatus;
|
|
50
|
+
status: EmbedEventStatus;
|
|
45
51
|
payload?: unknown;
|
|
46
52
|
}
|
|
53
|
+
interface DashboardEvents {
|
|
54
|
+
connections?: {
|
|
55
|
+
created?: (event: ConnectionCreatedEvent) => void | Promise<void>;
|
|
56
|
+
};
|
|
57
|
+
checkout?: {
|
|
58
|
+
published?: (event: CheckoutPublishedEvent) => void | Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
47
61
|
interface DashboardConfig {
|
|
48
62
|
baseUrl: string;
|
|
49
63
|
container: HTMLElement;
|
|
@@ -54,7 +68,7 @@ interface DashboardConfig {
|
|
|
54
68
|
testMode?: boolean;
|
|
55
69
|
onReady?: () => void;
|
|
56
70
|
onSessionExpired?: () => void | Promise<void>;
|
|
57
|
-
|
|
71
|
+
events?: DashboardEvents;
|
|
58
72
|
loading?: HTMLElement;
|
|
59
73
|
autoHeight?: boolean;
|
|
60
74
|
}
|
|
@@ -74,8 +88,10 @@ declare class Dashboard {
|
|
|
74
88
|
private token?;
|
|
75
89
|
private onReadyCallback?;
|
|
76
90
|
private onSessionExpiredCallback?;
|
|
77
|
-
private
|
|
91
|
+
private events?;
|
|
78
92
|
private autoHeight;
|
|
93
|
+
private overlayCount;
|
|
94
|
+
private savedIframeCss;
|
|
79
95
|
constructor(config: DashboardConfig);
|
|
80
96
|
setTheme(theme: DashboardTheme): void;
|
|
81
97
|
setLang(lang: string): void;
|
|
@@ -83,6 +99,11 @@ declare class Dashboard {
|
|
|
83
99
|
navigate(path: string): void;
|
|
84
100
|
setTestMode(enabled: boolean): void;
|
|
85
101
|
destroy(): void;
|
|
102
|
+
private invokeCallback;
|
|
103
|
+
private dispatchEmbedEvent;
|
|
104
|
+
private handleOverlaySignal;
|
|
105
|
+
private expandToViewport;
|
|
106
|
+
private restoreIframe;
|
|
86
107
|
private post;
|
|
87
108
|
private flush;
|
|
88
109
|
private onReady;
|
|
@@ -96,4 +117,4 @@ declare function initDashboard(config: DashboardConfig): Dashboard;
|
|
|
96
117
|
declare function getDashboard(): Dashboard | null;
|
|
97
118
|
declare function destroyDashboard(): void;
|
|
98
119
|
|
|
99
|
-
export { type CheckoutPublishedEvent, type
|
|
120
|
+
export { type CheckoutPublishedEvent, type ConnectionCreatedEvent, type ConnectionCreatedPayload, Dashboard, type DashboardConfig, type DashboardEvents, type DashboardTheme, type EmbedEventStatus, type ModeTokens, type ThemeColors, type ThemeTypography, destroyDashboard, getDashboard, initDashboard };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
// src/dashboard.ts
|
|
2
|
+
var EMBED_EVENT_MESSAGE_TYPE = "yuno-dashboard:embed-event";
|
|
3
|
+
var HOST_CAPABILITIES_MESSAGE_TYPE = "yuno-dashboard:host-capabilities";
|
|
4
|
+
function buildCapabilities(events) {
|
|
5
|
+
const capabilities = {};
|
|
6
|
+
if (!events) return capabilities;
|
|
7
|
+
const groups = events;
|
|
8
|
+
for (const domain of Object.keys(groups)) {
|
|
9
|
+
const group = groups[domain];
|
|
10
|
+
if (!group) continue;
|
|
11
|
+
for (const action of Object.keys(group)) {
|
|
12
|
+
if (typeof group[action] === "function") {
|
|
13
|
+
capabilities[domain] = capabilities[domain] ?? {};
|
|
14
|
+
capabilities[domain][action] = true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return capabilities;
|
|
19
|
+
}
|
|
2
20
|
var Dashboard = class {
|
|
3
21
|
constructor(config) {
|
|
4
22
|
this.overlay = null;
|
|
5
23
|
this.ready = false;
|
|
6
24
|
this.queue = [];
|
|
25
|
+
// Overlay (modal/drawer) mode: while >0 the iframe is expanded to the full
|
|
26
|
+
// viewport so the dashboard's overlay centers to the host page, not the
|
|
27
|
+
// iframe. Ref-counted to survive stacked overlays.
|
|
28
|
+
this.overlayCount = 0;
|
|
29
|
+
this.savedIframeCss = null;
|
|
7
30
|
this.baseUrl = config.baseUrl;
|
|
8
31
|
this.initialTheme = config.theme;
|
|
9
32
|
this.initialLang = config.lang;
|
|
@@ -12,7 +35,7 @@ var Dashboard = class {
|
|
|
12
35
|
this.token = config.token;
|
|
13
36
|
this.onReadyCallback = config.onReady;
|
|
14
37
|
this.onSessionExpiredCallback = config.onSessionExpired;
|
|
15
|
-
this.
|
|
38
|
+
this.events = config.events;
|
|
16
39
|
this.autoHeight = config.autoHeight ?? false;
|
|
17
40
|
this.wrapper = document.createElement("div");
|
|
18
41
|
this.wrapper.style.position = "relative";
|
|
@@ -38,39 +61,14 @@ var Dashboard = class {
|
|
|
38
61
|
return;
|
|
39
62
|
}
|
|
40
63
|
if (e.data?.type === "yuno-dashboard:session-expired") {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
(err) => (
|
|
46
|
-
// eslint-disable-next-line no-console
|
|
47
|
-
console.warn("[yuno-dashboard-sdk] onSessionExpired async callback rejected", err)
|
|
48
|
-
)
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.warn("[yuno-dashboard-sdk] onSessionExpired callback threw", err);
|
|
53
|
-
}
|
|
64
|
+
this.invokeCallback(
|
|
65
|
+
"onSessionExpired",
|
|
66
|
+
() => this.onSessionExpiredCallback?.()
|
|
67
|
+
);
|
|
54
68
|
return;
|
|
55
69
|
}
|
|
56
|
-
if (e.data?.type ===
|
|
57
|
-
|
|
58
|
-
const result = this.onCheckoutPublishedCallback?.({
|
|
59
|
-
source: e.data?.source,
|
|
60
|
-
status: e.data?.status,
|
|
61
|
-
payload: e.data?.payload
|
|
62
|
-
});
|
|
63
|
-
if (result && typeof result.catch === "function") {
|
|
64
|
-
result.catch(
|
|
65
|
-
(err) => (
|
|
66
|
-
// eslint-disable-next-line no-console
|
|
67
|
-
console.warn("[yuno-dashboard-sdk] onCheckoutPublished async callback rejected", err)
|
|
68
|
-
)
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
} catch (err) {
|
|
72
|
-
console.warn("[yuno-dashboard-sdk] onCheckoutPublished callback threw", err);
|
|
73
|
-
}
|
|
70
|
+
if (e.data?.type === EMBED_EVENT_MESSAGE_TYPE) {
|
|
71
|
+
this.dispatchEmbedEvent(e.data);
|
|
74
72
|
return;
|
|
75
73
|
}
|
|
76
74
|
if (e.data?.action === "ready") {
|
|
@@ -115,6 +113,68 @@ var Dashboard = class {
|
|
|
115
113
|
this.wrapper.remove();
|
|
116
114
|
this.queue = [];
|
|
117
115
|
this.ready = false;
|
|
116
|
+
this.overlayCount = 0;
|
|
117
|
+
this.savedIframeCss = null;
|
|
118
|
+
}
|
|
119
|
+
// Runs a host callback, swallowing sync throws and async rejections so a
|
|
120
|
+
// misbehaving host handler can never break the SDK's message loop.
|
|
121
|
+
invokeCallback(name, run) {
|
|
122
|
+
try {
|
|
123
|
+
const result = run();
|
|
124
|
+
if (result && typeof result.catch === "function") {
|
|
125
|
+
result.catch(
|
|
126
|
+
(err) => (
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.warn(`[yuno-dashboard-sdk] ${name} async callback rejected`, err)
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.warn(`[yuno-dashboard-sdk] ${name} callback threw`, err);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Routes an embed-event envelope to the host's events[domain][action] handler.
|
|
137
|
+
dispatchEmbedEvent(data) {
|
|
138
|
+
const domain = data?.domain;
|
|
139
|
+
const action = data?.action;
|
|
140
|
+
if (typeof domain !== "string" || typeof action !== "string") return;
|
|
141
|
+
if (domain === "ui") {
|
|
142
|
+
this.handleOverlaySignal(action);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const groups = this.events;
|
|
146
|
+
const handler = groups?.[domain]?.[action];
|
|
147
|
+
if (!handler) return;
|
|
148
|
+
this.invokeCallback(
|
|
149
|
+
`events.${domain}.${action}`,
|
|
150
|
+
() => handler({ status: data?.status, payload: data?.payload })
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
// Expands the iframe to the full viewport on the first open and restores it
|
|
154
|
+
// once the last overlay closes (ref-counted for stacked overlays).
|
|
155
|
+
handleOverlaySignal(action) {
|
|
156
|
+
if (action === "overlay-open") {
|
|
157
|
+
if (this.overlayCount === 0) this.expandToViewport();
|
|
158
|
+
this.overlayCount += 1;
|
|
159
|
+
} else if (action === "overlay-close") {
|
|
160
|
+
if (this.overlayCount === 0) return;
|
|
161
|
+
this.overlayCount -= 1;
|
|
162
|
+
if (this.overlayCount === 0) this.restoreIframe();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
expandToViewport() {
|
|
166
|
+
this.savedIframeCss = this.iframe.style.cssText;
|
|
167
|
+
Object.assign(this.iframe.style, {
|
|
168
|
+
position: "fixed",
|
|
169
|
+
inset: "0",
|
|
170
|
+
width: "100%",
|
|
171
|
+
height: "100%",
|
|
172
|
+
zIndex: "2147483647"
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
restoreIframe() {
|
|
176
|
+
this.iframe.style.cssText = this.savedIframeCss ?? "";
|
|
177
|
+
this.savedIframeCss = null;
|
|
118
178
|
}
|
|
119
179
|
post(message) {
|
|
120
180
|
if (this.ready && this.iframe.contentWindow) {
|
|
@@ -133,6 +193,10 @@ var Dashboard = class {
|
|
|
133
193
|
}
|
|
134
194
|
onReady() {
|
|
135
195
|
this.ready = true;
|
|
196
|
+
const capabilities = buildCapabilities(this.events);
|
|
197
|
+
if (Object.keys(capabilities).length > 0) {
|
|
198
|
+
this.post({ type: HOST_CAPABILITIES_MESSAGE_TYPE, capabilities });
|
|
199
|
+
}
|
|
136
200
|
if (this.token) {
|
|
137
201
|
this.post({ action: "setToken", token: this.token });
|
|
138
202
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dashboard.ts","../src/singleton.ts"],"sourcesContent":["import type { DashboardTheme, DashboardConfig, CheckoutPublishedEvent } from \"./types\";\n\nexport class Dashboard {\n private iframe: HTMLIFrameElement;\n private wrapper: HTMLDivElement;\n private overlay: HTMLElement | null = null;\n private baseUrl: string;\n private ready = false;\n private queue: Record<string, unknown>[] = [];\n private messageHandler: (e: MessageEvent) => void;\n private initialTheme?: DashboardTheme;\n private initialLang?: string;\n private initialPath: string;\n private initialTestMode?: boolean;\n private token?: string;\n private onReadyCallback?: () => void;\n private onSessionExpiredCallback?: () => void | Promise<void>;\n private onCheckoutPublishedCallback?: (event: CheckoutPublishedEvent) => void | Promise<void>;\n private autoHeight: boolean;\n\n constructor(config: DashboardConfig) {\n this.baseUrl = config.baseUrl;\n this.initialTheme = config.theme;\n this.initialLang = config.lang;\n this.initialPath = config.path ?? \"/\";\n this.initialTestMode = config.testMode;\n this.token = config.token;\n this.onReadyCallback = config.onReady;\n this.onSessionExpiredCallback = config.onSessionExpired;\n this.onCheckoutPublishedCallback = config.onCheckoutPublished;\n this.autoHeight = config.autoHeight ?? false;\n\n this.wrapper = document.createElement(\"div\");\n this.wrapper.style.position = \"relative\";\n this.wrapper.style.width = \"100%\";\n this.wrapper.style.height = \"100%\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.style.width = \"100%\";\n this.iframe.style.height = \"100%\";\n this.iframe.style.border = \"none\";\n this.iframe.title = \"Yuno Dashboard\";\n this.iframe.src = this.buildSrc();\n\n this.wrapper.appendChild(this.iframe);\n\n this.overlay = config.loading instanceof HTMLElement\n ? config.loading\n : this.createDefaultOverlay();\n this.overlay.style.position = \"absolute\";\n this.overlay.style.inset = \"0\";\n this.overlay.style.zIndex = \"1\";\n this.wrapper.appendChild(this.overlay);\n\n this.messageHandler = (e: MessageEvent) => {\n if (e.origin !== this.baseUrl) return;\n if (\n this.autoHeight &&\n e.data?.action === \"resize\" &&\n typeof e.data?.height === \"number\"\n ) {\n this.iframe.style.height = `${e.data.height}px`;\n this.wrapper.style.height = `${e.data.height}px`;\n return;\n }\n if (e.data?.type === \"yuno-dashboard:session-expired\") {\n try {\n const result = this.onSessionExpiredCallback?.();\n if (result && typeof (result as Promise<unknown>).catch === \"function\") {\n (result as Promise<unknown>).catch((err) =>\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onSessionExpired async callback rejected\", err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onSessionExpired callback threw\", err);\n }\n return;\n }\n if (e.data?.type === \"yuno-dashboard:checkout-published\") {\n try {\n const result = this.onCheckoutPublishedCallback?.({\n source: e.data?.source,\n status: e.data?.status,\n payload: e.data?.payload,\n });\n if (result && typeof (result as Promise<unknown>).catch === \"function\") {\n (result as Promise<unknown>).catch((err) =>\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onCheckoutPublished async callback rejected\", err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onCheckoutPublished callback threw\", err);\n }\n return;\n }\n if (e.data?.action === \"ready\") {\n this.onReady();\n }\n if (e.data?.action === \"tokenApplied\") {\n this.onTokenApplied();\n }\n };\n window.addEventListener(\"message\", this.messageHandler);\n\n config.container.appendChild(this.wrapper);\n }\n\n setTheme(theme: DashboardTheme): void {\n if (theme.tokens || theme.typography) {\n const themePayload: Record<string, unknown> = {};\n if (theme.tokens) themePayload.colors = theme.tokens;\n if (theme.typography) themePayload.typography = theme.typography;\n this.post({ action: \"setTheme\", theme: themePayload });\n }\n if (theme.mode) {\n this.post({ action: \"setMode\", mode: theme.mode });\n }\n if (theme.styles !== undefined) {\n this.post({ action: \"setExternalStyles\", css: theme.styles });\n }\n }\n\n setLang(lang: string): void {\n this.post({ action: \"setLang\", lang });\n }\n\n setToken(token: string): void {\n this.token = token;\n this.post({ action: \"setToken\", token });\n }\n\n navigate(path: string): void {\n this.post({ action: \"navigate\", path });\n }\n\n setTestMode(enabled: boolean): void {\n this.post({ action: \"setTestMode\", testMode: enabled });\n }\n\n destroy(): void {\n window.removeEventListener(\"message\", this.messageHandler);\n this.wrapper.remove();\n this.queue = [];\n this.ready = false;\n }\n\n private post(message: Record<string, unknown>): void {\n if (this.ready && this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n } else {\n this.queue.push(message);\n }\n }\n\n private flush(): void {\n const pending = this.queue.splice(0);\n for (const message of pending) {\n if (this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n }\n }\n }\n\n private onReady(): void {\n this.ready = true;\n if (this.token) {\n this.post({ action: \"setToken\", token: this.token });\n }\n if (this.initialTheme) {\n this.setTheme(this.initialTheme);\n }\n if (this.initialLang) {\n this.setLang(this.initialLang);\n }\n this.flush();\n }\n\n private onTokenApplied(): void {\n setTimeout(() => {\n this.hideOverlay();\n this.onReadyCallback?.();\n }, 1000);\n }\n\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.transition = \"opacity 0.3s\";\n this.overlay.style.opacity = \"0\";\n setTimeout(() => this.overlay?.remove(), 300);\n }\n\n private createDefaultOverlay(): HTMLElement {\n const overlay = document.createElement(\"div\");\n overlay.style.background = \"#ffffff\";\n overlay.style.display = \"flex\";\n overlay.style.alignItems = \"center\";\n overlay.style.justifyContent = \"center\";\n\n const spinner = document.createElement(\"div\");\n spinner.style.width = \"32px\";\n spinner.style.height = \"32px\";\n spinner.style.border = \"3px solid #e0e0e0\";\n spinner.style.borderTopColor = \"#666\";\n spinner.style.borderRadius = \"50%\";\n spinner.style.animation = \"yuno-spin 0.8s linear infinite\";\n\n const style = document.createElement(\"style\");\n style.textContent = \"@keyframes yuno-spin { to { transform: rotate(360deg); } }\";\n\n overlay.appendChild(style);\n overlay.appendChild(spinner);\n return overlay;\n }\n\n private buildSrc(): string {\n const params = new URLSearchParams({\n embed: \"true\",\n theme: this.initialTheme?.mode ?? \"light\",\n lang: this.initialLang ?? \"en\",\n });\n if (this.initialTestMode !== undefined) {\n params.set(\"test\", String(this.initialTestMode));\n }\n const path = !this.initialPath || this.initialPath === \"/\" ? \"/\" : this.initialPath;\n return `${this.baseUrl}${path}?${params.toString()}`;\n }\n}\n","import { Dashboard } from \"./dashboard\";\nimport type { DashboardConfig } from \"./types\";\n\nlet instance: Dashboard | null = null;\n\nexport function initDashboard(config: DashboardConfig): Dashboard {\n if (instance) {\n instance.destroy();\n }\n instance = new Dashboard(config);\n return instance;\n}\n\nexport function getDashboard(): Dashboard | null {\n return instance;\n}\n\nexport function destroyDashboard(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/dashboard.ts","../src/singleton.ts"],"sourcesContent":["import type { DashboardTheme, DashboardConfig, DashboardEvents } from \"./types\";\n\nconst EMBED_EVENT_MESSAGE_TYPE = \"yuno-dashboard:embed-event\";\nconst HOST_CAPABILITIES_MESSAGE_TYPE = \"yuno-dashboard:host-capabilities\";\n\ntype EmbedEventHandler = (event: {\n status?: unknown;\n payload?: unknown;\n}) => void | Promise<void>;\n\n// Build the capability map the dashboard reads to decide which actions the\n// host owns: { connections: { created: true }, ... } for every provided handler.\nfunction buildCapabilities(\n events?: DashboardEvents,\n): Record<string, Record<string, boolean>> {\n const capabilities: Record<string, Record<string, boolean>> = {};\n if (!events) return capabilities;\n const groups = events as Record<string, Record<string, unknown> | undefined>;\n for (const domain of Object.keys(groups)) {\n const group = groups[domain];\n if (!group) continue;\n for (const action of Object.keys(group)) {\n if (typeof group[action] === \"function\") {\n capabilities[domain] = capabilities[domain] ?? {};\n capabilities[domain][action] = true;\n }\n }\n }\n return capabilities;\n}\n\nexport class Dashboard {\n private iframe: HTMLIFrameElement;\n private wrapper: HTMLDivElement;\n private overlay: HTMLElement | null = null;\n private baseUrl: string;\n private ready = false;\n private queue: Record<string, unknown>[] = [];\n private messageHandler: (e: MessageEvent) => void;\n private initialTheme?: DashboardTheme;\n private initialLang?: string;\n private initialPath: string;\n private initialTestMode?: boolean;\n private token?: string;\n private onReadyCallback?: () => void;\n private onSessionExpiredCallback?: () => void | Promise<void>;\n private events?: DashboardEvents;\n private autoHeight: boolean;\n // Overlay (modal/drawer) mode: while >0 the iframe is expanded to the full\n // viewport so the dashboard's overlay centers to the host page, not the\n // iframe. Ref-counted to survive stacked overlays.\n private overlayCount = 0;\n private savedIframeCss: string | null = null;\n\n constructor(config: DashboardConfig) {\n this.baseUrl = config.baseUrl;\n this.initialTheme = config.theme;\n this.initialLang = config.lang;\n this.initialPath = config.path ?? \"/\";\n this.initialTestMode = config.testMode;\n this.token = config.token;\n this.onReadyCallback = config.onReady;\n this.onSessionExpiredCallback = config.onSessionExpired;\n this.events = config.events;\n this.autoHeight = config.autoHeight ?? false;\n\n this.wrapper = document.createElement(\"div\");\n this.wrapper.style.position = \"relative\";\n this.wrapper.style.width = \"100%\";\n this.wrapper.style.height = \"100%\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.style.width = \"100%\";\n this.iframe.style.height = \"100%\";\n this.iframe.style.border = \"none\";\n this.iframe.title = \"Yuno Dashboard\";\n this.iframe.src = this.buildSrc();\n\n this.wrapper.appendChild(this.iframe);\n\n this.overlay = config.loading instanceof HTMLElement\n ? config.loading\n : this.createDefaultOverlay();\n this.overlay.style.position = \"absolute\";\n this.overlay.style.inset = \"0\";\n this.overlay.style.zIndex = \"1\";\n this.wrapper.appendChild(this.overlay);\n\n this.messageHandler = (e: MessageEvent) => {\n if (e.origin !== this.baseUrl) return;\n if (\n this.autoHeight &&\n e.data?.action === \"resize\" &&\n typeof e.data?.height === \"number\"\n ) {\n this.iframe.style.height = `${e.data.height}px`;\n this.wrapper.style.height = `${e.data.height}px`;\n return;\n }\n if (e.data?.type === \"yuno-dashboard:session-expired\") {\n this.invokeCallback(\"onSessionExpired\", () =>\n this.onSessionExpiredCallback?.(),\n );\n return;\n }\n if (e.data?.type === EMBED_EVENT_MESSAGE_TYPE) {\n this.dispatchEmbedEvent(e.data);\n return;\n }\n if (e.data?.action === \"ready\") {\n this.onReady();\n }\n if (e.data?.action === \"tokenApplied\") {\n this.onTokenApplied();\n }\n };\n window.addEventListener(\"message\", this.messageHandler);\n\n config.container.appendChild(this.wrapper);\n }\n\n setTheme(theme: DashboardTheme): void {\n if (theme.tokens || theme.typography) {\n const themePayload: Record<string, unknown> = {};\n if (theme.tokens) themePayload.colors = theme.tokens;\n if (theme.typography) themePayload.typography = theme.typography;\n this.post({ action: \"setTheme\", theme: themePayload });\n }\n if (theme.mode) {\n this.post({ action: \"setMode\", mode: theme.mode });\n }\n if (theme.styles !== undefined) {\n this.post({ action: \"setExternalStyles\", css: theme.styles });\n }\n }\n\n setLang(lang: string): void {\n this.post({ action: \"setLang\", lang });\n }\n\n setToken(token: string): void {\n this.token = token;\n this.post({ action: \"setToken\", token });\n }\n\n navigate(path: string): void {\n this.post({ action: \"navigate\", path });\n }\n\n setTestMode(enabled: boolean): void {\n this.post({ action: \"setTestMode\", testMode: enabled });\n }\n\n destroy(): void {\n window.removeEventListener(\"message\", this.messageHandler);\n this.wrapper.remove();\n this.queue = [];\n this.ready = false;\n this.overlayCount = 0;\n this.savedIframeCss = null;\n }\n\n // Runs a host callback, swallowing sync throws and async rejections so a\n // misbehaving host handler can never break the SDK's message loop.\n private invokeCallback(\n name: string,\n run: () => void | Promise<void> | undefined,\n ): void {\n try {\n const result = run();\n if (result && typeof (result as Promise<unknown>).catch === \"function\") {\n (result as Promise<unknown>).catch((err) =>\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} async callback rejected`, err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} callback threw`, err);\n }\n }\n\n // Routes an embed-event envelope to the host's events[domain][action] handler.\n private dispatchEmbedEvent(data: Record<string, unknown>): void {\n const domain = data?.domain;\n const action = data?.action;\n if (typeof domain !== \"string\" || typeof action !== \"string\") return;\n\n // UI signals are handled by the SDK itself (iframe sizing), not routed to\n // a host callback.\n if (domain === \"ui\") {\n this.handleOverlaySignal(action);\n return;\n }\n\n const groups = this.events as\n | Record<string, Record<string, EmbedEventHandler | undefined> | undefined>\n | undefined;\n const handler = groups?.[domain]?.[action];\n if (!handler) return;\n\n this.invokeCallback(`events.${domain}.${action}`, () =>\n handler({ status: data?.status, payload: data?.payload }),\n );\n }\n\n // Expands the iframe to the full viewport on the first open and restores it\n // once the last overlay closes (ref-counted for stacked overlays).\n private handleOverlaySignal(action: string): void {\n if (action === \"overlay-open\") {\n if (this.overlayCount === 0) this.expandToViewport();\n this.overlayCount += 1;\n } else if (action === \"overlay-close\") {\n if (this.overlayCount === 0) return;\n this.overlayCount -= 1;\n if (this.overlayCount === 0) this.restoreIframe();\n }\n }\n\n private expandToViewport(): void {\n this.savedIframeCss = this.iframe.style.cssText;\n Object.assign(this.iframe.style, {\n position: \"fixed\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"2147483647\",\n });\n }\n\n private restoreIframe(): void {\n this.iframe.style.cssText = this.savedIframeCss ?? \"\";\n this.savedIframeCss = null;\n }\n\n private post(message: Record<string, unknown>): void {\n if (this.ready && this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n } else {\n this.queue.push(message);\n }\n }\n\n private flush(): void {\n const pending = this.queue.splice(0);\n for (const message of pending) {\n if (this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n }\n }\n }\n\n private onReady(): void {\n this.ready = true;\n const capabilities = buildCapabilities(this.events);\n if (Object.keys(capabilities).length > 0) {\n this.post({ type: HOST_CAPABILITIES_MESSAGE_TYPE, capabilities });\n }\n if (this.token) {\n this.post({ action: \"setToken\", token: this.token });\n }\n if (this.initialTheme) {\n this.setTheme(this.initialTheme);\n }\n if (this.initialLang) {\n this.setLang(this.initialLang);\n }\n this.flush();\n }\n\n private onTokenApplied(): void {\n setTimeout(() => {\n this.hideOverlay();\n this.onReadyCallback?.();\n }, 1000);\n }\n\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.transition = \"opacity 0.3s\";\n this.overlay.style.opacity = \"0\";\n setTimeout(() => this.overlay?.remove(), 300);\n }\n\n private createDefaultOverlay(): HTMLElement {\n const overlay = document.createElement(\"div\");\n overlay.style.background = \"#ffffff\";\n overlay.style.display = \"flex\";\n overlay.style.alignItems = \"center\";\n overlay.style.justifyContent = \"center\";\n\n const spinner = document.createElement(\"div\");\n spinner.style.width = \"32px\";\n spinner.style.height = \"32px\";\n spinner.style.border = \"3px solid #e0e0e0\";\n spinner.style.borderTopColor = \"#666\";\n spinner.style.borderRadius = \"50%\";\n spinner.style.animation = \"yuno-spin 0.8s linear infinite\";\n\n const style = document.createElement(\"style\");\n style.textContent = \"@keyframes yuno-spin { to { transform: rotate(360deg); } }\";\n\n overlay.appendChild(style);\n overlay.appendChild(spinner);\n return overlay;\n }\n\n private buildSrc(): string {\n const params = new URLSearchParams({\n embed: \"true\",\n theme: this.initialTheme?.mode ?? \"light\",\n lang: this.initialLang ?? \"en\",\n });\n if (this.initialTestMode !== undefined) {\n params.set(\"test\", String(this.initialTestMode));\n }\n const path = !this.initialPath || this.initialPath === \"/\" ? \"/\" : this.initialPath;\n return `${this.baseUrl}${path}?${params.toString()}`;\n }\n}\n","import { Dashboard } from \"./dashboard\";\nimport type { DashboardConfig } from \"./types\";\n\nlet instance: Dashboard | null = null;\n\nexport function initDashboard(config: DashboardConfig): Dashboard {\n if (instance) {\n instance.destroy();\n }\n instance = new Dashboard(config);\n return instance;\n}\n\nexport function getDashboard(): Dashboard | null {\n return instance;\n}\n\nexport function destroyDashboard(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n"],"mappings":"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","names":[]}
|