@goodo/platform-plugin 0.1.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.
- package/dist/communicator-BF77R7Qs.js +184 -0
- package/dist/communicator-BF77R7Qs.js.map +1 -0
- package/dist/communicator-DQZOTxOF.cjs +2 -0
- package/dist/communicator-DQZOTxOF.cjs.map +1 -0
- package/dist/communicator.d.ts +121 -0
- package/dist/communicator.d.ts.map +1 -0
- package/dist/host-sdk.d.ts +31 -0
- package/dist/host-sdk.d.ts.map +1 -0
- package/dist/host.cjs +2 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.ts +5 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +81 -0
- package/dist/host.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/widget-sdk.d.ts +28 -0
- package/dist/widget-sdk.d.ts.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
var T = Object.defineProperty;
|
|
2
|
+
var P = (r, t, e) => t in r ? T(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
+
var h = (r, t, e) => P(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
class M {
|
|
5
|
+
constructor(t, e = typeof window < "u" ? window : {}, s = 5e3) {
|
|
6
|
+
h(this, "target");
|
|
7
|
+
h(this, "host");
|
|
8
|
+
h(this, "pendingRequests", /* @__PURE__ */ new Map());
|
|
9
|
+
h(this, "handlers", /* @__PURE__ */ new Map());
|
|
10
|
+
h(this, "timeoutMs");
|
|
11
|
+
h(this, "idCounter", 0);
|
|
12
|
+
h(this, "handleIncomingMessage", async (t) => {
|
|
13
|
+
if (this.target && t.source !== this.target)
|
|
14
|
+
return;
|
|
15
|
+
const e = t.data;
|
|
16
|
+
if (!(!e || !e.id || !e.type || !e.action)) {
|
|
17
|
+
if (e.type === "response") {
|
|
18
|
+
const s = this.pendingRequests.get(e.id);
|
|
19
|
+
s && (clearTimeout(s.timer), this.pendingRequests.delete(e.id), e.error ? s.reject(new Error(e.error)) : s.resolve(e.payload));
|
|
20
|
+
} else if (e.type === "request") {
|
|
21
|
+
const s = this.handlers.get(e.action), l = (n, i) => {
|
|
22
|
+
const o = {
|
|
23
|
+
id: e.id,
|
|
24
|
+
type: "response",
|
|
25
|
+
action: e.action,
|
|
26
|
+
payload: n,
|
|
27
|
+
error: i
|
|
28
|
+
};
|
|
29
|
+
this.target.postMessage(o, "*");
|
|
30
|
+
};
|
|
31
|
+
if (!s || s.size === 0) {
|
|
32
|
+
l(void 0, `No handler found for action: ${e.action}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const n = await Promise.allSettled(
|
|
37
|
+
Array.from(s).map((o) => Promise.resolve().then(() => o(e.payload)))
|
|
38
|
+
), i = n.filter(
|
|
39
|
+
(o) => o.status === "fulfilled"
|
|
40
|
+
);
|
|
41
|
+
if (i.length > 0) {
|
|
42
|
+
const o = i.find((d) => d.value !== void 0), a = o !== void 0 ? o.value : i[0].value;
|
|
43
|
+
l(a);
|
|
44
|
+
} else {
|
|
45
|
+
const o = n.find(
|
|
46
|
+
(f) => f.status === "rejected"
|
|
47
|
+
), a = o == null ? void 0 : o.reason, d = typeof a == "string" ? a : (a == null ? void 0 : a.message) || "Unknown error handling request";
|
|
48
|
+
l(void 0, d);
|
|
49
|
+
}
|
|
50
|
+
} catch (n) {
|
|
51
|
+
const i = typeof n == "string" ? n : (n == null ? void 0 : n.message) || "Unknown error handling request";
|
|
52
|
+
l(void 0, i);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.target = t, this.host = e, this.timeoutMs = s, this.host.addEventListener("message", this.handleIncomingMessage);
|
|
58
|
+
}
|
|
59
|
+
generateId() {
|
|
60
|
+
return `${Date.now()}-${++this.idCounter}-${Math.random().toString(36).substring(2, 9)}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Registers a handler for an incoming request action.
|
|
64
|
+
* Returns a function to unregister the handler.
|
|
65
|
+
*/
|
|
66
|
+
registerHandler(t, e) {
|
|
67
|
+
return this.handlers.has(t) || this.handlers.set(t, /* @__PURE__ */ new Set()), this.handlers.get(t).add(e), () => {
|
|
68
|
+
const s = this.handlers.get(t);
|
|
69
|
+
s && (s.delete(e), s.size === 0 && this.handlers.delete(t));
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Sends a request and waits for the response.
|
|
74
|
+
*/
|
|
75
|
+
sendRequest(t, e, s) {
|
|
76
|
+
return new Promise((l, n) => {
|
|
77
|
+
const i = this.generateId(), o = {
|
|
78
|
+
id: i,
|
|
79
|
+
type: "request",
|
|
80
|
+
action: t,
|
|
81
|
+
payload: e
|
|
82
|
+
}, a = setTimeout(() => {
|
|
83
|
+
this.pendingRequests.delete(i), n(new Error(`Timeout waiting for response to action: ${t}`));
|
|
84
|
+
}, s || this.timeoutMs);
|
|
85
|
+
this.pendingRequests.set(i, { resolve: l, reject: n, timer: a }), this.target.postMessage(o, "*");
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
destroy() {
|
|
89
|
+
this.host.removeEventListener("message", this.handleIncomingMessage), this.pendingRequests.forEach((t) => {
|
|
90
|
+
clearTimeout(t.timer), t.reject(new Error("Communicator was destroyed before response was received."));
|
|
91
|
+
}), this.pendingRequests.clear(), this.handlers.clear();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const v = "__GOOD_PLUGIN_PING__", E = "__GOOD_PLUGIN_PONG__";
|
|
95
|
+
function I(r, t = {}) {
|
|
96
|
+
const {
|
|
97
|
+
timeoutMs: e = 1e4,
|
|
98
|
+
intervalMs: s = 100,
|
|
99
|
+
targetOrigin: l = "*",
|
|
100
|
+
allowedOrigins: n,
|
|
101
|
+
host: i = typeof window < "u" ? window : {},
|
|
102
|
+
communicatorTimeoutMs: o = 5e3
|
|
103
|
+
} = t;
|
|
104
|
+
return new Promise((a, d) => {
|
|
105
|
+
if (!r)
|
|
106
|
+
return d(new Error("Cannot ping an invalid target Window."));
|
|
107
|
+
const f = `handshake-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
108
|
+
let m = null, u = null;
|
|
109
|
+
const c = () => {
|
|
110
|
+
m !== null && (clearInterval(m), m = null), u !== null && (clearTimeout(u), u = null), i && typeof i.removeEventListener == "function" && i.removeEventListener("message", p);
|
|
111
|
+
}, p = (g) => {
|
|
112
|
+
if (g.source !== r || n && n.length > 0 && !n.includes(g.origin))
|
|
113
|
+
return;
|
|
114
|
+
const y = g.data;
|
|
115
|
+
y && typeof y == "object" && y.type === E && y.id === f && (c(), a(new M(r, i, o)));
|
|
116
|
+
};
|
|
117
|
+
i && typeof i.addEventListener == "function" && i.addEventListener("message", p);
|
|
118
|
+
const w = () => {
|
|
119
|
+
try {
|
|
120
|
+
if (r.closed) {
|
|
121
|
+
c(), d(new Error("Target window was closed before handshake completed."));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const g = {
|
|
125
|
+
type: v,
|
|
126
|
+
id: f
|
|
127
|
+
};
|
|
128
|
+
r.postMessage(g, l);
|
|
129
|
+
} catch (g) {
|
|
130
|
+
if (r.closed) {
|
|
131
|
+
c(), d(new Error("Target window was closed before handshake completed."));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.warn("Transient error during handshake ping attempt, retrying...", g);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
w(), m = setInterval(w, s), e > 0 && (u = setTimeout(() => {
|
|
138
|
+
c(), d(new Error(`Handshake ping timed out after ${e}ms waiting for target window.`));
|
|
139
|
+
}, e));
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function L(r = {}) {
|
|
143
|
+
const {
|
|
144
|
+
timeoutMs: t = 5e3,
|
|
145
|
+
targetOrigin: e = "*",
|
|
146
|
+
allowedOrigins: s,
|
|
147
|
+
expectedSource: l,
|
|
148
|
+
host: n = typeof window < "u" ? window : {},
|
|
149
|
+
communicatorTimeoutMs: i = 5e3
|
|
150
|
+
} = r;
|
|
151
|
+
return new Promise((o, a) => {
|
|
152
|
+
let d = null;
|
|
153
|
+
const f = () => {
|
|
154
|
+
d !== null && (clearTimeout(d), d = null), n && typeof n.removeEventListener == "function" && n.removeEventListener("message", m);
|
|
155
|
+
}, m = (u) => {
|
|
156
|
+
if (l && u.source !== l || !u.source || s && s.length > 0 && !s.includes(u.origin))
|
|
157
|
+
return;
|
|
158
|
+
const c = u.data;
|
|
159
|
+
if (c && typeof c == "object" && c.type === v && c.id) {
|
|
160
|
+
const p = u.source, w = {
|
|
161
|
+
type: E,
|
|
162
|
+
id: c.id
|
|
163
|
+
};
|
|
164
|
+
try {
|
|
165
|
+
p.postMessage(w, e);
|
|
166
|
+
} catch (g) {
|
|
167
|
+
console.warn("Failed to reply with pong to ping source:", g);
|
|
168
|
+
}
|
|
169
|
+
f(), o(new M(p, n, i));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
n && typeof n.addEventListener == "function" && n.addEventListener("message", m), t && t > 0 && (d = setTimeout(() => {
|
|
173
|
+
f(), a(new Error(`Handshake pong timed out after ${t}ms waiting for incoming ping.`));
|
|
174
|
+
}, t));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
M as C,
|
|
179
|
+
v as P,
|
|
180
|
+
E as a,
|
|
181
|
+
L as b,
|
|
182
|
+
I as p
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=communicator-BF77R7Qs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"communicator-BF77R7Qs.js","sources":["../src/communicator.ts"],"sourcesContent":["import { Message, ActionMap } from './types';\n\nexport class Communicator {\n private target: Window;\n private host: Window;\n private pendingRequests: Map<string, { resolve: (val: any) => void; reject: (err: any) => void; timer: any }> = new Map();\n private handlers: Map<keyof ActionMap, Set<(payload: any) => Promise<any> | any>> = new Map();\n private timeoutMs: number;\n private idCounter = 0;\n\n constructor(\n target: Window,\n host: Window = typeof window !== 'undefined' ? window : ({} as Window),\n timeoutMs: number = 5000\n ) {\n this.target = target;\n this.host = host;\n this.timeoutMs = timeoutMs;\n this.host.addEventListener('message', this.handleIncomingMessage);\n }\n\n private handleIncomingMessage = async (event: MessageEvent) => {\n if (this.target && event.source !== this.target) {\n return;\n }\n\n const message = event.data as Message;\n\n if (!message || !message.id || !message.type || !message.action) {\n return;\n }\n\n if (message.type === 'response') {\n const pending = this.pendingRequests.get(message.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(message.id);\n if (message.error) {\n pending.reject(new Error(message.error));\n } else {\n pending.resolve(message.payload);\n }\n }\n } else if (message.type === 'request') {\n const actionHandlers = this.handlers.get(message.action);\n\n const sendResponse = (payload?: any, error?: string) => {\n const responseMessage: Message<typeof message.action> = {\n id: message.id,\n type: 'response',\n action: message.action,\n payload,\n error\n };\n // Use event.source if available for better targeting, otherwise fallback to targetWindow\n // const target = (event.source as Window) || this.target;\n const target = this.target;\n\n target.postMessage(responseMessage, '*');\n };\n\n if (!actionHandlers || actionHandlers.size === 0) {\n sendResponse(undefined, `No handler found for action: ${message.action}`);\n return;\n }\n\n try {\n const settled = await Promise.allSettled(\n Array.from(actionHandlers).map((h) => Promise.resolve().then(() => h(message.payload)))\n );\n\n const fulfilled = settled.filter(\n (r): r is PromiseFulfilledResult<any> => r.status === 'fulfilled'\n );\n\n if (fulfilled.length > 0) {\n // Find the first non-undefined result among fulfilled handlers, or default to the first fulfilled result\n const nonUndefined = fulfilled.find((r) => r.value !== undefined);\n const responsePayload = nonUndefined !== undefined ? nonUndefined.value : fulfilled[0].value;\n sendResponse(responsePayload);\n } else {\n // All handlers rejected\n const firstRejected = settled.find(\n (r): r is PromiseRejectedResult => r.status === 'rejected'\n );\n const err = firstRejected?.reason;\n const errorMessage = typeof err === 'string' ? err : err?.message || 'Unknown error handling request';\n sendResponse(undefined, errorMessage);\n }\n } catch (err: any) {\n const errorMessage = typeof err === 'string' ? err : err?.message || 'Unknown error handling request';\n sendResponse(undefined, errorMessage);\n }\n }\n };\n\n private generateId(): string {\n return `${Date.now()}-${++this.idCounter}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Registers a handler for an incoming request action.\n * Returns a function to unregister the handler.\n */\n public registerHandler<A extends keyof ActionMap>(\n action: A,\n handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>\n ): () => void {\n if (!this.handlers.has(action)) {\n this.handlers.set(action, new Set());\n }\n this.handlers.get(action)!.add(handler);\n\n return () => {\n const handlersSet = this.handlers.get(action);\n if (handlersSet) {\n handlersSet.delete(handler);\n if (handlersSet.size === 0) {\n this.handlers.delete(action);\n }\n }\n };\n }\n\n /**\n * Sends a request and waits for the response.\n */\n public sendRequest<A extends keyof ActionMap>(\n action: A,\n payload?: Parameters<ActionMap[A]>[0],\n timeout?: number\n ): Promise<ReturnType<ActionMap[A]>> {\n return new Promise((resolve, reject) => {\n const id = this.generateId();\n const message: Message<A> = {\n id,\n type: 'request',\n action,\n payload\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Timeout waiting for response to action: ${action}`));\n }, timeout || this.timeoutMs);\n\n this.pendingRequests.set(id, { resolve, reject, timer });\n\n this.target.postMessage(message, '*');\n });\n }\n\n public destroy() {\n this.host.removeEventListener('message', this.handleIncomingMessage);\n this.pendingRequests.forEach((req) => {\n clearTimeout(req.timer);\n req.reject(new Error('Communicator was destroyed before response was received.'));\n });\n this.pendingRequests.clear();\n this.handlers.clear();\n }\n}\n\nexport const PING_TOKEN = '__GOOD_PLUGIN_PING__';\n\nexport const PONG_TOKEN = '__GOOD_PLUGIN_PONG__';\n\nexport interface HandshakePingMessage {\n type: typeof PING_TOKEN;\n id: string;\n payload?: any;\n}\n\nexport interface HandshakePongMessage {\n type: typeof PONG_TOKEN;\n id: string;\n payload?: any;\n}\n\nexport interface PingOptions {\n /**\n * Maximum time to wait for a pong response before rejecting (in milliseconds).\n * Set to 0 or negative to disable timeout.\n * @default 10000\n */\n timeoutMs?: number;\n /**\n * Interval between handshake ping attempts (in milliseconds).\n * @default 100\n */\n intervalMs?: number;\n /**\n * Target origin to restrict postMessage destination.\n * @default '*'\n */\n targetOrigin?: string;\n /**\n * Allowed origins to accept pong messages from.\n * If not provided, messages from any origin are accepted.\n */\n allowedOrigins?: string[];\n /**\n * Host window reference (defaults to `window`).\n */\n host?: Window;\n /**\n * Default timeout in ms for requests on the resulting Communicator.\n * @default 5000\n */\n communicatorTimeoutMs?: number;\n}\n\nexport interface OnPingOptions {\n /**\n * Maximum time to wait for an incoming ping before rejecting (in milliseconds).\n * If omitted or <= 0, waits indefinitely until a ping is received.\n */\n timeoutMs?: number;\n /**\n * Target origin when replying with pong.\n * @default '*'\n */\n targetOrigin?: string;\n /**\n * Allowed origins to accept ping messages from.\n * If not provided, messages from any origin are accepted.\n */\n allowedOrigins?: string[];\n /**\n * Expected source window. If provided, pings from other windows will be ignored.\n */\n expectedSource?: Window;\n /**\n * Host window reference (defaults to `window`).\n */\n host?: Window;\n /**\n * Default timeout in ms for requests on the resulting Communicator.\n * @default 5000\n */\n communicatorTimeoutMs?: number;\n}\n\n/**\n * Initiates a handshake with a target Window by periodically sending ping messages\n * until a matching pong ACK is received or timeout occurs.\n *\n * Edge cases handled:\n * - Cross-origin target window (cannot attach DOM load listeners)\n * - Race condition where target script hasn't loaded yet (interval retry)\n * - Target already loaded before ping is called (immediate first ping)\n * - Multiple iframes on the page (filtered by unique handshake id & event.source)\n * - Window closure/detachment during handshake\n * - Timeout and comprehensive cleanup of event listeners and timers\n */\nexport function ping(target: Window, options: PingOptions = {}): Promise<Communicator> {\n const {\n timeoutMs = 10000,\n intervalMs = 100,\n targetOrigin = '*',\n allowedOrigins,\n host = typeof window !== 'undefined' ? window : ({} as Window),\n communicatorTimeoutMs = 5000,\n } = options;\n\n return new Promise<Communicator>((resolve, reject) => {\n if (!target) {\n return reject(new Error('Cannot ping an invalid target Window.'));\n }\n\n const handshakeId = `handshake-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n\n let intervalTimer: ReturnType<typeof setInterval> | null = null;\n\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (intervalTimer !== null) {\n clearInterval(intervalTimer);\n\n intervalTimer = null;\n }\n\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n\n timeoutTimer = null;\n }\n\n if (host && typeof host.removeEventListener === 'function') {\n host.removeEventListener('message', onMessage);\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n // 1. Verify source window matches target\n if (event.source !== target) {\n return;\n }\n\n // 2. Verify origin if allowedOrigins specified\n if (allowedOrigins && allowedOrigins.length > 0 && !allowedOrigins.includes(event.origin)) {\n return;\n }\n\n // 3. Verify message structure and handshakeId\n const data = event.data as HandshakePongMessage;\n\n if (data && typeof data === 'object' && data.type === PONG_TOKEN && data.id === handshakeId) {\n cleanup();\n\n resolve(new Communicator(target, host, communicatorTimeoutMs));\n }\n };\n\n if (host && typeof host.addEventListener === 'function') {\n host.addEventListener('message', onMessage);\n }\n\n const sendPing = () => {\n try {\n // Guard if target window was closed or detached\n if (target.closed) {\n cleanup();\n\n reject(new Error('Target window was closed before handshake completed.'));\n\n return;\n }\n\n const message: HandshakePingMessage = {\n type: PING_TOKEN,\n id: handshakeId,\n };\n\n target.postMessage(message, targetOrigin);\n } catch (err) {\n if (target.closed) {\n cleanup();\n\n reject(new Error('Target window was closed before handshake completed.'));\n\n return;\n }\n // Log transient errors during handshake and allow retry interval to continue until timeout\n console.warn('Transient error during handshake ping attempt, retrying...', err);\n }\n };\n\n // Send first ping immediately\n sendPing();\n\n // Start periodic retry interval\n intervalTimer = setInterval(sendPing, intervalMs);\n\n // Setup overall timeout\n if (timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n cleanup();\n\n reject(new Error(`Handshake ping timed out after ${timeoutMs}ms waiting for target window.`));\n\n }, timeoutMs);\n }\n });\n}\n\n/**\n * Listens for an incoming ping handshake from another window, replies with a pong ACK,\n * and resolves with a connected Communicator instance.\n *\n * Edge cases handled:\n * - Origin validation and expected source window filtering\n * - Rejects non-handshake and invalid messages\n * - Proper cleanup of event listeners and optional timeout timer\n * - Safely handles closed/detached source window when sending pong\n */\nexport function pong(options: OnPingOptions = {}): Promise<Communicator> {\n const {\n timeoutMs = 5000,\n targetOrigin = '*',\n allowedOrigins,\n expectedSource,\n host = typeof window !== 'undefined' ? window : ({} as Window),\n communicatorTimeoutMs = 5000,\n } = options;\n\n return new Promise<Communicator>((resolve, reject) => {\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n\n timeoutTimer = null;\n }\n\n if (host && typeof host.removeEventListener === 'function') {\n host.removeEventListener('message', onMessage);\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n // 1. Verify expected source if specified\n if (expectedSource && event.source !== expectedSource) {\n return;\n }\n\n // 2. Verify source exists\n if (!event.source) {\n return;\n }\n\n // 3. Verify origin if allowedOrigins specified\n if (allowedOrigins && allowedOrigins.length > 0 && !allowedOrigins.includes(event.origin)) {\n return;\n }\n\n // 4. Verify message structure\n const data = event.data as HandshakePingMessage;\n if (data && typeof data === 'object' && data.type === PING_TOKEN && data.id) {\n const sourceWindow = event.source as Window;\n\n // Send PONG response back\n const pongMessage: HandshakePongMessage = {\n type: PONG_TOKEN,\n id: data.id,\n };\n\n try {\n sourceWindow.postMessage(pongMessage, targetOrigin);\n } catch (err) {\n console.warn('Failed to reply with pong to ping source:', err);\n }\n\n cleanup();\n resolve(new Communicator(sourceWindow, host, communicatorTimeoutMs));\n }\n };\n\n if (host && typeof host.addEventListener === 'function') {\n host.addEventListener('message', onMessage);\n }\n\n if (timeoutMs && timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n cleanup();\n\n reject(new Error(`Handshake pong timed out after ${timeoutMs}ms waiting for incoming ping.`));\n }, timeoutMs);\n }\n });\n}\n"],"names":["Communicator","target","host","timeoutMs","__publicField","event","message","pending","actionHandlers","sendResponse","payload","error","responseMessage","settled","h","fulfilled","r","nonUndefined","responsePayload","firstRejected","err","errorMessage","action","handler","handlersSet","timeout","resolve","reject","id","timer","req","PING_TOKEN","PONG_TOKEN","ping","options","intervalMs","targetOrigin","allowedOrigins","communicatorTimeoutMs","handshakeId","intervalTimer","timeoutTimer","cleanup","onMessage","data","sendPing","pong","expectedSource","sourceWindow","pongMessage"],"mappings":";;;AAEO,MAAMA,EAAa;AAAA,EAQxB,YACEC,GACAC,IAAe,OAAO,SAAW,MAAc,SAAU,CAAA,GACzDC,IAAoB,KACpB;AAXM,IAAAC,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA,6CAA4G,IAAA;AAC5G,IAAAA,EAAA,sCAAgF,IAAA;AAChF,IAAAA,EAAA;AACA,IAAAA,EAAA,mBAAY;AAaZ,IAAAA,EAAA,+BAAwB,OAAOC,MAAwB;AAC7D,UAAI,KAAK,UAAUA,EAAM,WAAW,KAAK;AACvC;AAGF,YAAMC,IAAUD,EAAM;AAEtB,UAAI,GAACC,KAAW,CAACA,EAAQ,MAAM,CAACA,EAAQ,QAAQ,CAACA,EAAQ;AAIzD,YAAIA,EAAQ,SAAS,YAAY;AAC/B,gBAAMC,IAAU,KAAK,gBAAgB,IAAID,EAAQ,EAAE;AACnD,UAAIC,MACF,aAAaA,EAAQ,KAAK,GAC1B,KAAK,gBAAgB,OAAOD,EAAQ,EAAE,GAClCA,EAAQ,QACVC,EAAQ,OAAO,IAAI,MAAMD,EAAQ,KAAK,CAAC,IAEvCC,EAAQ,QAAQD,EAAQ,OAAO;AAAA,QAGrC,WAAWA,EAAQ,SAAS,WAAW;AACrC,gBAAME,IAAiB,KAAK,SAAS,IAAIF,EAAQ,MAAM,GAEjDG,IAAe,CAACC,GAAeC,MAAmB;AACtD,kBAAMC,IAAkD;AAAA,cACtD,IAAIN,EAAQ;AAAA,cACZ,MAAM;AAAA,cACN,QAAQA,EAAQ;AAAA,cAChB,SAAAI;AAAA,cACA,OAAAC;AAAA,YAAA;AAMF,YAFe,KAAK,OAEb,YAAYC,GAAiB,GAAG;AAAA,UACzC;AAEA,cAAI,CAACJ,KAAkBA,EAAe,SAAS,GAAG;AAChD,YAAAC,EAAa,QAAW,gCAAgCH,EAAQ,MAAM,EAAE;AACxE;AAAA,UACF;AAEA,cAAI;AACF,kBAAMO,IAAU,MAAM,QAAQ;AAAA,cAC5B,MAAM,KAAKL,CAAc,EAAE,IAAI,CAACM,MAAM,QAAQ,QAAA,EAAU,KAAK,MAAMA,EAAER,EAAQ,OAAO,CAAC,CAAC;AAAA,YAAA,GAGlFS,IAAYF,EAAQ;AAAA,cACxB,CAACG,MAAwCA,EAAE,WAAW;AAAA,YAAA;AAGxD,gBAAID,EAAU,SAAS,GAAG;AAExB,oBAAME,IAAeF,EAAU,KAAK,CAACC,MAAMA,EAAE,UAAU,MAAS,GAC1DE,IAAkBD,MAAiB,SAAYA,EAAa,QAAQF,EAAU,CAAC,EAAE;AACvF,cAAAN,EAAaS,CAAe;AAAA,YAC9B,OAAO;AAEL,oBAAMC,IAAgBN,EAAQ;AAAA,gBAC5B,CAACG,MAAkCA,EAAE,WAAW;AAAA,cAAA,GAE5CI,IAAMD,KAAA,gBAAAA,EAAe,QACrBE,IAAe,OAAOD,KAAQ,WAAWA,KAAMA,KAAA,gBAAAA,EAAK,YAAW;AACrE,cAAAX,EAAa,QAAWY,CAAY;AAAA,YACtC;AAAA,UACF,SAASD,GAAU;AACjB,kBAAMC,IAAe,OAAOD,KAAQ,WAAWA,KAAMA,KAAA,gBAAAA,EAAK,YAAW;AACrE,YAAAX,EAAa,QAAWY,CAAY;AAAA,UACtC;AAAA,QACF;AAAA;AAAA,IACF;AA/EE,SAAK,SAASpB,GACd,KAAK,OAAOC,GACZ,KAAK,YAAYC,GACjB,KAAK,KAAK,iBAAiB,WAAW,KAAK,qBAAqB;AAAA,EAClE;AAAA,EA6EQ,aAAqB;AAC3B,WAAO,GAAG,KAAK,IAAA,CAAK,IAAI,EAAE,KAAK,SAAS,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,gBACLmB,GACAC,GACY;AACZ,WAAK,KAAK,SAAS,IAAID,CAAM,KAC3B,KAAK,SAAS,IAAIA,GAAQ,oBAAI,KAAK,GAErC,KAAK,SAAS,IAAIA,CAAM,EAAG,IAAIC,CAAO,GAE/B,MAAM;AACX,YAAMC,IAAc,KAAK,SAAS,IAAIF,CAAM;AAC5C,MAAIE,MACFA,EAAY,OAAOD,CAAO,GACtBC,EAAY,SAAS,KACvB,KAAK,SAAS,OAAOF,CAAM;AAAA,IAGjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,YACLA,GACAZ,GACAe,GACmC;AACnC,WAAO,IAAI,QAAQ,CAACC,GAASC,MAAW;AACtC,YAAMC,IAAK,KAAK,WAAA,GACVtB,IAAsB;AAAA,QAC1B,IAAAsB;AAAA,QACA,MAAM;AAAA,QACN,QAAAN;AAAA,QACA,SAAAZ;AAAA,MAAA,GAGImB,IAAQ,WAAW,MAAM;AAC7B,aAAK,gBAAgB,OAAOD,CAAE,GAC9BD,EAAO,IAAI,MAAM,2CAA2CL,CAAM,EAAE,CAAC;AAAA,MACvE,GAAGG,KAAW,KAAK,SAAS;AAE5B,WAAK,gBAAgB,IAAIG,GAAI,EAAE,SAAAF,GAAS,QAAAC,GAAQ,OAAAE,GAAO,GAEvD,KAAK,OAAO,YAAYvB,GAAS,GAAG;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEO,UAAU;AACf,SAAK,KAAK,oBAAoB,WAAW,KAAK,qBAAqB,GACnE,KAAK,gBAAgB,QAAQ,CAACwB,MAAQ;AACpC,mBAAaA,EAAI,KAAK,GACtBA,EAAI,OAAO,IAAI,MAAM,0DAA0D,CAAC;AAAA,IAClF,CAAC,GACD,KAAK,gBAAgB,MAAA,GACrB,KAAK,SAAS,MAAA;AAAA,EAChB;AACF;AAEO,MAAMC,IAAa,wBAEbC,IAAa;AA0FnB,SAASC,EAAKhC,GAAgBiC,IAAuB,IAA2B;AACrF,QAAM;AAAA,IACJ,WAAA/B,IAAY;AAAA,IACZ,YAAAgC,IAAa;AAAA,IACb,cAAAC,IAAe;AAAA,IACf,gBAAAC;AAAA,IACA,MAAAnC,IAAO,OAAO,SAAW,MAAc,SAAU,CAAA;AAAA,IACjD,uBAAAoC,IAAwB;AAAA,EAAA,IACtBJ;AAEJ,SAAO,IAAI,QAAsB,CAACR,GAASC,MAAW;AACpD,QAAI,CAAC1B;AACH,aAAO0B,EAAO,IAAI,MAAM,uCAAuC,CAAC;AAGlE,UAAMY,IAAc,aAAa,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAEzF,QAAIC,IAAuD,MAEvDC,IAAqD;AAEzD,UAAMC,IAAU,MAAM;AACpB,MAAIF,MAAkB,SACpB,cAAcA,CAAa,GAE3BA,IAAgB,OAGdC,MAAiB,SACnB,aAAaA,CAAY,GAEzBA,IAAe,OAGbvC,KAAQ,OAAOA,EAAK,uBAAwB,cAC9CA,EAAK,oBAAoB,WAAWyC,CAAS;AAAA,IAEjD,GAEMA,IAAY,CAACtC,MAAwB;AAOzC,UALIA,EAAM,WAAWJ,KAKjBoC,KAAkBA,EAAe,SAAS,KAAK,CAACA,EAAe,SAAShC,EAAM,MAAM;AACtF;AAIF,YAAMuC,IAAOvC,EAAM;AAEnB,MAAIuC,KAAQ,OAAOA,KAAS,YAAYA,EAAK,SAASZ,KAAcY,EAAK,OAAOL,MAC9EG,EAAA,GAEAhB,EAAQ,IAAI1B,EAAaC,GAAQC,GAAMoC,CAAqB,CAAC;AAAA,IAEjE;AAEA,IAAIpC,KAAQ,OAAOA,EAAK,oBAAqB,cAC3CA,EAAK,iBAAiB,WAAWyC,CAAS;AAG5C,UAAME,IAAW,MAAM;AACrB,UAAI;AAEF,YAAI5C,EAAO,QAAQ;AACjB,UAAAyC,EAAA,GAEAf,EAAO,IAAI,MAAM,sDAAsD,CAAC;AAExE;AAAA,QACF;AAEA,cAAMrB,IAAgC;AAAA,UACpC,MAAMyB;AAAA,UACN,IAAIQ;AAAA,QAAA;AAGN,QAAAtC,EAAO,YAAYK,GAAS8B,CAAY;AAAA,MAC1C,SAAShB,GAAK;AACZ,YAAInB,EAAO,QAAQ;AACjB,UAAAyC,EAAA,GAEAf,EAAO,IAAI,MAAM,sDAAsD,CAAC;AAExE;AAAA,QACF;AAEA,gBAAQ,KAAK,8DAA8DP,CAAG;AAAA,MAChF;AAAA,IACF;AAGA,IAAAyB,EAAA,GAGAL,IAAgB,YAAYK,GAAUV,CAAU,GAG5ChC,IAAY,MACdsC,IAAe,WAAW,MAAM;AAC9B,MAAAC,EAAA,GAEAf,EAAO,IAAI,MAAM,kCAAkCxB,CAAS,+BAA+B,CAAC;AAAA,IAE9F,GAAGA,CAAS;AAAA,EAEhB,CAAC;AACH;AAYO,SAAS2C,EAAKZ,IAAyB,IAA2B;AACvE,QAAM;AAAA,IACJ,WAAA/B,IAAY;AAAA,IACZ,cAAAiC,IAAe;AAAA,IACf,gBAAAC;AAAA,IACA,gBAAAU;AAAA,IACA,MAAA7C,IAAO,OAAO,SAAW,MAAc,SAAU,CAAA;AAAA,IACjD,uBAAAoC,IAAwB;AAAA,EAAA,IACtBJ;AAEJ,SAAO,IAAI,QAAsB,CAACR,GAASC,MAAW;AACpD,QAAIc,IAAqD;AAEzD,UAAMC,IAAU,MAAM;AACpB,MAAID,MAAiB,SACnB,aAAaA,CAAY,GAEzBA,IAAe,OAGbvC,KAAQ,OAAOA,EAAK,uBAAwB,cAC9CA,EAAK,oBAAoB,WAAWyC,CAAS;AAAA,IAEjD,GAEMA,IAAY,CAACtC,MAAwB;AAYzC,UAVI0C,KAAkB1C,EAAM,WAAW0C,KAKnC,CAAC1C,EAAM,UAKPgC,KAAkBA,EAAe,SAAS,KAAK,CAACA,EAAe,SAAShC,EAAM,MAAM;AACtF;AAIF,YAAMuC,IAAOvC,EAAM;AACnB,UAAIuC,KAAQ,OAAOA,KAAS,YAAYA,EAAK,SAASb,KAAca,EAAK,IAAI;AAC3E,cAAMI,IAAe3C,EAAM,QAGrB4C,IAAoC;AAAA,UACxC,MAAMjB;AAAA,UACN,IAAIY,EAAK;AAAA,QAAA;AAGX,YAAI;AACF,UAAAI,EAAa,YAAYC,GAAab,CAAY;AAAA,QACpD,SAAShB,GAAK;AACZ,kBAAQ,KAAK,6CAA6CA,CAAG;AAAA,QAC/D;AAEA,QAAAsB,EAAA,GACAhB,EAAQ,IAAI1B,EAAagD,GAAc9C,GAAMoC,CAAqB,CAAC;AAAA,MACrE;AAAA,IACF;AAEA,IAAIpC,KAAQ,OAAOA,EAAK,oBAAqB,cAC3CA,EAAK,iBAAiB,WAAWyC,CAAS,GAGxCxC,KAAaA,IAAY,MAC3BsC,IAAe,WAAW,MAAM;AAC9B,MAAAC,EAAA,GAEAf,EAAO,IAAI,MAAM,kCAAkCxB,CAAS,+BAA+B,CAAC;AAAA,IAC9F,GAAGA,CAAS;AAAA,EAEhB,CAAC;AACH;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var T=Object.defineProperty;var _=(r,t,e)=>t in r?T(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var h=(r,t,e)=>_(r,typeof t!="symbol"?t+"":t,e);class M{constructor(t,e=typeof window<"u"?window:{},s=5e3){h(this,"target");h(this,"host");h(this,"pendingRequests",new Map);h(this,"handlers",new Map);h(this,"timeoutMs");h(this,"idCounter",0);h(this,"handleIncomingMessage",async t=>{if(this.target&&t.source!==this.target)return;const e=t.data;if(!(!e||!e.id||!e.type||!e.action)){if(e.type==="response"){const s=this.pendingRequests.get(e.id);s&&(clearTimeout(s.timer),this.pendingRequests.delete(e.id),e.error?s.reject(new Error(e.error)):s.resolve(e.payload))}else if(e.type==="request"){const s=this.handlers.get(e.action),l=(n,i)=>{const o={id:e.id,type:"response",action:e.action,payload:n,error:i};this.target.postMessage(o,"*")};if(!s||s.size===0){l(void 0,`No handler found for action: ${e.action}`);return}try{const n=await Promise.allSettled(Array.from(s).map(o=>Promise.resolve().then(()=>o(e.payload)))),i=n.filter(o=>o.status==="fulfilled");if(i.length>0){const o=i.find(d=>d.value!==void 0),a=o!==void 0?o.value:i[0].value;l(a)}else{const o=n.find(f=>f.status==="rejected"),a=o==null?void 0:o.reason,d=typeof a=="string"?a:(a==null?void 0:a.message)||"Unknown error handling request";l(void 0,d)}}catch(n){const i=typeof n=="string"?n:(n==null?void 0:n.message)||"Unknown error handling request";l(void 0,i)}}}});this.target=t,this.host=e,this.timeoutMs=s,this.host.addEventListener("message",this.handleIncomingMessage)}generateId(){return`${Date.now()}-${++this.idCounter}-${Math.random().toString(36).substring(2,9)}`}registerHandler(t,e){return this.handlers.has(t)||this.handlers.set(t,new Set),this.handlers.get(t).add(e),()=>{const s=this.handlers.get(t);s&&(s.delete(e),s.size===0&&this.handlers.delete(t))}}sendRequest(t,e,s){return new Promise((l,n)=>{const i=this.generateId(),o={id:i,type:"request",action:t,payload:e},a=setTimeout(()=>{this.pendingRequests.delete(i),n(new Error(`Timeout waiting for response to action: ${t}`))},s||this.timeoutMs);this.pendingRequests.set(i,{resolve:l,reject:n,timer:a}),this.target.postMessage(o,"*")})}destroy(){this.host.removeEventListener("message",this.handleIncomingMessage),this.pendingRequests.forEach(t=>{clearTimeout(t.timer),t.reject(new Error("Communicator was destroyed before response was received."))}),this.pendingRequests.clear(),this.handlers.clear()}}const v="__GOOD_PLUGIN_PING__",E="__GOOD_PLUGIN_PONG__";function O(r,t={}){const{timeoutMs:e=1e4,intervalMs:s=100,targetOrigin:l="*",allowedOrigins:n,host:i=typeof window<"u"?window:{},communicatorTimeoutMs:o=5e3}=t;return new Promise((a,d)=>{if(!r)return d(new Error("Cannot ping an invalid target Window."));const f=`handshake-${Date.now()}-${Math.random().toString(36).substring(2,9)}`;let m=null,u=null;const c=()=>{m!==null&&(clearInterval(m),m=null),u!==null&&(clearTimeout(u),u=null),i&&typeof i.removeEventListener=="function"&&i.removeEventListener("message",p)},p=g=>{if(g.source!==r||n&&n.length>0&&!n.includes(g.origin))return;const y=g.data;y&&typeof y=="object"&&y.type===E&&y.id===f&&(c(),a(new M(r,i,o)))};i&&typeof i.addEventListener=="function"&&i.addEventListener("message",p);const w=()=>{try{if(r.closed){c(),d(new Error("Target window was closed before handshake completed."));return}const g={type:v,id:f};r.postMessage(g,l)}catch(g){if(r.closed){c(),d(new Error("Target window was closed before handshake completed."));return}console.warn("Transient error during handshake ping attempt, retrying...",g)}};w(),m=setInterval(w,s),e>0&&(u=setTimeout(()=>{c(),d(new Error(`Handshake ping timed out after ${e}ms waiting for target window.`))},e))})}function P(r={}){const{timeoutMs:t=5e3,targetOrigin:e="*",allowedOrigins:s,expectedSource:l,host:n=typeof window<"u"?window:{},communicatorTimeoutMs:i=5e3}=r;return new Promise((o,a)=>{let d=null;const f=()=>{d!==null&&(clearTimeout(d),d=null),n&&typeof n.removeEventListener=="function"&&n.removeEventListener("message",m)},m=u=>{if(l&&u.source!==l||!u.source||s&&s.length>0&&!s.includes(u.origin))return;const c=u.data;if(c&&typeof c=="object"&&c.type===v&&c.id){const p=u.source,w={type:E,id:c.id};try{p.postMessage(w,e)}catch(g){console.warn("Failed to reply with pong to ping source:",g)}f(),o(new M(p,n,i))}};n&&typeof n.addEventListener=="function"&&n.addEventListener("message",m),t&&t>0&&(d=setTimeout(()=>{f(),a(new Error(`Handshake pong timed out after ${t}ms waiting for incoming ping.`))},t))})}exports.Communicator=M;exports.PING_TOKEN=v;exports.PONG_TOKEN=E;exports.ping=O;exports.pong=P;
|
|
2
|
+
//# sourceMappingURL=communicator-DQZOTxOF.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"communicator-DQZOTxOF.cjs","sources":["../src/communicator.ts"],"sourcesContent":["import { Message, ActionMap } from './types';\n\nexport class Communicator {\n private target: Window;\n private host: Window;\n private pendingRequests: Map<string, { resolve: (val: any) => void; reject: (err: any) => void; timer: any }> = new Map();\n private handlers: Map<keyof ActionMap, Set<(payload: any) => Promise<any> | any>> = new Map();\n private timeoutMs: number;\n private idCounter = 0;\n\n constructor(\n target: Window,\n host: Window = typeof window !== 'undefined' ? window : ({} as Window),\n timeoutMs: number = 5000\n ) {\n this.target = target;\n this.host = host;\n this.timeoutMs = timeoutMs;\n this.host.addEventListener('message', this.handleIncomingMessage);\n }\n\n private handleIncomingMessage = async (event: MessageEvent) => {\n if (this.target && event.source !== this.target) {\n return;\n }\n\n const message = event.data as Message;\n\n if (!message || !message.id || !message.type || !message.action) {\n return;\n }\n\n if (message.type === 'response') {\n const pending = this.pendingRequests.get(message.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(message.id);\n if (message.error) {\n pending.reject(new Error(message.error));\n } else {\n pending.resolve(message.payload);\n }\n }\n } else if (message.type === 'request') {\n const actionHandlers = this.handlers.get(message.action);\n\n const sendResponse = (payload?: any, error?: string) => {\n const responseMessage: Message<typeof message.action> = {\n id: message.id,\n type: 'response',\n action: message.action,\n payload,\n error\n };\n // Use event.source if available for better targeting, otherwise fallback to targetWindow\n // const target = (event.source as Window) || this.target;\n const target = this.target;\n\n target.postMessage(responseMessage, '*');\n };\n\n if (!actionHandlers || actionHandlers.size === 0) {\n sendResponse(undefined, `No handler found for action: ${message.action}`);\n return;\n }\n\n try {\n const settled = await Promise.allSettled(\n Array.from(actionHandlers).map((h) => Promise.resolve().then(() => h(message.payload)))\n );\n\n const fulfilled = settled.filter(\n (r): r is PromiseFulfilledResult<any> => r.status === 'fulfilled'\n );\n\n if (fulfilled.length > 0) {\n // Find the first non-undefined result among fulfilled handlers, or default to the first fulfilled result\n const nonUndefined = fulfilled.find((r) => r.value !== undefined);\n const responsePayload = nonUndefined !== undefined ? nonUndefined.value : fulfilled[0].value;\n sendResponse(responsePayload);\n } else {\n // All handlers rejected\n const firstRejected = settled.find(\n (r): r is PromiseRejectedResult => r.status === 'rejected'\n );\n const err = firstRejected?.reason;\n const errorMessage = typeof err === 'string' ? err : err?.message || 'Unknown error handling request';\n sendResponse(undefined, errorMessage);\n }\n } catch (err: any) {\n const errorMessage = typeof err === 'string' ? err : err?.message || 'Unknown error handling request';\n sendResponse(undefined, errorMessage);\n }\n }\n };\n\n private generateId(): string {\n return `${Date.now()}-${++this.idCounter}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Registers a handler for an incoming request action.\n * Returns a function to unregister the handler.\n */\n public registerHandler<A extends keyof ActionMap>(\n action: A,\n handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>\n ): () => void {\n if (!this.handlers.has(action)) {\n this.handlers.set(action, new Set());\n }\n this.handlers.get(action)!.add(handler);\n\n return () => {\n const handlersSet = this.handlers.get(action);\n if (handlersSet) {\n handlersSet.delete(handler);\n if (handlersSet.size === 0) {\n this.handlers.delete(action);\n }\n }\n };\n }\n\n /**\n * Sends a request and waits for the response.\n */\n public sendRequest<A extends keyof ActionMap>(\n action: A,\n payload?: Parameters<ActionMap[A]>[0],\n timeout?: number\n ): Promise<ReturnType<ActionMap[A]>> {\n return new Promise((resolve, reject) => {\n const id = this.generateId();\n const message: Message<A> = {\n id,\n type: 'request',\n action,\n payload\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Timeout waiting for response to action: ${action}`));\n }, timeout || this.timeoutMs);\n\n this.pendingRequests.set(id, { resolve, reject, timer });\n\n this.target.postMessage(message, '*');\n });\n }\n\n public destroy() {\n this.host.removeEventListener('message', this.handleIncomingMessage);\n this.pendingRequests.forEach((req) => {\n clearTimeout(req.timer);\n req.reject(new Error('Communicator was destroyed before response was received.'));\n });\n this.pendingRequests.clear();\n this.handlers.clear();\n }\n}\n\nexport const PING_TOKEN = '__GOOD_PLUGIN_PING__';\n\nexport const PONG_TOKEN = '__GOOD_PLUGIN_PONG__';\n\nexport interface HandshakePingMessage {\n type: typeof PING_TOKEN;\n id: string;\n payload?: any;\n}\n\nexport interface HandshakePongMessage {\n type: typeof PONG_TOKEN;\n id: string;\n payload?: any;\n}\n\nexport interface PingOptions {\n /**\n * Maximum time to wait for a pong response before rejecting (in milliseconds).\n * Set to 0 or negative to disable timeout.\n * @default 10000\n */\n timeoutMs?: number;\n /**\n * Interval between handshake ping attempts (in milliseconds).\n * @default 100\n */\n intervalMs?: number;\n /**\n * Target origin to restrict postMessage destination.\n * @default '*'\n */\n targetOrigin?: string;\n /**\n * Allowed origins to accept pong messages from.\n * If not provided, messages from any origin are accepted.\n */\n allowedOrigins?: string[];\n /**\n * Host window reference (defaults to `window`).\n */\n host?: Window;\n /**\n * Default timeout in ms for requests on the resulting Communicator.\n * @default 5000\n */\n communicatorTimeoutMs?: number;\n}\n\nexport interface OnPingOptions {\n /**\n * Maximum time to wait for an incoming ping before rejecting (in milliseconds).\n * If omitted or <= 0, waits indefinitely until a ping is received.\n */\n timeoutMs?: number;\n /**\n * Target origin when replying with pong.\n * @default '*'\n */\n targetOrigin?: string;\n /**\n * Allowed origins to accept ping messages from.\n * If not provided, messages from any origin are accepted.\n */\n allowedOrigins?: string[];\n /**\n * Expected source window. If provided, pings from other windows will be ignored.\n */\n expectedSource?: Window;\n /**\n * Host window reference (defaults to `window`).\n */\n host?: Window;\n /**\n * Default timeout in ms for requests on the resulting Communicator.\n * @default 5000\n */\n communicatorTimeoutMs?: number;\n}\n\n/**\n * Initiates a handshake with a target Window by periodically sending ping messages\n * until a matching pong ACK is received or timeout occurs.\n *\n * Edge cases handled:\n * - Cross-origin target window (cannot attach DOM load listeners)\n * - Race condition where target script hasn't loaded yet (interval retry)\n * - Target already loaded before ping is called (immediate first ping)\n * - Multiple iframes on the page (filtered by unique handshake id & event.source)\n * - Window closure/detachment during handshake\n * - Timeout and comprehensive cleanup of event listeners and timers\n */\nexport function ping(target: Window, options: PingOptions = {}): Promise<Communicator> {\n const {\n timeoutMs = 10000,\n intervalMs = 100,\n targetOrigin = '*',\n allowedOrigins,\n host = typeof window !== 'undefined' ? window : ({} as Window),\n communicatorTimeoutMs = 5000,\n } = options;\n\n return new Promise<Communicator>((resolve, reject) => {\n if (!target) {\n return reject(new Error('Cannot ping an invalid target Window.'));\n }\n\n const handshakeId = `handshake-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n\n let intervalTimer: ReturnType<typeof setInterval> | null = null;\n\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (intervalTimer !== null) {\n clearInterval(intervalTimer);\n\n intervalTimer = null;\n }\n\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n\n timeoutTimer = null;\n }\n\n if (host && typeof host.removeEventListener === 'function') {\n host.removeEventListener('message', onMessage);\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n // 1. Verify source window matches target\n if (event.source !== target) {\n return;\n }\n\n // 2. Verify origin if allowedOrigins specified\n if (allowedOrigins && allowedOrigins.length > 0 && !allowedOrigins.includes(event.origin)) {\n return;\n }\n\n // 3. Verify message structure and handshakeId\n const data = event.data as HandshakePongMessage;\n\n if (data && typeof data === 'object' && data.type === PONG_TOKEN && data.id === handshakeId) {\n cleanup();\n\n resolve(new Communicator(target, host, communicatorTimeoutMs));\n }\n };\n\n if (host && typeof host.addEventListener === 'function') {\n host.addEventListener('message', onMessage);\n }\n\n const sendPing = () => {\n try {\n // Guard if target window was closed or detached\n if (target.closed) {\n cleanup();\n\n reject(new Error('Target window was closed before handshake completed.'));\n\n return;\n }\n\n const message: HandshakePingMessage = {\n type: PING_TOKEN,\n id: handshakeId,\n };\n\n target.postMessage(message, targetOrigin);\n } catch (err) {\n if (target.closed) {\n cleanup();\n\n reject(new Error('Target window was closed before handshake completed.'));\n\n return;\n }\n // Log transient errors during handshake and allow retry interval to continue until timeout\n console.warn('Transient error during handshake ping attempt, retrying...', err);\n }\n };\n\n // Send first ping immediately\n sendPing();\n\n // Start periodic retry interval\n intervalTimer = setInterval(sendPing, intervalMs);\n\n // Setup overall timeout\n if (timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n cleanup();\n\n reject(new Error(`Handshake ping timed out after ${timeoutMs}ms waiting for target window.`));\n\n }, timeoutMs);\n }\n });\n}\n\n/**\n * Listens for an incoming ping handshake from another window, replies with a pong ACK,\n * and resolves with a connected Communicator instance.\n *\n * Edge cases handled:\n * - Origin validation and expected source window filtering\n * - Rejects non-handshake and invalid messages\n * - Proper cleanup of event listeners and optional timeout timer\n * - Safely handles closed/detached source window when sending pong\n */\nexport function pong(options: OnPingOptions = {}): Promise<Communicator> {\n const {\n timeoutMs = 5000,\n targetOrigin = '*',\n allowedOrigins,\n expectedSource,\n host = typeof window !== 'undefined' ? window : ({} as Window),\n communicatorTimeoutMs = 5000,\n } = options;\n\n return new Promise<Communicator>((resolve, reject) => {\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n if (timeoutTimer !== null) {\n clearTimeout(timeoutTimer);\n\n timeoutTimer = null;\n }\n\n if (host && typeof host.removeEventListener === 'function') {\n host.removeEventListener('message', onMessage);\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n // 1. Verify expected source if specified\n if (expectedSource && event.source !== expectedSource) {\n return;\n }\n\n // 2. Verify source exists\n if (!event.source) {\n return;\n }\n\n // 3. Verify origin if allowedOrigins specified\n if (allowedOrigins && allowedOrigins.length > 0 && !allowedOrigins.includes(event.origin)) {\n return;\n }\n\n // 4. Verify message structure\n const data = event.data as HandshakePingMessage;\n if (data && typeof data === 'object' && data.type === PING_TOKEN && data.id) {\n const sourceWindow = event.source as Window;\n\n // Send PONG response back\n const pongMessage: HandshakePongMessage = {\n type: PONG_TOKEN,\n id: data.id,\n };\n\n try {\n sourceWindow.postMessage(pongMessage, targetOrigin);\n } catch (err) {\n console.warn('Failed to reply with pong to ping source:', err);\n }\n\n cleanup();\n resolve(new Communicator(sourceWindow, host, communicatorTimeoutMs));\n }\n };\n\n if (host && typeof host.addEventListener === 'function') {\n host.addEventListener('message', onMessage);\n }\n\n if (timeoutMs && timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n cleanup();\n\n reject(new Error(`Handshake pong timed out after ${timeoutMs}ms waiting for incoming ping.`));\n }, timeoutMs);\n }\n });\n}\n"],"names":["Communicator","target","host","timeoutMs","__publicField","event","message","pending","actionHandlers","sendResponse","payload","error","responseMessage","settled","h","fulfilled","r","nonUndefined","responsePayload","firstRejected","err","errorMessage","action","handler","handlersSet","timeout","resolve","reject","id","timer","req","PING_TOKEN","PONG_TOKEN","ping","options","intervalMs","targetOrigin","allowedOrigins","communicatorTimeoutMs","handshakeId","intervalTimer","timeoutTimer","cleanup","onMessage","data","sendPing","pong","expectedSource","sourceWindow","pongMessage"],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ActionMap } from './types';
|
|
2
|
+
export declare class Communicator {
|
|
3
|
+
private target;
|
|
4
|
+
private host;
|
|
5
|
+
private pendingRequests;
|
|
6
|
+
private handlers;
|
|
7
|
+
private timeoutMs;
|
|
8
|
+
private idCounter;
|
|
9
|
+
constructor(target: Window, host?: Window, timeoutMs?: number);
|
|
10
|
+
private handleIncomingMessage;
|
|
11
|
+
private generateId;
|
|
12
|
+
/**
|
|
13
|
+
* Registers a handler for an incoming request action.
|
|
14
|
+
* Returns a function to unregister the handler.
|
|
15
|
+
*/
|
|
16
|
+
registerHandler<A extends keyof ActionMap>(action: A, handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>): () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Sends a request and waits for the response.
|
|
19
|
+
*/
|
|
20
|
+
sendRequest<A extends keyof ActionMap>(action: A, payload?: Parameters<ActionMap[A]>[0], timeout?: number): Promise<ReturnType<ActionMap[A]>>;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare const PING_TOKEN = "__GOOD_PLUGIN_PING__";
|
|
24
|
+
export declare const PONG_TOKEN = "__GOOD_PLUGIN_PONG__";
|
|
25
|
+
export interface HandshakePingMessage {
|
|
26
|
+
type: typeof PING_TOKEN;
|
|
27
|
+
id: string;
|
|
28
|
+
payload?: any;
|
|
29
|
+
}
|
|
30
|
+
export interface HandshakePongMessage {
|
|
31
|
+
type: typeof PONG_TOKEN;
|
|
32
|
+
id: string;
|
|
33
|
+
payload?: any;
|
|
34
|
+
}
|
|
35
|
+
export interface PingOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Maximum time to wait for a pong response before rejecting (in milliseconds).
|
|
38
|
+
* Set to 0 or negative to disable timeout.
|
|
39
|
+
* @default 10000
|
|
40
|
+
*/
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Interval between handshake ping attempts (in milliseconds).
|
|
44
|
+
* @default 100
|
|
45
|
+
*/
|
|
46
|
+
intervalMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Target origin to restrict postMessage destination.
|
|
49
|
+
* @default '*'
|
|
50
|
+
*/
|
|
51
|
+
targetOrigin?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Allowed origins to accept pong messages from.
|
|
54
|
+
* If not provided, messages from any origin are accepted.
|
|
55
|
+
*/
|
|
56
|
+
allowedOrigins?: string[];
|
|
57
|
+
/**
|
|
58
|
+
* Host window reference (defaults to `window`).
|
|
59
|
+
*/
|
|
60
|
+
host?: Window;
|
|
61
|
+
/**
|
|
62
|
+
* Default timeout in ms for requests on the resulting Communicator.
|
|
63
|
+
* @default 5000
|
|
64
|
+
*/
|
|
65
|
+
communicatorTimeoutMs?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface OnPingOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Maximum time to wait for an incoming ping before rejecting (in milliseconds).
|
|
70
|
+
* If omitted or <= 0, waits indefinitely until a ping is received.
|
|
71
|
+
*/
|
|
72
|
+
timeoutMs?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Target origin when replying with pong.
|
|
75
|
+
* @default '*'
|
|
76
|
+
*/
|
|
77
|
+
targetOrigin?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Allowed origins to accept ping messages from.
|
|
80
|
+
* If not provided, messages from any origin are accepted.
|
|
81
|
+
*/
|
|
82
|
+
allowedOrigins?: string[];
|
|
83
|
+
/**
|
|
84
|
+
* Expected source window. If provided, pings from other windows will be ignored.
|
|
85
|
+
*/
|
|
86
|
+
expectedSource?: Window;
|
|
87
|
+
/**
|
|
88
|
+
* Host window reference (defaults to `window`).
|
|
89
|
+
*/
|
|
90
|
+
host?: Window;
|
|
91
|
+
/**
|
|
92
|
+
* Default timeout in ms for requests on the resulting Communicator.
|
|
93
|
+
* @default 5000
|
|
94
|
+
*/
|
|
95
|
+
communicatorTimeoutMs?: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Initiates a handshake with a target Window by periodically sending ping messages
|
|
99
|
+
* until a matching pong ACK is received or timeout occurs.
|
|
100
|
+
*
|
|
101
|
+
* Edge cases handled:
|
|
102
|
+
* - Cross-origin target window (cannot attach DOM load listeners)
|
|
103
|
+
* - Race condition where target script hasn't loaded yet (interval retry)
|
|
104
|
+
* - Target already loaded before ping is called (immediate first ping)
|
|
105
|
+
* - Multiple iframes on the page (filtered by unique handshake id & event.source)
|
|
106
|
+
* - Window closure/detachment during handshake
|
|
107
|
+
* - Timeout and comprehensive cleanup of event listeners and timers
|
|
108
|
+
*/
|
|
109
|
+
export declare function ping(target: Window, options?: PingOptions): Promise<Communicator>;
|
|
110
|
+
/**
|
|
111
|
+
* Listens for an incoming ping handshake from another window, replies with a pong ACK,
|
|
112
|
+
* and resolves with a connected Communicator instance.
|
|
113
|
+
*
|
|
114
|
+
* Edge cases handled:
|
|
115
|
+
* - Origin validation and expected source window filtering
|
|
116
|
+
* - Rejects non-handshake and invalid messages
|
|
117
|
+
* - Proper cleanup of event listeners and optional timeout timer
|
|
118
|
+
* - Safely handles closed/detached source window when sending pong
|
|
119
|
+
*/
|
|
120
|
+
export declare function pong(options?: OnPingOptions): Promise<Communicator>;
|
|
121
|
+
//# sourceMappingURL=communicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"communicator.d.ts","sourceRoot":"","sources":["../src/communicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,SAAS,EAAE,MAAM,SAAS,CAAC;AAE7C,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,eAAe,CAAmG;IAC1H,OAAO,CAAC,QAAQ,CAA8E;IAC9F,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAK;gBAGpB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAgE,EACtE,SAAS,GAAE,MAAa;IAQ1B,OAAO,CAAC,qBAAqB,CAyE3B;IAEF,OAAO,CAAC,UAAU;IAIlB;;;OAGG;IACI,eAAe,CAAC,CAAC,SAAS,MAAM,SAAS,EAC9C,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAC9G,MAAM,IAAI;IAiBb;;OAEG;IACI,WAAW,CAAC,CAAC,SAAS,MAAM,SAAS,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACrC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAqB7B,OAAO;CASf;AAED,eAAO,MAAM,UAAU,yBAAyB,CAAC;AAEjD,eAAO,MAAM,UAAU,yBAAyB,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,UAAU,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,UAAU,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8GrF;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2EvE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Communicator, PingOptions } from './communicator';
|
|
2
|
+
import { ActionMap } from './types';
|
|
3
|
+
export declare class Host {
|
|
4
|
+
private _communicator;
|
|
5
|
+
private iframeWindow;
|
|
6
|
+
private subscriptions;
|
|
7
|
+
private initPromise;
|
|
8
|
+
private isDestroyed;
|
|
9
|
+
private get communicator();
|
|
10
|
+
/**
|
|
11
|
+
* Initialize Host SDK for a specific iframe widget
|
|
12
|
+
* @param iframeWindow The contentWindow of the widget's iframe
|
|
13
|
+
*/
|
|
14
|
+
constructor(iframeWindow: Window);
|
|
15
|
+
/**
|
|
16
|
+
* Register a unified handler for a specific message action type.
|
|
17
|
+
* Can be called before or after `init()`.
|
|
18
|
+
* Returns an unsubscribe function.
|
|
19
|
+
*/
|
|
20
|
+
onMessage<A extends keyof ActionMap>(actionType: A, handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>): () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Send a message/request to the widget and wait for the response.
|
|
23
|
+
*/
|
|
24
|
+
sendMessage<A extends keyof ActionMap>(actionType: A, payload?: Parameters<ActionMap[A]>[0], timeout?: number): Promise<ReturnType<ActionMap[A]>>;
|
|
25
|
+
/**
|
|
26
|
+
* Complete the handshake with the target widget iframe and attach all registered handlers.
|
|
27
|
+
*/
|
|
28
|
+
init(options?: PingOptions): Promise<Communicator>;
|
|
29
|
+
destroy(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=host-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-sdk.d.ts","sourceRoot":"","sources":["../src/host-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAQ,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AASpC,qBAAa,IAAI;IACf,OAAO,CAAC,aAAa,CAA6B;IAElD,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,aAAa,CAA+B;IAEpD,OAAO,CAAC,WAAW,CAAsC;IAEzD,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,KAAK,YAAY,GAKvB;IAED;;;OAGG;gBACS,YAAY,EAAE,MAAM;IAIhC;;;;OAIG;IACI,SAAS,CAAC,CAAC,SAAS,MAAM,SAAS,EACxC,UAAU,EAAE,CAAC,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAC9G,MAAM,IAAI;IAyBb;;OAEG;IACU,WAAW,CAAC,CAAC,SAAS,MAAM,SAAS,EAChD,UAAU,EAAE,CAAC,EACb,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACrC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAcpC;;OAEG;IACU,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAuCxD,OAAO,IAAI,IAAI;CAkBvB"}
|
package/dist/host.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var n=Object.defineProperty;var c=(r,t,i)=>t in r?n(r,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):r[t]=i;var o=(r,t,i)=>c(r,typeof t!="symbol"?t+"":t,i);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./communicator-DQZOTxOF.cjs");class u{constructor(t){o(this,"_communicator",null);o(this,"iframeWindow");o(this,"subscriptions",new Set);o(this,"initPromise",null);o(this,"isDestroyed",!1);this.iframeWindow=t}get communicator(){if(!this._communicator)throw new Error("Host is not initialized. Call init() first.");return this._communicator}onMessage(t,i){const s={actionType:t,handler:i,off:null,cancelled:!1};return this._communicator&&(s.off=this._communicator.registerHandler(t,i)),this.subscriptions.add(s),()=>{s.cancelled||(s.cancelled=!0,this.subscriptions.delete(s),s.off&&(s.off(),s.off=null))}}async sendMessage(t,i,s){return this._communicator?this._communicator.sendRequest(t,i,s):this.initPromise?(await this.initPromise).sendRequest(t,i,s):this.communicator.sendRequest(t,i,s)}async init(t){if(this.isDestroyed)throw new Error("Host has already been destroyed.");return this._communicator?this._communicator:(this.initPromise||(this.initPromise=(async()=>{try{const i=await e.ping(this.iframeWindow,t);if(this.isDestroyed)throw i.destroy(),new Error("Host was destroyed during initialization.");this._communicator=i;for(const s of this.subscriptions)!s.cancelled&&!s.off&&(s.off=i.registerHandler(s.actionType,s.handler));return i}catch(i){throw this.initPromise=null,i}})()),this.initPromise)}destroy(){var t;this.isDestroyed=!0;for(const i of this.subscriptions)i.off&&(i.off(),i.off=null),i.cancelled=!0;this.subscriptions.clear(),(t=this._communicator)==null||t.destroy(),this._communicator=null,this.initPromise=null}}exports.Communicator=e.Communicator;exports.PING_TOKEN=e.PING_TOKEN;exports.PONG_TOKEN=e.PONG_TOKEN;exports.ping=e.ping;exports.Host=u;
|
|
2
|
+
//# sourceMappingURL=host.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.cjs","sources":["../src/host-sdk.ts"],"sourcesContent":["import { Communicator, ping, PingOptions } from './communicator';\nimport { ActionMap } from './types';\n\ninterface HostSubscription {\n actionType: keyof ActionMap;\n handler: (payload: any) => any;\n off: (() => void) | null;\n cancelled: boolean;\n}\n\nexport class Host {\n private _communicator: Communicator | null = null;\n\n private iframeWindow: Window;\n\n private subscriptions = new Set<HostSubscription>();\n\n private initPromise: Promise<Communicator> | null = null;\n\n private isDestroyed = false;\n\n private get communicator(): Communicator {\n if (!this._communicator) {\n throw new Error('Host is not initialized. Call init() first.');\n }\n return this._communicator;\n }\n\n /**\n * Initialize Host SDK for a specific iframe widget\n * @param iframeWindow The contentWindow of the widget's iframe\n */\n constructor(iframeWindow: Window) {\n this.iframeWindow = iframeWindow;\n }\n\n /**\n * Register a unified handler for a specific message action type.\n * Can be called before or after `init()`.\n * Returns an unsubscribe function.\n */\n public onMessage<A extends keyof ActionMap>(\n actionType: A,\n handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>\n ): () => void {\n const sub: HostSubscription = {\n actionType,\n handler,\n off: null,\n cancelled: false,\n };\n\n if (this._communicator) {\n sub.off = this._communicator.registerHandler(actionType, handler);\n }\n\n this.subscriptions.add(sub);\n\n return () => {\n if (sub.cancelled) return;\n sub.cancelled = true;\n this.subscriptions.delete(sub);\n if (sub.off) {\n sub.off();\n sub.off = null;\n }\n };\n }\n\n /**\n * Send a message/request to the widget and wait for the response.\n */\n public async sendMessage<A extends keyof ActionMap>(\n actionType: A,\n payload?: Parameters<ActionMap[A]>[0],\n timeout?: number\n ): Promise<ReturnType<ActionMap[A]>> {\n if (this._communicator) {\n return this._communicator.sendRequest(actionType, payload, timeout);\n }\n\n if (this.initPromise) {\n const comm = await this.initPromise;\n\n return comm.sendRequest(actionType, payload, timeout);\n }\n\n return this.communicator.sendRequest(actionType, payload, timeout);\n }\n\n /**\n * Complete the handshake with the target widget iframe and attach all registered handlers.\n */\n public async init(options?: PingOptions): Promise<Communicator> {\n if (this.isDestroyed) {\n throw new Error('Host has already been destroyed.');\n }\n\n if (this._communicator) {\n return this._communicator;\n }\n\n if (!this.initPromise) {\n this.initPromise = (async () => {\n try {\n const comm = await ping(this.iframeWindow, options);\n\n if (this.isDestroyed) {\n comm.destroy();\n throw new Error('Host was destroyed during initialization.');\n }\n\n this._communicator = comm;\n\n // Attach all active subscriptions to communicator\n for (const sub of this.subscriptions) {\n if (!sub.cancelled && !sub.off) {\n sub.off = comm.registerHandler(sub.actionType, sub.handler);\n }\n }\n\n return comm;\n } catch (err) {\n this.initPromise = null;\n throw err;\n }\n })();\n }\n\n return this.initPromise;\n }\n\n public destroy(): void {\n this.isDestroyed = true;\n\n for (const sub of this.subscriptions) {\n if (sub.off) {\n sub.off();\n sub.off = null;\n }\n sub.cancelled = true;\n }\n this.subscriptions.clear();\n\n this._communicator?.destroy();\n\n this._communicator = null;\n\n this.initPromise = null;\n }\n}\n"],"names":["Host","iframeWindow","__publicField","actionType","handler","sub","payload","timeout","options","comm","ping","err","_a"],"mappings":"mSAUO,MAAMA,CAAK,CAsBhB,YAAYC,EAAsB,CArB1BC,EAAA,qBAAqC,MAErCA,EAAA,qBAEAA,EAAA,yBAAoB,KAEpBA,EAAA,mBAA4C,MAE5CA,EAAA,mBAAc,IAcpB,KAAK,aAAeD,CACtB,CAbA,IAAY,cAA6B,CACvC,GAAI,CAAC,KAAK,cACR,MAAM,IAAI,MAAM,6CAA6C,EAE/D,OAAO,KAAK,aACd,CAeO,UACLE,EACAC,EACY,CACZ,MAAMC,EAAwB,CAC5B,WAAAF,EACA,QAAAC,EACA,IAAK,KACL,UAAW,EAAA,EAGb,OAAI,KAAK,gBACPC,EAAI,IAAM,KAAK,cAAc,gBAAgBF,EAAYC,CAAO,GAGlE,KAAK,cAAc,IAAIC,CAAG,EAEnB,IAAM,CACPA,EAAI,YACRA,EAAI,UAAY,GAChB,KAAK,cAAc,OAAOA,CAAG,EACzBA,EAAI,MACNA,EAAI,IAAA,EACJA,EAAI,IAAM,MAEd,CACF,CAKA,MAAa,YACXF,EACAG,EACAC,EACmC,CACnC,OAAI,KAAK,cACA,KAAK,cAAc,YAAYJ,EAAYG,EAASC,CAAO,EAGhE,KAAK,aACM,MAAM,KAAK,aAEZ,YAAYJ,EAAYG,EAASC,CAAO,EAG/C,KAAK,aAAa,YAAYJ,EAAYG,EAASC,CAAO,CACnE,CAKA,MAAa,KAAKC,EAA8C,CAC9D,GAAI,KAAK,YACP,MAAM,IAAI,MAAM,kCAAkC,EAGpD,OAAI,KAAK,cACA,KAAK,eAGT,KAAK,cACR,KAAK,aAAe,SAAY,CAC9B,GAAI,CACF,MAAMC,EAAO,MAAMC,EAAAA,KAAK,KAAK,aAAcF,CAAO,EAElD,GAAI,KAAK,YACP,MAAAC,EAAK,QAAA,EACC,IAAI,MAAM,2CAA2C,EAG7D,KAAK,cAAgBA,EAGrB,UAAWJ,KAAO,KAAK,cACjB,CAACA,EAAI,WAAa,CAACA,EAAI,MACzBA,EAAI,IAAMI,EAAK,gBAAgBJ,EAAI,WAAYA,EAAI,OAAO,GAI9D,OAAOI,CACT,OAASE,EAAK,CACZ,WAAK,YAAc,KACbA,CACR,CACF,GAAA,GAGK,KAAK,YACd,CAEO,SAAgB,OACrB,KAAK,YAAc,GAEnB,UAAWN,KAAO,KAAK,cACjBA,EAAI,MACNA,EAAI,IAAA,EACJA,EAAI,IAAM,MAEZA,EAAI,UAAY,GAElB,KAAK,cAAc,MAAA,GAEnBO,EAAA,KAAK,gBAAL,MAAAA,EAAoB,UAEpB,KAAK,cAAgB,KAErB,KAAK,YAAc,IACrB,CACF"}
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './host-sdk';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export { Communicator, ping, PING_TOKEN, PONG_TOKEN } from './communicator';
|
|
4
|
+
export type { PingOptions, OnPingOptions, HandshakePingMessage, HandshakePongMessage } from './communicator';
|
|
5
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
var e = Object.defineProperty;
|
|
2
|
+
var n = (r, t, i) => t in r ? e(r, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[t] = i;
|
|
3
|
+
var o = (r, t, i) => n(r, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import { p as c } from "./communicator-BF77R7Qs.js";
|
|
5
|
+
import { C as d, P as w, a as _ } from "./communicator-BF77R7Qs.js";
|
|
6
|
+
class f {
|
|
7
|
+
/**
|
|
8
|
+
* Initialize Host SDK for a specific iframe widget
|
|
9
|
+
* @param iframeWindow The contentWindow of the widget's iframe
|
|
10
|
+
*/
|
|
11
|
+
constructor(t) {
|
|
12
|
+
o(this, "_communicator", null);
|
|
13
|
+
o(this, "iframeWindow");
|
|
14
|
+
o(this, "subscriptions", /* @__PURE__ */ new Set());
|
|
15
|
+
o(this, "initPromise", null);
|
|
16
|
+
o(this, "isDestroyed", !1);
|
|
17
|
+
this.iframeWindow = t;
|
|
18
|
+
}
|
|
19
|
+
get communicator() {
|
|
20
|
+
if (!this._communicator)
|
|
21
|
+
throw new Error("Host is not initialized. Call init() first.");
|
|
22
|
+
return this._communicator;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Register a unified handler for a specific message action type.
|
|
26
|
+
* Can be called before or after `init()`.
|
|
27
|
+
* Returns an unsubscribe function.
|
|
28
|
+
*/
|
|
29
|
+
onMessage(t, i) {
|
|
30
|
+
const s = {
|
|
31
|
+
actionType: t,
|
|
32
|
+
handler: i,
|
|
33
|
+
off: null,
|
|
34
|
+
cancelled: !1
|
|
35
|
+
};
|
|
36
|
+
return this._communicator && (s.off = this._communicator.registerHandler(t, i)), this.subscriptions.add(s), () => {
|
|
37
|
+
s.cancelled || (s.cancelled = !0, this.subscriptions.delete(s), s.off && (s.off(), s.off = null));
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Send a message/request to the widget and wait for the response.
|
|
42
|
+
*/
|
|
43
|
+
async sendMessage(t, i, s) {
|
|
44
|
+
return this._communicator ? this._communicator.sendRequest(t, i, s) : this.initPromise ? (await this.initPromise).sendRequest(t, i, s) : this.communicator.sendRequest(t, i, s);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Complete the handshake with the target widget iframe and attach all registered handlers.
|
|
48
|
+
*/
|
|
49
|
+
async init(t) {
|
|
50
|
+
if (this.isDestroyed)
|
|
51
|
+
throw new Error("Host has already been destroyed.");
|
|
52
|
+
return this._communicator ? this._communicator : (this.initPromise || (this.initPromise = (async () => {
|
|
53
|
+
try {
|
|
54
|
+
const i = await c(this.iframeWindow, t);
|
|
55
|
+
if (this.isDestroyed)
|
|
56
|
+
throw i.destroy(), new Error("Host was destroyed during initialization.");
|
|
57
|
+
this._communicator = i;
|
|
58
|
+
for (const s of this.subscriptions)
|
|
59
|
+
!s.cancelled && !s.off && (s.off = i.registerHandler(s.actionType, s.handler));
|
|
60
|
+
return i;
|
|
61
|
+
} catch (i) {
|
|
62
|
+
throw this.initPromise = null, i;
|
|
63
|
+
}
|
|
64
|
+
})()), this.initPromise);
|
|
65
|
+
}
|
|
66
|
+
destroy() {
|
|
67
|
+
var t;
|
|
68
|
+
this.isDestroyed = !0;
|
|
69
|
+
for (const i of this.subscriptions)
|
|
70
|
+
i.off && (i.off(), i.off = null), i.cancelled = !0;
|
|
71
|
+
this.subscriptions.clear(), (t = this._communicator) == null || t.destroy(), this._communicator = null, this.initPromise = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
d as Communicator,
|
|
76
|
+
f as Host,
|
|
77
|
+
w as PING_TOKEN,
|
|
78
|
+
_ as PONG_TOKEN,
|
|
79
|
+
c as ping
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=host.js.map
|
package/dist/host.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sources":["../src/host-sdk.ts"],"sourcesContent":["import { Communicator, ping, PingOptions } from './communicator';\nimport { ActionMap } from './types';\n\ninterface HostSubscription {\n actionType: keyof ActionMap;\n handler: (payload: any) => any;\n off: (() => void) | null;\n cancelled: boolean;\n}\n\nexport class Host {\n private _communicator: Communicator | null = null;\n\n private iframeWindow: Window;\n\n private subscriptions = new Set<HostSubscription>();\n\n private initPromise: Promise<Communicator> | null = null;\n\n private isDestroyed = false;\n\n private get communicator(): Communicator {\n if (!this._communicator) {\n throw new Error('Host is not initialized. Call init() first.');\n }\n return this._communicator;\n }\n\n /**\n * Initialize Host SDK for a specific iframe widget\n * @param iframeWindow The contentWindow of the widget's iframe\n */\n constructor(iframeWindow: Window) {\n this.iframeWindow = iframeWindow;\n }\n\n /**\n * Register a unified handler for a specific message action type.\n * Can be called before or after `init()`.\n * Returns an unsubscribe function.\n */\n public onMessage<A extends keyof ActionMap>(\n actionType: A,\n handler: (payload: Parameters<ActionMap[A]>[0]) => Promise<ReturnType<ActionMap[A]>> | ReturnType<ActionMap[A]>\n ): () => void {\n const sub: HostSubscription = {\n actionType,\n handler,\n off: null,\n cancelled: false,\n };\n\n if (this._communicator) {\n sub.off = this._communicator.registerHandler(actionType, handler);\n }\n\n this.subscriptions.add(sub);\n\n return () => {\n if (sub.cancelled) return;\n sub.cancelled = true;\n this.subscriptions.delete(sub);\n if (sub.off) {\n sub.off();\n sub.off = null;\n }\n };\n }\n\n /**\n * Send a message/request to the widget and wait for the response.\n */\n public async sendMessage<A extends keyof ActionMap>(\n actionType: A,\n payload?: Parameters<ActionMap[A]>[0],\n timeout?: number\n ): Promise<ReturnType<ActionMap[A]>> {\n if (this._communicator) {\n return this._communicator.sendRequest(actionType, payload, timeout);\n }\n\n if (this.initPromise) {\n const comm = await this.initPromise;\n\n return comm.sendRequest(actionType, payload, timeout);\n }\n\n return this.communicator.sendRequest(actionType, payload, timeout);\n }\n\n /**\n * Complete the handshake with the target widget iframe and attach all registered handlers.\n */\n public async init(options?: PingOptions): Promise<Communicator> {\n if (this.isDestroyed) {\n throw new Error('Host has already been destroyed.');\n }\n\n if (this._communicator) {\n return this._communicator;\n }\n\n if (!this.initPromise) {\n this.initPromise = (async () => {\n try {\n const comm = await ping(this.iframeWindow, options);\n\n if (this.isDestroyed) {\n comm.destroy();\n throw new Error('Host was destroyed during initialization.');\n }\n\n this._communicator = comm;\n\n // Attach all active subscriptions to communicator\n for (const sub of this.subscriptions) {\n if (!sub.cancelled && !sub.off) {\n sub.off = comm.registerHandler(sub.actionType, sub.handler);\n }\n }\n\n return comm;\n } catch (err) {\n this.initPromise = null;\n throw err;\n }\n })();\n }\n\n return this.initPromise;\n }\n\n public destroy(): void {\n this.isDestroyed = true;\n\n for (const sub of this.subscriptions) {\n if (sub.off) {\n sub.off();\n sub.off = null;\n }\n sub.cancelled = true;\n }\n this.subscriptions.clear();\n\n this._communicator?.destroy();\n\n this._communicator = null;\n\n this.initPromise = null;\n }\n}\n"],"names":["Host","iframeWindow","__publicField","actionType","handler","sub","payload","timeout","options","comm","ping","err","_a"],"mappings":";;;;;AAUO,MAAMA,EAAK;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBhB,YAAYC,GAAsB;AArB1B,IAAAC,EAAA,uBAAqC;AAErC,IAAAA,EAAA;AAEA,IAAAA,EAAA,2CAAoB,IAAA;AAEpB,IAAAA,EAAA,qBAA4C;AAE5C,IAAAA,EAAA,qBAAc;AAcpB,SAAK,eAAeD;AAAA,EACtB;AAAA,EAbA,IAAY,eAA6B;AACvC,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,6CAA6C;AAE/D,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeO,UACLE,GACAC,GACY;AACZ,UAAMC,IAAwB;AAAA,MAC5B,YAAAF;AAAA,MACA,SAAAC;AAAA,MACA,KAAK;AAAA,MACL,WAAW;AAAA,IAAA;AAGb,WAAI,KAAK,kBACPC,EAAI,MAAM,KAAK,cAAc,gBAAgBF,GAAYC,CAAO,IAGlE,KAAK,cAAc,IAAIC,CAAG,GAEnB,MAAM;AACX,MAAIA,EAAI,cACRA,EAAI,YAAY,IAChB,KAAK,cAAc,OAAOA,CAAG,GACzBA,EAAI,QACNA,EAAI,IAAA,GACJA,EAAI,MAAM;AAAA,IAEd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,YACXF,GACAG,GACAC,GACmC;AACnC,WAAI,KAAK,gBACA,KAAK,cAAc,YAAYJ,GAAYG,GAASC,CAAO,IAGhE,KAAK,eACM,MAAM,KAAK,aAEZ,YAAYJ,GAAYG,GAASC,CAAO,IAG/C,KAAK,aAAa,YAAYJ,GAAYG,GAASC,CAAO;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,KAAKC,GAA8C;AAC9D,QAAI,KAAK;AACP,YAAM,IAAI,MAAM,kCAAkC;AAGpD,WAAI,KAAK,gBACA,KAAK,iBAGT,KAAK,gBACR,KAAK,eAAe,YAAY;AAC9B,UAAI;AACF,cAAMC,IAAO,MAAMC,EAAK,KAAK,cAAcF,CAAO;AAElD,YAAI,KAAK;AACP,gBAAAC,EAAK,QAAA,GACC,IAAI,MAAM,2CAA2C;AAG7D,aAAK,gBAAgBA;AAGrB,mBAAWJ,KAAO,KAAK;AACrB,UAAI,CAACA,EAAI,aAAa,CAACA,EAAI,QACzBA,EAAI,MAAMI,EAAK,gBAAgBJ,EAAI,YAAYA,EAAI,OAAO;AAI9D,eAAOI;AAAA,MACT,SAASE,GAAK;AACZ,mBAAK,cAAc,MACbA;AAAA,MACR;AAAA,IACF,GAAA,IAGK,KAAK;AAAA,EACd;AAAA,EAEO,UAAgB;;AACrB,SAAK,cAAc;AAEnB,eAAWN,KAAO,KAAK;AACrB,MAAIA,EAAI,QACNA,EAAI,IAAA,GACJA,EAAI,MAAM,OAEZA,EAAI,YAAY;AAElB,SAAK,cAAc,MAAA,IAEnBO,IAAA,KAAK,kBAAL,QAAAA,EAAoB,WAEpB,KAAK,gBAAgB,MAErB,KAAK,cAAc;AAAA,EACrB;AACF;"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./communicator-DQZOTxOF.cjs");let d=null;function a(t){return d||(d=s.pong(t).catch(e=>{throw d=null,e})),d}function l(t){a().then(e=>e.sendRequest("widget_ready")).then(e=>{t(e)}).catch(e=>{console.error("Failed to initialize widget:",e)})}async function m(t){return await(await a()).sendRequest("fetch_widget_data_source",t)}function g(t,e){const n=typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2,9);let r=!1,c=null,u=null;return a().then(i=>{u=i,!r&&(c=i.registerHandler("widget_data_source_update",o=>{(o==null?void 0:o.identity)===n&&e(o.data)}),i.sendRequest("add_widget_data_source_listener",{identity:n,config:t}).catch(o=>{console.error("Failed to add data source listener:",o)}))}).catch(i=>{console.error("Failed to initialize communicator for data source listener:",i)}),()=>{r=!0,c&&c(),u&&u.sendRequest("remove_widget_data_source_listener",{identity:n}).catch(i=>{console.error("Failed to remove data source listener:",i)})}}async function f(t,e){return await(await a()).sendRequest("emit_widget_event",{type:t,args:e})}async function _(t){return await(await a()).sendRequest("set_widget_personalization",t)}function h(t){let e=!1,n=null;return a().then(r=>{e||(n=r.registerHandler("widget_filter_change",c=>{t(c)}))}).catch(r=>{console.error("Failed to initialize communicator for filter change listener:",r)}),()=>{e=!0,n&&n()}}exports.Communicator=s.Communicator;exports.PING_TOKEN=s.PING_TOKEN;exports.PONG_TOKEN=s.PONG_TOKEN;exports.pong=s.pong;exports.addDataSourceListener=g;exports.emitWidgetEvent=f;exports.fetchDataSource=m;exports.onWidgetFilterChange=h;exports.onWidgetReady=l;exports.setWidgetPersonalization=_;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/widget-sdk.ts"],"sourcesContent":["import { Communicator, pong, OnPingOptions } from './communicator';\nimport { DataSourceConfig, WidgetConfig } from './types';\n\nlet communicatorPromise: Promise<Communicator> | null = null;\n\n/**\n * Get or initialize the Communicator instance for the widget.\n * Caches the connection promise so that multiple calls reuse the same handshake.\n */\nfunction getCommunicator(options?: OnPingOptions): Promise<Communicator> {\n if (!communicatorPromise) {\n communicatorPromise = pong(options).catch((err) => {\n communicatorPromise = null;\n throw err;\n });\n }\n\n return communicatorPromise;\n}\n\n/**\n * Called when the widget is ready to receive configuration from the host.\n */\nexport function onWidgetReady(callback: (config: WidgetConfig) => void) {\n getCommunicator()\n .then((comm) => comm.sendRequest('widget_ready'))\n .then((config) => {\n callback(config);\n })\n .catch((err) => {\n console.error('Failed to initialize widget:', err);\n });\n}\n\n/**\n * Fetch a data source from the host platform.\n */\nexport async function fetchDataSource(payload: DataSourceConfig): Promise<any[]> {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('fetch_widget_data_source', payload);\n}\n\n/**\n * Add a reactive data source listener on the host platform.\n * Returns an unsubscribe function.\n */\nexport function addDataSourceListener(config: DataSourceConfig, onChange: (value: any[]) => void): () => void {\n const identity =\n typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2, 9);\n\n let isCancelled = false;\n\n let unregisterHandler: (() => void) | null = null;\n\n let commInstance: Communicator | null = null;\n\n getCommunicator()\n .then((comm) => {\n commInstance = comm;\n\n if (isCancelled) {\n return;\n }\n\n unregisterHandler = comm.registerHandler('widget_data_source_update', (payload) => {\n if (payload?.identity === identity) {\n onChange(payload.data);\n }\n });\n\n comm.sendRequest('add_widget_data_source_listener', { identity, config }).catch((err) => {\n console.error('Failed to add data source listener:', err);\n });\n })\n .catch((err) => {\n console.error('Failed to initialize communicator for data source listener:', err);\n });\n\n return () => {\n isCancelled = true;\n\n if (unregisterHandler) {\n unregisterHandler();\n }\n\n if (commInstance) {\n commInstance.sendRequest('remove_widget_data_source_listener', { identity }).catch((err) => {\n console.error('Failed to remove data source listener:', err);\n });\n }\n };\n}\n\n/**\n * Emit a custom widget event to the host platform.\n */\nexport async function emitWidgetEvent(type: string, args?: Record<string, any>) {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('emit_widget_event', { type, args });\n}\n\n/**\n * Update widget personalization state on the host platform.\n */\nexport async function setWidgetPersonalization(data: any) {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('set_widget_personalization', data);\n}\n\n/**\n * Register a listener for widget filter changes from the host platform.\n * Returns an unsubscribe function.\n */\nexport function onWidgetFilterChange(callback: (condition: string) => void): () => void {\n let isCancelled = false;\n\n let unregisterHandler: (() => void) | null = null;\n\n getCommunicator()\n .then((comm) => {\n if (isCancelled) {\n return;\n }\n\n unregisterHandler = comm.registerHandler('widget_filter_change', (condition) => {\n callback(condition);\n });\n })\n .catch((err) => {\n console.error('Failed to initialize communicator for filter change listener:', err);\n });\n\n return () => {\n isCancelled = true;\n\n if (unregisterHandler) {\n unregisterHandler();\n }\n };\n}\n"],"names":["communicatorPromise","getCommunicator","options","pong","err","onWidgetReady","callback","comm","config","fetchDataSource","payload","addDataSourceListener","onChange","identity","isCancelled","unregisterHandler","commInstance","emitWidgetEvent","type","args","setWidgetPersonalization","data","onWidgetFilterChange","condition"],"mappings":"+HAGA,IAAIA,EAAoD,KAMxD,SAASC,EAAgBC,EAAgD,CACvE,OAAKF,IACHA,EAAsBG,EAAAA,KAAKD,CAAO,EAAE,MAAOE,GAAQ,CACjD,MAAAJ,EAAsB,KAChBI,CACR,CAAC,GAGIJ,CACT,CAKO,SAASK,EAAcC,EAA0C,CACtEL,IACG,KAAMM,GAASA,EAAK,YAAY,cAAc,CAAC,EAC/C,KAAMC,GAAW,CAChBF,EAASE,CAAM,CACjB,CAAC,EACA,MAAOJ,GAAQ,CACd,QAAQ,MAAM,+BAAgCA,CAAG,CACnD,CAAC,CACL,CAKA,eAAsBK,EAAgBC,EAA2C,CAG/E,OAAO,MAFM,MAAMT,EAAA,GAED,YAAY,2BAA4BS,CAAO,CACnE,CAMO,SAASC,EAAsBH,EAA0BI,EAA8C,CAC5G,MAAMC,EACJ,OAAO,OAAW,KAAe,OAAO,WAAa,OAAO,WAAA,EAAe,KAAK,SAAS,SAAS,EAAE,EAAE,UAAU,EAAG,CAAC,EAEtH,IAAIC,EAAc,GAEdC,EAAyC,KAEzCC,EAAoC,KAExC,OAAAf,EAAA,EACG,KAAMM,GAAS,CACdS,EAAeT,EAEX,CAAAO,IAIJC,EAAoBR,EAAK,gBAAgB,4BAA8BG,GAAY,EAC7EA,GAAA,YAAAA,EAAS,YAAaG,GACxBD,EAASF,EAAQ,IAAI,CAEzB,CAAC,EAEDH,EAAK,YAAY,kCAAmC,CAAE,SAAAM,EAAU,OAAAL,EAAQ,EAAE,MAAOJ,GAAQ,CACvF,QAAQ,MAAM,sCAAuCA,CAAG,CAC1D,CAAC,EACH,CAAC,EACA,MAAOA,GAAQ,CACd,QAAQ,MAAM,8DAA+DA,CAAG,CAClF,CAAC,EAEI,IAAM,CACXU,EAAc,GAEVC,GACFA,EAAA,EAGEC,GACFA,EAAa,YAAY,qCAAsC,CAAE,SAAAH,CAAA,CAAU,EAAE,MAAOT,GAAQ,CAC1F,QAAQ,MAAM,yCAA0CA,CAAG,CAC7D,CAAC,CAEL,CACF,CAKA,eAAsBa,EAAgBC,EAAcC,EAA4B,CAG9E,OAAO,MAFM,MAAMlB,EAAA,GAED,YAAY,oBAAqB,CAAE,KAAAiB,EAAM,KAAAC,EAAM,CACnE,CAKA,eAAsBC,EAAyBC,EAAW,CAGxD,OAAO,MAFM,MAAMpB,EAAA,GAED,YAAY,6BAA8BoB,CAAI,CAClE,CAMO,SAASC,EAAqBhB,EAAmD,CACtF,IAAIQ,EAAc,GAEdC,EAAyC,KAE7C,OAAAd,EAAA,EACG,KAAMM,GAAS,CACVO,IAIJC,EAAoBR,EAAK,gBAAgB,uBAAyBgB,GAAc,CAC9EjB,EAASiB,CAAS,CACpB,CAAC,EACH,CAAC,EACA,MAAOnB,GAAQ,CACd,QAAQ,MAAM,gEAAiEA,CAAG,CACpF,CAAC,EAEI,IAAM,CACXU,EAAc,GAEVC,GACFA,EAAA,CAEJ,CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './widget-sdk';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export { Communicator, pong, PING_TOKEN, PONG_TOKEN } from './communicator';
|
|
4
|
+
export type { PingOptions, OnPingOptions, HandshakePingMessage, HandshakePongMessage } from './communicator';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { b as u } from "./communicator-BF77R7Qs.js";
|
|
2
|
+
import { C as y, P as C, a as F } from "./communicator-BF77R7Qs.js";
|
|
3
|
+
let s = null;
|
|
4
|
+
function a(t) {
|
|
5
|
+
return s || (s = u(t).catch((e) => {
|
|
6
|
+
throw s = null, e;
|
|
7
|
+
})), s;
|
|
8
|
+
}
|
|
9
|
+
function m(t) {
|
|
10
|
+
a().then((e) => e.sendRequest("widget_ready")).then((e) => {
|
|
11
|
+
t(e);
|
|
12
|
+
}).catch((e) => {
|
|
13
|
+
console.error("Failed to initialize widget:", e);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async function f(t) {
|
|
17
|
+
return await (await a()).sendRequest("fetch_widget_data_source", t);
|
|
18
|
+
}
|
|
19
|
+
function g(t, e) {
|
|
20
|
+
const n = typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2, 9);
|
|
21
|
+
let i = !1, c = null, d = null;
|
|
22
|
+
return a().then((r) => {
|
|
23
|
+
d = r, !i && (c = r.registerHandler("widget_data_source_update", (o) => {
|
|
24
|
+
(o == null ? void 0 : o.identity) === n && e(o.data);
|
|
25
|
+
}), r.sendRequest("add_widget_data_source_listener", { identity: n, config: t }).catch((o) => {
|
|
26
|
+
console.error("Failed to add data source listener:", o);
|
|
27
|
+
}));
|
|
28
|
+
}).catch((r) => {
|
|
29
|
+
console.error("Failed to initialize communicator for data source listener:", r);
|
|
30
|
+
}), () => {
|
|
31
|
+
i = !0, c && c(), d && d.sendRequest("remove_widget_data_source_listener", { identity: n }).catch((r) => {
|
|
32
|
+
console.error("Failed to remove data source listener:", r);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function _(t, e) {
|
|
37
|
+
return await (await a()).sendRequest("emit_widget_event", { type: t, args: e });
|
|
38
|
+
}
|
|
39
|
+
async function h(t) {
|
|
40
|
+
return await (await a()).sendRequest("set_widget_personalization", t);
|
|
41
|
+
}
|
|
42
|
+
function w(t) {
|
|
43
|
+
let e = !1, n = null;
|
|
44
|
+
return a().then((i) => {
|
|
45
|
+
e || (n = i.registerHandler("widget_filter_change", (c) => {
|
|
46
|
+
t(c);
|
|
47
|
+
}));
|
|
48
|
+
}).catch((i) => {
|
|
49
|
+
console.error("Failed to initialize communicator for filter change listener:", i);
|
|
50
|
+
}), () => {
|
|
51
|
+
e = !0, n && n();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
y as Communicator,
|
|
56
|
+
C as PING_TOKEN,
|
|
57
|
+
F as PONG_TOKEN,
|
|
58
|
+
g as addDataSourceListener,
|
|
59
|
+
_ as emitWidgetEvent,
|
|
60
|
+
f as fetchDataSource,
|
|
61
|
+
w as onWidgetFilterChange,
|
|
62
|
+
m as onWidgetReady,
|
|
63
|
+
u as pong,
|
|
64
|
+
h as setWidgetPersonalization
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/widget-sdk.ts"],"sourcesContent":["import { Communicator, pong, OnPingOptions } from './communicator';\nimport { DataSourceConfig, WidgetConfig } from './types';\n\nlet communicatorPromise: Promise<Communicator> | null = null;\n\n/**\n * Get or initialize the Communicator instance for the widget.\n * Caches the connection promise so that multiple calls reuse the same handshake.\n */\nfunction getCommunicator(options?: OnPingOptions): Promise<Communicator> {\n if (!communicatorPromise) {\n communicatorPromise = pong(options).catch((err) => {\n communicatorPromise = null;\n throw err;\n });\n }\n\n return communicatorPromise;\n}\n\n/**\n * Called when the widget is ready to receive configuration from the host.\n */\nexport function onWidgetReady(callback: (config: WidgetConfig) => void) {\n getCommunicator()\n .then((comm) => comm.sendRequest('widget_ready'))\n .then((config) => {\n callback(config);\n })\n .catch((err) => {\n console.error('Failed to initialize widget:', err);\n });\n}\n\n/**\n * Fetch a data source from the host platform.\n */\nexport async function fetchDataSource(payload: DataSourceConfig): Promise<any[]> {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('fetch_widget_data_source', payload);\n}\n\n/**\n * Add a reactive data source listener on the host platform.\n * Returns an unsubscribe function.\n */\nexport function addDataSourceListener(config: DataSourceConfig, onChange: (value: any[]) => void): () => void {\n const identity =\n typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2, 9);\n\n let isCancelled = false;\n\n let unregisterHandler: (() => void) | null = null;\n\n let commInstance: Communicator | null = null;\n\n getCommunicator()\n .then((comm) => {\n commInstance = comm;\n\n if (isCancelled) {\n return;\n }\n\n unregisterHandler = comm.registerHandler('widget_data_source_update', (payload) => {\n if (payload?.identity === identity) {\n onChange(payload.data);\n }\n });\n\n comm.sendRequest('add_widget_data_source_listener', { identity, config }).catch((err) => {\n console.error('Failed to add data source listener:', err);\n });\n })\n .catch((err) => {\n console.error('Failed to initialize communicator for data source listener:', err);\n });\n\n return () => {\n isCancelled = true;\n\n if (unregisterHandler) {\n unregisterHandler();\n }\n\n if (commInstance) {\n commInstance.sendRequest('remove_widget_data_source_listener', { identity }).catch((err) => {\n console.error('Failed to remove data source listener:', err);\n });\n }\n };\n}\n\n/**\n * Emit a custom widget event to the host platform.\n */\nexport async function emitWidgetEvent(type: string, args?: Record<string, any>) {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('emit_widget_event', { type, args });\n}\n\n/**\n * Update widget personalization state on the host platform.\n */\nexport async function setWidgetPersonalization(data: any) {\n const comm = await getCommunicator();\n\n return await comm.sendRequest('set_widget_personalization', data);\n}\n\n/**\n * Register a listener for widget filter changes from the host platform.\n * Returns an unsubscribe function.\n */\nexport function onWidgetFilterChange(callback: (condition: string) => void): () => void {\n let isCancelled = false;\n\n let unregisterHandler: (() => void) | null = null;\n\n getCommunicator()\n .then((comm) => {\n if (isCancelled) {\n return;\n }\n\n unregisterHandler = comm.registerHandler('widget_filter_change', (condition) => {\n callback(condition);\n });\n })\n .catch((err) => {\n console.error('Failed to initialize communicator for filter change listener:', err);\n });\n\n return () => {\n isCancelled = true;\n\n if (unregisterHandler) {\n unregisterHandler();\n }\n };\n}\n"],"names":["communicatorPromise","getCommunicator","options","pong","err","onWidgetReady","callback","comm","config","fetchDataSource","payload","addDataSourceListener","onChange","identity","isCancelled","unregisterHandler","commInstance","emitWidgetEvent","type","args","setWidgetPersonalization","data","onWidgetFilterChange","condition"],"mappings":";;AAGA,IAAIA,IAAoD;AAMxD,SAASC,EAAgBC,GAAgD;AACvE,SAAKF,MACHA,IAAsBG,EAAKD,CAAO,EAAE,MAAM,CAACE,MAAQ;AACjD,UAAAJ,IAAsB,MAChBI;AAAA,EACR,CAAC,IAGIJ;AACT;AAKO,SAASK,EAAcC,GAA0C;AACtE,EAAAL,IACG,KAAK,CAACM,MAASA,EAAK,YAAY,cAAc,CAAC,EAC/C,KAAK,CAACC,MAAW;AAChB,IAAAF,EAASE,CAAM;AAAA,EACjB,CAAC,EACA,MAAM,CAACJ,MAAQ;AACd,YAAQ,MAAM,gCAAgCA,CAAG;AAAA,EACnD,CAAC;AACL;AAKA,eAAsBK,EAAgBC,GAA2C;AAG/E,SAAO,OAFM,MAAMT,EAAA,GAED,YAAY,4BAA4BS,CAAO;AACnE;AAMO,SAASC,EAAsBH,GAA0BI,GAA8C;AAC5G,QAAMC,IACJ,OAAO,SAAW,OAAe,OAAO,aAAa,OAAO,WAAA,IAAe,KAAK,SAAS,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AAEtH,MAAIC,IAAc,IAEdC,IAAyC,MAEzCC,IAAoC;AAExC,SAAAf,EAAA,EACG,KAAK,CAACM,MAAS;AAGd,IAFAS,IAAeT,GAEX,CAAAO,MAIJC,IAAoBR,EAAK,gBAAgB,6BAA6B,CAACG,MAAY;AACjF,OAAIA,KAAA,gBAAAA,EAAS,cAAaG,KACxBD,EAASF,EAAQ,IAAI;AAAA,IAEzB,CAAC,GAEDH,EAAK,YAAY,mCAAmC,EAAE,UAAAM,GAAU,QAAAL,GAAQ,EAAE,MAAM,CAACJ,MAAQ;AACvF,cAAQ,MAAM,uCAAuCA,CAAG;AAAA,IAC1D,CAAC;AAAA,EACH,CAAC,EACA,MAAM,CAACA,MAAQ;AACd,YAAQ,MAAM,+DAA+DA,CAAG;AAAA,EAClF,CAAC,GAEI,MAAM;AACX,IAAAU,IAAc,IAEVC,KACFA,EAAA,GAGEC,KACFA,EAAa,YAAY,sCAAsC,EAAE,UAAAH,EAAA,CAAU,EAAE,MAAM,CAACT,MAAQ;AAC1F,cAAQ,MAAM,0CAA0CA,CAAG;AAAA,IAC7D,CAAC;AAAA,EAEL;AACF;AAKA,eAAsBa,EAAgBC,GAAcC,GAA4B;AAG9E,SAAO,OAFM,MAAMlB,EAAA,GAED,YAAY,qBAAqB,EAAE,MAAAiB,GAAM,MAAAC,GAAM;AACnE;AAKA,eAAsBC,EAAyBC,GAAW;AAGxD,SAAO,OAFM,MAAMpB,EAAA,GAED,YAAY,8BAA8BoB,CAAI;AAClE;AAMO,SAASC,EAAqBhB,GAAmD;AACtF,MAAIQ,IAAc,IAEdC,IAAyC;AAE7C,SAAAd,EAAA,EACG,KAAK,CAACM,MAAS;AACd,IAAIO,MAIJC,IAAoBR,EAAK,gBAAgB,wBAAwB,CAACgB,MAAc;AAC9E,MAAAjB,EAASiB,CAAS;AAAA,IACpB,CAAC;AAAA,EACH,CAAC,EACA,MAAM,CAACnB,MAAQ;AACd,YAAQ,MAAM,iEAAiEA,CAAG;AAAA,EACpF,CAAC,GAEI,MAAM;AACX,IAAAU,IAAc,IAEVC,KACFA,EAAA;AAAA,EAEJ;AACF;"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface WidgetConfig {
|
|
2
|
+
props: Record<string, any>;
|
|
3
|
+
personalization?: any;
|
|
4
|
+
}
|
|
5
|
+
export interface DataSourceConfig {
|
|
6
|
+
node: string;
|
|
7
|
+
filterExpression?: string;
|
|
8
|
+
pagination?: {
|
|
9
|
+
no: number;
|
|
10
|
+
size: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface Message<A extends keyof ActionMap = keyof ActionMap> {
|
|
14
|
+
id: string;
|
|
15
|
+
type: 'request' | 'response';
|
|
16
|
+
action: A;
|
|
17
|
+
payload?: Parameters<ActionMap[A]>[0];
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ActionMap {
|
|
21
|
+
widget_ready: () => WidgetConfig;
|
|
22
|
+
widget_data_source_update: (payload: {
|
|
23
|
+
identity: string;
|
|
24
|
+
data: any[];
|
|
25
|
+
}) => void;
|
|
26
|
+
fetch_widget_data_source: (payload: DataSourceConfig) => any[];
|
|
27
|
+
add_widget_data_source_listener: (payload: {
|
|
28
|
+
identity: string;
|
|
29
|
+
config: DataSourceConfig;
|
|
30
|
+
}) => void;
|
|
31
|
+
remove_widget_data_source_listener: (payload: {
|
|
32
|
+
identity: string;
|
|
33
|
+
}) => void;
|
|
34
|
+
emit_widget_event: (payload: {
|
|
35
|
+
type: string;
|
|
36
|
+
args?: Record<string, {
|
|
37
|
+
data: any;
|
|
38
|
+
bindNode: string;
|
|
39
|
+
}>;
|
|
40
|
+
}) => void;
|
|
41
|
+
set_widget_personalization: (payload: any) => void;
|
|
42
|
+
widget_filter_change: (payload: string) => void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,eAAe,CAAC,EAAE,GAAG,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,SAAS;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,GAAG,UAAU,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,YAAY,CAAC;IACjC,yBAAyB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IAChF,wBAAwB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,GAAG,EAAE,CAAC;IAC/D,+BAA+B,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,KAAK,IAAI,CAAC;IACnG,kCAAkC,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5E,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CACX,MAAM,EACN;YACE,IAAI,EAAE,GAAG,CAAC;YACV,QAAQ,EAAE,MAAM,CAAC;SAClB,CACF,CAAC;KACH,KAAK,IAAI,CAAC;IACX,0BAA0B,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DataSourceConfig, WidgetConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Called when the widget is ready to receive configuration from the host.
|
|
4
|
+
*/
|
|
5
|
+
export declare function onWidgetReady(callback: (config: WidgetConfig) => void): void;
|
|
6
|
+
/**
|
|
7
|
+
* Fetch a data source from the host platform.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fetchDataSource(payload: DataSourceConfig): Promise<any[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Add a reactive data source listener on the host platform.
|
|
12
|
+
* Returns an unsubscribe function.
|
|
13
|
+
*/
|
|
14
|
+
export declare function addDataSourceListener(config: DataSourceConfig, onChange: (value: any[]) => void): () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Emit a custom widget event to the host platform.
|
|
17
|
+
*/
|
|
18
|
+
export declare function emitWidgetEvent(type: string, args?: Record<string, any>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Update widget personalization state on the host platform.
|
|
21
|
+
*/
|
|
22
|
+
export declare function setWidgetPersonalization(data: any): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Register a listener for widget filter changes from the host platform.
|
|
25
|
+
* Returns an unsubscribe function.
|
|
26
|
+
*/
|
|
27
|
+
export declare function onWidgetFilterChange(callback: (condition: string) => void): () => void;
|
|
28
|
+
//# sourceMappingURL=widget-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-sdk.d.ts","sourceRoot":"","sources":["../src/widget-sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAmBzD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,QASrE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAI/E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CA6C5G;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,iBAI7E;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,GAAG,iBAIvD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA0BtF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodo/platform-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "goodo platform plugin sdk",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./host": {
|
|
19
|
+
"types": "./dist/host.d.ts",
|
|
20
|
+
"import": "./dist/host.js",
|
|
21
|
+
"require": "./dist/host.cjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"host": [
|
|
27
|
+
"./dist/host.d.ts"
|
|
28
|
+
],
|
|
29
|
+
"*": [
|
|
30
|
+
"./dist/*"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^26.2.0",
|
|
39
|
+
"typescript": "^5.5.4",
|
|
40
|
+
"vite": "^5.4.2",
|
|
41
|
+
"vite-plugin-dts": "^4.0.3"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"dev": "vite build --watch",
|
|
45
|
+
"build": "vite build",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|