@highstate/cli 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/assets/template/packages/library/package.tpl.json +2 -2
- package/assets/template/packages/units/package.tpl.json +7 -5
- package/dist/chunk-VNBLDKUT.js +1744 -0
- package/dist/chunk-VNBLDKUT.js.map +1 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/highstate.manifest.json +2 -1
- package/dist/main.js +10 -1372
- package/dist/main.js.map +1 -1
- package/package.json +13 -4
- package/src/commands/designer.ts +25 -2
- package/src/commands/index.ts +12 -0
- package/src/commands/init.ts +52 -23
- package/src/commands/package/create.ts +1 -1
- package/src/commands/package/list.ts +1 -1
- package/src/commands/package/remove.ts +1 -1
- package/src/commands/package/update-references.ts +1 -1
- package/src/commands/update.ts +156 -0
- package/src/main.ts +18 -15
- package/src/shared/entry-points.ts +11 -4
- package/src/shared/index.ts +8 -0
- package/src/shared/npm-registry.ts +67 -0
- package/src/shared/overrides.ts +73 -0
- package/src/shared/package-json.ts +6 -0
- package/src/shared/pnpm-workspace.ts +32 -0
- package/src/shared/project-versions.ts +54 -0
- package/src/shared/pulumi-cli.ts +21 -0
- package/src/shared/version-bundle.ts +51 -0
- package/src/shared/version-sets.ts +36 -0
- package/src/commands/package/index.ts +0 -4
|
@@ -0,0 +1,1744 @@
|
|
|
1
|
+
import { int32ToBytes } from './chunk-CMECLVT7.js';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
import { loadConfig } from '@highstate/backend';
|
|
4
|
+
import { identityToRecipient } from 'age-encryption';
|
|
5
|
+
import { Command, Option, UsageError } from 'clipanion';
|
|
6
|
+
import { writeFile, mkdir, access, readdir, rm, readFile, stat } from 'node:fs/promises';
|
|
7
|
+
import { PassThrough } from 'node:stream';
|
|
8
|
+
import { LogLevels, consola } from 'consola';
|
|
9
|
+
import pino, { levels } from 'pino';
|
|
10
|
+
import { resolve, relative, dirname, join } from 'node:path';
|
|
11
|
+
import Handlebars from 'handlebars';
|
|
12
|
+
import { readPackageJSON, resolvePackageJSON } from 'pkg-types';
|
|
13
|
+
import { parse, stringify } from 'yaml';
|
|
14
|
+
import { execFile } from 'node:child_process';
|
|
15
|
+
import { promisify } from 'node:util';
|
|
16
|
+
import MagicString from 'magic-string';
|
|
17
|
+
import { parseAsync } from 'oxc-parser';
|
|
18
|
+
import { walk } from 'oxc-walker';
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
21
|
+
import { crc32 } from '@aws-crypto/crc32';
|
|
22
|
+
import { resolve as resolve$1 } from 'import-meta-resolve';
|
|
23
|
+
import { existsSync } from 'node:fs';
|
|
24
|
+
import { input, confirm, select } from '@inquirer/prompts';
|
|
25
|
+
import { Table } from 'console-table-printer';
|
|
26
|
+
import { encode } from '@msgpack/msgpack';
|
|
27
|
+
import { mapValues } from 'remeda';
|
|
28
|
+
import { build } from 'tsup';
|
|
29
|
+
import { colorize } from 'consola/utils';
|
|
30
|
+
import { getPort } from 'get-port-please';
|
|
31
|
+
import { addDevDependency, detectPackageManager, installDependencies } from 'nypm';
|
|
32
|
+
import semver from 'semver';
|
|
33
|
+
|
|
34
|
+
var logger = pino(
|
|
35
|
+
{
|
|
36
|
+
name: "highstate-cli",
|
|
37
|
+
level: process.env.LOG_LEVEL ?? "info"
|
|
38
|
+
},
|
|
39
|
+
createConsolaStream()
|
|
40
|
+
);
|
|
41
|
+
consola.level = LogLevels[process.env.LOG_LEVEL ?? "info"];
|
|
42
|
+
function createConsolaStream() {
|
|
43
|
+
const stream = new PassThrough();
|
|
44
|
+
stream.on("data", (data) => {
|
|
45
|
+
const { level, msg, error } = JSON.parse(String(data));
|
|
46
|
+
const levelLabel = levels.labels[level];
|
|
47
|
+
switch (levelLabel) {
|
|
48
|
+
case "info":
|
|
49
|
+
consola.info(msg);
|
|
50
|
+
break;
|
|
51
|
+
case "warn":
|
|
52
|
+
consola.warn(msg);
|
|
53
|
+
break;
|
|
54
|
+
case "error":
|
|
55
|
+
if (error) {
|
|
56
|
+
consola.error(msg, error);
|
|
57
|
+
} else {
|
|
58
|
+
consola.error(msg);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case "debug":
|
|
62
|
+
consola.debug(msg);
|
|
63
|
+
break;
|
|
64
|
+
case "fatal":
|
|
65
|
+
consola.fatal(msg);
|
|
66
|
+
break;
|
|
67
|
+
case "trace":
|
|
68
|
+
consola.trace(msg);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return stream;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/shared/bin-transformer.ts
|
|
76
|
+
function createBinTransformerPlugin(sourceFilePaths) {
|
|
77
|
+
const filter = new RegExp(`(${sourceFilePaths.join("|")})$`);
|
|
78
|
+
logger.debug("created bin transformer plugin with filter: %s", filter);
|
|
79
|
+
return {
|
|
80
|
+
name: "bin-transformer",
|
|
81
|
+
setup(build2) {
|
|
82
|
+
build2.onLoad({ filter }, async (args) => {
|
|
83
|
+
const content = await readFile(args.path, "utf-8");
|
|
84
|
+
return {
|
|
85
|
+
contents: `#!/usr/bin/env node
|
|
86
|
+
|
|
87
|
+
${content}`,
|
|
88
|
+
loader: "ts"
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/shared/entry-points.ts
|
|
96
|
+
function extractEntryPoints(packageJson) {
|
|
97
|
+
const exports = packageJson.exports;
|
|
98
|
+
let bin = packageJson.bin;
|
|
99
|
+
if (!exports && !bin) {
|
|
100
|
+
logger.warn("no exports or bin found in package.json");
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
if (exports !== void 0 && (typeof exports !== "object" || Array.isArray(exports))) {
|
|
104
|
+
throw new Error("Exports field in package.json must be an object");
|
|
105
|
+
}
|
|
106
|
+
if (bin !== void 0 && typeof bin !== "object") {
|
|
107
|
+
if (!packageJson.name) {
|
|
108
|
+
throw new Error("Package name is required when bin is a string");
|
|
109
|
+
}
|
|
110
|
+
bin = { [packageJson.name]: bin };
|
|
111
|
+
}
|
|
112
|
+
const result = {};
|
|
113
|
+
if (exports) {
|
|
114
|
+
for (const [key, value] of Object.entries(exports)) {
|
|
115
|
+
let distPath;
|
|
116
|
+
if (typeof value === "string") {
|
|
117
|
+
distPath = value;
|
|
118
|
+
} else if (typeof value === "object" && !Array.isArray(value)) {
|
|
119
|
+
if (!value.default) {
|
|
120
|
+
throw new Error(`Export "${key}" must have a default field in package.json`);
|
|
121
|
+
}
|
|
122
|
+
if (typeof value.default !== "string") {
|
|
123
|
+
throw new Error(`Export "${key}" default field must be a string in package.json`);
|
|
124
|
+
}
|
|
125
|
+
distPath = value.default;
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error(`Export "${key}" must be a string or an object in package.json`);
|
|
128
|
+
}
|
|
129
|
+
const isJsonExport = distPath.endsWith(".json");
|
|
130
|
+
const isJsExport = distPath.endsWith(".js");
|
|
131
|
+
if (!isJsonExport && !isJsExport) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`The default value of export "${key}" must end with ".js" or ".json" in package.json, got "${distPath}"`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (isJsExport && !distPath.startsWith("./dist/")) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`The default value of export "${key}" must start with "./dist/" when exporting ".js" in package.json, got "${distPath}"`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (isJsonExport) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const targetName = distPath.slice(7).slice(0, -3);
|
|
145
|
+
result[targetName] = {
|
|
146
|
+
entryPoint: `./src/${targetName}.ts`,
|
|
147
|
+
targetName,
|
|
148
|
+
distPath,
|
|
149
|
+
isBin: false,
|
|
150
|
+
key
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (bin) {
|
|
155
|
+
for (const [key, value] of Object.entries(bin)) {
|
|
156
|
+
if (typeof value !== "string") {
|
|
157
|
+
throw new Error(`Bin entry "${key}" must be a string in package.json`);
|
|
158
|
+
}
|
|
159
|
+
const distPath = value;
|
|
160
|
+
if (!distPath.startsWith("./dist/")) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`The value of bin entry "${key}" must start with "./dist/" in package.json, got "${distPath}"`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (!distPath.endsWith(".js")) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`The value of bin entry "${key}" must end with ".js" in package.json, got "${distPath}"`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const targetName = distPath.slice(7).slice(0, -3);
|
|
171
|
+
result[targetName] = {
|
|
172
|
+
entryPoint: `./src/${targetName}.ts`,
|
|
173
|
+
targetName,
|
|
174
|
+
distPath,
|
|
175
|
+
isBin: true,
|
|
176
|
+
key
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
async function generateFromTemplate(templatePath, destinationPath, variables) {
|
|
183
|
+
const resolvedTemplatePath = resolve(templatePath);
|
|
184
|
+
const resolvedDestinationPath = resolve(destinationPath);
|
|
185
|
+
const templateStats = await stat(resolvedTemplatePath);
|
|
186
|
+
if (!templateStats.isDirectory()) {
|
|
187
|
+
throw new Error(`templatePath must be a directory: ${resolvedTemplatePath}`);
|
|
188
|
+
}
|
|
189
|
+
await mkdir(resolvedDestinationPath, { recursive: true });
|
|
190
|
+
const renderTemplate = (raw) => {
|
|
191
|
+
const template = Handlebars.compile(raw, {
|
|
192
|
+
strict: true,
|
|
193
|
+
noEscape: true
|
|
194
|
+
});
|
|
195
|
+
return template(variables);
|
|
196
|
+
};
|
|
197
|
+
const visit = async (absoluteSourcePath) => {
|
|
198
|
+
const sourceStats = await stat(absoluteSourcePath);
|
|
199
|
+
if (sourceStats.isDirectory()) {
|
|
200
|
+
const relativePath = relative(resolvedTemplatePath, absoluteSourcePath);
|
|
201
|
+
const destinationDirPath = join(resolvedDestinationPath, relativePath);
|
|
202
|
+
await mkdir(destinationDirPath, { recursive: true });
|
|
203
|
+
const entries = await readdir(absoluteSourcePath, { withFileTypes: true });
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
await visit(join(absoluteSourcePath, entry.name));
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!sourceStats.isFile()) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const relativeFilePath = relative(resolvedTemplatePath, absoluteSourcePath);
|
|
213
|
+
const destinationRelativePath = relativeFilePath.replaceAll(".tpl", "");
|
|
214
|
+
const destinationFilePath = join(resolvedDestinationPath, destinationRelativePath);
|
|
215
|
+
await mkdir(dirname(destinationFilePath), { recursive: true });
|
|
216
|
+
const contents = await readFile(absoluteSourcePath, "utf8");
|
|
217
|
+
const rendered = renderTemplate(contents);
|
|
218
|
+
if (rendered.trim().length === 0) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
await writeFile(destinationFilePath, rendered, "utf8");
|
|
222
|
+
};
|
|
223
|
+
await visit(resolvedTemplatePath);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/shared/npm-registry.ts
|
|
227
|
+
async function fetchNpmPackument(packageName) {
|
|
228
|
+
const encoded = encodeURIComponent(packageName);
|
|
229
|
+
const url = `https://registry.npmjs.org/${encoded}`;
|
|
230
|
+
const response = await fetch(url);
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Failed to fetch package "${packageName}" from NPM registry (HTTP ${response.status})`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return await response.json();
|
|
237
|
+
}
|
|
238
|
+
async function fetchLatestVersion(packageName) {
|
|
239
|
+
const packument = await fetchNpmPackument(packageName);
|
|
240
|
+
const latest = packument["dist-tags"]?.latest;
|
|
241
|
+
if (!latest) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`NPM registry response for package "${packageName}" does not include "dist-tags.latest"`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return latest;
|
|
247
|
+
}
|
|
248
|
+
async function fetchManifest(packageName, version) {
|
|
249
|
+
const packument = await fetchNpmPackument(packageName);
|
|
250
|
+
const manifest = packument.versions?.[version];
|
|
251
|
+
if (!manifest) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`NPM registry response for package "${packageName}" does not include version "${version}"`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return manifest;
|
|
257
|
+
}
|
|
258
|
+
function getDependencyRange(manifest, dependencyName) {
|
|
259
|
+
return manifest.peerDependencies?.[dependencyName] ?? manifest.dependencies?.[dependencyName] ?? manifest.optionalDependencies?.[dependencyName] ?? null;
|
|
260
|
+
}
|
|
261
|
+
async function writeJsonFile(filePath, value) {
|
|
262
|
+
const contents = `${JSON.stringify(value, null, 2)}
|
|
263
|
+
`;
|
|
264
|
+
await writeFile(filePath, contents, "utf8");
|
|
265
|
+
}
|
|
266
|
+
function resolvePnpmWorkspacePath(projectRoot) {
|
|
267
|
+
return resolve(projectRoot, "pnpm-workspace.yaml");
|
|
268
|
+
}
|
|
269
|
+
async function readPnpmWorkspace(filePath) {
|
|
270
|
+
const raw = await readFile(filePath, "utf8");
|
|
271
|
+
const parsed = parse(raw);
|
|
272
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
273
|
+
return {};
|
|
274
|
+
}
|
|
275
|
+
return parsed;
|
|
276
|
+
}
|
|
277
|
+
async function writePnpmWorkspace(filePath, workspace) {
|
|
278
|
+
const raw = stringify(workspace);
|
|
279
|
+
await writeFile(filePath, raw, "utf8");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/shared/version-sets.ts
|
|
283
|
+
var PLATFORM_PACKAGES = [
|
|
284
|
+
"@highstate/api",
|
|
285
|
+
"@highstate/backend",
|
|
286
|
+
"@highstate/backend-api",
|
|
287
|
+
"@highstate/cli",
|
|
288
|
+
"@highstate/contract",
|
|
289
|
+
"@highstate/designer",
|
|
290
|
+
"@highstate/pulumi",
|
|
291
|
+
"@highstate/worker-sdk"
|
|
292
|
+
];
|
|
293
|
+
var STDLIB_PACKAGES = [
|
|
294
|
+
"@highstate/cilium",
|
|
295
|
+
"@highstate/cloudflare",
|
|
296
|
+
"@highstate/common",
|
|
297
|
+
"@highstate/distributions",
|
|
298
|
+
"@highstate/git",
|
|
299
|
+
"@highstate/k3s",
|
|
300
|
+
"@highstate/k8s",
|
|
301
|
+
"@highstate/k8s.apps",
|
|
302
|
+
"@highstate/k8s.game-servers",
|
|
303
|
+
"@highstate/k8s.monitor-worker",
|
|
304
|
+
"@highstate/k8s.obfuscators",
|
|
305
|
+
"@highstate/library",
|
|
306
|
+
"@highstate/mullvad",
|
|
307
|
+
"@highstate/nixos",
|
|
308
|
+
"@highstate/proxmox",
|
|
309
|
+
"@highstate/restic",
|
|
310
|
+
"@highstate/sops",
|
|
311
|
+
"@highstate/talos",
|
|
312
|
+
"@highstate/timeweb",
|
|
313
|
+
"@highstate/wireguard",
|
|
314
|
+
"@highstate/yandex"
|
|
315
|
+
];
|
|
316
|
+
var PULUMI_PACKAGES = ["@pulumi/pulumi"];
|
|
317
|
+
|
|
318
|
+
// src/shared/overrides.ts
|
|
319
|
+
function buildOverrides(bundle) {
|
|
320
|
+
const platform = Object.fromEntries(PLATFORM_PACKAGES.map((name) => [name, bundle.platformVersion]));
|
|
321
|
+
const stdlib = Object.fromEntries(STDLIB_PACKAGES.map((name) => [name, bundle.stdlibVersion]));
|
|
322
|
+
const pulumi = Object.fromEntries(PULUMI_PACKAGES.map((name) => [name, bundle.pulumiVersion]));
|
|
323
|
+
const merged = {
|
|
324
|
+
...platform,
|
|
325
|
+
...stdlib,
|
|
326
|
+
...pulumi
|
|
327
|
+
};
|
|
328
|
+
return merged;
|
|
329
|
+
}
|
|
330
|
+
async function applyOverrides(args) {
|
|
331
|
+
const { packageManager, overrides, projectRoot } = args;
|
|
332
|
+
if (packageManager === "pnpm") {
|
|
333
|
+
const pnpmWorkspacePath = resolvePnpmWorkspacePath(projectRoot);
|
|
334
|
+
try {
|
|
335
|
+
await access(pnpmWorkspacePath);
|
|
336
|
+
} catch {
|
|
337
|
+
throw new Error(`PNPM workspace file is missing: "${pnpmWorkspacePath}"`);
|
|
338
|
+
}
|
|
339
|
+
const workspace = await readPnpmWorkspace(pnpmWorkspacePath);
|
|
340
|
+
const nextWorkspace = {
|
|
341
|
+
...workspace,
|
|
342
|
+
overrides
|
|
343
|
+
};
|
|
344
|
+
await writePnpmWorkspace(pnpmWorkspacePath, nextWorkspace);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const packageJsonPath = await resolvePackageJSON(projectRoot);
|
|
348
|
+
const packageJson = await readPackageJSON(projectRoot);
|
|
349
|
+
if (packageManager === "npm") {
|
|
350
|
+
await writeJsonFile(packageJsonPath, {
|
|
351
|
+
...packageJson,
|
|
352
|
+
overrides
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (packageManager === "yarn") {
|
|
357
|
+
await writeJsonFile(packageJsonPath, {
|
|
358
|
+
...packageJson,
|
|
359
|
+
resolutions: overrides
|
|
360
|
+
});
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
await writeJsonFile(packageJsonPath, packageJson);
|
|
364
|
+
}
|
|
365
|
+
async function getProjectOverrideVersion(projectRoot, args) {
|
|
366
|
+
const { packageManager, packageName } = args;
|
|
367
|
+
if (packageManager === "pnpm") {
|
|
368
|
+
const path = resolvePnpmWorkspacePath(projectRoot);
|
|
369
|
+
const workspace = await readPnpmWorkspace(path);
|
|
370
|
+
return workspace.overrides?.[packageName] ?? null;
|
|
371
|
+
}
|
|
372
|
+
const packageJsonPath = await resolvePackageJSON(projectRoot);
|
|
373
|
+
const rawPackageJson = await readFile(packageJsonPath, "utf8");
|
|
374
|
+
const packageJson = JSON.parse(rawPackageJson);
|
|
375
|
+
if (packageManager === "npm") {
|
|
376
|
+
const overrides = packageJson.overrides;
|
|
377
|
+
return overrides?.[packageName] ?? null;
|
|
378
|
+
}
|
|
379
|
+
if (packageManager === "yarn") {
|
|
380
|
+
const resolutions = packageJson.resolutions;
|
|
381
|
+
return resolutions?.[packageName] ?? null;
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
async function getProjectPlatformVersion(projectRoot, args) {
|
|
386
|
+
return await getProjectOverrideVersion(projectRoot, {
|
|
387
|
+
packageManager: args.packageManager,
|
|
388
|
+
packageName: "@highstate/pulumi"
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
async function getProjectPulumiSdkVersion(projectRoot, args) {
|
|
392
|
+
return await getProjectOverrideVersion(projectRoot, {
|
|
393
|
+
packageManager: args.packageManager,
|
|
394
|
+
packageName: "@pulumi/pulumi"
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
var execFileAsync = promisify(execFile);
|
|
398
|
+
async function getPulumiCliVersion(cwd) {
|
|
399
|
+
try {
|
|
400
|
+
const { stdout } = await execFileAsync("pulumi", ["version"], {
|
|
401
|
+
cwd
|
|
402
|
+
});
|
|
403
|
+
const raw = stdout.trim();
|
|
404
|
+
if (raw.length === 0) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
return raw.startsWith("v") ? raw.slice(1) : raw;
|
|
408
|
+
} catch {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
var schemaTransformerPlugin = {
|
|
413
|
+
name: "schema-transformer",
|
|
414
|
+
setup(build2) {
|
|
415
|
+
build2.onLoad({ filter: /src\/.*\.ts$/ }, async (args) => {
|
|
416
|
+
const content = await readFile(args.path, "utf-8");
|
|
417
|
+
return {
|
|
418
|
+
contents: await applySchemaTransformations(content),
|
|
419
|
+
loader: "ts"
|
|
420
|
+
};
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
async function applySchemaTransformations(content) {
|
|
425
|
+
let result = await applyZodMetaTransformations(content);
|
|
426
|
+
result = await applyHelperFunctionTransformations(result);
|
|
427
|
+
result = await applyDefineFunctionMetaTransformations(result);
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
async function applyZodMetaTransformations(content) {
|
|
431
|
+
const { program, comments } = await parseAsync("file.ts", content);
|
|
432
|
+
const parentStack = [];
|
|
433
|
+
let hasTransformations = false;
|
|
434
|
+
const result = new MagicString(content);
|
|
435
|
+
walk(program, {
|
|
436
|
+
enter(node) {
|
|
437
|
+
parentStack.push(node);
|
|
438
|
+
if (isZodObjectProperty(node, parentStack)) {
|
|
439
|
+
const jsdoc = findLeadingComment(content, node, comments);
|
|
440
|
+
if (jsdoc) {
|
|
441
|
+
const description = cleanJsdoc(jsdoc.value);
|
|
442
|
+
const fieldName = "name" in node.key && typeof node.key.name === "string" ? node.key.name : "unknown";
|
|
443
|
+
const originalValue = content.substring(node.value.start, node.value.end);
|
|
444
|
+
if (!originalValue.includes(".meta(")) {
|
|
445
|
+
const newValue = `${originalValue}.meta({ title: __camelCaseToHumanReadable("${fieldName}"), description: \`${description}\` })`;
|
|
446
|
+
result.update(node.value.start, node.value.end, newValue);
|
|
447
|
+
hasTransformations = true;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
leave() {
|
|
453
|
+
parentStack.pop();
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
let finalResult = result.toString();
|
|
457
|
+
if (hasTransformations && !content.includes("__camelCaseToHumanReadable")) {
|
|
458
|
+
finalResult = 'import { camelCaseToHumanReadable as __camelCaseToHumanReadable } from "@highstate/contract"\n' + finalResult;
|
|
459
|
+
}
|
|
460
|
+
return finalResult;
|
|
461
|
+
}
|
|
462
|
+
async function applyHelperFunctionTransformations(content) {
|
|
463
|
+
const { program, comments } = await parseAsync("file.ts", content);
|
|
464
|
+
const parentStack = [];
|
|
465
|
+
let hasTransformations = false;
|
|
466
|
+
const result = new MagicString(content);
|
|
467
|
+
walk(program, {
|
|
468
|
+
enter(node) {
|
|
469
|
+
parentStack.push(node);
|
|
470
|
+
if (node.type === "Property" && "key" in node && node.key?.type === "Identifier") {
|
|
471
|
+
const propertyNode = node;
|
|
472
|
+
const helperFunction = getHelperFunctionForProperty(parentStack);
|
|
473
|
+
if (helperFunction) {
|
|
474
|
+
const jsdoc = findLeadingComment(content, node, comments);
|
|
475
|
+
if (jsdoc) {
|
|
476
|
+
const description = cleanJsdoc(jsdoc.value);
|
|
477
|
+
const originalValue = content.substring(
|
|
478
|
+
propertyNode.value.start,
|
|
479
|
+
propertyNode.value.end
|
|
480
|
+
);
|
|
481
|
+
const newValue = `${helperFunction}(${originalValue}, \`${description}\`)`;
|
|
482
|
+
result.update(propertyNode.value.start, propertyNode.value.end, newValue);
|
|
483
|
+
hasTransformations = true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
leave() {
|
|
489
|
+
parentStack.pop();
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
let finalResult = result.toString();
|
|
493
|
+
if (hasTransformations && !content.includes("$addArgumentDescription")) {
|
|
494
|
+
finalResult = 'import { $addArgumentDescription, $addInputDescription } from "@highstate/contract"\n' + finalResult;
|
|
495
|
+
}
|
|
496
|
+
return finalResult;
|
|
497
|
+
}
|
|
498
|
+
async function applyDefineFunctionMetaTransformations(content) {
|
|
499
|
+
const { program, comments } = await parseAsync("file.ts", content);
|
|
500
|
+
const parentStack = [];
|
|
501
|
+
const result = new MagicString(content);
|
|
502
|
+
walk(program, {
|
|
503
|
+
enter(node) {
|
|
504
|
+
parentStack.push(node);
|
|
505
|
+
if (node.type === "CallExpression" && "callee" in node && node.callee.type === "Identifier") {
|
|
506
|
+
const callNode = node;
|
|
507
|
+
const callee = callNode.callee;
|
|
508
|
+
const functionName = "name" in callee && typeof callee.name === "string" ? callee.name : void 0;
|
|
509
|
+
if (functionName && ["defineUnit", "defineEntity", "defineComponent"].includes(functionName)) {
|
|
510
|
+
const jsdoc = findJsdocForDefineFunction(content, parentStack, comments);
|
|
511
|
+
if (jsdoc && callNode.arguments && callNode.arguments.length > 0) {
|
|
512
|
+
const description = cleanJsdoc(jsdoc.value);
|
|
513
|
+
const firstArg = callNode.arguments[0];
|
|
514
|
+
if (firstArg.type === "ObjectExpression" && "properties" in firstArg) {
|
|
515
|
+
const properties = firstArg.properties;
|
|
516
|
+
const metaProperty = properties?.find(
|
|
517
|
+
(prop) => prop.type === "Property" && "key" in prop && prop.key?.type === "Identifier" && prop.key?.name === "meta"
|
|
518
|
+
);
|
|
519
|
+
if (metaProperty && "value" in metaProperty) {
|
|
520
|
+
const originalMetaValue = content.substring(
|
|
521
|
+
metaProperty.value.start,
|
|
522
|
+
metaProperty.value.end
|
|
523
|
+
);
|
|
524
|
+
const newMetaValue = injectDescriptionIntoMetaObject(originalMetaValue, description);
|
|
525
|
+
result.update(metaProperty.value.start, metaProperty.value.end, newMetaValue);
|
|
526
|
+
} else if (properties && properties.length > 0) {
|
|
527
|
+
const lastProperty = properties[properties.length - 1];
|
|
528
|
+
if (lastProperty && "end" in lastProperty) {
|
|
529
|
+
const insertPos = lastProperty.end;
|
|
530
|
+
const newMetaProperty = `,
|
|
531
|
+
|
|
532
|
+
meta: {
|
|
533
|
+
description: \`${description}\`,
|
|
534
|
+
}`;
|
|
535
|
+
result.appendLeft(insertPos, newMetaProperty);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
leave() {
|
|
544
|
+
parentStack.pop();
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
return result.toString();
|
|
548
|
+
}
|
|
549
|
+
function findJsdocForDefineFunction(content, parentStack, comments) {
|
|
550
|
+
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
551
|
+
const node = parentStack[i];
|
|
552
|
+
if (node.type === "VariableDeclarator" && "id" in node && node.id?.type === "Identifier") {
|
|
553
|
+
const jsdoc = findLeadingComment(content, node, comments);
|
|
554
|
+
if (jsdoc) return jsdoc;
|
|
555
|
+
}
|
|
556
|
+
if (node.type === "VariableDeclaration") {
|
|
557
|
+
const jsdoc = findLeadingComment(content, node, comments);
|
|
558
|
+
if (jsdoc) return jsdoc;
|
|
559
|
+
}
|
|
560
|
+
if (node.type === "ExportNamedDeclaration" && "declaration" in node && node.declaration) {
|
|
561
|
+
const jsdoc = findLeadingComment(content, node, comments);
|
|
562
|
+
if (jsdoc) return jsdoc;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
function isZodObjectProperty(node, parentStack) {
|
|
568
|
+
return node.type === "Property" && "key" in node && node.key?.type === "Identifier" && isInsideZodObject(parentStack);
|
|
569
|
+
}
|
|
570
|
+
function findLeadingComment(content, node, comments) {
|
|
571
|
+
return comments.find((comment) => isLeadingComment(content, node, comment)) ?? null;
|
|
572
|
+
}
|
|
573
|
+
function isLeadingComment(content, node, comment) {
|
|
574
|
+
if (comment.end > node.start) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
const contentRange = content.substring(comment.end, node.start);
|
|
578
|
+
return contentRange.trim().length === 0;
|
|
579
|
+
}
|
|
580
|
+
function cleanJsdoc(str) {
|
|
581
|
+
return str.replace(/^\s*\*/gm, "").replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\${/g, "\\${").trim();
|
|
582
|
+
}
|
|
583
|
+
function injectDescriptionIntoMetaObject(objectString, description) {
|
|
584
|
+
const trimmed = objectString.trim();
|
|
585
|
+
const hasDescription = /description\s*:/.test(trimmed);
|
|
586
|
+
if (hasDescription) {
|
|
587
|
+
return trimmed.replace(/description\s*:\s*`[^`]*`/, `description: \`${description}\``);
|
|
588
|
+
} else {
|
|
589
|
+
const openBraceIndex = trimmed.indexOf("{");
|
|
590
|
+
if (openBraceIndex === -1) {
|
|
591
|
+
return trimmed;
|
|
592
|
+
}
|
|
593
|
+
const beforeBrace = trimmed.substring(0, openBraceIndex + 1);
|
|
594
|
+
const afterBrace = trimmed.substring(openBraceIndex + 1);
|
|
595
|
+
return `${beforeBrace}
|
|
596
|
+
description: \`${description}\`,${afterBrace}`;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function isInsideZodObject(parentStack) {
|
|
600
|
+
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
601
|
+
const node = parentStack[i];
|
|
602
|
+
if (node.type === "CallExpression" && "callee" in node && node.callee.type === "MemberExpression" && isZodObjectCall(node.callee)) {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
function getHelperFunctionForProperty(parentStack) {
|
|
609
|
+
if (parentStack.length < 3) {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
const objectExpression = parentStack[parentStack.length - 2];
|
|
613
|
+
if (!objectExpression || objectExpression.type !== "ObjectExpression") {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
const containerParent = parentStack[parentStack.length - 3];
|
|
617
|
+
if (!containerParent) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
if (containerParent.type === "Property" && "value" in containerParent && containerParent.value === objectExpression && containerParent.key?.type === "Identifier") {
|
|
621
|
+
const keyName = containerParent.key.name;
|
|
622
|
+
if (keyName === "inputs" || keyName === "outputs") {
|
|
623
|
+
return "$addInputDescription";
|
|
624
|
+
}
|
|
625
|
+
if (keyName === "args" || keyName === "secrets") {
|
|
626
|
+
return "$addArgumentDescription";
|
|
627
|
+
}
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
if (containerParent.type === "CallExpression" && containerParent.callee.type === "Identifier" && containerParent.arguments.length > 0 && containerParent.arguments[0] === objectExpression) {
|
|
631
|
+
const calleeName = containerParent.callee.name;
|
|
632
|
+
if (calleeName === "$inputs" || calleeName === "$outputs") {
|
|
633
|
+
return "$addInputDescription";
|
|
634
|
+
}
|
|
635
|
+
if (calleeName === "$args" || calleeName === "$secrets") {
|
|
636
|
+
return "$addArgumentDescription";
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
function isZodObjectCall(memberExpression) {
|
|
642
|
+
if (memberExpression.type !== "MemberExpression" || !("object" in memberExpression) || !("property" in memberExpression)) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const member = memberExpression;
|
|
646
|
+
if (member.object.type === "Identifier" && "name" in member.object && member.object.name === "z" && member.property.type === "Identifier" && member.property.name === "object") {
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
if (member.property.type === "Identifier" && member.property.name === "object" && member.object.type === "CallExpression" && "callee" in member.object && member.object.callee.type === "MemberExpression") {
|
|
650
|
+
return startsWithZodCall(member.object);
|
|
651
|
+
}
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
function startsWithZodCall(callExpression) {
|
|
655
|
+
if (!callExpression || callExpression.type !== "CallExpression") {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
if (callExpression.callee.type === "MemberExpression") {
|
|
659
|
+
const callee = callExpression.callee;
|
|
660
|
+
if (callee.object.type === "Identifier" && "name" in callee.object && callee.object.name === "z") {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
if (callee.object.type === "CallExpression") {
|
|
664
|
+
return startsWithZodCall(callee.object);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
var sourceHashConfigSchema = z.discriminatedUnion("mode", [
|
|
670
|
+
z.object({
|
|
671
|
+
mode: z.literal("manual"),
|
|
672
|
+
version: z.string()
|
|
673
|
+
}),
|
|
674
|
+
z.object({
|
|
675
|
+
mode: z.literal("auto")
|
|
676
|
+
}),
|
|
677
|
+
z.object({
|
|
678
|
+
mode: z.literal("version")
|
|
679
|
+
}),
|
|
680
|
+
z.object({
|
|
681
|
+
mode: z.literal("none")
|
|
682
|
+
})
|
|
683
|
+
]);
|
|
684
|
+
var highstateConfigSchema = z.object({
|
|
685
|
+
type: z.enum(["source", "library", "worker"]).default("source"),
|
|
686
|
+
sourceHash: z.union([sourceHashConfigSchema, z.record(z.string(), sourceHashConfigSchema)]).optional()
|
|
687
|
+
});
|
|
688
|
+
var highstateManifestSchema = z.object({
|
|
689
|
+
sourceHashes: z.record(z.string(), z.number()).optional()
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// src/shared/services.ts
|
|
693
|
+
var services;
|
|
694
|
+
var disposePromise;
|
|
695
|
+
function getBackendServices() {
|
|
696
|
+
if (services) {
|
|
697
|
+
return services;
|
|
698
|
+
}
|
|
699
|
+
services = import('@highstate/backend').then(({ getSharedServices }) => {
|
|
700
|
+
return getSharedServices({
|
|
701
|
+
services: {
|
|
702
|
+
logger: logger.child({}, { msgPrefix: "[backend] " })
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
return services;
|
|
707
|
+
}
|
|
708
|
+
function disposeServices() {
|
|
709
|
+
if (!services) {
|
|
710
|
+
return Promise.resolve();
|
|
711
|
+
}
|
|
712
|
+
if (disposePromise) {
|
|
713
|
+
return disposePromise;
|
|
714
|
+
}
|
|
715
|
+
disposePromise = import('@highstate/backend').then(({ disposeServices: disposeServices2 }) => services.then((s) => disposeServices2(s)));
|
|
716
|
+
return disposePromise;
|
|
717
|
+
}
|
|
718
|
+
var SourceHashCalculator = class {
|
|
719
|
+
constructor(packageJsonPath, packageJson, logger2) {
|
|
720
|
+
this.packageJsonPath = packageJsonPath;
|
|
721
|
+
this.packageJson = packageJson;
|
|
722
|
+
this.logger = logger2;
|
|
723
|
+
}
|
|
724
|
+
dependencyHashes = /* @__PURE__ */ new Map();
|
|
725
|
+
fileHashes = /* @__PURE__ */ new Map();
|
|
726
|
+
/**
|
|
727
|
+
* Calculates CRC32 hash of a string.
|
|
728
|
+
*/
|
|
729
|
+
hashString(input3) {
|
|
730
|
+
return crc32(Buffer.from(input3));
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Gets the highstate configuration from package.json with defaults.
|
|
734
|
+
*/
|
|
735
|
+
getHighstateConfig(packageJson) {
|
|
736
|
+
const rawConfig = packageJson.highstate;
|
|
737
|
+
if (!rawConfig) {
|
|
738
|
+
return { type: "source" };
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
return highstateConfigSchema.parse(rawConfig);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
this.logger.warn(
|
|
744
|
+
{ error, packageName: packageJson.name },
|
|
745
|
+
"invalid highstate configuration, using defaults"
|
|
746
|
+
);
|
|
747
|
+
return { type: "source" };
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Gets the effective source hash configuration with defaults for a specific output.
|
|
752
|
+
*/
|
|
753
|
+
getSourceHashConfig(highstateConfig, exportKey) {
|
|
754
|
+
if (highstateConfig.sourceHash) {
|
|
755
|
+
const singleConfigResult = sourceHashConfigSchema.safeParse(highstateConfig.sourceHash);
|
|
756
|
+
if (singleConfigResult.success) {
|
|
757
|
+
return singleConfigResult.data;
|
|
758
|
+
}
|
|
759
|
+
const recordConfigResult = z.record(z.string(), sourceHashConfigSchema).safeParse(highstateConfig.sourceHash);
|
|
760
|
+
if (recordConfigResult.success && exportKey) {
|
|
761
|
+
const perOutputConfig = recordConfigResult.data[exportKey];
|
|
762
|
+
if (perOutputConfig) {
|
|
763
|
+
return perOutputConfig;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (highstateConfig.type === "library") {
|
|
768
|
+
return { mode: "none" };
|
|
769
|
+
}
|
|
770
|
+
return { mode: "auto" };
|
|
771
|
+
}
|
|
772
|
+
async writeHighstateManifest(distBasePath, distPathToExportKey) {
|
|
773
|
+
const highstateConfig = this.getHighstateConfig(this.packageJson);
|
|
774
|
+
const promises = [];
|
|
775
|
+
for (const [distPath, exportKey] of distPathToExportKey) {
|
|
776
|
+
const fullPath = resolve(distPath);
|
|
777
|
+
const sourceHashConfig = this.getSourceHashConfig(highstateConfig, exportKey);
|
|
778
|
+
switch (sourceHashConfig.mode) {
|
|
779
|
+
case "manual":
|
|
780
|
+
promises.push(
|
|
781
|
+
Promise.resolve({
|
|
782
|
+
distPath,
|
|
783
|
+
hash: this.hashString(sourceHashConfig.version)
|
|
784
|
+
})
|
|
785
|
+
);
|
|
786
|
+
break;
|
|
787
|
+
case "version":
|
|
788
|
+
promises.push(
|
|
789
|
+
Promise.resolve({
|
|
790
|
+
distPath,
|
|
791
|
+
hash: this.hashString(this.packageJson.version ?? "")
|
|
792
|
+
})
|
|
793
|
+
);
|
|
794
|
+
break;
|
|
795
|
+
case "none":
|
|
796
|
+
promises.push(
|
|
797
|
+
Promise.resolve({
|
|
798
|
+
distPath,
|
|
799
|
+
hash: 0
|
|
800
|
+
})
|
|
801
|
+
);
|
|
802
|
+
break;
|
|
803
|
+
default:
|
|
804
|
+
promises.push(
|
|
805
|
+
this.getFileHash(fullPath).then((hash) => ({
|
|
806
|
+
distPath,
|
|
807
|
+
hash
|
|
808
|
+
}))
|
|
809
|
+
);
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const manifest = {
|
|
814
|
+
sourceHashes: {}
|
|
815
|
+
};
|
|
816
|
+
const hashes = await Promise.all(promises);
|
|
817
|
+
for (const { distPath, hash } of hashes) {
|
|
818
|
+
manifest.sourceHashes[distPath] = hash;
|
|
819
|
+
}
|
|
820
|
+
const manifestPath = resolve(distBasePath, "highstate.manifest.json");
|
|
821
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
822
|
+
}
|
|
823
|
+
async getFileHash(fullPath) {
|
|
824
|
+
const existingHash = this.fileHashes.get(fullPath);
|
|
825
|
+
if (existingHash) {
|
|
826
|
+
return existingHash;
|
|
827
|
+
}
|
|
828
|
+
const hash = this.calculateFileHash(fullPath);
|
|
829
|
+
this.fileHashes.set(fullPath, hash);
|
|
830
|
+
return hash;
|
|
831
|
+
}
|
|
832
|
+
async calculateFileHash(fullPath) {
|
|
833
|
+
const content = await readFile(fullPath, "utf8");
|
|
834
|
+
const fileDeps = this.parseDependencies(fullPath, content);
|
|
835
|
+
const hashes = await Promise.all([
|
|
836
|
+
this.hashString(content),
|
|
837
|
+
...fileDeps.map((dep) => this.getDependencyHash(dep))
|
|
838
|
+
]);
|
|
839
|
+
return crc32(Buffer.concat(hashes.map(int32ToBytes)));
|
|
840
|
+
}
|
|
841
|
+
getDependencyHash(dependency) {
|
|
842
|
+
const existingHash = this.dependencyHashes.get(dependency.id);
|
|
843
|
+
if (existingHash) {
|
|
844
|
+
return existingHash;
|
|
845
|
+
}
|
|
846
|
+
const hash = this.calculateDependencyHash(dependency);
|
|
847
|
+
this.dependencyHashes.set(dependency.id, hash);
|
|
848
|
+
return hash;
|
|
849
|
+
}
|
|
850
|
+
async calculateDependencyHash(dependency) {
|
|
851
|
+
switch (dependency.type) {
|
|
852
|
+
case "relative": {
|
|
853
|
+
return await this.getFileHash(dependency.fullPath);
|
|
854
|
+
}
|
|
855
|
+
case "npm": {
|
|
856
|
+
let resolvedUrl;
|
|
857
|
+
try {
|
|
858
|
+
const baseUrl = pathToFileURL(this.packageJsonPath);
|
|
859
|
+
resolvedUrl = resolve$1(dependency.package, baseUrl.toString());
|
|
860
|
+
} catch (error) {
|
|
861
|
+
this.logger.error(`failed to resolve package "%s"`, dependency.package);
|
|
862
|
+
throw error;
|
|
863
|
+
}
|
|
864
|
+
const resolvedPath = fileURLToPath(resolvedUrl);
|
|
865
|
+
const [depPackageJsonPath, depPackageJson] = await this.getPackageJson(resolvedPath);
|
|
866
|
+
const packageName = depPackageJson.name;
|
|
867
|
+
this.logger.debug(
|
|
868
|
+
`resolved package.json for "%s": "%s"`,
|
|
869
|
+
dependency.package,
|
|
870
|
+
depPackageJsonPath
|
|
871
|
+
);
|
|
872
|
+
if (!this.packageJson.dependencies?.[packageName] && !this.packageJson.peerDependencies?.[packageName]) {
|
|
873
|
+
this.logger.warn(`package "%s" is not listed in package.json dependencies`, packageName);
|
|
874
|
+
}
|
|
875
|
+
let relativePath = relative(dirname(depPackageJsonPath), resolvedPath);
|
|
876
|
+
relativePath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
877
|
+
const highstateManifestPath = resolve(
|
|
878
|
+
dirname(depPackageJsonPath),
|
|
879
|
+
"dist",
|
|
880
|
+
"highstate.manifest.json"
|
|
881
|
+
);
|
|
882
|
+
let manifest;
|
|
883
|
+
try {
|
|
884
|
+
const manifestContent = await readFile(highstateManifestPath, "utf8");
|
|
885
|
+
manifest = highstateManifestSchema.parse(JSON.parse(manifestContent));
|
|
886
|
+
} catch (error) {
|
|
887
|
+
this.logger.debug(
|
|
888
|
+
{ error },
|
|
889
|
+
`failed to read highstate manifest for package "%s"`,
|
|
890
|
+
packageName
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
const sourceHash = manifest?.sourceHashes?.[relativePath];
|
|
894
|
+
if (sourceHash) {
|
|
895
|
+
this.logger.debug(`resolved source hash for package "%s"`, packageName);
|
|
896
|
+
return sourceHash;
|
|
897
|
+
}
|
|
898
|
+
this.logger.debug(`using package version as a fallback hash for "%s"`, packageName);
|
|
899
|
+
return this.hashString(depPackageJson.version ?? "0.0.0");
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async getPackageJson(basePath) {
|
|
904
|
+
while (true) {
|
|
905
|
+
const packageJson = await readPackageJSON(basePath);
|
|
906
|
+
if (packageJson.name) {
|
|
907
|
+
const packageJsonPath = await resolvePackageJSON(basePath);
|
|
908
|
+
return [packageJsonPath, packageJson];
|
|
909
|
+
}
|
|
910
|
+
basePath = resolve(dirname(basePath), "..");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
parseDependencies(filePath, content) {
|
|
914
|
+
const dependencyRegex = /^[ \t]*import[\s\S]*?\bfrom\s*["']((?<relativePath>\.\.?\/[^"']+)|(?<nodeBuiltin>node:[^"']+)|(?<npmPackage>[^"']+))["']/gm;
|
|
915
|
+
const matches = content.matchAll(dependencyRegex);
|
|
916
|
+
const dependencies = [];
|
|
917
|
+
for (const match of matches) {
|
|
918
|
+
const { nodeBuiltin, npmPackage, relativePath } = match.groups;
|
|
919
|
+
if (relativePath) {
|
|
920
|
+
const fullPath = resolve(dirname(filePath), relativePath);
|
|
921
|
+
dependencies.push({
|
|
922
|
+
type: "relative",
|
|
923
|
+
id: `relative:${fullPath}`,
|
|
924
|
+
fullPath
|
|
925
|
+
});
|
|
926
|
+
} else if (npmPackage) {
|
|
927
|
+
dependencies.push({
|
|
928
|
+
type: "npm",
|
|
929
|
+
id: `npm:${npmPackage}`,
|
|
930
|
+
package: npmPackage
|
|
931
|
+
});
|
|
932
|
+
} else ;
|
|
933
|
+
}
|
|
934
|
+
return dependencies;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
// src/shared/version-bundle.ts
|
|
939
|
+
var platformSourcePackage = "@highstate/pulumi";
|
|
940
|
+
var stdlibSourcePackage = "@highstate/library";
|
|
941
|
+
async function resolveVersionBundle(args) {
|
|
942
|
+
const platformVersion = normalizeProvidedVersion(args.platformVersion, "platform");
|
|
943
|
+
const stdlibVersion = normalizeProvidedVersion(args.stdlibVersion, "stdlib");
|
|
944
|
+
const resolvedPlatformVersion = platformVersion ?? await fetchLatestVersion(platformSourcePackage);
|
|
945
|
+
const resolvedStdlibVersion = stdlibVersion ?? await fetchLatestVersion(stdlibSourcePackage);
|
|
946
|
+
const platformManifest = await fetchManifest(platformSourcePackage, resolvedPlatformVersion);
|
|
947
|
+
const inferredPulumi = getDependencyRange(platformManifest, "@pulumi/pulumi");
|
|
948
|
+
if (!inferredPulumi) {
|
|
949
|
+
throw new Error(
|
|
950
|
+
`Unable to infer "@pulumi/pulumi" version from "${platformSourcePackage}@${resolvedPlatformVersion}"`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
return {
|
|
954
|
+
platformVersion: resolvedPlatformVersion,
|
|
955
|
+
stdlibVersion: resolvedStdlibVersion,
|
|
956
|
+
pulumiVersion: inferredPulumi
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function normalizeProvidedVersion(value, label) {
|
|
960
|
+
if (value === void 0) {
|
|
961
|
+
return void 0;
|
|
962
|
+
}
|
|
963
|
+
const trimmed = value.trim();
|
|
964
|
+
if (trimmed.length === 0) {
|
|
965
|
+
throw new Error(`Version flag "${label}" must not be empty`);
|
|
966
|
+
}
|
|
967
|
+
return trimmed;
|
|
968
|
+
}
|
|
969
|
+
var packageJsonSchema = z.object({
|
|
970
|
+
name: z.string(),
|
|
971
|
+
highstate: highstateConfigSchema.optional()
|
|
972
|
+
});
|
|
973
|
+
function generateTsconfigContent(workspaceRoot, packagePath) {
|
|
974
|
+
const relativePath = relative(workspaceRoot, packagePath);
|
|
975
|
+
const depth = relativePath.split("/").length;
|
|
976
|
+
const relativeNodeModules = `${"../".repeat(depth)}node_modules/@highstate/cli/assets/tsconfig.base.json`;
|
|
977
|
+
return {
|
|
978
|
+
extends: relativeNodeModules,
|
|
979
|
+
include: ["./src/**/*.ts", "./package.json", "./assets/**/*.json"]
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
async function findWorkspaceRoot(startPath = process.cwd()) {
|
|
983
|
+
let currentPath = resolve(startPath);
|
|
984
|
+
while (currentPath !== "/") {
|
|
985
|
+
const packageJsonPath = join(currentPath, "package.json");
|
|
986
|
+
if (existsSync(packageJsonPath)) {
|
|
987
|
+
try {
|
|
988
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
989
|
+
const packageJson = JSON.parse(content);
|
|
990
|
+
if (packageJson.workspaces) {
|
|
991
|
+
return currentPath;
|
|
992
|
+
}
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const parentPath = resolve(currentPath, "..");
|
|
997
|
+
if (parentPath === currentPath) break;
|
|
998
|
+
currentPath = parentPath;
|
|
999
|
+
}
|
|
1000
|
+
throw new Error("Could not find workspace root (no package.json with workspaces found)");
|
|
1001
|
+
}
|
|
1002
|
+
async function scanWorkspacePackages(workspaceRoot) {
|
|
1003
|
+
const packages = [];
|
|
1004
|
+
const packagesDir = join(workspaceRoot, "packages");
|
|
1005
|
+
if (!existsSync(packagesDir)) {
|
|
1006
|
+
return packages;
|
|
1007
|
+
}
|
|
1008
|
+
async function scanDirectory(dirPath, depth = 0) {
|
|
1009
|
+
const dirName = relative(packagesDir, dirPath).split("/").pop();
|
|
1010
|
+
if (dirName?.startsWith(".") || dirName === "node_modules") {
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
1014
|
+
for (const entry of entries) {
|
|
1015
|
+
if (!entry.isDirectory()) continue;
|
|
1016
|
+
const entryPath = join(dirPath, entry.name);
|
|
1017
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const packageJsonPath = join(entryPath, "package.json");
|
|
1021
|
+
if (existsSync(packageJsonPath)) {
|
|
1022
|
+
try {
|
|
1023
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
1024
|
+
const packageJson = packageJsonSchema.parse(JSON.parse(content));
|
|
1025
|
+
const relativePath = relative(workspaceRoot, entryPath);
|
|
1026
|
+
const type = packageJson.highstate?.type ?? "source";
|
|
1027
|
+
packages.push({
|
|
1028
|
+
path: entryPath,
|
|
1029
|
+
relativePath,
|
|
1030
|
+
name: packageJson.name,
|
|
1031
|
+
type
|
|
1032
|
+
});
|
|
1033
|
+
} catch {
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (depth < 3) {
|
|
1037
|
+
await scanDirectory(entryPath, depth + 1);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
await scanDirectory(packagesDir);
|
|
1042
|
+
return packages.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
1043
|
+
}
|
|
1044
|
+
async function updateTsconfigReferences(workspaceRoot, packages, ensureTsconfigs = false) {
|
|
1045
|
+
const tsconfigPath = join(workspaceRoot, "tsconfig.json");
|
|
1046
|
+
if (ensureTsconfigs) {
|
|
1047
|
+
await ensurePackageTsconfigs(
|
|
1048
|
+
workspaceRoot,
|
|
1049
|
+
// only udate for Highstate-managed packages
|
|
1050
|
+
packages.filter((pkg) => pkg.type !== void 0)
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
const references = packages.map((pkg) => ({
|
|
1054
|
+
path: `./${pkg.relativePath}/tsconfig.json`
|
|
1055
|
+
}));
|
|
1056
|
+
const tsconfigContent = {
|
|
1057
|
+
files: [],
|
|
1058
|
+
references
|
|
1059
|
+
};
|
|
1060
|
+
await writeFile(tsconfigPath, `${JSON.stringify(tsconfigContent, null, 2)}
|
|
1061
|
+
`, "utf-8");
|
|
1062
|
+
}
|
|
1063
|
+
async function ensurePackageTsconfigs(workspaceRoot, packages) {
|
|
1064
|
+
for (const pkg of packages) {
|
|
1065
|
+
const tsconfigPath = join(pkg.path, "tsconfig.json");
|
|
1066
|
+
const tsconfigContent = generateTsconfigContent(workspaceRoot, pkg.path);
|
|
1067
|
+
await writeFile(tsconfigPath, `${JSON.stringify(tsconfigContent, null, 2)}
|
|
1068
|
+
`, "utf-8");
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
async function createPackage(workspaceRoot, name, type) {
|
|
1072
|
+
const packagePath = join(workspaceRoot, "packages", name);
|
|
1073
|
+
const srcPath = join(packagePath, "src");
|
|
1074
|
+
await mkdir(packagePath, { recursive: true });
|
|
1075
|
+
await mkdir(srcPath, { recursive: true });
|
|
1076
|
+
const packageJson = {
|
|
1077
|
+
name: `@highstate/${name}`,
|
|
1078
|
+
version: "0.0.1",
|
|
1079
|
+
type: "module",
|
|
1080
|
+
highstate: {
|
|
1081
|
+
type
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
await writeFile(
|
|
1085
|
+
join(packagePath, "package.json"),
|
|
1086
|
+
`${JSON.stringify(packageJson, null, 2)}
|
|
1087
|
+
`,
|
|
1088
|
+
"utf-8"
|
|
1089
|
+
);
|
|
1090
|
+
const tsconfigContent = generateTsconfigContent(workspaceRoot, packagePath);
|
|
1091
|
+
await writeFile(
|
|
1092
|
+
join(packagePath, "tsconfig.json"),
|
|
1093
|
+
`${JSON.stringify(tsconfigContent, null, 2)}
|
|
1094
|
+
`,
|
|
1095
|
+
"utf-8"
|
|
1096
|
+
);
|
|
1097
|
+
await writeFile(join(srcPath, "index.ts"), `// ${name} package
|
|
1098
|
+
`, "utf-8");
|
|
1099
|
+
return {
|
|
1100
|
+
path: packagePath,
|
|
1101
|
+
relativePath: `packages/${name}`,
|
|
1102
|
+
name: `@highstate/${name}`,
|
|
1103
|
+
type
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/commands/backend/identity.ts
|
|
1108
|
+
var BackendIdentityCommand = class extends Command {
|
|
1109
|
+
static paths = [["backend", "identity"]];
|
|
1110
|
+
static usage = Command.Usage({
|
|
1111
|
+
category: "Backend",
|
|
1112
|
+
description: "Ensures the backend identity is set up and returns the recipient."
|
|
1113
|
+
});
|
|
1114
|
+
async execute() {
|
|
1115
|
+
const { getOrCreateBackendIdentity } = await import('@highstate/backend');
|
|
1116
|
+
const config = await loadConfig();
|
|
1117
|
+
const backendIdentity = await getOrCreateBackendIdentity(config, logger);
|
|
1118
|
+
const recipient = await identityToRecipient(backendIdentity);
|
|
1119
|
+
logger.info(`stored backend identity: "%s"`, recipient);
|
|
1120
|
+
const suggestedTitle = hostname();
|
|
1121
|
+
if (!suggestedTitle) {
|
|
1122
|
+
logger.info(`run "highstate backend unlock-method add %s" on a trusted device`, recipient);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
logger.info(
|
|
1126
|
+
`run "highstate backend unlock-method add %s --title %s" on a trusted device`,
|
|
1127
|
+
recipient,
|
|
1128
|
+
suggestedTitle
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var BackendUnlockMethodAddCommand = class extends Command {
|
|
1133
|
+
static paths = [["backend", "unlock-method", "add"]];
|
|
1134
|
+
static usage = Command.Usage({
|
|
1135
|
+
category: "Backend",
|
|
1136
|
+
description: "Adds a new backend unlock method for the current workspace.",
|
|
1137
|
+
examples: [["Add recipient", "highstate backend unlock-method add age1example --title Laptop"]]
|
|
1138
|
+
});
|
|
1139
|
+
recipient = Option.String();
|
|
1140
|
+
title = Option.String("--title");
|
|
1141
|
+
description = Option.String("--description");
|
|
1142
|
+
async execute() {
|
|
1143
|
+
let title = this.title;
|
|
1144
|
+
if (!title) {
|
|
1145
|
+
title = await input({
|
|
1146
|
+
message: "Unlock Method Title",
|
|
1147
|
+
default: "New Device",
|
|
1148
|
+
validate: (value) => value.trim().length > 0 ? true : "Title is required"
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
let description = this.description;
|
|
1152
|
+
if (description === void 0) {
|
|
1153
|
+
description = await input({
|
|
1154
|
+
message: "Description (optional)",
|
|
1155
|
+
default: ""
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
const services2 = await getBackendServices();
|
|
1159
|
+
try {
|
|
1160
|
+
const result = await services2.backendUnlockService.addUnlockMethod({
|
|
1161
|
+
recipient: this.recipient,
|
|
1162
|
+
meta: description ? { title: title.trim(), description: description.trim() } : { title: title.trim() }
|
|
1163
|
+
});
|
|
1164
|
+
logger.info(`added backend unlock method "%s"`, result.id);
|
|
1165
|
+
} finally {
|
|
1166
|
+
await disposeServices();
|
|
1167
|
+
}
|
|
1168
|
+
process.exit(0);
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
var BackendUnlockMethodDeleteCommand = class extends Command {
|
|
1172
|
+
static paths = [["backend", "unlock-method", "delete"]];
|
|
1173
|
+
static usage = Command.Usage({
|
|
1174
|
+
category: "Backend",
|
|
1175
|
+
description: "Removes a backend unlock method by its identifier."
|
|
1176
|
+
});
|
|
1177
|
+
id = Option.String();
|
|
1178
|
+
force = Option.Boolean("--force", false);
|
|
1179
|
+
async execute() {
|
|
1180
|
+
if (!this.force) {
|
|
1181
|
+
const answer = await confirm({
|
|
1182
|
+
message: `Delete backend unlock method ${this.id}?`,
|
|
1183
|
+
default: false
|
|
1184
|
+
});
|
|
1185
|
+
if (!answer) {
|
|
1186
|
+
logger.info("cancelled backend unlock method deletion");
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
const services2 = await getBackendServices();
|
|
1191
|
+
try {
|
|
1192
|
+
await services2.backendUnlockService.deleteUnlockMethod(this.id);
|
|
1193
|
+
logger.info(`deleted backend unlock method "%s"`, this.id);
|
|
1194
|
+
} finally {
|
|
1195
|
+
await disposeServices();
|
|
1196
|
+
}
|
|
1197
|
+
process.exit(0);
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
var BackendUnlockMethodListCommand = class extends Command {
|
|
1201
|
+
static paths = [["backend", "unlock-method", "list"]];
|
|
1202
|
+
static usage = Command.Usage({
|
|
1203
|
+
category: "Backend",
|
|
1204
|
+
description: "Lists backend unlock methods registered for the current workspace."
|
|
1205
|
+
});
|
|
1206
|
+
async execute() {
|
|
1207
|
+
const services2 = await getBackendServices();
|
|
1208
|
+
try {
|
|
1209
|
+
const methods = await services2.backendUnlockService.listUnlockMethods();
|
|
1210
|
+
if (methods.length === 0) {
|
|
1211
|
+
logger.warn("no backend unlock methods configured");
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
const table = new Table({
|
|
1215
|
+
columns: [
|
|
1216
|
+
{ name: "title", title: "Title" },
|
|
1217
|
+
{ name: "id", title: "ID" },
|
|
1218
|
+
{ name: "recipient", title: "Recipient" },
|
|
1219
|
+
{ name: "description", title: "Description", maxLen: 30 }
|
|
1220
|
+
],
|
|
1221
|
+
defaultColumnOptions: {
|
|
1222
|
+
alignment: "left"
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
table.addRows(
|
|
1226
|
+
methods.map((method) => ({
|
|
1227
|
+
title: method.meta.title,
|
|
1228
|
+
id: method.id,
|
|
1229
|
+
recipient: method.recipient,
|
|
1230
|
+
description: method.meta.description ?? ""
|
|
1231
|
+
}))
|
|
1232
|
+
);
|
|
1233
|
+
table.printTable();
|
|
1234
|
+
} finally {
|
|
1235
|
+
await disposeServices();
|
|
1236
|
+
}
|
|
1237
|
+
process.exit(0);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
var BuildCommand = class extends Command {
|
|
1241
|
+
static paths = [["build"]];
|
|
1242
|
+
static usage = Command.Usage({
|
|
1243
|
+
category: "Builder",
|
|
1244
|
+
description: "Builds the Highstate library or unit package."
|
|
1245
|
+
});
|
|
1246
|
+
watch = Option.Boolean("--watch", false);
|
|
1247
|
+
library = Option.Boolean("--library", false);
|
|
1248
|
+
silent = Option.Boolean("--silent", true);
|
|
1249
|
+
noSourceHash = Option.Boolean("--no-source-hash", false);
|
|
1250
|
+
async execute() {
|
|
1251
|
+
const packageJson = await readPackageJSON();
|
|
1252
|
+
const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {});
|
|
1253
|
+
if (highstateConfig.type === "library") {
|
|
1254
|
+
this.library = true;
|
|
1255
|
+
}
|
|
1256
|
+
if (highstateConfig.type === "worker") {
|
|
1257
|
+
this.noSourceHash = true;
|
|
1258
|
+
}
|
|
1259
|
+
if (!packageJson.name) {
|
|
1260
|
+
throw new Error("package.json must have a name field");
|
|
1261
|
+
}
|
|
1262
|
+
const entryPoints = extractEntryPoints(packageJson);
|
|
1263
|
+
if (Object.keys(entryPoints).length === 0) {
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const esbuildPlugins = [];
|
|
1267
|
+
const binSourceFilePaths = Object.values(entryPoints).filter((value) => value.isBin).map((value) => value.entryPoint.slice(2));
|
|
1268
|
+
if (this.library) {
|
|
1269
|
+
esbuildPlugins.push(schemaTransformerPlugin);
|
|
1270
|
+
}
|
|
1271
|
+
if (binSourceFilePaths.length > 0) {
|
|
1272
|
+
esbuildPlugins.push(createBinTransformerPlugin(binSourceFilePaths));
|
|
1273
|
+
}
|
|
1274
|
+
await build({
|
|
1275
|
+
entry: mapValues(entryPoints, (value) => value.entryPoint),
|
|
1276
|
+
outDir: "dist",
|
|
1277
|
+
watch: this.watch,
|
|
1278
|
+
sourcemap: true,
|
|
1279
|
+
clean: true,
|
|
1280
|
+
format: "esm",
|
|
1281
|
+
target: "es2024",
|
|
1282
|
+
platform: "node",
|
|
1283
|
+
external: ["@pulumi/pulumi"],
|
|
1284
|
+
esbuildPlugins,
|
|
1285
|
+
treeshake: true,
|
|
1286
|
+
removeNodeProtocol: false,
|
|
1287
|
+
silent: this.silent || ["warn", "error", "fatal"].includes(logger.level)
|
|
1288
|
+
});
|
|
1289
|
+
const packageJsonPath = await resolvePackageJSON();
|
|
1290
|
+
const upToDatePackageJson = await readPackageJSON();
|
|
1291
|
+
if (!this.noSourceHash) {
|
|
1292
|
+
const sourceHashCalculator = new SourceHashCalculator(
|
|
1293
|
+
packageJsonPath,
|
|
1294
|
+
upToDatePackageJson,
|
|
1295
|
+
logger
|
|
1296
|
+
);
|
|
1297
|
+
const distPathToExportKey = /* @__PURE__ */ new Map();
|
|
1298
|
+
for (const value of Object.values(entryPoints)) {
|
|
1299
|
+
distPathToExportKey.set(value.distPath, value.key);
|
|
1300
|
+
}
|
|
1301
|
+
await sourceHashCalculator.writeHighstateManifest("./dist", distPathToExportKey);
|
|
1302
|
+
}
|
|
1303
|
+
if (this.library) {
|
|
1304
|
+
const { loadLibrary } = await import('./library-loader-PZWYMBAE.js');
|
|
1305
|
+
const fullModulePaths = Object.values(entryPoints).map((value) => resolve(value.distPath));
|
|
1306
|
+
logger.info("evaluating library components from modules: %s", fullModulePaths.join(", "));
|
|
1307
|
+
const library = await loadLibrary(logger, fullModulePaths);
|
|
1308
|
+
const libraryPath = resolve("./dist", "highstate.library.msgpack");
|
|
1309
|
+
await writeFile(libraryPath, encode(library), "utf8");
|
|
1310
|
+
}
|
|
1311
|
+
logger.info("build completed successfully");
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
var DesignerCommand = class extends Command {
|
|
1315
|
+
static paths = [["designer"]];
|
|
1316
|
+
static usage = Command.Usage({
|
|
1317
|
+
category: "Designer",
|
|
1318
|
+
description: "Starts the Highstate designer in the current project."
|
|
1319
|
+
});
|
|
1320
|
+
async execute() {
|
|
1321
|
+
const packageJsonPath = await resolvePackageJSON();
|
|
1322
|
+
const packageJsonUrl = pathToFileURL(packageJsonPath).toString();
|
|
1323
|
+
const packageJson = await readPackageJSON(packageJsonPath);
|
|
1324
|
+
if (!packageJson.devDependencies?.["@highstate/cli"]) {
|
|
1325
|
+
throw new UsageError(
|
|
1326
|
+
"This project is not a Highstate project.\n@highstate/cli must be installed as a devDependency."
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
if (!packageJson.devDependencies?.["@highstate/designer"]) {
|
|
1330
|
+
logger.info("Installing @highstate/designer...");
|
|
1331
|
+
await addDevDependency(["@highstate/designer", "classic-level"]);
|
|
1332
|
+
}
|
|
1333
|
+
const projectRoot = process.cwd();
|
|
1334
|
+
const detected = await detectPackageManager(projectRoot);
|
|
1335
|
+
const packageManager = detected?.name;
|
|
1336
|
+
if (packageManager === "npm" || packageManager === "pnpm" || packageManager === "yarn") {
|
|
1337
|
+
const expectedPulumiSdk = await getProjectPulumiSdkVersion(projectRoot, { packageManager });
|
|
1338
|
+
const actualPulumiCli = await getPulumiCliVersion(projectRoot);
|
|
1339
|
+
if (expectedPulumiSdk && actualPulumiCli && expectedPulumiSdk !== actualPulumiCli) {
|
|
1340
|
+
logger.warn(
|
|
1341
|
+
`pulumi version mismatch detected, this may cause incompatibilities
|
|
1342
|
+
expected "%s" (from overrides for "@pulumi/pulumi"), got "%s" (from "pulumi version")
|
|
1343
|
+
recommended: run "highstate update" or downgrade your Pulumi CLI to "%s"`,
|
|
1344
|
+
expectedPulumiSdk,
|
|
1345
|
+
actualPulumiCli,
|
|
1346
|
+
expectedPulumiSdk
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
logger.info("starting highstate designer...");
|
|
1351
|
+
await getBackendServices();
|
|
1352
|
+
const oldConsoleLog = console.log;
|
|
1353
|
+
const port = await getPort({ port: 3e3 });
|
|
1354
|
+
const eventsPort = await getPort({ port: 3001 });
|
|
1355
|
+
const designerPackageJsonPath = resolve$1(
|
|
1356
|
+
"@highstate/designer/package.json",
|
|
1357
|
+
packageJsonUrl
|
|
1358
|
+
);
|
|
1359
|
+
const designerPackageJson = await readPackageJSON(designerPackageJsonPath);
|
|
1360
|
+
process.env.NITRO_PORT = port.toString();
|
|
1361
|
+
process.env.NITRO_HOST = "0.0.0.0";
|
|
1362
|
+
process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version;
|
|
1363
|
+
process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString();
|
|
1364
|
+
await new Promise((resolve7) => {
|
|
1365
|
+
console.log = (message) => {
|
|
1366
|
+
if (message.startsWith("Listening on")) {
|
|
1367
|
+
resolve7();
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
const serverPath = resolve$1("@highstate/designer/server", packageJsonUrl);
|
|
1371
|
+
void import(serverPath);
|
|
1372
|
+
});
|
|
1373
|
+
console.log = oldConsoleLog;
|
|
1374
|
+
consola.log(
|
|
1375
|
+
[
|
|
1376
|
+
"\n ",
|
|
1377
|
+
colorize("bold", colorize("cyanBright", "Highstate Designer")),
|
|
1378
|
+
"\n ",
|
|
1379
|
+
colorize("greenBright", "\u279C Local: "),
|
|
1380
|
+
colorize("underline", colorize("cyanBright", `http://localhost:${port}`)),
|
|
1381
|
+
"\n"
|
|
1382
|
+
].join("")
|
|
1383
|
+
);
|
|
1384
|
+
process.on("SIGINT", () => {
|
|
1385
|
+
process.stdout.write("\r");
|
|
1386
|
+
consola.info("shutting down highstate designer...");
|
|
1387
|
+
setTimeout(() => process.exit(0), 1e3);
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
var InitCommand = class extends Command {
|
|
1392
|
+
static paths = [["init"]];
|
|
1393
|
+
static usage = Command.Usage({
|
|
1394
|
+
description: "Initializes a new Highstate project."
|
|
1395
|
+
});
|
|
1396
|
+
pathOption = Option.String("--path,-p", {
|
|
1397
|
+
description: "The path where the project should be initialized."
|
|
1398
|
+
});
|
|
1399
|
+
packageManager = Option.String("--package-manager", {
|
|
1400
|
+
description: "The package manager to use (npm, yarn, pnpm)."
|
|
1401
|
+
});
|
|
1402
|
+
name = Option.String("--name", {
|
|
1403
|
+
description: "The project name."
|
|
1404
|
+
});
|
|
1405
|
+
platformVersion = Option.String("--platform-version", {
|
|
1406
|
+
description: "The Highstate platform version to use."
|
|
1407
|
+
});
|
|
1408
|
+
stdlibVersion = Option.String("--stdlib-version", {
|
|
1409
|
+
description: "The Highstate standard library version to use."
|
|
1410
|
+
});
|
|
1411
|
+
async execute() {
|
|
1412
|
+
const availablePackageManagers = await detectAvailablePackageManagers(["npm", "pnpm", "yarn"]);
|
|
1413
|
+
if (availablePackageManagers.length === 0) {
|
|
1414
|
+
throw new Error('No supported package managers found in PATH ("npm", "pnpm", "yarn")');
|
|
1415
|
+
}
|
|
1416
|
+
const projectName = await resolveProjectName(this.name);
|
|
1417
|
+
const destinationPath = await resolveDestinationPath(this.pathOption, projectName);
|
|
1418
|
+
const selectedPackageManager = await resolvePackageManager(
|
|
1419
|
+
this.packageManager,
|
|
1420
|
+
availablePackageManagers
|
|
1421
|
+
);
|
|
1422
|
+
const templatePath = resolveTemplatePath();
|
|
1423
|
+
const versionBundle = await resolveVersionBundle({
|
|
1424
|
+
platformVersion: this.platformVersion,
|
|
1425
|
+
stdlibVersion: this.stdlibVersion
|
|
1426
|
+
});
|
|
1427
|
+
await mkdir(destinationPath, { recursive: true });
|
|
1428
|
+
const isEmptyOrMissing = await isEmptyDirectory(destinationPath);
|
|
1429
|
+
if (!isEmptyOrMissing) {
|
|
1430
|
+
throw new Error(`Destination path is not empty: "${destinationPath}"`);
|
|
1431
|
+
}
|
|
1432
|
+
logger.info("initializing highstate project in %s", destinationPath);
|
|
1433
|
+
await generateFromTemplate(templatePath, destinationPath, {
|
|
1434
|
+
projectName,
|
|
1435
|
+
packageName: projectName,
|
|
1436
|
+
platformVersion: versionBundle.platformVersion,
|
|
1437
|
+
libraryVersion: versionBundle.stdlibVersion,
|
|
1438
|
+
isPnpm: selectedPackageManager === "pnpm" ? "true" : "",
|
|
1439
|
+
isYarn: selectedPackageManager === "yarn" ? "true" : "",
|
|
1440
|
+
isNpm: selectedPackageManager === "npm" ? "true" : ""
|
|
1441
|
+
});
|
|
1442
|
+
const overrides = buildOverrides(versionBundle);
|
|
1443
|
+
await applyOverrides({
|
|
1444
|
+
projectRoot: destinationPath,
|
|
1445
|
+
packageManager: selectedPackageManager,
|
|
1446
|
+
overrides
|
|
1447
|
+
});
|
|
1448
|
+
logger.info("installing dependencies using %s...", selectedPackageManager);
|
|
1449
|
+
await installDependencies({
|
|
1450
|
+
cwd: destinationPath,
|
|
1451
|
+
packageManager: selectedPackageManager,
|
|
1452
|
+
silent: false
|
|
1453
|
+
});
|
|
1454
|
+
logger.info("project initialized successfully");
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
async function resolveDestinationPath(pathOption, projectName) {
|
|
1458
|
+
if (pathOption) {
|
|
1459
|
+
return resolve(pathOption);
|
|
1460
|
+
}
|
|
1461
|
+
const defaultPath = resolve(process.cwd(), projectName);
|
|
1462
|
+
const pathValue = await input({
|
|
1463
|
+
message: "Project path",
|
|
1464
|
+
default: defaultPath,
|
|
1465
|
+
validate: (value) => value.trim().length > 0 ? true : "Path is required"
|
|
1466
|
+
});
|
|
1467
|
+
return resolve(pathValue);
|
|
1468
|
+
}
|
|
1469
|
+
async function resolveProjectName(nameOption) {
|
|
1470
|
+
if (nameOption !== void 0) {
|
|
1471
|
+
const trimmed = nameOption.trim();
|
|
1472
|
+
if (trimmed.length === 0) {
|
|
1473
|
+
throw new Error('Flag "--name" must not be empty');
|
|
1474
|
+
}
|
|
1475
|
+
return trimmed;
|
|
1476
|
+
}
|
|
1477
|
+
const value = await input({
|
|
1478
|
+
message: "Project name",
|
|
1479
|
+
default: "my-project",
|
|
1480
|
+
validate: (inputValue) => inputValue.trim().length > 0 ? true : "Name is required"
|
|
1481
|
+
});
|
|
1482
|
+
return value.trim();
|
|
1483
|
+
}
|
|
1484
|
+
async function resolvePackageManager(packageManagerOption, available) {
|
|
1485
|
+
if (packageManagerOption) {
|
|
1486
|
+
if (!isSupportedPackageManagerName(packageManagerOption)) {
|
|
1487
|
+
throw new Error(`Unsupported package manager: "${packageManagerOption}"`);
|
|
1488
|
+
}
|
|
1489
|
+
const name = packageManagerOption;
|
|
1490
|
+
if (!available.includes(name)) {
|
|
1491
|
+
throw new Error(`Package manager not found in PATH: "${name}"`);
|
|
1492
|
+
}
|
|
1493
|
+
return name;
|
|
1494
|
+
}
|
|
1495
|
+
const preferredOrder = ["pnpm", "yarn", "npm"];
|
|
1496
|
+
const defaultValue = preferredOrder.find((value) => available.includes(value)) ?? available[0];
|
|
1497
|
+
return await select({
|
|
1498
|
+
message: "Package manager",
|
|
1499
|
+
default: defaultValue,
|
|
1500
|
+
choices: available.map((value) => ({ name: value, value }))
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
function isSupportedPackageManagerName(value) {
|
|
1504
|
+
return value === "npm" || value === "pnpm" || value === "yarn";
|
|
1505
|
+
}
|
|
1506
|
+
async function detectAvailablePackageManagers(candidates) {
|
|
1507
|
+
const results = [];
|
|
1508
|
+
for (const candidate of candidates) {
|
|
1509
|
+
const exists = await isExecutableInPath(candidate);
|
|
1510
|
+
if (exists) {
|
|
1511
|
+
results.push(candidate);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return results;
|
|
1515
|
+
}
|
|
1516
|
+
async function isExecutableInPath(command) {
|
|
1517
|
+
const pathValue = process.env.PATH;
|
|
1518
|
+
if (!pathValue) {
|
|
1519
|
+
return false;
|
|
1520
|
+
}
|
|
1521
|
+
const parts = pathValue.split(":").filter(Boolean);
|
|
1522
|
+
for (const part of parts) {
|
|
1523
|
+
const candidate = resolve(part, command);
|
|
1524
|
+
try {
|
|
1525
|
+
await access(candidate);
|
|
1526
|
+
return true;
|
|
1527
|
+
} catch {
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
return false;
|
|
1531
|
+
}
|
|
1532
|
+
async function isEmptyDirectory(path) {
|
|
1533
|
+
try {
|
|
1534
|
+
const entries = await readdir(path);
|
|
1535
|
+
return entries.length === 0;
|
|
1536
|
+
} catch {
|
|
1537
|
+
return true;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
function resolveTemplatePath() {
|
|
1541
|
+
const here = fileURLToPath(new URL(import.meta.url));
|
|
1542
|
+
return resolve(here, "..", "..", "assets", "template");
|
|
1543
|
+
}
|
|
1544
|
+
var PackageCreateCommand = class extends Command {
|
|
1545
|
+
static paths = [["package", "create"]];
|
|
1546
|
+
static usage = Command.Usage({
|
|
1547
|
+
category: "Package",
|
|
1548
|
+
description: "Creates a new package in the workspace."
|
|
1549
|
+
});
|
|
1550
|
+
name = Option.String({ required: true });
|
|
1551
|
+
type = Option.String("--type,-t", {
|
|
1552
|
+
description: "Package type (source, library, worker)"
|
|
1553
|
+
});
|
|
1554
|
+
async execute() {
|
|
1555
|
+
const workspaceRoot = await findWorkspaceRoot();
|
|
1556
|
+
const packageType = highstateConfigSchema.shape.type.parse(this.type);
|
|
1557
|
+
await createPackage(workspaceRoot, this.name, packageType);
|
|
1558
|
+
const packages = await scanWorkspacePackages(workspaceRoot);
|
|
1559
|
+
await updateTsconfigReferences(workspaceRoot, packages);
|
|
1560
|
+
logger.info(`created package: @highstate/${this.name} (${packageType})`);
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
var PackageListCommand = class extends Command {
|
|
1564
|
+
static paths = [["package", "list"]];
|
|
1565
|
+
static usage = Command.Usage({
|
|
1566
|
+
category: "Package",
|
|
1567
|
+
description: "Lists all packages in the workspace with their types."
|
|
1568
|
+
});
|
|
1569
|
+
async execute() {
|
|
1570
|
+
const workspaceRoot = await findWorkspaceRoot();
|
|
1571
|
+
const packages = await scanWorkspacePackages(workspaceRoot);
|
|
1572
|
+
if (packages.length === 0) {
|
|
1573
|
+
logger.info("no packages found in workspace");
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
const table = new Table({
|
|
1577
|
+
columns: [
|
|
1578
|
+
{ name: "name", title: "Name" },
|
|
1579
|
+
{ name: "type", title: "Type" },
|
|
1580
|
+
{ name: "path", title: "Path" }
|
|
1581
|
+
]
|
|
1582
|
+
});
|
|
1583
|
+
table.addRows(
|
|
1584
|
+
packages.map((pkg) => ({
|
|
1585
|
+
name: pkg.name,
|
|
1586
|
+
type: pkg.type ?? "unknown",
|
|
1587
|
+
path: pkg.relativePath
|
|
1588
|
+
}))
|
|
1589
|
+
);
|
|
1590
|
+
table.printTable();
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
var PackageRemoveCommand = class extends Command {
|
|
1594
|
+
static paths = [["package", "remove"]];
|
|
1595
|
+
static usage = Command.Usage({
|
|
1596
|
+
category: "Package",
|
|
1597
|
+
description: "Removes a package from the workspace."
|
|
1598
|
+
});
|
|
1599
|
+
name = Option.String({ required: true });
|
|
1600
|
+
async execute() {
|
|
1601
|
+
const workspaceRoot = await findWorkspaceRoot();
|
|
1602
|
+
const packages = await scanWorkspacePackages(workspaceRoot);
|
|
1603
|
+
const targetPackage = packages.find(
|
|
1604
|
+
(pkg) => pkg.name === this.name || pkg.name === `@highstate/${this.name}` || pkg.relativePath.endsWith(this.name)
|
|
1605
|
+
);
|
|
1606
|
+
if (!targetPackage) {
|
|
1607
|
+
logger.error(`package not found: ${this.name}`);
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
}
|
|
1610
|
+
await rm(targetPackage.path, { recursive: true, force: true });
|
|
1611
|
+
const remainingPackages = await scanWorkspacePackages(workspaceRoot);
|
|
1612
|
+
await updateTsconfigReferences(workspaceRoot, remainingPackages);
|
|
1613
|
+
logger.info(`removed package: ${targetPackage.name}`);
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
var PackageUpdateReferencesCommand = class extends Command {
|
|
1617
|
+
static paths = [["package", "update-references"]];
|
|
1618
|
+
static usage = Command.Usage({
|
|
1619
|
+
category: "Package",
|
|
1620
|
+
description: "Updates the root tsconfig.json with references to all packages in the workspace."
|
|
1621
|
+
});
|
|
1622
|
+
async execute() {
|
|
1623
|
+
const workspaceRoot = await findWorkspaceRoot();
|
|
1624
|
+
const packages = await scanWorkspacePackages(workspaceRoot);
|
|
1625
|
+
await updateTsconfigReferences(workspaceRoot, packages, true);
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
var UpdateCommand = class extends Command {
|
|
1629
|
+
static paths = [["update"]];
|
|
1630
|
+
static usage = Command.Usage({
|
|
1631
|
+
description: "Updates version overrides in an existing Highstate project."
|
|
1632
|
+
});
|
|
1633
|
+
platformVersion = Option.String("--platform-version", {
|
|
1634
|
+
description: "The Highstate platform version to set."
|
|
1635
|
+
});
|
|
1636
|
+
stdlibVersion = Option.String("--stdlib-version", {
|
|
1637
|
+
description: "The Highstate standard library version to set."
|
|
1638
|
+
});
|
|
1639
|
+
platformOnly = Option.Boolean("--platform", false, {
|
|
1640
|
+
description: "Update only platform versions."
|
|
1641
|
+
});
|
|
1642
|
+
stdlibOnly = Option.Boolean("--stdlib", false, {
|
|
1643
|
+
description: "Update only standard library versions."
|
|
1644
|
+
});
|
|
1645
|
+
install = Option.Boolean("--install", true, {
|
|
1646
|
+
description: "Install dependencies after updating overrides."
|
|
1647
|
+
});
|
|
1648
|
+
async execute() {
|
|
1649
|
+
const projectRoot = process.cwd();
|
|
1650
|
+
const packageManager = await resolveProjectPackageManager(projectRoot);
|
|
1651
|
+
if (this.platformOnly && this.stdlibOnly) {
|
|
1652
|
+
throw new Error('Flags "--platform" and "--stdlib" cannot be used together');
|
|
1653
|
+
}
|
|
1654
|
+
const updatePlatform = this.platformOnly || !this.stdlibOnly;
|
|
1655
|
+
const updateStdlib = this.stdlibOnly || !this.platformOnly;
|
|
1656
|
+
if (this.stdlibOnly) {
|
|
1657
|
+
const currentPlatformVersion = await getProjectPlatformVersion(projectRoot, {
|
|
1658
|
+
packageManager
|
|
1659
|
+
});
|
|
1660
|
+
if (!currentPlatformVersion) {
|
|
1661
|
+
throw new Error('Current platform version is not set in overrides for "@highstate/pulumi"');
|
|
1662
|
+
}
|
|
1663
|
+
const targetStdlibVersion = (this.stdlibVersion ?? "").trim();
|
|
1664
|
+
if (targetStdlibVersion.length === 0) {
|
|
1665
|
+
throw new Error('Flag "--stdlib-version" must be provided when using "--stdlib"');
|
|
1666
|
+
}
|
|
1667
|
+
const stdlibManifest = await fetchManifest("@highstate/library", targetStdlibVersion);
|
|
1668
|
+
const supportedPlatformRange = getDependencyRange(stdlibManifest, "@highstate/pulumi");
|
|
1669
|
+
if (!supportedPlatformRange) {
|
|
1670
|
+
throw new Error(
|
|
1671
|
+
`Unable to infer "@highstate/pulumi" version from "@highstate/library@${targetStdlibVersion}"`
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
const validPlatform = semver.valid(currentPlatformVersion);
|
|
1675
|
+
if (!validPlatform) {
|
|
1676
|
+
throw new Error(
|
|
1677
|
+
`Current platform version is not a valid semver "${currentPlatformVersion}"`
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
const ok = semver.satisfies(validPlatform, supportedPlatformRange, {
|
|
1681
|
+
includePrerelease: true
|
|
1682
|
+
});
|
|
1683
|
+
if (!ok) {
|
|
1684
|
+
throw new Error(
|
|
1685
|
+
`Current platform version "${currentPlatformVersion}" does not satisfy requirement "${supportedPlatformRange}"`
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
const bundle = await resolveVersionBundle({
|
|
1690
|
+
platformVersion: updatePlatform ? this.platformVersion : void 0,
|
|
1691
|
+
stdlibVersion: updateStdlib ? this.stdlibVersion : void 0
|
|
1692
|
+
});
|
|
1693
|
+
const overrides = buildOverrides(bundle);
|
|
1694
|
+
await applyOverrides({
|
|
1695
|
+
projectRoot,
|
|
1696
|
+
packageManager,
|
|
1697
|
+
overrides
|
|
1698
|
+
});
|
|
1699
|
+
logger.info(
|
|
1700
|
+
"updated overrides: platform=%s stdlib=%s pulumi=%s",
|
|
1701
|
+
bundle.platformVersion,
|
|
1702
|
+
bundle.stdlibVersion,
|
|
1703
|
+
bundle.pulumiVersion
|
|
1704
|
+
);
|
|
1705
|
+
if (this.install) {
|
|
1706
|
+
const { installDependencies: installDependencies2 } = await import('nypm');
|
|
1707
|
+
logger.info("installing dependencies using %s...", packageManager);
|
|
1708
|
+
await installDependencies2({
|
|
1709
|
+
cwd: projectRoot,
|
|
1710
|
+
packageManager,
|
|
1711
|
+
silent: false
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
logger.info("update completed successfully");
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
async function resolveProjectPackageManager(projectRoot) {
|
|
1718
|
+
const detected = await detectPackageManager(projectRoot);
|
|
1719
|
+
if (!detected?.name) {
|
|
1720
|
+
throw new Error("Unable to detect package manager for this project");
|
|
1721
|
+
}
|
|
1722
|
+
if (detected.name === "bun") {
|
|
1723
|
+
throw new Error('Package manager "bun" is not supported');
|
|
1724
|
+
}
|
|
1725
|
+
if (detected.name === "deno") {
|
|
1726
|
+
throw new Error('Package manager "deno" is not supported');
|
|
1727
|
+
}
|
|
1728
|
+
if (detected.name !== "npm" && detected.name !== "pnpm" && detected.name !== "yarn") {
|
|
1729
|
+
throw new Error(`Unsupported package manager: "${detected.name}"`);
|
|
1730
|
+
}
|
|
1731
|
+
await assertPackageJsonExists(projectRoot);
|
|
1732
|
+
return detected.name;
|
|
1733
|
+
}
|
|
1734
|
+
async function assertPackageJsonExists(projectRoot) {
|
|
1735
|
+
try {
|
|
1736
|
+
await readFile(`${projectRoot}/package.json`, "utf8");
|
|
1737
|
+
} catch {
|
|
1738
|
+
throw new Error(`File "package.json" not found in "${projectRoot}"`);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
export { BackendIdentityCommand, BackendUnlockMethodAddCommand, BackendUnlockMethodDeleteCommand, BackendUnlockMethodListCommand, BuildCommand, DesignerCommand, InitCommand, PackageCreateCommand, PackageListCommand, PackageRemoveCommand, PackageUpdateReferencesCommand, UpdateCommand };
|
|
1743
|
+
//# sourceMappingURL=chunk-VNBLDKUT.js.map
|
|
1744
|
+
//# sourceMappingURL=chunk-VNBLDKUT.js.map
|