@highstate/cli 0.21.1 → 0.27.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.
@@ -1294,6 +1294,20 @@ import { resolve as resolve4 } from "path";
1294
1294
  import { encode } from "@msgpack/msgpack";
1295
1295
  import { Command as Command5, Option as Option3 } from "clipanion";
1296
1296
  import { readPackageJSON as readPackageJSON3, resolvePackageJSON as resolvePackageJSON4 } from "pkg-types";
1297
+ function formatUnknownError(error) {
1298
+ if (error instanceof Error) {
1299
+ return error.stack ?? error.message;
1300
+ }
1301
+ if (typeof error === "string") {
1302
+ return error;
1303
+ }
1304
+ try {
1305
+ return JSON.stringify(error);
1306
+ } catch {
1307
+ return String(error);
1308
+ }
1309
+ }
1310
+
1297
1311
  class BuildCommand extends Command5 {
1298
1312
  static paths = [["build"]];
1299
1313
  static usage = Command5.Usage({
@@ -1304,6 +1318,16 @@ class BuildCommand extends Command5 {
1304
1318
  silent = Option3.Boolean("--silent", true);
1305
1319
  noSourceHash = Option3.Boolean("--no-source-hash", false);
1306
1320
  async execute() {
1321
+ try {
1322
+ await this.build();
1323
+ } catch (error) {
1324
+ if (error instanceof Error) {
1325
+ throw error;
1326
+ }
1327
+ throw new Error(`Build failed with non-error rejection: ${formatUnknownError(error)}`);
1328
+ }
1329
+ }
1330
+ async build() {
1307
1331
  const packageJson = await readPackageJSON3();
1308
1332
  const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {});
1309
1333
  if (highstateConfig.type === "library") {
@@ -1383,10 +1407,12 @@ import { pathToFileURL as pathToFileURL2 } from "url";
1383
1407
  import { Command as Command6, UsageError } from "clipanion";
1384
1408
  import { consola as consola2 } from "consola";
1385
1409
  import { colorize } from "consola/utils";
1386
- import { getPort } from "get-port-please";
1410
+ import { checkPort, getPort } from "get-port-please";
1387
1411
  import { resolve as importMetaResolve2 } from "import-meta-resolve";
1388
1412
  import { addDevDependency } from "nypm";
1389
1413
  import { readPackageJSON as readPackageJSON4, resolvePackageJSON as resolvePackageJSON5 } from "pkg-types";
1414
+ var shuttingDown = false;
1415
+
1390
1416
  class DesignerCommand extends Command6 {
1391
1417
  static paths = [["designer"]];
1392
1418
  static usage = Command6.Usage({
@@ -1408,24 +1434,44 @@ class DesignerCommand extends Command6 {
1408
1434
  logger.info("starting highstate designer...");
1409
1435
  await getBackendServices();
1410
1436
  const oldConsoleLog = console.log;
1411
- const port = await getPort({ port: 3000 });
1412
- const eventsPort = await getPort({ port: 3001 });
1437
+ const host = "127.0.0.1";
1438
+ const configuredPort = process.env.HIGHSTATE_DESIGNER_PORT;
1439
+ const port = configuredPort === undefined ? 7283 : Number(configuredPort);
1440
+ if (!/^\d+$/.test(configuredPort ?? port.toString()) || port < 1 || port > 65535) {
1441
+ throw new UsageError(`HIGHSTATE_DESIGNER_PORT must be an integer between "1" and "65535"`);
1442
+ }
1443
+ if (configuredPort !== undefined) {
1444
+ logger.warn(`using custom designer port "%s"; changing the port changes the WebAuthn origin and may require registering security keys again`, port);
1445
+ }
1446
+ const availablePort = await checkPort(port, host);
1447
+ if (!availablePort) {
1448
+ throw new UsageError(`Port "${port}" is already in use`);
1449
+ }
1450
+ const eventsPort = await getPort({ random: true, host });
1413
1451
  const designerPackageJsonPath = importMetaResolve2("@highstate/designer/package.json", packageJsonUrl);
1414
1452
  const designerPackageJson = await readPackageJSON4(designerPackageJsonPath);
1415
1453
  process.env.NITRO_PORT = port.toString();
1416
- process.env.NITRO_HOST = "0.0.0.0";
1454
+ process.env.NITRO_HOST = host;
1455
+ process.env.NITRO_BUN_IDLE_TIMEOUT ??= "255";
1417
1456
  process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version;
1418
1457
  process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString();
1419
- await new Promise((resolve5) => {
1420
- console.log = (message) => {
1421
- if (message.startsWith("Listening on")) {
1422
- resolve5();
1423
- }
1424
- };
1425
- const serverPath = importMetaResolve2("@highstate/designer/server", packageJsonUrl);
1426
- import(serverPath);
1427
- });
1428
- console.log = oldConsoleLog;
1458
+ try {
1459
+ await new Promise((resolve5, reject) => {
1460
+ console.log = (message) => {
1461
+ if (message.startsWith("Listening on")) {
1462
+ if (!message.includes(`http://${host}:${port}`)) {
1463
+ reject(new Error(`Designer started on an unexpected endpoint: ${message}`));
1464
+ return;
1465
+ }
1466
+ resolve5();
1467
+ }
1468
+ };
1469
+ const serverPath = importMetaResolve2("@highstate/designer/server", packageJsonUrl);
1470
+ import(serverPath).catch(reject);
1471
+ });
1472
+ } finally {
1473
+ console.log = oldConsoleLog;
1474
+ }
1429
1475
  consola2.log([
1430
1476
  `
1431
1477
  `,
@@ -1433,11 +1479,15 @@ class DesignerCommand extends Command6 {
1433
1479
  `
1434
1480
  `,
1435
1481
  colorize("greenBright", "\u279C Local: "),
1436
- colorize("underline", colorize("cyanBright", `http://localhost:${port}`)),
1482
+ colorize("underline", colorize("cyanBright", `http://highstate.localhost:${port}`)),
1437
1483
  `
1438
1484
  `
1439
1485
  ].join(""));
1440
- process.on("SIGINT", () => {
1486
+ process.once("SIGINT", () => {
1487
+ if (shuttingDown) {
1488
+ return;
1489
+ }
1490
+ shuttingDown = true;
1441
1491
  process.stdout.write("\r");
1442
1492
  consola2.info("shutting down highstate designer...");
1443
1493
  setTimeout(() => process.exit(0), 1000);
@@ -1683,34 +1733,22 @@ class UpdateCommand extends Command12 {
1683
1733
  }
1684
1734
  const updatePlatform = this.platformOnly || !this.stdlibOnly;
1685
1735
  const updateStdlib = this.stdlibOnly || !this.platformOnly;
1736
+ let currentPlatformVersion;
1737
+ let resolvedStdlibVersion = this.stdlibVersion;
1686
1738
  if (this.stdlibOnly) {
1687
- const currentPlatformVersion = await getProjectPlatformVersion(projectRoot);
1688
- if (!currentPlatformVersion) {
1739
+ const projectPlatformVersion = await getProjectPlatformVersion(projectRoot);
1740
+ if (!projectPlatformVersion) {
1689
1741
  throw new Error('Current platform version is not set in overrides for "@highstate/pulumi"');
1690
1742
  }
1691
- const targetStdlibVersion = (this.stdlibVersion ?? "").trim();
1692
- if (targetStdlibVersion.length === 0) {
1693
- throw new Error('Flag "--stdlib-version" must be provided when using "--stdlib"');
1694
- }
1695
- const stdlibManifest = await fetchManifest("@highstate/library", targetStdlibVersion);
1696
- const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi");
1697
- if (!supportedPlatformRange) {
1698
- throw new Error(`Unable to infer "@highstate/pulumi" version from "@highstate/library@${targetStdlibVersion}"`);
1699
- }
1700
- const validPlatform = semver.valid(currentPlatformVersion);
1701
- if (!validPlatform) {
1702
- throw new Error(`Current platform version is not a valid semver "${currentPlatformVersion}"`);
1703
- }
1704
- const ok = semver.satisfies(validPlatform, supportedPlatformRange, {
1705
- includePrerelease: true
1743
+ currentPlatformVersion = projectPlatformVersion;
1744
+ resolvedStdlibVersion = await resolveCompatibleStdlibVersion({
1745
+ currentPlatformVersion: projectPlatformVersion,
1746
+ stdlibVersion: this.stdlibVersion
1706
1747
  });
1707
- if (!ok) {
1708
- throw new Error(`Current platform version "${currentPlatformVersion}" does not satisfy requirement "${supportedPlatformRange}"`);
1709
- }
1710
1748
  }
1711
1749
  const bundle = await resolveVersionBundle({
1712
- platformVersion: updatePlatform ? this.platformVersion : undefined,
1713
- stdlibVersion: updateStdlib ? this.stdlibVersion : undefined
1750
+ platformVersion: updatePlatform ? this.platformVersion : currentPlatformVersion,
1751
+ stdlibVersion: updateStdlib ? resolvedStdlibVersion : undefined
1714
1752
  });
1715
1753
  const overrides2 = buildOverrides(bundle);
1716
1754
  await applyOverrides({
@@ -1734,6 +1772,48 @@ class UpdateCommand extends Command12 {
1734
1772
  logger.info("update completed successfully");
1735
1773
  }
1736
1774
  }
1775
+ async function resolveCompatibleStdlibVersion(args) {
1776
+ const validPlatform = semver.valid(args.currentPlatformVersion);
1777
+ if (!validPlatform) {
1778
+ throw new Error(`Current platform version is not a valid semver "${args.currentPlatformVersion}"`);
1779
+ }
1780
+ const targetStdlibVersion = args.stdlibVersion?.trim();
1781
+ if (targetStdlibVersion) {
1782
+ await assertStdlibSupportsPlatform({
1783
+ currentPlatformVersion: validPlatform,
1784
+ stdlibVersion: targetStdlibVersion
1785
+ });
1786
+ return targetStdlibVersion;
1787
+ }
1788
+ const packument = await fetchNpmPackument("@highstate/library");
1789
+ const sortedVersions = Object.entries(packument.versions ?? {}).filter(([version]) => semver.valid(version)).sort(([a], [b]) => semver.rcompare(a, b));
1790
+ for (const [stdlibVersion, stdlibManifest] of sortedVersions) {
1791
+ const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi");
1792
+ if (!supportedPlatformRange) {
1793
+ continue;
1794
+ }
1795
+ const ok = semver.satisfies(validPlatform, supportedPlatformRange, {
1796
+ includePrerelease: true
1797
+ });
1798
+ if (ok) {
1799
+ return stdlibVersion;
1800
+ }
1801
+ }
1802
+ throw new Error(`Unable to find "@highstate/library" version compatible with platform "${validPlatform}"`);
1803
+ }
1804
+ async function assertStdlibSupportsPlatform(args) {
1805
+ const stdlibManifest = await fetchManifest("@highstate/library", args.stdlibVersion);
1806
+ const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi");
1807
+ if (!supportedPlatformRange) {
1808
+ throw new Error(`Unable to infer "@highstate/pulumi" version from "@highstate/library@${args.stdlibVersion}"`);
1809
+ }
1810
+ const ok = semver.satisfies(args.currentPlatformVersion, supportedPlatformRange, {
1811
+ includePrerelease: true
1812
+ });
1813
+ if (!ok) {
1814
+ throw new Error(`Current platform version "${args.currentPlatformVersion}" does not satisfy requirement "${supportedPlatformRange}"`);
1815
+ }
1816
+ }
1737
1817
  async function assertPackageJsonExists(projectRoot) {
1738
1818
  try {
1739
1819
  await readFile8(`${projectRoot}/package.json`, "utf8");
@@ -12,7 +12,7 @@ import {
12
12
  PackageRemoveCommand,
13
13
  PackageUpdateReferencesCommand,
14
14
  UpdateCommand
15
- } from "../chunk-n0q0pces.js";
15
+ } from "../chunk-2s546hay.js";
16
16
  import"../chunk-vcev74he.js";
17
17
  export {
18
18
  UpdateCommand,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "sourceHashes": {
3
- "./dist/commands/index.js": 2077043253,
4
- "./dist/main.js": 1478493098
3
+ "./dist/commands/index.js": 3807940790,
4
+ "./dist/main.js": 4186384902
5
5
  }
6
6
  }
package/dist/main.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  PackageRemoveCommand,
14
14
  PackageUpdateReferencesCommand,
15
15
  UpdateCommand
16
- } from "./chunk-n0q0pces.js";
16
+ } from "./chunk-2s546hay.js";
17
17
  import"./chunk-vcev74he.js";
18
18
 
19
19
  // src/main.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/cli",
3
- "version": "0.21.1",
3
+ "version": "0.27.0",
4
4
  "description": "The CLI for the Highstate project.",
5
5
  "type": "module",
6
6
  "files": [
@@ -34,8 +34,8 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@aws-crypto/crc32": "^5.2.0",
37
- "@highstate/backend": "0.20.0",
38
- "@highstate/contract": "0.20.0",
37
+ "@highstate/backend": "0.26.0",
38
+ "@highstate/contract": "0.26.0",
39
39
  "@inquirer/prompts": "^7.8.6",
40
40
  "@msgpack/msgpack": "^3.1.2",
41
41
  "@pulumi/pulumi": "3.232.0",
@@ -12,6 +12,22 @@ import {
12
12
  schemaTransformerPlugin,
13
13
  } from "../shared"
14
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
+
15
31
  export class BuildCommand extends Command {
16
32
  static paths = [["build"]]
17
33
 
@@ -25,6 +41,18 @@ export class BuildCommand extends Command {
25
41
  noSourceHash = Option.Boolean("--no-source-hash", false)
26
42
 
27
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> {
28
56
  const packageJson = await readPackageJSON()
29
57
 
30
58
  const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {})
@@ -2,12 +2,14 @@ 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
7
  import { addDevDependency } from "nypm"
8
8
  import { readPackageJSON, resolvePackageJSON } from "pkg-types"
9
9
  import { getBackendServices, logger } from "../shared"
10
10
 
11
+ let shuttingDown = false
12
+
11
13
  export class DesignerCommand extends Command {
12
14
  static paths = [["designer"]]
13
15
 
@@ -39,8 +41,26 @@ export class DesignerCommand extends Command {
39
41
 
40
42
  const oldConsoleLog = console.log
41
43
 
42
- const port = await getPort({ port: 3000 })
43
- 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 })
44
64
 
45
65
  const designerPackageJsonPath = importMetaResolve(
46
66
  "@highstate/designer/package.json",
@@ -49,22 +69,30 @@ export class DesignerCommand extends Command {
49
69
  const designerPackageJson = await readPackageJSON(designerPackageJsonPath)
50
70
 
51
71
  process.env.NITRO_PORT = port.toString()
52
- process.env.NITRO_HOST = "0.0.0.0"
72
+ process.env.NITRO_HOST = host
73
+ process.env.NITRO_BUN_IDLE_TIMEOUT ??= "255"
53
74
  process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version
54
75
  process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString()
55
76
 
56
- await new Promise<void>(resolve => {
57
- console.log = (message: string) => {
58
- if (message.startsWith("Listening on")) {
59
- 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
+ }
60
88
  }
61
- }
62
-
63
- const serverPath = importMetaResolve("@highstate/designer/server", packageJsonUrl)
64
- void import(serverPath)
65
- })
66
89
 
67
- 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
+ }
68
96
 
69
97
  consola.log(
70
98
  [
@@ -72,12 +100,17 @@ export class DesignerCommand extends Command {
72
100
  colorize("bold", colorize("cyanBright", "Highstate Designer")),
73
101
  "\n ",
74
102
  colorize("greenBright", "➜ Local: "),
75
- colorize("underline", colorize("cyanBright", `http://localhost:${port}`)),
103
+ colorize("underline", colorize("cyanBright", `http://highstate.localhost:${port}`)),
76
104
  "\n",
77
105
  ].join(""),
78
106
  )
79
107
 
80
- process.on("SIGINT", () => {
108
+ process.once("SIGINT", () => {
109
+ if (shuttingDown) {
110
+ return
111
+ }
112
+ shuttingDown = true
113
+
81
114
  process.stdout.write("\r")
82
115
  consola.info("shutting down highstate designer...")
83
116
 
@@ -6,6 +6,7 @@ import {
6
6
  applyOverrides,
7
7
  buildOverrides,
8
8
  fetchManifest,
9
+ fetchNpmPackument,
9
10
  getDependencyRange,
10
11
  getProjectPlatformVersion,
11
12
  logger,
@@ -52,45 +53,25 @@ export class UpdateCommand extends Command {
52
53
  const updatePlatform = this.platformOnly || !this.stdlibOnly
53
54
  const updateStdlib = this.stdlibOnly || !this.platformOnly
54
55
 
56
+ let currentPlatformVersion: string | undefined
57
+ let resolvedStdlibVersion = this.stdlibVersion
58
+
55
59
  if (this.stdlibOnly) {
56
- const currentPlatformVersion = await getProjectPlatformVersion(projectRoot)
57
- if (!currentPlatformVersion) {
60
+ const projectPlatformVersion = await getProjectPlatformVersion(projectRoot)
61
+ if (!projectPlatformVersion) {
58
62
  throw new Error('Current platform version is not set in overrides for "@highstate/pulumi"')
59
63
  }
60
64
 
61
- const targetStdlibVersion = (this.stdlibVersion ?? "").trim()
62
- if (targetStdlibVersion.length === 0) {
63
- throw new Error('Flag "--stdlib-version" must be provided when using "--stdlib"')
64
- }
65
-
66
- const stdlibManifest = await fetchManifest("@highstate/library", targetStdlibVersion)
67
- const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi")
68
- if (!supportedPlatformRange) {
69
- throw new Error(
70
- `Unable to infer "@highstate/pulumi" version from "@highstate/library@${targetStdlibVersion}"`,
71
- )
72
- }
73
-
74
- const validPlatform = semver.valid(currentPlatformVersion)
75
- if (!validPlatform) {
76
- throw new Error(
77
- `Current platform version is not a valid semver "${currentPlatformVersion}"`,
78
- )
79
- }
80
-
81
- const ok = semver.satisfies(validPlatform, supportedPlatformRange, {
82
- includePrerelease: true,
65
+ currentPlatformVersion = projectPlatformVersion
66
+ resolvedStdlibVersion = await resolveCompatibleStdlibVersion({
67
+ currentPlatformVersion: projectPlatformVersion,
68
+ stdlibVersion: this.stdlibVersion,
83
69
  })
84
- if (!ok) {
85
- throw new Error(
86
- `Current platform version "${currentPlatformVersion}" does not satisfy requirement "${supportedPlatformRange}"`,
87
- )
88
- }
89
70
  }
90
71
 
91
72
  const bundle = await resolveVersionBundle({
92
- platformVersion: updatePlatform ? this.platformVersion : undefined,
93
- stdlibVersion: updateStdlib ? this.stdlibVersion : undefined,
73
+ platformVersion: updatePlatform ? this.platformVersion : currentPlatformVersion,
74
+ stdlibVersion: updateStdlib ? resolvedStdlibVersion : undefined,
94
75
  })
95
76
 
96
77
  const overrides = buildOverrides(bundle)
@@ -126,6 +107,80 @@ export class UpdateCommand extends Command {
126
107
  }
127
108
  }
128
109
 
110
+ type ResolveCompatibleStdlibVersionArgs = {
111
+ currentPlatformVersion: string
112
+ stdlibVersion?: string
113
+ }
114
+
115
+ async function resolveCompatibleStdlibVersion(
116
+ args: ResolveCompatibleStdlibVersionArgs,
117
+ ): Promise<string> {
118
+ const validPlatform = semver.valid(args.currentPlatformVersion)
119
+ if (!validPlatform) {
120
+ throw new Error(
121
+ `Current platform version is not a valid semver "${args.currentPlatformVersion}"`,
122
+ )
123
+ }
124
+
125
+ const targetStdlibVersion = args.stdlibVersion?.trim()
126
+ if (targetStdlibVersion) {
127
+ await assertStdlibSupportsPlatform({
128
+ currentPlatformVersion: validPlatform,
129
+ stdlibVersion: targetStdlibVersion,
130
+ })
131
+
132
+ return targetStdlibVersion
133
+ }
134
+
135
+ const packument = await fetchNpmPackument("@highstate/library")
136
+ const sortedVersions = Object.entries(packument.versions ?? {})
137
+ .filter(([version]) => semver.valid(version))
138
+ .sort(([a], [b]) => semver.rcompare(a, b))
139
+
140
+ for (const [stdlibVersion, stdlibManifest] of sortedVersions) {
141
+ const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi")
142
+ if (!supportedPlatformRange) {
143
+ continue
144
+ }
145
+
146
+ const ok = semver.satisfies(validPlatform, supportedPlatformRange, {
147
+ includePrerelease: true,
148
+ })
149
+
150
+ if (ok) {
151
+ return stdlibVersion
152
+ }
153
+ }
154
+
155
+ throw new Error(
156
+ `Unable to find "@highstate/library" version compatible with platform "${validPlatform}"`,
157
+ )
158
+ }
159
+
160
+ type StdlibPlatformCompatibilityArgs = {
161
+ currentPlatformVersion: string
162
+ stdlibVersion: string
163
+ }
164
+
165
+ async function assertStdlibSupportsPlatform(args: StdlibPlatformCompatibilityArgs): Promise<void> {
166
+ const stdlibManifest = await fetchManifest("@highstate/library", args.stdlibVersion)
167
+ const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi")
168
+ if (!supportedPlatformRange) {
169
+ throw new Error(
170
+ `Unable to infer "@highstate/pulumi" version from "@highstate/library@${args.stdlibVersion}"`,
171
+ )
172
+ }
173
+
174
+ const ok = semver.satisfies(args.currentPlatformVersion, supportedPlatformRange, {
175
+ includePrerelease: true,
176
+ })
177
+ if (!ok) {
178
+ throw new Error(
179
+ `Current platform version "${args.currentPlatformVersion}" does not satisfy requirement "${supportedPlatformRange}"`,
180
+ )
181
+ }
182
+ }
183
+
129
184
  async function assertPackageJsonExists(projectRoot: string): Promise<void> {
130
185
  try {
131
186
  await readFile(`${projectRoot}/package.json`, "utf8")