@nextclaw/app-runtime 0.9.13 → 0.9.14

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.
Files changed (306) hide show
  1. package/README.md +2 -2
  2. package/dist/{main.js → app/main.js} +2 -2
  3. package/dist/app/main.js.map +1 -0
  4. package/dist/{commands → controllers}/create.controller.d.ts +2 -2
  5. package/dist/controllers/create.controller.d.ts.map +1 -0
  6. package/dist/{commands → controllers}/create.controller.js +2 -2
  7. package/dist/controllers/create.controller.js.map +1 -0
  8. package/dist/{commands → controllers}/dev.controller.js +1 -1
  9. package/dist/controllers/dev.controller.js.map +1 -0
  10. package/dist/{commands → controllers}/grant.controller.d.ts +3 -3
  11. package/dist/controllers/grant.controller.d.ts.map +1 -0
  12. package/dist/{commands → controllers}/grant.controller.js +2 -2
  13. package/dist/controllers/grant.controller.js.map +1 -0
  14. package/dist/{commands → controllers}/info.controller.d.ts +2 -2
  15. package/dist/controllers/info.controller.d.ts.map +1 -0
  16. package/dist/{commands → controllers}/info.controller.js +2 -2
  17. package/dist/controllers/info.controller.js.map +1 -0
  18. package/dist/{commands → controllers}/inspect.controller.js +8 -2
  19. package/dist/controllers/inspect.controller.js.map +1 -0
  20. package/dist/{commands → controllers}/install.controller.d.ts +2 -2
  21. package/dist/controllers/install.controller.d.ts.map +1 -0
  22. package/dist/{commands → controllers}/install.controller.js +2 -2
  23. package/dist/controllers/install.controller.js.map +1 -0
  24. package/dist/{commands → controllers}/list.controller.d.ts +2 -2
  25. package/dist/controllers/list.controller.d.ts.map +1 -0
  26. package/dist/{commands → controllers}/list.controller.js +2 -2
  27. package/dist/controllers/list.controller.js.map +1 -0
  28. package/dist/controllers/pack.controller.d.ts +19 -0
  29. package/dist/controllers/pack.controller.d.ts.map +1 -0
  30. package/dist/{commands → controllers}/pack.controller.js +8 -4
  31. package/dist/controllers/pack.controller.js.map +1 -0
  32. package/dist/{commands → controllers}/permissions.controller.d.ts +2 -2
  33. package/dist/controllers/permissions.controller.d.ts.map +1 -0
  34. package/dist/{commands → controllers}/permissions.controller.js +2 -2
  35. package/dist/controllers/permissions.controller.js.map +1 -0
  36. package/dist/{commands → controllers}/publish.controller.d.ts +3 -3
  37. package/dist/controllers/publish.controller.d.ts.map +1 -0
  38. package/dist/{commands → controllers}/publish.controller.js +2 -2
  39. package/dist/controllers/publish.controller.js.map +1 -0
  40. package/dist/{commands → controllers}/registry.controller.d.ts +2 -2
  41. package/dist/controllers/registry.controller.d.ts.map +1 -0
  42. package/dist/{commands → controllers}/registry.controller.js +2 -2
  43. package/dist/controllers/registry.controller.js.map +1 -0
  44. package/dist/{commands → controllers}/revoke.controller.d.ts +2 -2
  45. package/dist/controllers/revoke.controller.d.ts.map +1 -0
  46. package/dist/{commands → controllers}/revoke.controller.js +2 -2
  47. package/dist/controllers/revoke.controller.js.map +1 -0
  48. package/dist/{commands → controllers}/run.controller.js +8 -6
  49. package/dist/controllers/run.controller.js.map +1 -0
  50. package/dist/{commands → controllers}/uninstall.controller.d.ts +2 -2
  51. package/dist/controllers/uninstall.controller.d.ts.map +1 -0
  52. package/dist/{commands → controllers}/uninstall.controller.js +2 -2
  53. package/dist/controllers/uninstall.controller.js.map +1 -0
  54. package/dist/{commands → controllers}/update.controller.d.ts +2 -2
  55. package/dist/controllers/update.controller.d.ts.map +1 -0
  56. package/dist/{commands → controllers}/update.controller.js +2 -2
  57. package/dist/controllers/update.controller.js.map +1 -0
  58. package/dist/{commands → controllers}/validate-publish.controller.d.ts +3 -3
  59. package/dist/controllers/validate-publish.controller.d.ts.map +1 -0
  60. package/dist/{commands → controllers}/validate-publish.controller.js +9 -5
  61. package/dist/controllers/validate-publish.controller.js.map +1 -0
  62. package/dist/index.d.ts +51 -51
  63. package/dist/index.js +46 -45
  64. package/dist/package.js +1 -1
  65. package/dist/{runtime → services}/app-build.service.d.ts +2 -2
  66. package/dist/services/app-build.service.d.ts.map +1 -0
  67. package/dist/{runtime → services}/app-build.service.js +4 -2
  68. package/dist/services/app-build.service.js.map +1 -0
  69. package/dist/{bundle → services}/app-bundle.service.d.ts +18 -6
  70. package/dist/services/app-bundle.service.d.ts.map +1 -0
  71. package/dist/services/app-bundle.service.js +305 -0
  72. package/dist/services/app-bundle.service.js.map +1 -0
  73. package/dist/{permissions → services}/app-grant.service.d.ts +4 -4
  74. package/dist/services/app-grant.service.d.ts.map +1 -0
  75. package/dist/{permissions → services}/app-grant.service.js +7 -4
  76. package/dist/services/app-grant.service.js.map +1 -0
  77. package/dist/{paths → services}/app-home.service.d.ts +1 -1
  78. package/dist/services/app-home.service.d.ts.map +1 -0
  79. package/dist/{paths → services}/app-home.service.js +1 -1
  80. package/dist/services/app-home.service.js.map +1 -0
  81. package/dist/{host → services}/app-host.service.d.ts +2 -2
  82. package/dist/services/app-host.service.d.ts.map +1 -0
  83. package/dist/{host → services}/app-host.service.js +3 -3
  84. package/dist/services/app-host.service.js.map +1 -0
  85. package/dist/services/app-install-source.service.js +72 -0
  86. package/dist/services/app-install-source.service.js.map +1 -0
  87. package/dist/{install → services}/app-installation.service.d.ts +15 -14
  88. package/dist/services/app-installation.service.d.ts.map +1 -0
  89. package/dist/services/app-installation.service.js +332 -0
  90. package/dist/services/app-installation.service.js.map +1 -0
  91. package/dist/services/app-instance.service.d.ts +34 -0
  92. package/dist/services/app-instance.service.d.ts.map +1 -0
  93. package/dist/{host → services}/app-instance.service.js +4 -4
  94. package/dist/services/app-instance.service.js.map +1 -0
  95. package/dist/{manifest → services}/app-manifest.service.d.ts +12 -2
  96. package/dist/services/app-manifest.service.d.ts.map +1 -0
  97. package/dist/services/app-manifest.service.js +271 -0
  98. package/dist/services/app-manifest.service.js.map +1 -0
  99. package/dist/{publish → services}/app-marketplace-client.service.d.ts +2 -2
  100. package/dist/services/app-marketplace-client.service.d.ts.map +1 -0
  101. package/dist/{publish → services}/app-marketplace-client.service.js +2 -2
  102. package/dist/services/app-marketplace-client.service.js.map +1 -0
  103. package/dist/{publish → services}/app-marketplace-metadata.service.d.ts +3 -3
  104. package/dist/services/app-marketplace-metadata.service.d.ts.map +1 -0
  105. package/dist/{publish → services}/app-marketplace-metadata.service.js +1 -1
  106. package/dist/services/app-marketplace-metadata.service.js.map +1 -0
  107. package/dist/{permissions → services}/app-permissions.service.d.ts +3 -3
  108. package/dist/services/app-permissions.service.d.ts.map +1 -0
  109. package/dist/{permissions → services}/app-permissions.service.js +4 -1
  110. package/dist/services/app-permissions.service.js.map +1 -0
  111. package/dist/{publish → services}/app-publish-validation.service.d.ts +9 -7
  112. package/dist/services/app-publish-validation.service.d.ts.map +1 -0
  113. package/dist/{publish → services}/app-publish-validation.service.js +14 -10
  114. package/dist/services/app-publish-validation.service.js.map +1 -0
  115. package/dist/{publish → services}/app-publish.service.d.ts +5 -5
  116. package/dist/services/app-publish.service.d.ts.map +1 -0
  117. package/dist/{publish → services}/app-publish.service.js +6 -6
  118. package/dist/services/app-publish.service.js.map +1 -0
  119. package/dist/{registry → services}/app-registry-config.service.d.ts +3 -3
  120. package/dist/{registry → services}/app-registry-config.service.d.ts.map +1 -1
  121. package/dist/{registry → services}/app-registry-config.service.js +3 -3
  122. package/dist/services/app-registry-config.service.js.map +1 -0
  123. package/dist/{registry → services}/app-registry.service.d.ts +18 -5
  124. package/dist/services/app-registry.service.d.ts.map +1 -0
  125. package/dist/services/app-registry.service.js +210 -0
  126. package/dist/services/app-registry.service.js.map +1 -0
  127. package/dist/{registry → services}/app-remote-registry-client.service.d.ts +6 -2
  128. package/dist/services/app-remote-registry-client.service.d.ts.map +1 -0
  129. package/dist/{registry → services}/app-remote-registry-client.service.js +54 -8
  130. package/dist/services/app-remote-registry-client.service.js.map +1 -0
  131. package/dist/{cli → services}/app-runtime-cli.service.d.ts +1 -1
  132. package/dist/services/app-runtime-cli.service.d.ts.map +1 -0
  133. package/dist/{cli → services}/app-runtime-cli.service.js +19 -19
  134. package/dist/services/app-runtime-cli.service.js.map +1 -0
  135. package/dist/{cli → services}/app-runtime-options.service.d.ts +6 -6
  136. package/dist/services/app-runtime-options.service.d.ts.map +1 -0
  137. package/dist/{cli → services}/app-runtime-options.service.js +3 -9
  138. package/dist/services/app-runtime-options.service.js.map +1 -0
  139. package/dist/{runtime → services}/app-runtime-toolchain.service.d.ts +1 -1
  140. package/dist/services/app-runtime-toolchain.service.d.ts.map +1 -0
  141. package/dist/{runtime → services}/app-runtime-toolchain.service.js +1 -1
  142. package/dist/services/app-runtime-toolchain.service.js.map +1 -0
  143. package/dist/{scaffold → services}/app-scaffold.service.d.ts +1 -1
  144. package/dist/{scaffold → services}/app-scaffold.service.d.ts.map +1 -1
  145. package/dist/{scaffold → services}/app-scaffold.service.js +1 -1
  146. package/dist/{scaffold → services}/app-scaffold.service.js.map +1 -1
  147. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.d.ts +1 -1
  148. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.d.ts.map +1 -1
  149. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.js +1 -1
  150. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.js.map +1 -1
  151. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.d.ts +1 -1
  152. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.d.ts.map +1 -1
  153. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.js +1 -1
  154. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.js.map +1 -1
  155. package/dist/{bridge → services}/host-bridge.service.d.ts +2 -2
  156. package/dist/services/host-bridge.service.d.ts.map +1 -0
  157. package/dist/{bridge → services}/host-bridge.service.js +1 -1
  158. package/dist/services/host-bridge.service.js.map +1 -0
  159. package/dist/services/main-runner.service.d.ts +9 -0
  160. package/dist/services/main-runner.service.d.ts.map +1 -0
  161. package/dist/services/main-runner.service.js +6 -0
  162. package/dist/services/main-runner.service.js.map +1 -0
  163. package/dist/{publish → services}/platform-auth-state.service.d.ts +1 -1
  164. package/dist/services/platform-auth-state.service.d.ts.map +1 -0
  165. package/dist/{publish → services}/platform-auth-state.service.js +1 -1
  166. package/dist/services/platform-auth-state.service.js.map +1 -0
  167. package/dist/{ui → services}/ui-server.service.d.ts +3 -3
  168. package/dist/services/ui-server.service.d.ts.map +1 -0
  169. package/dist/{ui → services}/ui-server.service.js +1 -1
  170. package/dist/services/ui-server.service.js.map +1 -0
  171. package/dist/{runtime → services}/wasm-main-runner.service.d.ts +3 -3
  172. package/dist/services/wasm-main-runner.service.d.ts.map +1 -0
  173. package/dist/{runtime → services}/wasm-main-runner.service.js +2 -2
  174. package/dist/services/wasm-main-runner.service.js.map +1 -0
  175. package/dist/{sidecar → services}/wasm-sidecar-client.service.d.ts +1 -1
  176. package/dist/services/wasm-sidecar-client.service.d.ts.map +1 -0
  177. package/dist/{sidecar → services}/wasm-sidecar-client.service.js +1 -1
  178. package/dist/services/wasm-sidecar-client.service.js.map +1 -0
  179. package/dist/{runtime → services}/wasmtime-wasi-http-component.service.d.ts +1 -1
  180. package/dist/services/wasmtime-wasi-http-component.service.d.ts.map +1 -0
  181. package/dist/{runtime → services}/wasmtime-wasi-http-component.service.js +1 -1
  182. package/dist/services/wasmtime-wasi-http-component.service.js.map +1 -0
  183. package/dist/{bundle → types}/app-bundle.types.d.ts +1 -1
  184. package/dist/types/app-bundle.types.d.ts.map +1 -0
  185. package/dist/{install → types}/app-installation.types.d.ts +27 -7
  186. package/dist/types/app-installation.types.d.ts.map +1 -0
  187. package/dist/types/app-manifest.types.d.ts +119 -0
  188. package/dist/types/app-manifest.types.d.ts.map +1 -0
  189. package/dist/types/app-manifest.types.js +11 -0
  190. package/dist/types/app-manifest.types.js.map +1 -0
  191. package/dist/{permissions → types}/app-permissions.types.d.ts +2 -2
  192. package/dist/types/app-permissions.types.d.ts.map +1 -0
  193. package/dist/{publish → types}/app-publish.types.d.ts +4 -4
  194. package/dist/types/app-publish.types.d.ts.map +1 -0
  195. package/dist/{publish → types}/app-publish.types.js +1 -1
  196. package/dist/types/app-publish.types.js.map +1 -0
  197. package/dist/{registry → types}/app-registry.types.d.ts +8 -4
  198. package/dist/types/app-registry.types.d.ts.map +1 -0
  199. package/dist/{registry → types}/app-remote-registry.types.d.ts +3 -3
  200. package/dist/types/app-remote-registry.types.d.ts.map +1 -0
  201. package/dist/{registry → types}/app-remote-registry.types.js +1 -1
  202. package/dist/types/app-remote-registry.types.js.map +1 -0
  203. package/dist/{runtime → types}/main-runner.types.d.ts +3 -3
  204. package/dist/types/main-runner.types.d.ts.map +1 -0
  205. package/package.json +10 -3
  206. package/dist/bridge/host-bridge.service.d.ts.map +0 -1
  207. package/dist/bridge/host-bridge.service.js.map +0 -1
  208. package/dist/bundle/app-bundle.service.d.ts.map +0 -1
  209. package/dist/bundle/app-bundle.service.js +0 -196
  210. package/dist/bundle/app-bundle.service.js.map +0 -1
  211. package/dist/bundle/app-bundle.types.d.ts.map +0 -1
  212. package/dist/cli/app-runtime-cli.service.d.ts.map +0 -1
  213. package/dist/cli/app-runtime-cli.service.js.map +0 -1
  214. package/dist/cli/app-runtime-options.service.d.ts.map +0 -1
  215. package/dist/cli/app-runtime-options.service.js.map +0 -1
  216. package/dist/commands/create.controller.d.ts.map +0 -1
  217. package/dist/commands/create.controller.js.map +0 -1
  218. package/dist/commands/dev.controller.js.map +0 -1
  219. package/dist/commands/grant.controller.d.ts.map +0 -1
  220. package/dist/commands/grant.controller.js.map +0 -1
  221. package/dist/commands/info.controller.d.ts.map +0 -1
  222. package/dist/commands/info.controller.js.map +0 -1
  223. package/dist/commands/inspect.controller.js.map +0 -1
  224. package/dist/commands/install.controller.d.ts.map +0 -1
  225. package/dist/commands/install.controller.js.map +0 -1
  226. package/dist/commands/list.controller.d.ts.map +0 -1
  227. package/dist/commands/list.controller.js.map +0 -1
  228. package/dist/commands/pack.controller.d.ts +0 -17
  229. package/dist/commands/pack.controller.d.ts.map +0 -1
  230. package/dist/commands/pack.controller.js.map +0 -1
  231. package/dist/commands/permissions.controller.d.ts.map +0 -1
  232. package/dist/commands/permissions.controller.js.map +0 -1
  233. package/dist/commands/publish.controller.d.ts.map +0 -1
  234. package/dist/commands/publish.controller.js.map +0 -1
  235. package/dist/commands/registry.controller.d.ts.map +0 -1
  236. package/dist/commands/registry.controller.js.map +0 -1
  237. package/dist/commands/revoke.controller.d.ts.map +0 -1
  238. package/dist/commands/revoke.controller.js.map +0 -1
  239. package/dist/commands/run.controller.js.map +0 -1
  240. package/dist/commands/uninstall.controller.d.ts.map +0 -1
  241. package/dist/commands/uninstall.controller.js.map +0 -1
  242. package/dist/commands/update.controller.d.ts.map +0 -1
  243. package/dist/commands/update.controller.js.map +0 -1
  244. package/dist/commands/validate-publish.controller.d.ts.map +0 -1
  245. package/dist/commands/validate-publish.controller.js.map +0 -1
  246. package/dist/host/app-host.service.d.ts.map +0 -1
  247. package/dist/host/app-host.service.js.map +0 -1
  248. package/dist/host/app-instance.service.d.ts +0 -34
  249. package/dist/host/app-instance.service.d.ts.map +0 -1
  250. package/dist/host/app-instance.service.js.map +0 -1
  251. package/dist/install/app-installation.service.d.ts.map +0 -1
  252. package/dist/install/app-installation.service.js +0 -276
  253. package/dist/install/app-installation.service.js.map +0 -1
  254. package/dist/install/app-installation.types.d.ts.map +0 -1
  255. package/dist/main.js.map +0 -1
  256. package/dist/manifest/app-manifest.service.d.ts.map +0 -1
  257. package/dist/manifest/app-manifest.service.js +0 -147
  258. package/dist/manifest/app-manifest.service.js.map +0 -1
  259. package/dist/manifest/app-manifest.types.d.ts +0 -68
  260. package/dist/manifest/app-manifest.types.d.ts.map +0 -1
  261. package/dist/paths/app-home.service.d.ts.map +0 -1
  262. package/dist/paths/app-home.service.js.map +0 -1
  263. package/dist/permissions/app-grant.service.d.ts.map +0 -1
  264. package/dist/permissions/app-grant.service.js.map +0 -1
  265. package/dist/permissions/app-permissions.service.d.ts.map +0 -1
  266. package/dist/permissions/app-permissions.service.js.map +0 -1
  267. package/dist/permissions/app-permissions.types.d.ts.map +0 -1
  268. package/dist/publish/app-marketplace-client.service.d.ts.map +0 -1
  269. package/dist/publish/app-marketplace-client.service.js.map +0 -1
  270. package/dist/publish/app-marketplace-metadata.service.d.ts.map +0 -1
  271. package/dist/publish/app-marketplace-metadata.service.js.map +0 -1
  272. package/dist/publish/app-publish-validation.service.d.ts.map +0 -1
  273. package/dist/publish/app-publish-validation.service.js.map +0 -1
  274. package/dist/publish/app-publish.service.d.ts.map +0 -1
  275. package/dist/publish/app-publish.service.js.map +0 -1
  276. package/dist/publish/app-publish.types.d.ts.map +0 -1
  277. package/dist/publish/app-publish.types.js.map +0 -1
  278. package/dist/publish/platform-auth-state.service.d.ts.map +0 -1
  279. package/dist/publish/platform-auth-state.service.js.map +0 -1
  280. package/dist/registry/app-registry-config.service.js.map +0 -1
  281. package/dist/registry/app-registry.service.d.ts.map +0 -1
  282. package/dist/registry/app-registry.service.js +0 -118
  283. package/dist/registry/app-registry.service.js.map +0 -1
  284. package/dist/registry/app-registry.types.d.ts.map +0 -1
  285. package/dist/registry/app-remote-registry-client.service.d.ts.map +0 -1
  286. package/dist/registry/app-remote-registry-client.service.js.map +0 -1
  287. package/dist/registry/app-remote-registry.types.d.ts.map +0 -1
  288. package/dist/registry/app-remote-registry.types.js.map +0 -1
  289. package/dist/runtime/app-build.service.d.ts.map +0 -1
  290. package/dist/runtime/app-build.service.js.map +0 -1
  291. package/dist/runtime/app-runtime-toolchain.service.d.ts.map +0 -1
  292. package/dist/runtime/app-runtime-toolchain.service.js.map +0 -1
  293. package/dist/runtime/main-runner.service.d.ts +0 -9
  294. package/dist/runtime/main-runner.service.d.ts.map +0 -1
  295. package/dist/runtime/main-runner.service.js +0 -6
  296. package/dist/runtime/main-runner.service.js.map +0 -1
  297. package/dist/runtime/main-runner.types.d.ts.map +0 -1
  298. package/dist/runtime/wasm-main-runner.service.d.ts.map +0 -1
  299. package/dist/runtime/wasm-main-runner.service.js.map +0 -1
  300. package/dist/runtime/wasmtime-wasi-http-component.service.d.ts.map +0 -1
  301. package/dist/runtime/wasmtime-wasi-http-component.service.js.map +0 -1
  302. package/dist/sidecar/wasm-sidecar-client.service.d.ts.map +0 -1
  303. package/dist/sidecar/wasm-sidecar-client.service.js.map +0 -1
  304. package/dist/ui/ui-server.service.d.ts.map +0 -1
  305. package/dist/ui/ui-server.service.js.map +0 -1
  306. /package/dist/{main.d.ts → app/main.d.ts} +0 -0
@@ -0,0 +1,305 @@
1
+ import { AppManifestService } from "./app-manifest.service.js";
2
+ import { isAppComponentManifestBundle, isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ import { access, lstat, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { strToU8, unzipSync, zipSync } from "fflate";
7
+ //#region src/services/app-bundle.service.ts
8
+ const MAX_COMPRESSED_BYTES = 25 * 1024 * 1024;
9
+ const MAX_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;
10
+ const MAX_FILE_BYTES = 25 * 1024 * 1024;
11
+ const MAX_FILE_COUNT = 2e3;
12
+ const CHECKSUMS_PATH = ".napp/checksums.json";
13
+ const BUNDLE_METADATA_PATH = ".napp/bundle.json";
14
+ var AppBundleService = class {
15
+ constructor(manifestService = new AppManifestService()) {
16
+ this.manifestService = manifestService;
17
+ }
18
+ packAppDirectory = async (params) => {
19
+ const { appDirectory, outputPath, mode: requestedMode } = params;
20
+ const bundle = await this.manifestService.load(appDirectory);
21
+ const mode = requestedMode ?? "bundle";
22
+ if (isAppComponentManifestBundle(bundle) && mode !== "bundle") throw new Error("schema v2 组合包只支持 bundle 分发,不允许运行安装脚本。");
23
+ const { appFiles, filePaths } = mode === "source" ? await this.collectSourceFiles(bundle) : await this.collectRuntimeFiles(bundle);
24
+ this.assertFileBudgets(appFiles);
25
+ const metadata = this.buildMetadata(bundle.manifest.id, bundle.manifest.name, bundle.manifest.version, mode);
26
+ const bundleJsonBytes = strToU8(`${JSON.stringify(metadata, null, 2)}\n`);
27
+ const checksums = this.buildChecksums({
28
+ ...appFiles,
29
+ [BUNDLE_METADATA_PATH]: bundleJsonBytes
30
+ });
31
+ const checksumsJsonBytes = strToU8(`${JSON.stringify(checksums, null, 2)}\n`);
32
+ const archiveBytes = zipSync({
33
+ ...appFiles,
34
+ [BUNDLE_METADATA_PATH]: bundleJsonBytes,
35
+ [CHECKSUMS_PATH]: checksumsJsonBytes
36
+ }, { level: 9 });
37
+ if (archiveBytes.byteLength > MAX_COMPRESSED_BYTES) throw new Error(`bundle 压缩后超过 ${MAX_COMPRESSED_BYTES} bytes 上限。`);
38
+ const resolvedOutputPath = outputPath ? path.resolve(outputPath) : path.join(path.dirname(bundle.appDirectory), `${this.normalizeBundleFileName(bundle.manifest.id)}-${bundle.manifest.version}.napp`);
39
+ await mkdir(path.dirname(resolvedOutputPath), { recursive: true });
40
+ await writeFile(resolvedOutputPath, Buffer.from(archiveBytes));
41
+ return {
42
+ bundlePath: resolvedOutputPath,
43
+ metadata,
44
+ sizeBytes: archiveBytes.byteLength,
45
+ filePaths
46
+ };
47
+ };
48
+ extractBundle = async (params) => {
49
+ const bundlePath = path.resolve(params.bundlePath);
50
+ const targetDirectory = path.resolve(params.targetDirectory);
51
+ const { archive, metadata, checksums } = await this.readValidatedArchive(bundlePath);
52
+ await this.replaceTargetWithArchive({
53
+ archive,
54
+ metadata,
55
+ targetDirectory
56
+ });
57
+ return {
58
+ appDirectory: targetDirectory,
59
+ metadata,
60
+ checksums
61
+ };
62
+ };
63
+ readValidatedArchive = async (bundlePath) => {
64
+ const bundleStats = await stat(bundlePath);
65
+ if (!bundleStats.isFile() || bundleStats.size > MAX_COMPRESSED_BYTES) throw new Error(`bundle 压缩体积超过 ${MAX_COMPRESSED_BYTES} bytes 上限。`);
66
+ let fileCount = 0;
67
+ let totalBytes = 0;
68
+ const archive = unzipSync(new Uint8Array(await readFile(bundlePath)), { filter: (file) => {
69
+ fileCount += 1;
70
+ totalBytes += file.originalSize;
71
+ if (fileCount > MAX_FILE_COUNT) throw new Error(`bundle 文件数超过 ${MAX_FILE_COUNT} 上限。`);
72
+ if (file.originalSize > MAX_FILE_BYTES) throw new Error(`bundle 单文件超过 ${MAX_FILE_BYTES} bytes 上限:${file.name}`);
73
+ if (totalBytes > MAX_UNCOMPRESSED_BYTES) throw new Error(`bundle 解压后超过 ${MAX_UNCOMPRESSED_BYTES} bytes 上限。`);
74
+ return true;
75
+ } });
76
+ const normalizedArchive = this.normalizeArchive(archive);
77
+ const metadata = this.readMetadataFromArchive(normalizedArchive);
78
+ const checksums = this.readChecksumsFromArchive(normalizedArchive);
79
+ this.verifyArchiveChecksums(normalizedArchive, checksums);
80
+ return {
81
+ archive: normalizedArchive,
82
+ metadata,
83
+ checksums
84
+ };
85
+ };
86
+ replaceTargetWithArchive = async (params) => {
87
+ const { archive, metadata, targetDirectory } = params;
88
+ const targetParent = path.dirname(targetDirectory);
89
+ const operationId = randomUUID();
90
+ const stagedDirectory = path.join(targetParent, `.${path.basename(targetDirectory)}.extracting-${operationId}`);
91
+ const backupDirectory = path.join(targetParent, `.${path.basename(targetDirectory)}.backup-${operationId}`);
92
+ const targetExists = await this.pathExists(targetDirectory);
93
+ await mkdir(targetParent, { recursive: true });
94
+ try {
95
+ await mkdir(stagedDirectory);
96
+ await this.writeArchiveEntries(archive, stagedDirectory);
97
+ await this.assertExtractedManifest(stagedDirectory, metadata);
98
+ if (targetExists) await rename(targetDirectory, backupDirectory);
99
+ await rename(stagedDirectory, targetDirectory);
100
+ await rm(backupDirectory, {
101
+ recursive: true,
102
+ force: true
103
+ });
104
+ } catch (error) {
105
+ await rm(stagedDirectory, {
106
+ recursive: true,
107
+ force: true
108
+ });
109
+ if (targetExists && await this.pathExists(backupDirectory)) {
110
+ await rm(targetDirectory, {
111
+ recursive: true,
112
+ force: true
113
+ });
114
+ try {
115
+ await rename(backupDirectory, targetDirectory);
116
+ } catch (restoreError) {
117
+ throw new AggregateError([error, restoreError], `bundle 解压失败,且无法恢复原目标目录:${targetDirectory}`);
118
+ }
119
+ }
120
+ throw error;
121
+ } finally {
122
+ await rm(stagedDirectory, {
123
+ recursive: true,
124
+ force: true
125
+ });
126
+ await rm(backupDirectory, {
127
+ recursive: true,
128
+ force: true
129
+ });
130
+ }
131
+ };
132
+ writeArchiveEntries = async (archive, targetDirectory) => {
133
+ for (const [entryName, bytes] of Object.entries(archive)) {
134
+ const targetPath = path.join(targetDirectory, entryName);
135
+ await mkdir(path.dirname(targetPath), { recursive: true });
136
+ await writeFile(targetPath, Buffer.from(bytes));
137
+ }
138
+ };
139
+ assertExtractedManifest = async (appDirectory, metadata) => {
140
+ const manifestBundle = await this.manifestService.load(appDirectory);
141
+ if (metadata.appId !== manifestBundle.manifest.id || metadata.name !== manifestBundle.manifest.name || metadata.version !== manifestBundle.manifest.version) throw new Error("bundle metadata 与 manifest.json 身份不一致。");
142
+ if (manifestBundle.manifest.schemaVersion === 2 && metadata.distributionMode !== "bundle") throw new Error("schema v2 组合包只支持 bundle 分发。");
143
+ };
144
+ collectRuntimeFiles = async (bundle) => {
145
+ const filePaths = new Set([path.relative(bundle.appDirectory, bundle.manifestPath).replace(/\\/g, "/")]);
146
+ if (isAppStandaloneManifestBundle(bundle)) {
147
+ filePaths.add(path.relative(bundle.appDirectory, bundle.mainEntryPath).replace(/\\/g, "/"));
148
+ await this.collectDirectoryPaths(bundle.uiDirectoryPath, bundle.appDirectory, filePaths);
149
+ await this.collectDirectoryPaths(bundle.assetsDirectoryPath, bundle.appDirectory, filePaths);
150
+ } else {
151
+ for (const component of bundle.components) await this.collectDirectoryPaths(component.componentDirectory, bundle.appDirectory, filePaths);
152
+ await this.collectDirectoryPaths(bundle.assetsDirectoryPath, bundle.appDirectory, filePaths);
153
+ await this.addOptionalFile(bundle.appDirectory, "marketplace.json", filePaths);
154
+ }
155
+ if (bundle.iconPath) filePaths.add(path.relative(bundle.appDirectory, bundle.iconPath).replace(/\\/g, "/"));
156
+ return await this.readAppFiles(bundle.appDirectory, filePaths);
157
+ };
158
+ collectSourceFiles = async (bundle) => {
159
+ if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包不支持 source 分发。");
160
+ const filePaths = /* @__PURE__ */ new Set();
161
+ await this.collectSourceDirectoryPaths(bundle.appDirectory, bundle.appDirectory, filePaths);
162
+ const result = await this.readAppFiles(bundle.appDirectory, filePaths);
163
+ if (bundle.manifest.main.kind === "wasi-http-component") result.appFiles[bundle.manifest.main.entry] = SOURCE_WASM_PLACEHOLDER_BYTES;
164
+ return result;
165
+ };
166
+ readAppFiles = async (appDirectory, filePaths) => {
167
+ const sortedPaths = Array.from(filePaths).sort((left, right) => left.localeCompare(right));
168
+ const appFiles = {};
169
+ for (const relativePath of sortedPaths) appFiles[relativePath] = new Uint8Array(await readFile(path.join(appDirectory, relativePath)));
170
+ return {
171
+ appFiles,
172
+ filePaths: sortedPaths
173
+ };
174
+ };
175
+ collectDirectoryPaths = async (directoryPath, appDirectory, filePaths) => {
176
+ try {
177
+ const entries = await readdir(directoryPath, { withFileTypes: true });
178
+ for (const entry of entries) {
179
+ const entryPath = path.join(directoryPath, entry.name);
180
+ const relativePath = path.relative(appDirectory, entryPath).replace(/\\/g, "/");
181
+ if ((await lstat(entryPath)).isSymbolicLink()) throw new Error(`bundle 不允许包含符号链接:${relativePath}`);
182
+ if (entry.isDirectory()) {
183
+ if (!this.shouldExcludeRuntimePath(relativePath)) await this.collectDirectoryPaths(entryPath, appDirectory, filePaths);
184
+ continue;
185
+ }
186
+ if (entry.isFile() && !this.shouldExcludeRuntimePath(relativePath)) filePaths.add(relativePath);
187
+ }
188
+ } catch (error) {
189
+ if (this.isMissingFileError(error)) return;
190
+ throw error;
191
+ }
192
+ };
193
+ collectSourceDirectoryPaths = async (directoryPath, appDirectory, filePaths) => {
194
+ const entries = await readdir(directoryPath, { withFileTypes: true });
195
+ for (const entry of entries) {
196
+ const entryPath = path.join(directoryPath, entry.name);
197
+ const relativePath = path.relative(appDirectory, entryPath).replace(/\\/g, "/");
198
+ if ((await lstat(entryPath)).isSymbolicLink()) throw new Error(`bundle 不允许包含符号链接:${relativePath}`);
199
+ if (entry.isDirectory()) {
200
+ if (!this.shouldExcludeSourcePath(relativePath)) await this.collectSourceDirectoryPaths(entryPath, appDirectory, filePaths);
201
+ } else if (entry.isFile() && !this.shouldExcludeSourcePath(relativePath)) filePaths.add(relativePath);
202
+ }
203
+ };
204
+ addOptionalFile = async (appDirectory, relativePath, filePaths) => {
205
+ try {
206
+ const stats = await lstat(path.join(appDirectory, relativePath));
207
+ if (stats.isFile() && !stats.isSymbolicLink()) filePaths.add(relativePath);
208
+ } catch (error) {
209
+ if (!this.isMissingFileError(error)) throw error;
210
+ }
211
+ };
212
+ shouldExcludeRuntimePath = (relativePath) => {
213
+ return relativePath.split("/").some((segment) => segment === "node_modules" || segment === ".git" || segment === "coverage" || segment === "tests" || segment === "__tests__" || segment === "fixtures") || /(?:^|\/)\.(?:DS_Store|eslintcache)$/.test(relativePath) || relativePath.endsWith(".map");
214
+ };
215
+ shouldExcludeSourcePath = (relativePath) => {
216
+ return this.shouldExcludeRuntimePath(relativePath) || relativePath === ".napp" || relativePath.startsWith(".napp/") || relativePath === "main/dist" || relativePath.startsWith("main/dist/") || relativePath === "main/generated" || relativePath.startsWith("main/generated/");
217
+ };
218
+ assertFileBudgets = (files) => {
219
+ const entries = Object.entries(files);
220
+ if (entries.length + 2 > MAX_FILE_COUNT) throw new Error(`bundle 文件数超过 ${MAX_FILE_COUNT} 上限。`);
221
+ let totalBytes = 0;
222
+ for (const [relativePath, bytes] of entries) {
223
+ if (bytes.byteLength > MAX_FILE_BYTES) throw new Error(`bundle 单文件超过 ${MAX_FILE_BYTES} bytes 上限:${relativePath}`);
224
+ totalBytes += bytes.byteLength;
225
+ }
226
+ if (totalBytes > MAX_UNCOMPRESSED_BYTES) throw new Error(`bundle 解压后超过 ${MAX_UNCOMPRESSED_BYTES} bytes 上限。`);
227
+ };
228
+ normalizeArchive = (archive) => {
229
+ const normalizedArchive = {};
230
+ for (const [entryName, bytes] of Object.entries(archive)) {
231
+ const normalizedEntry = this.normalizeArchiveEntry(entryName);
232
+ if (Object.hasOwn(normalizedArchive, normalizedEntry)) throw new Error(`bundle 包含重复路径:${normalizedEntry}`);
233
+ normalizedArchive[normalizedEntry] = bytes;
234
+ }
235
+ return normalizedArchive;
236
+ };
237
+ readMetadataFromArchive = (archive) => {
238
+ const raw = this.readJsonArchiveEntry(archive, BUNDLE_METADATA_PATH, "bundle metadata");
239
+ const distributionMode = raw.distributionMode === void 0 ? "bundle" : raw.distributionMode;
240
+ if (raw.bundleFormatVersion !== 1 || distributionMode !== "bundle" && distributionMode !== "source" || typeof raw.appId !== "string" || !raw.appId || typeof raw.name !== "string" || !raw.name || typeof raw.version !== "string" || !raw.version || raw.entryManifest !== "manifest.json" || raw.checksumsFile !== CHECKSUMS_PATH) throw new Error("bundle metadata 无效。");
241
+ return {
242
+ ...raw,
243
+ distributionMode
244
+ };
245
+ };
246
+ readChecksumsFromArchive = (archive) => {
247
+ const raw = this.readJsonArchiveEntry(archive, CHECKSUMS_PATH, "bundle checksums");
248
+ if (raw.algorithm !== "sha256" || !raw.files || typeof raw.files !== "object") throw new Error("bundle checksums 无效。");
249
+ return raw;
250
+ };
251
+ verifyArchiveChecksums = (archive, checksums) => {
252
+ const actualPaths = Object.keys(archive).filter((entry) => entry !== CHECKSUMS_PATH).sort((left, right) => left.localeCompare(right));
253
+ const checksumPaths = Object.keys(checksums.files).map((entry) => this.normalizeArchiveEntry(entry)).sort((left, right) => left.localeCompare(right));
254
+ if (actualPaths.length !== checksumPaths.length || actualPaths.some((entry, index) => entry !== checksumPaths[index])) throw new Error("bundle checksums 必须精确覆盖所有 artifact 文件。");
255
+ for (const relativePath of actualPaths) {
256
+ const expectedHash = checksums.files[relativePath];
257
+ if (!expectedHash || !/^[a-f0-9]{64}$/.test(expectedHash)) throw new Error(`bundle checksum 格式无效:${relativePath}`);
258
+ if (this.computeSha256(archive[relativePath]) !== expectedHash) throw new Error(`bundle checksum 校验失败:${relativePath}`);
259
+ }
260
+ };
261
+ readJsonArchiveEntry = (archive, entryPath, label) => {
262
+ const bytes = archive[entryPath];
263
+ if (!bytes) throw new Error(`bundle 缺少 ${label}。`);
264
+ try {
265
+ return JSON.parse(Buffer.from(bytes).toString("utf-8"));
266
+ } catch (error) {
267
+ throw new Error(`无法读取 ${label}:${error instanceof Error ? error.message : String(error)}`);
268
+ }
269
+ };
270
+ buildMetadata = (appId, name, version, distributionMode) => ({
271
+ bundleFormatVersion: 1,
272
+ distributionMode,
273
+ appId,
274
+ name,
275
+ version,
276
+ entryManifest: "manifest.json",
277
+ checksumsFile: CHECKSUMS_PATH
278
+ });
279
+ buildChecksums = (files) => ({
280
+ algorithm: "sha256",
281
+ files: Object.fromEntries(Object.entries(files).sort(([left], [right]) => left.localeCompare(right)).map(([relativePath, fileBytes]) => [relativePath, this.computeSha256(fileBytes)]))
282
+ });
283
+ computeSha256 = (fileBytes) => createHash("sha256").update(Buffer.from(fileBytes)).digest("hex");
284
+ normalizeBundleFileName = (appId) => appId.replace(/[^a-zA-Z0-9._-]+/g, "-");
285
+ normalizeArchiveEntry = (entryName) => {
286
+ const normalized = entryName.replace(/\\/g, "/");
287
+ const segments = normalized.split("/");
288
+ if (!normalized || normalized.startsWith("/") || normalized.includes("\0") || /^[A-Za-z]:/.test(normalized) || segments.some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`bundle 内包含非法路径:${entryName}`);
289
+ return segments.join("/");
290
+ };
291
+ isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
292
+ pathExists = async (targetPath) => {
293
+ try {
294
+ await access(targetPath);
295
+ return true;
296
+ } catch {
297
+ return false;
298
+ }
299
+ };
300
+ };
301
+ const SOURCE_WASM_PLACEHOLDER_BYTES = Uint8Array.from(Buffer.from("AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL", "base64"));
302
+ //#endregion
303
+ export { AppBundleService };
304
+
305
+ //# sourceMappingURL=app-bundle.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-bundle.service.js","names":[],"sources":["../../src/services/app-bundle.service.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { access, lstat, mkdir, readFile, readdir, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { strToU8, unzipSync, zipSync } from \"fflate\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n type AppManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport type {\n AppBundleChecksums,\n AppBundleExtractResult,\n AppBundleMetadata,\n AppBundlePackResult,\n AppDistributionMode,\n} from \"#app-runtime/types/app-bundle.types.js\";\n\nconst MAX_COMPRESSED_BYTES = 25 * 1024 * 1024;\nconst MAX_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;\nconst MAX_FILE_BYTES = 25 * 1024 * 1024;\nconst MAX_FILE_COUNT = 2_000;\nconst CHECKSUMS_PATH = \".napp/checksums.json\";\nconst BUNDLE_METADATA_PATH = \".napp/bundle.json\";\n\nexport class AppBundleService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n ) {}\n\n packAppDirectory = async (params: {\n appDirectory: string;\n outputPath?: string;\n mode?: AppDistributionMode;\n }): Promise<AppBundlePackResult> => {\n const { appDirectory, outputPath, mode: requestedMode } = params;\n const bundle = await this.manifestService.load(appDirectory);\n const mode = requestedMode ?? \"bundle\";\n if (isAppComponentManifestBundle(bundle) && mode !== \"bundle\") {\n throw new Error(\"schema v2 组合包只支持 bundle 分发,不允许运行安装脚本。\");\n }\n const { appFiles, filePaths } = mode === \"source\"\n ? await this.collectSourceFiles(bundle)\n : await this.collectRuntimeFiles(bundle);\n this.assertFileBudgets(appFiles);\n const metadata = this.buildMetadata(\n bundle.manifest.id,\n bundle.manifest.name,\n bundle.manifest.version,\n mode,\n );\n const bundleJsonBytes = strToU8(`${JSON.stringify(metadata, null, 2)}\\n`);\n const checksums = this.buildChecksums({\n ...appFiles,\n [BUNDLE_METADATA_PATH]: bundleJsonBytes,\n });\n const checksumsJsonBytes = strToU8(`${JSON.stringify(checksums, null, 2)}\\n`);\n const archiveBytes = zipSync(\n {\n ...appFiles,\n [BUNDLE_METADATA_PATH]: bundleJsonBytes,\n [CHECKSUMS_PATH]: checksumsJsonBytes,\n },\n { level: 9 },\n );\n if (archiveBytes.byteLength > MAX_COMPRESSED_BYTES) {\n throw new Error(`bundle 压缩后超过 ${MAX_COMPRESSED_BYTES} bytes 上限。`);\n }\n const resolvedOutputPath = outputPath\n ? path.resolve(outputPath)\n : path.join(\n path.dirname(bundle.appDirectory),\n `${this.normalizeBundleFileName(bundle.manifest.id)}-${bundle.manifest.version}.napp`,\n );\n await mkdir(path.dirname(resolvedOutputPath), { recursive: true });\n await writeFile(resolvedOutputPath, Buffer.from(archiveBytes));\n return {\n bundlePath: resolvedOutputPath,\n metadata,\n sizeBytes: archiveBytes.byteLength,\n filePaths,\n };\n };\n\n extractBundle = async (params: {\n bundlePath: string;\n targetDirectory: string;\n }): Promise<AppBundleExtractResult> => {\n const bundlePath = path.resolve(params.bundlePath);\n const targetDirectory = path.resolve(params.targetDirectory);\n const { archive, metadata, checksums } = await this.readValidatedArchive(bundlePath);\n await this.replaceTargetWithArchive({ archive, metadata, targetDirectory });\n return { appDirectory: targetDirectory, metadata, checksums };\n };\n\n private readValidatedArchive = async (bundlePath: string): Promise<{\n archive: Record<string, Uint8Array>;\n metadata: AppBundleMetadata;\n checksums: AppBundleChecksums;\n }> => {\n const bundleStats = await stat(bundlePath);\n if (!bundleStats.isFile() || bundleStats.size > MAX_COMPRESSED_BYTES) {\n throw new Error(`bundle 压缩体积超过 ${MAX_COMPRESSED_BYTES} bytes 上限。`);\n }\n let fileCount = 0;\n let totalBytes = 0;\n const archive = unzipSync(new Uint8Array(await readFile(bundlePath)), {\n filter: (file) => {\n fileCount += 1;\n totalBytes += file.originalSize;\n if (fileCount > MAX_FILE_COUNT) {\n throw new Error(`bundle 文件数超过 ${MAX_FILE_COUNT} 上限。`);\n }\n if (file.originalSize > MAX_FILE_BYTES) {\n throw new Error(`bundle 单文件超过 ${MAX_FILE_BYTES} bytes 上限:${file.name}`);\n }\n if (totalBytes > MAX_UNCOMPRESSED_BYTES) {\n throw new Error(`bundle 解压后超过 ${MAX_UNCOMPRESSED_BYTES} bytes 上限。`);\n }\n return true;\n },\n });\n const normalizedArchive = this.normalizeArchive(archive);\n const metadata = this.readMetadataFromArchive(normalizedArchive);\n const checksums = this.readChecksumsFromArchive(normalizedArchive);\n this.verifyArchiveChecksums(normalizedArchive, checksums);\n return { archive: normalizedArchive, metadata, checksums };\n };\n\n private replaceTargetWithArchive = async (params: {\n archive: Record<string, Uint8Array>;\n metadata: AppBundleMetadata;\n targetDirectory: string;\n }): Promise<void> => {\n const { archive, metadata, targetDirectory } = params;\n const targetParent = path.dirname(targetDirectory);\n const operationId = randomUUID();\n const stagedDirectory = path.join(\n targetParent,\n `.${path.basename(targetDirectory)}.extracting-${operationId}`,\n );\n const backupDirectory = path.join(\n targetParent,\n `.${path.basename(targetDirectory)}.backup-${operationId}`,\n );\n const targetExists = await this.pathExists(targetDirectory);\n await mkdir(targetParent, { recursive: true });\n try {\n await mkdir(stagedDirectory);\n await this.writeArchiveEntries(archive, stagedDirectory);\n await this.assertExtractedManifest(stagedDirectory, metadata);\n if (targetExists) {\n await rename(targetDirectory, backupDirectory);\n }\n await rename(stagedDirectory, targetDirectory);\n await rm(backupDirectory, { recursive: true, force: true });\n } catch (error) {\n await rm(stagedDirectory, { recursive: true, force: true });\n if (targetExists && await this.pathExists(backupDirectory)) {\n await rm(targetDirectory, { recursive: true, force: true });\n try {\n await rename(backupDirectory, targetDirectory);\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n `bundle 解压失败,且无法恢复原目标目录:${targetDirectory}`,\n );\n }\n }\n throw error;\n } finally {\n await rm(stagedDirectory, { recursive: true, force: true });\n await rm(backupDirectory, { recursive: true, force: true });\n }\n };\n\n private writeArchiveEntries = async (\n archive: Record<string, Uint8Array>,\n targetDirectory: string,\n ): Promise<void> => {\n for (const [entryName, bytes] of Object.entries(archive)) {\n const targetPath = path.join(targetDirectory, entryName);\n await mkdir(path.dirname(targetPath), { recursive: true });\n await writeFile(targetPath, Buffer.from(bytes));\n }\n };\n\n private assertExtractedManifest = async (\n appDirectory: string,\n metadata: AppBundleMetadata,\n ): Promise<void> => {\n const manifestBundle = await this.manifestService.load(appDirectory);\n if (\n metadata.appId !== manifestBundle.manifest.id ||\n metadata.name !== manifestBundle.manifest.name ||\n metadata.version !== manifestBundle.manifest.version\n ) {\n throw new Error(\"bundle metadata 与 manifest.json 身份不一致。\");\n }\n if (manifestBundle.manifest.schemaVersion === 2 && metadata.distributionMode !== \"bundle\") {\n throw new Error(\"schema v2 组合包只支持 bundle 分发。\");\n }\n };\n\n private collectRuntimeFiles = async (\n bundle: AppManifestBundle,\n ): Promise<{ appFiles: Record<string, Uint8Array>; filePaths: string[] }> => {\n const filePaths = new Set<string>([\n path.relative(bundle.appDirectory, bundle.manifestPath).replace(/\\\\/g, \"/\"),\n ]);\n if (isAppStandaloneManifestBundle(bundle)) {\n filePaths.add(path.relative(bundle.appDirectory, bundle.mainEntryPath).replace(/\\\\/g, \"/\"));\n await this.collectDirectoryPaths(bundle.uiDirectoryPath, bundle.appDirectory, filePaths);\n await this.collectDirectoryPaths(bundle.assetsDirectoryPath, bundle.appDirectory, filePaths);\n } else {\n for (const component of bundle.components) {\n await this.collectDirectoryPaths(component.componentDirectory, bundle.appDirectory, filePaths);\n }\n await this.collectDirectoryPaths(bundle.assetsDirectoryPath, bundle.appDirectory, filePaths);\n await this.addOptionalFile(bundle.appDirectory, \"marketplace.json\", filePaths);\n }\n if (bundle.iconPath) {\n filePaths.add(path.relative(bundle.appDirectory, bundle.iconPath).replace(/\\\\/g, \"/\"));\n }\n return await this.readAppFiles(bundle.appDirectory, filePaths);\n };\n\n private collectSourceFiles = async (\n bundle: AppManifestBundle,\n ): Promise<{ appFiles: Record<string, Uint8Array>; filePaths: string[] }> => {\n if (!isAppStandaloneManifestBundle(bundle)) {\n throw new Error(\"schema v2 组合包不支持 source 分发。\");\n }\n const filePaths = new Set<string>();\n await this.collectSourceDirectoryPaths(bundle.appDirectory, bundle.appDirectory, filePaths);\n const result = await this.readAppFiles(bundle.appDirectory, filePaths);\n if (bundle.manifest.main.kind === \"wasi-http-component\") {\n result.appFiles[bundle.manifest.main.entry] = SOURCE_WASM_PLACEHOLDER_BYTES;\n }\n return result;\n };\n\n private readAppFiles = async (\n appDirectory: string,\n filePaths: Set<string>,\n ): Promise<{ appFiles: Record<string, Uint8Array>; filePaths: string[] }> => {\n const sortedPaths = Array.from(filePaths).sort((left, right) => left.localeCompare(right));\n const appFiles: Record<string, Uint8Array> = {};\n for (const relativePath of sortedPaths) {\n appFiles[relativePath] = new Uint8Array(await readFile(path.join(appDirectory, relativePath)));\n }\n return { appFiles, filePaths: sortedPaths };\n };\n\n private collectDirectoryPaths = async (\n directoryPath: string,\n appDirectory: string,\n filePaths: Set<string>,\n ): Promise<void> => {\n try {\n const entries = await readdir(directoryPath, { withFileTypes: true });\n for (const entry of entries) {\n const entryPath = path.join(directoryPath, entry.name);\n const relativePath = path.relative(appDirectory, entryPath).replace(/\\\\/g, \"/\");\n const entryStats = await lstat(entryPath);\n if (entryStats.isSymbolicLink()) {\n throw new Error(`bundle 不允许包含符号链接:${relativePath}`);\n }\n if (entry.isDirectory()) {\n if (!this.shouldExcludeRuntimePath(relativePath)) {\n await this.collectDirectoryPaths(entryPath, appDirectory, filePaths);\n }\n continue;\n }\n if (entry.isFile() && !this.shouldExcludeRuntimePath(relativePath)) {\n filePaths.add(relativePath);\n }\n }\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return;\n }\n throw error;\n }\n };\n\n private collectSourceDirectoryPaths = async (\n directoryPath: string,\n appDirectory: string,\n filePaths: Set<string>,\n ): Promise<void> => {\n const entries = await readdir(directoryPath, { withFileTypes: true });\n for (const entry of entries) {\n const entryPath = path.join(directoryPath, entry.name);\n const relativePath = path.relative(appDirectory, entryPath).replace(/\\\\/g, \"/\");\n const entryStats = await lstat(entryPath);\n if (entryStats.isSymbolicLink()) {\n throw new Error(`bundle 不允许包含符号链接:${relativePath}`);\n }\n if (entry.isDirectory()) {\n if (!this.shouldExcludeSourcePath(relativePath)) {\n await this.collectSourceDirectoryPaths(entryPath, appDirectory, filePaths);\n }\n } else if (entry.isFile() && !this.shouldExcludeSourcePath(relativePath)) {\n filePaths.add(relativePath);\n }\n }\n };\n\n private addOptionalFile = async (\n appDirectory: string,\n relativePath: string,\n filePaths: Set<string>,\n ): Promise<void> => {\n try {\n const stats = await lstat(path.join(appDirectory, relativePath));\n if (stats.isFile() && !stats.isSymbolicLink()) {\n filePaths.add(relativePath);\n }\n } catch (error) {\n if (!this.isMissingFileError(error)) {\n throw error;\n }\n }\n };\n\n private shouldExcludeRuntimePath = (relativePath: string): boolean => {\n const segments = relativePath.split(\"/\");\n return segments.some((segment) =>\n segment === \"node_modules\" ||\n segment === \".git\" ||\n segment === \"coverage\" ||\n segment === \"tests\" ||\n segment === \"__tests__\" ||\n segment === \"fixtures\"\n ) || /(?:^|\\/)\\.(?:DS_Store|eslintcache)$/.test(relativePath) || relativePath.endsWith(\".map\");\n };\n\n private shouldExcludeSourcePath = (relativePath: string): boolean => {\n return this.shouldExcludeRuntimePath(relativePath) ||\n relativePath === \".napp\" || relativePath.startsWith(\".napp/\") ||\n relativePath === \"main/dist\" || relativePath.startsWith(\"main/dist/\") ||\n relativePath === \"main/generated\" || relativePath.startsWith(\"main/generated/\");\n };\n\n private assertFileBudgets = (files: Record<string, Uint8Array>): void => {\n const entries = Object.entries(files);\n if (entries.length + 2 > MAX_FILE_COUNT) {\n throw new Error(`bundle 文件数超过 ${MAX_FILE_COUNT} 上限。`);\n }\n let totalBytes = 0;\n for (const [relativePath, bytes] of entries) {\n if (bytes.byteLength > MAX_FILE_BYTES) {\n throw new Error(`bundle 单文件超过 ${MAX_FILE_BYTES} bytes 上限:${relativePath}`);\n }\n totalBytes += bytes.byteLength;\n }\n if (totalBytes > MAX_UNCOMPRESSED_BYTES) {\n throw new Error(`bundle 解压后超过 ${MAX_UNCOMPRESSED_BYTES} bytes 上限。`);\n }\n };\n\n private normalizeArchive = (\n archive: Record<string, Uint8Array>,\n ): Record<string, Uint8Array> => {\n const normalizedArchive: Record<string, Uint8Array> = {};\n for (const [entryName, bytes] of Object.entries(archive)) {\n const normalizedEntry = this.normalizeArchiveEntry(entryName);\n if (Object.hasOwn(normalizedArchive, normalizedEntry)) {\n throw new Error(`bundle 包含重复路径:${normalizedEntry}`);\n }\n normalizedArchive[normalizedEntry] = bytes;\n }\n return normalizedArchive;\n };\n\n private readMetadataFromArchive = (\n archive: Record<string, Uint8Array>,\n ): AppBundleMetadata => {\n const raw = this.readJsonArchiveEntry<Partial<AppBundleMetadata>>(\n archive,\n BUNDLE_METADATA_PATH,\n \"bundle metadata\",\n );\n const distributionMode = raw.distributionMode === undefined\n ? \"bundle\"\n : raw.distributionMode;\n if (\n raw.bundleFormatVersion !== 1 ||\n (distributionMode !== \"bundle\" && distributionMode !== \"source\") ||\n typeof raw.appId !== \"string\" || !raw.appId ||\n typeof raw.name !== \"string\" || !raw.name ||\n typeof raw.version !== \"string\" || !raw.version ||\n raw.entryManifest !== \"manifest.json\" ||\n raw.checksumsFile !== CHECKSUMS_PATH\n ) {\n throw new Error(\"bundle metadata 无效。\");\n }\n return {\n ...raw,\n distributionMode,\n } as AppBundleMetadata;\n };\n\n private readChecksumsFromArchive = (\n archive: Record<string, Uint8Array>,\n ): AppBundleChecksums => {\n const raw = this.readJsonArchiveEntry<Partial<AppBundleChecksums>>(\n archive,\n CHECKSUMS_PATH,\n \"bundle checksums\",\n );\n if (raw.algorithm !== \"sha256\" || !raw.files || typeof raw.files !== \"object\") {\n throw new Error(\"bundle checksums 无效。\");\n }\n return raw as AppBundleChecksums;\n };\n\n private verifyArchiveChecksums = (\n archive: Record<string, Uint8Array>,\n checksums: AppBundleChecksums,\n ): void => {\n const actualPaths = Object.keys(archive)\n .filter((entry) => entry !== CHECKSUMS_PATH)\n .sort((left, right) => left.localeCompare(right));\n const checksumPaths = Object.keys(checksums.files)\n .map((entry) => this.normalizeArchiveEntry(entry))\n .sort((left, right) => left.localeCompare(right));\n if (\n actualPaths.length !== checksumPaths.length ||\n actualPaths.some((entry, index) => entry !== checksumPaths[index])\n ) {\n throw new Error(\"bundle checksums 必须精确覆盖所有 artifact 文件。\");\n }\n for (const relativePath of actualPaths) {\n const expectedHash = checksums.files[relativePath];\n if (!expectedHash || !/^[a-f0-9]{64}$/.test(expectedHash)) {\n throw new Error(`bundle checksum 格式无效:${relativePath}`);\n }\n const actualHash = this.computeSha256(archive[relativePath] as Uint8Array);\n if (actualHash !== expectedHash) {\n throw new Error(`bundle checksum 校验失败:${relativePath}`);\n }\n }\n };\n\n private readJsonArchiveEntry = <T>(\n archive: Record<string, Uint8Array>,\n entryPath: string,\n label: string,\n ): T => {\n const bytes = archive[entryPath];\n if (!bytes) {\n throw new Error(`bundle 缺少 ${label}。`);\n }\n try {\n return JSON.parse(Buffer.from(bytes).toString(\"utf-8\")) as T;\n } catch (error) {\n throw new Error(`无法读取 ${label}:${error instanceof Error ? error.message : String(error)}`);\n }\n };\n\n private buildMetadata = (\n appId: string,\n name: string,\n version: string,\n distributionMode: AppDistributionMode,\n ): AppBundleMetadata => ({\n bundleFormatVersion: 1,\n distributionMode,\n appId,\n name,\n version,\n entryManifest: \"manifest.json\",\n checksumsFile: CHECKSUMS_PATH,\n });\n\n private buildChecksums = (files: Record<string, Uint8Array>): AppBundleChecksums => ({\n algorithm: \"sha256\",\n files: Object.fromEntries(\n Object.entries(files)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([relativePath, fileBytes]) => [relativePath, this.computeSha256(fileBytes)]),\n ),\n });\n\n private computeSha256 = (fileBytes: Uint8Array): string =>\n createHash(\"sha256\").update(Buffer.from(fileBytes)).digest(\"hex\");\n\n private normalizeBundleFileName = (appId: string): string =>\n appId.replace(/[^a-zA-Z0-9._-]+/g, \"-\");\n\n private normalizeArchiveEntry = (entryName: string): string => {\n const normalized = entryName.replace(/\\\\/g, \"/\");\n const segments = normalized.split(\"/\");\n if (\n !normalized || normalized.startsWith(\"/\") || normalized.includes(\"\\0\") ||\n /^[A-Za-z]:/.test(normalized) ||\n segments.some((segment) => !segment || segment === \".\" || segment === \"..\")\n ) {\n throw new Error(`bundle 内包含非法路径:${entryName}`);\n }\n return segments.join(\"/\");\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 private pathExists = async (targetPath: string): Promise<boolean> => {\n try {\n await access(targetPath);\n return true;\n } catch {\n return false;\n }\n };\n}\n\nconst SOURCE_WASM_PLACEHOLDER_BYTES = Uint8Array.from(\n Buffer.from(\n \"AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL\",\n \"base64\",\n ),\n);\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { AppManifestService } from "../manifest/app-manifest.service.js";
2
- import { AppDocumentGrantMutationResult, AppInstalledPermissionState } from "./app-permissions.types.js";
3
- import { AppRegistryService } from "../registry/app-registry.service.js";
1
+ import { AppManifestService } from "./app-manifest.service.js";
2
+ import { AppDocumentGrantMutationResult, AppInstalledPermissionState } from "../types/app-permissions.types.js";
3
+ import { AppRegistryService } from "./app-registry.service.js";
4
4
 
5
- //#region src/permissions/app-grant.service.d.ts
5
+ //#region src/services/app-grant.service.d.ts
6
6
  declare class AppGrantService {
7
7
  private readonly registryService;
8
8
  private readonly manifestService;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-grant.service.d.ts","names":[],"sources":["../../src/services/app-grant.service.ts"],"mappings":";;;;;cAUa,eAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,eAAA;cADA,eAAA,GAAiB,kBAAA,EACjB,eAAA,GAAiB,kBAAA;EAGpC,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,2BAAA;EAyC3C,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,OAAA;IACA,aAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;EA2BZ,mBAAA,GAA6B,MAAA;IAC3B,KAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;AAAA"}
@@ -1,8 +1,9 @@
1
- import { AppManifestService } from "../manifest/app-manifest.service.js";
2
- import { AppRegistryService } from "../registry/app-registry.service.js";
1
+ import { AppManifestService } from "./app-manifest.service.js";
2
+ import { isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
3
+ import { AppRegistryService } from "./app-registry.service.js";
3
4
  import { access } from "node:fs/promises";
4
5
  import path from "node:path";
5
- //#region src/permissions/app-grant.service.ts
6
+ //#region src/services/app-grant.service.ts
6
7
  var AppGrantService = class {
7
8
  constructor(registryService = new AppRegistryService(), manifestService = new AppManifestService()) {
8
9
  this.registryService = registryService;
@@ -13,7 +14,9 @@ var AppGrantService = class {
13
14
  if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
14
15
  const activeVersion = appRecord.installedVersions[appRecord.activeVersion];
15
16
  if (!activeVersion) throw new Error(`已安装应用缺少激活版本:${appId}`);
16
- const requestedPermissions = (await this.manifestService.load(activeVersion.installDirectory)).manifest.permissions ?? {};
17
+ const bundle = await this.manifestService.load(activeVersion.installDirectory);
18
+ if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包权限由 Panel/Service runtime grant 管理。");
19
+ const requestedPermissions = bundle.manifest.permissions ?? {};
17
20
  return {
18
21
  appId: appRecord.appId,
19
22
  name: appRecord.name,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-grant.service.js","names":[],"sources":["../../src/services/app-grant.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { isAppStandaloneManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport type {\n AppDocumentGrantMutationResult,\n AppInstalledPermissionState,\n} from \"#app-runtime/types/app-permissions.types.js\";\n\nexport class AppGrantService {\n constructor(\n private readonly registryService: AppRegistryService = new AppRegistryService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n ) {}\n\n summarize = async (appId: string): Promise<AppInstalledPermissionState> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appId}`);\n }\n const bundle = await this.manifestService.load(activeVersion.installDirectory);\n if (!isAppStandaloneManifestBundle(bundle)) {\n throw new Error(\"schema v2 组合包权限由 Panel/Service runtime grant 管理。\");\n }\n const requestedPermissions = bundle.manifest.permissions ?? {};\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n activeVersion: appRecord.activeVersion,\n documentAccess: (requestedPermissions.documentAccess ?? []).map((scope) => ({\n id: scope.id,\n mode: scope.mode,\n description: scope.description,\n granted: Boolean(appRecord.grants[scope.id]),\n grantedPath: appRecord.grants[scope.id],\n })),\n allowedDomains: requestedPermissions.allowedDomains ?? [],\n storage: {\n enabled:\n requestedPermissions.storage !== undefined &&\n requestedPermissions.storage !== false,\n namespace:\n typeof requestedPermissions.storage === \"object\"\n ? requestedPermissions.storage.namespace\n : undefined,\n },\n capabilities: {\n hostBridge: requestedPermissions.capabilities?.hostBridge !== false,\n },\n };\n };\n\n grantDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n directoryPath: string;\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId, directoryPath } = params;\n const permissionState = await this.summarize(appId);\n const requestedScope = permissionState.documentAccess.find(\n (scope) => scope.id === scopeId,\n );\n if (!requestedScope) {\n throw new Error(`应用 ${appId} 未声明 documentAccess scope:${scopeId}`);\n }\n const normalizedDirectory = path.resolve(directoryPath);\n try {\n await access(normalizedDirectory);\n } catch {\n throw new Error(`授权目录不存在:${normalizedDirectory}`);\n }\n await this.registryService.setDocumentGrant(\n appId,\n scopeId,\n normalizedDirectory,\n );\n return {\n appId,\n scopeId,\n grantedPath: normalizedDirectory,\n };\n };\n\n revokeDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId } = params;\n await this.summarize(appId);\n const removed = await this.registryService.removeDocumentGrant(\n appId,\n scopeId,\n );\n return {\n appId,\n scopeId,\n removed,\n };\n };\n}\n"],"mappings":";;;;;;AAUA,IAAa,kBAAb,MAA6B;CAC3B,YACE,kBAAuD,IAAI,oBAAoB,EAC/E,kBAAuD,IAAI,oBAAoB,EAC/E;AAFiB,OAAA,kBAAA;AACA,OAAA,kBAAA;;CAGnB,YAAY,OAAO,UAAwD;EACzE,MAAM,YAAY,MAAM,KAAK,gBAAgB,OAAO,MAAM;AAC1D,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;EAEtC,MAAM,gBAAgB,UAAU,kBAAkB,UAAU;AAC5D,MAAI,CAAC,cACH,OAAM,IAAI,MAAM,eAAe,QAAQ;EAEzC,MAAM,SAAS,MAAM,KAAK,gBAAgB,KAAK,cAAc,iBAAiB;AAC9E,MAAI,CAAC,8BAA8B,OAAO,CACxC,OAAM,IAAI,MAAM,mDAAmD;EAErE,MAAM,uBAAuB,OAAO,SAAS,eAAe,EAAE;AAC9D,SAAO;GACL,OAAO,UAAU;GACjB,MAAM,UAAU;GAChB,eAAe,UAAU;GACzB,iBAAiB,qBAAqB,kBAAkB,EAAE,EAAE,KAAK,WAAW;IAC1E,IAAI,MAAM;IACV,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,SAAS,QAAQ,UAAU,OAAO,MAAM,IAAI;IAC5C,aAAa,UAAU,OAAO,MAAM;IACrC,EAAE;GACH,gBAAgB,qBAAqB,kBAAkB,EAAE;GACzD,SAAS;IACP,SACE,qBAAqB,YAAY,KAAA,KACjC,qBAAqB,YAAY;IACnC,WACE,OAAO,qBAAqB,YAAY,WACpC,qBAAqB,QAAQ,YAC7B,KAAA;IACP;GACD,cAAc,EACZ,YAAY,qBAAqB,cAAc,eAAe,OAC/D;GACF;;CAGH,qBAAqB,OAAO,WAImB;EAC7C,MAAM,EAAE,OAAO,SAAS,kBAAkB;AAK1C,MAAI,EAJoB,MAAM,KAAK,UAAU,MAAM,EACZ,eAAe,MACnD,UAAU,MAAM,OAAO,QACzB,CAEC,OAAM,IAAI,MAAM,MAAM,MAAM,4BAA4B,UAAU;EAEpE,MAAM,sBAAsB,KAAK,QAAQ,cAAc;AACvD,MAAI;AACF,SAAM,OAAO,oBAAoB;UAC3B;AACN,SAAM,IAAI,MAAM,WAAW,sBAAsB;;AAEnD,QAAM,KAAK,gBAAgB,iBACzB,OACA,SACA,oBACD;AACD,SAAO;GACL;GACA;GACA,aAAa;GACd;;CAGH,sBAAsB,OAAO,WAGkB;EAC7C,MAAM,EAAE,OAAO,YAAY;AAC3B,QAAM,KAAK,UAAU,MAAM;AAK3B,SAAO;GACL;GACA;GACA,SAPc,MAAM,KAAK,gBAAgB,oBACzC,OACA,QACD;GAKA"}
@@ -1,4 +1,4 @@
1
- //#region src/paths/app-home.service.d.ts
1
+ //#region src/services/app-home.service.d.ts
2
2
  declare class AppHomeService {
3
3
  private readonly appHomeDirectory;
4
4
  constructor(appHomeDirectory?: string);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-home.service.d.ts","names":[],"sources":["../../src/services/app-home.service.ts"],"mappings":";cAIa,cAAA;EAAA,iBAEQ,gBAAA;cAAA,gBAAA;EAKnB,mBAAA;EAIA,oBAAA;EAIA,gBAAA;EAIA,eAAA;EAIA,aAAA;EAIA,mBAAA,GAAuB,KAAA,UAAe,OAAA;EAItC,mBAAA,GAAuB,KAAA;EAIvB,qBAAA,QAAkC,OAAA;EAQlC,wBAAA,GAAkC,MAAA,aAAiB,OAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { mkdir, mkdtemp } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { tmpdir } from "node:os";
4
- //#region src/paths/app-home.service.ts
4
+ //#region src/services/app-home.service.ts
5
5
  var AppHomeService = class {
6
6
  constructor(appHomeDirectory = process.env.NEXTCLAW_APP_HOME ? path.resolve(process.env.NEXTCLAW_APP_HOME) : path.join(process.env.HOME ?? process.cwd(), ".nextclaw", "apps")) {
7
7
  this.appHomeDirectory = appHomeDirectory;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-home.service.js","names":[],"sources":["../../src/services/app-home.service.ts"],"sourcesContent":["import { mkdir, mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nexport class AppHomeService {\n constructor(\n private readonly appHomeDirectory: string = process.env.NEXTCLAW_APP_HOME\n ? path.resolve(process.env.NEXTCLAW_APP_HOME)\n : path.join(process.env.HOME ?? process.cwd(), \".nextclaw\", \"apps\"),\n ) {}\n\n getAppHomeDirectory = (): string => {\n return this.appHomeDirectory;\n };\n\n getPackagesDirectory = (): string => {\n return path.join(this.appHomeDirectory, \"packages\");\n };\n\n getDataDirectory = (): string => {\n return path.join(this.appHomeDirectory, \"data\");\n };\n\n getRegistryPath = (): string => {\n return path.join(this.appHomeDirectory, \"registry.json\");\n };\n\n getConfigPath = (): string => {\n return path.join(this.appHomeDirectory, \"config.json\");\n };\n\n getInstallDirectory = (appId: string, version: string): string => {\n return path.join(this.getPackagesDirectory(), appId, version);\n };\n\n getAppDataDirectory = (appId: string): string => {\n return path.join(this.getDataDirectory(), appId);\n };\n\n ensureBaseDirectories = async (): Promise<void> => {\n await Promise.all([\n mkdir(this.appHomeDirectory, { recursive: true }),\n mkdir(this.getPackagesDirectory(), { recursive: true }),\n mkdir(this.getDataDirectory(), { recursive: true }),\n ]);\n };\n\n createTemporaryDirectory = async (prefix: string): Promise<string> => {\n return mkdtemp(path.join(tmpdir(), prefix));\n };\n}\n"],"mappings":";;;;AAIA,IAAa,iBAAb,MAA4B;CAC1B,YACE,mBAA4C,QAAQ,IAAI,oBACpD,KAAK,QAAQ,QAAQ,IAAI,kBAAkB,GAC3C,KAAK,KAAK,QAAQ,IAAI,QAAQ,QAAQ,KAAK,EAAE,aAAa,OAAO,EACrE;AAHiB,OAAA,mBAAA;;CAKnB,4BAAoC;AAClC,SAAO,KAAK;;CAGd,6BAAqC;AACnC,SAAO,KAAK,KAAK,KAAK,kBAAkB,WAAW;;CAGrD,yBAAiC;AAC/B,SAAO,KAAK,KAAK,KAAK,kBAAkB,OAAO;;CAGjD,wBAAgC;AAC9B,SAAO,KAAK,KAAK,KAAK,kBAAkB,gBAAgB;;CAG1D,sBAA8B;AAC5B,SAAO,KAAK,KAAK,KAAK,kBAAkB,cAAc;;CAGxD,uBAAuB,OAAe,YAA4B;AAChE,SAAO,KAAK,KAAK,KAAK,sBAAsB,EAAE,OAAO,QAAQ;;CAG/D,uBAAuB,UAA0B;AAC/C,SAAO,KAAK,KAAK,KAAK,kBAAkB,EAAE,MAAM;;CAGlD,wBAAwB,YAA2B;AACjD,QAAM,QAAQ,IAAI;GAChB,MAAM,KAAK,kBAAkB,EAAE,WAAW,MAAM,CAAC;GACjD,MAAM,KAAK,sBAAsB,EAAE,EAAE,WAAW,MAAM,CAAC;GACvD,MAAM,KAAK,kBAAkB,EAAE,EAAE,WAAW,MAAM,CAAC;GACpD,CAAC;;CAGJ,2BAA2B,OAAO,WAAoC;AACpE,SAAO,QAAQ,KAAK,KAAK,QAAQ,EAAE,OAAO,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { AppInstanceService } from "./app-instance.service.js";
2
- import { WasmtimeWasiHttpComponentService } from "../runtime/wasmtime-wasi-http-component.service.js";
2
+ import { WasmtimeWasiHttpComponentService } from "./wasmtime-wasi-http-component.service.js";
3
3
 
4
- //#region src/host/app-host.service.d.ts
4
+ //#region src/services/app-host.service.d.ts
5
5
  type AppHostStartOptions = {
6
6
  host: string;
7
7
  port: number;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-host.service.d.ts","names":[],"sources":["../../src/services/app-host.service.ts"],"mappings":";;;;KAMY,mBAAA;EACV,IAAA;EACA,IAAA;AAAA;AAAA,KAGU,aAAA;EACV,KAAA;EACA,GAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,cAGW,cAAA;EAAA,iBAMQ,WAAA;EAAA,iBACA,wBAAA;EAAA,QANX,MAAA;EAAA,iBACS,YAAA;EAAA,iBACA,QAAA;cAGE,WAAA,EAAa,kBAAA,EACb,wBAAA,GAA2B,gCAAA;EAM9C,KAAA,GAAe,OAAA,EAAS,mBAAA,KAAsB,OAAA,CAAQ,aAAA;EA2BtD,IAAA,QAAiB,OAAA;EAAA,QAgBT,aAAA;AAAA"}
@@ -1,7 +1,7 @@
1
- import { HostBridgeServer } from "../bridge/host-bridge.service.js";
2
- import { UiServerService } from "../ui/ui-server.service.js";
1
+ import { HostBridgeServer } from "./host-bridge.service.js";
2
+ import { UiServerService } from "./ui-server.service.js";
3
3
  import { createServer } from "node:http";
4
- //#region src/host/app-host.service.ts
4
+ //#region src/services/app-host.service.ts
5
5
  var AppHostService = class {
6
6
  server;
7
7
  bridgeServer;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-host.service.js","names":[],"sources":["../../src/services/app-host.service.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { HostBridgeServer } from \"#app-runtime/services/host-bridge.service.js\";\nimport type { AppInstanceService } from \"./app-instance.service.js\";\nimport { UiServerService } from \"#app-runtime/services/ui-server.service.js\";\nimport type { WasmtimeWasiHttpComponentService } from \"#app-runtime/services/wasmtime-wasi-http-component.service.js\";\n\nexport type AppHostStartOptions = {\n host: string;\n port: number;\n};\n\nexport type AppHostHandle = {\n appId: string;\n url: string;\n host: string;\n port: number;\n};\n\nexport class AppHostService {\n private server?: Server;\n private readonly bridgeServer: HostBridgeServer;\n private readonly uiServer: UiServerService;\n\n constructor(\n private readonly appInstance: AppInstanceService,\n private readonly wasiHttpComponentService?: WasmtimeWasiHttpComponentService,\n ) {\n this.bridgeServer = new HostBridgeServer(appInstance);\n this.uiServer = new UiServerService(appInstance.bundle);\n }\n\n start = async (options: AppHostStartOptions): Promise<AppHostHandle> => {\n if (this.server) {\n throw new Error(\"应用宿主已经启动。\");\n }\n const server = createServer((request, response) => {\n void this.handleRequest(request, response);\n });\n this.server = server;\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(options.port, options.host, () => {\n server.off(\"error\", reject);\n resolve();\n });\n });\n const address = server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"无法获取应用宿主监听地址。\");\n }\n return {\n appId: this.appInstance.bundle.manifest.id,\n url: `http://${address.address}:${address.port}`,\n host: address.address,\n port: address.port,\n };\n };\n\n stop = async (): Promise<void> => {\n if (!this.server) {\n return;\n }\n await new Promise<void>((resolve, reject) => {\n this.server?.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n this.server = undefined;\n };\n\n private handleRequest = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n try {\n if (this.wasiHttpComponentService) {\n const backendHandled = await this.wasiHttpComponentService.handleRequest(request, response);\n if (backendHandled) {\n return;\n }\n }\n const bridgeHandled = await this.bridgeServer.handle(request, response);\n if (bridgeHandled) {\n return;\n }\n await this.uiServer.handle(request, response);\n } catch (error) {\n response.writeHead(500, {\n \"content-type\": \"application/json; charset=utf-8\",\n });\n response.end(\n JSON.stringify(\n {\n ok: false,\n error: {\n code: \"APP_HOST_ERROR\",\n message: error instanceof Error ? error.message : String(error),\n },\n },\n null,\n 2,\n ),\n );\n }\n };\n}\n"],"mappings":";;;;AAkBA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CAEA,YACE,aACA,0BACA;AAFiB,OAAA,cAAA;AACA,OAAA,2BAAA;AAEjB,OAAK,eAAe,IAAI,iBAAiB,YAAY;AACrD,OAAK,WAAW,IAAI,gBAAgB,YAAY,OAAO;;CAGzD,QAAQ,OAAO,YAAyD;AACtE,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,YAAY;EAE9B,MAAM,SAAS,cAAc,SAAS,aAAa;AAC5C,QAAK,cAAc,SAAS,SAAS;IAC1C;AACF,OAAK,SAAS;AACd,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,UAAO,KAAK,SAAS,OAAO;AAC5B,UAAO,OAAO,QAAQ,MAAM,QAAQ,YAAY;AAC9C,WAAO,IAAI,SAAS,OAAO;AAC3B,aAAS;KACT;IACF;EACF,MAAM,UAAU,OAAO,SAAS;AAChC,MAAI,CAAC,WAAW,OAAO,YAAY,SACjC,OAAM,IAAI,MAAM,gBAAgB;AAElC,SAAO;GACL,OAAO,KAAK,YAAY,OAAO,SAAS;GACxC,KAAK,UAAU,QAAQ,QAAQ,GAAG,QAAQ;GAC1C,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf;;CAGH,OAAO,YAA2B;AAChC,MAAI,CAAC,KAAK,OACR;AAEF,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,QAAK,QAAQ,OAAO,UAAU;AAC5B,QAAI,OAAO;AACT,YAAO,MAAM;AACb;;AAEF,aAAS;KACT;IACF;AACF,OAAK,SAAS,KAAA;;CAGhB,gBAAwB,OACtB,SACA,aACkB;AAClB,MAAI;AACF,OAAI,KAAK;QACgB,MAAM,KAAK,yBAAyB,cAAc,SAAS,SAAS,CAEzF;;AAIJ,OADsB,MAAM,KAAK,aAAa,OAAO,SAAS,SAAS,CAErE;AAEF,SAAM,KAAK,SAAS,OAAO,SAAS,SAAS;WACtC,OAAO;AACd,YAAS,UAAU,KAAK,EACtB,gBAAgB,mCACjB,CAAC;AACF,YAAS,IACP,KAAK,UACH;IACE,IAAI;IACJ,OAAO;KACL,MAAM;KACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KAChE;IACF,EACD,MACA,EACD,CACF"}
@@ -0,0 +1,72 @@
1
+ import { access } from "node:fs/promises";
2
+ import path from "node:path";
3
+ //#region src/services/app-install-source.service.ts
4
+ var AppInstallSourceService = class {
5
+ constructor(manifestService, remoteRegistryClient) {
6
+ this.manifestService = manifestService;
7
+ this.remoteRegistryClient = remoteRegistryClient;
8
+ }
9
+ resolve = async (appSource, registryUrl) => {
10
+ const localSource = await this.detectLocal(appSource);
11
+ if (localSource.kind === "directory") return {
12
+ kind: "directory",
13
+ appDirectory: localSource.appDirectory,
14
+ sourceRef: localSource.appDirectory
15
+ };
16
+ if (localSource.kind === "bundle") return {
17
+ kind: "bundle",
18
+ bundlePath: localSource.bundlePath,
19
+ sourceRef: localSource.bundlePath
20
+ };
21
+ const registrySpec = this.parseRegistrySpec(appSource);
22
+ if (!registrySpec) {
23
+ if (this.looksLikePath(appSource)) throw new Error(`本地安装源不存在:${appSource}`);
24
+ throw new Error(`无法识别安装源:${appSource}`);
25
+ }
26
+ const registryResolution = await this.remoteRegistryClient.resolve({
27
+ appId: registrySpec.appId,
28
+ version: registrySpec.version,
29
+ registryUrl
30
+ });
31
+ return {
32
+ kind: "registry",
33
+ sourceRef: `${registryResolution.appId}@${registryResolution.version}`,
34
+ registryResolution
35
+ };
36
+ };
37
+ detectLocal = async (sourcePath, allowBundle = true) => {
38
+ const normalizedSource = path.resolve(sourcePath);
39
+ try {
40
+ await access(normalizedSource);
41
+ if ((await this.manifestService.load(normalizedSource)).appDirectory) return {
42
+ kind: "directory",
43
+ appDirectory: normalizedSource
44
+ };
45
+ } catch {
46
+ if (allowBundle && normalizedSource.endsWith(".napp")) try {
47
+ await access(normalizedSource);
48
+ return {
49
+ kind: "bundle",
50
+ bundlePath: normalizedSource
51
+ };
52
+ } catch {
53
+ return { kind: "missing" };
54
+ }
55
+ return { kind: "missing" };
56
+ }
57
+ return { kind: "missing" };
58
+ };
59
+ parseRegistrySpec = (appSource) => {
60
+ const match = /^(?<appId>[a-z0-9][a-z0-9._-]*)(?:@(?<version>[A-Za-z0-9._+-]+))?$/i.exec(appSource.trim());
61
+ if (!match?.groups?.appId) return;
62
+ return {
63
+ appId: match.groups.appId,
64
+ version: match.groups.version
65
+ };
66
+ };
67
+ looksLikePath = (appSource) => appSource.startsWith(".") || appSource.startsWith("/") || appSource.includes(path.sep);
68
+ };
69
+ //#endregion
70
+ export { AppInstallSourceService };
71
+
72
+ //# sourceMappingURL=app-install-source.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-install-source.service.js","names":[],"sources":["../../src/services/app-install-source.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport type { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\n\ntype LocalAppSource =\n | { kind: \"directory\"; appDirectory: string }\n | { kind: \"bundle\"; bundlePath: string }\n | { kind: \"missing\" };\n\nexport type ResolvedAppInstallSource =\n | {\n kind: \"directory\";\n appDirectory: string;\n sourceRef: string;\n }\n | {\n kind: \"bundle\";\n bundlePath: string;\n sourceRef: string;\n }\n | {\n kind: \"registry\";\n sourceRef: string;\n registryResolution: Awaited<ReturnType<AppRemoteRegistryClientService[\"resolve\"]>>;\n };\n\nexport class AppInstallSourceService {\n constructor(\n private readonly manifestService: AppManifestService,\n private readonly remoteRegistryClient: AppRemoteRegistryClientService,\n ) {}\n\n resolve = async (\n appSource: string,\n registryUrl?: string,\n ): Promise<ResolvedAppInstallSource> => {\n const localSource = await this.detectLocal(appSource);\n if (localSource.kind === \"directory\") {\n return {\n kind: \"directory\",\n appDirectory: localSource.appDirectory,\n sourceRef: localSource.appDirectory,\n };\n }\n if (localSource.kind === \"bundle\") {\n return {\n kind: \"bundle\",\n bundlePath: localSource.bundlePath,\n sourceRef: localSource.bundlePath,\n };\n }\n const registrySpec = this.parseRegistrySpec(appSource);\n if (!registrySpec) {\n if (this.looksLikePath(appSource)) {\n throw new Error(`本地安装源不存在:${appSource}`);\n }\n throw new Error(`无法识别安装源:${appSource}`);\n }\n const registryResolution = await this.remoteRegistryClient.resolve({\n appId: registrySpec.appId,\n version: registrySpec.version,\n registryUrl,\n });\n return {\n kind: \"registry\",\n sourceRef: `${registryResolution.appId}@${registryResolution.version}`,\n registryResolution,\n };\n };\n\n detectLocal = async (\n sourcePath: string,\n allowBundle: boolean = true,\n ): Promise<LocalAppSource> => {\n const normalizedSource = path.resolve(sourcePath);\n try {\n await access(normalizedSource);\n const manifestBundle = await this.manifestService.load(normalizedSource);\n if (manifestBundle.appDirectory) {\n return {\n kind: \"directory\",\n appDirectory: normalizedSource,\n };\n }\n } catch {\n if (allowBundle && normalizedSource.endsWith(\".napp\")) {\n try {\n await access(normalizedSource);\n return {\n kind: \"bundle\",\n bundlePath: normalizedSource,\n };\n } catch {\n return { kind: \"missing\" };\n }\n }\n return { kind: \"missing\" };\n }\n return { kind: \"missing\" };\n };\n\n private parseRegistrySpec = (\n appSource: string,\n ): { appId: string; version?: string } | undefined => {\n const match = /^(?<appId>[a-z0-9][a-z0-9._-]*)(?:@(?<version>[A-Za-z0-9._+-]+))?$/i.exec(\n appSource.trim(),\n );\n if (!match?.groups?.appId) {\n return undefined;\n }\n return {\n appId: match.groups.appId,\n version: match.groups.version,\n };\n };\n\n private looksLikePath = (appSource: string): boolean =>\n appSource.startsWith(\".\") ||\n appSource.startsWith(\"/\") ||\n appSource.includes(path.sep);\n}\n"],"mappings":";;;AA2BA,IAAa,0BAAb,MAAqC;CACnC,YACE,iBACA,sBACA;AAFiB,OAAA,kBAAA;AACA,OAAA,uBAAA;;CAGnB,UAAU,OACR,WACA,gBACsC;EACtC,MAAM,cAAc,MAAM,KAAK,YAAY,UAAU;AACrD,MAAI,YAAY,SAAS,YACvB,QAAO;GACL,MAAM;GACN,cAAc,YAAY;GAC1B,WAAW,YAAY;GACxB;AAEH,MAAI,YAAY,SAAS,SACvB,QAAO;GACL,MAAM;GACN,YAAY,YAAY;GACxB,WAAW,YAAY;GACxB;EAEH,MAAM,eAAe,KAAK,kBAAkB,UAAU;AACtD,MAAI,CAAC,cAAc;AACjB,OAAI,KAAK,cAAc,UAAU,CAC/B,OAAM,IAAI,MAAM,YAAY,YAAY;AAE1C,SAAM,IAAI,MAAM,WAAW,YAAY;;EAEzC,MAAM,qBAAqB,MAAM,KAAK,qBAAqB,QAAQ;GACjE,OAAO,aAAa;GACpB,SAAS,aAAa;GACtB;GACD,CAAC;AACF,SAAO;GACL,MAAM;GACN,WAAW,GAAG,mBAAmB,MAAM,GAAG,mBAAmB;GAC7D;GACD;;CAGH,cAAc,OACZ,YACA,cAAuB,SACK;EAC5B,MAAM,mBAAmB,KAAK,QAAQ,WAAW;AACjD,MAAI;AACF,SAAM,OAAO,iBAAiB;AAE9B,QADuB,MAAM,KAAK,gBAAgB,KAAK,iBAAiB,EACrD,aACjB,QAAO;IACL,MAAM;IACN,cAAc;IACf;UAEG;AACN,OAAI,eAAe,iBAAiB,SAAS,QAAQ,CACnD,KAAI;AACF,UAAM,OAAO,iBAAiB;AAC9B,WAAO;KACL,MAAM;KACN,YAAY;KACb;WACK;AACN,WAAO,EAAE,MAAM,WAAW;;AAG9B,UAAO,EAAE,MAAM,WAAW;;AAE5B,SAAO,EAAE,MAAM,WAAW;;CAG5B,qBACE,cACoD;EACpD,MAAM,QAAQ,sEAAsE,KAClF,UAAU,MAAM,CACjB;AACD,MAAI,CAAC,OAAO,QAAQ,MAClB;AAEF,SAAO;GACL,OAAO,MAAM,OAAO;GACpB,SAAS,MAAM,OAAO;GACvB;;CAGH,iBAAyB,cACvB,UAAU,WAAW,IAAI,IACzB,UAAU,WAAW,IAAI,IACzB,UAAU,SAAS,KAAK,IAAI"}