diff --git a/.agents/skills/shenzhen-natural-museum-dev/SKILL.md b/.agents/skills/shenzhen-natural-museum-dev/SKILL.md index 649b32b..376ff36 100644 --- a/.agents/skills/shenzhen-natural-museum-dev/SKILL.md +++ b/.agents/skills/shenzhen-natural-museum-dev/SKILL.md @@ -452,7 +452,7 @@ pnpm build:h5 - Audio fallback URLs such as `example.com` are not valid explain capability. - `src/pages/route/detail.vue` may still contain historical navigation states such as planning/navigating/arrived/location-error. Keep them blocked unless real graph data exists. - Runtime static JSON loading through `uni.request` must be verified on H5 for current work; verify mp-weixin only when explicitly requested. -- SGS Map SDK H5 integration depends on the sibling project `E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system`, especially `sgs-frontend-map/sdk-src`, `public/sdk/sgs-map-sdk.min.js`, and `/h5-sdk`. +- SGS Map SDK H5 integration depends on the sibling project `E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system`, especially `sgs-frontend-map/sdk-src`, `public/sdk/sgs-map-sdk.min.js`, and the independently deployed `/engine/index.html`. Treat an SPA fallback HTTP 200 as unhealthy; validate Engine title/assets and `HELLO` -> `ENGINE_READY`. - SGS Map SDK uses iframe + `postMessage`; target origin, iframe lifecycle, and event bridge errors are separate operational risks from local Three.js rendering. - SDK mode should not be treated as proof of certified indoor navigation unless route graph/nav data and runtime route behavior are verified. - Mixing static nav-assets POI IDs with backend SGS Map POI IDs can break search, focus, explain location previews, and route actions unless adapters normalize stable IDs deliberately. diff --git a/.claude/skills/shenzhen-natural-museum-dev/SKILL.md b/.claude/skills/shenzhen-natural-museum-dev/SKILL.md index 649b32b..376ff36 100644 --- a/.claude/skills/shenzhen-natural-museum-dev/SKILL.md +++ b/.claude/skills/shenzhen-natural-museum-dev/SKILL.md @@ -452,7 +452,7 @@ pnpm build:h5 - Audio fallback URLs such as `example.com` are not valid explain capability. - `src/pages/route/detail.vue` may still contain historical navigation states such as planning/navigating/arrived/location-error. Keep them blocked unless real graph data exists. - Runtime static JSON loading through `uni.request` must be verified on H5 for current work; verify mp-weixin only when explicitly requested. -- SGS Map SDK H5 integration depends on the sibling project `E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system`, especially `sgs-frontend-map/sdk-src`, `public/sdk/sgs-map-sdk.min.js`, and `/h5-sdk`. +- SGS Map SDK H5 integration depends on the sibling project `E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system`, especially `sgs-frontend-map/sdk-src`, `public/sdk/sgs-map-sdk.min.js`, and the independently deployed `/engine/index.html`. Treat an SPA fallback HTTP 200 as unhealthy; validate Engine title/assets and `HELLO` -> `ENGINE_READY`. - SGS Map SDK uses iframe + `postMessage`; target origin, iframe lifecycle, and event bridge errors are separate operational risks from local Three.js rendering. - SDK mode should not be treated as proof of certified indoor navigation unless route graph/nav data and runtime route behavior are verified. - Mixing static nav-assets POI IDs with backend SGS Map POI IDs can break search, focus, explain location previews, and route actions unless adapters normalize stable IDs deliberately. diff --git a/.env.development b/.env.development index 93d0f37..9190f68 100644 --- a/.env.development +++ b/.env.development @@ -15,8 +15,8 @@ VITE_AUDIO_API_BASE_URL=/app-api VITE_AUDIO_LANGUAGE=zh-CN VITE_SGS_API_BASE_URL=/app-api VITE_SGS_MAP_ID=1 -VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.4.1 -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.5.0 +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN= VITE_SGS_SDK_TIMEOUT_MS=15000 VITE_TENCENT_MAP_KEY=__REPLACE_WITH_TENCENT_MAP_WEB_KEY__ @@ -25,7 +25,7 @@ VITE_PUBLIC_LEGACY_AUDIO_HOST= # Vite 开发服务器代理目标,只由 vite.config.ts 读取。 DEV_PROXY_APP_API_TARGET=http://localhost:3001 -DEV_PROXY_H5_SDK_TARGET=http://localhost:3001 +DEV_PROXY_ENGINE_TARGET=http://localhost:3001 DEV_PROXY_SDK_TARGET=http://localhost:3001 DEV_PROXY_MUSEUM_ASSETS_TARGET=http://1.92.206.90:9000 DEV_PROXY_MINIO_TARGET=http://localhost:3001 diff --git a/.env.example b/.env.example index 9313fae..4656d72 100644 --- a/.env.example +++ b/.env.example @@ -14,8 +14,8 @@ VITE_AUDIO_API_BASE_URL=/app-api VITE_AUDIO_LANGUAGE=zh-CN VITE_SGS_API_BASE_URL=/app-api VITE_SGS_MAP_ID=1 -VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.4.1 -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.5.0 +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN= VITE_SGS_SDK_TIMEOUT_MS=15000 VITE_TENCENT_MAP_KEY=__REPLACE_WITH_TENCENT_MAP_WEB_KEY__ @@ -24,7 +24,7 @@ VITE_PUBLIC_LEGACY_AUDIO_HOST= # 仅开发环境使用的 Vite 代理目标,只由 vite.config.ts 读取。 DEV_PROXY_APP_API_TARGET=http://localhost:3001 -DEV_PROXY_H5_SDK_TARGET=http://localhost:3001 +DEV_PROXY_ENGINE_TARGET=http://localhost:3001 DEV_PROXY_SDK_TARGET=http://localhost:3001 DEV_PROXY_MUSEUM_ASSETS_TARGET=http://1.92.206.90:9000 DEV_PROXY_MINIO_TARGET=http://localhost:3001 diff --git a/.env.production b/.env.production index bba8945..e0f1e17 100644 --- a/.env.production +++ b/.env.production @@ -14,8 +14,8 @@ VITE_AUDIO_API_BASE_URL=/app-api VITE_AUDIO_LANGUAGE=zh-CN VITE_SGS_API_BASE_URL=/app-api VITE_SGS_MAP_ID=1 -VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.4.1 -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.5.0 +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN=https://guide.sznhmuseum.org.cn VITE_SGS_SDK_TIMEOUT_MS=15000 VITE_TENCENT_MAP_KEY=__REPLACE_WITH_TENCENT_MAP_WEB_KEY__ diff --git a/.env.test b/.env.test index 0535cb0..6629b93 100644 --- a/.env.test +++ b/.env.test @@ -14,8 +14,8 @@ VITE_AUDIO_API_BASE_URL=/app-api VITE_AUDIO_LANGUAGE=zh-CN VITE_SGS_API_BASE_URL=/app-api VITE_SGS_MAP_ID=1 -VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.4.1 -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.5.0 +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN=https://guide.whaoyue.com VITE_SGS_SDK_TIMEOUT_MS=30000 VITE_TENCENT_MAP_KEY=__REPLACE_WITH_TENCENT_MAP_WEB_KEY__ diff --git a/README.md b/README.md index be5f10b..ce845e8 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,8 @@ VITE_AUDIO_LANGUAGE=zh-CN # SGS SDK/H5 地图基座配置;当前代码尚未把 SDK renderer 接入页面渲染 VITE_SGS_MAP_ID=1 -VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.4.1 -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_SDK_SCRIPT_URL=/static/sgs-map-sdk/index.global.js?v=2.5.0 +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN= VITE_SGS_SDK_TIMEOUT_MS=5000 ``` @@ -177,7 +177,7 @@ uni build -p h5 && node scripts/copy-h5-nav-assets.cjs - `dist/build/h5` 存在应用产物。 - H5 可访问 `static/nav-assets/...` 下的 GLB/GLTF/bin/texture/manifest 文件。 - H5 可访问 `static/guide-data` 下的讲解和内容数据。 -- 如启用 `api` 或 `sdk` 模式,Nginx/网关需代理 `/app-api`、`/yudao-server`、`/h5-sdk` 等路径。 +- 如启用 `api` 或 `sdk` 模式,Nginx/网关需代理 `/app-api`、`/yudao-server`、`/engine` 等路径。`/engine/index.html` 必须是 SGS Map SDK Engine 2.5.x 的独立发布页面,且其资源路径可加载并能完成 `HELLO` -> `ENGINE_READY`;业务 SPA fallback 返回的 200 不是 Engine 健康。 更完整的部署说明见 `docs/H5_DEPLOYMENT_GUIDE.md`。 diff --git a/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md b/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md index f5b4b96..7ffb631 100644 --- a/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md +++ b/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md @@ -11,7 +11,7 @@ SGS Map SDK 接入分为两条边界: | 边界 | 职责 | 允许接触 SDK/后端字段的位置 | 禁止事项 | | --- | --- | --- | --- | | 数据层 | 拉取楼层、POI、空间面、导航目的地、诊断信息,并转换为 `MuseumFloor`、`MuseumPoi`、`GuideLocationPreview` 等领域模型 | `src/data/providers`、`src/data/adapters`、`src/repositories` | 页面和组件直接请求 `/app-api/gis/sdk/*` | -| 渲染层 | 加载 SGS H5 地图基座、管理 iframe/SDK 生命周期、执行切楼层和聚焦命令 | `src/services/sgs`、`src/components/map/SgsMapRenderer.vue` | 把后端数据解析逻辑写进 `SgsMapRenderer.vue` | +| 渲染层 | 加载 SGS H5 Engine、管理 iframe/SDK 生命周期、执行切楼层和聚焦命令 | `src/services/sgs`、未来独立 renderer | 把后端数据解析逻辑写进 renderer | 展示层必须只通过以下入口拿数据: @@ -34,7 +34,7 @@ SGS Map SDK 接入分为两条边界: | --- | --- | --- | | 后端直连 | `http://1.92.206.90:48080/yudao-server/app-api` | 当前开发机访问超时,需服务端排查防火墙/安全组/监听地址 | | 前端代理 | `http://1.92.206.90:3001/app-api` | 可访问,返回后端 `CommonResult code=0` | -| H5 SDK 基座 | `http://1.92.206.90:3001/h5-sdk` | 可访问 | +| H5 SDK Engine | `http://1.92.206.90:3001/engine/index.html` | 需部署并按 `HELLO` -> `ENGINE_READY` 验证,HTTP 200 不足以证明健康 | | SDK 脚本 | `http://1.92.206.90:3001/sdk/sgs-map-sdk.min.js` | 可访问 | 本项目推荐环境变量: @@ -43,7 +43,7 @@ SGS Map SDK 接入分为两条边界: VITE_DATA_SOURCE_MODE=sdk VITE_API_BASE_URL=/app-api VITE_SGS_SDK_SCRIPT_URL=/sdk/sgs-map-sdk.min.js -VITE_SGS_H5_ENGINE_URL=/h5-sdk +VITE_SGS_H5_ENGINE_URL=/engine/index.html VITE_SGS_SDK_ORIGIN=http://1.92.206.90:3001 VITE_SGS_SDK_TIMEOUT_MS=10000 ``` @@ -52,7 +52,7 @@ VITE_SGS_SDK_TIMEOUT_MS=10000 ```env VITE_API_BASE_URL=http://1.92.206.90:3001/app-api -VITE_SGS_H5_ENGINE_URL=http://1.92.206.90:3001/h5-sdk +VITE_SGS_H5_ENGINE_URL=http://1.92.206.90:3001/engine/index.html VITE_SGS_SDK_SCRIPT_URL=http://1.92.206.90:3001/sdk/sgs-map-sdk.min.js VITE_SGS_SDK_ORIGIN=http://1.92.206.90:3001 ``` @@ -435,7 +435,7 @@ export const createGuideRepository = () => { | --- | --- | --- | | `static` | 本地 clean nav-assets | `ThreeMap` | | `api` | 后端 SGS App API | 仍可用 `ThreeMap` 或本地渲染 | -| `sdk` | 后端 SGS App API | `SgsMapRenderer` | +| `sdk` | 后端 SGS App API | 当前仍为 `ThreeMap`;SDK iframe renderer 尚未接入 | 不要把 `sdk` 理解成“页面直接调用 `SGSMapSDK.getFloorPois()`”。数据仍从 Repository 进来,SDK 只做地图渲染和交互命令。 @@ -447,7 +447,7 @@ export const createGuideRepository = () => { - 调用 `guideUseCase.searchPois(keyword)` - 调用 `guideUseCase.getPoiById(id)` - 传 `GuideLocationPreview.positionGltf` 给地图聚焦 -- 通过 `GuideMapShell` 选择 `ThreeMap` 或 `SgsMapRenderer` +- 当前 `GuideMapShell` 固定使用 `ThreeMap`;启用 SDK iframe renderer 需要单独功能任务 禁止展示层做的事: @@ -478,7 +478,7 @@ await service.getFloorPois(floorId) 3. 新增 `SgsSdkGuideRepository`,实现现有 `GuideRepository` interface。 4. 新增仓库工厂,根据 `dataSourceConfig.mode` 选择 static 或 SGS 后端数据。 5. 让 `guideUseCase` 使用仓库工厂,不改页面调用方式。 -6. 在 `sdk` 模式下让 `GuideMapShell` 继续选择 `SgsMapRenderer`,但 POI/楼层数据仍来自 `GuideUseCase`。 +6. 保持当前 `GuideMapShell` 使用 `ThreeMap`;若产品启用 iframe renderer,单独实施并保持 POI/楼层数据来自 `GuideUseCase`。 7. 增加数据健康检查脚本或开发命令,校验楼层、POI、空间面、导航目的地计数。 8. 通过 H5 浏览器检查搜索、楼层切换、POI 聚焦、位置预览。 @@ -530,9 +530,9 @@ pnpm build:h5 - `static` 模式仍可加载本地 3D/POI。 - `api` 模式能展示后端楼层和 POI,渲染器不变。 -- `sdk` 模式能加载 SGS 地图基座,楼层切换与 POI 聚焦可用。 -- SDK 失败时有错误态,不出现空白地图。 -- 顶部 tabs、搜索、楼层控件、详情卡片不被 iframe/canvas 遮挡。 +- 当前 `sdk` 模式仍以 `ThreeMap` 渲染,验证后端数据与本地三维模型正常显示。 +- SDK Engine 仅在未来 iframe renderer 接入时验证:`/engine/index.html` 不是 SPA fallback、资源可加载、并完成 `HELLO` -> `ENGINE_READY`。 +- 顶部 tabs、搜索、楼层控件、详情卡片不被 canvas 遮挡;未来 iframe renderer 也必须满足该约束。 ## 12. 上线前阻断项 diff --git a/src/config/dataSource.ts b/src/config/dataSource.ts index e91748e..37040c3 100644 --- a/src/config/dataSource.ts +++ b/src/config/dataSource.ts @@ -4,8 +4,9 @@ export type ConfiguredAudioLanguage = 'zh-CN' | 'yue-HK' | 'en-US' const allowedModes = new Set(['static', 'api', 'sdk']) const allowedGuideContentModes = new Set(['static', 'remote', 'mock']) -const defaultSdkScriptUrl = '/static/sgs-map-sdk/index.global.js?v=2.4.1' -const defaultSgsEngineUrl = '/h5-sdk' +export const SGS_MAP_SDK_VERSION = '2.5.0' +const defaultSdkScriptUrl = `/static/sgs-map-sdk/index.global.js?v=${SGS_MAP_SDK_VERSION}` +const defaultSgsEngineUrl = '/engine/index.html' const defaultSdkTimeoutMs = 5000 const defaultApiBaseUrl = '/app-api' const defaultSgsMapId = '1' diff --git a/static/sgs-map-sdk/README.md b/static/sgs-map-sdk/README.md index e9eb817..08af1b0 100644 --- a/static/sgs-map-sdk/README.md +++ b/static/sgs-map-sdk/README.md @@ -1,8 +1,10 @@ # SGS Map SDK 交付包 -> **版本**:V2.4.0 +> **本项目同步版本**:V2.5.0 > **定位**:深圳自然博物馆统一三维高精地图导航服务平台 H5 SDK +> 发布源的 README 标题仍保留 V2.4.0,CHANGELOG 最高仍为 2.4.1;本项目以 `package.json`、`dist` 产物自报版本、SHA-256 和 2.5 Engine 迁移报告为发布依据,不把这份旧标题视为 2.5.0 验证记录。 + 欢迎接入 SGS Map SDK!本 SDK 将复杂的 WebGL 三维渲染、GLB 模型管线与 NavMesh 物理寻路引擎封装在服务端基座中,业务端(H5 / 大屏 / 小程序)只需通过几行代码即可极速唤起 3D 地图。 ## 📁 目录结构 @@ -35,11 +37,11 @@ sgs-map-sdk-release/ 1. **`sdkUrl`(渲染基座地址)**: - 释义:独立渲染基座的 URL 路径,SDK 会自动在您的页面中创建一个不可见的 iframe 或 Web-View 连接到这里。 - - **请联系地图管理平台管理员获取最新的正式/测试域名**,例如:`https://map.museum.com/h5-sdk`。 + - **请联系地图管理平台管理员获取最新的正式/测试域名**,例如:`https://map.museum.com/engine/index.html`。 2. **`targetOrigin`(安全通信域)**: - 释义:这是指**地图渲染基座的来源域名**(即 `sdkUrl` 的 Origin)。为了安全,SDK 只接收来自该域名 iframe 的消息。 - - 配置要求:请传入您的地图基座部署域名,例如:`https://map.museum.com`。如果不传,SDK 会自动从 `sdkUrl` 参数推导。注意:**不要**填成您业务页面的域名(业务页面的域名是交给 h5-sdk 做白名单校验的)。 + - 配置要求:请传入独立 SDK Engine 的部署域名,例如:`https://map.museum.com`。如果不传,SDK 会自动从 `sdkUrl` 参数推导。注意:**不要**填成业务页面的域名。 ## 📦 独立渲染基座部署与版本关系 diff --git a/static/sgs-map-sdk/index.d.mts b/static/sgs-map-sdk/index.d.mts index a4e748f..eca39d8 100644 --- a/static/sgs-map-sdk/index.d.mts +++ b/static/sgs-map-sdk/index.d.mts @@ -932,11 +932,11 @@ declare namespace utils { /** * SGS 3D 地图 H5 SDK 入口类 * - *

通过 iframe 嵌入 /h5-sdk 底座页面,以 postMessage 进行双向通信。 + *

通过 iframe 嵌入独立发布的 /engine/index.html 渲染引擎,以 postMessage 进行双向通信。 *

所有需要后端数据的操作均通过 postMessage 指令由底座代理, * Demo / 宿主不允许直连后端 API。 * - * @version 2.4.1 + * @version 2.5.0 */ declare class SGSMapSDK extends EventEmitter { static utils: typeof utils; diff --git a/static/sgs-map-sdk/index.d.ts b/static/sgs-map-sdk/index.d.ts index a4e748f..eca39d8 100644 --- a/static/sgs-map-sdk/index.d.ts +++ b/static/sgs-map-sdk/index.d.ts @@ -932,11 +932,11 @@ declare namespace utils { /** * SGS 3D 地图 H5 SDK 入口类 * - *

通过 iframe 嵌入 /h5-sdk 底座页面,以 postMessage 进行双向通信。 + *

通过 iframe 嵌入独立发布的 /engine/index.html 渲染引擎,以 postMessage 进行双向通信。 *

所有需要后端数据的操作均通过 postMessage 指令由底座代理, * Demo / 宿主不允许直连后端 API。 * - * @version 2.4.1 + * @version 2.5.0 */ declare class SGSMapSDK extends EventEmitter { static utils: typeof utils; diff --git a/static/sgs-map-sdk/index.global.js b/static/sgs-map-sdk/index.global.js index e541f3b..7cb8479 100644 --- a/static/sgs-map-sdk/index.global.js +++ b/static/sgs-map-sdk/index.global.js @@ -1,2 +1,2 @@ -"use strict";(()=>{var y=Object.defineProperty;var f=(d,o)=>{for(var e in o)y(d,e,{get:o[e],enumerable:!0})};var u=class{constructor(){this.events={}}on(o,e){this.events[o]||(this.events[o]=[]),this.events[o].push(e)}off(o,e){this.events[o]&&(this.events[o]=this.events[o].filter(s=>s!==e))}once(o,e){let s=t=>{this.off(o,s),e(t)};this.on(o,s)}emit(o,e){this.events[o]&&[...this.events[o]].forEach(t=>{try{t(e)}catch(r){console.error(`[EventEmitter] Error emitting event ${String(o)}:`,r)}})}removeAllListeners(o){o!==void 0?delete this.events[o]:this.events={}}listenerCount(o){return this.events[o]?.length??0}};var h={};f(h,{isIndoorNavigableFloor:()=>_,sortIndoorFloors:()=>R});function _(d){return d?d.toUpperCase()!=="EXTERIOR":!1}function R(d){let o=["L-2","L-1","B2","B1","L1","L1.5","L2","L3","L4","L5"],e=d.filter(t=>_(t.floorCode)),s=t=>{let r=t.trim().toUpperCase();r.startsWith("F")&&(r="L"+r.substring(1));let i=o.findIndex(a=>a.toUpperCase()===r);if(i!==-1)return i;let n=r.match(/-?\d+(\.\d+)?/);if(n){let a=parseFloat(n[0]);return r.startsWith("B")||r.includes("-")?-Math.abs(a)-100:a}return 0};return[...e].sort((t,r)=>s(t.floorCode)-s(r.floorCode))}var g="2.4.1",S=2,E=3e4,P=5e3,l=class extends u{constructor(e){super();this.iframe=null;this._isReady=!1;this._isDestroyed=!1;this._readyFailed=!1;this._readyFailedError=null;this.promiseQueue={};this._pendingCommands=[];this._pendingFireAndForget=[];this._messageListener=null;this._readyTimer=null;this._helloInterval=null;this.readyPayload=null;this._readyResolve=null;this._readyReject=null;this._iframeDomReadyMs=null;this._engineReadyMs=null;this._sdkCreatedAt=Date.now();let s=typeof e.container=="string"?document.getElementById(e.container):e.container;if(!s)throw new Error(`[SGSMapSDK] Cannot find container: ${e.container}`);this.container=s,this.sdkUrl=e.sdkUrl||"/h5-sdk",this.floorId=e.floorId||1,this.mapId=e.mapId||1,this.defaultTimeout=e.timeout||P,this.readyTimeoutMs=e.readyTimeout||E,this.targetOrigin=e.targetOrigin,this.targetOrigin||(this.sdkUrl.startsWith("http")?this.targetOrigin=new URL(this.sdkUrl).origin:typeof window<"u"?this.targetOrigin=window.location.origin:this.targetOrigin="*"),this._readyPromise=new Promise((t,r)=>{this._readyResolve=t,this._readyReject=r}),this._initIframeBridge()}_initIframeBridge(){let e=document.createElement("iframe"),s=new URL(this.sdkUrl,typeof window<"u"?window.location.href:"http://localhost");typeof window<"u"&&new URLSearchParams(window.location.search).forEach((r,i)=>{s.searchParams.set(i,r)}),s.searchParams.has("floorId")||s.searchParams.set("floorId",String(this.floorId)),e.src=s.toString(),e.style.cssText="width:100%;height:100%;border:none;background:transparent;",this.container.innerHTML="",this.container.appendChild(e),this.iframe=e,this._messageListener=t=>{if(this.targetOrigin!=="*"&&t.origin!==this.targetOrigin||!this.iframe||t.source!==this.iframe.contentWindow)return;let r=t.data;if(r){if(r.type==="SGS_MAP_EVENT"){let{action:i,payload:n}=r;i==="ON_POI_CLICK"?this.emit("poiClick",n):i==="ON_POI_DETAIL"?this.emit("poiDetail",n):i==="ON_POI_NAVIGATE"?this.emit("poiNavigate",n):i==="ON_POI_CLOSE"?this.emit("poiClose",n):i==="ON_SPACE_CLICK"?this.emit("spaceClick",n):i==="ON_SPACE_DETAIL"?this.emit("spaceDetail",n):i==="ON_SPACE_NAVIGATE"?this.emit("spaceNavigate",n):i==="ON_SPACE_CLOSE"?this.emit("spaceClose",n):i==="ENGINE_READY"?this._onEngineReady(n):i==="ON_FLOOR_CHANGED"?this.emit("floorChanged",n):i==="ON_ROUTE_TRANSFER"?this.emit("routeTransfer",n):i==="ON_MODEL_LOADING"?this.emit("modelLoading",n):i==="ON_MODEL_READY"?this.emit("modelReady",n):i==="ON_MODEL_ERROR"?this.emit("modelError",n):i==="ON_FLOOR_BUNDLE_READY"?this.emit("floorBundleReady",n):i==="ON_ROUTE_READY"?this.emit("routeReady",n):i==="ON_ROAMING_PROGRESS"?this.emit("roamingProgress",n):i==="ON_ROAMING_DONE"&&this.emit("roamingDone",n)}if(r.type==="SGS_MAP_RESPONSE"){let{action:i,requestId:n,payload:a,error:m}=r,c=this.promiseQueue[n];if(c){if(a&&a.success!==!1)c.resolve(a);else{let p=m||{code:"ERR_FAILED",message:`${i} command failed`};c.reject(p)}delete this.promiseQueue[n]}}}},window.addEventListener("message",this._messageListener),e.onload=()=>{if(this._iframeDomReadyMs=Date.now()-this._sdkCreatedAt,this._isReady)return;let t=()=>{if(this._isReady||this._isDestroyed){this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null);return}this._postCommand("HELLO",{protocolVersion:S,sdkVersion:g})};t(),this._helloInterval=setInterval(t,500),console.log(`[SGSMapSDK] Sent HELLO handshake (v${g}), will retry every 500ms until ENGINE_READY...`)},e.onerror=t=>{console.error("[SGSMapSDK] Iframe load error",t);let r={code:"ERR_IFRAME_LOAD",message:"Failed to load map engine iframe."};this.emit("loadError",{type:"iframe_error",detail:r.message}),this._rejectReady(r)},this._readyTimer=setTimeout(()=>{if(!this._isReady&&!this._isDestroyed){let t={code:"ERR_READY_TIMEOUT",message:`ENGINE_READY not received within ${this.readyTimeoutMs}ms`};console.error(`[SGSMapSDK] ${t.message}`),this._rejectReady(t)}},this.readyTimeoutMs)}_onEngineReady(e){this._isReady||(this._isReady=!0,this.readyPayload=e,this._engineReadyMs=Date.now()-this._sdkCreatedAt,this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._readyResolve&&(this._readyResolve(e),this._readyResolve=null,this._readyReject=null),this.emit("ready",e),console.log("[SGSMapSDK] Handshake complete: Engine Ready received \u2705"),this._flushPendingCommands())}_rejectReady(e){this._readyFailed=!0,this._readyFailedError=e,this._readyReject&&(this._readyReject(e),this._readyResolve=null,this._readyReject=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._rejectAllPending(e.code,e.message),this._pendingFireAndForget.splice(0)}_flushPendingCommands(){let e=this._pendingFireAndForget.splice(0);for(let r of e)this._postCommand(r.action,r.payload);let s=this._pendingCommands.splice(0);for(let r of s)this._postCommandAsyncInner(r.action,r.payload,r.timeout).then(r.resolve).catch(r.reject);let t=e.length+s.length;t>0&&console.log(`[SGSMapSDK] Flushed ${t} pending commands after ready (${e.length} fire-and-forget, ${s.length} async)`)}_rejectAllPending(e,s){Object.keys(this.promiseQueue).forEach(r=>{let i=this.promiseQueue[r];i&&i.reject({code:e,message:s}),delete this.promiseQueue[r]});let t=this._pendingCommands.splice(0);for(let r of t)r.reject({code:e,message:s})}whenReady(){return this._readyPromise}ready(){return this._readyPromise}getIsReady(){return this._isReady}getCurrentFloor(){return this.floorId}getMapId(){return this.mapId}getVersion(){return g}_generateRequestId(){return`req_sdk_${Date.now()}_${Math.floor(Math.random()*1e5)}`}_postCommand(e,s,t){if(!this.iframe||!this.iframe.contentWindow){console.error("[SGSMapSDK] SDK Iframe is not available!");return}this.iframe.contentWindow.postMessage({type:"SGS_MAP_COMMAND",action:e,requestId:t,payload:s},this.targetOrigin)}_postCommandAsync(e,s,t=this.defaultTimeout){return this._isDestroyed?Promise.reject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed. Cannot send commands."}):this._readyFailed?Promise.reject(this._readyFailedError||{code:"ERR_NOT_READY",message:"SDK ready failed."}):this._isReady?this._postCommandAsyncInner(e,s,t):new Promise((r,i)=>{this._pendingCommands.push({action:e,payload:s,timeout:t,resolve:r,reject:i}),console.log(`[SGSMapSDK] Command "${e}" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`)})}_postCommandSafe(e,s){if(this._isDestroyed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK destroyed.`);return}if(this._readyFailed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK ready failed.`);return}if(!this._isReady){this._pendingFireAndForget.push({action:e,payload:s}),console.log(`[SGSMapSDK] Command "${e}" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);return}this._postCommand(e,s)}_postCommandAsyncInner(e,s,t=this.defaultTimeout){let r=this._generateRequestId();return new Promise((i,n)=>{let a=setTimeout(()=>{this.promiseQueue[r]&&(this.promiseQueue[r].reject({code:"ERR_TIMEOUT",message:`Command "${e}" timed out after ${t}ms`}),delete this.promiseQueue[r])},t);this.promiseQueue[r]={resolve:m=>{clearTimeout(a),i(m)},reject:m=>{clearTimeout(a),n(m)}},this._postCommand(e,s,r)})}focusTo(e){typeof e=="string"?this._postCommandSafe("FOCUS_TO",{nodeName:e}):this._postCommandSafe("FOCUS_TO",e)}changeFloor(e,s,t){return this._postCommandAsync("CHANGE_FLOOR",{floorId:e,...t},s).then(r=>(this.floorId=e,r))}addMarker(e){this._postCommandSafe("ADD_MARKER",e)}removeMarker(e){this._postCommandSafe("REMOVE_MARKER",{markerId:e})}clearMarkers(){this._postCommandSafe("CLEAR_MARKERS")}highlightPolygon(e){typeof e=="string"?this._postCommandSafe("HIGHLIGHT_POLYGON",{nodeName:e}):this._postCommandSafe("HIGHLIGHT_POLYGON",e)}planRoute(e,s,t,r=5e3){let i=typeof e=="string"?e:void 0,n=typeof s=="string"?s:void 0,a={};return i?a.startNode=i:a.from=e,n?a.endNode=n:a.to=s,t&&(a.options=t),this._postCommandAsync("PLAN_ROUTE",a,r)}planRouteV2(e,s,t,r=5e3){let i={from:e,to:s,options:t};return this._postCommandAsync("PLAN_ROUTE",i,r)}clearRoute(){this._postCommandSafe("CLEAR_ROUTE")}renderRoutePoints(e,s,t=12e3){return this._postCommandAsync("RENDER_ROUTE_POINTS",{...s,pathPoints:e},t)}startRoaming(e=[],s,t=12e3){return this._postCommandAsync("START_ROAMING",{...s,pathPoints:e},t)}stopRoaming(e){return this._postCommandAsync("STOP_ROAMING",{},e)}toggleRoamingPause(e){return this._postCommandAsync("TOGGLE_ROAMING_PAUSE",{},e)}planRouteToPoi(e,s,t,r=5e3){let n={from:typeof e=="string"||typeof e=="number"?{poiId:e}:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_TO_POI",n,r)}planRouteBetweenPois(e,s,t,r=5e3){let i={fromPoiId:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_BETWEEN_POIS",i,r)}getState(){return this._postCommandAsync("GET_STATE")}resetView(e){return this._postCommandAsync("RESET_VIEW",{},e)}setViewMode(e,s){return this._postCommandAsync("SET_VIEW_MODE",{mode:e},s)}toggleLayer(e,s,t){return this._postCommandAsync("TOGGLE_LAYER",{types:e,visible:s},t)}setVisibleLayers(e,s){return this._postCommandAsync("SET_VISIBLE_TYPES",{types:e},s)}setRouteViewActive(e,s){return this._postCommandAsync("SET_ROUTE_VIEW_ACTIVE",{active:e},s)}getManifest(e){return this._postCommandAsync("GET_MANIFEST",{},e)}loadFloorBundle(e,s){return this._postCommandAsync("LOAD_FLOOR_BUNDLE",{floorId:e},s)}getFloorPois(e,s){return this._postCommandAsync("GET_FLOOR_POIS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.pois||[])}getNavigablePlaces(e,s){return this._postCommandAsync("GET_NAVIGABLE_PLACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.navigablePlaces||t?.places||[])}getPois(e,s){return this._postCommandAsync("GET_POIS",{floorCode:e},s)}getSpaces(e,s){return this._postCommandAsync("GET_SPACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.spaces||[])}getGuideStops(e,s){return this._postCommandAsync("GET_GUIDE_STOPS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}getGuideStopsByHall(e,s){return this._postCommandAsync("GET_GUIDE_STOPS_BY_HALL",{hallId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}preloadFloor(e,s){return this._postCommandAsync("PRELOAD_FLOOR",{floorId:e},s)}getPerformanceStats(e){return this._postCommandAsync("GET_PERFORMANCE_STATS",{},e).then(s=>({sdkCreatedAt:this._sdkCreatedAt,iframeDomReadyMs:this._iframeDomReadyMs??void 0,engineReadyMs:this._engineReadyMs??void 0,...s}))}getDiagnostics(e){return this._postCommandAsync("GET_DIAGNOSTICS",{},e)}getFloorDiagnostics(e,s){return this._postCommandAsync("GET_FLOOR_DIAGNOSTICS",{floorId:e},s)}clearModelCache(){this._postCommandSafe("CLEAR_MODEL_CACHE")}getBusinessPois(e,s,t){return this._postCommandAsync("GET_BUSINESS_POIS",{floorId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.pois||[])}queryPois(e,s){return this._postCommandAsync("QUERY_POIS",e,s).then(t=>Array.isArray(t)?t:t?.pois||[])}getFeaturedRoutes(e,s,t){return this._postCommandAsync("GET_FEATURED_ROUTES",{mapId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.routes||[])}getFeaturedRoute(e,s){return this._postCommandAsync("GET_FEATURED_ROUTE",{routeId:e},s).then(t=>t?.route||t)}planFeaturedRoutePreview(e,s=15e3){return this._postCommandAsync("PLAN_FEATURED_ROUTE_PREVIEW",e,s)}showFlowline(e,s,t){return this._postCommandAsync("SHOW_FLOWLINE",{routeId:e,options:s},t)}clearFlowline(){this._postCommandSafe("CLEAR_FLOWLINE")}getReleaseManifest(e){return console.warn("[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getReleaseManifest() is deprecated. Use getManifest() instead."})}getModelResources(e){return console.warn("[SGSMapSDK] getModelResources() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getModelResources() is deprecated."})}getLayerTree(e){return console.warn("[SGSMapSDK] getLayerTree() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getLayerTree() is deprecated."})}loadMockReleaseRoute(e){return console.warn("[SGSMapSDK] loadMockReleaseRoute() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"loadMockReleaseRoute() is deprecated."})}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyReject&&(this._readyReject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed before ready."}),this._readyResolve=null,this._readyReject=null),this._rejectAllPending("ERR_DESTROYED","SGSMapSDK has been destroyed. Pending promises were rejected."),this.iframe&&(this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this._messageListener&&(window.removeEventListener("message",this._messageListener),this._messageListener=null),this.removeAllListeners(),this._isReady=!1,console.log("[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues."))}};l.utils=h;typeof window<"u"&&(window.SGSMapSDK=l);})(); +"use strict";(()=>{var y=Object.defineProperty;var f=(d,o)=>{for(var e in o)y(d,e,{get:o[e],enumerable:!0})};var u=class{constructor(){this.events={}}on(o,e){this.events[o]||(this.events[o]=[]),this.events[o].push(e)}off(o,e){this.events[o]&&(this.events[o]=this.events[o].filter(s=>s!==e))}once(o,e){let s=t=>{this.off(o,s),e(t)};this.on(o,s)}emit(o,e){this.events[o]&&[...this.events[o]].forEach(t=>{try{t(e)}catch(r){console.error(`[EventEmitter] Error emitting event ${String(o)}:`,r)}})}removeAllListeners(o){o!==void 0?delete this.events[o]:this.events={}}listenerCount(o){return this.events[o]?.length??0}};var h={};f(h,{isIndoorNavigableFloor:()=>_,sortIndoorFloors:()=>R});function _(d){return d?d.toUpperCase()!=="EXTERIOR":!1}function R(d){let o=["L-2","L-1","B2","B1","L1","L1.5","L2","L3","L4","L5"],e=d.filter(t=>_(t.floorCode)),s=t=>{let r=t.trim().toUpperCase();r.startsWith("F")&&(r="L"+r.substring(1));let i=o.findIndex(a=>a.toUpperCase()===r);if(i!==-1)return i;let n=r.match(/-?\d+(\.\d+)?/);if(n){let a=parseFloat(n[0]);return r.startsWith("B")||r.includes("-")?-Math.abs(a)-100:a}return 0};return[...e].sort((t,r)=>s(t.floorCode)-s(r.floorCode))}var g="2.5.0",S=2,E=3e4,P=5e3,l=class extends u{constructor(e){super();this.iframe=null;this._isReady=!1;this._isDestroyed=!1;this._readyFailed=!1;this._readyFailedError=null;this.promiseQueue={};this._pendingCommands=[];this._pendingFireAndForget=[];this._messageListener=null;this._readyTimer=null;this._helloInterval=null;this.readyPayload=null;this._readyResolve=null;this._readyReject=null;this._iframeDomReadyMs=null;this._engineReadyMs=null;this._sdkCreatedAt=Date.now();let s=typeof e.container=="string"?document.getElementById(e.container):e.container;if(!s)throw new Error(`[SGSMapSDK] Cannot find container: ${e.container}`);this.container=s,this.sdkUrl=e.sdkUrl||"/engine/index.html",this.floorId=e.floorId||1,this.mapId=e.mapId||1,this.defaultTimeout=e.timeout||P,this.readyTimeoutMs=e.readyTimeout||E,this.targetOrigin=e.targetOrigin,this.targetOrigin||(this.sdkUrl.startsWith("http")?this.targetOrigin=new URL(this.sdkUrl).origin:typeof window<"u"?this.targetOrigin=window.location.origin:this.targetOrigin="*"),this._readyPromise=new Promise((t,r)=>{this._readyResolve=t,this._readyReject=r}),this._initIframeBridge()}_initIframeBridge(){let e=document.createElement("iframe"),s=new URL(this.sdkUrl,typeof window<"u"?window.location.href:"http://localhost");typeof window<"u"&&new URLSearchParams(window.location.search).forEach((r,i)=>{s.searchParams.set(i,r)}),s.searchParams.has("floorId")||s.searchParams.set("floorId",String(this.floorId)),e.src=s.toString(),e.style.cssText="width:100%;height:100%;border:none;background:transparent;",this.container.innerHTML="",this.container.appendChild(e),this.iframe=e,this._messageListener=t=>{if(this.targetOrigin!=="*"&&t.origin!==this.targetOrigin||!this.iframe||t.source!==this.iframe.contentWindow)return;let r=t.data;if(r){if(r.type==="SGS_MAP_EVENT"){let{action:i,payload:n}=r;i==="ON_POI_CLICK"?this.emit("poiClick",n):i==="ON_POI_DETAIL"?this.emit("poiDetail",n):i==="ON_POI_NAVIGATE"?this.emit("poiNavigate",n):i==="ON_POI_CLOSE"?this.emit("poiClose",n):i==="ON_SPACE_CLICK"?this.emit("spaceClick",n):i==="ON_SPACE_DETAIL"?this.emit("spaceDetail",n):i==="ON_SPACE_NAVIGATE"?this.emit("spaceNavigate",n):i==="ON_SPACE_CLOSE"?this.emit("spaceClose",n):i==="ENGINE_READY"?this._onEngineReady(n):i==="ON_FLOOR_CHANGED"?this.emit("floorChanged",n):i==="ON_ROUTE_TRANSFER"?this.emit("routeTransfer",n):i==="ON_MODEL_LOADING"?this.emit("modelLoading",n):i==="ON_MODEL_READY"?this.emit("modelReady",n):i==="ON_MODEL_ERROR"?this.emit("modelError",n):i==="ON_FLOOR_BUNDLE_READY"?this.emit("floorBundleReady",n):i==="ON_ROUTE_READY"?this.emit("routeReady",n):i==="ON_ROAMING_PROGRESS"?this.emit("roamingProgress",n):i==="ON_ROAMING_DONE"&&this.emit("roamingDone",n)}if(r.type==="SGS_MAP_RESPONSE"){let{action:i,requestId:n,payload:a,error:m}=r,c=this.promiseQueue[n];if(c){if(a&&a.success!==!1)c.resolve(a);else{let p=m||{code:"ERR_FAILED",message:`${i} command failed`};c.reject(p)}delete this.promiseQueue[n]}}}},window.addEventListener("message",this._messageListener),e.onload=()=>{if(this._iframeDomReadyMs=Date.now()-this._sdkCreatedAt,this._isReady)return;let t=()=>{if(this._isReady||this._isDestroyed){this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null);return}this._postCommand("HELLO",{protocolVersion:S,sdkVersion:g})};t(),this._helloInterval=setInterval(t,500),console.log(`[SGSMapSDK] Sent HELLO handshake (v${g}), will retry every 500ms until ENGINE_READY...`)},e.onerror=t=>{console.error("[SGSMapSDK] Iframe load error",t);let r={code:"ERR_IFRAME_LOAD",message:"Failed to load map engine iframe."};this.emit("loadError",{type:"iframe_error",detail:r.message}),this._rejectReady(r)},this._readyTimer=setTimeout(()=>{if(!this._isReady&&!this._isDestroyed){let t={code:"ERR_READY_TIMEOUT",message:`ENGINE_READY not received within ${this.readyTimeoutMs}ms`};console.error(`[SGSMapSDK] ${t.message}`),this._rejectReady(t)}},this.readyTimeoutMs)}_onEngineReady(e){this._isReady||(this._isReady=!0,this.readyPayload=e,this._engineReadyMs=Date.now()-this._sdkCreatedAt,this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._readyResolve&&(this._readyResolve(e),this._readyResolve=null,this._readyReject=null),this.emit("ready",e),console.log("[SGSMapSDK] Handshake complete: Engine Ready received \u2705"),this._flushPendingCommands())}_rejectReady(e){this._readyFailed=!0,this._readyFailedError=e,this._readyReject&&(this._readyReject(e),this._readyResolve=null,this._readyReject=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._rejectAllPending(e.code,e.message),this._pendingFireAndForget.splice(0)}_flushPendingCommands(){let e=this._pendingFireAndForget.splice(0);for(let r of e)this._postCommand(r.action,r.payload);let s=this._pendingCommands.splice(0);for(let r of s)this._postCommandAsyncInner(r.action,r.payload,r.timeout).then(r.resolve).catch(r.reject);let t=e.length+s.length;t>0&&console.log(`[SGSMapSDK] Flushed ${t} pending commands after ready (${e.length} fire-and-forget, ${s.length} async)`)}_rejectAllPending(e,s){Object.keys(this.promiseQueue).forEach(r=>{let i=this.promiseQueue[r];i&&i.reject({code:e,message:s}),delete this.promiseQueue[r]});let t=this._pendingCommands.splice(0);for(let r of t)r.reject({code:e,message:s})}whenReady(){return this._readyPromise}ready(){return this._readyPromise}getIsReady(){return this._isReady}getCurrentFloor(){return this.floorId}getMapId(){return this.mapId}getVersion(){return g}_generateRequestId(){return`req_sdk_${Date.now()}_${Math.floor(Math.random()*1e5)}`}_postCommand(e,s,t){if(!this.iframe||!this.iframe.contentWindow){console.error("[SGSMapSDK] SDK Iframe is not available!");return}this.iframe.contentWindow.postMessage({type:"SGS_MAP_COMMAND",action:e,requestId:t,payload:s},this.targetOrigin)}_postCommandAsync(e,s,t=this.defaultTimeout){return this._isDestroyed?Promise.reject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed. Cannot send commands."}):this._readyFailed?Promise.reject(this._readyFailedError||{code:"ERR_NOT_READY",message:"SDK ready failed."}):this._isReady?this._postCommandAsyncInner(e,s,t):new Promise((r,i)=>{this._pendingCommands.push({action:e,payload:s,timeout:t,resolve:r,reject:i}),console.log(`[SGSMapSDK] Command "${e}" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`)})}_postCommandSafe(e,s){if(this._isDestroyed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK destroyed.`);return}if(this._readyFailed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK ready failed.`);return}if(!this._isReady){this._pendingFireAndForget.push({action:e,payload:s}),console.log(`[SGSMapSDK] Command "${e}" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);return}this._postCommand(e,s)}_postCommandAsyncInner(e,s,t=this.defaultTimeout){let r=this._generateRequestId();return new Promise((i,n)=>{let a=setTimeout(()=>{this.promiseQueue[r]&&(this.promiseQueue[r].reject({code:"ERR_TIMEOUT",message:`Command "${e}" timed out after ${t}ms`}),delete this.promiseQueue[r])},t);this.promiseQueue[r]={resolve:m=>{clearTimeout(a),i(m)},reject:m=>{clearTimeout(a),n(m)}},this._postCommand(e,s,r)})}focusTo(e){typeof e=="string"?this._postCommandSafe("FOCUS_TO",{nodeName:e}):this._postCommandSafe("FOCUS_TO",e)}changeFloor(e,s,t){return this._postCommandAsync("CHANGE_FLOOR",{floorId:e,...t},s).then(r=>(this.floorId=e,r))}addMarker(e){this._postCommandSafe("ADD_MARKER",e)}removeMarker(e){this._postCommandSafe("REMOVE_MARKER",{markerId:e})}clearMarkers(){this._postCommandSafe("CLEAR_MARKERS")}highlightPolygon(e){typeof e=="string"?this._postCommandSafe("HIGHLIGHT_POLYGON",{nodeName:e}):this._postCommandSafe("HIGHLIGHT_POLYGON",e)}planRoute(e,s,t,r=5e3){let i=typeof e=="string"?e:void 0,n=typeof s=="string"?s:void 0,a={};return i?a.startNode=i:a.from=e,n?a.endNode=n:a.to=s,t&&(a.options=t),this._postCommandAsync("PLAN_ROUTE",a,r)}planRouteV2(e,s,t,r=5e3){let i={from:e,to:s,options:t};return this._postCommandAsync("PLAN_ROUTE",i,r)}clearRoute(){this._postCommandSafe("CLEAR_ROUTE")}renderRoutePoints(e,s,t=12e3){return this._postCommandAsync("RENDER_ROUTE_POINTS",{...s,pathPoints:e},t)}startRoaming(e=[],s,t=12e3){return this._postCommandAsync("START_ROAMING",{...s,pathPoints:e},t)}stopRoaming(e){return this._postCommandAsync("STOP_ROAMING",{},e)}toggleRoamingPause(e){return this._postCommandAsync("TOGGLE_ROAMING_PAUSE",{},e)}planRouteToPoi(e,s,t,r=5e3){let n={from:typeof e=="string"||typeof e=="number"?{poiId:e}:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_TO_POI",n,r)}planRouteBetweenPois(e,s,t,r=5e3){let i={fromPoiId:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_BETWEEN_POIS",i,r)}getState(){return this._postCommandAsync("GET_STATE")}resetView(e){return this._postCommandAsync("RESET_VIEW",{},e)}setViewMode(e,s){return this._postCommandAsync("SET_VIEW_MODE",{mode:e},s)}toggleLayer(e,s,t){return this._postCommandAsync("TOGGLE_LAYER",{types:e,visible:s},t)}setVisibleLayers(e,s){return this._postCommandAsync("SET_VISIBLE_TYPES",{types:e},s)}setRouteViewActive(e,s){return this._postCommandAsync("SET_ROUTE_VIEW_ACTIVE",{active:e},s)}getManifest(e){return this._postCommandAsync("GET_MANIFEST",{},e)}loadFloorBundle(e,s){return this._postCommandAsync("LOAD_FLOOR_BUNDLE",{floorId:e},s)}getFloorPois(e,s){return this._postCommandAsync("GET_FLOOR_POIS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.pois||[])}getNavigablePlaces(e,s){return this._postCommandAsync("GET_NAVIGABLE_PLACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.navigablePlaces||t?.places||[])}getPois(e,s){return this._postCommandAsync("GET_POIS",{floorCode:e},s)}getSpaces(e,s){return this._postCommandAsync("GET_SPACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.spaces||[])}getGuideStops(e,s){return this._postCommandAsync("GET_GUIDE_STOPS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}getGuideStopsByHall(e,s){return this._postCommandAsync("GET_GUIDE_STOPS_BY_HALL",{hallId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}preloadFloor(e,s){return this._postCommandAsync("PRELOAD_FLOOR",{floorId:e},s)}getPerformanceStats(e){return this._postCommandAsync("GET_PERFORMANCE_STATS",{},e).then(s=>({sdkCreatedAt:this._sdkCreatedAt,iframeDomReadyMs:this._iframeDomReadyMs??void 0,engineReadyMs:this._engineReadyMs??void 0,...s}))}getDiagnostics(e){return this._postCommandAsync("GET_DIAGNOSTICS",{},e)}getFloorDiagnostics(e,s){return this._postCommandAsync("GET_FLOOR_DIAGNOSTICS",{floorId:e},s)}clearModelCache(){this._postCommandSafe("CLEAR_MODEL_CACHE")}getBusinessPois(e,s,t){return this._postCommandAsync("GET_BUSINESS_POIS",{floorId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.pois||[])}queryPois(e,s){return this._postCommandAsync("QUERY_POIS",e,s).then(t=>Array.isArray(t)?t:t?.pois||[])}getFeaturedRoutes(e,s,t){return this._postCommandAsync("GET_FEATURED_ROUTES",{mapId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.routes||[])}getFeaturedRoute(e,s){return this._postCommandAsync("GET_FEATURED_ROUTE",{routeId:e},s).then(t=>t?.route||t)}planFeaturedRoutePreview(e,s=15e3){return this._postCommandAsync("PLAN_FEATURED_ROUTE_PREVIEW",e,s)}showFlowline(e,s,t){return this._postCommandAsync("SHOW_FLOWLINE",{routeId:e,options:s},t)}clearFlowline(){this._postCommandSafe("CLEAR_FLOWLINE")}getReleaseManifest(e){return console.warn("[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getReleaseManifest() is deprecated. Use getManifest() instead."})}getModelResources(e){return console.warn("[SGSMapSDK] getModelResources() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getModelResources() is deprecated."})}getLayerTree(e){return console.warn("[SGSMapSDK] getLayerTree() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getLayerTree() is deprecated."})}loadMockReleaseRoute(e){return console.warn("[SGSMapSDK] loadMockReleaseRoute() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"loadMockReleaseRoute() is deprecated."})}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyReject&&(this._readyReject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed before ready."}),this._readyResolve=null,this._readyReject=null),this._rejectAllPending("ERR_DESTROYED","SGSMapSDK has been destroyed. Pending promises were rejected."),this.iframe&&(this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this._messageListener&&(window.removeEventListener("message",this._messageListener),this._messageListener=null),this.removeAllListeners(),this._isReady=!1,console.log("[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues."))}};l.utils=h;typeof window<"u"&&(window.SGSMapSDK=l);})(); //# sourceMappingURL=index.global.js.map \ No newline at end of file diff --git a/static/sgs-map-sdk/index.global.js.map b/static/sgs-map-sdk/index.global.js.map index 7f01be3..39e689c 100644 --- a/static/sgs-map-sdk/index.global.js.map +++ b/static/sgs-map-sdk/index.global.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../sdk-src/event-emitter.ts","../../sdk-src/utils.ts","../../sdk-src/index.ts"],"sourcesContent":["/**\r\n * 轻量级类型安全事件发射器\r\n *\r\n *

SDK 内部事件系统基类,提供 on/off/once/emit/removeAllListeners/listenerCount。\r\n */\r\nexport class EventEmitter> {\r\n private events: { [K in keyof Events]?: Array<(payload: Events[K]) => void> };\r\n\r\n constructor() {\r\n this.events = {};\r\n }\r\n\r\n /** 注册事件监听器 */\r\n on(event: K, listener: (payload: Events[K]) => void): void {\r\n if (!this.events[event]) {\r\n this.events[event] = [];\r\n }\r\n this.events[event]!.push(listener);\r\n }\r\n\r\n /** 移除指定事件的指定监听器 */\r\n off(event: K, listener: (payload: Events[K]) => void): void {\r\n if (this.events[event]) {\r\n this.events[event] = this.events[event]!.filter(l => l !== listener);\r\n }\r\n }\r\n\r\n /** 注册一次性监听器,触发后自动移除 */\r\n once(event: K, listener: (payload: Events[K]) => void): void {\r\n const wrapper = (payload: Events[K]) => {\r\n this.off(event, wrapper);\r\n listener(payload);\r\n };\r\n this.on(event, wrapper);\r\n }\r\n\r\n /** 触发事件,通知所有已注册的监听器 */\r\n emit(event: K, data: Events[K]): void {\r\n if (this.events[event]) {\r\n // 浅拷贝防止 once 触发时修改数组导致跳过\r\n const listeners = [...this.events[event]!];\r\n listeners.forEach(l => {\r\n try {\r\n l(data);\r\n } catch (e) {\r\n console.error(`[EventEmitter] Error emitting event ${String(event)}:`, e);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** 移除指定事件的全部监听器;不传参则移除所有事件的所有监听器 */\r\n removeAllListeners(event?: K): void {\r\n if (event !== undefined) {\r\n delete this.events[event];\r\n } else {\r\n this.events = {};\r\n }\r\n }\r\n\r\n /** 返回指定事件的监听器数量 */\r\n listenerCount(event: K): number {\r\n return this.events[event]?.length ?? 0;\r\n }\r\n}\r\n","/**\r\n * 过滤 EXTERIOR 楼层,检查楼层是否是室内可导航楼层\r\n */\r\nexport function isIndoorNavigableFloor(floorCode: string): boolean {\r\n if (!floorCode) return false;\r\n return floorCode.toUpperCase() !== 'EXTERIOR';\r\n}\r\n\r\n/**\r\n * 物理楼层顺序由低到高排序\r\n */\r\nexport function sortIndoorFloors(floors: T[]): T[] {\r\n const FLOOR_ORDER = [\"L-2\", \"L-1\", \"B2\", \"B1\", \"L1\", \"L1.5\", \"L2\", \"L3\", \"L4\", \"L5\"];\r\n \r\n const indoorFloors = floors.filter(f => isIndoorNavigableFloor(f.floorCode));\r\n\r\n const getFloorIndex = (floorCode: string): number => {\r\n let code = floorCode.trim().toUpperCase();\r\n if (code.startsWith('F')) {\r\n code = 'L' + code.substring(1);\r\n }\r\n const matchedIndex = FLOOR_ORDER.findIndex(f => f.toUpperCase() === code);\r\n if (matchedIndex !== -1) return matchedIndex;\r\n \r\n const numMatch = code.match(/-?\\d+(\\.\\d+)?/);\r\n if (numMatch) {\r\n const val = parseFloat(numMatch[0]);\r\n if (code.startsWith('B') || code.includes('-')) {\r\n return -Math.abs(val) - 100;\r\n }\r\n return val;\r\n }\r\n return 0;\r\n };\r\n\r\n return [...indoorFloors].sort((a, b) => getFloorIndex(a.floorCode) - getFloorIndex(b.floorCode));\r\n}\r\n","import { EventEmitter } from './event-emitter';\r\nimport {\r\n SGSMapSDKOptions,\r\n SGSMapEvents,\r\n RouteResult,\r\n SgsMapNode,\r\n MarkerConfig,\r\n SgsSdkManifest,\r\n SgsFloorBundle,\r\n SgsPoi,\r\n SgsSpaceArea,\r\n SgsGuideStop,\r\n SgsPerformanceStats,\r\n SgsNavigablePlace,\r\n SgsMapDiagnostics,\r\n SgsFloorDiagnostics,\r\n SgsPoiQueryParams,\r\n SgsFeaturedRouteSummary,\r\n SgsFeaturedRouteDetail,\r\n SgsFeaturedRoutePreviewPayload,\r\n SgsFlowlineOptions,\r\n SgsRouteRoamingOptions,\r\n RouteOptions,\r\n SdkRoutePoint,\r\n} from './types';\r\n\r\nexport * from './types';\r\n\r\nimport * as utils from './utils';\r\n\r\n/** SDK 版本号常量 — 与后端 SdkMapServiceImpl.SDK_VERSION 对齐 */\r\nconst SDK_VERSION = '2.4.1';\r\n/** 协议版本号 */\r\nconst PROTOCOL_VERSION = 2;\r\n/** 默认 ready 超时(ms) */\r\nconst DEFAULT_READY_TIMEOUT = 30000; // 🔧 P0: 从 15s 提升到 30s,配合延迟 ENGINE_READY 策略\r\n/** 默认命令超时(ms) */\r\nconst DEFAULT_CMD_TIMEOUT = 5000;\r\n\r\n/**\r\n * 命令队列条目 — ready 前调用的异步命令暂存于此\r\n */\r\ninterface PendingCommand {\r\n action: string;\r\n payload?: any;\r\n timeout: number;\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n}\r\n\r\n/**\r\n * SGS 3D 地图 H5 SDK 入口类\r\n *\r\n *

通过 iframe 嵌入 /h5-sdk 底座页面,以 postMessage 进行双向通信。\r\n *

所有需要后端数据的操作均通过 postMessage 指令由底座代理,\r\n * Demo / 宿主不允许直连后端 API。\r\n *\r\n * @version 2.4.1\r\n */\r\nexport default class SGSMapSDK extends EventEmitter {\r\n public static utils = utils;\r\n private container: HTMLElement;\r\n private sdkUrl: string;\r\n private floorId: string | number;\r\n private mapId: string | number;\r\n private targetOrigin: string;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private _isReady: boolean = false;\r\n private _isDestroyed: boolean = false;\r\n /** ready 失败终态:iframe 加载失败或 ready 超时后置 true,后续命令直接 reject */\r\n private _readyFailed: boolean = false;\r\n /** ready 失败时的错误对象,用于后续命令的立即 reject */\r\n private _readyFailedError: { code: string; message: string } | null = null;\r\n private defaultTimeout: number;\r\n private readyTimeoutMs: number;\r\n /** 已 ready 后正在等待响应的命令队列 */\r\n private promiseQueue: {\r\n [key: string]: {\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n };\r\n } = {};\r\n /** ready 前暂存的异步命令队列 */\r\n private _pendingCommands: PendingCommand[] = [];\r\n /** ready 前暂存的 fire-and-forget 命令队列 */\r\n private _pendingFireAndForget: Array<{ action: string; payload?: any }> = [];\r\n private _messageListener: ((event: MessageEvent) => void) | null = null;\r\n /** ready 超时计时器 */\r\n private _readyTimer: ReturnType | null = null;\r\n /** 🔧 P0: HELLO 重试定时器 — 解决 iframe onload 与 React useEffect 时序竞争 */\r\n private _helloInterval: ReturnType | null = null;\r\n\r\n /** ENGINE_READY 返回的 payload(供外部读取) */\r\n public readyPayload: any = null;\r\n\r\n /** ready Promise 的 resolve/reject,保证 whenReady() 返回同一个 Promise */\r\n private _readyResolve: ((value: any) => void) | null = null;\r\n private _readyReject: ((reason: any) => void) | null = null;\r\n private _readyPromise: Promise;\r\n\r\n /** 性能追踪字段 */\r\n private _sdkCreatedAt: number;\r\n private _iframeDomReadyMs: number | null = null;\r\n private _engineReadyMs: number | null = null;\r\n\r\n constructor(options: SGSMapSDKOptions) {\r\n super();\r\n this._sdkCreatedAt = Date.now();\r\n\r\n const containerEl =\r\n typeof options.container === 'string'\r\n ? document.getElementById(options.container)\r\n : options.container;\r\n\r\n if (!containerEl) {\r\n throw new Error(`[SGSMapSDK] Cannot find container: ${options.container}`);\r\n }\r\n\r\n this.container = containerEl;\r\n this.sdkUrl = options.sdkUrl || '/h5-sdk';\r\n this.floorId = options.floorId || 1;\r\n this.mapId = options.mapId || 1;\r\n this.defaultTimeout = options.timeout || DEFAULT_CMD_TIMEOUT;\r\n this.readyTimeoutMs = options.readyTimeout || DEFAULT_READY_TIMEOUT;\r\n\r\n this.targetOrigin = options.targetOrigin as string;\r\n if (!this.targetOrigin) {\r\n if (this.sdkUrl.startsWith('http')) {\r\n this.targetOrigin = new URL(this.sdkUrl).origin;\r\n } else if (typeof window !== 'undefined') {\r\n this.targetOrigin = window.location.origin;\r\n } else {\r\n this.targetOrigin = '*';\r\n }\r\n }\r\n\r\n // 初始化 ready Promise(全局唯一,whenReady() 返回同一个)\r\n this._readyPromise = new Promise((resolve, reject) => {\r\n this._readyResolve = resolve;\r\n this._readyReject = reject;\r\n });\r\n\r\n this._initIframeBridge();\r\n }\r\n\r\n // ==================== iframe 桥接初始化 ====================\r\n\r\n private _initIframeBridge() {\r\n const iframe = document.createElement('iframe');\r\n\r\n // 拷贝父级 URL 上的查询参数传递给 iframe 底座\r\n const iframeUrl = new URL(\r\n this.sdkUrl,\r\n typeof window !== 'undefined' ? window.location.href : 'http://localhost'\r\n );\r\n if (typeof window !== 'undefined') {\r\n const parentParams = new URLSearchParams(window.location.search);\r\n parentParams.forEach((value, key) => {\r\n iframeUrl.searchParams.set(key, value);\r\n });\r\n }\r\n // 附带 floorId\r\n if (!iframeUrl.searchParams.has('floorId')) {\r\n iframeUrl.searchParams.set('floorId', String(this.floorId));\r\n }\r\n\r\n iframe.src = iframeUrl.toString();\r\n iframe.style.cssText = 'width:100%;height:100%;border:none;background:transparent;';\r\n\r\n this.container.innerHTML = '';\r\n this.container.appendChild(iframe);\r\n this.iframe = iframe;\r\n\r\n // ── 消息监听 ──\r\n this._messageListener = (event: MessageEvent) => {\r\n if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {\r\n return; // 静默忽略不匹配的来源\r\n }\r\n\r\n if (!this.iframe || event.source !== this.iframe.contentWindow) {\r\n return;\r\n }\r\n\r\n const data = event.data;\r\n if (!data) return;\r\n\r\n // ── 事件类型消息 ──\r\n if (data.type === 'SGS_MAP_EVENT') {\r\n const { action, payload } = data;\r\n if (action === 'ON_POI_CLICK') {\r\n this.emit('poiClick', payload);\r\n } else if (action === 'ON_POI_DETAIL') {\r\n this.emit('poiDetail', payload);\r\n } else if (action === 'ON_POI_NAVIGATE') {\r\n this.emit('poiNavigate', payload);\r\n } else if (action === 'ON_POI_CLOSE') {\r\n this.emit('poiClose', payload);\r\n } else if (action === 'ON_SPACE_CLICK') {\r\n this.emit('spaceClick', payload);\r\n } else if (action === 'ON_SPACE_DETAIL') {\r\n this.emit('spaceDetail', payload);\r\n } else if (action === 'ON_SPACE_NAVIGATE') {\r\n this.emit('spaceNavigate', payload);\r\n } else if (action === 'ON_SPACE_CLOSE') {\r\n this.emit('spaceClose', payload);\r\n } else if (action === 'ENGINE_READY') {\r\n this._onEngineReady(payload);\r\n } else if (action === 'ON_FLOOR_CHANGED') {\r\n this.emit('floorChanged', payload);\r\n } else if (action === 'ON_ROUTE_TRANSFER') {\r\n this.emit('routeTransfer', payload);\r\n } else if (action === 'ON_MODEL_LOADING') {\r\n this.emit('modelLoading', payload);\r\n } else if (action === 'ON_MODEL_READY') {\r\n this.emit('modelReady', payload);\r\n } else if (action === 'ON_MODEL_ERROR') {\r\n this.emit('modelError', payload);\r\n } else if (action === 'ON_FLOOR_BUNDLE_READY') {\r\n this.emit('floorBundleReady', payload);\r\n } else if (action === 'ON_ROUTE_READY') {\r\n this.emit('routeReady', payload);\r\n } else if (action === 'ON_ROAMING_PROGRESS') {\r\n this.emit('roamingProgress', payload);\r\n } else if (action === 'ON_ROAMING_DONE') {\r\n this.emit('roamingDone', payload);\r\n }\r\n }\r\n\r\n // ── 命令响应消息 ──\r\n if (data.type === 'SGS_MAP_RESPONSE') {\r\n const { action, requestId, payload, error } = data;\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n if (payload && payload.success !== false) {\r\n resolver.resolve(payload);\r\n } else {\r\n const errObj = error || { code: 'ERR_FAILED', message: `${action} command failed` };\r\n resolver.reject(errObj);\r\n }\r\n delete this.promiseQueue[requestId];\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener('message', this._messageListener);\r\n\r\n // ── iframe 加载完成 → 发送 HELLO 握手 ──\r\n iframe.onload = () => {\r\n this._iframeDomReadyMs = Date.now() - this._sdkCreatedAt;\r\n if (this._isReady) return;\r\n\r\n // 🔧 P0 修复:HELLO 重试机制\r\n // iframe onload 触发时 React useEffect 可能尚未挂载 message listener,\r\n // 单次 HELLO 会丢失。改为每 500ms 重试,直到 ENGINE_READY 到达。\r\n const sendHello = () => {\r\n if (this._isReady || this._isDestroyed) {\r\n if (this._helloInterval) { clearInterval(this._helloInterval); this._helloInterval = null; }\r\n return;\r\n }\r\n this._postCommand('HELLO', { protocolVersion: PROTOCOL_VERSION, sdkVersion: SDK_VERSION });\r\n };\r\n sendHello(); // 立即发第一次\r\n this._helloInterval = setInterval(sendHello, 500);\r\n console.log(`[SGSMapSDK] Sent HELLO handshake (v${SDK_VERSION}), will retry every 500ms until ENGINE_READY...`);\r\n };\r\n\r\n // ── iframe 加载失败 → reject ready + 清空命令队列 ──\r\n iframe.onerror = (err) => {\r\n console.error('[SGSMapSDK] Iframe load error', err);\r\n const error = { code: 'ERR_IFRAME_LOAD' as const, message: 'Failed to load map engine iframe.' };\r\n this.emit('loadError', { type: 'iframe_error', detail: error.message });\r\n this._rejectReady(error);\r\n };\r\n\r\n // ── ready 超时计时器 ──\r\n this._readyTimer = setTimeout(() => {\r\n if (!this._isReady && !this._isDestroyed) {\r\n const error = {\r\n code: 'ERR_READY_TIMEOUT' as const,\r\n message: `ENGINE_READY not received within ${this.readyTimeoutMs}ms`\r\n };\r\n console.error(`[SGSMapSDK] ${error.message}`);\r\n this._rejectReady(error);\r\n }\r\n }, this.readyTimeoutMs);\r\n }\r\n\r\n // ==================== ready 生命周期管理 ====================\r\n\r\n /**\r\n * ENGINE_READY 到达时的处理\r\n */\r\n private _onEngineReady(payload: any) {\r\n if (this._isReady) return; // 防重入\r\n\r\n this._isReady = true;\r\n this.readyPayload = payload;\r\n this._engineReadyMs = Date.now() - this._sdkCreatedAt;\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // resolve ready Promise\r\n if (this._readyResolve) {\r\n this._readyResolve(payload);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 触发 ready 事件\r\n this.emit('ready', payload);\r\n console.log('[SGSMapSDK] Handshake complete: Engine Ready received ✅');\r\n\r\n // 排空命令队列 — ready 前暂存的命令逐个发送\r\n this._flushPendingCommands();\r\n }\r\n\r\n /**\r\n * reject ready Promise 并清空所有挂起命令,设置失败终态\r\n */\r\n private _rejectReady(error: { code: string; message: string }) {\r\n // 设置失败终态 — 后续所有新命令直接 reject\r\n this._readyFailed = true;\r\n this._readyFailedError = error;\r\n\r\n // reject ready Promise\r\n if (this._readyReject) {\r\n this._readyReject(error);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 清空 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // reject 所有命令队列 + 清空 fire-and-forget 队列\r\n this._rejectAllPending(error.code, error.message);\r\n this._pendingFireAndForget.splice(0);\r\n }\r\n\r\n /**\r\n * 排空 ready 前暂存的命令队列 — 逐个以 _postCommandAsync 发送\r\n */\r\n private _flushPendingCommands() {\r\n // 1. 排空 fire-and-forget 队列\r\n const fireAndForget = this._pendingFireAndForget.splice(0);\r\n for (const cmd of fireAndForget) {\r\n this._postCommand(cmd.action, cmd.payload);\r\n }\r\n\r\n // 2. 排空异步命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n this._postCommandAsyncInner(cmd.action, cmd.payload, cmd.timeout)\r\n .then(cmd.resolve)\r\n .catch(cmd.reject);\r\n }\r\n\r\n const total = fireAndForget.length + pending.length;\r\n if (total > 0) {\r\n console.log(`[SGSMapSDK] Flushed ${total} pending commands after ready (${fireAndForget.length} fire-and-forget, ${pending.length} async)`);\r\n }\r\n }\r\n\r\n /**\r\n * reject 所有挂起的 Promise 和命令队列\r\n */\r\n private _rejectAllPending(code: string, message: string) {\r\n // 1. reject 已发送的 promiseQueue\r\n Object.keys(this.promiseQueue).forEach((requestId) => {\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n resolver.reject({ code, message });\r\n }\r\n delete this.promiseQueue[requestId];\r\n });\r\n\r\n // 2. reject 命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n cmd.reject({ code, message });\r\n }\r\n }\r\n\r\n // ==================== 公共生命周期方法 ====================\r\n\r\n /**\r\n * 返回 ready Promise(全局唯一,多次调用返回同一个)。\r\n * 别名:ready()\r\n *\r\n * @example\r\n * await sdk.whenReady();\r\n * sdk.changeFloor(floorId);\r\n */\r\n whenReady(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /**\r\n * whenReady 的别名\r\n */\r\n ready(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /** 获取 ready 状态 */\r\n getIsReady(): boolean {\r\n return this._isReady;\r\n }\r\n\r\n /** 获取当前楼层 ID */\r\n getCurrentFloor(): string | number {\r\n return this.floorId;\r\n }\r\n\r\n /** 获取当前 mapId */\r\n getMapId(): string | number {\r\n return this.mapId;\r\n }\r\n\r\n /** 获取 SDK 版本号 */\r\n getVersion(): string {\r\n return SDK_VERSION;\r\n }\r\n\r\n // ==================== 核心通信方法 ====================\r\n\r\n private _generateRequestId(): string {\r\n return `req_sdk_${Date.now()}_${Math.floor(Math.random() * 100000)}`;\r\n }\r\n\r\n private _postCommand(action: string, payload?: any, requestId?: string | null) {\r\n if (!this.iframe || !this.iframe.contentWindow) {\r\n console.error('[SGSMapSDK] SDK Iframe is not available!');\r\n return;\r\n }\r\n\r\n this.iframe.contentWindow.postMessage(\r\n {\r\n type: 'SGS_MAP_COMMAND',\r\n action,\r\n requestId,\r\n payload\r\n },\r\n this.targetOrigin\r\n );\r\n }\r\n\r\n /**\r\n * 异步命令发送(外部入口)— 支持命令队列\r\n *\r\n *

状态机优先级:destroyed > readyFailed > !ready(入队) > ready(发送)\r\n */\r\n private _postCommandAsync(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n // destroyed 后直接 reject\r\n if (this._isDestroyed) {\r\n return Promise.reject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed. Cannot send commands.'\r\n });\r\n }\r\n\r\n // ready 失败终态 — 立即 reject,不再入队\r\n if (this._readyFailed) {\r\n return Promise.reject(\r\n this._readyFailedError || { code: 'ERR_NOT_READY', message: 'SDK ready failed.' }\r\n );\r\n }\r\n\r\n // ready 前入队\r\n if (!this._isReady) {\r\n return new Promise((resolve, reject) => {\r\n this._pendingCommands.push({\r\n action,\r\n payload,\r\n timeout,\r\n resolve,\r\n reject\r\n });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`);\r\n });\r\n }\r\n\r\n // ready 后直接发送\r\n return this._postCommandAsyncInner(action, payload, timeout);\r\n }\r\n\r\n /**\r\n * fire-and-forget 命令发送(void 方法统一入口)\r\n *\r\n *

受 destroyed/readyFailed/!ready 状态保护:\r\n * - destroyed/readyFailed:静默丢弃 + console.warn\r\n * - !ready:暂存到 _pendingFireAndForget,ready 后自动发送\r\n * - ready:立即发送\r\n */\r\n private _postCommandSafe(action: string, payload?: any): void {\r\n if (this._isDestroyed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK destroyed.`);\r\n return;\r\n }\r\n if (this._readyFailed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK ready failed.`);\r\n return;\r\n }\r\n if (!this._isReady) {\r\n this._pendingFireAndForget.push({ action, payload });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);\r\n return;\r\n }\r\n this._postCommand(action, payload);\r\n }\r\n\r\n /**\r\n * 异步命令发送(内部实现)— 已确认 ready\r\n */\r\n private _postCommandAsyncInner(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n const requestId = this._generateRequestId();\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n if (this.promiseQueue[requestId]) {\r\n this.promiseQueue[requestId].reject({\r\n code: 'ERR_TIMEOUT',\r\n message: `Command \"${action}\" timed out after ${timeout}ms`\r\n });\r\n delete this.promiseQueue[requestId];\r\n }\r\n }, timeout);\r\n\r\n this.promiseQueue[requestId] = {\r\n resolve: (data) => {\r\n clearTimeout(timer);\r\n resolve(data as T);\r\n },\r\n reject: (error) => {\r\n clearTimeout(timer);\r\n reject(error);\r\n }\r\n };\r\n\r\n this._postCommand(action, payload, requestId);\r\n });\r\n }\r\n\r\n // ==================== v2.0 核心指令 API ====================\r\n\r\n /** 聚焦到指定要素节点 */\r\n focusTo(node: SgsMapNode | string) {\r\n if (typeof node === 'string') {\r\n this._postCommandSafe('FOCUS_TO', { nodeName: node });\r\n } else {\r\n this._postCommandSafe('FOCUS_TO', node);\r\n }\r\n }\r\n\r\n /** 切换楼层 */\r\n changeFloor(floorId: string | number, timeout?: number, options?: { forceReload?: boolean }): Promise<{ success: boolean; floorId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; floorId: string | number }>('CHANGE_FLOOR', { floorId, ...options }, timeout).then((res) => {\r\n this.floorId = floorId;\r\n return res;\r\n });\r\n }\r\n\r\n /** 添加业务图钉 */\r\n addMarker(config: MarkerConfig) {\r\n this._postCommandSafe('ADD_MARKER', config);\r\n }\r\n\r\n /** 移除指定图钉 */\r\n removeMarker(markerId: string) {\r\n this._postCommandSafe('REMOVE_MARKER', { markerId });\r\n }\r\n\r\n /** 清空所有业务图钉 */\r\n clearMarkers() {\r\n this._postCommandSafe('CLEAR_MARKERS');\r\n }\r\n\r\n /** 高亮多边形轮廓 */\r\n highlightPolygon(payload: string | object) {\r\n if (typeof payload === 'string') {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', { nodeName: payload });\r\n } else {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', payload);\r\n }\r\n }\r\n\r\n /** 寻路规划(v1 兼容) */\r\n planRoute(from: SgsMapNode | string, to: SgsMapNode | string, options?: any, timeout: number = 5000): Promise {\r\n const startNode = typeof from === 'string' ? from : undefined;\r\n const endNode = typeof to === 'string' ? to : undefined;\r\n\r\n const payload: any = {};\r\n if (startNode) payload.startNode = startNode;\r\n else payload.from = from;\r\n\r\n if (endNode) payload.endNode = endNode;\r\n else payload.to = to;\r\n\r\n if (options) payload.options = options;\r\n\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 寻路规划 V2(结构化参数) */\r\n planRouteV2(from: SgsMapNode, to: SgsMapNode, options?: any, timeout: number = 5000): Promise {\r\n const payload = { from, to, options };\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 清除当前路线 */\r\n clearRoute() {\r\n this._postCommandSafe('CLEAR_ROUTE');\r\n }\r\n\r\n /** 渲染已有路线点,供外部宿主直接展示自定义路线 */\r\n renderRoutePoints(\r\n pathPoints: Array<{ x: number; y: number; z: number }>,\r\n options?: any,\r\n timeout: number = 12000\r\n ): Promise {\r\n return this._postCommandAsync('RENDER_ROUTE_POINTS', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 启动路线漫游;pathPoints 为空时,基座会优先使用当前已渲染路线 */\r\n startRoaming(\r\n pathPoints: Array<{ x: number; y: number; z: number }> = [],\r\n options?: SgsRouteRoamingOptions,\r\n timeout: number = 12000\r\n ): Promise<{ success: boolean; mode?: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode?: string }>('START_ROAMING', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 停止路线漫游 */\r\n stopRoaming(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('STOP_ROAMING', {}, timeout);\r\n }\r\n\r\n /** 暂停或继续路线漫游 */\r\n toggleRoamingPause(timeout?: number): Promise<{ success: boolean; paused?: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; paused?: boolean }>('TOGGLE_ROAMING_PAUSE', {}, timeout);\r\n }\r\n\r\n // ==================== SDK 2.4.1 高层导航 API ====================\r\n\r\n /**\r\n * 导航至POI(SDK 2.4.1)\r\n *\r\n * 支持从坐标或POI导航到目标POI,后端自动查询POI坐标。\r\n *\r\n * @param from 起点(POI ID字符串/数字 或 坐标对象)\r\n * @param toPoiId 目标POI ID(建议使用string避免Snowflake精度丢失)\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteToPoi(from: string | number | SdkRoutePoint, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n // 将 string/number 转为 { poiId: from }\r\n const fromPoint: SdkRoutePoint = (typeof from === 'string' || typeof from === 'number')\r\n ? { poiId: from }\r\n : from;\r\n\r\n const payload = {\r\n from: fromPoint,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_TO_POI', payload, timeout);\r\n }\r\n\r\n /**\r\n * POI间导航(SDK 2.4.1)\r\n *\r\n * 两个POI之间的导航,后端自动查询POI坐标。\r\n *\r\n * @param fromPoiId 起点POI ID\r\n * @param toPoiId 终点POI ID\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteBetweenPois(fromPoiId: string | number, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n const payload = {\r\n fromPoiId,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_BETWEEN_POIS', payload, timeout);\r\n }\r\n\r\n // ==================== 状态和视图控制 ====================\r\n getState(): Promise {\r\n return this._postCommandAsync('GET_STATE');\r\n }\r\n\r\n /** 重置视角 */\r\n resetView(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('RESET_VIEW', {}, timeout);\r\n }\r\n\r\n /** 设置 2D/3D 视图模式 */\r\n setViewMode(mode: '2d' | '3d', timeout?: number): Promise<{ success: boolean; mode: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode: string }>('SET_VIEW_MODE', { mode }, timeout);\r\n }\r\n\r\n /** 切换图层可见性 */\r\n toggleLayer(types: string[], visible: boolean, timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('TOGGLE_LAYER', { types, visible }, timeout);\r\n }\r\n\r\n /** 设置可见图层类型列表 */\r\n setVisibleLayers(types: string[], timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('SET_VISIBLE_TYPES', { types }, timeout);\r\n }\r\n\r\n /** 设置路线展示视图是否活跃 */\r\n setRouteViewActive(active: boolean, timeout?: number): Promise<{ success: boolean; active: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; active: boolean }>('SET_ROUTE_VIEW_ACTIVE', { active }, timeout);\r\n }\r\n\r\n // ==================== v2.2.0 / v2.2.0 数据消费 API ====================\r\n\r\n /** 获取地图 Manifest 清单 */\r\n getManifest(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_MANIFEST', {}, timeout);\r\n }\r\n\r\n /** 加载指定楼层的完整 Bundle 数据(含 POI/空间面/讲解点/路网) */\r\n loadFloorBundle(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('LOAD_FLOOR_BUNDLE', { floorId }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的 POI 列表 */\r\n getFloorPois(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_POIS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取指定楼层的起终点候选目的地列表(门点/POI/空间等吸附后目的地) */\r\n getNavigablePlaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_NAVIGABLE_PLACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.navigablePlaces || res?.places || []));\r\n }\r\n\r\n /** 获取当前楼层的 POI 列表(根据 floorCode 参数) */\r\n getPois(floorCode: string, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_POIS', { floorCode }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的空间面列表 */\r\n getSpaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_SPACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.spaces || []));\r\n }\r\n\r\n /** 获取指定楼层的已标定讲解点列表(地图渲染入口) */\r\n getGuideStops(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 获取指定展厅的已标定讲解点列表(业务导览入口) */\r\n getGuideStopsByHall(hallId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS_BY_HALL', { hallId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 预加载指定楼层的模型资源(不渲染) */\r\n preloadFloor(floorId: string | number, timeout?: number): Promise<{ cached: boolean }> {\r\n return this._postCommandAsync<{ cached: boolean }>('PRELOAD_FLOOR', { floorId }, timeout);\r\n }\r\n\r\n /** 获取性能统计信息 */\r\n getPerformanceStats(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_PERFORMANCE_STATS', {}, timeout).then(stats => ({\r\n sdkCreatedAt: this._sdkCreatedAt,\r\n iframeDomReadyMs: this._iframeDomReadyMs ?? undefined,\r\n engineReadyMs: this._engineReadyMs ?? undefined,\r\n ...stats\r\n }));\r\n }\r\n\r\n // ==================== v2.3.0 诊断接口 ====================\r\n\r\n /** 获取地图级诊断信息 */\r\n getDiagnostics(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_DIAGNOSTICS', {}, timeout);\r\n }\r\n\r\n /** 获取楼层级诊断信息 */\r\n getFloorDiagnostics(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_DIAGNOSTICS', { floorId }, timeout);\r\n }\r\n\r\n /** 清空客户端模型缓存 */\r\n clearModelCache(): void {\r\n this._postCommandSafe('CLEAR_MODEL_CACHE');\r\n }\r\n\r\n // ==================== v2.4.0 新增 API ====================\r\n\r\n /** 获取指定楼层的业务 POI 列表 */\r\n getBusinessPois(floorId: string | number, options?: { types?: string[]; keyword?: string; limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_BUSINESS_POIS', { floorId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 查询 POI 列表 */\r\n queryPois(params: SgsPoiQueryParams, timeout?: number): Promise {\r\n return this._postCommandAsync('QUERY_POIS', params, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取特色路线列表 */\r\n getFeaturedRoutes(mapId?: string | number, options?: { limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTES', { mapId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.routes || []));\r\n }\r\n\r\n /** 获取特色路线详情 */\r\n getFeaturedRoute(routeId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTE', { routeId }, timeout)\r\n .then((res) => res?.route || res);\r\n }\r\n\r\n /** 生成并展示精品路线预览 */\r\n planFeaturedRoutePreview(\r\n payload: SgsFeaturedRoutePreviewPayload,\r\n timeout: number = 15000\r\n ): Promise {\r\n return this._postCommandAsync('PLAN_FEATURED_ROUTE_PREVIEW', payload, timeout);\r\n }\r\n\r\n /** 显示动线 */\r\n showFlowline(routeId: string | number, options?: SgsFlowlineOptions, timeout?: number): Promise<{ success: boolean; routeId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; routeId: string | number }>('SHOW_FLOWLINE', { routeId, options }, timeout);\r\n }\r\n\r\n /** 清除动线 */\r\n clearFlowline(): void {\r\n this._postCommandSafe('CLEAR_FLOWLINE');\r\n }\r\n\r\n // ==================== @deprecated 兼容层 ====================\r\n // 以下方法已废弃,保留 API 签名避免外部接入方断裂。\r\n // 内部返回 ERR_UNSUPPORTED,不进入任何发布路径。\r\n\r\n /** @deprecated 使用 getManifest() 替代 */\r\n getReleaseManifest(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getReleaseManifest() is deprecated. Use getManifest() instead.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getModelResources(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getModelResources() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getModelResources() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getLayerTree(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getLayerTree() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getLayerTree() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n loadMockReleaseRoute(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] loadMockReleaseRoute() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'loadMockReleaseRoute() is deprecated.' });\r\n }\r\n\r\n // ==================== 销毁 ====================\r\n\r\n /**\r\n * 销毁 SDK 实例\r\n *\r\n *

清理 iframe、移除事件监听器、reject 所有挂起的 Promise。\r\n *

销毁后调用任何命令方法都会 reject ERR_DESTROYED。\r\n */\r\n destroy() {\r\n if (this._isDestroyed) return; // 防重复销毁\r\n this._isDestroyed = true;\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // reject ready Promise(如果还没 resolve/reject)\r\n if (this._readyReject) {\r\n this._readyReject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed before ready.'\r\n });\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // reject 所有挂起的命令\r\n this._rejectAllPending('ERR_DESTROYED', 'SGSMapSDK has been destroyed. Pending promises were rejected.');\r\n\r\n // 移除 iframe\r\n if (this.iframe) {\r\n if (this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n this.iframe = null;\r\n }\r\n\r\n // 移除事件监听器\r\n if (this._messageListener) {\r\n window.removeEventListener('message', this._messageListener);\r\n this._messageListener = null;\r\n }\r\n\r\n // 移除所有 EventEmitter 监听器\r\n this.removeAllListeners();\r\n\r\n this._isReady = false;\r\n console.log('[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues.');\r\n }\r\n}\r\n\r\n// 全局暴露(IIFE 产物使用)\r\nif (typeof window !== 'undefined') {\r\n (window as any).SGSMapSDK = SGSMapSDK;\r\n}\r\n"],"mappings":"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","names":["EventEmitter","event","listener","l","wrapper","payload","data","e","utils_exports","__export","isIndoorNavigableFloor","sortIndoorFloors","floorCode","floors","FLOOR_ORDER","indoorFloors","f","getFloorIndex","code","matchedIndex","numMatch","val","a","b","SDK_VERSION","PROTOCOL_VERSION","DEFAULT_READY_TIMEOUT","DEFAULT_CMD_TIMEOUT","SGSMapSDK","EventEmitter","options","containerEl","resolve","reject","iframe","iframeUrl","value","key","event","data","action","payload","requestId","error","resolver","errObj","sendHello","err","fireAndForget","cmd","pending","total","code","message","timeout","timer","node","floorId","res","config","markerId","from","to","startNode","endNode","pathPoints","toPoiId","fromPoiId","mode","types","visible","active","floorCode","hallId","stats","params","mapId","routeId","utils_exports"]} \ No newline at end of file +{"version":3,"sources":["../../sdk-src/event-emitter.ts","../../sdk-src/utils.ts","../../sdk-src/index.ts"],"sourcesContent":["/**\r\n * 轻量级类型安全事件发射器\r\n *\r\n *

SDK 内部事件系统基类,提供 on/off/once/emit/removeAllListeners/listenerCount。\r\n */\r\nexport class EventEmitter> {\r\n private events: { [K in keyof Events]?: Array<(payload: Events[K]) => void> };\r\n\r\n constructor() {\r\n this.events = {};\r\n }\r\n\r\n /** 注册事件监听器 */\r\n on(event: K, listener: (payload: Events[K]) => void): void {\r\n if (!this.events[event]) {\r\n this.events[event] = [];\r\n }\r\n this.events[event]!.push(listener);\r\n }\r\n\r\n /** 移除指定事件的指定监听器 */\r\n off(event: K, listener: (payload: Events[K]) => void): void {\r\n if (this.events[event]) {\r\n this.events[event] = this.events[event]!.filter(l => l !== listener);\r\n }\r\n }\r\n\r\n /** 注册一次性监听器,触发后自动移除 */\r\n once(event: K, listener: (payload: Events[K]) => void): void {\r\n const wrapper = (payload: Events[K]) => {\r\n this.off(event, wrapper);\r\n listener(payload);\r\n };\r\n this.on(event, wrapper);\r\n }\r\n\r\n /** 触发事件,通知所有已注册的监听器 */\r\n emit(event: K, data: Events[K]): void {\r\n if (this.events[event]) {\r\n // 浅拷贝防止 once 触发时修改数组导致跳过\r\n const listeners = [...this.events[event]!];\r\n listeners.forEach(l => {\r\n try {\r\n l(data);\r\n } catch (e) {\r\n console.error(`[EventEmitter] Error emitting event ${String(event)}:`, e);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** 移除指定事件的全部监听器;不传参则移除所有事件的所有监听器 */\r\n removeAllListeners(event?: K): void {\r\n if (event !== undefined) {\r\n delete this.events[event];\r\n } else {\r\n this.events = {};\r\n }\r\n }\r\n\r\n /** 返回指定事件的监听器数量 */\r\n listenerCount(event: K): number {\r\n return this.events[event]?.length ?? 0;\r\n }\r\n}\r\n","/**\r\n * 过滤 EXTERIOR 楼层,检查楼层是否是室内可导航楼层\r\n */\r\nexport function isIndoorNavigableFloor(floorCode: string): boolean {\r\n if (!floorCode) return false;\r\n return floorCode.toUpperCase() !== 'EXTERIOR';\r\n}\r\n\r\n/**\r\n * 物理楼层顺序由低到高排序\r\n */\r\nexport function sortIndoorFloors(floors: T[]): T[] {\r\n const FLOOR_ORDER = [\"L-2\", \"L-1\", \"B2\", \"B1\", \"L1\", \"L1.5\", \"L2\", \"L3\", \"L4\", \"L5\"];\r\n \r\n const indoorFloors = floors.filter(f => isIndoorNavigableFloor(f.floorCode));\r\n\r\n const getFloorIndex = (floorCode: string): number => {\r\n let code = floorCode.trim().toUpperCase();\r\n if (code.startsWith('F')) {\r\n code = 'L' + code.substring(1);\r\n }\r\n const matchedIndex = FLOOR_ORDER.findIndex(f => f.toUpperCase() === code);\r\n if (matchedIndex !== -1) return matchedIndex;\r\n \r\n const numMatch = code.match(/-?\\d+(\\.\\d+)?/);\r\n if (numMatch) {\r\n const val = parseFloat(numMatch[0]);\r\n if (code.startsWith('B') || code.includes('-')) {\r\n return -Math.abs(val) - 100;\r\n }\r\n return val;\r\n }\r\n return 0;\r\n };\r\n\r\n return [...indoorFloors].sort((a, b) => getFloorIndex(a.floorCode) - getFloorIndex(b.floorCode));\r\n}\r\n","import { EventEmitter } from './event-emitter';\r\nimport {\r\n SGSMapSDKOptions,\r\n SGSMapEvents,\r\n RouteResult,\r\n SgsMapNode,\r\n MarkerConfig,\r\n SgsSdkManifest,\r\n SgsFloorBundle,\r\n SgsPoi,\r\n SgsSpaceArea,\r\n SgsGuideStop,\r\n SgsPerformanceStats,\r\n SgsNavigablePlace,\r\n SgsMapDiagnostics,\r\n SgsFloorDiagnostics,\r\n SgsPoiQueryParams,\r\n SgsFeaturedRouteSummary,\r\n SgsFeaturedRouteDetail,\r\n SgsFeaturedRoutePreviewPayload,\r\n SgsFlowlineOptions,\r\n SgsRouteRoamingOptions,\r\n RouteOptions,\r\n SdkRoutePoint,\r\n} from './types';\r\n\r\nexport * from './types';\r\n\r\nimport * as utils from './utils';\r\n\r\n/** SDK 版本号常量 — 与后端 SdkMapServiceImpl.SDK_VERSION 对齐 */\r\nconst SDK_VERSION = '2.5.0';\n/** 协议版本号 */\r\nconst PROTOCOL_VERSION = 2;\r\n/** 默认 ready 超时(ms) */\r\nconst DEFAULT_READY_TIMEOUT = 30000; // 🔧 P0: 从 15s 提升到 30s,配合延迟 ENGINE_READY 策略\r\n/** 默认命令超时(ms) */\r\nconst DEFAULT_CMD_TIMEOUT = 5000;\r\n\r\n/**\r\n * 命令队列条目 — ready 前调用的异步命令暂存于此\r\n */\r\ninterface PendingCommand {\r\n action: string;\r\n payload?: any;\r\n timeout: number;\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n}\r\n\r\n/**\r\n * SGS 3D 地图 H5 SDK 入口类\r\n *\r\n *

通过 iframe 嵌入独立发布的 /engine/index.html 渲染引擎,以 postMessage 进行双向通信。\n *

所有需要后端数据的操作均通过 postMessage 指令由底座代理,\r\n * Demo / 宿主不允许直连后端 API。\r\n *\r\n * @version 2.5.0\n */\r\nexport default class SGSMapSDK extends EventEmitter {\r\n public static utils = utils;\r\n private container: HTMLElement;\r\n private sdkUrl: string;\r\n private floorId: string | number;\r\n private mapId: string | number;\r\n private targetOrigin: string;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private _isReady: boolean = false;\r\n private _isDestroyed: boolean = false;\r\n /** ready 失败终态:iframe 加载失败或 ready 超时后置 true,后续命令直接 reject */\r\n private _readyFailed: boolean = false;\r\n /** ready 失败时的错误对象,用于后续命令的立即 reject */\r\n private _readyFailedError: { code: string; message: string } | null = null;\r\n private defaultTimeout: number;\r\n private readyTimeoutMs: number;\r\n /** 已 ready 后正在等待响应的命令队列 */\r\n private promiseQueue: {\r\n [key: string]: {\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n };\r\n } = {};\r\n /** ready 前暂存的异步命令队列 */\r\n private _pendingCommands: PendingCommand[] = [];\r\n /** ready 前暂存的 fire-and-forget 命令队列 */\r\n private _pendingFireAndForget: Array<{ action: string; payload?: any }> = [];\r\n private _messageListener: ((event: MessageEvent) => void) | null = null;\r\n /** ready 超时计时器 */\r\n private _readyTimer: ReturnType | null = null;\r\n /** 🔧 P0: HELLO 重试定时器 — 解决 iframe onload 与 React useEffect 时序竞争 */\r\n private _helloInterval: ReturnType | null = null;\r\n\r\n /** ENGINE_READY 返回的 payload(供外部读取) */\r\n public readyPayload: any = null;\r\n\r\n /** ready Promise 的 resolve/reject,保证 whenReady() 返回同一个 Promise */\r\n private _readyResolve: ((value: any) => void) | null = null;\r\n private _readyReject: ((reason: any) => void) | null = null;\r\n private _readyPromise: Promise;\r\n\r\n /** 性能追踪字段 */\r\n private _sdkCreatedAt: number;\r\n private _iframeDomReadyMs: number | null = null;\r\n private _engineReadyMs: number | null = null;\r\n\r\n constructor(options: SGSMapSDKOptions) {\r\n super();\r\n this._sdkCreatedAt = Date.now();\r\n\r\n const containerEl =\r\n typeof options.container === 'string'\r\n ? document.getElementById(options.container)\r\n : options.container;\r\n\r\n if (!containerEl) {\r\n throw new Error(`[SGSMapSDK] Cannot find container: ${options.container}`);\r\n }\r\n\r\n this.container = containerEl;\r\n this.sdkUrl = options.sdkUrl || '/engine/index.html';\n this.floorId = options.floorId || 1;\r\n this.mapId = options.mapId || 1;\r\n this.defaultTimeout = options.timeout || DEFAULT_CMD_TIMEOUT;\r\n this.readyTimeoutMs = options.readyTimeout || DEFAULT_READY_TIMEOUT;\r\n\r\n this.targetOrigin = options.targetOrigin as string;\r\n if (!this.targetOrigin) {\r\n if (this.sdkUrl.startsWith('http')) {\r\n this.targetOrigin = new URL(this.sdkUrl).origin;\r\n } else if (typeof window !== 'undefined') {\r\n this.targetOrigin = window.location.origin;\r\n } else {\r\n this.targetOrigin = '*';\r\n }\r\n }\r\n\r\n // 初始化 ready Promise(全局唯一,whenReady() 返回同一个)\r\n this._readyPromise = new Promise((resolve, reject) => {\r\n this._readyResolve = resolve;\r\n this._readyReject = reject;\r\n });\r\n\r\n this._initIframeBridge();\r\n }\r\n\r\n // ==================== iframe 桥接初始化 ====================\r\n\r\n private _initIframeBridge() {\r\n const iframe = document.createElement('iframe');\r\n\r\n // 拷贝父级 URL 上的查询参数传递给 iframe 底座\r\n const iframeUrl = new URL(\r\n this.sdkUrl,\r\n typeof window !== 'undefined' ? window.location.href : 'http://localhost'\r\n );\r\n if (typeof window !== 'undefined') {\r\n const parentParams = new URLSearchParams(window.location.search);\r\n parentParams.forEach((value, key) => {\r\n iframeUrl.searchParams.set(key, value);\r\n });\r\n }\r\n // 附带 floorId\r\n if (!iframeUrl.searchParams.has('floorId')) {\r\n iframeUrl.searchParams.set('floorId', String(this.floorId));\r\n }\r\n\r\n iframe.src = iframeUrl.toString();\r\n iframe.style.cssText = 'width:100%;height:100%;border:none;background:transparent;';\r\n\r\n this.container.innerHTML = '';\r\n this.container.appendChild(iframe);\r\n this.iframe = iframe;\r\n\r\n // ── 消息监听 ──\r\n this._messageListener = (event: MessageEvent) => {\r\n if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {\r\n return; // 静默忽略不匹配的来源\r\n }\r\n\r\n if (!this.iframe || event.source !== this.iframe.contentWindow) {\r\n return;\r\n }\r\n\r\n const data = event.data;\r\n if (!data) return;\r\n\r\n // ── 事件类型消息 ──\r\n if (data.type === 'SGS_MAP_EVENT') {\r\n const { action, payload } = data;\r\n if (action === 'ON_POI_CLICK') {\r\n this.emit('poiClick', payload);\r\n } else if (action === 'ON_POI_DETAIL') {\r\n this.emit('poiDetail', payload);\r\n } else if (action === 'ON_POI_NAVIGATE') {\r\n this.emit('poiNavigate', payload);\r\n } else if (action === 'ON_POI_CLOSE') {\r\n this.emit('poiClose', payload);\r\n } else if (action === 'ON_SPACE_CLICK') {\r\n this.emit('spaceClick', payload);\r\n } else if (action === 'ON_SPACE_DETAIL') {\r\n this.emit('spaceDetail', payload);\r\n } else if (action === 'ON_SPACE_NAVIGATE') {\r\n this.emit('spaceNavigate', payload);\r\n } else if (action === 'ON_SPACE_CLOSE') {\r\n this.emit('spaceClose', payload);\r\n } else if (action === 'ENGINE_READY') {\r\n this._onEngineReady(payload);\r\n } else if (action === 'ON_FLOOR_CHANGED') {\r\n this.emit('floorChanged', payload);\r\n } else if (action === 'ON_ROUTE_TRANSFER') {\r\n this.emit('routeTransfer', payload);\r\n } else if (action === 'ON_MODEL_LOADING') {\r\n this.emit('modelLoading', payload);\r\n } else if (action === 'ON_MODEL_READY') {\r\n this.emit('modelReady', payload);\r\n } else if (action === 'ON_MODEL_ERROR') {\r\n this.emit('modelError', payload);\r\n } else if (action === 'ON_FLOOR_BUNDLE_READY') {\r\n this.emit('floorBundleReady', payload);\r\n } else if (action === 'ON_ROUTE_READY') {\r\n this.emit('routeReady', payload);\r\n } else if (action === 'ON_ROAMING_PROGRESS') {\r\n this.emit('roamingProgress', payload);\r\n } else if (action === 'ON_ROAMING_DONE') {\r\n this.emit('roamingDone', payload);\r\n }\r\n }\r\n\r\n // ── 命令响应消息 ──\r\n if (data.type === 'SGS_MAP_RESPONSE') {\r\n const { action, requestId, payload, error } = data;\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n if (payload && payload.success !== false) {\r\n resolver.resolve(payload);\r\n } else {\r\n const errObj = error || { code: 'ERR_FAILED', message: `${action} command failed` };\r\n resolver.reject(errObj);\r\n }\r\n delete this.promiseQueue[requestId];\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener('message', this._messageListener);\r\n\r\n // ── iframe 加载完成 → 发送 HELLO 握手 ──\r\n iframe.onload = () => {\r\n this._iframeDomReadyMs = Date.now() - this._sdkCreatedAt;\r\n if (this._isReady) return;\r\n\r\n // 🔧 P0 修复:HELLO 重试机制\r\n // iframe onload 触发时 React useEffect 可能尚未挂载 message listener,\r\n // 单次 HELLO 会丢失。改为每 500ms 重试,直到 ENGINE_READY 到达。\r\n const sendHello = () => {\r\n if (this._isReady || this._isDestroyed) {\r\n if (this._helloInterval) { clearInterval(this._helloInterval); this._helloInterval = null; }\r\n return;\r\n }\r\n this._postCommand('HELLO', { protocolVersion: PROTOCOL_VERSION, sdkVersion: SDK_VERSION });\r\n };\r\n sendHello(); // 立即发第一次\r\n this._helloInterval = setInterval(sendHello, 500);\r\n console.log(`[SGSMapSDK] Sent HELLO handshake (v${SDK_VERSION}), will retry every 500ms until ENGINE_READY...`);\r\n };\r\n\r\n // ── iframe 加载失败 → reject ready + 清空命令队列 ──\r\n iframe.onerror = (err) => {\r\n console.error('[SGSMapSDK] Iframe load error', err);\r\n const error = { code: 'ERR_IFRAME_LOAD' as const, message: 'Failed to load map engine iframe.' };\r\n this.emit('loadError', { type: 'iframe_error', detail: error.message });\r\n this._rejectReady(error);\r\n };\r\n\r\n // ── ready 超时计时器 ──\r\n this._readyTimer = setTimeout(() => {\r\n if (!this._isReady && !this._isDestroyed) {\r\n const error = {\r\n code: 'ERR_READY_TIMEOUT' as const,\r\n message: `ENGINE_READY not received within ${this.readyTimeoutMs}ms`\r\n };\r\n console.error(`[SGSMapSDK] ${error.message}`);\r\n this._rejectReady(error);\r\n }\r\n }, this.readyTimeoutMs);\r\n }\r\n\r\n // ==================== ready 生命周期管理 ====================\r\n\r\n /**\r\n * ENGINE_READY 到达时的处理\r\n */\r\n private _onEngineReady(payload: any) {\r\n if (this._isReady) return; // 防重入\r\n\r\n this._isReady = true;\r\n this.readyPayload = payload;\r\n this._engineReadyMs = Date.now() - this._sdkCreatedAt;\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // resolve ready Promise\r\n if (this._readyResolve) {\r\n this._readyResolve(payload);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 触发 ready 事件\r\n this.emit('ready', payload);\r\n console.log('[SGSMapSDK] Handshake complete: Engine Ready received ✅');\r\n\r\n // 排空命令队列 — ready 前暂存的命令逐个发送\r\n this._flushPendingCommands();\r\n }\r\n\r\n /**\r\n * reject ready Promise 并清空所有挂起命令,设置失败终态\r\n */\r\n private _rejectReady(error: { code: string; message: string }) {\r\n // 设置失败终态 — 后续所有新命令直接 reject\r\n this._readyFailed = true;\r\n this._readyFailedError = error;\r\n\r\n // reject ready Promise\r\n if (this._readyReject) {\r\n this._readyReject(error);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 清空 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // reject 所有命令队列 + 清空 fire-and-forget 队列\r\n this._rejectAllPending(error.code, error.message);\r\n this._pendingFireAndForget.splice(0);\r\n }\r\n\r\n /**\r\n * 排空 ready 前暂存的命令队列 — 逐个以 _postCommandAsync 发送\r\n */\r\n private _flushPendingCommands() {\r\n // 1. 排空 fire-and-forget 队列\r\n const fireAndForget = this._pendingFireAndForget.splice(0);\r\n for (const cmd of fireAndForget) {\r\n this._postCommand(cmd.action, cmd.payload);\r\n }\r\n\r\n // 2. 排空异步命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n this._postCommandAsyncInner(cmd.action, cmd.payload, cmd.timeout)\r\n .then(cmd.resolve)\r\n .catch(cmd.reject);\r\n }\r\n\r\n const total = fireAndForget.length + pending.length;\r\n if (total > 0) {\r\n console.log(`[SGSMapSDK] Flushed ${total} pending commands after ready (${fireAndForget.length} fire-and-forget, ${pending.length} async)`);\r\n }\r\n }\r\n\r\n /**\r\n * reject 所有挂起的 Promise 和命令队列\r\n */\r\n private _rejectAllPending(code: string, message: string) {\r\n // 1. reject 已发送的 promiseQueue\r\n Object.keys(this.promiseQueue).forEach((requestId) => {\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n resolver.reject({ code, message });\r\n }\r\n delete this.promiseQueue[requestId];\r\n });\r\n\r\n // 2. reject 命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n cmd.reject({ code, message });\r\n }\r\n }\r\n\r\n // ==================== 公共生命周期方法 ====================\r\n\r\n /**\r\n * 返回 ready Promise(全局唯一,多次调用返回同一个)。\r\n * 别名:ready()\r\n *\r\n * @example\r\n * await sdk.whenReady();\r\n * sdk.changeFloor(floorId);\r\n */\r\n whenReady(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /**\r\n * whenReady 的别名\r\n */\r\n ready(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /** 获取 ready 状态 */\r\n getIsReady(): boolean {\r\n return this._isReady;\r\n }\r\n\r\n /** 获取当前楼层 ID */\r\n getCurrentFloor(): string | number {\r\n return this.floorId;\r\n }\r\n\r\n /** 获取当前 mapId */\r\n getMapId(): string | number {\r\n return this.mapId;\r\n }\r\n\r\n /** 获取 SDK 版本号 */\r\n getVersion(): string {\r\n return SDK_VERSION;\r\n }\r\n\r\n // ==================== 核心通信方法 ====================\r\n\r\n private _generateRequestId(): string {\r\n return `req_sdk_${Date.now()}_${Math.floor(Math.random() * 100000)}`;\r\n }\r\n\r\n private _postCommand(action: string, payload?: any, requestId?: string | null) {\r\n if (!this.iframe || !this.iframe.contentWindow) {\r\n console.error('[SGSMapSDK] SDK Iframe is not available!');\r\n return;\r\n }\r\n\r\n this.iframe.contentWindow.postMessage(\r\n {\r\n type: 'SGS_MAP_COMMAND',\r\n action,\r\n requestId,\r\n payload\r\n },\r\n this.targetOrigin\r\n );\r\n }\r\n\r\n /**\r\n * 异步命令发送(外部入口)— 支持命令队列\r\n *\r\n *

状态机优先级:destroyed > readyFailed > !ready(入队) > ready(发送)\r\n */\r\n private _postCommandAsync(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n // destroyed 后直接 reject\r\n if (this._isDestroyed) {\r\n return Promise.reject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed. Cannot send commands.'\r\n });\r\n }\r\n\r\n // ready 失败终态 — 立即 reject,不再入队\r\n if (this._readyFailed) {\r\n return Promise.reject(\r\n this._readyFailedError || { code: 'ERR_NOT_READY', message: 'SDK ready failed.' }\r\n );\r\n }\r\n\r\n // ready 前入队\r\n if (!this._isReady) {\r\n return new Promise((resolve, reject) => {\r\n this._pendingCommands.push({\r\n action,\r\n payload,\r\n timeout,\r\n resolve,\r\n reject\r\n });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`);\r\n });\r\n }\r\n\r\n // ready 后直接发送\r\n return this._postCommandAsyncInner(action, payload, timeout);\r\n }\r\n\r\n /**\r\n * fire-and-forget 命令发送(void 方法统一入口)\r\n *\r\n *

受 destroyed/readyFailed/!ready 状态保护:\r\n * - destroyed/readyFailed:静默丢弃 + console.warn\r\n * - !ready:暂存到 _pendingFireAndForget,ready 后自动发送\r\n * - ready:立即发送\r\n */\r\n private _postCommandSafe(action: string, payload?: any): void {\r\n if (this._isDestroyed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK destroyed.`);\r\n return;\r\n }\r\n if (this._readyFailed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK ready failed.`);\r\n return;\r\n }\r\n if (!this._isReady) {\r\n this._pendingFireAndForget.push({ action, payload });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);\r\n return;\r\n }\r\n this._postCommand(action, payload);\r\n }\r\n\r\n /**\r\n * 异步命令发送(内部实现)— 已确认 ready\r\n */\r\n private _postCommandAsyncInner(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n const requestId = this._generateRequestId();\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n if (this.promiseQueue[requestId]) {\r\n this.promiseQueue[requestId].reject({\r\n code: 'ERR_TIMEOUT',\r\n message: `Command \"${action}\" timed out after ${timeout}ms`\r\n });\r\n delete this.promiseQueue[requestId];\r\n }\r\n }, timeout);\r\n\r\n this.promiseQueue[requestId] = {\r\n resolve: (data) => {\r\n clearTimeout(timer);\r\n resolve(data as T);\r\n },\r\n reject: (error) => {\r\n clearTimeout(timer);\r\n reject(error);\r\n }\r\n };\r\n\r\n this._postCommand(action, payload, requestId);\r\n });\r\n }\r\n\r\n // ==================== v2.0 核心指令 API ====================\r\n\r\n /** 聚焦到指定要素节点 */\r\n focusTo(node: SgsMapNode | string) {\r\n if (typeof node === 'string') {\r\n this._postCommandSafe('FOCUS_TO', { nodeName: node });\r\n } else {\r\n this._postCommandSafe('FOCUS_TO', node);\r\n }\r\n }\r\n\r\n /** 切换楼层 */\r\n changeFloor(floorId: string | number, timeout?: number, options?: { forceReload?: boolean }): Promise<{ success: boolean; floorId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; floorId: string | number }>('CHANGE_FLOOR', { floorId, ...options }, timeout).then((res) => {\r\n this.floorId = floorId;\r\n return res;\r\n });\r\n }\r\n\r\n /** 添加业务图钉 */\r\n addMarker(config: MarkerConfig) {\r\n this._postCommandSafe('ADD_MARKER', config);\r\n }\r\n\r\n /** 移除指定图钉 */\r\n removeMarker(markerId: string) {\r\n this._postCommandSafe('REMOVE_MARKER', { markerId });\r\n }\r\n\r\n /** 清空所有业务图钉 */\r\n clearMarkers() {\r\n this._postCommandSafe('CLEAR_MARKERS');\r\n }\r\n\r\n /** 高亮多边形轮廓 */\r\n highlightPolygon(payload: string | object) {\r\n if (typeof payload === 'string') {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', { nodeName: payload });\r\n } else {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', payload);\r\n }\r\n }\r\n\r\n /** 寻路规划(v1 兼容) */\r\n planRoute(from: SgsMapNode | string, to: SgsMapNode | string, options?: any, timeout: number = 5000): Promise {\r\n const startNode = typeof from === 'string' ? from : undefined;\r\n const endNode = typeof to === 'string' ? to : undefined;\r\n\r\n const payload: any = {};\r\n if (startNode) payload.startNode = startNode;\r\n else payload.from = from;\r\n\r\n if (endNode) payload.endNode = endNode;\r\n else payload.to = to;\r\n\r\n if (options) payload.options = options;\r\n\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 寻路规划 V2(结构化参数) */\r\n planRouteV2(from: SgsMapNode, to: SgsMapNode, options?: any, timeout: number = 5000): Promise {\r\n const payload = { from, to, options };\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 清除当前路线 */\r\n clearRoute() {\r\n this._postCommandSafe('CLEAR_ROUTE');\r\n }\r\n\r\n /** 渲染已有路线点,供外部宿主直接展示自定义路线 */\r\n renderRoutePoints(\r\n pathPoints: Array<{ x: number; y: number; z: number }>,\r\n options?: any,\r\n timeout: number = 12000\r\n ): Promise {\r\n return this._postCommandAsync('RENDER_ROUTE_POINTS', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 启动路线漫游;pathPoints 为空时,基座会优先使用当前已渲染路线 */\r\n startRoaming(\r\n pathPoints: Array<{ x: number; y: number; z: number }> = [],\r\n options?: SgsRouteRoamingOptions,\r\n timeout: number = 12000\r\n ): Promise<{ success: boolean; mode?: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode?: string }>('START_ROAMING', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 停止路线漫游 */\r\n stopRoaming(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('STOP_ROAMING', {}, timeout);\r\n }\r\n\r\n /** 暂停或继续路线漫游 */\r\n toggleRoamingPause(timeout?: number): Promise<{ success: boolean; paused?: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; paused?: boolean }>('TOGGLE_ROAMING_PAUSE', {}, timeout);\r\n }\r\n\r\n // ==================== SDK 2.4.1 高层导航 API ====================\r\n\r\n /**\r\n * 导航至POI(SDK 2.4.1)\r\n *\r\n * 支持从坐标或POI导航到目标POI,后端自动查询POI坐标。\r\n *\r\n * @param from 起点(POI ID字符串/数字 或 坐标对象)\r\n * @param toPoiId 目标POI ID(建议使用string避免Snowflake精度丢失)\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteToPoi(from: string | number | SdkRoutePoint, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n // 将 string/number 转为 { poiId: from }\r\n const fromPoint: SdkRoutePoint = (typeof from === 'string' || typeof from === 'number')\r\n ? { poiId: from }\r\n : from;\r\n\r\n const payload = {\r\n from: fromPoint,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_TO_POI', payload, timeout);\r\n }\r\n\r\n /**\r\n * POI间导航(SDK 2.4.1)\r\n *\r\n * 两个POI之间的导航,后端自动查询POI坐标。\r\n *\r\n * @param fromPoiId 起点POI ID\r\n * @param toPoiId 终点POI ID\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteBetweenPois(fromPoiId: string | number, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n const payload = {\r\n fromPoiId,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_BETWEEN_POIS', payload, timeout);\r\n }\r\n\r\n // ==================== 状态和视图控制 ====================\r\n getState(): Promise {\r\n return this._postCommandAsync('GET_STATE');\r\n }\r\n\r\n /** 重置视角 */\r\n resetView(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('RESET_VIEW', {}, timeout);\r\n }\r\n\r\n /** 设置 2D/3D 视图模式 */\r\n setViewMode(mode: '2d' | '3d', timeout?: number): Promise<{ success: boolean; mode: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode: string }>('SET_VIEW_MODE', { mode }, timeout);\r\n }\r\n\r\n /** 切换图层可见性 */\r\n toggleLayer(types: string[], visible: boolean, timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('TOGGLE_LAYER', { types, visible }, timeout);\r\n }\r\n\r\n /** 设置可见图层类型列表 */\r\n setVisibleLayers(types: string[], timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('SET_VISIBLE_TYPES', { types }, timeout);\r\n }\r\n\r\n /** 设置路线展示视图是否活跃 */\r\n setRouteViewActive(active: boolean, timeout?: number): Promise<{ success: boolean; active: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; active: boolean }>('SET_ROUTE_VIEW_ACTIVE', { active }, timeout);\r\n }\r\n\r\n // ==================== v2.2.0 / v2.2.0 数据消费 API ====================\r\n\r\n /** 获取地图 Manifest 清单 */\r\n getManifest(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_MANIFEST', {}, timeout);\r\n }\r\n\r\n /** 加载指定楼层的完整 Bundle 数据(含 POI/空间面/讲解点/路网) */\r\n loadFloorBundle(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('LOAD_FLOOR_BUNDLE', { floorId }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的 POI 列表 */\r\n getFloorPois(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_POIS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取指定楼层的起终点候选目的地列表(门点/POI/空间等吸附后目的地) */\r\n getNavigablePlaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_NAVIGABLE_PLACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.navigablePlaces || res?.places || []));\r\n }\r\n\r\n /** 获取当前楼层的 POI 列表(根据 floorCode 参数) */\r\n getPois(floorCode: string, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_POIS', { floorCode }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的空间面列表 */\r\n getSpaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_SPACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.spaces || []));\r\n }\r\n\r\n /** 获取指定楼层的已标定讲解点列表(地图渲染入口) */\r\n getGuideStops(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 获取指定展厅的已标定讲解点列表(业务导览入口) */\r\n getGuideStopsByHall(hallId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS_BY_HALL', { hallId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 预加载指定楼层的模型资源(不渲染) */\r\n preloadFloor(floorId: string | number, timeout?: number): Promise<{ cached: boolean }> {\r\n return this._postCommandAsync<{ cached: boolean }>('PRELOAD_FLOOR', { floorId }, timeout);\r\n }\r\n\r\n /** 获取性能统计信息 */\r\n getPerformanceStats(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_PERFORMANCE_STATS', {}, timeout).then(stats => ({\r\n sdkCreatedAt: this._sdkCreatedAt,\r\n iframeDomReadyMs: this._iframeDomReadyMs ?? undefined,\r\n engineReadyMs: this._engineReadyMs ?? undefined,\r\n ...stats\r\n }));\r\n }\r\n\r\n // ==================== v2.3.0 诊断接口 ====================\r\n\r\n /** 获取地图级诊断信息 */\r\n getDiagnostics(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_DIAGNOSTICS', {}, timeout);\r\n }\r\n\r\n /** 获取楼层级诊断信息 */\r\n getFloorDiagnostics(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_DIAGNOSTICS', { floorId }, timeout);\r\n }\r\n\r\n /** 清空客户端模型缓存 */\r\n clearModelCache(): void {\r\n this._postCommandSafe('CLEAR_MODEL_CACHE');\r\n }\r\n\r\n // ==================== v2.4.0 新增 API ====================\r\n\r\n /** 获取指定楼层的业务 POI 列表 */\r\n getBusinessPois(floorId: string | number, options?: { types?: string[]; keyword?: string; limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_BUSINESS_POIS', { floorId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 查询 POI 列表 */\r\n queryPois(params: SgsPoiQueryParams, timeout?: number): Promise {\r\n return this._postCommandAsync('QUERY_POIS', params, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取特色路线列表 */\r\n getFeaturedRoutes(mapId?: string | number, options?: { limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTES', { mapId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.routes || []));\r\n }\r\n\r\n /** 获取特色路线详情 */\r\n getFeaturedRoute(routeId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTE', { routeId }, timeout)\r\n .then((res) => res?.route || res);\r\n }\r\n\r\n /** 生成并展示精品路线预览 */\r\n planFeaturedRoutePreview(\r\n payload: SgsFeaturedRoutePreviewPayload,\r\n timeout: number = 15000\r\n ): Promise {\r\n return this._postCommandAsync('PLAN_FEATURED_ROUTE_PREVIEW', payload, timeout);\r\n }\r\n\r\n /** 显示动线 */\r\n showFlowline(routeId: string | number, options?: SgsFlowlineOptions, timeout?: number): Promise<{ success: boolean; routeId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; routeId: string | number }>('SHOW_FLOWLINE', { routeId, options }, timeout);\r\n }\r\n\r\n /** 清除动线 */\r\n clearFlowline(): void {\r\n this._postCommandSafe('CLEAR_FLOWLINE');\r\n }\r\n\r\n // ==================== @deprecated 兼容层 ====================\r\n // 以下方法已废弃,保留 API 签名避免外部接入方断裂。\r\n // 内部返回 ERR_UNSUPPORTED,不进入任何发布路径。\r\n\r\n /** @deprecated 使用 getManifest() 替代 */\r\n getReleaseManifest(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getReleaseManifest() is deprecated. Use getManifest() instead.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getModelResources(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getModelResources() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getModelResources() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getLayerTree(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getLayerTree() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getLayerTree() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n loadMockReleaseRoute(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] loadMockReleaseRoute() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'loadMockReleaseRoute() is deprecated.' });\r\n }\r\n\r\n // ==================== 销毁 ====================\r\n\r\n /**\r\n * 销毁 SDK 实例\r\n *\r\n *

清理 iframe、移除事件监听器、reject 所有挂起的 Promise。\r\n *

销毁后调用任何命令方法都会 reject ERR_DESTROYED。\r\n */\r\n destroy() {\r\n if (this._isDestroyed) return; // 防重复销毁\r\n this._isDestroyed = true;\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // reject ready Promise(如果还没 resolve/reject)\r\n if (this._readyReject) {\r\n this._readyReject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed before ready.'\r\n });\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // reject 所有挂起的命令\r\n this._rejectAllPending('ERR_DESTROYED', 'SGSMapSDK has been destroyed. Pending promises were rejected.');\r\n\r\n // 移除 iframe\r\n if (this.iframe) {\r\n if (this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n this.iframe = null;\r\n }\r\n\r\n // 移除事件监听器\r\n if (this._messageListener) {\r\n window.removeEventListener('message', this._messageListener);\r\n this._messageListener = null;\r\n }\r\n\r\n // 移除所有 EventEmitter 监听器\r\n this.removeAllListeners();\r\n\r\n this._isReady = false;\r\n console.log('[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues.');\r\n }\r\n}\r\n\r\n// 全局暴露(IIFE 产物使用)\r\nif (typeof window !== 'undefined') {\r\n (window as any).SGSMapSDK = SGSMapSDK;\r\n}\r\n"],"mappings":"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","names":["EventEmitter","event","listener","l","wrapper","payload","data","e","utils_exports","__export","isIndoorNavigableFloor","sortIndoorFloors","floorCode","floors","FLOOR_ORDER","indoorFloors","f","getFloorIndex","code","matchedIndex","numMatch","val","a","b","SDK_VERSION","PROTOCOL_VERSION","DEFAULT_READY_TIMEOUT","DEFAULT_CMD_TIMEOUT","SGSMapSDK","EventEmitter","options","containerEl","resolve","reject","iframe","iframeUrl","value","key","event","data","action","payload","requestId","error","resolver","errObj","sendHello","err","fireAndForget","cmd","pending","total","code","message","timeout","timer","node","floorId","res","config","markerId","from","to","startNode","endNode","pathPoints","toPoiId","fromPoiId","mode","types","visible","active","floorCode","hallId","stats","params","mapId","routeId","utils_exports"]} \ No newline at end of file diff --git a/static/sgs-map-sdk/index.mjs b/static/sgs-map-sdk/index.mjs index 983f540..25faaa1 100644 --- a/static/sgs-map-sdk/index.mjs +++ b/static/sgs-map-sdk/index.mjs @@ -1,2 +1,2 @@ -var y=Object.defineProperty;var f=(d,o)=>{for(var e in o)y(d,e,{get:o[e],enumerable:!0})};var u=class{constructor(){this.events={}}on(o,e){this.events[o]||(this.events[o]=[]),this.events[o].push(e)}off(o,e){this.events[o]&&(this.events[o]=this.events[o].filter(s=>s!==e))}once(o,e){let s=t=>{this.off(o,s),e(t)};this.on(o,s)}emit(o,e){this.events[o]&&[...this.events[o]].forEach(t=>{try{t(e)}catch(r){console.error(`[EventEmitter] Error emitting event ${String(o)}:`,r)}})}removeAllListeners(o){o!==void 0?delete this.events[o]:this.events={}}listenerCount(o){return this.events[o]?.length??0}};var h={};f(h,{isIndoorNavigableFloor:()=>_,sortIndoorFloors:()=>R});function _(d){return d?d.toUpperCase()!=="EXTERIOR":!1}function R(d){let o=["L-2","L-1","B2","B1","L1","L1.5","L2","L3","L4","L5"],e=d.filter(t=>_(t.floorCode)),s=t=>{let r=t.trim().toUpperCase();r.startsWith("F")&&(r="L"+r.substring(1));let i=o.findIndex(a=>a.toUpperCase()===r);if(i!==-1)return i;let n=r.match(/-?\d+(\.\d+)?/);if(n){let a=parseFloat(n[0]);return r.startsWith("B")||r.includes("-")?-Math.abs(a)-100:a}return 0};return[...e].sort((t,r)=>s(t.floorCode)-s(r.floorCode))}var g="2.4.1",S=2,E=3e4,P=5e3,l=class extends u{constructor(e){super();this.iframe=null;this._isReady=!1;this._isDestroyed=!1;this._readyFailed=!1;this._readyFailedError=null;this.promiseQueue={};this._pendingCommands=[];this._pendingFireAndForget=[];this._messageListener=null;this._readyTimer=null;this._helloInterval=null;this.readyPayload=null;this._readyResolve=null;this._readyReject=null;this._iframeDomReadyMs=null;this._engineReadyMs=null;this._sdkCreatedAt=Date.now();let s=typeof e.container=="string"?document.getElementById(e.container):e.container;if(!s)throw new Error(`[SGSMapSDK] Cannot find container: ${e.container}`);this.container=s,this.sdkUrl=e.sdkUrl||"/h5-sdk",this.floorId=e.floorId||1,this.mapId=e.mapId||1,this.defaultTimeout=e.timeout||P,this.readyTimeoutMs=e.readyTimeout||E,this.targetOrigin=e.targetOrigin,this.targetOrigin||(this.sdkUrl.startsWith("http")?this.targetOrigin=new URL(this.sdkUrl).origin:typeof window<"u"?this.targetOrigin=window.location.origin:this.targetOrigin="*"),this._readyPromise=new Promise((t,r)=>{this._readyResolve=t,this._readyReject=r}),this._initIframeBridge()}_initIframeBridge(){let e=document.createElement("iframe"),s=new URL(this.sdkUrl,typeof window<"u"?window.location.href:"http://localhost");typeof window<"u"&&new URLSearchParams(window.location.search).forEach((r,i)=>{s.searchParams.set(i,r)}),s.searchParams.has("floorId")||s.searchParams.set("floorId",String(this.floorId)),e.src=s.toString(),e.style.cssText="width:100%;height:100%;border:none;background:transparent;",this.container.innerHTML="",this.container.appendChild(e),this.iframe=e,this._messageListener=t=>{if(this.targetOrigin!=="*"&&t.origin!==this.targetOrigin||!this.iframe||t.source!==this.iframe.contentWindow)return;let r=t.data;if(r){if(r.type==="SGS_MAP_EVENT"){let{action:i,payload:n}=r;i==="ON_POI_CLICK"?this.emit("poiClick",n):i==="ON_POI_DETAIL"?this.emit("poiDetail",n):i==="ON_POI_NAVIGATE"?this.emit("poiNavigate",n):i==="ON_POI_CLOSE"?this.emit("poiClose",n):i==="ON_SPACE_CLICK"?this.emit("spaceClick",n):i==="ON_SPACE_DETAIL"?this.emit("spaceDetail",n):i==="ON_SPACE_NAVIGATE"?this.emit("spaceNavigate",n):i==="ON_SPACE_CLOSE"?this.emit("spaceClose",n):i==="ENGINE_READY"?this._onEngineReady(n):i==="ON_FLOOR_CHANGED"?this.emit("floorChanged",n):i==="ON_ROUTE_TRANSFER"?this.emit("routeTransfer",n):i==="ON_MODEL_LOADING"?this.emit("modelLoading",n):i==="ON_MODEL_READY"?this.emit("modelReady",n):i==="ON_MODEL_ERROR"?this.emit("modelError",n):i==="ON_FLOOR_BUNDLE_READY"?this.emit("floorBundleReady",n):i==="ON_ROUTE_READY"?this.emit("routeReady",n):i==="ON_ROAMING_PROGRESS"?this.emit("roamingProgress",n):i==="ON_ROAMING_DONE"&&this.emit("roamingDone",n)}if(r.type==="SGS_MAP_RESPONSE"){let{action:i,requestId:n,payload:a,error:m}=r,c=this.promiseQueue[n];if(c){if(a&&a.success!==!1)c.resolve(a);else{let p=m||{code:"ERR_FAILED",message:`${i} command failed`};c.reject(p)}delete this.promiseQueue[n]}}}},window.addEventListener("message",this._messageListener),e.onload=()=>{if(this._iframeDomReadyMs=Date.now()-this._sdkCreatedAt,this._isReady)return;let t=()=>{if(this._isReady||this._isDestroyed){this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null);return}this._postCommand("HELLO",{protocolVersion:S,sdkVersion:g})};t(),this._helloInterval=setInterval(t,500),console.log(`[SGSMapSDK] Sent HELLO handshake (v${g}), will retry every 500ms until ENGINE_READY...`)},e.onerror=t=>{console.error("[SGSMapSDK] Iframe load error",t);let r={code:"ERR_IFRAME_LOAD",message:"Failed to load map engine iframe."};this.emit("loadError",{type:"iframe_error",detail:r.message}),this._rejectReady(r)},this._readyTimer=setTimeout(()=>{if(!this._isReady&&!this._isDestroyed){let t={code:"ERR_READY_TIMEOUT",message:`ENGINE_READY not received within ${this.readyTimeoutMs}ms`};console.error(`[SGSMapSDK] ${t.message}`),this._rejectReady(t)}},this.readyTimeoutMs)}_onEngineReady(e){this._isReady||(this._isReady=!0,this.readyPayload=e,this._engineReadyMs=Date.now()-this._sdkCreatedAt,this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._readyResolve&&(this._readyResolve(e),this._readyResolve=null,this._readyReject=null),this.emit("ready",e),console.log("[SGSMapSDK] Handshake complete: Engine Ready received \u2705"),this._flushPendingCommands())}_rejectReady(e){this._readyFailed=!0,this._readyFailedError=e,this._readyReject&&(this._readyReject(e),this._readyResolve=null,this._readyReject=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._rejectAllPending(e.code,e.message),this._pendingFireAndForget.splice(0)}_flushPendingCommands(){let e=this._pendingFireAndForget.splice(0);for(let r of e)this._postCommand(r.action,r.payload);let s=this._pendingCommands.splice(0);for(let r of s)this._postCommandAsyncInner(r.action,r.payload,r.timeout).then(r.resolve).catch(r.reject);let t=e.length+s.length;t>0&&console.log(`[SGSMapSDK] Flushed ${t} pending commands after ready (${e.length} fire-and-forget, ${s.length} async)`)}_rejectAllPending(e,s){Object.keys(this.promiseQueue).forEach(r=>{let i=this.promiseQueue[r];i&&i.reject({code:e,message:s}),delete this.promiseQueue[r]});let t=this._pendingCommands.splice(0);for(let r of t)r.reject({code:e,message:s})}whenReady(){return this._readyPromise}ready(){return this._readyPromise}getIsReady(){return this._isReady}getCurrentFloor(){return this.floorId}getMapId(){return this.mapId}getVersion(){return g}_generateRequestId(){return`req_sdk_${Date.now()}_${Math.floor(Math.random()*1e5)}`}_postCommand(e,s,t){if(!this.iframe||!this.iframe.contentWindow){console.error("[SGSMapSDK] SDK Iframe is not available!");return}this.iframe.contentWindow.postMessage({type:"SGS_MAP_COMMAND",action:e,requestId:t,payload:s},this.targetOrigin)}_postCommandAsync(e,s,t=this.defaultTimeout){return this._isDestroyed?Promise.reject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed. Cannot send commands."}):this._readyFailed?Promise.reject(this._readyFailedError||{code:"ERR_NOT_READY",message:"SDK ready failed."}):this._isReady?this._postCommandAsyncInner(e,s,t):new Promise((r,i)=>{this._pendingCommands.push({action:e,payload:s,timeout:t,resolve:r,reject:i}),console.log(`[SGSMapSDK] Command "${e}" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`)})}_postCommandSafe(e,s){if(this._isDestroyed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK destroyed.`);return}if(this._readyFailed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK ready failed.`);return}if(!this._isReady){this._pendingFireAndForget.push({action:e,payload:s}),console.log(`[SGSMapSDK] Command "${e}" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);return}this._postCommand(e,s)}_postCommandAsyncInner(e,s,t=this.defaultTimeout){let r=this._generateRequestId();return new Promise((i,n)=>{let a=setTimeout(()=>{this.promiseQueue[r]&&(this.promiseQueue[r].reject({code:"ERR_TIMEOUT",message:`Command "${e}" timed out after ${t}ms`}),delete this.promiseQueue[r])},t);this.promiseQueue[r]={resolve:m=>{clearTimeout(a),i(m)},reject:m=>{clearTimeout(a),n(m)}},this._postCommand(e,s,r)})}focusTo(e){typeof e=="string"?this._postCommandSafe("FOCUS_TO",{nodeName:e}):this._postCommandSafe("FOCUS_TO",e)}changeFloor(e,s,t){return this._postCommandAsync("CHANGE_FLOOR",{floorId:e,...t},s).then(r=>(this.floorId=e,r))}addMarker(e){this._postCommandSafe("ADD_MARKER",e)}removeMarker(e){this._postCommandSafe("REMOVE_MARKER",{markerId:e})}clearMarkers(){this._postCommandSafe("CLEAR_MARKERS")}highlightPolygon(e){typeof e=="string"?this._postCommandSafe("HIGHLIGHT_POLYGON",{nodeName:e}):this._postCommandSafe("HIGHLIGHT_POLYGON",e)}planRoute(e,s,t,r=5e3){let i=typeof e=="string"?e:void 0,n=typeof s=="string"?s:void 0,a={};return i?a.startNode=i:a.from=e,n?a.endNode=n:a.to=s,t&&(a.options=t),this._postCommandAsync("PLAN_ROUTE",a,r)}planRouteV2(e,s,t,r=5e3){let i={from:e,to:s,options:t};return this._postCommandAsync("PLAN_ROUTE",i,r)}clearRoute(){this._postCommandSafe("CLEAR_ROUTE")}renderRoutePoints(e,s,t=12e3){return this._postCommandAsync("RENDER_ROUTE_POINTS",{...s,pathPoints:e},t)}startRoaming(e=[],s,t=12e3){return this._postCommandAsync("START_ROAMING",{...s,pathPoints:e},t)}stopRoaming(e){return this._postCommandAsync("STOP_ROAMING",{},e)}toggleRoamingPause(e){return this._postCommandAsync("TOGGLE_ROAMING_PAUSE",{},e)}planRouteToPoi(e,s,t,r=5e3){let n={from:typeof e=="string"||typeof e=="number"?{poiId:e}:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_TO_POI",n,r)}planRouteBetweenPois(e,s,t,r=5e3){let i={fromPoiId:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_BETWEEN_POIS",i,r)}getState(){return this._postCommandAsync("GET_STATE")}resetView(e){return this._postCommandAsync("RESET_VIEW",{},e)}setViewMode(e,s){return this._postCommandAsync("SET_VIEW_MODE",{mode:e},s)}toggleLayer(e,s,t){return this._postCommandAsync("TOGGLE_LAYER",{types:e,visible:s},t)}setVisibleLayers(e,s){return this._postCommandAsync("SET_VISIBLE_TYPES",{types:e},s)}setRouteViewActive(e,s){return this._postCommandAsync("SET_ROUTE_VIEW_ACTIVE",{active:e},s)}getManifest(e){return this._postCommandAsync("GET_MANIFEST",{},e)}loadFloorBundle(e,s){return this._postCommandAsync("LOAD_FLOOR_BUNDLE",{floorId:e},s)}getFloorPois(e,s){return this._postCommandAsync("GET_FLOOR_POIS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.pois||[])}getNavigablePlaces(e,s){return this._postCommandAsync("GET_NAVIGABLE_PLACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.navigablePlaces||t?.places||[])}getPois(e,s){return this._postCommandAsync("GET_POIS",{floorCode:e},s)}getSpaces(e,s){return this._postCommandAsync("GET_SPACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.spaces||[])}getGuideStops(e,s){return this._postCommandAsync("GET_GUIDE_STOPS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}getGuideStopsByHall(e,s){return this._postCommandAsync("GET_GUIDE_STOPS_BY_HALL",{hallId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}preloadFloor(e,s){return this._postCommandAsync("PRELOAD_FLOOR",{floorId:e},s)}getPerformanceStats(e){return this._postCommandAsync("GET_PERFORMANCE_STATS",{},e).then(s=>({sdkCreatedAt:this._sdkCreatedAt,iframeDomReadyMs:this._iframeDomReadyMs??void 0,engineReadyMs:this._engineReadyMs??void 0,...s}))}getDiagnostics(e){return this._postCommandAsync("GET_DIAGNOSTICS",{},e)}getFloorDiagnostics(e,s){return this._postCommandAsync("GET_FLOOR_DIAGNOSTICS",{floorId:e},s)}clearModelCache(){this._postCommandSafe("CLEAR_MODEL_CACHE")}getBusinessPois(e,s,t){return this._postCommandAsync("GET_BUSINESS_POIS",{floorId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.pois||[])}queryPois(e,s){return this._postCommandAsync("QUERY_POIS",e,s).then(t=>Array.isArray(t)?t:t?.pois||[])}getFeaturedRoutes(e,s,t){return this._postCommandAsync("GET_FEATURED_ROUTES",{mapId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.routes||[])}getFeaturedRoute(e,s){return this._postCommandAsync("GET_FEATURED_ROUTE",{routeId:e},s).then(t=>t?.route||t)}planFeaturedRoutePreview(e,s=15e3){return this._postCommandAsync("PLAN_FEATURED_ROUTE_PREVIEW",e,s)}showFlowline(e,s,t){return this._postCommandAsync("SHOW_FLOWLINE",{routeId:e,options:s},t)}clearFlowline(){this._postCommandSafe("CLEAR_FLOWLINE")}getReleaseManifest(e){return console.warn("[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getReleaseManifest() is deprecated. Use getManifest() instead."})}getModelResources(e){return console.warn("[SGSMapSDK] getModelResources() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getModelResources() is deprecated."})}getLayerTree(e){return console.warn("[SGSMapSDK] getLayerTree() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getLayerTree() is deprecated."})}loadMockReleaseRoute(e){return console.warn("[SGSMapSDK] loadMockReleaseRoute() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"loadMockReleaseRoute() is deprecated."})}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyReject&&(this._readyReject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed before ready."}),this._readyResolve=null,this._readyReject=null),this._rejectAllPending("ERR_DESTROYED","SGSMapSDK has been destroyed. Pending promises were rejected."),this.iframe&&(this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this._messageListener&&(window.removeEventListener("message",this._messageListener),this._messageListener=null),this.removeAllListeners(),this._isReady=!1,console.log("[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues."))}};l.utils=h;typeof window<"u"&&(window.SGSMapSDK=l);export{l as default}; +var y=Object.defineProperty;var f=(d,o)=>{for(var e in o)y(d,e,{get:o[e],enumerable:!0})};var u=class{constructor(){this.events={}}on(o,e){this.events[o]||(this.events[o]=[]),this.events[o].push(e)}off(o,e){this.events[o]&&(this.events[o]=this.events[o].filter(s=>s!==e))}once(o,e){let s=t=>{this.off(o,s),e(t)};this.on(o,s)}emit(o,e){this.events[o]&&[...this.events[o]].forEach(t=>{try{t(e)}catch(r){console.error(`[EventEmitter] Error emitting event ${String(o)}:`,r)}})}removeAllListeners(o){o!==void 0?delete this.events[o]:this.events={}}listenerCount(o){return this.events[o]?.length??0}};var h={};f(h,{isIndoorNavigableFloor:()=>_,sortIndoorFloors:()=>R});function _(d){return d?d.toUpperCase()!=="EXTERIOR":!1}function R(d){let o=["L-2","L-1","B2","B1","L1","L1.5","L2","L3","L4","L5"],e=d.filter(t=>_(t.floorCode)),s=t=>{let r=t.trim().toUpperCase();r.startsWith("F")&&(r="L"+r.substring(1));let i=o.findIndex(a=>a.toUpperCase()===r);if(i!==-1)return i;let n=r.match(/-?\d+(\.\d+)?/);if(n){let a=parseFloat(n[0]);return r.startsWith("B")||r.includes("-")?-Math.abs(a)-100:a}return 0};return[...e].sort((t,r)=>s(t.floorCode)-s(r.floorCode))}var g="2.5.0",S=2,E=3e4,P=5e3,l=class extends u{constructor(e){super();this.iframe=null;this._isReady=!1;this._isDestroyed=!1;this._readyFailed=!1;this._readyFailedError=null;this.promiseQueue={};this._pendingCommands=[];this._pendingFireAndForget=[];this._messageListener=null;this._readyTimer=null;this._helloInterval=null;this.readyPayload=null;this._readyResolve=null;this._readyReject=null;this._iframeDomReadyMs=null;this._engineReadyMs=null;this._sdkCreatedAt=Date.now();let s=typeof e.container=="string"?document.getElementById(e.container):e.container;if(!s)throw new Error(`[SGSMapSDK] Cannot find container: ${e.container}`);this.container=s,this.sdkUrl=e.sdkUrl||"/engine/index.html",this.floorId=e.floorId||1,this.mapId=e.mapId||1,this.defaultTimeout=e.timeout||P,this.readyTimeoutMs=e.readyTimeout||E,this.targetOrigin=e.targetOrigin,this.targetOrigin||(this.sdkUrl.startsWith("http")?this.targetOrigin=new URL(this.sdkUrl).origin:typeof window<"u"?this.targetOrigin=window.location.origin:this.targetOrigin="*"),this._readyPromise=new Promise((t,r)=>{this._readyResolve=t,this._readyReject=r}),this._initIframeBridge()}_initIframeBridge(){let e=document.createElement("iframe"),s=new URL(this.sdkUrl,typeof window<"u"?window.location.href:"http://localhost");typeof window<"u"&&new URLSearchParams(window.location.search).forEach((r,i)=>{s.searchParams.set(i,r)}),s.searchParams.has("floorId")||s.searchParams.set("floorId",String(this.floorId)),e.src=s.toString(),e.style.cssText="width:100%;height:100%;border:none;background:transparent;",this.container.innerHTML="",this.container.appendChild(e),this.iframe=e,this._messageListener=t=>{if(this.targetOrigin!=="*"&&t.origin!==this.targetOrigin||!this.iframe||t.source!==this.iframe.contentWindow)return;let r=t.data;if(r){if(r.type==="SGS_MAP_EVENT"){let{action:i,payload:n}=r;i==="ON_POI_CLICK"?this.emit("poiClick",n):i==="ON_POI_DETAIL"?this.emit("poiDetail",n):i==="ON_POI_NAVIGATE"?this.emit("poiNavigate",n):i==="ON_POI_CLOSE"?this.emit("poiClose",n):i==="ON_SPACE_CLICK"?this.emit("spaceClick",n):i==="ON_SPACE_DETAIL"?this.emit("spaceDetail",n):i==="ON_SPACE_NAVIGATE"?this.emit("spaceNavigate",n):i==="ON_SPACE_CLOSE"?this.emit("spaceClose",n):i==="ENGINE_READY"?this._onEngineReady(n):i==="ON_FLOOR_CHANGED"?this.emit("floorChanged",n):i==="ON_ROUTE_TRANSFER"?this.emit("routeTransfer",n):i==="ON_MODEL_LOADING"?this.emit("modelLoading",n):i==="ON_MODEL_READY"?this.emit("modelReady",n):i==="ON_MODEL_ERROR"?this.emit("modelError",n):i==="ON_FLOOR_BUNDLE_READY"?this.emit("floorBundleReady",n):i==="ON_ROUTE_READY"?this.emit("routeReady",n):i==="ON_ROAMING_PROGRESS"?this.emit("roamingProgress",n):i==="ON_ROAMING_DONE"&&this.emit("roamingDone",n)}if(r.type==="SGS_MAP_RESPONSE"){let{action:i,requestId:n,payload:a,error:m}=r,c=this.promiseQueue[n];if(c){if(a&&a.success!==!1)c.resolve(a);else{let p=m||{code:"ERR_FAILED",message:`${i} command failed`};c.reject(p)}delete this.promiseQueue[n]}}}},window.addEventListener("message",this._messageListener),e.onload=()=>{if(this._iframeDomReadyMs=Date.now()-this._sdkCreatedAt,this._isReady)return;let t=()=>{if(this._isReady||this._isDestroyed){this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null);return}this._postCommand("HELLO",{protocolVersion:S,sdkVersion:g})};t(),this._helloInterval=setInterval(t,500),console.log(`[SGSMapSDK] Sent HELLO handshake (v${g}), will retry every 500ms until ENGINE_READY...`)},e.onerror=t=>{console.error("[SGSMapSDK] Iframe load error",t);let r={code:"ERR_IFRAME_LOAD",message:"Failed to load map engine iframe."};this.emit("loadError",{type:"iframe_error",detail:r.message}),this._rejectReady(r)},this._readyTimer=setTimeout(()=>{if(!this._isReady&&!this._isDestroyed){let t={code:"ERR_READY_TIMEOUT",message:`ENGINE_READY not received within ${this.readyTimeoutMs}ms`};console.error(`[SGSMapSDK] ${t.message}`),this._rejectReady(t)}},this.readyTimeoutMs)}_onEngineReady(e){this._isReady||(this._isReady=!0,this.readyPayload=e,this._engineReadyMs=Date.now()-this._sdkCreatedAt,this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._readyResolve&&(this._readyResolve(e),this._readyResolve=null,this._readyReject=null),this.emit("ready",e),console.log("[SGSMapSDK] Handshake complete: Engine Ready received \u2705"),this._flushPendingCommands())}_rejectReady(e){this._readyFailed=!0,this._readyFailedError=e,this._readyReject&&(this._readyReject(e),this._readyResolve=null,this._readyReject=null),this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._rejectAllPending(e.code,e.message),this._pendingFireAndForget.splice(0)}_flushPendingCommands(){let e=this._pendingFireAndForget.splice(0);for(let r of e)this._postCommand(r.action,r.payload);let s=this._pendingCommands.splice(0);for(let r of s)this._postCommandAsyncInner(r.action,r.payload,r.timeout).then(r.resolve).catch(r.reject);let t=e.length+s.length;t>0&&console.log(`[SGSMapSDK] Flushed ${t} pending commands after ready (${e.length} fire-and-forget, ${s.length} async)`)}_rejectAllPending(e,s){Object.keys(this.promiseQueue).forEach(r=>{let i=this.promiseQueue[r];i&&i.reject({code:e,message:s}),delete this.promiseQueue[r]});let t=this._pendingCommands.splice(0);for(let r of t)r.reject({code:e,message:s})}whenReady(){return this._readyPromise}ready(){return this._readyPromise}getIsReady(){return this._isReady}getCurrentFloor(){return this.floorId}getMapId(){return this.mapId}getVersion(){return g}_generateRequestId(){return`req_sdk_${Date.now()}_${Math.floor(Math.random()*1e5)}`}_postCommand(e,s,t){if(!this.iframe||!this.iframe.contentWindow){console.error("[SGSMapSDK] SDK Iframe is not available!");return}this.iframe.contentWindow.postMessage({type:"SGS_MAP_COMMAND",action:e,requestId:t,payload:s},this.targetOrigin)}_postCommandAsync(e,s,t=this.defaultTimeout){return this._isDestroyed?Promise.reject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed. Cannot send commands."}):this._readyFailed?Promise.reject(this._readyFailedError||{code:"ERR_NOT_READY",message:"SDK ready failed."}):this._isReady?this._postCommandAsyncInner(e,s,t):new Promise((r,i)=>{this._pendingCommands.push({action:e,payload:s,timeout:t,resolve:r,reject:i}),console.log(`[SGSMapSDK] Command "${e}" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`)})}_postCommandSafe(e,s){if(this._isDestroyed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK destroyed.`);return}if(this._readyFailed){console.warn(`[SGSMapSDK] Command "${e}" ignored: SDK ready failed.`);return}if(!this._isReady){this._pendingFireAndForget.push({action:e,payload:s}),console.log(`[SGSMapSDK] Command "${e}" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);return}this._postCommand(e,s)}_postCommandAsyncInner(e,s,t=this.defaultTimeout){let r=this._generateRequestId();return new Promise((i,n)=>{let a=setTimeout(()=>{this.promiseQueue[r]&&(this.promiseQueue[r].reject({code:"ERR_TIMEOUT",message:`Command "${e}" timed out after ${t}ms`}),delete this.promiseQueue[r])},t);this.promiseQueue[r]={resolve:m=>{clearTimeout(a),i(m)},reject:m=>{clearTimeout(a),n(m)}},this._postCommand(e,s,r)})}focusTo(e){typeof e=="string"?this._postCommandSafe("FOCUS_TO",{nodeName:e}):this._postCommandSafe("FOCUS_TO",e)}changeFloor(e,s,t){return this._postCommandAsync("CHANGE_FLOOR",{floorId:e,...t},s).then(r=>(this.floorId=e,r))}addMarker(e){this._postCommandSafe("ADD_MARKER",e)}removeMarker(e){this._postCommandSafe("REMOVE_MARKER",{markerId:e})}clearMarkers(){this._postCommandSafe("CLEAR_MARKERS")}highlightPolygon(e){typeof e=="string"?this._postCommandSafe("HIGHLIGHT_POLYGON",{nodeName:e}):this._postCommandSafe("HIGHLIGHT_POLYGON",e)}planRoute(e,s,t,r=5e3){let i=typeof e=="string"?e:void 0,n=typeof s=="string"?s:void 0,a={};return i?a.startNode=i:a.from=e,n?a.endNode=n:a.to=s,t&&(a.options=t),this._postCommandAsync("PLAN_ROUTE",a,r)}planRouteV2(e,s,t,r=5e3){let i={from:e,to:s,options:t};return this._postCommandAsync("PLAN_ROUTE",i,r)}clearRoute(){this._postCommandSafe("CLEAR_ROUTE")}renderRoutePoints(e,s,t=12e3){return this._postCommandAsync("RENDER_ROUTE_POINTS",{...s,pathPoints:e},t)}startRoaming(e=[],s,t=12e3){return this._postCommandAsync("START_ROAMING",{...s,pathPoints:e},t)}stopRoaming(e){return this._postCommandAsync("STOP_ROAMING",{},e)}toggleRoamingPause(e){return this._postCommandAsync("TOGGLE_ROAMING_PAUSE",{},e)}planRouteToPoi(e,s,t,r=5e3){let n={from:typeof e=="string"||typeof e=="number"?{poiId:e}:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_TO_POI",n,r)}planRouteBetweenPois(e,s,t,r=5e3){let i={fromPoiId:e,toPoiId:s,wheelchair:t?.wheelchair?1:0,preferElevator:t?.preferElevator};return this._postCommandAsync("PLAN_ROUTE_BETWEEN_POIS",i,r)}getState(){return this._postCommandAsync("GET_STATE")}resetView(e){return this._postCommandAsync("RESET_VIEW",{},e)}setViewMode(e,s){return this._postCommandAsync("SET_VIEW_MODE",{mode:e},s)}toggleLayer(e,s,t){return this._postCommandAsync("TOGGLE_LAYER",{types:e,visible:s},t)}setVisibleLayers(e,s){return this._postCommandAsync("SET_VISIBLE_TYPES",{types:e},s)}setRouteViewActive(e,s){return this._postCommandAsync("SET_ROUTE_VIEW_ACTIVE",{active:e},s)}getManifest(e){return this._postCommandAsync("GET_MANIFEST",{},e)}loadFloorBundle(e,s){return this._postCommandAsync("LOAD_FLOOR_BUNDLE",{floorId:e},s)}getFloorPois(e,s){return this._postCommandAsync("GET_FLOOR_POIS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.pois||[])}getNavigablePlaces(e,s){return this._postCommandAsync("GET_NAVIGABLE_PLACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.navigablePlaces||t?.places||[])}getPois(e,s){return this._postCommandAsync("GET_POIS",{floorCode:e},s)}getSpaces(e,s){return this._postCommandAsync("GET_SPACES",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.spaces||[])}getGuideStops(e,s){return this._postCommandAsync("GET_GUIDE_STOPS",{floorId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}getGuideStopsByHall(e,s){return this._postCommandAsync("GET_GUIDE_STOPS_BY_HALL",{hallId:e},s).then(t=>Array.isArray(t)?t:t?.guideStops||[])}preloadFloor(e,s){return this._postCommandAsync("PRELOAD_FLOOR",{floorId:e},s)}getPerformanceStats(e){return this._postCommandAsync("GET_PERFORMANCE_STATS",{},e).then(s=>({sdkCreatedAt:this._sdkCreatedAt,iframeDomReadyMs:this._iframeDomReadyMs??void 0,engineReadyMs:this._engineReadyMs??void 0,...s}))}getDiagnostics(e){return this._postCommandAsync("GET_DIAGNOSTICS",{},e)}getFloorDiagnostics(e,s){return this._postCommandAsync("GET_FLOOR_DIAGNOSTICS",{floorId:e},s)}clearModelCache(){this._postCommandSafe("CLEAR_MODEL_CACHE")}getBusinessPois(e,s,t){return this._postCommandAsync("GET_BUSINESS_POIS",{floorId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.pois||[])}queryPois(e,s){return this._postCommandAsync("QUERY_POIS",e,s).then(t=>Array.isArray(t)?t:t?.pois||[])}getFeaturedRoutes(e,s,t){return this._postCommandAsync("GET_FEATURED_ROUTES",{mapId:e,options:s},t).then(r=>Array.isArray(r)?r:r?.routes||[])}getFeaturedRoute(e,s){return this._postCommandAsync("GET_FEATURED_ROUTE",{routeId:e},s).then(t=>t?.route||t)}planFeaturedRoutePreview(e,s=15e3){return this._postCommandAsync("PLAN_FEATURED_ROUTE_PREVIEW",e,s)}showFlowline(e,s,t){return this._postCommandAsync("SHOW_FLOWLINE",{routeId:e,options:s},t)}clearFlowline(){this._postCommandSafe("CLEAR_FLOWLINE")}getReleaseManifest(e){return console.warn("[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getReleaseManifest() is deprecated. Use getManifest() instead."})}getModelResources(e){return console.warn("[SGSMapSDK] getModelResources() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getModelResources() is deprecated."})}getLayerTree(e){return console.warn("[SGSMapSDK] getLayerTree() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"getLayerTree() is deprecated."})}loadMockReleaseRoute(e){return console.warn("[SGSMapSDK] loadMockReleaseRoute() is deprecated."),Promise.reject({code:"ERR_UNSUPPORTED",message:"loadMockReleaseRoute() is deprecated."})}destroy(){this._isDestroyed||(this._isDestroyed=!0,this._readyTimer&&(clearTimeout(this._readyTimer),this._readyTimer=null),this._helloInterval&&(clearInterval(this._helloInterval),this._helloInterval=null),this._readyReject&&(this._readyReject({code:"ERR_DESTROYED",message:"SGSMapSDK has been destroyed before ready."}),this._readyResolve=null,this._readyReject=null),this._rejectAllPending("ERR_DESTROYED","SGSMapSDK has been destroyed. Pending promises were rejected."),this.iframe&&(this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this._messageListener&&(window.removeEventListener("message",this._messageListener),this._messageListener=null),this.removeAllListeners(),this._isReady=!1,console.log("[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues."))}};l.utils=h;typeof window<"u"&&(window.SGSMapSDK=l);export{l as default}; //# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/static/sgs-map-sdk/index.mjs.map b/static/sgs-map-sdk/index.mjs.map index 784f3ab..7e37f3e 100644 --- a/static/sgs-map-sdk/index.mjs.map +++ b/static/sgs-map-sdk/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../../sdk-src/event-emitter.ts","../../sdk-src/utils.ts","../../sdk-src/index.ts"],"sourcesContent":["/**\r\n * 轻量级类型安全事件发射器\r\n *\r\n *

SDK 内部事件系统基类,提供 on/off/once/emit/removeAllListeners/listenerCount。\r\n */\r\nexport class EventEmitter> {\r\n private events: { [K in keyof Events]?: Array<(payload: Events[K]) => void> };\r\n\r\n constructor() {\r\n this.events = {};\r\n }\r\n\r\n /** 注册事件监听器 */\r\n on(event: K, listener: (payload: Events[K]) => void): void {\r\n if (!this.events[event]) {\r\n this.events[event] = [];\r\n }\r\n this.events[event]!.push(listener);\r\n }\r\n\r\n /** 移除指定事件的指定监听器 */\r\n off(event: K, listener: (payload: Events[K]) => void): void {\r\n if (this.events[event]) {\r\n this.events[event] = this.events[event]!.filter(l => l !== listener);\r\n }\r\n }\r\n\r\n /** 注册一次性监听器,触发后自动移除 */\r\n once(event: K, listener: (payload: Events[K]) => void): void {\r\n const wrapper = (payload: Events[K]) => {\r\n this.off(event, wrapper);\r\n listener(payload);\r\n };\r\n this.on(event, wrapper);\r\n }\r\n\r\n /** 触发事件,通知所有已注册的监听器 */\r\n emit(event: K, data: Events[K]): void {\r\n if (this.events[event]) {\r\n // 浅拷贝防止 once 触发时修改数组导致跳过\r\n const listeners = [...this.events[event]!];\r\n listeners.forEach(l => {\r\n try {\r\n l(data);\r\n } catch (e) {\r\n console.error(`[EventEmitter] Error emitting event ${String(event)}:`, e);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** 移除指定事件的全部监听器;不传参则移除所有事件的所有监听器 */\r\n removeAllListeners(event?: K): void {\r\n if (event !== undefined) {\r\n delete this.events[event];\r\n } else {\r\n this.events = {};\r\n }\r\n }\r\n\r\n /** 返回指定事件的监听器数量 */\r\n listenerCount(event: K): number {\r\n return this.events[event]?.length ?? 0;\r\n }\r\n}\r\n","/**\r\n * 过滤 EXTERIOR 楼层,检查楼层是否是室内可导航楼层\r\n */\r\nexport function isIndoorNavigableFloor(floorCode: string): boolean {\r\n if (!floorCode) return false;\r\n return floorCode.toUpperCase() !== 'EXTERIOR';\r\n}\r\n\r\n/**\r\n * 物理楼层顺序由低到高排序\r\n */\r\nexport function sortIndoorFloors(floors: T[]): T[] {\r\n const FLOOR_ORDER = [\"L-2\", \"L-1\", \"B2\", \"B1\", \"L1\", \"L1.5\", \"L2\", \"L3\", \"L4\", \"L5\"];\r\n \r\n const indoorFloors = floors.filter(f => isIndoorNavigableFloor(f.floorCode));\r\n\r\n const getFloorIndex = (floorCode: string): number => {\r\n let code = floorCode.trim().toUpperCase();\r\n if (code.startsWith('F')) {\r\n code = 'L' + code.substring(1);\r\n }\r\n const matchedIndex = FLOOR_ORDER.findIndex(f => f.toUpperCase() === code);\r\n if (matchedIndex !== -1) return matchedIndex;\r\n \r\n const numMatch = code.match(/-?\\d+(\\.\\d+)?/);\r\n if (numMatch) {\r\n const val = parseFloat(numMatch[0]);\r\n if (code.startsWith('B') || code.includes('-')) {\r\n return -Math.abs(val) - 100;\r\n }\r\n return val;\r\n }\r\n return 0;\r\n };\r\n\r\n return [...indoorFloors].sort((a, b) => getFloorIndex(a.floorCode) - getFloorIndex(b.floorCode));\r\n}\r\n","import { EventEmitter } from './event-emitter';\r\nimport {\r\n SGSMapSDKOptions,\r\n SGSMapEvents,\r\n RouteResult,\r\n SgsMapNode,\r\n MarkerConfig,\r\n SgsSdkManifest,\r\n SgsFloorBundle,\r\n SgsPoi,\r\n SgsSpaceArea,\r\n SgsGuideStop,\r\n SgsPerformanceStats,\r\n SgsNavigablePlace,\r\n SgsMapDiagnostics,\r\n SgsFloorDiagnostics,\r\n SgsPoiQueryParams,\r\n SgsFeaturedRouteSummary,\r\n SgsFeaturedRouteDetail,\r\n SgsFeaturedRoutePreviewPayload,\r\n SgsFlowlineOptions,\r\n SgsRouteRoamingOptions,\r\n RouteOptions,\r\n SdkRoutePoint,\r\n} from './types';\r\n\r\nexport * from './types';\r\n\r\nimport * as utils from './utils';\r\n\r\n/** SDK 版本号常量 — 与后端 SdkMapServiceImpl.SDK_VERSION 对齐 */\r\nconst SDK_VERSION = '2.4.1';\r\n/** 协议版本号 */\r\nconst PROTOCOL_VERSION = 2;\r\n/** 默认 ready 超时(ms) */\r\nconst DEFAULT_READY_TIMEOUT = 30000; // 🔧 P0: 从 15s 提升到 30s,配合延迟 ENGINE_READY 策略\r\n/** 默认命令超时(ms) */\r\nconst DEFAULT_CMD_TIMEOUT = 5000;\r\n\r\n/**\r\n * 命令队列条目 — ready 前调用的异步命令暂存于此\r\n */\r\ninterface PendingCommand {\r\n action: string;\r\n payload?: any;\r\n timeout: number;\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n}\r\n\r\n/**\r\n * SGS 3D 地图 H5 SDK 入口类\r\n *\r\n *

通过 iframe 嵌入 /h5-sdk 底座页面,以 postMessage 进行双向通信。\r\n *

所有需要后端数据的操作均通过 postMessage 指令由底座代理,\r\n * Demo / 宿主不允许直连后端 API。\r\n *\r\n * @version 2.4.1\r\n */\r\nexport default class SGSMapSDK extends EventEmitter {\r\n public static utils = utils;\r\n private container: HTMLElement;\r\n private sdkUrl: string;\r\n private floorId: string | number;\r\n private mapId: string | number;\r\n private targetOrigin: string;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private _isReady: boolean = false;\r\n private _isDestroyed: boolean = false;\r\n /** ready 失败终态:iframe 加载失败或 ready 超时后置 true,后续命令直接 reject */\r\n private _readyFailed: boolean = false;\r\n /** ready 失败时的错误对象,用于后续命令的立即 reject */\r\n private _readyFailedError: { code: string; message: string } | null = null;\r\n private defaultTimeout: number;\r\n private readyTimeoutMs: number;\r\n /** 已 ready 后正在等待响应的命令队列 */\r\n private promiseQueue: {\r\n [key: string]: {\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n };\r\n } = {};\r\n /** ready 前暂存的异步命令队列 */\r\n private _pendingCommands: PendingCommand[] = [];\r\n /** ready 前暂存的 fire-and-forget 命令队列 */\r\n private _pendingFireAndForget: Array<{ action: string; payload?: any }> = [];\r\n private _messageListener: ((event: MessageEvent) => void) | null = null;\r\n /** ready 超时计时器 */\r\n private _readyTimer: ReturnType | null = null;\r\n /** 🔧 P0: HELLO 重试定时器 — 解决 iframe onload 与 React useEffect 时序竞争 */\r\n private _helloInterval: ReturnType | null = null;\r\n\r\n /** ENGINE_READY 返回的 payload(供外部读取) */\r\n public readyPayload: any = null;\r\n\r\n /** ready Promise 的 resolve/reject,保证 whenReady() 返回同一个 Promise */\r\n private _readyResolve: ((value: any) => void) | null = null;\r\n private _readyReject: ((reason: any) => void) | null = null;\r\n private _readyPromise: Promise;\r\n\r\n /** 性能追踪字段 */\r\n private _sdkCreatedAt: number;\r\n private _iframeDomReadyMs: number | null = null;\r\n private _engineReadyMs: number | null = null;\r\n\r\n constructor(options: SGSMapSDKOptions) {\r\n super();\r\n this._sdkCreatedAt = Date.now();\r\n\r\n const containerEl =\r\n typeof options.container === 'string'\r\n ? document.getElementById(options.container)\r\n : options.container;\r\n\r\n if (!containerEl) {\r\n throw new Error(`[SGSMapSDK] Cannot find container: ${options.container}`);\r\n }\r\n\r\n this.container = containerEl;\r\n this.sdkUrl = options.sdkUrl || '/h5-sdk';\r\n this.floorId = options.floorId || 1;\r\n this.mapId = options.mapId || 1;\r\n this.defaultTimeout = options.timeout || DEFAULT_CMD_TIMEOUT;\r\n this.readyTimeoutMs = options.readyTimeout || DEFAULT_READY_TIMEOUT;\r\n\r\n this.targetOrigin = options.targetOrigin as string;\r\n if (!this.targetOrigin) {\r\n if (this.sdkUrl.startsWith('http')) {\r\n this.targetOrigin = new URL(this.sdkUrl).origin;\r\n } else if (typeof window !== 'undefined') {\r\n this.targetOrigin = window.location.origin;\r\n } else {\r\n this.targetOrigin = '*';\r\n }\r\n }\r\n\r\n // 初始化 ready Promise(全局唯一,whenReady() 返回同一个)\r\n this._readyPromise = new Promise((resolve, reject) => {\r\n this._readyResolve = resolve;\r\n this._readyReject = reject;\r\n });\r\n\r\n this._initIframeBridge();\r\n }\r\n\r\n // ==================== iframe 桥接初始化 ====================\r\n\r\n private _initIframeBridge() {\r\n const iframe = document.createElement('iframe');\r\n\r\n // 拷贝父级 URL 上的查询参数传递给 iframe 底座\r\n const iframeUrl = new URL(\r\n this.sdkUrl,\r\n typeof window !== 'undefined' ? window.location.href : 'http://localhost'\r\n );\r\n if (typeof window !== 'undefined') {\r\n const parentParams = new URLSearchParams(window.location.search);\r\n parentParams.forEach((value, key) => {\r\n iframeUrl.searchParams.set(key, value);\r\n });\r\n }\r\n // 附带 floorId\r\n if (!iframeUrl.searchParams.has('floorId')) {\r\n iframeUrl.searchParams.set('floorId', String(this.floorId));\r\n }\r\n\r\n iframe.src = iframeUrl.toString();\r\n iframe.style.cssText = 'width:100%;height:100%;border:none;background:transparent;';\r\n\r\n this.container.innerHTML = '';\r\n this.container.appendChild(iframe);\r\n this.iframe = iframe;\r\n\r\n // ── 消息监听 ──\r\n this._messageListener = (event: MessageEvent) => {\r\n if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {\r\n return; // 静默忽略不匹配的来源\r\n }\r\n\r\n if (!this.iframe || event.source !== this.iframe.contentWindow) {\r\n return;\r\n }\r\n\r\n const data = event.data;\r\n if (!data) return;\r\n\r\n // ── 事件类型消息 ──\r\n if (data.type === 'SGS_MAP_EVENT') {\r\n const { action, payload } = data;\r\n if (action === 'ON_POI_CLICK') {\r\n this.emit('poiClick', payload);\r\n } else if (action === 'ON_POI_DETAIL') {\r\n this.emit('poiDetail', payload);\r\n } else if (action === 'ON_POI_NAVIGATE') {\r\n this.emit('poiNavigate', payload);\r\n } else if (action === 'ON_POI_CLOSE') {\r\n this.emit('poiClose', payload);\r\n } else if (action === 'ON_SPACE_CLICK') {\r\n this.emit('spaceClick', payload);\r\n } else if (action === 'ON_SPACE_DETAIL') {\r\n this.emit('spaceDetail', payload);\r\n } else if (action === 'ON_SPACE_NAVIGATE') {\r\n this.emit('spaceNavigate', payload);\r\n } else if (action === 'ON_SPACE_CLOSE') {\r\n this.emit('spaceClose', payload);\r\n } else if (action === 'ENGINE_READY') {\r\n this._onEngineReady(payload);\r\n } else if (action === 'ON_FLOOR_CHANGED') {\r\n this.emit('floorChanged', payload);\r\n } else if (action === 'ON_ROUTE_TRANSFER') {\r\n this.emit('routeTransfer', payload);\r\n } else if (action === 'ON_MODEL_LOADING') {\r\n this.emit('modelLoading', payload);\r\n } else if (action === 'ON_MODEL_READY') {\r\n this.emit('modelReady', payload);\r\n } else if (action === 'ON_MODEL_ERROR') {\r\n this.emit('modelError', payload);\r\n } else if (action === 'ON_FLOOR_BUNDLE_READY') {\r\n this.emit('floorBundleReady', payload);\r\n } else if (action === 'ON_ROUTE_READY') {\r\n this.emit('routeReady', payload);\r\n } else if (action === 'ON_ROAMING_PROGRESS') {\r\n this.emit('roamingProgress', payload);\r\n } else if (action === 'ON_ROAMING_DONE') {\r\n this.emit('roamingDone', payload);\r\n }\r\n }\r\n\r\n // ── 命令响应消息 ──\r\n if (data.type === 'SGS_MAP_RESPONSE') {\r\n const { action, requestId, payload, error } = data;\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n if (payload && payload.success !== false) {\r\n resolver.resolve(payload);\r\n } else {\r\n const errObj = error || { code: 'ERR_FAILED', message: `${action} command failed` };\r\n resolver.reject(errObj);\r\n }\r\n delete this.promiseQueue[requestId];\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener('message', this._messageListener);\r\n\r\n // ── iframe 加载完成 → 发送 HELLO 握手 ──\r\n iframe.onload = () => {\r\n this._iframeDomReadyMs = Date.now() - this._sdkCreatedAt;\r\n if (this._isReady) return;\r\n\r\n // 🔧 P0 修复:HELLO 重试机制\r\n // iframe onload 触发时 React useEffect 可能尚未挂载 message listener,\r\n // 单次 HELLO 会丢失。改为每 500ms 重试,直到 ENGINE_READY 到达。\r\n const sendHello = () => {\r\n if (this._isReady || this._isDestroyed) {\r\n if (this._helloInterval) { clearInterval(this._helloInterval); this._helloInterval = null; }\r\n return;\r\n }\r\n this._postCommand('HELLO', { protocolVersion: PROTOCOL_VERSION, sdkVersion: SDK_VERSION });\r\n };\r\n sendHello(); // 立即发第一次\r\n this._helloInterval = setInterval(sendHello, 500);\r\n console.log(`[SGSMapSDK] Sent HELLO handshake (v${SDK_VERSION}), will retry every 500ms until ENGINE_READY...`);\r\n };\r\n\r\n // ── iframe 加载失败 → reject ready + 清空命令队列 ──\r\n iframe.onerror = (err) => {\r\n console.error('[SGSMapSDK] Iframe load error', err);\r\n const error = { code: 'ERR_IFRAME_LOAD' as const, message: 'Failed to load map engine iframe.' };\r\n this.emit('loadError', { type: 'iframe_error', detail: error.message });\r\n this._rejectReady(error);\r\n };\r\n\r\n // ── ready 超时计时器 ──\r\n this._readyTimer = setTimeout(() => {\r\n if (!this._isReady && !this._isDestroyed) {\r\n const error = {\r\n code: 'ERR_READY_TIMEOUT' as const,\r\n message: `ENGINE_READY not received within ${this.readyTimeoutMs}ms`\r\n };\r\n console.error(`[SGSMapSDK] ${error.message}`);\r\n this._rejectReady(error);\r\n }\r\n }, this.readyTimeoutMs);\r\n }\r\n\r\n // ==================== ready 生命周期管理 ====================\r\n\r\n /**\r\n * ENGINE_READY 到达时的处理\r\n */\r\n private _onEngineReady(payload: any) {\r\n if (this._isReady) return; // 防重入\r\n\r\n this._isReady = true;\r\n this.readyPayload = payload;\r\n this._engineReadyMs = Date.now() - this._sdkCreatedAt;\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // resolve ready Promise\r\n if (this._readyResolve) {\r\n this._readyResolve(payload);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 触发 ready 事件\r\n this.emit('ready', payload);\r\n console.log('[SGSMapSDK] Handshake complete: Engine Ready received ✅');\r\n\r\n // 排空命令队列 — ready 前暂存的命令逐个发送\r\n this._flushPendingCommands();\r\n }\r\n\r\n /**\r\n * reject ready Promise 并清空所有挂起命令,设置失败终态\r\n */\r\n private _rejectReady(error: { code: string; message: string }) {\r\n // 设置失败终态 — 后续所有新命令直接 reject\r\n this._readyFailed = true;\r\n this._readyFailedError = error;\r\n\r\n // reject ready Promise\r\n if (this._readyReject) {\r\n this._readyReject(error);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 清空 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // reject 所有命令队列 + 清空 fire-and-forget 队列\r\n this._rejectAllPending(error.code, error.message);\r\n this._pendingFireAndForget.splice(0);\r\n }\r\n\r\n /**\r\n * 排空 ready 前暂存的命令队列 — 逐个以 _postCommandAsync 发送\r\n */\r\n private _flushPendingCommands() {\r\n // 1. 排空 fire-and-forget 队列\r\n const fireAndForget = this._pendingFireAndForget.splice(0);\r\n for (const cmd of fireAndForget) {\r\n this._postCommand(cmd.action, cmd.payload);\r\n }\r\n\r\n // 2. 排空异步命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n this._postCommandAsyncInner(cmd.action, cmd.payload, cmd.timeout)\r\n .then(cmd.resolve)\r\n .catch(cmd.reject);\r\n }\r\n\r\n const total = fireAndForget.length + pending.length;\r\n if (total > 0) {\r\n console.log(`[SGSMapSDK] Flushed ${total} pending commands after ready (${fireAndForget.length} fire-and-forget, ${pending.length} async)`);\r\n }\r\n }\r\n\r\n /**\r\n * reject 所有挂起的 Promise 和命令队列\r\n */\r\n private _rejectAllPending(code: string, message: string) {\r\n // 1. reject 已发送的 promiseQueue\r\n Object.keys(this.promiseQueue).forEach((requestId) => {\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n resolver.reject({ code, message });\r\n }\r\n delete this.promiseQueue[requestId];\r\n });\r\n\r\n // 2. reject 命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n cmd.reject({ code, message });\r\n }\r\n }\r\n\r\n // ==================== 公共生命周期方法 ====================\r\n\r\n /**\r\n * 返回 ready Promise(全局唯一,多次调用返回同一个)。\r\n * 别名:ready()\r\n *\r\n * @example\r\n * await sdk.whenReady();\r\n * sdk.changeFloor(floorId);\r\n */\r\n whenReady(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /**\r\n * whenReady 的别名\r\n */\r\n ready(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /** 获取 ready 状态 */\r\n getIsReady(): boolean {\r\n return this._isReady;\r\n }\r\n\r\n /** 获取当前楼层 ID */\r\n getCurrentFloor(): string | number {\r\n return this.floorId;\r\n }\r\n\r\n /** 获取当前 mapId */\r\n getMapId(): string | number {\r\n return this.mapId;\r\n }\r\n\r\n /** 获取 SDK 版本号 */\r\n getVersion(): string {\r\n return SDK_VERSION;\r\n }\r\n\r\n // ==================== 核心通信方法 ====================\r\n\r\n private _generateRequestId(): string {\r\n return `req_sdk_${Date.now()}_${Math.floor(Math.random() * 100000)}`;\r\n }\r\n\r\n private _postCommand(action: string, payload?: any, requestId?: string | null) {\r\n if (!this.iframe || !this.iframe.contentWindow) {\r\n console.error('[SGSMapSDK] SDK Iframe is not available!');\r\n return;\r\n }\r\n\r\n this.iframe.contentWindow.postMessage(\r\n {\r\n type: 'SGS_MAP_COMMAND',\r\n action,\r\n requestId,\r\n payload\r\n },\r\n this.targetOrigin\r\n );\r\n }\r\n\r\n /**\r\n * 异步命令发送(外部入口)— 支持命令队列\r\n *\r\n *

状态机优先级:destroyed > readyFailed > !ready(入队) > ready(发送)\r\n */\r\n private _postCommandAsync(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n // destroyed 后直接 reject\r\n if (this._isDestroyed) {\r\n return Promise.reject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed. Cannot send commands.'\r\n });\r\n }\r\n\r\n // ready 失败终态 — 立即 reject,不再入队\r\n if (this._readyFailed) {\r\n return Promise.reject(\r\n this._readyFailedError || { code: 'ERR_NOT_READY', message: 'SDK ready failed.' }\r\n );\r\n }\r\n\r\n // ready 前入队\r\n if (!this._isReady) {\r\n return new Promise((resolve, reject) => {\r\n this._pendingCommands.push({\r\n action,\r\n payload,\r\n timeout,\r\n resolve,\r\n reject\r\n });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`);\r\n });\r\n }\r\n\r\n // ready 后直接发送\r\n return this._postCommandAsyncInner(action, payload, timeout);\r\n }\r\n\r\n /**\r\n * fire-and-forget 命令发送(void 方法统一入口)\r\n *\r\n *

受 destroyed/readyFailed/!ready 状态保护:\r\n * - destroyed/readyFailed:静默丢弃 + console.warn\r\n * - !ready:暂存到 _pendingFireAndForget,ready 后自动发送\r\n * - ready:立即发送\r\n */\r\n private _postCommandSafe(action: string, payload?: any): void {\r\n if (this._isDestroyed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK destroyed.`);\r\n return;\r\n }\r\n if (this._readyFailed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK ready failed.`);\r\n return;\r\n }\r\n if (!this._isReady) {\r\n this._pendingFireAndForget.push({ action, payload });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);\r\n return;\r\n }\r\n this._postCommand(action, payload);\r\n }\r\n\r\n /**\r\n * 异步命令发送(内部实现)— 已确认 ready\r\n */\r\n private _postCommandAsyncInner(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n const requestId = this._generateRequestId();\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n if (this.promiseQueue[requestId]) {\r\n this.promiseQueue[requestId].reject({\r\n code: 'ERR_TIMEOUT',\r\n message: `Command \"${action}\" timed out after ${timeout}ms`\r\n });\r\n delete this.promiseQueue[requestId];\r\n }\r\n }, timeout);\r\n\r\n this.promiseQueue[requestId] = {\r\n resolve: (data) => {\r\n clearTimeout(timer);\r\n resolve(data as T);\r\n },\r\n reject: (error) => {\r\n clearTimeout(timer);\r\n reject(error);\r\n }\r\n };\r\n\r\n this._postCommand(action, payload, requestId);\r\n });\r\n }\r\n\r\n // ==================== v2.0 核心指令 API ====================\r\n\r\n /** 聚焦到指定要素节点 */\r\n focusTo(node: SgsMapNode | string) {\r\n if (typeof node === 'string') {\r\n this._postCommandSafe('FOCUS_TO', { nodeName: node });\r\n } else {\r\n this._postCommandSafe('FOCUS_TO', node);\r\n }\r\n }\r\n\r\n /** 切换楼层 */\r\n changeFloor(floorId: string | number, timeout?: number, options?: { forceReload?: boolean }): Promise<{ success: boolean; floorId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; floorId: string | number }>('CHANGE_FLOOR', { floorId, ...options }, timeout).then((res) => {\r\n this.floorId = floorId;\r\n return res;\r\n });\r\n }\r\n\r\n /** 添加业务图钉 */\r\n addMarker(config: MarkerConfig) {\r\n this._postCommandSafe('ADD_MARKER', config);\r\n }\r\n\r\n /** 移除指定图钉 */\r\n removeMarker(markerId: string) {\r\n this._postCommandSafe('REMOVE_MARKER', { markerId });\r\n }\r\n\r\n /** 清空所有业务图钉 */\r\n clearMarkers() {\r\n this._postCommandSafe('CLEAR_MARKERS');\r\n }\r\n\r\n /** 高亮多边形轮廓 */\r\n highlightPolygon(payload: string | object) {\r\n if (typeof payload === 'string') {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', { nodeName: payload });\r\n } else {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', payload);\r\n }\r\n }\r\n\r\n /** 寻路规划(v1 兼容) */\r\n planRoute(from: SgsMapNode | string, to: SgsMapNode | string, options?: any, timeout: number = 5000): Promise {\r\n const startNode = typeof from === 'string' ? from : undefined;\r\n const endNode = typeof to === 'string' ? to : undefined;\r\n\r\n const payload: any = {};\r\n if (startNode) payload.startNode = startNode;\r\n else payload.from = from;\r\n\r\n if (endNode) payload.endNode = endNode;\r\n else payload.to = to;\r\n\r\n if (options) payload.options = options;\r\n\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 寻路规划 V2(结构化参数) */\r\n planRouteV2(from: SgsMapNode, to: SgsMapNode, options?: any, timeout: number = 5000): Promise {\r\n const payload = { from, to, options };\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 清除当前路线 */\r\n clearRoute() {\r\n this._postCommandSafe('CLEAR_ROUTE');\r\n }\r\n\r\n /** 渲染已有路线点,供外部宿主直接展示自定义路线 */\r\n renderRoutePoints(\r\n pathPoints: Array<{ x: number; y: number; z: number }>,\r\n options?: any,\r\n timeout: number = 12000\r\n ): Promise {\r\n return this._postCommandAsync('RENDER_ROUTE_POINTS', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 启动路线漫游;pathPoints 为空时,基座会优先使用当前已渲染路线 */\r\n startRoaming(\r\n pathPoints: Array<{ x: number; y: number; z: number }> = [],\r\n options?: SgsRouteRoamingOptions,\r\n timeout: number = 12000\r\n ): Promise<{ success: boolean; mode?: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode?: string }>('START_ROAMING', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 停止路线漫游 */\r\n stopRoaming(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('STOP_ROAMING', {}, timeout);\r\n }\r\n\r\n /** 暂停或继续路线漫游 */\r\n toggleRoamingPause(timeout?: number): Promise<{ success: boolean; paused?: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; paused?: boolean }>('TOGGLE_ROAMING_PAUSE', {}, timeout);\r\n }\r\n\r\n // ==================== SDK 2.4.1 高层导航 API ====================\r\n\r\n /**\r\n * 导航至POI(SDK 2.4.1)\r\n *\r\n * 支持从坐标或POI导航到目标POI,后端自动查询POI坐标。\r\n *\r\n * @param from 起点(POI ID字符串/数字 或 坐标对象)\r\n * @param toPoiId 目标POI ID(建议使用string避免Snowflake精度丢失)\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteToPoi(from: string | number | SdkRoutePoint, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n // 将 string/number 转为 { poiId: from }\r\n const fromPoint: SdkRoutePoint = (typeof from === 'string' || typeof from === 'number')\r\n ? { poiId: from }\r\n : from;\r\n\r\n const payload = {\r\n from: fromPoint,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_TO_POI', payload, timeout);\r\n }\r\n\r\n /**\r\n * POI间导航(SDK 2.4.1)\r\n *\r\n * 两个POI之间的导航,后端自动查询POI坐标。\r\n *\r\n * @param fromPoiId 起点POI ID\r\n * @param toPoiId 终点POI ID\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteBetweenPois(fromPoiId: string | number, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n const payload = {\r\n fromPoiId,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_BETWEEN_POIS', payload, timeout);\r\n }\r\n\r\n // ==================== 状态和视图控制 ====================\r\n getState(): Promise {\r\n return this._postCommandAsync('GET_STATE');\r\n }\r\n\r\n /** 重置视角 */\r\n resetView(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('RESET_VIEW', {}, timeout);\r\n }\r\n\r\n /** 设置 2D/3D 视图模式 */\r\n setViewMode(mode: '2d' | '3d', timeout?: number): Promise<{ success: boolean; mode: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode: string }>('SET_VIEW_MODE', { mode }, timeout);\r\n }\r\n\r\n /** 切换图层可见性 */\r\n toggleLayer(types: string[], visible: boolean, timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('TOGGLE_LAYER', { types, visible }, timeout);\r\n }\r\n\r\n /** 设置可见图层类型列表 */\r\n setVisibleLayers(types: string[], timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('SET_VISIBLE_TYPES', { types }, timeout);\r\n }\r\n\r\n /** 设置路线展示视图是否活跃 */\r\n setRouteViewActive(active: boolean, timeout?: number): Promise<{ success: boolean; active: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; active: boolean }>('SET_ROUTE_VIEW_ACTIVE', { active }, timeout);\r\n }\r\n\r\n // ==================== v2.2.0 / v2.2.0 数据消费 API ====================\r\n\r\n /** 获取地图 Manifest 清单 */\r\n getManifest(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_MANIFEST', {}, timeout);\r\n }\r\n\r\n /** 加载指定楼层的完整 Bundle 数据(含 POI/空间面/讲解点/路网) */\r\n loadFloorBundle(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('LOAD_FLOOR_BUNDLE', { floorId }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的 POI 列表 */\r\n getFloorPois(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_POIS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取指定楼层的起终点候选目的地列表(门点/POI/空间等吸附后目的地) */\r\n getNavigablePlaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_NAVIGABLE_PLACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.navigablePlaces || res?.places || []));\r\n }\r\n\r\n /** 获取当前楼层的 POI 列表(根据 floorCode 参数) */\r\n getPois(floorCode: string, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_POIS', { floorCode }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的空间面列表 */\r\n getSpaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_SPACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.spaces || []));\r\n }\r\n\r\n /** 获取指定楼层的已标定讲解点列表(地图渲染入口) */\r\n getGuideStops(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 获取指定展厅的已标定讲解点列表(业务导览入口) */\r\n getGuideStopsByHall(hallId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS_BY_HALL', { hallId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 预加载指定楼层的模型资源(不渲染) */\r\n preloadFloor(floorId: string | number, timeout?: number): Promise<{ cached: boolean }> {\r\n return this._postCommandAsync<{ cached: boolean }>('PRELOAD_FLOOR', { floorId }, timeout);\r\n }\r\n\r\n /** 获取性能统计信息 */\r\n getPerformanceStats(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_PERFORMANCE_STATS', {}, timeout).then(stats => ({\r\n sdkCreatedAt: this._sdkCreatedAt,\r\n iframeDomReadyMs: this._iframeDomReadyMs ?? undefined,\r\n engineReadyMs: this._engineReadyMs ?? undefined,\r\n ...stats\r\n }));\r\n }\r\n\r\n // ==================== v2.3.0 诊断接口 ====================\r\n\r\n /** 获取地图级诊断信息 */\r\n getDiagnostics(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_DIAGNOSTICS', {}, timeout);\r\n }\r\n\r\n /** 获取楼层级诊断信息 */\r\n getFloorDiagnostics(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_DIAGNOSTICS', { floorId }, timeout);\r\n }\r\n\r\n /** 清空客户端模型缓存 */\r\n clearModelCache(): void {\r\n this._postCommandSafe('CLEAR_MODEL_CACHE');\r\n }\r\n\r\n // ==================== v2.4.0 新增 API ====================\r\n\r\n /** 获取指定楼层的业务 POI 列表 */\r\n getBusinessPois(floorId: string | number, options?: { types?: string[]; keyword?: string; limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_BUSINESS_POIS', { floorId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 查询 POI 列表 */\r\n queryPois(params: SgsPoiQueryParams, timeout?: number): Promise {\r\n return this._postCommandAsync('QUERY_POIS', params, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取特色路线列表 */\r\n getFeaturedRoutes(mapId?: string | number, options?: { limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTES', { mapId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.routes || []));\r\n }\r\n\r\n /** 获取特色路线详情 */\r\n getFeaturedRoute(routeId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTE', { routeId }, timeout)\r\n .then((res) => res?.route || res);\r\n }\r\n\r\n /** 生成并展示精品路线预览 */\r\n planFeaturedRoutePreview(\r\n payload: SgsFeaturedRoutePreviewPayload,\r\n timeout: number = 15000\r\n ): Promise {\r\n return this._postCommandAsync('PLAN_FEATURED_ROUTE_PREVIEW', payload, timeout);\r\n }\r\n\r\n /** 显示动线 */\r\n showFlowline(routeId: string | number, options?: SgsFlowlineOptions, timeout?: number): Promise<{ success: boolean; routeId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; routeId: string | number }>('SHOW_FLOWLINE', { routeId, options }, timeout);\r\n }\r\n\r\n /** 清除动线 */\r\n clearFlowline(): void {\r\n this._postCommandSafe('CLEAR_FLOWLINE');\r\n }\r\n\r\n // ==================== @deprecated 兼容层 ====================\r\n // 以下方法已废弃,保留 API 签名避免外部接入方断裂。\r\n // 内部返回 ERR_UNSUPPORTED,不进入任何发布路径。\r\n\r\n /** @deprecated 使用 getManifest() 替代 */\r\n getReleaseManifest(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getReleaseManifest() is deprecated. Use getManifest() instead.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getModelResources(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getModelResources() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getModelResources() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getLayerTree(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getLayerTree() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getLayerTree() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n loadMockReleaseRoute(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] loadMockReleaseRoute() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'loadMockReleaseRoute() is deprecated.' });\r\n }\r\n\r\n // ==================== 销毁 ====================\r\n\r\n /**\r\n * 销毁 SDK 实例\r\n *\r\n *

清理 iframe、移除事件监听器、reject 所有挂起的 Promise。\r\n *

销毁后调用任何命令方法都会 reject ERR_DESTROYED。\r\n */\r\n destroy() {\r\n if (this._isDestroyed) return; // 防重复销毁\r\n this._isDestroyed = true;\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // reject ready Promise(如果还没 resolve/reject)\r\n if (this._readyReject) {\r\n this._readyReject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed before ready.'\r\n });\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // reject 所有挂起的命令\r\n this._rejectAllPending('ERR_DESTROYED', 'SGSMapSDK has been destroyed. Pending promises were rejected.');\r\n\r\n // 移除 iframe\r\n if (this.iframe) {\r\n if (this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n this.iframe = null;\r\n }\r\n\r\n // 移除事件监听器\r\n if (this._messageListener) {\r\n window.removeEventListener('message', this._messageListener);\r\n this._messageListener = null;\r\n }\r\n\r\n // 移除所有 EventEmitter 监听器\r\n this.removeAllListeners();\r\n\r\n this._isReady = false;\r\n console.log('[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues.');\r\n }\r\n}\r\n\r\n// 全局暴露(IIFE 产物使用)\r\nif (typeof window !== 'undefined') {\r\n (window as any).SGSMapSDK = SGSMapSDK;\r\n}\r\n"],"mappings":"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","names":["EventEmitter","event","listener","l","wrapper","payload","data","e","utils_exports","__export","isIndoorNavigableFloor","sortIndoorFloors","floorCode","floors","FLOOR_ORDER","indoorFloors","f","getFloorIndex","code","matchedIndex","numMatch","val","a","b","SDK_VERSION","PROTOCOL_VERSION","DEFAULT_READY_TIMEOUT","DEFAULT_CMD_TIMEOUT","SGSMapSDK","EventEmitter","options","containerEl","resolve","reject","iframe","iframeUrl","value","key","event","data","action","payload","requestId","error","resolver","errObj","sendHello","err","fireAndForget","cmd","pending","total","code","message","timeout","timer","node","floorId","res","config","markerId","from","to","startNode","endNode","pathPoints","toPoiId","fromPoiId","mode","types","visible","active","floorCode","hallId","stats","params","mapId","routeId","utils_exports"]} \ No newline at end of file +{"version":3,"sources":["../../sdk-src/event-emitter.ts","../../sdk-src/utils.ts","../../sdk-src/index.ts"],"sourcesContent":["/**\r\n * 轻量级类型安全事件发射器\r\n *\r\n *

SDK 内部事件系统基类,提供 on/off/once/emit/removeAllListeners/listenerCount。\r\n */\r\nexport class EventEmitter> {\r\n private events: { [K in keyof Events]?: Array<(payload: Events[K]) => void> };\r\n\r\n constructor() {\r\n this.events = {};\r\n }\r\n\r\n /** 注册事件监听器 */\r\n on(event: K, listener: (payload: Events[K]) => void): void {\r\n if (!this.events[event]) {\r\n this.events[event] = [];\r\n }\r\n this.events[event]!.push(listener);\r\n }\r\n\r\n /** 移除指定事件的指定监听器 */\r\n off(event: K, listener: (payload: Events[K]) => void): void {\r\n if (this.events[event]) {\r\n this.events[event] = this.events[event]!.filter(l => l !== listener);\r\n }\r\n }\r\n\r\n /** 注册一次性监听器,触发后自动移除 */\r\n once(event: K, listener: (payload: Events[K]) => void): void {\r\n const wrapper = (payload: Events[K]) => {\r\n this.off(event, wrapper);\r\n listener(payload);\r\n };\r\n this.on(event, wrapper);\r\n }\r\n\r\n /** 触发事件,通知所有已注册的监听器 */\r\n emit(event: K, data: Events[K]): void {\r\n if (this.events[event]) {\r\n // 浅拷贝防止 once 触发时修改数组导致跳过\r\n const listeners = [...this.events[event]!];\r\n listeners.forEach(l => {\r\n try {\r\n l(data);\r\n } catch (e) {\r\n console.error(`[EventEmitter] Error emitting event ${String(event)}:`, e);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** 移除指定事件的全部监听器;不传参则移除所有事件的所有监听器 */\r\n removeAllListeners(event?: K): void {\r\n if (event !== undefined) {\r\n delete this.events[event];\r\n } else {\r\n this.events = {};\r\n }\r\n }\r\n\r\n /** 返回指定事件的监听器数量 */\r\n listenerCount(event: K): number {\r\n return this.events[event]?.length ?? 0;\r\n }\r\n}\r\n","/**\r\n * 过滤 EXTERIOR 楼层,检查楼层是否是室内可导航楼层\r\n */\r\nexport function isIndoorNavigableFloor(floorCode: string): boolean {\r\n if (!floorCode) return false;\r\n return floorCode.toUpperCase() !== 'EXTERIOR';\r\n}\r\n\r\n/**\r\n * 物理楼层顺序由低到高排序\r\n */\r\nexport function sortIndoorFloors(floors: T[]): T[] {\r\n const FLOOR_ORDER = [\"L-2\", \"L-1\", \"B2\", \"B1\", \"L1\", \"L1.5\", \"L2\", \"L3\", \"L4\", \"L5\"];\r\n \r\n const indoorFloors = floors.filter(f => isIndoorNavigableFloor(f.floorCode));\r\n\r\n const getFloorIndex = (floorCode: string): number => {\r\n let code = floorCode.trim().toUpperCase();\r\n if (code.startsWith('F')) {\r\n code = 'L' + code.substring(1);\r\n }\r\n const matchedIndex = FLOOR_ORDER.findIndex(f => f.toUpperCase() === code);\r\n if (matchedIndex !== -1) return matchedIndex;\r\n \r\n const numMatch = code.match(/-?\\d+(\\.\\d+)?/);\r\n if (numMatch) {\r\n const val = parseFloat(numMatch[0]);\r\n if (code.startsWith('B') || code.includes('-')) {\r\n return -Math.abs(val) - 100;\r\n }\r\n return val;\r\n }\r\n return 0;\r\n };\r\n\r\n return [...indoorFloors].sort((a, b) => getFloorIndex(a.floorCode) - getFloorIndex(b.floorCode));\r\n}\r\n","import { EventEmitter } from './event-emitter';\r\nimport {\r\n SGSMapSDKOptions,\r\n SGSMapEvents,\r\n RouteResult,\r\n SgsMapNode,\r\n MarkerConfig,\r\n SgsSdkManifest,\r\n SgsFloorBundle,\r\n SgsPoi,\r\n SgsSpaceArea,\r\n SgsGuideStop,\r\n SgsPerformanceStats,\r\n SgsNavigablePlace,\r\n SgsMapDiagnostics,\r\n SgsFloorDiagnostics,\r\n SgsPoiQueryParams,\r\n SgsFeaturedRouteSummary,\r\n SgsFeaturedRouteDetail,\r\n SgsFeaturedRoutePreviewPayload,\r\n SgsFlowlineOptions,\r\n SgsRouteRoamingOptions,\r\n RouteOptions,\r\n SdkRoutePoint,\r\n} from './types';\r\n\r\nexport * from './types';\r\n\r\nimport * as utils from './utils';\r\n\r\n/** SDK 版本号常量 — 与后端 SdkMapServiceImpl.SDK_VERSION 对齐 */\r\nconst SDK_VERSION = '2.5.0';\n/** 协议版本号 */\r\nconst PROTOCOL_VERSION = 2;\r\n/** 默认 ready 超时(ms) */\r\nconst DEFAULT_READY_TIMEOUT = 30000; // 🔧 P0: 从 15s 提升到 30s,配合延迟 ENGINE_READY 策略\r\n/** 默认命令超时(ms) */\r\nconst DEFAULT_CMD_TIMEOUT = 5000;\r\n\r\n/**\r\n * 命令队列条目 — ready 前调用的异步命令暂存于此\r\n */\r\ninterface PendingCommand {\r\n action: string;\r\n payload?: any;\r\n timeout: number;\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n}\r\n\r\n/**\r\n * SGS 3D 地图 H5 SDK 入口类\r\n *\r\n *

通过 iframe 嵌入独立发布的 /engine/index.html 渲染引擎,以 postMessage 进行双向通信。\n *

所有需要后端数据的操作均通过 postMessage 指令由底座代理,\r\n * Demo / 宿主不允许直连后端 API。\r\n *\r\n * @version 2.5.0\n */\r\nexport default class SGSMapSDK extends EventEmitter {\r\n public static utils = utils;\r\n private container: HTMLElement;\r\n private sdkUrl: string;\r\n private floorId: string | number;\r\n private mapId: string | number;\r\n private targetOrigin: string;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private _isReady: boolean = false;\r\n private _isDestroyed: boolean = false;\r\n /** ready 失败终态:iframe 加载失败或 ready 超时后置 true,后续命令直接 reject */\r\n private _readyFailed: boolean = false;\r\n /** ready 失败时的错误对象,用于后续命令的立即 reject */\r\n private _readyFailedError: { code: string; message: string } | null = null;\r\n private defaultTimeout: number;\r\n private readyTimeoutMs: number;\r\n /** 已 ready 后正在等待响应的命令队列 */\r\n private promiseQueue: {\r\n [key: string]: {\r\n resolve: (data: any) => void;\r\n reject: (error: any) => void;\r\n };\r\n } = {};\r\n /** ready 前暂存的异步命令队列 */\r\n private _pendingCommands: PendingCommand[] = [];\r\n /** ready 前暂存的 fire-and-forget 命令队列 */\r\n private _pendingFireAndForget: Array<{ action: string; payload?: any }> = [];\r\n private _messageListener: ((event: MessageEvent) => void) | null = null;\r\n /** ready 超时计时器 */\r\n private _readyTimer: ReturnType | null = null;\r\n /** 🔧 P0: HELLO 重试定时器 — 解决 iframe onload 与 React useEffect 时序竞争 */\r\n private _helloInterval: ReturnType | null = null;\r\n\r\n /** ENGINE_READY 返回的 payload(供外部读取) */\r\n public readyPayload: any = null;\r\n\r\n /** ready Promise 的 resolve/reject,保证 whenReady() 返回同一个 Promise */\r\n private _readyResolve: ((value: any) => void) | null = null;\r\n private _readyReject: ((reason: any) => void) | null = null;\r\n private _readyPromise: Promise;\r\n\r\n /** 性能追踪字段 */\r\n private _sdkCreatedAt: number;\r\n private _iframeDomReadyMs: number | null = null;\r\n private _engineReadyMs: number | null = null;\r\n\r\n constructor(options: SGSMapSDKOptions) {\r\n super();\r\n this._sdkCreatedAt = Date.now();\r\n\r\n const containerEl =\r\n typeof options.container === 'string'\r\n ? document.getElementById(options.container)\r\n : options.container;\r\n\r\n if (!containerEl) {\r\n throw new Error(`[SGSMapSDK] Cannot find container: ${options.container}`);\r\n }\r\n\r\n this.container = containerEl;\r\n this.sdkUrl = options.sdkUrl || '/engine/index.html';\n this.floorId = options.floorId || 1;\r\n this.mapId = options.mapId || 1;\r\n this.defaultTimeout = options.timeout || DEFAULT_CMD_TIMEOUT;\r\n this.readyTimeoutMs = options.readyTimeout || DEFAULT_READY_TIMEOUT;\r\n\r\n this.targetOrigin = options.targetOrigin as string;\r\n if (!this.targetOrigin) {\r\n if (this.sdkUrl.startsWith('http')) {\r\n this.targetOrigin = new URL(this.sdkUrl).origin;\r\n } else if (typeof window !== 'undefined') {\r\n this.targetOrigin = window.location.origin;\r\n } else {\r\n this.targetOrigin = '*';\r\n }\r\n }\r\n\r\n // 初始化 ready Promise(全局唯一,whenReady() 返回同一个)\r\n this._readyPromise = new Promise((resolve, reject) => {\r\n this._readyResolve = resolve;\r\n this._readyReject = reject;\r\n });\r\n\r\n this._initIframeBridge();\r\n }\r\n\r\n // ==================== iframe 桥接初始化 ====================\r\n\r\n private _initIframeBridge() {\r\n const iframe = document.createElement('iframe');\r\n\r\n // 拷贝父级 URL 上的查询参数传递给 iframe 底座\r\n const iframeUrl = new URL(\r\n this.sdkUrl,\r\n typeof window !== 'undefined' ? window.location.href : 'http://localhost'\r\n );\r\n if (typeof window !== 'undefined') {\r\n const parentParams = new URLSearchParams(window.location.search);\r\n parentParams.forEach((value, key) => {\r\n iframeUrl.searchParams.set(key, value);\r\n });\r\n }\r\n // 附带 floorId\r\n if (!iframeUrl.searchParams.has('floorId')) {\r\n iframeUrl.searchParams.set('floorId', String(this.floorId));\r\n }\r\n\r\n iframe.src = iframeUrl.toString();\r\n iframe.style.cssText = 'width:100%;height:100%;border:none;background:transparent;';\r\n\r\n this.container.innerHTML = '';\r\n this.container.appendChild(iframe);\r\n this.iframe = iframe;\r\n\r\n // ── 消息监听 ──\r\n this._messageListener = (event: MessageEvent) => {\r\n if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {\r\n return; // 静默忽略不匹配的来源\r\n }\r\n\r\n if (!this.iframe || event.source !== this.iframe.contentWindow) {\r\n return;\r\n }\r\n\r\n const data = event.data;\r\n if (!data) return;\r\n\r\n // ── 事件类型消息 ──\r\n if (data.type === 'SGS_MAP_EVENT') {\r\n const { action, payload } = data;\r\n if (action === 'ON_POI_CLICK') {\r\n this.emit('poiClick', payload);\r\n } else if (action === 'ON_POI_DETAIL') {\r\n this.emit('poiDetail', payload);\r\n } else if (action === 'ON_POI_NAVIGATE') {\r\n this.emit('poiNavigate', payload);\r\n } else if (action === 'ON_POI_CLOSE') {\r\n this.emit('poiClose', payload);\r\n } else if (action === 'ON_SPACE_CLICK') {\r\n this.emit('spaceClick', payload);\r\n } else if (action === 'ON_SPACE_DETAIL') {\r\n this.emit('spaceDetail', payload);\r\n } else if (action === 'ON_SPACE_NAVIGATE') {\r\n this.emit('spaceNavigate', payload);\r\n } else if (action === 'ON_SPACE_CLOSE') {\r\n this.emit('spaceClose', payload);\r\n } else if (action === 'ENGINE_READY') {\r\n this._onEngineReady(payload);\r\n } else if (action === 'ON_FLOOR_CHANGED') {\r\n this.emit('floorChanged', payload);\r\n } else if (action === 'ON_ROUTE_TRANSFER') {\r\n this.emit('routeTransfer', payload);\r\n } else if (action === 'ON_MODEL_LOADING') {\r\n this.emit('modelLoading', payload);\r\n } else if (action === 'ON_MODEL_READY') {\r\n this.emit('modelReady', payload);\r\n } else if (action === 'ON_MODEL_ERROR') {\r\n this.emit('modelError', payload);\r\n } else if (action === 'ON_FLOOR_BUNDLE_READY') {\r\n this.emit('floorBundleReady', payload);\r\n } else if (action === 'ON_ROUTE_READY') {\r\n this.emit('routeReady', payload);\r\n } else if (action === 'ON_ROAMING_PROGRESS') {\r\n this.emit('roamingProgress', payload);\r\n } else if (action === 'ON_ROAMING_DONE') {\r\n this.emit('roamingDone', payload);\r\n }\r\n }\r\n\r\n // ── 命令响应消息 ──\r\n if (data.type === 'SGS_MAP_RESPONSE') {\r\n const { action, requestId, payload, error } = data;\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n if (payload && payload.success !== false) {\r\n resolver.resolve(payload);\r\n } else {\r\n const errObj = error || { code: 'ERR_FAILED', message: `${action} command failed` };\r\n resolver.reject(errObj);\r\n }\r\n delete this.promiseQueue[requestId];\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener('message', this._messageListener);\r\n\r\n // ── iframe 加载完成 → 发送 HELLO 握手 ──\r\n iframe.onload = () => {\r\n this._iframeDomReadyMs = Date.now() - this._sdkCreatedAt;\r\n if (this._isReady) return;\r\n\r\n // 🔧 P0 修复:HELLO 重试机制\r\n // iframe onload 触发时 React useEffect 可能尚未挂载 message listener,\r\n // 单次 HELLO 会丢失。改为每 500ms 重试,直到 ENGINE_READY 到达。\r\n const sendHello = () => {\r\n if (this._isReady || this._isDestroyed) {\r\n if (this._helloInterval) { clearInterval(this._helloInterval); this._helloInterval = null; }\r\n return;\r\n }\r\n this._postCommand('HELLO', { protocolVersion: PROTOCOL_VERSION, sdkVersion: SDK_VERSION });\r\n };\r\n sendHello(); // 立即发第一次\r\n this._helloInterval = setInterval(sendHello, 500);\r\n console.log(`[SGSMapSDK] Sent HELLO handshake (v${SDK_VERSION}), will retry every 500ms until ENGINE_READY...`);\r\n };\r\n\r\n // ── iframe 加载失败 → reject ready + 清空命令队列 ──\r\n iframe.onerror = (err) => {\r\n console.error('[SGSMapSDK] Iframe load error', err);\r\n const error = { code: 'ERR_IFRAME_LOAD' as const, message: 'Failed to load map engine iframe.' };\r\n this.emit('loadError', { type: 'iframe_error', detail: error.message });\r\n this._rejectReady(error);\r\n };\r\n\r\n // ── ready 超时计时器 ──\r\n this._readyTimer = setTimeout(() => {\r\n if (!this._isReady && !this._isDestroyed) {\r\n const error = {\r\n code: 'ERR_READY_TIMEOUT' as const,\r\n message: `ENGINE_READY not received within ${this.readyTimeoutMs}ms`\r\n };\r\n console.error(`[SGSMapSDK] ${error.message}`);\r\n this._rejectReady(error);\r\n }\r\n }, this.readyTimeoutMs);\r\n }\r\n\r\n // ==================== ready 生命周期管理 ====================\r\n\r\n /**\r\n * ENGINE_READY 到达时的处理\r\n */\r\n private _onEngineReady(payload: any) {\r\n if (this._isReady) return; // 防重入\r\n\r\n this._isReady = true;\r\n this.readyPayload = payload;\r\n this._engineReadyMs = Date.now() - this._sdkCreatedAt;\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // resolve ready Promise\r\n if (this._readyResolve) {\r\n this._readyResolve(payload);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 触发 ready 事件\r\n this.emit('ready', payload);\r\n console.log('[SGSMapSDK] Handshake complete: Engine Ready received ✅');\r\n\r\n // 排空命令队列 — ready 前暂存的命令逐个发送\r\n this._flushPendingCommands();\r\n }\r\n\r\n /**\r\n * reject ready Promise 并清空所有挂起命令,设置失败终态\r\n */\r\n private _rejectReady(error: { code: string; message: string }) {\r\n // 设置失败终态 — 后续所有新命令直接 reject\r\n this._readyFailed = true;\r\n this._readyFailedError = error;\r\n\r\n // reject ready Promise\r\n if (this._readyReject) {\r\n this._readyReject(error);\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // 清空 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // reject 所有命令队列 + 清空 fire-and-forget 队列\r\n this._rejectAllPending(error.code, error.message);\r\n this._pendingFireAndForget.splice(0);\r\n }\r\n\r\n /**\r\n * 排空 ready 前暂存的命令队列 — 逐个以 _postCommandAsync 发送\r\n */\r\n private _flushPendingCommands() {\r\n // 1. 排空 fire-and-forget 队列\r\n const fireAndForget = this._pendingFireAndForget.splice(0);\r\n for (const cmd of fireAndForget) {\r\n this._postCommand(cmd.action, cmd.payload);\r\n }\r\n\r\n // 2. 排空异步命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n this._postCommandAsyncInner(cmd.action, cmd.payload, cmd.timeout)\r\n .then(cmd.resolve)\r\n .catch(cmd.reject);\r\n }\r\n\r\n const total = fireAndForget.length + pending.length;\r\n if (total > 0) {\r\n console.log(`[SGSMapSDK] Flushed ${total} pending commands after ready (${fireAndForget.length} fire-and-forget, ${pending.length} async)`);\r\n }\r\n }\r\n\r\n /**\r\n * reject 所有挂起的 Promise 和命令队列\r\n */\r\n private _rejectAllPending(code: string, message: string) {\r\n // 1. reject 已发送的 promiseQueue\r\n Object.keys(this.promiseQueue).forEach((requestId) => {\r\n const resolver = this.promiseQueue[requestId];\r\n if (resolver) {\r\n resolver.reject({ code, message });\r\n }\r\n delete this.promiseQueue[requestId];\r\n });\r\n\r\n // 2. reject 命令队列\r\n const pending = this._pendingCommands.splice(0);\r\n for (const cmd of pending) {\r\n cmd.reject({ code, message });\r\n }\r\n }\r\n\r\n // ==================== 公共生命周期方法 ====================\r\n\r\n /**\r\n * 返回 ready Promise(全局唯一,多次调用返回同一个)。\r\n * 别名:ready()\r\n *\r\n * @example\r\n * await sdk.whenReady();\r\n * sdk.changeFloor(floorId);\r\n */\r\n whenReady(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /**\r\n * whenReady 的别名\r\n */\r\n ready(): Promise {\r\n return this._readyPromise;\r\n }\r\n\r\n /** 获取 ready 状态 */\r\n getIsReady(): boolean {\r\n return this._isReady;\r\n }\r\n\r\n /** 获取当前楼层 ID */\r\n getCurrentFloor(): string | number {\r\n return this.floorId;\r\n }\r\n\r\n /** 获取当前 mapId */\r\n getMapId(): string | number {\r\n return this.mapId;\r\n }\r\n\r\n /** 获取 SDK 版本号 */\r\n getVersion(): string {\r\n return SDK_VERSION;\r\n }\r\n\r\n // ==================== 核心通信方法 ====================\r\n\r\n private _generateRequestId(): string {\r\n return `req_sdk_${Date.now()}_${Math.floor(Math.random() * 100000)}`;\r\n }\r\n\r\n private _postCommand(action: string, payload?: any, requestId?: string | null) {\r\n if (!this.iframe || !this.iframe.contentWindow) {\r\n console.error('[SGSMapSDK] SDK Iframe is not available!');\r\n return;\r\n }\r\n\r\n this.iframe.contentWindow.postMessage(\r\n {\r\n type: 'SGS_MAP_COMMAND',\r\n action,\r\n requestId,\r\n payload\r\n },\r\n this.targetOrigin\r\n );\r\n }\r\n\r\n /**\r\n * 异步命令发送(外部入口)— 支持命令队列\r\n *\r\n *

状态机优先级:destroyed > readyFailed > !ready(入队) > ready(发送)\r\n */\r\n private _postCommandAsync(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n // destroyed 后直接 reject\r\n if (this._isDestroyed) {\r\n return Promise.reject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed. Cannot send commands.'\r\n });\r\n }\r\n\r\n // ready 失败终态 — 立即 reject,不再入队\r\n if (this._readyFailed) {\r\n return Promise.reject(\r\n this._readyFailedError || { code: 'ERR_NOT_READY', message: 'SDK ready failed.' }\r\n );\r\n }\r\n\r\n // ready 前入队\r\n if (!this._isReady) {\r\n return new Promise((resolve, reject) => {\r\n this._pendingCommands.push({\r\n action,\r\n payload,\r\n timeout,\r\n resolve,\r\n reject\r\n });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (waiting for ready). Queue size: ${this._pendingCommands.length}`);\r\n });\r\n }\r\n\r\n // ready 后直接发送\r\n return this._postCommandAsyncInner(action, payload, timeout);\r\n }\r\n\r\n /**\r\n * fire-and-forget 命令发送(void 方法统一入口)\r\n *\r\n *

受 destroyed/readyFailed/!ready 状态保护:\r\n * - destroyed/readyFailed:静默丢弃 + console.warn\r\n * - !ready:暂存到 _pendingFireAndForget,ready 后自动发送\r\n * - ready:立即发送\r\n */\r\n private _postCommandSafe(action: string, payload?: any): void {\r\n if (this._isDestroyed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK destroyed.`);\r\n return;\r\n }\r\n if (this._readyFailed) {\r\n console.warn(`[SGSMapSDK] Command \"${action}\" ignored: SDK ready failed.`);\r\n return;\r\n }\r\n if (!this._isReady) {\r\n this._pendingFireAndForget.push({ action, payload });\r\n console.log(`[SGSMapSDK] Command \"${action}\" queued (fire-and-forget, waiting for ready). Queue size: ${this._pendingFireAndForget.length}`);\r\n return;\r\n }\r\n this._postCommand(action, payload);\r\n }\r\n\r\n /**\r\n * 异步命令发送(内部实现)— 已确认 ready\r\n */\r\n private _postCommandAsyncInner(action: string, payload?: any, timeout: number = this.defaultTimeout): Promise {\r\n const requestId = this._generateRequestId();\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n if (this.promiseQueue[requestId]) {\r\n this.promiseQueue[requestId].reject({\r\n code: 'ERR_TIMEOUT',\r\n message: `Command \"${action}\" timed out after ${timeout}ms`\r\n });\r\n delete this.promiseQueue[requestId];\r\n }\r\n }, timeout);\r\n\r\n this.promiseQueue[requestId] = {\r\n resolve: (data) => {\r\n clearTimeout(timer);\r\n resolve(data as T);\r\n },\r\n reject: (error) => {\r\n clearTimeout(timer);\r\n reject(error);\r\n }\r\n };\r\n\r\n this._postCommand(action, payload, requestId);\r\n });\r\n }\r\n\r\n // ==================== v2.0 核心指令 API ====================\r\n\r\n /** 聚焦到指定要素节点 */\r\n focusTo(node: SgsMapNode | string) {\r\n if (typeof node === 'string') {\r\n this._postCommandSafe('FOCUS_TO', { nodeName: node });\r\n } else {\r\n this._postCommandSafe('FOCUS_TO', node);\r\n }\r\n }\r\n\r\n /** 切换楼层 */\r\n changeFloor(floorId: string | number, timeout?: number, options?: { forceReload?: boolean }): Promise<{ success: boolean; floorId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; floorId: string | number }>('CHANGE_FLOOR', { floorId, ...options }, timeout).then((res) => {\r\n this.floorId = floorId;\r\n return res;\r\n });\r\n }\r\n\r\n /** 添加业务图钉 */\r\n addMarker(config: MarkerConfig) {\r\n this._postCommandSafe('ADD_MARKER', config);\r\n }\r\n\r\n /** 移除指定图钉 */\r\n removeMarker(markerId: string) {\r\n this._postCommandSafe('REMOVE_MARKER', { markerId });\r\n }\r\n\r\n /** 清空所有业务图钉 */\r\n clearMarkers() {\r\n this._postCommandSafe('CLEAR_MARKERS');\r\n }\r\n\r\n /** 高亮多边形轮廓 */\r\n highlightPolygon(payload: string | object) {\r\n if (typeof payload === 'string') {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', { nodeName: payload });\r\n } else {\r\n this._postCommandSafe('HIGHLIGHT_POLYGON', payload);\r\n }\r\n }\r\n\r\n /** 寻路规划(v1 兼容) */\r\n planRoute(from: SgsMapNode | string, to: SgsMapNode | string, options?: any, timeout: number = 5000): Promise {\r\n const startNode = typeof from === 'string' ? from : undefined;\r\n const endNode = typeof to === 'string' ? to : undefined;\r\n\r\n const payload: any = {};\r\n if (startNode) payload.startNode = startNode;\r\n else payload.from = from;\r\n\r\n if (endNode) payload.endNode = endNode;\r\n else payload.to = to;\r\n\r\n if (options) payload.options = options;\r\n\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 寻路规划 V2(结构化参数) */\r\n planRouteV2(from: SgsMapNode, to: SgsMapNode, options?: any, timeout: number = 5000): Promise {\r\n const payload = { from, to, options };\r\n return this._postCommandAsync('PLAN_ROUTE', payload, timeout);\r\n }\r\n\r\n /** 清除当前路线 */\r\n clearRoute() {\r\n this._postCommandSafe('CLEAR_ROUTE');\r\n }\r\n\r\n /** 渲染已有路线点,供外部宿主直接展示自定义路线 */\r\n renderRoutePoints(\r\n pathPoints: Array<{ x: number; y: number; z: number }>,\r\n options?: any,\r\n timeout: number = 12000\r\n ): Promise {\r\n return this._postCommandAsync('RENDER_ROUTE_POINTS', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 启动路线漫游;pathPoints 为空时,基座会优先使用当前已渲染路线 */\r\n startRoaming(\r\n pathPoints: Array<{ x: number; y: number; z: number }> = [],\r\n options?: SgsRouteRoamingOptions,\r\n timeout: number = 12000\r\n ): Promise<{ success: boolean; mode?: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode?: string }>('START_ROAMING', { ...options, pathPoints }, timeout);\r\n }\r\n\r\n /** 停止路线漫游 */\r\n stopRoaming(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('STOP_ROAMING', {}, timeout);\r\n }\r\n\r\n /** 暂停或继续路线漫游 */\r\n toggleRoamingPause(timeout?: number): Promise<{ success: boolean; paused?: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; paused?: boolean }>('TOGGLE_ROAMING_PAUSE', {}, timeout);\r\n }\r\n\r\n // ==================== SDK 2.4.1 高层导航 API ====================\r\n\r\n /**\r\n * 导航至POI(SDK 2.4.1)\r\n *\r\n * 支持从坐标或POI导航到目标POI,后端自动查询POI坐标。\r\n *\r\n * @param from 起点(POI ID字符串/数字 或 坐标对象)\r\n * @param toPoiId 目标POI ID(建议使用string避免Snowflake精度丢失)\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteToPoi(from: string | number | SdkRoutePoint, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n // 将 string/number 转为 { poiId: from }\r\n const fromPoint: SdkRoutePoint = (typeof from === 'string' || typeof from === 'number')\r\n ? { poiId: from }\r\n : from;\r\n\r\n const payload = {\r\n from: fromPoint,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_TO_POI', payload, timeout);\r\n }\r\n\r\n /**\r\n * POI间导航(SDK 2.4.1)\r\n *\r\n * 两个POI之间的导航,后端自动查询POI坐标。\r\n *\r\n * @param fromPoiId 起点POI ID\r\n * @param toPoiId 终点POI ID\r\n * @param options 路径选项(wheelchair, preferElevator)\r\n * @param timeout 超时时间(毫秒)\r\n */\r\n planRouteBetweenPois(fromPoiId: string | number, toPoiId: string | number, options?: RouteOptions, timeout: number = 5000): Promise {\r\n const payload = {\r\n fromPoiId,\r\n toPoiId,\r\n wheelchair: options?.wheelchair ? 1 : 0,\r\n preferElevator: options?.preferElevator,\r\n };\r\n return this._postCommandAsync('PLAN_ROUTE_BETWEEN_POIS', payload, timeout);\r\n }\r\n\r\n // ==================== 状态和视图控制 ====================\r\n getState(): Promise {\r\n return this._postCommandAsync('GET_STATE');\r\n }\r\n\r\n /** 重置视角 */\r\n resetView(timeout?: number): Promise<{ success: boolean }> {\r\n return this._postCommandAsync<{ success: boolean }>('RESET_VIEW', {}, timeout);\r\n }\r\n\r\n /** 设置 2D/3D 视图模式 */\r\n setViewMode(mode: '2d' | '3d', timeout?: number): Promise<{ success: boolean; mode: string }> {\r\n return this._postCommandAsync<{ success: boolean; mode: string }>('SET_VIEW_MODE', { mode }, timeout);\r\n }\r\n\r\n /** 切换图层可见性 */\r\n toggleLayer(types: string[], visible: boolean, timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('TOGGLE_LAYER', { types, visible }, timeout);\r\n }\r\n\r\n /** 设置可见图层类型列表 */\r\n setVisibleLayers(types: string[], timeout?: number): Promise<{ success: boolean; visibleTypes: string[] }> {\r\n return this._postCommandAsync<{ success: boolean; visibleTypes: string[] }>('SET_VISIBLE_TYPES', { types }, timeout);\r\n }\r\n\r\n /** 设置路线展示视图是否活跃 */\r\n setRouteViewActive(active: boolean, timeout?: number): Promise<{ success: boolean; active: boolean }> {\r\n return this._postCommandAsync<{ success: boolean; active: boolean }>('SET_ROUTE_VIEW_ACTIVE', { active }, timeout);\r\n }\r\n\r\n // ==================== v2.2.0 / v2.2.0 数据消费 API ====================\r\n\r\n /** 获取地图 Manifest 清单 */\r\n getManifest(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_MANIFEST', {}, timeout);\r\n }\r\n\r\n /** 加载指定楼层的完整 Bundle 数据(含 POI/空间面/讲解点/路网) */\r\n loadFloorBundle(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('LOAD_FLOOR_BUNDLE', { floorId }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的 POI 列表 */\r\n getFloorPois(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_POIS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取指定楼层的起终点候选目的地列表(门点/POI/空间等吸附后目的地) */\r\n getNavigablePlaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_NAVIGABLE_PLACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.navigablePlaces || res?.places || []));\r\n }\r\n\r\n /** 获取当前楼层的 POI 列表(根据 floorCode 参数) */\r\n getPois(floorCode: string, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_POIS', { floorCode }, timeout);\r\n }\r\n\r\n /** 获取指定楼层的空间面列表 */\r\n getSpaces(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_SPACES', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.spaces || []));\r\n }\r\n\r\n /** 获取指定楼层的已标定讲解点列表(地图渲染入口) */\r\n getGuideStops(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS', { floorId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 获取指定展厅的已标定讲解点列表(业务导览入口) */\r\n getGuideStopsByHall(hallId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_GUIDE_STOPS_BY_HALL', { hallId }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.guideStops || []));\r\n }\r\n\r\n /** 预加载指定楼层的模型资源(不渲染) */\r\n preloadFloor(floorId: string | number, timeout?: number): Promise<{ cached: boolean }> {\r\n return this._postCommandAsync<{ cached: boolean }>('PRELOAD_FLOOR', { floorId }, timeout);\r\n }\r\n\r\n /** 获取性能统计信息 */\r\n getPerformanceStats(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_PERFORMANCE_STATS', {}, timeout).then(stats => ({\r\n sdkCreatedAt: this._sdkCreatedAt,\r\n iframeDomReadyMs: this._iframeDomReadyMs ?? undefined,\r\n engineReadyMs: this._engineReadyMs ?? undefined,\r\n ...stats\r\n }));\r\n }\r\n\r\n // ==================== v2.3.0 诊断接口 ====================\r\n\r\n /** 获取地图级诊断信息 */\r\n getDiagnostics(timeout?: number): Promise {\r\n return this._postCommandAsync('GET_DIAGNOSTICS', {}, timeout);\r\n }\r\n\r\n /** 获取楼层级诊断信息 */\r\n getFloorDiagnostics(floorId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FLOOR_DIAGNOSTICS', { floorId }, timeout);\r\n }\r\n\r\n /** 清空客户端模型缓存 */\r\n clearModelCache(): void {\r\n this._postCommandSafe('CLEAR_MODEL_CACHE');\r\n }\r\n\r\n // ==================== v2.4.0 新增 API ====================\r\n\r\n /** 获取指定楼层的业务 POI 列表 */\r\n getBusinessPois(floorId: string | number, options?: { types?: string[]; keyword?: string; limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_BUSINESS_POIS', { floorId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 查询 POI 列表 */\r\n queryPois(params: SgsPoiQueryParams, timeout?: number): Promise {\r\n return this._postCommandAsync('QUERY_POIS', params, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.pois || []));\r\n }\r\n\r\n /** 获取特色路线列表 */\r\n getFeaturedRoutes(mapId?: string | number, options?: { limit?: number }, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTES', { mapId, options }, timeout)\r\n .then((res) => Array.isArray(res) ? res : (res?.routes || []));\r\n }\r\n\r\n /** 获取特色路线详情 */\r\n getFeaturedRoute(routeId: string | number, timeout?: number): Promise {\r\n return this._postCommandAsync('GET_FEATURED_ROUTE', { routeId }, timeout)\r\n .then((res) => res?.route || res);\r\n }\r\n\r\n /** 生成并展示精品路线预览 */\r\n planFeaturedRoutePreview(\r\n payload: SgsFeaturedRoutePreviewPayload,\r\n timeout: number = 15000\r\n ): Promise {\r\n return this._postCommandAsync('PLAN_FEATURED_ROUTE_PREVIEW', payload, timeout);\r\n }\r\n\r\n /** 显示动线 */\r\n showFlowline(routeId: string | number, options?: SgsFlowlineOptions, timeout?: number): Promise<{ success: boolean; routeId: string | number }> {\r\n return this._postCommandAsync<{ success: boolean; routeId: string | number }>('SHOW_FLOWLINE', { routeId, options }, timeout);\r\n }\r\n\r\n /** 清除动线 */\r\n clearFlowline(): void {\r\n this._postCommandSafe('CLEAR_FLOWLINE');\r\n }\r\n\r\n // ==================== @deprecated 兼容层 ====================\r\n // 以下方法已废弃,保留 API 签名避免外部接入方断裂。\r\n // 内部返回 ERR_UNSUPPORTED,不进入任何发布路径。\r\n\r\n /** @deprecated 使用 getManifest() 替代 */\r\n getReleaseManifest(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getReleaseManifest() is deprecated. Use getManifest() instead.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getReleaseManifest() is deprecated. Use getManifest() instead.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getModelResources(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getModelResources() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getModelResources() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n getLayerTree(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] getLayerTree() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'getLayerTree() is deprecated.' });\r\n }\r\n\r\n /** @deprecated 已废弃,无替代方法 */\r\n loadMockReleaseRoute(timeout?: number): Promise {\r\n console.warn('[SGSMapSDK] loadMockReleaseRoute() is deprecated.');\r\n return Promise.reject({ code: 'ERR_UNSUPPORTED', message: 'loadMockReleaseRoute() is deprecated.' });\r\n }\r\n\r\n // ==================== 销毁 ====================\r\n\r\n /**\r\n * 销毁 SDK 实例\r\n *\r\n *

清理 iframe、移除事件监听器、reject 所有挂起的 Promise。\r\n *

销毁后调用任何命令方法都会 reject ERR_DESTROYED。\r\n */\r\n destroy() {\r\n if (this._isDestroyed) return; // 防重复销毁\r\n this._isDestroyed = true;\r\n\r\n // 清除 ready 超时计时器\r\n if (this._readyTimer) {\r\n clearTimeout(this._readyTimer);\r\n this._readyTimer = null;\r\n }\r\n\r\n // 🔧 P0: 清除 HELLO 重试定时器\r\n if (this._helloInterval) {\r\n clearInterval(this._helloInterval);\r\n this._helloInterval = null;\r\n }\r\n\r\n // reject ready Promise(如果还没 resolve/reject)\r\n if (this._readyReject) {\r\n this._readyReject({\r\n code: 'ERR_DESTROYED',\r\n message: 'SGSMapSDK has been destroyed before ready.'\r\n });\r\n this._readyResolve = null;\r\n this._readyReject = null;\r\n }\r\n\r\n // reject 所有挂起的命令\r\n this._rejectAllPending('ERR_DESTROYED', 'SGSMapSDK has been destroyed. Pending promises were rejected.');\r\n\r\n // 移除 iframe\r\n if (this.iframe) {\r\n if (this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n this.iframe = null;\r\n }\r\n\r\n // 移除事件监听器\r\n if (this._messageListener) {\r\n window.removeEventListener('message', this._messageListener);\r\n this._messageListener = null;\r\n }\r\n\r\n // 移除所有 EventEmitter 监听器\r\n this.removeAllListeners();\r\n\r\n this._isReady = false;\r\n console.log('[SGSMapSDK] Destroyed successfully. Cleaned up iframe, listeners and pending queues.');\r\n }\r\n}\r\n\r\n// 全局暴露(IIFE 产物使用)\r\nif (typeof window !== 'undefined') {\r\n (window as any).SGSMapSDK = SGSMapSDK;\r\n}\r\n"],"mappings":"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","names":["EventEmitter","event","listener","l","wrapper","payload","data","e","utils_exports","__export","isIndoorNavigableFloor","sortIndoorFloors","floorCode","floors","FLOOR_ORDER","indoorFloors","f","getFloorIndex","code","matchedIndex","numMatch","val","a","b","SDK_VERSION","PROTOCOL_VERSION","DEFAULT_READY_TIMEOUT","DEFAULT_CMD_TIMEOUT","SGSMapSDK","EventEmitter","options","containerEl","resolve","reject","iframe","iframeUrl","value","key","event","data","action","payload","requestId","error","resolver","errObj","sendHello","err","fireAndForget","cmd","pending","total","code","message","timeout","timer","node","floorId","res","config","markerId","from","to","startNode","endNode","pathPoints","toPoiId","fromPoiId","mode","types","visible","active","floorCode","hallId","stats","params","mapId","routeId","utils_exports"]} \ No newline at end of file diff --git a/static/sgs-map-sdk/package.json b/static/sgs-map-sdk/package.json index 685f2bd..8059224 100644 --- a/static/sgs-map-sdk/package.json +++ b/static/sgs-map-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sgs/map-sdk", - "version": "2.4.1", + "version": "2.5.0", "description": "SGS Map SDK API contract copy for frontend-miniapp; iframe runtime is not enabled by this project", "main": "./index.global.js", "types": "./index.d.ts", @@ -22,7 +22,8 @@ "CHANGELOG.md", "sdk-quickstart.md", "sdk-api-reference.md", - "sdk-protocol.md" + "sdk-protocol.md", + "sdk-bridge-contract.md" ], "devDependencies": {}, "author": "SGS Team", diff --git a/static/sgs-map-sdk/sdk-api-reference.md b/static/sgs-map-sdk/sdk-api-reference.md index 3b34e74..88097ce 100644 --- a/static/sgs-map-sdk/sdk-api-reference.md +++ b/static/sgs-map-sdk/sdk-api-reference.md @@ -1,8 +1,10 @@ # SGS Map SDK API Reference -> 版本: V2.3.0 +> 本项目同步版本: V2.5.0 > 适用对象: 第三方 H5、大屏、Kiosk、业务前端开发人员 +> 发布源保留了 V2.3.0 文档标题和历史 API 注释;实际发行版本以 `package.json` 与 `dist` 产物自报的 2.5.0 为准。本文不表示本项目已经接入或验证 SDK iframe renderer。 + 本文档按公开 SDK 方法组织,说明初始化参数、事件、常用 API、返回值和错误处理。快速接入请先阅读 `sdk-quickstart.md`,底层通信协议请阅读 `sdk-protocol.md`。 ## 1. 接入入口 @@ -14,7 +16,7 @@ + ``` ### ESM @@ -34,7 +36,7 @@ import SGSMapSDK from './dist/index.mjs'; const map = new SGSMapSDK({ container: document.getElementById('map-container')!, - sdkUrl: 'https://map.example.com/h5-sdk', + sdkUrl: 'https://map.example.com/engine/index.html', targetOrigin: 'https://map.example.com', floorId: 1 }); @@ -57,7 +59,7 @@ interface SGSMapSDKOptions { | 字段 | 必填 | 默认值 | 说明 | |------|------|--------|------| | `container` | 是 | 无 | 地图 iframe 挂载容器,可传 DOM id 或 HTMLElement | -| `sdkUrl` | 否 | `/h5-sdk` | H5 SDK 渲染基座地址 | +| `sdkUrl` | 否 | `/engine/index.html` | 独立发布的 SDK 渲染引擎地址 | | `targetOrigin` | 否 | 从 `sdkUrl` 推导 | `postMessage` 安全通信域,生产环境建议显式填写 | | `floorId` | 否 | `1` | 初始楼层 ID,支持字符串或数字 | | `mapId` | 否 | `1` | 地图 ID,当前由 SDK 实例保存 | @@ -107,7 +109,7 @@ window.addEventListener('beforeunload', () => { ### `getVersion(): string` -返回 SDK 版本号,v2.3.0 返回 `"2.3.0"`。 +返回 SDK 版本号;本项目同步的 2.5.0 Bridge 返回 `"2.5.0"`。 ### `getState(): Promise` @@ -654,9 +656,9 @@ try { ## 15. 第三方接入检查清单 - 页面中存在 SDK 挂载容器,且容器有稳定宽高。 -- `sdkUrl` 可在浏览器直接访问,并能加载 `/h5-sdk`。 +- `sdkUrl` 可在浏览器直接访问,并能加载独立发布的 `/engine/index.html`。 - `targetOrigin` 与 `sdkUrl` 的 Origin 一致。 - 所有地图控制 API 在 `await map.whenReady()` 后调用。 - 坐标统一使用 GLB/Three.js 米制坐标,水平面为 `x/z`。 - 页面卸载时调用 `map.destroy()`。 -- 生产发布时 SDK 版本、基座版本和后端 Manifest 的 `sdkVersion` 保持 `2.3.0`。 +- 生产发布时 SDK、Engine 与后端 Manifest 的 `sdkVersion` 必须保持兼容;本次 Bridge 为 `2.5.0`,上线前需验证 `/engine/index.html` 真实加载 SDK Engine 并完成 `HELLO` -> `ENGINE_READY`。 diff --git a/static/sgs-map-sdk/sdk-bridge-contract.md b/static/sgs-map-sdk/sdk-bridge-contract.md new file mode 100644 index 0000000..e080879 --- /dev/null +++ b/static/sgs-map-sdk/sdk-bridge-contract.md @@ -0,0 +1,1160 @@ +# SGS Map SDK Bridge 契约文档 + +> 版本: 2.5.0 | 最后更新: 2026-07-15 | 基于独立 `/engine/` 渲染引擎实际实现 + +## 概述 + +本文档记录 SGS Map SDK(客户端 JS 库)与独立发布的 `/engine/` 渲染引擎之间的 `postMessage` 通信契约。运行时引擎源码位于 `sgs-frontend-map/sdk-engine`,发布产物位于 `sgs-map-sdk-release/engine`。 + +**核心架构**: +``` +┌─────────────┐ postMessage ┌───────────────────┐ REST API ┌──────────┐ +│ 宿主页面 │ ◄───────────► │ /engine/ 引擎 │ ◄──────────► │ 后端 │ +│ (SDK.js) │ SGS_MAP_* │ (page.tsx iframe) │ /app-api/* │ Spring │ +└─────────────┘ └───────────────────┘ └──────────┘ +``` + +## 通信协议 + +### 请求格式(宿主 → 基座) + +```ts +{ + type: 'SGS_MAP_COMMAND', + action: string, // 指令名(如 'CHANGE_FLOOR') + requestId: string, // 唯一请求 ID,用于匹配响应 + payload: object // 指令参数 +} +``` + +### 响应格式(基座 → 宿主) + +```ts +{ + type: 'SGS_MAP_RESPONSE', + action: string, // 原样回显请求的 action(A2.6 规约) + requestId: string, // 原样回显请求的 requestId + payload: object, // 响应数据(含 success: boolean) + error?: { // 仅失败时存在 + code: string, + message: string + } +} +``` + +### 事件格式(基座 → 宿主,主动推送) + +```ts +{ + type: 'SGS_MAP_EVENT', + action: string, // 事件名(如 'ENGINE_READY', 'ON_POI_CLICK') + payload: object +} +``` + +### Origin 安全校验 + +基座通过 URL 参数 `?targetOrigin=` 启用 origin 白名单校验。若 `targetOrigin` 非 `*` 且与 `event.origin` 不匹配,消息将被静默丢弃。 + +--- + +## Bridge 支持矩阵 + +> ✅ = 代码中存在对应 `case` 分支,已实际实现 +> ❌ = 代码中无对应 `case` 分支 + +| # | SDK 方法名 | postMessage action | /engine/ 支持 | 最低版本 | 类别 | +|---|-----------|-------------------|:---:|---------|------| +| 1 | `自动握手` | `HELLO` | ✅ | 1.0.0 | 生命周期 | +| 2 | `changeFloor()` | `CHANGE_FLOOR` | ✅ | 1.0.0 | 地图控制 | +| 3 | `focusTo()` | `FOCUS_TO` | ✅ | 1.0.0 | 地图控制 | +| 4 | `setViewMode()` | `SET_VIEW_MODE` | ✅ | 2.3.0 | 地图控制 | +| 5 | `addMarker()` | `ADD_MARKER` / `ADD_BUSINESS_MARKER` | ✅ | 1.0.0 | 图钉 | +| 6 | `removeMarker()` | `REMOVE_MARKER` / `REMOVE_BUSINESS_MARKER` | ✅ | 1.0.0 | 图钉 | +| 7 | `clearMarkers()` | `CLEAR_MARKERS` | ✅ | 1.0.0 | 图钉 | +| 8 | `planRoute()` / `planRouteV2()` | `PLAN_ROUTE` | ✅ | 1.0.0 | 路径规划 | +| 9 | `highlightPolygon()` | `HIGHLIGHT_POLYGON` | ✅ | 1.0.0 | 地图控制 | +| 10 | `clearRoute()` | `CLEAR_ROUTE` | ✅ | 1.0.0 | 路径规划 | +| 11 | `resetView()` | `RESET_VIEW` | ✅ | 1.0.0 | 地图控制 | +| 12 | `getState()` | `GET_STATE` | ✅ | 1.0.0 | 状态查询 | +| 13 | `toggleLayer()` | `TOGGLE_LAYER` | ✅ | 1.5.0 | 图层控制 | +| 14 | `setVisibleLayers()` | `SET_VISIBLE_TYPES` | ✅ | 1.5.0 | 图层控制 | +| 15 | `getManifest()` | `GET_MANIFEST` | ✅ | 2.1.0 | 数据查询 | +| 16 | `loadFloorBundle()` | `LOAD_FLOOR_BUNDLE` | ✅ | 2.1.0 | 数据查询 | +| 17 | `getFloorPois()` | `GET_FLOOR_POIS` | ✅ | 2.1.0 | 数据查询 | +| 18 | `getPois()` | `GET_POIS` | ✅ | 1.0.0 | 数据查询 | +| 19 | `getSpaces()` | `GET_SPACES` | ✅ | 2.1.0 | 数据查询 | +| 20 | `getGuideStops()` | `GET_GUIDE_STOPS` | ✅ | 2.1.0 | 数据查询 | +| 21 | `getGuideStopsByHall()` | `GET_GUIDE_STOPS_BY_HALL` | ✅ | 2.1.0 | 数据查询 | +| 22 | `getNavigablePlaces()` | `GET_NAVIGABLE_PLACES` | ✅ | 2.3.0 | 数据查询 | +| 23 | `preloadFloor()` | `PRELOAD_FLOOR` | ✅ | 2.1.0 | 性能优化 | +| 24 | `clearModelCache()` | `CLEAR_MODEL_CACHE` | ✅ | 2.1.0 | 性能优化 | +| 25 | `getPerformanceStats()` | `GET_PERFORMANCE_STATS` | ✅ | 2.1.0 | 诊断 | +| 26 | `getDiagnostics()` | `GET_DIAGNOSTICS` | ✅ | 2.3.0 | 诊断 | +| 27 | `getFloorDiagnostics()` | `GET_FLOOR_DIAGNOSTICS` | ✅ | 2.3.0 | 诊断 | + +### 仅 mock-release 模式专用指令(非公开 SDK 方法) + +| # | postMessage action | /engine/ 支持 | 说明 | +|---|-------------------|:---:|------| +| M1 | `GET_RELEASE_MANIFEST` | ✅ | 仅 `env=mock-release` 时可用 | +| M2 | `GET_MODEL_RESOURCES` | ✅ | 仅 `env=mock-release` 时可用 | +| M3 | `GET_LAYER_TREE` | ✅ | 仅 `env=mock-release` 时可用 | +| M4 | `LOAD_MOCK_RELEASE_ROUTE` | ✅ | 仅 `env=mock-release` 时可用 | + +> 以上 M1–M4 已在 SDK v2.3.0 中标记为废弃 (`deprecated`),新接入方不应使用。 + +--- + +## 各指令详细规格 + +--- + +### 1. HELLO + +握手协议。SDK 创建 iframe 后自动发送,基座收到后立即触发 `ENGINE_READY` 事件。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `HELLO` | +| **SDK 方法** | 自动握手(SDK 内部调用,不暴露公开方法) | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ version?: string } // SDK 版本号(可选) +``` + +**成功响应**: 无显式 `SGS_MAP_RESPONSE`。基座通过 `SGS_MAP_EVENT` 发送 `ENGINE_READY`: +```ts +// type: 'SGS_MAP_EVENT', action: 'ENGINE_READY' +{ + engineVersion: "2.0.0", + protocolVersion: 2, + sdkVersion: "2.3.0", + floorId: string | number, + poiCount: number, + hasRouteNetwork: boolean +} +``` + +**错误响应**: N/A + +--- + +### 2. CHANGE_FLOOR + +切换当前显示楼层。触发 GPU 显存回收、状态重置和新楼层数据加载。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `CHANGE_FLOOR` | +| **SDK 方法** | `sdk.changeFloor(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ + floorId: string | number // 目标楼层 ID 或 floorCode(real-preview 模式支持) +} +``` + +**成功响应**: +```ts +{ + success: true, + floorId: string | number // 实际切换到的楼层 ID +} +``` + +**错误响应**: N/A(当前实现始终返回 success) + +**特殊行为**: +- 若目标楼层与当前楼层相同,直接返回 success,不触发重新加载(防抖) +- `real-preview` 模式下支持通过 `floorCode`(如 `"L2"`)匹配楼层 + +--- + +### 3. FOCUS_TO + +镜头聚焦到指定 POI、坐标或场景节点,支持自适应半径 FlyTo 动画。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `FOCUS_TO` | +| **SDK 方法** | `sdk.focusTo(node)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**(三种模式): + +```ts +// 模式 1: 坐标聚焦 +{ + type: "coord", + x: number, // GLB X 坐标(米) + z: number, // GLB Z 坐标(米) + y?: number, // 高度,默认 0.2 + radius?: number // 聚焦半径,默认 3.0 +} + +// 模式 2: POI ID 聚焦 +{ + type: "poiId", + value: string | number // POI ID +} + +// 模式 3: 节点名聚焦 +{ + type: "nodeName", + value: string // GLB 节点名或 POI 名称 +} + +// 模式 4: 字符串简写 +"Hall_Dinosaur" // 直接传 POI 名称字符串 +``` + +**成功响应**: 无显式响应(fire-and-forget 模式) + +**错误响应**: 无显式响应,仅 console.warn + +**备注**: 基座会同时在 POI 列表和讲解点列表中检索匹配项,按 `name` / `nameEn` / `id` / `guideStopId` 匹配。 + +--- + +### 4. SET_VIEW_MODE + +切换 2D 俯视 / 3D 透视模式。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `SET_VIEW_MODE` | +| **SDK 方法** | `sdk.setViewMode(mode)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.3.0 | + +**请求 payload**: +```ts +{ + mode: "2d" | "3d" +} +``` + +**成功响应**: +```ts +{ + success: true, + mode: "2d" | "3d" +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_MODE", + message: "Invalid mode: . Expected '2d' or '3d'" +} +``` + +--- + +### 5. ADD_MARKER / ADD_BUSINESS_MARKER + +添加业务图钉。两个 action 名共享同一处理逻辑,均可使用。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `ADD_MARKER` 或 `ADD_BUSINESS_MARKER` | +| **SDK 方法** | `sdk.addMarker(config)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ + id: string, // 唯一图钉 ID(重复 id 会覆盖已有图钉) + name: string, // 显示名称 + iconUrl: string, // 图标 URL + anchorNodeName?: string, // 可选,绑定到 GLB 场景节点 + position?: [number, number, number] // [x, y, z] 世界坐标 +} +``` + +**成功响应**: 无显式响应(fire-and-forget 模式) + +**错误响应**: N/A + +--- + +### 6. REMOVE_MARKER / REMOVE_BUSINESS_MARKER + +移除指定业务图钉。两个 action 名共享同一处理逻辑。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `REMOVE_MARKER` 或 `REMOVE_BUSINESS_MARKER` | +| **SDK 方法** | `sdk.removeMarker(markerId)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ + markerId: string // 要移除的图钉 ID +} +``` + +**成功响应**: 无显式响应(fire-and-forget 模式) + +**错误响应**: N/A + +--- + +### 7. CLEAR_MARKERS + +清空所有业务图钉。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `CLEAR_MARKERS` | +| **SDK 方法** | `sdk.clearMarkers()` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: 无显式响应(fire-and-forget 模式) + +**错误响应**: N/A + +--- + +### 8. PLAN_ROUTE + +路径规划。支持同层和跨层 NavMesh 寻路,起终点支持 POI 名称、POI ID 和坐标三种模式。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `PLAN_ROUTE` | +| **SDK 方法** | `sdk.planRoute(from, to, options?, timeout?)` / `sdk.planRouteV2(...)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ + startNode: string | SgsMapNode, // 起点(POI 名称字符串或结构化节点) + endNode: string | SgsMapNode, // 终点 + options?: { + mode?: "walk" | "accessible", // 路线模式 + wheelchair?: boolean // 无障碍路线 + } +} +``` + +其中 `SgsMapNode`: +```ts +type SgsMapNode = + | { type: "coord"; floorId: string | number; x: number; z: number } + | { type: "poiId"; value: string | number } + | string // POI 名称 +``` + +**成功响应**: +```ts +{ + success: true, + distance: number, // 总距离(米) + duration: number, // 预计耗时(秒) + segments: Array<{ // 分段路线 + floorId: string | number, + transferType: "WALK" | "ELEVATOR" | "STAIRS" | "ESCALATOR", + points: Array<{ x: number; y: number; z: number }>, + pathGeoJson?: string + }> +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_PARAMS" | "ERR_ROUTING_FAILED", + message: string +} +``` + +--- + +### 9. HIGHLIGHT_POLYGON + +高亮 POI 关联的多边形边界或空间面。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `HIGHLIGHT_POLYGON` | +| **SDK 方法** | `sdk.highlightPolygon(nodeName)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: +```ts +{ + nodeName?: string, // POI/空间面名称 + name?: string, // 备选字段 + id?: string | number // 备选字段 +} +``` + +**成功响应**: 无显式响应(fire-and-forget 模式) + +**错误响应**: N/A + +**备注**: 先在 POI 列表中查找(匹配 `polygonWkt`),若无结果再在空间面列表中查找(匹配 `boundaryWkt`)。 + +--- + +### 10. CLEAR_ROUTE + +清除当前导航路线。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `CLEAR_ROUTE` | +| **SDK 方法** | `sdk.clearRoute()` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true +} +``` + +**错误响应**: N/A + +--- + +### 11. RESET_VIEW + +重置相机视角,清除聚焦和高亮状态,将视角模式恢复为 3D。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `RESET_VIEW` | +| **SDK 方法** | `sdk.resetView()` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true +} +``` + +**错误响应**: N/A + +**备注**: 不会清除导航路线(`routeSegments`),仅重置选中 POI、聚焦目标和高亮节点。视角模式恢复为 `"3d"`。 + +--- + +### 12. GET_STATE + +获取基座当前内部状态快照。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_STATE` | +| **SDK 方法** | `sdk.getState()` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true, + floorId: string | number, + poiCount: number, + isNavActive: boolean, + engineVersion: "2.0.0", + routeSegments: any[], + activeSegmentIndex: number, + hasRouteNetwork: boolean, + spatialAreas: Array<{ id: any; name: string; wkt: string }>, + // mock-release 模式附加字段 + envMode?: "mock-release", + floorCode?: string, + manifest?: { version: string; releaseId: string; totalFloors: number } | null +} +``` + +**错误响应**: N/A + +--- + +### 13. TOGGLE_LAYER + +切换指定 POI 类型图层的显示/隐藏。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `TOGGLE_LAYER` | +| **SDK 方法** | `sdk.toggleLayer(types, visible, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.5.0 | + +**请求 payload**: +```ts +{ + types: string[], // POI 类型列表(如 ["TOILET", "ELEVATOR"]) + visible: boolean // true = 显示, false = 隐藏 +} +``` + +**成功响应**: +```ts +{ + success: true, + visibleTypes: string[] // 操作后的完整可见类型白名单 +} +``` + +**错误响应**: N/A + +--- + +### 14. SET_VISIBLE_TYPES + +设置当前可见的图层类型列表(完全替换模式)。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `SET_VISIBLE_TYPES` | +| **SDK 方法** | `sdk.setVisibleLayers(types, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.5.0 | + +**请求 payload**: +```ts +{ + types: string[] // 可见类型列表 +} +``` + +特殊值语义: +- `[]` → 切换为 `space` 模式(仅显示空间面) +- `["space"]` → 空间面模式 +- `["guide_stop"]` → 仅讲解点 +- `["poi"]` → 仅 POI +- 其他 → 自定义类型白名单 + +**成功响应**: +```ts +{ + success: true, + visibleTypes: string[] +} +``` + +**错误响应**: N/A + +--- + +### 15. GET_MANIFEST + +获取地图 Manifest(地图名称、楼层摘要、能力声明)。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_MANIFEST` | +| **SDK 方法** | `sdk.getManifest(timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + mapId?: string | number // 地图 ID,默认 "1" +} +``` + +**成功响应**: +```ts +{ + success: true, + mapId: string | number, + mapName: string, + sdkVersion: string, + protocolVersion: number, + dataVersion: string, + updatedAt: string, + coordinateSystem: string, + floors: SgsSdkFloorSummary[], + capabilities: { ... } +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +### 16. LOAD_FLOOR_BUNDLE + +一次性加载指定楼层的完整数据包(模型、POI、空间面、讲解点、路网摘要)。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `LOAD_FLOOR_BUNDLE` | +| **SDK 方法** | `sdk.loadFloorBundle(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + floor: { floorId, floorCode, floorName, sortOrder }, + model: { modelUrl, fallbackModelUrl, compressionType, sizeBytes }, + pois: SgsPoi[], + spaces: SgsSpaceArea[], + guideStops: SgsGuideStop[], + routeSummary: { hasRouteNetwork, nodeCount, edgeCount }, + hiddenSceneNodeNames: string[], + dataVersion: string +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +### 17. GET_FLOOR_POIS + +获取指定楼层 POI 列表。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_FLOOR_POIS` | +| **SDK 方法** | `sdk.getFloorPois(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + pois: SgsPoi[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_FAILED", + message: string +} +``` + +--- + +### 18. GET_POIS + +获取当前楼层 POI 列表(直接返回基座内存中的 POI 数据)。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_POIS` | +| **SDK 方法** | `sdk.getPois(floorCode, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 1.0.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true, + pois: any[], // 当前基座内存中的 POI 数组 + floorCode: string // 当前楼层代码 +} +``` + +**错误响应**: N/A + +--- + +### 19. GET_SPACES + +获取指定楼层空间面列表。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_SPACES` | +| **SDK 方法** | `sdk.getSpaces(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + spaces: SgsSpaceArea[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_FAILED", + message: string +} +``` + +--- + +### 20. GET_GUIDE_STOPS + +获取指定楼层讲解点列表。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_GUIDE_STOPS` | +| **SDK 方法** | `sdk.getGuideStops(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + guideStops: SgsGuideStop[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_FAILED", + message: string +} +``` + +--- + +### 21. GET_GUIDE_STOPS_BY_HALL + +按展厅 ID 获取讲解点列表。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_GUIDE_STOPS_BY_HALL` | +| **SDK 方法** | `sdk.getGuideStopsByHall(hallId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + hallId: string | number // 必填,展厅 ID +} +``` + +**成功响应**: +```ts +{ + success: true, + guideStops: SgsGuideStop[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_PARAMS", // hallId 为空时 + message: "hallId 不能为空" +} +// 或 +{ + code: "ERR_FAILED", + message: string +} +``` + +--- + +### 22. GET_NAVIGABLE_PLACES + +获取指定楼层可导航目的地列表。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_NAVIGABLE_PLACES` | +| **SDK 方法** | `sdk.getNavigablePlaces(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.3.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + navigablePlaces: SgsNavigablePlace[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_PARAMS", // floorId 为空时 + message: "floorId 不能为空" +} +// 或 +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +### 23. PRELOAD_FLOOR + +后台预加载指定楼层的 bundle 数据到缓存(LRU,最多缓存 2 个楼层),不切换当前视图。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `PRELOAD_FLOOR` | +| **SDK 方法** | `sdk.preloadFloor(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: +```ts +{ + floorId: string | number // 必填,要预加载的楼层 ID +} +``` + +**成功响应**: +```ts +{ + success: true, + cached: boolean, // true = 命中已有缓存, false = 新加载 + loadMs?: number // 新加载时的耗时(仅 cached=false 时存在) +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_PARAMS", // floorId 为空时 + message: "floorId 不能为空" +} +// 或 +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +### 24. CLEAR_MODEL_CACHE + +清空客户端 bundle 缓存。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `CLEAR_MODEL_CACHE` | +| **SDK 方法** | `sdk.clearModelCache()` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true +} +``` + +**错误响应**: N/A + +--- + +### 25. GET_PERFORMANCE_STATS + +获取渲染基座的性能统计数据。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_PERFORMANCE_STATS` | +| **SDK 方法** | `sdk.getPerformanceStats(timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.1.0 | + +**请求 payload**: `{}` (空对象) + +**成功响应**: +```ts +{ + success: true, + currentFloorId: string | number, + manifestLoadMs?: number, + bundleLoadMs?: number, + modelParseMs?: number, + firstRenderMs?: number, + dracoUsed?: boolean, + fallbackUsed?: boolean +} +``` + +**错误响应**: N/A + +--- + +### 26. GET_DIAGNOSTICS + +获取地图整体诊断信息。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_DIAGNOSTICS` | +| **SDK 方法** | `sdk.getDiagnostics(timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.3.0 | + +**请求 payload**: +```ts +{ + mapId?: string | number // 地图 ID,默认 "1" +} +``` + +**成功响应**: +```ts +{ + success: true, + mapId: string | number, + mapName: string, + sdkVersion: string, + status: "OK" | "WARN" | "ERROR", + floors: SgsFloorDiagnosticsSummary[], + summary: { + floorCount: number, + modelReadyFloorCount: number, + poiCount: number, + spaceCount: number, + guideStopCount: number, + navigablePlaceCount: number, + routeNodeCount: number, + routeEdgeCount: number + }, + warnings: string[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +### 27. GET_FLOOR_DIAGNOSTICS + +获取单个楼层的详细诊断信息。 + +| 项目 | 值 | +|------|---| +| **postMessage action** | `GET_FLOOR_DIAGNOSTICS` | +| **SDK 方法** | `sdk.getFloorDiagnostics(floorId, timeout?)` | +| **/engine/ 支持** | ✅ | +| **最低版本** | 2.3.0 | + +**请求 payload**: +```ts +{ + floorId?: string | number // 目标楼层 ID,默认当前楼层 +} +``` + +**成功响应**: +```ts +{ + success: true, + floorId: string | number, + floorCode: string, + floorName: string, + status: "OK" | "WARN" | "ERROR", + modelReady: boolean, + poiCount: number, + spaceCount: number, + guideStopCount: number, + navigablePlaceCount: number, + routeNodeCount: number, + routeEdgeCount: number, + routePlanningReady: boolean, + warnings: string[] +} +``` + +**错误响应**: +```ts +{ + success: false +} +// error: +{ + code: "ERR_INVALID_PARAMS", // floorId 为空时 + message: "floorId 不能为空" +} +// 或 +{ + code: "ERR_NETWORK" | "ERR_FAILED", + message: string +} +``` + +--- + +## 基座主动事件一览 + +以下事件由基座通过 `SGS_MAP_EVENT` 类型主动推送,无需 SDK 请求: + +| 事件 action | 触发时机 | payload 关键字段 | +|-------------|---------|-----------------| +| `ENGINE_READY` | 引擎初始化完成 / HELLO 响应 | `engineVersion`, `protocolVersion`, `sdkVersion`, `floorId`, `poiCount`, `hasRouteNetwork` | +| `ON_MODEL_LOADING` | 模型开始加载 | `floorId`, `modelUrl` | +| `ON_MODEL_READY` | 模型解析完成 | `floorId`, `parseTimeMs` | +| `ON_MODEL_ERROR` | 模型加载失败/超时 | `floorId`, `error`, `fallbackUsed` | +| `ON_FLOOR_BUNDLE_READY` | 楼层 bundle 数据加载完成 | `floorId`, `dataVersion` | +| `ON_POI_CLICK` | 用户点击 POI | `id`, `name`, `type`, `floorId` | +| `ON_ROUTE_TRANSFER` | 跨楼层换乘提示 | `currentFloorId`, `targetFloorId`, `transferType` | + +--- + +## 错误码汇总 + +| 错误码 | 说明 | 出现指令 | +|--------|------|---------| +| `ERR_INVALID_MODE` | `SET_VIEW_MODE` 参数非法 | SET_VIEW_MODE | +| `ERR_INVALID_PARAMS` | 必填参数缺失或无效 | PLAN_ROUTE, GET_GUIDE_STOPS_BY_HALL, GET_NAVIGABLE_PLACES, PRELOAD_FLOOR, GET_FLOOR_DIAGNOSTICS | +| `ERR_ROUTING_FAILED` | NavMesh 路径规划失败 | PLAN_ROUTE | +| `ERR_NETWORK` | 后端 API 网络异常 | GET_MANIFEST, LOAD_FLOOR_BUNDLE, GET_NAVIGABLE_PLACES, PRELOAD_FLOOR, GET_DIAGNOSTICS, GET_FLOOR_DIAGNOSTICS | +| `ERR_FAILED` | 通用失败 | 多个异步指令的 catch 兜底 | +| `ERR_UNSUPPORTED` | 基座不支持该指令 | 未识别的 action(default 分支)、mock-release 专用指令在非 mock 模式下调用 | + +--- + +## 版本演进 + +| 版本 | 新增 action | +|------|------------| +| 1.0.0 | HELLO, CHANGE_FLOOR, FOCUS_TO, ADD_MARKER, ADD_BUSINESS_MARKER, REMOVE_MARKER, REMOVE_BUSINESS_MARKER, CLEAR_MARKERS, PLAN_ROUTE, HIGHLIGHT_POLYGON, CLEAR_ROUTE, RESET_VIEW, GET_STATE, GET_POIS | +| 1.5.0 | TOGGLE_LAYER, SET_VISIBLE_TYPES | +| 2.1.0 | GET_MANIFEST, LOAD_FLOOR_BUNDLE, GET_FLOOR_POIS, GET_SPACES, GET_GUIDE_STOPS, GET_GUIDE_STOPS_BY_HALL, PRELOAD_FLOOR, CLEAR_MODEL_CACHE, GET_PERFORMANCE_STATS | +| 2.3.0 | SET_VIEW_MODE, GET_NAVIGABLE_PLACES, GET_DIAGNOSTICS, GET_FLOOR_DIAGNOSTICS | diff --git a/static/sgs-map-sdk/sdk-quickstart.md b/static/sgs-map-sdk/sdk-quickstart.md index d43fdea..6c97bed 100644 --- a/static/sgs-map-sdk/sdk-quickstart.md +++ b/static/sgs-map-sdk/sdk-quickstart.md @@ -1,6 +1,8 @@ # SGS Map SDK 快速接入指南 -> 版本: V2.3.0 | 支持终端: H5 浏览器、大屏终端、微信小程序 +> 本项目同步版本: V2.5.0 | 支持终端: H5 浏览器、大屏终端、微信小程序 + +> 发布源保留了 V2.3.0 文档标题和历史 API 注释;实际发行版本以 `package.json` 与 `dist` 产物自报的 2.5.0 为准。本文只同步其公开接入资料,不声明未经本项目验证的 iframe renderer 能力。 ## 1. 引入方式 @@ -18,7 +20,7 @@ SDK 提供多种模块规范的产出文件,适配不同的应用场景: 如果 SDK 已由 `sgs-frontend-map` 发布到前端静态目录,也可以直接使用浏览器发布文件: ```html - + ``` ### 1.2 ESM Import (Webpack / Vite / Next.js) @@ -33,7 +35,7 @@ const sdk = new SGSMapSDK({ ... }); 在微信小程序中,无需引入外壳 SDK,直接通过 `web-view` 组件加载地图基座,并通过 URL 传递参数。 ```html - + ``` ### 1.4 渲染基座地址 @@ -41,7 +43,7 @@ const sdk = new SGSMapSDK({ ... }); SDK 通过 `sdkUrl` 连接独立渲染基座。正式接入时请使用地图服务平台管理员提供的基座地址,例如: ```text -https://map.museum.com/h5-sdk +https://map.museum.com/engine/index.html ``` 发布包内附带 `demo/` 目录,是一个完整的 SDK 能力展示页面。运行方式: @@ -61,7 +63,7 @@ npx -y serve . -l 5555 ```javascript const map = new SGSMapSDK({ container: 'map-container', // 挂载的 DOM 容器 ID - sdkUrl: 'https://map.museum.com/h5-sdk', // 渲染基座的 URL + sdkUrl: 'https://map.museum.com/engine/index.html', // 独立渲染引擎 URL targetOrigin: 'https://map.museum.com', // [重要] 安全防范,配置只允许该域名通信 floorId: 1, // 初始楼层 ID timeout: 5000 // API 调用的默认超时时间(毫秒) diff --git a/tests/unit/SgsMapSdkReleaseContract.spec.ts b/tests/unit/SgsMapSdkReleaseContract.spec.ts new file mode 100644 index 0000000..41a89e1 --- /dev/null +++ b/tests/unit/SgsMapSdkReleaseContract.spec.ts @@ -0,0 +1,33 @@ +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { describe, expect, it } from 'vitest' + +const sdkDirectory = resolve(process.cwd(), 'static/sgs-map-sdk') +const readSdkFile = (name: string) => readFileSync(resolve(sdkDirectory, name), 'utf8') + +describe('SGS Map SDK 2.5.0 release contract', () => { + it('keeps the local package, bridge self-report, and default Engine path aligned', () => { + const packageJson = JSON.parse(readSdkFile('package.json')) as { version: string } + const bridge = readSdkFile('index.global.js') + const dataSource = readFileSync(resolve(process.cwd(), 'src/config/dataSource.ts'), 'utf8') + + expect(packageJson.version).toBe('2.5.0') + expect(bridge).toContain('"2.5.0"') + expect(bridge).toContain('/engine/index.html') + expect(dataSource).toContain("SGS_MAP_SDK_VERSION = '2.5.0'") + expect(dataSource).toContain("const defaultSgsEngineUrl = '/engine/index.html'") + }) + + it('ships every runtime bridge artifact required by the flattened package', () => { + [ + 'index.global.js', + 'index.global.js.map', + 'index.mjs', + 'index.mjs.map', + 'index.d.ts', + 'index.d.mts' + ].forEach((file) => { + expect(existsSync(resolve(sdkDirectory, file))).toBe(true) + }) + }) +}) diff --git a/tests/unit/SgsMapService.spec.ts b/tests/unit/SgsMapService.spec.ts index b5b2a3f..d9fa3f4 100644 --- a/tests/unit/SgsMapService.spec.ts +++ b/tests/unit/SgsMapService.spec.ts @@ -32,8 +32,8 @@ const createHarness = async () => { const service = new SgsMapService({ container: {} as HTMLElement, - scriptUrl: '/static/sgs-map-sdk/index.global.js?v=2.4.1', - sdkUrl: '/h5-sdk', + scriptUrl: '/static/sgs-map-sdk/index.global.js?v=2.5.0', + sdkUrl: '/engine/index.html', targetOrigin: 'https://map.example.com', floorId: 'L1', timeout: 7000 @@ -42,19 +42,31 @@ const createHarness = async () => { await vi.waitFor(() => expect(SDKConstructor).toHaveBeenCalledOnce()) listeners.get('ready')?.({ - engineVersion: '2.4.1', + engineVersion: '2.5.0', protocolVersion: 2 } as never) await ready - return { sdk, service } + return { sdk, service, SDKConstructor } } beforeEach(() => { loadSgsMapScript.mockReset() }) -describe('SGS Map SDK 2.4.1 service adapter', () => { +describe('SGS Map SDK 2.5.0 service adapter', () => { + it('向 SDK 构造器传入完整的 Engine 桥接配置', async () => { + const { SDKConstructor } = await createHarness() + + expect(SDKConstructor).toHaveBeenCalledWith({ + container: expect.any(Object), + sdkUrl: '/engine/index.html', + targetOrigin: 'https://map.example.com', + floorId: 'L1', + timeout: 7000 + }) + }) + it('透传楼层强制刷新选项和统一命令超时', async () => { const { sdk, service } = await createHarness() diff --git a/vite.config.ts b/vite.config.ts index 0318930..b4600f4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -30,7 +30,7 @@ export default defineConfig(({ mode }) => { ) const buildPlatform = process.env.UNI_PLATFORM === 'mp-weixin' ? 'mp-weixin' : 'h5' const appApiProxyTarget = normalizeEnvUrl(env.DEV_PROXY_APP_API_TARGET, 'http://localhost:3001') - const h5SdkProxyTarget = normalizeEnvUrl(env.DEV_PROXY_H5_SDK_TARGET, appApiProxyTarget) + const engineProxyTarget = normalizeEnvUrl(env.DEV_PROXY_ENGINE_TARGET, appApiProxyTarget) const sdkProxyTarget = normalizeEnvUrl(env.DEV_PROXY_SDK_TARGET, appApiProxyTarget) const museumAssetsProxyTarget = normalizeEnvUrl(env.DEV_PROXY_MUSEUM_ASSETS_TARGET, 'http://localhost:9000') const minioProxyTarget = normalizeEnvUrl(env.DEV_PROXY_MINIO_TARGET, appApiProxyTarget) @@ -51,8 +51,8 @@ export default defineConfig(({ mode }) => { target: appApiProxyTarget, changeOrigin: true }, - '/h5-sdk': { - target: h5SdkProxyTarget, + '/engine': { + target: engineProxyTarget, changeOrigin: true }, '/sdk': {