@nextclaw/app-runtime 0.15.0 → 0.16.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/package.js +1 -1
- package/dist/services/app-grant.service.d.ts +2 -0
- package/dist/services/app-grant.service.d.ts.map +1 -1
- package/dist/services/app-grant.service.js +40 -16
- package/dist/services/app-grant.service.js.map +1 -1
- package/dist/services/app-installation-lifecycle.service.js +29 -2
- package/dist/services/app-installation-lifecycle.service.js.map +1 -1
- package/dist/services/app-installation.service.d.ts.map +1 -1
- package/dist/services/app-installation.service.js +1 -1
- package/dist/services/app-installation.service.js.map +1 -1
- package/dist/services/app-manifest.service.d.ts +1 -0
- package/dist/services/app-manifest.service.d.ts.map +1 -1
- package/dist/services/app-manifest.service.js +27 -0
- package/dist/services/app-manifest.service.js.map +1 -1
- package/dist/services/app-permissions.service.d.ts.map +1 -1
- package/dist/services/app-permissions.service.js +0 -3
- package/dist/services/app-permissions.service.js.map +1 -1
- package/dist/services/app-registry-parser.service.js +228 -0
- package/dist/services/app-registry-parser.service.js.map +1 -0
- package/dist/services/app-registry.service.d.ts +7 -11
- package/dist/services/app-registry.service.d.ts.map +1 -1
- package/dist/services/app-registry.service.js +91 -173
- package/dist/services/app-registry.service.js.map +1 -1
- package/dist/services/app-rust-wasi-scaffold-template.service.d.ts.map +1 -1
- package/dist/services/app-rust-wasi-scaffold-template.service.js +37 -0
- package/dist/services/app-rust-wasi-scaffold-template.service.js.map +1 -1
- package/dist/types/app-installation.types.d.ts +2 -2
- package/dist/types/app-installation.types.d.ts.map +1 -1
- package/dist/types/app-manifest.types.d.ts +8 -1
- package/dist/types/app-manifest.types.d.ts.map +1 -1
- package/dist/types/app-manifest.types.js.map +1 -1
- package/dist/types/app-permissions.types.d.ts +12 -1
- package/dist/types/app-permissions.types.d.ts.map +1 -1
- package/dist/types/app-registry.types.d.ts +10 -3
- package/dist/types/app-registry.types.d.ts.map +1 -1
- package/package.json +2 -2
- package/resources/wit/deps/clocks@0.2.6/monotonic-clock.wit +50 -0
- package/resources/wit/deps/clocks@0.2.6/timezone.wit +55 -0
- package/resources/wit/deps/clocks@0.2.6/wall-clock.wit +46 -0
- package/resources/wit/deps/clocks@0.2.6/world.wit +11 -0
- package/resources/wit/deps/config@0.2.0-draft-2024-09-27/package.wit +1 -0
- package/resources/wit/deps/config@0.2.0-draft-2024-09-27/store.wit +9 -0
- package/resources/wit/deps/http@0.2.6/handler.wit +49 -0
- package/resources/wit/deps/http@0.2.6/package.wit +1 -0
- package/resources/wit/deps/http@0.2.6/types.wit +688 -0
- package/resources/wit/deps/io@0.2.6/error.wit +34 -0
- package/resources/wit/deps/io@0.2.6/poll.wit +47 -0
- package/resources/wit/deps/io@0.2.6/streams.wit +290 -0
- package/resources/wit/deps/io@0.2.6/world.wit +10 -0
- package/resources/wit/deps/spin@2.0.0/package.wit +1 -0
- package/resources/wit/deps/spin@2.0.0/sqlite.wit +50 -0
- package/resources/wit/portable-service.wit +73 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AppArtifactArchitecture, AppArtifactTarget, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDarwinArtifactTarget, AppDistributionDeclaration, AppDocumentAccessMode, AppDocumentAccessScope, AppLinuxArtifactTarget, AppMainManifest, AppManifest, AppManifestBundle, AppManifestSummary, AppNativeArtifactTarget, AppPermissions, AppPlatformSecuritySummary, AppResolvedComponent, AppRuntimeDeclaration, AppRuntimeIsolation, AppRuntimeProfile, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppStorageDeclaration, AppUiManifest, AppUniversalArtifactTarget, AppWasiHttpComponentMainManifest, AppWindowsArtifactTarget, isAppComponentManifestBundle, isAppStandaloneManifestBundle } from "./types/app-manifest.types.js";
|
|
1
|
+
import { AppArtifactArchitecture, AppArtifactTarget, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDarwinArtifactTarget, AppDistributionDeclaration, AppDocumentAccessMode, AppDocumentAccessScope, AppLinuxArtifactTarget, AppMainManifest, AppManifest, AppManifestBundle, AppManifestSummary, AppNativeArtifactTarget, AppPermissions, AppPlatformSecuritySummary, AppResolvedComponent, AppRuntimeDeclaration, AppRuntimeIsolation, AppRuntimeProfile, AppSecretSlot, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppStorageDeclaration, AppUiManifest, AppUniversalArtifactTarget, AppWasiHttpComponentMainManifest, AppWindowsArtifactTarget, isAppComponentManifestBundle, isAppStandaloneManifestBundle } from "./types/app-manifest.types.js";
|
|
2
2
|
import { AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppDistributionMode } from "./types/app-bundle.types.js";
|
|
3
3
|
import { AppArtifactValidationExpectation, AppArtifactValidationResult, AppArtifactValidationService } from "./services/app-artifact-validation.service.js";
|
|
4
4
|
import { AppPlatformTargetService } from "./services/app-platform-target.service.js";
|
|
5
5
|
import { AppManifestService } from "./services/app-manifest.service.js";
|
|
6
6
|
import { AppBundleService } from "./services/app-bundle.service.js";
|
|
7
|
-
import { AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppInstalledPermissionState, AppPermissionSummary, ResolvedDocumentGrant, ResolvedPermissions } from "./types/app-permissions.types.js";
|
|
7
|
+
import { AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppInstalledPermissionState, AppPermissionSummary, AppStoredDocumentGrant, AppStoredDocumentGrantMap, ResolvedDocumentGrant, ResolvedPermissions } from "./types/app-permissions.types.js";
|
|
8
8
|
import { AppPermissionsService } from "./services/app-permissions.service.js";
|
|
9
9
|
import { MainRunRequest, MainRunResult, WasmDocumentSummaryInput } from "./types/main-runner.types.js";
|
|
10
10
|
import { MainRunnerService } from "./services/main-runner.service.js";
|
|
@@ -22,7 +22,7 @@ import { CreateCommand } from "./controllers/create.controller.js";
|
|
|
22
22
|
import { AppHomeService } from "./services/app-home.service.js";
|
|
23
23
|
import { APP_STORAGE_LAYOUT_VERSION, AppInstanceInventory, AppInstanceInventoryDiagnostic, AppInstanceInventoryEntry, AppInstanceMetadata, AppInstanceRecord, AppStorageContext, AppStorageLayout, AppStorageUsage, DEFAULT_APP_INSTANCE_ID } from "./types/app-storage.types.js";
|
|
24
24
|
import { AppPublisher, AppRegistryConfig, AppRegistryConfigSnapshot, AppRemoteRegistryArtifact, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, DEFAULT_APP_REGISTRY_URL } from "./types/app-remote-registry.types.js";
|
|
25
|
-
import { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion } from "./types/app-registry.types.js";
|
|
25
|
+
import { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion, AppSecretBinding, AppSecretBindingMap } from "./types/app-registry.types.js";
|
|
26
26
|
import { AppRegistryService } from "./services/app-registry.service.js";
|
|
27
27
|
import { AppGrantService } from "./services/app-grant.service.js";
|
|
28
28
|
import { GrantCommand } from "./controllers/grant.controller.js";
|
|
@@ -58,4 +58,4 @@ import { AppServiceLaunch, AppServiceLaunchService } from "./services/app-servic
|
|
|
58
58
|
import { AppInstanceInventoryService } from "./services/app-instance-inventory.service.js";
|
|
59
59
|
import { FileLockBusyError, FileLockLease, FileLockService } from "./services/file-lock.service.js";
|
|
60
60
|
import { UiServerService } from "./services/ui-server.service.js";
|
|
61
|
-
export { APP_STORAGE_LAYOUT_VERSION, AppActivationResult, AppArtifactArchitecture, AppArtifactTarget, AppArtifactValidationExpectation, AppArtifactValidationResult, AppArtifactValidationService, AppBuildResult, AppBuildService, AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppBundleService, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDarwinArtifactTarget, AppDistributionDeclaration, AppDistributionMode, AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppGrantService, AppHomeService, AppHostHandle, AppHostService, AppHostStartOptions, AppInfoResult, AppInstallProgressHandler, AppInstallProgressPhase, AppInstallResult, AppInstallSourceKind, AppInstallationIntegrityService, AppInstallationService, AppInstalledPermissionState, AppInstanceInventory, AppInstanceInventoryDiagnostic, AppInstanceInventoryEntry, AppInstanceInventoryService, AppInstanceMetadata, AppInstanceRecord, AppInstanceService, AppInstanceStorageService, AppLaunchResolution, AppLinuxArtifactTarget, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppMarketplaceClientService, AppMarketplaceMetadata, AppMarketplaceMetadataService, AppMarketplaceVisuals, AppNativeArtifactTarget, AppPermissionSummary, AppPermissions, AppPermissionsService, AppPlatformSecuritySummary, AppPlatformTargetService, AppPreparedPublishArtifact, AppPublishArtifact, AppPublishArtifactService, AppPublishFile, AppPublishPayload, AppPublishResult, AppPublishService, AppPublishValidationResult, AppPublishValidationService, AppPublishValidationWarning, AppPublishValidationWarningCode, AppPublisher, AppRegistry, AppRegistryAppRecord, AppRegistryConfig, AppRegistryConfigService, AppRegistryConfigSnapshot, AppRegistryInstalledVersion, AppRegistryService, AppRemoteRegistryArtifact, AppRemoteRegistryClientService, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, AppResolvedComponent, AppRollbackResult, AppRunResult, AppRuntimeCliService, AppRuntimeCommandResult, AppRuntimeDeclaration, AppRuntimeDoctorResult, AppRuntimeIsolation, AppRuntimeOptionsService, AppRuntimeProfile, AppRuntimeToolStatus, AppRuntimeToolchainProfile, AppRuntimeToolchainService, AppRustWasiScaffoldTemplateService, AppScaffoldFile, AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate, AppServiceLaunch, AppServiceLaunchService, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppStorageContext, AppStorageDeclaration, AppStorageLayout, AppStorageUsage, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, AppUiManifest, AppUninstallResult, AppUniversalArtifactTarget, AppUpdateResult, AppWasiHttpComponentMainManifest, AppWindowsArtifactTarget, BuildCliOptions, CreateCliOptions, CreateCommand, DEFAULT_APP_INSTANCE_ID, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, FileLockBusyError, FileLockLease, FileLockService, 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 };
|
|
61
|
+
export { APP_STORAGE_LAYOUT_VERSION, AppActivationResult, AppArtifactArchitecture, AppArtifactTarget, AppArtifactValidationExpectation, AppArtifactValidationResult, AppArtifactValidationService, AppBuildResult, AppBuildService, AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppBundleService, AppComponentKind, AppComponentManifest, AppComponentManifestBundle, AppComponentManifestSummary, AppComponentReference, AppCoreWasmMainManifest, AppDarwinArtifactTarget, AppDistributionDeclaration, AppDistributionMode, AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppGrantService, AppHomeService, AppHostHandle, AppHostService, AppHostStartOptions, AppInfoResult, AppInstallProgressHandler, AppInstallProgressPhase, AppInstallResult, AppInstallSourceKind, AppInstallationIntegrityService, AppInstallationService, AppInstalledPermissionState, AppInstanceInventory, AppInstanceInventoryDiagnostic, AppInstanceInventoryEntry, AppInstanceInventoryService, AppInstanceMetadata, AppInstanceRecord, AppInstanceService, AppInstanceStorageService, AppLaunchResolution, AppLinuxArtifactTarget, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppMarketplaceClientService, AppMarketplaceMetadata, AppMarketplaceMetadataService, AppMarketplaceVisuals, AppNativeArtifactTarget, AppPermissionSummary, AppPermissions, AppPermissionsService, AppPlatformSecuritySummary, AppPlatformTargetService, AppPreparedPublishArtifact, AppPublishArtifact, AppPublishArtifactService, AppPublishFile, AppPublishPayload, AppPublishResult, AppPublishService, AppPublishValidationResult, AppPublishValidationService, AppPublishValidationWarning, AppPublishValidationWarningCode, AppPublisher, AppRegistry, AppRegistryAppRecord, AppRegistryConfig, AppRegistryConfigService, AppRegistryConfigSnapshot, AppRegistryInstalledVersion, AppRegistryService, AppRemoteRegistryArtifact, AppRemoteRegistryClientService, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, AppResolvedComponent, AppRollbackResult, AppRunResult, AppRuntimeCliService, AppRuntimeCommandResult, AppRuntimeDeclaration, AppRuntimeDoctorResult, AppRuntimeIsolation, AppRuntimeOptionsService, AppRuntimeProfile, AppRuntimeToolStatus, AppRuntimeToolchainProfile, AppRuntimeToolchainService, AppRustWasiScaffoldTemplateService, AppScaffoldFile, AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate, AppSecretBinding, AppSecretBindingMap, AppSecretSlot, AppServiceLaunch, AppServiceLaunchService, AppStandaloneManifest, AppStandaloneManifestBundle, AppStandaloneManifestSummary, AppStorageContext, AppStorageDeclaration, AppStorageLayout, AppStorageUsage, AppStoredDocumentGrant, AppStoredDocumentGrantMap, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, AppUiManifest, AppUninstallResult, AppUniversalArtifactTarget, AppUpdateResult, AppWasiHttpComponentMainManifest, AppWindowsArtifactTarget, BuildCliOptions, CreateCliOptions, CreateCommand, DEFAULT_APP_INSTANCE_ID, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, FileLockBusyError, FileLockLease, FileLockService, 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
|
@@ -12,11 +12,13 @@ declare class AppGrantService {
|
|
|
12
12
|
appId: string;
|
|
13
13
|
scopeId: string;
|
|
14
14
|
directoryPath: string;
|
|
15
|
+
mode?: "read" | "read-write";
|
|
15
16
|
}) => Promise<AppDocumentGrantMutationResult>;
|
|
16
17
|
revokeDocumentScope: (params: {
|
|
17
18
|
appId: string;
|
|
18
19
|
scopeId: string;
|
|
19
20
|
}) => Promise<AppDocumentGrantMutationResult>;
|
|
21
|
+
private isAvailableDirectory;
|
|
20
22
|
}
|
|
21
23
|
//#endregion
|
|
22
24
|
export { AppGrantService };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-grant.service.d.ts","names":[],"sources":["../../src/services/app-grant.service.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"app-grant.service.d.ts","names":[],"sources":["../../src/services/app-grant.service.ts"],"mappings":";;;;;cASa,eAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,eAAA;cADA,eAAA,GAAiB,kBAAA,EACjB,eAAA,GAAiB,kBAAA;EAGpC,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,2BAAA;EAoE3C,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,OAAA;IACA,aAAA;IACA,IAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;EA0CZ,mBAAA,GAA6B,MAAA;IAC3B,KAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;EAAA,QAcJ,oBAAA;AAAA"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AppManifestService } from "./app-manifest.service.js";
|
|
2
|
-
import { isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
|
|
3
2
|
import { AppRegistryService } from "./app-registry.service.js";
|
|
4
|
-
import {
|
|
3
|
+
import { realpath, stat } from "node:fs/promises";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
//#region src/services/app-grant.service.ts
|
|
7
6
|
var AppGrantService = class {
|
|
@@ -14,19 +13,31 @@ var AppGrantService = class {
|
|
|
14
13
|
if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
|
|
15
14
|
const activeVersion = appRecord.installedVersions[appRecord.activeVersion];
|
|
16
15
|
if (!activeVersion) throw new Error(`已安装应用缺少激活版本:${appId}`);
|
|
17
|
-
const
|
|
18
|
-
if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包权限由 Panel/Service runtime grant 管理。");
|
|
19
|
-
const requestedPermissions = bundle.manifest.permissions ?? {};
|
|
16
|
+
const requestedPermissions = (await this.manifestService.load(activeVersion.installDirectory)).manifest.permissions ?? {};
|
|
20
17
|
return {
|
|
21
18
|
appId: appRecord.appId,
|
|
22
19
|
name: appRecord.name,
|
|
23
20
|
activeVersion: appRecord.activeVersion,
|
|
24
|
-
documentAccess: (requestedPermissions.documentAccess ?? []).map((scope) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
documentAccess: await Promise.all((requestedPermissions.documentAccess ?? []).map(async (scope) => {
|
|
22
|
+
const grant = appRecord.grants[scope.id];
|
|
23
|
+
const available = grant ? await this.isAvailableDirectory(grant.path) : false;
|
|
24
|
+
const status = !grant ? "ungranted" : !available ? "unavailable" : "granted";
|
|
25
|
+
return {
|
|
26
|
+
id: scope.id,
|
|
27
|
+
mode: scope.mode,
|
|
28
|
+
description: scope.description,
|
|
29
|
+
granted: Boolean(grant),
|
|
30
|
+
grantedPath: grant?.path,
|
|
31
|
+
effectiveMode: grant?.mode,
|
|
32
|
+
grantedAt: grant?.grantedAt,
|
|
33
|
+
status,
|
|
34
|
+
availableActions: !grant ? ["grant"] : [
|
|
35
|
+
"replace",
|
|
36
|
+
...grant.mode === "read" && scope.mode === "read-write" ? ["upgrade"] : [],
|
|
37
|
+
...grant.mode === "read-write" ? ["downgrade"] : [],
|
|
38
|
+
"revoke"
|
|
39
|
+
]
|
|
40
|
+
};
|
|
30
41
|
})),
|
|
31
42
|
allowedDomains: requestedPermissions.allowedDomains ?? [],
|
|
32
43
|
storage: {
|
|
@@ -38,18 +49,24 @@ var AppGrantService = class {
|
|
|
38
49
|
};
|
|
39
50
|
grantDocumentScope = async (params) => {
|
|
40
51
|
const { appId, scopeId, directoryPath } = params;
|
|
41
|
-
|
|
52
|
+
const requestedScope = (await this.summarize(appId)).documentAccess.find((scope) => scope.id === scopeId);
|
|
53
|
+
if (!requestedScope) throw new Error(`应用 ${appId} 未声明 documentAccess scope:${scopeId}`);
|
|
54
|
+
const effectiveMode = params.mode ?? requestedScope.mode;
|
|
55
|
+
if (effectiveMode === "read-write" && requestedScope.mode !== "read-write") throw new Error(`应用 ${appId} 的 documentAccess scope ${scopeId} 只声明了 read。`);
|
|
42
56
|
const normalizedDirectory = path.resolve(directoryPath);
|
|
57
|
+
let canonicalDirectory;
|
|
43
58
|
try {
|
|
44
|
-
await
|
|
59
|
+
canonicalDirectory = await realpath(normalizedDirectory);
|
|
60
|
+
if (!(await stat(canonicalDirectory)).isDirectory()) throw new Error("not-directory");
|
|
45
61
|
} catch {
|
|
46
|
-
throw new Error(
|
|
62
|
+
throw new Error(`授权资源不是可用目录:${normalizedDirectory}`);
|
|
47
63
|
}
|
|
48
|
-
await this.registryService.setDocumentGrant(appId, scopeId,
|
|
64
|
+
await this.registryService.setDocumentGrant(appId, scopeId, canonicalDirectory, effectiveMode);
|
|
49
65
|
return {
|
|
50
66
|
appId,
|
|
51
67
|
scopeId,
|
|
52
|
-
grantedPath:
|
|
68
|
+
grantedPath: canonicalDirectory,
|
|
69
|
+
effectiveMode
|
|
53
70
|
};
|
|
54
71
|
};
|
|
55
72
|
revokeDocumentScope = async (params) => {
|
|
@@ -61,6 +78,13 @@ var AppGrantService = class {
|
|
|
61
78
|
removed: await this.registryService.removeDocumentGrant(appId, scopeId)
|
|
62
79
|
};
|
|
63
80
|
};
|
|
81
|
+
isAvailableDirectory = async (directoryPath) => {
|
|
82
|
+
try {
|
|
83
|
+
return (await stat(await realpath(directoryPath))).isDirectory();
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
64
88
|
};
|
|
65
89
|
//#endregion
|
|
66
90
|
export { AppGrantService };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-grant.service.js","names":[],"sources":["../../src/services/app-grant.service.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"app-grant.service.js","names":[],"sources":["../../src/services/app-grant.service.ts"],"sourcesContent":["import { realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport type {\n AppDocumentGrantMutationResult,\n AppInstalledPermissionState,\n} from \"#app-runtime/types/app-permissions.types.js\";\n\nexport class AppGrantService {\n constructor(\n private readonly registryService: AppRegistryService = new AppRegistryService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n ) {}\n\n summarize = async (appId: string): Promise<AppInstalledPermissionState> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appId}`);\n }\n const bundle = await this.manifestService.load(\n activeVersion.installDirectory,\n );\n const requestedPermissions = bundle.manifest.permissions ?? {};\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n activeVersion: appRecord.activeVersion,\n documentAccess: await Promise.all(\n (requestedPermissions.documentAccess ?? []).map(async (scope) => {\n const grant = appRecord.grants[scope.id];\n const available = grant\n ? await this.isAvailableDirectory(grant.path)\n : false;\n const status = !grant\n ? (\"ungranted\" as const)\n : !available\n ? (\"unavailable\" as const)\n : (\"granted\" as const);\n return {\n id: scope.id,\n mode: scope.mode,\n description: scope.description,\n granted: Boolean(grant),\n grantedPath: grant?.path,\n effectiveMode: grant?.mode,\n grantedAt: grant?.grantedAt,\n status,\n availableActions: !grant\n ? [\"grant\" as const]\n : [\n \"replace\" as const,\n ...(grant.mode === \"read\" && scope.mode === \"read-write\"\n ? [\"upgrade\" as const]\n : []),\n ...(grant.mode === \"read-write\"\n ? [\"downgrade\" as const]\n : []),\n \"revoke\" as const,\n ],\n };\n }),\n ),\n allowedDomains: requestedPermissions.allowedDomains ?? [],\n storage: {\n enabled:\n requestedPermissions.storage !== undefined &&\n requestedPermissions.storage !== false,\n namespace:\n typeof requestedPermissions.storage === \"object\"\n ? requestedPermissions.storage.namespace\n : undefined,\n },\n capabilities: {\n hostBridge: requestedPermissions.capabilities?.hostBridge !== false,\n },\n };\n };\n\n grantDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n directoryPath: string;\n mode?: \"read\" | \"read-write\";\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId, directoryPath } = params;\n const permissionState = await this.summarize(appId);\n const requestedScope = permissionState.documentAccess.find(\n (scope) => scope.id === scopeId,\n );\n if (!requestedScope) {\n throw new Error(`应用 ${appId} 未声明 documentAccess scope:${scopeId}`);\n }\n const effectiveMode = params.mode ?? requestedScope.mode;\n if (\n effectiveMode === \"read-write\" &&\n requestedScope.mode !== \"read-write\"\n ) {\n throw new Error(\n `应用 ${appId} 的 documentAccess scope ${scopeId} 只声明了 read。`,\n );\n }\n const normalizedDirectory = path.resolve(directoryPath);\n let canonicalDirectory: string;\n try {\n canonicalDirectory = await realpath(normalizedDirectory);\n if (!(await stat(canonicalDirectory)).isDirectory()) {\n throw new Error(\"not-directory\");\n }\n } catch {\n throw new Error(`授权资源不是可用目录:${normalizedDirectory}`);\n }\n await this.registryService.setDocumentGrant(\n appId,\n scopeId,\n canonicalDirectory,\n effectiveMode,\n );\n return {\n appId,\n scopeId,\n grantedPath: canonicalDirectory,\n effectiveMode,\n };\n };\n\n revokeDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId } = params;\n await this.summarize(appId);\n const removed = await this.registryService.removeDocumentGrant(\n appId,\n scopeId,\n );\n return {\n appId,\n scopeId,\n removed,\n };\n };\n\n private isAvailableDirectory = async (\n directoryPath: string,\n ): Promise<boolean> => {\n try {\n return (await stat(await realpath(directoryPath))).isDirectory();\n } catch {\n return false;\n }\n };\n}\n"],"mappings":";;;;;AASA,IAAa,kBAAb,MAA6B;CAC3B,YACE,kBAAuD,IAAI,oBAAoB,EAC/E,kBAAuD,IAAI,oBAAoB,EAC/E;AAFiB,OAAA,kBAAA;AACA,OAAA,kBAAA;;CAGnB,YAAY,OAAO,UAAwD;EACzE,MAAM,YAAY,MAAM,KAAK,gBAAgB,OAAO,MAAM;AAC1D,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;EAEtC,MAAM,gBAAgB,UAAU,kBAAkB,UAAU;AAC5D,MAAI,CAAC,cACH,OAAM,IAAI,MAAM,eAAe,QAAQ;EAKzC,MAAM,wBAHS,MAAM,KAAK,gBAAgB,KACxC,cAAc,iBACf,EACmC,SAAS,eAAe,EAAE;AAC9D,SAAO;GACL,OAAO,UAAU;GACjB,MAAM,UAAU;GAChB,eAAe,UAAU;GACzB,gBAAgB,MAAM,QAAQ,KAC3B,qBAAqB,kBAAkB,EAAE,EAAE,IAAI,OAAO,UAAU;IAC/D,MAAM,QAAQ,UAAU,OAAO,MAAM;IACrC,MAAM,YAAY,QACd,MAAM,KAAK,qBAAqB,MAAM,KAAK,GAC3C;IACJ,MAAM,SAAS,CAAC,QACX,cACD,CAAC,YACE,gBACA;AACP,WAAO;KACL,IAAI,MAAM;KACV,MAAM,MAAM;KACZ,aAAa,MAAM;KACnB,SAAS,QAAQ,MAAM;KACvB,aAAa,OAAO;KACpB,eAAe,OAAO;KACtB,WAAW,OAAO;KAClB;KACA,kBAAkB,CAAC,QACf,CAAC,QAAiB,GAClB;MACE;MACA,GAAI,MAAM,SAAS,UAAU,MAAM,SAAS,eACxC,CAAC,UAAmB,GACpB,EAAE;MACN,GAAI,MAAM,SAAS,eACf,CAAC,YAAqB,GACtB,EAAE;MACN;MACD;KACN;KACD,CACH;GACD,gBAAgB,qBAAqB,kBAAkB,EAAE;GACzD,SAAS;IACP,SACE,qBAAqB,YAAY,KAAA,KACjC,qBAAqB,YAAY;IACnC,WACE,OAAO,qBAAqB,YAAY,WACpC,qBAAqB,QAAQ,YAC7B,KAAA;IACP;GACD,cAAc,EACZ,YAAY,qBAAqB,cAAc,eAAe,OAC/D;GACF;;CAGH,qBAAqB,OAAO,WAKmB;EAC7C,MAAM,EAAE,OAAO,SAAS,kBAAkB;EAE1C,MAAM,kBADkB,MAAM,KAAK,UAAU,MAAM,EACZ,eAAe,MACnD,UAAU,MAAM,OAAO,QACzB;AACD,MAAI,CAAC,eACH,OAAM,IAAI,MAAM,MAAM,MAAM,4BAA4B,UAAU;EAEpE,MAAM,gBAAgB,OAAO,QAAQ,eAAe;AACpD,MACE,kBAAkB,gBAClB,eAAe,SAAS,aAExB,OAAM,IAAI,MACR,MAAM,MAAM,0BAA0B,QAAQ,aAC/C;EAEH,MAAM,sBAAsB,KAAK,QAAQ,cAAc;EACvD,IAAI;AACJ,MAAI;AACF,wBAAqB,MAAM,SAAS,oBAAoB;AACxD,OAAI,EAAE,MAAM,KAAK,mBAAmB,EAAE,aAAa,CACjD,OAAM,IAAI,MAAM,gBAAgB;UAE5B;AACN,SAAM,IAAI,MAAM,cAAc,sBAAsB;;AAEtD,QAAM,KAAK,gBAAgB,iBACzB,OACA,SACA,oBACA,cACD;AACD,SAAO;GACL;GACA;GACA,aAAa;GACb;GACD;;CAGH,sBAAsB,OAAO,WAGkB;EAC7C,MAAM,EAAE,OAAO,YAAY;AAC3B,QAAM,KAAK,UAAU,MAAM;AAK3B,SAAO;GACL;GACA;GACA,SAPc,MAAM,KAAK,gBAAgB,oBACzC,OACA,QACD;GAKA;;CAGH,uBAA+B,OAC7B,kBACqB;AACrB,MAAI;AACF,WAAQ,MAAM,KAAK,MAAM,SAAS,cAAc,CAAC,EAAE,aAAa;UAC1D;AACN,UAAO"}
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { rename } from "node:fs/promises";
|
|
3
|
+
import { setTimeout } from "node:timers/promises";
|
|
3
4
|
//#region src/services/app-installation-lifecycle.service.ts
|
|
5
|
+
const WINDOWS_RENAME_RETRY_DELAYS_MS = [
|
|
6
|
+
25,
|
|
7
|
+
50,
|
|
8
|
+
100,
|
|
9
|
+
200,
|
|
10
|
+
400
|
|
11
|
+
];
|
|
12
|
+
const WINDOWS_TRANSIENT_RENAME_CODES = new Set([
|
|
13
|
+
"EACCES",
|
|
14
|
+
"EBUSY",
|
|
15
|
+
"EPERM"
|
|
16
|
+
]);
|
|
17
|
+
async function renameAppLifecyclePath(originalPath, targetPath, options = {}) {
|
|
18
|
+
const platform = options.platform ?? process.platform;
|
|
19
|
+
const renamePath = options.renamePath ?? rename;
|
|
20
|
+
const wait = options.wait ?? setTimeout;
|
|
21
|
+
for (let attempt = 0;; attempt += 1) try {
|
|
22
|
+
await renamePath(originalPath, targetPath);
|
|
23
|
+
return;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const retryDelay = WINDOWS_RENAME_RETRY_DELAYS_MS[attempt];
|
|
26
|
+
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
27
|
+
if (platform !== "win32" || retryDelay === void 0 || !code || !WINDOWS_TRANSIENT_RENAME_CODES.has(code)) throw error;
|
|
28
|
+
await wait(retryDelay);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
4
31
|
var AppInstallationLifecycleService = class {
|
|
5
32
|
constructor(params) {
|
|
6
33
|
this.params = params;
|
|
@@ -91,7 +118,7 @@ var AppInstallationLifecycleService = class {
|
|
|
91
118
|
stagePath = async (originalPath, stagedPaths) => {
|
|
92
119
|
if (!await this.params.integrityService.pathExists(originalPath)) return;
|
|
93
120
|
const stagedPath = `${originalPath}.uninstalling-${randomUUID()}`;
|
|
94
|
-
await
|
|
121
|
+
await renameAppLifecyclePath(originalPath, stagedPath);
|
|
95
122
|
stagedPaths.push({
|
|
96
123
|
originalPath,
|
|
97
124
|
stagedPath
|
|
@@ -99,7 +126,7 @@ var AppInstallationLifecycleService = class {
|
|
|
99
126
|
};
|
|
100
127
|
restoreOrThrow = async (appId, stagedPaths, operationError) => {
|
|
101
128
|
try {
|
|
102
|
-
for (const entry of [...stagedPaths].reverse()) if (await this.params.integrityService.pathExists(entry.stagedPath)) await
|
|
129
|
+
for (const entry of [...stagedPaths].reverse()) if (await this.params.integrityService.pathExists(entry.stagedPath)) await renameAppLifecyclePath(entry.stagedPath, entry.originalPath);
|
|
103
130
|
} catch (restoreError) {
|
|
104
131
|
throw new AggregateError([operationError, restoreError], `卸载 ${appId} 失败,且无法完全恢复已暂存文件。`);
|
|
105
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-installation-lifecycle.service.js","names":[],"sources":["../../src/services/app-installation-lifecycle.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { rename } from \"node:fs/promises\";\nimport type { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport type { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport type { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport type { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport type { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport type {\n AppInstallProgressHandler,\n AppInstallResult,\n AppRollbackResult,\n AppUninstallResult,\n AppUpdateResult,\n} from \"#app-runtime/types/app-installation.types.js\";\n\ntype AppUpdateOptions = {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n};\n\nexport class AppInstallationLifecycleService {\n constructor(private readonly params: {\n registryService: AppRegistryService;\n registryConfigService: AppRegistryConfigService;\n remoteRegistryClient: AppRemoteRegistryClientService;\n instanceStorageService: AppInstanceStorageService;\n integrityService: AppInstallationIntegrityService;\n install: (source: string, options: AppUpdateOptions) => Promise<AppInstallResult>;\n rollback: (appId: string, version: string) => Promise<AppRollbackResult>;\n }) {}\n\n update = async (appId: string, options: AppUpdateOptions = {}): Promise<AppUpdateResult> => {\n const { onProgress, registryUrl: requestedRegistryUrl, version } = options;\n await onProgress?.(\"resolving\");\n const appRecord = await this.params.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];\n const registryUrl = requestedRegistryUrl ??\n activeVersionRecord?.registryUrl ??\n (await this.params.registryConfigService.getSnapshot()).currentUrl;\n const resolution = await this.params.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 this.toUpdateResult(appRecord, activeVersionRecord, appRecord.activeVersion, false);\n }\n const installedTarget = appRecord.installedVersions[resolution.version];\n if (installedTarget) {\n await onProgress?.(\"verifying\");\n await onProgress?.(\"installing\");\n if (options.activate !== false) {\n await this.params.rollback(appId, resolution.version);\n }\n await onProgress?.(\"finalizing\");\n return this.toUpdateResult(appRecord, installedTarget, resolution.version, true);\n }\n const installResult = await this.params.install(`${appId}@${resolution.version}`, {\n registryUrl,\n onProgress,\n activate: options.activate,\n });\n return {\n ...installResult,\n previousVersion: appRecord.activeVersion,\n updated: true,\n };\n };\n\n uninstall = async (appId: string, purgeData: boolean): Promise<AppUninstallResult> => {\n const appRecord = await this.params.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (!purgeData) {\n await this.params.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: appRecord.publisher?.id,\n legacyDataDirectory: appRecord.defaultInstance.storage.layout === \"legacy\"\n ? appRecord.dataDirectory\n : undefined,\n dataSchemaVersion: appRecord.defaultInstance.dataSchemaVersion,\n });\n }\n const removedVersions = Object.keys(appRecord.installedVersions).sort((left, right) =>\n left.localeCompare(right));\n const stagedPaths: Array<{ originalPath: string; stagedPath: string }> = [];\n try {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n await this.stagePath(versionRecord.installDirectory, stagedPaths);\n }\n if (purgeData) {\n await this.stagePath(\n appRecord.defaultInstance.storage.layout === \"instance-v1\"\n ? appRecord.defaultInstance.storage.instanceDirectory\n : appRecord.dataDirectory,\n stagedPaths,\n );\n }\n if (!await this.params.registryService.removeApp(appId)) {\n throw new Error(`卸载过程中应用记录已发生变化:${appId}`);\n }\n } catch (error) {\n await this.restoreOrThrow(appId, stagedPaths, error);\n throw error;\n }\n await Promise.all(stagedPaths.map(async (entry) =>\n await this.params.integrityService.removeDirectory(entry.stagedPath)));\n return { appId, removedVersions, dataRemoved: purgeData };\n };\n\n private toUpdateResult = (\n appRecord: NonNullable<Awaited<ReturnType<AppRegistryService[\"getApp\"]>>>,\n versionRecord: NonNullable<Awaited<ReturnType<AppRegistryService[\"getApp\"]>>>[\"installedVersions\"][string],\n version: string,\n updated: boolean,\n ): AppUpdateResult => ({\n appId: appRecord.appId,\n name: appRecord.name,\n version,\n previousVersion: appRecord.activeVersion,\n installDirectory: versionRecord.installDirectory,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n permissions: versionRecord.permissions,\n registryUrl: versionRecord.registryUrl,\n bundleUrl: versionRecord.bundleUrl,\n sha256: versionRecord.sha256,\n target: versionRecord.target,\n publisher: versionRecord.publisher,\n enabled: appRecord.enabled,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n updated,\n });\n\n private stagePath = async (\n originalPath: string,\n stagedPaths: Array<{ originalPath: string; stagedPath: string }>,\n ): Promise<void> => {\n if (!await this.params.integrityService.pathExists(originalPath)) {\n return;\n }\n const stagedPath = `${originalPath}.uninstalling-${randomUUID()}`;\n await rename(originalPath, stagedPath);\n stagedPaths.push({ originalPath, stagedPath });\n };\n\n private restoreOrThrow = async (\n appId: string,\n stagedPaths: Array<{ originalPath: string; stagedPath: string }>,\n operationError: unknown,\n ): Promise<void> => {\n try {\n for (const entry of [...stagedPaths].reverse()) {\n if (await this.params.integrityService.pathExists(entry.stagedPath)) {\n await rename(entry.stagedPath, entry.originalPath);\n }\n }\n } catch (restoreError) {\n throw new AggregateError(\n [operationError, restoreError],\n `卸载 ${appId} 失败,且无法完全恢复已暂存文件。`,\n );\n }\n };\n}\n"],"mappings":";;;AAsBA,IAAa,kCAAb,MAA6C;CAC3C,YAAY,QAQT;AAR0B,OAAA,SAAA;;CAU7B,SAAS,OAAO,OAAe,UAA4B,EAAE,KAA+B;EAC1F,MAAM,EAAE,YAAY,aAAa,sBAAsB,YAAY;AACnE,QAAM,aAAa,YAAY;EAC/B,MAAM,YAAY,MAAM,KAAK,OAAO,gBAAgB,OAAO,MAAM;AACjE,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;EAEtC,MAAM,sBAAsB,UAAU,kBAAkB,UAAU;EAClE,MAAM,cAAc,wBAClB,qBAAqB,gBACpB,MAAM,KAAK,OAAO,sBAAsB,aAAa,EAAE;EAC1D,MAAM,aAAa,MAAM,KAAK,OAAO,qBAAqB,QAAQ;GAChE;GACA;GACA;GACD,CAAC;AACF,MAAI,WAAW,YAAY,UAAU,eAAe;AAClD,OAAI,CAAC,oBACH,OAAM,IAAI,MAAM,eAAe,QAAQ;AAEzC,UAAO,KAAK,eAAe,WAAW,qBAAqB,UAAU,eAAe,MAAM;;EAE5F,MAAM,kBAAkB,UAAU,kBAAkB,WAAW;AAC/D,MAAI,iBAAiB;AACnB,SAAM,aAAa,YAAY;AAC/B,SAAM,aAAa,aAAa;AAChC,OAAI,QAAQ,aAAa,MACvB,OAAM,KAAK,OAAO,SAAS,OAAO,WAAW,QAAQ;AAEvD,SAAM,aAAa,aAAa;AAChC,UAAO,KAAK,eAAe,WAAW,iBAAiB,WAAW,SAAS,KAAK;;AAOlF,SAAO;GACL,GANoB,MAAM,KAAK,OAAO,QAAQ,GAAG,MAAM,GAAG,WAAW,WAAW;IAChF;IACA;IACA,UAAU,QAAQ;IACnB,CAAC;GAGA,iBAAiB,UAAU;GAC3B,SAAS;GACV;;CAGH,YAAY,OAAO,OAAe,cAAoD;EACpF,MAAM,YAAY,MAAM,KAAK,OAAO,gBAAgB,OAAO,MAAM;AACjE,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;AAEtC,MAAI,CAAC,UACH,OAAM,KAAK,OAAO,uBAAuB,2BAA2B;GAClE;GACA,aAAa,UAAU,WAAW;GAClC,qBAAqB,UAAU,gBAAgB,QAAQ,WAAW,WAC9D,UAAU,gBACV,KAAA;GACJ,mBAAmB,UAAU,gBAAgB;GAC9C,CAAC;EAEJ,MAAM,kBAAkB,OAAO,KAAK,UAAU,kBAAkB,CAAC,MAAM,MAAM,UAC3E,KAAK,cAAc,MAAM,CAAC;EAC5B,MAAM,cAAmE,EAAE;AAC3E,MAAI;AACF,QAAK,MAAM,iBAAiB,OAAO,OAAO,UAAU,kBAAkB,CACpE,OAAM,KAAK,UAAU,cAAc,kBAAkB,YAAY;AAEnE,OAAI,UACF,OAAM,KAAK,UACT,UAAU,gBAAgB,QAAQ,WAAW,gBACzC,UAAU,gBAAgB,QAAQ,oBAClC,UAAU,eACd,YACD;AAEH,OAAI,CAAC,MAAM,KAAK,OAAO,gBAAgB,UAAU,MAAM,CACrD,OAAM,IAAI,MAAM,kBAAkB,QAAQ;WAErC,OAAO;AACd,SAAM,KAAK,eAAe,OAAO,aAAa,MAAM;AACpD,SAAM;;AAER,QAAM,QAAQ,IAAI,YAAY,IAAI,OAAO,UACvC,MAAM,KAAK,OAAO,iBAAiB,gBAAgB,MAAM,WAAW,CAAC,CAAC;AACxE,SAAO;GAAE;GAAO;GAAiB,aAAa;GAAW;;CAG3D,kBACE,WACA,eACA,SACA,aACqB;EACrB,OAAO,UAAU;EACjB,MAAM,UAAU;EAChB;EACA,iBAAiB,UAAU;EAC3B,kBAAkB,cAAc;EAChC,eAAe,UAAU;EACzB,UAAU,UAAU;EACpB,YAAY,cAAc;EAC1B,kBAAkB,cAAc;EAChC,WAAW,cAAc;EACzB,aAAa,cAAc;EAC3B,aAAa,cAAc;EAC3B,WAAW,cAAc;EACzB,QAAQ,cAAc;EACtB,QAAQ,cAAc;EACtB,WAAW,cAAc;EACzB,SAAS,UAAU;EACnB,uBAAuB,cAAc;EACrC,YAAY,cAAc;EAC1B,gBAAgB,cAAc;EAC9B;EACD;CAED,YAAoB,OAClB,cACA,gBACkB;AAClB,MAAI,CAAC,MAAM,KAAK,OAAO,iBAAiB,WAAW,aAAa,CAC9D;EAEF,MAAM,aAAa,GAAG,aAAa,gBAAgB,YAAY;AAC/D,QAAM,OAAO,cAAc,WAAW;AACtC,cAAY,KAAK;GAAE;GAAc;GAAY,CAAC;;CAGhD,iBAAyB,OACvB,OACA,aACA,mBACkB;AAClB,MAAI;AACF,QAAK,MAAM,SAAS,CAAC,GAAG,YAAY,CAAC,SAAS,CAC5C,KAAI,MAAM,KAAK,OAAO,iBAAiB,WAAW,MAAM,WAAW,CACjE,OAAM,OAAO,MAAM,YAAY,MAAM,aAAa;WAG/C,cAAc;AACrB,SAAM,IAAI,eACR,CAAC,gBAAgB,aAAa,EAC9B,MAAM,MAAM,mBACb"}
|
|
1
|
+
{"version":3,"file":"app-installation-lifecycle.service.js","names":["delay"],"sources":["../../src/services/app-installation-lifecycle.service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { rename } from \"node:fs/promises\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport type { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport type { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport type { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport type { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport type { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport type {\n AppInstallProgressHandler,\n AppInstallResult,\n AppRollbackResult,\n AppUninstallResult,\n AppUpdateResult,\n} from \"#app-runtime/types/app-installation.types.js\";\n\ntype AppUpdateOptions = {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n};\n\nconst WINDOWS_RENAME_RETRY_DELAYS_MS = [25, 50, 100, 200, 400] as const;\nconst WINDOWS_TRANSIENT_RENAME_CODES = new Set([\"EACCES\", \"EBUSY\", \"EPERM\"]);\n\nexport async function renameAppLifecyclePath(\n originalPath: string,\n targetPath: string,\n options: {\n platform?: NodeJS.Platform;\n renamePath?: typeof rename;\n wait?: (milliseconds: number) => Promise<unknown>;\n } = {},\n): Promise<void> {\n const platform = options.platform ?? process.platform;\n const renamePath = options.renamePath ?? rename;\n const wait = options.wait ?? delay;\n for (let attempt = 0; ; attempt += 1) {\n try {\n await renamePath(originalPath, targetPath);\n return;\n } catch (error) {\n const retryDelay = WINDOWS_RENAME_RETRY_DELAYS_MS[attempt];\n const code = error instanceof Error && \"code\" in error\n ? (error as NodeJS.ErrnoException).code\n : undefined;\n if (\n platform !== \"win32\" ||\n retryDelay === undefined ||\n !code ||\n !WINDOWS_TRANSIENT_RENAME_CODES.has(code)\n ) {\n throw error;\n }\n await wait(retryDelay);\n }\n }\n}\n\nexport class AppInstallationLifecycleService {\n constructor(private readonly params: {\n registryService: AppRegistryService;\n registryConfigService: AppRegistryConfigService;\n remoteRegistryClient: AppRemoteRegistryClientService;\n instanceStorageService: AppInstanceStorageService;\n integrityService: AppInstallationIntegrityService;\n install: (source: string, options: AppUpdateOptions) => Promise<AppInstallResult>;\n rollback: (appId: string, version: string) => Promise<AppRollbackResult>;\n }) {}\n\n update = async (appId: string, options: AppUpdateOptions = {}): Promise<AppUpdateResult> => {\n const { onProgress, registryUrl: requestedRegistryUrl, version } = options;\n await onProgress?.(\"resolving\");\n const appRecord = await this.params.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];\n const registryUrl = requestedRegistryUrl ??\n activeVersionRecord?.registryUrl ??\n (await this.params.registryConfigService.getSnapshot()).currentUrl;\n const resolution = await this.params.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 this.toUpdateResult(appRecord, activeVersionRecord, appRecord.activeVersion, false);\n }\n const installedTarget = appRecord.installedVersions[resolution.version];\n if (installedTarget) {\n await onProgress?.(\"verifying\");\n await onProgress?.(\"installing\");\n if (options.activate !== false) {\n await this.params.rollback(appId, resolution.version);\n }\n await onProgress?.(\"finalizing\");\n return this.toUpdateResult(appRecord, installedTarget, resolution.version, true);\n }\n const installResult = await this.params.install(`${appId}@${resolution.version}`, {\n registryUrl,\n onProgress,\n activate: options.activate,\n });\n return {\n ...installResult,\n previousVersion: appRecord.activeVersion,\n updated: true,\n };\n };\n\n uninstall = async (appId: string, purgeData: boolean): Promise<AppUninstallResult> => {\n const appRecord = await this.params.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (!purgeData) {\n await this.params.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: appRecord.publisher?.id,\n legacyDataDirectory: appRecord.defaultInstance.storage.layout === \"legacy\"\n ? appRecord.dataDirectory\n : undefined,\n dataSchemaVersion: appRecord.defaultInstance.dataSchemaVersion,\n });\n }\n const removedVersions = Object.keys(appRecord.installedVersions).sort((left, right) =>\n left.localeCompare(right));\n const stagedPaths: Array<{ originalPath: string; stagedPath: string }> = [];\n try {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n await this.stagePath(versionRecord.installDirectory, stagedPaths);\n }\n if (purgeData) {\n await this.stagePath(\n appRecord.defaultInstance.storage.layout === \"instance-v1\"\n ? appRecord.defaultInstance.storage.instanceDirectory\n : appRecord.dataDirectory,\n stagedPaths,\n );\n }\n if (!await this.params.registryService.removeApp(appId)) {\n throw new Error(`卸载过程中应用记录已发生变化:${appId}`);\n }\n } catch (error) {\n await this.restoreOrThrow(appId, stagedPaths, error);\n throw error;\n }\n await Promise.all(stagedPaths.map(async (entry) =>\n await this.params.integrityService.removeDirectory(entry.stagedPath)));\n return { appId, removedVersions, dataRemoved: purgeData };\n };\n\n private toUpdateResult = (\n appRecord: NonNullable<Awaited<ReturnType<AppRegistryService[\"getApp\"]>>>,\n versionRecord: NonNullable<Awaited<ReturnType<AppRegistryService[\"getApp\"]>>>[\"installedVersions\"][string],\n version: string,\n updated: boolean,\n ): AppUpdateResult => ({\n appId: appRecord.appId,\n name: appRecord.name,\n version,\n previousVersion: appRecord.activeVersion,\n installDirectory: versionRecord.installDirectory,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n permissions: versionRecord.permissions,\n registryUrl: versionRecord.registryUrl,\n bundleUrl: versionRecord.bundleUrl,\n sha256: versionRecord.sha256,\n target: versionRecord.target,\n publisher: versionRecord.publisher,\n enabled: appRecord.enabled,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n updated,\n });\n\n private stagePath = async (\n originalPath: string,\n stagedPaths: Array<{ originalPath: string; stagedPath: string }>,\n ): Promise<void> => {\n if (!await this.params.integrityService.pathExists(originalPath)) {\n return;\n }\n const stagedPath = `${originalPath}.uninstalling-${randomUUID()}`;\n await renameAppLifecyclePath(originalPath, stagedPath);\n stagedPaths.push({ originalPath, stagedPath });\n };\n\n private restoreOrThrow = async (\n appId: string,\n stagedPaths: Array<{ originalPath: string; stagedPath: string }>,\n operationError: unknown,\n ): Promise<void> => {\n try {\n for (const entry of [...stagedPaths].reverse()) {\n if (await this.params.integrityService.pathExists(entry.stagedPath)) {\n await renameAppLifecyclePath(entry.stagedPath, entry.originalPath);\n }\n }\n } catch (restoreError) {\n throw new AggregateError(\n [operationError, restoreError],\n `卸载 ${appId} 失败,且无法完全恢复已暂存文件。`,\n );\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"app-installation.service.d.ts","names":[],"sources":["../../src/services/app-installation.service.ts"],"mappings":";;;;;;;;;;;;;KAqCK,iBAAA;EACH,WAAA;EACA,UAAA,GAAa,yBAAA;EACb,QAAA;EACA,gBAAA,GAAmB,YAAA;AAAA;AAAA,cAaR,sBAAA;EAAA,iBAUQ,cAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,iBACA,YAAA;EAAA,iBACA,eAAA;EAAA,iBACA,qBAAA;EAAA,iBACA,oBAAA;EAAA,iBAfF,oBAAA;EAAA,iBACA,sBAAA;EAAA,iBACA,eAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,iBAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,mBAAA;cAGE,cAAA,GAAgB,cAAA,EAChB,aAAA,GAAe,gBAAA,EACf,eAAA,GAAiB,kBAAA,EACjB,YAAA,GAAc,eAAA,EACd,eAAA,GAAiB,kBAAA,EACjB,qBAAA,GAAuB,wBAAA,EACvB,oBAAA,GAAsB,8BAAA,EACvC,sBAAA,GAAyB,yBAAA;EAqB3B,OAAA,GAAiB,SAAA,UAAmB,OAAA,GAAS,iBAAA,KAAyB,OAAA,CAAQ,gBAAA;EAAA,QA8BtE,qBAAA;EAAA,QAKA,qBAAA;EAkGR,MAAA,GACE,KAAA,UACA,OAAA;IACE,OAAA;IACA,WAAA;IACA,UAAA,GAAa,yBAAA;IACb,QAAA;EAAA,MAED,OAAA,CAAQ,eAAA;EAIX,SAAA,GAAmB,KAAA,UAAe,SAAA,cAAqB,OAAA,CAAQ,kBAAA;EAI/D,IAAA,QAAiB,OAAA,CAAQ,oBAAA;EAczB,mBAAA,QAAgC,OAAA;EAqFhC,IAAA,GAAc,KAAA,UAAe,OAAA;IAAW,mBAAA;EAAA,MAAuC,OAAA,CAAQ,aAAA;EAoCvF,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,mBAAA;EAW9D,QAAA,GAAkB,KAAA,UAAe,OAAA,aAAkB,OAAA,CAAQ,iBAAA;EAkC3D,sBAAA,GAAgC,KAAA,UAAe,OAAA,cAAmB,OAAA;EAqBlE,gBAAA,MAA6B,KAAA,UAAe,SAAA,QAAiB,OAAA,CAAQ,CAAA,MAAK,OAAA,CAAQ,CAAA;EAgBlF,aAAA,GAAuB,YAAA,UAAsB,wBAAA,EAA0B,mBAAA,KAAsB,OAAA,CAAQ,mBAAA;EAAA,QA6B7F,sBAAA;EAOR,aAAA,GAAuB,KAAA,sBAA2B,gBAAA,EAAkB,mBAAA,KAAsB,OAAA;AAAA"}
|
|
@@ -376,7 +376,7 @@ var AppInstallationService = class {
|
|
|
376
376
|
dataDirectory: appRecord.dataDirectory,
|
|
377
377
|
storage: appRecord.defaultInstance.storage,
|
|
378
378
|
documentGrantMap: {
|
|
379
|
-
...appRecord.grants,
|
|
379
|
+
...Object.fromEntries(Object.entries(appRecord.grants).map(([scopeId, grant]) => [scopeId, grant.path])),
|
|
380
380
|
...explicitDocumentGrantMap
|
|
381
381
|
}
|
|
382
382
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { isAppComponentManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport { AppInstallationFilesystemService } from \"#app-runtime/services/app-installation-filesystem.service.js\";\nimport { AppInstallationLifecycleService } from \"#app-runtime/services/app-installation-lifecycle.service.js\";\nimport { FileLockService } from \"#app-runtime/services/file-lock.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppBundleExtractResult } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\nimport type { AppRegistryInstalledVersion } from \"#app-runtime/types/app-registry.types.js\";\nimport { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport {\n AppInstallSourceService,\n type ResolvedAppInstallSource,\n} from \"#app-runtime/services/app-install-source.service.js\";\nimport { resolveAppInstallationContract } from \"#app-runtime/utils/app-installation-contract.utils.js\";\nimport type {\n AppInfoResult,\n AppInstallProgressHandler,\n AppActivationResult,\n AppInstallResult,\n AppLaunchResolution,\n AppUpdateResult,\n AppRollbackResult,\n AppUninstallResult,\n InstalledAppListItem,\n} from \"#app-runtime/types/app-installation.types.js\";\n\nconst SAFE_APP_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\n\ntype AppInstallOptions = {\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n trustedPublisher?: AppPublisher;\n};\n\ntype PreparedAppInstall = {\n source: ResolvedAppInstallSource;\n extractedDirectory: string;\n extractedMetadata: AppBundleExtractResult;\n manifestBundle: AppManifestBundle;\n options: AppInstallOptions;\n};\n\ntype RemoteSourceFields = Pick<AppRegistryInstalledVersion, \"registryUrl\" | \"bundleUrl\" | \"sha256\" | \"target\">;\n\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\n private readonly instanceStorageService: AppInstanceStorageService;\n private readonly fileLockService = new FileLockService();\n private readonly integrityService: AppInstallationIntegrityService;\n private readonly filesystemService: AppInstallationFilesystemService;\n private readonly lifecycleService: AppInstallationLifecycleService;\n private readonly appOperationContext = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly buildService: AppBuildService = new AppBuildService(),\n private readonly registryService: AppRegistryService = new AppRegistryService(appHomeService),\n private readonly registryConfigService: AppRegistryConfigService = new AppRegistryConfigService(\n appHomeService,\n ),\n private readonly remoteRegistryClient: AppRemoteRegistryClientService = new AppRemoteRegistryClientService(\n new AppRegistryConfigService(appHomeService),\n ),\n instanceStorageService?: AppInstanceStorageService,\n ) {\n this.installSourceService = new AppInstallSourceService(\n this.manifestService,\n this.remoteRegistryClient,\n this.bundleService,\n );\n this.instanceStorageService = instanceStorageService ?? new AppInstanceStorageService(appHomeService);\n this.integrityService = new AppInstallationIntegrityService(manifestService);\n this.filesystemService = new AppInstallationFilesystemService({\n buildService,\n integrityService: this.integrityService,\n manifestService,\n });\n this.lifecycleService = new AppInstallationLifecycleService({\n registryService,\n registryConfigService,\n remoteRegistryClient,\n instanceStorageService: this.instanceStorageService,\n integrityService: this.integrityService,\n install: this.install,\n rollback: this.rollback,\n });\n }\n\n install = async (\n appSource: string,\n options: AppInstallOptions = {},\n ): Promise<AppInstallResult> => {\n const { onProgress, registryUrl } = options;\n await onProgress?.(\"resolving\");\n const source = await this.installSourceService.resolve(appSource, registryUrl);\n const tempDirectory = await this.appHomeService.createTemporaryDirectory(\"napp-install-\");\n try {\n const bundlePath = await this.installSourceService.materializeBundle(\n source,\n tempDirectory,\n onProgress,\n );\n await onProgress?.(\"verifying\");\n const extractedDirectory = path.join(tempDirectory, \"bundle\");\n const extractedMetadata = await this.bundleService.extractBundle({\n bundlePath,\n targetDirectory: extractedDirectory,\n });\n await this.filesystemService.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n return await this.installPreparedBundle({\n source,\n extractedDirectory,\n extractedMetadata,\n manifestBundle,\n options,\n });\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n private installPreparedBundle = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const appId = prepared.manifestBundle.manifest.id;\n return await this.withAppOperation(\n appId,\n async () => await this.commitPreparedInstall(prepared),\n );\n };\n\n private commitPreparedInstall = async (\n prepared: PreparedAppInstall,\n ): Promise<AppInstallResult> => {\n const { extractedDirectory, extractedMetadata, manifestBundle, options, source } = prepared;\n const { onProgress } = options;\n const { id: appId, version } = manifestBundle.manifest;\n const installDirectory = this.appHomeService.getInstallDirectory(appId, version);\n if (source.kind === \"registry\" && appId !== source.registryResolution.appId) {\n throw new Error(\n `bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${appId}`,\n );\n }\n await onProgress?.(\"installing\");\n const existingRecord = await this.registryService.getApp(appId);\n const { currentPublisher, dataSchemaVersion, publisher } =\n resolveAppInstallationContract({\n appId, existingRecord, manifestBundle, source,\n trustedPublisher: options.trustedPublisher,\n });\n const instanceDirectory = this.appHomeService.getAppInstanceDirectory(appId, \"default\");\n const instanceExisted = await this.integrityService.pathExists(instanceDirectory);\n const contentSha256 = await this.filesystemService.copyToImmutableInstallDirectory({\n appId, appVersion: version, extractedDirectory, installDirectory,\n target: extractedMetadata.metadata.target?.kind === \"native\"\n ? extractedMetadata.metadata.target\n : undefined,\n });\n let defaultInstance: Awaited<ReturnType<\n AppInstanceStorageService[\"materializeDefaultInstance\"]\n >> | undefined;\n let registryRecord;\n try {\n defaultInstance = await this.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: (currentPublisher ?? publisher)?.id,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n dataSchemaVersion,\n });\n await onProgress?.(\"finalizing\");\n registryRecord = await this.registryService.upsertInstallation({\n appId,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version,\n installDirectory,\n defaultInstance,\n sourceKind: source.kind,\n distributionMode: source.kind === \"registry\"\n ? source.registryResolution.distributionMode\n : extractedMetadata.metadata.distributionMode,\n sourceRef: source.sourceRef,\n installedAt: new Date().toISOString(),\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : this.manifestService.resolvePlatformSecurity(manifestBundle.manifest).permissions,\n ...this.readRemoteSourceFields(source.kind === \"registry\" ? source.registryResolution : undefined),\n publisher,\n manifestSchemaVersion: manifestBundle.manifest.schemaVersion,\n components: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.components.map((component) => ({\n ...component,\n componentDirectory: path.join(installDirectory, component.path),\n manifestPath: path.join(\n installDirectory,\n component.path,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n }))\n : undefined,\n primaryPanelId: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.primaryPanelId\n : undefined,\n security: isAppComponentManifestBundle(manifestBundle)\n ? this.manifestService.resolvePlatformSecurity(manifestBundle.manifest)\n : undefined,\n dataSchemaVersion,\n contentSha256,\n activate: options.activate,\n });\n } catch (error) {\n await this.integrityService.removeDirectory(installDirectory);\n if (!instanceExisted && defaultInstance) {\n await this.instanceStorageService.rollbackNewInstance({\n instance: defaultInstance,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\"\n ? existingRecord.dataDirectory\n : undefined,\n });\n }\n throw error;\n }\n if (!defaultInstance) {\n throw new Error(`应用 ${appId} 安装完成但缺少默认实例。`);\n }\n const installedVersion = registryRecord.installedVersions[version];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version,\n installDirectory,\n dataDirectory: defaultInstance.storage.dataDirectory,\n instance: registryRecord.defaultInstance,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode: installedVersion?.distributionMode,\n permissions: installedVersion?.permissions ?? {},\n ...this.readRemoteSourceFields(installedVersion),\n publisher: installedVersion?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: installedVersion?.manifestSchemaVersion ?? 1,\n components: installedVersion?.components,\n primaryPanelId: installedVersion?.primaryPanelId,\n };\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n },\n ): Promise<AppUpdateResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.update(appId, options),\n );\n };\n\n uninstall = async (\n appId: string,\n purgeData: boolean,\n ): Promise<AppUninstallResult> => {\n return await this.withAppOperation(\n appId,\n async () => await this.lifecycleService.uninstall(appId, purgeData),\n );\n };\n\n list = async (): Promise<InstalledAppListItem[]> => {\n const appRecords = await this.registryService.listApps();\n return appRecords.map((appRecord) => ({\n appId: appRecord.appId,\n name: appRecord.name,\n activeVersion: appRecord.activeVersion,\n sourceKind:\n appRecord.installedVersions[appRecord.activeVersion]?.sourceKind ?? \"directory\",\n distributionMode:\n appRecord.installedVersions[appRecord.activeVersion]?.distributionMode,\n enabled: appRecord.enabled,\n manifestSchemaVersion:\n appRecord.installedVersions[appRecord.activeVersion]?.manifestSchemaVersion ?? 1,\n primaryPanelId:\n appRecord.installedVersions[appRecord.activeVersion]?.primaryPanelId,\n }));\n };\n\n reconcileFilesystem = async (): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n let appRecords = await this.registryService.listApps();\n for (const appRecord of appRecords) {\n await this.withAppOperation(appRecord.appId, async () => {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n if (!await this.integrityService.pathExists(versionRecord.installDirectory)) {\n continue;\n }\n if (!versionRecord.contentSha256) {\n const contentSha256 = await this.integrityService.calculateDigest(\n versionRecord.installDirectory,\n );\n await this.registryService.setVersionContentDigest(\n appRecord.appId,\n versionRecord.version,\n contentSha256,\n );\n }\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n });\n }\n appRecords = await this.registryService.listApps();\n const referencedInstallPaths = new Set(appRecords.flatMap((record) =>\n Object.values(record.installedVersions).map((version) => path.resolve(version.installDirectory))));\n const referencedDataPaths = new Set(appRecords.map((record) => path.resolve(\n record.defaultInstance.storage.layout === \"instance-v1\"\n ? record.defaultInstance.storage.instanceDirectory\n : record.dataDirectory,\n )));\n\n const packageDirectories = await this.filesystemService.readDirectories(\n this.appHomeService.getPackagesDirectory(),\n );\n for (const appDirectory of packageDirectories) {\n await this.filesystemService.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n this.appHomeService.getDataDirectory(),\n referencedDataPaths,\n );\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"instance-v1\") {\n continue;\n }\n await this.filesystemService.reconcileGeneratedSiblings(\n appRecord.defaultInstance.storage.instanceDirectory,\n new Set([path.resolve(appRecord.defaultInstance.storage.dataDirectory)]),\n );\n }\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"legacy\") {\n continue;\n }\n await this.withAppOperation(appRecord.appId, async () => {\n const currentRecord = await this.registryService.getApp(appRecord.appId);\n if (!currentRecord || currentRecord.defaultInstance.storage.layout !== \"legacy\") {\n return;\n }\n const activeVersion = currentRecord.installedVersions[currentRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${currentRecord.appId}`);\n }\n const instance = await this.instanceStorageService.materializeDefaultInstance({\n appId: currentRecord.appId,\n publisherId: currentRecord.publisher?.id,\n legacyDataDirectory: currentRecord.dataDirectory,\n dataSchemaVersion: currentRecord.defaultInstance.dataSchemaVersion,\n });\n try {\n await this.registryService.upsertInstallation({\n appId: currentRecord.appId,\n name: currentRecord.name,\n description: currentRecord.description,\n version: currentRecord.activeVersion,\n installDirectory: activeVersion.installDirectory,\n defaultInstance: instance,\n sourceKind: activeVersion.sourceKind,\n sourceRef: activeVersion.sourceRef,\n installedAt: activeVersion.installedAt,\n distributionMode: activeVersion.distributionMode,\n permissions: activeVersion.permissions,\n ...this.readRemoteSourceFields(activeVersion),\n publisher: currentRecord.publisher ?? activeVersion.publisher,\n manifestSchemaVersion: activeVersion.manifestSchemaVersion,\n components: activeVersion.components,\n primaryPanelId: activeVersion.primaryPanelId,\n security: activeVersion.security,\n dataSchemaVersion: activeVersion.dataSchemaVersion,\n contentSha256: activeVersion.contentSha256,\n enabled: currentRecord.enabled,\n });\n } catch (error) {\n await this.instanceStorageService.rollbackNewInstance({\n instance,\n legacyDataDirectory: currentRecord.dataDirectory,\n });\n throw error;\n }\n });\n }\n };\n\n info = async (\n appId: string,\n options: { measureStorageUsage?: boolean } = {},\n ): Promise<AppInfoResult> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const installedVersions = Object.values(appRecord.installedVersions).sort((left, right) =>\n left.version.localeCompare(right.version),\n );\n const storageUsage = options.measureStorageUsage === false\n ? undefined\n : await this.instanceStorageService.measureUsage(appRecord.defaultInstance.storage);\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n description: appRecord.description,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n storage: appRecord.defaultInstance.storage,\n storageUsage,\n installedVersions: installedVersions.map((versionRecord) => ({\n version: versionRecord.version,\n installDirectory: versionRecord.installDirectory,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n installedAt: versionRecord.installedAt,\n permissions: versionRecord.permissions,\n ...this.readRemoteSourceFields(versionRecord),\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n contentSha256: versionRecord.contentSha256,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\n return await this.withAppOperation(appId, async () => {\n const appRecord = await this.registryService.setEnabled(appId, enabled);\n return {\n appId: appRecord.appId,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n };\n });\n };\n\n rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\n return await this.withAppOperation(appId, async () => {\n const currentRecord = await this.registryService.getApp(appId);\n if (!currentRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (currentRecord.activeVersion === version) {\n return {\n appId,\n activeVersion: version,\n previousVersion: version,\n enabled: currentRecord.enabled,\n rolledBack: false,\n };\n }\n const targetVersion = currentRecord.installedVersions[version];\n if (!targetVersion) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n if (targetVersion.dataSchemaVersion !== currentRecord.defaultInstance.dataSchemaVersion) {\n throw new Error(\n `应用 ${appId}@${version} 需要数据 schema ${targetVersion.dataSchemaVersion},当前实例为 ${currentRecord.defaultInstance.dataSchemaVersion}。没有可用 checkpoint,已阻止回滚。`,\n );\n }\n await this.assertVersionIntegrity(appId, version);\n const appRecord = await this.registryService.activateVersion(appId, version);\n return {\n appId,\n activeVersion: appRecord.activeVersion,\n previousVersion: currentRecord.activeVersion,\n enabled: appRecord.enabled,\n rolledBack: true,\n };\n });\n };\n\n assertVersionIntegrity = async (appId: string, version?: string): Promise<void> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const targetVersion = version ?? appRecord.activeVersion;\n const versionRecord = appRecord.installedVersions[targetVersion];\n if (!versionRecord) {\n throw new Error(`应用 ${appId} 未安装版本 ${targetVersion}。`);\n }\n const contentSha256 = await this.integrityService.assertVersion({\n appId,\n version: targetVersion,\n versionRecord,\n });\n if (!versionRecord.contentSha256) {\n await this.registryService.setVersionContentDigest(appId, targetVersion, contentSha256);\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n };\n\n withAppOperation = async <T>(appId: string, operation: () => Promise<T>): Promise<T> => {\n if (!SAFE_APP_ID_PATTERN.test(appId)) {\n throw new Error(`appId 不是安全的 App 操作标识:${appId}`);\n }\n const lockPath = path.resolve(this.appHomeService.getAppOperationLockPath(appId));\n const activeLocks = this.appOperationContext.getStore();\n if (activeLocks?.has(lockPath)) {\n return await operation();\n }\n return await this.fileLockService.withLock(lockPath, async () => {\n const nextLocks = new Set(activeLocks ?? []);\n nextLocks.add(lockPath);\n return await this.appOperationContext.run(nextLocks, operation);\n });\n };\n\n resolveLaunch = async (\n appReference: string,\n explicitDocumentGrantMap: AppDocumentGrantMap,\n ): Promise<AppLaunchResolution> => {\n const sourceType = await this.installSourceService.detectLocal(appReference, false);\n if (sourceType.kind === \"directory\") {\n return {\n appDirectory: sourceType.appDirectory,\n documentGrantMap: explicitDocumentGrantMap,\n };\n }\n const appRecord = await this.registryService.getApp(appReference);\n if (!appRecord) {\n throw new Error(`未找到应用目录,也未找到已安装应用:${appReference}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appReference}`);\n }\n await this.assertVersionIntegrity(appRecord.appId, appRecord.activeVersion);\n return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n storage: appRecord.defaultInstance.storage,\n documentGrantMap: {\n ...appRecord.grants,\n ...explicitDocumentGrantMap,\n },\n };\n };\n\n private readRemoteSourceFields = (source?: RemoteSourceFields): RemoteSourceFields => ({\n registryUrl: source?.registryUrl,\n bundleUrl: source?.bundleUrl,\n sha256: source?.sha256,\n target: source?.target,\n });\n\n persistGrants = async (\n appId: string | undefined,\n documentGrantMap: AppDocumentGrantMap,\n ): Promise<void> => {\n if (!appId || Object.keys(documentGrantMap).length === 0) {\n return;\n }\n await this.registryService.updateGrants(appId, documentGrantMap);\n };\n\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-installation.service.js","names":[],"sources":["../../src/services/app-installation.service.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { isAppComponentManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppManifestBundle } from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport { AppInstanceStorageService } from \"#app-runtime/services/app-instance-storage.service.js\";\nimport { AppInstallationIntegrityService } from \"#app-runtime/services/app-installation-integrity.service.js\";\nimport { AppInstallationFilesystemService } from \"#app-runtime/services/app-installation-filesystem.service.js\";\nimport { AppInstallationLifecycleService } from \"#app-runtime/services/app-installation-lifecycle.service.js\";\nimport { FileLockService } from \"#app-runtime/services/file-lock.service.js\";\nimport { AppBuildService } from \"#app-runtime/services/app-build.service.js\";\nimport type { AppBundleExtractResult } from \"#app-runtime/types/app-bundle.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport type { AppPublisher } from \"#app-runtime/types/app-remote-registry.types.js\";\nimport type { AppRegistryInstalledVersion } from \"#app-runtime/types/app-registry.types.js\";\nimport { AppRegistryConfigService } from \"#app-runtime/services/app-registry-config.service.js\";\nimport { AppRemoteRegistryClientService } from \"#app-runtime/services/app-remote-registry-client.service.js\";\nimport { AppRegistryService } from \"#app-runtime/services/app-registry.service.js\";\nimport { AppInstallSourceService, type ResolvedAppInstallSource } from \"#app-runtime/services/app-install-source.service.js\";\nimport { resolveAppInstallationContract } from \"#app-runtime/utils/app-installation-contract.utils.js\";\nimport type {\n AppInfoResult,\n AppInstallProgressHandler,\n AppActivationResult,\n AppInstallResult,\n AppLaunchResolution,\n AppUpdateResult,\n AppRollbackResult,\n AppUninstallResult,\n InstalledAppListItem,\n} from \"#app-runtime/types/app-installation.types.js\";\n\nconst SAFE_APP_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\n\ntype AppInstallOptions = {\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n trustedPublisher?: AppPublisher;\n};\n\ntype PreparedAppInstall = {\n source: ResolvedAppInstallSource;\n extractedDirectory: string;\n extractedMetadata: AppBundleExtractResult;\n manifestBundle: AppManifestBundle;\n options: AppInstallOptions;\n};\n\ntype RemoteSourceFields = Pick<AppRegistryInstalledVersion, \"registryUrl\" | \"bundleUrl\" | \"sha256\" | \"target\">;\n\nexport class AppInstallationService {\n private readonly installSourceService: AppInstallSourceService;\n private readonly instanceStorageService: AppInstanceStorageService;\n private readonly fileLockService = new FileLockService();\n private readonly integrityService: AppInstallationIntegrityService;\n private readonly filesystemService: AppInstallationFilesystemService;\n private readonly lifecycleService: AppInstallationLifecycleService;\n private readonly appOperationContext = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly buildService: AppBuildService = new AppBuildService(),\n private readonly registryService: AppRegistryService = new AppRegistryService(appHomeService),\n private readonly registryConfigService: AppRegistryConfigService = new AppRegistryConfigService(appHomeService),\n private readonly remoteRegistryClient: AppRemoteRegistryClientService = new AppRemoteRegistryClientService(new AppRegistryConfigService(appHomeService)),\n instanceStorageService?: AppInstanceStorageService,\n ) {\n this.installSourceService = new AppInstallSourceService(this.manifestService, this.remoteRegistryClient, this.bundleService);\n this.instanceStorageService = instanceStorageService ?? new AppInstanceStorageService(appHomeService);\n this.integrityService = new AppInstallationIntegrityService(manifestService);\n this.filesystemService = new AppInstallationFilesystemService({\n buildService,\n integrityService: this.integrityService,\n manifestService,\n });\n this.lifecycleService = new AppInstallationLifecycleService({\n registryService,\n registryConfigService,\n remoteRegistryClient,\n instanceStorageService: this.instanceStorageService,\n integrityService: this.integrityService,\n install: this.install,\n rollback: this.rollback,\n });\n }\n\n install = async (appSource: string, options: AppInstallOptions = {}): Promise<AppInstallResult> => {\n const { onProgress, registryUrl } = options;\n await onProgress?.(\"resolving\");\n const source = await this.installSourceService.resolve(appSource, registryUrl);\n const tempDirectory = await this.appHomeService.createTemporaryDirectory(\"napp-install-\");\n try {\n const bundlePath = await this.installSourceService.materializeBundle(source, tempDirectory, onProgress);\n await onProgress?.(\"verifying\");\n const extractedDirectory = path.join(tempDirectory, \"bundle\");\n const extractedMetadata = await this.bundleService.extractBundle({\n bundlePath,\n targetDirectory: extractedDirectory,\n });\n await this.filesystemService.materializeDistribution({\n appDirectory: extractedDirectory,\n distributionMode: extractedMetadata.metadata.distributionMode,\n });\n const manifestBundle = await this.manifestService.load(extractedDirectory);\n return await this.installPreparedBundle({\n source,\n extractedDirectory,\n extractedMetadata,\n manifestBundle,\n options,\n });\n } finally {\n await rm(tempDirectory, { recursive: true, force: true });\n }\n };\n\n private installPreparedBundle = async (prepared: PreparedAppInstall): Promise<AppInstallResult> => {\n const appId = prepared.manifestBundle.manifest.id;\n return await this.withAppOperation(appId, async () => await this.commitPreparedInstall(prepared));\n };\n\n private commitPreparedInstall = async (prepared: PreparedAppInstall): Promise<AppInstallResult> => {\n const { extractedDirectory, extractedMetadata, manifestBundle, options, source } = prepared;\n const { onProgress } = options;\n const { id: appId, version } = manifestBundle.manifest;\n const installDirectory = this.appHomeService.getInstallDirectory(appId, version);\n if (source.kind === \"registry\" && appId !== source.registryResolution.appId) {\n throw new Error(`bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${appId}`);\n }\n await onProgress?.(\"installing\");\n const existingRecord = await this.registryService.getApp(appId);\n const { currentPublisher, dataSchemaVersion, publisher } = resolveAppInstallationContract({\n appId,\n existingRecord,\n manifestBundle,\n source,\n trustedPublisher: options.trustedPublisher,\n });\n const instanceDirectory = this.appHomeService.getAppInstanceDirectory(appId, \"default\");\n const instanceExisted = await this.integrityService.pathExists(instanceDirectory);\n const contentSha256 = await this.filesystemService.copyToImmutableInstallDirectory({\n appId,\n appVersion: version,\n extractedDirectory,\n installDirectory,\n target: extractedMetadata.metadata.target?.kind === \"native\" ? extractedMetadata.metadata.target : undefined,\n });\n let defaultInstance: Awaited<ReturnType<AppInstanceStorageService[\"materializeDefaultInstance\"]>> | undefined;\n let registryRecord;\n try {\n defaultInstance = await this.instanceStorageService.materializeDefaultInstance({\n appId,\n publisherId: (currentPublisher ?? publisher)?.id,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\" ? existingRecord.dataDirectory : undefined,\n dataSchemaVersion,\n });\n await onProgress?.(\"finalizing\");\n registryRecord = await this.registryService.upsertInstallation({\n appId,\n name: manifestBundle.manifest.name,\n description: manifestBundle.manifest.description,\n version,\n installDirectory,\n defaultInstance,\n sourceKind: source.kind,\n distributionMode: source.kind === \"registry\" ? source.registryResolution.distributionMode : extractedMetadata.metadata.distributionMode,\n sourceRef: source.sourceRef,\n installedAt: new Date().toISOString(),\n permissions: manifestBundle.manifest.schemaVersion === 1 ? (manifestBundle.manifest.permissions ?? {}) : this.manifestService.resolvePlatformSecurity(manifestBundle.manifest).permissions,\n ...this.readRemoteSourceFields(source.kind === \"registry\" ? source.registryResolution : undefined),\n publisher,\n manifestSchemaVersion: manifestBundle.manifest.schemaVersion,\n components: isAppComponentManifestBundle(manifestBundle)\n ? manifestBundle.components.map((component) => ({\n ...component,\n componentDirectory: path.join(installDirectory, component.path),\n manifestPath: path.join(installDirectory, component.path, component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\"),\n }))\n : undefined,\n primaryPanelId: isAppComponentManifestBundle(manifestBundle) ? manifestBundle.primaryPanelId : undefined,\n security: isAppComponentManifestBundle(manifestBundle) ? this.manifestService.resolvePlatformSecurity(manifestBundle.manifest) : undefined,\n dataSchemaVersion,\n contentSha256,\n activate: options.activate,\n });\n } catch (error) {\n await this.integrityService.removeDirectory(installDirectory);\n if (!instanceExisted && defaultInstance) {\n await this.instanceStorageService.rollbackNewInstance({\n instance: defaultInstance,\n legacyDataDirectory: existingRecord?.defaultInstance.storage.layout === \"legacy\" ? existingRecord.dataDirectory : undefined,\n });\n }\n throw error;\n }\n if (!defaultInstance) {\n throw new Error(`应用 ${appId} 安装完成但缺少默认实例。`);\n }\n const installedVersion = registryRecord.installedVersions[version];\n return {\n appId: registryRecord.appId,\n name: registryRecord.name,\n version,\n installDirectory,\n dataDirectory: defaultInstance.storage.dataDirectory,\n instance: registryRecord.defaultInstance,\n sourceKind: source.kind,\n sourceRef: source.sourceRef,\n distributionMode: installedVersion?.distributionMode,\n permissions: installedVersion?.permissions ?? {},\n ...this.readRemoteSourceFields(installedVersion),\n publisher: installedVersion?.publisher,\n enabled: registryRecord.enabled,\n manifestSchemaVersion: installedVersion?.manifestSchemaVersion ?? 1,\n components: installedVersion?.components,\n primaryPanelId: installedVersion?.primaryPanelId,\n };\n };\n\n update = async (\n appId: string,\n options?: {\n version?: string;\n registryUrl?: string;\n onProgress?: AppInstallProgressHandler;\n activate?: boolean;\n },\n ): Promise<AppUpdateResult> => {\n return await this.withAppOperation(appId, async () => await this.lifecycleService.update(appId, options));\n };\n\n uninstall = async (appId: string, purgeData: boolean): Promise<AppUninstallResult> => {\n return await this.withAppOperation(appId, async () => await this.lifecycleService.uninstall(appId, purgeData));\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: appRecord.installedVersions[appRecord.activeVersion]?.sourceKind ?? \"directory\",\n distributionMode: appRecord.installedVersions[appRecord.activeVersion]?.distributionMode,\n enabled: appRecord.enabled,\n manifestSchemaVersion: appRecord.installedVersions[appRecord.activeVersion]?.manifestSchemaVersion ?? 1,\n primaryPanelId: appRecord.installedVersions[appRecord.activeVersion]?.primaryPanelId,\n }));\n };\n\n reconcileFilesystem = async (): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n let appRecords = await this.registryService.listApps();\n for (const appRecord of appRecords) {\n await this.withAppOperation(appRecord.appId, async () => {\n for (const versionRecord of Object.values(appRecord.installedVersions)) {\n if (!(await this.integrityService.pathExists(versionRecord.installDirectory))) {\n continue;\n }\n if (!versionRecord.contentSha256) {\n const contentSha256 = await this.integrityService.calculateDigest(versionRecord.installDirectory);\n await this.registryService.setVersionContentDigest(appRecord.appId, versionRecord.version, contentSha256);\n }\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n });\n }\n appRecords = await this.registryService.listApps();\n const referencedInstallPaths = new Set(appRecords.flatMap((record) => Object.values(record.installedVersions).map((version) => path.resolve(version.installDirectory))));\n const referencedDataPaths = new Set(appRecords.map((record) => path.resolve(record.defaultInstance.storage.layout === \"instance-v1\" ? record.defaultInstance.storage.instanceDirectory : record.dataDirectory)));\n\n const packageDirectories = await this.filesystemService.readDirectories(this.appHomeService.getPackagesDirectory());\n for (const appDirectory of packageDirectories) {\n await this.filesystemService.reconcileGeneratedSiblings(appDirectory, referencedInstallPaths);\n }\n await this.filesystemService.reconcileGeneratedSiblings(this.appHomeService.getDataDirectory(), referencedDataPaths);\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"instance-v1\") {\n continue;\n }\n await this.filesystemService.reconcileGeneratedSiblings(appRecord.defaultInstance.storage.instanceDirectory, new Set([path.resolve(appRecord.defaultInstance.storage.dataDirectory)]));\n }\n for (const appRecord of appRecords) {\n if (appRecord.defaultInstance.storage.layout !== \"legacy\") {\n continue;\n }\n await this.withAppOperation(appRecord.appId, async () => {\n const currentRecord = await this.registryService.getApp(appRecord.appId);\n if (!currentRecord || currentRecord.defaultInstance.storage.layout !== \"legacy\") {\n return;\n }\n const activeVersion = currentRecord.installedVersions[currentRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${currentRecord.appId}`);\n }\n const instance = await this.instanceStorageService.materializeDefaultInstance({\n appId: currentRecord.appId,\n publisherId: currentRecord.publisher?.id,\n legacyDataDirectory: currentRecord.dataDirectory,\n dataSchemaVersion: currentRecord.defaultInstance.dataSchemaVersion,\n });\n try {\n await this.registryService.upsertInstallation({\n appId: currentRecord.appId,\n name: currentRecord.name,\n description: currentRecord.description,\n version: currentRecord.activeVersion,\n installDirectory: activeVersion.installDirectory,\n defaultInstance: instance,\n sourceKind: activeVersion.sourceKind,\n sourceRef: activeVersion.sourceRef,\n installedAt: activeVersion.installedAt,\n distributionMode: activeVersion.distributionMode,\n permissions: activeVersion.permissions,\n ...this.readRemoteSourceFields(activeVersion),\n publisher: currentRecord.publisher ?? activeVersion.publisher,\n manifestSchemaVersion: activeVersion.manifestSchemaVersion,\n components: activeVersion.components,\n primaryPanelId: activeVersion.primaryPanelId,\n security: activeVersion.security,\n dataSchemaVersion: activeVersion.dataSchemaVersion,\n contentSha256: activeVersion.contentSha256,\n enabled: currentRecord.enabled,\n });\n } catch (error) {\n await this.instanceStorageService.rollbackNewInstance({\n instance,\n legacyDataDirectory: currentRecord.dataDirectory,\n });\n throw error;\n }\n });\n }\n };\n\n info = async (appId: string, options: { measureStorageUsage?: boolean } = {}): 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) => left.version.localeCompare(right.version));\n const storageUsage = options.measureStorageUsage === false ? undefined : await this.instanceStorageService.measureUsage(appRecord.defaultInstance.storage);\n return {\n appId: appRecord.appId,\n name: appRecord.name,\n description: appRecord.description,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n dataDirectory: appRecord.dataDirectory,\n instance: appRecord.defaultInstance,\n storage: appRecord.defaultInstance.storage,\n storageUsage,\n installedVersions: installedVersions.map((versionRecord) => ({\n version: versionRecord.version,\n installDirectory: versionRecord.installDirectory,\n sourceKind: versionRecord.sourceKind,\n distributionMode: versionRecord.distributionMode,\n sourceRef: versionRecord.sourceRef,\n installedAt: versionRecord.installedAt,\n permissions: versionRecord.permissions,\n ...this.readRemoteSourceFields(versionRecord),\n publisher: versionRecord.publisher,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion,\n components: versionRecord.components,\n primaryPanelId: versionRecord.primaryPanelId,\n contentSha256: versionRecord.contentSha256,\n })),\n grants: appRecord.grants,\n };\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppActivationResult> => {\n return await this.withAppOperation(appId, async () => {\n const appRecord = await this.registryService.setEnabled(appId, enabled);\n return {\n appId: appRecord.appId,\n activeVersion: appRecord.activeVersion,\n enabled: appRecord.enabled,\n };\n });\n };\n\n rollback = async (appId: string, version: string): Promise<AppRollbackResult> => {\n return await this.withAppOperation(appId, async () => {\n const currentRecord = await this.registryService.getApp(appId);\n if (!currentRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (currentRecord.activeVersion === version) {\n return {\n appId,\n activeVersion: version,\n previousVersion: version,\n enabled: currentRecord.enabled,\n rolledBack: false,\n };\n }\n const targetVersion = currentRecord.installedVersions[version];\n if (!targetVersion) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n if (targetVersion.dataSchemaVersion !== currentRecord.defaultInstance.dataSchemaVersion) {\n throw new Error(`应用 ${appId}@${version} 需要数据 schema ${targetVersion.dataSchemaVersion},当前实例为 ${currentRecord.defaultInstance.dataSchemaVersion}。没有可用 checkpoint,已阻止回滚。`);\n }\n await this.assertVersionIntegrity(appId, version);\n const appRecord = await this.registryService.activateVersion(appId, version);\n return {\n appId,\n activeVersion: appRecord.activeVersion,\n previousVersion: currentRecord.activeVersion,\n enabled: appRecord.enabled,\n rolledBack: true,\n };\n });\n };\n\n assertVersionIntegrity = async (appId: string, version?: string): Promise<void> => {\n const appRecord = await this.registryService.getApp(appId);\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const targetVersion = version ?? appRecord.activeVersion;\n const versionRecord = appRecord.installedVersions[targetVersion];\n if (!versionRecord) {\n throw new Error(`应用 ${appId} 未安装版本 ${targetVersion}。`);\n }\n const contentSha256 = await this.integrityService.assertVersion({\n appId,\n version: targetVersion,\n versionRecord,\n });\n if (!versionRecord.contentSha256) {\n await this.registryService.setVersionContentDigest(appId, targetVersion, contentSha256);\n await this.integrityService.protectDirectory(versionRecord.installDirectory);\n }\n };\n\n withAppOperation = async <T>(appId: string, operation: () => Promise<T>): Promise<T> => {\n if (!SAFE_APP_ID_PATTERN.test(appId)) {\n throw new Error(`appId 不是安全的 App 操作标识:${appId}`);\n }\n const lockPath = path.resolve(this.appHomeService.getAppOperationLockPath(appId));\n const activeLocks = this.appOperationContext.getStore();\n if (activeLocks?.has(lockPath)) {\n return await operation();\n }\n return await this.fileLockService.withLock(lockPath, async () => {\n const nextLocks = new Set(activeLocks ?? []);\n nextLocks.add(lockPath);\n return await this.appOperationContext.run(nextLocks, operation);\n });\n };\n\n resolveLaunch = async (appReference: string, explicitDocumentGrantMap: AppDocumentGrantMap): Promise<AppLaunchResolution> => {\n const sourceType = await this.installSourceService.detectLocal(appReference, false);\n if (sourceType.kind === \"directory\") {\n return {\n appDirectory: sourceType.appDirectory,\n documentGrantMap: explicitDocumentGrantMap,\n };\n }\n const appRecord = await this.registryService.getApp(appReference);\n if (!appRecord) {\n throw new Error(`未找到应用目录,也未找到已安装应用:${appReference}`);\n }\n const activeVersion = appRecord.installedVersions[appRecord.activeVersion];\n if (!activeVersion) {\n throw new Error(`已安装应用缺少激活版本:${appReference}`);\n }\n await this.assertVersionIntegrity(appRecord.appId, appRecord.activeVersion);\n return {\n appDirectory: activeVersion.installDirectory,\n appId: appRecord.appId,\n dataDirectory: appRecord.dataDirectory,\n storage: appRecord.defaultInstance.storage,\n documentGrantMap: {\n ...Object.fromEntries(Object.entries(appRecord.grants).map(([scopeId, grant]) => [scopeId, grant.path])),\n ...explicitDocumentGrantMap,\n },\n };\n };\n\n private readRemoteSourceFields = (source?: RemoteSourceFields): RemoteSourceFields => ({\n registryUrl: source?.registryUrl,\n bundleUrl: source?.bundleUrl,\n sha256: source?.sha256,\n target: source?.target,\n });\n\n persistGrants = async (appId: string | undefined, documentGrantMap: AppDocumentGrantMap): Promise<void> => {\n if (!appId || Object.keys(documentGrantMap).length === 0) {\n return;\n }\n await this.registryService.updateGrants(appId, documentGrantMap);\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-manifest.service.d.ts","names":[],"sources":["../../src/services/app-manifest.service.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"app-manifest.service.d.ts","names":[],"sources":["../../src/services/app-manifest.service.ts"],"mappings":";;;;cAuBa,kBAAA;EAAA,iBAEQ,qBAAA;cAAA,qBAAA,GAAqB,wBAAA;EAGxC,IAAA,GAAc,YAAA,aAAuB,OAAA,CAAQ,iBAAA;EAU7C,SAAA,GAAa,MAAA,EAAQ,iBAAA,KAAoB,kBAAA;EAAA,QAwCjC,oBAAA;EAAA,QA8BA,mBAAA;EAAA,QA8DA,aAAA;EAgCR,uBAAA,GACE,QAAA,EAAU,oBAAA,KACT,0BAAA;EAAA,QAgCK,mBAAA;EAAA,QAcA,SAAA;EAAA,QAoBA,OAAA;EAAA,QAKA,eAAA;EAAA,QA+BA,YAAA;EAAA,QAUA,iBAAA;EAAA,QAeA,YAAA;EAAA,QAcA,eAAA;EAAA,QAkBA,8BAAA;EAAA,QAsBA,eAAA;EAAA,QA0BA,gBAAA;EAAA,QAcA,mBAAA;EAAA,QAwBA,gBAAA;EAAA,QAoCA,gBAAA;EAAA,QAUA,YAAA;EAAA,QAQA,iBAAA;EAAA,QAeA,kBAAA;EAAA,QAaA,uBAAA;EAAA,QAcA,aAAA;EAAA,QAUA,qBAAA;EAAA,QAWA,YAAA;EAAA,QAOA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAIA,UAAA;EAAA,QAOA,WAAA;AAAA"}
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
//#region src/services/app-manifest.service.ts
|
|
5
5
|
const PACKAGE_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
6
6
|
const COMPONENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7
|
+
const SECRET_SLOT_ID_PATTERN = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
|
|
7
8
|
var AppManifestService = class {
|
|
8
9
|
constructor(platformTargetService = new AppPlatformTargetService()) {
|
|
9
10
|
this.platformTargetService = platformTargetService;
|
|
@@ -248,6 +249,7 @@ var AppManifestService = class {
|
|
|
248
249
|
const candidate = this.assertObject(rawPermissions, "permissions");
|
|
249
250
|
return {
|
|
250
251
|
documentAccess: this.parseDocumentAccess(candidate.documentAccess),
|
|
252
|
+
secrets: this.parseSecretSlots(candidate.secrets),
|
|
251
253
|
allowedDomains: this.parseStringArray(candidate.allowedDomains, "permissions.allowedDomains"),
|
|
252
254
|
storage: this.parseStorage(candidate.storage),
|
|
253
255
|
capabilities: this.parseCapabilities(candidate.capabilities)
|
|
@@ -267,6 +269,31 @@ var AppManifestService = class {
|
|
|
267
269
|
};
|
|
268
270
|
});
|
|
269
271
|
};
|
|
272
|
+
parseSecretSlots = (rawSecrets) => {
|
|
273
|
+
if (rawSecrets === void 0) return;
|
|
274
|
+
if (!Array.isArray(rawSecrets)) throw new Error("permissions.secrets 必须是数组。");
|
|
275
|
+
const slots = rawSecrets.map((rawSlot, index) => {
|
|
276
|
+
const field = `permissions.secrets[${index}]`;
|
|
277
|
+
const candidate = this.assertObject(rawSlot, field);
|
|
278
|
+
if (Object.keys(candidate).find((key) => ![
|
|
279
|
+
"id",
|
|
280
|
+
"title",
|
|
281
|
+
"description",
|
|
282
|
+
"required"
|
|
283
|
+
].includes(key))) throw new Error(`${field} 只能声明 id、title、description 和 required。`);
|
|
284
|
+
const id = this.readRequiredString(candidate.id, `${field}.id`);
|
|
285
|
+
if (!SECRET_SLOT_ID_PATTERN.test(id)) throw new Error(`${field}.id 必须是稳定的 lowercase slot id。`);
|
|
286
|
+
return {
|
|
287
|
+
id,
|
|
288
|
+
title: this.readRequiredString(candidate.title, `${field}.title`),
|
|
289
|
+
description: this.readRequiredString(candidate.description, `${field}.description`),
|
|
290
|
+
required: this.readBoolean(candidate.required, `${field}.required`)
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
const duplicate = slots.find((slot, index) => slots.findIndex((entry) => entry.id === slot.id) !== index);
|
|
294
|
+
if (duplicate) throw new Error(`permissions.secrets 包含重复 slot id:${duplicate.id}`);
|
|
295
|
+
return slots;
|
|
296
|
+
};
|
|
270
297
|
parseStringArray = (rawValue, fieldName) => {
|
|
271
298
|
if (rawValue === void 0) return;
|
|
272
299
|
if (!Array.isArray(rawValue)) throw new Error(`${fieldName} 必须是字符串数组。`);
|