@nextclaw/app-runtime 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/package.js +1 -1
  3. package/dist/services/app-bundle.service.d.ts.map +1 -1
  4. package/dist/services/app-bundle.service.js +1 -1
  5. package/dist/services/app-bundle.service.js.map +1 -1
  6. package/dist/services/app-home.service.d.ts +1 -0
  7. package/dist/services/app-home.service.d.ts.map +1 -1
  8. package/dist/services/app-home.service.js +3 -0
  9. package/dist/services/app-home.service.js.map +1 -1
  10. package/dist/services/app-installation.service.d.ts +7 -1
  11. package/dist/services/app-installation.service.d.ts.map +1 -1
  12. package/dist/services/app-installation.service.js +84 -5
  13. package/dist/services/app-installation.service.js.map +1 -1
  14. package/dist/services/app-marketplace-metadata.service.d.ts +5 -1
  15. package/dist/services/app-marketplace-metadata.service.d.ts.map +1 -1
  16. package/dist/services/app-marketplace-metadata.service.js +44 -2
  17. package/dist/services/app-marketplace-metadata.service.js.map +1 -1
  18. package/dist/services/app-publish.service.d.ts.map +1 -1
  19. package/dist/services/app-publish.service.js +4 -1
  20. package/dist/services/app-publish.service.js.map +1 -1
  21. package/dist/services/app-registry.service.d.ts +2 -0
  22. package/dist/services/app-registry.service.d.ts.map +1 -1
  23. package/dist/services/app-registry.service.js +20 -2
  24. package/dist/services/app-registry.service.js.map +1 -1
  25. package/dist/types/app-installation.types.d.ts +3 -1
  26. package/dist/types/app-installation.types.d.ts.map +1 -1
  27. package/dist/types/app-publish.types.d.ts +7 -1
  28. package/dist/types/app-publish.types.d.ts.map +1 -1
  29. package/dist/types/app-publish.types.js.map +1 -1
  30. package/dist/types/app-registry.types.d.ts +3 -0
  31. package/dist/types/app-registry.types.d.ts.map +1 -1
  32. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -26,14 +26,14 @@ import { AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeToolStatus,
26
26
  import { AppBuildResult, AppBuildService } from "./services/app-build.service.js";
27
27
  import { AppRegistryConfigService } from "./services/app-registry-config.service.js";
28
28
  import { AppRemoteRegistryClientService } from "./services/app-remote-registry-client.service.js";
29
- import { AppActivationResult, AppInfoResult, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "./types/app-installation.types.js";
29
+ import { AppActivationResult, AppInfoResult, AppInstallProgressHandler, AppInstallProgressPhase, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "./types/app-installation.types.js";
30
30
  import { AppInstallationService } from "./services/app-installation.service.js";
31
31
  import { InfoCommand } from "./controllers/info.controller.js";
32
32
  import { InstallCommand } from "./controllers/install.controller.js";
33
33
  import { ListCommand } from "./controllers/list.controller.js";
34
34
  import { PackCommand } from "./controllers/pack.controller.js";
35
35
  import { PermissionsCommand } from "./controllers/permissions.controller.js";
36
- import { AppMarketplaceMetadata, AppPublishFile, AppPublishPayload, AppPublishResult, DEFAULT_APP_MARKETPLACE_API_BASE } from "./types/app-publish.types.js";
36
+ import { AppMarketplaceMetadata, AppMarketplaceVisuals, AppPublishFile, AppPublishPayload, AppPublishResult, DEFAULT_APP_MARKETPLACE_API_BASE } from "./types/app-publish.types.js";
37
37
  import { AppMarketplaceClientService } from "./services/app-marketplace-client.service.js";
38
38
  import { AppMarketplaceMetadataService } from "./services/app-marketplace-metadata.service.js";
39
39
  import { PlatformAuthStateService, PlatformPublishAuthState } from "./services/platform-auth-state.service.js";
@@ -48,4 +48,4 @@ import { ValidatePublishCommand } from "./controllers/validate-publish.controlle
48
48
  import { WasmtimeWasiHttpComponentHandle, WasmtimeWasiHttpComponentService, WasmtimeWasiHttpComponentStartOptions } from "./services/wasmtime-wasi-http-component.service.js";
49
49
  import { AppHostHandle, AppHostService, AppHostStartOptions } from "./services/app-host.service.js";
50
50
  import { UiServerService } from "./services/ui-server.service.js";
51
- export { AppActivationResult, AppBuildResult, AppBuildService, AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppBundleService, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDistributionMode, AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppGrantService, AppHomeService, AppHostHandle, AppHostService, AppHostStartOptions, AppInfoResult, AppInstallResult, AppInstallSourceKind, AppInstallationService, AppInstalledPermissionState, AppInstanceService, AppLaunchResolution, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppMarketplaceClientService, AppMarketplaceMetadata, AppMarketplaceMetadataService, AppPermissionSummary, AppPermissions, AppPermissionsService, AppPublishFile, AppPublishPayload, AppPublishResult, AppPublishService, AppPublishValidationResult, AppPublishValidationService, AppPublishValidationWarning, AppPublishValidationWarningCode, AppPublisher, AppRegistry, AppRegistryAppRecord, AppRegistryConfig, AppRegistryConfigService, AppRegistryConfigSnapshot, AppRegistryInstalledVersion, AppRegistryService, AppRemoteRegistryClientService, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, AppResolvedComponent, AppRollbackResult, AppRunResult, AppRuntimeCliService, AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeOptionsService, AppRuntimeToolStatus, AppRuntimeToolchainService, AppScaffoldFile, AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, AppUiManifest, AppUninstallResult, AppUpdateResult, AppWasiHttpComponentMainManifest, BuildCliOptions, CreateCliOptions, CreateCommand, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, GrantCliOptions, GrantCommand, HostBridgeServer, InfoCommand, InstallCliOptions, InstallCommand, InstalledAppListItem, JsonOnlyCliOptions, ListCommand, MainRunRequest, MainRunResult, MainRunnerService, PackCliOptions, PackCommand, PermissionsCommand, PlatformAuthStateService, PlatformPublishAuthState, PublishCliOptions, PublishCommand, RegistryCommand, ResolvedDocumentGrant, ResolvedPermissions, RevokeCliOptions, RevokeCommand, RuntimeCliOptions, UiServerService, UninstallCliOptions, UninstallCommand, UpdateCliOptions, UpdateCommand, ValidatePublishCommand, WasmDocumentSummaryInput, WasmMainRunnerService, WasmSidecarClientService, WasmtimeWasiHttpComponentHandle, WasmtimeWasiHttpComponentService, WasmtimeWasiHttpComponentStartOptions, isAppComponentManifestBundle, isAppStandaloneManifestBundle };
51
+ export { AppActivationResult, AppBuildResult, AppBuildService, AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppBundleService, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDistributionMode, AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppGrantService, AppHomeService, AppHostHandle, AppHostService, AppHostStartOptions, AppInfoResult, AppInstallProgressHandler, AppInstallProgressPhase, AppInstallResult, AppInstallSourceKind, AppInstallationService, AppInstalledPermissionState, AppInstanceService, AppLaunchResolution, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppMarketplaceClientService, AppMarketplaceMetadata, AppMarketplaceMetadataService, AppMarketplaceVisuals, AppPermissionSummary, AppPermissions, AppPermissionsService, AppPublishFile, AppPublishPayload, AppPublishResult, AppPublishService, AppPublishValidationResult, AppPublishValidationService, AppPublishValidationWarning, AppPublishValidationWarningCode, AppPublisher, AppRegistry, AppRegistryAppRecord, AppRegistryConfig, AppRegistryConfigService, AppRegistryConfigSnapshot, AppRegistryInstalledVersion, AppRegistryService, AppRemoteRegistryClientService, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, AppResolvedComponent, AppRollbackResult, AppRunResult, AppRuntimeCliService, AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeOptionsService, AppRuntimeToolStatus, AppRuntimeToolchainService, AppScaffoldFile, AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, AppUiManifest, AppUninstallResult, AppUpdateResult, AppWasiHttpComponentMainManifest, BuildCliOptions, CreateCliOptions, CreateCommand, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, GrantCliOptions, GrantCommand, HostBridgeServer, InfoCommand, InstallCliOptions, InstallCommand, InstalledAppListItem, JsonOnlyCliOptions, ListCommand, MainRunRequest, MainRunResult, MainRunnerService, PackCliOptions, PackCommand, PermissionsCommand, PlatformAuthStateService, PlatformPublishAuthState, PublishCliOptions, PublishCommand, RegistryCommand, ResolvedDocumentGrant, ResolvedPermissions, RevokeCliOptions, RevokeCommand, RuntimeCliOptions, UiServerService, UninstallCliOptions, UninstallCommand, UpdateCliOptions, UpdateCommand, ValidatePublishCommand, WasmDocumentSummaryInput, WasmMainRunnerService, WasmSidecarClientService, WasmtimeWasiHttpComponentHandle, WasmtimeWasiHttpComponentService, WasmtimeWasiHttpComponentStartOptions, isAppComponentManifestBundle, isAppStandaloneManifestBundle };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.9.14";
2
+ var version = "0.9.15";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"app-bundle.service.d.ts","names":[],"sources":["../../src/services/app-bundle.service.ts"],"mappings":";;;;cAyBa,gBAAA;EAAA,iBAEQ,eAAA;cAAA,eAAA,GAAiB,kBAAA;EAGpC,gBAAA,GAA0B,MAAA;IACxB,YAAA;IACA,UAAA;IACA,IAAA,GAAO,mBAAA;EAAA,MACL,OAAA,CAAQ,mBAAA;EAkDZ,aAAA,GAAuB,MAAA;IACrB,UAAA;IACA,eAAA;EAAA,MACE,OAAA,CAAQ,sBAAA;EAAA,QAQJ,oBAAA;EAAA,QAkCA,wBAAA;EAAA,QA+CA,mBAAA;EAAA,QAWA,uBAAA;EAAA,QAiBA,mBAAA;EAAA,QAuBA,kBAAA;EAAA,QAeA,YAAA;EAAA,QAYA,qBAAA;EAAA,QAgCA,2BAAA;EAAA,QAuBA,eAAA;EAAA,QAiBA,wBAAA;EAAA,QAYA,uBAAA;EAAA,QAOA,iBAAA;EAAA,QAiBA,gBAAA;EAAA,QAcA,uBAAA;EAAA,QA4BA,wBAAA;EAAA,QAcA,sBAAA;EAAA,QA4BA,oBAAA;EAAA,QAgBA,aAAA;EAAA,QAeA,cAAA;EAAA,QASA,aAAA;EAAA,QAGA,uBAAA;EAAA,QAGA,qBAAA;EAAA,QAaA,kBAAA;EAAA,QAIA,UAAA;AAAA"}
1
+ {"version":3,"file":"app-bundle.service.d.ts","names":[],"sources":["../../src/services/app-bundle.service.ts"],"mappings":";;;;cAyBa,gBAAA;EAAA,iBAEQ,eAAA;cAAA,eAAA,GAAiB,kBAAA;EAGpC,gBAAA,GAA0B,MAAA;IACxB,YAAA;IACA,UAAA;IACA,IAAA,GAAO,mBAAA;EAAA,MACL,OAAA,CAAQ,mBAAA;EAkDZ,aAAA,GAAuB,MAAA;IACrB,UAAA;IACA,eAAA;EAAA,MACE,OAAA,CAAQ,sBAAA;EAAA,QAQJ,oBAAA;EAAA,QAkCA,wBAAA;EAAA,QA+CA,mBAAA;EAAA,QAWA,uBAAA;EAAA,QAiBA,mBAAA;EAAA,QAuBA,kBAAA;EAAA,QAeA,YAAA;EAAA,QAYA,qBAAA;EAAA,QAgCA,2BAAA;EAAA,QAuBA,eAAA;EAAA,QAiBA,wBAAA;EAAA,QAaA,uBAAA;EAAA,QAOA,iBAAA;EAAA,QAiBA,gBAAA;EAAA,QAcA,uBAAA;EAAA,QA4BA,wBAAA;EAAA,QAcA,sBAAA;EAAA,QA4BA,oBAAA;EAAA,QAgBA,aAAA;EAAA,QAeA,cAAA;EAAA,QASA,aAAA;EAAA,QAGA,uBAAA;EAAA,QAGA,qBAAA;EAAA,QAaA,kBAAA;EAAA,QAIA,UAAA;AAAA"}
@@ -210,7 +210,7 @@ var AppBundleService = class {
210
210
  }
211
211
  };
212
212
  shouldExcludeRuntimePath = (relativePath) => {
213
- return relativePath.split("/").some((segment) => segment === "node_modules" || segment === ".git" || segment === "coverage" || segment === "tests" || segment === "__tests__" || segment === "fixtures") || /(?:^|\/)\.(?:DS_Store|eslintcache)$/.test(relativePath) || relativePath.endsWith(".map");
213
+ return relativePath.split("/").some((segment) => segment === "node_modules" || segment === ".git" || segment === "marketplace-assets" || segment === "coverage" || segment === "tests" || segment === "__tests__" || segment === "fixtures") || /(?:^|\/)\.(?:DS_Store|eslintcache)$/.test(relativePath) || relativePath.endsWith(".map");
214
214
  };
215
215
  shouldExcludeSourcePath = (relativePath) => {
216
216
  return this.shouldExcludeRuntimePath(relativePath) || relativePath === ".napp" || relativePath.startsWith(".napp/") || relativePath === "main/dist" || relativePath.startsWith("main/dist/") || relativePath === "main/generated" || relativePath.startsWith("main/generated/");
@@ -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":"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"}
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 === \"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 normalizeArchive = (\n archive: Record<string, Uint8Array>,\n ): Record<string, Uint8Array> => {\n const normalizedArchive: Record<string, Uint8Array> = {};\n for (const [entryName, bytes] of Object.entries(archive)) {\n const normalizedEntry = this.normalizeArchiveEntry(entryName);\n if (Object.hasOwn(normalizedArchive, normalizedEntry)) {\n throw new Error(`bundle 包含重复路径:${normalizedEntry}`);\n }\n normalizedArchive[normalizedEntry] = bytes;\n }\n return normalizedArchive;\n };\n\n private readMetadataFromArchive = (\n archive: Record<string, Uint8Array>,\n ): AppBundleMetadata => {\n const raw = this.readJsonArchiveEntry<Partial<AppBundleMetadata>>(\n archive,\n BUNDLE_METADATA_PATH,\n \"bundle metadata\",\n );\n const distributionMode = raw.distributionMode === undefined\n ? \"bundle\"\n : raw.distributionMode;\n if (\n raw.bundleFormatVersion !== 1 ||\n (distributionMode !== \"bundle\" && distributionMode !== \"source\") ||\n typeof raw.appId !== \"string\" || !raw.appId ||\n typeof raw.name !== \"string\" || !raw.name ||\n typeof raw.version !== \"string\" || !raw.version ||\n raw.entryManifest !== \"manifest.json\" ||\n raw.checksumsFile !== CHECKSUMS_PATH\n ) {\n throw new Error(\"bundle metadata 无效。\");\n }\n return {\n ...raw,\n distributionMode,\n } as AppBundleMetadata;\n };\n\n private readChecksumsFromArchive = (\n archive: Record<string, Uint8Array>,\n ): AppBundleChecksums => {\n const raw = this.readJsonArchiveEntry<Partial<AppBundleChecksums>>(\n archive,\n CHECKSUMS_PATH,\n \"bundle checksums\",\n );\n if (raw.algorithm !== \"sha256\" || !raw.files || typeof raw.files !== \"object\") {\n throw new Error(\"bundle checksums 无效。\");\n }\n return raw as AppBundleChecksums;\n };\n\n private verifyArchiveChecksums = (\n archive: Record<string, Uint8Array>,\n checksums: AppBundleChecksums,\n ): void => {\n const actualPaths = Object.keys(archive)\n .filter((entry) => entry !== CHECKSUMS_PATH)\n .sort((left, right) => left.localeCompare(right));\n const checksumPaths = Object.keys(checksums.files)\n .map((entry) => this.normalizeArchiveEntry(entry))\n .sort((left, right) => left.localeCompare(right));\n if (\n actualPaths.length !== checksumPaths.length ||\n actualPaths.some((entry, index) => entry !== checksumPaths[index])\n ) {\n throw new Error(\"bundle checksums 必须精确覆盖所有 artifact 文件。\");\n }\n for (const relativePath of actualPaths) {\n const expectedHash = checksums.files[relativePath];\n if (!expectedHash || !/^[a-f0-9]{64}$/.test(expectedHash)) {\n throw new Error(`bundle checksum 格式无效:${relativePath}`);\n }\n const actualHash = this.computeSha256(archive[relativePath] as Uint8Array);\n if (actualHash !== expectedHash) {\n throw new Error(`bundle checksum 校验失败:${relativePath}`);\n }\n }\n };\n\n private readJsonArchiveEntry = <T>(\n archive: Record<string, Uint8Array>,\n entryPath: string,\n label: string,\n ): T => {\n const bytes = archive[entryPath];\n if (!bytes) {\n throw new Error(`bundle 缺少 ${label}。`);\n }\n try {\n return JSON.parse(Buffer.from(bytes).toString(\"utf-8\")) as T;\n } catch (error) {\n throw new Error(`无法读取 ${label}:${error instanceof Error ? error.message : String(error)}`);\n }\n };\n\n private buildMetadata = (\n appId: string,\n name: string,\n version: string,\n distributionMode: AppDistributionMode,\n ): AppBundleMetadata => ({\n bundleFormatVersion: 1,\n distributionMode,\n appId,\n name,\n version,\n entryManifest: \"manifest.json\",\n checksumsFile: CHECKSUMS_PATH,\n });\n\n private buildChecksums = (files: Record<string, Uint8Array>): AppBundleChecksums => ({\n algorithm: \"sha256\",\n files: Object.fromEntries(\n Object.entries(files)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([relativePath, fileBytes]) => [relativePath, this.computeSha256(fileBytes)]),\n ),\n });\n\n private computeSha256 = (fileBytes: Uint8Array): string =>\n createHash(\"sha256\").update(Buffer.from(fileBytes)).digest(\"hex\");\n\n private normalizeBundleFileName = (appId: string): string =>\n appId.replace(/[^a-zA-Z0-9._-]+/g, \"-\");\n\n private normalizeArchiveEntry = (entryName: string): string => {\n const normalized = entryName.replace(/\\\\/g, \"/\");\n const segments = normalized.split(\"/\");\n if (\n !normalized || normalized.startsWith(\"/\") || normalized.includes(\"\\0\") ||\n /^[A-Za-z]:/.test(normalized) ||\n segments.some((segment) => !segment || segment === \".\" || segment === \"..\")\n ) {\n throw new Error(`bundle 内包含非法路径:${entryName}`);\n }\n return segments.join(\"/\");\n };\n\n private isMissingFileError = (error: unknown): boolean =>\n typeof error === \"object\" && error !== null &&\n \"code\" in error && (error as { code?: unknown }).code === \"ENOENT\";\n\n private pathExists = async (targetPath: string): Promise<boolean> => {\n try {\n await access(targetPath);\n return true;\n } catch {\n return false;\n }\n };\n}\n\nconst SOURCE_WASM_PLACEHOLDER_BYTES = Uint8Array.from(\n Buffer.from(\n \"AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL\",\n \"base64\",\n ),\n);\n"],"mappings":"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"}
@@ -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":";;;;;;;;;;;cA4Ba,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;EAAA,MAED,OAAA,CAAQ,gBAAA;EA4HX,MAAA,GACE,KAAA,UACA,OAAA;IACE,OAAA;IACA,WAAA;EAAA,MAED,OAAA,CAAQ,eAAA;EAkDX,SAAA,GACE,KAAA,UACA,SAAA,cACC,OAAA,CAAQ,kBAAA;EAwDX,IAAA,QAAiB,OAAA,CAAQ,oBAAA;EAkBzB,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,UAAA;AAAA"}
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 source = await this.installSourceService.resolve(appSource, options?.registryUrl);
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 = options?.registryUrl ?? activeVersionRecord?.registryUrl ?? (await this.registryConfigService.getSnapshot()).currentUrl;
129
+ const registryUrl = requestedRegistryUrl ?? activeVersionRecord?.registryUrl ?? (await this.registryConfigService.getSnapshot()).currentUrl;
122
130
  const resolution = await this.remoteRegistryClient.resolve({
123
131
  appId,
124
- version: options?.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}`, { registryUrl }),
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":";;;;cAMa,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,YAAA;EAAA,MACE,OAAA,CAAQ,KAAA;IAAQ,IAAA;IAAc,KAAA,EAAO,MAAA;EAAA;EAAA,QAoBjC,aAAA;EAAA,QA+BA,cAAA;EAAA,QAiBA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAUA,eAAA;EAAA,QASA,mBAAA;EAAA,QAaA,oBAAA;AAAA"}
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"}
@@ -2,6 +2,14 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { existsSync } from "node:fs";
4
4
  //#region src/services/app-marketplace-metadata.service.ts
5
+ const COVER_EXTENSIONS = new Set([
6
+ ".avif",
7
+ ".jpg",
8
+ ".jpeg",
9
+ ".png",
10
+ ".webp"
11
+ ]);
12
+ const MAX_COVER_BYTES = 512 * 1024;
5
13
  var AppMarketplaceMetadataService = class {
6
14
  load = async (params) => {
7
15
  const { appDirectory, manifest, metadataPath: customMetadataPath } = params;
@@ -10,9 +18,10 @@ var AppMarketplaceMetadataService = class {
10
18
  return this.parseMetadata(raw, manifest);
11
19
  };
12
20
  collectPublishFiles = async (params) => {
21
+ const { iconPath, metadataPath: customMetadataPath, visuals } = params;
13
22
  const appDirectory = path.resolve(params.appDirectory);
14
23
  const publishFiles = [];
15
- const metadataPath = params.metadataPath ? path.resolve(params.metadataPath) : path.join(appDirectory, "marketplace.json");
24
+ const metadataPath = customMetadataPath ? path.resolve(customMetadataPath) : path.join(appDirectory, "marketplace.json");
16
25
  publishFiles.push({
17
26
  path: "marketplace.json",
18
27
  bytes: Buffer.from(await readFile(metadataPath))
@@ -22,6 +31,19 @@ var AppMarketplaceMetadataService = class {
22
31
  path: "README.md",
23
32
  bytes: Buffer.from(await readFile(readmePath))
24
33
  });
34
+ if (iconPath) publishFiles.push({
35
+ path: iconPath,
36
+ bytes: Buffer.from(await readFile(path.join(appDirectory, iconPath)))
37
+ });
38
+ if (visuals) {
39
+ const coverPath = path.join(appDirectory, visuals.cover);
40
+ const bytes = Buffer.from(await readFile(coverPath));
41
+ if (bytes.byteLength > MAX_COVER_BYTES) throw new Error(`visuals.cover 不能超过 ${MAX_COVER_BYTES} bytes。`);
42
+ publishFiles.push({
43
+ path: visuals.cover,
44
+ bytes
45
+ });
46
+ }
25
47
  return publishFiles;
26
48
  };
27
49
  parseMetadata = (rawMetadata, manifest) => {
@@ -43,7 +65,21 @@ var AppMarketplaceMetadataService = class {
43
65
  sourceRepo: this.readOptionalString(candidate.sourceRepo, "sourceRepo"),
44
66
  homepage: this.readOptionalString(candidate.homepage, "homepage"),
45
67
  featured: this.readOptionalBoolean(candidate.featured, "featured") ?? false,
46
- publisher
68
+ publisher,
69
+ visuals: this.parseVisuals(candidate.visuals)
70
+ };
71
+ };
72
+ parseVisuals = (rawVisuals) => {
73
+ if (rawVisuals === void 0) return;
74
+ if (!rawVisuals || typeof rawVisuals !== "object" || Array.isArray(rawVisuals)) throw new Error("visuals 必须是对象。");
75
+ const candidate = rawVisuals;
76
+ const cover = this.readSafeRelativePath(candidate.cover, "visuals.cover");
77
+ if (!COVER_EXTENSIONS.has(path.extname(cover).toLowerCase())) throw new Error("visuals.cover 必须是 AVIF、JPEG、PNG 或 WebP 图片。");
78
+ const accentColor = this.readRequiredString(candidate.accentColor, "visuals.accentColor");
79
+ if (!/^#[0-9a-f]{6}$/i.test(accentColor)) throw new Error("visuals.accentColor 必须是六位十六进制颜色。");
80
+ return {
81
+ cover,
82
+ accentColor: accentColor.toUpperCase()
47
83
  };
48
84
  };
49
85
  parsePublisher = (rawPublisher) => {
@@ -64,6 +100,12 @@ var AppMarketplaceMetadataService = class {
64
100
  if (value === void 0) return;
65
101
  return this.readRequiredString(value, fieldName);
66
102
  };
103
+ readSafeRelativePath = (value, fieldName) => {
104
+ const relativePath = this.readRequiredString(value, fieldName).replace(/\\/g, "/");
105
+ const segments = relativePath.split("/");
106
+ if (relativePath.startsWith("/") || /^[A-Za-z]:/.test(relativePath) || segments.some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`${fieldName} 必须是安全的相对路径。`);
107
+ return segments.join("/");
108
+ };
67
109
  readStringArray = (value, fieldName) => {
68
110
  if (!Array.isArray(value) || value.length === 0) throw new Error(`${fieldName} 必须是非空字符串数组。`);
69
111
  return value.map((item, index) => this.readRequiredString(item, `${fieldName}[${index}]`));
@@ -1 +1 @@
1
- {"version":3,"file":"app-marketplace-metadata.service.js","names":[],"sources":["../../src/services/app-marketplace-metadata.service.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppManifest } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppMarketplaceMetadata } from \"#app-runtime/types/app-publish.types.js\";\n\nexport class AppMarketplaceMetadataService {\n load = async (params: {\n appDirectory: string;\n manifest: AppManifest;\n metadataPath?: string;\n }): Promise<AppMarketplaceMetadata> => {\n const { appDirectory, manifest, metadataPath: customMetadataPath } = params;\n const metadataPath = customMetadataPath\n ? path.resolve(customMetadataPath)\n : path.join(path.resolve(appDirectory), \"marketplace.json\");\n const raw = JSON.parse(await readFile(metadataPath, \"utf-8\")) as unknown;\n return this.parseMetadata(raw, manifest);\n };\n\n collectPublishFiles = async (params: {\n appDirectory: string;\n metadataPath?: string;\n }): Promise<Array<{ path: string; bytes: Buffer }>> => {\n const appDirectory = path.resolve(params.appDirectory);\n const publishFiles: Array<{ path: string; bytes: Buffer }> = [];\n const metadataPath = params.metadataPath\n ? path.resolve(params.metadataPath)\n : path.join(appDirectory, \"marketplace.json\");\n publishFiles.push({\n path: \"marketplace.json\",\n bytes: Buffer.from(await readFile(metadataPath)),\n });\n const readmePath = path.join(appDirectory, \"README.md\");\n if (existsSync(readmePath)) {\n publishFiles.push({\n path: \"README.md\",\n bytes: Buffer.from(await readFile(readmePath)),\n });\n }\n return publishFiles;\n };\n\n private parseMetadata = (\n rawMetadata: unknown,\n manifest: AppManifest,\n ): AppMarketplaceMetadata => {\n if (!rawMetadata || typeof rawMetadata !== \"object\" || Array.isArray(rawMetadata)) {\n throw new Error(\"marketplace.json 必须是对象。\");\n }\n const candidate = rawMetadata as Record<string, unknown>;\n const slug = this.readRequiredString(candidate.slug, \"slug\");\n const summary = this.readRequiredString(candidate.summary, \"summary\");\n const description = this.readOptionalString(candidate.description, \"description\");\n const author =\n this.readOptionalString(candidate.author, \"author\") ?? manifest.name;\n const publisher = this.parsePublisher(candidate.publisher);\n return {\n slug,\n summary,\n summaryI18n: this.readLocalizedTextMap(candidate.summaryI18n, \"summaryI18n\", summary),\n description,\n descriptionI18n: description\n ? this.readLocalizedTextMap(candidate.descriptionI18n, \"descriptionI18n\", description)\n : undefined,\n author,\n tags: this.readStringArray(candidate.tags, \"tags\"),\n sourceRepo: this.readOptionalString(candidate.sourceRepo, \"sourceRepo\"),\n homepage: this.readOptionalString(candidate.homepage, \"homepage\"),\n featured: this.readOptionalBoolean(candidate.featured, \"featured\") ?? false,\n publisher,\n };\n };\n\n private parsePublisher = (\n rawPublisher: unknown,\n ): AppMarketplaceMetadata[\"publisher\"] => {\n if (rawPublisher === undefined) {\n return undefined;\n }\n if (!rawPublisher || typeof rawPublisher !== \"object\" || Array.isArray(rawPublisher)) {\n throw new Error(\"publisher 必须是对象。\");\n }\n const candidate = rawPublisher as Record<string, unknown>;\n return {\n id: this.readRequiredString(candidate.id, \"publisher.id\"),\n name: this.readRequiredString(candidate.name, \"publisher.name\"),\n url: this.readOptionalString(candidate.url, \"publisher.url\"),\n };\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (\n value: unknown,\n fieldName: string,\n ): string | undefined => {\n if (value === undefined) {\n return undefined;\n }\n return this.readRequiredString(value, fieldName);\n };\n\n private readStringArray = (value: unknown, fieldName: string): string[] => {\n if (!Array.isArray(value) || value.length === 0) {\n throw new Error(`${fieldName} 必须是非空字符串数组。`);\n }\n return value.map((item, index) =>\n this.readRequiredString(item, `${fieldName}[${index}]`),\n );\n };\n\n private readOptionalBoolean = (\n value: unknown,\n fieldName: string,\n ): boolean | undefined => {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n\n private readLocalizedTextMap = (\n value: unknown,\n fieldName: string,\n fallbackEn: string,\n ): Record<string, string> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n const candidate = value as Record<string, unknown>;\n const normalized = Object.fromEntries(\n Object.entries(candidate).map(([locale, localeValue]) => [\n locale,\n this.readRequiredString(localeValue, `${fieldName}.${locale}`),\n ]),\n );\n if (!normalized.en) {\n normalized.en = fallbackEn;\n }\n return normalized;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-marketplace-metadata.service.js","names":[],"sources":["../../src/services/app-marketplace-metadata.service.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppManifest } from \"#app-runtime/types/app-manifest.types.js\";\nimport type {\n AppMarketplaceMetadata,\n AppMarketplaceVisuals,\n} from \"#app-runtime/types/app-publish.types.js\";\n\nconst COVER_EXTENSIONS = new Set([\".avif\", \".jpg\", \".jpeg\", \".png\", \".webp\"]);\nconst MAX_COVER_BYTES = 512 * 1024;\n\nexport class AppMarketplaceMetadataService {\n load = async (params: {\n appDirectory: string;\n manifest: AppManifest;\n metadataPath?: string;\n }): Promise<AppMarketplaceMetadata> => {\n const { appDirectory, manifest, metadataPath: customMetadataPath } = params;\n const metadataPath = customMetadataPath\n ? path.resolve(customMetadataPath)\n : path.join(path.resolve(appDirectory), \"marketplace.json\");\n const raw = JSON.parse(await readFile(metadataPath, \"utf-8\")) as unknown;\n return this.parseMetadata(raw, manifest);\n };\n\n collectPublishFiles = async (params: {\n appDirectory: string;\n iconPath?: string;\n metadataPath?: string;\n visuals?: AppMarketplaceVisuals;\n }): Promise<Array<{ path: string; bytes: Buffer }>> => {\n const { iconPath, metadataPath: customMetadataPath, visuals } = params;\n const appDirectory = path.resolve(params.appDirectory);\n const publishFiles: Array<{ path: string; bytes: Buffer }> = [];\n const metadataPath = customMetadataPath\n ? path.resolve(customMetadataPath)\n : path.join(appDirectory, \"marketplace.json\");\n publishFiles.push({\n path: \"marketplace.json\",\n bytes: Buffer.from(await readFile(metadataPath)),\n });\n const readmePath = path.join(appDirectory, \"README.md\");\n if (existsSync(readmePath)) {\n publishFiles.push({\n path: \"README.md\",\n bytes: Buffer.from(await readFile(readmePath)),\n });\n }\n if (iconPath) {\n publishFiles.push({\n path: iconPath,\n bytes: Buffer.from(await readFile(path.join(appDirectory, iconPath))),\n });\n }\n if (visuals) {\n const coverPath = path.join(appDirectory, visuals.cover);\n const bytes = Buffer.from(await readFile(coverPath));\n if (bytes.byteLength > MAX_COVER_BYTES) {\n throw new Error(`visuals.cover 不能超过 ${MAX_COVER_BYTES} bytes。`);\n }\n publishFiles.push({ path: visuals.cover, bytes });\n }\n return publishFiles;\n };\n\n private parseMetadata = (\n rawMetadata: unknown,\n manifest: AppManifest,\n ): AppMarketplaceMetadata => {\n if (!rawMetadata || typeof rawMetadata !== \"object\" || Array.isArray(rawMetadata)) {\n throw new Error(\"marketplace.json 必须是对象。\");\n }\n const candidate = rawMetadata as Record<string, unknown>;\n const slug = this.readRequiredString(candidate.slug, \"slug\");\n const summary = this.readRequiredString(candidate.summary, \"summary\");\n const description = this.readOptionalString(candidate.description, \"description\");\n const author =\n this.readOptionalString(candidate.author, \"author\") ?? manifest.name;\n const publisher = this.parsePublisher(candidate.publisher);\n return {\n slug,\n summary,\n summaryI18n: this.readLocalizedTextMap(candidate.summaryI18n, \"summaryI18n\", summary),\n description,\n descriptionI18n: description\n ? this.readLocalizedTextMap(candidate.descriptionI18n, \"descriptionI18n\", description)\n : undefined,\n author,\n tags: this.readStringArray(candidate.tags, \"tags\"),\n sourceRepo: this.readOptionalString(candidate.sourceRepo, \"sourceRepo\"),\n homepage: this.readOptionalString(candidate.homepage, \"homepage\"),\n featured: this.readOptionalBoolean(candidate.featured, \"featured\") ?? false,\n publisher,\n visuals: this.parseVisuals(candidate.visuals),\n };\n };\n\n private parseVisuals = (rawVisuals: unknown): AppMarketplaceVisuals | undefined => {\n if (rawVisuals === undefined) {\n return undefined;\n }\n if (!rawVisuals || typeof rawVisuals !== \"object\" || Array.isArray(rawVisuals)) {\n throw new Error(\"visuals 必须是对象。\");\n }\n const candidate = rawVisuals as Record<string, unknown>;\n const cover = this.readSafeRelativePath(candidate.cover, \"visuals.cover\");\n if (!COVER_EXTENSIONS.has(path.extname(cover).toLowerCase())) {\n throw new Error(\"visuals.cover 必须是 AVIF、JPEG、PNG 或 WebP 图片。\");\n }\n const accentColor = this.readRequiredString(candidate.accentColor, \"visuals.accentColor\");\n if (!/^#[0-9a-f]{6}$/i.test(accentColor)) {\n throw new Error(\"visuals.accentColor 必须是六位十六进制颜色。\");\n }\n return { cover, accentColor: accentColor.toUpperCase() };\n };\n\n private parsePublisher = (\n rawPublisher: unknown,\n ): AppMarketplaceMetadata[\"publisher\"] => {\n if (rawPublisher === undefined) {\n return undefined;\n }\n if (!rawPublisher || typeof rawPublisher !== \"object\" || Array.isArray(rawPublisher)) {\n throw new Error(\"publisher 必须是对象。\");\n }\n const candidate = rawPublisher as Record<string, unknown>;\n return {\n id: this.readRequiredString(candidate.id, \"publisher.id\"),\n name: this.readRequiredString(candidate.name, \"publisher.name\"),\n url: this.readOptionalString(candidate.url, \"publisher.url\"),\n };\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (\n value: unknown,\n fieldName: string,\n ): string | undefined => {\n if (value === undefined) {\n return undefined;\n }\n return this.readRequiredString(value, fieldName);\n };\n\n private readSafeRelativePath = (value: unknown, fieldName: string): string => {\n const relativePath = this.readRequiredString(value, fieldName).replace(/\\\\/g, \"/\");\n const segments = relativePath.split(\"/\");\n if (\n relativePath.startsWith(\"/\") ||\n /^[A-Za-z]:/.test(relativePath) ||\n segments.some((segment) => !segment || segment === \".\" || segment === \"..\")\n ) {\n throw new Error(`${fieldName} 必须是安全的相对路径。`);\n }\n return segments.join(\"/\");\n };\n\n private readStringArray = (value: unknown, fieldName: string): string[] => {\n if (!Array.isArray(value) || value.length === 0) {\n throw new Error(`${fieldName} 必须是非空字符串数组。`);\n }\n return value.map((item, index) =>\n this.readRequiredString(item, `${fieldName}[${index}]`),\n );\n };\n\n private readOptionalBoolean = (\n value: unknown,\n fieldName: string,\n ): boolean | undefined => {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n\n private readLocalizedTextMap = (\n value: unknown,\n fieldName: string,\n fallbackEn: string,\n ): Record<string, string> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n const candidate = value as Record<string, unknown>;\n const normalized = Object.fromEntries(\n Object.entries(candidate).map(([locale, localeValue]) => [\n locale,\n this.readRequiredString(localeValue, `${fieldName}.${locale}`),\n ]),\n );\n if (!normalized.en) {\n normalized.en = fallbackEn;\n }\n return normalized;\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.service.d.ts","names":[],"sources":["../../src/services/app-publish.service.ts"],"mappings":";;;;;;;;;cAkBa,iBAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,iBAAA;EAAA,iBACA,gBAAA;cAJA,eAAA,GAAiB,kBAAA,EACjB,aAAA,GAAe,gBAAA,EACf,eAAA,GAAiB,6BAAA,EACjB,iBAAA,GAAmB,2BAAA,EACnB,gBAAA,GAAkB,wBAAA;EAGrC,OAAA,GAAiB,MAAA;IACf,YAAA;IACA,YAAA;IACA,UAAA;IACA,KAAA;IACA,IAAA,GAAO,mBAAA;EAAA,MACL,OAAA,CAAQ,gBAAA;EAAA,QAoEJ,mBAAA;EAAA,QA6CA,wBAAA;EAAA,QA6CA,sBAAA;EAAA,QAOA,kBAAA;EAAA,QAkBA,sBAAA;AAAA"}
1
+ {"version":3,"file":"app-publish.service.d.ts","names":[],"sources":["../../src/services/app-publish.service.ts"],"mappings":";;;;;;;;;cAkBa,iBAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,iBAAA;EAAA,iBACA,gBAAA;cAJA,eAAA,GAAiB,kBAAA,EACjB,aAAA,GAAe,gBAAA,EACf,eAAA,GAAiB,6BAAA,EACjB,iBAAA,GAAmB,2BAAA,EACnB,gBAAA,GAAkB,wBAAA;EAGrC,OAAA,GAAiB,MAAA;IACf,YAAA;IACA,YAAA;IACA,UAAA;IACA,KAAA;IACA,IAAA,GAAO,mBAAA;EAAA,MACL,OAAA,CAAQ,gBAAA;EAAA,QAuEJ,mBAAA;EAAA,QA6CA,wBAAA;EAAA,QA6CA,sBAAA;EAAA,QAOA,kBAAA;EAAA,QAkBA,sBAAA;AAAA"}
@@ -39,7 +39,9 @@ var AppPublishService = class {
39
39
  const bundleSha256 = createHash("sha256").update(bundleBytes).digest("hex");
40
40
  const publishFiles = await this.metadataService.collectPublishFiles({
41
41
  appDirectory,
42
- metadataPath
42
+ iconPath: manifestBundle.manifest.icon,
43
+ metadataPath,
44
+ visuals: metadata.visuals
43
45
  });
44
46
  const payload = {
45
47
  slug: metadata.slug,
@@ -56,6 +58,7 @@ var AppPublishService = class {
56
58
  homepage: metadata.homepage,
57
59
  featured: metadata.featured ?? false,
58
60
  publisher: actor.publisher,
61
+ visuals: metadata.visuals,
59
62
  distributionMode,
60
63
  manifest: manifestBundle.manifest,
61
64
  permissions: manifestBundle.manifest.schemaVersion === 1 ? manifestBundle.manifest.permissions ?? {} : {},
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.service.js","names":[],"sources":["../../src/services/app-publish.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppMarketplaceClientService } from \"./app-marketplace-client.service.js\";\nimport { AppMarketplaceMetadataService } from \"./app-marketplace-metadata.service.js\";\nimport type { AppPublishPayload, AppPublishResult } from \"#app-runtime/types/app-publish.types.js\";\nimport { PlatformAuthStateService } from \"./platform-auth-state.service.js\";\n\nconst DEFAULT_PLATFORM_API_BASE = \"https://ai-gateway-api.nextclaw.io\";\n\ntype ResolvedPublishActor = {\n token: string;\n publisher: NonNullable<AppPublishPayload[\"publisher\"]>;\n};\n\nexport class AppPublishService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly metadataService: AppMarketplaceMetadataService = new AppMarketplaceMetadataService(),\n private readonly marketplaceClient: AppMarketplaceClientService = new AppMarketplaceClientService(),\n private readonly authStateService: PlatformAuthStateService = new PlatformAuthStateService(),\n ) {}\n\n publish = async (params: {\n appDirectory: string;\n metadataPath?: string;\n apiBaseUrl?: string;\n token?: string;\n mode?: AppDistributionMode;\n }): Promise<AppPublishResult> => {\n const { appDirectory: inputAppDirectory, metadataPath, apiBaseUrl, token } = params;\n const appDirectory = path.resolve(inputAppDirectory);\n const manifestBundle = await this.manifestService.load(appDirectory);\n const distributionMode = params.mode ??\n (manifestBundle.manifest.schemaVersion === 2 ? \"bundle\" : \"source\");\n const metadata = await this.metadataService.load({\n appDirectory,\n manifest: manifestBundle.manifest,\n metadataPath,\n });\n const actor = await this.resolvePublishActor({\n apiBaseUrl,\n explicitToken: token,\n appId: manifestBundle.manifest.id,\n });\n const bundle = await this.bundleService.packAppDirectory({\n appDirectory,\n mode: distributionMode,\n });\n const bundleBytes = Buffer.from(await readFile(bundle.bundlePath));\n const bundleSha256 = createHash(\"sha256\").update(bundleBytes).digest(\"hex\");\n const publishFiles = await this.metadataService.collectPublishFiles({\n appDirectory,\n metadataPath,\n });\n const payload: AppPublishPayload = {\n slug: metadata.slug,\n appId: manifestBundle.manifest.id,\n name: manifestBundle.manifest.name,\n version: manifestBundle.manifest.version,\n summary: metadata.summary,\n summaryI18n: metadata.summaryI18n,\n description: metadata.description ?? manifestBundle.manifest.description,\n descriptionI18n: metadata.descriptionI18n,\n author: metadata.author,\n tags: metadata.tags,\n sourceRepo: metadata.sourceRepo,\n homepage: metadata.homepage,\n featured: metadata.featured ?? false,\n publisher: actor.publisher,\n distributionMode,\n manifest: manifestBundle.manifest,\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : {},\n bundleBase64: bundleBytes.toString(\"base64\"),\n bundleSha256,\n files: publishFiles.map((file) => ({\n path: file.path,\n contentBase64: file.bytes.toString(\"base64\"),\n })),\n };\n const result = await this.marketplaceClient.publish({\n payload,\n apiBaseUrl,\n token: actor.token,\n });\n return {\n ...result,\n distribution: {\n path: bundle.bundlePath,\n sha256: bundleSha256,\n mode: distributionMode,\n },\n };\n };\n\n private resolvePublishActor = async (params: {\n apiBaseUrl?: string;\n explicitToken?: string;\n appId: string;\n }): Promise<ResolvedPublishActor> => {\n const explicitToken = params.explicitToken?.trim();\n const envAdminToken = process.env.NEXTCLAW_MARKETPLACE_ADMIN_TOKEN?.trim();\n if (explicitToken) {\n const token = explicitToken;\n if (!token) {\n throw new Error(\"缺少 publish token。\");\n }\n const me = await this.fetchCurrentPlatformUser({\n token,\n platformApiBase: this.resolvePlatformApiBase(),\n });\n return {\n token,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n const authState = this.authStateService.readCurrentAuthState();\n const platformToken = authState.token?.trim();\n if (platformToken) {\n const me = await this.fetchCurrentPlatformUser({\n token: platformToken,\n platformApiBase: this.resolvePlatformApiBase(authState.apiBaseUrl),\n });\n return {\n token: platformToken,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n if (envAdminToken) {\n return {\n token: envAdminToken,\n publisher: this.buildOfficialPublisher(),\n };\n }\n\n throw new Error(\"发布需要 NextClaw 平台登录态。请先运行 nextclaw login,或传入 --token。\");\n };\n\n private fetchCurrentPlatformUser = async (params: {\n token: string;\n platformApiBase: string;\n }): Promise<{\n id: string;\n username: string | null;\n role: \"admin\" | \"user\";\n }> => {\n const response = await fetch(`${params.platformApiBase}/platform/auth/me`, {\n headers: {\n authorization: `Bearer ${params.token}`,\n accept: \"application/json\",\n },\n });\n const payload = await response.json().catch(() => null);\n if (!response.ok) {\n const message =\n typeof payload === \"object\" &&\n payload &&\n \"error\" in payload &&\n typeof (payload as { error?: { message?: unknown } }).error?.message === \"string\"\n ? (payload as { error: { message: string } }).error.message\n : `${response.status} ${response.statusText}`;\n throw new Error(`读取 NextClaw 登录态失败:${message}`);\n }\n const user = typeof payload === \"object\" &&\n payload &&\n \"data\" in payload &&\n typeof (payload as { data?: { user?: unknown } }).data?.user === \"object\" &&\n (payload as { data: { user: Record<string, unknown> } }).data.user\n ? (payload as { data: { user: Record<string, unknown> } }).data.user\n : null;\n const id = typeof user?.id === \"string\" ? user.id.trim() : \"\";\n const username = typeof user?.username === \"string\" ? user.username.trim() : \"\";\n const role = user?.role === \"admin\" ? \"admin\" : \"user\";\n if (!id) {\n throw new Error(\"平台登录态缺少用户 id。\");\n }\n return {\n id,\n username: username || null,\n role,\n };\n };\n\n private resolvePlatformApiBase = (configuredApiBase?: string): string => {\n const source = configuredApiBase?.trim() || process.env.NEXTCLAW_PLATFORM_API_BASE?.trim() || DEFAULT_PLATFORM_API_BASE;\n const normalized = new URL(source);\n normalized.pathname = normalized.pathname.replace(/\\/v1\\/?$/, \"/\");\n return normalized.toString().replace(/\\/+$/, \"\");\n };\n\n private buildUserPublisher = (\n user: { id: string; username: string | null; role: \"admin\" | \"user\" },\n appId: string,\n ): NonNullable<AppPublishPayload[\"publisher\"]> => {\n const isOfficialScope = appId.startsWith(\"nextclaw.\");\n if (isOfficialScope && user.role === \"admin\") {\n return this.buildOfficialPublisher();\n }\n if (!user.username) {\n throw new Error(\"当前 NextClaw 账号还没有 username,无法发布个人 scope app。请先在平台账号页设置用户名。\");\n }\n return {\n id: user.username,\n name: user.username,\n url: `https://platform.nextclaw.io/account`,\n };\n };\n\n private buildOfficialPublisher = (): NonNullable<AppPublishPayload[\"publisher\"]> => {\n return {\n id: \"nextclaw\",\n name: \"NextClaw\",\n url: \"https://nextclaw.io\",\n };\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-publish.service.js","names":[],"sources":["../../src/services/app-publish.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppMarketplaceClientService } from \"./app-marketplace-client.service.js\";\nimport { AppMarketplaceMetadataService } from \"./app-marketplace-metadata.service.js\";\nimport type { AppPublishPayload, AppPublishResult } from \"#app-runtime/types/app-publish.types.js\";\nimport { PlatformAuthStateService } from \"./platform-auth-state.service.js\";\n\nconst DEFAULT_PLATFORM_API_BASE = \"https://ai-gateway-api.nextclaw.io\";\n\ntype ResolvedPublishActor = {\n token: string;\n publisher: NonNullable<AppPublishPayload[\"publisher\"]>;\n};\n\nexport class AppPublishService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly metadataService: AppMarketplaceMetadataService = new AppMarketplaceMetadataService(),\n private readonly marketplaceClient: AppMarketplaceClientService = new AppMarketplaceClientService(),\n private readonly authStateService: PlatformAuthStateService = new PlatformAuthStateService(),\n ) {}\n\n publish = async (params: {\n appDirectory: string;\n metadataPath?: string;\n apiBaseUrl?: string;\n token?: string;\n mode?: AppDistributionMode;\n }): Promise<AppPublishResult> => {\n const { appDirectory: inputAppDirectory, metadataPath, apiBaseUrl, token } = params;\n const appDirectory = path.resolve(inputAppDirectory);\n const manifestBundle = await this.manifestService.load(appDirectory);\n const distributionMode = params.mode ??\n (manifestBundle.manifest.schemaVersion === 2 ? \"bundle\" : \"source\");\n const metadata = await this.metadataService.load({\n appDirectory,\n manifest: manifestBundle.manifest,\n metadataPath,\n });\n const actor = await this.resolvePublishActor({\n apiBaseUrl,\n explicitToken: token,\n appId: manifestBundle.manifest.id,\n });\n const bundle = await this.bundleService.packAppDirectory({\n appDirectory,\n mode: distributionMode,\n });\n const bundleBytes = Buffer.from(await readFile(bundle.bundlePath));\n const bundleSha256 = createHash(\"sha256\").update(bundleBytes).digest(\"hex\");\n const publishFiles = await this.metadataService.collectPublishFiles({\n appDirectory,\n iconPath: manifestBundle.manifest.icon,\n metadataPath,\n visuals: metadata.visuals,\n });\n const payload: AppPublishPayload = {\n slug: metadata.slug,\n appId: manifestBundle.manifest.id,\n name: manifestBundle.manifest.name,\n version: manifestBundle.manifest.version,\n summary: metadata.summary,\n summaryI18n: metadata.summaryI18n,\n description: metadata.description ?? manifestBundle.manifest.description,\n descriptionI18n: metadata.descriptionI18n,\n author: metadata.author,\n tags: metadata.tags,\n sourceRepo: metadata.sourceRepo,\n homepage: metadata.homepage,\n featured: metadata.featured ?? false,\n publisher: actor.publisher,\n visuals: metadata.visuals,\n distributionMode,\n manifest: manifestBundle.manifest,\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : {},\n bundleBase64: bundleBytes.toString(\"base64\"),\n bundleSha256,\n files: publishFiles.map((file) => ({\n path: file.path,\n contentBase64: file.bytes.toString(\"base64\"),\n })),\n };\n const result = await this.marketplaceClient.publish({\n payload,\n apiBaseUrl,\n token: actor.token,\n });\n return {\n ...result,\n distribution: {\n path: bundle.bundlePath,\n sha256: bundleSha256,\n mode: distributionMode,\n },\n };\n };\n\n private resolvePublishActor = async (params: {\n apiBaseUrl?: string;\n explicitToken?: string;\n appId: string;\n }): Promise<ResolvedPublishActor> => {\n const explicitToken = params.explicitToken?.trim();\n const envAdminToken = process.env.NEXTCLAW_MARKETPLACE_ADMIN_TOKEN?.trim();\n if (explicitToken) {\n const token = explicitToken;\n if (!token) {\n throw new Error(\"缺少 publish token。\");\n }\n const me = await this.fetchCurrentPlatformUser({\n token,\n platformApiBase: this.resolvePlatformApiBase(),\n });\n return {\n token,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n const authState = this.authStateService.readCurrentAuthState();\n const platformToken = authState.token?.trim();\n if (platformToken) {\n const me = await this.fetchCurrentPlatformUser({\n token: platformToken,\n platformApiBase: this.resolvePlatformApiBase(authState.apiBaseUrl),\n });\n return {\n token: platformToken,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n if (envAdminToken) {\n return {\n token: envAdminToken,\n publisher: this.buildOfficialPublisher(),\n };\n }\n\n throw new Error(\"发布需要 NextClaw 平台登录态。请先运行 nextclaw login,或传入 --token。\");\n };\n\n private fetchCurrentPlatformUser = async (params: {\n token: string;\n platformApiBase: string;\n }): Promise<{\n id: string;\n username: string | null;\n role: \"admin\" | \"user\";\n }> => {\n const response = await fetch(`${params.platformApiBase}/platform/auth/me`, {\n headers: {\n authorization: `Bearer ${params.token}`,\n accept: \"application/json\",\n },\n });\n const payload = await response.json().catch(() => null);\n if (!response.ok) {\n const message =\n typeof payload === \"object\" &&\n payload &&\n \"error\" in payload &&\n typeof (payload as { error?: { message?: unknown } }).error?.message === \"string\"\n ? (payload as { error: { message: string } }).error.message\n : `${response.status} ${response.statusText}`;\n throw new Error(`读取 NextClaw 登录态失败:${message}`);\n }\n const user = typeof payload === \"object\" &&\n payload &&\n \"data\" in payload &&\n typeof (payload as { data?: { user?: unknown } }).data?.user === \"object\" &&\n (payload as { data: { user: Record<string, unknown> } }).data.user\n ? (payload as { data: { user: Record<string, unknown> } }).data.user\n : null;\n const id = typeof user?.id === \"string\" ? user.id.trim() : \"\";\n const username = typeof user?.username === \"string\" ? user.username.trim() : \"\";\n const role = user?.role === \"admin\" ? \"admin\" : \"user\";\n if (!id) {\n throw new Error(\"平台登录态缺少用户 id。\");\n }\n return {\n id,\n username: username || null,\n role,\n };\n };\n\n private resolvePlatformApiBase = (configuredApiBase?: string): string => {\n const source = configuredApiBase?.trim() || process.env.NEXTCLAW_PLATFORM_API_BASE?.trim() || DEFAULT_PLATFORM_API_BASE;\n const normalized = new URL(source);\n normalized.pathname = normalized.pathname.replace(/\\/v1\\/?$/, \"/\");\n return normalized.toString().replace(/\\/+$/, \"\");\n };\n\n private buildUserPublisher = (\n user: { id: string; username: string | null; role: \"admin\" | \"user\" },\n appId: string,\n ): NonNullable<AppPublishPayload[\"publisher\"]> => {\n const isOfficialScope = appId.startsWith(\"nextclaw.\");\n if (isOfficialScope && user.role === \"admin\") {\n return this.buildOfficialPublisher();\n }\n if (!user.username) {\n throw new Error(\"当前 NextClaw 账号还没有 username,无法发布个人 scope app。请先在平台账号页设置用户名。\");\n }\n return {\n id: user.username,\n name: user.username,\n url: `https://platform.nextclaw.io/account`,\n };\n };\n\n private buildOfficialPublisher = (): NonNullable<AppPublishPayload[\"publisher\"]> => {\n return {\n id: \"nextclaw\",\n name: \"NextClaw\",\n url: \"https://nextclaw.io\",\n };\n };\n}\n"],"mappings":"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"}
@@ -41,6 +41,8 @@ declare class AppRegistryService {
41
41
  setDocumentGrant: (appId: string, scopeId: string, directoryPath: string) => Promise<AppRegistryAppRecord>;
42
42
  removeDocumentGrant: (appId: string, scopeId: string) => Promise<boolean>;
43
43
  removeApp: (appId: string) => Promise<AppRegistryAppRecord | undefined>;
44
+ isBuiltInSuppressed: (appId: string) => Promise<boolean>;
45
+ setBuiltInSuppressed: (appId: string, suppressed: boolean) => Promise<void>;
44
46
  private updateApp;
45
47
  private withMutation;
46
48
  private parseRegistry;
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry.service.d.ts","names":[],"sources":["../../src/services/app-registry.service.ts"],"mappings":";;;;;;cAYa,kBAAA;EAAA,iBAGkB,cAAA;EAAA,wBAFL,cAAA;cAEK,cAAA,GAAgB,cAAA;EAE7C,IAAA,QAAiB,OAAA,CAAQ,WAAA;EAazB,IAAA,GAAc,QAAA,EAAU,WAAA,KAAc,OAAA;EAAA,QAI9B,YAAA;EAsBR,QAAA,QAAqB,OAAA,CAAQ,oBAAA;EAK7B,MAAA,GAAgB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAKxC,gBAAA,GAA0B,KAAA,aAAgB,OAAA,CAAQ,2BAAA;EAKlD,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,IAAA;IACA,WAAA;IACA,OAAA;IACA,gBAAA;IACA,aAAA;IACA,UAAA,EAAY,oBAAA;IACZ,SAAA;IACA,WAAA;IACA,gBAAA,GAAmB,2BAAA;IACnB,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,2BAAA;IACZ,qBAAA;IACA,UAAA,GAAa,oBAAA;IACb,cAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,oBAAA;EAsCZ,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,oBAAA;EAI9D,eAAA,GACE,KAAA,UACA,OAAA,aACC,OAAA,CAAQ,oBAAA;EASX,YAAA,GACE,KAAA,UACA,MAAA,EAAQ,mBAAA,KACP,OAAA,CAAQ,oBAAA;EAOX,gBAAA,GACE,KAAA,UACA,OAAA,UACA,aAAA,aACC,OAAA,CAAQ,oBAAA;EAIX,mBAAA,GAA6B,KAAA,UAAe,OAAA,aAAkB,OAAA;EAc9D,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAAA,QAanC,SAAA;EAAA,QAiBA,YAAA;EAAA,QAcA,aAAA;EAAA,QAyCA,YAAA;EAAA,QAOA,aAAA;EAAA,QAOA,kBAAA;AAAA"}
1
+ {"version":3,"file":"app-registry.service.d.ts","names":[],"sources":["../../src/services/app-registry.service.ts"],"mappings":";;;;;;cAYa,kBAAA;EAAA,iBAGkB,cAAA;EAAA,wBAFL,cAAA;cAEK,cAAA,GAAgB,cAAA;EAE7C,IAAA,QAAiB,OAAA,CAAQ,WAAA;EAazB,IAAA,GAAc,QAAA,EAAU,WAAA,KAAc,OAAA;EAAA,QAI9B,YAAA;EAsBR,QAAA,QAAqB,OAAA,CAAQ,oBAAA;EAK7B,MAAA,GAAgB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAKxC,gBAAA,GAA0B,KAAA,aAAgB,OAAA,CAAQ,2BAAA;EAKlD,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,IAAA;IACA,WAAA;IACA,OAAA;IACA,gBAAA;IACA,aAAA;IACA,UAAA,EAAY,oBAAA;IACZ,SAAA;IACA,WAAA;IACA,gBAAA,GAAmB,2BAAA;IACnB,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,2BAAA;IACZ,qBAAA;IACA,UAAA,GAAa,oBAAA;IACb,cAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,oBAAA;EAsCZ,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,oBAAA;EAI9D,eAAA,GACE,KAAA,UACA,OAAA,aACC,OAAA,CAAQ,oBAAA;EASX,YAAA,GACE,KAAA,UACA,MAAA,EAAQ,mBAAA,KACP,OAAA,CAAQ,oBAAA;EAOX,gBAAA,GACE,KAAA,UACA,OAAA,UACA,aAAA,aACC,OAAA,CAAQ,oBAAA;EAIX,mBAAA,GAA6B,KAAA,UAAe,OAAA,aAAkB,OAAA;EAc9D,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAa3C,mBAAA,GAA6B,KAAA,aAAgB,OAAA;EAK7C,oBAAA,GAA8B,KAAA,UAAe,UAAA,cAAsB,OAAA;EAAA,QAY3D,SAAA;EAAA,QAiBA,YAAA;EAAA,QAcA,aAAA;EAAA,QAoDA,YAAA;EAAA,QAOA,aAAA;EAAA,QAOA,kBAAA;AAAA"}
@@ -15,7 +15,8 @@ var AppRegistryService = class AppRegistryService {
15
15
  } catch (error) {
16
16
  if (this.isMissingFileError(error)) return {
17
17
  schemaVersion: 1,
18
- apps: {}
18
+ apps: {},
19
+ suppressedBuiltIns: {}
19
20
  };
20
21
  throw error;
21
22
  }
@@ -140,6 +141,18 @@ var AppRegistryService = class AppRegistryService {
140
141
  return appRecord;
141
142
  });
142
143
  };
144
+ isBuiltInSuppressed = async (appId) => {
145
+ const registry = await this.load();
146
+ return Boolean(registry.suppressedBuiltIns[appId]);
147
+ };
148
+ setBuiltInSuppressed = async (appId, suppressed) => {
149
+ await this.withMutation(async () => {
150
+ const registry = await this.load();
151
+ if (suppressed) registry.suppressedBuiltIns[appId] = { suppressedAt: (/* @__PURE__ */ new Date()).toISOString() };
152
+ else delete registry.suppressedBuiltIns[appId];
153
+ await this.saveUnlocked(registry);
154
+ });
155
+ };
143
156
  updateApp = async (appId, update) => {
144
157
  return await this.withMutation(async () => {
145
158
  const registry = await this.load();
@@ -191,7 +204,12 @@ var AppRegistryService = class AppRegistryService {
191
204
  }
192
205
  return {
193
206
  schemaVersion: 1,
194
- apps
207
+ apps,
208
+ suppressedBuiltIns: candidate.suppressedBuiltIns && typeof candidate.suppressedBuiltIns === "object" && !Array.isArray(candidate.suppressedBuiltIns) ? Object.fromEntries(Object.entries(candidate.suppressedBuiltIns).flatMap(([appId, raw]) => {
209
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
210
+ const suppressedAt = raw.suppressedAt;
211
+ return typeof suppressedAt === "string" ? [[appId, { suppressedAt }]] : [];
212
+ })) : {}
195
213
  };
196
214
  };
197
215
  assertRecord = (value, field) => {
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry.service.js","names":[],"sources":["../../src/services/app-registry.service.ts"],"sourcesContent":["import { open, readFile, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppPermissions, AppResolvedComponent } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport type {\n AppInstallSourceKind,\n AppRegistry,\n AppRegistryAppRecord,\n AppRegistryInstalledVersion,\n} from \"#app-runtime/types/app-registry.types.js\";\n\nexport class AppRegistryService {\n private static readonly mutationQueues = new Map<string, Promise<unknown>>();\n\n constructor(private readonly appHomeService: AppHomeService = new AppHomeService()) {}\n\n load = async (): Promise<AppRegistry> => {\n await this.appHomeService.ensureBaseDirectories();\n try {\n const raw = await readFile(this.appHomeService.getRegistryPath(), \"utf-8\");\n return this.parseRegistry(JSON.parse(raw) as unknown);\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return { schemaVersion: 1, apps: {} };\n }\n throw error;\n }\n };\n\n save = async (registry: AppRegistry): Promise<void> => {\n await this.withMutation(async () => await this.saveUnlocked(registry));\n };\n\n private saveUnlocked = async (registry: AppRegistry): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n const registryPath = this.appHomeService.getRegistryPath();\n const temporaryPath = path.join(\n path.dirname(registryPath),\n `.${path.basename(registryPath)}.${process.pid}.${Date.now()}.tmp`,\n );\n const handle = await open(temporaryPath, \"wx\", 0o600);\n try {\n await handle.writeFile(`${JSON.stringify(registry, null, 2)}\\n`, \"utf-8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await rename(temporaryPath, registryPath);\n } catch (error) {\n await rm(temporaryPath, { force: true });\n throw error;\n }\n };\n\n listApps = async (): Promise<AppRegistryAppRecord[]> => {\n const registry = await this.load();\n return Object.values(registry.apps).sort((left, right) => left.appId.localeCompare(right.appId));\n };\n\n getApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n const registry = await this.load();\n return registry.apps[appId];\n };\n\n getActiveVersion = async (appId: string): Promise<AppRegistryInstalledVersion | undefined> => {\n const appRecord = await this.getApp(appId);\n return appRecord?.installedVersions[appRecord.activeVersion];\n };\n\n upsertInstallation = async (params: {\n appId: string;\n name: string;\n description?: string;\n version: string;\n installDirectory: string;\n dataDirectory: string;\n sourceKind: AppInstallSourceKind;\n sourceRef: string;\n installedAt: string;\n distributionMode?: AppRegistryInstalledVersion[\"distributionMode\"];\n permissions: AppPermissions;\n registryUrl?: string;\n bundleUrl?: string;\n sha256?: string;\n publisher?: AppRegistryInstalledVersion[\"publisher\"];\n manifestSchemaVersion: 1 | 2;\n components?: AppResolvedComponent[];\n primaryPanelId?: string;\n enabled?: boolean;\n }): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const currentRecord = registry.apps[params.appId];\n const nextRecord: AppRegistryAppRecord = {\n appId: params.appId,\n name: params.name,\n description: params.description,\n activeVersion: params.version,\n enabled: params.enabled ?? currentRecord?.enabled ?? params.manifestSchemaVersion === 1,\n dataDirectory: params.dataDirectory,\n installedVersions: {\n ...(currentRecord?.installedVersions ?? {}),\n [params.version]: {\n version: params.version,\n installDirectory: params.installDirectory,\n sourceKind: params.sourceKind,\n sourceRef: params.sourceRef,\n installedAt: params.installedAt,\n distributionMode: params.distributionMode,\n permissions: params.permissions,\n registryUrl: params.registryUrl,\n bundleUrl: params.bundleUrl,\n sha256: params.sha256,\n publisher: params.publisher,\n manifestSchemaVersion: params.manifestSchemaVersion,\n components: params.components,\n primaryPanelId: params.primaryPanelId,\n },\n },\n grants: currentRecord?.grants ?? {},\n };\n registry.apps[params.appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({ ...record, enabled }));\n };\n\n activateVersion = async (\n appId: string,\n version: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => {\n if (!record.installedVersions[version]) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n return { ...record, activeVersion: version };\n });\n };\n\n updateGrants = async (\n appId: string,\n grants: AppDocumentGrantMap,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({\n ...record,\n grants: { ...record.grants, ...grants },\n }));\n };\n\n setDocumentGrant = async (\n appId: string,\n scopeId: string,\n directoryPath: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateGrants(appId, { [scopeId]: directoryPath });\n };\n\n removeDocumentGrant = async (appId: string, scopeId: string): Promise<boolean> => {\n let removed = false;\n await this.updateApp(appId, (record) => {\n if (!(scopeId in record.grants)) {\n return record;\n }\n const grants = { ...record.grants };\n delete grants[scopeId];\n removed = true;\n return { ...record, grants };\n });\n return removed;\n };\n\n removeApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n return undefined;\n }\n delete registry.apps[appId];\n await this.saveUnlocked(registry);\n return appRecord;\n });\n };\n\n private updateApp = async (\n appId: string,\n update: (record: AppRegistryAppRecord) => AppRegistryAppRecord,\n ): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const nextRecord = update(appRecord);\n registry.apps[appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n private withMutation = async <T>(operation: () => Promise<T>): Promise<T> => {\n const registryPath = path.resolve(this.appHomeService.getRegistryPath());\n const previous = AppRegistryService.mutationQueues.get(registryPath) ?? Promise.resolve();\n const current = previous.catch(() => undefined).then(operation);\n AppRegistryService.mutationQueues.set(registryPath, current);\n try {\n return await current;\n } finally {\n if (AppRegistryService.mutationQueues.get(registryPath) === current) {\n AppRegistryService.mutationQueues.delete(registryPath);\n }\n }\n };\n\n private parseRegistry = (rawRegistry: unknown): AppRegistry => {\n const candidate = this.assertRecord(rawRegistry, \"registry.json\");\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 registry schemaVersion = 1。\");\n }\n const rawApps = this.assertRecord(candidate.apps, \"registry.apps\");\n const apps: Record<string, AppRegistryAppRecord> = {};\n for (const [appId, rawApp] of Object.entries(rawApps)) {\n const app = this.assertRecord(rawApp, `registry.apps.${appId}`) as Partial<AppRegistryAppRecord>;\n const installedVersions: Record<string, AppRegistryInstalledVersion> = {};\n const rawVersions = this.assertRecord(\n app.installedVersions,\n `registry.apps.${appId}.installedVersions`,\n );\n for (const [version, rawVersion] of Object.entries(rawVersions)) {\n const versionRecord = this.assertRecord(\n rawVersion,\n `registry.apps.${appId}.installedVersions.${version}`,\n ) as Partial<AppRegistryInstalledVersion>;\n installedVersions[version] = {\n ...(versionRecord as AppRegistryInstalledVersion),\n version,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion === 2 ? 2 : 1,\n };\n }\n const activeVersion = this.requireString(app.activeVersion, `registry.apps.${appId}.activeVersion`);\n if (!installedVersions[activeVersion]) {\n throw new Error(`registry.apps.${appId} 缺少 activeVersion ${activeVersion}。`);\n }\n apps[appId] = {\n ...(app as AppRegistryAppRecord),\n appId,\n enabled: typeof app.enabled === \"boolean\" ? app.enabled : true,\n activeVersion,\n installedVersions,\n grants: app.grants && typeof app.grants === \"object\" ? app.grants : {},\n };\n }\n return { schemaVersion: 1, apps };\n };\n\n private assertRecord = (value: unknown, field: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${field} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private requireString = (value: unknown, field: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${field} 必须是非空字符串。`);\n }\n return value;\n };\n\n private isMissingFileError = (error: unknown): boolean =>\n typeof error === \"object\" && error !== null &&\n \"code\" in error && (error as { code?: unknown }).code === \"ENOENT\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-registry.service.js","names":[],"sources":["../../src/services/app-registry.service.ts"],"sourcesContent":["import { open, readFile, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppPermissions, AppResolvedComponent } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport type {\n AppInstallSourceKind,\n AppRegistry,\n AppRegistryAppRecord,\n AppRegistryInstalledVersion,\n} from \"#app-runtime/types/app-registry.types.js\";\n\nexport class AppRegistryService {\n private static readonly mutationQueues = new Map<string, Promise<unknown>>();\n\n constructor(private readonly appHomeService: AppHomeService = new AppHomeService()) {}\n\n load = async (): Promise<AppRegistry> => {\n await this.appHomeService.ensureBaseDirectories();\n try {\n const raw = await readFile(this.appHomeService.getRegistryPath(), \"utf-8\");\n return this.parseRegistry(JSON.parse(raw) as unknown);\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return { schemaVersion: 1, apps: {}, suppressedBuiltIns: {} };\n }\n throw error;\n }\n };\n\n save = async (registry: AppRegistry): Promise<void> => {\n await this.withMutation(async () => await this.saveUnlocked(registry));\n };\n\n private saveUnlocked = async (registry: AppRegistry): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n const registryPath = this.appHomeService.getRegistryPath();\n const temporaryPath = path.join(\n path.dirname(registryPath),\n `.${path.basename(registryPath)}.${process.pid}.${Date.now()}.tmp`,\n );\n const handle = await open(temporaryPath, \"wx\", 0o600);\n try {\n await handle.writeFile(`${JSON.stringify(registry, null, 2)}\\n`, \"utf-8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await rename(temporaryPath, registryPath);\n } catch (error) {\n await rm(temporaryPath, { force: true });\n throw error;\n }\n };\n\n listApps = async (): Promise<AppRegistryAppRecord[]> => {\n const registry = await this.load();\n return Object.values(registry.apps).sort((left, right) => left.appId.localeCompare(right.appId));\n };\n\n getApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n const registry = await this.load();\n return registry.apps[appId];\n };\n\n getActiveVersion = async (appId: string): Promise<AppRegistryInstalledVersion | undefined> => {\n const appRecord = await this.getApp(appId);\n return appRecord?.installedVersions[appRecord.activeVersion];\n };\n\n upsertInstallation = async (params: {\n appId: string;\n name: string;\n description?: string;\n version: string;\n installDirectory: string;\n dataDirectory: string;\n sourceKind: AppInstallSourceKind;\n sourceRef: string;\n installedAt: string;\n distributionMode?: AppRegistryInstalledVersion[\"distributionMode\"];\n permissions: AppPermissions;\n registryUrl?: string;\n bundleUrl?: string;\n sha256?: string;\n publisher?: AppRegistryInstalledVersion[\"publisher\"];\n manifestSchemaVersion: 1 | 2;\n components?: AppResolvedComponent[];\n primaryPanelId?: string;\n enabled?: boolean;\n }): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const currentRecord = registry.apps[params.appId];\n const nextRecord: AppRegistryAppRecord = {\n appId: params.appId,\n name: params.name,\n description: params.description,\n activeVersion: params.version,\n enabled: params.enabled ?? currentRecord?.enabled ?? params.manifestSchemaVersion === 1,\n dataDirectory: params.dataDirectory,\n installedVersions: {\n ...(currentRecord?.installedVersions ?? {}),\n [params.version]: {\n version: params.version,\n installDirectory: params.installDirectory,\n sourceKind: params.sourceKind,\n sourceRef: params.sourceRef,\n installedAt: params.installedAt,\n distributionMode: params.distributionMode,\n permissions: params.permissions,\n registryUrl: params.registryUrl,\n bundleUrl: params.bundleUrl,\n sha256: params.sha256,\n publisher: params.publisher,\n manifestSchemaVersion: params.manifestSchemaVersion,\n components: params.components,\n primaryPanelId: params.primaryPanelId,\n },\n },\n grants: currentRecord?.grants ?? {},\n };\n registry.apps[params.appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({ ...record, enabled }));\n };\n\n activateVersion = async (\n appId: string,\n version: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => {\n if (!record.installedVersions[version]) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n return { ...record, activeVersion: version };\n });\n };\n\n updateGrants = async (\n appId: string,\n grants: AppDocumentGrantMap,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({\n ...record,\n grants: { ...record.grants, ...grants },\n }));\n };\n\n setDocumentGrant = async (\n appId: string,\n scopeId: string,\n directoryPath: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateGrants(appId, { [scopeId]: directoryPath });\n };\n\n removeDocumentGrant = async (appId: string, scopeId: string): Promise<boolean> => {\n let removed = false;\n await this.updateApp(appId, (record) => {\n if (!(scopeId in record.grants)) {\n return record;\n }\n const grants = { ...record.grants };\n delete grants[scopeId];\n removed = true;\n return { ...record, grants };\n });\n return removed;\n };\n\n removeApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n return undefined;\n }\n delete registry.apps[appId];\n await this.saveUnlocked(registry);\n return appRecord;\n });\n };\n\n isBuiltInSuppressed = async (appId: string): Promise<boolean> => {\n const registry = await this.load();\n return Boolean(registry.suppressedBuiltIns[appId]);\n };\n\n setBuiltInSuppressed = async (appId: string, suppressed: boolean): Promise<void> => {\n await this.withMutation(async () => {\n const registry = await this.load();\n if (suppressed) {\n registry.suppressedBuiltIns[appId] = { suppressedAt: new Date().toISOString() };\n } else {\n delete registry.suppressedBuiltIns[appId];\n }\n await this.saveUnlocked(registry);\n });\n };\n\n private updateApp = async (\n appId: string,\n update: (record: AppRegistryAppRecord) => AppRegistryAppRecord,\n ): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const nextRecord = update(appRecord);\n registry.apps[appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n private withMutation = async <T>(operation: () => Promise<T>): Promise<T> => {\n const registryPath = path.resolve(this.appHomeService.getRegistryPath());\n const previous = AppRegistryService.mutationQueues.get(registryPath) ?? Promise.resolve();\n const current = previous.catch(() => undefined).then(operation);\n AppRegistryService.mutationQueues.set(registryPath, current);\n try {\n return await current;\n } finally {\n if (AppRegistryService.mutationQueues.get(registryPath) === current) {\n AppRegistryService.mutationQueues.delete(registryPath);\n }\n }\n };\n\n private parseRegistry = (rawRegistry: unknown): AppRegistry => {\n const candidate = this.assertRecord(rawRegistry, \"registry.json\");\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 registry schemaVersion = 1。\");\n }\n const rawApps = this.assertRecord(candidate.apps, \"registry.apps\");\n const apps: Record<string, AppRegistryAppRecord> = {};\n for (const [appId, rawApp] of Object.entries(rawApps)) {\n const app = this.assertRecord(rawApp, `registry.apps.${appId}`) as Partial<AppRegistryAppRecord>;\n const installedVersions: Record<string, AppRegistryInstalledVersion> = {};\n const rawVersions = this.assertRecord(\n app.installedVersions,\n `registry.apps.${appId}.installedVersions`,\n );\n for (const [version, rawVersion] of Object.entries(rawVersions)) {\n const versionRecord = this.assertRecord(\n rawVersion,\n `registry.apps.${appId}.installedVersions.${version}`,\n ) as Partial<AppRegistryInstalledVersion>;\n installedVersions[version] = {\n ...(versionRecord as AppRegistryInstalledVersion),\n version,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion === 2 ? 2 : 1,\n };\n }\n const activeVersion = this.requireString(app.activeVersion, `registry.apps.${appId}.activeVersion`);\n if (!installedVersions[activeVersion]) {\n throw new Error(`registry.apps.${appId} 缺少 activeVersion ${activeVersion}。`);\n }\n apps[appId] = {\n ...(app as AppRegistryAppRecord),\n appId,\n enabled: typeof app.enabled === \"boolean\" ? app.enabled : true,\n activeVersion,\n installedVersions,\n grants: app.grants && typeof app.grants === \"object\" ? app.grants : {},\n };\n }\n const suppressedBuiltIns = candidate.suppressedBuiltIns &&\n typeof candidate.suppressedBuiltIns === \"object\" &&\n !Array.isArray(candidate.suppressedBuiltIns)\n ? Object.fromEntries(Object.entries(candidate.suppressedBuiltIns).flatMap(([appId, raw]) => {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n return [];\n }\n const suppressedAt = (raw as { suppressedAt?: unknown }).suppressedAt;\n return typeof suppressedAt === \"string\" ? [[appId, { suppressedAt }]] : [];\n }))\n : {};\n return { schemaVersion: 1, apps, suppressedBuiltIns };\n };\n\n private assertRecord = (value: unknown, field: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${field} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private requireString = (value: unknown, field: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${field} 必须是非空字符串。`);\n }\n return value;\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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"}
@@ -5,6 +5,8 @@ import { AppPublisher } from "./app-remote-registry.types.js";
5
5
  import { AppInstallSourceKind } from "./app-registry.types.js";
6
6
 
7
7
  //#region src/types/app-installation.types.d.ts
8
+ type AppInstallProgressPhase = "resolving" | "downloading" | "verifying" | "installing" | "finalizing";
9
+ type AppInstallProgressHandler = (phase: AppInstallProgressPhase) => void | Promise<void>;
8
10
  type AppInstallResult = {
9
11
  appId: string;
10
12
  name: string;
@@ -84,5 +86,5 @@ type AppRollbackResult = AppActivationResult & {
84
86
  rolledBack: boolean;
85
87
  };
86
88
  //#endregion
87
- export { AppActivationResult, AppInfoResult, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem };
89
+ export { AppActivationResult, AppInfoResult, AppInstallProgressHandler, AppInstallProgressPhase, AppInstallResult, AppLaunchResolution, AppRollbackResult, AppUninstallResult, AppUpdateResult, InstalledAppListItem };
88
90
  //# sourceMappingURL=app-installation.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-installation.types.d.ts","names":[],"sources":["../../src/types/app-installation.types.ts"],"mappings":";;;;;;;KAMY,gBAAA;EACV,KAAA;EACA,IAAA;EACA,OAAA;EACA,gBAAA;EACA,aAAA;EACA,UAAA,EAAY,oBAAA;EACZ,gBAAA,GAAmB,mBAAA;EACnB,SAAA;EACA,WAAA,EAAa,cAAA;EACb,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA,GAAY,YAAA;EACZ,OAAA;EACA,qBAAA;EACA,UAAA,GAAa,oBAAA;EACb,cAAA;AAAA;AAAA,KAGU,aAAA;EACV,KAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,OAAA;EACA,aAAA;EACA,iBAAA,EAAmB,KAAA;IACjB,OAAA;IACA,gBAAA;IACA,UAAA,EAAY,oBAAA;IACZ,gBAAA,GAAmB,mBAAA;IACnB,SAAA;IACA,WAAA;IACA,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,YAAA;IACZ,qBAAA;IACA,UAAA,GAAa,oBAAA;IACb,cAAA;EAAA;EAEF,MAAA,EAAQ,mBAAA;AAAA;AAAA,KAGE,oBAAA;EACV,KAAA;EACA,IAAA;EACA,aAAA;EACA,UAAA,EAAY,oBAAA;EACZ,gBAAA,GAAmB,mBAAA;EACnB,OAAA;EACA,qBAAA;EACA,cAAA;AAAA;AAAA,KAGU,kBAAA;EACV,KAAA;EACA,eAAA;EACA,WAAA;AAAA;AAAA,KAGU,mBAAA;EACV,YAAA;EACA,KAAA;EACA,aAAA;EACA,gBAAA,EAAkB,mBAAA;AAAA;AAAA,KAGR,eAAA,GAAkB,gBAAA;EAC5B,eAAA;EACA,OAAA;AAAA;AAAA,KAGU,mBAAA;EACV,KAAA;EACA,aAAA;EACA,OAAA;AAAA;AAAA,KAGU,iBAAA,GAAoB,mBAAA;EAC9B,eAAA;EACA,UAAA;AAAA"}
1
+ {"version":3,"file":"app-installation.types.d.ts","names":[],"sources":["../../src/types/app-installation.types.ts"],"mappings":";;;;;;;KAMY,uBAAA;AAAA,KAOA,yBAAA,IACV,KAAA,EAAO,uBAAA,YACG,OAAA;AAAA,KAEA,gBAAA;EACV,KAAA;EACA,IAAA;EACA,OAAA;EACA,gBAAA;EACA,aAAA;EACA,UAAA,EAAY,oBAAA;EACZ,gBAAA,GAAmB,mBAAA;EACnB,SAAA;EACA,WAAA,EAAa,cAAA;EACb,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA,GAAY,YAAA;EACZ,OAAA;EACA,qBAAA;EACA,UAAA,GAAa,oBAAA;EACb,cAAA;AAAA;AAAA,KAGU,aAAA;EACV,KAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,OAAA;EACA,aAAA;EACA,iBAAA,EAAmB,KAAA;IACjB,OAAA;IACA,gBAAA;IACA,UAAA,EAAY,oBAAA;IACZ,gBAAA,GAAmB,mBAAA;IACnB,SAAA;IACA,WAAA;IACA,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,YAAA;IACZ,qBAAA;IACA,UAAA,GAAa,oBAAA;IACb,cAAA;EAAA;EAEF,MAAA,EAAQ,mBAAA;AAAA;AAAA,KAGE,oBAAA;EACV,KAAA;EACA,IAAA;EACA,aAAA;EACA,UAAA,EAAY,oBAAA;EACZ,gBAAA,GAAmB,mBAAA;EACnB,OAAA;EACA,qBAAA;EACA,cAAA;AAAA;AAAA,KAGU,kBAAA;EACV,KAAA;EACA,eAAA;EACA,WAAA;AAAA;AAAA,KAGU,mBAAA;EACV,YAAA;EACA,KAAA;EACA,aAAA;EACA,gBAAA,EAAkB,mBAAA;AAAA;AAAA,KAGR,eAAA,GAAkB,gBAAA;EAC5B,eAAA;EACA,OAAA;AAAA;AAAA,KAGU,mBAAA;EACV,KAAA;EACA,aAAA;EACA,OAAA;AAAA;AAAA,KAGU,iBAAA,GAAoB,mBAAA;EAC9B,eAAA;EACA,UAAA;AAAA"}
@@ -4,6 +4,10 @@ import { AppPublisher } from "./app-remote-registry.types.js";
4
4
 
5
5
  //#region src/types/app-publish.types.d.ts
6
6
  declare const DEFAULT_APP_MARKETPLACE_API_BASE = "https://apps-registry.nextclaw.io";
7
+ type AppMarketplaceVisuals = {
8
+ cover: string;
9
+ accentColor: string;
10
+ };
7
11
  type AppMarketplaceMetadata = {
8
12
  slug: string;
9
13
  summary: string;
@@ -16,6 +20,7 @@ type AppMarketplaceMetadata = {
16
20
  homepage?: string;
17
21
  featured?: boolean;
18
22
  publisher?: AppPublisher;
23
+ visuals?: AppMarketplaceVisuals;
19
24
  };
20
25
  type AppPublishFile = {
21
26
  path: string;
@@ -36,6 +41,7 @@ type AppPublishPayload = {
36
41
  homepage?: string;
37
42
  featured: boolean;
38
43
  publisher: AppPublisher;
44
+ visuals?: AppMarketplaceVisuals;
39
45
  distributionMode: AppDistributionMode;
40
46
  manifest: AppManifest;
41
47
  permissions: AppPermissions;
@@ -66,5 +72,5 @@ type AppPublishResult = {
66
72
  fileCount: number;
67
73
  };
68
74
  //#endregion
69
- export { AppMarketplaceMetadata, AppPublishFile, AppPublishPayload, AppPublishResult, DEFAULT_APP_MARKETPLACE_API_BASE };
75
+ export { AppMarketplaceMetadata, AppMarketplaceVisuals, AppPublishFile, AppPublishPayload, AppPublishResult, DEFAULT_APP_MARKETPLACE_API_BASE };
70
76
  //# sourceMappingURL=app-publish.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.types.d.ts","names":[],"sources":["../../src/types/app-publish.types.ts"],"mappings":";;;;;cAIa,gCAAA;AAAA,KAED,sBAAA;EACV,IAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,GAAY,YAAA;AAAA;AAAA,KAGF,cAAA;EACV,IAAA;EACA,aAAA;AAAA;AAAA,KAGU,iBAAA;EACV,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,EAAW,YAAA;EACX,gBAAA,EAAkB,mBAAA;EAClB,QAAA,EAAU,WAAA;EACV,WAAA,EAAa,cAAA;EACb,YAAA;EACA,YAAA;EACA,KAAA,EAAO,cAAA;AAAA;AAAA,KAGG,gBAAA;EACV,OAAA;EACA,IAAA;IACE,IAAA;IACA,KAAA;IACA,IAAA;IACA,aAAA;IACA,MAAA;IACA,OAAA;MACE,IAAA;MACA,IAAA;MACA,OAAA;MACA,QAAA;IAAA;EAAA;EAGJ,YAAA;IACE,IAAA;IACA,MAAA;IACA,IAAA,EAAM,mBAAA;EAAA;EAER,SAAA;AAAA"}
1
+ {"version":3,"file":"app-publish.types.d.ts","names":[],"sources":["../../src/types/app-publish.types.ts"],"mappings":";;;;;cAIa,gCAAA;AAAA,KAED,qBAAA;EACV,KAAA;EACA,WAAA;AAAA;AAAA,KAGU,sBAAA;EACV,IAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,GAAY,YAAA;EACZ,OAAA,GAAU,qBAAA;AAAA;AAAA,KAGA,cAAA;EACV,IAAA;EACA,aAAA;AAAA;AAAA,KAGU,iBAAA;EACV,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,WAAA,EAAa,MAAA;EACb,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,MAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA,EAAW,YAAA;EACX,OAAA,GAAU,qBAAA;EACV,gBAAA,EAAkB,mBAAA;EAClB,QAAA,EAAU,WAAA;EACV,WAAA,EAAa,cAAA;EACb,YAAA;EACA,YAAA;EACA,KAAA,EAAO,cAAA;AAAA;AAAA,KAGG,gBAAA;EACV,OAAA;EACA,IAAA;IACE,IAAA;IACA,KAAA;IACA,IAAA;IACA,aAAA;IACA,MAAA;IACA,OAAA;MACE,IAAA;MACA,IAAA;MACA,OAAA;MACA,QAAA;IAAA;EAAA;EAGJ,YAAA;IACE,IAAA;IACA,MAAA;IACA,IAAA,EAAM,mBAAA;EAAA;EAER,SAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-publish.types.js","names":[],"sources":["../../src/types/app-publish.types.ts"],"sourcesContent":["import type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppManifest, AppPermissions } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\n\nexport const DEFAULT_APP_MARKETPLACE_API_BASE = \"https://apps-registry.nextclaw.io\";\n\nexport type AppMarketplaceMetadata = {\n slug: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured?: boolean;\n publisher?: AppPublisher;\n};\n\nexport type AppPublishFile = {\n path: string;\n contentBase64: string;\n};\n\nexport type AppPublishPayload = {\n slug: string;\n appId: string;\n name: string;\n version: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured: boolean;\n publisher: AppPublisher;\n distributionMode: AppDistributionMode;\n manifest: AppManifest;\n permissions: AppPermissions;\n bundleBase64: string;\n bundleSha256: string;\n files: AppPublishFile[];\n};\n\nexport type AppPublishResult = {\n created: boolean;\n item: {\n slug: string;\n appId: string;\n name: string;\n latestVersion: string;\n webUrl?: string;\n install: {\n kind: \"registry\";\n spec: string;\n command: string;\n registry: string;\n };\n };\n distribution: {\n path: string;\n sha256: string;\n mode: AppDistributionMode;\n };\n fileCount: number;\n};\n"],"mappings":";AAIA,MAAa,mCAAmC"}
1
+ {"version":3,"file":"app-publish.types.js","names":[],"sources":["../../src/types/app-publish.types.ts"],"sourcesContent":["import type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppManifest, AppPermissions } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\n\nexport const DEFAULT_APP_MARKETPLACE_API_BASE = \"https://apps-registry.nextclaw.io\";\n\nexport type AppMarketplaceVisuals = {\n cover: string;\n accentColor: string;\n};\n\nexport type AppMarketplaceMetadata = {\n slug: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured?: boolean;\n publisher?: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n};\n\nexport type AppPublishFile = {\n path: string;\n contentBase64: string;\n};\n\nexport type AppPublishPayload = {\n slug: string;\n appId: string;\n name: string;\n version: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured: boolean;\n publisher: AppPublisher;\n visuals?: AppMarketplaceVisuals;\n distributionMode: AppDistributionMode;\n manifest: AppManifest;\n permissions: AppPermissions;\n bundleBase64: string;\n bundleSha256: string;\n files: AppPublishFile[];\n};\n\nexport type AppPublishResult = {\n created: boolean;\n item: {\n slug: string;\n appId: string;\n name: string;\n latestVersion: string;\n webUrl?: string;\n install: {\n kind: \"registry\";\n spec: string;\n command: string;\n registry: string;\n };\n };\n distribution: {\n path: string;\n sha256: string;\n mode: AppDistributionMode;\n };\n fileCount: number;\n};\n"],"mappings":";AAIA,MAAa,mCAAmC"}
@@ -34,6 +34,9 @@ type AppRegistryAppRecord = {
34
34
  type AppRegistry = {
35
35
  schemaVersion: 1;
36
36
  apps: Record<string, AppRegistryAppRecord>;
37
+ suppressedBuiltIns: Record<string, {
38
+ suppressedAt: string;
39
+ }>;
37
40
  };
38
41
  //#endregion
39
42
  export { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion };
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry.types.d.ts","names":[],"sources":["../../src/types/app-registry.types.ts"],"mappings":";;;;;;KAKY,oBAAA;AAAA,KAEA,2BAAA;EACV,OAAA;EACA,gBAAA;EACA,UAAA,EAAY,oBAAA;EACZ,SAAA;EACA,WAAA;EACA,gBAAA,GAAmB,mBAAA;EACnB,WAAA,EAAa,cAAA;EACb,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA,GAAY,YAAA;EACZ,qBAAA;EACA,UAAA,GAAa,oBAAA;EACb,cAAA;AAAA;AAAA,KAGU,oBAAA;EACV,KAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,OAAA;EACA,aAAA;EACA,iBAAA,EAAmB,MAAA,SAAe,2BAAA;EAClC,MAAA,EAAQ,mBAAA;AAAA;AAAA,KAGE,WAAA;EACV,aAAA;EACA,IAAA,EAAM,MAAA,SAAe,oBAAA;AAAA"}
1
+ {"version":3,"file":"app-registry.types.d.ts","names":[],"sources":["../../src/types/app-registry.types.ts"],"mappings":";;;;;;KAKY,oBAAA;AAAA,KAEA,2BAAA;EACV,OAAA;EACA,gBAAA;EACA,UAAA,EAAY,oBAAA;EACZ,SAAA;EACA,WAAA;EACA,gBAAA,GAAmB,mBAAA;EACnB,WAAA,EAAa,cAAA;EACb,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA,GAAY,YAAA;EACZ,qBAAA;EACA,UAAA,GAAa,oBAAA;EACb,cAAA;AAAA;AAAA,KAGU,oBAAA;EACV,KAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,OAAA;EACA,aAAA;EACA,iBAAA,EAAmB,MAAA,SAAe,2BAAA;EAClC,MAAA,EAAQ,mBAAA;AAAA;AAAA,KAGE,WAAA;EACV,aAAA;EACA,IAAA,EAAM,MAAA,SAAe,oBAAA;EACrB,kBAAA,EAAoB,MAAA;IAClB,YAAA;EAAA;AAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/app-runtime",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
4
4
  "private": false,
5
5
  "description": "Standalone micro app runtime and CLI for NextClaw apps.",
6
6
  "type": "module",