From 0102ffbb3c931dd6d1cf856705abb0e4c4d97dda Mon Sep 17 00:00:00 2001 From: que01 Date: Wed, 12 Aug 2026 09:06:04 +0800 Subject: [PATCH] docs(assets): document reusable asset package contract --- .trellis/spec/config/index.md | 26 +++- .trellis/spec/pipeline/cli-and-stages.md | 179 ++++++++++++++++++----- .trellis/spec/pipeline/index.md | 23 +-- .trellis/spec/preview/index.md | 10 +- 4 files changed, 182 insertions(+), 56 deletions(-) diff --git a/.trellis/spec/config/index.md b/.trellis/spec/config/index.md index 087f455..e91c80a 100644 --- a/.trellis/spec/config/index.md +++ b/.trellis/spec/config/index.md @@ -68,11 +68,14 @@ cp config/examples/template.json config/areas/my-area.json | `intermediates` | `true` | 旧名 `qgis` 仍被接受 | | `blender` | `true` | | | `cesium` | `true` | | +| `compress` | `true` | 压缩 staged GLB,供随后发布使用 | +| `package` | `true` | 将通过校验的静态模型原子发布到 `package/` | +| `preview` | `true` | 基于已发布 package 写验证预览及 `_preview/` 动态资源 | -`reimport` 和 `preview` **在这里配也没用**——`normalizeAreaConfig` 把它们 -硬编码为 `false`,只能靠 `--stages` 显式请求。`compress` 始终默认开启。 +`reimport` **在这里配也没用**——`normalizeAreaConfig` 把它硬编码为 `false`,只能靠 +`--stages` 显式请求。`compress`、`package` 和 `preview` 始终默认开启。 -> 恢复动作(reimport)和补丁动作(preview)不该被一份配置文件变成默认行为;压缩是标准交付链的一部分。 +> 恢复动作(reimport)不该被一份配置文件变成默认行为;压缩、发布和验证预览是标准交付链的一部分。 `--stages` 会整体覆盖这里的默认值。 @@ -133,7 +136,8 @@ cp config/examples/template.json config/areas/my-area.json ### `compress` 完整构建和显式 `--stages compress` 都使用此配置。默认压缩链是 texture resize + WebP -transcode,成功后覆盖标准 `.glb`、`.json` 和预览 HTML;未压缩源只保留在构建临时目录。 +transcode,成功后替换**package staging** 中的主 GLB 与 manifest;未压缩源只保留在构建临时目录。 +只有随后的 `package` 阶段才会原子发布到 `package/`。 | 字段 | 默认 | 说明 | |---|---|---| @@ -161,14 +165,16 @@ transcode,成功后覆盖标准 `.glb`、`.json` 和预览 HTML;未 ### `outputs`(逃生舱) -默认全部从 `id` 推导为 `//.`。需要定制时逐项覆盖: +默认全部从 `id` 推导。发布给下游的静态资产固定在 +`//package/`,临时静态资产在 `_pipeline/package-staging/`,预览专用动态资源在 +`//_preview/`。需要定制时逐项覆盖: ```json { "outputs": { "areaDir": "/absolute/path/to/custom-area", "blend": "/absolute/path/to/custom.blend", - "glb": "/absolute/path/to/custom.glb", + "packageDir": "/absolute/path/to/custom-package", "cesiumPreview": "/absolute/path/to/custom-preview.html" } } @@ -178,6 +184,11 @@ transcode,成功后覆盖标准 `.glb`、`.json` 和预览 HTML;未 `qgisProject`、`qgisPreview`、`blend`、`render`、`glb`、`metadata`、`cesiumPreview`、 `vehicleRoute`、`vehicleModel`、`pipelineDir`、`stageManifestDir`。 +静态发布路径另有 `packageDir`、`packageStagingDir`、`packageManifest`、 +`packageStagingManifest`、`packageModelDir`、`packageStagingModelDir` 与 +`packagePrimaryGlb`;预览路径另有 `previewDir` 与 `previewDescriptor`。除非在迁移旧调用, +不要覆盖 `glb` / `metadata`:它们是 staging 内部路径,不是下游资产入口。 + **优先改 `fileStem` 或 `areaDir`**——它们能一次性影响全部派生路径。逐个覆盖容易漏。 --- @@ -219,7 +230,8 @@ transcode,成功后覆盖标准 `.glb`、`.json` 和预览 HTML;未 | 布尔字段用 `\|\|` 兜底 | `false` 被翻转 | | 给新字段造顶层平铺别名 | 扩大历史包袱 | | 逐个覆盖 `outputs` 而不用 `fileStem` | 漏掉某个产物路径 | -| 在 `stages` 里配 `reimport` / `preview` | 无效,被硬编码为 false | +| 将 `glb` / `metadata` 当作下游入口 | 它们位于 staging;应只读取 `package/manifest.json` | +| 在 `stages` 里配 `reimport` | 无效,被硬编码为 false | | 加数值字段不做范围校验 | 错配置在中途才崩,输出已被破坏 | --- diff --git a/.trellis/spec/pipeline/cli-and-stages.md b/.trellis/spec/pipeline/cli-and-stages.md index 8505655..86fd810 100644 --- a/.trellis/spec/pipeline/cli-and-stages.md +++ b/.trellis/spec/pipeline/cli-and-stages.md @@ -25,9 +25,10 @@ ### 1. Scope / Trigger -`compress` 是完整构建和 `all` 的默认末端阶段。它在已有 Cesium GLB 上执行 texture -resize + WebP transcode,并以标准 `.glb`、`.json` 和预览 HTML 替换未压缩导出。 -未压缩版本只在 `/_pipeline/compress-*` 临时目录中存在,成功或失败后都会清理。 +`compress` 是完整构建的默认阶段,位于 `cesium` 与 `package` 之间。它在 +`_pipeline/package-staging/` 的主 GLB 上执行 texture resize + WebP transcode,并替换 staged +主 GLB 与 staged manifest;不触碰已发布的 `package/`,也不重写 preview HTML。未压缩版本只在 +`/_pipeline/compress-*` 临时目录中存在,成功或失败后都会清理。 `scripts/compress-glb.js` 是该阶段调用的低层脚本,也可单独运行做实验。 @@ -63,18 +64,16 @@ npm run compress:glb -- --input in.glb --output out.glb [options] - 输入必须是现有 `.glb` 文件;`--output` 必须不同于 `--input`。 - 低层脚本仍输出到与输入不同的路径;`build-area.js` 使用临时输入/输出路径,避免原地压缩。 -- `compress` 阶段依赖标准 `glb`、`metadata` 和 `cesiumPreview` 已存在,并在全部临时交付文件 - 生成和校验后以 `renameSync` 替换它们。 +- `compress` 阶段依赖 staged `glb` 与 `metadata` 已存在,并在全部临时交付文件生成和校验后 + 以 `renameSync` 替换它们。 - 默认链固定为 `gltf-transform resize -> gltf-transform webp`。 - `compress` 阶段默认 `textureSize=768`;低层脚本单独运行时默认 `--texture-size 1024`。 -- `build-area.js` 最终只保留 `.glb`、`.json` 和 - `-cesium-preview.html`;不会写并列 `-compressed-*` 交付物。 -- 伴生 metadata 的 `asset` 与 `id="main"` 资产 URL 保持为标准 GLB 文件名;其余资产(包括 - `category="semantic"` 的 Cesium 分类检查 GLB)必须原样保留。 -- preview HTML 只替换 `window.OSM_ASSET_PREVIEW_CONFIG` 的 `glbName` / - `metadataName` 和 loading 文案,不改 preview runtime。 +- `build-area.js` 不会写并列 `-compressed-*` 交付物;`package` 是唯一能发布静态文件到 + `package/` 的阶段。 +- staged manifest 的 `assets[*].uri` 必须保持 package-relative,如 `models/.glb`;分类 + layer GLB 必须原样保留。 - 成功时 stdout 打印 `GLB_COMPRESS_DONE `,包含压缩前后大小、image / - non-image bytes、结构计数、扩展、metadata / preview 输出路径。 + non-image bytes、结构计数、扩展与 metadata 输出路径。 ### 4. Validation & Error Matrix @@ -85,23 +84,21 @@ npm run compress:glb -- --input in.glb --output out.glb [options] | `--output` 等于 `--input` | 抛错,避免覆盖源 GLB | | 数值参数超范围 | 抛错并指出合法范围 | | `gltf-transform` 退出非零 | 抛错并带上 status / signal | -| `--preview` 没有 `--metadata` | 抛错,因为 preview 必须指向存在的 metadata | -| preview HTML 找不到配置块 | 抛错,不做猜测替换 | -| `--stages compress` 但标准 preview 不存在 | `Cesium preview not found: ` | +| `--stages compress` 但 staged manifest 不存在 | `Cesium metadata not found: ` | ### 5. Good/Base/Bad Cases -- Good: `all` 或默认构建先导出 Cesium,再压缩成标准交付物。 -- Good: `--stages compress` 对已有标准交付物重新压缩,临时源 GLB 不会留在输出目录。 +- Good: 默认构建先导出 Cesium,再压缩 staged 资产,最后由 `package` 发布。 +- Good: `--stages compress` 对已有 staged 交付物重新压缩,临时源 GLB 不会留在输出目录。 - Base: 只传 `--input --output` 生成压缩 GLB,不生成伴生文件。 - Bad: 使用 `--meshopt` 后没有做 Cesium 兼容性验证就当默认产物发布。 ### 6. Tests Required - `node --check scripts/compress-glb.js` -- `npm run test:compress-glb`:断言压缩 metadata 只替换主资产,不丢失语义资产。 +- `npm run test:compress-glb`:断言压缩 manifest 只替换主资产,不丢失语义 layer。 - `node --check scripts/build-area.js` -- 对目标区域跑一次 `npm run compress:glb -- ... --metadata --preview` +- 对目标区域跑一次 `npm run compress:glb -- ... --metadata` - 对目标区域跑一次默认 `npm run build:area` - `node scripts/glb-digest.js .glb` 确认可解析结构和扩展 - 浏览器/Cesium 预览标准 HTML,确认 `EXT_texture_webp` 在目标环境可加载 @@ -122,6 +119,103 @@ npm run compress:glb -- --input outputs/a/a.glb --output /tmp/a-compressed.glb --- +## 可复用资产包发布 + +### 1. Scope / Trigger + +`package` 将已验证的 static staging 提升为下游可消费的唯一交付边界: +`/package/`。它不复制大文件到第二个发布目录;成功时将整个 staging 目录原子 rename +为 package。车辆、路线和动态信号属于预览能力,必须留在 `/_preview/`,不能进入 package。 + +### 2. Signatures + +```bash +npm run build:area -- --config config/areas/.json --stages cesium,compress,package +npm run build:area -- --config config/areas/.json --stages preview +node scripts/test-package-contract.js +node scripts/test-package-examples.js +``` + +下游入口永远是: + +```text +outputs//package/manifest.json +``` + +### 3. Contracts + +发布 manifest 使用 `schema: "osm-asset-package/v1"`,至少包含: + +```json +{ + "schema": "osm-asset-package/v1", + "packageVersion": "1.0.0", + "areaId": "example", + "coordinateSystem": { "axes": "ENU", "units": "meters", "x": "east", "y": "north", "z": "up" }, + "placement": { "longitude": 114.3, "latitude": 30.5, "height": 0, "headingCorrectionDegrees": -90 }, + "bounds": { "minLon": 114.2, "minLat": 30.4, "maxLon": 114.4, "maxLat": 30.6 }, + "assets": [{ + "id": "main", + "role": "scene", + "category": "scene", + "uri": "models/example.glb", + "defaultLoad": true, + "integrity": { "bytes": 123, "sha256": "..." } + }] +} +``` + +- `coordinateSystem` 固定为 ENU meters,`placement` 是 WGS84 锚点与 heading correction。 +- 必须恰有一个 `role="scene"` / `category="scene"` / `defaultLoad=true` 主资产;roads、buildings、 + vegetation、water 是 `role="layer"` 且 `defaultLoad=false` 的可选语义层。 +- `assets[*].uri` 只能是包内正向相对路径,禁止绝对路径、反斜杠和 `..`。 +- `package` 在晋升前调用 `validateManifest()`;再为每个模型写 bytes 与 SHA-256,然后复验。 +- `package` 是唯一拥有最终 package file records 的 stage。`cesium` / `compress` 只能拥有 staging + 或计算摘要,不能留下指向最终 package 文件的 outputs record。 +- 下游代码和 preview 都从 manifest URL 解析 `assets[*].uri`。参考 + `examples/cesium-asset-package.js` 与 `examples/three-asset-package.js`。 + +### 4. Validation & Error Matrix + +| 条件 | 结果 | +|---|---| +| staging manifest 缺失或无效 JSON | `package` 失败,不替换上次成功 package | +| URI 是绝对路径、含 `..` 或 `\\` | `validateManifest()` 失败 | +| ENU / WGS84 字段缺失或越界 | `validateManifest()` 失败 | +| 没有或多于一个 scene asset | `validateManifest()` 失败 | +| manifest 声明的模型文件缺失 | `validateManifest()` 失败 | +| rename 发布异常 | 尝试恢复 `.previous`,保留原 package | + +### 5. Good/Base/Bad Cases + +- Good: 下游只带走 `package/`,按 manifest 的 WGS84 placement 放置主模型,按需加载 layer。 +- Base: 只有 main scene 的区域仍是合法 package;没有几何的语义类别不写空 GLB。 +- Bad: 将 `_preview/` 的车辆模型或动态交通灯加进 `assets`,使资产包绑定本项目的验证运行时。 + +### 6. Tests Required + +- `npm run test:package-contract`:schema、URI、placement、唯一 scene 和模型存在性。 +- `npm run test:package-examples`:Cesium / Three.js resolver 都相对 manifest 解析 URI。 +- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium,compress,package,preview` +- `npm run check:area -- --config config/areas/nantaizi-lake-innovation-valley.json` +- 将 `package/` 复制到其他目录后,重新运行 manifest validator;所有 `assets[*].uri` 必须仍能解析。 + +### 7. Wrong vs Correct + +Wrong: + +```js +const modelUrl = asset.uri; // Relative to preview HTML by accident. +``` + +Correct: + +```js +const modelUrl = new URL(asset.uri, new URL(manifestUrl, window.location.href)).href; +``` + +--- + ## OSM 预检命令 ### 1. Scope / Trigger @@ -605,7 +699,7 @@ const gate = classifyAreaQuality(result); ### 1. Scope / Trigger Stage manifest 是区域构建阶段或独立验证通过后的机器可读产物契约。它覆盖预检记录与完整区域链: -`preflight`、`intermediates`、`reimport`、`blender`、`cesium`、`preview` 和 `compress`。 +`preflight`、`intermediates`、`reimport`、`blender`、`cesium`、`compress`、`package` 和 `preview`。 它用于诊断产物是否存在、是否 stale、体量是否超预算,以及后续 `check:area` / 增量构建判断。 @@ -627,6 +721,7 @@ Manifest 路径固定: /_pipeline/stages/cesium.manifest.json /_pipeline/stages/preview.manifest.json /_pipeline/stages/compress.manifest.json +/_pipeline/stages/package.manifest.json ``` 代码入口: @@ -719,9 +814,18 @@ File records use this shape: - `summary.glb.extensionsUsed` - `summary.budget`:effective limits、usage 和 violations;warnings 来自同一个预算评估 -`cesium` 会调用 preview 生成函数,但 preview HTML / route / vehicle model 的 freshness -所有权属于独立 `preview` manifest。否则单跑 `--stages preview` 会把 Cesium manifest -错误判 stale。 +`cesium` 只拥有 staging 内的静态模型与 manifest;完成后必须依次由 `compress`、`package` +交付。preview HTML / route / vehicle model 的 freshness 所有权属于独立 `preview` manifest。 + +`compress` manifest 不记录最终文件 records:它们位于可被 `package` rename 的 staging 目录。 +`package` manifest 是唯一记录 `package/manifest.json`、主 GLB 与 package 目录的 stage manifest, +避免发布后 compress manifest 因路径迁移而立刻 stale。 + +`package` manifest: + +- `inputs.stagingManifest`:发布后的同一 manifest 文件记录 +- `outputs.packageDir`、`outputs.manifest`、`outputs.primaryGlb` +- `summary.assets` 与 `summary.packageDir` ### Preview Assembly Boundary @@ -786,8 +890,8 @@ Manifest files are written atomically via `*.tmp` then `renameSync`. - Good: `--stages intermediates` 成功后写 `intermediates.manifest.json`,诊断显示 `ok intermediates`。 - Good: `--stages blender` 成功后写 `blender.manifest.json`,诊断显示 `ok blender`。 -- Good: `--stages cesium` 成功后写 `cesium.manifest.json` 和 `preview.manifest.json`。 -- Good: `--stages preview` 只更新 preview manifest,不让 cesium manifest stale。 +- Good: `--stages cesium,compress,package,preview` 成功后依序写四份各自拥有的 manifest。 +- Good: `--stages preview` 只更新 preview manifest,不让 package manifest stale。 - Good: `--stages compress` 成功后写 `compress.manifest.json`,summary 记录压缩比和节省字节。 - Base: 旧产物没有当前 ownership 路径的 manifest,诊断显示 expected manifest missing, 提示重跑对应阶段。 @@ -806,6 +910,7 @@ Manifest files are written atomically via `*.tmp` then `renameSync`. - `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages intermediates` - `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender` - `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium` +- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress,package,preview` - `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages preview` - `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress` - `npm run diagnose:area -- --config config/areas/nantaizi-lake-innovation-valley.json` @@ -924,15 +1029,16 @@ gpkg: path.resolve(outputOverrides.gpkg || path.join(areaDir, `${fileStem}.gpkg` | `intermediates` | OSM → osm2streets GeoJSON → GeoPackage → QGIS 工程 + 预览图 | `.osm` | `osm2streets_web_out/`、`.gpkg`、`.qgz`、`-preview.png` | | `reimport` | GeoPackage → GeoJSON(**反向**) | `.gpkg` | `osm2streets_web_out/` | | `blender` | OSM + GeoJSON → 场景 | `.osm`、`osm2streets_web_out/` | `.blend`、`.png` | -| `cesium` | 场景 → GLB + 元数据 + 预览页 | `.blend` | `.glb`、`.json`、预览 HTML 及其静态资源 | -| `preview` | 只补生成预览页 | `.glb`、`.json` | 预览 HTML 及其静态资源 | -| `compress` | 将 Cesium 临时导出替换为压缩交付产物 | 标准 `.glb`、`.json`、预览 HTML | 标准 `.glb/json/html` | +| `cesium` | 场景 → static staging GLB + manifest | `.blend` | `_pipeline/package-staging/models/*.glb`、staged manifest;动态 GLB 到 `_preview/` | +| `compress` | 压缩 Cesium staging 主模型 | staged `.glb`、manifest | 压缩 staged `.glb`、manifest | +| `package` | 校验并原子发布静态资产包 | staging manifest 与 models | `package/manifest.json`、`package/models/*.glb` | +| `preview` | 用已发布 manifest 生成验证预览 | `package/manifest.json`、动态输入 | 预览 HTML、静态 runtime、`_preview/` 动态资源 | 调度是顶层的阶段 `if`(`build-area.js` 开头),顺序固定,**阶段之间不传内存状态, 只通过磁盘产物耦合**。这就是单跑某个阶段能work 的原因。 -`cesium` 阶段结束时会直接调 `writeCesiumPreview(area)`(`build-area.js:285`),所以 -`preview` 只在"已有 GLB、只想重生成 HTML"时才需要单独跑。 +完整构建中 `preview` 在 `package` 后运行。单跑 `preview` 用于已发布 package 但想重生成 +验证 UI 或 `_preview/` 动态资源的情况。 ### 别名 @@ -947,11 +1053,11 @@ gpkg: path.resolve(outputOverrides.gpkg || path.join(areaDir, `${fileStem}.gpkg` ```js // 'reimport' is deliberately absent from 'all': it is a recovery step for // hand-edited GeoPackages, never part of a full build. build-area.js:159-160 -all: ["intermediates", "blender", "cesium", "compress"], +all: ["intermediates", "blender", "cesium", "compress", "package", "preview"], ``` -`preview` 不在 `all` 里——`cesium` 已经包含它。`compress` 在 `all` 里,确保完整构建 -以压缩后的标准路径交付。 +`compress`、`package`、`preview` 均在 `all` 中,确保完整构建以压缩后的可复用静态 package +以及可运行的验证预览交付。 ### `intermediates` 与 `reimport` 互斥 @@ -961,9 +1067,8 @@ all: ["intermediates", "blender", "cesium", "compress"], 这是**显式拒绝而不是警告**——两者同时开,无论谁先跑,另一个的工作都白做。 -`normalizeAreaConfig` 里 `stages.reimport` 和 `stages.preview` 硬编码为 `false`, -只能靠 `--stages` 显式请求;`stages.compress` 为默认 `true`。 -恢复动作和补丁动作不该被一份配置文件变成默认行为,压缩则属于标准交付链。 +`normalizeAreaConfig` 里只有 `stages.reimport` 硬编码为 `false`,只能靠 `--stages` 显式请求; +`stages.compress`、`stages.package` 和 `stages.preview` 为默认 `true`。 --- @@ -1000,7 +1105,7 @@ parity 校验依赖 stage 的 stdout 标记来判断阶段是否跑到(如 `SC | 在阶段函数里现拼输出路径 | 路径规则出现第二份定义 | | 低层脚本直接读 `config/areas/*.json` | 打破两层配置边界 | | 布尔配置用 `\|\|` 兜底 | `false` 被翻转成默认值 | -| 让 `reimport` / `preview` 能从配置文件默认开启 | 恢复动作变成常规行为 | +| 让 `reimport` 能从配置文件默认开启 | 恢复动作变成常规行为 | | 新阶段忘了 `ensureFile` 前置校验 | 单跑时报底层堆栈而非人话 | | 改 stage 的 stdout 标记 | 静默破坏 parity 契约 | | 顺手把多份 `parseArgs` 合并 | 扩大 diff,且独立入口的独立性是刻意的 | diff --git a/.trellis/spec/pipeline/index.md b/.trellis/spec/pipeline/index.md index be2e661..c14afb5 100644 --- a/.trellis/spec/pipeline/index.md +++ b/.trellis/spec/pipeline/index.md @@ -51,17 +51,21 @@ config/areas/.json │ → _pipeline/stages/blender.manifest.json │ ├─[cesium]────────▶ Blender + blender/export_cesium.py - │ 读 .blend → .glb + .json - │ → 并自动执行 preview + │ 读 .blend → _pipeline/package-staging/models/.glb + │ + staged manifest,动态预览 GLB 写入 _preview/ │ → _pipeline/stages/cesium.manifest.json │ - ├─[preview]───────▶ 生成 -cesium-preview.html - │ + 拷贝 lib/cesium-preview.{js,css} - │ + 车辆巡航路线与模型 - │ → _pipeline/stages/preview.manifest.json + ├─[compress]──────▶ 压缩 staging 内主 GLB 并更新 staged manifest + │ → _pipeline/stages/compress.manifest.json │ - └─[compress]──────▶ 临时保存未压缩导出,再以标准 GLB / metadata / preview 路径交付压缩版本 - → _pipeline/stages/compress.manifest.json + ├─[package]───────▶ 校验 manifest 与全部静态模型,原子发布 package/ + │ → package/manifest.json + package/models/*.glb + │ → _pipeline/stages/package.manifest.json + │ + └─[preview]───────▶ 生成 -cesium-preview.html + │ + 拷贝 lib/cesium-preview.{js,css} + + _preview/ 车辆巡航路线、模型、动态信号 + │ → _pipeline/stages/preview.manifest.json ``` **阶段之间只通过磁盘产物耦合**,不传内存状态。这是单跑任意阶段能work 的前提。 @@ -87,7 +91,7 @@ config/areas/.json | 文件 | 行数 | 职责 | |---|---|---| -| `build-area.js` | 约 530 | 主入口:区域配置读取、阶段调度、preview 文件写入和 stage manifest ownership | +| `build-area.js` | 主入口:区域配置读取、阶段调度、package 发布、preview 文件写入和 stage manifest ownership | | `diagnose-area.js` | 36 | 快速诊断入口:调用共享 area diagnostics 并打印完整报告 | | `check-area.js` | 74 | 区域质量门入口:调用共享 area diagnostics,输出 PASS/FAIL 并设置退出码 | | `lib/area-diagnostics.js` | 776 | 共享区域诊断事实源:OSM、产物、metadata、stage manifest、GLB digest 和质量门分类 | @@ -99,6 +103,7 @@ config/areas/.json | `lib/vehicle-route.js` | 约 180 | 从 OSM 提取确定性预览巡航路线 | | `lib/vehicle-model.js` | 约 150 | 生成内嵌 buffer 的预览车辆 glTF | | `lib/area-preview.js` | 约 110 | 复制 preview runtime、生成 HTML 与转义配置注入 | +| `lib/package-contract.js` | package manifest 校验、相对 URI 与 SHA-256 完整性记录 | | `lib/cesium-preview.js` / `.css` | 672 / 230 | 预览页运行时,见 [../preview/](../preview/index.md) | | `normalize-lane-arrows.py` | 182 | 合并 osm2streets 的三角网箭头(跑在 QGIS Python 里) | | `parity.js` | 270 | 产物一致性校验驱动 | diff --git a/.trellis/spec/preview/index.md b/.trellis/spec/preview/index.md index dd89bfc..b5383da 100644 --- a/.trellis/spec/preview/index.md +++ b/.trellis/spec/preview/index.md @@ -8,8 +8,8 @@ ## 定位 -预览层是**验证性的,不是产物本身**。它加载 `cesium` 阶段导出的 `.glb` + `.json`, -用来确认资产在真实 Cesium 里的样子。改这一层**不会**改变 Blender/GLB 主资产。 +预览层是**验证性的,不是产物本身**。它加载 `package/manifest.json`,用来确认已发布资产 +在真实 Cesium 里的样子。改这一层**不会**改变 package 内的 Blender/GLB 静态资产。 车辆巡航同理——README 里写明它是"用于验证高精度巡航可用性的预览层功能"。 @@ -53,7 +53,11 @@ const config = window.OSM_ASSET_PREVIEW_CONFIG || {}; // :4 **加一个新的可配置项**:`cesiumPreviewHtml()` 里加进注入的 JSON,JS 侧从 `config` 读, 两边都要动。 -`build-area.js` 只保留 GLB / metadata 依赖检查、写入顺序和 preview manifest ownership; +`glbName` 与 `metadataName` 都是 `package/manifest.json`。浏览器读取 package manifest 后, +每个 `assets[*].uri` 必须相对**manifest 文件**解析,绝不能相对 preview HTML 解析;否则将 +错误请求 `outputs//models/...` 而不是 `outputs//package/models/...`。 + +`build-area.js` 只保留已发布 package / 动态输入的依赖检查、写入顺序和 preview manifest ownership; 不要把 HTML 模板、runtime copy 或转义实现移回阶段调度器。路线 JSON 与车辆 glTF 分别由 `vehicle-route.js` 和 `vehicle-model.js` 生成,二者都是不启动外部工具的 Node 模块。