@nextclaw/app-runtime 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,239 +1,37 @@
1
1
  # NextClaw App Runtime
2
2
 
3
- `@nextclaw/app-runtime` 是一个独立的微应用 runtime/CLI 包。
3
+ `@nextclaw/app-runtime` is the independently published runtime package that powers NextClaw Mini Apps. It owns artifact validation, package formats, local package state, and the runtime contracts used by the NextClaw host.
4
4
 
5
- 当前相对完善 MVP 提供下面这些能力:
5
+ ## Install Mini Apps
6
6
 
7
- - `napp create <app-dir>`:生成一个最小可跑的微应用骨架
8
- - `napp inspect <app-dir>`:校验应用目录与 manifest
9
- - `napp doctor`:检查本机 NApp/WASI HTTP 开发运行环境
10
- - `napp build <app-dir> --install`:安装模板依赖并构建 TS/WASI HTTP 后端
11
- - `napp run <app-dir|app-id>`:启动本地宿主,支持目录运行和已安装应用运行
12
- - `napp dev <app-dir>`:当前等价于 `run`
13
- - `napp pack <app-dir>`:把应用目录打成 `.napp` 分发包;独立应用默认使用轻量 `source`,组合包默认使用安全的 `bundle`
14
- - `napp validate-publish <app-dir>`:本地做发布前校验并输出体积/包内容 warning
15
- - `napp publish <app-dir>`:把应用目录发布到官方 apps registry;独立应用默认使用 `source`,组合包默认使用 `bundle`
16
- - `napp install <app-dir|bundle.napp|app-id[@version]>`:从本地或 registry 安装应用
17
- - `napp update <app-id>`:更新已安装应用
18
- - `napp uninstall <app-id>`:卸载已安装应用,默认保留受管实例数据;增加 `--purge-data` 才会同时删除
19
- - `napp list`:列出已安装应用
20
- - `napp info <app-id>`:查看已安装应用详情
21
- - `napp registry [get|set|reset]`:查看或切换 registry
22
- - `napp permissions <app-id>`:查看应用权限状态
23
- - `napp grant <app-id> --document scope=/path`:写入目录授权
24
- - `napp revoke <app-id> --document scope`:撤销目录授权
25
-
26
- ## 安装
27
-
28
- ```bash
29
- npm install -g @nextclaw/app-runtime
30
- ```
31
-
32
- 安装后可用:
33
-
34
- ```bash
35
- napp --help
36
- napp --version
37
- ```
38
-
39
- 当前版本先聚焦“独立可运行的微应用宿主 + 可分发、可安装、可更新、可授权的 CLI/runtime 闭环”。普通用户入口推荐通过 NextClaw 的 `nextclaw-app-runtime` skill 编排这些命令。
40
-
41
- ## 应用目录
42
-
43
- ```text
44
- manifest.json
45
- main/
46
- app.wasm
47
- package.json # ts-http / ts-http-lite 模板存在
48
- src/ # ts-http / ts-http-lite 模板存在
49
- ui/
50
- index.html
51
- assets/
52
- ```
53
-
54
- ## 当前 MVP 范围
55
-
56
- - `main` 执行形态:`wasm` 或 `wasi-http-component`
57
- - UI 装载:本地静态服务
58
- - 宿主桥接:`/__napp/*`
59
- - WASI HTTP 业务 API:`/api/*`
60
- - 权限词汇:`documentAccess`、`allowedDomains`、`storage`、`capabilities`
61
- - 当前 Wasm 执行底座:Node 原生 `WebAssembly` 与 Wasmtime `serve`
62
- - 分发包形态:`.napp`(底层为 zip,支持 `source` / `bundle` 双模式)
63
- - 安装目录:`~/.nextclaw/apps/packages/<app-id>/<version>/`
64
- - 受管实例目录:`~/.nextclaw/apps/instances/<app-id>/default/`
65
- - 受管实例分类:`data/`、`config/`、`state/`、`cache/`、`tmp/`、`logs/`,并由 `metadata.json` 记录稳定身份
66
- - 旧用户数据目录:`~/.nextclaw/apps/data/<app-id>/`(首次使用时原子迁移到受管实例)
67
- - 本地 registry:`~/.nextclaw/apps/registry.json`
68
- - 本地 config:`~/.nextclaw/apps/config.json`
69
- - 默认 registry:`https://apps-registry.nextclaw.io/api/v1/apps/registry/`
70
-
71
- ## 当前 MVP 工作流
72
-
73
- 开发者工作流:
7
+ Use the NextClaw CLI for every user or AI-facing Mini App action:
74
8
 
75
9
  ```bash
76
- napp doctor
77
- napp create ./my-first-napp --template ts-http
78
- napp build ./my-first-napp --install
79
- napp inspect ./my-first-napp
80
- napp validate-publish ./my-first-napp
81
- napp run ./my-first-napp --data ./my-first-napp/.napp/data
82
- napp pack ./my-first-napp
83
- napp publish ./my-first-napp
10
+ nextclaw app install <app-id|local-app-directory|bundle.napp>
11
+ nextclaw app list
12
+ nextclaw app info <app-id>
13
+ nextclaw app enable <app-id>
14
+ nextclaw app disable <app-id>
15
+ nextclaw app update <app-id>
16
+ nextclaw app rollback <app-id> --version <version>
17
+ nextclaw app uninstall <app-id> --confirm <app-id>
84
18
  ```
85
19
 
86
- 体积优先工作流:
20
+ The install source may be an App Marketplace identifier, a local app directory, or a local `.napp` bundle. The host records an operation receipt; use `nextclaw app operations --json` to inspect its progress and result.
87
21
 
88
- ```bash
89
- napp doctor
90
- napp create ./my-small-napp --template ts-http-lite
91
- napp build ./my-small-napp --install
92
- napp validate-publish ./my-small-napp
93
- napp run ./my-small-napp --data ./my-small-napp/.napp/data
94
- ```
95
-
96
- 本地安装工作流:
97
-
98
- ```bash
99
- napp install ./my-first-napp
100
- napp list
101
- napp info nextclaw.my-first-napp
102
- napp run nextclaw.my-first-napp
103
- ```
104
-
105
- registry 安装与更新工作流:
106
-
107
- ```bash
108
- napp registry set https://registry.example.com/
109
- napp install nextclaw.hello-notes
110
- napp permissions nextclaw.hello-notes
111
- napp grant nextclaw.hello-notes --document notes=/absolute/path/to/notes
112
- napp run nextclaw.hello-notes
113
- napp update nextclaw.hello-notes
114
- ```
22
+ ## For Runtime Integrators
115
23
 
116
- 卸载与权限回收:
24
+ The package remains independently published so the NextClaw host and build tooling can consume its stable runtime contracts. Its package and registry implementation are not a second public product surface. Use the `nextclaw app` CLI and the NextClaw documentation for supported operations.
117
25
 
118
- ```bash
119
- napp revoke nextclaw.my-first-napp --document notes
120
- napp uninstall nextclaw.my-first-napp
121
- napp uninstall nextclaw.my-first-napp --purge-data
122
- ```
26
+ ## Package Formats
123
27
 
124
- 不带 `--purge-data` 时只卸载应用代码和注册记录,受管实例会保留,以便重装后继续使用。带上该选项后,卸载事务才会同时移除该 App 的默认实例。宿主只管理上述实例目录;通过文档授权访问的外部文件或目录不属于 App 实例,卸载和清理数据都不会删除它们。
28
+ Mini Apps use a `.napp` container. A package may be distributed as either:
125
29
 
126
- 运行中的 NextClaw 还提供统一的数据清单和残留数据清理入口:
30
+ - `source`: source, UI assets, and manifest are materialized locally at install time.
31
+ - `bundle`: a ready-to-run `main/app.wasm` is included for deterministic or offline delivery.
127
32
 
128
- ```bash
129
- nextclaw app data list --json
130
- nextclaw app data delete <data-id> --confirm <app-id> --json
131
- ```
33
+ Both variants carry a manifest and checksums. The runtime installs package code separately from each managed App instance, so uninstalling a package can retain the instance data unless the caller explicitly requests data removal.
132
34
 
133
- `data-id` 必须取自最新的 `list` 结果;删除命令只接受已经卸载或已经从 workspace 移除的 `retained` 实例,并要求 `--confirm` 与目标 App id 完全一致。开发 Service App 时,可以显式重置隔离的开发实例:
35
+ ## Registry Metadata
134
36
 
135
- ```bash
136
- nextclaw app dev <service-app-dir> --reset-data --confirm <app-id> --json
137
- ```
138
-
139
- ## 示例
140
-
141
- ```bash
142
- napp create ./my-first-napp
143
- napp inspect ./my-first-napp
144
- napp pack ./my-first-napp
145
- napp install ./my-first-napp
146
- napp list
147
- napp info nextclaw.my-first-napp
148
- napp permissions nextclaw.my-first-napp
149
- napp run nextclaw.my-first-napp
150
- ```
151
-
152
- 模板选择建议:
153
-
154
- - `ts-http`:默认推荐,开发体验优先,适合大多数普通前后端小应用
155
- - `ts-http-lite`:体积优先,继续走官方 WASI HTTP 路线,但不使用默认 Hono 路由层
156
-
157
- 已有示例应用:
158
-
159
- ```bash
160
- napp inspect ./apps/examples/hello-notes
161
- napp pack ./apps/examples/hello-notes
162
- napp publish ./apps/examples/hello-notes
163
- napp install ./apps/examples/hello-notes
164
- napp grant nextclaw.hello-notes --document notes=/absolute/path/to/notes
165
- napp run ./apps/examples/hello-notes --document notes=/absolute/path/to/notes
166
- ```
167
-
168
- registry 示例:
169
-
170
- ```bash
171
- napp registry get
172
- napp registry set https://registry.example.com/
173
- napp install nextclaw.hello-notes
174
- napp update nextclaw.hello-notes
175
- ```
176
-
177
- 官方 apps 入口:
178
-
179
- - Web:`https://apps.nextclaw.io`
180
- - Registry/API:`https://apps-registry.nextclaw.io`
181
-
182
- ## Bundle 结构
183
-
184
- `.napp` bundle 的最小结构如下:
185
-
186
- ```text
187
- manifest.json
188
- main/
189
- ui/
190
- assets/
191
- .napp/
192
- bundle.json
193
- checksums.json
194
- ```
195
-
196
- ## 分发模式
197
-
198
- `.napp` 仍然是统一容器,但现在支持两种分发模式:
199
-
200
- - `source`:默认模式。包里主要是用户源码、UI、资源和 manifest;安装时本地物化成运行态,适合轻量分发。
201
- - `bundle`:显式模式。包里直接带运行态 `main/app.wasm`,适合高级用户、确定性发布或离线场景。
202
-
203
- 命令示例:
204
-
205
- ```bash
206
- napp validate-publish ./my-first-napp --mode source
207
- napp pack ./my-first-napp --mode source
208
- napp publish ./my-first-napp --mode source
209
- napp pack ./my-first-napp --mode bundle
210
- ```
211
-
212
- ## Registry Metadata 结构
213
-
214
- registry 按 npm 风格使用一个 base URL,再按 app id 拉取 metadata 文档。metadata 的最小结构如下:
215
-
216
- ```json
217
- {
218
- "name": "nextclaw.hello-notes",
219
- "description": "Registry-hosted Hello Notes",
220
- "dist-tags": {
221
- "latest": "0.2.0"
222
- },
223
- "versions": {
224
- "0.2.0": {
225
- "name": "nextclaw.hello-notes",
226
- "version": "0.2.0",
227
- "publisher": {
228
- "id": "nextclaw",
229
- "name": "NextClaw Official"
230
- },
231
- "dist": {
232
- "kind": "source",
233
- "bundle": "./-/hello-notes-0.2.0.napp",
234
- "sha256": "<sha256>"
235
- }
236
- }
237
- }
238
- }
239
- ```
37
+ The registry stores package metadata, versions, publisher identity, distribution location, and integrity data. It does not store a per-App install command: clients derive the single supported command from the app identifier.
@@ -1 +1 @@
1
- {"version":3,"file":"publish.controller.d.ts","names":[],"sources":["../../src/controllers/publish.controller.ts"],"mappings":";;;cAEa,cAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,iBAAA;EAGnC,GAAA,GAAa,MAAA;IACX,YAAA;IACA,YAAA;IACA,UAAA;IACA,KAAA;IACA,IAAA;IACA,IAAA;IACA,KAAA,GAAQ,IAAA;EAAA,MACN,OAAA;AAAA"}
1
+ {"version":3,"file":"publish.controller.d.ts","names":[],"sources":["../../src/controllers/publish.controller.ts"],"mappings":";;;cAGa,cAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,iBAAA;EAGnC,GAAA,GAAa,MAAA;IACX,YAAA;IACA,YAAA;IACA,UAAA;IACA,KAAA;IACA,IAAA;IACA,IAAA;IACA,KAAA,GAAQ,IAAA;EAAA,MACN,OAAA;AAAA"}
@@ -1,4 +1,5 @@
1
1
  import { AppPublishService } from "../services/app-publish.service.js";
2
+ import { formatNextClawAppInstallCommand } from "@nextclaw/shared";
2
3
  //#region src/controllers/publish.controller.ts
3
4
  var PublishCommand = class {
4
5
  constructor(publishService = new AppPublishService()) {
@@ -30,7 +31,7 @@ var PublishCommand = class {
30
31
  write(`${result.created ? "Published" : "Updated"} ${result.item.name} (${result.item.appId}) ${result.item.latestVersion}\n`);
31
32
  write(`Distribution: ${result.distribution.path} (${result.distribution.mode})\n`);
32
33
  if (result.item.webUrl) write(`Details: ${result.item.webUrl}\n`);
33
- write(`Install: ${result.item.install.command}\n`);
34
+ write(`Install: ${formatNextClawAppInstallCommand(result.item.install.spec)}\n`);
34
35
  };
35
36
  };
36
37
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"publish.controller.js","names":[],"sources":["../../src/controllers/publish.controller.ts"],"sourcesContent":["import { AppPublishService } from \"#app-runtime/services/app-publish.service.js\";\n\nexport class PublishCommand {\n constructor(\n private readonly publishService: AppPublishService = new AppPublishService(),\n ) {}\n\n run = async (params: {\n appDirectory: string;\n metadataPath?: string;\n apiBaseUrl?: string;\n token?: string;\n mode?: \"source\" | \"bundle\";\n json: boolean;\n write: (text: string) => void;\n }): Promise<void> => {\n const { appDirectory, metadataPath, apiBaseUrl, token, mode, json, write } = params;\n const result = await this.publishService.publish({\n appDirectory,\n metadataPath,\n apiBaseUrl,\n token,\n mode,\n });\n if (json) {\n write(`${JSON.stringify({ ok: true, publish: result }, null, 2)}\\n`);\n return;\n }\n if (result.item.publishStatus === \"pending\") {\n write(\n `Submitted ${result.item.name} (${result.item.appId}) ${result.item.latestVersion} for review.\\n`,\n );\n write(\"Status: pending\\n\");\n write(\"The app will appear in the App Marketplace after approval.\\n\");\n write(\"Manage submissions: https://platform.nextclaw.io/apps\\n\");\n return;\n }\n write(\n `${result.created ? \"Published\" : \"Updated\"} ${result.item.name} (${result.item.appId}) ${result.item.latestVersion}\\n`,\n );\n write(`Distribution: ${result.distribution.path} (${result.distribution.mode})\\n`);\n if (result.item.webUrl) {\n write(`Details: ${result.item.webUrl}\\n`);\n }\n write(`Install: ${result.item.install.command}\\n`);\n };\n}\n"],"mappings":";;AAEA,IAAa,iBAAb,MAA4B;CAC1B,YACE,iBAAqD,IAAI,mBAAmB,EAC5E;AADiB,OAAA,iBAAA;;CAGnB,MAAM,OAAO,WAQQ;EACnB,MAAM,EAAE,cAAc,cAAc,YAAY,OAAO,MAAM,MAAM,UAAU;EAC7E,MAAM,SAAS,MAAM,KAAK,eAAe,QAAQ;GAC/C;GACA;GACA;GACA;GACA;GACD,CAAC;AACF,MAAI,MAAM;AACR,SAAM,GAAG,KAAK,UAAU;IAAE,IAAI;IAAM,SAAS;IAAQ,EAAE,MAAM,EAAE,CAAC,IAAI;AACpE;;AAEF,MAAI,OAAO,KAAK,kBAAkB,WAAW;AAC3C,SACE,aAAa,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,cAAc,gBACnF;AACD,SAAM,oBAAoB;AAC1B,SAAM,+DAA+D;AACrE,SAAM,0DAA0D;AAChE;;AAEF,QACE,GAAG,OAAO,UAAU,cAAc,UAAU,GAAG,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,cAAc,IACrH;AACD,QAAM,iBAAiB,OAAO,aAAa,KAAK,IAAI,OAAO,aAAa,KAAK,KAAK;AAClF,MAAI,OAAO,KAAK,OACd,OAAM,YAAY,OAAO,KAAK,OAAO,IAAI;AAE3C,QAAM,YAAY,OAAO,KAAK,QAAQ,QAAQ,IAAI"}
1
+ {"version":3,"file":"publish.controller.js","names":[],"sources":["../../src/controllers/publish.controller.ts"],"sourcesContent":["import { AppPublishService } from \"#app-runtime/services/app-publish.service.js\";\nimport { formatNextClawAppInstallCommand } from \"@nextclaw/shared\";\n\nexport class PublishCommand {\n constructor(\n private readonly publishService: AppPublishService = new AppPublishService(),\n ) {}\n\n run = async (params: {\n appDirectory: string;\n metadataPath?: string;\n apiBaseUrl?: string;\n token?: string;\n mode?: \"source\" | \"bundle\";\n json: boolean;\n write: (text: string) => void;\n }): Promise<void> => {\n const { appDirectory, metadataPath, apiBaseUrl, token, mode, json, write } = params;\n const result = await this.publishService.publish({\n appDirectory,\n metadataPath,\n apiBaseUrl,\n token,\n mode,\n });\n if (json) {\n write(`${JSON.stringify({ ok: true, publish: result }, null, 2)}\\n`);\n return;\n }\n if (result.item.publishStatus === \"pending\") {\n write(\n `Submitted ${result.item.name} (${result.item.appId}) ${result.item.latestVersion} for review.\\n`,\n );\n write(\"Status: pending\\n\");\n write(\"The app will appear in the App Marketplace after approval.\\n\");\n write(\"Manage submissions: https://platform.nextclaw.io/apps\\n\");\n return;\n }\n write(\n `${result.created ? \"Published\" : \"Updated\"} ${result.item.name} (${result.item.appId}) ${result.item.latestVersion}\\n`,\n );\n write(`Distribution: ${result.distribution.path} (${result.distribution.mode})\\n`);\n if (result.item.webUrl) {\n write(`Details: ${result.item.webUrl}\\n`);\n }\n write(`Install: ${formatNextClawAppInstallCommand(result.item.install.spec)}\\n`);\n };\n}\n"],"mappings":";;;AAGA,IAAa,iBAAb,MAA4B;CAC1B,YACE,iBAAqD,IAAI,mBAAmB,EAC5E;AADiB,OAAA,iBAAA;;CAGnB,MAAM,OAAO,WAQQ;EACnB,MAAM,EAAE,cAAc,cAAc,YAAY,OAAO,MAAM,MAAM,UAAU;EAC7E,MAAM,SAAS,MAAM,KAAK,eAAe,QAAQ;GAC/C;GACA;GACA;GACA;GACA;GACD,CAAC;AACF,MAAI,MAAM;AACR,SAAM,GAAG,KAAK,UAAU;IAAE,IAAI;IAAM,SAAS;IAAQ,EAAE,MAAM,EAAE,CAAC,IAAI;AACpE;;AAEF,MAAI,OAAO,KAAK,kBAAkB,WAAW;AAC3C,SACE,aAAa,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,cAAc,gBACnF;AACD,SAAM,oBAAoB;AAC1B,SAAM,+DAA+D;AACrE,SAAM,0DAA0D;AAChE;;AAEF,QACE,GAAG,OAAO,UAAU,cAAc,UAAU,GAAG,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,cAAc,IACrH;AACD,QAAM,iBAAiB,OAAO,aAAa,KAAK,IAAI,OAAO,aAAa,KAAK,KAAK;AAClF,MAAI,OAAO,KAAK,OACd,OAAM,YAAY,OAAO,KAAK,OAAO,IAAI;AAE3C,QAAM,YAAY,gCAAgC,OAAO,KAAK,QAAQ,KAAK,CAAC,IAAI"}
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.13.2";
2
+ var version = "0.14.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -36,6 +36,7 @@ var AppInstallationFilesystemService = class {
36
36
  for (const component of manifestBundle.components) {
37
37
  if (component.kind !== "service") continue;
38
38
  const serviceManifest = JSON.parse(await readFile(component.manifestPath, "utf8"));
39
+ if (serviceManifest.protocol === "wasi-component") continue;
39
40
  const { command } = launchService.resolve(serviceManifest, target);
40
41
  if (path.isAbsolute(command) || !command.includes("/")) continue;
41
42
  const commandPath = path.resolve(component.componentDirectory, command);
@@ -1 +1 @@
1
- {"version":3,"file":"app-installation-filesystem.service.js","names":[],"sources":["../../src/services/app-installation-filesystem.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, cp, lstat, mkdir, readFile, readdir, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport type { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppServiceLaunchService } from \"#app-runtime/services/app-service-launch.service.js\";\nimport {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n type AppManifestBundle,\n type AppNativeArtifactTarget,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\n\nexport class AppInstallationFilesystemService {\n constructor(private readonly params: {\n buildService: AppBuildService;\n integrityService: AppInstallationIntegrityService;\n manifestService: AppManifestService;\n }) {}\n\n copyToImmutableInstallDirectory = async (params: {\n appId: string;\n appVersion: string;\n extractedDirectory: string;\n installDirectory: string;\n target?: AppNativeArtifactTarget;\n }): Promise<string> => {\n const { appId, appVersion, extractedDirectory, installDirectory, target } = params;\n if (await this.params.integrityService.pathExists(installDirectory)) {\n throw new Error(`应用版本目录已存在,不能覆盖不可变版本:${appId}@${appVersion}`);\n }\n await mkdir(path.dirname(installDirectory), { recursive: true });\n const stagedInstallDirectory = `${installDirectory}.staging-${randomUUID()}`;\n try {\n await cp(extractedDirectory, stagedInstallDirectory, { recursive: true });\n const stagedManifest = await this.params.manifestService.load(stagedInstallDirectory);\n if (\n stagedManifest.manifest.id !== appId ||\n stagedManifest.manifest.version !== appVersion\n ) {\n throw new Error(\"staging manifest 与已验证 bundle 身份不一致。\");\n }\n await this.restoreServiceCommandPermissions(stagedManifest, target);\n const contentSha256 = await this.params.integrityService.calculateDigest(\n stagedInstallDirectory,\n );\n await rename(stagedInstallDirectory, installDirectory);\n await this.params.integrityService.protectDirectory(installDirectory);\n return contentSha256;\n } catch (error) {\n await rm(stagedInstallDirectory, { recursive: true, force: true });\n throw error;\n }\n };\n\n private restoreServiceCommandPermissions = async (\n manifestBundle: AppManifestBundle,\n target?: AppNativeArtifactTarget,\n ): Promise<void> => {\n if (!isAppComponentManifestBundle(manifestBundle)) {\n return;\n }\n const launchService = new AppServiceLaunchService();\n for (const component of manifestBundle.components) {\n if (component.kind !== \"service\") {\n continue;\n }\n const serviceManifest = JSON.parse(\n await readFile(component.manifestPath, \"utf8\"),\n ) as Record<string, unknown>;\n const { command } = launchService.resolve(serviceManifest, target);\n if (path.isAbsolute(command) || !command.includes(\"/\")) {\n continue;\n }\n const commandPath = path.resolve(component.componentDirectory, command);\n const relativeCommandPath = path.relative(component.componentDirectory, commandPath);\n if (\n !relativeCommandPath ||\n relativeCommandPath.startsWith(\"..\") ||\n path.isAbsolute(relativeCommandPath)\n ) {\n throw new Error(`service app command 必须位于组件目录内:${command}`);\n }\n const stats = await lstat(commandPath);\n if (!stats.isFile()) {\n throw new Error(`service app command 必须是普通文件:${command}`);\n }\n await chmod(commandPath, stats.mode | 0o100);\n }\n };\n\n materializeDistribution = async (params: {\n appDirectory: string;\n distributionMode: AppDistributionMode;\n }): Promise<void> => {\n if (params.distributionMode !== \"source\") {\n return;\n }\n const manifestBundle = await this.params.manifestService.load(params.appDirectory);\n if (!isAppStandaloneManifestBundle(manifestBundle)) {\n throw new Error(\"schema v2 组合包不允许运行安装期 build。\");\n }\n if (manifestBundle.manifest.main.kind !== \"wasi-http-component\") {\n return;\n }\n await this.params.buildService.build({\n appDirectory: params.appDirectory,\n install: true,\n });\n };\n\n reconcileGeneratedSiblings = async (\n parentDirectory: string,\n referencedPaths: Set<string>,\n ): Promise<void> => {\n for (const entry of await this.readDirectories(parentDirectory)) {\n const entryName = path.basename(entry);\n if (entryName.includes(\".staging-\")) {\n await this.params.integrityService.removeDirectory(entry);\n continue;\n }\n const markerIndex = entryName.indexOf(\".uninstalling-\");\n if (markerIndex < 0) {\n continue;\n }\n const originalPath = path.join(parentDirectory, entryName.slice(0, markerIndex));\n if (\n referencedPaths.has(path.resolve(originalPath)) &&\n !await this.params.integrityService.pathExists(originalPath)\n ) {\n await rename(entry, originalPath);\n } else {\n await this.params.integrityService.removeDirectory(entry);\n }\n }\n };\n\n readDirectories = async (directory: string): Promise<string[]> => {\n try {\n return (await readdir(directory, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => path.join(directory, entry.name));\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n };\n\n private isMissingFileError = (error: unknown): boolean =>\n typeof error === \"object\" && error !== null &&\n \"code\" in error && (error as { code?: unknown }).code === \"ENOENT\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-installation-filesystem.service.js","names":[],"sources":["../../src/services/app-installation-filesystem.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, cp, lstat, mkdir, readFile, readdir, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport type { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppServiceLaunchService } from \"#app-runtime/services/app-service-launch.service.js\";\nimport {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n type AppManifestBundle,\n type AppNativeArtifactTarget,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\n\nexport class AppInstallationFilesystemService {\n constructor(private readonly params: {\n buildService: AppBuildService;\n integrityService: AppInstallationIntegrityService;\n manifestService: AppManifestService;\n }) {}\n\n copyToImmutableInstallDirectory = async (params: {\n appId: string;\n appVersion: string;\n extractedDirectory: string;\n installDirectory: string;\n target?: AppNativeArtifactTarget;\n }): Promise<string> => {\n const { appId, appVersion, extractedDirectory, installDirectory, target } = params;\n if (await this.params.integrityService.pathExists(installDirectory)) {\n throw new Error(`应用版本目录已存在,不能覆盖不可变版本:${appId}@${appVersion}`);\n }\n await mkdir(path.dirname(installDirectory), { recursive: true });\n const stagedInstallDirectory = `${installDirectory}.staging-${randomUUID()}`;\n try {\n await cp(extractedDirectory, stagedInstallDirectory, { recursive: true });\n const stagedManifest = await this.params.manifestService.load(stagedInstallDirectory);\n if (\n stagedManifest.manifest.id !== appId ||\n stagedManifest.manifest.version !== appVersion\n ) {\n throw new Error(\"staging manifest 与已验证 bundle 身份不一致。\");\n }\n await this.restoreServiceCommandPermissions(stagedManifest, target);\n const contentSha256 = await this.params.integrityService.calculateDigest(\n stagedInstallDirectory,\n );\n await rename(stagedInstallDirectory, installDirectory);\n await this.params.integrityService.protectDirectory(installDirectory);\n return contentSha256;\n } catch (error) {\n await rm(stagedInstallDirectory, { recursive: true, force: true });\n throw error;\n }\n };\n\n private restoreServiceCommandPermissions = async (\n manifestBundle: AppManifestBundle,\n target?: AppNativeArtifactTarget,\n ): Promise<void> => {\n if (!isAppComponentManifestBundle(manifestBundle)) {\n return;\n }\n const launchService = new AppServiceLaunchService();\n for (const component of manifestBundle.components) {\n if (component.kind !== \"service\") {\n continue;\n }\n const serviceManifest = JSON.parse(\n await readFile(component.manifestPath, \"utf8\"),\n ) as Record<string, unknown>;\n if (serviceManifest.protocol === \"wasi-component\") {\n continue;\n }\n const { command } = launchService.resolve(serviceManifest, target);\n if (path.isAbsolute(command) || !command.includes(\"/\")) {\n continue;\n }\n const commandPath = path.resolve(component.componentDirectory, command);\n const relativeCommandPath = path.relative(component.componentDirectory, commandPath);\n if (\n !relativeCommandPath ||\n relativeCommandPath.startsWith(\"..\") ||\n path.isAbsolute(relativeCommandPath)\n ) {\n throw new Error(`service app command 必须位于组件目录内:${command}`);\n }\n const stats = await lstat(commandPath);\n if (!stats.isFile()) {\n throw new Error(`service app command 必须是普通文件:${command}`);\n }\n await chmod(commandPath, stats.mode | 0o100);\n }\n };\n\n materializeDistribution = async (params: {\n appDirectory: string;\n distributionMode: AppDistributionMode;\n }): Promise<void> => {\n if (params.distributionMode !== \"source\") {\n return;\n }\n const manifestBundle = await this.params.manifestService.load(params.appDirectory);\n if (!isAppStandaloneManifestBundle(manifestBundle)) {\n throw new Error(\"schema v2 组合包不允许运行安装期 build。\");\n }\n if (manifestBundle.manifest.main.kind !== \"wasi-http-component\") {\n return;\n }\n await this.params.buildService.build({\n appDirectory: params.appDirectory,\n install: true,\n });\n };\n\n reconcileGeneratedSiblings = async (\n parentDirectory: string,\n referencedPaths: Set<string>,\n ): Promise<void> => {\n for (const entry of await this.readDirectories(parentDirectory)) {\n const entryName = path.basename(entry);\n if (entryName.includes(\".staging-\")) {\n await this.params.integrityService.removeDirectory(entry);\n continue;\n }\n const markerIndex = entryName.indexOf(\".uninstalling-\");\n if (markerIndex < 0) {\n continue;\n }\n const originalPath = path.join(parentDirectory, entryName.slice(0, markerIndex));\n if (\n referencedPaths.has(path.resolve(originalPath)) &&\n !await this.params.integrityService.pathExists(originalPath)\n ) {\n await rename(entry, originalPath);\n } else {\n await this.params.integrityService.removeDirectory(entry);\n }\n }\n };\n\n readDirectories = async (directory: string): Promise<string[]> => {\n try {\n return (await readdir(directory, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => path.join(directory, entry.name));\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n };\n\n private isMissingFileError = (error: unknown): boolean =>\n typeof error === \"object\" && error !== null &&\n \"code\" in error && (error as { code?: unknown }).code === \"ENOENT\";\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;;;KAuCK,iBAAA;EACH,WAAA;EACA,UAAA,GAAa,yBAAA;EACb,QAAA;EACA,gBAAA,GAAmB,YAAA;AAAA;AAAA,cAaR,sBAAA;EAAA,iBAUQ,cAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,YAAA;EAAA,iBACA,eAAA;EAAA,iBACA,qBAAA;EAAA,iBAGA,oBAAA;EAAA,iBAjBF,oBAAA;EAAA,iBACA,sBAAA;EAAA,iBACA,eAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,iBAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,mBAAA;cAGE,cAAA,GAAgB,cAAA,EAChB,aAAA,GAAe,gBAAA,EACf,eAAA,GAAiB,kBAAA,EACjB,YAAA,GAAc,eAAA,EACd,eAAA,GAAiB,kBAAA,EACjB,qBAAA,GAAuB,wBAAA,EAGvB,oBAAA,GAAsB,8BAAA,EAGvC,sBAAA,GAAyB,yBAAA;EAyB3B,OAAA,GACE,SAAA,UACA,OAAA,GAAS,iBAAA,KACR,OAAA,CAAQ,gBAAA;EAAA,QAkCH,qBAAA;EAAA,QAUA,qBAAA;EA8HR,MAAA,GACE,KAAA,UACA,OAAA;IACE,OAAA;IACA,WAAA;IACA,UAAA,GAAa,yBAAA;IACb,QAAA;EAAA,MAED,OAAA,CAAQ,eAAA;EAOX,SAAA,GACE,KAAA,UACA,SAAA,cACC,OAAA,CAAQ,kBAAA;EAOX,IAAA,QAAiB,OAAA,CAAQ,oBAAA;EAkBzB,mBAAA,QAAgC,OAAA;EAwGhC,IAAA,GACE,KAAA,UACA,OAAA;IAAW,mBAAA;EAAA,MACV,OAAA,CAAQ,aAAA;EAwCX,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,mBAAA;EAW9D,QAAA,GAAkB,KAAA,UAAe,OAAA,aAAkB,OAAA,CAAQ,iBAAA;EAoC3D,sBAAA,GAAgC,KAAA,UAAe,OAAA,cAAmB,OAAA;EAqBlE,gBAAA,MAA6B,KAAA,UAAe,SAAA,QAAiB,OAAA,CAAQ,CAAA,MAAK,OAAA,CAAQ,CAAA;EAgBlF,aAAA,GACE,YAAA,UACA,wBAAA,EAA0B,mBAAA,KACzB,OAAA,CAAQ,mBAAA;EAAA,QA6BH,sBAAA;EAOR,aAAA,GACE,KAAA,sBACA,gBAAA,EAAkB,mBAAA,KACjB,OAAA;AAAA"}
1
+ {"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;;;KAwCK,iBAAA;EACH,WAAA;EACA,UAAA,GAAa,yBAAA;EACb,QAAA;EACA,gBAAA,GAAmB,YAAA;AAAA;AAAA,cAaR,sBAAA;EAAA,iBAUQ,cAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,YAAA;EAAA,iBACA,eAAA;EAAA,iBACA,qBAAA;EAAA,iBAGA,oBAAA;EAAA,iBAjBF,oBAAA;EAAA,iBACA,sBAAA;EAAA,iBACA,eAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,iBAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,mBAAA;cAGE,cAAA,GAAgB,cAAA,EAChB,aAAA,GAAe,gBAAA,EACf,eAAA,GAAiB,kBAAA,EACjB,YAAA,GAAc,eAAA,EACd,eAAA,GAAiB,kBAAA,EACjB,qBAAA,GAAuB,wBAAA,EAGvB,oBAAA,GAAsB,8BAAA,EAGvC,sBAAA,GAAyB,yBAAA;EAyB3B,OAAA,GACE,SAAA,UACA,OAAA,GAAS,iBAAA,KACR,OAAA,CAAQ,gBAAA;EAAA,QAkCH,qBAAA;EAAA,QAUA,qBAAA;EAqHR,MAAA,GACE,KAAA,UACA,OAAA;IACE,OAAA;IACA,WAAA;IACA,UAAA,GAAa,yBAAA;IACb,QAAA;EAAA,MAED,OAAA,CAAQ,eAAA;EAOX,SAAA,GACE,KAAA,UACA,SAAA,cACC,OAAA,CAAQ,kBAAA;EAOX,IAAA,QAAiB,OAAA,CAAQ,oBAAA;EAkBzB,mBAAA,QAAgC,OAAA;EAwGhC,IAAA,GACE,KAAA,UACA,OAAA;IAAW,mBAAA;EAAA,MACV,OAAA,CAAQ,aAAA;EAwCX,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,mBAAA;EAW9D,QAAA,GAAkB,KAAA,UAAe,OAAA,aAAkB,OAAA,CAAQ,iBAAA;EAoC3D,sBAAA,GAAgC,KAAA,UAAe,OAAA,cAAmB,OAAA;EAqBlE,gBAAA,MAA6B,KAAA,UAAe,SAAA,QAAiB,OAAA,CAAQ,CAAA,MAAK,OAAA,CAAQ,CAAA;EAgBlF,aAAA,GACE,YAAA,UACA,wBAAA,EAA0B,mBAAA,KACzB,OAAA,CAAQ,mBAAA;EAAA,QA6BH,sBAAA;EAOR,aAAA,GACE,KAAA,sBACA,gBAAA,EAAkB,mBAAA,KACjB,OAAA;AAAA"}
@@ -12,6 +12,7 @@ import { AppRegistryConfigService } from "./app-registry-config.service.js";
12
12
  import { AppRemoteRegistryClientService } from "./app-remote-registry-client.service.js";
13
13
  import { AppRegistryService } from "./app-registry.service.js";
14
14
  import { AppInstallSourceService } from "./app-install-source.service.js";
15
+ import { resolveAppInstallationContract } from "../utils/app-installation-contract.utils.js";
15
16
  import { rm } from "node:fs/promises";
16
17
  import path from "node:path";
17
18
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -95,11 +96,13 @@ var AppInstallationService = class {
95
96
  if (source.kind === "registry" && appId !== source.registryResolution.appId) throw new Error(`bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${appId}`);
96
97
  await onProgress?.("installing");
97
98
  const existingRecord = await this.registryService.getApp(appId);
98
- const publisher = source.kind === "registry" ? source.registryResolution.publisher : options.trustedPublisher;
99
- const currentPublisher = existingRecord?.publisher ?? existingRecord?.installedVersions[existingRecord.activeVersion]?.publisher;
100
- if (currentPublisher && currentPublisher.id !== publisher?.id) throw new Error(`应用 ${appId} 已绑定发布者 ${currentPublisher.id},拒绝由 ${publisher?.id ?? "未验证本地来源"} 覆盖。`);
101
- const dataSchemaVersion = manifestBundle.manifest.schemaVersion === 2 ? manifestBundle.manifest.storage?.schemaVersion ?? 1 : 1;
102
- if (existingRecord?.defaultInstance.dataSchemaVersion !== void 0 && existingRecord.defaultInstance.dataSchemaVersion !== dataSchemaVersion) throw new Error(`应用 ${appId} 数据 schema 从 ${existingRecord.defaultInstance.dataSchemaVersion} 升级到 ${dataSchemaVersion},但包内没有受支持的迁移合同。更新已停止,现有数据和版本保持不变。`);
99
+ const { currentPublisher, dataSchemaVersion, publisher } = resolveAppInstallationContract({
100
+ appId,
101
+ existingRecord,
102
+ manifestBundle,
103
+ source,
104
+ trustedPublisher: options.trustedPublisher
105
+ });
103
106
  const instanceDirectory = this.appHomeService.getAppInstanceDirectory(appId, "default");
104
107
  const instanceExisted = await this.integrityService.pathExists(instanceDirectory);
105
108
  const contentSha256 = await this.filesystemService.copyToImmutableInstallDirectory({
@@ -109,15 +112,16 @@ var AppInstallationService = class {
109
112
  installDirectory,
110
113
  target: extractedMetadata.metadata.target?.kind === "native" ? extractedMetadata.metadata.target : void 0
111
114
  });
112
- const defaultInstance = await this.instanceStorageService.materializeDefaultInstance({
113
- appId,
114
- publisherId: (currentPublisher ?? publisher)?.id,
115
- legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === "legacy" ? existingRecord.dataDirectory : void 0,
116
- dataSchemaVersion
117
- });
118
- await onProgress?.("finalizing");
115
+ let defaultInstance;
119
116
  let registryRecord;
120
117
  try {
118
+ defaultInstance = await this.instanceStorageService.materializeDefaultInstance({
119
+ appId,
120
+ publisherId: (currentPublisher ?? publisher)?.id,
121
+ legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === "legacy" ? existingRecord.dataDirectory : void 0,
122
+ dataSchemaVersion
123
+ });
124
+ await onProgress?.("finalizing");
121
125
  registryRecord = await this.registryService.upsertInstallation({
122
126
  appId,
123
127
  name: manifestBundle.manifest.name,
@@ -146,12 +150,13 @@ var AppInstallationService = class {
146
150
  });
147
151
  } catch (error) {
148
152
  await this.integrityService.removeDirectory(installDirectory);
149
- if (!instanceExisted) await this.instanceStorageService.rollbackNewInstance({
153
+ if (!instanceExisted && defaultInstance) await this.instanceStorageService.rollbackNewInstance({
150
154
  instance: defaultInstance,
151
155
  legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === "legacy" ? existingRecord.dataDirectory : void 0
152
156
  });
153
157
  throw error;
154
158
  }
159
+ if (!defaultInstance) throw new Error(`应用 ${appId} 安装完成但缺少默认实例。`);
155
160
  const installedVersion = registryRecord.installedVersions[version];
156
161
  return {
157
162
  appId: registryRecord.appId,
@@ -1 +1 @@
1
- {"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { isAppComponentManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport { AppInstallationFilesystemService } from \"#app-runtime/services/app-installation-filesystem.service.js\";\nimport { AppInstallationLifecycleService } from \"#app-runtime/services/app-installation-lifecycle.service.js\";\nimport { FileLockService } from \"#app-runtime/services/file-lock.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppBundleExtractResult } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\nimport type { AppRegistryInstalledVersion } from \"#app-runtime/types/app-registry.types.js\";\nimport { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport {\n AppInstallSourceService,\n type ResolvedAppInstallSource,\n} from \"#app-runtime/services/app-install-source.service.js\";\nimport type {\n AppInfoResult,\n AppInstallProgressHandler,\n AppActivationResult,\n AppInstallResult,\n AppLaunchResolution,\n AppUpdateResult,\n AppRollbackResult,\n AppUninstallResult,\n InstalledAppListItem,\n} from \"#app-runtime/types/app-installation.types.js\";\n\nconst SAFE_APP_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\n\ntype AppInstallOptions = {\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n trustedPublisher?: AppPublisher;\n};\n\ntype PreparedAppInstall = {\n source: ResolvedAppInstallSource;\n extractedDirectory: string;\n extractedMetadata: AppBundleExtractResult;\n manifestBundle: AppManifestBundle;\n options: AppInstallOptions;\n};\n\ntype RemoteSourceFields = Pick<AppRegistryInstalledVersion, \"registryUrl\" | \"bundleUrl\" | \"sha256\" | \"target\">;\n\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\n private readonly instanceStorageService: AppInstanceStorageService;\n private readonly fileLockService = new FileLockService();\n private readonly integrityService: AppInstallationIntegrityService;\n private readonly filesystemService: AppInstallationFilesystemService;\n private readonly lifecycleService: AppInstallationLifecycleService;\n private readonly appOperationContext = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly buildService: AppBuildService = new AppBuildService(),\n private readonly registryService: AppRegistryService = new AppRegistryService(appHomeService),\n private readonly registryConfigService: AppRegistryConfigService = new AppRegistryConfigService(\n appHomeService,\n ),\n private readonly remoteRegistryClient: AppRemoteRegistryClientService = new AppRemoteRegistryClientService(\n new AppRegistryConfigService(appHomeService),\n ),\n instanceStorageService?: AppInstanceStorageService,\n ) {\n this.installSourceService = new AppInstallSourceService(\n this.manifestService,\n this.remoteRegistryClient,\n this.bundleService,\n );\n this.instanceStorageService = instanceStorageService ?? new AppInstanceStorageService(appHomeService);\n this.integrityService = new AppInstallationIntegrityService(manifestService);\n this.filesystemService = new AppInstallationFilesystemService({\n buildService,\n integrityService: this.integrityService,\n manifestService,\n });\n this.lifecycleService = new AppInstallationLifecycleService({\n registryService,\n registryConfigService,\n remoteRegistryClient,\n instanceStorageService: this.instanceStorageService,\n integrityService: this.integrityService,\n install: this.install,\n rollback: this.rollback,\n });\n }\n\n install = async (\n appSource: string,\n options: AppInstallOptions = {},\n ): Promise<AppInstallResult> => {\n const { onProgress, registryUrl } = options;\n await onProgress?.(\"resolving\");\n const source = await this.installSourceService.resolve(appSource, registryUrl);\n const tempDirectory = await this.appHomeService.createTemporaryDirectory(\"napp-install-\");\n try {\n const bundlePath = await this.installSourceService.materializeBundle(\n source,\n tempDirectory,\n onProgress,\n );\n await onProgress?.(\"verifying\");\n const extractedDirectory = path.join(tempDirectory, \"bundle\");\n const extractedMetadata = await this.bundleService.extractBundle({\n bundlePath,\n targetDirectory: extractedDirectory,\n });\n await this.filesystemService.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n return await this.installPreparedBundle({\n source,\n extractedDirectory,\n extractedMetadata,\n manifestBundle,\n options,\n });\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n private installPreparedBundle = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const appId = prepared.manifestBundle.manifest.id;\n return await this.withAppOperation(\n appId,\n async () => await this.commitPreparedInstall(prepared),\n );\n };\n\n private commitPreparedInstall = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const { extractedDirectory, extractedMetadata, manifestBundle, options, source } = prepared;\n const { onProgress } = options;\n const { id: appId, version } = manifestBundle.manifest;\n const installDirectory = this.appHomeService.getInstallDirectory(appId, version);\n if (source.kind === \"registry\" && appId !== source.registryResolution.appId) {\n throw new Error(\n `bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${appId}`,\n );\n }\n await onProgress?.(\"installing\");\n const existingRecord = await this.registryService.getApp(appId);\n const publisher = source.kind === \"registry\"\n ? source.registryResolution.publisher\n : options.trustedPublisher;\n const currentPublisher = existingRecord?.publisher ??\n existingRecord?.installedVersions[existingRecord.activeVersion]?.publisher;\n if (currentPublisher && currentPublisher.id !== publisher?.id) {\n throw new Error(\n `应用 ${appId} 已绑定发布者 ${currentPublisher.id},拒绝由 ${publisher?.id ?? \"未验证本地来源\"} 覆盖。`,\n );\n }\n const dataSchemaVersion = manifestBundle.manifest.schemaVersion === 2\n ? manifestBundle.manifest.storage?.schemaVersion ?? 1\n : 1;\n if (existingRecord?.defaultInstance.dataSchemaVersion !== undefined &&\n existingRecord.defaultInstance.dataSchemaVersion !== dataSchemaVersion) {\n throw new Error(\n `应用 ${appId} 数据 schema 从 ${existingRecord.defaultInstance.dataSchemaVersion} 升级到 ${dataSchemaVersion},但包内没有受支持的迁移合同。更新已停止,现有数据和版本保持不变。`,\n );\n }\n const instanceDirectory = this.appHomeService.getAppInstanceDirectory(appId, \"default\");\n const instanceExisted = await this.integrityService.pathExists(instanceDirectory);\n const contentSha256 = await this.filesystemService.copyToImmutableInstallDirectory({\n appId,\n appVersion: version,\n extractedDirectory,\n installDirectory,\n target: extractedMetadata.metadata.target?.kind === \"native\" ? extractedMetadata.metadata.target : undefined,\n });\n const defaultInstance = await this.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: (currentPublisher ?? publisher)?.id,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n dataSchemaVersion,\n });\n await onProgress?.(\"finalizing\");\n let registryRecord;\n try {\n registryRecord = await this.registryService.upsertInstallation({\n appId,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version,\n installDirectory,\n defaultInstance,\n sourceKind: source.kind,\n distributionMode: source.kind === \"registry\"\n ? source.registryResolution.distributionMode\n : extractedMetadata.metadata.distributionMode,\n sourceRef: source.sourceRef,\n installedAt: new Date().toISOString(),\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : this.manifestService.resolvePlatformSecurity(manifestBundle.manifest).permissions,\n ...this.readRemoteSourceFields(source.kind === \"registry\" ? source.registryResolution : undefined),\n publisher,\n manifestSchemaVersion: manifestBundle.manifest.schemaVersion,\n components: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.components.map((component) => ({\n ...component,\n componentDirectory: path.join(installDirectory, component.path),\n manifestPath: path.join(\n installDirectory,\n component.path,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n }))\n : undefined,\n primaryPanelId: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.primaryPanelId\n : undefined,\n security: isAppComponentManifestBundle(manifestBundle)\n ? this.manifestService.resolvePlatformSecurity(manifestBundle.manifest)\n : undefined,\n dataSchemaVersion,\n contentSha256,\n activate: options.activate,\n });\n } catch (error) {\n await this.integrityService.removeDirectory(installDirectory);\n if (!instanceExisted) {\n await this.instanceStorageService.rollbackNewInstance({\n instance: defaultInstance,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n });\n }\n throw error;\n }\n const installedVersion = registryRecord.installedVersions[version];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version,\n installDirectory,\n dataDirectory: defaultInstance.storage.dataDirectory,\n instance: registryRecord.defaultInstance,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode: installedVersion?.distributionMode,\n permissions: installedVersion?.permissions ?? {},\n ...this.readRemoteSourceFields(installedVersion),\n publisher: installedVersion?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: installedVersion?.manifestSchemaVersion ?? 1,\n components: installedVersion?.components,\n primaryPanelId: installedVersion?.primaryPanelId,\n };\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n },\n ): Promise<AppUpdateResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.update(appId, options),\n );\n };\n\n uninstall = async (\n appId: string,\n purgeData: boolean,\n ): Promise<AppUninstallResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.uninstall(appId, purgeData),\n );\n };\n\n list = async (): Promise<InstalledAppListItem[]> => {\n const appRecords = await this.registryService.listApps();\n return appRecords.map((appRecord) => ({\n appId: appRecord.appId,\n name: appRecord.name,\n activeVersion: appRecord.activeVersion,\n sourceKind:\n appRecord.installedVersions[appRecord.activeVersion]?.sourceKind ?? \"directory\",\n distributionMode:\n appRecord.installedVersions[appRecord.activeVersion]?.distributionMode,\n enabled: appRecord.enabled,\n manifestSchemaVersion:\n appRecord.installedVersions[appRecord.activeVersion]?.manifestSchemaVersion ?? 1,\n primaryPanelId:\n appRecord.installedVersions[appRecord.activeVersion]?.primaryPanelId,\n }));\n };\n\n reconcileFilesystem = async (): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n let appRecords = await this.registryService.listApps();\n for (const appRecord of appRecords) {\n await this.withAppOperation(appRecord.appId, async () => {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n if (!await this.integrityService.pathExists(versionRecord.installDirectory)) {\n continue;\n }\n if (!versionRecord.contentSha256) {\n const contentSha256 = await this.integrityService.calculateDigest(\n versionRecord.installDirectory,\n );\n await this.registryService.setVersionContentDigest(\n appRecord.appId,\n versionRecord.version,\n contentSha256,\n );\n }\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n });\n }\n appRecords = await this.registryService.listApps();\n const referencedInstallPaths = new Set(appRecords.flatMap((record) =>\n Object.values(record.installedVersions).map((version) => path.resolve(version.installDirectory))));\n const referencedDataPaths = new Set(appRecords.map((record) => path.resolve(\n record.defaultInstance.storage.layout === \"instance-v1\"\n ? record.defaultInstance.storage.instanceDirectory\n : record.dataDirectory,\n )));\n\n const packageDirectories = await this.filesystemService.readDirectories(\n this.appHomeService.getPackagesDirectory(),\n );\n for (const appDirectory of packageDirectories) {\n await this.filesystemService.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n this.appHomeService.getDataDirectory(),\n referencedDataPaths,\n );\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"instance-v1\") {\n continue;\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n appRecord.defaultInstance.storage.instanceDirectory,\n new Set([path.resolve(appRecord.defaultInstance.storage.dataDirectory)]),\n );\n }\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"legacy\") {\n continue;\n }\n await this.withAppOperation(appRecord.appId, async () => {\n const currentRecord = await this.registryService.getApp(appRecord.appId);\n if (!currentRecord || currentRecord.defaultInstance.storage.layout !== \"legacy\") {\n return;\n }\n const activeVersion = currentRecord.installedVersions[currentRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${currentRecord.appId}`);\n }\n const instance = await this.instanceStorageService.materializeDefaultInstance({\n appId: currentRecord.appId,\n publisherId: currentRecord.publisher?.id,\n legacyDataDirectory: currentRecord.dataDirectory,\n dataSchemaVersion: currentRecord.defaultInstance.dataSchemaVersion,\n });\n try {\n await this.registryService.upsertInstallation({\n appId: currentRecord.appId,\n name: currentRecord.name,\n description: currentRecord.description,\n version: currentRecord.activeVersion,\n installDirectory: activeVersion.installDirectory,\n defaultInstance: instance,\n sourceKind: activeVersion.sourceKind,\n sourceRef: activeVersion.sourceRef,\n installedAt: activeVersion.installedAt,\n distributionMode: activeVersion.distributionMode,\n permissions: activeVersion.permissions,\n ...this.readRemoteSourceFields(activeVersion),\n publisher: currentRecord.publisher ?? activeVersion.publisher,\n manifestSchemaVersion: activeVersion.manifestSchemaVersion,\n components: activeVersion.components,\n primaryPanelId: activeVersion.primaryPanelId,\n security: activeVersion.security,\n dataSchemaVersion: activeVersion.dataSchemaVersion,\n contentSha256: activeVersion.contentSha256,\n enabled: currentRecord.enabled,\n });\n } catch (error) {\n await this.instanceStorageService.rollbackNewInstance({\n instance,\n legacyDataDirectory: currentRecord.dataDirectory,\n });\n throw error;\n }\n });\n }\n };\n\n info = async (\n appId: string,\n options: { measureStorageUsage?: boolean } = {},\n ): Promise<AppInfoResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const installedVersions = Object.values(appRecord.installedVersions).sort((left, right) =>\n left.version.localeCompare(right.version),\n );\n const storageUsage = options.measureStorageUsage === false\n ? undefined\n : await this.instanceStorageService.measureUsage(appRecord.defaultInstance.storage);\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n description: appRecord.description,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n storage: appRecord.defaultInstance.storage,\n storageUsage,\n installedVersions: installedVersions.map((versionRecord) => ({\n version: versionRecord.version,\n installDirectory: versionRecord.installDirectory,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n installedAt: versionRecord.installedAt,\n permissions: versionRecord.permissions,\n ...this.readRemoteSourceFields(versionRecord),\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n contentSha256: versionRecord.contentSha256,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\n return await this.withAppOperation(appId, async () => {\n const appRecord = await this.registryService.setEnabled(appId, enabled);\n return {\n appId: appRecord.appId,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n };\n });\n };\n\n rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\n return await this.withAppOperation(appId, async () => {\n const currentRecord = await this.registryService.getApp(appId);\n if (!currentRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (currentRecord.activeVersion === version) {\n return {\n appId,\n activeVersion: version,\n previousVersion: version,\n enabled: currentRecord.enabled,\n rolledBack: false,\n };\n }\n const targetVersion = currentRecord.installedVersions[version];\n if (!targetVersion) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n if (targetVersion.dataSchemaVersion !== currentRecord.defaultInstance.dataSchemaVersion) {\n throw new Error(\n `应用 ${appId}@${version} 需要数据 schema ${targetVersion.dataSchemaVersion},当前实例为 ${currentRecord.defaultInstance.dataSchemaVersion}。没有可用 checkpoint,已阻止回滚。`,\n );\n }\n await this.assertVersionIntegrity(appId, version);\n const appRecord = await this.registryService.activateVersion(appId, version);\n return {\n appId,\n activeVersion: appRecord.activeVersion,\n previousVersion: currentRecord.activeVersion,\n enabled: appRecord.enabled,\n rolledBack: true,\n };\n });\n };\n\n assertVersionIntegrity = async (appId: string, version?: string): Promise<void> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const targetVersion = version ?? appRecord.activeVersion;\n const versionRecord = appRecord.installedVersions[targetVersion];\n if (!versionRecord) {\n throw new Error(`应用 ${appId} 未安装版本 ${targetVersion}。`);\n }\n const contentSha256 = await this.integrityService.assertVersion({\n appId,\n version: targetVersion,\n versionRecord,\n });\n if (!versionRecord.contentSha256) {\n await this.registryService.setVersionContentDigest(appId, targetVersion, contentSha256);\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n };\n\n withAppOperation = async <T>(appId: string, operation: () => Promise<T>): Promise<T> => {\n if (!SAFE_APP_ID_PATTERN.test(appId)) {\n throw new Error(`appId 不是安全的 App 操作标识:${appId}`);\n }\n const lockPath = path.resolve(this.appHomeService.getAppOperationLockPath(appId));\n const activeLocks = this.appOperationContext.getStore();\n if (activeLocks?.has(lockPath)) {\n return await operation();\n }\n return await this.fileLockService.withLock(lockPath, async () => {\n const nextLocks = new Set(activeLocks ?? []);\n nextLocks.add(lockPath);\n return await this.appOperationContext.run(nextLocks, operation);\n });\n };\n\n resolveLaunch = async (\n appReference: string,\n explicitDocumentGrantMap: AppDocumentGrantMap,\n ): Promise<AppLaunchResolution> => {\n const sourceType = await this.installSourceService.detectLocal(appReference, false);\n if (sourceType.kind === \"directory\") {\n return {\n appDirectory: sourceType.appDirectory,\n documentGrantMap: explicitDocumentGrantMap,\n };\n }\n const appRecord = await this.registryService.getApp(appReference);\n if (!appRecord) {\n throw new Error(`未找到应用目录,也未找到已安装应用:${appReference}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appReference}`);\n }\n await this.assertVersionIntegrity(appRecord.appId, appRecord.activeVersion);\n return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n storage: appRecord.defaultInstance.storage,\n documentGrantMap: {\n ...appRecord.grants,\n ...explicitDocumentGrantMap,\n },\n };\n };\n\n private readRemoteSourceFields = (source?: RemoteSourceFields): RemoteSourceFields => ({\n registryUrl: source?.registryUrl,\n bundleUrl: source?.bundleUrl,\n sha256: source?.sha256,\n target: source?.target,\n });\n\n persistGrants = async (\n appId: string | undefined,\n documentGrantMap: AppDocumentGrantMap,\n ): Promise<void> => {\n if (!appId || Object.keys(documentGrantMap).length === 0) {\n return;\n }\n await this.registryService.updateGrants(appId, documentGrantMap);\n };\n\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { isAppComponentManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport { AppInstallationFilesystemService } from \"#app-runtime/services/app-installation-filesystem.service.js\";\nimport { AppInstallationLifecycleService } from \"#app-runtime/services/app-installation-lifecycle.service.js\";\nimport { FileLockService } from \"#app-runtime/services/file-lock.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppBundleExtractResult } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\nimport type { AppRegistryInstalledVersion } from \"#app-runtime/types/app-registry.types.js\";\nimport { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport {\n AppInstallSourceService,\n type ResolvedAppInstallSource,\n} from \"#app-runtime/services/app-install-source.service.js\";\nimport { resolveAppInstallationContract } from \"#app-runtime/utils/app-installation-contract.utils.js\";\nimport type {\n AppInfoResult,\n AppInstallProgressHandler,\n AppActivationResult,\n AppInstallResult,\n AppLaunchResolution,\n AppUpdateResult,\n AppRollbackResult,\n AppUninstallResult,\n InstalledAppListItem,\n} from \"#app-runtime/types/app-installation.types.js\";\n\nconst SAFE_APP_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\n\ntype AppInstallOptions = {\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n trustedPublisher?: AppPublisher;\n};\n\ntype PreparedAppInstall = {\n source: ResolvedAppInstallSource;\n extractedDirectory: string;\n extractedMetadata: AppBundleExtractResult;\n manifestBundle: AppManifestBundle;\n options: AppInstallOptions;\n};\n\ntype RemoteSourceFields = Pick<AppRegistryInstalledVersion, \"registryUrl\" | \"bundleUrl\" | \"sha256\" | \"target\">;\n\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\n private readonly instanceStorageService: AppInstanceStorageService;\n private readonly fileLockService = new FileLockService();\n private readonly integrityService: AppInstallationIntegrityService;\n private readonly filesystemService: AppInstallationFilesystemService;\n private readonly lifecycleService: AppInstallationLifecycleService;\n private readonly appOperationContext = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly buildService: AppBuildService = new AppBuildService(),\n private readonly registryService: AppRegistryService = new AppRegistryService(appHomeService),\n private readonly registryConfigService: AppRegistryConfigService = new AppRegistryConfigService(\n appHomeService,\n ),\n private readonly remoteRegistryClient: AppRemoteRegistryClientService = new AppRemoteRegistryClientService(\n new AppRegistryConfigService(appHomeService),\n ),\n instanceStorageService?: AppInstanceStorageService,\n ) {\n this.installSourceService = new AppInstallSourceService(\n this.manifestService,\n this.remoteRegistryClient,\n this.bundleService,\n );\n this.instanceStorageService = instanceStorageService ?? new AppInstanceStorageService(appHomeService);\n this.integrityService = new AppInstallationIntegrityService(manifestService);\n this.filesystemService = new AppInstallationFilesystemService({\n buildService,\n integrityService: this.integrityService,\n manifestService,\n });\n this.lifecycleService = new AppInstallationLifecycleService({\n registryService,\n registryConfigService,\n remoteRegistryClient,\n instanceStorageService: this.instanceStorageService,\n integrityService: this.integrityService,\n install: this.install,\n rollback: this.rollback,\n });\n }\n\n install = async (\n appSource: string,\n options: AppInstallOptions = {},\n ): Promise<AppInstallResult> => {\n const { onProgress, registryUrl } = options;\n await onProgress?.(\"resolving\");\n const source = await this.installSourceService.resolve(appSource, registryUrl);\n const tempDirectory = await this.appHomeService.createTemporaryDirectory(\"napp-install-\");\n try {\n const bundlePath = await this.installSourceService.materializeBundle(\n source,\n tempDirectory,\n onProgress,\n );\n await onProgress?.(\"verifying\");\n const extractedDirectory = path.join(tempDirectory, \"bundle\");\n const extractedMetadata = await this.bundleService.extractBundle({\n bundlePath,\n targetDirectory: extractedDirectory,\n });\n await this.filesystemService.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n return await this.installPreparedBundle({\n source,\n extractedDirectory,\n extractedMetadata,\n manifestBundle,\n options,\n });\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n private installPreparedBundle = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const appId = prepared.manifestBundle.manifest.id;\n return await this.withAppOperation(\n appId,\n async () => await this.commitPreparedInstall(prepared),\n );\n };\n\n private commitPreparedInstall = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const { extractedDirectory, extractedMetadata, manifestBundle, options, source } = prepared;\n const { onProgress } = options;\n const { id: appId, version } = manifestBundle.manifest;\n const installDirectory = this.appHomeService.getInstallDirectory(appId, version);\n if (source.kind === \"registry\" && appId !== source.registryResolution.appId) {\n throw new Error(\n `bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${appId}`,\n );\n }\n await onProgress?.(\"installing\");\n const existingRecord = await this.registryService.getApp(appId);\n const { currentPublisher, dataSchemaVersion, publisher } =\n resolveAppInstallationContract({\n appId, existingRecord, manifestBundle, source,\n trustedPublisher: options.trustedPublisher,\n });\n const instanceDirectory = this.appHomeService.getAppInstanceDirectory(appId, \"default\");\n const instanceExisted = await this.integrityService.pathExists(instanceDirectory);\n const contentSha256 = await this.filesystemService.copyToImmutableInstallDirectory({\n appId, appVersion: version, extractedDirectory, installDirectory,\n target: extractedMetadata.metadata.target?.kind === \"native\"\n ? extractedMetadata.metadata.target\n : undefined,\n });\n let defaultInstance: Awaited<ReturnType<\n AppInstanceStorageService[\"materializeDefaultInstance\"]\n >> | undefined;\n let registryRecord;\n try {\n defaultInstance = await this.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: (currentPublisher ?? publisher)?.id,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n dataSchemaVersion,\n });\n await onProgress?.(\"finalizing\");\n registryRecord = await this.registryService.upsertInstallation({\n appId,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version,\n installDirectory,\n defaultInstance,\n sourceKind: source.kind,\n distributionMode: source.kind === \"registry\"\n ? source.registryResolution.distributionMode\n : extractedMetadata.metadata.distributionMode,\n sourceRef: source.sourceRef,\n installedAt: new Date().toISOString(),\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : this.manifestService.resolvePlatformSecurity(manifestBundle.manifest).permissions,\n ...this.readRemoteSourceFields(source.kind === \"registry\" ? source.registryResolution : undefined),\n publisher,\n manifestSchemaVersion: manifestBundle.manifest.schemaVersion,\n components: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.components.map((component) => ({\n ...component,\n componentDirectory: path.join(installDirectory, component.path),\n manifestPath: path.join(\n installDirectory,\n component.path,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n }))\n : undefined,\n primaryPanelId: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.primaryPanelId\n : undefined,\n security: isAppComponentManifestBundle(manifestBundle)\n ? this.manifestService.resolvePlatformSecurity(manifestBundle.manifest)\n : undefined,\n dataSchemaVersion,\n contentSha256,\n activate: options.activate,\n });\n } catch (error) {\n await this.integrityService.removeDirectory(installDirectory);\n if (!instanceExisted && defaultInstance) {\n await this.instanceStorageService.rollbackNewInstance({\n instance: defaultInstance,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n });\n }\n throw error;\n }\n if (!defaultInstance) {\n throw new Error(`应用 ${appId} 安装完成但缺少默认实例。`);\n }\n const installedVersion = registryRecord.installedVersions[version];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version,\n installDirectory,\n dataDirectory: defaultInstance.storage.dataDirectory,\n instance: registryRecord.defaultInstance,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode: installedVersion?.distributionMode,\n permissions: installedVersion?.permissions ?? {},\n ...this.readRemoteSourceFields(installedVersion),\n publisher: installedVersion?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: installedVersion?.manifestSchemaVersion ?? 1,\n components: installedVersion?.components,\n primaryPanelId: installedVersion?.primaryPanelId,\n };\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n },\n ): Promise<AppUpdateResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.update(appId, options),\n );\n };\n\n uninstall = async (\n appId: string,\n purgeData: boolean,\n ): Promise<AppUninstallResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.uninstall(appId, purgeData),\n );\n };\n\n list = async (): Promise<InstalledAppListItem[]> => {\n const appRecords = await this.registryService.listApps();\n return appRecords.map((appRecord) => ({\n appId: appRecord.appId,\n name: appRecord.name,\n activeVersion: appRecord.activeVersion,\n sourceKind:\n appRecord.installedVersions[appRecord.activeVersion]?.sourceKind ?? \"directory\",\n distributionMode:\n appRecord.installedVersions[appRecord.activeVersion]?.distributionMode,\n enabled: appRecord.enabled,\n manifestSchemaVersion:\n appRecord.installedVersions[appRecord.activeVersion]?.manifestSchemaVersion ?? 1,\n primaryPanelId:\n appRecord.installedVersions[appRecord.activeVersion]?.primaryPanelId,\n }));\n };\n\n reconcileFilesystem = async (): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n let appRecords = await this.registryService.listApps();\n for (const appRecord of appRecords) {\n await this.withAppOperation(appRecord.appId, async () => {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n if (!await this.integrityService.pathExists(versionRecord.installDirectory)) {\n continue;\n }\n if (!versionRecord.contentSha256) {\n const contentSha256 = await this.integrityService.calculateDigest(\n versionRecord.installDirectory,\n );\n await this.registryService.setVersionContentDigest(\n appRecord.appId,\n versionRecord.version,\n contentSha256,\n );\n }\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n });\n }\n appRecords = await this.registryService.listApps();\n const referencedInstallPaths = new Set(appRecords.flatMap((record) =>\n Object.values(record.installedVersions).map((version) => path.resolve(version.installDirectory))));\n const referencedDataPaths = new Set(appRecords.map((record) => path.resolve(\n record.defaultInstance.storage.layout === \"instance-v1\"\n ? record.defaultInstance.storage.instanceDirectory\n : record.dataDirectory,\n )));\n\n const packageDirectories = await this.filesystemService.readDirectories(\n this.appHomeService.getPackagesDirectory(),\n );\n for (const appDirectory of packageDirectories) {\n await this.filesystemService.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n this.appHomeService.getDataDirectory(),\n referencedDataPaths,\n );\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"instance-v1\") {\n continue;\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n appRecord.defaultInstance.storage.instanceDirectory,\n new Set([path.resolve(appRecord.defaultInstance.storage.dataDirectory)]),\n );\n }\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"legacy\") {\n continue;\n }\n await this.withAppOperation(appRecord.appId, async () => {\n const currentRecord = await this.registryService.getApp(appRecord.appId);\n if (!currentRecord || currentRecord.defaultInstance.storage.layout !== \"legacy\") {\n return;\n }\n const activeVersion = currentRecord.installedVersions[currentRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${currentRecord.appId}`);\n }\n const instance = await this.instanceStorageService.materializeDefaultInstance({\n appId: currentRecord.appId,\n publisherId: currentRecord.publisher?.id,\n legacyDataDirectory: currentRecord.dataDirectory,\n dataSchemaVersion: currentRecord.defaultInstance.dataSchemaVersion,\n });\n try {\n await this.registryService.upsertInstallation({\n appId: currentRecord.appId,\n name: currentRecord.name,\n description: currentRecord.description,\n version: currentRecord.activeVersion,\n installDirectory: activeVersion.installDirectory,\n defaultInstance: instance,\n sourceKind: activeVersion.sourceKind,\n sourceRef: activeVersion.sourceRef,\n installedAt: activeVersion.installedAt,\n distributionMode: activeVersion.distributionMode,\n permissions: activeVersion.permissions,\n ...this.readRemoteSourceFields(activeVersion),\n publisher: currentRecord.publisher ?? activeVersion.publisher,\n manifestSchemaVersion: activeVersion.manifestSchemaVersion,\n components: activeVersion.components,\n primaryPanelId: activeVersion.primaryPanelId,\n security: activeVersion.security,\n dataSchemaVersion: activeVersion.dataSchemaVersion,\n contentSha256: activeVersion.contentSha256,\n enabled: currentRecord.enabled,\n });\n } catch (error) {\n await this.instanceStorageService.rollbackNewInstance({\n instance,\n legacyDataDirectory: currentRecord.dataDirectory,\n });\n throw error;\n }\n });\n }\n };\n\n info = async (\n appId: string,\n options: { measureStorageUsage?: boolean } = {},\n ): Promise<AppInfoResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const installedVersions = Object.values(appRecord.installedVersions).sort((left, right) =>\n left.version.localeCompare(right.version),\n );\n const storageUsage = options.measureStorageUsage === false\n ? undefined\n : await this.instanceStorageService.measureUsage(appRecord.defaultInstance.storage);\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n description: appRecord.description,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n storage: appRecord.defaultInstance.storage,\n storageUsage,\n installedVersions: installedVersions.map((versionRecord) => ({\n version: versionRecord.version,\n installDirectory: versionRecord.installDirectory,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n installedAt: versionRecord.installedAt,\n permissions: versionRecord.permissions,\n ...this.readRemoteSourceFields(versionRecord),\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n contentSha256: versionRecord.contentSha256,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\n return await this.withAppOperation(appId, async () => {\n const appRecord = await this.registryService.setEnabled(appId, enabled);\n return {\n appId: appRecord.appId,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n };\n });\n };\n\n rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\n return await this.withAppOperation(appId, async () => {\n const currentRecord = await this.registryService.getApp(appId);\n if (!currentRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (currentRecord.activeVersion === version) {\n return {\n appId,\n activeVersion: version,\n previousVersion: version,\n enabled: currentRecord.enabled,\n rolledBack: false,\n };\n }\n const targetVersion = currentRecord.installedVersions[version];\n if (!targetVersion) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n if (targetVersion.dataSchemaVersion !== currentRecord.defaultInstance.dataSchemaVersion) {\n throw new Error(\n `应用 ${appId}@${version} 需要数据 schema ${targetVersion.dataSchemaVersion},当前实例为 ${currentRecord.defaultInstance.dataSchemaVersion}。没有可用 checkpoint,已阻止回滚。`,\n );\n }\n await this.assertVersionIntegrity(appId, version);\n const appRecord = await this.registryService.activateVersion(appId, version);\n return {\n appId,\n activeVersion: appRecord.activeVersion,\n previousVersion: currentRecord.activeVersion,\n enabled: appRecord.enabled,\n rolledBack: true,\n };\n });\n };\n\n assertVersionIntegrity = async (appId: string, version?: string): Promise<void> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const targetVersion = version ?? appRecord.activeVersion;\n const versionRecord = appRecord.installedVersions[targetVersion];\n if (!versionRecord) {\n throw new Error(`应用 ${appId} 未安装版本 ${targetVersion}。`);\n }\n const contentSha256 = await this.integrityService.assertVersion({\n appId,\n version: targetVersion,\n versionRecord,\n });\n if (!versionRecord.contentSha256) {\n await this.registryService.setVersionContentDigest(appId, targetVersion, contentSha256);\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n };\n\n withAppOperation = async <T>(appId: string, operation: () => Promise<T>): Promise<T> => {\n if (!SAFE_APP_ID_PATTERN.test(appId)) {\n throw new Error(`appId 不是安全的 App 操作标识:${appId}`);\n }\n const lockPath = path.resolve(this.appHomeService.getAppOperationLockPath(appId));\n const activeLocks = this.appOperationContext.getStore();\n if (activeLocks?.has(lockPath)) {\n return await operation();\n }\n return await this.fileLockService.withLock(lockPath, async () => {\n const nextLocks = new Set(activeLocks ?? []);\n nextLocks.add(lockPath);\n return await this.appOperationContext.run(nextLocks, operation);\n });\n };\n\n resolveLaunch = async (\n appReference: string,\n explicitDocumentGrantMap: AppDocumentGrantMap,\n ): Promise<AppLaunchResolution> => {\n const sourceType = await this.installSourceService.detectLocal(appReference, false);\n if (sourceType.kind === \"directory\") {\n return {\n appDirectory: sourceType.appDirectory,\n documentGrantMap: explicitDocumentGrantMap,\n };\n }\n const appRecord = await this.registryService.getApp(appReference);\n if (!appRecord) {\n throw new Error(`未找到应用目录,也未找到已安装应用:${appReference}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appReference}`);\n }\n await this.assertVersionIntegrity(appRecord.appId, appRecord.activeVersion);\n return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n storage: appRecord.defaultInstance.storage,\n documentGrantMap: {\n ...appRecord.grants,\n ...explicitDocumentGrantMap,\n },\n };\n };\n\n private readRemoteSourceFields = (source?: RemoteSourceFields): RemoteSourceFields => ({\n registryUrl: source?.registryUrl,\n bundleUrl: source?.bundleUrl,\n sha256: source?.sha256,\n target: source?.target,\n });\n\n persistGrants = async (\n appId: string | undefined,\n documentGrantMap: AppDocumentGrantMap,\n ): Promise<void> => {\n if (!appId || Object.keys(documentGrantMap).length === 0) {\n return;\n }\n await this.registryService.updateGrants(appId, documentGrantMap);\n };\n\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-manifest.service.d.ts","names":[],"sources":["../../src/services/app-manifest.service.ts"],"mappings":";;;;cAqBa,kBAAA;EAAA,iBAEQ,qBAAA;cAAA,qBAAA,GAAqB,wBAAA;EAGxC,IAAA,GAAc,YAAA,aAAuB,OAAA,CAAQ,iBAAA;EAU7C,SAAA,GAAa,MAAA,EAAQ,iBAAA,KAAoB,kBAAA;EAAA,QAwCjC,oBAAA;EAAA,QA8BA,mBAAA;EAAA,QA8DA,aAAA;EAgCR,uBAAA,GACE,QAAA,EAAU,oBAAA,KACT,0BAAA;EAAA,QAgCK,mBAAA;EAAA,QAcA,SAAA;EAAA,QAoBA,OAAA;EAAA,QAKA,eAAA;EAAA,QA+BA,YAAA;EAAA,QAUA,iBAAA;EAAA,QAeA,YAAA;EAAA,QAcA,eAAA;EAAA,QAkBA,8BAAA;EAAA,QAqBA,eAAA;EAAA,QA0BA,gBAAA;EAAA,QAaA,mBAAA;EAAA,QAwBA,gBAAA;EAAA,QAUA,YAAA;EAAA,QAQA,iBAAA;EAAA,QAeA,kBAAA;EAAA,QAaA,uBAAA;EAAA,QAcA,aAAA;EAAA,QAUA,qBAAA;EAAA,QAWA,YAAA;EAAA,QAOA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAIA,UAAA;EAAA,QAOA,WAAA;AAAA"}
1
+ {"version":3,"file":"app-manifest.service.d.ts","names":[],"sources":["../../src/services/app-manifest.service.ts"],"mappings":";;;;cAqBa,kBAAA;EAAA,iBAEQ,qBAAA;cAAA,qBAAA,GAAqB,wBAAA;EAGxC,IAAA,GAAc,YAAA,aAAuB,OAAA,CAAQ,iBAAA;EAU7C,SAAA,GAAa,MAAA,EAAQ,iBAAA,KAAoB,kBAAA;EAAA,QAwCjC,oBAAA;EAAA,QA8BA,mBAAA;EAAA,QA8DA,aAAA;EAgCR,uBAAA,GACE,QAAA,EAAU,oBAAA,KACT,0BAAA;EAAA,QAgCK,mBAAA;EAAA,QAcA,SAAA;EAAA,QAoBA,OAAA;EAAA,QAKA,eAAA;EAAA,QA+BA,YAAA;EAAA,QAUA,iBAAA;EAAA,QAeA,YAAA;EAAA,QAcA,eAAA;EAAA,QAkBA,8BAAA;EAAA,QAsBA,eAAA;EAAA,QA0BA,gBAAA;EAAA,QAaA,mBAAA;EAAA,QAwBA,gBAAA;EAAA,QAUA,YAAA;EAAA,QAQA,iBAAA;EAAA,QAeA,kBAAA;EAAA,QAaA,uBAAA;EAAA,QAcA,aAAA;EAAA,QAUA,qBAAA;EAAA,QAWA,YAAA;EAAA,QAOA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAIA,UAAA;EAAA,QAOA,WAAA;AAAA"}
@@ -225,7 +225,10 @@ var AppManifestService = class {
225
225
  if (!runtime) return;
226
226
  const hasService = components.some((component) => component.kind === "service");
227
227
  if (runtime.profile === "panel-only" && hasService) throw new Error("runtime.profile=panel-only 不能包含 Service component。");
228
- if (runtime.profile === "wasi") throw new Error("schema v2 Service component 尚不支持 WASI runtime;当前 Service App 合同会启动宿主进程,请使用 native-process,或改为 panel-only App。");
228
+ if (runtime.profile === "wasi") {
229
+ if (!hasService) throw new Error("wasi runtime 必须包含 Service component。");
230
+ return;
231
+ }
229
232
  if (runtime.profile !== "panel-only" && !hasService) throw new Error(`${runtime.profile} runtime 必须包含 Service component。`);
230
233
  };
231
234
  readComponentId = async (component, componentDirectory) => {
@@ -1 +1 @@
1
- {"version":3,"file":"app-manifest.service.js","names":[],"sources":["../../src/services/app-manifest.service.ts"],"sourcesContent":["import { access, lstat, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppPlatformTargetService } from \"#app-runtime/services/app-platform-target.service.js\";\nimport type {\n AppComponentManifest,\n AppComponentManifestBundle,\n AppComponentReference,\n AppDocumentAccessScope,\n AppManifest,\n AppManifestBundle,\n AppManifestSummary,\n AppPermissions,\n AppPlatformSecuritySummary,\n AppResolvedComponent,\n AppStandaloneManifest,\n AppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\n\nconst PACKAGE_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\nconst COMPONENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nexport class AppManifestService {\n constructor(\n private readonly platformTargetService = new AppPlatformTargetService(),\n ) {}\n\n load = async (appDirectory: string): Promise<AppManifestBundle> => {\n const normalizedDirectory = path.resolve(appDirectory);\n const manifestPath = path.join(normalizedDirectory, \"manifest.json\");\n const rawManifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as unknown;\n const manifest = this.parseManifest(rawManifest);\n return manifest.schemaVersion === 1\n ? await this.loadStandaloneBundle(normalizedDirectory, manifestPath, manifest)\n : await this.loadComponentBundle(normalizedDirectory, manifestPath, manifest);\n };\n\n summarize = (bundle: AppManifestBundle): AppManifestSummary => {\n if (bundle.manifest.schemaVersion === 2) {\n const componentBundle = bundle as AppComponentManifestBundle;\n return {\n schemaVersion: 2,\n id: componentBundle.manifest.id,\n name: componentBundle.manifest.name,\n version: componentBundle.manifest.version,\n description: componentBundle.manifest.description,\n manifestPath: componentBundle.manifestPath,\n iconPath: componentBundle.iconPath,\n primaryPanelId: componentBundle.primaryPanelId,\n components: componentBundle.components,\n distribution: this.platformTargetService.resolveDistribution(\n componentBundle.manifest.distribution,\n ),\n security: this.resolvePlatformSecurity(componentBundle.manifest),\n };\n }\n const standaloneBundle = bundle as AppStandaloneManifestBundle;\n const permissions = standaloneBundle.manifest.permissions ?? {};\n return {\n schemaVersion: 1,\n id: standaloneBundle.manifest.id,\n name: standaloneBundle.manifest.name,\n version: standaloneBundle.manifest.version,\n description: standaloneBundle.manifest.description,\n mainKind: standaloneBundle.manifest.main.kind,\n action:\n standaloneBundle.manifest.main.kind === \"wasm\"\n ? standaloneBundle.manifest.main.action\n : undefined,\n manifestPath: standaloneBundle.manifestPath,\n mainEntryPath: standaloneBundle.mainEntryPath,\n uiEntryPath: standaloneBundle.uiEntryPath,\n iconPath: standaloneBundle.iconPath,\n permissions,\n };\n };\n\n private loadStandaloneBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppStandaloneManifest,\n ): Promise<AppStandaloneManifestBundle> => {\n const mainEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.main.entry,\n \"main.entry\",\n );\n const uiEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.ui.entry,\n \"ui.entry\",\n );\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n mainEntryPath,\n uiEntryPath,\n uiDirectoryPath: path.dirname(uiEntryPath),\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n };\n };\n\n private loadComponentBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppComponentManifest,\n ): Promise<AppComponentManifestBundle> => {\n const components: AppResolvedComponent[] = [];\n for (const [index, component] of manifest.components.entries()) {\n const componentDirectory = await this.assertResolvedDirectory(\n appDirectory,\n component.path,\n `components[${index}].path`,\n );\n const componentId = await this.readComponentId(component, componentDirectory);\n const expectedPrefix = `${manifest.id.replace(/[^a-z0-9]+/g, \"-\")}-`;\n if (!componentId.startsWith(expectedPrefix)) {\n throw new Error(\n `组件 ${componentId} 必须使用包前缀 ${expectedPrefix}。`,\n );\n }\n components.push({\n ...component,\n id: componentId,\n componentDirectory,\n manifestPath: path.join(\n componentDirectory,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n });\n }\n const duplicateId = components.find(\n (component, index) => components.findIndex((entry) => entry.id === component.id) !== index,\n );\n if (duplicateId) {\n throw new Error(`components 包含重复组件 id:${duplicateId.id}`);\n }\n const panelIds = components\n .filter((component) => component.kind === \"panel\")\n .map((component) => component.id);\n const declaredPrimaryPanel = manifest.presentation?.primaryPanel;\n if (declaredPrimaryPanel && !panelIds.includes(declaredPrimaryPanel)) {\n throw new Error(\"presentation.primaryPanel 必须引用真实 Panel component id。\");\n }\n if (panelIds.length > 1 && !declaredPrimaryPanel) {\n throw new Error(\"包含多个 Panel 时必须声明 presentation.primaryPanel。\");\n }\n if (panelIds.length === 0 && declaredPrimaryPanel) {\n throw new Error(\"不含 Panel 的包不能声明 presentation.primaryPanel。\");\n }\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n components,\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n primaryPanelId: declaredPrimaryPanel ?? panelIds[0],\n };\n };\n\n private parseManifest = (rawManifest: unknown): AppManifest => {\n const candidate = this.assertObject(rawManifest, \"manifest.json\");\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"schemaVersion\");\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n throw new Error(\"当前只支持 schemaVersion = 1 或 2。\");\n }\n const common = this.parseCommonManifest(candidate);\n if (schemaVersion === 1) {\n return {\n schemaVersion: 1,\n ...common,\n main: this.parseMain(candidate.main),\n ui: this.parseUi(candidate.ui),\n permissions: this.parsePermissions(candidate.permissions),\n };\n }\n const components = this.parseComponents(candidate.components);\n const runtime = this.parseRuntime(candidate.runtime);\n this.assertRuntimeMatchesComponents(runtime, components);\n return {\n schemaVersion: 2,\n ...common,\n engines: this.parseEngines(candidate.engines),\n presentation: this.parsePresentation(candidate.presentation),\n runtime,\n distribution: this.platformTargetService.parseDistribution(candidate.distribution),\n storage: this.parseAppStorage(candidate.storage),\n permissions: this.parsePermissions(candidate.permissions),\n components,\n };\n };\n\n resolvePlatformSecurity = (\n manifest: AppComponentManifest,\n ): AppPlatformSecuritySummary => {\n const hasServiceComponents = manifest.components.some(\n (component) => component.kind === \"service\",\n );\n const runtimeProfile = manifest.runtime?.profile ?? (\n hasServiceComponents ? \"native-process\" : \"panel-only\"\n );\n const isolation = runtimeProfile === \"panel-only\"\n ? \"sandboxed\"\n : runtimeProfile === \"wasi\"\n ? \"host-mediated\"\n : \"full-user\";\n const declaredPermissions = manifest.permissions ?? {};\n const permissions: AppPermissions = runtimeProfile === \"native-process\"\n ? {\n ...declaredPermissions,\n storage: declaredPermissions.storage ?? true,\n capabilities: {\n ...declaredPermissions.capabilities,\n nativeProcess: true,\n },\n }\n : declaredPermissions;\n return {\n runtimeProfile,\n isolation,\n hasServiceComponents,\n inferred: manifest.runtime === undefined,\n permissions,\n };\n };\n\n private parseCommonManifest = (candidate: Record<string, unknown>) => {\n const id = this.readRequiredString(candidate.id, \"id\");\n if (!PACKAGE_ID_PATTERN.test(id)) {\n throw new Error(\"id 必须由小写字母、数字、点或连字符组成。\");\n }\n return {\n id,\n name: this.readRequiredString(candidate.name, \"name\"),\n version: this.readRequiredString(candidate.version, \"version\"),\n description: this.readOptionalString(candidate.description, \"description\"),\n icon: this.readOptionalString(candidate.icon, \"icon\"),\n };\n };\n\n private parseMain = (rawMain: unknown): AppStandaloneManifest[\"main\"] => {\n const candidate = this.assertObject(rawMain, \"main\");\n const kind = this.readRequiredString(candidate.kind, \"main.kind\");\n if (kind === \"wasm\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n export: this.readRequiredString(candidate.export, \"main.export\"),\n action: this.readRequiredString(candidate.action, \"main.action\"),\n };\n }\n if (kind === \"wasi-http-component\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n };\n }\n throw new Error(\"当前 main.kind 只支持 wasm 或 wasi-http-component。\");\n };\n\n private parseUi = (rawUi: unknown): AppStandaloneManifest[\"ui\"] => {\n const candidate = this.assertObject(rawUi, \"ui\");\n return { entry: this.readRequiredString(candidate.entry, \"ui.entry\") };\n };\n\n private parseComponents = (rawComponents: unknown): AppComponentReference[] => {\n if (!Array.isArray(rawComponents) || rawComponents.length === 0) {\n throw new Error(\"components 必须是非空数组。\");\n }\n const components = rawComponents.map((rawComponent, index) => {\n const candidate = this.assertObject(rawComponent, `components[${index}]`);\n const kind = this.readRequiredString(candidate.kind, `components[${index}].kind`);\n if (kind !== \"panel\" && kind !== \"service\") {\n throw new Error(`components[${index}].kind 只支持 panel 或 service。`);\n }\n const normalizedKind: AppComponentReference[\"kind\"] = kind;\n return {\n kind: normalizedKind,\n path: this.normalizeRelativePath(\n this.readRequiredString(candidate.path, `components[${index}].path`),\n `components[${index}].path`,\n ),\n };\n });\n for (let leftIndex = 0; leftIndex < components.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < components.length; rightIndex += 1) {\n const left = components[leftIndex]?.path;\n const right = components[rightIndex]?.path;\n if (left && right && (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`))) {\n throw new Error(`components path 不能重复或重叠:${left} / ${right}`);\n }\n }\n }\n return components;\n };\n\n private parseEngines = (\n rawEngines: unknown,\n ): AppComponentManifest[\"engines\"] => {\n if (rawEngines === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawEngines, \"engines\");\n return { nextclaw: this.readOptionalString(candidate.nextclaw, \"engines.nextclaw\") };\n };\n\n private parsePresentation = (\n rawPresentation: unknown,\n ): AppComponentManifest[\"presentation\"] => {\n if (rawPresentation === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPresentation, \"presentation\");\n return {\n primaryPanel: this.readOptionalString(\n candidate.primaryPanel,\n \"presentation.primaryPanel\",\n ),\n };\n };\n\n private parseRuntime = (\n rawRuntime: unknown,\n ): AppComponentManifest[\"runtime\"] => {\n if (rawRuntime === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawRuntime, \"runtime\");\n const profile = this.readRequiredString(candidate.profile, \"runtime.profile\");\n if (profile !== \"panel-only\" && profile !== \"wasi\" && profile !== \"native-process\") {\n throw new Error(\"runtime.profile 只支持 panel-only、wasi 或 native-process。\");\n }\n return { profile };\n };\n\n private parseAppStorage = (\n rawStorage: unknown,\n ): AppComponentManifest[\"storage\"] => {\n if (rawStorage === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawStorage, \"storage\");\n const scope = this.readRequiredString(candidate.scope, \"storage.scope\");\n if (scope !== \"global\") {\n throw new Error(\"当前 storage.scope 只支持 global。\");\n }\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"storage.schemaVersion\");\n if (!Number.isSafeInteger(schemaVersion) || schemaVersion < 1) {\n throw new Error(\"storage.schemaVersion 必须是正整数。\");\n }\n return { scope, schemaVersion };\n };\n\n private assertRuntimeMatchesComponents = (\n runtime: AppComponentManifest[\"runtime\"],\n components: AppComponentReference[],\n ): void => {\n if (!runtime) {\n return;\n }\n const hasService = components.some((component) => component.kind === \"service\");\n if (runtime.profile === \"panel-only\" && hasService) {\n throw new Error(\"runtime.profile=panel-only 不能包含 Service component。\");\n }\n if (runtime.profile === \"wasi\") {\n throw new Error(\n \"schema v2 Service component 尚不支持 WASI runtime;当前 Service App 合同会启动宿主进程,请使用 native-process,或改为 panel-only App。\",\n );\n }\n if (runtime.profile !== \"panel-only\" && !hasService) {\n throw new Error(`${runtime.profile} runtime 必须包含 Service component。`);\n }\n };\n\n private readComponentId = async (\n component: AppComponentReference,\n componentDirectory: string,\n ): Promise<string> => {\n const manifestFile = component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\";\n const raw = JSON.parse(\n await readFile(path.join(componentDirectory, manifestFile), \"utf-8\"),\n ) as unknown;\n const manifest = this.assertObject(raw, `${component.path}/${manifestFile}`);\n const id = this.readRequiredString(manifest.id, `${component.path}/${manifestFile}.id`);\n if (!COMPONENT_ID_PATTERN.test(id)) {\n throw new Error(`组件 id 必须是 kebab-case:${id}`);\n }\n const directoryName = path.basename(componentDirectory);\n const expectedId = component.kind === \"panel\"\n ? directoryName.replace(/\\.panel$/, \"\")\n : directoryName;\n if (component.kind === \"panel\" && !directoryName.endsWith(\".panel\")) {\n throw new Error(`Panel component 目录必须以 .panel 结尾:${component.path}`);\n }\n if (id !== expectedId) {\n throw new Error(`组件 id 必须与目录名一致:期望 ${expectedId},实际 ${id}`);\n }\n return id;\n };\n\n private parsePermissions = (rawPermissions: unknown): AppPermissions | undefined => {\n if (rawPermissions === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPermissions, \"permissions\");\n return {\n documentAccess: this.parseDocumentAccess(candidate.documentAccess),\n allowedDomains: this.parseStringArray(candidate.allowedDomains, \"permissions.allowedDomains\"),\n storage: this.parseStorage(candidate.storage),\n capabilities: this.parseCapabilities(candidate.capabilities),\n };\n };\n\n private parseDocumentAccess = (rawDocumentAccess: unknown): AppDocumentAccessScope[] | undefined => {\n if (rawDocumentAccess === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawDocumentAccess)) {\n throw new Error(\"permissions.documentAccess 必须是数组。\");\n }\n return rawDocumentAccess.map((scope, index) => {\n const candidate = this.assertObject(scope, `permissions.documentAccess[${index}]`);\n const mode = this.readRequiredString(candidate.mode, `permissions.documentAccess[${index}].mode`);\n if (mode !== \"read\" && mode !== \"read-write\") {\n throw new Error(`permissions.documentAccess[${index}].mode 只支持 read 或 read-write。`);\n }\n return {\n id: this.readRequiredString(candidate.id, `permissions.documentAccess[${index}].id`),\n mode,\n description: this.readOptionalString(\n candidate.description,\n `permissions.documentAccess[${index}].description`,\n ),\n };\n });\n };\n\n private parseStringArray = (rawValue: unknown, fieldName: string): string[] | undefined => {\n if (rawValue === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawValue)) {\n throw new Error(`${fieldName} 必须是字符串数组。`);\n }\n return rawValue.map((item, index) => this.readRequiredString(item, `${fieldName}[${index}]`));\n };\n\n private parseStorage = (rawStorage: unknown): AppPermissions[\"storage\"] | undefined => {\n if (rawStorage === undefined || typeof rawStorage === \"boolean\") {\n return rawStorage;\n }\n const candidate = this.assertObject(rawStorage, \"permissions.storage\");\n return { namespace: this.readOptionalString(candidate.namespace, \"permissions.storage.namespace\") };\n };\n\n private parseCapabilities = (rawCapabilities: unknown): AppPermissions[\"capabilities\"] | undefined => {\n if (rawCapabilities === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawCapabilities, \"permissions.capabilities\");\n return {\n hostBridge: candidate.hostBridge === undefined\n ? undefined\n : this.readBoolean(candidate.hostBridge, \"permissions.capabilities.hostBridge\"),\n nativeProcess: candidate.nativeProcess === undefined\n ? undefined\n : this.readBoolean(candidate.nativeProcess, \"permissions.capabilities.nativeProcess\"),\n };\n };\n\n private assertResolvedFile = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isFile() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通文件。`);\n }\n return resolvedPath;\n };\n\n private assertResolvedDirectory = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isDirectory() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通目录。`);\n }\n await access(resolvedPath);\n return resolvedPath;\n };\n\n private resolveInside = (appDirectory: string, relativePath: string, fieldName: string): string => {\n const normalizedPath = this.normalizeRelativePath(relativePath, fieldName);\n const resolvedPath = path.resolve(appDirectory, normalizedPath);\n const relative = path.relative(appDirectory, resolvedPath);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`${fieldName} 不能指向应用目录之外。`);\n }\n return resolvedPath;\n };\n\n private normalizeRelativePath = (relativePath: string, fieldName: string): string => {\n if (path.isAbsolute(relativePath) || relativePath.includes(\"\\0\")) {\n throw new Error(`${fieldName} 必须使用包内相对路径。`);\n }\n const segments = relativePath.replace(/\\\\/g, \"/\").split(\"/\");\n if (segments.some((segment) => !segment || segment === \".\" || segment === \"..\")) {\n throw new Error(`${fieldName} 包含非法路径片段。`);\n }\n return segments.join(\"/\");\n };\n\n private assertObject = (value: unknown, fieldName: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (value: unknown, fieldName: string): string | undefined => {\n return value === undefined ? undefined : this.readRequiredString(value, fieldName);\n };\n\n private readNumber = (value: unknown, fieldName: string): number => {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${fieldName} 必须是数字。`);\n }\n return value;\n };\n\n private readBoolean = (value: unknown, fieldName: string): boolean => {\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-manifest.service.js","names":[],"sources":["../../src/services/app-manifest.service.ts"],"sourcesContent":["import { access, lstat, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppPlatformTargetService } from \"#app-runtime/services/app-platform-target.service.js\";\nimport type {\n AppComponentManifest,\n AppComponentManifestBundle,\n AppComponentReference,\n AppDocumentAccessScope,\n AppManifest,\n AppManifestBundle,\n AppManifestSummary,\n AppPermissions,\n AppPlatformSecuritySummary,\n AppResolvedComponent,\n AppStandaloneManifest,\n AppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\n\nconst PACKAGE_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\nconst COMPONENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nexport class AppManifestService {\n constructor(\n private readonly platformTargetService = new AppPlatformTargetService(),\n ) {}\n\n load = async (appDirectory: string): Promise<AppManifestBundle> => {\n const normalizedDirectory = path.resolve(appDirectory);\n const manifestPath = path.join(normalizedDirectory, \"manifest.json\");\n const rawManifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as unknown;\n const manifest = this.parseManifest(rawManifest);\n return manifest.schemaVersion === 1\n ? await this.loadStandaloneBundle(normalizedDirectory, manifestPath, manifest)\n : await this.loadComponentBundle(normalizedDirectory, manifestPath, manifest);\n };\n\n summarize = (bundle: AppManifestBundle): AppManifestSummary => {\n if (bundle.manifest.schemaVersion === 2) {\n const componentBundle = bundle as AppComponentManifestBundle;\n return {\n schemaVersion: 2,\n id: componentBundle.manifest.id,\n name: componentBundle.manifest.name,\n version: componentBundle.manifest.version,\n description: componentBundle.manifest.description,\n manifestPath: componentBundle.manifestPath,\n iconPath: componentBundle.iconPath,\n primaryPanelId: componentBundle.primaryPanelId,\n components: componentBundle.components,\n distribution: this.platformTargetService.resolveDistribution(\n componentBundle.manifest.distribution,\n ),\n security: this.resolvePlatformSecurity(componentBundle.manifest),\n };\n }\n const standaloneBundle = bundle as AppStandaloneManifestBundle;\n const permissions = standaloneBundle.manifest.permissions ?? {};\n return {\n schemaVersion: 1,\n id: standaloneBundle.manifest.id,\n name: standaloneBundle.manifest.name,\n version: standaloneBundle.manifest.version,\n description: standaloneBundle.manifest.description,\n mainKind: standaloneBundle.manifest.main.kind,\n action:\n standaloneBundle.manifest.main.kind === \"wasm\"\n ? standaloneBundle.manifest.main.action\n : undefined,\n manifestPath: standaloneBundle.manifestPath,\n mainEntryPath: standaloneBundle.mainEntryPath,\n uiEntryPath: standaloneBundle.uiEntryPath,\n iconPath: standaloneBundle.iconPath,\n permissions,\n };\n };\n\n private loadStandaloneBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppStandaloneManifest,\n ): Promise<AppStandaloneManifestBundle> => {\n const mainEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.main.entry,\n \"main.entry\",\n );\n const uiEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.ui.entry,\n \"ui.entry\",\n );\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n mainEntryPath,\n uiEntryPath,\n uiDirectoryPath: path.dirname(uiEntryPath),\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n };\n };\n\n private loadComponentBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppComponentManifest,\n ): Promise<AppComponentManifestBundle> => {\n const components: AppResolvedComponent[] = [];\n for (const [index, component] of manifest.components.entries()) {\n const componentDirectory = await this.assertResolvedDirectory(\n appDirectory,\n component.path,\n `components[${index}].path`,\n );\n const componentId = await this.readComponentId(component, componentDirectory);\n const expectedPrefix = `${manifest.id.replace(/[^a-z0-9]+/g, \"-\")}-`;\n if (!componentId.startsWith(expectedPrefix)) {\n throw new Error(\n `组件 ${componentId} 必须使用包前缀 ${expectedPrefix}。`,\n );\n }\n components.push({\n ...component,\n id: componentId,\n componentDirectory,\n manifestPath: path.join(\n componentDirectory,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n });\n }\n const duplicateId = components.find(\n (component, index) => components.findIndex((entry) => entry.id === component.id) !== index,\n );\n if (duplicateId) {\n throw new Error(`components 包含重复组件 id:${duplicateId.id}`);\n }\n const panelIds = components\n .filter((component) => component.kind === \"panel\")\n .map((component) => component.id);\n const declaredPrimaryPanel = manifest.presentation?.primaryPanel;\n if (declaredPrimaryPanel && !panelIds.includes(declaredPrimaryPanel)) {\n throw new Error(\"presentation.primaryPanel 必须引用真实 Panel component id。\");\n }\n if (panelIds.length > 1 && !declaredPrimaryPanel) {\n throw new Error(\"包含多个 Panel 时必须声明 presentation.primaryPanel。\");\n }\n if (panelIds.length === 0 && declaredPrimaryPanel) {\n throw new Error(\"不含 Panel 的包不能声明 presentation.primaryPanel。\");\n }\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n components,\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n primaryPanelId: declaredPrimaryPanel ?? panelIds[0],\n };\n };\n\n private parseManifest = (rawManifest: unknown): AppManifest => {\n const candidate = this.assertObject(rawManifest, \"manifest.json\");\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"schemaVersion\");\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n throw new Error(\"当前只支持 schemaVersion = 1 或 2。\");\n }\n const common = this.parseCommonManifest(candidate);\n if (schemaVersion === 1) {\n return {\n schemaVersion: 1,\n ...common,\n main: this.parseMain(candidate.main),\n ui: this.parseUi(candidate.ui),\n permissions: this.parsePermissions(candidate.permissions),\n };\n }\n const components = this.parseComponents(candidate.components);\n const runtime = this.parseRuntime(candidate.runtime);\n this.assertRuntimeMatchesComponents(runtime, components);\n return {\n schemaVersion: 2,\n ...common,\n engines: this.parseEngines(candidate.engines),\n presentation: this.parsePresentation(candidate.presentation),\n runtime,\n distribution: this.platformTargetService.parseDistribution(candidate.distribution),\n storage: this.parseAppStorage(candidate.storage),\n permissions: this.parsePermissions(candidate.permissions),\n components,\n };\n };\n\n resolvePlatformSecurity = (\n manifest: AppComponentManifest,\n ): AppPlatformSecuritySummary => {\n const hasServiceComponents = manifest.components.some(\n (component) => component.kind === \"service\",\n );\n const runtimeProfile = manifest.runtime?.profile ?? (\n hasServiceComponents ? \"native-process\" : \"panel-only\"\n );\n const isolation = runtimeProfile === \"panel-only\"\n ? \"sandboxed\"\n : runtimeProfile === \"wasi\"\n ? \"host-mediated\"\n : \"full-user\";\n const declaredPermissions = manifest.permissions ?? {};\n const permissions: AppPermissions = runtimeProfile === \"native-process\"\n ? {\n ...declaredPermissions,\n storage: declaredPermissions.storage ?? true,\n capabilities: {\n ...declaredPermissions.capabilities,\n nativeProcess: true,\n },\n }\n : declaredPermissions;\n return {\n runtimeProfile,\n isolation,\n hasServiceComponents,\n inferred: manifest.runtime === undefined,\n permissions,\n };\n };\n\n private parseCommonManifest = (candidate: Record<string, unknown>) => {\n const id = this.readRequiredString(candidate.id, \"id\");\n if (!PACKAGE_ID_PATTERN.test(id)) {\n throw new Error(\"id 必须由小写字母、数字、点或连字符组成。\");\n }\n return {\n id,\n name: this.readRequiredString(candidate.name, \"name\"),\n version: this.readRequiredString(candidate.version, \"version\"),\n description: this.readOptionalString(candidate.description, \"description\"),\n icon: this.readOptionalString(candidate.icon, \"icon\"),\n };\n };\n\n private parseMain = (rawMain: unknown): AppStandaloneManifest[\"main\"] => {\n const candidate = this.assertObject(rawMain, \"main\");\n const kind = this.readRequiredString(candidate.kind, \"main.kind\");\n if (kind === \"wasm\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n export: this.readRequiredString(candidate.export, \"main.export\"),\n action: this.readRequiredString(candidate.action, \"main.action\"),\n };\n }\n if (kind === \"wasi-http-component\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n };\n }\n throw new Error(\"当前 main.kind 只支持 wasm 或 wasi-http-component。\");\n };\n\n private parseUi = (rawUi: unknown): AppStandaloneManifest[\"ui\"] => {\n const candidate = this.assertObject(rawUi, \"ui\");\n return { entry: this.readRequiredString(candidate.entry, \"ui.entry\") };\n };\n\n private parseComponents = (rawComponents: unknown): AppComponentReference[] => {\n if (!Array.isArray(rawComponents) || rawComponents.length === 0) {\n throw new Error(\"components 必须是非空数组。\");\n }\n const components = rawComponents.map((rawComponent, index) => {\n const candidate = this.assertObject(rawComponent, `components[${index}]`);\n const kind = this.readRequiredString(candidate.kind, `components[${index}].kind`);\n if (kind !== \"panel\" && kind !== \"service\") {\n throw new Error(`components[${index}].kind 只支持 panel 或 service。`);\n }\n const normalizedKind: AppComponentReference[\"kind\"] = kind;\n return {\n kind: normalizedKind,\n path: this.normalizeRelativePath(\n this.readRequiredString(candidate.path, `components[${index}].path`),\n `components[${index}].path`,\n ),\n };\n });\n for (let leftIndex = 0; leftIndex < components.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < components.length; rightIndex += 1) {\n const left = components[leftIndex]?.path;\n const right = components[rightIndex]?.path;\n if (left && right && (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`))) {\n throw new Error(`components path 不能重复或重叠:${left} / ${right}`);\n }\n }\n }\n return components;\n };\n\n private parseEngines = (\n rawEngines: unknown,\n ): AppComponentManifest[\"engines\"] => {\n if (rawEngines === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawEngines, \"engines\");\n return { nextclaw: this.readOptionalString(candidate.nextclaw, \"engines.nextclaw\") };\n };\n\n private parsePresentation = (\n rawPresentation: unknown,\n ): AppComponentManifest[\"presentation\"] => {\n if (rawPresentation === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPresentation, \"presentation\");\n return {\n primaryPanel: this.readOptionalString(\n candidate.primaryPanel,\n \"presentation.primaryPanel\",\n ),\n };\n };\n\n private parseRuntime = (\n rawRuntime: unknown,\n ): AppComponentManifest[\"runtime\"] => {\n if (rawRuntime === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawRuntime, \"runtime\");\n const profile = this.readRequiredString(candidate.profile, \"runtime.profile\");\n if (profile !== \"panel-only\" && profile !== \"wasi\" && profile !== \"native-process\") {\n throw new Error(\"runtime.profile 只支持 panel-only、wasi 或 native-process。\");\n }\n return { profile };\n };\n\n private parseAppStorage = (\n rawStorage: unknown,\n ): AppComponentManifest[\"storage\"] => {\n if (rawStorage === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawStorage, \"storage\");\n const scope = this.readRequiredString(candidate.scope, \"storage.scope\");\n if (scope !== \"global\") {\n throw new Error(\"当前 storage.scope 只支持 global。\");\n }\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"storage.schemaVersion\");\n if (!Number.isSafeInteger(schemaVersion) || schemaVersion < 1) {\n throw new Error(\"storage.schemaVersion 必须是正整数。\");\n }\n return { scope, schemaVersion };\n };\n\n private assertRuntimeMatchesComponents = (\n runtime: AppComponentManifest[\"runtime\"],\n components: AppComponentReference[],\n ): void => {\n if (!runtime) {\n return;\n }\n const hasService = components.some((component) => component.kind === \"service\");\n if (runtime.profile === \"panel-only\" && hasService) {\n throw new Error(\"runtime.profile=panel-only 不能包含 Service component。\");\n }\n if (runtime.profile === \"wasi\") {\n if (!hasService) {\n throw new Error(\"wasi runtime 必须包含 Service component。\");\n }\n return;\n }\n if (runtime.profile !== \"panel-only\" && !hasService) {\n throw new Error(`${runtime.profile} runtime 必须包含 Service component。`);\n }\n };\n\n private readComponentId = async (\n component: AppComponentReference,\n componentDirectory: string,\n ): Promise<string> => {\n const manifestFile = component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\";\n const raw = JSON.parse(\n await readFile(path.join(componentDirectory, manifestFile), \"utf-8\"),\n ) as unknown;\n const manifest = this.assertObject(raw, `${component.path}/${manifestFile}`);\n const id = this.readRequiredString(manifest.id, `${component.path}/${manifestFile}.id`);\n if (!COMPONENT_ID_PATTERN.test(id)) {\n throw new Error(`组件 id 必须是 kebab-case:${id}`);\n }\n const directoryName = path.basename(componentDirectory);\n const expectedId = component.kind === \"panel\"\n ? directoryName.replace(/\\.panel$/, \"\")\n : directoryName;\n if (component.kind === \"panel\" && !directoryName.endsWith(\".panel\")) {\n throw new Error(`Panel component 目录必须以 .panel 结尾:${component.path}`);\n }\n if (id !== expectedId) {\n throw new Error(`组件 id 必须与目录名一致:期望 ${expectedId},实际 ${id}`);\n }\n return id;\n };\n\n private parsePermissions = (rawPermissions: unknown): AppPermissions | undefined => {\n if (rawPermissions === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPermissions, \"permissions\");\n return {\n documentAccess: this.parseDocumentAccess(candidate.documentAccess),\n allowedDomains: this.parseStringArray(candidate.allowedDomains, \"permissions.allowedDomains\"),\n storage: this.parseStorage(candidate.storage),\n capabilities: this.parseCapabilities(candidate.capabilities),\n };\n };\n\n private parseDocumentAccess = (rawDocumentAccess: unknown): AppDocumentAccessScope[] | undefined => {\n if (rawDocumentAccess === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawDocumentAccess)) {\n throw new Error(\"permissions.documentAccess 必须是数组。\");\n }\n return rawDocumentAccess.map((scope, index) => {\n const candidate = this.assertObject(scope, `permissions.documentAccess[${index}]`);\n const mode = this.readRequiredString(candidate.mode, `permissions.documentAccess[${index}].mode`);\n if (mode !== \"read\" && mode !== \"read-write\") {\n throw new Error(`permissions.documentAccess[${index}].mode 只支持 read 或 read-write。`);\n }\n return {\n id: this.readRequiredString(candidate.id, `permissions.documentAccess[${index}].id`),\n mode,\n description: this.readOptionalString(\n candidate.description,\n `permissions.documentAccess[${index}].description`,\n ),\n };\n });\n };\n\n private parseStringArray = (rawValue: unknown, fieldName: string): string[] | undefined => {\n if (rawValue === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawValue)) {\n throw new Error(`${fieldName} 必须是字符串数组。`);\n }\n return rawValue.map((item, index) => this.readRequiredString(item, `${fieldName}[${index}]`));\n };\n\n private parseStorage = (rawStorage: unknown): AppPermissions[\"storage\"] | undefined => {\n if (rawStorage === undefined || typeof rawStorage === \"boolean\") {\n return rawStorage;\n }\n const candidate = this.assertObject(rawStorage, \"permissions.storage\");\n return { namespace: this.readOptionalString(candidate.namespace, \"permissions.storage.namespace\") };\n };\n\n private parseCapabilities = (rawCapabilities: unknown): AppPermissions[\"capabilities\"] | undefined => {\n if (rawCapabilities === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawCapabilities, \"permissions.capabilities\");\n return {\n hostBridge: candidate.hostBridge === undefined\n ? undefined\n : this.readBoolean(candidate.hostBridge, \"permissions.capabilities.hostBridge\"),\n nativeProcess: candidate.nativeProcess === undefined\n ? undefined\n : this.readBoolean(candidate.nativeProcess, \"permissions.capabilities.nativeProcess\"),\n };\n };\n\n private assertResolvedFile = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isFile() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通文件。`);\n }\n return resolvedPath;\n };\n\n private assertResolvedDirectory = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isDirectory() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通目录。`);\n }\n await access(resolvedPath);\n return resolvedPath;\n };\n\n private resolveInside = (appDirectory: string, relativePath: string, fieldName: string): string => {\n const normalizedPath = this.normalizeRelativePath(relativePath, fieldName);\n const resolvedPath = path.resolve(appDirectory, normalizedPath);\n const relative = path.relative(appDirectory, resolvedPath);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`${fieldName} 不能指向应用目录之外。`);\n }\n return resolvedPath;\n };\n\n private normalizeRelativePath = (relativePath: string, fieldName: string): string => {\n if (path.isAbsolute(relativePath) || relativePath.includes(\"\\0\")) {\n throw new Error(`${fieldName} 必须使用包内相对路径。`);\n }\n const segments = relativePath.replace(/\\\\/g, \"/\").split(\"/\");\n if (segments.some((segment) => !segment || segment === \".\" || segment === \"..\")) {\n throw new Error(`${fieldName} 包含非法路径片段。`);\n }\n return segments.join(\"/\");\n };\n\n private assertObject = (value: unknown, fieldName: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (value: unknown, fieldName: string): string | undefined => {\n return value === undefined ? undefined : this.readRequiredString(value, fieldName);\n };\n\n private readNumber = (value: unknown, fieldName: string): number => {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${fieldName} 必须是数字。`);\n }\n return value;\n };\n\n private readBoolean = (value: unknown, fieldName: string): boolean => {\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n}\n"],"mappings":"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"}
@@ -76,7 +76,6 @@ type AppPublishResult = {
76
76
  install: {
77
77
  kind: "registry";
78
78
  spec: string;
79
- command: string;
80
79
  registry: string;
81
80
  };
82
81
  };
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.types.d.ts","names":[],"sources":["../../src/types/app-publish.types.ts"],"mappings":";;;;;cAQa,gCAAA;AAAA,KAGD,qBAAA;EACV,KAAA;EACA,WAAA;AAAA;AAAA,KAGU,sBAAA;EACV,IAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,GAAY,YAAA;EACZ,OAAA,GAAU,qBAAA;AAAA;AAAA,KAGA,cAAA;EACV,IAAA;EACA,aAAA;AAAA;AAAA,KAGU,kBAAA;EACV,MAAA,EAAQ,iBAAA;EACR,YAAA;EACA,YAAA;EACA,SAAA;AAAA;AAAA,KAGG,qBAAA;EACH,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,EAAW,YAAA;EACX,OAAA,GAAU,qBAAA;EACV,gBAAA,EAAkB,mBAAA;EAClB,QAAA,EAAU,WAAA;EACV,WAAA,EAAa,cAAA;EACb,KAAA,EAAO,cAAA;AAAA;AAAA,KAGG,iBAAA,GAAoB,qBAAA;EAE1B,YAAA;EACA,YAAA;EACA,SAAA;AAAA;EAGA,YAAA;EACA,YAAA;EACA,SAAA,EAAW,kBAAA;AAAA;AAAA,KAIL,gBAAA;EACV,OAAA;EACA,IAAA;IACE,IAAA;IACA,KAAA;IACA,UAAA;IACA,OAAA;IACA,aAAA;IACA,IAAA;IACA,aAAA;IACA,MAAA;IACA,OAAA;MACE,IAAA;MACA,IAAA;MACA,OAAA;MACA,QAAA;IAAA;EAAA;EAGJ,YAAA;IACE,IAAA;IACA,MAAA;IACA,IAAA,EAAM,mBAAA;IACN,SAAA,GAAY,KAAA;MACV,MAAA,EAAQ,iBAAA;MACR,IAAA;MACA,MAAA;MACA,SAAA;IAAA;EAAA;EAGJ,SAAA;AAAA"}
1
+ {"version":3,"file":"app-publish.types.d.ts","names":[],"sources":["../../src/types/app-publish.types.ts"],"mappings":";;;;;cAQa,gCAAA;AAAA,KAGD,qBAAA;EACV,KAAA;EACA,WAAA;AAAA;AAAA,KAGU,sBAAA;EACV,IAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,GAAY,YAAA;EACZ,OAAA,GAAU,qBAAA;AAAA;AAAA,KAGA,cAAA;EACV,IAAA;EACA,aAAA;AAAA;AAAA,KAGU,kBAAA;EACV,MAAA,EAAQ,iBAAA;EACR,YAAA;EACA,YAAA;EACA,SAAA;AAAA;AAAA,KAGG,qBAAA;EACH,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,EAAW,YAAA;EACX,OAAA,GAAU,qBAAA;EACV,gBAAA,EAAkB,mBAAA;EAClB,QAAA,EAAU,WAAA;EACV,WAAA,EAAa,cAAA;EACb,KAAA,EAAO,cAAA;AAAA;AAAA,KAGG,iBAAA,GAAoB,qBAAA;EAE1B,YAAA;EACA,YAAA;EACA,SAAA;AAAA;EAGA,YAAA;EACA,YAAA;EACA,SAAA,EAAW,kBAAA;AAAA;AAAA,KAIL,gBAAA;EACV,OAAA;EACA,IAAA;IACE,IAAA;IACA,KAAA;IACA,UAAA;IACA,OAAA;IACA,aAAA;IACA,IAAA;IACA,aAAA;IACA,MAAA;IACA,OAAA;MACE,IAAA;MACA,IAAA;MACA,QAAA;IAAA;EAAA;EAGJ,YAAA;IACE,IAAA;IACA,MAAA;IACA,IAAA,EAAM,mBAAA;IACN,SAAA,GAAY,KAAA;MACV,MAAA,EAAQ,iBAAA;MACR,IAAA;MACA,MAAA;MACA,SAAA;IAAA;EAAA;EAGJ,SAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.types.js","names":[],"sources":["../../src/types/app-publish.types.ts"],"sourcesContent":["import type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type {\n AppArtifactTarget,\n AppManifest,\n AppPermissions,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\n\nexport const DEFAULT_APP_MARKETPLACE_API_BASE =\n \"https://apps-registry.nextclaw.io\";\n\nexport type AppMarketplaceVisuals = {\n cover: string;\n accentColor: string;\n};\n\nexport type AppMarketplaceMetadata = {\n slug: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured?: boolean;\n publisher?: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n};\n\nexport type AppPublishFile = {\n path: string;\n contentBase64: string;\n};\n\nexport type AppPublishArtifact = {\n target: AppArtifactTarget;\n bundleBase64: string;\n bundleSha256: string;\n sizeBytes: number;\n};\n\ntype AppPublishPayloadBase = {\n slug: string;\n appId: string;\n name: string;\n version: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured: boolean;\n publisher: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n distributionMode: AppDistributionMode;\n manifest: AppManifest;\n permissions: AppPermissions;\n files: AppPublishFile[];\n};\n\nexport type AppPublishPayload = AppPublishPayloadBase & (\n | {\n bundleBase64: string;\n bundleSha256: string;\n artifacts?: never;\n }\n | {\n bundleBase64?: never;\n bundleSha256?: never;\n artifacts: AppPublishArtifact[];\n }\n);\n\nexport type AppPublishResult = {\n created: boolean;\n item: {\n slug: string;\n appId: string;\n ownerScope: string;\n appName: string;\n publishStatus: \"pending\" | \"published\";\n name: string;\n latestVersion: string;\n webUrl?: string;\n install: {\n kind: \"registry\";\n spec: string;\n command: string;\n registry: string;\n };\n };\n distribution: {\n path?: string;\n sha256?: string;\n mode: AppDistributionMode;\n artifacts?: Array<{\n target: AppArtifactTarget;\n path?: string;\n sha256: string;\n sizeBytes: number;\n }>;\n };\n fileCount: number;\n};\n"],"mappings":";AAQA,MAAa,mCACX"}
1
+ {"version":3,"file":"app-publish.types.js","names":[],"sources":["../../src/types/app-publish.types.ts"],"sourcesContent":["import type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type {\n AppArtifactTarget,\n AppManifest,\n AppPermissions,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\n\nexport const DEFAULT_APP_MARKETPLACE_API_BASE =\n \"https://apps-registry.nextclaw.io\";\n\nexport type AppMarketplaceVisuals = {\n cover: string;\n accentColor: string;\n};\n\nexport type AppMarketplaceMetadata = {\n slug: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured?: boolean;\n publisher?: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n};\n\nexport type AppPublishFile = {\n path: string;\n contentBase64: string;\n};\n\nexport type AppPublishArtifact = {\n target: AppArtifactTarget;\n bundleBase64: string;\n bundleSha256: string;\n sizeBytes: number;\n};\n\ntype AppPublishPayloadBase = {\n slug: string;\n appId: string;\n name: string;\n version: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured: boolean;\n publisher: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n distributionMode: AppDistributionMode;\n manifest: AppManifest;\n permissions: AppPermissions;\n files: AppPublishFile[];\n};\n\nexport type AppPublishPayload = AppPublishPayloadBase & (\n | {\n bundleBase64: string;\n bundleSha256: string;\n artifacts?: never;\n }\n | {\n bundleBase64?: never;\n bundleSha256?: never;\n artifacts: AppPublishArtifact[];\n }\n);\n\nexport type AppPublishResult = {\n created: boolean;\n item: {\n slug: string;\n appId: string;\n ownerScope: string;\n appName: string;\n publishStatus: \"pending\" | \"published\";\n name: string;\n latestVersion: string;\n webUrl?: string;\n install: {\n kind: \"registry\";\n spec: string;\n registry: string;\n };\n };\n distribution: {\n path?: string;\n sha256?: string;\n mode: AppDistributionMode;\n artifacts?: Array<{\n target: AppArtifactTarget;\n path?: string;\n sha256: string;\n sizeBytes: number;\n }>;\n };\n fileCount: number;\n};\n"],"mappings":";AAQA,MAAa,mCACX"}
@@ -0,0 +1,18 @@
1
+ //#region src/utils/app-installation-contract.utils.ts
2
+ function resolveAppInstallationContract(params) {
3
+ const publisher = params.source.kind === "registry" ? params.source.registryResolution.publisher : params.trustedPublisher;
4
+ const currentPublisher = params.existingRecord?.publisher ?? params.existingRecord?.installedVersions[params.existingRecord.activeVersion]?.publisher;
5
+ if (currentPublisher && currentPublisher.id !== publisher?.id) throw new Error(`应用 ${params.appId} 已绑定发布者 ${currentPublisher.id},拒绝由 ${publisher?.id ?? "未验证本地来源"} 覆盖。`);
6
+ const dataSchemaVersion = params.manifestBundle.manifest.schemaVersion === 2 ? params.manifestBundle.manifest.storage?.schemaVersion ?? 1 : 1;
7
+ const currentSchemaVersion = params.existingRecord?.defaultInstance.dataSchemaVersion;
8
+ if (currentSchemaVersion !== void 0 && currentSchemaVersion !== dataSchemaVersion) throw new Error(`应用 ${params.appId} 数据 schema 从 ${currentSchemaVersion} 升级到 ${dataSchemaVersion},但包内没有受支持的迁移合同。更新已停止,现有数据和版本保持不变。`);
9
+ return {
10
+ currentPublisher,
11
+ dataSchemaVersion,
12
+ publisher
13
+ };
14
+ }
15
+ //#endregion
16
+ export { resolveAppInstallationContract };
17
+
18
+ //# sourceMappingURL=app-installation-contract.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-installation-contract.utils.js","names":[],"sources":["../../src/utils/app-installation-contract.utils.ts"],"sourcesContent":["import type { AppManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\nimport type { AppRegistryAppRecord } from \"#app-runtime/types/app-registry.types.js\";\nimport type { ResolvedAppInstallSource } from \"#app-runtime/services/app-install-source.service.js\";\n\nexport function resolveAppInstallationContract(params: {\n appId: string;\n existingRecord?: AppRegistryAppRecord;\n manifestBundle: AppManifestBundle;\n source: ResolvedAppInstallSource;\n trustedPublisher?: AppPublisher;\n}): {\n currentPublisher?: AppPublisher;\n dataSchemaVersion: number;\n publisher?: AppPublisher;\n} {\n const publisher = params.source.kind === \"registry\"\n ? params.source.registryResolution.publisher\n : params.trustedPublisher;\n const currentPublisher = params.existingRecord?.publisher ??\n params.existingRecord?.installedVersions[params.existingRecord.activeVersion]?.publisher;\n if (currentPublisher && currentPublisher.id !== publisher?.id) {\n throw new Error(\n `应用 ${params.appId} 已绑定发布者 ${currentPublisher.id},拒绝由 ${publisher?.id ?? \"未验证本地来源\"} 覆盖。`,\n );\n }\n const dataSchemaVersion = params.manifestBundle.manifest.schemaVersion === 2\n ? params.manifestBundle.manifest.storage?.schemaVersion ?? 1\n : 1;\n const currentSchemaVersion = params.existingRecord?.defaultInstance.dataSchemaVersion;\n if (currentSchemaVersion !== undefined && currentSchemaVersion !== dataSchemaVersion) {\n throw new Error(\n `应用 ${params.appId} 数据 schema 从 ${currentSchemaVersion} 升级到 ${dataSchemaVersion},但包内没有受支持的迁移合同。更新已停止,现有数据和版本保持不变。`,\n );\n }\n return { currentPublisher, dataSchemaVersion, publisher };\n}\n"],"mappings":";AAKA,SAAgB,+BAA+B,QAU7C;CACA,MAAM,YAAY,OAAO,OAAO,SAAS,aACrC,OAAO,OAAO,mBAAmB,YACjC,OAAO;CACX,MAAM,mBAAmB,OAAO,gBAAgB,aAC9C,OAAO,gBAAgB,kBAAkB,OAAO,eAAe,gBAAgB;AACjF,KAAI,oBAAoB,iBAAiB,OAAO,WAAW,GACzD,OAAM,IAAI,MACR,MAAM,OAAO,MAAM,UAAU,iBAAiB,GAAG,OAAO,WAAW,MAAM,UAAU,MACpF;CAEH,MAAM,oBAAoB,OAAO,eAAe,SAAS,kBAAkB,IACvE,OAAO,eAAe,SAAS,SAAS,iBAAiB,IACzD;CACJ,MAAM,uBAAuB,OAAO,gBAAgB,gBAAgB;AACpE,KAAI,yBAAyB,KAAA,KAAa,yBAAyB,kBACjE,OAAM,IAAI,MACR,MAAM,OAAO,MAAM,eAAe,qBAAqB,OAAO,kBAAkB,mCACjF;AAEH,QAAO;EAAE;EAAkB;EAAmB;EAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/app-runtime",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "private": false,
5
5
  "description": "Standalone micro app runtime and CLI for NextClaw apps.",
6
6
  "type": "module",
@@ -56,7 +56,8 @@
56
56
  "README.md"
57
57
  ],
58
58
  "dependencies": {
59
- "fflate": "^0.8.2"
59
+ "fflate": "^0.8.2",
60
+ "@nextclaw/shared": "0.4.29"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@types/node": "^20.17.6",