@nextclaw/app-runtime 0.1.0 → 0.3.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.
Files changed (59) hide show
  1. package/README.md +125 -3
  2. package/dist/bundle/app-bundle.service.d.ts +27 -0
  3. package/dist/bundle/app-bundle.service.js +133 -0
  4. package/dist/bundle/app-bundle.types.d.ts +24 -0
  5. package/dist/cli/app-runtime-cli.service.d.ts +27 -0
  6. package/dist/cli/app-runtime-cli.service.js +281 -0
  7. package/dist/cli/app-runtime-options.service.d.ts +60 -0
  8. package/dist/cli/app-runtime-options.service.js +215 -0
  9. package/dist/commands/create.controller.d.ts +14 -0
  10. package/dist/commands/create.controller.js +23 -0
  11. package/dist/commands/grant.controller.d.ts +16 -0
  12. package/dist/commands/grant.controller.js +25 -0
  13. package/dist/commands/info.controller.d.ts +14 -0
  14. package/dist/commands/info.controller.js +27 -0
  15. package/dist/commands/install.controller.d.ts +15 -0
  16. package/dist/commands/install.controller.js +24 -0
  17. package/dist/commands/list.controller.d.ts +13 -0
  18. package/dist/commands/list.controller.js +25 -0
  19. package/dist/commands/pack.controller.d.ts +15 -0
  20. package/dist/commands/pack.controller.js +25 -0
  21. package/dist/commands/permissions.controller.d.ts +14 -0
  22. package/dist/commands/permissions.controller.js +26 -0
  23. package/dist/commands/registry.controller.d.ts +16 -0
  24. package/dist/commands/registry.controller.js +27 -0
  25. package/dist/commands/revoke.controller.d.ts +15 -0
  26. package/dist/commands/revoke.controller.js +24 -0
  27. package/dist/commands/run.controller.js +14 -6
  28. package/dist/commands/uninstall.controller.d.ts +15 -0
  29. package/dist/commands/uninstall.controller.js +23 -0
  30. package/dist/commands/update.controller.d.ts +16 -0
  31. package/dist/commands/update.controller.js +29 -0
  32. package/dist/host/app-instance.service.d.ts +3 -1
  33. package/dist/host/app-instance.service.js +2 -2
  34. package/dist/index.d.ts +26 -2
  35. package/dist/index.js +23 -2
  36. package/dist/install/app-installation.service.d.ts +37 -0
  37. package/dist/install/app-installation.service.js +255 -0
  38. package/dist/install/app-installation.types.d.ts +62 -0
  39. package/dist/main.js +5 -108
  40. package/dist/package.js +1 -1
  41. package/dist/paths/app-home.service.d.ts +16 -0
  42. package/dist/paths/app-home.service.js +42 -0
  43. package/dist/permissions/app-grant.service.d.ts +22 -0
  44. package/dist/permissions/app-grant.service.js +63 -0
  45. package/dist/permissions/app-permissions.service.d.ts +3 -1
  46. package/dist/permissions/app-permissions.service.js +12 -5
  47. package/dist/permissions/app-permissions.types.d.ts +28 -1
  48. package/dist/registry/app-registry-config.service.d.ts +16 -0
  49. package/dist/registry/app-registry-config.service.js +84 -0
  50. package/dist/registry/app-registry.service.d.ts +38 -0
  51. package/dist/registry/app-registry.service.js +115 -0
  52. package/dist/registry/app-registry.types.d.ts +33 -0
  53. package/dist/registry/app-remote-registry-client.service.d.ts +28 -0
  54. package/dist/registry/app-remote-registry-client.service.js +114 -0
  55. package/dist/registry/app-remote-registry.types.d.ts +52 -0
  56. package/dist/registry/app-remote-registry.types.js +4 -0
  57. package/dist/scaffold/app-scaffold.service.d.ts +19 -0
  58. package/dist/scaffold/app-scaffold.service.js +260 -0
  59. package/package.json +4 -2
@@ -1,16 +1,20 @@
1
- import { AppHostService } from "../host/app-host.service.js";
2
1
  import { AppManifestService } from "../manifest/app-manifest.service.js";
2
+ import { AppHostService } from "../host/app-host.service.js";
3
3
  import { AppInstanceService } from "../host/app-instance.service.js";
4
+ import { AppInstallationService } from "../install/app-installation.service.js";
4
5
  //#region src/commands/run.controller.ts
5
6
  var RunCommand = class {
6
- constructor(manifestService = new AppManifestService()) {
7
+ constructor(manifestService = new AppManifestService(), installationService = new AppInstallationService()) {
7
8
  this.manifestService = manifestService;
9
+ this.installationService = installationService;
8
10
  }
9
11
  run = async (params) => {
10
- const { appDirectory, host, port, json, documentGrantMap, write } = params;
11
- const bundle = await this.manifestService.load(appDirectory);
12
+ const { appReference, host, port, json, documentGrantMap, write } = params;
13
+ const launch = await this.installationService.resolveLaunch(appReference, documentGrantMap);
14
+ const bundle = await this.manifestService.load(launch.appDirectory);
12
15
  const appInstance = new AppInstanceService(bundle);
13
- await appInstance.initialize(documentGrantMap);
16
+ await appInstance.initialize(launch.documentGrantMap, { appId: launch.appId });
17
+ await this.installationService.persistGrants(launch.appId, launch.documentGrantMap);
14
18
  const appHost = new AppHostService(appInstance);
15
19
  const hostHandle = await appHost.start({
16
20
  host,
@@ -29,7 +33,11 @@ var RunCommand = class {
29
33
  if (json) {
30
34
  write(`${JSON.stringify({
31
35
  ok: true,
32
- host: hostHandle
36
+ host: hostHandle,
37
+ app: {
38
+ appId: launch.appId ?? bundle.manifest.id,
39
+ appDirectory: launch.appDirectory
40
+ }
33
41
  }, null, 2)}\n`);
34
42
  return;
35
43
  }
@@ -0,0 +1,15 @@
1
+ import { AppInstallationService } from "../install/app-installation.service.js";
2
+
3
+ //#region src/commands/uninstall.controller.d.ts
4
+ declare class UninstallCommand {
5
+ private readonly installationService;
6
+ constructor(installationService?: AppInstallationService);
7
+ run: (params: {
8
+ appId: string;
9
+ purgeData: boolean;
10
+ json: boolean;
11
+ write: (text: string) => void;
12
+ }) => Promise<void>;
13
+ }
14
+ //#endregion
15
+ export { UninstallCommand };
@@ -0,0 +1,23 @@
1
+ import { AppInstallationService } from "../install/app-installation.service.js";
2
+ //#region src/commands/uninstall.controller.ts
3
+ var UninstallCommand = class {
4
+ constructor(installationService = new AppInstallationService()) {
5
+ this.installationService = installationService;
6
+ }
7
+ run = async (params) => {
8
+ const { appId, purgeData, json, write } = params;
9
+ const result = await this.installationService.uninstall(appId, purgeData);
10
+ if (json) {
11
+ write(`${JSON.stringify({
12
+ ok: true,
13
+ uninstall: result
14
+ }, null, 2)}\n`);
15
+ return;
16
+ }
17
+ write(`Uninstalled ${result.appId}\n`);
18
+ write(`Versions: ${result.removedVersions.join(", ")}\n`);
19
+ write(`Data removed: ${result.dataRemoved ? "yes" : "no"}\n`);
20
+ };
21
+ };
22
+ //#endregion
23
+ export { UninstallCommand };
@@ -0,0 +1,16 @@
1
+ import { AppInstallationService } from "../install/app-installation.service.js";
2
+
3
+ //#region src/commands/update.controller.d.ts
4
+ declare class UpdateCommand {
5
+ private readonly installationService;
6
+ constructor(installationService?: AppInstallationService);
7
+ run: (params: {
8
+ appId: string;
9
+ version?: string;
10
+ registryUrl?: string;
11
+ json: boolean;
12
+ write: (text: string) => void;
13
+ }) => Promise<void>;
14
+ }
15
+ //#endregion
16
+ export { UpdateCommand };
@@ -0,0 +1,29 @@
1
+ import { AppInstallationService } from "../install/app-installation.service.js";
2
+ //#region src/commands/update.controller.ts
3
+ var UpdateCommand = class {
4
+ constructor(installationService = new AppInstallationService()) {
5
+ this.installationService = installationService;
6
+ }
7
+ run = async (params) => {
8
+ const { appId, version, registryUrl, json, write } = params;
9
+ const result = await this.installationService.update(appId, {
10
+ version,
11
+ registryUrl
12
+ });
13
+ if (json) {
14
+ write(`${JSON.stringify({
15
+ ok: true,
16
+ update: result
17
+ }, null, 2)}\n`);
18
+ return;
19
+ }
20
+ if (!result.updated) {
21
+ write(`Already up to date: ${result.appId} ${result.version}\n`);
22
+ return;
23
+ }
24
+ write(`Updated ${result.name} (${result.appId}) ${result.previousVersion} -> ${result.version}\n`);
25
+ write(`Code: ${result.installDirectory}\n`);
26
+ };
27
+ };
28
+ //#endregion
29
+ export { UpdateCommand };
@@ -19,7 +19,9 @@ declare class AppInstanceService {
19
19
  private readonly manifestService;
20
20
  private permissions?;
21
21
  constructor(bundle: AppManifestBundle, permissionsService?: AppPermissionsService, mainRunner?: WasmMainRunnerService, manifestService?: AppManifestService);
22
- initialize: (documentGrantMap: AppDocumentGrantMap) => Promise<void>;
22
+ initialize: (documentGrantMap: AppDocumentGrantMap, context?: {
23
+ appId?: string;
24
+ }) => Promise<void>;
23
25
  summarizeManifest: () => AppManifestSummary;
24
26
  summarizePermissions: () => AppPermissionSummary;
25
27
  runAction: (action?: string) => Promise<AppRunResult>;
@@ -12,8 +12,8 @@ var AppInstanceService = class {
12
12
  this.mainRunner = mainRunner;
13
13
  this.manifestService = manifestService;
14
14
  }
15
- initialize = async (documentGrantMap) => {
16
- this.permissions = await this.permissionsService.resolve(this.bundle, documentGrantMap);
15
+ initialize = async (documentGrantMap, context) => {
16
+ this.permissions = await this.permissionsService.resolve(this.bundle, documentGrantMap, context);
17
17
  };
18
18
  summarizeManifest = () => {
19
19
  return this.manifestService.summarize(this.bundle);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { AppDocumentAccessMode, AppDocumentAccessScope, AppMainManifest, AppManifest, AppManifestBundle, AppManifestSummary, AppPermissions, AppUiManifest } from "./manifest/app-manifest.types.js";
2
2
  import { AppManifestService } from "./manifest/app-manifest.service.js";
3
- import { AppDocumentGrantMap, AppPermissionSummary, ResolvedDocumentGrant, ResolvedPermissions } from "./permissions/app-permissions.types.js";
3
+ import { AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult } from "./bundle/app-bundle.types.js";
4
+ import { AppBundleService } from "./bundle/app-bundle.service.js";
5
+ import { AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppInstalledPermissionState, AppPermissionSummary, ResolvedDocumentGrant, ResolvedPermissions } from "./permissions/app-permissions.types.js";
4
6
  import { AppPermissionsService } from "./permissions/app-permissions.service.js";
5
7
  import { MainRunRequest, MainRunResult, WasmDocumentSummaryInput } from "./runtime/main-runner.types.js";
6
8
  import { MainRunnerService } from "./runtime/main-runner.service.js";
@@ -8,6 +10,28 @@ import { WasmSidecarClientService } from "./sidecar/wasm-sidecar-client.service.
8
10
  import { WasmMainRunnerService } from "./runtime/wasm-main-runner.service.js";
9
11
  import { AppInstanceService, AppRunResult } from "./host/app-instance.service.js";
10
12
  import { HostBridgeServer } from "./bridge/host-bridge.service.js";
13
+ import { AppRuntimeOptionsService, CreateCliOptions, GrantCliOptions, InstallCliOptions, JsonOnlyCliOptions, PackCliOptions, RevokeCliOptions, RuntimeCliOptions, UninstallCliOptions, UpdateCliOptions } from "./cli/app-runtime-options.service.js";
14
+ import { AppRuntimeCliService } from "./cli/app-runtime-cli.service.js";
15
+ import { CreateCommand } from "./commands/create.controller.js";
16
+ import { AppHomeService } from "./paths/app-home.service.js";
17
+ import { AppPublisher, AppRegistryConfig, AppRegistryConfigSnapshot, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, DEFAULT_APP_REGISTRY_URL } from "./registry/app-remote-registry.types.js";
18
+ import { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion } from "./registry/app-registry.types.js";
19
+ import { AppRegistryService } from "./registry/app-registry.service.js";
20
+ import { AppGrantService } from "./permissions/app-grant.service.js";
21
+ import { GrantCommand } from "./commands/grant.controller.js";
22
+ import { AppRegistryConfigService } from "./registry/app-registry-config.service.js";
23
+ import { AppRemoteRegistryClientService } from "./registry/app-remote-registry-client.service.js";
24
+ import { AppInfoResult, AppInstallResult, AppLaunchResolution, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "./install/app-installation.types.js";
25
+ import { AppInstallationService } from "./install/app-installation.service.js";
26
+ import { InfoCommand } from "./commands/info.controller.js";
27
+ import { InstallCommand } from "./commands/install.controller.js";
28
+ import { ListCommand } from "./commands/list.controller.js";
29
+ import { PackCommand } from "./commands/pack.controller.js";
30
+ import { PermissionsCommand } from "./commands/permissions.controller.js";
31
+ import { RegistryCommand } from "./commands/registry.controller.js";
32
+ import { RevokeCommand } from "./commands/revoke.controller.js";
33
+ import { UpdateCommand } from "./commands/update.controller.js";
34
+ import { UninstallCommand } from "./commands/uninstall.controller.js";
11
35
  import { AppHostHandle, AppHostService, AppHostStartOptions } from "./host/app-host.service.js";
12
36
  import { UiServerService } from "./ui/ui-server.service.js";
13
- export { AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppHostHandle, AppHostService, AppHostStartOptions, AppInstanceService, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppPermissionSummary, AppPermissions, AppPermissionsService, AppRunResult, AppUiManifest, HostBridgeServer, MainRunRequest, MainRunResult, MainRunnerService, ResolvedDocumentGrant, ResolvedPermissions, UiServerService, WasmDocumentSummaryInput, WasmMainRunnerService, WasmSidecarClientService };
37
+ export { AppBundleChecksums, AppBundleExtractResult, AppBundleMetadata, AppBundlePackResult, AppBundleService, AppDocumentAccessMode, AppDocumentAccessScope, AppDocumentGrantMap, AppDocumentGrantMutationResult, AppDocumentGrantState, AppGrantService, AppHomeService, AppHostHandle, AppHostService, AppHostStartOptions, AppInfoResult, AppInstallResult, AppInstallSourceKind, AppInstallationService, AppInstalledPermissionState, AppInstanceService, AppLaunchResolution, AppMainManifest, AppManifest, AppManifestBundle, AppManifestService, AppManifestSummary, AppPermissionSummary, AppPermissions, AppPermissionsService, AppPublisher, AppRegistry, AppRegistryAppRecord, AppRegistryConfig, AppRegistryConfigService, AppRegistryConfigSnapshot, AppRegistryInstalledVersion, AppRegistryService, AppRemoteRegistryClientService, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, AppRunResult, AppRuntimeCliService, AppRuntimeOptionsService, AppUiManifest, AppUninstallResult, AppUpdateResult, CreateCliOptions, CreateCommand, DEFAULT_APP_REGISTRY_URL, GrantCliOptions, GrantCommand, HostBridgeServer, InfoCommand, InstallCliOptions, InstallCommand, InstalledAppListItem, JsonOnlyCliOptions, ListCommand, MainRunRequest, MainRunResult, MainRunnerService, PackCliOptions, PackCommand, PermissionsCommand, RegistryCommand, ResolvedDocumentGrant, ResolvedPermissions, RevokeCliOptions, RevokeCommand, RuntimeCliOptions, UiServerService, UninstallCliOptions, UninstallCommand, UpdateCliOptions, UpdateCommand, WasmDocumentSummaryInput, WasmMainRunnerService, WasmSidecarClientService };
package/dist/index.js CHANGED
@@ -1,10 +1,31 @@
1
+ import { AppManifestService } from "./manifest/app-manifest.service.js";
2
+ import { AppBundleService } from "./bundle/app-bundle.service.js";
1
3
  import { HostBridgeServer } from "./bridge/host-bridge.service.js";
4
+ import { CreateCommand } from "./commands/create.controller.js";
2
5
  import { UiServerService } from "./ui/ui-server.service.js";
3
6
  import { AppHostService } from "./host/app-host.service.js";
4
- import { AppManifestService } from "./manifest/app-manifest.service.js";
5
7
  import { AppPermissionsService } from "./permissions/app-permissions.service.js";
6
8
  import { MainRunnerService } from "./runtime/main-runner.service.js";
7
9
  import { WasmSidecarClientService } from "./sidecar/wasm-sidecar-client.service.js";
8
10
  import { WasmMainRunnerService } from "./runtime/wasm-main-runner.service.js";
9
11
  import { AppInstanceService } from "./host/app-instance.service.js";
10
- export { AppHostService, AppInstanceService, AppManifestService, AppPermissionsService, HostBridgeServer, MainRunnerService, UiServerService, WasmMainRunnerService, WasmSidecarClientService };
12
+ import { AppHomeService } from "./paths/app-home.service.js";
13
+ import { DEFAULT_APP_REGISTRY_URL } from "./registry/app-remote-registry.types.js";
14
+ import { AppRegistryConfigService } from "./registry/app-registry-config.service.js";
15
+ import { AppRemoteRegistryClientService } from "./registry/app-remote-registry-client.service.js";
16
+ import { AppRegistryService } from "./registry/app-registry.service.js";
17
+ import { AppInstallationService } from "./install/app-installation.service.js";
18
+ import { AppGrantService } from "./permissions/app-grant.service.js";
19
+ import { GrantCommand } from "./commands/grant.controller.js";
20
+ import { InfoCommand } from "./commands/info.controller.js";
21
+ import { InstallCommand } from "./commands/install.controller.js";
22
+ import { ListCommand } from "./commands/list.controller.js";
23
+ import { PackCommand } from "./commands/pack.controller.js";
24
+ import { PermissionsCommand } from "./commands/permissions.controller.js";
25
+ import { RegistryCommand } from "./commands/registry.controller.js";
26
+ import { RevokeCommand } from "./commands/revoke.controller.js";
27
+ import { UninstallCommand } from "./commands/uninstall.controller.js";
28
+ import { UpdateCommand } from "./commands/update.controller.js";
29
+ import { AppRuntimeOptionsService } from "./cli/app-runtime-options.service.js";
30
+ import { AppRuntimeCliService } from "./cli/app-runtime-cli.service.js";
31
+ export { AppBundleService, AppGrantService, AppHomeService, AppHostService, AppInstallationService, AppInstanceService, AppManifestService, AppPermissionsService, AppRegistryConfigService, AppRegistryService, AppRemoteRegistryClientService, AppRuntimeCliService, AppRuntimeOptionsService, CreateCommand, DEFAULT_APP_REGISTRY_URL, GrantCommand, HostBridgeServer, InfoCommand, InstallCommand, ListCommand, MainRunnerService, PackCommand, PermissionsCommand, RegistryCommand, RevokeCommand, UiServerService, UninstallCommand, UpdateCommand, WasmMainRunnerService, WasmSidecarClientService };
@@ -0,0 +1,37 @@
1
+ import { AppManifestService } from "../manifest/app-manifest.service.js";
2
+ import { AppBundleService } from "../bundle/app-bundle.service.js";
3
+ import { AppDocumentGrantMap } from "../permissions/app-permissions.types.js";
4
+ import { AppHomeService } from "../paths/app-home.service.js";
5
+ import { AppRegistryService } from "../registry/app-registry.service.js";
6
+ import { AppRegistryConfigService } from "../registry/app-registry-config.service.js";
7
+ import { AppRemoteRegistryClientService } from "../registry/app-remote-registry-client.service.js";
8
+ import { AppInfoResult, AppInstallResult, AppLaunchResolution, AppUninstallResult, AppUpdateResult, InstalledAppListItem } from "./app-installation.types.js";
9
+
10
+ //#region src/install/app-installation.service.d.ts
11
+ declare class AppInstallationService {
12
+ private readonly appHomeService;
13
+ private readonly bundleService;
14
+ private readonly manifestService;
15
+ private readonly registryService;
16
+ private readonly registryConfigService;
17
+ private readonly remoteRegistryClient;
18
+ constructor(appHomeService?: AppHomeService, bundleService?: AppBundleService, manifestService?: AppManifestService, registryService?: AppRegistryService, registryConfigService?: AppRegistryConfigService, remoteRegistryClient?: AppRemoteRegistryClientService);
19
+ install: (appSource: string, options?: {
20
+ registryUrl?: string;
21
+ }) => Promise<AppInstallResult>;
22
+ update: (appId: string, options?: {
23
+ version?: string;
24
+ registryUrl?: string;
25
+ }) => Promise<AppUpdateResult>;
26
+ uninstall: (appId: string, purgeData: boolean) => Promise<AppUninstallResult>;
27
+ list: () => Promise<InstalledAppListItem[]>;
28
+ info: (appId: string) => Promise<AppInfoResult>;
29
+ resolveLaunch: (appReference: string, explicitDocumentGrantMap: AppDocumentGrantMap) => Promise<AppLaunchResolution>;
30
+ persistGrants: (appId: string | undefined, documentGrantMap: AppDocumentGrantMap) => Promise<void>;
31
+ private resolveInstallSource;
32
+ private detectLocalSourceType;
33
+ private parseRegistrySpec;
34
+ private looksLikePath;
35
+ }
36
+ //#endregion
37
+ export { AppInstallationService };
@@ -0,0 +1,255 @@
1
+ import { AppManifestService } from "../manifest/app-manifest.service.js";
2
+ import { AppBundleService } from "../bundle/app-bundle.service.js";
3
+ import { AppHomeService } from "../paths/app-home.service.js";
4
+ import { AppRegistryConfigService } from "../registry/app-registry-config.service.js";
5
+ import { AppRemoteRegistryClientService } from "../registry/app-remote-registry-client.service.js";
6
+ import { AppRegistryService } from "../registry/app-registry.service.js";
7
+ import { access, cp, mkdir, rm } from "node:fs/promises";
8
+ import path from "node:path";
9
+ //#region src/install/app-installation.service.ts
10
+ var AppInstallationService = class {
11
+ constructor(appHomeService = new AppHomeService(), bundleService = new AppBundleService(), manifestService = new AppManifestService(), registryService = new AppRegistryService(appHomeService), registryConfigService = new AppRegistryConfigService(appHomeService), remoteRegistryClient = new AppRemoteRegistryClientService(new AppRegistryConfigService(appHomeService))) {
12
+ this.appHomeService = appHomeService;
13
+ this.bundleService = bundleService;
14
+ this.manifestService = manifestService;
15
+ this.registryService = registryService;
16
+ this.registryConfigService = registryConfigService;
17
+ this.remoteRegistryClient = remoteRegistryClient;
18
+ }
19
+ install = async (appSource, options) => {
20
+ const source = await this.resolveInstallSource(appSource, options?.registryUrl);
21
+ const tempDirectory = await this.appHomeService.createTemporaryDirectory("napp-install-");
22
+ try {
23
+ const bundlePath = source.kind === "directory" ? (await this.bundleService.packAppDirectory({
24
+ appDirectory: source.appDirectory,
25
+ outputPath: path.join(tempDirectory, "app.napp")
26
+ })).bundlePath : source.kind === "bundle" ? source.bundlePath : (await this.remoteRegistryClient.downloadBundle({
27
+ resolution: source.registryResolution,
28
+ targetDirectory: tempDirectory
29
+ })).bundlePath;
30
+ const extractedDirectory = path.join(tempDirectory, "bundle");
31
+ await this.bundleService.extractBundle({
32
+ bundlePath,
33
+ targetDirectory: extractedDirectory
34
+ });
35
+ const manifestBundle = await this.manifestService.load(extractedDirectory);
36
+ const installDirectory = this.appHomeService.getInstallDirectory(manifestBundle.manifest.id, manifestBundle.manifest.version);
37
+ if (source.kind === "registry" && manifestBundle.manifest.id !== source.registryResolution.appId) throw new Error(`bundle manifest.appId 与 registry 请求不一致:期望 ${source.registryResolution.appId},实际 ${manifestBundle.manifest.id}`);
38
+ const dataDirectory = this.appHomeService.getAppDataDirectory(manifestBundle.manifest.id);
39
+ await rm(installDirectory, {
40
+ recursive: true,
41
+ force: true
42
+ });
43
+ await mkdir(path.dirname(installDirectory), { recursive: true });
44
+ await mkdir(dataDirectory, { recursive: true });
45
+ await cp(extractedDirectory, installDirectory, { recursive: true });
46
+ const registryRecord = await this.registryService.upsertInstallation({
47
+ appId: manifestBundle.manifest.id,
48
+ name: manifestBundle.manifest.name,
49
+ description: manifestBundle.manifest.description,
50
+ version: manifestBundle.manifest.version,
51
+ installDirectory,
52
+ dataDirectory,
53
+ sourceKind: source.kind,
54
+ sourceRef: source.sourceRef,
55
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
56
+ permissions: manifestBundle.manifest.permissions ?? {},
57
+ registryUrl: source.kind === "registry" ? source.registryResolution.registryUrl : void 0,
58
+ bundleUrl: source.kind === "registry" ? source.registryResolution.bundleUrl : void 0,
59
+ sha256: source.kind === "registry" ? source.registryResolution.sha256 : void 0,
60
+ publisher: source.kind === "registry" ? source.registryResolution.publisher : void 0
61
+ });
62
+ return {
63
+ appId: registryRecord.appId,
64
+ name: registryRecord.name,
65
+ version: registryRecord.activeVersion,
66
+ installDirectory,
67
+ dataDirectory,
68
+ sourceKind: source.kind,
69
+ sourceRef: source.sourceRef,
70
+ permissions: registryRecord.installedVersions[registryRecord.activeVersion]?.permissions ?? {},
71
+ registryUrl: registryRecord.installedVersions[registryRecord.activeVersion]?.registryUrl,
72
+ bundleUrl: registryRecord.installedVersions[registryRecord.activeVersion]?.bundleUrl,
73
+ sha256: registryRecord.installedVersions[registryRecord.activeVersion]?.sha256,
74
+ publisher: registryRecord.installedVersions[registryRecord.activeVersion]?.publisher
75
+ };
76
+ } finally {
77
+ await rm(tempDirectory, {
78
+ recursive: true,
79
+ force: true
80
+ });
81
+ }
82
+ };
83
+ update = async (appId, options) => {
84
+ const appRecord = await this.registryService.getApp(appId);
85
+ if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
86
+ const activeVersionRecord = appRecord.installedVersions[appRecord.activeVersion];
87
+ const registryUrl = options?.registryUrl ?? activeVersionRecord?.registryUrl ?? (await this.registryConfigService.getSnapshot()).currentUrl;
88
+ const resolution = await this.remoteRegistryClient.resolve({
89
+ appId,
90
+ version: options?.version,
91
+ registryUrl
92
+ });
93
+ if (resolution.version === appRecord.activeVersion) {
94
+ if (!activeVersionRecord) throw new Error(`已安装应用缺少激活版本:${appId}`);
95
+ return {
96
+ appId: appRecord.appId,
97
+ name: appRecord.name,
98
+ version: appRecord.activeVersion,
99
+ previousVersion: appRecord.activeVersion,
100
+ installDirectory: activeVersionRecord.installDirectory,
101
+ dataDirectory: appRecord.dataDirectory,
102
+ sourceKind: activeVersionRecord.sourceKind,
103
+ sourceRef: activeVersionRecord.sourceRef,
104
+ permissions: activeVersionRecord.permissions,
105
+ registryUrl: activeVersionRecord.registryUrl,
106
+ bundleUrl: activeVersionRecord.bundleUrl,
107
+ sha256: activeVersionRecord.sha256,
108
+ publisher: activeVersionRecord.publisher,
109
+ updated: false
110
+ };
111
+ }
112
+ return {
113
+ ...await this.install(`${appId}@${resolution.version}`, { registryUrl }),
114
+ previousVersion: appRecord.activeVersion,
115
+ updated: true
116
+ };
117
+ };
118
+ uninstall = async (appId, purgeData) => {
119
+ const appRecord = await this.registryService.removeApp(appId);
120
+ if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
121
+ const removedVersions = Object.keys(appRecord.installedVersions).sort((left, right) => left.localeCompare(right));
122
+ await Promise.all(Object.values(appRecord.installedVersions).map((versionRecord) => rm(versionRecord.installDirectory, {
123
+ recursive: true,
124
+ force: true
125
+ })));
126
+ if (purgeData) await rm(appRecord.dataDirectory, {
127
+ recursive: true,
128
+ force: true
129
+ });
130
+ return {
131
+ appId,
132
+ removedVersions,
133
+ dataRemoved: purgeData
134
+ };
135
+ };
136
+ list = async () => {
137
+ return (await this.registryService.listApps()).map((appRecord) => ({
138
+ appId: appRecord.appId,
139
+ name: appRecord.name,
140
+ activeVersion: appRecord.activeVersion,
141
+ sourceKind: appRecord.installedVersions[appRecord.activeVersion]?.sourceKind ?? "directory"
142
+ }));
143
+ };
144
+ info = async (appId) => {
145
+ const appRecord = await this.registryService.getApp(appId);
146
+ if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
147
+ const installedVersions = Object.values(appRecord.installedVersions).sort((left, right) => left.version.localeCompare(right.version));
148
+ return {
149
+ appId: appRecord.appId,
150
+ name: appRecord.name,
151
+ description: appRecord.description,
152
+ activeVersion: appRecord.activeVersion,
153
+ dataDirectory: appRecord.dataDirectory,
154
+ installedVersions: installedVersions.map((versionRecord) => ({
155
+ version: versionRecord.version,
156
+ installDirectory: versionRecord.installDirectory,
157
+ sourceKind: versionRecord.sourceKind,
158
+ sourceRef: versionRecord.sourceRef,
159
+ installedAt: versionRecord.installedAt,
160
+ permissions: versionRecord.permissions,
161
+ registryUrl: versionRecord.registryUrl,
162
+ bundleUrl: versionRecord.bundleUrl,
163
+ sha256: versionRecord.sha256,
164
+ publisher: versionRecord.publisher
165
+ })),
166
+ grants: appRecord.grants
167
+ };
168
+ };
169
+ resolveLaunch = async (appReference, explicitDocumentGrantMap) => {
170
+ const sourceType = await this.detectLocalSourceType(appReference, false);
171
+ if (sourceType.kind === "directory") return {
172
+ appDirectory: sourceType.appDirectory,
173
+ documentGrantMap: explicitDocumentGrantMap
174
+ };
175
+ const appRecord = await this.registryService.getApp(appReference);
176
+ if (!appRecord) throw new Error(`未找到应用目录,也未找到已安装应用:${appReference}`);
177
+ const activeVersion = appRecord.installedVersions[appRecord.activeVersion];
178
+ if (!activeVersion) throw new Error(`已安装应用缺少激活版本:${appReference}`);
179
+ return {
180
+ appDirectory: activeVersion.installDirectory,
181
+ appId: appRecord.appId,
182
+ documentGrantMap: {
183
+ ...appRecord.grants,
184
+ ...explicitDocumentGrantMap
185
+ }
186
+ };
187
+ };
188
+ persistGrants = async (appId, documentGrantMap) => {
189
+ if (!appId || Object.keys(documentGrantMap).length === 0) return;
190
+ await this.registryService.updateGrants(appId, documentGrantMap);
191
+ };
192
+ resolveInstallSource = async (appSource, registryUrl) => {
193
+ const localSource = await this.detectLocalSourceType(appSource);
194
+ if (localSource.kind === "directory") return {
195
+ kind: "directory",
196
+ appDirectory: localSource.appDirectory,
197
+ sourceRef: localSource.appDirectory
198
+ };
199
+ if (localSource.kind === "bundle") return {
200
+ kind: "bundle",
201
+ bundlePath: localSource.bundlePath,
202
+ sourceRef: localSource.bundlePath
203
+ };
204
+ const registrySpec = this.parseRegistrySpec(appSource);
205
+ if (!registrySpec) {
206
+ if (this.looksLikePath(appSource)) throw new Error(`本地安装源不存在:${appSource}`);
207
+ throw new Error(`无法识别安装源:${appSource}`);
208
+ }
209
+ const registryResolution = await this.remoteRegistryClient.resolve({
210
+ appId: registrySpec.appId,
211
+ version: registrySpec.version,
212
+ registryUrl
213
+ });
214
+ return {
215
+ kind: "registry",
216
+ sourceRef: `${registryResolution.appId}@${registryResolution.version}`,
217
+ registryResolution
218
+ };
219
+ };
220
+ detectLocalSourceType = async (sourcePath, allowBundle = true) => {
221
+ const normalizedSource = path.resolve(sourcePath);
222
+ try {
223
+ await access(normalizedSource);
224
+ if ((await this.manifestService.load(normalizedSource)).appDirectory) return {
225
+ kind: "directory",
226
+ appDirectory: normalizedSource
227
+ };
228
+ } catch {
229
+ if (allowBundle && normalizedSource.endsWith(".napp")) try {
230
+ await access(normalizedSource);
231
+ return {
232
+ kind: "bundle",
233
+ bundlePath: normalizedSource
234
+ };
235
+ } catch {
236
+ return { kind: "missing" };
237
+ }
238
+ return { kind: "missing" };
239
+ }
240
+ return { kind: "missing" };
241
+ };
242
+ parseRegistrySpec = (appSource) => {
243
+ const match = /^(?<appId>[a-z0-9][a-z0-9._-]*)(?:@(?<version>[A-Za-z0-9._+-]+))?$/i.exec(appSource.trim());
244
+ if (!match?.groups?.appId) return;
245
+ return {
246
+ appId: match.groups.appId,
247
+ version: match.groups.version
248
+ };
249
+ };
250
+ looksLikePath = (appSource) => {
251
+ return appSource.startsWith(".") || appSource.startsWith("/") || appSource.includes(path.sep);
252
+ };
253
+ };
254
+ //#endregion
255
+ export { AppInstallationService };
@@ -0,0 +1,62 @@
1
+ import { AppPermissions } from "../manifest/app-manifest.types.js";
2
+ import { AppDocumentGrantMap } from "../permissions/app-permissions.types.js";
3
+ import { AppPublisher } from "../registry/app-remote-registry.types.js";
4
+ import { AppInstallSourceKind } from "../registry/app-registry.types.js";
5
+
6
+ //#region src/install/app-installation.types.d.ts
7
+ type AppInstallResult = {
8
+ appId: string;
9
+ name: string;
10
+ version: string;
11
+ installDirectory: string;
12
+ dataDirectory: string;
13
+ sourceKind: AppInstallSourceKind;
14
+ sourceRef: string;
15
+ permissions: AppPermissions;
16
+ registryUrl?: string;
17
+ bundleUrl?: string;
18
+ sha256?: string;
19
+ publisher?: AppPublisher;
20
+ };
21
+ type AppInfoResult = {
22
+ appId: string;
23
+ name: string;
24
+ description?: string;
25
+ activeVersion: string;
26
+ dataDirectory: string;
27
+ installedVersions: Array<{
28
+ version: string;
29
+ installDirectory: string;
30
+ sourceKind: AppInstallSourceKind;
31
+ sourceRef: string;
32
+ installedAt: string;
33
+ permissions: AppPermissions;
34
+ registryUrl?: string;
35
+ bundleUrl?: string;
36
+ sha256?: string;
37
+ publisher?: AppPublisher;
38
+ }>;
39
+ grants: AppDocumentGrantMap;
40
+ };
41
+ type InstalledAppListItem = {
42
+ appId: string;
43
+ name: string;
44
+ activeVersion: string;
45
+ sourceKind: AppInstallSourceKind;
46
+ };
47
+ type AppUninstallResult = {
48
+ appId: string;
49
+ removedVersions: string[];
50
+ dataRemoved: boolean;
51
+ };
52
+ type AppLaunchResolution = {
53
+ appDirectory: string;
54
+ appId?: string;
55
+ documentGrantMap: AppDocumentGrantMap;
56
+ };
57
+ type AppUpdateResult = AppInstallResult & {
58
+ previousVersion: string;
59
+ updated: boolean;
60
+ };
61
+ //#endregion
62
+ export { AppInfoResult, AppInstallResult, AppLaunchResolution, AppUninstallResult, AppUpdateResult, InstalledAppListItem };