@nextclaw/app-runtime 0.9.14 → 0.10.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/dist/controllers/publish.controller.js +7 -0
- package/dist/controllers/publish.controller.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/package.js +1 -1
- package/dist/services/app-artifact-validation.service.d.ts +46 -0
- package/dist/services/app-artifact-validation.service.d.ts.map +1 -0
- package/dist/services/app-artifact-validation.service.js +250 -0
- package/dist/services/app-artifact-validation.service.js.map +1 -0
- package/dist/services/app-bundle.service.d.ts +4 -8
- package/dist/services/app-bundle.service.d.ts.map +1 -1
- package/dist/services/app-bundle.service.js +7 -66
- package/dist/services/app-bundle.service.js.map +1 -1
- package/dist/services/app-home.service.d.ts +1 -0
- package/dist/services/app-home.service.d.ts.map +1 -1
- package/dist/services/app-home.service.js +3 -0
- package/dist/services/app-home.service.js.map +1 -1
- package/dist/services/app-installation.service.d.ts +7 -1
- package/dist/services/app-installation.service.d.ts.map +1 -1
- package/dist/services/app-installation.service.js +84 -5
- package/dist/services/app-installation.service.js.map +1 -1
- package/dist/services/app-marketplace-metadata.service.d.ts +5 -1
- package/dist/services/app-marketplace-metadata.service.d.ts.map +1 -1
- package/dist/services/app-marketplace-metadata.service.js +44 -2
- package/dist/services/app-marketplace-metadata.service.js.map +1 -1
- package/dist/services/app-publish-validation.service.d.ts +1 -1
- package/dist/services/app-publish.service.d.ts +2 -1
- package/dist/services/app-publish.service.d.ts.map +1 -1
- package/dist/services/app-publish.service.js +6 -2
- package/dist/services/app-publish.service.js.map +1 -1
- package/dist/services/app-registry.service.d.ts +2 -0
- package/dist/services/app-registry.service.d.ts.map +1 -1
- package/dist/services/app-registry.service.js +20 -2
- package/dist/services/app-registry.service.js.map +1 -1
- package/dist/types/app-installation.types.d.ts +4 -2
- package/dist/types/app-installation.types.d.ts.map +1 -1
- package/dist/types/app-publish.types.d.ts +11 -2
- package/dist/types/app-publish.types.d.ts.map +1 -1
- package/dist/types/app-publish.types.js.map +1 -1
- package/dist/types/app-registry.types.d.ts +4 -1
- package/dist/types/app-registry.types.d.ts.map +1 -1
- package/dist/types/app-remote-registry.types.d.ts +1 -1
- package/package.json +6 -1
|
@@ -1 +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":";;;;;;;AAkBA,MAAM,uBAAuB,KAAK,OAAO;AACzC,MAAM,yBAAyB,MAAM,OAAO;AAC5C,MAAM,iBAAiB,KAAK,OAAO;AACnC,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AACvB,MAAM,uBAAuB;AAE7B,IAAa,mBAAb,MAA8B;CAC5B,YACE,kBAAuD,IAAI,oBAAoB,EAC/E;AADiB,OAAA,kBAAA;;CAGnB,mBAAmB,OAAO,WAIU;EAClC,MAAM,EAAE,cAAc,YAAY,MAAM,kBAAkB;EAC1D,MAAM,SAAS,MAAM,KAAK,gBAAgB,KAAK,aAAa;EAC5D,MAAM,OAAO,iBAAiB;AAC9B,MAAI,6BAA6B,OAAO,IAAI,SAAS,SACnD,OAAM,IAAI,MAAM,wCAAwC;EAE1D,MAAM,EAAE,UAAU,cAAc,SAAS,WACrC,MAAM,KAAK,mBAAmB,OAAO,GACrC,MAAM,KAAK,oBAAoB,OAAO;AAC1C,OAAK,kBAAkB,SAAS;EAChC,MAAM,WAAW,KAAK,cACpB,OAAO,SAAS,IAChB,OAAO,SAAS,MAChB,OAAO,SAAS,SAChB,KACD;EACD,MAAM,kBAAkB,QAAQ,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,IAAI;EACzE,MAAM,YAAY,KAAK,eAAe;GACpC,GAAG;IACF,uBAAuB;GACzB,CAAC;EACF,MAAM,qBAAqB,QAAQ,GAAG,KAAK,UAAU,WAAW,MAAM,EAAE,CAAC,IAAI;EAC7E,MAAM,eAAe,QACnB;GACE,GAAG;IACF,uBAAuB;IACvB,iBAAiB;GACnB,EACD,EAAE,OAAO,GAAG,CACb;AACD,MAAI,aAAa,aAAa,qBAC5B,OAAM,IAAI,MAAM,gBAAgB,qBAAqB,YAAY;EAEnE,MAAM,qBAAqB,aACvB,KAAK,QAAQ,WAAW,GACxB,KAAK,KACH,KAAK,QAAQ,OAAO,aAAa,EACjC,GAAG,KAAK,wBAAwB,OAAO,SAAS,GAAG,CAAC,GAAG,OAAO,SAAS,QAAQ,OAChF;AACL,QAAM,MAAM,KAAK,QAAQ,mBAAmB,EAAE,EAAE,WAAW,MAAM,CAAC;AAClE,QAAM,UAAU,oBAAoB,OAAO,KAAK,aAAa,CAAC;AAC9D,SAAO;GACL,YAAY;GACZ;GACA,WAAW,aAAa;GACxB;GACD;;CAGH,gBAAgB,OAAO,WAGgB;EACrC,MAAM,aAAa,KAAK,QAAQ,OAAO,WAAW;EAClD,MAAM,kBAAkB,KAAK,QAAQ,OAAO,gBAAgB;EAC5D,MAAM,EAAE,SAAS,UAAU,cAAc,MAAM,KAAK,qBAAqB,WAAW;AACpF,QAAM,KAAK,yBAAyB;GAAE;GAAS;GAAU;GAAiB,CAAC;AAC3E,SAAO;GAAE,cAAc;GAAiB;GAAU;GAAW;;CAG/D,uBAA+B,OAAO,eAIhC;EACJ,MAAM,cAAc,MAAM,KAAK,WAAW;AAC1C,MAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,OAAO,qBAC9C,OAAM,IAAI,MAAM,iBAAiB,qBAAqB,YAAY;EAEpE,IAAI,YAAY;EAChB,IAAI,aAAa;EACjB,MAAM,UAAU,UAAU,IAAI,WAAW,MAAM,SAAS,WAAW,CAAC,EAAE,EACpE,SAAS,SAAS;AAChB,gBAAa;AACb,iBAAc,KAAK;AACnB,OAAI,YAAY,eACd,OAAM,IAAI,MAAM,gBAAgB,eAAe,MAAM;AAEvD,OAAI,KAAK,eAAe,eACtB,OAAM,IAAI,MAAM,gBAAgB,eAAe,YAAY,KAAK,OAAO;AAEzE,OAAI,aAAa,uBACf,OAAM,IAAI,MAAM,gBAAgB,uBAAuB,YAAY;AAErE,UAAO;KAEV,CAAC;EACF,MAAM,oBAAoB,KAAK,iBAAiB,QAAQ;EACxD,MAAM,WAAW,KAAK,wBAAwB,kBAAkB;EAChE,MAAM,YAAY,KAAK,yBAAyB,kBAAkB;AAClE,OAAK,uBAAuB,mBAAmB,UAAU;AACzD,SAAO;GAAE,SAAS;GAAmB;GAAU;GAAW;;CAG5D,2BAAmC,OAAO,WAIrB;EACnB,MAAM,EAAE,SAAS,UAAU,oBAAoB;EAC/C,MAAM,eAAe,KAAK,QAAQ,gBAAgB;EAClD,MAAM,cAAc,YAAY;EAChC,MAAM,kBAAkB,KAAK,KAC3B,cACA,IAAI,KAAK,SAAS,gBAAgB,CAAC,cAAc,cAClD;EACD,MAAM,kBAAkB,KAAK,KAC3B,cACA,IAAI,KAAK,SAAS,gBAAgB,CAAC,UAAU,cAC9C;EACD,MAAM,eAAe,MAAM,KAAK,WAAW,gBAAgB;AAC3D,QAAM,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC;AAC9C,MAAI;AACF,SAAM,MAAM,gBAAgB;AAC5B,SAAM,KAAK,oBAAoB,SAAS,gBAAgB;AACxD,SAAM,KAAK,wBAAwB,iBAAiB,SAAS;AAC7D,OAAI,aACF,OAAM,OAAO,iBAAiB,gBAAgB;AAEhD,SAAM,OAAO,iBAAiB,gBAAgB;AAC9C,SAAM,GAAG,iBAAiB;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;WACpD,OAAO;AACd,SAAM,GAAG,iBAAiB;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AAC3D,OAAI,gBAAgB,MAAM,KAAK,WAAW,gBAAgB,EAAE;AAC1D,UAAM,GAAG,iBAAiB;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;AAC3D,QAAI;AACF,WAAM,OAAO,iBAAiB,gBAAgB;aACvC,cAAc;AACrB,WAAM,IAAI,eACR,CAAC,OAAO,aAAa,EACrB,0BAA0B,kBAC3B;;;AAGL,SAAM;YACE;AACR,SAAM,GAAG,iBAAiB;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AAC3D,SAAM,GAAG,iBAAiB;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;;;CAI/D,sBAA8B,OAC5B,SACA,oBACkB;AAClB,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,QAAQ,EAAE;GACxD,MAAM,aAAa,KAAK,KAAK,iBAAiB,UAAU;AACxD,SAAM,MAAM,KAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC1D,SAAM,UAAU,YAAY,OAAO,KAAK,MAAM,CAAC;;;CAInD,0BAAkC,OAChC,cACA,aACkB;EAClB,MAAM,iBAAiB,MAAM,KAAK,gBAAgB,KAAK,aAAa;AACpE,MACE,SAAS,UAAU,eAAe,SAAS,MAC3C,SAAS,SAAS,eAAe,SAAS,QAC1C,SAAS,YAAY,eAAe,SAAS,QAE7C,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,eAAe,SAAS,kBAAkB,KAAK,SAAS,qBAAqB,SAC/E,OAAM,IAAI,MAAM,8BAA8B;;CAIlD,sBAA8B,OAC5B,WAC2E;EAC3E,MAAM,YAAY,IAAI,IAAY,CAChC,KAAK,SAAS,OAAO,cAAc,OAAO,aAAa,CAAC,QAAQ,OAAO,IAAI,CAC5E,CAAC;AACF,MAAI,8BAA8B,OAAO,EAAE;AACzC,aAAU,IAAI,KAAK,SAAS,OAAO,cAAc,OAAO,cAAc,CAAC,QAAQ,OAAO,IAAI,CAAC;AAC3F,SAAM,KAAK,sBAAsB,OAAO,iBAAiB,OAAO,cAAc,UAAU;AACxF,SAAM,KAAK,sBAAsB,OAAO,qBAAqB,OAAO,cAAc,UAAU;SACvF;AACL,QAAK,MAAM,aAAa,OAAO,WAC7B,OAAM,KAAK,sBAAsB,UAAU,oBAAoB,OAAO,cAAc,UAAU;AAEhG,SAAM,KAAK,sBAAsB,OAAO,qBAAqB,OAAO,cAAc,UAAU;AAC5F,SAAM,KAAK,gBAAgB,OAAO,cAAc,oBAAoB,UAAU;;AAEhF,MAAI,OAAO,SACT,WAAU,IAAI,KAAK,SAAS,OAAO,cAAc,OAAO,SAAS,CAAC,QAAQ,OAAO,IAAI,CAAC;AAExF,SAAO,MAAM,KAAK,aAAa,OAAO,cAAc,UAAU;;CAGhE,qBAA6B,OAC3B,WAC2E;AAC3E,MAAI,CAAC,8BAA8B,OAAO,CACxC,OAAM,IAAI,MAAM,8BAA8B;EAEhD,MAAM,4BAAY,IAAI,KAAa;AACnC,QAAM,KAAK,4BAA4B,OAAO,cAAc,OAAO,cAAc,UAAU;EAC3F,MAAM,SAAS,MAAM,KAAK,aAAa,OAAO,cAAc,UAAU;AACtE,MAAI,OAAO,SAAS,KAAK,SAAS,sBAChC,QAAO,SAAS,OAAO,SAAS,KAAK,SAAS;AAEhD,SAAO;;CAGT,eAAuB,OACrB,cACA,cAC2E;EAC3E,MAAM,cAAc,MAAM,KAAK,UAAU,CAAC,MAAM,MAAM,UAAU,KAAK,cAAc,MAAM,CAAC;EAC1F,MAAM,WAAuC,EAAE;AAC/C,OAAK,MAAM,gBAAgB,YACzB,UAAS,gBAAgB,IAAI,WAAW,MAAM,SAAS,KAAK,KAAK,cAAc,aAAa,CAAC,CAAC;AAEhG,SAAO;GAAE;GAAU,WAAW;GAAa;;CAG7C,wBAAgC,OAC9B,eACA,cACA,cACkB;AAClB,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,eAAe,EAAE,eAAe,MAAM,CAAC;AACrE,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,YAAY,KAAK,KAAK,eAAe,MAAM,KAAK;IACtD,MAAM,eAAe,KAAK,SAAS,cAAc,UAAU,CAAC,QAAQ,OAAO,IAAI;AAE/E,SADmB,MAAM,MAAM,UAAU,EAC1B,gBAAgB,CAC7B,OAAM,IAAI,MAAM,oBAAoB,eAAe;AAErD,QAAI,MAAM,aAAa,EAAE;AACvB,SAAI,CAAC,KAAK,yBAAyB,aAAa,CAC9C,OAAM,KAAK,sBAAsB,WAAW,cAAc,UAAU;AAEtE;;AAEF,QAAI,MAAM,QAAQ,IAAI,CAAC,KAAK,yBAAyB,aAAa,CAChE,WAAU,IAAI,aAAa;;WAGxB,OAAO;AACd,OAAI,KAAK,mBAAmB,MAAM,CAChC;AAEF,SAAM;;;CAIV,8BAAsC,OACpC,eACA,cACA,cACkB;EAClB,MAAM,UAAU,MAAM,QAAQ,eAAe,EAAE,eAAe,MAAM,CAAC;AACrE,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,YAAY,KAAK,KAAK,eAAe,MAAM,KAAK;GACtD,MAAM,eAAe,KAAK,SAAS,cAAc,UAAU,CAAC,QAAQ,OAAO,IAAI;AAE/E,QADmB,MAAM,MAAM,UAAU,EAC1B,gBAAgB,CAC7B,OAAM,IAAI,MAAM,oBAAoB,eAAe;AAErD,OAAI,MAAM,aAAa;QACjB,CAAC,KAAK,wBAAwB,aAAa,CAC7C,OAAM,KAAK,4BAA4B,WAAW,cAAc,UAAU;cAEnE,MAAM,QAAQ,IAAI,CAAC,KAAK,wBAAwB,aAAa,CACtE,WAAU,IAAI,aAAa;;;CAKjC,kBAA0B,OACxB,cACA,cACA,cACkB;AAClB,MAAI;GACF,MAAM,QAAQ,MAAM,MAAM,KAAK,KAAK,cAAc,aAAa,CAAC;AAChE,OAAI,MAAM,QAAQ,IAAI,CAAC,MAAM,gBAAgB,CAC3C,WAAU,IAAI,aAAa;WAEtB,OAAO;AACd,OAAI,CAAC,KAAK,mBAAmB,MAAM,CACjC,OAAM;;;CAKZ,4BAAoC,iBAAkC;AAEpE,SADiB,aAAa,MAAM,IAAI,CACxB,MAAM,YACpB,YAAY,kBACZ,YAAY,UACZ,YAAY,cACZ,YAAY,WACZ,YAAY,eACZ,YAAY,WACb,IAAI,sCAAsC,KAAK,aAAa,IAAI,aAAa,SAAS,OAAO;;CAGhG,2BAAmC,iBAAkC;AACnE,SAAO,KAAK,yBAAyB,aAAa,IAChD,iBAAiB,WAAW,aAAa,WAAW,SAAS,IAC7D,iBAAiB,eAAe,aAAa,WAAW,aAAa,IACrE,iBAAiB,oBAAoB,aAAa,WAAW,kBAAkB;;CAGnF,qBAA6B,UAA4C;EACvE,MAAM,UAAU,OAAO,QAAQ,MAAM;AACrC,MAAI,QAAQ,SAAS,IAAI,eACvB,OAAM,IAAI,MAAM,gBAAgB,eAAe,MAAM;EAEvD,IAAI,aAAa;AACjB,OAAK,MAAM,CAAC,cAAc,UAAU,SAAS;AAC3C,OAAI,MAAM,aAAa,eACrB,OAAM,IAAI,MAAM,gBAAgB,eAAe,YAAY,eAAe;AAE5E,iBAAc,MAAM;;AAEtB,MAAI,aAAa,uBACf,OAAM,IAAI,MAAM,gBAAgB,uBAAuB,YAAY;;CAIvE,oBACE,YAC+B;EAC/B,MAAM,oBAAgD,EAAE;AACxD,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,QAAQ,EAAE;GACxD,MAAM,kBAAkB,KAAK,sBAAsB,UAAU;AAC7D,OAAI,OAAO,OAAO,mBAAmB,gBAAgB,CACnD,OAAM,IAAI,MAAM,iBAAiB,kBAAkB;AAErD,qBAAkB,mBAAmB;;AAEvC,SAAO;;CAGT,2BACE,YACsB;EACtB,MAAM,MAAM,KAAK,qBACf,SACA,sBACA,kBACD;EACD,MAAM,mBAAmB,IAAI,qBAAqB,KAAA,IAC9C,WACA,IAAI;AACR,MACE,IAAI,wBAAwB,KAC3B,qBAAqB,YAAY,qBAAqB,YACvD,OAAO,IAAI,UAAU,YAAY,CAAC,IAAI,SACtC,OAAO,IAAI,SAAS,YAAY,CAAC,IAAI,QACrC,OAAO,IAAI,YAAY,YAAY,CAAC,IAAI,WACxC,IAAI,kBAAkB,mBACtB,IAAI,kBAAkB,eAEtB,OAAM,IAAI,MAAM,sBAAsB;AAExC,SAAO;GACL,GAAG;GACH;GACD;;CAGH,4BACE,YACuB;EACvB,MAAM,MAAM,KAAK,qBACf,SACA,gBACA,mBACD;AACD,MAAI,IAAI,cAAc,YAAY,CAAC,IAAI,SAAS,OAAO,IAAI,UAAU,SACnE,OAAM,IAAI,MAAM,uBAAuB;AAEzC,SAAO;;CAGT,0BACE,SACA,cACS;EACT,MAAM,cAAc,OAAO,KAAK,QAAQ,CACrC,QAAQ,UAAU,UAAU,eAAe,CAC3C,MAAM,MAAM,UAAU,KAAK,cAAc,MAAM,CAAC;EACnD,MAAM,gBAAgB,OAAO,KAAK,UAAU,MAAM,CAC/C,KAAK,UAAU,KAAK,sBAAsB,MAAM,CAAC,CACjD,MAAM,MAAM,UAAU,KAAK,cAAc,MAAM,CAAC;AACnD,MACE,YAAY,WAAW,cAAc,UACrC,YAAY,MAAM,OAAO,UAAU,UAAU,cAAc,OAAO,CAElE,OAAM,IAAI,MAAM,yCAAyC;AAE3D,OAAK,MAAM,gBAAgB,aAAa;GACtC,MAAM,eAAe,UAAU,MAAM;AACrC,OAAI,CAAC,gBAAgB,CAAC,iBAAiB,KAAK,aAAa,CACvD,OAAM,IAAI,MAAM,wBAAwB,eAAe;AAGzD,OADmB,KAAK,cAAc,QAAQ,cAA4B,KACvD,aACjB,OAAM,IAAI,MAAM,wBAAwB,eAAe;;;CAK7D,wBACE,SACA,WACA,UACM;EACN,MAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,aAAa,MAAM,GAAG;AAExC,MAAI;AACF,UAAO,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,SAAS,QAAQ,CAAC;WAChD,OAAO;AACd,SAAM,IAAI,MAAM,QAAQ,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;;;CAI9F,iBACE,OACA,MACA,SACA,sBACuB;EACvB,qBAAqB;EACrB;EACA;EACA;EACA;EACA,eAAe;EACf,eAAe;EAChB;CAED,kBAA0B,WAA2D;EACnF,WAAW;EACX,OAAO,OAAO,YACZ,OAAO,QAAQ,MAAM,CAClB,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,MAAM,CAAC,CACpD,KAAK,CAAC,cAAc,eAAe,CAAC,cAAc,KAAK,cAAc,UAAU,CAAC,CAAC,CACrF;EACF;CAED,iBAAyB,cACvB,WAAW,SAAS,CAAC,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,OAAO,MAAM;CAEnE,2BAAmC,UACjC,MAAM,QAAQ,qBAAqB,IAAI;CAEzC,yBAAiC,cAA8B;EAC7D,MAAM,aAAa,UAAU,QAAQ,OAAO,IAAI;EAChD,MAAM,WAAW,WAAW,MAAM,IAAI;AACtC,MACE,CAAC,cAAc,WAAW,WAAW,IAAI,IAAI,WAAW,SAAS,KAAK,IACtE,aAAa,KAAK,WAAW,IAC7B,SAAS,MAAM,YAAY,CAAC,WAAW,YAAY,OAAO,YAAY,KAAK,CAE3E,OAAM,IAAI,MAAM,kBAAkB,YAAY;AAEhD,SAAO,SAAS,KAAK,IAAI;;CAG3B,sBAA8B,UAC5B,OAAO,UAAU,YAAY,UAAU,QACvC,UAAU,SAAU,MAA6B,SAAS;CAE5D,aAAqB,OAAO,eAAyC;AACnE,MAAI;AACF,SAAM,OAAO,WAAW;AACxB,UAAO;UACD;AACN,UAAO;;;;AAKb,MAAM,gCAAgC,WAAW,KAC/C,OAAO,KACL,gFACA,SACD,CACF"}
|
|
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, zipSync } from \"fflate\";\nimport { AppArtifactValidationService } from \"#app-runtime/services/app-artifact-validation.service.js\";\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 private readonly artifactValidationService: AppArtifactValidationService = new AppArtifactValidationService(),\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 const { archive, metadata, checksums } = await this.artifactValidationService.validate({\n bytes: new Uint8Array(await readFile(bundlePath)),\n });\n return { archive, 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 === \"marketplace-assets\" ||\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 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 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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"}
|
|
@@ -7,6 +7,7 @@ declare class AppHomeService {
|
|
|
7
7
|
getDataDirectory: () => string;
|
|
8
8
|
getRegistryPath: () => string;
|
|
9
9
|
getConfigPath: () => string;
|
|
10
|
+
getOperationsPath: () => string;
|
|
10
11
|
getInstallDirectory: (appId: string, version: string) => string;
|
|
11
12
|
getAppDataDirectory: (appId: string) => string;
|
|
12
13
|
ensureBaseDirectories: () => Promise<void>;
|
|
@@ -1 +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
|
+
{"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,iBAAA;EAIA,mBAAA,GAAuB,KAAA,UAAe,OAAA;EAItC,mBAAA,GAAuB,KAAA;EAIvB,qBAAA,QAAkC,OAAA;EAQlC,wBAAA,GAAkC,MAAA,aAAiB,OAAA;AAAA"}
|
|
@@ -21,6 +21,9 @@ var AppHomeService = class {
|
|
|
21
21
|
getConfigPath = () => {
|
|
22
22
|
return path.join(this.appHomeDirectory, "config.json");
|
|
23
23
|
};
|
|
24
|
+
getOperationsPath = () => {
|
|
25
|
+
return path.join(this.appHomeDirectory, "operations.json");
|
|
26
|
+
};
|
|
24
27
|
getInstallDirectory = (appId, version) => {
|
|
25
28
|
return path.join(this.getPackagesDirectory(), appId, version);
|
|
26
29
|
};
|
|
@@ -1 +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
|
+
{"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 getOperationsPath = (): string => {\n return path.join(this.appHomeDirectory, \"operations.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,0BAAkC;AAChC,SAAO,KAAK,KAAK,KAAK,kBAAkB,kBAAkB;;CAG5D,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"}
|
|
@@ -6,7 +6,7 @@ import { AppRegistryService } from "./app-registry.service.js";
|
|
|
6
6
|
import { AppBuildService } from "./app-build.service.js";
|
|
7
7
|
import { AppRegistryConfigService } from "./app-registry-config.service.js";
|
|
8
8
|
import { AppRemoteRegistryClientService } from "./app-remote-registry-client.service.js";
|
|
9
|
-
import { AppActivationResult, AppInfoResult, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "../types/app-installation.types.js";
|
|
9
|
+
import { AppActivationResult, AppInfoResult, AppInstallProgressHandler, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "../types/app-installation.types.js";
|
|
10
10
|
|
|
11
11
|
//#region src/services/app-installation.service.d.ts
|
|
12
12
|
declare class AppInstallationService {
|
|
@@ -21,13 +21,16 @@ declare class AppInstallationService {
|
|
|
21
21
|
constructor(appHomeService?: AppHomeService, bundleService?: AppBundleService, manifestService?: AppManifestService, buildService?: AppBuildService, registryService?: AppRegistryService, registryConfigService?: AppRegistryConfigService, remoteRegistryClient?: AppRemoteRegistryClientService);
|
|
22
22
|
install: (appSource: string, options?: {
|
|
23
23
|
registryUrl?: string;
|
|
24
|
+
onProgress?: AppInstallProgressHandler;
|
|
24
25
|
}) => Promise<AppInstallResult>;
|
|
25
26
|
update: (appId: string, options?: {
|
|
26
27
|
version?: string;
|
|
27
28
|
registryUrl?: string;
|
|
29
|
+
onProgress?: AppInstallProgressHandler;
|
|
28
30
|
}) => Promise<AppUpdateResult>;
|
|
29
31
|
uninstall: (appId: string, purgeData: boolean) => Promise<AppUninstallResult>;
|
|
30
32
|
list: () => Promise<InstalledAppListItem[]>;
|
|
33
|
+
reconcileFilesystem: () => Promise<void>;
|
|
31
34
|
info: (appId: string) => Promise<AppInfoResult>;
|
|
32
35
|
setEnabled: (appId: string, enabled: boolean) => Promise<AppActivationResult>;
|
|
33
36
|
rollback: (appId: string, version: string) => Promise<AppRollbackResult>;
|
|
@@ -35,7 +38,10 @@ declare class AppInstallationService {
|
|
|
35
38
|
persistGrants: (appId: string | undefined, documentGrantMap: AppDocumentGrantMap) => Promise<void>;
|
|
36
39
|
private copyToImmutableInstallDirectory;
|
|
37
40
|
private materializeDistribution;
|
|
41
|
+
private reconcileGeneratedSiblings;
|
|
42
|
+
private readDirectories;
|
|
38
43
|
private pathExists;
|
|
44
|
+
private isMissingFileError;
|
|
39
45
|
}
|
|
40
46
|
//#endregion
|
|
41
47
|
export { AppInstallationService };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;cA6Ba,sBAAA;EAAA,iBAIQ,cAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,YAAA;EAAA,iBACA,eAAA;EAAA,iBACA,qBAAA;EAAA,iBAGA,oBAAA;EAAA,iBAXF,oBAAA;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;EAUzC,OAAA,GACE,SAAA,UACA,OAAA;IACE,WAAA;IACA,UAAA,GAAa,yBAAA;EAAA,MAEd,OAAA,CAAQ,gBAAA;EAoIX,MAAA,GACE,KAAA,UACA,OAAA;IACE,OAAA;IACA,WAAA;IACA,UAAA,GAAa,yBAAA;EAAA,MAEd,OAAA,CAAQ,eAAA;EAiFX,SAAA,GACE,KAAA,UACA,SAAA,cACC,OAAA,CAAQ,kBAAA;EAwDX,IAAA,QAAiB,OAAA,CAAQ,oBAAA;EAkBzB,mBAAA,QAAgC,OAAA;EAiBhC,IAAA,GAAc,KAAA,aAAgB,OAAA,CAAQ,aAAA;EAmCtC,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,mBAAA;EAS9D,QAAA,GAAkB,KAAA,UAAe,OAAA,aAAkB,OAAA,CAAQ,iBAAA;EAgC3D,aAAA,GACE,YAAA,UACA,wBAAA,EAA0B,mBAAA,KACzB,OAAA,CAAQ,mBAAA;EA2BX,aAAA,GACE,KAAA,sBACA,gBAAA,EAAkB,mBAAA,KACjB,OAAA;EAAA,QAOK,+BAAA;EAAA,QA4BA,uBAAA;EAAA,QAoBA,0BAAA;EAAA,QAyBA,eAAA;EAAA,QAaA,UAAA;EAAA,QASA,kBAAA;AAAA"}
|
|
@@ -8,7 +8,7 @@ import { AppRemoteRegistryClientService } from "./app-remote-registry-client.ser
|
|
|
8
8
|
import { AppRegistryService } from "./app-registry.service.js";
|
|
9
9
|
import { AppInstallSourceService } from "./app-install-source.service.js";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import { access, cp, mkdir, rename, rm } from "node:fs/promises";
|
|
11
|
+
import { access, cp, mkdir, readdir, rename, rm } from "node:fs/promises";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
//#region src/services/app-installation.service.ts
|
|
14
14
|
var AppInstallationService = class {
|
|
@@ -24,9 +24,12 @@ var AppInstallationService = class {
|
|
|
24
24
|
this.installSourceService = new AppInstallSourceService(this.manifestService, this.remoteRegistryClient);
|
|
25
25
|
}
|
|
26
26
|
install = async (appSource, options) => {
|
|
27
|
-
const
|
|
27
|
+
const { onProgress, registryUrl } = options ?? {};
|
|
28
|
+
await onProgress?.("resolving");
|
|
29
|
+
const source = await this.installSourceService.resolve(appSource, registryUrl);
|
|
28
30
|
const tempDirectory = await this.appHomeService.createTemporaryDirectory("napp-install-");
|
|
29
31
|
try {
|
|
32
|
+
if (source.kind === "registry") await onProgress?.("downloading");
|
|
30
33
|
const bundlePath = source.kind === "directory" ? (await this.bundleService.packAppDirectory({
|
|
31
34
|
appDirectory: source.appDirectory,
|
|
32
35
|
outputPath: path.join(tempDirectory, "app.napp")
|
|
@@ -34,6 +37,7 @@ var AppInstallationService = class {
|
|
|
34
37
|
resolution: source.registryResolution,
|
|
35
38
|
targetDirectory: tempDirectory
|
|
36
39
|
})).bundlePath;
|
|
40
|
+
await onProgress?.("verifying");
|
|
37
41
|
const extractedDirectory = path.join(tempDirectory, "bundle");
|
|
38
42
|
const extractedMetadata = await this.bundleService.extractBundle({
|
|
39
43
|
bundlePath,
|
|
@@ -46,6 +50,7 @@ var AppInstallationService = class {
|
|
|
46
50
|
const manifestBundle = await this.manifestService.load(extractedDirectory);
|
|
47
51
|
const installDirectory = this.appHomeService.getInstallDirectory(manifestBundle.manifest.id, manifestBundle.manifest.version);
|
|
48
52
|
if (source.kind === "registry" && manifestBundle.manifest.id !== source.registryResolution.appId) throw new Error(`bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${manifestBundle.manifest.id}`);
|
|
53
|
+
await onProgress?.("installing");
|
|
49
54
|
const dataDirectory = this.appHomeService.getAppDataDirectory(manifestBundle.manifest.id);
|
|
50
55
|
await this.copyToImmutableInstallDirectory({
|
|
51
56
|
appId: manifestBundle.manifest.id,
|
|
@@ -54,6 +59,7 @@ var AppInstallationService = class {
|
|
|
54
59
|
installDirectory
|
|
55
60
|
});
|
|
56
61
|
await mkdir(dataDirectory, { recursive: true });
|
|
62
|
+
await onProgress?.("finalizing");
|
|
57
63
|
let registryRecord;
|
|
58
64
|
try {
|
|
59
65
|
registryRecord = await this.registryService.upsertInstallation({
|
|
@@ -115,13 +121,15 @@ var AppInstallationService = class {
|
|
|
115
121
|
}
|
|
116
122
|
};
|
|
117
123
|
update = async (appId, options) => {
|
|
124
|
+
const { onProgress, registryUrl: requestedRegistryUrl, version } = options ?? {};
|
|
125
|
+
await onProgress?.("resolving");
|
|
118
126
|
const appRecord = await this.registryService.getApp(appId);
|
|
119
127
|
if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
|
|
120
128
|
const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];
|
|
121
|
-
const registryUrl =
|
|
129
|
+
const registryUrl = requestedRegistryUrl ?? activeVersionRecord?.registryUrl ?? (await this.registryConfigService.getSnapshot()).currentUrl;
|
|
122
130
|
const resolution = await this.remoteRegistryClient.resolve({
|
|
123
131
|
appId,
|
|
124
|
-
version
|
|
132
|
+
version,
|
|
125
133
|
registryUrl
|
|
126
134
|
});
|
|
127
135
|
if (resolution.version === appRecord.activeVersion) {
|
|
@@ -147,8 +155,39 @@ var AppInstallationService = class {
|
|
|
147
155
|
updated: false
|
|
148
156
|
};
|
|
149
157
|
}
|
|
158
|
+
const installedTarget = appRecord.installedVersions[resolution.version];
|
|
159
|
+
if (installedTarget) {
|
|
160
|
+
await onProgress?.("verifying");
|
|
161
|
+
await onProgress?.("installing");
|
|
162
|
+
await this.rollback(appId, resolution.version);
|
|
163
|
+
await onProgress?.("finalizing");
|
|
164
|
+
return {
|
|
165
|
+
appId: appRecord.appId,
|
|
166
|
+
name: appRecord.name,
|
|
167
|
+
version: resolution.version,
|
|
168
|
+
previousVersion: appRecord.activeVersion,
|
|
169
|
+
installDirectory: installedTarget.installDirectory,
|
|
170
|
+
dataDirectory: appRecord.dataDirectory,
|
|
171
|
+
sourceKind: installedTarget.sourceKind,
|
|
172
|
+
distributionMode: installedTarget.distributionMode,
|
|
173
|
+
sourceRef: installedTarget.sourceRef,
|
|
174
|
+
permissions: installedTarget.permissions,
|
|
175
|
+
registryUrl: installedTarget.registryUrl,
|
|
176
|
+
bundleUrl: installedTarget.bundleUrl,
|
|
177
|
+
sha256: installedTarget.sha256,
|
|
178
|
+
publisher: installedTarget.publisher,
|
|
179
|
+
enabled: appRecord.enabled,
|
|
180
|
+
manifestSchemaVersion: installedTarget.manifestSchemaVersion,
|
|
181
|
+
components: installedTarget.components,
|
|
182
|
+
primaryPanelId: installedTarget.primaryPanelId,
|
|
183
|
+
updated: true
|
|
184
|
+
};
|
|
185
|
+
}
|
|
150
186
|
return {
|
|
151
|
-
...await this.install(`${appId}@${resolution.version}`, {
|
|
187
|
+
...await this.install(`${appId}@${resolution.version}`, {
|
|
188
|
+
registryUrl,
|
|
189
|
+
onProgress
|
|
190
|
+
}),
|
|
152
191
|
previousVersion: appRecord.activeVersion,
|
|
153
192
|
updated: true
|
|
154
193
|
};
|
|
@@ -204,6 +243,15 @@ var AppInstallationService = class {
|
|
|
204
243
|
primaryPanelId: appRecord.installedVersions[appRecord.activeVersion]?.primaryPanelId
|
|
205
244
|
}));
|
|
206
245
|
};
|
|
246
|
+
reconcileFilesystem = async () => {
|
|
247
|
+
await this.appHomeService.ensureBaseDirectories();
|
|
248
|
+
const appRecords = await this.registryService.listApps();
|
|
249
|
+
const referencedInstallPaths = new Set(appRecords.flatMap((record) => Object.values(record.installedVersions).map((version) => path.resolve(version.installDirectory))));
|
|
250
|
+
const referencedDataPaths = new Set(appRecords.map((record) => path.resolve(record.dataDirectory)));
|
|
251
|
+
const packageDirectories = await this.readDirectories(this.appHomeService.getPackagesDirectory());
|
|
252
|
+
for (const appDirectory of packageDirectories) await this.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);
|
|
253
|
+
await this.reconcileGeneratedSiblings(this.appHomeService.getDataDirectory(), referencedDataPaths);
|
|
254
|
+
};
|
|
207
255
|
info = async (appId) => {
|
|
208
256
|
const appRecord = await this.registryService.getApp(appId);
|
|
209
257
|
if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
|
|
@@ -317,6 +365,36 @@ var AppInstallationService = class {
|
|
|
317
365
|
install: true
|
|
318
366
|
});
|
|
319
367
|
};
|
|
368
|
+
reconcileGeneratedSiblings = async (parentDirectory, referencedPaths) => {
|
|
369
|
+
for (const entry of await this.readDirectories(parentDirectory)) {
|
|
370
|
+
const entryName = path.basename(entry);
|
|
371
|
+
const stagingMarker = ".staging-";
|
|
372
|
+
const uninstallingMarker = ".uninstalling-";
|
|
373
|
+
if (entryName.includes(stagingMarker)) {
|
|
374
|
+
await rm(entry, {
|
|
375
|
+
recursive: true,
|
|
376
|
+
force: true
|
|
377
|
+
});
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const markerIndex = entryName.indexOf(uninstallingMarker);
|
|
381
|
+
if (markerIndex < 0) continue;
|
|
382
|
+
const originalPath = path.join(parentDirectory, entryName.slice(0, markerIndex));
|
|
383
|
+
if (referencedPaths.has(path.resolve(originalPath)) && !await this.pathExists(originalPath)) await rename(entry, originalPath);
|
|
384
|
+
else await rm(entry, {
|
|
385
|
+
recursive: true,
|
|
386
|
+
force: true
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
readDirectories = async (directory) => {
|
|
391
|
+
try {
|
|
392
|
+
return (await readdir(directory, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path.join(directory, entry.name));
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (this.isMissingFileError(error)) return [];
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
320
398
|
pathExists = async (targetPath) => {
|
|
321
399
|
try {
|
|
322
400
|
await access(targetPath);
|
|
@@ -325,6 +403,7 @@ var AppInstallationService = class {
|
|
|
325
403
|
return false;
|
|
326
404
|
}
|
|
327
405
|
};
|
|
406
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
328
407
|
};
|
|
329
408
|
//#endregion
|
|
330
409
|
export { AppInstallationService };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { access, cp, mkdir, rename, 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 {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.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 { AppInstallSourceService } from \"#app-runtime/services/app-install-source.service.js\";\nimport type {\n AppInfoResult,\n AppActivationResult,\n AppInstallResult,\n AppLaunchResolution,\n AppUpdateResult,\n AppRollbackResult,\n AppUninstallResult,\n InstalledAppListItem,\n} from \"#app-runtime/types/app-installation.types.js\";\n\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\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 ) {\n this.installSourceService = new AppInstallSourceService(\n this.manifestService,\n this.remoteRegistryClient,\n );\n }\n\n install = async (\n appSource: string,\n options?: {\n registryUrl?: string;\n },\n ): Promise<AppInstallResult> => {\n const source = await this.installSourceService.resolve(appSource, options?.registryUrl);\n const tempDirectory = await this.appHomeService.createTemporaryDirectory(\"napp-install-\");\n try {\n const bundlePath =\n source.kind === \"directory\"\n ? (await this.bundleService.packAppDirectory({\n appDirectory: source.appDirectory,\n outputPath: path.join(tempDirectory, \"app.napp\"),\n })).bundlePath\n : source.kind === \"bundle\"\n ? source.bundlePath\n : (\n await this.remoteRegistryClient.downloadBundle({\n resolution: source.registryResolution,\n targetDirectory: tempDirectory,\n })\n ).bundlePath;\n const extractedDirectory = path.join(tempDirectory, \"bundle\");\n const extractedMetadata = await this.bundleService.extractBundle({\n bundlePath,\n targetDirectory: extractedDirectory,\n });\n await this.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n const installDirectory = this.appHomeService.getInstallDirectory(\n manifestBundle.manifest.id,\n manifestBundle.manifest.version,\n );\n if (\n source.kind === \"registry\" &&\n manifestBundle.manifest.id !== source.registryResolution.appId\n ) {\n throw new Error(\n `bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${manifestBundle.manifest.id}`,\n );\n }\n const dataDirectory = this.appHomeService.getAppDataDirectory(manifestBundle.manifest.id);\n await this.copyToImmutableInstallDirectory({\n appId: manifestBundle.manifest.id,\n appVersion: manifestBundle.manifest.version,\n extractedDirectory,\n installDirectory,\n });\n await mkdir(dataDirectory, { recursive: true });\n let registryRecord;\n try {\n registryRecord = await this.registryService.upsertInstallation({\n appId: manifestBundle.manifest.id,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version: manifestBundle.manifest.version,\n installDirectory,\n dataDirectory,\n sourceKind: source.kind,\n distributionMode:\n 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 : {},\n registryUrl:\n source.kind === \"registry\" ? source.registryResolution.registryUrl : undefined,\n bundleUrl:\n source.kind === \"registry\" ? source.registryResolution.bundleUrl : undefined,\n sha256: source.kind === \"registry\" ? source.registryResolution.sha256 : undefined,\n publisher:\n source.kind === \"registry\" ? source.registryResolution.publisher : undefined,\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 });\n } catch (error) {\n await rm(installDirectory, { recursive: true, force: true });\n throw error;\n }\n const activeVersionRecord = registryRecord.installedVersions[registryRecord.activeVersion];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version: registryRecord.activeVersion,\n installDirectory,\n dataDirectory,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode:\n registryRecord.installedVersions[registryRecord.activeVersion]?.distributionMode,\n permissions:\n registryRecord.installedVersions[registryRecord.activeVersion]?.permissions ?? {},\n registryUrl:\n registryRecord.installedVersions[registryRecord.activeVersion]?.registryUrl,\n bundleUrl:\n registryRecord.installedVersions[registryRecord.activeVersion]?.bundleUrl,\n sha256: registryRecord.installedVersions[registryRecord.activeVersion]?.sha256,\n publisher:\n registryRecord.installedVersions[registryRecord.activeVersion]?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: activeVersionRecord?.manifestSchemaVersion ?? 1,\n components: activeVersionRecord?.components,\n primaryPanelId: activeVersionRecord?.primaryPanelId,\n };\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n },\n ): Promise<AppUpdateResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];\n const registryUrl =\n options?.registryUrl ??\n activeVersionRecord?.registryUrl ??\n (await this.registryConfigService.getSnapshot()).currentUrl;\n const resolution = await this.remoteRegistryClient.resolve({\n appId,\n version: options?.version,\n registryUrl,\n });\n if (resolution.version === appRecord.activeVersion) {\n if (!activeVersionRecord) {\n throw new Error(`已安装应用缺少激活版本:${appId}`);\n }\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n version: appRecord.activeVersion,\n previousVersion: appRecord.activeVersion,\n installDirectory: activeVersionRecord.installDirectory,\n dataDirectory: appRecord.dataDirectory,\n sourceKind: activeVersionRecord.sourceKind,\n sourceRef: activeVersionRecord.sourceRef,\n permissions: activeVersionRecord.permissions,\n registryUrl: activeVersionRecord.registryUrl,\n bundleUrl: activeVersionRecord.bundleUrl,\n sha256: activeVersionRecord.sha256,\n publisher: activeVersionRecord.publisher,\n enabled: appRecord.enabled,\n manifestSchemaVersion: activeVersionRecord.manifestSchemaVersion,\n components: activeVersionRecord.components,\n primaryPanelId: activeVersionRecord.primaryPanelId,\n updated: false,\n };\n }\n const installResult = await this.install(`${appId}@${resolution.version}`, {\n registryUrl,\n });\n return {\n ...installResult,\n previousVersion: appRecord.activeVersion,\n updated: true,\n };\n };\n\n uninstall = async (\n appId: string,\n purgeData: boolean,\n ): Promise<AppUninstallResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const removedVersions = Object.keys(appRecord.installedVersions).sort((left, right) =>\n left.localeCompare(right),\n );\n const stagedPaths: Array<{ originalPath: string; stagedPath: string }> = [];\n const stagePath = async (originalPath: string): Promise<void> => {\n if (!await this.pathExists(originalPath)) {\n return;\n }\n const stagedPath = `${originalPath}.uninstalling-${randomUUID()}`;\n await rename(originalPath, stagedPath);\n stagedPaths.push({ originalPath, stagedPath });\n };\n const restoreStagedPaths = async (): Promise<void> => {\n for (const entry of [...stagedPaths].reverse()) {\n if (await this.pathExists(entry.stagedPath)) {\n await rename(entry.stagedPath, entry.originalPath);\n }\n }\n };\n try {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n await stagePath(versionRecord.installDirectory);\n }\n if (purgeData) {\n await stagePath(appRecord.dataDirectory);\n }\n const removedRecord = await this.registryService.removeApp(appId);\n if (!removedRecord) {\n throw new Error(`卸载过程中应用记录已发生变化:${appId}`);\n }\n } catch (error) {\n try {\n await restoreStagedPaths();\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n `卸载 ${appId} 失败,且无法完全恢复已暂存文件。`,\n );\n }\n throw error;\n }\n await Promise.all(stagedPaths.map((entry) =>\n rm(entry.stagedPath, { recursive: true, force: true }),\n ));\n return {\n appId,\n removedVersions,\n dataRemoved: 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 info = async (appId: string): 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 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 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 registryUrl: versionRecord.registryUrl,\n bundleUrl: versionRecord.bundleUrl,\n sha256: versionRecord.sha256,\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\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 rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\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 const targetManifest = await this.manifestService.load(targetVersion.installDirectory);\n if (targetManifest.manifest.id !== appId || targetManifest.manifest.version !== version) {\n throw new Error(`回滚目标 ${appId}@${version} 校验失败。`);\n }\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 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 return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n documentGrantMap: {\n ...appRecord.grants,\n ...explicitDocumentGrantMap,\n },\n };\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 private copyToImmutableInstallDirectory = async (params: {\n appId: string;\n appVersion: string;\n extractedDirectory: string;\n installDirectory: string;\n }): Promise<void> => {\n const { appId, appVersion, extractedDirectory, installDirectory } = params;\n if (await this.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.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 rename(stagedInstallDirectory, installDirectory);\n } catch (error) {\n await rm(stagedInstallDirectory, { recursive: true, force: true });\n throw error;\n }\n };\n\n private 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.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.buildService.build({\n appDirectory: params.appDirectory,\n install: true,\n });\n };\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { access, cp, mkdir, readdir, rename, 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 {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.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 { AppInstallSourceService } 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\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\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 ) {\n this.installSourceService = new AppInstallSourceService(\n this.manifestService,\n this.remoteRegistryClient,\n );\n }\n\n install = async (\n appSource: string,\n options?: {\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n },\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 if (source.kind === \"registry\") {\n await onProgress?.(\"downloading\");\n }\n const bundlePath =\n source.kind === \"directory\"\n ? (await this.bundleService.packAppDirectory({\n appDirectory: source.appDirectory,\n outputPath: path.join(tempDirectory, \"app.napp\"),\n })).bundlePath\n : source.kind === \"bundle\"\n ? source.bundlePath\n : (\n await this.remoteRegistryClient.downloadBundle({\n resolution: source.registryResolution,\n targetDirectory: tempDirectory,\n })\n ).bundlePath;\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.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n const installDirectory = this.appHomeService.getInstallDirectory(\n manifestBundle.manifest.id,\n manifestBundle.manifest.version,\n );\n if (\n source.kind === \"registry\" &&\n manifestBundle.manifest.id !== source.registryResolution.appId\n ) {\n throw new Error(\n `bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${manifestBundle.manifest.id}`,\n );\n }\n await onProgress?.(\"installing\");\n const dataDirectory = this.appHomeService.getAppDataDirectory(manifestBundle.manifest.id);\n await this.copyToImmutableInstallDirectory({\n appId: manifestBundle.manifest.id,\n appVersion: manifestBundle.manifest.version,\n extractedDirectory,\n installDirectory,\n });\n await mkdir(dataDirectory, { recursive: true });\n await onProgress?.(\"finalizing\");\n let registryRecord;\n try {\n registryRecord = await this.registryService.upsertInstallation({\n appId: manifestBundle.manifest.id,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version: manifestBundle.manifest.version,\n installDirectory,\n dataDirectory,\n sourceKind: source.kind,\n distributionMode:\n 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 : {},\n registryUrl:\n source.kind === \"registry\" ? source.registryResolution.registryUrl : undefined,\n bundleUrl:\n source.kind === \"registry\" ? source.registryResolution.bundleUrl : undefined,\n sha256: source.kind === \"registry\" ? source.registryResolution.sha256 : undefined,\n publisher:\n source.kind === \"registry\" ? source.registryResolution.publisher : undefined,\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 });\n } catch (error) {\n await rm(installDirectory, { recursive: true, force: true });\n throw error;\n }\n const activeVersionRecord = registryRecord.installedVersions[registryRecord.activeVersion];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version: registryRecord.activeVersion,\n installDirectory,\n dataDirectory,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode:\n registryRecord.installedVersions[registryRecord.activeVersion]?.distributionMode,\n permissions:\n registryRecord.installedVersions[registryRecord.activeVersion]?.permissions ?? {},\n registryUrl:\n registryRecord.installedVersions[registryRecord.activeVersion]?.registryUrl,\n bundleUrl:\n registryRecord.installedVersions[registryRecord.activeVersion]?.bundleUrl,\n sha256: registryRecord.installedVersions[registryRecord.activeVersion]?.sha256,\n publisher:\n registryRecord.installedVersions[registryRecord.activeVersion]?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: activeVersionRecord?.manifestSchemaVersion ?? 1,\n components: activeVersionRecord?.components,\n primaryPanelId: activeVersionRecord?.primaryPanelId,\n };\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n },\n ): Promise<AppUpdateResult> => {\n const { onProgress, registryUrl: requestedRegistryUrl, version } = options ?? {};\n await onProgress?.(\"resolving\");\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];\n const registryUrl =\n requestedRegistryUrl ??\n activeVersionRecord?.registryUrl ??\n (await this.registryConfigService.getSnapshot()).currentUrl;\n const resolution = await this.remoteRegistryClient.resolve({\n appId,\n version,\n registryUrl,\n });\n if (resolution.version === appRecord.activeVersion) {\n if (!activeVersionRecord) {\n throw new Error(`已安装应用缺少激活版本:${appId}`);\n }\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n version: appRecord.activeVersion,\n previousVersion: appRecord.activeVersion,\n installDirectory: activeVersionRecord.installDirectory,\n dataDirectory: appRecord.dataDirectory,\n sourceKind: activeVersionRecord.sourceKind,\n sourceRef: activeVersionRecord.sourceRef,\n permissions: activeVersionRecord.permissions,\n registryUrl: activeVersionRecord.registryUrl,\n bundleUrl: activeVersionRecord.bundleUrl,\n sha256: activeVersionRecord.sha256,\n publisher: activeVersionRecord.publisher,\n enabled: appRecord.enabled,\n manifestSchemaVersion: activeVersionRecord.manifestSchemaVersion,\n components: activeVersionRecord.components,\n primaryPanelId: activeVersionRecord.primaryPanelId,\n updated: false,\n };\n }\n const installedTarget = appRecord.installedVersions[resolution.version];\n if (installedTarget) {\n await onProgress?.(\"verifying\");\n await onProgress?.(\"installing\");\n await this.rollback(appId, resolution.version);\n await onProgress?.(\"finalizing\");\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n version: resolution.version,\n previousVersion: appRecord.activeVersion,\n installDirectory: installedTarget.installDirectory,\n dataDirectory: appRecord.dataDirectory,\n sourceKind: installedTarget.sourceKind,\n distributionMode: installedTarget.distributionMode,\n sourceRef: installedTarget.sourceRef,\n permissions: installedTarget.permissions,\n registryUrl: installedTarget.registryUrl,\n bundleUrl: installedTarget.bundleUrl,\n sha256: installedTarget.sha256,\n publisher: installedTarget.publisher,\n enabled: appRecord.enabled,\n manifestSchemaVersion: installedTarget.manifestSchemaVersion,\n components: installedTarget.components,\n primaryPanelId: installedTarget.primaryPanelId,\n updated: true,\n };\n }\n const installResult = await this.install(`${appId}@${resolution.version}`, {\n registryUrl,\n onProgress,\n });\n return {\n ...installResult,\n previousVersion: appRecord.activeVersion,\n updated: true,\n };\n };\n\n uninstall = async (\n appId: string,\n purgeData: boolean,\n ): Promise<AppUninstallResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const removedVersions = Object.keys(appRecord.installedVersions).sort((left, right) =>\n left.localeCompare(right),\n );\n const stagedPaths: Array<{ originalPath: string; stagedPath: string }> = [];\n const stagePath = async (originalPath: string): Promise<void> => {\n if (!await this.pathExists(originalPath)) {\n return;\n }\n const stagedPath = `${originalPath}.uninstalling-${randomUUID()}`;\n await rename(originalPath, stagedPath);\n stagedPaths.push({ originalPath, stagedPath });\n };\n const restoreStagedPaths = async (): Promise<void> => {\n for (const entry of [...stagedPaths].reverse()) {\n if (await this.pathExists(entry.stagedPath)) {\n await rename(entry.stagedPath, entry.originalPath);\n }\n }\n };\n try {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n await stagePath(versionRecord.installDirectory);\n }\n if (purgeData) {\n await stagePath(appRecord.dataDirectory);\n }\n const removedRecord = await this.registryService.removeApp(appId);\n if (!removedRecord) {\n throw new Error(`卸载过程中应用记录已发生变化:${appId}`);\n }\n } catch (error) {\n try {\n await restoreStagedPaths();\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n `卸载 ${appId} 失败,且无法完全恢复已暂存文件。`,\n );\n }\n throw error;\n }\n await Promise.all(stagedPaths.map((entry) =>\n rm(entry.stagedPath, { recursive: true, force: true }),\n ));\n return {\n appId,\n removedVersions,\n dataRemoved: 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 const 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(record.dataDirectory)));\n\n const packageDirectories = await this.readDirectories(this.appHomeService.getPackagesDirectory());\n for (const appDirectory of packageDirectories) {\n await this.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);\n }\n await this.reconcileGeneratedSiblings(\n this.appHomeService.getDataDirectory(),\n referencedDataPaths,\n );\n };\n\n info = async (appId: string): 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 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 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 registryUrl: versionRecord.registryUrl,\n bundleUrl: versionRecord.bundleUrl,\n sha256: versionRecord.sha256,\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\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 rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\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 const targetManifest = await this.manifestService.load(targetVersion.installDirectory);\n if (targetManifest.manifest.id !== appId || targetManifest.manifest.version !== version) {\n throw new Error(`回滚目标 ${appId}@${version} 校验失败。`);\n }\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 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 return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n documentGrantMap: {\n ...appRecord.grants,\n ...explicitDocumentGrantMap,\n },\n };\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 private copyToImmutableInstallDirectory = async (params: {\n appId: string;\n appVersion: string;\n extractedDirectory: string;\n installDirectory: string;\n }): Promise<void> => {\n const { appId, appVersion, extractedDirectory, installDirectory } = params;\n if (await this.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.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 rename(stagedInstallDirectory, installDirectory);\n } catch (error) {\n await rm(stagedInstallDirectory, { recursive: true, force: true });\n throw error;\n }\n };\n\n private 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.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.buildService.build({\n appDirectory: params.appDirectory,\n install: true,\n });\n };\n\n private 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 const stagingMarker = \".staging-\";\n const uninstallingMarker = \".uninstalling-\";\n if (entryName.includes(stagingMarker)) {\n await rm(entry, { recursive: true, force: true });\n continue;\n }\n const markerIndex = entryName.indexOf(uninstallingMarker);\n if (markerIndex < 0) {\n continue;\n }\n const originalPath = path.join(parentDirectory, entryName.slice(0, markerIndex));\n if (referencedPaths.has(path.resolve(originalPath)) && !await this.pathExists(originalPath)) {\n await rename(entry, originalPath);\n } else {\n await rm(entry, { recursive: true, force: true });\n }\n }\n };\n\n private 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 pathExists = async (targetPath: string): Promise<boolean> => {\n try {\n await access(targetPath);\n return true;\n } catch {\n return false;\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,5 +1,5 @@
|
|
|
1
1
|
import { AppManifest } from "../types/app-manifest.types.js";
|
|
2
|
-
import { AppMarketplaceMetadata } from "../types/app-publish.types.js";
|
|
2
|
+
import { AppMarketplaceMetadata, AppMarketplaceVisuals } from "../types/app-publish.types.js";
|
|
3
3
|
|
|
4
4
|
//#region src/services/app-marketplace-metadata.service.d.ts
|
|
5
5
|
declare class AppMarketplaceMetadataService {
|
|
@@ -10,15 +10,19 @@ declare class AppMarketplaceMetadataService {
|
|
|
10
10
|
}) => Promise<AppMarketplaceMetadata>;
|
|
11
11
|
collectPublishFiles: (params: {
|
|
12
12
|
appDirectory: string;
|
|
13
|
+
iconPath?: string;
|
|
13
14
|
metadataPath?: string;
|
|
15
|
+
visuals?: AppMarketplaceVisuals;
|
|
14
16
|
}) => Promise<Array<{
|
|
15
17
|
path: string;
|
|
16
18
|
bytes: Buffer;
|
|
17
19
|
}>>;
|
|
18
20
|
private parseMetadata;
|
|
21
|
+
private parseVisuals;
|
|
19
22
|
private parsePublisher;
|
|
20
23
|
private readRequiredString;
|
|
21
24
|
private readOptionalString;
|
|
25
|
+
private readSafeRelativePath;
|
|
22
26
|
private readStringArray;
|
|
23
27
|
private readOptionalBoolean;
|
|
24
28
|
private readLocalizedTextMap;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-marketplace-metadata.service.d.ts","names":[],"sources":["../../src/services/app-marketplace-metadata.service.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"app-marketplace-metadata.service.d.ts","names":[],"sources":["../../src/services/app-marketplace-metadata.service.ts"],"mappings":";;;;cAYa,6BAAA;EACX,IAAA,GAAc,MAAA;IACZ,YAAA;IACA,QAAA,EAAU,WAAA;IACV,YAAA;EAAA,MACE,OAAA,CAAQ,sBAAA;EASZ,mBAAA,GAA6B,MAAA;IAC3B,YAAA;IACA,QAAA;IACA,YAAA;IACA,OAAA,GAAU,qBAAA;EAAA,MACR,OAAA,CAAQ,KAAA;IAAQ,IAAA;IAAc,KAAA,EAAO,MAAA;EAAA;EAAA,QAmCjC,aAAA;EAAA,QAgCA,YAAA;EAAA,QAmBA,cAAA;EAAA,QAiBA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAUA,oBAAA;EAAA,QAaA,eAAA;EAAA,QASA,mBAAA;EAAA,QAaA,oBAAA;AAAA"}
|