@obm/apaas-embed-sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -46
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# MediPaaS Embed SDK
|
|
2
2
|
|
|
3
|
-
在第三方网页中通过 iframe 打开 MediPaaS 应用页面。SDK 负责嵌入与实例管理;第三方后端负责获取 embed token。
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
发布到 npm 后,可在第三方项目中安装:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @obm/apaas-embed-sdk
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
当前为 `0.1.0` 初期 MVP 版本,API 仍可能调整。支持 ESM、CommonJS 与 TypeScript 类型声明。iframe 嵌入需要浏览器 DOM 环境。
|
|
3
|
+
在第三方网页中通过 iframe 打开 MediPaaS 应用页面。SDK 负责嵌入与实例管理;第三方后端负责获取 embed token。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
发布到 npm 后,可在第三方项目中安装:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @obm/apaas-embed-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
当前为 `0.1.0` 初期 MVP 版本,API 仍可能调整。支持 ESM、CommonJS 与 TypeScript 类型声明。iframe 嵌入需要浏览器 DOM 环境。
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import { ApassEmbedSDK, type EmbedParamValue } from '@obm/apaas-embed-sdk'
|
|
16
|
+
import { ApassEmbedSDK, type EmbedParamValue } from '@obm/apaas-embed-sdk'
|
|
17
17
|
|
|
18
18
|
// getToken 是第三方系统自己的接口。后端调用
|
|
19
19
|
// POST /api/auth/token/embed?clientId=xxx,返回 token 和 runtimeAppId。
|
|
@@ -25,10 +25,10 @@ const params: Record<string, EmbedParamValue> = {
|
|
|
25
25
|
const instance = await sdk.embed({
|
|
26
26
|
appId: String(runtimeAppId), apiName: 'order_detail', token,
|
|
27
27
|
containerId: 'medipaas-container', params,
|
|
28
|
-
onError: error => console.error(error),
|
|
29
|
-
onEvent: ({ name, payload }) => {
|
|
30
|
-
if (name === 'order.saved') refreshOrder((payload as { orderId: string }).orderId)
|
|
31
|
-
},
|
|
28
|
+
onError: error => console.error(error),
|
|
29
|
+
onEvent: ({ name, payload }) => {
|
|
30
|
+
if (name === 'order.saved') refreshOrder((payload as { orderId: string }).orderId)
|
|
31
|
+
},
|
|
32
32
|
})
|
|
33
33
|
// 需要关闭时:instance.close()
|
|
34
34
|
```
|
|
@@ -82,11 +82,11 @@ Content-Type: application/json
|
|
|
82
82
|
| `containerId` | 可选宿主 DOM ID;省略时创建遮罩弹窗 |
|
|
83
83
|
| `fullscreen` | 默认 false,仅控制默认弹窗尺寸 |
|
|
84
84
|
| `iframeAttrs` | iframe 属性字典,如 title、allow、sandbox;禁止 src/srcdoc(大小写均禁止) |
|
|
85
|
-
| `onReady` | iframe load 回调,不表示首次数据查询完成 |
|
|
86
|
-
| `onBridgeReady` | 消息桥握手完成回调;此后可调用实例 `send()` |
|
|
85
|
+
| `onReady` | iframe load 回调,不表示首次数据查询完成 |
|
|
86
|
+
| `onBridgeReady` | 消息桥握手完成回调;此后可调用实例 `send()` |
|
|
87
87
|
| `onClose` | 实例 close() 回调;closeAll()、遮罩点击不会触发 |
|
|
88
|
-
| `onError` | 参数、容器或 iframe 错误回调;参数错误同时拒绝 embed Promise |
|
|
89
|
-
| `onEvent` | 嵌入页业务事件回调,参数为 `{ name, payload }` |
|
|
88
|
+
| `onError` | 参数、容器或 iframe 错误回调;参数错误同时拒绝 embed Promise |
|
|
89
|
+
| `onEvent` | 嵌入页业务事件回调,参数为 `{ name, payload }` |
|
|
90
90
|
|
|
91
91
|
`params` 的值允许字符串、有限数字、布尔值、null、undefined。字符串通过 URLSearchParams 编码,调用方传原始值,不预先编码。数字与布尔转为字符串;0、false、空字符串保留;null/undefined 属性忽略。对象、数组、函数、NaN、Infinity 均拒绝;params 本身不能为 null。
|
|
92
92
|
|
|
@@ -94,17 +94,28 @@ Content-Type: application/json
|
|
|
94
94
|
|
|
95
95
|
页面通过 `{{ urlParams.value.orderId }}` 读取。所有 SDK 参数在页面中均为字符串,布尔判断使用 `{{ urlParams.value.editable === 'true' }}`,ID 建议传字符串以保留前导零和大整数精度。URL 显式值优先于页面参数默认值,显式空字符串不会被默认值覆盖。目标页面需将参数绑定到数据源查询条件或初始化动作。
|
|
96
96
|
|
|
97
|
-
参数属于当前 iframe 地址,刷新可恢复,各实例互不共享;页间导航需显式传参。切换记录可重新 embed,或在握手完成后调用 `instance.send('params.update', { orderId: '000124' })` 更新目标页面已声明的 URL 参数;返回 `false` 表示通道尚未就绪。`instance.send('page.refresh')` 可重新加载当前页面。URL 中的业务参数不能赋予权限,服务端仍验证 token 与记录权限;不要通过 URL 传敏感内容或大量数据。
|
|
98
|
-
|
|
99
|
-
## 双向消息
|
|
97
|
+
参数属于当前 iframe 地址,刷新可恢复,各实例互不共享;页间导航需显式传参。切换记录可重新 embed,或在握手完成后调用 `instance.send('params.update', { orderId: '000124' })` 更新目标页面已声明的 URL 参数;返回 `false` 表示通道尚未就绪。`instance.send('page.refresh')` 可重新加载当前页面。URL 中的业务参数不能赋予权限,服务端仍验证 token 与记录权限;不要通过 URL 传敏感内容或大量数据。
|
|
98
|
+
|
|
99
|
+
## 双向消息
|
|
100
|
+
|
|
101
|
+
页面在按钮点击或数据请求成功时调用 JS 方法,通过原生 postMessage 发送业务事件;第三方通过 `embed({ onEvent })` 接收 `{ name, payload }`:
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
```js
|
|
104
|
+
window.parent.postMessage({
|
|
105
|
+
name: 'order.saved',
|
|
106
|
+
payload: { orderId: '000123' },
|
|
107
|
+
}, 'https://your-host-domain.com') // 第三方宿主的 origin(协议、域名、端口)
|
|
108
|
+
```
|
|
102
109
|
|
|
103
|
-
SDK
|
|
110
|
+
业务消息无需 namespace、version、channelId、type,也无需等待握手。SDK 只接收当前 iframe、配置 baseUrl 同源的消息,以及非空字符串 name;回调传入 `{ name, payload }`。旧的 `type: 'event'` 消息仍可接收,握手控制消息不会触发 onEvent。接收端保留 source 和 origin 校验;不要在事件载荷中传 token 或敏感业务数据。
|
|
104
111
|
|
|
105
|
-
|
|
112
|
+
设计器已移除“通知第三方宿主”专用动作及执行逻辑。已有页面的 notifyHost 动作需手动改为调用 JS 方法,并保存、发布对应应用;本次不批量修改已保存的业务页面。
|
|
113
|
+
|
|
114
|
+
宿主向页面发送 `page.refresh`、`params.update` 的握手与指令仍使用内部 `medipaas.embed` v1 协议,业务脚本无需处理这些字段。
|
|
106
115
|
|
|
107
|
-
|
|
116
|
+
`onReady` 仍只表示 iframe `load`,不表示业务数据完成或消息桥就绪。`onBridgeReady` 表示可以调用实例 `send()`;提前调用会返回 `false`。`onEvent` 接收不依赖这两个回调。页面内保存成功通知应通过数据请求成功事件调用 JS 方法。消息仅送达当前打开的 iframe,不提供离线投递。
|
|
117
|
+
|
|
118
|
+
实例返回 iframe、token、expiresAt(当前固定 0)、send() 和 close()。SDK 还提供 closeAll() 与 getInstanceCount()。
|
|
108
119
|
|
|
109
120
|
## 构建与示例
|
|
110
121
|
|
|
@@ -114,23 +125,29 @@ SDK 与嵌入页使用 `medipaas.embed` v1 协议。SDK 核对消息来源窗口
|
|
|
114
125
|
- [文档索引](docs/README.md)
|
|
115
126
|
- [原生 HTML 示例](examples/basic-html/index.html):构建后以 HTTP 静态服务打开,填入前端地址、runtimeAppId、apiName、token 和参数。
|
|
116
127
|
|
|
117
|
-
测试:`pnpm --filter @obm/apaas-embed-sdk exec vitest run`。
|
|
128
|
+
测试:`pnpm --filter @obm/apaas-embed-sdk exec vitest run`。
|
|
118
129
|
|
|
119
|
-
在前端工作区运行 `pnpm --filter medipaas-frontend test:embed-params`,会先构建最新 SDK,再验证设计器生成代码与页面参数查询链路,避免读取旧 dist。
|
|
120
|
-
|
|
121
|
-
## 发布检查
|
|
122
|
-
|
|
123
|
-
在 SDK 目录执行 `pnpm run verify`,依次检查 lint、单元测试、构建、打包、临时项目安装、ESM/CJS 导入及类型声明。临时测试文件会自动清理。
|
|
124
|
-
|
|
125
|
-
`npm publish` 会通过 `prepublishOnly` 自动执行完整验证,检查失败时停止发布。发布前需确认使用 `obm` 账号发布到 `@obm` scope,且目标版本尚未发布。
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
pnpm run publish:check
|
|
129
|
-
pnpm run publish:release
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
第一条仅预演;第二条实际发布公开包,使用默认的 `latest` 标签。npm 版本发布后不能覆盖,后续发布需更新 `package.json` 版本号。
|
|
133
|
-
|
|
134
|
-
## 许可与反馈
|
|
135
|
-
|
|
136
|
-
本 SDK 使用 [MIT License](LICENSE)。仓库及问题反馈地址见 `package.json` 的 `repository`、`homepage` 与 `bugs` 字段;访问项目 GitLab 可能需要账号权限。
|
|
130
|
+
在前端工作区运行 `pnpm --filter medipaas-frontend test:embed-params`,会先构建最新 SDK,再验证设计器生成代码与页面参数查询链路,避免读取旧 dist。
|
|
131
|
+
|
|
132
|
+
## 发布检查
|
|
133
|
+
|
|
134
|
+
在 SDK 目录执行 `pnpm run verify`,依次检查 lint、单元测试、构建、打包、临时项目安装、ESM/CJS 导入及类型声明。临时测试文件会自动清理。
|
|
135
|
+
|
|
136
|
+
`npm publish` 会通过 `prepublishOnly` 自动执行完整验证,检查失败时停止发布。发布前需确认使用 `obm` 账号发布到 `@obm` scope,且目标版本尚未发布。
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pnpm run publish:check
|
|
140
|
+
pnpm run publish:release
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
第一条仅预演;第二条实际发布公开包,使用默认的 `latest` 标签。npm 版本发布后不能覆盖,后续发布需更新 `package.json` 版本号。
|
|
144
|
+
|
|
145
|
+
## 许可与反馈
|
|
146
|
+
|
|
147
|
+
本 SDK 使用 [MIT License](LICENSE)。仓库及问题反馈地址见 `package.json` 的 `repository`、`homepage` 与 `bugs` 字段;访问项目 GitLab 可能需要账号权限。
|
|
148
|
+
|
|
149
|
+
公开会话中的 `runtimeAppId` 字段保留,值为稳定的逻辑应用 ID(响应如提供 `appId`,含义相同)。
|
|
150
|
+
始终将此值传给 SDK 的 `appId`。后续发布或回滚无需更换 appId、URL 或 token;
|
|
151
|
+
APaaS 页面会在发现配置已更新时内部重新加载,不要求宿主监听发版事件。
|
|
152
|
+
触发版本冲突的写入或流程不会自动重试,页面更新后由用户重新操作。
|
|
153
|
+
物理版本预览凭证仍访问指定版本;本变更不迁移旧 token 或旧物理公开 URL。
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var g=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var K=(r,e,t)=>e in r?g(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var L=(r,e)=>{for(var t in e)g(r,t,{get:e[t],enumerable:!0})},N=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of $(e))!k.call(r,s)&&s!==t&&g(r,s,{get:()=>e[s],enumerable:!(n=D(e,s))||n.enumerable});return r};var T=r=>N(g({},"__esModule",{value:!0}),r);var
|
|
1
|
+
"use strict";var g=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var K=(r,e,t)=>e in r?g(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var L=(r,e)=>{for(var t in e)g(r,t,{get:e[t],enumerable:!0})},N=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of $(e))!k.call(r,s)&&s!==t&&g(r,s,{get:()=>e[s],enumerable:!(n=D(e,s))||n.enumerable});return r};var T=r=>N(g({},"__esModule",{value:!0}),r);var f=(r,e,t)=>K(r,typeof e!="symbol"?e+"":e,t);var z={};L(z,{ApassEmbedSDK:()=>h});module.exports=T(z);var _=new Set(["token","__proto__","constructor","prototype"]);function x(r){if(r===null||typeof r!="object")return!1;let e=Object.getPrototypeOf(r);return e===null?!0:Object.getPrototypeOf(e)===null&&Object.prototype.hasOwnProperty.call(e,"constructor")&&typeof e.constructor=="function"&&Function.prototype.toString.call(e.constructor)===Function.prototype.toString.call(Object)}function O(r={}){if(!x(r))throw new Error("iframeAttrs must be a plain object");let e=Object.entries(r);for(let[t]of e){let n=t.trim().toLowerCase();if(n==="src"||n==="srcdoc")throw new Error(`iframeAttrs.${t} cannot override the SDK page address`);if(!t||/[\s"'>/=\u0000-\u001f]/.test(t))throw new Error(`Invalid iframe attribute name: ${t}`)}return Object.fromEntries(e)}function I(r,e){if(!r||r==="."||r==="..")throw new Error(`${e} must be a non-empty path segment other than . or ..`);return encodeURIComponent(r)}function S(r,e){if(!e.token)throw new Error("Token is required");let t=String(e.apiName||e.pageCode||e.pageId||"").trim();if(!t)throw new Error("Either apiName, pageCode (deprecated), or pageId (deprecated) is required");let n=new URL(`/apps/${I(e.appId,"appId")}/user/${I(t,"apiName")}`,r);n.searchParams.set("token",e.token);let s=e.params;if(s!==void 0){if(!x(s))throw new Error("params must be a plain object");for(let[i,c]of Object.entries(s)){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)||_.has(i))throw new Error(`Invalid or reserved parameter name: ${i}`);if(c!=null){if(!["string","number","boolean"].includes(typeof c)||typeof c=="number"&&!Number.isFinite(c))throw new Error(`Parameter ${i} must be a string, finite number, or boolean`);n.searchParams.set(i,String(c))}}}return n.toString()}var h=class{constructor(e){f(this,"config");f(this,"instances",new Map);f(this,"cleanups",new Map);f(this,"instanceCounter",0);this.validateConfig(e),this.config={timeout:3e4,...e}}async embed(e){let{apiName:t,pageCode:n,pageId:s,token:i,containerId:c,onReady:A,onBridgeReady:R,onClose:U,onError:E,onEvent:j,iframeAttrs:P={},fullscreen:M=!1}=e;try{let d=S(this.config.baseUrl,e),l=O(P);n&&!t&&console.warn("[MediPaaS SDK] pageCode is deprecated. Please use apiName instead."),s&&!t&&!n&&console.warn("[MediPaaS SDK] pageId is deprecated. Please use apiName instead.");let p=this.createIframe(d,l),v=this.prepareContainer(c,M),m=`apass-${++this.instanceCounter}`,b=crypto.randomUUID(),w=new URL(d).origin,u=!1,C=o=>{if(o.source!==p.contentWindow||o.origin!==w)return;let a=o.data;if(!(!a||typeof a!="object"||Array.isArray(a))){if(a.type==="ready"){a.namespace==="medipaas.embed"&&a.version===1&&a.channelId===b&&!u&&(u=!0,R?.());return}(a.type===void 0||a.type==="event")&&typeof a.name=="string"&&a.name.trim()&&j?.({name:a.name,payload:a.payload})}};window.addEventListener("message",C);let y={iframe:p,token:i,expiresAt:0,close:()=>{this.closeInstance(m),U?.()},send:(o,a)=>{if(!u||!this.instances.has(m)||o!=="page.refresh"&&o!=="params.update")return!1;try{return p.contentWindow?.postMessage({namespace:"medipaas.embed",version:1,channelId:b,type:"event",name:o,payload:a},w),!0}catch{return!1}}};if(!c){let o=v.parentElement;o.dataset.apassOverlay=m,o.addEventListener("click",a=>{a.target===o&&y.close()})}return p.onload=()=>{u=!1,p.contentWindow?.postMessage({namespace:"medipaas.embed",version:1,channelId:b,type:"hello"},w),A?.()},p.onerror=()=>{let o=new Error("Failed to load A-pass iframe");E?.(o)},v.appendChild(p),this.instances.set(m,y),this.cleanups.set(m,()=>window.removeEventListener("message",C)),y}catch(d){let l=d instanceof Error?d:new Error(String(d));throw E?.(l),l}}closeInstance(e){let t=this.instances.get(e);if(t){this.cleanups.get(e)?.(),this.cleanups.delete(e);let n=t.iframe.parentElement?.parentElement;t.iframe.remove(),n?.dataset.apassOverlay===e&&n.remove(),this.instances.delete(e)}}prepareContainer(e,t){if(e){let i=document.getElementById(e);if(!i)throw new Error(`Container with id "${e}" not found`);return i}let n=document.createElement("div");n.style.cssText=`
|
|
2
2
|
position: fixed;
|
|
3
3
|
top: 0;
|
|
4
4
|
left: 0;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/embedUrl.ts","../src/core/sdk.ts"],"sourcesContent":["export { ApassEmbedSDK } from './core/sdk';\r\nexport type {\r\n SDKConfig,\r\n EmbedOptions,\r\n EmbedParamValue,\n EmbedBusinessEvent,\n ApassInstanceRef,\r\n} from './types';\r\n","import type { EmbedOptions } from '../types';\r\n\r\nconst reservedNames = new Set(['token', '__proto__', 'constructor', 'prototype']);\r\n\r\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\r\n if (value === null || typeof value !== 'object') return false;\r\n const prototype = Object.getPrototypeOf(value);\r\n if (prototype === null) return true;\r\n // Object.prototype differs between same-origin windows. Compare its native\r\n // constructor instead of requiring this window's exact prototype identity.\r\n return Object.getPrototypeOf(prototype) === null &&\r\n Object.prototype.hasOwnProperty.call(prototype, 'constructor') &&\r\n typeof prototype.constructor === 'function' &&\r\n Function.prototype.toString.call(prototype.constructor) === Function.prototype.toString.call(Object);\r\n}\r\n\r\n/** Snapshot once so parameter/attribute getters cannot later replace the address. */\r\nexport function prepareIframeAttributes(attrs: Record<string, string> = {}): Record<string, string> {\r\n if (!isPlainObject(attrs)) throw new Error('iframeAttrs must be a plain object');\r\n const entries = Object.entries(attrs);\r\n for (const [name] of entries) {\r\n const normalized = name.trim().toLowerCase();\r\n if (normalized === 'src' || normalized === 'srcdoc') {\r\n throw new Error(`iframeAttrs.${name} cannot override the SDK page address`);\r\n }\r\n // eslint-disable-next-line no-control-regex -- Intentionally reject ASCII control characters in attribute names.\r\n if (!name || /[\\s\"'>/=\\u0000-\\u001f]/.test(name)) {\r\n throw new Error(`Invalid iframe attribute name: ${name}`);\r\n }\r\n }\r\n return Object.fromEntries(entries);\r\n}\r\n\r\nfunction encodeSegment(value: string, name: string): string {\r\n if (!value || value === '.' || value === '..') {\r\n throw new Error(`${name} must be a non-empty path segment other than . or ..`);\r\n }\r\n return encodeURIComponent(value);\r\n}\r\n\r\n/** Build and validate the complete iframe address before any DOM is created. */\r\nexport function buildEmbedUrl(baseUrl: string, options: EmbedOptions): string {\r\n if (!options.token) throw new Error('Token is required');\r\n const apiName = String(options.apiName || options.pageCode || options.pageId || '').trim();\r\n if (!apiName) {\r\n throw new Error('Either apiName, pageCode (deprecated), or pageId (deprecated) is required');\r\n }\r\n const url = new URL(`/apps/${encodeSegment(options.appId, 'appId')}/user/${encodeSegment(apiName, 'apiName')}`, baseUrl);\r\n url.searchParams.set('token', options.token);\r\n const params = options.params;\r\n if (params !== undefined) {\r\n if (!isPlainObject(params)) {\r\n throw new Error('params must be a plain object');\r\n }\r\n for (const [name, value] of Object.entries(params)) {\r\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || reservedNames.has(name)) {\r\n throw new Error(`Invalid or reserved parameter name: ${name}`);\r\n }\r\n if (value === null || value === undefined) continue;\r\n if (!['string', 'number', 'boolean'].includes(typeof value) ||\r\n (typeof value === 'number' && !Number.isFinite(value))) {\r\n throw new Error(`Parameter ${name} must be a string, finite number, or boolean`);\r\n }\r\n url.searchParams.set(name, String(value));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n","import type {\r\n SDKConfig,\r\n EmbedOptions,\r\n ApassInstanceRef,\r\n} from '../types';\r\nimport { buildEmbedUrl, prepareIframeAttributes } from './embedUrl';\r\n\r\n/**\r\n * Main SDK class for embedding A-pass applications\r\n *\r\n * @example\r\n * ```ts\r\n * const sdk = new ApassEmbedSDK({\r\n * baseUrl: 'https://apass.example.com',\r\n * });\r\n *\r\n * // CRM 项目先调自己的后端获取 token\r\n * const tokenResponse = await fetch('/api/embed-token', {\r\n * method: 'POST',\r\n * body: JSON.stringify({\r\n * recordId: 416,\r\n * enterpriseId: 456,\r\n * }),\r\n * });\r\n * const { token } = await tokenResponse.json();\r\n *\r\n * // 然后把 token 传给 SDK\r\n * await sdk.embed({\r\n * appId: '180',\r\n * apiName: 'patient_list',\r\n * token: token,\r\n * containerId: 'my-container',\r\n * });\r\n * ```\r\n */\r\nexport class ApassEmbedSDK {\r\n private config: SDKConfig;\r\n private instances: Map<string, ApassInstanceRef> = new Map();\n private cleanups: Map<string, () => void> = new Map();\n private instanceCounter = 0;\r\n\r\n constructor(config: SDKConfig) {\r\n this.validateConfig(config);\r\n this.config = {\r\n timeout: 30000,\r\n ...config,\r\n };\r\n }\r\n\r\n /**\r\n * Embed an A-pass application in an iframe\r\n */\r\n async embed(options: EmbedOptions): Promise<ApassInstanceRef> {\r\n const {\r\n apiName,\r\n pageCode,\r\n pageId,\r\n token,\r\n containerId,\r\n onReady,\n onBridgeReady,\n onClose,\r\n onError,\n onEvent,\n iframeAttrs = {},\r\n fullscreen = false,\r\n } = options;\r\n\r\n try {\r\n const url = buildEmbedUrl(this.config.baseUrl, options);\r\n const attrs = prepareIframeAttributes(iframeAttrs);\r\n // Warn if using deprecated parameters\r\n if (pageCode && !apiName) {\r\n console.warn('[MediPaaS SDK] pageCode is deprecated. Please use apiName instead.');\r\n }\r\n if (pageId && !apiName && !pageCode) {\r\n console.warn('[MediPaaS SDK] pageId is deprecated. Please use apiName instead.');\r\n }\r\n\r\n // Prepare the detached iframe first: DOM attribute validation must finish\r\n // before a default overlay is attached to the host page.\r\n const iframe = this.createIframe(url, attrs);\r\n const container = this.prepareContainer(containerId, fullscreen);\r\n\r\n // Step 5: Setup lifecycle handlers\r\n const instanceId = `apass-${++this.instanceCounter}`;\n const channelId = crypto.randomUUID();\n const targetOrigin = new URL(url).origin;\n let ready = false;\n const receiveMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow || event.origin !== targetOrigin) return;\n const message = event.data;\n if (!message || typeof message !== 'object' || message.namespace !== 'medipaas.embed'\n || message.version !== 1 || message.channelId !== channelId) return;\n if (message.type === 'ready' && !ready) {\n ready = true;\n onBridgeReady?.();\n }\n if (message.type === 'event' && ready && typeof message.name === 'string' && message.name) {\n onEvent?.({ name: message.name, payload: message.payload });\n }\n };\n window.addEventListener('message', receiveMessage);\n\n const instance: ApassInstanceRef = {\n iframe,\r\n token,\r\n expiresAt: 0, // Token expiration not tracked in this mode\r\n close: () => {\n this.closeInstance(instanceId);\n onClose?.();\n },\n send: (name, payload) => {\n if (!ready || !this.instances.has(instanceId)) return false;\n if (name !== 'page.refresh' && name !== 'params.update') return false;\n try {\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'event', name, payload }, targetOrigin);\n return true;\n } catch {\n return false;\n }\n },\n };\n\n if (!containerId) {\n const overlay = container.parentElement!;\n overlay.dataset.apassOverlay = instanceId;\n overlay.addEventListener('click', (event) => {\n if (event.target === overlay) instance.close();\n });\n }\n\r\n // Step 6: Setup ready handler before attaching the iframe\n iframe.onload = () => {\n ready = false;\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'hello' }, targetOrigin);\n onReady?.();\n };\r\n\r\n // Step 8: Setup error handler\r\n iframe.onerror = () => {\n const error = new Error('Failed to load A-pass iframe');\r\n onError?.(error);\r\n };\n\n container.appendChild(iframe);\n\r\n // Store instance reference\r\n this.instances.set(instanceId, instance);\n this.cleanups.set(instanceId, () => window.removeEventListener('message', receiveMessage));\n\r\n return instance;\r\n } catch (error) {\r\n const err =\r\n error instanceof Error ? error : new Error(String(error));\r\n onError?.(err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Close and cleanup an instance\r\n */\r\n private closeInstance(instanceId: string): void {\n const instance = this.instances.get(instanceId);\r\n if (instance) {\n this.cleanups.get(instanceId)?.();\n this.cleanups.delete(instanceId);\n // Remove iframe from DOM\n const overlay = instance.iframe.parentElement?.parentElement;\n instance.iframe.remove();\n if (overlay?.dataset.apassOverlay === instanceId) overlay.remove();\n // Remove from tracking\r\n this.instances.delete(instanceId);\r\n }\r\n }\n\r\n\r\n /**\r\n * Prepare container for iframe\r\n */\r\n private prepareContainer(\r\n containerId: string | undefined,\r\n fullscreen: boolean,\r\n ): HTMLElement {\r\n if (containerId) {\r\n const container = document.getElementById(containerId);\r\n if (!container) {\r\n throw new Error(`Container with id \"${containerId}\" not found`);\r\n }\r\n return container;\r\n }\r\n\r\n // Create default container\r\n const container = document.createElement('div');\r\n container.style.cssText = `\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 10000;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n `;\r\n\r\n const wrapper = document.createElement('div');\r\n if (fullscreen) {\r\n wrapper.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n `;\r\n } else {\r\n wrapper.style.cssText = `\r\n width: 90%;\r\n height: 90%;\r\n max-width: 1200px;\r\n max-height: 800px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n `;\r\n }\r\n\r\n container.appendChild(wrapper);\r\n document.body.appendChild(container);\r\n\r\n return wrapper;\n }\r\n\r\n /**\r\n * Create iframe element\r\n */\r\n private createIframe(\r\n url: string,\r\n attrs: Record<string, string>,\r\n ): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n\r\n // Build A-pass URL with token parameter\r\n iframe.src = url;\r\n iframe.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n `;\r\n\r\n // Apply custom attributes\r\n Object.entries(attrs).forEach(([key, value]) => {\r\n iframe.setAttribute(key, value);\r\n });\r\n\r\n return iframe;\r\n }\r\n\r\n /**\r\n * Validate SDK configuration\r\n */\r\n private validateConfig(config: SDKConfig): void {\r\n if (!config.baseUrl) {\r\n throw new Error('SDKConfig.baseUrl is required');\r\n }\r\n\r\n try {\r\n new URL(config.baseUrl);\r\n } catch {\r\n throw new Error(\r\n `SDKConfig.baseUrl must be a valid URL: ${config.baseUrl}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Close all instances\r\n */\r\n closeAll(): void {\r\n this.instances.forEach((_, instanceId) => {\r\n this.closeInstance(instanceId);\r\n });\r\n }\r\n\r\n /**\r\n * Get active instances count\r\n */\r\n getInstanceCount(): number {\r\n return this.instances.size;\r\n }\r\n}\r\n"],"mappings":"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","names":["src_exports","__export","ApassEmbedSDK","__toCommonJS","reservedNames","isPlainObject","value","prototype","prepareIframeAttributes","attrs","entries","name","normalized","encodeSegment","buildEmbedUrl","baseUrl","options","apiName","url","params","ApassEmbedSDK","config","__publicField","options","apiName","pageCode","pageId","token","containerId","onReady","onBridgeReady","onClose","onError","onEvent","iframeAttrs","fullscreen","url","buildEmbedUrl","attrs","prepareIframeAttributes","iframe","container","instanceId","channelId","targetOrigin","ready","receiveMessage","event","message","instance","name","payload","overlay","error","err","wrapper","key","value","_"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/embedUrl.ts","../src/core/sdk.ts"],"sourcesContent":["export { ApassEmbedSDK } from './core/sdk';\r\nexport type {\r\n SDKConfig,\r\n EmbedOptions,\r\n EmbedParamValue,\n EmbedBusinessEvent,\n ApassInstanceRef,\r\n} from './types';\r\n","import type { EmbedOptions } from '../types';\r\n\r\nconst reservedNames = new Set(['token', '__proto__', 'constructor', 'prototype']);\r\n\r\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\r\n if (value === null || typeof value !== 'object') return false;\r\n const prototype = Object.getPrototypeOf(value);\r\n if (prototype === null) return true;\r\n // Object.prototype differs between same-origin windows. Compare its native\r\n // constructor instead of requiring this window's exact prototype identity.\r\n return Object.getPrototypeOf(prototype) === null &&\r\n Object.prototype.hasOwnProperty.call(prototype, 'constructor') &&\r\n typeof prototype.constructor === 'function' &&\r\n Function.prototype.toString.call(prototype.constructor) === Function.prototype.toString.call(Object);\r\n}\r\n\r\n/** Snapshot once so parameter/attribute getters cannot later replace the address. */\r\nexport function prepareIframeAttributes(attrs: Record<string, string> = {}): Record<string, string> {\r\n if (!isPlainObject(attrs)) throw new Error('iframeAttrs must be a plain object');\r\n const entries = Object.entries(attrs);\r\n for (const [name] of entries) {\r\n const normalized = name.trim().toLowerCase();\r\n if (normalized === 'src' || normalized === 'srcdoc') {\r\n throw new Error(`iframeAttrs.${name} cannot override the SDK page address`);\r\n }\r\n // eslint-disable-next-line no-control-regex -- Intentionally reject ASCII control characters in attribute names.\r\n if (!name || /[\\s\"'>/=\\u0000-\\u001f]/.test(name)) {\r\n throw new Error(`Invalid iframe attribute name: ${name}`);\r\n }\r\n }\r\n return Object.fromEntries(entries);\r\n}\r\n\r\nfunction encodeSegment(value: string, name: string): string {\r\n if (!value || value === '.' || value === '..') {\r\n throw new Error(`${name} must be a non-empty path segment other than . or ..`);\r\n }\r\n return encodeURIComponent(value);\r\n}\r\n\r\n/** Build and validate the complete iframe address before any DOM is created. */\r\nexport function buildEmbedUrl(baseUrl: string, options: EmbedOptions): string {\r\n if (!options.token) throw new Error('Token is required');\r\n const apiName = String(options.apiName || options.pageCode || options.pageId || '').trim();\r\n if (!apiName) {\r\n throw new Error('Either apiName, pageCode (deprecated), or pageId (deprecated) is required');\r\n }\r\n const url = new URL(`/apps/${encodeSegment(options.appId, 'appId')}/user/${encodeSegment(apiName, 'apiName')}`, baseUrl);\r\n url.searchParams.set('token', options.token);\r\n const params = options.params;\r\n if (params !== undefined) {\r\n if (!isPlainObject(params)) {\r\n throw new Error('params must be a plain object');\r\n }\r\n for (const [name, value] of Object.entries(params)) {\r\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || reservedNames.has(name)) {\r\n throw new Error(`Invalid or reserved parameter name: ${name}`);\r\n }\r\n if (value === null || value === undefined) continue;\r\n if (!['string', 'number', 'boolean'].includes(typeof value) ||\r\n (typeof value === 'number' && !Number.isFinite(value))) {\r\n throw new Error(`Parameter ${name} must be a string, finite number, or boolean`);\r\n }\r\n url.searchParams.set(name, String(value));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n","import type {\r\n SDKConfig,\r\n EmbedOptions,\r\n ApassInstanceRef,\r\n} from '../types';\r\nimport { buildEmbedUrl, prepareIframeAttributes } from './embedUrl';\r\n\r\n/**\r\n * Main SDK class for embedding A-pass applications\r\n *\r\n * @example\r\n * ```ts\r\n * const sdk = new ApassEmbedSDK({\r\n * baseUrl: 'https://apass.example.com',\r\n * });\r\n *\r\n * // CRM 项目先调自己的后端获取 token\r\n * const tokenResponse = await fetch('/api/embed-token', {\r\n * method: 'POST',\r\n * body: JSON.stringify({\r\n * recordId: 416,\r\n * enterpriseId: 456,\r\n * }),\r\n * });\r\n * const { token } = await tokenResponse.json();\r\n *\r\n * // 然后把 token 传给 SDK\r\n * await sdk.embed({\r\n * appId: '180',\r\n * apiName: 'patient_list',\r\n * token: token,\r\n * containerId: 'my-container',\r\n * });\r\n * ```\r\n */\r\nexport class ApassEmbedSDK {\r\n private config: SDKConfig;\r\n private instances: Map<string, ApassInstanceRef> = new Map();\n private cleanups: Map<string, () => void> = new Map();\n private instanceCounter = 0;\r\n\r\n constructor(config: SDKConfig) {\r\n this.validateConfig(config);\r\n this.config = {\r\n timeout: 30000,\r\n ...config,\r\n };\r\n }\r\n\r\n /**\r\n * Embed an A-pass application in an iframe\r\n */\r\n async embed(options: EmbedOptions): Promise<ApassInstanceRef> {\r\n const {\r\n apiName,\r\n pageCode,\r\n pageId,\r\n token,\r\n containerId,\r\n onReady,\n onBridgeReady,\n onClose,\r\n onError,\n onEvent,\n iframeAttrs = {},\r\n fullscreen = false,\r\n } = options;\r\n\r\n try {\r\n const url = buildEmbedUrl(this.config.baseUrl, options);\r\n const attrs = prepareIframeAttributes(iframeAttrs);\r\n // Warn if using deprecated parameters\r\n if (pageCode && !apiName) {\r\n console.warn('[MediPaaS SDK] pageCode is deprecated. Please use apiName instead.');\r\n }\r\n if (pageId && !apiName && !pageCode) {\r\n console.warn('[MediPaaS SDK] pageId is deprecated. Please use apiName instead.');\r\n }\r\n\r\n // Prepare the detached iframe first: DOM attribute validation must finish\r\n // before a default overlay is attached to the host page.\r\n const iframe = this.createIframe(url, attrs);\r\n const container = this.prepareContainer(containerId, fullscreen);\r\n\r\n // Step 5: Setup lifecycle handlers\r\n const instanceId = `apass-${++this.instanceCounter}`;\n const channelId = crypto.randomUUID();\n const targetOrigin = new URL(url).origin;\n let ready = false;\n const receiveMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow || event.origin !== targetOrigin) return;\n const message = event.data;\n if (!message || typeof message !== 'object' || Array.isArray(message)) return;\n if (message.type === 'ready') {\n if (message.namespace === 'medipaas.embed' && message.version === 1\n && message.channelId === channelId && !ready) {\n ready = true;\n onBridgeReady?.();\n }\n return;\n }\n // Business scripts can post { name, payload } directly, without a handshake.\n if ((message.type === undefined || message.type === 'event')\n && typeof message.name === 'string' && message.name.trim()) {\n onEvent?.({ name: message.name, payload: message.payload });\n }\n };\n window.addEventListener('message', receiveMessage);\n\n const instance: ApassInstanceRef = {\n iframe,\r\n token,\r\n expiresAt: 0, // Token expiration not tracked in this mode\r\n close: () => {\n this.closeInstance(instanceId);\n onClose?.();\n },\n send: (name, payload) => {\n if (!ready || !this.instances.has(instanceId)) return false;\n if (name !== 'page.refresh' && name !== 'params.update') return false;\n try {\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'event', name, payload }, targetOrigin);\n return true;\n } catch {\n return false;\n }\n },\n };\n\n if (!containerId) {\n const overlay = container.parentElement!;\n overlay.dataset.apassOverlay = instanceId;\n overlay.addEventListener('click', (event) => {\n if (event.target === overlay) instance.close();\n });\n }\n\r\n // Step 6: Setup ready handler before attaching the iframe\n iframe.onload = () => {\n ready = false;\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'hello' }, targetOrigin);\n onReady?.();\n };\r\n\r\n // Step 8: Setup error handler\r\n iframe.onerror = () => {\n const error = new Error('Failed to load A-pass iframe');\r\n onError?.(error);\r\n };\n\n container.appendChild(iframe);\n\r\n // Store instance reference\r\n this.instances.set(instanceId, instance);\n this.cleanups.set(instanceId, () => window.removeEventListener('message', receiveMessage));\n\r\n return instance;\r\n } catch (error) {\r\n const err =\r\n error instanceof Error ? error : new Error(String(error));\r\n onError?.(err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Close and cleanup an instance\r\n */\r\n private closeInstance(instanceId: string): void {\n const instance = this.instances.get(instanceId);\r\n if (instance) {\n this.cleanups.get(instanceId)?.();\n this.cleanups.delete(instanceId);\n // Remove iframe from DOM\n const overlay = instance.iframe.parentElement?.parentElement;\n instance.iframe.remove();\n if (overlay?.dataset.apassOverlay === instanceId) overlay.remove();\n // Remove from tracking\r\n this.instances.delete(instanceId);\r\n }\r\n }\n\r\n\r\n /**\r\n * Prepare container for iframe\r\n */\r\n private prepareContainer(\r\n containerId: string | undefined,\r\n fullscreen: boolean,\r\n ): HTMLElement {\r\n if (containerId) {\r\n const container = document.getElementById(containerId);\r\n if (!container) {\r\n throw new Error(`Container with id \"${containerId}\" not found`);\r\n }\r\n return container;\r\n }\r\n\r\n // Create default container\r\n const container = document.createElement('div');\r\n container.style.cssText = `\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 10000;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n `;\r\n\r\n const wrapper = document.createElement('div');\r\n if (fullscreen) {\r\n wrapper.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n `;\r\n } else {\r\n wrapper.style.cssText = `\r\n width: 90%;\r\n height: 90%;\r\n max-width: 1200px;\r\n max-height: 800px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n `;\r\n }\r\n\r\n container.appendChild(wrapper);\r\n document.body.appendChild(container);\r\n\r\n return wrapper;\n }\r\n\r\n /**\r\n * Create iframe element\r\n */\r\n private createIframe(\r\n url: string,\r\n attrs: Record<string, string>,\r\n ): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n\r\n // Build A-pass URL with token parameter\r\n iframe.src = url;\r\n iframe.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n `;\r\n\r\n // Apply custom attributes\r\n Object.entries(attrs).forEach(([key, value]) => {\r\n iframe.setAttribute(key, value);\r\n });\r\n\r\n return iframe;\r\n }\r\n\r\n /**\r\n * Validate SDK configuration\r\n */\r\n private validateConfig(config: SDKConfig): void {\r\n if (!config.baseUrl) {\r\n throw new Error('SDKConfig.baseUrl is required');\r\n }\r\n\r\n try {\r\n new URL(config.baseUrl);\r\n } catch {\r\n throw new Error(\r\n `SDKConfig.baseUrl must be a valid URL: ${config.baseUrl}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Close all instances\r\n */\r\n closeAll(): void {\r\n this.instances.forEach((_, instanceId) => {\r\n this.closeInstance(instanceId);\r\n });\r\n }\r\n\r\n /**\r\n * Get active instances count\r\n */\r\n getInstanceCount(): number {\r\n return this.instances.size;\r\n }\r\n}\r\n"],"mappings":"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","names":["src_exports","__export","ApassEmbedSDK","__toCommonJS","reservedNames","isPlainObject","value","prototype","prepareIframeAttributes","attrs","entries","name","normalized","encodeSegment","buildEmbedUrl","baseUrl","options","apiName","url","params","ApassEmbedSDK","config","__publicField","options","apiName","pageCode","pageId","token","containerId","onReady","onBridgeReady","onClose","onError","onEvent","iframeAttrs","fullscreen","url","buildEmbedUrl","attrs","prepareIframeAttributes","iframe","container","instanceId","channelId","targetOrigin","ready","receiveMessage","event","message","instance","name","payload","overlay","error","err","wrapper","key","value","_"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -38,7 +38,7 @@ interface EmbedOptions {
|
|
|
38
38
|
onClose?: () => void;
|
|
39
39
|
/** Optional callback on error */
|
|
40
40
|
onError?: (error: Error) => void;
|
|
41
|
-
/**
|
|
41
|
+
/** Receives iframe postMessage({ name, payload }); no handshake required. */
|
|
42
42
|
onEvent?: (event: EmbedBusinessEvent) => void;
|
|
43
43
|
/** Optional custom iframe attributes. src and srcdoc are prohibited. */
|
|
44
44
|
iframeAttrs?: Record<string, string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ interface EmbedOptions {
|
|
|
38
38
|
onClose?: () => void;
|
|
39
39
|
/** Optional callback on error */
|
|
40
40
|
onError?: (error: Error) => void;
|
|
41
|
-
/**
|
|
41
|
+
/** Receives iframe postMessage({ name, payload }); no handshake required. */
|
|
42
42
|
onEvent?: (event: EmbedBusinessEvent) => void;
|
|
43
43
|
/** Optional custom iframe attributes. src and srcdoc are prohibited. */
|
|
44
44
|
iframeAttrs?: Record<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var M=Object.defineProperty;var D=(n,e,t)=>e in n?M(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var
|
|
1
|
+
var M=Object.defineProperty;var D=(n,e,t)=>e in n?M(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var f=(n,e,t)=>D(n,typeof e!="symbol"?e+"":e,t);var $=new Set(["token","__proto__","constructor","prototype"]);function I(n){if(n===null||typeof n!="object")return!1;let e=Object.getPrototypeOf(n);return e===null?!0:Object.getPrototypeOf(e)===null&&Object.prototype.hasOwnProperty.call(e,"constructor")&&typeof e.constructor=="function"&&Function.prototype.toString.call(e.constructor)===Function.prototype.toString.call(Object)}function x(n={}){if(!I(n))throw new Error("iframeAttrs must be a plain object");let e=Object.entries(n);for(let[t]of e){let r=t.trim().toLowerCase();if(r==="src"||r==="srcdoc")throw new Error(`iframeAttrs.${t} cannot override the SDK page address`);if(!t||/[\s"'>/=\u0000-\u001f]/.test(t))throw new Error(`Invalid iframe attribute name: ${t}`)}return Object.fromEntries(e)}function C(n,e){if(!n||n==="."||n==="..")throw new Error(`${e} must be a non-empty path segment other than . or ..`);return encodeURIComponent(n)}function O(n,e){if(!e.token)throw new Error("Token is required");let t=String(e.apiName||e.pageCode||e.pageId||"").trim();if(!t)throw new Error("Either apiName, pageCode (deprecated), or pageId (deprecated) is required");let r=new URL(`/apps/${C(e.appId,"appId")}/user/${C(t,"apiName")}`,n);r.searchParams.set("token",e.token);let o=e.params;if(o!==void 0){if(!I(o))throw new Error("params must be a plain object");for(let[i,c]of Object.entries(o)){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)||$.has(i))throw new Error(`Invalid or reserved parameter name: ${i}`);if(c!=null){if(!["string","number","boolean"].includes(typeof c)||typeof c=="number"&&!Number.isFinite(c))throw new Error(`Parameter ${i} must be a string, finite number, or boolean`);r.searchParams.set(i,String(c))}}}return r.toString()}var w=class{constructor(e){f(this,"config");f(this,"instances",new Map);f(this,"cleanups",new Map);f(this,"instanceCounter",0);this.validateConfig(e),this.config={timeout:3e4,...e}}async embed(e){let{apiName:t,pageCode:r,pageId:o,token:i,containerId:c,onReady:S,onBridgeReady:A,onClose:R,onError:y,onEvent:U,iframeAttrs:j={},fullscreen:P=!1}=e;try{let d=O(this.config.baseUrl,e),l=x(j);r&&!t&&console.warn("[MediPaaS SDK] pageCode is deprecated. Please use apiName instead."),o&&!t&&!r&&console.warn("[MediPaaS SDK] pageId is deprecated. Please use apiName instead.");let p=this.createIframe(d,l),E=this.prepareContainer(c,P),m=`apass-${++this.instanceCounter}`,g=crypto.randomUUID(),h=new URL(d).origin,u=!1,v=a=>{if(a.source!==p.contentWindow||a.origin!==h)return;let s=a.data;if(!(!s||typeof s!="object"||Array.isArray(s))){if(s.type==="ready"){s.namespace==="medipaas.embed"&&s.version===1&&s.channelId===g&&!u&&(u=!0,A?.());return}(s.type===void 0||s.type==="event")&&typeof s.name=="string"&&s.name.trim()&&U?.({name:s.name,payload:s.payload})}};window.addEventListener("message",v);let b={iframe:p,token:i,expiresAt:0,close:()=>{this.closeInstance(m),R?.()},send:(a,s)=>{if(!u||!this.instances.has(m)||a!=="page.refresh"&&a!=="params.update")return!1;try{return p.contentWindow?.postMessage({namespace:"medipaas.embed",version:1,channelId:g,type:"event",name:a,payload:s},h),!0}catch{return!1}}};if(!c){let a=E.parentElement;a.dataset.apassOverlay=m,a.addEventListener("click",s=>{s.target===a&&b.close()})}return p.onload=()=>{u=!1,p.contentWindow?.postMessage({namespace:"medipaas.embed",version:1,channelId:g,type:"hello"},h),S?.()},p.onerror=()=>{let a=new Error("Failed to load A-pass iframe");y?.(a)},E.appendChild(p),this.instances.set(m,b),this.cleanups.set(m,()=>window.removeEventListener("message",v)),b}catch(d){let l=d instanceof Error?d:new Error(String(d));throw y?.(l),l}}closeInstance(e){let t=this.instances.get(e);if(t){this.cleanups.get(e)?.(),this.cleanups.delete(e);let r=t.iframe.parentElement?.parentElement;t.iframe.remove(),r?.dataset.apassOverlay===e&&r.remove(),this.instances.delete(e)}}prepareContainer(e,t){if(e){let i=document.getElementById(e);if(!i)throw new Error(`Container with id "${e}" not found`);return i}let r=document.createElement("div");r.style.cssText=`
|
|
2
2
|
position: fixed;
|
|
3
3
|
top: 0;
|
|
4
4
|
left: 0;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/embedUrl.ts","../src/core/sdk.ts"],"sourcesContent":["import type { EmbedOptions } from '../types';\r\n\r\nconst reservedNames = new Set(['token', '__proto__', 'constructor', 'prototype']);\r\n\r\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\r\n if (value === null || typeof value !== 'object') return false;\r\n const prototype = Object.getPrototypeOf(value);\r\n if (prototype === null) return true;\r\n // Object.prototype differs between same-origin windows. Compare its native\r\n // constructor instead of requiring this window's exact prototype identity.\r\n return Object.getPrototypeOf(prototype) === null &&\r\n Object.prototype.hasOwnProperty.call(prototype, 'constructor') &&\r\n typeof prototype.constructor === 'function' &&\r\n Function.prototype.toString.call(prototype.constructor) === Function.prototype.toString.call(Object);\r\n}\r\n\r\n/** Snapshot once so parameter/attribute getters cannot later replace the address. */\r\nexport function prepareIframeAttributes(attrs: Record<string, string> = {}): Record<string, string> {\r\n if (!isPlainObject(attrs)) throw new Error('iframeAttrs must be a plain object');\r\n const entries = Object.entries(attrs);\r\n for (const [name] of entries) {\r\n const normalized = name.trim().toLowerCase();\r\n if (normalized === 'src' || normalized === 'srcdoc') {\r\n throw new Error(`iframeAttrs.${name} cannot override the SDK page address`);\r\n }\r\n // eslint-disable-next-line no-control-regex -- Intentionally reject ASCII control characters in attribute names.\r\n if (!name || /[\\s\"'>/=\\u0000-\\u001f]/.test(name)) {\r\n throw new Error(`Invalid iframe attribute name: ${name}`);\r\n }\r\n }\r\n return Object.fromEntries(entries);\r\n}\r\n\r\nfunction encodeSegment(value: string, name: string): string {\r\n if (!value || value === '.' || value === '..') {\r\n throw new Error(`${name} must be a non-empty path segment other than . or ..`);\r\n }\r\n return encodeURIComponent(value);\r\n}\r\n\r\n/** Build and validate the complete iframe address before any DOM is created. */\r\nexport function buildEmbedUrl(baseUrl: string, options: EmbedOptions): string {\r\n if (!options.token) throw new Error('Token is required');\r\n const apiName = String(options.apiName || options.pageCode || options.pageId || '').trim();\r\n if (!apiName) {\r\n throw new Error('Either apiName, pageCode (deprecated), or pageId (deprecated) is required');\r\n }\r\n const url = new URL(`/apps/${encodeSegment(options.appId, 'appId')}/user/${encodeSegment(apiName, 'apiName')}`, baseUrl);\r\n url.searchParams.set('token', options.token);\r\n const params = options.params;\r\n if (params !== undefined) {\r\n if (!isPlainObject(params)) {\r\n throw new Error('params must be a plain object');\r\n }\r\n for (const [name, value] of Object.entries(params)) {\r\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || reservedNames.has(name)) {\r\n throw new Error(`Invalid or reserved parameter name: ${name}`);\r\n }\r\n if (value === null || value === undefined) continue;\r\n if (!['string', 'number', 'boolean'].includes(typeof value) ||\r\n (typeof value === 'number' && !Number.isFinite(value))) {\r\n throw new Error(`Parameter ${name} must be a string, finite number, or boolean`);\r\n }\r\n url.searchParams.set(name, String(value));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n","import type {\r\n SDKConfig,\r\n EmbedOptions,\r\n ApassInstanceRef,\r\n} from '../types';\r\nimport { buildEmbedUrl, prepareIframeAttributes } from './embedUrl';\r\n\r\n/**\r\n * Main SDK class for embedding A-pass applications\r\n *\r\n * @example\r\n * ```ts\r\n * const sdk = new ApassEmbedSDK({\r\n * baseUrl: 'https://apass.example.com',\r\n * });\r\n *\r\n * // CRM 项目先调自己的后端获取 token\r\n * const tokenResponse = await fetch('/api/embed-token', {\r\n * method: 'POST',\r\n * body: JSON.stringify({\r\n * recordId: 416,\r\n * enterpriseId: 456,\r\n * }),\r\n * });\r\n * const { token } = await tokenResponse.json();\r\n *\r\n * // 然后把 token 传给 SDK\r\n * await sdk.embed({\r\n * appId: '180',\r\n * apiName: 'patient_list',\r\n * token: token,\r\n * containerId: 'my-container',\r\n * });\r\n * ```\r\n */\r\nexport class ApassEmbedSDK {\r\n private config: SDKConfig;\r\n private instances: Map<string, ApassInstanceRef> = new Map();\n private cleanups: Map<string, () => void> = new Map();\n private instanceCounter = 0;\r\n\r\n constructor(config: SDKConfig) {\r\n this.validateConfig(config);\r\n this.config = {\r\n timeout: 30000,\r\n ...config,\r\n };\r\n }\r\n\r\n /**\r\n * Embed an A-pass application in an iframe\r\n */\r\n async embed(options: EmbedOptions): Promise<ApassInstanceRef> {\r\n const {\r\n apiName,\r\n pageCode,\r\n pageId,\r\n token,\r\n containerId,\r\n onReady,\n onBridgeReady,\n onClose,\r\n onError,\n onEvent,\n iframeAttrs = {},\r\n fullscreen = false,\r\n } = options;\r\n\r\n try {\r\n const url = buildEmbedUrl(this.config.baseUrl, options);\r\n const attrs = prepareIframeAttributes(iframeAttrs);\r\n // Warn if using deprecated parameters\r\n if (pageCode && !apiName) {\r\n console.warn('[MediPaaS SDK] pageCode is deprecated. Please use apiName instead.');\r\n }\r\n if (pageId && !apiName && !pageCode) {\r\n console.warn('[MediPaaS SDK] pageId is deprecated. Please use apiName instead.');\r\n }\r\n\r\n // Prepare the detached iframe first: DOM attribute validation must finish\r\n // before a default overlay is attached to the host page.\r\n const iframe = this.createIframe(url, attrs);\r\n const container = this.prepareContainer(containerId, fullscreen);\r\n\r\n // Step 5: Setup lifecycle handlers\r\n const instanceId = `apass-${++this.instanceCounter}`;\n const channelId = crypto.randomUUID();\n const targetOrigin = new URL(url).origin;\n let ready = false;\n const receiveMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow || event.origin !== targetOrigin) return;\n const message = event.data;\n if (!message || typeof message !== 'object' || message.namespace !== 'medipaas.embed'\n || message.version !== 1 || message.channelId !== channelId) return;\n if (message.type === 'ready' && !ready) {\n ready = true;\n onBridgeReady?.();\n }\n if (message.type === 'event' && ready && typeof message.name === 'string' && message.name) {\n onEvent?.({ name: message.name, payload: message.payload });\n }\n };\n window.addEventListener('message', receiveMessage);\n\n const instance: ApassInstanceRef = {\n iframe,\r\n token,\r\n expiresAt: 0, // Token expiration not tracked in this mode\r\n close: () => {\n this.closeInstance(instanceId);\n onClose?.();\n },\n send: (name, payload) => {\n if (!ready || !this.instances.has(instanceId)) return false;\n if (name !== 'page.refresh' && name !== 'params.update') return false;\n try {\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'event', name, payload }, targetOrigin);\n return true;\n } catch {\n return false;\n }\n },\n };\n\n if (!containerId) {\n const overlay = container.parentElement!;\n overlay.dataset.apassOverlay = instanceId;\n overlay.addEventListener('click', (event) => {\n if (event.target === overlay) instance.close();\n });\n }\n\r\n // Step 6: Setup ready handler before attaching the iframe\n iframe.onload = () => {\n ready = false;\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'hello' }, targetOrigin);\n onReady?.();\n };\r\n\r\n // Step 8: Setup error handler\r\n iframe.onerror = () => {\n const error = new Error('Failed to load A-pass iframe');\r\n onError?.(error);\r\n };\n\n container.appendChild(iframe);\n\r\n // Store instance reference\r\n this.instances.set(instanceId, instance);\n this.cleanups.set(instanceId, () => window.removeEventListener('message', receiveMessage));\n\r\n return instance;\r\n } catch (error) {\r\n const err =\r\n error instanceof Error ? error : new Error(String(error));\r\n onError?.(err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Close and cleanup an instance\r\n */\r\n private closeInstance(instanceId: string): void {\n const instance = this.instances.get(instanceId);\r\n if (instance) {\n this.cleanups.get(instanceId)?.();\n this.cleanups.delete(instanceId);\n // Remove iframe from DOM\n const overlay = instance.iframe.parentElement?.parentElement;\n instance.iframe.remove();\n if (overlay?.dataset.apassOverlay === instanceId) overlay.remove();\n // Remove from tracking\r\n this.instances.delete(instanceId);\r\n }\r\n }\n\r\n\r\n /**\r\n * Prepare container for iframe\r\n */\r\n private prepareContainer(\r\n containerId: string | undefined,\r\n fullscreen: boolean,\r\n ): HTMLElement {\r\n if (containerId) {\r\n const container = document.getElementById(containerId);\r\n if (!container) {\r\n throw new Error(`Container with id \"${containerId}\" not found`);\r\n }\r\n return container;\r\n }\r\n\r\n // Create default container\r\n const container = document.createElement('div');\r\n container.style.cssText = `\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 10000;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n `;\r\n\r\n const wrapper = document.createElement('div');\r\n if (fullscreen) {\r\n wrapper.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n `;\r\n } else {\r\n wrapper.style.cssText = `\r\n width: 90%;\r\n height: 90%;\r\n max-width: 1200px;\r\n max-height: 800px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n `;\r\n }\r\n\r\n container.appendChild(wrapper);\r\n document.body.appendChild(container);\r\n\r\n return wrapper;\n }\r\n\r\n /**\r\n * Create iframe element\r\n */\r\n private createIframe(\r\n url: string,\r\n attrs: Record<string, string>,\r\n ): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n\r\n // Build A-pass URL with token parameter\r\n iframe.src = url;\r\n iframe.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n `;\r\n\r\n // Apply custom attributes\r\n Object.entries(attrs).forEach(([key, value]) => {\r\n iframe.setAttribute(key, value);\r\n });\r\n\r\n return iframe;\r\n }\r\n\r\n /**\r\n * Validate SDK configuration\r\n */\r\n private validateConfig(config: SDKConfig): void {\r\n if (!config.baseUrl) {\r\n throw new Error('SDKConfig.baseUrl is required');\r\n }\r\n\r\n try {\r\n new URL(config.baseUrl);\r\n } catch {\r\n throw new Error(\r\n `SDKConfig.baseUrl must be a valid URL: ${config.baseUrl}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Close all instances\r\n */\r\n closeAll(): void {\r\n this.instances.forEach((_, instanceId) => {\r\n this.closeInstance(instanceId);\r\n });\r\n }\r\n\r\n /**\r\n * Get active instances count\r\n */\r\n getInstanceCount(): number {\r\n return this.instances.size;\r\n }\r\n}\r\n"],"mappings":"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","names":["reservedNames","isPlainObject","value","prototype","prepareIframeAttributes","attrs","entries","name","normalized","encodeSegment","buildEmbedUrl","baseUrl","options","apiName","url","params","ApassEmbedSDK","config","__publicField","options","apiName","pageCode","pageId","token","containerId","onReady","onBridgeReady","onClose","onError","onEvent","iframeAttrs","fullscreen","url","buildEmbedUrl","attrs","prepareIframeAttributes","iframe","container","instanceId","channelId","targetOrigin","ready","receiveMessage","event","message","instance","name","payload","overlay","error","err","wrapper","key","value","_"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/embedUrl.ts","../src/core/sdk.ts"],"sourcesContent":["import type { EmbedOptions } from '../types';\r\n\r\nconst reservedNames = new Set(['token', '__proto__', 'constructor', 'prototype']);\r\n\r\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\r\n if (value === null || typeof value !== 'object') return false;\r\n const prototype = Object.getPrototypeOf(value);\r\n if (prototype === null) return true;\r\n // Object.prototype differs between same-origin windows. Compare its native\r\n // constructor instead of requiring this window's exact prototype identity.\r\n return Object.getPrototypeOf(prototype) === null &&\r\n Object.prototype.hasOwnProperty.call(prototype, 'constructor') &&\r\n typeof prototype.constructor === 'function' &&\r\n Function.prototype.toString.call(prototype.constructor) === Function.prototype.toString.call(Object);\r\n}\r\n\r\n/** Snapshot once so parameter/attribute getters cannot later replace the address. */\r\nexport function prepareIframeAttributes(attrs: Record<string, string> = {}): Record<string, string> {\r\n if (!isPlainObject(attrs)) throw new Error('iframeAttrs must be a plain object');\r\n const entries = Object.entries(attrs);\r\n for (const [name] of entries) {\r\n const normalized = name.trim().toLowerCase();\r\n if (normalized === 'src' || normalized === 'srcdoc') {\r\n throw new Error(`iframeAttrs.${name} cannot override the SDK page address`);\r\n }\r\n // eslint-disable-next-line no-control-regex -- Intentionally reject ASCII control characters in attribute names.\r\n if (!name || /[\\s\"'>/=\\u0000-\\u001f]/.test(name)) {\r\n throw new Error(`Invalid iframe attribute name: ${name}`);\r\n }\r\n }\r\n return Object.fromEntries(entries);\r\n}\r\n\r\nfunction encodeSegment(value: string, name: string): string {\r\n if (!value || value === '.' || value === '..') {\r\n throw new Error(`${name} must be a non-empty path segment other than . or ..`);\r\n }\r\n return encodeURIComponent(value);\r\n}\r\n\r\n/** Build and validate the complete iframe address before any DOM is created. */\r\nexport function buildEmbedUrl(baseUrl: string, options: EmbedOptions): string {\r\n if (!options.token) throw new Error('Token is required');\r\n const apiName = String(options.apiName || options.pageCode || options.pageId || '').trim();\r\n if (!apiName) {\r\n throw new Error('Either apiName, pageCode (deprecated), or pageId (deprecated) is required');\r\n }\r\n const url = new URL(`/apps/${encodeSegment(options.appId, 'appId')}/user/${encodeSegment(apiName, 'apiName')}`, baseUrl);\r\n url.searchParams.set('token', options.token);\r\n const params = options.params;\r\n if (params !== undefined) {\r\n if (!isPlainObject(params)) {\r\n throw new Error('params must be a plain object');\r\n }\r\n for (const [name, value] of Object.entries(params)) {\r\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || reservedNames.has(name)) {\r\n throw new Error(`Invalid or reserved parameter name: ${name}`);\r\n }\r\n if (value === null || value === undefined) continue;\r\n if (!['string', 'number', 'boolean'].includes(typeof value) ||\r\n (typeof value === 'number' && !Number.isFinite(value))) {\r\n throw new Error(`Parameter ${name} must be a string, finite number, or boolean`);\r\n }\r\n url.searchParams.set(name, String(value));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n","import type {\r\n SDKConfig,\r\n EmbedOptions,\r\n ApassInstanceRef,\r\n} from '../types';\r\nimport { buildEmbedUrl, prepareIframeAttributes } from './embedUrl';\r\n\r\n/**\r\n * Main SDK class for embedding A-pass applications\r\n *\r\n * @example\r\n * ```ts\r\n * const sdk = new ApassEmbedSDK({\r\n * baseUrl: 'https://apass.example.com',\r\n * });\r\n *\r\n * // CRM 项目先调自己的后端获取 token\r\n * const tokenResponse = await fetch('/api/embed-token', {\r\n * method: 'POST',\r\n * body: JSON.stringify({\r\n * recordId: 416,\r\n * enterpriseId: 456,\r\n * }),\r\n * });\r\n * const { token } = await tokenResponse.json();\r\n *\r\n * // 然后把 token 传给 SDK\r\n * await sdk.embed({\r\n * appId: '180',\r\n * apiName: 'patient_list',\r\n * token: token,\r\n * containerId: 'my-container',\r\n * });\r\n * ```\r\n */\r\nexport class ApassEmbedSDK {\r\n private config: SDKConfig;\r\n private instances: Map<string, ApassInstanceRef> = new Map();\n private cleanups: Map<string, () => void> = new Map();\n private instanceCounter = 0;\r\n\r\n constructor(config: SDKConfig) {\r\n this.validateConfig(config);\r\n this.config = {\r\n timeout: 30000,\r\n ...config,\r\n };\r\n }\r\n\r\n /**\r\n * Embed an A-pass application in an iframe\r\n */\r\n async embed(options: EmbedOptions): Promise<ApassInstanceRef> {\r\n const {\r\n apiName,\r\n pageCode,\r\n pageId,\r\n token,\r\n containerId,\r\n onReady,\n onBridgeReady,\n onClose,\r\n onError,\n onEvent,\n iframeAttrs = {},\r\n fullscreen = false,\r\n } = options;\r\n\r\n try {\r\n const url = buildEmbedUrl(this.config.baseUrl, options);\r\n const attrs = prepareIframeAttributes(iframeAttrs);\r\n // Warn if using deprecated parameters\r\n if (pageCode && !apiName) {\r\n console.warn('[MediPaaS SDK] pageCode is deprecated. Please use apiName instead.');\r\n }\r\n if (pageId && !apiName && !pageCode) {\r\n console.warn('[MediPaaS SDK] pageId is deprecated. Please use apiName instead.');\r\n }\r\n\r\n // Prepare the detached iframe first: DOM attribute validation must finish\r\n // before a default overlay is attached to the host page.\r\n const iframe = this.createIframe(url, attrs);\r\n const container = this.prepareContainer(containerId, fullscreen);\r\n\r\n // Step 5: Setup lifecycle handlers\r\n const instanceId = `apass-${++this.instanceCounter}`;\n const channelId = crypto.randomUUID();\n const targetOrigin = new URL(url).origin;\n let ready = false;\n const receiveMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow || event.origin !== targetOrigin) return;\n const message = event.data;\n if (!message || typeof message !== 'object' || Array.isArray(message)) return;\n if (message.type === 'ready') {\n if (message.namespace === 'medipaas.embed' && message.version === 1\n && message.channelId === channelId && !ready) {\n ready = true;\n onBridgeReady?.();\n }\n return;\n }\n // Business scripts can post { name, payload } directly, without a handshake.\n if ((message.type === undefined || message.type === 'event')\n && typeof message.name === 'string' && message.name.trim()) {\n onEvent?.({ name: message.name, payload: message.payload });\n }\n };\n window.addEventListener('message', receiveMessage);\n\n const instance: ApassInstanceRef = {\n iframe,\r\n token,\r\n expiresAt: 0, // Token expiration not tracked in this mode\r\n close: () => {\n this.closeInstance(instanceId);\n onClose?.();\n },\n send: (name, payload) => {\n if (!ready || !this.instances.has(instanceId)) return false;\n if (name !== 'page.refresh' && name !== 'params.update') return false;\n try {\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'event', name, payload }, targetOrigin);\n return true;\n } catch {\n return false;\n }\n },\n };\n\n if (!containerId) {\n const overlay = container.parentElement!;\n overlay.dataset.apassOverlay = instanceId;\n overlay.addEventListener('click', (event) => {\n if (event.target === overlay) instance.close();\n });\n }\n\r\n // Step 6: Setup ready handler before attaching the iframe\n iframe.onload = () => {\n ready = false;\n iframe.contentWindow?.postMessage({ namespace: 'medipaas.embed', version: 1,\n channelId, type: 'hello' }, targetOrigin);\n onReady?.();\n };\r\n\r\n // Step 8: Setup error handler\r\n iframe.onerror = () => {\n const error = new Error('Failed to load A-pass iframe');\r\n onError?.(error);\r\n };\n\n container.appendChild(iframe);\n\r\n // Store instance reference\r\n this.instances.set(instanceId, instance);\n this.cleanups.set(instanceId, () => window.removeEventListener('message', receiveMessage));\n\r\n return instance;\r\n } catch (error) {\r\n const err =\r\n error instanceof Error ? error : new Error(String(error));\r\n onError?.(err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Close and cleanup an instance\r\n */\r\n private closeInstance(instanceId: string): void {\n const instance = this.instances.get(instanceId);\r\n if (instance) {\n this.cleanups.get(instanceId)?.();\n this.cleanups.delete(instanceId);\n // Remove iframe from DOM\n const overlay = instance.iframe.parentElement?.parentElement;\n instance.iframe.remove();\n if (overlay?.dataset.apassOverlay === instanceId) overlay.remove();\n // Remove from tracking\r\n this.instances.delete(instanceId);\r\n }\r\n }\n\r\n\r\n /**\r\n * Prepare container for iframe\r\n */\r\n private prepareContainer(\r\n containerId: string | undefined,\r\n fullscreen: boolean,\r\n ): HTMLElement {\r\n if (containerId) {\r\n const container = document.getElementById(containerId);\r\n if (!container) {\r\n throw new Error(`Container with id \"${containerId}\" not found`);\r\n }\r\n return container;\r\n }\r\n\r\n // Create default container\r\n const container = document.createElement('div');\r\n container.style.cssText = `\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 10000;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n `;\r\n\r\n const wrapper = document.createElement('div');\r\n if (fullscreen) {\r\n wrapper.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n `;\r\n } else {\r\n wrapper.style.cssText = `\r\n width: 90%;\r\n height: 90%;\r\n max-width: 1200px;\r\n max-height: 800px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n `;\r\n }\r\n\r\n container.appendChild(wrapper);\r\n document.body.appendChild(container);\r\n\r\n return wrapper;\n }\r\n\r\n /**\r\n * Create iframe element\r\n */\r\n private createIframe(\r\n url: string,\r\n attrs: Record<string, string>,\r\n ): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n\r\n // Build A-pass URL with token parameter\r\n iframe.src = url;\r\n iframe.style.cssText = `\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n `;\r\n\r\n // Apply custom attributes\r\n Object.entries(attrs).forEach(([key, value]) => {\r\n iframe.setAttribute(key, value);\r\n });\r\n\r\n return iframe;\r\n }\r\n\r\n /**\r\n * Validate SDK configuration\r\n */\r\n private validateConfig(config: SDKConfig): void {\r\n if (!config.baseUrl) {\r\n throw new Error('SDKConfig.baseUrl is required');\r\n }\r\n\r\n try {\r\n new URL(config.baseUrl);\r\n } catch {\r\n throw new Error(\r\n `SDKConfig.baseUrl must be a valid URL: ${config.baseUrl}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Close all instances\r\n */\r\n closeAll(): void {\r\n this.instances.forEach((_, instanceId) => {\r\n this.closeInstance(instanceId);\r\n });\r\n }\r\n\r\n /**\r\n * Get active instances count\r\n */\r\n getInstanceCount(): number {\r\n return this.instances.size;\r\n }\r\n}\r\n"],"mappings":"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","names":["reservedNames","isPlainObject","value","prototype","prepareIframeAttributes","attrs","entries","name","normalized","encodeSegment","buildEmbedUrl","baseUrl","options","apiName","url","params","ApassEmbedSDK","config","__publicField","options","apiName","pageCode","pageId","token","containerId","onReady","onBridgeReady","onClose","onError","onEvent","iframeAttrs","fullscreen","url","buildEmbedUrl","attrs","prepareIframeAttributes","iframe","container","instanceId","channelId","targetOrigin","ready","receiveMessage","event","message","instance","name","payload","overlay","error","err","wrapper","key","value","_"]}
|