@nextclaw/app-runtime 0.14.1 → 0.16.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 +5 -4
- package/dist/index.js +2 -1
- package/dist/package.js +1 -1
- package/dist/services/app-build.service.d.ts +1 -0
- package/dist/services/app-build.service.d.ts.map +1 -1
- package/dist/services/app-build.service.js +37 -2
- package/dist/services/app-build.service.js.map +1 -1
- package/dist/services/app-grant.service.d.ts.map +1 -1
- package/dist/services/app-grant.service.js +1 -4
- 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-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 +200 -0
- package/dist/services/app-registry-parser.service.js.map +1 -0
- package/dist/services/app-registry.service.d.ts +4 -9
- package/dist/services/app-registry.service.d.ts.map +1 -1
- package/dist/services/app-registry.service.js +40 -164
- package/dist/services/app-registry.service.js.map +1 -1
- package/dist/services/app-runtime-cli.service.js +1 -1
- package/dist/services/app-runtime-cli.service.js.map +1 -1
- package/dist/services/app-runtime-options.service.d.ts.map +1 -1
- package/dist/services/app-runtime-options.service.js +2 -2
- package/dist/services/app-runtime-options.service.js.map +1 -1
- package/dist/services/app-runtime-toolchain.service.d.ts +5 -2
- package/dist/services/app-runtime-toolchain.service.d.ts.map +1 -1
- package/dist/services/app-runtime-toolchain.service.js +36 -4
- package/dist/services/app-runtime-toolchain.service.js.map +1 -1
- package/dist/services/app-rust-wasi-scaffold-template.service.d.ts +24 -0
- package/dist/services/app-rust-wasi-scaffold-template.service.d.ts.map +1 -0
- package/dist/services/app-rust-wasi-scaffold-template.service.js +394 -0
- package/dist/services/app-rust-wasi-scaffold-template.service.js.map +1 -0
- package/dist/services/app-scaffold.service.d.ts +4 -2
- package/dist/services/app-scaffold.service.d.ts.map +1 -1
- package/dist/services/app-scaffold.service.js +20 -4
- package/dist/services/app-scaffold.service.js.map +1 -1
- package/dist/services/platform-auth-state.service.js +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-registry.types.d.ts +8 -1
- package/dist/types/app-registry.types.d.ts.map +1 -1
- package/package.json +2 -1
- package/resources/rust-wasi/Cargo.lock +454 -0
- 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 +120 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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";
|
|
@@ -14,6 +14,7 @@ import { AppInstanceService, AppRunResult } from "./services/app-instance.servic
|
|
|
14
14
|
import { HostBridgeServer } from "./services/host-bridge.service.js";
|
|
15
15
|
import { AppScaffoldFile, AppTsHttpScaffoldTemplateService } from "./services/app-ts-http-scaffold-template.service.js";
|
|
16
16
|
import { AppTsHttpLiteScaffoldTemplateService } from "./services/app-ts-http-lite-scaffold-template.service.js";
|
|
17
|
+
import { AppRustWasiScaffoldTemplateService } from "./services/app-rust-wasi-scaffold-template.service.js";
|
|
17
18
|
import { AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate } from "./services/app-scaffold.service.js";
|
|
18
19
|
import { AppRuntimeOptionsService, BuildCliOptions, CreateCliOptions, GrantCliOptions, InstallCliOptions, JsonOnlyCliOptions, PackCliOptions, PublishCliOptions, RevokeCliOptions, RuntimeCliOptions, UninstallCliOptions, UpdateCliOptions } from "./services/app-runtime-options.service.js";
|
|
19
20
|
import { AppRuntimeCliService } from "./services/app-runtime-cli.service.js";
|
|
@@ -21,12 +22,12 @@ import { CreateCommand } from "./controllers/create.controller.js";
|
|
|
21
22
|
import { AppHomeService } from "./services/app-home.service.js";
|
|
22
23
|
import { APP_STORAGE_LAYOUT_VERSION, AppInstanceInventory, AppInstanceInventoryDiagnostic, AppInstanceInventoryEntry, AppInstanceMetadata, AppInstanceRecord, AppStorageContext, AppStorageLayout, AppStorageUsage, DEFAULT_APP_INSTANCE_ID } from "./types/app-storage.types.js";
|
|
23
24
|
import { AppPublisher, AppRegistryConfig, AppRegistryConfigSnapshot, AppRemoteRegistryArtifact, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, DEFAULT_APP_REGISTRY_URL } from "./types/app-remote-registry.types.js";
|
|
24
|
-
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";
|
|
25
26
|
import { AppRegistryService } from "./services/app-registry.service.js";
|
|
26
27
|
import { AppGrantService } from "./services/app-grant.service.js";
|
|
27
28
|
import { GrantCommand } from "./controllers/grant.controller.js";
|
|
28
29
|
import { AppInstanceStorageService } from "./services/app-instance-storage.service.js";
|
|
29
|
-
import { AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeToolStatus, AppRuntimeToolchainService } from "./services/app-runtime-toolchain.service.js";
|
|
30
|
+
import { AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeToolStatus, AppRuntimeToolchainProfile, AppRuntimeToolchainService } from "./services/app-runtime-toolchain.service.js";
|
|
30
31
|
import { AppBuildResult, AppBuildService } from "./services/app-build.service.js";
|
|
31
32
|
import { AppRegistryConfigService } from "./services/app-registry-config.service.js";
|
|
32
33
|
import { AppRemoteRegistryClientService } from "./services/app-remote-registry-client.service.js";
|
|
@@ -57,4 +58,4 @@ import { AppServiceLaunch, AppServiceLaunchService } from "./services/app-servic
|
|
|
57
58
|
import { AppInstanceInventoryService } from "./services/app-instance-inventory.service.js";
|
|
58
59
|
import { FileLockBusyError, FileLockLease, FileLockService } from "./services/file-lock.service.js";
|
|
59
60
|
import { UiServerService } from "./services/ui-server.service.js";
|
|
60
|
-
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, AppRuntimeToolchainService, 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, 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/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { AppBundleService } from "./services/app-bundle.service.js";
|
|
|
7
7
|
import { HostBridgeServer } from "./services/host-bridge.service.js";
|
|
8
8
|
import { AppTsHttpScaffoldTemplateService } from "./services/app-ts-http-scaffold-template.service.js";
|
|
9
9
|
import { AppTsHttpLiteScaffoldTemplateService } from "./services/app-ts-http-lite-scaffold-template.service.js";
|
|
10
|
+
import { AppRustWasiScaffoldTemplateService } from "./services/app-rust-wasi-scaffold-template.service.js";
|
|
10
11
|
import { AppScaffoldService } from "./services/app-scaffold.service.js";
|
|
11
12
|
import { CreateCommand } from "./controllers/create.controller.js";
|
|
12
13
|
import { UiServerService } from "./services/ui-server.service.js";
|
|
@@ -52,4 +53,4 @@ import { ValidatePublishCommand } from "./controllers/validate-publish.controlle
|
|
|
52
53
|
import { AppRuntimeOptionsService } from "./services/app-runtime-options.service.js";
|
|
53
54
|
import { AppRuntimeCliService } from "./services/app-runtime-cli.service.js";
|
|
54
55
|
import { AppInstanceInventoryService } from "./services/app-instance-inventory.service.js";
|
|
55
|
-
export { APP_STORAGE_LAYOUT_VERSION, AppArtifactValidationService, AppBuildService, AppBundleService, AppGrantService, AppHomeService, AppHostService, AppInstallationIntegrityService, AppInstallationService, AppInstanceInventoryService, AppInstanceService, AppInstanceStorageService, AppManifestService, AppMarketplaceClientService, AppMarketplaceMetadataService, AppPermissionsService, AppPlatformTargetService, AppPublishArtifactService, AppPublishService, AppPublishValidationService, AppRegistryConfigService, AppRegistryService, AppRemoteRegistryClientService, AppRuntimeCliService, AppRuntimeOptionsService, AppRuntimeToolchainService, AppScaffoldService, AppServiceLaunchService, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, CreateCommand, DEFAULT_APP_INSTANCE_ID, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, FileLockBusyError, FileLockService, GrantCommand, HostBridgeServer, InfoCommand, InstallCommand, ListCommand, MainRunnerService, PackCommand, PermissionsCommand, PlatformAuthStateService, PublishCommand, RegistryCommand, RevokeCommand, UiServerService, UninstallCommand, UpdateCommand, ValidatePublishCommand, WasmMainRunnerService, WasmSidecarClientService, WasmtimeWasiHttpComponentService, isAppComponentManifestBundle, isAppStandaloneManifestBundle };
|
|
56
|
+
export { APP_STORAGE_LAYOUT_VERSION, AppArtifactValidationService, AppBuildService, AppBundleService, AppGrantService, AppHomeService, AppHostService, AppInstallationIntegrityService, AppInstallationService, AppInstanceInventoryService, AppInstanceService, AppInstanceStorageService, AppManifestService, AppMarketplaceClientService, AppMarketplaceMetadataService, AppPermissionsService, AppPlatformTargetService, AppPublishArtifactService, AppPublishService, AppPublishValidationService, AppRegistryConfigService, AppRegistryService, AppRemoteRegistryClientService, AppRuntimeCliService, AppRuntimeOptionsService, AppRuntimeToolchainService, AppRustWasiScaffoldTemplateService, AppScaffoldService, AppServiceLaunchService, AppTsHttpLiteScaffoldTemplateService, AppTsHttpScaffoldTemplateService, CreateCommand, DEFAULT_APP_INSTANCE_ID, DEFAULT_APP_MARKETPLACE_API_BASE, DEFAULT_APP_REGISTRY_URL, FileLockBusyError, FileLockService, GrantCommand, HostBridgeServer, InfoCommand, InstallCommand, ListCommand, MainRunnerService, PackCommand, PermissionsCommand, PlatformAuthStateService, PublishCommand, RegistryCommand, RevokeCommand, UiServerService, UninstallCommand, UpdateCommand, ValidatePublishCommand, WasmMainRunnerService, WasmSidecarClientService, WasmtimeWasiHttpComponentService, isAppComponentManifestBundle, isAppStandaloneManifestBundle };
|
package/dist/package.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-build.service.d.ts","names":[],"sources":["../../src/services/app-build.service.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"app-build.service.d.ts","names":[],"sources":["../../src/services/app-build.service.ts"],"mappings":";;;;KASY,cAAA;EACV,YAAA;EACA,QAAA;EACA,aAAA;EACA,qBAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,cAGW,eAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,gBAAA;cADA,eAAA,GAAiB,kBAAA,EACjB,gBAAA,GAAkB,0BAAA;EAGrC,KAAA,GAAe,MAAA;IACb,YAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,cAAA;EAAA,QA8CJ,sBAAA;EAAA,QA4CA,UAAA;AAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AppManifestService } from "./app-manifest.service.js";
|
|
2
|
-
import { isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
|
|
2
|
+
import { isAppComponentManifestBundle, isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
|
|
3
3
|
import { AppRuntimeToolchainService } from "./app-runtime-toolchain.service.js";
|
|
4
|
-
import { access } from "node:fs/promises";
|
|
4
|
+
import { access, copyFile, readFile } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
//#region src/services/app-build.service.ts
|
|
7
7
|
var AppBuildService = class {
|
|
@@ -12,6 +12,7 @@ var AppBuildService = class {
|
|
|
12
12
|
build = async (params) => {
|
|
13
13
|
const appDirectory = path.resolve(params.appDirectory);
|
|
14
14
|
const bundle = await this.manifestService.load(appDirectory);
|
|
15
|
+
if (isAppComponentManifestBundle(bundle)) return await this.buildRustWasiComponent(appDirectory, bundle.components);
|
|
15
16
|
if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包必须提交可直接运行的 bundle,不能执行本地 build。");
|
|
16
17
|
if (bundle.manifest.main.kind !== "wasi-http-component") return {
|
|
17
18
|
appDirectory,
|
|
@@ -44,6 +45,40 @@ var AppBuildService = class {
|
|
|
44
45
|
built: true
|
|
45
46
|
};
|
|
46
47
|
};
|
|
48
|
+
buildRustWasiComponent = async (appDirectory, components) => {
|
|
49
|
+
const serviceComponents = components.filter((component) => component.kind === "service");
|
|
50
|
+
if (serviceComponents.length !== 1) throw new Error(`Rust/WASI scaffold build requires exactly one Service Component; found ${serviceComponents.length}.`);
|
|
51
|
+
const guestDirectory = path.join(appDirectory, "guest");
|
|
52
|
+
const cargoTomlPath = path.join(guestDirectory, "Cargo.toml");
|
|
53
|
+
const cargoLockPath = path.join(guestDirectory, "Cargo.lock");
|
|
54
|
+
await Promise.all([access(cargoTomlPath), access(cargoLockPath)]);
|
|
55
|
+
await this.toolchainService.assertReadyForWasiComponentBuild();
|
|
56
|
+
await this.toolchainService.runCommand({
|
|
57
|
+
command: "cargo",
|
|
58
|
+
args: [
|
|
59
|
+
"build",
|
|
60
|
+
"--locked",
|
|
61
|
+
"--release",
|
|
62
|
+
"--target",
|
|
63
|
+
"wasm32-wasip2"
|
|
64
|
+
],
|
|
65
|
+
cwd: guestDirectory
|
|
66
|
+
});
|
|
67
|
+
const cargoToml = await readFile(cargoTomlPath, "utf8");
|
|
68
|
+
const crateName = /^name\s*=\s*"([^"]+)"/m.exec(cargoToml)?.[1];
|
|
69
|
+
if (!crateName) throw new Error("Rust Guest Cargo.toml must declare package.name.");
|
|
70
|
+
const compiledPath = path.join(guestDirectory, "target", "wasm32-wasip2", "release", `${crateName.replace(/-/g, "_")}.wasm`);
|
|
71
|
+
const mainEntryPath = path.join(serviceComponents[0].componentDirectory, "service.wasm");
|
|
72
|
+
await access(compiledPath);
|
|
73
|
+
await copyFile(compiledPath, mainEntryPath);
|
|
74
|
+
return {
|
|
75
|
+
appDirectory,
|
|
76
|
+
mainKind: "wasi-component",
|
|
77
|
+
mainEntryPath,
|
|
78
|
+
installedDependencies: false,
|
|
79
|
+
built: true
|
|
80
|
+
};
|
|
81
|
+
};
|
|
47
82
|
pathExists = async (targetPath) => {
|
|
48
83
|
try {
|
|
49
84
|
await access(targetPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-build.service.js","names":[],"sources":["../../src/services/app-build.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"app-build.service.js","names":[],"sources":["../../src/services/app-build.service.ts"],"sourcesContent":["import { access, copyFile, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport {\n isAppComponentManifestBundle,\n isAppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\nimport { AppRuntimeToolchainService } from \"./app-runtime-toolchain.service.js\";\n\nexport type AppBuildResult = {\n appDirectory: string;\n mainKind: string;\n mainEntryPath: string;\n installedDependencies: boolean;\n built: boolean;\n skippedReason?: string;\n};\n\nexport class AppBuildService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly toolchainService: AppRuntimeToolchainService = new AppRuntimeToolchainService(),\n ) {}\n\n build = async (params: {\n appDirectory: string;\n install: boolean;\n }): Promise<AppBuildResult> => {\n const appDirectory = path.resolve(params.appDirectory);\n const bundle = await this.manifestService.load(appDirectory);\n if (isAppComponentManifestBundle(bundle)) {\n return await this.buildRustWasiComponent(appDirectory, bundle.components);\n }\n if (!isAppStandaloneManifestBundle(bundle)) {\n throw new Error(\"schema v2 组合包必须提交可直接运行的 bundle,不能执行本地 build。\");\n }\n if (bundle.manifest.main.kind !== \"wasi-http-component\") {\n return {\n appDirectory,\n mainKind: bundle.manifest.main.kind,\n mainEntryPath: bundle.mainEntryPath,\n installedDependencies: false,\n built: false,\n skippedReason: \"main.kind=wasm 应用不需要 TS/WASI HTTP 构建。\",\n };\n }\n\n await this.toolchainService.assertReadyForWasiHttpBuild();\n const mainDirectory = path.join(appDirectory, \"main\");\n await access(path.join(mainDirectory, \"package.json\"));\n const shouldInstall = params.install || !(await this.pathExists(path.join(mainDirectory, \"node_modules\")));\n if (shouldInstall) {\n await this.toolchainService.runCommand({\n command: \"npm\",\n args: [\"install\"],\n cwd: mainDirectory,\n });\n }\n await this.toolchainService.runCommand({\n command: \"npm\",\n args: [\"run\", \"build\"],\n cwd: mainDirectory,\n });\n await access(bundle.mainEntryPath);\n return {\n appDirectory,\n mainKind: bundle.manifest.main.kind,\n mainEntryPath: bundle.mainEntryPath,\n installedDependencies: shouldInstall,\n built: true,\n };\n };\n\n private buildRustWasiComponent = async (\n appDirectory: string,\n components: Array<{ kind: \"panel\" | \"service\"; componentDirectory: string }>,\n ): Promise<AppBuildResult> => {\n const serviceComponents = components.filter((component) => component.kind === \"service\");\n if (serviceComponents.length !== 1) {\n throw new Error(\n `Rust/WASI scaffold build requires exactly one Service Component; found ${serviceComponents.length}.`,\n );\n }\n const guestDirectory = path.join(appDirectory, \"guest\");\n const cargoTomlPath = path.join(guestDirectory, \"Cargo.toml\");\n const cargoLockPath = path.join(guestDirectory, \"Cargo.lock\");\n await Promise.all([access(cargoTomlPath), access(cargoLockPath)]);\n await this.toolchainService.assertReadyForWasiComponentBuild();\n await this.toolchainService.runCommand({\n command: \"cargo\",\n args: [\"build\", \"--locked\", \"--release\", \"--target\", \"wasm32-wasip2\"],\n cwd: guestDirectory,\n });\n const cargoToml = await readFile(cargoTomlPath, \"utf8\");\n const crateName = /^name\\s*=\\s*\"([^\"]+)\"/m.exec(cargoToml)?.[1];\n if (!crateName) {\n throw new Error(\"Rust Guest Cargo.toml must declare package.name.\");\n }\n const compiledPath = path.join(\n guestDirectory,\n \"target\",\n \"wasm32-wasip2\",\n \"release\",\n `${crateName.replace(/-/g, \"_\")}.wasm`,\n );\n const mainEntryPath = path.join(serviceComponents[0]!.componentDirectory, \"service.wasm\");\n await access(compiledPath);\n await copyFile(compiledPath, mainEntryPath);\n return {\n appDirectory,\n mainKind: \"wasi-component\",\n mainEntryPath,\n installedDependencies: false,\n built: true,\n };\n };\n\n private pathExists = async (targetPath: string): Promise<boolean> => {\n try {\n await access(targetPath);\n return true;\n } catch {\n return false;\n }\n };\n}\n"],"mappings":";;;;;;AAkBA,IAAa,kBAAb,MAA6B;CAC3B,YACE,kBAAuD,IAAI,oBAAoB,EAC/E,mBAAgE,IAAI,4BAA4B,EAChG;AAFiB,OAAA,kBAAA;AACA,OAAA,mBAAA;;CAGnB,QAAQ,OAAO,WAGgB;EAC7B,MAAM,eAAe,KAAK,QAAQ,OAAO,aAAa;EACtD,MAAM,SAAS,MAAM,KAAK,gBAAgB,KAAK,aAAa;AAC5D,MAAI,6BAA6B,OAAO,CACtC,QAAO,MAAM,KAAK,uBAAuB,cAAc,OAAO,WAAW;AAE3E,MAAI,CAAC,8BAA8B,OAAO,CACxC,OAAM,IAAI,MAAM,+CAA+C;AAEjE,MAAI,OAAO,SAAS,KAAK,SAAS,sBAChC,QAAO;GACL;GACA,UAAU,OAAO,SAAS,KAAK;GAC/B,eAAe,OAAO;GACtB,uBAAuB;GACvB,OAAO;GACP,eAAe;GAChB;AAGH,QAAM,KAAK,iBAAiB,6BAA6B;EACzD,MAAM,gBAAgB,KAAK,KAAK,cAAc,OAAO;AACrD,QAAM,OAAO,KAAK,KAAK,eAAe,eAAe,CAAC;EACtD,MAAM,gBAAgB,OAAO,WAAW,CAAE,MAAM,KAAK,WAAW,KAAK,KAAK,eAAe,eAAe,CAAC;AACzG,MAAI,cACF,OAAM,KAAK,iBAAiB,WAAW;GACrC,SAAS;GACT,MAAM,CAAC,UAAU;GACjB,KAAK;GACN,CAAC;AAEJ,QAAM,KAAK,iBAAiB,WAAW;GACrC,SAAS;GACT,MAAM,CAAC,OAAO,QAAQ;GACtB,KAAK;GACN,CAAC;AACF,QAAM,OAAO,OAAO,cAAc;AAClC,SAAO;GACL;GACA,UAAU,OAAO,SAAS,KAAK;GAC/B,eAAe,OAAO;GACtB,uBAAuB;GACvB,OAAO;GACR;;CAGH,yBAAiC,OAC/B,cACA,eAC4B;EAC5B,MAAM,oBAAoB,WAAW,QAAQ,cAAc,UAAU,SAAS,UAAU;AACxF,MAAI,kBAAkB,WAAW,EAC/B,OAAM,IAAI,MACR,0EAA0E,kBAAkB,OAAO,GACpG;EAEH,MAAM,iBAAiB,KAAK,KAAK,cAAc,QAAQ;EACvD,MAAM,gBAAgB,KAAK,KAAK,gBAAgB,aAAa;EAC7D,MAAM,gBAAgB,KAAK,KAAK,gBAAgB,aAAa;AAC7D,QAAM,QAAQ,IAAI,CAAC,OAAO,cAAc,EAAE,OAAO,cAAc,CAAC,CAAC;AACjE,QAAM,KAAK,iBAAiB,kCAAkC;AAC9D,QAAM,KAAK,iBAAiB,WAAW;GACrC,SAAS;GACT,MAAM;IAAC;IAAS;IAAY;IAAa;IAAY;IAAgB;GACrE,KAAK;GACN,CAAC;EACF,MAAM,YAAY,MAAM,SAAS,eAAe,OAAO;EACvD,MAAM,YAAY,yBAAyB,KAAK,UAAU,GAAG;AAC7D,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,mDAAmD;EAErE,MAAM,eAAe,KAAK,KACxB,gBACA,UACA,iBACA,WACA,GAAG,UAAU,QAAQ,MAAM,IAAI,CAAC,OACjC;EACD,MAAM,gBAAgB,KAAK,KAAK,kBAAkB,GAAI,oBAAoB,eAAe;AACzF,QAAM,OAAO,aAAa;AAC1B,QAAM,SAAS,cAAc,cAAc;AAC3C,SAAO;GACL;GACA,UAAU;GACV;GACA,uBAAuB;GACvB,OAAO;GACR;;CAGH,aAAqB,OAAO,eAAyC;AACnE,MAAI;AACF,SAAM,OAAO,WAAW;AACxB,UAAO;UACD;AACN,UAAO"}
|
|
@@ -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;EA0C3C,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,OAAA;IACA,aAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;EA2BZ,mBAAA,GAA6B,MAAA;IAC3B,KAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,8BAAA;AAAA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
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
3
|
import { access } from "node:fs/promises";
|
|
5
4
|
import path from "node:path";
|
|
@@ -14,9 +13,7 @@ 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,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-grant.service.js","names":[],"sources":["../../src/services/app-grant.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"app-grant.service.js","names":[],"sources":["../../src/services/app-grant.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { 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: (requestedPermissions.documentAccess ?? []).map(\n (scope) => ({\n id: scope.id,\n mode: scope.mode,\n description: scope.description,\n granted: Boolean(appRecord.grants[scope.id]),\n grantedPath: appRecord.grants[scope.id],\n }),\n ),\n allowedDomains: requestedPermissions.allowedDomains ?? [],\n storage: {\n enabled:\n requestedPermissions.storage !== undefined &&\n requestedPermissions.storage !== false,\n namespace:\n typeof requestedPermissions.storage === \"object\"\n ? requestedPermissions.storage.namespace\n : undefined,\n },\n capabilities: {\n hostBridge: requestedPermissions.capabilities?.hostBridge !== false,\n },\n };\n };\n\n grantDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n directoryPath: string;\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId, directoryPath } = params;\n const permissionState = await this.summarize(appId);\n const requestedScope = permissionState.documentAccess.find(\n (scope) => scope.id === scopeId,\n );\n if (!requestedScope) {\n throw new Error(`应用 ${appId} 未声明 documentAccess scope:${scopeId}`);\n }\n const normalizedDirectory = path.resolve(directoryPath);\n try {\n await access(normalizedDirectory);\n } catch {\n throw new Error(`授权目录不存在:${normalizedDirectory}`);\n }\n await this.registryService.setDocumentGrant(\n appId,\n scopeId,\n normalizedDirectory,\n );\n return {\n appId,\n scopeId,\n grantedPath: normalizedDirectory,\n };\n };\n\n revokeDocumentScope = async (params: {\n appId: string;\n scopeId: string;\n }): Promise<AppDocumentGrantMutationResult> => {\n const { appId, scopeId } = params;\n await this.summarize(appId);\n const removed = await this.registryService.removeDocumentGrant(\n appId,\n scopeId,\n );\n return {\n appId,\n scopeId,\n removed,\n };\n };\n}\n"],"mappings":";;;;;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,iBAAiB,qBAAqB,kBAAkB,EAAE,EAAE,KACzD,WAAW;IACV,IAAI,MAAM;IACV,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,SAAS,QAAQ,UAAU,OAAO,MAAM,IAAI;IAC5C,aAAa,UAAU,OAAO,MAAM;IACrC,EACF;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,WAImB;EAC7C,MAAM,EAAE,OAAO,SAAS,kBAAkB;AAK1C,MAAI,EAJoB,MAAM,KAAK,UAAU,MAAM,EACZ,eAAe,MACnD,UAAU,MAAM,OAAO,QACzB,CAEC,OAAM,IAAI,MAAM,MAAM,MAAM,4BAA4B,UAAU;EAEpE,MAAM,sBAAsB,KAAK,QAAQ,cAAc;AACvD,MAAI;AACF,SAAM,OAAO,oBAAoB;UAC3B;AACN,SAAM,IAAI,MAAM,WAAW,sBAAsB;;AAEnD,QAAM,KAAK,gBAAgB,iBACzB,OACA,SACA,oBACD;AACD,SAAO;GACL;GACA;GACA,aAAa;GACd;;CAGH,sBAAsB,OAAO,WAGkB;EAC7C,MAAM,EAAE,OAAO,YAAY;AAC3B,QAAM,KAAK,UAAU,MAAM;AAK3B,SAAO;GACL;GACA;GACA,SAPc,MAAM,KAAK,gBAAgB,oBACzC,OACA,QACD;GAKA"}
|
|
@@ -1,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":"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"}
|
|
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-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} 必须是字符串数组。`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-manifest.service.js","names":[],"sources":["../../src/services/app-manifest.service.ts"],"sourcesContent":["import { access, lstat, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppPlatformTargetService } from \"#app-runtime/services/app-platform-target.service.js\";\nimport type {\n AppComponentManifest,\n AppComponentManifestBundle,\n AppComponentReference,\n AppDocumentAccessScope,\n AppManifest,\n AppManifestBundle,\n AppManifestSummary,\n AppPermissions,\n AppPlatformSecuritySummary,\n AppResolvedComponent,\n AppStandaloneManifest,\n AppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\n\nconst PACKAGE_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\nconst COMPONENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nexport class AppManifestService {\n constructor(\n private readonly platformTargetService = new AppPlatformTargetService(),\n ) {}\n\n load = async (appDirectory: string): Promise<AppManifestBundle> => {\n const normalizedDirectory = path.resolve(appDirectory);\n const manifestPath = path.join(normalizedDirectory, \"manifest.json\");\n const rawManifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as unknown;\n const manifest = this.parseManifest(rawManifest);\n return manifest.schemaVersion === 1\n ? await this.loadStandaloneBundle(normalizedDirectory, manifestPath, manifest)\n : await this.loadComponentBundle(normalizedDirectory, manifestPath, manifest);\n };\n\n summarize = (bundle: AppManifestBundle): AppManifestSummary => {\n if (bundle.manifest.schemaVersion === 2) {\n const componentBundle = bundle as AppComponentManifestBundle;\n return {\n schemaVersion: 2,\n id: componentBundle.manifest.id,\n name: componentBundle.manifest.name,\n version: componentBundle.manifest.version,\n description: componentBundle.manifest.description,\n manifestPath: componentBundle.manifestPath,\n iconPath: componentBundle.iconPath,\n primaryPanelId: componentBundle.primaryPanelId,\n components: componentBundle.components,\n distribution: this.platformTargetService.resolveDistribution(\n componentBundle.manifest.distribution,\n ),\n security: this.resolvePlatformSecurity(componentBundle.manifest),\n };\n }\n const standaloneBundle = bundle as AppStandaloneManifestBundle;\n const permissions = standaloneBundle.manifest.permissions ?? {};\n return {\n schemaVersion: 1,\n id: standaloneBundle.manifest.id,\n name: standaloneBundle.manifest.name,\n version: standaloneBundle.manifest.version,\n description: standaloneBundle.manifest.description,\n mainKind: standaloneBundle.manifest.main.kind,\n action:\n standaloneBundle.manifest.main.kind === \"wasm\"\n ? standaloneBundle.manifest.main.action\n : undefined,\n manifestPath: standaloneBundle.manifestPath,\n mainEntryPath: standaloneBundle.mainEntryPath,\n uiEntryPath: standaloneBundle.uiEntryPath,\n iconPath: standaloneBundle.iconPath,\n permissions,\n };\n };\n\n private loadStandaloneBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppStandaloneManifest,\n ): Promise<AppStandaloneManifestBundle> => {\n const mainEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.main.entry,\n \"main.entry\",\n );\n const uiEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.ui.entry,\n \"ui.entry\",\n );\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n mainEntryPath,\n uiEntryPath,\n uiDirectoryPath: path.dirname(uiEntryPath),\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n };\n };\n\n private loadComponentBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppComponentManifest,\n ): Promise<AppComponentManifestBundle> => {\n const components: AppResolvedComponent[] = [];\n for (const [index, component] of manifest.components.entries()) {\n const componentDirectory = await this.assertResolvedDirectory(\n appDirectory,\n component.path,\n `components[${index}].path`,\n );\n const componentId = await this.readComponentId(component, componentDirectory);\n const expectedPrefix = `${manifest.id.replace(/[^a-z0-9]+/g, \"-\")}-`;\n if (!componentId.startsWith(expectedPrefix)) {\n throw new Error(\n `组件 ${componentId} 必须使用包前缀 ${expectedPrefix}。`,\n );\n }\n components.push({\n ...component,\n id: componentId,\n componentDirectory,\n manifestPath: path.join(\n componentDirectory,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n });\n }\n const duplicateId = components.find(\n (component, index) => components.findIndex((entry) => entry.id === component.id) !== index,\n );\n if (duplicateId) {\n throw new Error(`components 包含重复组件 id:${duplicateId.id}`);\n }\n const panelIds = components\n .filter((component) => component.kind === \"panel\")\n .map((component) => component.id);\n const declaredPrimaryPanel = manifest.presentation?.primaryPanel;\n if (declaredPrimaryPanel && !panelIds.includes(declaredPrimaryPanel)) {\n throw new Error(\"presentation.primaryPanel 必须引用真实 Panel component id。\");\n }\n if (panelIds.length > 1 && !declaredPrimaryPanel) {\n throw new Error(\"包含多个 Panel 时必须声明 presentation.primaryPanel。\");\n }\n if (panelIds.length === 0 && declaredPrimaryPanel) {\n throw new Error(\"不含 Panel 的包不能声明 presentation.primaryPanel。\");\n }\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n components,\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n primaryPanelId: declaredPrimaryPanel ?? panelIds[0],\n };\n };\n\n private parseManifest = (rawManifest: unknown): AppManifest => {\n const candidate = this.assertObject(rawManifest, \"manifest.json\");\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"schemaVersion\");\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n throw new Error(\"当前只支持 schemaVersion = 1 或 2。\");\n }\n const common = this.parseCommonManifest(candidate);\n if (schemaVersion === 1) {\n return {\n schemaVersion: 1,\n ...common,\n main: this.parseMain(candidate.main),\n ui: this.parseUi(candidate.ui),\n permissions: this.parsePermissions(candidate.permissions),\n };\n }\n const components = this.parseComponents(candidate.components);\n const runtime = this.parseRuntime(candidate.runtime);\n this.assertRuntimeMatchesComponents(runtime, components);\n return {\n schemaVersion: 2,\n ...common,\n engines: this.parseEngines(candidate.engines),\n presentation: this.parsePresentation(candidate.presentation),\n runtime,\n distribution: this.platformTargetService.parseDistribution(candidate.distribution),\n storage: this.parseAppStorage(candidate.storage),\n permissions: this.parsePermissions(candidate.permissions),\n components,\n };\n };\n\n resolvePlatformSecurity = (\n manifest: AppComponentManifest,\n ): AppPlatformSecuritySummary => {\n const hasServiceComponents = manifest.components.some(\n (component) => component.kind === \"service\",\n );\n const runtimeProfile = manifest.runtime?.profile ?? (\n hasServiceComponents ? \"native-process\" : \"panel-only\"\n );\n const isolation = runtimeProfile === \"panel-only\"\n ? \"sandboxed\"\n : runtimeProfile === \"wasi\"\n ? \"host-mediated\"\n : \"full-user\";\n const declaredPermissions = manifest.permissions ?? {};\n const permissions: AppPermissions = runtimeProfile === \"native-process\"\n ? {\n ...declaredPermissions,\n storage: declaredPermissions.storage ?? true,\n capabilities: {\n ...declaredPermissions.capabilities,\n nativeProcess: true,\n },\n }\n : declaredPermissions;\n return {\n runtimeProfile,\n isolation,\n hasServiceComponents,\n inferred: manifest.runtime === undefined,\n permissions,\n };\n };\n\n private parseCommonManifest = (candidate: Record<string, unknown>) => {\n const id = this.readRequiredString(candidate.id, \"id\");\n if (!PACKAGE_ID_PATTERN.test(id)) {\n throw new Error(\"id 必须由小写字母、数字、点或连字符组成。\");\n }\n return {\n id,\n name: this.readRequiredString(candidate.name, \"name\"),\n version: this.readRequiredString(candidate.version, \"version\"),\n description: this.readOptionalString(candidate.description, \"description\"),\n icon: this.readOptionalString(candidate.icon, \"icon\"),\n };\n };\n\n private parseMain = (rawMain: unknown): AppStandaloneManifest[\"main\"] => {\n const candidate = this.assertObject(rawMain, \"main\");\n const kind = this.readRequiredString(candidate.kind, \"main.kind\");\n if (kind === \"wasm\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n export: this.readRequiredString(candidate.export, \"main.export\"),\n action: this.readRequiredString(candidate.action, \"main.action\"),\n };\n }\n if (kind === \"wasi-http-component\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n };\n }\n throw new Error(\"当前 main.kind 只支持 wasm 或 wasi-http-component。\");\n };\n\n private parseUi = (rawUi: unknown): AppStandaloneManifest[\"ui\"] => {\n const candidate = this.assertObject(rawUi, \"ui\");\n return { entry: this.readRequiredString(candidate.entry, \"ui.entry\") };\n };\n\n private parseComponents = (rawComponents: unknown): AppComponentReference[] => {\n if (!Array.isArray(rawComponents) || rawComponents.length === 0) {\n throw new Error(\"components 必须是非空数组。\");\n }\n const components = rawComponents.map((rawComponent, index) => {\n const candidate = this.assertObject(rawComponent, `components[${index}]`);\n const kind = this.readRequiredString(candidate.kind, `components[${index}].kind`);\n if (kind !== \"panel\" && kind !== \"service\") {\n throw new Error(`components[${index}].kind 只支持 panel 或 service。`);\n }\n const normalizedKind: AppComponentReference[\"kind\"] = kind;\n return {\n kind: normalizedKind,\n path: this.normalizeRelativePath(\n this.readRequiredString(candidate.path, `components[${index}].path`),\n `components[${index}].path`,\n ),\n };\n });\n for (let leftIndex = 0; leftIndex < components.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < components.length; rightIndex += 1) {\n const left = components[leftIndex]?.path;\n const right = components[rightIndex]?.path;\n if (left && right && (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`))) {\n throw new Error(`components path 不能重复或重叠:${left} / ${right}`);\n }\n }\n }\n return components;\n };\n\n private parseEngines = (\n rawEngines: unknown,\n ): AppComponentManifest[\"engines\"] => {\n if (rawEngines === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawEngines, \"engines\");\n return { nextclaw: this.readOptionalString(candidate.nextclaw, \"engines.nextclaw\") };\n };\n\n private parsePresentation = (\n rawPresentation: unknown,\n ): AppComponentManifest[\"presentation\"] => {\n if (rawPresentation === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPresentation, \"presentation\");\n return {\n primaryPanel: this.readOptionalString(\n candidate.primaryPanel,\n \"presentation.primaryPanel\",\n ),\n };\n };\n\n private parseRuntime = (\n rawRuntime: unknown,\n ): AppComponentManifest[\"runtime\"] => {\n if (rawRuntime === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawRuntime, \"runtime\");\n const profile = this.readRequiredString(candidate.profile, \"runtime.profile\");\n if (profile !== \"panel-only\" && profile !== \"wasi\" && profile !== \"native-process\") {\n throw new Error(\"runtime.profile 只支持 panel-only、wasi 或 native-process。\");\n }\n return { profile };\n };\n\n private parseAppStorage = (\n rawStorage: unknown,\n ): AppComponentManifest[\"storage\"] => {\n if (rawStorage === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawStorage, \"storage\");\n const scope = this.readRequiredString(candidate.scope, \"storage.scope\");\n if (scope !== \"global\") {\n throw new Error(\"当前 storage.scope 只支持 global。\");\n }\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"storage.schemaVersion\");\n if (!Number.isSafeInteger(schemaVersion) || schemaVersion < 1) {\n throw new Error(\"storage.schemaVersion 必须是正整数。\");\n }\n return { scope, schemaVersion };\n };\n\n private assertRuntimeMatchesComponents = (\n runtime: AppComponentManifest[\"runtime\"],\n components: AppComponentReference[],\n ): void => {\n if (!runtime) {\n return;\n }\n const hasService = components.some((component) => component.kind === \"service\");\n if (runtime.profile === \"panel-only\" && hasService) {\n throw new Error(\"runtime.profile=panel-only 不能包含 Service component。\");\n }\n if (runtime.profile === \"wasi\") {\n if (!hasService) {\n throw new Error(\"wasi runtime 必须包含 Service component。\");\n }\n return;\n }\n if (runtime.profile !== \"panel-only\" && !hasService) {\n throw new Error(`${runtime.profile} runtime 必须包含 Service component。`);\n }\n };\n\n private readComponentId = async (\n component: AppComponentReference,\n componentDirectory: string,\n ): Promise<string> => {\n const manifestFile = component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\";\n const raw = JSON.parse(\n await readFile(path.join(componentDirectory, manifestFile), \"utf-8\"),\n ) as unknown;\n const manifest = this.assertObject(raw, `${component.path}/${manifestFile}`);\n const id = this.readRequiredString(manifest.id, `${component.path}/${manifestFile}.id`);\n if (!COMPONENT_ID_PATTERN.test(id)) {\n throw new Error(`组件 id 必须是 kebab-case:${id}`);\n }\n const directoryName = path.basename(componentDirectory);\n const expectedId = component.kind === \"panel\"\n ? directoryName.replace(/\\.panel$/, \"\")\n : directoryName;\n if (component.kind === \"panel\" && !directoryName.endsWith(\".panel\")) {\n throw new Error(`Panel component 目录必须以 .panel 结尾:${component.path}`);\n }\n if (id !== expectedId) {\n throw new Error(`组件 id 必须与目录名一致:期望 ${expectedId},实际 ${id}`);\n }\n return id;\n };\n\n private parsePermissions = (rawPermissions: unknown): AppPermissions | undefined => {\n if (rawPermissions === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPermissions, \"permissions\");\n return {\n documentAccess: this.parseDocumentAccess(candidate.documentAccess),\n allowedDomains: this.parseStringArray(candidate.allowedDomains, \"permissions.allowedDomains\"),\n storage: this.parseStorage(candidate.storage),\n capabilities: this.parseCapabilities(candidate.capabilities),\n };\n };\n\n private parseDocumentAccess = (rawDocumentAccess: unknown): AppDocumentAccessScope[] | undefined => {\n if (rawDocumentAccess === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawDocumentAccess)) {\n throw new Error(\"permissions.documentAccess 必须是数组。\");\n }\n return rawDocumentAccess.map((scope, index) => {\n const candidate = this.assertObject(scope, `permissions.documentAccess[${index}]`);\n const mode = this.readRequiredString(candidate.mode, `permissions.documentAccess[${index}].mode`);\n if (mode !== \"read\" && mode !== \"read-write\") {\n throw new Error(`permissions.documentAccess[${index}].mode 只支持 read 或 read-write。`);\n }\n return {\n id: this.readRequiredString(candidate.id, `permissions.documentAccess[${index}].id`),\n mode,\n description: this.readOptionalString(\n candidate.description,\n `permissions.documentAccess[${index}].description`,\n ),\n };\n });\n };\n\n private parseStringArray = (rawValue: unknown, fieldName: string): string[] | undefined => {\n if (rawValue === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawValue)) {\n throw new Error(`${fieldName} 必须是字符串数组。`);\n }\n return rawValue.map((item, index) => this.readRequiredString(item, `${fieldName}[${index}]`));\n };\n\n private parseStorage = (rawStorage: unknown): AppPermissions[\"storage\"] | undefined => {\n if (rawStorage === undefined || typeof rawStorage === \"boolean\") {\n return rawStorage;\n }\n const candidate = this.assertObject(rawStorage, \"permissions.storage\");\n return { namespace: this.readOptionalString(candidate.namespace, \"permissions.storage.namespace\") };\n };\n\n private parseCapabilities = (rawCapabilities: unknown): AppPermissions[\"capabilities\"] | undefined => {\n if (rawCapabilities === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawCapabilities, \"permissions.capabilities\");\n return {\n hostBridge: candidate.hostBridge === undefined\n ? undefined\n : this.readBoolean(candidate.hostBridge, \"permissions.capabilities.hostBridge\"),\n nativeProcess: candidate.nativeProcess === undefined\n ? undefined\n : this.readBoolean(candidate.nativeProcess, \"permissions.capabilities.nativeProcess\"),\n };\n };\n\n private assertResolvedFile = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isFile() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通文件。`);\n }\n return resolvedPath;\n };\n\n private assertResolvedDirectory = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isDirectory() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通目录。`);\n }\n await access(resolvedPath);\n return resolvedPath;\n };\n\n private resolveInside = (appDirectory: string, relativePath: string, fieldName: string): string => {\n const normalizedPath = this.normalizeRelativePath(relativePath, fieldName);\n const resolvedPath = path.resolve(appDirectory, normalizedPath);\n const relative = path.relative(appDirectory, resolvedPath);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`${fieldName} 不能指向应用目录之外。`);\n }\n return resolvedPath;\n };\n\n private normalizeRelativePath = (relativePath: string, fieldName: string): string => {\n if (path.isAbsolute(relativePath) || relativePath.includes(\"\\0\")) {\n throw new Error(`${fieldName} 必须使用包内相对路径。`);\n }\n const segments = relativePath.replace(/\\\\/g, \"/\").split(\"/\");\n if (segments.some((segment) => !segment || segment === \".\" || segment === \"..\")) {\n throw new Error(`${fieldName} 包含非法路径片段。`);\n }\n return segments.join(\"/\");\n };\n\n private assertObject = (value: unknown, fieldName: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (value: unknown, fieldName: string): string | undefined => {\n return value === undefined ? undefined : this.readRequiredString(value, fieldName);\n };\n\n private readNumber = (value: unknown, fieldName: string): number => {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${fieldName} 必须是数字。`);\n }\n return value;\n };\n\n private readBoolean = (value: unknown, fieldName: string): boolean => {\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-manifest.service.js","names":[],"sources":["../../src/services/app-manifest.service.ts"],"sourcesContent":["import { access, lstat, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppPlatformTargetService } from \"#app-runtime/services/app-platform-target.service.js\";\nimport type {\n AppComponentManifest,\n AppComponentManifestBundle,\n AppComponentReference,\n AppDocumentAccessScope,\n AppManifest,\n AppManifestBundle,\n AppManifestSummary,\n AppPermissions,\n AppPlatformSecuritySummary,\n AppResolvedComponent,\n AppSecretSlot,\n AppStandaloneManifest,\n AppStandaloneManifestBundle,\n} from \"#app-runtime/types/app-manifest.types.js\";\n\nconst PACKAGE_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;\nconst COMPONENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst SECRET_SLOT_ID_PATTERN = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;\n\nexport class AppManifestService {\n constructor(\n private readonly platformTargetService = new AppPlatformTargetService(),\n ) {}\n\n load = async (appDirectory: string): Promise<AppManifestBundle> => {\n const normalizedDirectory = path.resolve(appDirectory);\n const manifestPath = path.join(normalizedDirectory, \"manifest.json\");\n const rawManifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as unknown;\n const manifest = this.parseManifest(rawManifest);\n return manifest.schemaVersion === 1\n ? await this.loadStandaloneBundle(normalizedDirectory, manifestPath, manifest)\n : await this.loadComponentBundle(normalizedDirectory, manifestPath, manifest);\n };\n\n summarize = (bundle: AppManifestBundle): AppManifestSummary => {\n if (bundle.manifest.schemaVersion === 2) {\n const componentBundle = bundle as AppComponentManifestBundle;\n return {\n schemaVersion: 2,\n id: componentBundle.manifest.id,\n name: componentBundle.manifest.name,\n version: componentBundle.manifest.version,\n description: componentBundle.manifest.description,\n manifestPath: componentBundle.manifestPath,\n iconPath: componentBundle.iconPath,\n primaryPanelId: componentBundle.primaryPanelId,\n components: componentBundle.components,\n distribution: this.platformTargetService.resolveDistribution(\n componentBundle.manifest.distribution,\n ),\n security: this.resolvePlatformSecurity(componentBundle.manifest),\n };\n }\n const standaloneBundle = bundle as AppStandaloneManifestBundle;\n const permissions = standaloneBundle.manifest.permissions ?? {};\n return {\n schemaVersion: 1,\n id: standaloneBundle.manifest.id,\n name: standaloneBundle.manifest.name,\n version: standaloneBundle.manifest.version,\n description: standaloneBundle.manifest.description,\n mainKind: standaloneBundle.manifest.main.kind,\n action:\n standaloneBundle.manifest.main.kind === \"wasm\"\n ? standaloneBundle.manifest.main.action\n : undefined,\n manifestPath: standaloneBundle.manifestPath,\n mainEntryPath: standaloneBundle.mainEntryPath,\n uiEntryPath: standaloneBundle.uiEntryPath,\n iconPath: standaloneBundle.iconPath,\n permissions,\n };\n };\n\n private loadStandaloneBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppStandaloneManifest,\n ): Promise<AppStandaloneManifestBundle> => {\n const mainEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.main.entry,\n \"main.entry\",\n );\n const uiEntryPath = await this.assertResolvedFile(\n appDirectory,\n manifest.ui.entry,\n \"ui.entry\",\n );\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n mainEntryPath,\n uiEntryPath,\n uiDirectoryPath: path.dirname(uiEntryPath),\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n };\n };\n\n private loadComponentBundle = async (\n appDirectory: string,\n manifestPath: string,\n manifest: AppComponentManifest,\n ): Promise<AppComponentManifestBundle> => {\n const components: AppResolvedComponent[] = [];\n for (const [index, component] of manifest.components.entries()) {\n const componentDirectory = await this.assertResolvedDirectory(\n appDirectory,\n component.path,\n `components[${index}].path`,\n );\n const componentId = await this.readComponentId(component, componentDirectory);\n const expectedPrefix = `${manifest.id.replace(/[^a-z0-9]+/g, \"-\")}-`;\n if (!componentId.startsWith(expectedPrefix)) {\n throw new Error(\n `组件 ${componentId} 必须使用包前缀 ${expectedPrefix}。`,\n );\n }\n components.push({\n ...component,\n id: componentId,\n componentDirectory,\n manifestPath: path.join(\n componentDirectory,\n component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\",\n ),\n });\n }\n const duplicateId = components.find(\n (component, index) => components.findIndex((entry) => entry.id === component.id) !== index,\n );\n if (duplicateId) {\n throw new Error(`components 包含重复组件 id:${duplicateId.id}`);\n }\n const panelIds = components\n .filter((component) => component.kind === \"panel\")\n .map((component) => component.id);\n const declaredPrimaryPanel = manifest.presentation?.primaryPanel;\n if (declaredPrimaryPanel && !panelIds.includes(declaredPrimaryPanel)) {\n throw new Error(\"presentation.primaryPanel 必须引用真实 Panel component id。\");\n }\n if (panelIds.length > 1 && !declaredPrimaryPanel) {\n throw new Error(\"包含多个 Panel 时必须声明 presentation.primaryPanel。\");\n }\n if (panelIds.length === 0 && declaredPrimaryPanel) {\n throw new Error(\"不含 Panel 的包不能声明 presentation.primaryPanel。\");\n }\n const iconPath = manifest.icon\n ? await this.assertResolvedFile(appDirectory, manifest.icon, \"icon\")\n : undefined;\n return {\n appDirectory,\n manifestPath,\n manifest,\n components,\n assetsDirectoryPath: path.join(appDirectory, \"assets\"),\n iconPath,\n primaryPanelId: declaredPrimaryPanel ?? panelIds[0],\n };\n };\n\n private parseManifest = (rawManifest: unknown): AppManifest => {\n const candidate = this.assertObject(rawManifest, \"manifest.json\");\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"schemaVersion\");\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n throw new Error(\"当前只支持 schemaVersion = 1 或 2。\");\n }\n const common = this.parseCommonManifest(candidate);\n if (schemaVersion === 1) {\n return {\n schemaVersion: 1,\n ...common,\n main: this.parseMain(candidate.main),\n ui: this.parseUi(candidate.ui),\n permissions: this.parsePermissions(candidate.permissions),\n };\n }\n const components = this.parseComponents(candidate.components);\n const runtime = this.parseRuntime(candidate.runtime);\n this.assertRuntimeMatchesComponents(runtime, components);\n return {\n schemaVersion: 2,\n ...common,\n engines: this.parseEngines(candidate.engines),\n presentation: this.parsePresentation(candidate.presentation),\n runtime,\n distribution: this.platformTargetService.parseDistribution(candidate.distribution),\n storage: this.parseAppStorage(candidate.storage),\n permissions: this.parsePermissions(candidate.permissions),\n components,\n };\n };\n\n resolvePlatformSecurity = (\n manifest: AppComponentManifest,\n ): AppPlatformSecuritySummary => {\n const hasServiceComponents = manifest.components.some(\n (component) => component.kind === \"service\",\n );\n const runtimeProfile = manifest.runtime?.profile ?? (\n hasServiceComponents ? \"native-process\" : \"panel-only\"\n );\n const isolation = runtimeProfile === \"panel-only\"\n ? \"sandboxed\"\n : runtimeProfile === \"wasi\"\n ? \"host-mediated\"\n : \"full-user\";\n const declaredPermissions = manifest.permissions ?? {};\n const permissions: AppPermissions = runtimeProfile === \"native-process\"\n ? {\n ...declaredPermissions,\n storage: declaredPermissions.storage ?? true,\n capabilities: {\n ...declaredPermissions.capabilities,\n nativeProcess: true,\n },\n }\n : declaredPermissions;\n return {\n runtimeProfile,\n isolation,\n hasServiceComponents,\n inferred: manifest.runtime === undefined,\n permissions,\n };\n };\n\n private parseCommonManifest = (candidate: Record<string, unknown>) => {\n const id = this.readRequiredString(candidate.id, \"id\");\n if (!PACKAGE_ID_PATTERN.test(id)) {\n throw new Error(\"id 必须由小写字母、数字、点或连字符组成。\");\n }\n return {\n id,\n name: this.readRequiredString(candidate.name, \"name\"),\n version: this.readRequiredString(candidate.version, \"version\"),\n description: this.readOptionalString(candidate.description, \"description\"),\n icon: this.readOptionalString(candidate.icon, \"icon\"),\n };\n };\n\n private parseMain = (rawMain: unknown): AppStandaloneManifest[\"main\"] => {\n const candidate = this.assertObject(rawMain, \"main\");\n const kind = this.readRequiredString(candidate.kind, \"main.kind\");\n if (kind === \"wasm\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n export: this.readRequiredString(candidate.export, \"main.export\"),\n action: this.readRequiredString(candidate.action, \"main.action\"),\n };\n }\n if (kind === \"wasi-http-component\") {\n return {\n kind,\n entry: this.readRequiredString(candidate.entry, \"main.entry\"),\n };\n }\n throw new Error(\"当前 main.kind 只支持 wasm 或 wasi-http-component。\");\n };\n\n private parseUi = (rawUi: unknown): AppStandaloneManifest[\"ui\"] => {\n const candidate = this.assertObject(rawUi, \"ui\");\n return { entry: this.readRequiredString(candidate.entry, \"ui.entry\") };\n };\n\n private parseComponents = (rawComponents: unknown): AppComponentReference[] => {\n if (!Array.isArray(rawComponents) || rawComponents.length === 0) {\n throw new Error(\"components 必须是非空数组。\");\n }\n const components = rawComponents.map((rawComponent, index) => {\n const candidate = this.assertObject(rawComponent, `components[${index}]`);\n const kind = this.readRequiredString(candidate.kind, `components[${index}].kind`);\n if (kind !== \"panel\" && kind !== \"service\") {\n throw new Error(`components[${index}].kind 只支持 panel 或 service。`);\n }\n const normalizedKind: AppComponentReference[\"kind\"] = kind;\n return {\n kind: normalizedKind,\n path: this.normalizeRelativePath(\n this.readRequiredString(candidate.path, `components[${index}].path`),\n `components[${index}].path`,\n ),\n };\n });\n for (let leftIndex = 0; leftIndex < components.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < components.length; rightIndex += 1) {\n const left = components[leftIndex]?.path;\n const right = components[rightIndex]?.path;\n if (left && right && (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`))) {\n throw new Error(`components path 不能重复或重叠:${left} / ${right}`);\n }\n }\n }\n return components;\n };\n\n private parseEngines = (\n rawEngines: unknown,\n ): AppComponentManifest[\"engines\"] => {\n if (rawEngines === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawEngines, \"engines\");\n return { nextclaw: this.readOptionalString(candidate.nextclaw, \"engines.nextclaw\") };\n };\n\n private parsePresentation = (\n rawPresentation: unknown,\n ): AppComponentManifest[\"presentation\"] => {\n if (rawPresentation === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPresentation, \"presentation\");\n return {\n primaryPanel: this.readOptionalString(\n candidate.primaryPanel,\n \"presentation.primaryPanel\",\n ),\n };\n };\n\n private parseRuntime = (\n rawRuntime: unknown,\n ): AppComponentManifest[\"runtime\"] => {\n if (rawRuntime === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawRuntime, \"runtime\");\n const profile = this.readRequiredString(candidate.profile, \"runtime.profile\");\n if (profile !== \"panel-only\" && profile !== \"wasi\" && profile !== \"native-process\") {\n throw new Error(\"runtime.profile 只支持 panel-only、wasi 或 native-process。\");\n }\n return { profile };\n };\n\n private parseAppStorage = (\n rawStorage: unknown,\n ): AppComponentManifest[\"storage\"] => {\n if (rawStorage === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawStorage, \"storage\");\n const scope = this.readRequiredString(candidate.scope, \"storage.scope\");\n if (scope !== \"global\") {\n throw new Error(\"当前 storage.scope 只支持 global。\");\n }\n const schemaVersion = this.readNumber(candidate.schemaVersion, \"storage.schemaVersion\");\n if (!Number.isSafeInteger(schemaVersion) || schemaVersion < 1) {\n throw new Error(\"storage.schemaVersion 必须是正整数。\");\n }\n return { scope, schemaVersion };\n };\n\n private assertRuntimeMatchesComponents = (\n runtime: AppComponentManifest[\"runtime\"],\n components: AppComponentReference[],\n ): void => {\n if (!runtime) {\n return;\n }\n const hasService = components.some((component) => component.kind === \"service\");\n if (runtime.profile === \"panel-only\" && hasService) {\n throw new Error(\"runtime.profile=panel-only 不能包含 Service component。\");\n }\n if (runtime.profile === \"wasi\") {\n if (!hasService) {\n throw new Error(\"wasi runtime 必须包含 Service component。\");\n }\n return;\n }\n if (runtime.profile !== \"panel-only\" && !hasService) {\n throw new Error(`${runtime.profile} runtime 必须包含 Service component。`);\n }\n };\n\n private readComponentId = async (\n component: AppComponentReference,\n componentDirectory: string,\n ): Promise<string> => {\n const manifestFile = component.kind === \"panel\" ? \"panel-app.json\" : \"service-app.json\";\n const raw = JSON.parse(\n await readFile(path.join(componentDirectory, manifestFile), \"utf-8\"),\n ) as unknown;\n const manifest = this.assertObject(raw, `${component.path}/${manifestFile}`);\n const id = this.readRequiredString(manifest.id, `${component.path}/${manifestFile}.id`);\n if (!COMPONENT_ID_PATTERN.test(id)) {\n throw new Error(`组件 id 必须是 kebab-case:${id}`);\n }\n const directoryName = path.basename(componentDirectory);\n const expectedId = component.kind === \"panel\"\n ? directoryName.replace(/\\.panel$/, \"\")\n : directoryName;\n if (component.kind === \"panel\" && !directoryName.endsWith(\".panel\")) {\n throw new Error(`Panel component 目录必须以 .panel 结尾:${component.path}`);\n }\n if (id !== expectedId) {\n throw new Error(`组件 id 必须与目录名一致:期望 ${expectedId},实际 ${id}`);\n }\n return id;\n };\n\n private parsePermissions = (rawPermissions: unknown): AppPermissions | undefined => {\n if (rawPermissions === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawPermissions, \"permissions\");\n return {\n documentAccess: this.parseDocumentAccess(candidate.documentAccess),\n secrets: this.parseSecretSlots(candidate.secrets),\n allowedDomains: this.parseStringArray(candidate.allowedDomains, \"permissions.allowedDomains\"),\n storage: this.parseStorage(candidate.storage),\n capabilities: this.parseCapabilities(candidate.capabilities),\n };\n };\n\n private parseDocumentAccess = (rawDocumentAccess: unknown): AppDocumentAccessScope[] | undefined => {\n if (rawDocumentAccess === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawDocumentAccess)) {\n throw new Error(\"permissions.documentAccess 必须是数组。\");\n }\n return rawDocumentAccess.map((scope, index) => {\n const candidate = this.assertObject(scope, `permissions.documentAccess[${index}]`);\n const mode = this.readRequiredString(candidate.mode, `permissions.documentAccess[${index}].mode`);\n if (mode !== \"read\" && mode !== \"read-write\") {\n throw new Error(`permissions.documentAccess[${index}].mode 只支持 read 或 read-write。`);\n }\n return {\n id: this.readRequiredString(candidate.id, `permissions.documentAccess[${index}].id`),\n mode,\n description: this.readOptionalString(\n candidate.description,\n `permissions.documentAccess[${index}].description`,\n ),\n };\n });\n };\n\n private parseSecretSlots = (rawSecrets: unknown): AppSecretSlot[] | undefined => {\n if (rawSecrets === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawSecrets)) {\n throw new Error(\"permissions.secrets 必须是数组。\");\n }\n const slots = rawSecrets.map((rawSlot, index) => {\n const field = `permissions.secrets[${index}]`;\n const candidate = this.assertObject(rawSlot, field);\n const unsupportedField = Object.keys(candidate).find(\n (key) => ![\"id\", \"title\", \"description\", \"required\"].includes(key),\n );\n if (unsupportedField) {\n throw new Error(`${field} 只能声明 id、title、description 和 required。`);\n }\n const id = this.readRequiredString(candidate.id, `${field}.id`);\n if (!SECRET_SLOT_ID_PATTERN.test(id)) {\n throw new Error(`${field}.id 必须是稳定的 lowercase slot id。`);\n }\n return {\n id,\n title: this.readRequiredString(candidate.title, `${field}.title`),\n description: this.readRequiredString(candidate.description, `${field}.description`),\n required: this.readBoolean(candidate.required, `${field}.required`),\n };\n });\n const duplicate = slots.find(\n (slot, index) => slots.findIndex((entry) => entry.id === slot.id) !== index,\n );\n if (duplicate) {\n throw new Error(`permissions.secrets 包含重复 slot id:${duplicate.id}`);\n }\n return slots;\n };\n\n private parseStringArray = (rawValue: unknown, fieldName: string): string[] | undefined => {\n if (rawValue === undefined) {\n return undefined;\n }\n if (!Array.isArray(rawValue)) {\n throw new Error(`${fieldName} 必须是字符串数组。`);\n }\n return rawValue.map((item, index) => this.readRequiredString(item, `${fieldName}[${index}]`));\n };\n\n private parseStorage = (rawStorage: unknown): AppPermissions[\"storage\"] | undefined => {\n if (rawStorage === undefined || typeof rawStorage === \"boolean\") {\n return rawStorage;\n }\n const candidate = this.assertObject(rawStorage, \"permissions.storage\");\n return { namespace: this.readOptionalString(candidate.namespace, \"permissions.storage.namespace\") };\n };\n\n private parseCapabilities = (rawCapabilities: unknown): AppPermissions[\"capabilities\"] | undefined => {\n if (rawCapabilities === undefined) {\n return undefined;\n }\n const candidate = this.assertObject(rawCapabilities, \"permissions.capabilities\");\n return {\n hostBridge: candidate.hostBridge === undefined\n ? undefined\n : this.readBoolean(candidate.hostBridge, \"permissions.capabilities.hostBridge\"),\n nativeProcess: candidate.nativeProcess === undefined\n ? undefined\n : this.readBoolean(candidate.nativeProcess, \"permissions.capabilities.nativeProcess\"),\n };\n };\n\n private assertResolvedFile = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isFile() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通文件。`);\n }\n return resolvedPath;\n };\n\n private assertResolvedDirectory = async (\n appDirectory: string,\n relativePath: string,\n fieldName: string,\n ): Promise<string> => {\n const resolvedPath = this.resolveInside(appDirectory, relativePath, fieldName);\n const stats = await lstat(resolvedPath);\n if (!stats.isDirectory() || stats.isSymbolicLink()) {\n throw new Error(`${fieldName} 必须指向包内普通目录。`);\n }\n await access(resolvedPath);\n return resolvedPath;\n };\n\n private resolveInside = (appDirectory: string, relativePath: string, fieldName: string): string => {\n const normalizedPath = this.normalizeRelativePath(relativePath, fieldName);\n const resolvedPath = path.resolve(appDirectory, normalizedPath);\n const relative = path.relative(appDirectory, resolvedPath);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`${fieldName} 不能指向应用目录之外。`);\n }\n return resolvedPath;\n };\n\n private normalizeRelativePath = (relativePath: string, fieldName: string): string => {\n if (path.isAbsolute(relativePath) || relativePath.includes(\"\\0\")) {\n throw new Error(`${fieldName} 必须使用包内相对路径。`);\n }\n const segments = relativePath.replace(/\\\\/g, \"/\").split(\"/\");\n if (segments.some((segment) => !segment || segment === \".\" || segment === \"..\")) {\n throw new Error(`${fieldName} 包含非法路径片段。`);\n }\n return segments.join(\"/\");\n };\n\n private assertObject = (value: unknown, fieldName: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${fieldName} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (value: unknown, fieldName: string): string | undefined => {\n return value === undefined ? undefined : this.readRequiredString(value, fieldName);\n };\n\n private readNumber = (value: unknown, fieldName: string): number => {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${fieldName} 必须是数字。`);\n }\n return value;\n };\n\n private readBoolean = (value: unknown, fieldName: string): boolean => {\n if (typeof value !== \"boolean\") {\n throw new Error(`${fieldName} 必须是布尔值。`);\n }\n return value;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-permissions.service.d.ts","names":[],"sources":["../../src/services/app-permissions.service.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"app-permissions.service.d.ts","names":[],"sources":["../../src/services/app-permissions.service.ts"],"mappings":";;;;cAaa,qBAAA;EACX,OAAA,GACE,MAAA,EAAQ,iBAAA,EACR,gBAAA,EAAkB,mBAAA,EAClB,OAAA;IACE,KAAA;EAAA,MAED,OAAA,CAAQ,mBAAA;EAiCX,SAAA,GACE,MAAA,EAAQ,iBAAA,EACR,WAAA,EAAa,mBAAA,KACZ,oBAAA;EAAA,QAUK,oBAAA;AAAA"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { isAppStandaloneManifestBundle } from "../types/app-manifest.types.js";
|
|
2
1
|
import { access } from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
//#region src/services/app-permissions.service.ts
|
|
5
4
|
var AppPermissionsService = class {
|
|
6
5
|
resolve = async (bundle, documentGrantMap, context) => {
|
|
7
|
-
if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包权限由 Panel/Service runtime grant 管理。");
|
|
8
6
|
const requestedPermissions = bundle.manifest.permissions ?? {};
|
|
9
7
|
return {
|
|
10
8
|
documentAccess: requestedPermissions.documentAccess === void 0 ? [] : await Promise.all(requestedPermissions.documentAccess.map((scope) => this.resolveDocumentGrant(scope, documentGrantMap, context?.appId))),
|
|
@@ -17,7 +15,6 @@ var AppPermissionsService = class {
|
|
|
17
15
|
};
|
|
18
16
|
};
|
|
19
17
|
summarize = (bundle, permissions) => {
|
|
20
|
-
if (!isAppStandaloneManifestBundle(bundle)) throw new Error("schema v2 组合包权限由 Panel/Service runtime grant 管理。");
|
|
21
18
|
return {
|
|
22
19
|
requested: bundle.manifest.permissions ?? {},
|
|
23
20
|
documentAccess: permissions.documentAccess,
|