@highstate/cli 0.20.0 → 0.26.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.
@@ -1,9 +1,17 @@
1
- import { int32ToBytes } from './chunk-CMECLVT7.js';
2
- import console from 'node:console';
3
- import { Crc32, crc32 } from '@aws-crypto/crc32';
4
- import { isComponent, isEntity, isUnitModel } from '@highstate/contract';
5
- import { encode } from '@msgpack/msgpack';
1
+ // @bun
2
+ import {
3
+ int32ToBytes
4
+ } from "./chunk-vcev74he.js";
6
5
 
6
+ // src/shared/library-loader.ts
7
+ import console from "console";
8
+ import { Crc32, crc32 } from "@aws-crypto/crc32";
9
+ import {
10
+ isComponent,
11
+ isEntity,
12
+ isUnitModel
13
+ } from "@highstate/contract";
14
+ import { encode } from "@msgpack/msgpack";
7
15
  async function loadLibrary(logger, modulePaths) {
8
16
  const modules = {};
9
17
  for (const modulePath of modulePaths) {
@@ -19,13 +27,10 @@ async function loadLibrary(logger, modulePaths) {
19
27
  const components = {};
20
28
  const entities = {};
21
29
  await _loadLibrary(modules, components, entities);
22
- logger.info(
23
- {
24
- componentCount: Object.keys(components).length,
25
- entityCount: Object.keys(entities).length
26
- },
27
- "library loaded"
28
- );
30
+ logger.info({
31
+ componentCount: Object.keys(components).length,
32
+ entityCount: Object.keys(entities).length
33
+ }, "library loaded");
29
34
  logger.trace({ components, entities }, "library content");
30
35
  return { components, entities };
31
36
  }
@@ -63,7 +68,7 @@ async function _loadLibrary(value, components, entities) {
63
68
  }
64
69
  }
65
70
  async function calculateComponentDefinitionHash(component, entityHashes) {
66
- const result = new Crc32();
71
+ const result = new Crc32;
67
72
  result.update(encode(component.model));
68
73
  if (!isUnitModel(component.model)) {
69
74
  const serializedCreate = { text: "TODO: investigate why serializeFunction hangs" };
@@ -77,7 +82,6 @@ async function calculateComponentDefinitionHash(component, entityHashes) {
77
82
  function calculateEntityDefinitionHash(entity) {
78
83
  return crc32(encode(entity.model));
79
84
  }
80
-
81
- export { loadLibrary };
82
- //# sourceMappingURL=library-loader-PZWYMBAE.js.map
83
- //# sourceMappingURL=library-loader-PZWYMBAE.js.map
85
+ export {
86
+ loadLibrary
87
+ };
@@ -1,3 +1,6 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
1
4
  // src/shared/utils.ts
2
5
  function int32ToBytes(value) {
3
6
  const buffer = new ArrayBuffer(4);
@@ -6,6 +9,4 @@ function int32ToBytes(value) {
6
9
  return new Uint8Array(buffer);
7
10
  }
8
11
 
9
- export { int32ToBytes };
10
- //# sourceMappingURL=chunk-CMECLVT7.js.map
11
- //# sourceMappingURL=chunk-CMECLVT7.js.map
12
+ export { __require, int32ToBytes };
@@ -1,4 +1,30 @@
1
- export { BackendIdentityCommand, BackendUnlockMethodAddCommand, BackendUnlockMethodDeleteCommand, BackendUnlockMethodListCommand, BuildCommand, DesignerCommand, InitCommand, PackageCreateCommand, PackageListCommand, PackageRemoveCommand, PackageUpdateReferencesCommand, UpdateCommand } from '../chunk-KA6ZC6BV.js';
2
- import '../chunk-CMECLVT7.js';
3
- //# sourceMappingURL=index.js.map
4
- //# sourceMappingURL=index.js.map
1
+ // @bun
2
+ import {
3
+ BackendIdentityCommand,
4
+ BackendUnlockMethodAddCommand,
5
+ BackendUnlockMethodDeleteCommand,
6
+ BackendUnlockMethodListCommand,
7
+ BuildCommand,
8
+ DesignerCommand,
9
+ InitCommand,
10
+ PackageCreateCommand,
11
+ PackageListCommand,
12
+ PackageRemoveCommand,
13
+ PackageUpdateReferencesCommand,
14
+ UpdateCommand
15
+ } from "../chunk-2s546hay.js";
16
+ import"../chunk-vcev74he.js";
17
+ export {
18
+ UpdateCommand,
19
+ PackageUpdateReferencesCommand,
20
+ PackageRemoveCommand,
21
+ PackageListCommand,
22
+ PackageCreateCommand,
23
+ InitCommand,
24
+ DesignerCommand,
25
+ BuildCommand,
26
+ BackendUnlockMethodListCommand,
27
+ BackendUnlockMethodDeleteCommand,
28
+ BackendUnlockMethodAddCommand,
29
+ BackendIdentityCommand
30
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "sourceHashes": {
3
- "./dist/commands/index.js": 3267890704,
4
- "./dist/main.js": 610943987
3
+ "./dist/commands/index.js": 4100963334,
4
+ "./dist/main.js": 527856276
5
5
  }
6
6
  }
package/dist/main.js CHANGED
@@ -1,12 +1,26 @@
1
- #!/usr/bin/env node
2
- import { BuildCommand, DesignerCommand, InitCommand, UpdateCommand, BackendIdentityCommand, BackendUnlockMethodListCommand, BackendUnlockMethodAddCommand, BackendUnlockMethodDeleteCommand, PackageUpdateReferencesCommand, PackageListCommand, PackageCreateCommand, PackageRemoveCommand } from './chunk-KA6ZC6BV.js';
3
- import './chunk-CMECLVT7.js';
4
- import { Cli, Builtins } from 'clipanion';
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+ import {
4
+ BackendIdentityCommand,
5
+ BackendUnlockMethodAddCommand,
6
+ BackendUnlockMethodDeleteCommand,
7
+ BackendUnlockMethodListCommand,
8
+ BuildCommand,
9
+ DesignerCommand,
10
+ InitCommand,
11
+ PackageCreateCommand,
12
+ PackageListCommand,
13
+ PackageRemoveCommand,
14
+ PackageUpdateReferencesCommand,
15
+ UpdateCommand
16
+ } from "./chunk-2s546hay.js";
17
+ import"./chunk-vcev74he.js";
5
18
 
19
+ // src/main.ts
20
+ import { Builtins, Cli } from "clipanion";
6
21
  var cli = new Cli({
7
22
  binaryName: "highstate",
8
23
  binaryLabel: "Highstate"
9
- // binaryVersion: version,
10
24
  });
11
25
  cli.register(BuildCommand);
12
26
  cli.register(DesignerCommand);
@@ -23,5 +37,3 @@ cli.register(PackageRemoveCommand);
23
37
  cli.register(Builtins.HelpCommand);
24
38
  cli.register(Builtins.VersionCommand);
25
39
  await cli.runExit(process.argv.slice(2));
26
- //# sourceMappingURL=main.js.map
27
- //# sourceMappingURL=main.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/cli",
3
- "version": "0.20.0",
3
+ "version": "0.26.0",
4
4
  "description": "The CLI for the Highstate project.",
5
5
  "type": "module",
6
6
  "files": [
@@ -25,11 +25,20 @@
25
25
  "platform"
26
26
  ]
27
27
  },
28
+ "scripts": {
29
+ "build": "bun run --bun highstate build",
30
+ "test": "bun run --bun vitest run",
31
+ "typecheck": "tsgo --noEmit --skipLibCheck",
32
+ "biome": "biome check --write --unsafe --error-on-warnings",
33
+ "biome:check": "biome check --error-on-warnings"
34
+ },
28
35
  "dependencies": {
29
36
  "@aws-crypto/crc32": "^5.2.0",
37
+ "@highstate/backend": "0.25.0",
38
+ "@highstate/contract": "0.25.0",
30
39
  "@inquirer/prompts": "^7.8.6",
31
40
  "@msgpack/msgpack": "^3.1.2",
32
- "@pulumi/pulumi": "3.220.0",
41
+ "@pulumi/pulumi": "3.232.0",
33
42
  "age-encryption": "^0.2.4",
34
43
  "ajv": "^8.17.1",
35
44
  "clipanion": "^4.0.0-rc.4",
@@ -49,9 +58,7 @@
49
58
  "semver": "^7.7.3",
50
59
  "tsup": "^8.4.0",
51
60
  "yaml": "^2.8.2",
52
- "zod": "^4.0.5",
53
- "@highstate/backend": "0.20.0",
54
- "@highstate/contract": "0.20.0"
61
+ "zod": "^4.0.5"
55
62
  },
56
63
  "peerDependenciesMeta": {
57
64
  "@pulumi/pulumi": {
@@ -66,17 +73,9 @@
66
73
  "@types/semver": "^7.7.1",
67
74
  "@typescript/native-preview": "^7.0.0-dev.20250920.1",
68
75
  "esbuild": "^0.25.11",
69
- "highstate-cli-bootstrap": "npm:@highstate/cli",
70
76
  "vitest": "^3.2.4"
71
77
  },
72
78
  "repository": {
73
79
  "url": "https://github.com/highstate-io/highstate"
74
- },
75
- "scripts": {
76
- "build": "highstate build",
77
- "test": "vitest run",
78
- "typecheck": "tsgo --noEmit --skipLibCheck",
79
- "biome": "biome check --write --unsafe --error-on-warnings",
80
- "biome:check": "biome check --error-on-warnings"
81
80
  }
82
- }
81
+ }
@@ -1,11 +1,8 @@
1
- import type { Plugin } from "esbuild"
2
- import { writeFile } from "node:fs/promises"
1
+ import { chmod, readFile, rm, writeFile } from "node:fs/promises"
3
2
  import { resolve } from "node:path"
4
3
  import { encode } from "@msgpack/msgpack"
5
4
  import { Command, Option } from "clipanion"
6
5
  import { readPackageJSON, resolvePackageJSON } from "pkg-types"
7
- import { mapValues } from "remeda"
8
- import { build } from "tsup"
9
6
  import {
10
7
  createBinTransformerPlugin,
11
8
  extractEntryPoints,
@@ -15,6 +12,22 @@ import {
15
12
  schemaTransformerPlugin,
16
13
  } from "../shared"
17
14
 
15
+ function formatUnknownError(error: unknown): string {
16
+ if (error instanceof Error) {
17
+ return error.stack ?? error.message
18
+ }
19
+
20
+ if (typeof error === "string") {
21
+ return error
22
+ }
23
+
24
+ try {
25
+ return JSON.stringify(error)
26
+ } catch {
27
+ return String(error)
28
+ }
29
+ }
30
+
18
31
  export class BuildCommand extends Command {
19
32
  static paths = [["build"]]
20
33
 
@@ -23,12 +36,23 @@ export class BuildCommand extends Command {
23
36
  description: "Builds the Highstate library or unit package.",
24
37
  })
25
38
 
26
- watch = Option.Boolean("--watch", false)
27
39
  library = Option.Boolean("--library", false)
28
40
  silent = Option.Boolean("--silent", true)
29
41
  noSourceHash = Option.Boolean("--no-source-hash", false)
30
42
 
31
43
  async execute(): Promise<void> {
44
+ try {
45
+ await this.build()
46
+ } catch (error) {
47
+ if (error instanceof Error) {
48
+ throw error
49
+ }
50
+
51
+ throw new Error(`Build failed with non-error rejection: ${formatUnknownError(error)}`)
52
+ }
53
+ }
54
+
55
+ private async build(): Promise<void> {
32
56
  const packageJson = await readPackageJSON()
33
57
 
34
58
  const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {})
@@ -50,36 +74,56 @@ export class BuildCommand extends Command {
50
74
  return
51
75
  }
52
76
 
53
- const esbuildPlugins: Plugin[] = []
77
+ const bunPlugins: Bun.BunPlugin[] = []
54
78
 
55
79
  const binSourceFilePaths = Object.values(entryPoints)
56
80
  .filter(value => value.isBin)
57
81
  .map(value => value.entryPoint.slice(2)) // remove "./"
58
82
 
59
83
  if (this.library) {
60
- esbuildPlugins.push(schemaTransformerPlugin)
84
+ bunPlugins.push(schemaTransformerPlugin)
61
85
  }
62
86
 
63
87
  if (binSourceFilePaths.length > 0) {
64
- esbuildPlugins.push(createBinTransformerPlugin(binSourceFilePaths))
88
+ bunPlugins.push(createBinTransformerPlugin(binSourceFilePaths))
65
89
  }
66
90
 
67
- await build({
68
- entry: mapValues(entryPoints, value => value.entryPoint),
69
- outDir: "dist",
70
- watch: this.watch,
71
- sourcemap: true,
72
- clean: true,
91
+ await rm("dist", { recursive: true, force: true })
92
+
93
+ const bunEntryPoints = Object.values(entryPoints).map(value => value.entryPoint)
94
+
95
+ const result = await Bun.build({
96
+ entrypoints: bunEntryPoints,
97
+ outdir: "dist",
98
+ root: "./src",
73
99
  format: "esm",
74
- target: "es2024",
75
- platform: "node",
100
+ target: "bun",
76
101
  external: ["@pulumi/pulumi"],
77
- esbuildPlugins,
78
- treeshake: true,
79
- removeNodeProtocol: false,
80
- silent: this.silent || ["warn", "error", "fatal"].includes(logger.level),
102
+ packages: "external",
103
+ splitting: true,
104
+ plugins: bunPlugins,
81
105
  })
82
106
 
107
+ if (!result.success) {
108
+ for (const log of result.logs) {
109
+ logger.error(log.message)
110
+ }
111
+
112
+ throw new Error("build failed")
113
+ }
114
+
115
+ const binEntryPoints = Object.values(entryPoints).filter(value => value.isBin)
116
+ for (const binEntryPoint of binEntryPoints) {
117
+ const binPath = resolve(binEntryPoint.distPath)
118
+ const binContent = await readFile(binPath, "utf8")
119
+
120
+ if (!binContent.startsWith("#!/usr/bin/env bun\n")) {
121
+ await writeFile(binPath, `#!/usr/bin/env bun\n${binContent}`, "utf8")
122
+ }
123
+
124
+ await chmod(binPath, 0o755)
125
+ }
126
+
83
127
  const packageJsonPath = await resolvePackageJSON()
84
128
  const upToDatePackageJson = await readPackageJSON()
85
129
 
@@ -2,16 +2,13 @@ import { pathToFileURL } from "node:url"
2
2
  import { Command, UsageError } from "clipanion"
3
3
  import { consola } from "consola"
4
4
  import { colorize } from "consola/utils"
5
- import { getPort } from "get-port-please"
5
+ import { checkPort, getPort } from "get-port-please"
6
6
  import { resolve as importMetaResolve } from "import-meta-resolve"
7
- import { addDevDependency, detectPackageManager } from "nypm"
7
+ import { addDevDependency } from "nypm"
8
8
  import { readPackageJSON, resolvePackageJSON } from "pkg-types"
9
- import {
10
- getBackendServices,
11
- getProjectPulumiSdkVersion,
12
- getPulumiCliVersion,
13
- logger,
14
- } from "../shared"
9
+ import { getBackendServices, logger } from "../shared"
10
+
11
+ let shuttingDown = false
15
12
 
16
13
  export class DesignerCommand extends Command {
17
14
  static paths = [["designer"]]
@@ -38,32 +35,32 @@ export class DesignerCommand extends Command {
38
35
  await addDevDependency(["@highstate/designer", "classic-level"])
39
36
  }
40
37
 
41
- const projectRoot = process.cwd()
42
- const detected = await detectPackageManager(projectRoot)
43
- const packageManager = detected?.name
44
-
45
- if (packageManager === "npm" || packageManager === "pnpm" || packageManager === "yarn") {
46
- const expectedPulumiSdk = await getProjectPulumiSdkVersion(projectRoot, { packageManager })
47
- const actualPulumiCli = await getPulumiCliVersion(projectRoot)
48
-
49
- if (expectedPulumiSdk && actualPulumiCli && expectedPulumiSdk !== actualPulumiCli) {
50
- logger.warn(
51
- `pulumi version mismatch detected, this may cause incompatibilities\nexpected "%s" (from overrides for "@pulumi/pulumi"), got "%s" (from "pulumi version")\nrecommended: run "highstate update" or downgrade your Pulumi CLI to "%s"`,
52
- expectedPulumiSdk,
53
- actualPulumiCli,
54
- expectedPulumiSdk,
55
- )
56
- }
57
- }
58
-
59
38
  logger.info("starting highstate designer...")
60
39
 
61
40
  await getBackendServices()
62
41
 
63
42
  const oldConsoleLog = console.log
64
43
 
65
- const port = await getPort({ port: 3000 })
66
- const eventsPort = await getPort({ port: 3001 })
44
+ const host = "127.0.0.1"
45
+ const configuredPort = process.env.HIGHSTATE_DESIGNER_PORT
46
+ const port = configuredPort === undefined ? 7283 : Number(configuredPort)
47
+ if (!/^\d+$/.test(configuredPort ?? port.toString()) || port < 1 || port > 65535) {
48
+ throw new UsageError(`HIGHSTATE_DESIGNER_PORT must be an integer between "1" and "65535"`)
49
+ }
50
+
51
+ if (configuredPort !== undefined) {
52
+ logger.warn(
53
+ `using custom designer port "%s"; changing the port changes the WebAuthn origin and may require registering security keys again`,
54
+ port,
55
+ )
56
+ }
57
+
58
+ const availablePort = await checkPort(port, host)
59
+ if (!availablePort) {
60
+ throw new UsageError(`Port "${port}" is already in use`)
61
+ }
62
+
63
+ const eventsPort = await getPort({ random: true, host })
67
64
 
68
65
  const designerPackageJsonPath = importMetaResolve(
69
66
  "@highstate/designer/package.json",
@@ -72,22 +69,30 @@ export class DesignerCommand extends Command {
72
69
  const designerPackageJson = await readPackageJSON(designerPackageJsonPath)
73
70
 
74
71
  process.env.NITRO_PORT = port.toString()
75
- process.env.NITRO_HOST = "0.0.0.0"
72
+ process.env.NITRO_HOST = host
73
+ process.env.NITRO_BUN_IDLE_TIMEOUT ??= "255"
76
74
  process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version
77
75
  process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString()
78
76
 
79
- await new Promise<void>(resolve => {
80
- console.log = (message: string) => {
81
- if (message.startsWith("Listening on")) {
82
- resolve()
77
+ try {
78
+ await new Promise<void>((resolve, reject) => {
79
+ console.log = (message: string) => {
80
+ if (message.startsWith("Listening on")) {
81
+ if (!message.includes(`http://${host}:${port}`)) {
82
+ reject(new Error(`Designer started on an unexpected endpoint: ${message}`))
83
+ return
84
+ }
85
+
86
+ resolve()
87
+ }
83
88
  }
84
- }
85
-
86
- const serverPath = importMetaResolve("@highstate/designer/server", packageJsonUrl)
87
- void import(serverPath)
88
- })
89
89
 
90
- console.log = oldConsoleLog
90
+ const serverPath = importMetaResolve("@highstate/designer/server", packageJsonUrl)
91
+ void import(serverPath).catch(reject)
92
+ })
93
+ } finally {
94
+ console.log = oldConsoleLog
95
+ }
91
96
 
92
97
  consola.log(
93
98
  [
@@ -95,12 +100,17 @@ export class DesignerCommand extends Command {
95
100
  colorize("bold", colorize("cyanBright", "Highstate Designer")),
96
101
  "\n ",
97
102
  colorize("greenBright", "➜ Local: "),
98
- colorize("underline", colorize("cyanBright", `http://localhost:${port}`)),
103
+ colorize("underline", colorize("cyanBright", `http://highstate.localhost:${port}`)),
99
104
  "\n",
100
105
  ].join(""),
101
106
  )
102
107
 
103
- process.on("SIGINT", () => {
108
+ process.once("SIGINT", () => {
109
+ if (shuttingDown) {
110
+ return
111
+ }
112
+ shuttingDown = true
113
+
104
114
  process.stdout.write("\r")
105
115
  consola.info("shutting down highstate designer...")
106
116
 
@@ -1,8 +1,7 @@
1
- import type { PackageManagerName } from "nypm"
2
1
  import { access, mkdir, readdir } from "node:fs/promises"
3
2
  import { resolve } from "node:path"
4
3
  import { fileURLToPath } from "node:url"
5
- import { input, select } from "@inquirer/prompts"
4
+ import { input } from "@inquirer/prompts"
6
5
  import { Command, Option } from "clipanion"
7
6
  import { installDependencies } from "nypm"
8
7
  import {
@@ -24,10 +23,6 @@ export class InitCommand extends Command {
24
23
  description: "The path where the project should be initialized.",
25
24
  })
26
25
 
27
- packageManager = Option.String("--package-manager", {
28
- description: "The package manager to use (npm, yarn, pnpm).",
29
- })
30
-
31
26
  name = Option.String("--name", {
32
27
  description: "The project name.",
33
28
  })
@@ -41,19 +36,14 @@ export class InitCommand extends Command {
41
36
  })
42
37
 
43
38
  async execute(): Promise<void> {
44
- const availablePackageManagers = await detectAvailablePackageManagers(["npm", "pnpm", "yarn"])
45
- if (availablePackageManagers.length === 0) {
46
- throw new Error('No supported package managers found in PATH ("npm", "pnpm", "yarn")')
39
+ const isBunAvailable = await isExecutableInPath("bun")
40
+ if (!isBunAvailable) {
41
+ throw new Error('Required package manager "bun" was not found in PATH')
47
42
  }
48
43
 
49
44
  const projectName = await resolveProjectName(this.name)
50
45
  const destinationPath = await resolveDestinationPath(this.pathOption, projectName)
51
46
 
52
- const selectedPackageManager = await resolvePackageManager(
53
- this.packageManager,
54
- availablePackageManagers,
55
- )
56
-
57
47
  const templatePath = resolveTemplatePath()
58
48
 
59
49
  const versionBundle = await resolveVersionBundle({
@@ -75,23 +65,19 @@ export class InitCommand extends Command {
75
65
  packageName: projectName,
76
66
  platformVersion: versionBundle.platformVersion,
77
67
  libraryVersion: versionBundle.stdlibVersion,
78
- isPnpm: selectedPackageManager === "pnpm" ? "true" : "",
79
- isYarn: selectedPackageManager === "yarn" ? "true" : "",
80
- isNpm: selectedPackageManager === "npm" ? "true" : "",
81
68
  })
82
69
 
83
70
  const overrides = buildOverrides(versionBundle)
84
71
  await applyOverrides({
85
72
  projectRoot: destinationPath,
86
- packageManager: selectedPackageManager,
87
73
  overrides,
88
74
  })
89
75
 
90
- logger.info("installing dependencies using %s...", selectedPackageManager)
76
+ logger.info("installing dependencies using bun...")
91
77
 
92
78
  await installDependencies({
93
79
  cwd: destinationPath,
94
- packageManager: selectedPackageManager,
80
+ packageManager: "bun",
95
81
  silent: false,
96
82
  })
97
83
 
@@ -137,52 +123,6 @@ async function resolveProjectName(nameOption: string | undefined): Promise<strin
137
123
  return value.trim()
138
124
  }
139
125
 
140
- async function resolvePackageManager(
141
- packageManagerOption: string | undefined,
142
- available: PackageManagerName[],
143
- ): Promise<PackageManagerName> {
144
- if (packageManagerOption) {
145
- if (!isSupportedPackageManagerName(packageManagerOption)) {
146
- throw new Error(`Unsupported package manager: "${packageManagerOption}"`)
147
- }
148
-
149
- const name = packageManagerOption
150
- if (!available.includes(name)) {
151
- throw new Error(`Package manager not found in PATH: "${name}"`)
152
- }
153
-
154
- return name
155
- }
156
-
157
- const preferredOrder: PackageManagerName[] = ["pnpm", "yarn", "npm"]
158
- const defaultValue = preferredOrder.find(value => available.includes(value)) ?? available[0]
159
-
160
- return await select({
161
- message: "Package manager",
162
- default: defaultValue,
163
- choices: available.map(value => ({ name: value, value })),
164
- })
165
- }
166
-
167
- function isSupportedPackageManagerName(value: string): value is PackageManagerName {
168
- return value === "npm" || value === "pnpm" || value === "yarn"
169
- }
170
-
171
- async function detectAvailablePackageManagers(
172
- candidates: PackageManagerName[],
173
- ): Promise<PackageManagerName[]> {
174
- const results: PackageManagerName[] = []
175
-
176
- for (const candidate of candidates) {
177
- const exists = await isExecutableInPath(candidate)
178
- if (exists) {
179
- results.push(candidate)
180
- }
181
- }
182
-
183
- return results
184
- }
185
-
186
126
  async function isExecutableInPath(command: string): Promise<boolean> {
187
127
  const pathValue = process.env.PATH
188
128
  if (!pathValue) {