---
name: easymap-dev
description: Easymap 7.1.17 旗艦版開發技能。用於 Easymap、dgWKT、dgSource、dg3D、MapLibre runtime、WMTS、OpenLayers 10、Cesium 1.146 與 CesiumEngine 的整合、除錯與相容升級，包含 Wasm-only CSP／CORS 部署；保留既有 Easymap 公開 API。
---

# Easymap 7.1.17 旗艦版 AI 開發技能

本技能包更新於 2026-10-14，對應 Easymap 7.1.17 build `7.1.17_2026-10-14_10-22`、loader cache-buster `7.1.17_20261014`。目標是讓 AI 協助升級、整合或擴充圖台時，能先維持既有案場的公開合約，再使用新版 OpenLayers、Cesium、WMTS 與 MapLibre runtime 能力。

## 產品定位

- 7.1.17 是 Easymap 7 的旗艦最新版本；7.0.25 是長期相容穩定版本。
- 既有業務程式仍以 `Easymap + dg* + map.addItem(...) + map.removeItem(...)` 為主。
- `dgWKT`、`dgSource`、`dg3D`、`dgMarker`、`dgGeoJson`、`map_ini.js` 與 loader 是既有案場的相容邊界；未獲要求不可任意改成另一套 API。
- 預設 2D/3D runtime 使用 OpenLayers 10.10.0、OLCS 2.23.1、Cesium 1.146.0；獨立 CesiumEngine 為選配。MapLibre 是另一條 renderer runtime，不代表已支援每個 Cesium 專屬功能。

## 載入與部署規則

預設 loader：

```html
<script src="https://www.focusit.com.tw/easymap/easymap/easymap.js"></script>
```

MapLibre runtime：

```html
<script src="https://www.focusit.com.tw/easymap/easymap/easymap.js?v=maplibre"></script>
```

- loader 會載入 `map_ini.js`、`7/dg.js`、`7/easymap.js` 與支援資產；它不是單一 JS 檔。
- 部署 7.1.17 時，完整保留 `Easymap` 根目錄、`7/` 的 `Cesium.js`、`Workers/`、`Assets/`、`ThirdParty/`、`Widgets/` 和 `MapLibre/`，清理被取代的舊 Worker chunk。採用 Engine 時另部署完整 `CesiumEngine/`，不要只覆蓋主 JS 或混用其他版本資產。
- `map_ini.js` 是預設底圖、中心、解析度及內建服務的 runtime contract；遇到啟動／底圖異常先確認它是否同版載入。

## 公開使用模式

```js
var map = new Easymap("map");
var layer = new dgWKT(data, "EPSG:4326");
map.addItem(layer);
```

- 優先使用 `addItem`、`removeItem`、`attachEvent`、`setDrawMode`、`openInfoWindow` 等公開生命週期。
- 除非正在維護 SDK runtime，不要直接操作 `_olmap`、`_items`、`_instance`、`_olcesium` 或 MapLibre 的內部 layer/source id。
- 查範例先看 `offical/api.js`；查 SDK 行為再依序看 `EasymapDev/src/easymap.js`、`digi3d.js`、`dg/*.js`、`map.init.js`。

## 7.1.17 重點

- 2026-10-14 更新 Cesium 1.146.0（core 0.1.0／engine 26.4.0／widgets 16.3.0），Full 與選配 Engine 同版；Engine 的 core 正式匯出由 SDK 處理，業務層繼續使用公開 API。

- `dgSource("WMTS", options)` 預設 `autoCapabilities:true`，可由 capabilities 補齊有效範圍、格式及樣式；手動指定的值優先，失敗時維持既有圖源；可設為 `false` 停用。
- WMTS matrix tile、OL 10 `dgWebGLPoint`、ArcGIS/Rex MVT、OLCS proxy 與 `dgWKT.setClusterDistance([distance, minDistance])` 已納入本版相容修正。
- `dgWKT` 沒有 hover callback 時會略過滑鼠命中計算；拖曳與縮放動畫時會暫停 hover 計算，點選與群聚行為仍維持。
- 3D 點位符號的正式預設為：`dgMarker` 與 `dgWKT` Point／MultiPoint 使用 `relativeToGround` 並抬高 15m；`dgWKT` 的線、面與舊式 `dgPolyline`、`dgPolygon`、`dgCurve`、box 繪圖使用 `clampToGround`，避免切換 3D 後被 terrain 遮蔽。加入圖台後可用 `setAltitudeMode(mode)`、`setTerrainOffset(meters)` 與 `setAlwaysVisible(enabled)` 微調；`setAlwaysVisible(true)` 只針對該 icon 寫入 Cesium 深度測試距離，不改全域 terrain depth test。`dgMarker.setZ()` 仍是 absolute-height 語意。
- 舊式線面以 `[0, 0]` 表示實線時，7.1.17 會轉成無 dash style，避免 OL-Cesium 誤建零長度 PolylineDash material 使 3D 線消失；明確設定的虛線陣列維持原樣。直接改 `dgWKT.getStyle()` 後要呼叫 `dgWKT.refresh()` 一次，讓 2D 與 3D 同步重繪；不要以 remove/add 當成樣式更新手段。
- MapLibre runtime 仍以同一組 `Easymap + dg*` 公開 API 為設計目標；使用 3D 專屬功能前，必須依目前 renderer 實作與案場資料做實測，不能假定每個 Cesium 專屬能力都已由 MapLibre 取代。

## CesiumEngine、CSP 與跨來源部署

先確認案場選用的 runtime，不要只換 CSP 關鍵字：

| Runtime | 契約 |
| --- | --- |
| Full 預設 `Easymap/7/Cesium.js` | 保留 Viewer widgets；維持原案場政策，不宣稱 Wasm-only 或完整 Trusted Types 相容 |
| 選配 `CesiumEngine/Cesium.js` | 無 Viewer／Knockout，保留 `window.Cesium` 與 Scene／OLCS；可用 Wasm-only CSP，Viewer／3D 鷹眼不適用 |
| Lite | 不附 Cesium runtime；先設定 base URL、載入外部 Engine，再指定同一來源後啟用 3D |

以下放在已載入 Easymap、DOM ready 後執行的外部 JS。相對路徑以案場頁面目錄為例：

```js
window.CESIUM_BASE_URL = new URL("CesiumEngine/", document.baseURI).href;
var map = new Easymap("map");
map.set3DCesiumPath(new URL("Cesium.js", window.CESIUM_BASE_URL).href);
map.enable3D({ webgl: "webgl2" }, function () {
  console.log("3D ready", Cesium.VERSION);
});
```

Lite 必須先載入外部 `CesiumEngine/Cesium.js`，並以 `set3DCesiumPath()` 指向該來源。Lite loader 的預設 `7/Cesium.js` 並不存在，不能把那個 default 當成外部來源。

同站 engine-only 3D document 的政策片段：

```text
script-src 'self' blob: 'wasm-unsafe-eval';
worker-src 'self' blob:;
```

- `wasm-unsafe-eval` 只允許 Wasm 編譯／執行，不允許 JS `eval/new Function`；不要同時加入 `unsafe-eval`。其餘來源、CSS、影像、連線、nonce／Trusted Types 依實際案場政策處理，這兩項不是完整 CSP。
- 同站內建 module Worker 的 blob 啟動器繼承建立頁 CSP；訊息、error、constructor 失敗、terminate 都回收 URL。冷頁不會獲得 Wasm 權限，靜態 Worker 可以維持 strict header。
- 自訂 Worker 絕對 URL、未知 module ID 或自訂 prefix 保留直接載入，以維持相對 import／資源 URL 的基準；自訂 Wasm Worker 須配置自己的政策，不假定套用內建 adapter。
- 跨來源 CORS 由提供 JS module、Wasm、圖磚或模型的服務端設定；配合 `script-src/worker-src/connect-src/img-src` allowlist 及 `blob:`。不要在前端試圖繞過瀏覽器限制。
- 必須完整同步同版 `Workers/Assets/ThirdParty`、LICENSE、manifest；先檢查 MIME／module imports／Wasm，再測真實 Scene、模型與圖資。
- 2026-10-14 使用者確認本次 SDK Fortify 重掃 0 項風險、驗收通過。既有九處字串編碼屬 scanner compatibility，保留 null／HTTP 原值，不等於加密、憑證保護或 CSRF 防護，也不是跨版本或全案場的安全保證。
- 本機已驗證 Scene、Draco 初始化、合成地形、WKT／marker、三輪 2D/3D 切換、自訂 Worker 相對路徑與兩個 origin。Draco 初始化不是實際模型解碼證據；其他 codecs、真實模型／3D Tiles／正式 terrain、完整業務 UI／HTTPS CDN／IIS／production 另需驗收。

維護 SDK 時沿用 lockfile，透過 `npm run build_cesium_engine` 產出 Engine；core／engine 匯出需消除同名 deprecated wrapper，Worker／掃描 adapter 的精確匹配與 drift guards 不可放寬。修改 source 後 build，勿手改生成 Worker 或 node_modules。

## 查找與驗證

1. 舊案相容問題：先比對 `offical/api.js` 的公開範例與呼叫方式。
2. OpenLayers/Cesium 問題：查 `EasymapDev/src`，改完執行最小相關 Node test。
3. MapLibre 問題：先讀 `offical/Easymap/MapLibre/skill.md`，再執行對應 `EasymapDev/tests/maplibre-*.test.js`。
4. 發行驗證：重建 Full / Lite，確認 release zip 同時含 loader、`map_ini.js`、Cesium runtime、Workers 與 MapLibre runtime。

不要以 HTTP 200、Webpack exit code 或單一 bundle 存在，宣稱案場已成功升級；仍須分開驗證 browser、IIS、外部 WMTS/CORS、3D 資產與使用者實際資料。

## 開發前先選對工作層級

先判斷需求落在哪一層，再開始寫程式。不要因為底層是 OpenLayers、Cesium 或 MapLibre，就直接改用它們的原生 API。

| 任務 | 優先位置 | 首選作法 |
| --- | --- | --- |
| 舊案新增圖層、點位、事件、popup、繪圖 | 案場 app code | `Easymap` + `dg*` + `addItem/removeItem` |
| WMS、WMTS、XYZ、MVT 等服務整合 | 案場 app code | `new dgSource(type, options)`，比對 `api.js` 同類範例 |
| WKT/KML/GeoJSON 顯示與互動 | 案場 app code | `dgWKT` / `dgKml` / `dgGeoJson`，再用公開 setter |
| Cesium 地形、glTF、3D Tiles | 案場 app code + 3D callback | 先 `enable3D(callback)`，資產 ready 後在 `onload` 操作 |
| SDK 增加 `dg*` 行為或修正 add/remove | SDK source | model class、`addItem` dispatch、2D/3D render、remove cleanup 一起檢查 |
| MapLibre renderer 行為 | MapLibre source / tests | 先確認 public contract，勿把 Cesium 專屬內部物件搬到 MapLibre |
| 預設底圖、中心、解析度 | `map_ini.js` / `map.init.js` | 只在需求明確為預設設定時修改 |

### 公開契約優先

Easymap 是以 browser global 與 CDN/static assets 發行的 SDK，不是讓案場直接 `import` 的 tree-shaking ESM package。一般整合應保持：

```text
Easymap loader
  + map_ini.js
  + 7/dg.js
  + 7/easymap.js
  + Cesium / MapLibre / 其他 runtime assets
  + window.Easymap 與 window.dg* globals
```

- 優先使用 `map.addItem(...)`、`map.removeItem(...)`、`map.attachEvent(...)`、`map.detachEvent(...)`、`map.setDrawMode(...)`、`map.openInfoWindow(...)`。
- `addItem` 會配置內部 id、建立 OpenLayers/Cesium instance、維護點擊/hover/remove 關係；不要以直接改 `_items`、`_instance` 或 `_olmap` 取代它。
- 很多 setter 需要完成 `addItem` 後才有 renderer instance；例如 marker 的位置、內容、地形高度與 WKT 的 feature 操作。
- 只有在明確維護 SDK 時才讀寫 `_olmap`、`_olcesium`、`_items`、`_instance`，並且要同時確認 remove、hover、popup 與 3D 路徑沒有遺漏。

## 官方文件與權威查找順序

發生疑問時，優先以同版本的可執行範例與 SDK source 為準；網路搜尋或原生 OpenLayers/Cesium 文件只能補充底層概念，不能推翻 Easymap 的公開相容契約。

1. [Easymap API 範例中心](https://www.focusit.com.tw/easymap/api.aspx)：可直接操作、搜尋與執行範例。
2. [完整 `api.js` 範例索引](https://www.focusit.com.tw/easymap/api.js)：查 `funName`、`methode` 與實際 options 形狀。
3. [Easymap 7 開發文件](https://www.focusit.com.tw/easymap/easymapdoc.aspx)：公開類別、載入方式、2D/3D 與排錯說明。
4. [SDK 下載與部署資料](https://www.focusit.com.tw/easymap/download.aspx)：確認發行版本、完整 archive 與 runtime assets。
5. 本機官網範例：`D:\GD\5project\108easymap\code\easymap7\offical\api.js`。
6. SDK source：`D:\GD\5project\108easymap\code\easymap_official\EasymapDev\src`。

### `api.js` 是範例資料庫，不是單一範例檔

`offical/api.js` 的主要結構是 `window.data = [...]`。每個分類通常有 `tab`，範例節點則會有：

- `name`：範例顯示名稱。
- `funName`：可搜尋的函式識別字。
- `methode`：真正可抽取的示範函式。

先找同類範例，再只摘取要用的公開 API；不要連同範例中心的 `myW`、`smallComment`、表格、按鈕或 jQuery UI helper 一起複製到案場。

```powershell
# 在官網 repo 內快速找到完整示範與產生來源
rg -n "funName: 'f_marker'|new dgSource|enable3D|setDrawMode" offical/api.js offical/appjs
```

高頻入口：

| 需求 | 先搜尋的 `funName` 或 token |
| --- | --- |
| 初始化與載入時機 | `dgFunc0`、`new Easymap` |
| Marker、icon、popup | `f_marker`、`f_markers`、`dgMarker` |
| 大量點位 | `f_markers_tooMany`、`dgWKT`、`dgWebGLPoint` |
| WKT 字串、陣列、URL | `f_dgWKT_string`、`f_dgWKT_array` |
| 繪圖與量測 | `f_MeasuringToolsInside`、`setDrawMode` |
| WMS / WMTS / XYZ / MVT | `new dgSource`、`WMTS`、`VectorTile` |
| 3D 開關與地形 | `f_enable3D`、`f_enable3DTerrain`、`f_get3DGroundAltitude` |
| glTF / 3D Tiles | `dg3D('gltf'`、`dg3D('3dtiles'`、`onload` |

`offical/appjs` 內的 `.js` 多數是要被 `appjs/merge.php` 注入的物件片段，不是單獨可執行的 JavaScript 檔。改完片段必須重新產生 `api.js`；驗證時以 object wrapper 做語法檢查，不能把每一支片段直接當 standalone script 執行。

## 初始化與最小可用範例

容器必須存在且有可見寬高。若 script 在 `<head>` 載入，應等 DOM ready；官網 `dgFunc0` 範例同樣把初始化放在 `window.onload` 或容器之後。

```html
<div id="map" style="width:100%;height:100vh"></div>
<script src="https://www.focusit.com.tw/easymap/easymap/easymap.js"></script>
<script>
window.onload = function () {
  var map = new Easymap("map");
  map.zoomToXY(new dgXY(121.517, 25.047), 14);
};
</script>
```

座標多以 `new dgXY(lon, lat)` 表達。遇到位置錯誤時，依序檢查經緯順序、資料 SRS、`EPSG:3826` / `EPSG:3828` / `EPSG:4326` 與是否需要 `map.projTransfer(...)`；不要直接把 OpenLayers 內部 `EPSG:3857` 座標塞給公開 API。

## 可直接改寫的公開 API 範例

下列程式只使用公開 API。URL、欄位、SRS、樣式與操作時機仍要換成實際案場資料。

### Marker：加點、事件與 3D 地形可視性

```js
var icon = new dgIcon("images/pin.png", 32, 32);
var marker = new dgMarker(new dgXY(121.517, 25.047), icon, false);

marker.onclick = function (xy) {
  map.openInfoWindow(xy, "<strong>站點名稱</strong>", 180, 90);
};

map.addItem(marker);

// 只有需要調整地形顯示時，才在 addItem 後設定。
map.enable3D(function () {
  marker.setAltitudeMode("relativeToGround");
  marker.setTerrainOffset(20);
  // marker.setAlwaysVisible(true); // 僅在 terrain/tiles 遮蔽確實是需求時使用
});
```

`setAlwaysVisible(true)` 是 icon 層級的 Cesium depth-test 設定，不是「看穿地底」的全域開關。預設 `relativeToGround + 15m` 已用於 7.1.17 的 `dgMarker` 與 `dgWKT` Point／MultiPoint；`dgWKT` 線面及舊式 `dgPolyline`、`dgPolygon`、`dgCurve`、box 繪圖會以 `clampToGround` 貼地。若既有程式呼叫 `marker.setZ()`，它保持 absolute-height 語意，不能假定它等同 terrain offset。

### `dgWKT`：陣列、圖片點位與 feature click

```js
var records = [
  {
    label: "水位站 A",
    wkt: "POINT(121.517 25.047)",
    pic: "images/pin.png"
  },
  {
    label: "警戒範圍",
    wkt: "POLYGON((121.50 25.04,121.53 25.04,121.53 25.06,121.50 25.06,121.50 25.04))"
  }
];

var layer = new dgWKT(records, "EPSG:4326", function () {
  // 只有確實依賴載入完成的後續操作放這裡。
});

layer.setFeatureClick(function (properties, geometryType, xy) {
  map.openInfoWindow(xy, properties.label || geometryType, 220, 120);
});

map.addItem(layer);
```

### dgWKT：直接修改樣式後 refresh

當既有程式透過 getStyle() 改顏色、寬度、填色或 icon 設定時，不需要 remove/add。全部變更後呼叫一次 refresh()，它會逐筆通知 OpenLayers feature，也會讓 OL-Cesium 在 3D 重建對應 primitive：

~~~js
var a = new dgWKT("LINESTRING(121 23,120 22)", "EPSG:4326");
map.addItem(a);

a.getStyle().LineString.getStroke().setColor("RED");
a.getStyle().LineString.getStroke().setWidth(10);
a.refresh();
~~~

refresh() 在尚未 addItem 時是安全 no-op，完成後回傳 dgWKT 自己，可用於鏈式呼叫。舊式 draw 物件的預設 [0, 0] 代表實線；不要把它改成無意義的虛線 pattern。

- 大量點、線、面優先考慮 `dgWKT`，不要用數千個 HTML `dgMarker`。
- 只有需要 hover 行為時才設定 `setFeatureHover`；7.1.17 在沒有 hover callback、或拖曳/縮放動畫中，會避免不必要的命中計算。
- 對 Point／MultiPoint 圖示可在 `addItem` 後使用 `setAltitudeMode`、`setTerrainOffset`、`setAlwaysVisible`；不要對線/面套用 icon-only 可視性預期。

### `dgSource`：WMTS 與 WMS

```js
var wmts = new dgSource("WMTS", {
  name: "MyWMTS",
  chname: "我的 WMTS",
  bg: false,
  url: "https://example.com/wmts",
  layer: "workspace:layer",
  matrixSet: "EPSG:3857",
  format: "image/png",
  opacity: 0.8,
  autoCapabilities: true
});

map.addItem(wmts);
```

```js
var wms = new dgSource("WMS", {
  name: "MyWMS",
  chname: "我的 WMS",
  bg: false,
  url: "https://example.com/geoserver/wms",
  layer: "workspace:layer",
  projection: "EPSG:4326",
  singleTile: true,
  opacity: 0.7
});

map.addItem(wms);
```

`autoCapabilities:true` 是 7.1.17 WMTS 的預設行為，用來補齊可用範圍、format、style 等資訊；手動 options 優先。服務有特殊 matrix、CORS、認證或固定樣式時，先以官網同類範例和實際 capabilities 回應驗證，不要因為一個 HTTP 200 就認定圖層可畫。

### 繪圖完成後轉成 WKT

```js
map.clearDraw();
map.setDrawMessage("開始繪製", "移動滑鼠繼續繪製");
map.setDrawMode("polygon", function () {
  var measure = map.getDrawMeasure();
  var wkt = map.geometryToWKT(measure.geom);
  var result = new dgWKT(wkt, "EPSG:4326");
  map.addItem(result);
});
```

常見 mode 包含 `polyline`、`polygon`、`circle`、`box`、`erbl`。需要清除時用 `clearDraw()` 或取消目前操作用 `cancelDrawMode()`，不要自行清空 OpenLayers interaction。

### 3D：等待圖台與資產 ready

```js
map.enable3D(function () {
  map.enable3DTerrain();

  var model = new dg3D("gltf", "data/model.glb", {
    dgxy: new dgXY(121.517, 25.047),
    z: 10,
    scale: 1,
    onload: function (layer) {
      layer.setZ(20);
      layer.setScale(1.2);
    }
  });

  map.addItem(model);
});
```

`enable3D(callback)` 是 3D viewer 可用的界線；GLTF / 3D Tiles 的 load-dependent 操作放到 options `onload`，不要在 `map.addItem(model)` 下一行直接做。3D Tiles callback 要相容新版 `(tileset, layer)` 與舊版只傳 wrapper 的形態：

```js
onload: function (tileset, layer) {
  layer = layer || tileset;
  layer.setZ(30);
}
```

`3dion` 仍依既有 `.loaded()` 路徑，不應為了統一語法硬改成 options `onload`。所有 3D 範例都還要以真實 terrain、斜視角、3D Tiles 與 CORS 資產進行 browser 驗收。

## SDK 原始碼導航

SDK 需求不是只改一個檔案就結束。先由公開 API 反查，再沿生命週期確認變更會不會破壞 2D、3D 或 cleanup。

| 行為 | 優先檔案 | 仍須確認 |
| --- | --- | --- |
| 建圖、popup、事件、draw、`addItem/removeItem` | `EasymapDev/src/easymap.js` | dispatch、layer/feature tags、remove cleanup |
| Cesium、OLCS、terrain、相機、3D assets | `EasymapDev/src/digi3d.js` | `enable3D` 時機、viewer、Cesium runtime 路徑 |
| 共用座標、投影、資料轉換、基底狀態 | `EasymapDev/src/digimap.js` | 公開回傳資料的 SRS 與 backward compatibility |
| Marker 的 public setter / 3D entity | `EasymapDev/src/dg/dgMarker.js` | 2D overlay、3D entity、absolute vs terrain height |
| WKT parsing、style、feature interaction | `EasymapDev/src/dg/dgWKT.js` | Point/MultiPoint、line/polygon、cluster、hover |
| WMS/WMTS/XYZ/MVT options | `EasymapDev/src/dg/dgSource.js` | `_getTileLayer`、capabilities、CORS、fallback |
| public `window.dg*` export | `EasymapDev/src/dg/index.js` | global contract 與 Full/Lite bundle |
| 預設中心、底圖、圖資設定 | `EasymapDev/src/map.init.js` → deploy `map_ini.js` | 同版 loader 與 runtime asset |

新增一個 SDK 內部資料型別時，至少確認：建立穩定 `_type`、`dg/index.js` public export、`addItem` branch、`removeItem` cleanup、feature/layer 的 Easymap tag、interactive routing，以及 2D/3D 是否有明確行為。不應只加 constructor 就宣稱 SDK 已支援。

## 除錯順序與驗收邊界

| 現象 | 先查 | 不要直接做 |
| --- | --- | --- |
| 地圖空白 | 容器寬高、loader、`map_ini.js`、console 404/CORS | 先改 SDK 核心 |
| layer/marker 不見 | 是否 `addItem`、SRS、extent/zoom、`_type` | 直接塞 raw OL layer |
| popup / click 無效 | `onclick` 或 `setFeatureClick`、圖層次序、addItem 時機 | 改內部 event arrays |
| WMTS 不顯示 | capabilities、matrixSet、format/style、CORS | 關掉 autoCapabilities 後猜參數 |
| 3D 操作不生效 | `enable3D(callback)`、asset URL、onload、terrain/CORS | `addItem` 後立刻呼叫 3D setter |
| icon 半埋 terrain | Point/MultiPoint、預設 relative offset、`setTerrainOffset` | 直接關閉全域深度測試 |
| 大量資料卡頓 | `dgWKT` / cluster / `dgWebGLPoint`、hover 設定 | 無限制新增 HTML marker |
| MapLibre 行為不同 | `?v=maplibre` runtime、MapLibre source/tests、公開合約 | 假設 Cesium 私有 API 仍存在 |

本機測試、webpack build、ZIP contract、browser、IIS、外部服務與使用者資料是不同證據層級。回報時必須分開寫明哪些已驗證、哪些尚未驗證；不可將 bundle build 成功描述成正式站台或 3D 實景已驗收。

## 發行與修改後的最小檢查

1. app 範例改動：確認對應 `api.js` 範例、fragment merge 與生成後 `api.js` syntax。
2. SDK API 改動：至少執行相關 Node test，並確認 Full/Lite 都有必要的 public export。
3. runtime / loader / `map_ini.js` 改動：確認生成 archive 保留根 loader、`map_ini.js`、`7/dg.js`、`7/easymap.js`、Cesium Workers/Assets/ThirdParty/Widgets、MapLibre runtime 與完整選配 CesiumEngine。
4. 發行前：Full/Lite serial build、archive entry contract、版本與 `fix_history.txt`、下載頁/文件/AI skill 同步；PilotGaea archive 只能含自研 `easymap_pilotgaea.js` adapter，不能夾帶第三方 runtime。
5. 完成後：保留 browser、IIS、CDN、WMTS、3D/terrain 與真實案場資料仍待驗收的界線，除非已有各自的實測證據。
