@lark-apaas/aily-web-sdk 0.0.2-beta.1 → 0.0.2-beta.10
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/index.cjs +68 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -13
- package/dist/index.d.ts +44 -13
- package/dist/index.js +66 -27
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
DEFAULT_ANONYMOUS_CHANNEL_TYPE: () => DEFAULT_ANONYMOUS_CHANNEL_TYPE,
|
|
25
|
+
DEFAULT_CHANNEL_TYPE: () => DEFAULT_CHANNEL_TYPE,
|
|
24
26
|
IFRAME_BASE_URL: () => IFRAME_BASE_URL,
|
|
25
27
|
MESSAGE_TIMEOUT: () => MESSAGE_TIMEOUT,
|
|
26
28
|
getIframeBaseURL: () => getIframeBaseURL,
|
|
@@ -44,7 +46,7 @@ function getIframeBaseURL(env) {
|
|
|
44
46
|
if (hostname.includes("aiforce-boe")) {
|
|
45
47
|
return IFRAME_BASE_URL.boe;
|
|
46
48
|
}
|
|
47
|
-
if (hostname.includes("aiforce-pre")) {
|
|
49
|
+
if (hostname.includes("aiforce-pre") || hostname.includes("force-pre.feishuapp.net") || hostname.includes("fsapp.kundou.cn")) {
|
|
48
50
|
return IFRAME_BASE_URL.staging;
|
|
49
51
|
}
|
|
50
52
|
if (hostname.includes("localhost") || hostname.includes("127.0.0.1")) {
|
|
@@ -54,8 +56,8 @@ function getIframeBaseURL(env) {
|
|
|
54
56
|
}
|
|
55
57
|
__name(getIframeBaseURL, "getIframeBaseURL");
|
|
56
58
|
var MESSAGE_TIMEOUT = 30 * 1e3;
|
|
57
|
-
var
|
|
58
|
-
var
|
|
59
|
+
var DEFAULT_CHANNEL_TYPE = "MIAODA_CUI_SDK";
|
|
60
|
+
var DEFAULT_ANONYMOUS_CHANNEL_TYPE = "MIAODA_ANONYMOUS_CUI_SDK";
|
|
59
61
|
var ALLOWED_ORIGINS = [
|
|
60
62
|
"https://aily.feishu.cn",
|
|
61
63
|
"https://aily.feishu-pre.cn",
|
|
@@ -65,6 +67,9 @@ var ALLOWED_ORIGINS = [
|
|
|
65
67
|
"http://localhost:8080"
|
|
66
68
|
];
|
|
67
69
|
|
|
70
|
+
// src/iframe-manager.ts
|
|
71
|
+
var import_internal_slardar2 = require("@lark-apaas/internal-slardar");
|
|
72
|
+
|
|
68
73
|
// src/device-detector.ts
|
|
69
74
|
var MOBILE_BREAKPOINT = 768;
|
|
70
75
|
var DeviceDetector = class {
|
|
@@ -103,6 +108,7 @@ var DeviceDetector = class {
|
|
|
103
108
|
};
|
|
104
109
|
|
|
105
110
|
// src/message-bridge.ts
|
|
111
|
+
var import_internal_slardar = require("@lark-apaas/internal-slardar");
|
|
106
112
|
var MessageBridge = class {
|
|
107
113
|
static {
|
|
108
114
|
__name(this, "MessageBridge");
|
|
@@ -155,7 +161,13 @@ var MessageBridge = class {
|
|
|
155
161
|
};
|
|
156
162
|
const timer = setTimeout(() => {
|
|
157
163
|
this.pendingRequests.delete(id);
|
|
158
|
-
|
|
164
|
+
const error = new Error(`Message timeout: ${type}`);
|
|
165
|
+
import_internal_slardar.slardar.captureException(error, {
|
|
166
|
+
source: "aily-web-sdk",
|
|
167
|
+
module: "message-bridge",
|
|
168
|
+
messageType: type
|
|
169
|
+
});
|
|
170
|
+
reject(error);
|
|
159
171
|
}, MESSAGE_TIMEOUT);
|
|
160
172
|
this.pendingRequests.set(id, {
|
|
161
173
|
resolve,
|
|
@@ -198,7 +210,13 @@ var MessageBridge = class {
|
|
|
198
210
|
clearTimeout(pending.timer);
|
|
199
211
|
this.pendingRequests.delete(data.id);
|
|
200
212
|
if (data.error) {
|
|
201
|
-
|
|
213
|
+
const error = new Error(data.error.message);
|
|
214
|
+
import_internal_slardar.slardar.captureException(error, {
|
|
215
|
+
source: "aily-web-sdk",
|
|
216
|
+
module: "message-bridge",
|
|
217
|
+
errorCode: data.error.code
|
|
218
|
+
});
|
|
219
|
+
pending.reject(error);
|
|
202
220
|
} else {
|
|
203
221
|
pending.resolve(data.data);
|
|
204
222
|
}
|
|
@@ -236,7 +254,8 @@ var IframeManager = class {
|
|
|
236
254
|
constructor(root, config) {
|
|
237
255
|
this.root = root;
|
|
238
256
|
this.config = config;
|
|
239
|
-
|
|
257
|
+
const defaultChannel = config.anonymous ? DEFAULT_ANONYMOUS_CHANNEL_TYPE : DEFAULT_CHANNEL_TYPE;
|
|
258
|
+
this.channelType = config.common?.channelType || defaultChannel;
|
|
240
259
|
this.messageBridge = new MessageBridge(getIframeBaseURL());
|
|
241
260
|
}
|
|
242
261
|
/**
|
|
@@ -282,7 +301,7 @@ var IframeManager = class {
|
|
|
282
301
|
this.deviceDetector.destroy();
|
|
283
302
|
this.deviceDetector = null;
|
|
284
303
|
}
|
|
285
|
-
if (this.iframe
|
|
304
|
+
if (this.iframe?.parentNode) {
|
|
286
305
|
this.iframe.parentNode.removeChild(this.iframe);
|
|
287
306
|
}
|
|
288
307
|
this.iframe = null;
|
|
@@ -296,16 +315,25 @@ var IframeManager = class {
|
|
|
296
315
|
*/
|
|
297
316
|
buildIframeURL() {
|
|
298
317
|
const baseURL = getIframeBaseURL();
|
|
299
|
-
const { events, anonymous: _anonymous, ...configWithoutEvents } = this.config;
|
|
318
|
+
const { events: _events, anonymous: _anonymous, ...configWithoutEvents } = this.config;
|
|
319
|
+
const editorConfig = configWithoutEvents.editor?.skill ? {
|
|
320
|
+
...configWithoutEvents.editor,
|
|
321
|
+
skill: {
|
|
322
|
+
type: "custom",
|
|
323
|
+
builtinType: "unspecified",
|
|
324
|
+
...configWithoutEvents.editor.skill
|
|
325
|
+
}
|
|
326
|
+
} : configWithoutEvents.editor;
|
|
300
327
|
const iframeConfig = {
|
|
301
328
|
...configWithoutEvents,
|
|
302
329
|
common: {
|
|
303
330
|
...configWithoutEvents.common,
|
|
304
331
|
channelType: this.channelType,
|
|
305
|
-
resourceType: this.channelType
|
|
306
|
-
}
|
|
332
|
+
resourceType: configWithoutEvents.common?.resourceType || this.channelType
|
|
333
|
+
},
|
|
334
|
+
editor: editorConfig
|
|
307
335
|
};
|
|
308
|
-
const configBase64 = btoa(JSON.stringify(iframeConfig));
|
|
336
|
+
const configBase64 = btoa(encodeURIComponent(JSON.stringify(iframeConfig)));
|
|
309
337
|
const params = new URLSearchParams({
|
|
310
338
|
appKey: this.config.appKey,
|
|
311
339
|
config: encodeURIComponent(configBase64)
|
|
@@ -318,7 +346,12 @@ var IframeManager = class {
|
|
|
318
346
|
waitForReady() {
|
|
319
347
|
return new Promise((resolve, reject) => {
|
|
320
348
|
const timeout = setTimeout(() => {
|
|
321
|
-
|
|
349
|
+
const error = new Error("Iframe ready timeout");
|
|
350
|
+
import_internal_slardar2.slardar.captureException(error, {
|
|
351
|
+
source: "aily-web-sdk",
|
|
352
|
+
module: "iframe-ready"
|
|
353
|
+
});
|
|
354
|
+
reject(error);
|
|
322
355
|
}, 3e4);
|
|
323
356
|
const checkMessage = /* @__PURE__ */ __name((event) => {
|
|
324
357
|
if (event.data?.messageBizId === MESSAGE_BIZ_ID && event.data?.type === "event" && event.data?.eventName === "ready") {
|
|
@@ -339,30 +372,36 @@ var IframeManager = class {
|
|
|
339
372
|
if (events.onError) {
|
|
340
373
|
this.messageBridge.onEvent("error", (data) => {
|
|
341
374
|
const msg = data?.message || "Unknown error";
|
|
342
|
-
events.onError(new Error(msg));
|
|
375
|
+
events.onError?.(new Error(msg));
|
|
343
376
|
});
|
|
344
377
|
}
|
|
345
378
|
if (events.onMessage) {
|
|
346
379
|
this.messageBridge.onEvent("onMessage", events.onMessage);
|
|
347
380
|
}
|
|
381
|
+
if (events.onInited) {
|
|
382
|
+
this.messageBridge.onEvent("inited", events.onInited);
|
|
383
|
+
}
|
|
384
|
+
if (events.onInitedWithWelcome) {
|
|
385
|
+
this.messageBridge.onEvent("initedWithWelcome", events.onInitedWithWelcome);
|
|
386
|
+
}
|
|
387
|
+
if (events.onClose) {
|
|
388
|
+
this.messageBridge.onEvent("onClose", events.onClose);
|
|
389
|
+
}
|
|
348
390
|
}
|
|
349
391
|
reportInitMetrics(initStart) {
|
|
350
392
|
try {
|
|
351
393
|
const cost = performance.now() - initStart;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
394
|
+
import_internal_slardar2.slardar.sendEvent({
|
|
395
|
+
name: "cui_init_total",
|
|
396
|
+
metrics: {
|
|
397
|
+
cost
|
|
398
|
+
},
|
|
399
|
+
categories: {
|
|
400
|
+
arch: "iframe-static",
|
|
401
|
+
appKey: this.config.appKey,
|
|
402
|
+
channelType: this.channelType
|
|
403
|
+
}
|
|
404
|
+
});
|
|
366
405
|
} catch {
|
|
367
406
|
}
|
|
368
407
|
}
|
|
@@ -411,6 +450,8 @@ async function initAilyChat(root, config) {
|
|
|
411
450
|
__name(initAilyChat, "initAilyChat");
|
|
412
451
|
// Annotate the CommonJS export names for ESM import in node:
|
|
413
452
|
0 && (module.exports = {
|
|
453
|
+
DEFAULT_ANONYMOUS_CHANNEL_TYPE,
|
|
454
|
+
DEFAULT_CHANNEL_TYPE,
|
|
414
455
|
IFRAME_BASE_URL,
|
|
415
456
|
MESSAGE_TIMEOUT,
|
|
416
457
|
getIframeBaseURL,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/device-detector.ts","../src/message-bridge.ts","../src/iframe-manager.ts"],"sourcesContent":["/*\n * index.ts\n * Author: perterpon.wang<perterpon.wang@bytedance.com>\n * Create: Tue Feb 10 2026\n */\n\nimport { IframeManager } from './iframe-manager';\n\nimport type { ChatPanel, WebSDKConfig } from './types/config';\n\n/**\n * Initialize Aily Chat via iframe\n * This is the primary method for initializing the chat panel\n * @param root - Container element for the iframe\n * @param config - Configuration options\n * @returns ChatPanel instance for controlling the chat\n */\nexport async function initAilyChat(root: HTMLElement, config: WebSDKConfig): Promise<ChatPanel> {\n const manager = new IframeManager(root, config);\n return manager.init();\n}\n\n// Export types\nexport type {\n ChatPanel,\n WebSDKConfig,\n WebSDKCommonConfig,\n WebSDKConversionConfig,\n WebSDKEditorConfig,\n WebSDKEvents,\n} from './types/config';\n\n// Export constants\nexport { getIframeBaseURL, IFRAME_BASE_URL, MESSAGE_TIMEOUT } from './constants';\n\n// Export message types\nexport type {\n IframeMessageType,\n IframeRequestMessage,\n IframeResponseMessage,\n IframeEventMessage,\n IframeMessage,\n} from './types/message';\n","/**\n * Constants for Aily Web SDK\n */\n\n/** Message business ID for postMessage identification */\nexport const MESSAGE_BIZ_ID = 'cui-sdk-message' as const;\n\n/** Iframe base URLs by environment */\nexport const IFRAME_BASE_URL: Record<string, string> = {\n production: 'https://aily.feishu.cn/cui',\n staging: 'https://aily.feishu-pre.cn/cui',\n boe: 'https://aily.feishu-boe.cn/cui',\n development: 'http://localhost:8080/cui',\n};\n\n/**\n * Get iframe base URL based on environment\n */\nexport function getIframeBaseURL(env?: string): string {\n if (env && IFRAME_BASE_URL[env]) {\n return IFRAME_BASE_URL[env];\n }\n\n // Auto-detect based on hostname\n // MiaoDa 宿主域名: miaoda.feishu[-env].cn, *.aiforce[-env][-preview].bytedance.net, *.aiforce.run, *.aiforce.cloud\n const hostname = typeof window !== 'undefined' ? window.location.hostname : '';\n\n if (hostname.includes('aiforce-boe')) {\n return IFRAME_BASE_URL.boe;\n }\n if (hostname.includes('aiforce-pre')) {\n return IFRAME_BASE_URL.staging;\n }\n if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {\n return IFRAME_BASE_URL.development;\n }\n\n return IFRAME_BASE_URL.production;\n}\n\n/** Timeout for postMessage requests (30 seconds) */\nexport const MESSAGE_TIMEOUT = 30 * 1000;\n\n/** Miaoda 渠道标识(本 SDK 为 Miaoda 专用包,不允许外部自定义) */\nexport const CHANNEL_TYPE = 'MIAODA_CUI_SDK';\nexport const ANONYMOUS_CHANNEL_TYPE = 'MIAODA_ANONYMOUS_CUI_SDK';\n\n/** Allowed origins for postMessage (cui-iframe 的 origin) */\nexport const ALLOWED_ORIGINS = [\n 'https://aily.feishu.cn',\n 'https://aily.feishu-pre.cn',\n 'https://aily.feishu-boe.cn',\n 'http://localhost:3000',\n 'http://localhost:5173',\n 'http://localhost:8080',\n];\n","const MOBILE_BREAKPOINT = 768;\n\ntype DeviceType = 'pc' | 'mobile';\ntype DeviceChangeCallback = (device: DeviceType) => void;\n\n/**\n * 设备类型自动检测器\n *\n * 使用 matchMedia + 768px 断点检测设备类型,与 miaoda useIsMobile 逻辑一致。\n * 检测在宿主页面(而非 iframe)中执行,结果通过 postMessage 推送给 iframe。\n */\nexport class DeviceDetector {\n private mediaQuery: MediaQueryList;\n private currentDevice: DeviceType;\n private callback: DeviceChangeCallback | null = null;\n private handleChange: (() => void) | null = null;\n\n constructor() {\n this.mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n this.currentDevice = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n }\n\n getDevice(): DeviceType {\n return this.currentDevice;\n }\n\n observe(callback: DeviceChangeCallback): void {\n this.callback = callback;\n this.handleChange = () => {\n const newDevice: DeviceType = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n if (newDevice !== this.currentDevice) {\n this.currentDevice = newDevice;\n this.callback?.(newDevice);\n }\n };\n this.mediaQuery.addEventListener('change', this.handleChange);\n }\n\n destroy(): void {\n if (this.handleChange) {\n this.mediaQuery.removeEventListener('change', this.handleChange);\n this.handleChange = null;\n }\n this.callback = null;\n }\n}\n","import { MESSAGE_TIMEOUT, ALLOWED_ORIGINS } from './constants';\nimport {\n MESSAGE_BIZ_ID,\n type IframeMessage,\n type IframeRequestMessage,\n type IframeResponseMessage,\n type IframeEventMessage,\n type IframeMessageType,\n} from './types/message';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MessageBridge handles postMessage communication between parent and iframe\n */\nexport class MessageBridge {\n private iframe: HTMLIFrameElement | null = null;\n private requestId = 0;\n private pendingRequests = new Map<number, PendingRequest>();\n private eventHandlers = new Map<string, ((data: unknown) => void)[]>();\n private targetOrigin: string;\n\n constructor(targetOrigin: string) {\n this.targetOrigin = targetOrigin;\n this.handleMessage = this.handleMessage.bind(this);\n }\n\n /**\n * Attach to an iframe and start listening for messages\n */\n attach(iframe: HTMLIFrameElement): void {\n this.iframe = iframe;\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Detach from iframe and clean up\n */\n detach(): void {\n window.removeEventListener('message', this.handleMessage);\n this.iframe = null;\n // Clear all pending requests\n this.pendingRequests.forEach(({ reject }) => {\n reject(new Error('MessageBridge detached'));\n });\n this.pendingRequests.clear();\n }\n\n /**\n * Send a message to the iframe and wait for response\n */\n send<T = unknown>(type: IframeMessageType, data?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.iframe?.contentWindow) {\n reject(new Error('Iframe not attached'));\n return;\n }\n\n this.requestId += 1;\n const id = this.requestId;\n const message: IframeRequestMessage = {\n messageBizId: MESSAGE_BIZ_ID,\n id,\n type,\n data,\n timestamp: Date.now(),\n isRequest: true,\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Message timeout: ${type}`));\n }, MESSAGE_TIMEOUT);\n\n this.pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer });\n\n this.iframe.contentWindow.postMessage(message, this.targetOrigin);\n });\n }\n\n /**\n * Register an event handler\n */\n onEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n handlers.push(handler);\n this.eventHandlers.set(eventName, handlers);\n }\n\n /**\n * Remove an event handler\n */\n offEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n const index = handlers.indexOf(handler);\n if (index > -1) {\n handlers.splice(index, 1);\n this.eventHandlers.set(eventName, handlers);\n }\n }\n\n private handleMessage(event: MessageEvent<IframeMessage>): void {\n // Validate origin\n if (!ALLOWED_ORIGINS.includes(event.origin)) {\n return;\n }\n\n const { data } = event;\n\n // Validate message format\n if (data?.messageBizId !== MESSAGE_BIZ_ID) {\n return;\n }\n\n // Handle response messages\n if (this.isResponseMessage(data)) {\n const pending = this.pendingRequests.get(data.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(data.id);\n\n if (data.error) {\n pending.reject(new Error(data.error.message));\n } else {\n pending.resolve(data.data);\n }\n }\n }\n\n // Handle event messages\n if (this.isEventMessage(data)) {\n const handlers = this.eventHandlers.get(data.eventName) || [];\n handlers.forEach((handler) => {\n try {\n handler(data.data);\n } catch {\n // Ignore handler errors\n }\n });\n }\n }\n\n private isResponseMessage(data: IframeMessage): data is IframeResponseMessage {\n return 'isResponse' in data && data.isResponse === true;\n }\n\n private isEventMessage(data: IframeMessage): data is IframeEventMessage {\n return data.type === 'event';\n }\n}\n","import { getIframeBaseURL, MESSAGE_BIZ_ID, CHANNEL_TYPE, ANONYMOUS_CHANNEL_TYPE } from './constants';\nimport { DeviceDetector } from './device-detector';\nimport { MessageBridge } from './message-bridge';\n\nimport type { WebSDKConfig, ChatPanel } from './types/config';\n\n/**\n * IframeManager handles iframe creation and lifecycle\n *\n * iframe 采用自初始化模式:配置通过 URL hash 传入,iframe 加载后自行解析并渲染,\n * 与旧版 copilot iframe 的行为保持一致。宿主通过 postMessage 进行后续操作。\n */\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private messageBridge: MessageBridge;\n private config: WebSDKConfig;\n private channelType: string;\n private root: HTMLElement;\n private deviceDetector: DeviceDetector | null = null;\n\n constructor(root: HTMLElement, config: WebSDKConfig) {\n this.root = root;\n this.config = config;\n this.channelType = config.anonymous ? ANONYMOUS_CHANNEL_TYPE : CHANNEL_TYPE;\n this.messageBridge = new MessageBridge(getIframeBaseURL());\n }\n\n /**\n * Initialize the iframe and establish communication\n * 配置通过 URL hash 编码传入 iframe,iframe 加载后自行初始化\n */\n async init(): Promise<ChatPanel> {\n const initStart = performance.now();\n\n // Auto-detect device if not explicitly provided\n if (!this.config.device) {\n this.deviceDetector = new DeviceDetector();\n this.config.device = this.deviceDetector.getDevice();\n }\n\n // Create iframe element\n this.iframe = document.createElement('iframe');\n this.iframe.src = this.buildIframeURL();\n this.iframe.style.width = '100%';\n this.iframe.style.height = '100%';\n this.iframe.style.border = 'none';\n this.iframe.allow = 'microphone; clipboard-write';\n\n // Attach to DOM\n this.root.appendChild(this.iframe);\n\n // Attach message bridge (开始监听消息)\n this.messageBridge.attach(this.iframe);\n\n // Setup event handlers (在 iframe 初始化完成之前就开始监听,确保不丢失事件)\n this.setupEventHandlers();\n\n // Listen for device changes and push updates to iframe\n if (this.deviceDetector) {\n this.deviceDetector.observe((newDevice) => {\n this.messageBridge.send('updateConfig', { device: newDevice });\n });\n }\n\n // Wait for iframe to finish initialization (iframe 自行读取 URL 配置并渲染,完成后发送 ready 事件)\n await this.waitForReady();\n\n // 上报 cui_init_total 耗时(iframe 创建 → init 完成)\n this.reportInitMetrics(initStart);\n\n // Return ChatPanel interface\n return this.createChatPanel();\n }\n\n /**\n * Destroy the iframe and cleanup\n */\n async destroy(): Promise<void> {\n try {\n await this.messageBridge.send('destroy');\n } catch {\n // Ignore errors during destroy\n }\n\n this.messageBridge.detach();\n\n if (this.deviceDetector) {\n this.deviceDetector.destroy();\n this.deviceDetector = null;\n }\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n this.iframe = null;\n }\n\n /**\n * 构建 iframe URL,将配置编码到 hash 中\n * 格式: {baseURL}#{params} 其中 config 为 base64(JSON.stringify(configWithoutEvents))\n *\n * 鉴权说明:SDK 不参与 token 获取/传递。iframe 自行处理飞书登录流程,\n * 允许用户在 iframe 中独立登录。匿名渠道跳过登录检查。\n */\n private buildIframeURL(): string {\n const baseURL = getIframeBaseURL();\n\n // 移除 events(函数无法序列化)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { events, anonymous: _anonymous, ...configWithoutEvents } = this.config;\n // 注入 channelType/resourceType(SDK 内部控制,不暴露给消费者)\n const iframeConfig = {\n ...configWithoutEvents,\n common: {\n ...configWithoutEvents.common,\n channelType: this.channelType,\n resourceType: this.channelType,\n },\n };\n const configBase64 = btoa(JSON.stringify(iframeConfig));\n\n const params = new URLSearchParams({\n appKey: this.config.appKey,\n config: encodeURIComponent(configBase64),\n });\n\n return `${baseURL}#${params.toString()}`;\n }\n\n /**\n * 等待 iframe 自行初始化完成后发送 ready 事件\n */\n private waitForReady(): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Iframe ready timeout'));\n }, 30000);\n\n const checkMessage = (event: MessageEvent) => {\n if (\n event.data?.messageBizId === MESSAGE_BIZ_ID &&\n event.data?.type === 'event' &&\n event.data?.eventName === 'ready'\n ) {\n clearTimeout(timeout);\n window.removeEventListener('message', checkMessage);\n resolve();\n }\n };\n\n window.addEventListener('message', checkMessage);\n });\n }\n\n private setupEventHandlers(): void {\n const { events } = this.config;\n if (!events) return;\n\n if (events.onReady) {\n this.messageBridge.onEvent('ready', events.onReady);\n }\n\n if (events.onError) {\n this.messageBridge.onEvent('error', (data: unknown) => {\n const msg = (data as { message?: string })?.message || 'Unknown error';\n events.onError!(new Error(msg));\n });\n }\n\n if (events.onMessage) {\n this.messageBridge.onEvent('onMessage', events.onMessage);\n }\n }\n\n private reportInitMetrics(initStart: number): void {\n try {\n const cost = performance.now() - initStart;\n const Slardar = (window as unknown as Record<string, unknown>).__Slardar as\n | { sendEvent?: (event: Record<string, unknown>) => void }\n | undefined;\n if (Slardar?.sendEvent) {\n Slardar.sendEvent({\n name: 'cui_init_total',\n metrics: { cost },\n categories: {\n arch: 'iframe-static',\n appKey: this.config.appKey,\n channelType: this.channelType,\n },\n });\n }\n } catch {\n // 性能上报失败不影响功能\n }\n }\n\n private createChatPanel(): ChatPanel {\n return {\n sendMessage: async (data) => {\n await this.messageBridge.send('sendMessage', data);\n },\n clear: async () => {\n await this.messageBridge.send('clear');\n },\n cancelMessage: async (messageItem) => {\n await this.messageBridge.send('cancelMessage', messageItem);\n },\n clearAndStop: async () => {\n await this.messageBridge.send('clearAndStop');\n },\n updateWelcomeMessage: async () => {\n await this.messageBridge.send('updateWelcomeMessage');\n },\n updateConfig: async (config) => {\n await this.messageBridge.send('updateConfig', config);\n },\n observeSkill: async (skillId, name, skillType) => {\n await this.messageBridge.send('observeSkill', { skillId, name, skillType });\n },\n setCurrentSkill: async (skill) => {\n await this.messageBridge.send('setCurrentSkill', skill);\n },\n destroy: async () => {\n await this.destroy();\n },\n };\n }\n}\n"],"mappings":"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","names":["MESSAGE_BIZ_ID","IFRAME_BASE_URL","production","staging","boe","development","getIframeBaseURL","env","hostname","window","location","includes","MESSAGE_TIMEOUT","CHANNEL_TYPE","ANONYMOUS_CHANNEL_TYPE","ALLOWED_ORIGINS","MOBILE_BREAKPOINT","DeviceDetector","mediaQuery","currentDevice","callback","handleChange","window","matchMedia","innerWidth","getDevice","observe","newDevice","addEventListener","destroy","removeEventListener","MessageBridge","iframe","requestId","pendingRequests","Map","eventHandlers","targetOrigin","handleMessage","bind","attach","window","addEventListener","detach","removeEventListener","forEach","reject","Error","clear","send","type","data","Promise","resolve","contentWindow","id","message","messageBizId","MESSAGE_BIZ_ID","timestamp","Date","now","isRequest","timer","setTimeout","delete","MESSAGE_TIMEOUT","set","postMessage","onEvent","eventName","handler","handlers","get","push","offEvent","index","indexOf","splice","event","ALLOWED_ORIGINS","includes","origin","isResponseMessage","pending","clearTimeout","error","isEventMessage","isResponse","IframeManager","iframe","messageBridge","config","channelType","root","deviceDetector","anonymous","ANONYMOUS_CHANNEL_TYPE","CHANNEL_TYPE","MessageBridge","getIframeBaseURL","init","initStart","performance","now","device","DeviceDetector","getDevice","document","createElement","src","buildIframeURL","style","width","height","border","allow","appendChild","attach","setupEventHandlers","observe","newDevice","send","waitForReady","reportInitMetrics","createChatPanel","destroy","detach","parentNode","removeChild","baseURL","events","_anonymous","configWithoutEvents","iframeConfig","common","resourceType","configBase64","btoa","JSON","stringify","params","URLSearchParams","appKey","encodeURIComponent","toString","Promise","resolve","reject","timeout","setTimeout","Error","checkMessage","event","data","messageBizId","MESSAGE_BIZ_ID","type","eventName","clearTimeout","window","removeEventListener","addEventListener","onReady","onEvent","onError","msg","message","onMessage","cost","Slardar","__Slardar","sendEvent","name","metrics","categories","arch","sendMessage","clear","cancelMessage","messageItem","clearAndStop","updateWelcomeMessage","updateConfig","observeSkill","skillId","skillType","setCurrentSkill","skill","initAilyChat","root","config","manager","IframeManager","init"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/iframe-manager.ts","../src/device-detector.ts","../src/message-bridge.ts"],"sourcesContent":["/*\n * index.ts\n * Author: perterpon.wang<perterpon.wang@bytedance.com>\n * Create: Tue Feb 10 2026\n */\n\nimport { IframeManager } from \"./iframe-manager\";\n\nimport type { ChatPanel, WebSDKConfig } from \"./types/config\";\n\n/**\n * Initialize Aily Chat via iframe\n * This is the primary method for initializing the chat panel\n * @param root - Container element for the iframe\n * @param config - Configuration options\n * @returns ChatPanel instance for controlling the chat\n */\nexport async function initAilyChat(\n\troot: HTMLElement,\n\tconfig: WebSDKConfig,\n): Promise<ChatPanel> {\n\tconst manager = new IframeManager(root, config);\n\treturn manager.init();\n}\n\n// Export constants\nexport {\n\tDEFAULT_ANONYMOUS_CHANNEL_TYPE,\n\tDEFAULT_CHANNEL_TYPE,\n\tgetIframeBaseURL,\n\tIFRAME_BASE_URL,\n\tMESSAGE_TIMEOUT,\n} from \"./constants\";\n// Export types\nexport type {\n\tChatPanel,\n\tWebSDKCommonConfig,\n\tWebSDKConfig,\n\tWebSDKConversionConfig,\n\tWebSDKEditorConfig,\n\tWebSDKEvents,\n\tWebSDKSkillInfo,\n} from \"./types/config\";\n\n// Export message types\nexport type {\n\tIframeEventMessage,\n\tIframeMessage,\n\tIframeMessageType,\n\tIframeRequestMessage,\n\tIframeResponseMessage,\n} from \"./types/message\";\n","/**\n * Constants for Aily Web SDK\n */\n\n/** Message business ID for postMessage identification */\nexport const MESSAGE_BIZ_ID = \"cui-sdk-message\" as const;\n\n/** Iframe base URLs by environment */\nexport const IFRAME_BASE_URL: Record<string, string> = {\n\tproduction: \"https://aily.feishu.cn/cui\",\n\tstaging: \"https://aily.feishu-pre.cn/cui\",\n\tboe: \"https://aily.feishu-boe.cn/cui\",\n\tdevelopment: \"http://localhost:8080/cui\",\n};\n\n/**\n * Get iframe base URL based on environment\n */\nexport function getIframeBaseURL(env?: string): string {\n\tif (env && IFRAME_BASE_URL[env]) {\n\t\treturn IFRAME_BASE_URL[env];\n\t}\n\n\t// Auto-detect based on hostname\n\t// MiaoDa 宿主域名: miaoda.feishu[-env].cn, *.aiforce[-env][-preview].bytedance.net, *.force-pre.feishuapp.net, *.fsapp.kundou.cn, *.aiforce.run, *.aiforce.cloud\n\tconst hostname =\n\t\ttypeof window !== \"undefined\" ? window.location.hostname : \"\";\n\n\tif (hostname.includes(\"aiforce-boe\")) {\n\t\treturn IFRAME_BASE_URL.boe;\n\t}\n\tif (\n\t\thostname.includes(\"aiforce-pre\") ||\n\t\thostname.includes(\"force-pre.feishuapp.net\") ||\n\t\thostname.includes(\"fsapp.kundou.cn\")\n\t) {\n\t\treturn IFRAME_BASE_URL.staging;\n\t}\n\tif (hostname.includes(\"localhost\") || hostname.includes(\"127.0.0.1\")) {\n\t\treturn IFRAME_BASE_URL.development;\n\t}\n\n\treturn IFRAME_BASE_URL.production;\n}\n\n/** Timeout for postMessage requests (30 seconds) */\nexport const MESSAGE_TIMEOUT = 30 * 1000;\n\n/** Miaoda 渠道标识默认值(用户可通过 config.common.channelType 覆盖) */\nexport const DEFAULT_CHANNEL_TYPE = \"MIAODA_CUI_SDK\";\nexport const DEFAULT_ANONYMOUS_CHANNEL_TYPE = \"MIAODA_ANONYMOUS_CUI_SDK\";\n\n/** Allowed origins for postMessage (cui-iframe 的 origin) */\nexport const ALLOWED_ORIGINS = [\n\t\"https://aily.feishu.cn\",\n\t\"https://aily.feishu-pre.cn\",\n\t\"https://aily.feishu-boe.cn\",\n\t\"http://localhost:3000\",\n\t\"http://localhost:5173\",\n\t\"http://localhost:8080\",\n];\n","import {\n\tDEFAULT_ANONYMOUS_CHANNEL_TYPE,\n\tDEFAULT_CHANNEL_TYPE,\n\tgetIframeBaseURL,\n\tMESSAGE_BIZ_ID,\n} from \"./constants\";\nimport { slardar } from \"@lark-apaas/internal-slardar\";\nimport { DeviceDetector } from \"./device-detector\";\nimport { MessageBridge } from \"./message-bridge\";\n\nimport type { ChatPanel, WebSDKConfig } from \"./types/config\";\n\n/**\n * IframeManager handles iframe creation and lifecycle\n *\n * iframe 采用自初始化模式:配置通过 URL hash 传入,iframe 加载后自行解析并渲染,\n * 与旧版 copilot iframe 的行为保持一致。宿主通过 postMessage 进行后续操作。\n */\nexport class IframeManager {\n\tprivate iframe: HTMLIFrameElement | null = null;\n\tprivate messageBridge: MessageBridge;\n\tprivate config: WebSDKConfig;\n\tprivate channelType: string;\n\tprivate root: HTMLElement;\n\tprivate deviceDetector: DeviceDetector | null = null;\n\n\tconstructor(root: HTMLElement, config: WebSDKConfig) {\n\t\tthis.root = root;\n\t\tthis.config = config;\n\t\tconst defaultChannel = config.anonymous\n\t\t\t? DEFAULT_ANONYMOUS_CHANNEL_TYPE\n\t\t\t: DEFAULT_CHANNEL_TYPE;\n\t\tthis.channelType = config.common?.channelType || defaultChannel;\n\t\tthis.messageBridge = new MessageBridge(getIframeBaseURL());\n\t}\n\n\t/**\n\t * Initialize the iframe and establish communication\n\t * 配置通过 URL hash 编码传入 iframe,iframe 加载后自行初始化\n\t */\n\tasync init(): Promise<ChatPanel> {\n\t\tconst initStart = performance.now();\n\n\t\t// Auto-detect device if not explicitly provided\n\t\tif (!this.config.device) {\n\t\t\tthis.deviceDetector = new DeviceDetector();\n\t\t\tthis.config.device = this.deviceDetector.getDevice();\n\t\t}\n\n\t\t// Create iframe element\n\t\tthis.iframe = document.createElement(\"iframe\");\n\t\tthis.iframe.src = this.buildIframeURL();\n\t\tthis.iframe.style.width = \"100%\";\n\t\tthis.iframe.style.height = \"100%\";\n\t\tthis.iframe.style.border = \"none\";\n\t\tthis.iframe.allow = \"microphone; clipboard-write\";\n\n\t\t// Attach to DOM\n\t\tthis.root.appendChild(this.iframe);\n\n\t\t// Attach message bridge (开始监听消息)\n\t\tthis.messageBridge.attach(this.iframe);\n\n\t\t// Setup event handlers (在 iframe 初始化完成之前就开始监听,确保不丢失事件)\n\t\tthis.setupEventHandlers();\n\n\t\t// Listen for device changes and push updates to iframe\n\t\tif (this.deviceDetector) {\n\t\t\tthis.deviceDetector.observe((newDevice) => {\n\t\t\t\tthis.messageBridge.send(\"updateConfig\", { device: newDevice });\n\t\t\t});\n\t\t}\n\n\t\t// Wait for iframe to finish initialization (iframe 自行读取 URL 配置并渲染,完成后发送 ready 事件)\n\t\tawait this.waitForReady();\n\n\t\t// 上报 cui_init_total 耗时(iframe 创建 → init 完成)\n\t\tthis.reportInitMetrics(initStart);\n\n\t\t// Return ChatPanel interface\n\t\treturn this.createChatPanel();\n\t}\n\n\t/**\n\t * Destroy the iframe and cleanup\n\t */\n\tasync destroy(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.messageBridge.send(\"destroy\");\n\t\t} catch {\n\t\t\t// Ignore errors during destroy\n\t\t}\n\n\t\tthis.messageBridge.detach();\n\n\t\tif (this.deviceDetector) {\n\t\t\tthis.deviceDetector.destroy();\n\t\t\tthis.deviceDetector = null;\n\t\t}\n\n\t\tif (this.iframe?.parentNode) {\n\t\t\tthis.iframe.parentNode.removeChild(this.iframe);\n\t\t}\n\n\t\tthis.iframe = null;\n\t}\n\n\t/**\n\t * 构建 iframe URL,将配置编码到 hash 中\n\t * 格式: {baseURL}#{params} 其中 config 为 base64(JSON.stringify(configWithoutEvents))\n\t *\n\t * 鉴权说明:SDK 不参与 token 获取/传递。iframe 自行处理飞书登录流程,\n\t * 允许用户在 iframe 中独立登录。匿名渠道跳过登录检查。\n\t */\n\tprivate buildIframeURL(): string {\n\t\tconst baseURL = getIframeBaseURL();\n\n\t\t// 移除 events(函数无法序列化)\n\t\tconst {\n\t\t\tevents: _events,\n\t\t\tanonymous: _anonymous,\n\t\t\t...configWithoutEvents\n\t\t} = this.config;\n\t\t// 注入 channelType/resourceType(SDK 内部控制,不暴露给消费者)\n\t\t// 补全 editor.skill 硬编码字段\n\t\tconst editorConfig = configWithoutEvents.editor?.skill\n\t\t\t? {\n\t\t\t\t\t...configWithoutEvents.editor,\n\t\t\t\t\tskill: {\n\t\t\t\t\t\ttype: \"custom\",\n\t\t\t\t\t\tbuiltinType: \"unspecified\",\n\t\t\t\t\t\t...configWithoutEvents.editor.skill,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: configWithoutEvents.editor;\n\n\t\tconst iframeConfig = {\n\t\t\t...configWithoutEvents,\n\t\t\tcommon: {\n\t\t\t\t...configWithoutEvents.common,\n\t\t\t\tchannelType: this.channelType,\n\t\t\t\tresourceType:\n\t\t\t\t\tconfigWithoutEvents.common?.resourceType || this.channelType,\n\t\t\t},\n\t\t\teditor: editorConfig,\n\t\t};\n\t\tconst configBase64 = btoa(encodeURIComponent(JSON.stringify(iframeConfig)));\n\n\t\tconst params = new URLSearchParams({\n\t\t\tappKey: this.config.appKey,\n\t\t\tconfig: encodeURIComponent(configBase64),\n\t\t});\n\n\t\treturn `${baseURL}#${params.toString()}`;\n\t}\n\n\t/**\n\t * 等待 iframe 自行初始化完成后发送 ready 事件\n\t */\n\tprivate waitForReady(): Promise<void> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\tconst error = new Error(\"Iframe ready timeout\");\n\t\t\t\tslardar.captureException(error, { source: 'aily-web-sdk', module: 'iframe-ready' });\n\t\t\t\treject(error);\n\t\t\t}, 30000);\n\n\t\t\tconst checkMessage = (event: MessageEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tevent.data?.messageBizId === MESSAGE_BIZ_ID &&\n\t\t\t\t\tevent.data?.type === \"event\" &&\n\t\t\t\t\tevent.data?.eventName === \"ready\"\n\t\t\t\t) {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\twindow.removeEventListener(\"message\", checkMessage);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\twindow.addEventListener(\"message\", checkMessage);\n\t\t});\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tconst { events } = this.config;\n\t\tif (!events) return;\n\n\t\tif (events.onReady) {\n\t\t\tthis.messageBridge.onEvent(\"ready\", events.onReady);\n\t\t}\n\n\t\tif (events.onError) {\n\t\t\tthis.messageBridge.onEvent(\"error\", (data: unknown) => {\n\t\t\t\tconst msg = (data as { message?: string })?.message || \"Unknown error\";\n\t\t\t\tevents.onError?.(new Error(msg));\n\t\t\t});\n\t\t}\n\n\t\tif (events.onMessage) {\n\t\t\tthis.messageBridge.onEvent(\"onMessage\", events.onMessage);\n\t\t}\n\n\t\tif (events.onInited) {\n\t\t\tthis.messageBridge.onEvent(\n\t\t\t\t\"inited\",\n\t\t\t\tevents.onInited as (data: unknown) => void,\n\t\t\t);\n\t\t}\n\n\t\tif (events.onInitedWithWelcome) {\n\t\t\tthis.messageBridge.onEvent(\n\t\t\t\t\"initedWithWelcome\",\n\t\t\t\tevents.onInitedWithWelcome,\n\t\t\t);\n\t\t}\n\n\t\tif (events.onClose) {\n\t\t\tthis.messageBridge.onEvent(\"onClose\", events.onClose);\n\t\t}\n\t}\n\n\tprivate reportInitMetrics(initStart: number): void {\n\t\ttry {\n\t\t\tconst cost = performance.now() - initStart;\n\t\t\tslardar.sendEvent({\n\t\t\t\tname: \"cui_init_total\",\n\t\t\t\tmetrics: { cost },\n\t\t\t\tcategories: {\n\t\t\t\t\tarch: \"iframe-static\",\n\t\t\t\t\tappKey: this.config.appKey,\n\t\t\t\t\tchannelType: this.channelType,\n\t\t\t\t},\n\t\t\t});\n\t\t} catch {\n\t\t\t// 性能上报失败不影响功能\n\t\t}\n\t}\n\n\tprivate createChatPanel(): ChatPanel {\n\t\treturn {\n\t\t\tsendMessage: async (data) => {\n\t\t\t\tawait this.messageBridge.send(\"sendMessage\", data);\n\t\t\t},\n\t\t\tclear: async () => {\n\t\t\t\tawait this.messageBridge.send(\"clear\");\n\t\t\t},\n\t\t\tcancelMessage: async (messageItem) => {\n\t\t\t\tawait this.messageBridge.send(\"cancelMessage\", messageItem);\n\t\t\t},\n\t\t\tclearAndStop: async () => {\n\t\t\t\tawait this.messageBridge.send(\"clearAndStop\");\n\t\t\t},\n\t\t\tupdateWelcomeMessage: async () => {\n\t\t\t\tawait this.messageBridge.send(\"updateWelcomeMessage\");\n\t\t\t},\n\t\t\tupdateConfig: async (config) => {\n\t\t\t\tawait this.messageBridge.send(\"updateConfig\", config);\n\t\t\t},\n\t\t\tobserveSkill: async (skillId, name, skillType) => {\n\t\t\t\tawait this.messageBridge.send(\"observeSkill\", {\n\t\t\t\t\tskillId,\n\t\t\t\t\tname,\n\t\t\t\t\tskillType,\n\t\t\t\t});\n\t\t\t},\n\t\t\tsetCurrentSkill: async (skill) => {\n\t\t\t\tawait this.messageBridge.send(\"setCurrentSkill\", skill);\n\t\t\t},\n\t\t\tdestroy: async () => {\n\t\t\t\tawait this.destroy();\n\t\t\t},\n\t\t};\n\t}\n}\n","const MOBILE_BREAKPOINT = 768;\n\ntype DeviceType = 'pc' | 'mobile';\ntype DeviceChangeCallback = (device: DeviceType) => void;\n\n/**\n * 设备类型自动检测器\n *\n * 使用 matchMedia + 768px 断点检测设备类型,与 miaoda useIsMobile 逻辑一致。\n * 检测在宿主页面(而非 iframe)中执行,结果通过 postMessage 推送给 iframe。\n */\nexport class DeviceDetector {\n private mediaQuery: MediaQueryList;\n private currentDevice: DeviceType;\n private callback: DeviceChangeCallback | null = null;\n private handleChange: (() => void) | null = null;\n\n constructor() {\n this.mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n this.currentDevice = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n }\n\n getDevice(): DeviceType {\n return this.currentDevice;\n }\n\n observe(callback: DeviceChangeCallback): void {\n this.callback = callback;\n this.handleChange = () => {\n const newDevice: DeviceType = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n if (newDevice !== this.currentDevice) {\n this.currentDevice = newDevice;\n this.callback?.(newDevice);\n }\n };\n this.mediaQuery.addEventListener('change', this.handleChange);\n }\n\n destroy(): void {\n if (this.handleChange) {\n this.mediaQuery.removeEventListener('change', this.handleChange);\n this.handleChange = null;\n }\n this.callback = null;\n }\n}\n","import { MESSAGE_TIMEOUT, ALLOWED_ORIGINS } from './constants';\nimport {\n MESSAGE_BIZ_ID,\n type IframeMessage,\n type IframeRequestMessage,\n type IframeResponseMessage,\n type IframeEventMessage,\n type IframeMessageType,\n} from './types/message';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MessageBridge handles postMessage communication between parent and iframe\n */\nexport class MessageBridge {\n private iframe: HTMLIFrameElement | null = null;\n private requestId = 0;\n private pendingRequests = new Map<number, PendingRequest>();\n private eventHandlers = new Map<string, ((data: unknown) => void)[]>();\n private targetOrigin: string;\n\n constructor(targetOrigin: string) {\n this.targetOrigin = targetOrigin;\n this.handleMessage = this.handleMessage.bind(this);\n }\n\n /**\n * Attach to an iframe and start listening for messages\n */\n attach(iframe: HTMLIFrameElement): void {\n this.iframe = iframe;\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Detach from iframe and clean up\n */\n detach(): void {\n window.removeEventListener('message', this.handleMessage);\n this.iframe = null;\n // Clear all pending requests\n this.pendingRequests.forEach(({ reject }) => {\n reject(new Error('MessageBridge detached'));\n });\n this.pendingRequests.clear();\n }\n\n /**\n * Send a message to the iframe and wait for response\n */\n send<T = unknown>(type: IframeMessageType, data?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.iframe?.contentWindow) {\n reject(new Error('Iframe not attached'));\n return;\n }\n\n this.requestId += 1;\n const id = this.requestId;\n const message: IframeRequestMessage = {\n messageBizId: MESSAGE_BIZ_ID,\n id,\n type,\n data,\n timestamp: Date.now(),\n isRequest: true,\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n const error = new Error(`Message timeout: ${type}`);\n slardar.captureException(error, { source: 'aily-web-sdk', module: 'message-bridge', messageType: type });\n reject(error);\n }, MESSAGE_TIMEOUT);\n\n this.pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer });\n\n this.iframe.contentWindow.postMessage(message, this.targetOrigin);\n });\n }\n\n /**\n * Register an event handler\n */\n onEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n handlers.push(handler);\n this.eventHandlers.set(eventName, handlers);\n }\n\n /**\n * Remove an event handler\n */\n offEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n const index = handlers.indexOf(handler);\n if (index > -1) {\n handlers.splice(index, 1);\n this.eventHandlers.set(eventName, handlers);\n }\n }\n\n private handleMessage(event: MessageEvent<IframeMessage>): void {\n // Validate origin\n if (!ALLOWED_ORIGINS.includes(event.origin)) {\n return;\n }\n\n const { data } = event;\n\n // Validate message format\n if (data?.messageBizId !== MESSAGE_BIZ_ID) {\n return;\n }\n\n // Handle response messages\n if (this.isResponseMessage(data)) {\n const pending = this.pendingRequests.get(data.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(data.id);\n\n if (data.error) {\n const error = new Error(data.error.message);\n slardar.captureException(error, { source: 'aily-web-sdk', module: 'message-bridge', errorCode: data.error.code });\n pending.reject(error);\n } else {\n pending.resolve(data.data);\n }\n }\n }\n\n // Handle event messages\n if (this.isEventMessage(data)) {\n const handlers = this.eventHandlers.get(data.eventName) || [];\n handlers.forEach((handler) => {\n try {\n handler(data.data);\n } catch {\n // Ignore handler errors\n }\n });\n }\n }\n\n private isResponseMessage(data: IframeMessage): data is IframeResponseMessage {\n return 'isResponse' in data && data.isResponse === true;\n }\n\n private isEventMessage(data: IframeMessage): data is IframeEventMessage {\n return data.type === 'event';\n }\n}\n"],"mappings":"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","names":["MESSAGE_BIZ_ID","IFRAME_BASE_URL","production","staging","boe","development","getIframeBaseURL","env","hostname","window","location","includes","MESSAGE_TIMEOUT","DEFAULT_CHANNEL_TYPE","DEFAULT_ANONYMOUS_CHANNEL_TYPE","ALLOWED_ORIGINS","import_internal_slardar","MOBILE_BREAKPOINT","DeviceDetector","mediaQuery","currentDevice","callback","handleChange","window","matchMedia","innerWidth","getDevice","observe","newDevice","addEventListener","destroy","removeEventListener","MessageBridge","iframe","requestId","pendingRequests","Map","eventHandlers","targetOrigin","handleMessage","bind","attach","window","addEventListener","detach","removeEventListener","forEach","reject","Error","clear","send","type","data","Promise","resolve","contentWindow","id","message","messageBizId","MESSAGE_BIZ_ID","timestamp","Date","now","isRequest","timer","setTimeout","delete","error","slardar","captureException","source","module","messageType","MESSAGE_TIMEOUT","set","postMessage","onEvent","eventName","handler","handlers","get","push","offEvent","index","indexOf","splice","event","ALLOWED_ORIGINS","includes","origin","isResponseMessage","pending","clearTimeout","errorCode","code","isEventMessage","isResponse","IframeManager","iframe","messageBridge","config","channelType","root","deviceDetector","defaultChannel","anonymous","DEFAULT_ANONYMOUS_CHANNEL_TYPE","DEFAULT_CHANNEL_TYPE","common","MessageBridge","getIframeBaseURL","init","initStart","performance","now","device","DeviceDetector","getDevice","document","createElement","src","buildIframeURL","style","width","height","border","allow","appendChild","attach","setupEventHandlers","observe","newDevice","send","waitForReady","reportInitMetrics","createChatPanel","destroy","detach","parentNode","removeChild","baseURL","events","_events","_anonymous","configWithoutEvents","editorConfig","editor","skill","type","builtinType","iframeConfig","resourceType","configBase64","btoa","encodeURIComponent","JSON","stringify","params","URLSearchParams","appKey","toString","Promise","resolve","reject","timeout","setTimeout","error","Error","slardar","captureException","source","module","checkMessage","event","data","messageBizId","MESSAGE_BIZ_ID","eventName","clearTimeout","window","removeEventListener","addEventListener","onReady","onEvent","onError","msg","message","onMessage","onInited","onInitedWithWelcome","onClose","cost","sendEvent","name","metrics","categories","arch","sendMessage","clear","cancelMessage","messageItem","clearAndStop","updateWelcomeMessage","updateConfig","observeSkill","skillId","skillType","setCurrentSkill","initAilyChat","root","config","manager","IframeManager","init"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
interface WebSDKCommonConfig {
|
|
8
8
|
/** Base URL for the Aily service */
|
|
9
|
-
baseURL
|
|
9
|
+
baseURL?: string;
|
|
10
10
|
/** Additional headers to send with requests */
|
|
11
11
|
headers?: Record<string, string>;
|
|
12
12
|
/** APAAS application info for API authentication */
|
|
@@ -15,6 +15,14 @@ interface WebSDKCommonConfig {
|
|
|
15
15
|
tenantID?: number;
|
|
16
16
|
tenantType?: number;
|
|
17
17
|
};
|
|
18
|
+
/** 渠道标识,不再硬编码 */
|
|
19
|
+
channelType?: string;
|
|
20
|
+
/** 资源类型,默认等于 channelType */
|
|
21
|
+
resourceType?: string;
|
|
22
|
+
/** 传给后端 API 的额外上下文 */
|
|
23
|
+
channelContext?: Record<string, unknown>;
|
|
24
|
+
/** 最小轮询间隔 (ms),默认 2000 */
|
|
25
|
+
minPollInterval?: number;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
20
28
|
* Event callbacks
|
|
@@ -26,6 +34,16 @@ interface WebSDKEvents {
|
|
|
26
34
|
onError?: (error: Error) => void;
|
|
27
35
|
/** Callback when a message is received */
|
|
28
36
|
onMessage?: (message: unknown) => void;
|
|
37
|
+
/** 会话创建完成、轮询开始后触发 */
|
|
38
|
+
onInited?: (data: {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
conversationId: string;
|
|
41
|
+
handler: string;
|
|
42
|
+
}) => void;
|
|
43
|
+
/** 欢迎消息创建完成后触发 */
|
|
44
|
+
onInitedWithWelcome?: () => void;
|
|
45
|
+
/** 用户点击关闭按钮时触发 */
|
|
46
|
+
onClose?: () => void;
|
|
29
47
|
}
|
|
30
48
|
/**
|
|
31
49
|
* Conversion (conversation) configuration passed through to cui-sdk
|
|
@@ -41,19 +59,31 @@ interface WebSDKConversionConfig {
|
|
|
41
59
|
needAvatar?: boolean;
|
|
42
60
|
/** Whether to show feedback buttons. Default: true */
|
|
43
61
|
supportFeedback?: boolean;
|
|
44
|
-
/**
|
|
45
|
-
|
|
62
|
+
/** 是否显示欢迎语,默认 true */
|
|
63
|
+
needWelcomeMessage?: boolean;
|
|
64
|
+
/** 系统头像 URL(iframe 序列化限制,只支持 string) */
|
|
65
|
+
systemAvatar?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Skill information for preset skill
|
|
69
|
+
* 字段名与 cui-sdk SkillBaseInfo 对齐,iframe 透传后直接消费
|
|
70
|
+
*/
|
|
71
|
+
interface WebSDKSkillInfo {
|
|
72
|
+
/** API 标识 */
|
|
73
|
+
apiID?: string;
|
|
46
74
|
}
|
|
47
75
|
/**
|
|
48
76
|
* Editor configuration passed through to cui-sdk
|
|
49
77
|
*/
|
|
50
78
|
interface WebSDKEditorConfig {
|
|
51
|
-
/** Interaction mode: 'v1' for classic, 'v2' for new interaction. Default: 'v1' */
|
|
52
|
-
interactionMode?: 'v1' | 'v2';
|
|
53
|
-
/** Auto skill selection. Default: true */
|
|
54
|
-
autoSkill?: boolean;
|
|
55
79
|
/** Whether to show the editor. Default: true */
|
|
56
80
|
display?: boolean;
|
|
81
|
+
/** 预设技能信息 */
|
|
82
|
+
skill?: WebSDKSkillInfo;
|
|
83
|
+
/** 是否显示顶部 header(含标题和新建对话按钮)。仅 V2 生效。默认: true */
|
|
84
|
+
needHeader?: boolean;
|
|
85
|
+
/** 对话标题,显示在 header 中。默认取应用名称 */
|
|
86
|
+
headerTitle?: string;
|
|
57
87
|
}
|
|
58
88
|
/**
|
|
59
89
|
* Main configuration interface for Web SDK
|
|
@@ -64,9 +94,7 @@ interface WebSDKConfig {
|
|
|
64
94
|
/** User identifier */
|
|
65
95
|
uuid?: string;
|
|
66
96
|
/** Device type */
|
|
67
|
-
device?:
|
|
68
|
-
/** Interaction mode: 'v1' for classic, 'v2' for new interaction */
|
|
69
|
-
interactionMode?: 'v1' | 'v2';
|
|
97
|
+
device?: "pc" | "mobile";
|
|
70
98
|
/**
|
|
71
99
|
* Enable anonymous mode (skips login check). Default: false
|
|
72
100
|
* 非匿名模式下,iframe 内自行完成飞书登录(用户在 iframe 中独立登录),
|
|
@@ -74,7 +102,7 @@ interface WebSDKConfig {
|
|
|
74
102
|
*/
|
|
75
103
|
anonymous?: boolean;
|
|
76
104
|
/** Common configuration */
|
|
77
|
-
common
|
|
105
|
+
common?: WebSDKCommonConfig;
|
|
78
106
|
/** Conversation/conversion configuration (avatar, caching, etc.) */
|
|
79
107
|
conversion?: WebSDKConversionConfig;
|
|
80
108
|
/** Editor configuration (interaction mode, auto skill, etc.) */
|
|
@@ -104,7 +132,7 @@ interface ChatPanel {
|
|
|
104
132
|
/** Update panel config */
|
|
105
133
|
updateConfig: (config: unknown) => Promise<void>;
|
|
106
134
|
/** Observe a skill */
|
|
107
|
-
observeSkill: (skillId: string, name: string, skillType:
|
|
135
|
+
observeSkill: (skillId: string, name: string, skillType: "qna" | "bi" | "dataCreate") => Promise<void>;
|
|
108
136
|
/** Set current skill */
|
|
109
137
|
setCurrentSkill: (skill: {
|
|
110
138
|
id: string;
|
|
@@ -122,6 +150,9 @@ declare const IFRAME_BASE_URL: Record<string, string>;
|
|
|
122
150
|
declare function getIframeBaseURL(env?: string): string;
|
|
123
151
|
/** Timeout for postMessage requests (30 seconds) */
|
|
124
152
|
declare const MESSAGE_TIMEOUT: number;
|
|
153
|
+
/** Miaoda 渠道标识默认值(用户可通过 config.common.channelType 覆盖) */
|
|
154
|
+
declare const DEFAULT_CHANNEL_TYPE = "MIAODA_CUI_SDK";
|
|
155
|
+
declare const DEFAULT_ANONYMOUS_CHANNEL_TYPE = "MIAODA_ANONYMOUS_CUI_SDK";
|
|
125
156
|
|
|
126
157
|
/**
|
|
127
158
|
* Message types for postMessage communication between aily-web-sdk and iframe
|
|
@@ -172,4 +203,4 @@ type IframeMessage = IframeRequestMessage | IframeResponseMessage | IframeEventM
|
|
|
172
203
|
*/
|
|
173
204
|
declare function initAilyChat(root: HTMLElement, config: WebSDKConfig): Promise<ChatPanel>;
|
|
174
205
|
|
|
175
|
-
export { type ChatPanel, IFRAME_BASE_URL, type IframeEventMessage, type IframeMessage, type IframeMessageType, type IframeRequestMessage, type IframeResponseMessage, MESSAGE_TIMEOUT, type WebSDKCommonConfig, type WebSDKConfig, type WebSDKConversionConfig, type WebSDKEditorConfig, type WebSDKEvents, getIframeBaseURL, initAilyChat };
|
|
206
|
+
export { type ChatPanel, DEFAULT_ANONYMOUS_CHANNEL_TYPE, DEFAULT_CHANNEL_TYPE, IFRAME_BASE_URL, type IframeEventMessage, type IframeMessage, type IframeMessageType, type IframeRequestMessage, type IframeResponseMessage, MESSAGE_TIMEOUT, type WebSDKCommonConfig, type WebSDKConfig, type WebSDKConversionConfig, type WebSDKEditorConfig, type WebSDKEvents, type WebSDKSkillInfo, getIframeBaseURL, initAilyChat };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
interface WebSDKCommonConfig {
|
|
8
8
|
/** Base URL for the Aily service */
|
|
9
|
-
baseURL
|
|
9
|
+
baseURL?: string;
|
|
10
10
|
/** Additional headers to send with requests */
|
|
11
11
|
headers?: Record<string, string>;
|
|
12
12
|
/** APAAS application info for API authentication */
|
|
@@ -15,6 +15,14 @@ interface WebSDKCommonConfig {
|
|
|
15
15
|
tenantID?: number;
|
|
16
16
|
tenantType?: number;
|
|
17
17
|
};
|
|
18
|
+
/** 渠道标识,不再硬编码 */
|
|
19
|
+
channelType?: string;
|
|
20
|
+
/** 资源类型,默认等于 channelType */
|
|
21
|
+
resourceType?: string;
|
|
22
|
+
/** 传给后端 API 的额外上下文 */
|
|
23
|
+
channelContext?: Record<string, unknown>;
|
|
24
|
+
/** 最小轮询间隔 (ms),默认 2000 */
|
|
25
|
+
minPollInterval?: number;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
20
28
|
* Event callbacks
|
|
@@ -26,6 +34,16 @@ interface WebSDKEvents {
|
|
|
26
34
|
onError?: (error: Error) => void;
|
|
27
35
|
/** Callback when a message is received */
|
|
28
36
|
onMessage?: (message: unknown) => void;
|
|
37
|
+
/** 会话创建完成、轮询开始后触发 */
|
|
38
|
+
onInited?: (data: {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
conversationId: string;
|
|
41
|
+
handler: string;
|
|
42
|
+
}) => void;
|
|
43
|
+
/** 欢迎消息创建完成后触发 */
|
|
44
|
+
onInitedWithWelcome?: () => void;
|
|
45
|
+
/** 用户点击关闭按钮时触发 */
|
|
46
|
+
onClose?: () => void;
|
|
29
47
|
}
|
|
30
48
|
/**
|
|
31
49
|
* Conversion (conversation) configuration passed through to cui-sdk
|
|
@@ -41,19 +59,31 @@ interface WebSDKConversionConfig {
|
|
|
41
59
|
needAvatar?: boolean;
|
|
42
60
|
/** Whether to show feedback buttons. Default: true */
|
|
43
61
|
supportFeedback?: boolean;
|
|
44
|
-
/**
|
|
45
|
-
|
|
62
|
+
/** 是否显示欢迎语,默认 true */
|
|
63
|
+
needWelcomeMessage?: boolean;
|
|
64
|
+
/** 系统头像 URL(iframe 序列化限制,只支持 string) */
|
|
65
|
+
systemAvatar?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Skill information for preset skill
|
|
69
|
+
* 字段名与 cui-sdk SkillBaseInfo 对齐,iframe 透传后直接消费
|
|
70
|
+
*/
|
|
71
|
+
interface WebSDKSkillInfo {
|
|
72
|
+
/** API 标识 */
|
|
73
|
+
apiID?: string;
|
|
46
74
|
}
|
|
47
75
|
/**
|
|
48
76
|
* Editor configuration passed through to cui-sdk
|
|
49
77
|
*/
|
|
50
78
|
interface WebSDKEditorConfig {
|
|
51
|
-
/** Interaction mode: 'v1' for classic, 'v2' for new interaction. Default: 'v1' */
|
|
52
|
-
interactionMode?: 'v1' | 'v2';
|
|
53
|
-
/** Auto skill selection. Default: true */
|
|
54
|
-
autoSkill?: boolean;
|
|
55
79
|
/** Whether to show the editor. Default: true */
|
|
56
80
|
display?: boolean;
|
|
81
|
+
/** 预设技能信息 */
|
|
82
|
+
skill?: WebSDKSkillInfo;
|
|
83
|
+
/** 是否显示顶部 header(含标题和新建对话按钮)。仅 V2 生效。默认: true */
|
|
84
|
+
needHeader?: boolean;
|
|
85
|
+
/** 对话标题,显示在 header 中。默认取应用名称 */
|
|
86
|
+
headerTitle?: string;
|
|
57
87
|
}
|
|
58
88
|
/**
|
|
59
89
|
* Main configuration interface for Web SDK
|
|
@@ -64,9 +94,7 @@ interface WebSDKConfig {
|
|
|
64
94
|
/** User identifier */
|
|
65
95
|
uuid?: string;
|
|
66
96
|
/** Device type */
|
|
67
|
-
device?:
|
|
68
|
-
/** Interaction mode: 'v1' for classic, 'v2' for new interaction */
|
|
69
|
-
interactionMode?: 'v1' | 'v2';
|
|
97
|
+
device?: "pc" | "mobile";
|
|
70
98
|
/**
|
|
71
99
|
* Enable anonymous mode (skips login check). Default: false
|
|
72
100
|
* 非匿名模式下,iframe 内自行完成飞书登录(用户在 iframe 中独立登录),
|
|
@@ -74,7 +102,7 @@ interface WebSDKConfig {
|
|
|
74
102
|
*/
|
|
75
103
|
anonymous?: boolean;
|
|
76
104
|
/** Common configuration */
|
|
77
|
-
common
|
|
105
|
+
common?: WebSDKCommonConfig;
|
|
78
106
|
/** Conversation/conversion configuration (avatar, caching, etc.) */
|
|
79
107
|
conversion?: WebSDKConversionConfig;
|
|
80
108
|
/** Editor configuration (interaction mode, auto skill, etc.) */
|
|
@@ -104,7 +132,7 @@ interface ChatPanel {
|
|
|
104
132
|
/** Update panel config */
|
|
105
133
|
updateConfig: (config: unknown) => Promise<void>;
|
|
106
134
|
/** Observe a skill */
|
|
107
|
-
observeSkill: (skillId: string, name: string, skillType:
|
|
135
|
+
observeSkill: (skillId: string, name: string, skillType: "qna" | "bi" | "dataCreate") => Promise<void>;
|
|
108
136
|
/** Set current skill */
|
|
109
137
|
setCurrentSkill: (skill: {
|
|
110
138
|
id: string;
|
|
@@ -122,6 +150,9 @@ declare const IFRAME_BASE_URL: Record<string, string>;
|
|
|
122
150
|
declare function getIframeBaseURL(env?: string): string;
|
|
123
151
|
/** Timeout for postMessage requests (30 seconds) */
|
|
124
152
|
declare const MESSAGE_TIMEOUT: number;
|
|
153
|
+
/** Miaoda 渠道标识默认值(用户可通过 config.common.channelType 覆盖) */
|
|
154
|
+
declare const DEFAULT_CHANNEL_TYPE = "MIAODA_CUI_SDK";
|
|
155
|
+
declare const DEFAULT_ANONYMOUS_CHANNEL_TYPE = "MIAODA_ANONYMOUS_CUI_SDK";
|
|
125
156
|
|
|
126
157
|
/**
|
|
127
158
|
* Message types for postMessage communication between aily-web-sdk and iframe
|
|
@@ -172,4 +203,4 @@ type IframeMessage = IframeRequestMessage | IframeResponseMessage | IframeEventM
|
|
|
172
203
|
*/
|
|
173
204
|
declare function initAilyChat(root: HTMLElement, config: WebSDKConfig): Promise<ChatPanel>;
|
|
174
205
|
|
|
175
|
-
export { type ChatPanel, IFRAME_BASE_URL, type IframeEventMessage, type IframeMessage, type IframeMessageType, type IframeRequestMessage, type IframeResponseMessage, MESSAGE_TIMEOUT, type WebSDKCommonConfig, type WebSDKConfig, type WebSDKConversionConfig, type WebSDKEditorConfig, type WebSDKEvents, getIframeBaseURL, initAilyChat };
|
|
206
|
+
export { type ChatPanel, DEFAULT_ANONYMOUS_CHANNEL_TYPE, DEFAULT_CHANNEL_TYPE, IFRAME_BASE_URL, type IframeEventMessage, type IframeMessage, type IframeMessageType, type IframeRequestMessage, type IframeResponseMessage, MESSAGE_TIMEOUT, type WebSDKCommonConfig, type WebSDKConfig, type WebSDKConversionConfig, type WebSDKEditorConfig, type WebSDKEvents, type WebSDKSkillInfo, getIframeBaseURL, initAilyChat };
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ function getIframeBaseURL(env) {
|
|
|
17
17
|
if (hostname.includes("aiforce-boe")) {
|
|
18
18
|
return IFRAME_BASE_URL.boe;
|
|
19
19
|
}
|
|
20
|
-
if (hostname.includes("aiforce-pre")) {
|
|
20
|
+
if (hostname.includes("aiforce-pre") || hostname.includes("force-pre.feishuapp.net") || hostname.includes("fsapp.kundou.cn")) {
|
|
21
21
|
return IFRAME_BASE_URL.staging;
|
|
22
22
|
}
|
|
23
23
|
if (hostname.includes("localhost") || hostname.includes("127.0.0.1")) {
|
|
@@ -27,8 +27,8 @@ function getIframeBaseURL(env) {
|
|
|
27
27
|
}
|
|
28
28
|
__name(getIframeBaseURL, "getIframeBaseURL");
|
|
29
29
|
var MESSAGE_TIMEOUT = 30 * 1e3;
|
|
30
|
-
var
|
|
31
|
-
var
|
|
30
|
+
var DEFAULT_CHANNEL_TYPE = "MIAODA_CUI_SDK";
|
|
31
|
+
var DEFAULT_ANONYMOUS_CHANNEL_TYPE = "MIAODA_ANONYMOUS_CUI_SDK";
|
|
32
32
|
var ALLOWED_ORIGINS = [
|
|
33
33
|
"https://aily.feishu.cn",
|
|
34
34
|
"https://aily.feishu-pre.cn",
|
|
@@ -38,6 +38,9 @@ var ALLOWED_ORIGINS = [
|
|
|
38
38
|
"http://localhost:8080"
|
|
39
39
|
];
|
|
40
40
|
|
|
41
|
+
// src/iframe-manager.ts
|
|
42
|
+
import { slardar as slardar2 } from "@lark-apaas/internal-slardar";
|
|
43
|
+
|
|
41
44
|
// src/device-detector.ts
|
|
42
45
|
var MOBILE_BREAKPOINT = 768;
|
|
43
46
|
var DeviceDetector = class {
|
|
@@ -76,6 +79,7 @@ var DeviceDetector = class {
|
|
|
76
79
|
};
|
|
77
80
|
|
|
78
81
|
// src/message-bridge.ts
|
|
82
|
+
import { slardar } from "@lark-apaas/internal-slardar";
|
|
79
83
|
var MessageBridge = class {
|
|
80
84
|
static {
|
|
81
85
|
__name(this, "MessageBridge");
|
|
@@ -128,7 +132,13 @@ var MessageBridge = class {
|
|
|
128
132
|
};
|
|
129
133
|
const timer = setTimeout(() => {
|
|
130
134
|
this.pendingRequests.delete(id);
|
|
131
|
-
|
|
135
|
+
const error = new Error(`Message timeout: ${type}`);
|
|
136
|
+
slardar.captureException(error, {
|
|
137
|
+
source: "aily-web-sdk",
|
|
138
|
+
module: "message-bridge",
|
|
139
|
+
messageType: type
|
|
140
|
+
});
|
|
141
|
+
reject(error);
|
|
132
142
|
}, MESSAGE_TIMEOUT);
|
|
133
143
|
this.pendingRequests.set(id, {
|
|
134
144
|
resolve,
|
|
@@ -171,7 +181,13 @@ var MessageBridge = class {
|
|
|
171
181
|
clearTimeout(pending.timer);
|
|
172
182
|
this.pendingRequests.delete(data.id);
|
|
173
183
|
if (data.error) {
|
|
174
|
-
|
|
184
|
+
const error = new Error(data.error.message);
|
|
185
|
+
slardar.captureException(error, {
|
|
186
|
+
source: "aily-web-sdk",
|
|
187
|
+
module: "message-bridge",
|
|
188
|
+
errorCode: data.error.code
|
|
189
|
+
});
|
|
190
|
+
pending.reject(error);
|
|
175
191
|
} else {
|
|
176
192
|
pending.resolve(data.data);
|
|
177
193
|
}
|
|
@@ -209,7 +225,8 @@ var IframeManager = class {
|
|
|
209
225
|
constructor(root, config) {
|
|
210
226
|
this.root = root;
|
|
211
227
|
this.config = config;
|
|
212
|
-
|
|
228
|
+
const defaultChannel = config.anonymous ? DEFAULT_ANONYMOUS_CHANNEL_TYPE : DEFAULT_CHANNEL_TYPE;
|
|
229
|
+
this.channelType = config.common?.channelType || defaultChannel;
|
|
213
230
|
this.messageBridge = new MessageBridge(getIframeBaseURL());
|
|
214
231
|
}
|
|
215
232
|
/**
|
|
@@ -255,7 +272,7 @@ var IframeManager = class {
|
|
|
255
272
|
this.deviceDetector.destroy();
|
|
256
273
|
this.deviceDetector = null;
|
|
257
274
|
}
|
|
258
|
-
if (this.iframe
|
|
275
|
+
if (this.iframe?.parentNode) {
|
|
259
276
|
this.iframe.parentNode.removeChild(this.iframe);
|
|
260
277
|
}
|
|
261
278
|
this.iframe = null;
|
|
@@ -269,16 +286,25 @@ var IframeManager = class {
|
|
|
269
286
|
*/
|
|
270
287
|
buildIframeURL() {
|
|
271
288
|
const baseURL = getIframeBaseURL();
|
|
272
|
-
const { events, anonymous: _anonymous, ...configWithoutEvents } = this.config;
|
|
289
|
+
const { events: _events, anonymous: _anonymous, ...configWithoutEvents } = this.config;
|
|
290
|
+
const editorConfig = configWithoutEvents.editor?.skill ? {
|
|
291
|
+
...configWithoutEvents.editor,
|
|
292
|
+
skill: {
|
|
293
|
+
type: "custom",
|
|
294
|
+
builtinType: "unspecified",
|
|
295
|
+
...configWithoutEvents.editor.skill
|
|
296
|
+
}
|
|
297
|
+
} : configWithoutEvents.editor;
|
|
273
298
|
const iframeConfig = {
|
|
274
299
|
...configWithoutEvents,
|
|
275
300
|
common: {
|
|
276
301
|
...configWithoutEvents.common,
|
|
277
302
|
channelType: this.channelType,
|
|
278
|
-
resourceType: this.channelType
|
|
279
|
-
}
|
|
303
|
+
resourceType: configWithoutEvents.common?.resourceType || this.channelType
|
|
304
|
+
},
|
|
305
|
+
editor: editorConfig
|
|
280
306
|
};
|
|
281
|
-
const configBase64 = btoa(JSON.stringify(iframeConfig));
|
|
307
|
+
const configBase64 = btoa(encodeURIComponent(JSON.stringify(iframeConfig)));
|
|
282
308
|
const params = new URLSearchParams({
|
|
283
309
|
appKey: this.config.appKey,
|
|
284
310
|
config: encodeURIComponent(configBase64)
|
|
@@ -291,7 +317,12 @@ var IframeManager = class {
|
|
|
291
317
|
waitForReady() {
|
|
292
318
|
return new Promise((resolve, reject) => {
|
|
293
319
|
const timeout = setTimeout(() => {
|
|
294
|
-
|
|
320
|
+
const error = new Error("Iframe ready timeout");
|
|
321
|
+
slardar2.captureException(error, {
|
|
322
|
+
source: "aily-web-sdk",
|
|
323
|
+
module: "iframe-ready"
|
|
324
|
+
});
|
|
325
|
+
reject(error);
|
|
295
326
|
}, 3e4);
|
|
296
327
|
const checkMessage = /* @__PURE__ */ __name((event) => {
|
|
297
328
|
if (event.data?.messageBizId === MESSAGE_BIZ_ID && event.data?.type === "event" && event.data?.eventName === "ready") {
|
|
@@ -312,30 +343,36 @@ var IframeManager = class {
|
|
|
312
343
|
if (events.onError) {
|
|
313
344
|
this.messageBridge.onEvent("error", (data) => {
|
|
314
345
|
const msg = data?.message || "Unknown error";
|
|
315
|
-
events.onError(new Error(msg));
|
|
346
|
+
events.onError?.(new Error(msg));
|
|
316
347
|
});
|
|
317
348
|
}
|
|
318
349
|
if (events.onMessage) {
|
|
319
350
|
this.messageBridge.onEvent("onMessage", events.onMessage);
|
|
320
351
|
}
|
|
352
|
+
if (events.onInited) {
|
|
353
|
+
this.messageBridge.onEvent("inited", events.onInited);
|
|
354
|
+
}
|
|
355
|
+
if (events.onInitedWithWelcome) {
|
|
356
|
+
this.messageBridge.onEvent("initedWithWelcome", events.onInitedWithWelcome);
|
|
357
|
+
}
|
|
358
|
+
if (events.onClose) {
|
|
359
|
+
this.messageBridge.onEvent("onClose", events.onClose);
|
|
360
|
+
}
|
|
321
361
|
}
|
|
322
362
|
reportInitMetrics(initStart) {
|
|
323
363
|
try {
|
|
324
364
|
const cost = performance.now() - initStart;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
}
|
|
365
|
+
slardar2.sendEvent({
|
|
366
|
+
name: "cui_init_total",
|
|
367
|
+
metrics: {
|
|
368
|
+
cost
|
|
369
|
+
},
|
|
370
|
+
categories: {
|
|
371
|
+
arch: "iframe-static",
|
|
372
|
+
appKey: this.config.appKey,
|
|
373
|
+
channelType: this.channelType
|
|
374
|
+
}
|
|
375
|
+
});
|
|
339
376
|
} catch {
|
|
340
377
|
}
|
|
341
378
|
}
|
|
@@ -383,6 +420,8 @@ async function initAilyChat(root, config) {
|
|
|
383
420
|
}
|
|
384
421
|
__name(initAilyChat, "initAilyChat");
|
|
385
422
|
export {
|
|
423
|
+
DEFAULT_ANONYMOUS_CHANNEL_TYPE,
|
|
424
|
+
DEFAULT_CHANNEL_TYPE,
|
|
386
425
|
IFRAME_BASE_URL,
|
|
387
426
|
MESSAGE_TIMEOUT,
|
|
388
427
|
getIframeBaseURL,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/device-detector.ts","../src/message-bridge.ts","../src/iframe-manager.ts","../src/index.ts"],"sourcesContent":["/**\n * Constants for Aily Web SDK\n */\n\n/** Message business ID for postMessage identification */\nexport const MESSAGE_BIZ_ID = 'cui-sdk-message' as const;\n\n/** Iframe base URLs by environment */\nexport const IFRAME_BASE_URL: Record<string, string> = {\n production: 'https://aily.feishu.cn/cui',\n staging: 'https://aily.feishu-pre.cn/cui',\n boe: 'https://aily.feishu-boe.cn/cui',\n development: 'http://localhost:8080/cui',\n};\n\n/**\n * Get iframe base URL based on environment\n */\nexport function getIframeBaseURL(env?: string): string {\n if (env && IFRAME_BASE_URL[env]) {\n return IFRAME_BASE_URL[env];\n }\n\n // Auto-detect based on hostname\n // MiaoDa 宿主域名: miaoda.feishu[-env].cn, *.aiforce[-env][-preview].bytedance.net, *.aiforce.run, *.aiforce.cloud\n const hostname = typeof window !== 'undefined' ? window.location.hostname : '';\n\n if (hostname.includes('aiforce-boe')) {\n return IFRAME_BASE_URL.boe;\n }\n if (hostname.includes('aiforce-pre')) {\n return IFRAME_BASE_URL.staging;\n }\n if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {\n return IFRAME_BASE_URL.development;\n }\n\n return IFRAME_BASE_URL.production;\n}\n\n/** Timeout for postMessage requests (30 seconds) */\nexport const MESSAGE_TIMEOUT = 30 * 1000;\n\n/** Miaoda 渠道标识(本 SDK 为 Miaoda 专用包,不允许外部自定义) */\nexport const CHANNEL_TYPE = 'MIAODA_CUI_SDK';\nexport const ANONYMOUS_CHANNEL_TYPE = 'MIAODA_ANONYMOUS_CUI_SDK';\n\n/** Allowed origins for postMessage (cui-iframe 的 origin) */\nexport const ALLOWED_ORIGINS = [\n 'https://aily.feishu.cn',\n 'https://aily.feishu-pre.cn',\n 'https://aily.feishu-boe.cn',\n 'http://localhost:3000',\n 'http://localhost:5173',\n 'http://localhost:8080',\n];\n","const MOBILE_BREAKPOINT = 768;\n\ntype DeviceType = 'pc' | 'mobile';\ntype DeviceChangeCallback = (device: DeviceType) => void;\n\n/**\n * 设备类型自动检测器\n *\n * 使用 matchMedia + 768px 断点检测设备类型,与 miaoda useIsMobile 逻辑一致。\n * 检测在宿主页面(而非 iframe)中执行,结果通过 postMessage 推送给 iframe。\n */\nexport class DeviceDetector {\n private mediaQuery: MediaQueryList;\n private currentDevice: DeviceType;\n private callback: DeviceChangeCallback | null = null;\n private handleChange: (() => void) | null = null;\n\n constructor() {\n this.mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n this.currentDevice = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n }\n\n getDevice(): DeviceType {\n return this.currentDevice;\n }\n\n observe(callback: DeviceChangeCallback): void {\n this.callback = callback;\n this.handleChange = () => {\n const newDevice: DeviceType = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n if (newDevice !== this.currentDevice) {\n this.currentDevice = newDevice;\n this.callback?.(newDevice);\n }\n };\n this.mediaQuery.addEventListener('change', this.handleChange);\n }\n\n destroy(): void {\n if (this.handleChange) {\n this.mediaQuery.removeEventListener('change', this.handleChange);\n this.handleChange = null;\n }\n this.callback = null;\n }\n}\n","import { MESSAGE_TIMEOUT, ALLOWED_ORIGINS } from './constants';\nimport {\n MESSAGE_BIZ_ID,\n type IframeMessage,\n type IframeRequestMessage,\n type IframeResponseMessage,\n type IframeEventMessage,\n type IframeMessageType,\n} from './types/message';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MessageBridge handles postMessage communication between parent and iframe\n */\nexport class MessageBridge {\n private iframe: HTMLIFrameElement | null = null;\n private requestId = 0;\n private pendingRequests = new Map<number, PendingRequest>();\n private eventHandlers = new Map<string, ((data: unknown) => void)[]>();\n private targetOrigin: string;\n\n constructor(targetOrigin: string) {\n this.targetOrigin = targetOrigin;\n this.handleMessage = this.handleMessage.bind(this);\n }\n\n /**\n * Attach to an iframe and start listening for messages\n */\n attach(iframe: HTMLIFrameElement): void {\n this.iframe = iframe;\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Detach from iframe and clean up\n */\n detach(): void {\n window.removeEventListener('message', this.handleMessage);\n this.iframe = null;\n // Clear all pending requests\n this.pendingRequests.forEach(({ reject }) => {\n reject(new Error('MessageBridge detached'));\n });\n this.pendingRequests.clear();\n }\n\n /**\n * Send a message to the iframe and wait for response\n */\n send<T = unknown>(type: IframeMessageType, data?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.iframe?.contentWindow) {\n reject(new Error('Iframe not attached'));\n return;\n }\n\n this.requestId += 1;\n const id = this.requestId;\n const message: IframeRequestMessage = {\n messageBizId: MESSAGE_BIZ_ID,\n id,\n type,\n data,\n timestamp: Date.now(),\n isRequest: true,\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Message timeout: ${type}`));\n }, MESSAGE_TIMEOUT);\n\n this.pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer });\n\n this.iframe.contentWindow.postMessage(message, this.targetOrigin);\n });\n }\n\n /**\n * Register an event handler\n */\n onEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n handlers.push(handler);\n this.eventHandlers.set(eventName, handlers);\n }\n\n /**\n * Remove an event handler\n */\n offEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n const index = handlers.indexOf(handler);\n if (index > -1) {\n handlers.splice(index, 1);\n this.eventHandlers.set(eventName, handlers);\n }\n }\n\n private handleMessage(event: MessageEvent<IframeMessage>): void {\n // Validate origin\n if (!ALLOWED_ORIGINS.includes(event.origin)) {\n return;\n }\n\n const { data } = event;\n\n // Validate message format\n if (data?.messageBizId !== MESSAGE_BIZ_ID) {\n return;\n }\n\n // Handle response messages\n if (this.isResponseMessage(data)) {\n const pending = this.pendingRequests.get(data.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(data.id);\n\n if (data.error) {\n pending.reject(new Error(data.error.message));\n } else {\n pending.resolve(data.data);\n }\n }\n }\n\n // Handle event messages\n if (this.isEventMessage(data)) {\n const handlers = this.eventHandlers.get(data.eventName) || [];\n handlers.forEach((handler) => {\n try {\n handler(data.data);\n } catch {\n // Ignore handler errors\n }\n });\n }\n }\n\n private isResponseMessage(data: IframeMessage): data is IframeResponseMessage {\n return 'isResponse' in data && data.isResponse === true;\n }\n\n private isEventMessage(data: IframeMessage): data is IframeEventMessage {\n return data.type === 'event';\n }\n}\n","import { getIframeBaseURL, MESSAGE_BIZ_ID, CHANNEL_TYPE, ANONYMOUS_CHANNEL_TYPE } from './constants';\nimport { DeviceDetector } from './device-detector';\nimport { MessageBridge } from './message-bridge';\n\nimport type { WebSDKConfig, ChatPanel } from './types/config';\n\n/**\n * IframeManager handles iframe creation and lifecycle\n *\n * iframe 采用自初始化模式:配置通过 URL hash 传入,iframe 加载后自行解析并渲染,\n * 与旧版 copilot iframe 的行为保持一致。宿主通过 postMessage 进行后续操作。\n */\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private messageBridge: MessageBridge;\n private config: WebSDKConfig;\n private channelType: string;\n private root: HTMLElement;\n private deviceDetector: DeviceDetector | null = null;\n\n constructor(root: HTMLElement, config: WebSDKConfig) {\n this.root = root;\n this.config = config;\n this.channelType = config.anonymous ? ANONYMOUS_CHANNEL_TYPE : CHANNEL_TYPE;\n this.messageBridge = new MessageBridge(getIframeBaseURL());\n }\n\n /**\n * Initialize the iframe and establish communication\n * 配置通过 URL hash 编码传入 iframe,iframe 加载后自行初始化\n */\n async init(): Promise<ChatPanel> {\n const initStart = performance.now();\n\n // Auto-detect device if not explicitly provided\n if (!this.config.device) {\n this.deviceDetector = new DeviceDetector();\n this.config.device = this.deviceDetector.getDevice();\n }\n\n // Create iframe element\n this.iframe = document.createElement('iframe');\n this.iframe.src = this.buildIframeURL();\n this.iframe.style.width = '100%';\n this.iframe.style.height = '100%';\n this.iframe.style.border = 'none';\n this.iframe.allow = 'microphone; clipboard-write';\n\n // Attach to DOM\n this.root.appendChild(this.iframe);\n\n // Attach message bridge (开始监听消息)\n this.messageBridge.attach(this.iframe);\n\n // Setup event handlers (在 iframe 初始化完成之前就开始监听,确保不丢失事件)\n this.setupEventHandlers();\n\n // Listen for device changes and push updates to iframe\n if (this.deviceDetector) {\n this.deviceDetector.observe((newDevice) => {\n this.messageBridge.send('updateConfig', { device: newDevice });\n });\n }\n\n // Wait for iframe to finish initialization (iframe 自行读取 URL 配置并渲染,完成后发送 ready 事件)\n await this.waitForReady();\n\n // 上报 cui_init_total 耗时(iframe 创建 → init 完成)\n this.reportInitMetrics(initStart);\n\n // Return ChatPanel interface\n return this.createChatPanel();\n }\n\n /**\n * Destroy the iframe and cleanup\n */\n async destroy(): Promise<void> {\n try {\n await this.messageBridge.send('destroy');\n } catch {\n // Ignore errors during destroy\n }\n\n this.messageBridge.detach();\n\n if (this.deviceDetector) {\n this.deviceDetector.destroy();\n this.deviceDetector = null;\n }\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n this.iframe = null;\n }\n\n /**\n * 构建 iframe URL,将配置编码到 hash 中\n * 格式: {baseURL}#{params} 其中 config 为 base64(JSON.stringify(configWithoutEvents))\n *\n * 鉴权说明:SDK 不参与 token 获取/传递。iframe 自行处理飞书登录流程,\n * 允许用户在 iframe 中独立登录。匿名渠道跳过登录检查。\n */\n private buildIframeURL(): string {\n const baseURL = getIframeBaseURL();\n\n // 移除 events(函数无法序列化)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { events, anonymous: _anonymous, ...configWithoutEvents } = this.config;\n // 注入 channelType/resourceType(SDK 内部控制,不暴露给消费者)\n const iframeConfig = {\n ...configWithoutEvents,\n common: {\n ...configWithoutEvents.common,\n channelType: this.channelType,\n resourceType: this.channelType,\n },\n };\n const configBase64 = btoa(JSON.stringify(iframeConfig));\n\n const params = new URLSearchParams({\n appKey: this.config.appKey,\n config: encodeURIComponent(configBase64),\n });\n\n return `${baseURL}#${params.toString()}`;\n }\n\n /**\n * 等待 iframe 自行初始化完成后发送 ready 事件\n */\n private waitForReady(): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Iframe ready timeout'));\n }, 30000);\n\n const checkMessage = (event: MessageEvent) => {\n if (\n event.data?.messageBizId === MESSAGE_BIZ_ID &&\n event.data?.type === 'event' &&\n event.data?.eventName === 'ready'\n ) {\n clearTimeout(timeout);\n window.removeEventListener('message', checkMessage);\n resolve();\n }\n };\n\n window.addEventListener('message', checkMessage);\n });\n }\n\n private setupEventHandlers(): void {\n const { events } = this.config;\n if (!events) return;\n\n if (events.onReady) {\n this.messageBridge.onEvent('ready', events.onReady);\n }\n\n if (events.onError) {\n this.messageBridge.onEvent('error', (data: unknown) => {\n const msg = (data as { message?: string })?.message || 'Unknown error';\n events.onError!(new Error(msg));\n });\n }\n\n if (events.onMessage) {\n this.messageBridge.onEvent('onMessage', events.onMessage);\n }\n }\n\n private reportInitMetrics(initStart: number): void {\n try {\n const cost = performance.now() - initStart;\n const Slardar = (window as unknown as Record<string, unknown>).__Slardar as\n | { sendEvent?: (event: Record<string, unknown>) => void }\n | undefined;\n if (Slardar?.sendEvent) {\n Slardar.sendEvent({\n name: 'cui_init_total',\n metrics: { cost },\n categories: {\n arch: 'iframe-static',\n appKey: this.config.appKey,\n channelType: this.channelType,\n },\n });\n }\n } catch {\n // 性能上报失败不影响功能\n }\n }\n\n private createChatPanel(): ChatPanel {\n return {\n sendMessage: async (data) => {\n await this.messageBridge.send('sendMessage', data);\n },\n clear: async () => {\n await this.messageBridge.send('clear');\n },\n cancelMessage: async (messageItem) => {\n await this.messageBridge.send('cancelMessage', messageItem);\n },\n clearAndStop: async () => {\n await this.messageBridge.send('clearAndStop');\n },\n updateWelcomeMessage: async () => {\n await this.messageBridge.send('updateWelcomeMessage');\n },\n updateConfig: async (config) => {\n await this.messageBridge.send('updateConfig', config);\n },\n observeSkill: async (skillId, name, skillType) => {\n await this.messageBridge.send('observeSkill', { skillId, name, skillType });\n },\n setCurrentSkill: async (skill) => {\n await this.messageBridge.send('setCurrentSkill', skill);\n },\n destroy: async () => {\n await this.destroy();\n },\n };\n }\n}\n","/*\n * index.ts\n * Author: perterpon.wang<perterpon.wang@bytedance.com>\n * Create: Tue Feb 10 2026\n */\n\nimport { IframeManager } from './iframe-manager';\n\nimport type { ChatPanel, WebSDKConfig } from './types/config';\n\n/**\n * Initialize Aily Chat via iframe\n * This is the primary method for initializing the chat panel\n * @param root - Container element for the iframe\n * @param config - Configuration options\n * @returns ChatPanel instance for controlling the chat\n */\nexport async function initAilyChat(root: HTMLElement, config: WebSDKConfig): Promise<ChatPanel> {\n const manager = new IframeManager(root, config);\n return manager.init();\n}\n\n// Export types\nexport type {\n ChatPanel,\n WebSDKConfig,\n WebSDKCommonConfig,\n WebSDKConversionConfig,\n WebSDKEditorConfig,\n WebSDKEvents,\n} from './types/config';\n\n// Export constants\nexport { getIframeBaseURL, IFRAME_BASE_URL, MESSAGE_TIMEOUT } from './constants';\n\n// Export message types\nexport type {\n IframeMessageType,\n IframeRequestMessage,\n IframeResponseMessage,\n IframeEventMessage,\n IframeMessage,\n} from './types/message';\n"],"mappings":"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","names":["MESSAGE_BIZ_ID","IFRAME_BASE_URL","production","staging","boe","development","getIframeBaseURL","env","hostname","window","location","includes","MESSAGE_TIMEOUT","CHANNEL_TYPE","ANONYMOUS_CHANNEL_TYPE","ALLOWED_ORIGINS","MOBILE_BREAKPOINT","DeviceDetector","mediaQuery","currentDevice","callback","handleChange","window","matchMedia","innerWidth","getDevice","observe","newDevice","addEventListener","destroy","removeEventListener","MessageBridge","iframe","requestId","pendingRequests","Map","eventHandlers","targetOrigin","handleMessage","bind","attach","window","addEventListener","detach","removeEventListener","forEach","reject","Error","clear","send","type","data","Promise","resolve","contentWindow","id","message","messageBizId","MESSAGE_BIZ_ID","timestamp","Date","now","isRequest","timer","setTimeout","delete","MESSAGE_TIMEOUT","set","postMessage","onEvent","eventName","handler","handlers","get","push","offEvent","index","indexOf","splice","event","ALLOWED_ORIGINS","includes","origin","isResponseMessage","pending","clearTimeout","error","isEventMessage","isResponse","IframeManager","iframe","messageBridge","config","channelType","root","deviceDetector","anonymous","ANONYMOUS_CHANNEL_TYPE","CHANNEL_TYPE","MessageBridge","getIframeBaseURL","init","initStart","performance","now","device","DeviceDetector","getDevice","document","createElement","src","buildIframeURL","style","width","height","border","allow","appendChild","attach","setupEventHandlers","observe","newDevice","send","waitForReady","reportInitMetrics","createChatPanel","destroy","detach","parentNode","removeChild","baseURL","events","_anonymous","configWithoutEvents","iframeConfig","common","resourceType","configBase64","btoa","JSON","stringify","params","URLSearchParams","appKey","encodeURIComponent","toString","Promise","resolve","reject","timeout","setTimeout","Error","checkMessage","event","data","messageBizId","MESSAGE_BIZ_ID","type","eventName","clearTimeout","window","removeEventListener","addEventListener","onReady","onEvent","onError","msg","message","onMessage","cost","Slardar","__Slardar","sendEvent","name","metrics","categories","arch","sendMessage","clear","cancelMessage","messageItem","clearAndStop","updateWelcomeMessage","updateConfig","observeSkill","skillId","skillType","setCurrentSkill","skill","initAilyChat","root","config","manager","IframeManager","init"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/iframe-manager.ts","../src/device-detector.ts","../src/message-bridge.ts","../src/index.ts"],"sourcesContent":["/**\n * Constants for Aily Web SDK\n */\n\n/** Message business ID for postMessage identification */\nexport const MESSAGE_BIZ_ID = \"cui-sdk-message\" as const;\n\n/** Iframe base URLs by environment */\nexport const IFRAME_BASE_URL: Record<string, string> = {\n\tproduction: \"https://aily.feishu.cn/cui\",\n\tstaging: \"https://aily.feishu-pre.cn/cui\",\n\tboe: \"https://aily.feishu-boe.cn/cui\",\n\tdevelopment: \"http://localhost:8080/cui\",\n};\n\n/**\n * Get iframe base URL based on environment\n */\nexport function getIframeBaseURL(env?: string): string {\n\tif (env && IFRAME_BASE_URL[env]) {\n\t\treturn IFRAME_BASE_URL[env];\n\t}\n\n\t// Auto-detect based on hostname\n\t// MiaoDa 宿主域名: miaoda.feishu[-env].cn, *.aiforce[-env][-preview].bytedance.net, *.force-pre.feishuapp.net, *.fsapp.kundou.cn, *.aiforce.run, *.aiforce.cloud\n\tconst hostname =\n\t\ttypeof window !== \"undefined\" ? window.location.hostname : \"\";\n\n\tif (hostname.includes(\"aiforce-boe\")) {\n\t\treturn IFRAME_BASE_URL.boe;\n\t}\n\tif (\n\t\thostname.includes(\"aiforce-pre\") ||\n\t\thostname.includes(\"force-pre.feishuapp.net\") ||\n\t\thostname.includes(\"fsapp.kundou.cn\")\n\t) {\n\t\treturn IFRAME_BASE_URL.staging;\n\t}\n\tif (hostname.includes(\"localhost\") || hostname.includes(\"127.0.0.1\")) {\n\t\treturn IFRAME_BASE_URL.development;\n\t}\n\n\treturn IFRAME_BASE_URL.production;\n}\n\n/** Timeout for postMessage requests (30 seconds) */\nexport const MESSAGE_TIMEOUT = 30 * 1000;\n\n/** Miaoda 渠道标识默认值(用户可通过 config.common.channelType 覆盖) */\nexport const DEFAULT_CHANNEL_TYPE = \"MIAODA_CUI_SDK\";\nexport const DEFAULT_ANONYMOUS_CHANNEL_TYPE = \"MIAODA_ANONYMOUS_CUI_SDK\";\n\n/** Allowed origins for postMessage (cui-iframe 的 origin) */\nexport const ALLOWED_ORIGINS = [\n\t\"https://aily.feishu.cn\",\n\t\"https://aily.feishu-pre.cn\",\n\t\"https://aily.feishu-boe.cn\",\n\t\"http://localhost:3000\",\n\t\"http://localhost:5173\",\n\t\"http://localhost:8080\",\n];\n","import {\n\tDEFAULT_ANONYMOUS_CHANNEL_TYPE,\n\tDEFAULT_CHANNEL_TYPE,\n\tgetIframeBaseURL,\n\tMESSAGE_BIZ_ID,\n} from \"./constants\";\nimport { slardar } from \"@lark-apaas/internal-slardar\";\nimport { DeviceDetector } from \"./device-detector\";\nimport { MessageBridge } from \"./message-bridge\";\n\nimport type { ChatPanel, WebSDKConfig } from \"./types/config\";\n\n/**\n * IframeManager handles iframe creation and lifecycle\n *\n * iframe 采用自初始化模式:配置通过 URL hash 传入,iframe 加载后自行解析并渲染,\n * 与旧版 copilot iframe 的行为保持一致。宿主通过 postMessage 进行后续操作。\n */\nexport class IframeManager {\n\tprivate iframe: HTMLIFrameElement | null = null;\n\tprivate messageBridge: MessageBridge;\n\tprivate config: WebSDKConfig;\n\tprivate channelType: string;\n\tprivate root: HTMLElement;\n\tprivate deviceDetector: DeviceDetector | null = null;\n\n\tconstructor(root: HTMLElement, config: WebSDKConfig) {\n\t\tthis.root = root;\n\t\tthis.config = config;\n\t\tconst defaultChannel = config.anonymous\n\t\t\t? DEFAULT_ANONYMOUS_CHANNEL_TYPE\n\t\t\t: DEFAULT_CHANNEL_TYPE;\n\t\tthis.channelType = config.common?.channelType || defaultChannel;\n\t\tthis.messageBridge = new MessageBridge(getIframeBaseURL());\n\t}\n\n\t/**\n\t * Initialize the iframe and establish communication\n\t * 配置通过 URL hash 编码传入 iframe,iframe 加载后自行初始化\n\t */\n\tasync init(): Promise<ChatPanel> {\n\t\tconst initStart = performance.now();\n\n\t\t// Auto-detect device if not explicitly provided\n\t\tif (!this.config.device) {\n\t\t\tthis.deviceDetector = new DeviceDetector();\n\t\t\tthis.config.device = this.deviceDetector.getDevice();\n\t\t}\n\n\t\t// Create iframe element\n\t\tthis.iframe = document.createElement(\"iframe\");\n\t\tthis.iframe.src = this.buildIframeURL();\n\t\tthis.iframe.style.width = \"100%\";\n\t\tthis.iframe.style.height = \"100%\";\n\t\tthis.iframe.style.border = \"none\";\n\t\tthis.iframe.allow = \"microphone; clipboard-write\";\n\n\t\t// Attach to DOM\n\t\tthis.root.appendChild(this.iframe);\n\n\t\t// Attach message bridge (开始监听消息)\n\t\tthis.messageBridge.attach(this.iframe);\n\n\t\t// Setup event handlers (在 iframe 初始化完成之前就开始监听,确保不丢失事件)\n\t\tthis.setupEventHandlers();\n\n\t\t// Listen for device changes and push updates to iframe\n\t\tif (this.deviceDetector) {\n\t\t\tthis.deviceDetector.observe((newDevice) => {\n\t\t\t\tthis.messageBridge.send(\"updateConfig\", { device: newDevice });\n\t\t\t});\n\t\t}\n\n\t\t// Wait for iframe to finish initialization (iframe 自行读取 URL 配置并渲染,完成后发送 ready 事件)\n\t\tawait this.waitForReady();\n\n\t\t// 上报 cui_init_total 耗时(iframe 创建 → init 完成)\n\t\tthis.reportInitMetrics(initStart);\n\n\t\t// Return ChatPanel interface\n\t\treturn this.createChatPanel();\n\t}\n\n\t/**\n\t * Destroy the iframe and cleanup\n\t */\n\tasync destroy(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.messageBridge.send(\"destroy\");\n\t\t} catch {\n\t\t\t// Ignore errors during destroy\n\t\t}\n\n\t\tthis.messageBridge.detach();\n\n\t\tif (this.deviceDetector) {\n\t\t\tthis.deviceDetector.destroy();\n\t\t\tthis.deviceDetector = null;\n\t\t}\n\n\t\tif (this.iframe?.parentNode) {\n\t\t\tthis.iframe.parentNode.removeChild(this.iframe);\n\t\t}\n\n\t\tthis.iframe = null;\n\t}\n\n\t/**\n\t * 构建 iframe URL,将配置编码到 hash 中\n\t * 格式: {baseURL}#{params} 其中 config 为 base64(JSON.stringify(configWithoutEvents))\n\t *\n\t * 鉴权说明:SDK 不参与 token 获取/传递。iframe 自行处理飞书登录流程,\n\t * 允许用户在 iframe 中独立登录。匿名渠道跳过登录检查。\n\t */\n\tprivate buildIframeURL(): string {\n\t\tconst baseURL = getIframeBaseURL();\n\n\t\t// 移除 events(函数无法序列化)\n\t\tconst {\n\t\t\tevents: _events,\n\t\t\tanonymous: _anonymous,\n\t\t\t...configWithoutEvents\n\t\t} = this.config;\n\t\t// 注入 channelType/resourceType(SDK 内部控制,不暴露给消费者)\n\t\t// 补全 editor.skill 硬编码字段\n\t\tconst editorConfig = configWithoutEvents.editor?.skill\n\t\t\t? {\n\t\t\t\t\t...configWithoutEvents.editor,\n\t\t\t\t\tskill: {\n\t\t\t\t\t\ttype: \"custom\",\n\t\t\t\t\t\tbuiltinType: \"unspecified\",\n\t\t\t\t\t\t...configWithoutEvents.editor.skill,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: configWithoutEvents.editor;\n\n\t\tconst iframeConfig = {\n\t\t\t...configWithoutEvents,\n\t\t\tcommon: {\n\t\t\t\t...configWithoutEvents.common,\n\t\t\t\tchannelType: this.channelType,\n\t\t\t\tresourceType:\n\t\t\t\t\tconfigWithoutEvents.common?.resourceType || this.channelType,\n\t\t\t},\n\t\t\teditor: editorConfig,\n\t\t};\n\t\tconst configBase64 = btoa(encodeURIComponent(JSON.stringify(iframeConfig)));\n\n\t\tconst params = new URLSearchParams({\n\t\t\tappKey: this.config.appKey,\n\t\t\tconfig: encodeURIComponent(configBase64),\n\t\t});\n\n\t\treturn `${baseURL}#${params.toString()}`;\n\t}\n\n\t/**\n\t * 等待 iframe 自行初始化完成后发送 ready 事件\n\t */\n\tprivate waitForReady(): Promise<void> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\tconst error = new Error(\"Iframe ready timeout\");\n\t\t\t\tslardar.captureException(error, { source: 'aily-web-sdk', module: 'iframe-ready' });\n\t\t\t\treject(error);\n\t\t\t}, 30000);\n\n\t\t\tconst checkMessage = (event: MessageEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tevent.data?.messageBizId === MESSAGE_BIZ_ID &&\n\t\t\t\t\tevent.data?.type === \"event\" &&\n\t\t\t\t\tevent.data?.eventName === \"ready\"\n\t\t\t\t) {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\twindow.removeEventListener(\"message\", checkMessage);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\twindow.addEventListener(\"message\", checkMessage);\n\t\t});\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tconst { events } = this.config;\n\t\tif (!events) return;\n\n\t\tif (events.onReady) {\n\t\t\tthis.messageBridge.onEvent(\"ready\", events.onReady);\n\t\t}\n\n\t\tif (events.onError) {\n\t\t\tthis.messageBridge.onEvent(\"error\", (data: unknown) => {\n\t\t\t\tconst msg = (data as { message?: string })?.message || \"Unknown error\";\n\t\t\t\tevents.onError?.(new Error(msg));\n\t\t\t});\n\t\t}\n\n\t\tif (events.onMessage) {\n\t\t\tthis.messageBridge.onEvent(\"onMessage\", events.onMessage);\n\t\t}\n\n\t\tif (events.onInited) {\n\t\t\tthis.messageBridge.onEvent(\n\t\t\t\t\"inited\",\n\t\t\t\tevents.onInited as (data: unknown) => void,\n\t\t\t);\n\t\t}\n\n\t\tif (events.onInitedWithWelcome) {\n\t\t\tthis.messageBridge.onEvent(\n\t\t\t\t\"initedWithWelcome\",\n\t\t\t\tevents.onInitedWithWelcome,\n\t\t\t);\n\t\t}\n\n\t\tif (events.onClose) {\n\t\t\tthis.messageBridge.onEvent(\"onClose\", events.onClose);\n\t\t}\n\t}\n\n\tprivate reportInitMetrics(initStart: number): void {\n\t\ttry {\n\t\t\tconst cost = performance.now() - initStart;\n\t\t\tslardar.sendEvent({\n\t\t\t\tname: \"cui_init_total\",\n\t\t\t\tmetrics: { cost },\n\t\t\t\tcategories: {\n\t\t\t\t\tarch: \"iframe-static\",\n\t\t\t\t\tappKey: this.config.appKey,\n\t\t\t\t\tchannelType: this.channelType,\n\t\t\t\t},\n\t\t\t});\n\t\t} catch {\n\t\t\t// 性能上报失败不影响功能\n\t\t}\n\t}\n\n\tprivate createChatPanel(): ChatPanel {\n\t\treturn {\n\t\t\tsendMessage: async (data) => {\n\t\t\t\tawait this.messageBridge.send(\"sendMessage\", data);\n\t\t\t},\n\t\t\tclear: async () => {\n\t\t\t\tawait this.messageBridge.send(\"clear\");\n\t\t\t},\n\t\t\tcancelMessage: async (messageItem) => {\n\t\t\t\tawait this.messageBridge.send(\"cancelMessage\", messageItem);\n\t\t\t},\n\t\t\tclearAndStop: async () => {\n\t\t\t\tawait this.messageBridge.send(\"clearAndStop\");\n\t\t\t},\n\t\t\tupdateWelcomeMessage: async () => {\n\t\t\t\tawait this.messageBridge.send(\"updateWelcomeMessage\");\n\t\t\t},\n\t\t\tupdateConfig: async (config) => {\n\t\t\t\tawait this.messageBridge.send(\"updateConfig\", config);\n\t\t\t},\n\t\t\tobserveSkill: async (skillId, name, skillType) => {\n\t\t\t\tawait this.messageBridge.send(\"observeSkill\", {\n\t\t\t\t\tskillId,\n\t\t\t\t\tname,\n\t\t\t\t\tskillType,\n\t\t\t\t});\n\t\t\t},\n\t\t\tsetCurrentSkill: async (skill) => {\n\t\t\t\tawait this.messageBridge.send(\"setCurrentSkill\", skill);\n\t\t\t},\n\t\t\tdestroy: async () => {\n\t\t\t\tawait this.destroy();\n\t\t\t},\n\t\t};\n\t}\n}\n","const MOBILE_BREAKPOINT = 768;\n\ntype DeviceType = 'pc' | 'mobile';\ntype DeviceChangeCallback = (device: DeviceType) => void;\n\n/**\n * 设备类型自动检测器\n *\n * 使用 matchMedia + 768px 断点检测设备类型,与 miaoda useIsMobile 逻辑一致。\n * 检测在宿主页面(而非 iframe)中执行,结果通过 postMessage 推送给 iframe。\n */\nexport class DeviceDetector {\n private mediaQuery: MediaQueryList;\n private currentDevice: DeviceType;\n private callback: DeviceChangeCallback | null = null;\n private handleChange: (() => void) | null = null;\n\n constructor() {\n this.mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n this.currentDevice = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n }\n\n getDevice(): DeviceType {\n return this.currentDevice;\n }\n\n observe(callback: DeviceChangeCallback): void {\n this.callback = callback;\n this.handleChange = () => {\n const newDevice: DeviceType = window.innerWidth < MOBILE_BREAKPOINT ? 'mobile' : 'pc';\n if (newDevice !== this.currentDevice) {\n this.currentDevice = newDevice;\n this.callback?.(newDevice);\n }\n };\n this.mediaQuery.addEventListener('change', this.handleChange);\n }\n\n destroy(): void {\n if (this.handleChange) {\n this.mediaQuery.removeEventListener('change', this.handleChange);\n this.handleChange = null;\n }\n this.callback = null;\n }\n}\n","import { MESSAGE_TIMEOUT, ALLOWED_ORIGINS } from './constants';\nimport {\n MESSAGE_BIZ_ID,\n type IframeMessage,\n type IframeRequestMessage,\n type IframeResponseMessage,\n type IframeEventMessage,\n type IframeMessageType,\n} from './types/message';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MessageBridge handles postMessage communication between parent and iframe\n */\nexport class MessageBridge {\n private iframe: HTMLIFrameElement | null = null;\n private requestId = 0;\n private pendingRequests = new Map<number, PendingRequest>();\n private eventHandlers = new Map<string, ((data: unknown) => void)[]>();\n private targetOrigin: string;\n\n constructor(targetOrigin: string) {\n this.targetOrigin = targetOrigin;\n this.handleMessage = this.handleMessage.bind(this);\n }\n\n /**\n * Attach to an iframe and start listening for messages\n */\n attach(iframe: HTMLIFrameElement): void {\n this.iframe = iframe;\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Detach from iframe and clean up\n */\n detach(): void {\n window.removeEventListener('message', this.handleMessage);\n this.iframe = null;\n // Clear all pending requests\n this.pendingRequests.forEach(({ reject }) => {\n reject(new Error('MessageBridge detached'));\n });\n this.pendingRequests.clear();\n }\n\n /**\n * Send a message to the iframe and wait for response\n */\n send<T = unknown>(type: IframeMessageType, data?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.iframe?.contentWindow) {\n reject(new Error('Iframe not attached'));\n return;\n }\n\n this.requestId += 1;\n const id = this.requestId;\n const message: IframeRequestMessage = {\n messageBizId: MESSAGE_BIZ_ID,\n id,\n type,\n data,\n timestamp: Date.now(),\n isRequest: true,\n };\n\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n const error = new Error(`Message timeout: ${type}`);\n slardar.captureException(error, { source: 'aily-web-sdk', module: 'message-bridge', messageType: type });\n reject(error);\n }, MESSAGE_TIMEOUT);\n\n this.pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer });\n\n this.iframe.contentWindow.postMessage(message, this.targetOrigin);\n });\n }\n\n /**\n * Register an event handler\n */\n onEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n handlers.push(handler);\n this.eventHandlers.set(eventName, handlers);\n }\n\n /**\n * Remove an event handler\n */\n offEvent(eventName: string, handler: (data: unknown) => void): void {\n const handlers = this.eventHandlers.get(eventName) || [];\n const index = handlers.indexOf(handler);\n if (index > -1) {\n handlers.splice(index, 1);\n this.eventHandlers.set(eventName, handlers);\n }\n }\n\n private handleMessage(event: MessageEvent<IframeMessage>): void {\n // Validate origin\n if (!ALLOWED_ORIGINS.includes(event.origin)) {\n return;\n }\n\n const { data } = event;\n\n // Validate message format\n if (data?.messageBizId !== MESSAGE_BIZ_ID) {\n return;\n }\n\n // Handle response messages\n if (this.isResponseMessage(data)) {\n const pending = this.pendingRequests.get(data.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingRequests.delete(data.id);\n\n if (data.error) {\n const error = new Error(data.error.message);\n slardar.captureException(error, { source: 'aily-web-sdk', module: 'message-bridge', errorCode: data.error.code });\n pending.reject(error);\n } else {\n pending.resolve(data.data);\n }\n }\n }\n\n // Handle event messages\n if (this.isEventMessage(data)) {\n const handlers = this.eventHandlers.get(data.eventName) || [];\n handlers.forEach((handler) => {\n try {\n handler(data.data);\n } catch {\n // Ignore handler errors\n }\n });\n }\n }\n\n private isResponseMessage(data: IframeMessage): data is IframeResponseMessage {\n return 'isResponse' in data && data.isResponse === true;\n }\n\n private isEventMessage(data: IframeMessage): data is IframeEventMessage {\n return data.type === 'event';\n }\n}\n","/*\n * index.ts\n * Author: perterpon.wang<perterpon.wang@bytedance.com>\n * Create: Tue Feb 10 2026\n */\n\nimport { IframeManager } from \"./iframe-manager\";\n\nimport type { ChatPanel, WebSDKConfig } from \"./types/config\";\n\n/**\n * Initialize Aily Chat via iframe\n * This is the primary method for initializing the chat panel\n * @param root - Container element for the iframe\n * @param config - Configuration options\n * @returns ChatPanel instance for controlling the chat\n */\nexport async function initAilyChat(\n\troot: HTMLElement,\n\tconfig: WebSDKConfig,\n): Promise<ChatPanel> {\n\tconst manager = new IframeManager(root, config);\n\treturn manager.init();\n}\n\n// Export constants\nexport {\n\tDEFAULT_ANONYMOUS_CHANNEL_TYPE,\n\tDEFAULT_CHANNEL_TYPE,\n\tgetIframeBaseURL,\n\tIFRAME_BASE_URL,\n\tMESSAGE_TIMEOUT,\n} from \"./constants\";\n// Export types\nexport type {\n\tChatPanel,\n\tWebSDKCommonConfig,\n\tWebSDKConfig,\n\tWebSDKConversionConfig,\n\tWebSDKEditorConfig,\n\tWebSDKEvents,\n\tWebSDKSkillInfo,\n} from \"./types/config\";\n\n// Export message types\nexport type {\n\tIframeEventMessage,\n\tIframeMessage,\n\tIframeMessageType,\n\tIframeRequestMessage,\n\tIframeResponseMessage,\n} from \"./types/message\";\n"],"mappings":"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","names":["MESSAGE_BIZ_ID","IFRAME_BASE_URL","production","staging","boe","development","getIframeBaseURL","env","hostname","window","location","includes","MESSAGE_TIMEOUT","DEFAULT_CHANNEL_TYPE","DEFAULT_ANONYMOUS_CHANNEL_TYPE","ALLOWED_ORIGINS","slardar","MOBILE_BREAKPOINT","DeviceDetector","mediaQuery","currentDevice","callback","handleChange","window","matchMedia","innerWidth","getDevice","observe","newDevice","addEventListener","destroy","removeEventListener","slardar","MessageBridge","iframe","requestId","pendingRequests","Map","eventHandlers","targetOrigin","handleMessage","bind","attach","window","addEventListener","detach","removeEventListener","forEach","reject","Error","clear","send","type","data","Promise","resolve","contentWindow","id","message","messageBizId","MESSAGE_BIZ_ID","timestamp","Date","now","isRequest","timer","setTimeout","delete","error","slardar","captureException","source","module","messageType","MESSAGE_TIMEOUT","set","postMessage","onEvent","eventName","handler","handlers","get","push","offEvent","index","indexOf","splice","event","ALLOWED_ORIGINS","includes","origin","isResponseMessage","pending","clearTimeout","errorCode","code","isEventMessage","isResponse","IframeManager","iframe","messageBridge","config","channelType","root","deviceDetector","defaultChannel","anonymous","DEFAULT_ANONYMOUS_CHANNEL_TYPE","DEFAULT_CHANNEL_TYPE","common","MessageBridge","getIframeBaseURL","init","initStart","performance","now","device","DeviceDetector","getDevice","document","createElement","src","buildIframeURL","style","width","height","border","allow","appendChild","attach","setupEventHandlers","observe","newDevice","send","waitForReady","reportInitMetrics","createChatPanel","destroy","detach","parentNode","removeChild","baseURL","events","_events","_anonymous","configWithoutEvents","editorConfig","editor","skill","type","builtinType","iframeConfig","resourceType","configBase64","btoa","encodeURIComponent","JSON","stringify","params","URLSearchParams","appKey","toString","Promise","resolve","reject","timeout","setTimeout","error","Error","slardar","captureException","source","module","checkMessage","event","data","messageBizId","MESSAGE_BIZ_ID","eventName","clearTimeout","window","removeEventListener","addEventListener","onReady","onEvent","onError","msg","message","onMessage","onInited","onInitedWithWelcome","onClose","cost","sendEvent","name","metrics","categories","arch","sendMessage","clear","cancelMessage","messageItem","clearAndStop","updateWelcomeMessage","updateConfig","observeSkill","skillId","skillType","setCurrentSkill","initAilyChat","root","config","manager","IframeManager","init"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/aily-web-sdk",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
3
|
+
"version": "0.0.2-beta.10",
|
|
4
4
|
"description": "Aily Web SDK - iframe-based chat panel integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
18
19
|
"import": "./dist/index.js",
|
|
19
|
-
"require": "./dist/index.cjs"
|
|
20
|
-
"types": "./dist/index.d.ts"
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"build:storybook": "storybook build",
|
|
30
30
|
"prepublishOnly": "npm run build"
|
|
31
31
|
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@lark-apaas/internal-slardar": ">=0.0.1"
|
|
34
|
+
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"@rsbuild/core": "~1.4.13",
|
|
34
37
|
"@rsbuild/plugin-react": "^1.3.4",
|