@highstate/cli 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -1,1387 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import { int32ToBytes } from './chunk-CMECLVT7.js';
3
- import { Command, Option, UsageError, Cli, Builtins } from 'clipanion';
4
- import { hostname } from 'node:os';
5
- import { loadConfig } from '@highstate/backend';
6
- import { identityToRecipient } from 'age-encryption';
7
- import { writeFile, mkdir, access, readdir, rm, readFile, stat } from 'node:fs/promises';
8
- import { PassThrough } from 'node:stream';
9
- import { LogLevels, consola } from 'consola';
10
- import pino, { levels } from 'pino';
11
- import { resolve, basename, relative, dirname, join } from 'node:path';
12
- import Handlebars from 'handlebars';
13
- import MagicString from 'magic-string';
14
- import { parseAsync } from 'oxc-parser';
15
- import { walk } from 'oxc-walker';
16
- import { z } from 'zod';
17
- import { pathToFileURL, fileURLToPath } from 'node:url';
18
- import { crc32 } from '@aws-crypto/crc32';
19
- import { resolve as resolve$1 } from 'import-meta-resolve';
20
- import { readPackageJSON, resolvePackageJSON } from 'pkg-types';
21
- import { existsSync } from 'node:fs';
22
- import { input, confirm, select } from '@inquirer/prompts';
23
- import { Table } from 'console-table-printer';
24
- import { encode } from '@msgpack/msgpack';
25
- import { mapValues } from 'remeda';
26
- import { build } from 'tsup';
27
- import { colorize } from 'consola/utils';
28
- import { getPort } from 'get-port-please';
29
- import { addDevDependency, installDependencies } from 'nypm';
2
+ import { BuildCommand, DesignerCommand, InitCommand, UpdateCommand, BackendIdentityCommand, BackendUnlockMethodListCommand, BackendUnlockMethodAddCommand, BackendUnlockMethodDeleteCommand, PackageUpdateReferencesCommand, PackageListCommand, PackageCreateCommand, PackageRemoveCommand } from './chunk-VNBLDKUT.js';
3
+ import './chunk-CMECLVT7.js';
4
+ import { Cli, Builtins } from 'clipanion';
30
5
 
31
- // package.json
32
- var version = "0.14.1";
33
- var logger = pino(
34
- {
35
- name: "highstate-cli",
36
- level: process.env.LOG_LEVEL ?? "info"
37
- },
38
- createConsolaStream()
39
- );
40
- consola.level = LogLevels[process.env.LOG_LEVEL ?? "info"];
41
- function createConsolaStream() {
42
- const stream = new PassThrough();
43
- stream.on("data", (data) => {
44
- const { level, msg, error } = JSON.parse(String(data));
45
- const levelLabel = levels.labels[level];
46
- switch (levelLabel) {
47
- case "info":
48
- consola.info(msg);
49
- break;
50
- case "warn":
51
- consola.warn(msg);
52
- break;
53
- case "error":
54
- if (error) {
55
- consola.error(msg, error);
56
- } else {
57
- consola.error(msg);
58
- }
59
- break;
60
- case "debug":
61
- consola.debug(msg);
62
- break;
63
- case "fatal":
64
- consola.fatal(msg);
65
- break;
66
- case "trace":
67
- consola.trace(msg);
68
- break;
69
- }
70
- });
71
- return stream;
72
- }
73
-
74
- // src/shared/bin-transformer.ts
75
- function createBinTransformerPlugin(sourceFilePaths) {
76
- const filter = new RegExp(`(${sourceFilePaths.join("|")})$`);
77
- logger.debug("created bin transformer plugin with filter: %s", filter);
78
- return {
79
- name: "bin-transformer",
80
- setup(build2) {
81
- build2.onLoad({ filter }, async (args) => {
82
- const content = await readFile(args.path, "utf-8");
83
- return {
84
- contents: `#!/usr/bin/env node
85
-
86
- ${content}`,
87
- loader: "ts"
88
- };
89
- });
90
- }
91
- };
92
- }
93
-
94
- // src/shared/entry-points.ts
95
- function extractEntryPoints(packageJson) {
96
- const exports = packageJson.exports;
97
- let bin = packageJson.bin;
98
- if (!exports && !bin) {
99
- logger.warn("no exports or bin found in package.json");
100
- return {};
101
- }
102
- if (exports !== void 0 && (typeof exports !== "object" || Array.isArray(exports))) {
103
- throw new Error("Exports field in package.json must be an object");
104
- }
105
- if (bin !== void 0 && typeof bin !== "object") {
106
- if (!packageJson.name) {
107
- throw new Error("Package name is required when bin is a string");
108
- }
109
- bin = { [packageJson.name]: bin };
110
- }
111
- const result = {};
112
- if (exports) {
113
- for (const [key, value] of Object.entries(exports)) {
114
- let distPath;
115
- if (typeof value === "string") {
116
- distPath = value;
117
- } else if (typeof value === "object" && !Array.isArray(value)) {
118
- if (!value.default) {
119
- throw new Error(`Export "${key}" must have a default field in package.json`);
120
- }
121
- if (typeof value.default !== "string") {
122
- throw new Error(`Export "${key}" default field must be a string in package.json`);
123
- }
124
- distPath = value.default;
125
- } else {
126
- throw new Error(`Export "${key}" must be a string or an object in package.json`);
127
- }
128
- if (!distPath.startsWith("./dist/")) {
129
- throw new Error(
130
- `The default value of export "${key}" must start with "./dist/" in package.json, got "${distPath}"`
131
- );
132
- }
133
- if (!distPath.endsWith(".js")) {
134
- throw new Error(
135
- `The default value of export "${key}" must end with ".js" in package.json, got "${distPath}"`
136
- );
137
- }
138
- const targetName = distPath.slice(7).slice(0, -3);
139
- result[targetName] = {
140
- entryPoint: `./src/${targetName}.ts`,
141
- targetName,
142
- distPath,
143
- isBin: false,
144
- key
145
- };
146
- }
147
- }
148
- if (bin) {
149
- for (const [key, value] of Object.entries(bin)) {
150
- if (typeof value !== "string") {
151
- throw new Error(`Bin entry "${key}" must be a string in package.json`);
152
- }
153
- const distPath = value;
154
- if (!distPath.startsWith("./dist/")) {
155
- throw new Error(
156
- `The value of bin entry "${key}" must start with "./dist/" in package.json, got "${distPath}"`
157
- );
158
- }
159
- if (!distPath.endsWith(".js")) {
160
- throw new Error(
161
- `The value of bin entry "${key}" must end with ".js" in package.json, got "${distPath}"`
162
- );
163
- }
164
- const targetName = distPath.slice(7).slice(0, -3);
165
- result[targetName] = {
166
- entryPoint: `./src/${targetName}.ts`,
167
- targetName,
168
- distPath,
169
- isBin: true,
170
- key
171
- };
172
- }
173
- }
174
- return result;
175
- }
176
- async function generateFromTemplate(templatePath, destinationPath, variables) {
177
- const resolvedTemplatePath = resolve(templatePath);
178
- const resolvedDestinationPath = resolve(destinationPath);
179
- const templateStats = await stat(resolvedTemplatePath);
180
- if (!templateStats.isDirectory()) {
181
- throw new Error(`templatePath must be a directory: ${resolvedTemplatePath}`);
182
- }
183
- await mkdir(resolvedDestinationPath, { recursive: true });
184
- const renderTemplate = (raw) => {
185
- const template = Handlebars.compile(raw, {
186
- strict: true,
187
- noEscape: true
188
- });
189
- return template(variables);
190
- };
191
- const visit = async (absoluteSourcePath) => {
192
- const sourceStats = await stat(absoluteSourcePath);
193
- if (sourceStats.isDirectory()) {
194
- const relativePath = relative(resolvedTemplatePath, absoluteSourcePath);
195
- const destinationDirPath = join(resolvedDestinationPath, relativePath);
196
- await mkdir(destinationDirPath, { recursive: true });
197
- const entries = await readdir(absoluteSourcePath, { withFileTypes: true });
198
- for (const entry of entries) {
199
- await visit(join(absoluteSourcePath, entry.name));
200
- }
201
- return;
202
- }
203
- if (!sourceStats.isFile()) {
204
- return;
205
- }
206
- const relativeFilePath = relative(resolvedTemplatePath, absoluteSourcePath);
207
- const destinationRelativePath = relativeFilePath.replaceAll(".tpl", "");
208
- const destinationFilePath = join(resolvedDestinationPath, destinationRelativePath);
209
- await mkdir(dirname(destinationFilePath), { recursive: true });
210
- const contents = await readFile(absoluteSourcePath, "utf8");
211
- const rendered = renderTemplate(contents);
212
- if (rendered.trim().length === 0) {
213
- return;
214
- }
215
- await writeFile(destinationFilePath, rendered, "utf8");
216
- };
217
- await visit(resolvedTemplatePath);
218
- }
219
- var schemaTransformerPlugin = {
220
- name: "schema-transformer",
221
- setup(build2) {
222
- build2.onLoad({ filter: /src\/.*\.ts$/ }, async (args) => {
223
- const content = await readFile(args.path, "utf-8");
224
- return {
225
- contents: await applySchemaTransformations(content),
226
- loader: "ts"
227
- };
228
- });
229
- }
230
- };
231
- async function applySchemaTransformations(content) {
232
- let result = await applyZodMetaTransformations(content);
233
- result = await applyHelperFunctionTransformations(result);
234
- result = await applyDefineFunctionMetaTransformations(result);
235
- return result;
236
- }
237
- async function applyZodMetaTransformations(content) {
238
- const { program, comments } = await parseAsync("file.ts", content);
239
- const parentStack = [];
240
- let hasTransformations = false;
241
- const result = new MagicString(content);
242
- walk(program, {
243
- enter(node) {
244
- parentStack.push(node);
245
- if (isZodObjectProperty(node, parentStack)) {
246
- const jsdoc = findLeadingComment(content, node, comments);
247
- if (jsdoc) {
248
- const description = cleanJsdoc(jsdoc.value);
249
- const fieldName = "name" in node.key && typeof node.key.name === "string" ? node.key.name : "unknown";
250
- const originalValue = content.substring(node.value.start, node.value.end);
251
- if (!originalValue.includes(".meta(")) {
252
- const newValue = `${originalValue}.meta({ title: __camelCaseToHumanReadable("${fieldName}"), description: \`${description}\` })`;
253
- result.update(node.value.start, node.value.end, newValue);
254
- hasTransformations = true;
255
- }
256
- }
257
- }
258
- },
259
- leave() {
260
- parentStack.pop();
261
- }
262
- });
263
- let finalResult = result.toString();
264
- if (hasTransformations && !content.includes("__camelCaseToHumanReadable")) {
265
- finalResult = 'import { camelCaseToHumanReadable as __camelCaseToHumanReadable } from "@highstate/contract"\n' + finalResult;
266
- }
267
- return finalResult;
268
- }
269
- async function applyHelperFunctionTransformations(content) {
270
- const { program, comments } = await parseAsync("file.ts", content);
271
- const parentStack = [];
272
- let hasTransformations = false;
273
- const result = new MagicString(content);
274
- walk(program, {
275
- enter(node) {
276
- parentStack.push(node);
277
- if (node.type === "Property" && "key" in node && node.key?.type === "Identifier") {
278
- const propertyNode = node;
279
- const helperFunction = getHelperFunctionForProperty(parentStack);
280
- if (helperFunction) {
281
- const jsdoc = findLeadingComment(content, node, comments);
282
- if (jsdoc) {
283
- const description = cleanJsdoc(jsdoc.value);
284
- const originalValue = content.substring(
285
- propertyNode.value.start,
286
- propertyNode.value.end
287
- );
288
- const newValue = `${helperFunction}(${originalValue}, \`${description}\`)`;
289
- result.update(propertyNode.value.start, propertyNode.value.end, newValue);
290
- hasTransformations = true;
291
- }
292
- }
293
- }
294
- },
295
- leave() {
296
- parentStack.pop();
297
- }
298
- });
299
- let finalResult = result.toString();
300
- if (hasTransformations && !content.includes("$addArgumentDescription")) {
301
- finalResult = 'import { $addArgumentDescription, $addInputDescription } from "@highstate/contract"\n' + finalResult;
302
- }
303
- return finalResult;
304
- }
305
- async function applyDefineFunctionMetaTransformations(content) {
306
- const { program, comments } = await parseAsync("file.ts", content);
307
- const parentStack = [];
308
- const result = new MagicString(content);
309
- walk(program, {
310
- enter(node) {
311
- parentStack.push(node);
312
- if (node.type === "CallExpression" && "callee" in node && node.callee.type === "Identifier") {
313
- const callNode = node;
314
- const callee = callNode.callee;
315
- const functionName = "name" in callee && typeof callee.name === "string" ? callee.name : void 0;
316
- if (functionName && ["defineUnit", "defineEntity", "defineComponent"].includes(functionName)) {
317
- const jsdoc = findJsdocForDefineFunction(content, parentStack, comments);
318
- if (jsdoc && callNode.arguments && callNode.arguments.length > 0) {
319
- const description = cleanJsdoc(jsdoc.value);
320
- const firstArg = callNode.arguments[0];
321
- if (firstArg.type === "ObjectExpression" && "properties" in firstArg) {
322
- const properties = firstArg.properties;
323
- const metaProperty = properties?.find(
324
- (prop) => prop.type === "Property" && "key" in prop && prop.key?.type === "Identifier" && prop.key?.name === "meta"
325
- );
326
- if (metaProperty && "value" in metaProperty) {
327
- const originalMetaValue = content.substring(
328
- metaProperty.value.start,
329
- metaProperty.value.end
330
- );
331
- const newMetaValue = injectDescriptionIntoMetaObject(originalMetaValue, description);
332
- result.update(metaProperty.value.start, metaProperty.value.end, newMetaValue);
333
- } else if (properties && properties.length > 0) {
334
- const lastProperty = properties[properties.length - 1];
335
- if (lastProperty && "end" in lastProperty) {
336
- const insertPos = lastProperty.end;
337
- const newMetaProperty = `,
338
-
339
- meta: {
340
- description: \`${description}\`,
341
- }`;
342
- result.appendLeft(insertPos, newMetaProperty);
343
- }
344
- }
345
- }
346
- }
347
- }
348
- }
349
- },
350
- leave() {
351
- parentStack.pop();
352
- }
353
- });
354
- return result.toString();
355
- }
356
- function findJsdocForDefineFunction(content, parentStack, comments) {
357
- for (let i = parentStack.length - 1; i >= 0; i--) {
358
- const node = parentStack[i];
359
- if (node.type === "VariableDeclarator" && "id" in node && node.id?.type === "Identifier") {
360
- const jsdoc = findLeadingComment(content, node, comments);
361
- if (jsdoc) return jsdoc;
362
- }
363
- if (node.type === "VariableDeclaration") {
364
- const jsdoc = findLeadingComment(content, node, comments);
365
- if (jsdoc) return jsdoc;
366
- }
367
- if (node.type === "ExportNamedDeclaration" && "declaration" in node && node.declaration) {
368
- const jsdoc = findLeadingComment(content, node, comments);
369
- if (jsdoc) return jsdoc;
370
- }
371
- }
372
- return null;
373
- }
374
- function isZodObjectProperty(node, parentStack) {
375
- return node.type === "Property" && "key" in node && node.key?.type === "Identifier" && isInsideZodObject(parentStack);
376
- }
377
- function findLeadingComment(content, node, comments) {
378
- return comments.find((comment) => isLeadingComment(content, node, comment)) ?? null;
379
- }
380
- function isLeadingComment(content, node, comment) {
381
- if (comment.end > node.start) {
382
- return false;
383
- }
384
- const contentRange = content.substring(comment.end, node.start);
385
- return contentRange.trim().length === 0;
386
- }
387
- function cleanJsdoc(str) {
388
- return str.replace(/^\s*\*/gm, "").replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\${/g, "\\${").trim();
389
- }
390
- function injectDescriptionIntoMetaObject(objectString, description) {
391
- const trimmed = objectString.trim();
392
- const hasDescription = /description\s*:/.test(trimmed);
393
- if (hasDescription) {
394
- return trimmed.replace(/description\s*:\s*`[^`]*`/, `description: \`${description}\``);
395
- } else {
396
- const openBraceIndex = trimmed.indexOf("{");
397
- if (openBraceIndex === -1) {
398
- return trimmed;
399
- }
400
- const beforeBrace = trimmed.substring(0, openBraceIndex + 1);
401
- const afterBrace = trimmed.substring(openBraceIndex + 1);
402
- return `${beforeBrace}
403
- description: \`${description}\`,${afterBrace}`;
404
- }
405
- }
406
- function isInsideZodObject(parentStack) {
407
- for (let i = parentStack.length - 1; i >= 0; i--) {
408
- const node = parentStack[i];
409
- if (node.type === "CallExpression" && "callee" in node && node.callee.type === "MemberExpression" && isZodObjectCall(node.callee)) {
410
- return true;
411
- }
412
- }
413
- return false;
414
- }
415
- function getHelperFunctionForProperty(parentStack) {
416
- if (parentStack.length < 3) {
417
- return null;
418
- }
419
- const objectExpression = parentStack[parentStack.length - 2];
420
- if (!objectExpression || objectExpression.type !== "ObjectExpression") {
421
- return null;
422
- }
423
- const containerParent = parentStack[parentStack.length - 3];
424
- if (!containerParent) {
425
- return null;
426
- }
427
- if (containerParent.type === "Property" && "value" in containerParent && containerParent.value === objectExpression && containerParent.key?.type === "Identifier") {
428
- const keyName = containerParent.key.name;
429
- if (keyName === "inputs" || keyName === "outputs") {
430
- return "$addInputDescription";
431
- }
432
- if (keyName === "args" || keyName === "secrets") {
433
- return "$addArgumentDescription";
434
- }
435
- return null;
436
- }
437
- if (containerParent.type === "CallExpression" && containerParent.callee.type === "Identifier" && containerParent.arguments.length > 0 && containerParent.arguments[0] === objectExpression) {
438
- const calleeName = containerParent.callee.name;
439
- if (calleeName === "$inputs" || calleeName === "$outputs") {
440
- return "$addInputDescription";
441
- }
442
- if (calleeName === "$args" || calleeName === "$secrets") {
443
- return "$addArgumentDescription";
444
- }
445
- }
446
- return null;
447
- }
448
- function isZodObjectCall(memberExpression) {
449
- if (memberExpression.type !== "MemberExpression" || !("object" in memberExpression) || !("property" in memberExpression)) {
450
- return false;
451
- }
452
- const member = memberExpression;
453
- if (member.object.type === "Identifier" && "name" in member.object && member.object.name === "z" && member.property.type === "Identifier" && member.property.name === "object") {
454
- return true;
455
- }
456
- if (member.property.type === "Identifier" && member.property.name === "object" && member.object.type === "CallExpression" && "callee" in member.object && member.object.callee.type === "MemberExpression") {
457
- return startsWithZodCall(member.object);
458
- }
459
- return false;
460
- }
461
- function startsWithZodCall(callExpression) {
462
- if (!callExpression || callExpression.type !== "CallExpression") {
463
- return false;
464
- }
465
- if (callExpression.callee.type === "MemberExpression") {
466
- const callee = callExpression.callee;
467
- if (callee.object.type === "Identifier" && "name" in callee.object && callee.object.name === "z") {
468
- return true;
469
- }
470
- if (callee.object.type === "CallExpression") {
471
- return startsWithZodCall(callee.object);
472
- }
473
- }
474
- return false;
475
- }
476
- var sourceHashConfigSchema = z.discriminatedUnion("mode", [
477
- z.object({
478
- mode: z.literal("manual"),
479
- version: z.string()
480
- }),
481
- z.object({
482
- mode: z.literal("auto")
483
- }),
484
- z.object({
485
- mode: z.literal("version")
486
- }),
487
- z.object({
488
- mode: z.literal("none")
489
- })
490
- ]);
491
- var highstateConfigSchema = z.object({
492
- type: z.enum(["source", "library", "worker"]).default("source"),
493
- sourceHash: z.union([sourceHashConfigSchema, z.record(z.string(), sourceHashConfigSchema)]).optional()
494
- });
495
- var highstateManifestSchema = z.object({
496
- sourceHashes: z.record(z.string(), z.number()).optional()
497
- });
498
-
499
- // src/shared/services.ts
500
- var services;
501
- var disposePromise;
502
- function getBackendServices() {
503
- if (services) {
504
- return services;
505
- }
506
- services = import('@highstate/backend').then(({ getSharedServices }) => {
507
- return getSharedServices({
508
- services: {
509
- logger: logger.child({}, { msgPrefix: "[backend] " })
510
- }
511
- });
512
- });
513
- return services;
514
- }
515
- function disposeServices() {
516
- if (!services) {
517
- return Promise.resolve();
518
- }
519
- if (disposePromise) {
520
- return disposePromise;
521
- }
522
- disposePromise = import('@highstate/backend').then(({ disposeServices: disposeServices2 }) => services.then((s) => disposeServices2(s)));
523
- return disposePromise;
524
- }
525
- var SourceHashCalculator = class {
526
- constructor(packageJsonPath, packageJson, logger2) {
527
- this.packageJsonPath = packageJsonPath;
528
- this.packageJson = packageJson;
529
- this.logger = logger2;
530
- }
531
- dependencyHashes = /* @__PURE__ */ new Map();
532
- fileHashes = /* @__PURE__ */ new Map();
533
- /**
534
- * Calculates CRC32 hash of a string.
535
- */
536
- hashString(input3) {
537
- return crc32(Buffer.from(input3));
538
- }
539
- /**
540
- * Gets the highstate configuration from package.json with defaults.
541
- */
542
- getHighstateConfig(packageJson) {
543
- const rawConfig = packageJson.highstate;
544
- if (!rawConfig) {
545
- return { type: "source" };
546
- }
547
- try {
548
- return highstateConfigSchema.parse(rawConfig);
549
- } catch (error) {
550
- this.logger.warn(
551
- { error, packageName: packageJson.name },
552
- "invalid highstate configuration, using defaults"
553
- );
554
- return { type: "source" };
555
- }
556
- }
557
- /**
558
- * Gets the effective source hash configuration with defaults for a specific output.
559
- */
560
- getSourceHashConfig(highstateConfig, exportKey) {
561
- if (highstateConfig.sourceHash) {
562
- const singleConfigResult = sourceHashConfigSchema.safeParse(highstateConfig.sourceHash);
563
- if (singleConfigResult.success) {
564
- return singleConfigResult.data;
565
- }
566
- const recordConfigResult = z.record(z.string(), sourceHashConfigSchema).safeParse(highstateConfig.sourceHash);
567
- if (recordConfigResult.success && exportKey) {
568
- const perOutputConfig = recordConfigResult.data[exportKey];
569
- if (perOutputConfig) {
570
- return perOutputConfig;
571
- }
572
- }
573
- }
574
- if (highstateConfig.type === "library") {
575
- return { mode: "none" };
576
- }
577
- return { mode: "auto" };
578
- }
579
- async writeHighstateManifest(distBasePath, distPathToExportKey) {
580
- const highstateConfig = this.getHighstateConfig(this.packageJson);
581
- const promises = [];
582
- for (const [distPath, exportKey] of distPathToExportKey) {
583
- const fullPath = resolve(distPath);
584
- const sourceHashConfig = this.getSourceHashConfig(highstateConfig, exportKey);
585
- switch (sourceHashConfig.mode) {
586
- case "manual":
587
- promises.push(
588
- Promise.resolve({
589
- distPath,
590
- hash: this.hashString(sourceHashConfig.version)
591
- })
592
- );
593
- break;
594
- case "version":
595
- promises.push(
596
- Promise.resolve({
597
- distPath,
598
- hash: this.hashString(this.packageJson.version ?? "")
599
- })
600
- );
601
- break;
602
- case "none":
603
- promises.push(
604
- Promise.resolve({
605
- distPath,
606
- hash: 0
607
- })
608
- );
609
- break;
610
- default:
611
- promises.push(
612
- this.getFileHash(fullPath).then((hash) => ({
613
- distPath,
614
- hash
615
- }))
616
- );
617
- break;
618
- }
619
- }
620
- const manifest = {
621
- sourceHashes: {}
622
- };
623
- const hashes = await Promise.all(promises);
624
- for (const { distPath, hash } of hashes) {
625
- manifest.sourceHashes[distPath] = hash;
626
- }
627
- const manifestPath = resolve(distBasePath, "highstate.manifest.json");
628
- await writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
629
- }
630
- async getFileHash(fullPath) {
631
- const existingHash = this.fileHashes.get(fullPath);
632
- if (existingHash) {
633
- return existingHash;
634
- }
635
- const hash = this.calculateFileHash(fullPath);
636
- this.fileHashes.set(fullPath, hash);
637
- return hash;
638
- }
639
- async calculateFileHash(fullPath) {
640
- const content = await readFile(fullPath, "utf8");
641
- const fileDeps = this.parseDependencies(fullPath, content);
642
- const hashes = await Promise.all([
643
- this.hashString(content),
644
- ...fileDeps.map((dep) => this.getDependencyHash(dep))
645
- ]);
646
- return crc32(Buffer.concat(hashes.map(int32ToBytes)));
647
- }
648
- getDependencyHash(dependency) {
649
- const existingHash = this.dependencyHashes.get(dependency.id);
650
- if (existingHash) {
651
- return existingHash;
652
- }
653
- const hash = this.calculateDependencyHash(dependency);
654
- this.dependencyHashes.set(dependency.id, hash);
655
- return hash;
656
- }
657
- async calculateDependencyHash(dependency) {
658
- switch (dependency.type) {
659
- case "relative": {
660
- return await this.getFileHash(dependency.fullPath);
661
- }
662
- case "npm": {
663
- let resolvedUrl;
664
- try {
665
- const baseUrl = pathToFileURL(this.packageJsonPath);
666
- resolvedUrl = resolve$1(dependency.package, baseUrl.toString());
667
- } catch (error) {
668
- this.logger.error(`failed to resolve package "%s"`, dependency.package);
669
- throw error;
670
- }
671
- const resolvedPath = fileURLToPath(resolvedUrl);
672
- const [depPackageJsonPath, depPackageJson] = await this.getPackageJson(resolvedPath);
673
- const packageName = depPackageJson.name;
674
- this.logger.debug(
675
- `resolved package.json for "%s": "%s"`,
676
- dependency.package,
677
- depPackageJsonPath
678
- );
679
- if (!this.packageJson.dependencies?.[packageName] && !this.packageJson.peerDependencies?.[packageName]) {
680
- this.logger.warn(`package "%s" is not listed in package.json dependencies`, packageName);
681
- }
682
- let relativePath = relative(dirname(depPackageJsonPath), resolvedPath);
683
- relativePath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
684
- const highstateManifestPath = resolve(
685
- dirname(depPackageJsonPath),
686
- "dist",
687
- "highstate.manifest.json"
688
- );
689
- let manifest;
690
- try {
691
- const manifestContent = await readFile(highstateManifestPath, "utf8");
692
- manifest = highstateManifestSchema.parse(JSON.parse(manifestContent));
693
- } catch (error) {
694
- this.logger.debug(
695
- { error },
696
- `failed to read highstate manifest for package "%s"`,
697
- packageName
698
- );
699
- }
700
- const sourceHash = manifest?.sourceHashes?.[relativePath];
701
- if (sourceHash) {
702
- this.logger.debug(`resolved source hash for package "%s"`, packageName);
703
- return sourceHash;
704
- }
705
- this.logger.debug(`using package version as a fallback hash for "%s"`, packageName);
706
- return this.hashString(depPackageJson.version ?? "0.0.0");
707
- }
708
- }
709
- }
710
- async getPackageJson(basePath) {
711
- while (true) {
712
- const packageJson = await readPackageJSON(basePath);
713
- if (packageJson.name) {
714
- const packageJsonPath = await resolvePackageJSON(basePath);
715
- return [packageJsonPath, packageJson];
716
- }
717
- basePath = resolve(dirname(basePath), "..");
718
- }
719
- }
720
- parseDependencies(filePath, content) {
721
- const dependencyRegex = /^[ \t]*import[\s\S]*?\bfrom\s*["']((?<relativePath>\.\.?\/[^"']+)|(?<nodeBuiltin>node:[^"']+)|(?<npmPackage>[^"']+))["']/gm;
722
- const matches = content.matchAll(dependencyRegex);
723
- const dependencies = [];
724
- for (const match of matches) {
725
- const { nodeBuiltin, npmPackage, relativePath } = match.groups;
726
- if (relativePath) {
727
- const fullPath = resolve(dirname(filePath), relativePath);
728
- dependencies.push({
729
- type: "relative",
730
- id: `relative:${fullPath}`,
731
- fullPath
732
- });
733
- } else if (npmPackage) {
734
- dependencies.push({
735
- type: "npm",
736
- id: `npm:${npmPackage}`,
737
- package: npmPackage
738
- });
739
- } else ;
740
- }
741
- return dependencies;
742
- }
743
- };
744
- var packageJsonSchema = z.object({
745
- name: z.string(),
746
- highstate: highstateConfigSchema.optional()
747
- });
748
- function generateTsconfigContent(workspaceRoot, packagePath) {
749
- const relativePath = relative(workspaceRoot, packagePath);
750
- const depth = relativePath.split("/").length;
751
- const relativeNodeModules = `${"../".repeat(depth)}node_modules/@highstate/cli/assets/tsconfig.base.json`;
752
- return {
753
- extends: relativeNodeModules,
754
- include: ["./src/**/*.ts", "./package.json", "./assets/**/*.json"]
755
- };
756
- }
757
- async function findWorkspaceRoot(startPath = process.cwd()) {
758
- let currentPath = resolve(startPath);
759
- while (currentPath !== "/") {
760
- const packageJsonPath = join(currentPath, "package.json");
761
- if (existsSync(packageJsonPath)) {
762
- try {
763
- const content = await readFile(packageJsonPath, "utf-8");
764
- const packageJson = JSON.parse(content);
765
- if (packageJson.workspaces) {
766
- return currentPath;
767
- }
768
- } catch {
769
- }
770
- }
771
- const parentPath = resolve(currentPath, "..");
772
- if (parentPath === currentPath) break;
773
- currentPath = parentPath;
774
- }
775
- throw new Error("Could not find workspace root (no package.json with workspaces found)");
776
- }
777
- async function scanWorkspacePackages(workspaceRoot) {
778
- const packages = [];
779
- const packagesDir = join(workspaceRoot, "packages");
780
- if (!existsSync(packagesDir)) {
781
- return packages;
782
- }
783
- async function scanDirectory(dirPath, depth = 0) {
784
- const dirName = relative(packagesDir, dirPath).split("/").pop();
785
- if (dirName?.startsWith(".") || dirName === "node_modules") {
786
- return;
787
- }
788
- const entries = await readdir(dirPath, { withFileTypes: true });
789
- for (const entry of entries) {
790
- if (!entry.isDirectory()) continue;
791
- const entryPath = join(dirPath, entry.name);
792
- if (entry.name.startsWith(".") || entry.name === "node_modules") {
793
- continue;
794
- }
795
- const packageJsonPath = join(entryPath, "package.json");
796
- if (existsSync(packageJsonPath)) {
797
- try {
798
- const content = await readFile(packageJsonPath, "utf-8");
799
- const packageJson = packageJsonSchema.parse(JSON.parse(content));
800
- const relativePath = relative(workspaceRoot, entryPath);
801
- const type = packageJson.highstate?.type ?? "source";
802
- packages.push({
803
- path: entryPath,
804
- relativePath,
805
- name: packageJson.name,
806
- type
807
- });
808
- } catch {
809
- }
810
- }
811
- if (depth < 3) {
812
- await scanDirectory(entryPath, depth + 1);
813
- }
814
- }
815
- }
816
- await scanDirectory(packagesDir);
817
- return packages.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
818
- }
819
- async function updateTsconfigReferences(workspaceRoot, packages, ensureTsconfigs = false) {
820
- const tsconfigPath = join(workspaceRoot, "tsconfig.json");
821
- if (ensureTsconfigs) {
822
- await ensurePackageTsconfigs(
823
- workspaceRoot,
824
- // only udate for Highstate-managed packages
825
- packages.filter((pkg) => pkg.type !== void 0)
826
- );
827
- }
828
- const references = packages.map((pkg) => ({
829
- path: `./${pkg.relativePath}/tsconfig.json`
830
- }));
831
- const tsconfigContent = {
832
- files: [],
833
- references
834
- };
835
- await writeFile(tsconfigPath, `${JSON.stringify(tsconfigContent, null, 2)}
836
- `, "utf-8");
837
- }
838
- async function ensurePackageTsconfigs(workspaceRoot, packages) {
839
- for (const pkg of packages) {
840
- const tsconfigPath = join(pkg.path, "tsconfig.json");
841
- const tsconfigContent = generateTsconfigContent(workspaceRoot, pkg.path);
842
- await writeFile(tsconfigPath, `${JSON.stringify(tsconfigContent, null, 2)}
843
- `, "utf-8");
844
- }
845
- }
846
- async function createPackage(workspaceRoot, name, type) {
847
- const packagePath = join(workspaceRoot, "packages", name);
848
- const srcPath = join(packagePath, "src");
849
- await mkdir(packagePath, { recursive: true });
850
- await mkdir(srcPath, { recursive: true });
851
- const packageJson = {
852
- name: `@highstate/${name}`,
853
- version: "0.0.1",
854
- type: "module",
855
- highstate: {
856
- type
857
- }
858
- };
859
- await writeFile(
860
- join(packagePath, "package.json"),
861
- `${JSON.stringify(packageJson, null, 2)}
862
- `,
863
- "utf-8"
864
- );
865
- const tsconfigContent = generateTsconfigContent(workspaceRoot, packagePath);
866
- await writeFile(
867
- join(packagePath, "tsconfig.json"),
868
- `${JSON.stringify(tsconfigContent, null, 2)}
869
- `,
870
- "utf-8"
871
- );
872
- await writeFile(join(srcPath, "index.ts"), `// ${name} package
873
- `, "utf-8");
874
- return {
875
- path: packagePath,
876
- relativePath: `packages/${name}`,
877
- name: `@highstate/${name}`,
878
- type
879
- };
880
- }
881
-
882
- // src/commands/backend/identity.ts
883
- var BackendIdentityCommand = class extends Command {
884
- static paths = [["backend", "identity"]];
885
- static usage = Command.Usage({
886
- category: "Backend",
887
- description: "Ensures the backend identity is set up and returns the recipient."
888
- });
889
- async execute() {
890
- const { getOrCreateBackendIdentity } = await import('@highstate/backend');
891
- const config = await loadConfig();
892
- const backendIdentity = await getOrCreateBackendIdentity(config, logger);
893
- const recipient = await identityToRecipient(backendIdentity);
894
- logger.info(`stored backend identity: "%s"`, recipient);
895
- const suggestedTitle = hostname();
896
- if (!suggestedTitle) {
897
- logger.info(`run "highstate backend unlock-method add %s" on a trusted device`, recipient);
898
- return;
899
- }
900
- logger.info(
901
- `run "highstate backend unlock-method add %s --title %s" on a trusted device`,
902
- recipient,
903
- suggestedTitle
904
- );
905
- }
906
- };
907
- var BackendUnlockMethodAddCommand = class extends Command {
908
- static paths = [["backend", "unlock-method", "add"]];
909
- static usage = Command.Usage({
910
- category: "Backend",
911
- description: "Adds a new backend unlock method for the current workspace.",
912
- examples: [["Add recipient", "highstate backend unlock-method add age1example --title Laptop"]]
913
- });
914
- recipient = Option.String();
915
- title = Option.String("--title");
916
- description = Option.String("--description");
917
- async execute() {
918
- let title = this.title;
919
- if (!title) {
920
- title = await input({
921
- message: "Unlock Method Title",
922
- default: "New Device",
923
- validate: (value) => value.trim().length > 0 ? true : "Title is required"
924
- });
925
- }
926
- let description = this.description;
927
- if (description === void 0) {
928
- description = await input({
929
- message: "Description (optional)",
930
- default: ""
931
- });
932
- }
933
- const services2 = await getBackendServices();
934
- try {
935
- const result = await services2.backendUnlockService.addUnlockMethod({
936
- recipient: this.recipient,
937
- meta: description ? { title: title.trim(), description: description.trim() } : { title: title.trim() }
938
- });
939
- logger.info(`added backend unlock method "%s"`, result.id);
940
- } finally {
941
- await disposeServices();
942
- }
943
- process.exit(0);
944
- }
945
- };
946
- var BackendUnlockMethodDeleteCommand = class extends Command {
947
- static paths = [["backend", "unlock-method", "delete"]];
948
- static usage = Command.Usage({
949
- category: "Backend",
950
- description: "Removes a backend unlock method by its identifier."
951
- });
952
- id = Option.String();
953
- force = Option.Boolean("--force", false);
954
- async execute() {
955
- if (!this.force) {
956
- const answer = await confirm({
957
- message: `Delete backend unlock method ${this.id}?`,
958
- default: false
959
- });
960
- if (!answer) {
961
- logger.info("cancelled backend unlock method deletion");
962
- return;
963
- }
964
- }
965
- const services2 = await getBackendServices();
966
- try {
967
- await services2.backendUnlockService.deleteUnlockMethod(this.id);
968
- logger.info(`deleted backend unlock method "%s"`, this.id);
969
- } finally {
970
- await disposeServices();
971
- }
972
- process.exit(0);
973
- }
974
- };
975
- var BackendUnlockMethodListCommand = class extends Command {
976
- static paths = [["backend", "unlock-method", "list"]];
977
- static usage = Command.Usage({
978
- category: "Backend",
979
- description: "Lists backend unlock methods registered for the current workspace."
980
- });
981
- async execute() {
982
- const services2 = await getBackendServices();
983
- try {
984
- const methods = await services2.backendUnlockService.listUnlockMethods();
985
- if (methods.length === 0) {
986
- logger.warn("no backend unlock methods configured");
987
- return;
988
- }
989
- const table = new Table({
990
- columns: [
991
- { name: "title", title: "Title" },
992
- { name: "id", title: "ID" },
993
- { name: "recipient", title: "Recipient" },
994
- { name: "description", title: "Description", maxLen: 30 }
995
- ],
996
- defaultColumnOptions: {
997
- alignment: "left"
998
- }
999
- });
1000
- table.addRows(
1001
- methods.map((method) => ({
1002
- title: method.meta.title,
1003
- id: method.id,
1004
- recipient: method.recipient,
1005
- description: method.meta.description ?? ""
1006
- }))
1007
- );
1008
- table.printTable();
1009
- } finally {
1010
- await disposeServices();
1011
- }
1012
- process.exit(0);
1013
- }
1014
- };
1015
- var BuildCommand = class extends Command {
1016
- static paths = [["build"]];
1017
- static usage = Command.Usage({
1018
- category: "Builder",
1019
- description: "Builds the Highstate library or unit package."
1020
- });
1021
- watch = Option.Boolean("--watch", false);
1022
- library = Option.Boolean("--library", false);
1023
- silent = Option.Boolean("--silent", true);
1024
- noSourceHash = Option.Boolean("--no-source-hash", false);
1025
- async execute() {
1026
- const packageJson = await readPackageJSON();
1027
- const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {});
1028
- if (highstateConfig.type === "library") {
1029
- this.library = true;
1030
- }
1031
- if (highstateConfig.type === "worker") {
1032
- this.noSourceHash = true;
1033
- }
1034
- if (!packageJson.name) {
1035
- throw new Error("package.json must have a name field");
1036
- }
1037
- const entryPoints = extractEntryPoints(packageJson);
1038
- if (Object.keys(entryPoints).length === 0) {
1039
- return;
1040
- }
1041
- const esbuildPlugins = [];
1042
- const binSourceFilePaths = Object.values(entryPoints).filter((value) => value.isBin).map((value) => value.entryPoint.slice(2));
1043
- if (this.library) {
1044
- esbuildPlugins.push(schemaTransformerPlugin);
1045
- }
1046
- if (binSourceFilePaths.length > 0) {
1047
- esbuildPlugins.push(createBinTransformerPlugin(binSourceFilePaths));
1048
- }
1049
- await build({
1050
- entry: mapValues(entryPoints, (value) => value.entryPoint),
1051
- outDir: "dist",
1052
- watch: this.watch,
1053
- sourcemap: true,
1054
- clean: true,
1055
- format: "esm",
1056
- target: "es2024",
1057
- platform: "node",
1058
- external: ["@pulumi/pulumi"],
1059
- esbuildPlugins,
1060
- treeshake: true,
1061
- removeNodeProtocol: false,
1062
- silent: this.silent || ["warn", "error", "fatal"].includes(logger.level)
1063
- });
1064
- const packageJsonPath = await resolvePackageJSON();
1065
- const upToDatePackageJson = await readPackageJSON();
1066
- if (!this.noSourceHash) {
1067
- const sourceHashCalculator = new SourceHashCalculator(
1068
- packageJsonPath,
1069
- upToDatePackageJson,
1070
- logger
1071
- );
1072
- const distPathToExportKey = /* @__PURE__ */ new Map();
1073
- for (const value of Object.values(entryPoints)) {
1074
- distPathToExportKey.set(value.distPath, value.key);
1075
- }
1076
- await sourceHashCalculator.writeHighstateManifest("./dist", distPathToExportKey);
1077
- }
1078
- if (this.library) {
1079
- const { loadLibrary } = await import('./library-loader-PZWYMBAE.js');
1080
- const fullModulePaths = Object.values(entryPoints).map((value) => resolve(value.distPath));
1081
- logger.info("evaluating library components from modules: %s", fullModulePaths.join(", "));
1082
- const library = await loadLibrary(logger, fullModulePaths);
1083
- const libraryPath = resolve("./dist", "highstate.library.msgpack");
1084
- await writeFile(libraryPath, encode(library), "utf8");
1085
- }
1086
- logger.info("build completed successfully");
1087
- }
1088
- };
1089
- var DesignerCommand = class extends Command {
1090
- static paths = [["designer"]];
1091
- static usage = Command.Usage({
1092
- category: "Designer",
1093
- description: "Starts the Highstate designer in the current project."
1094
- });
1095
- async execute() {
1096
- const packageJsonPath = await resolvePackageJSON();
1097
- const packageJsonUrl = pathToFileURL(packageJsonPath).toString();
1098
- const packageJson = await readPackageJSON(packageJsonPath);
1099
- if (!packageJson.devDependencies?.["@highstate/cli"]) {
1100
- throw new UsageError(
1101
- "This project is not a Highstate project.\n@highstate/cli must be installed as a devDependency."
1102
- );
1103
- }
1104
- if (!packageJson.devDependencies?.["@highstate/designer"]) {
1105
- logger.info("Installing @highstate/designer...");
1106
- await addDevDependency(["@highstate/designer", "classic-level"]);
1107
- }
1108
- logger.info("starting highstate designer...");
1109
- await getBackendServices();
1110
- const oldConsoleLog = console.log;
1111
- const port = await getPort({ port: 3e3 });
1112
- const eventsPort = await getPort({ port: 3001 });
1113
- const designerPackageJsonPath = resolve$1(
1114
- "@highstate/designer/package.json",
1115
- packageJsonUrl
1116
- );
1117
- const designerPackageJson = await readPackageJSON(designerPackageJsonPath);
1118
- process.env.NITRO_PORT = port.toString();
1119
- process.env.NITRO_HOST = "0.0.0.0";
1120
- process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version;
1121
- process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString();
1122
- await new Promise((resolve6) => {
1123
- console.log = (message) => {
1124
- if (message.startsWith("Listening on")) {
1125
- resolve6();
1126
- }
1127
- };
1128
- const serverPath = resolve$1("@highstate/designer/server", packageJsonUrl);
1129
- void import(serverPath);
1130
- });
1131
- console.log = oldConsoleLog;
1132
- consola.log(
1133
- [
1134
- "\n ",
1135
- colorize("bold", colorize("cyanBright", "Highstate Designer")),
1136
- "\n ",
1137
- colorize("greenBright", "\u279C Local: "),
1138
- colorize("underline", colorize("cyanBright", `http://localhost:${port}`)),
1139
- "\n"
1140
- ].join("")
1141
- );
1142
- process.on("SIGINT", () => {
1143
- process.stdout.write("\r");
1144
- consola.info("shutting down highstate designer...");
1145
- setTimeout(() => process.exit(0), 1e3);
1146
- });
1147
- }
1148
- };
1149
- var InitCommand = class _InitCommand extends Command {
1150
- static paths = [["init"]];
1151
- static usage = Command.Usage({
1152
- description: "Initializes a new Highstate project."
1153
- });
1154
- pathOption = Option.String("--path,-p", {
1155
- description: "The path where the project should be initialized."
1156
- });
1157
- packageManager = Option.String("--package-manager", {
1158
- description: "The package manager to use (npm, yarn, pnpm)."
1159
- });
1160
- name = Option.String("--name", {
1161
- description: "The project name."
1162
- });
1163
- static defaultPlatformVersion = "0.14.0";
1164
- static defaultLibraryVersion = "0.14.0";
1165
- async execute() {
1166
- const availablePackageManagers = await detectAvailablePackageManagers(["npm", "pnpm", "yarn"]);
1167
- if (availablePackageManagers.length === 0) {
1168
- throw new Error("no supported package managers found in PATH (npm, pnpm, yarn)");
1169
- }
1170
- const destinationPath = await resolveDestinationPath(this.pathOption);
1171
- const defaultName = basename(destinationPath);
1172
- const projectName = await resolveProjectName(this.name, defaultName);
1173
- const selectedPackageManager = await resolvePackageManager(
1174
- this.packageManager,
1175
- availablePackageManagers
1176
- );
1177
- const templatePath = resolveTemplatePath();
1178
- await mkdir(destinationPath, { recursive: true });
1179
- const isEmptyOrMissing = await isEmptyDirectory(destinationPath);
1180
- if (!isEmptyOrMissing) {
1181
- throw new Error(`destination path is not empty: ${destinationPath}`);
1182
- }
1183
- logger.info("initializing highstate project in %s", destinationPath);
1184
- await generateFromTemplate(templatePath, destinationPath, {
1185
- projectName,
1186
- packageName: projectName,
1187
- platformVersion: _InitCommand.defaultPlatformVersion,
1188
- libraryVersion: _InitCommand.defaultLibraryVersion,
1189
- isPnpm: selectedPackageManager === "pnpm" ? "true" : "",
1190
- isYarn: selectedPackageManager === "yarn" ? "true" : ""
1191
- });
1192
- logger.info("installing dependencies using %s...", selectedPackageManager);
1193
- await installDependencies({
1194
- cwd: destinationPath,
1195
- packageManager: selectedPackageManager,
1196
- silent: false
1197
- });
1198
- logger.info("project initialized successfully");
1199
- }
1200
- };
1201
- async function resolveDestinationPath(pathOption) {
1202
- if (pathOption) {
1203
- return resolve(pathOption);
1204
- }
1205
- const pathValue = await input({
1206
- message: "Project path",
1207
- default: ".",
1208
- validate: (value) => value.trim().length > 0 ? true : "Path is required"
1209
- });
1210
- return resolve(pathValue);
1211
- }
1212
- async function resolveProjectName(nameOption, defaultName) {
1213
- if (nameOption) {
1214
- return nameOption.trim();
1215
- }
1216
- const value = await input({
1217
- message: "Project name",
1218
- default: defaultName,
1219
- validate: (inputValue) => inputValue.trim().length > 0 ? true : "Name is required"
1220
- });
1221
- return value.trim();
1222
- }
1223
- async function resolvePackageManager(packageManagerOption, available) {
1224
- if (packageManagerOption) {
1225
- if (!isSupportedPackageManagerName(packageManagerOption)) {
1226
- throw new Error(`unsupported package manager: ${packageManagerOption}`);
1227
- }
1228
- const name = packageManagerOption;
1229
- if (!available.includes(name)) {
1230
- throw new Error(`package manager not found in PATH: ${name}`);
1231
- }
1232
- return name;
1233
- }
1234
- const preferredOrder = ["pnpm", "yarn", "npm"];
1235
- const defaultValue = preferredOrder.find((value) => available.includes(value)) ?? available[0];
1236
- return await select({
1237
- message: "Package manager",
1238
- default: defaultValue,
1239
- choices: available.map((value) => ({ name: value, value }))
1240
- });
1241
- }
1242
- function isSupportedPackageManagerName(value) {
1243
- return value === "npm" || value === "pnpm" || value === "yarn";
1244
- }
1245
- async function detectAvailablePackageManagers(candidates) {
1246
- const results = [];
1247
- for (const candidate of candidates) {
1248
- const exists = await isExecutableInPath(candidate);
1249
- if (exists) {
1250
- results.push(candidate);
1251
- }
1252
- }
1253
- return results;
1254
- }
1255
- async function isExecutableInPath(command) {
1256
- const pathValue = process.env.PATH;
1257
- if (!pathValue) {
1258
- return false;
1259
- }
1260
- const parts = pathValue.split(":").filter(Boolean);
1261
- for (const part of parts) {
1262
- const candidate = resolve(part, command);
1263
- try {
1264
- await access(candidate);
1265
- return true;
1266
- } catch {
1267
- }
1268
- }
1269
- return false;
1270
- }
1271
- async function isEmptyDirectory(path) {
1272
- try {
1273
- const entries = await readdir(path);
1274
- return entries.length === 0;
1275
- } catch {
1276
- return true;
1277
- }
1278
- }
1279
- function resolveTemplatePath() {
1280
- const here = fileURLToPath(new URL(import.meta.url));
1281
- return resolve(here, "..", "..", "assets", "template");
1282
- }
1283
- var CreateCommand = class extends Command {
1284
- static paths = [["package", "create"]];
1285
- static usage = Command.Usage({
1286
- category: "Package",
1287
- description: "Creates a new package in the workspace."
1288
- });
1289
- name = Option.String({ required: true });
1290
- type = Option.String("--type,-t", {
1291
- description: "Package type (source, library, worker)"
1292
- });
1293
- async execute() {
1294
- const workspaceRoot = await findWorkspaceRoot();
1295
- const packageType = highstateConfigSchema.shape.type.parse(this.type);
1296
- await createPackage(workspaceRoot, this.name, packageType);
1297
- const packages = await scanWorkspacePackages(workspaceRoot);
1298
- await updateTsconfigReferences(workspaceRoot, packages);
1299
- logger.info(`created package: @highstate/${this.name} (${packageType})`);
1300
- }
1301
- };
1302
- var ListCommand = class extends Command {
1303
- static paths = [["package", "list"]];
1304
- static usage = Command.Usage({
1305
- category: "Package",
1306
- description: "Lists all packages in the workspace with their types."
1307
- });
1308
- async execute() {
1309
- const workspaceRoot = await findWorkspaceRoot();
1310
- const packages = await scanWorkspacePackages(workspaceRoot);
1311
- if (packages.length === 0) {
1312
- logger.info("no packages found in workspace");
1313
- return;
1314
- }
1315
- const table = new Table({
1316
- columns: [
1317
- { name: "name", title: "Name" },
1318
- { name: "type", title: "Type" },
1319
- { name: "path", title: "Path" }
1320
- ]
1321
- });
1322
- table.addRows(
1323
- packages.map((pkg) => ({
1324
- name: pkg.name,
1325
- type: pkg.type ?? "unknown",
1326
- path: pkg.relativePath
1327
- }))
1328
- );
1329
- table.printTable();
1330
- }
1331
- };
1332
- var RemoveCommand = class extends Command {
1333
- static paths = [["package", "remove"]];
1334
- static usage = Command.Usage({
1335
- category: "Package",
1336
- description: "Removes a package from the workspace."
1337
- });
1338
- name = Option.String({ required: true });
1339
- async execute() {
1340
- const workspaceRoot = await findWorkspaceRoot();
1341
- const packages = await scanWorkspacePackages(workspaceRoot);
1342
- const targetPackage = packages.find(
1343
- (pkg) => pkg.name === this.name || pkg.name === `@highstate/${this.name}` || pkg.relativePath.endsWith(this.name)
1344
- );
1345
- if (!targetPackage) {
1346
- logger.error(`package not found: ${this.name}`);
1347
- process.exit(1);
1348
- }
1349
- await rm(targetPackage.path, { recursive: true, force: true });
1350
- const remainingPackages = await scanWorkspacePackages(workspaceRoot);
1351
- await updateTsconfigReferences(workspaceRoot, remainingPackages);
1352
- logger.info(`removed package: ${targetPackage.name}`);
1353
- }
1354
- };
1355
- var UpdateReferencesCommand = class extends Command {
1356
- static paths = [["package", "update-references"]];
1357
- static usage = Command.Usage({
1358
- category: "Package",
1359
- description: "Updates the root tsconfig.json with references to all packages in the workspace."
1360
- });
1361
- async execute() {
1362
- const workspaceRoot = await findWorkspaceRoot();
1363
- const packages = await scanWorkspacePackages(workspaceRoot);
1364
- await updateTsconfigReferences(workspaceRoot, packages, true);
1365
- }
1366
- };
1367
-
1368
- // src/main.ts
1369
6
  var cli = new Cli({
1370
7
  binaryName: "highstate",
1371
- binaryLabel: "Highstate",
1372
- binaryVersion: version
8
+ binaryLabel: "Highstate"
9
+ // binaryVersion: version,
1373
10
  });
1374
11
  cli.register(BuildCommand);
1375
12
  cli.register(DesignerCommand);
1376
13
  cli.register(InitCommand);
14
+ cli.register(UpdateCommand);
1377
15
  cli.register(BackendIdentityCommand);
1378
16
  cli.register(BackendUnlockMethodListCommand);
1379
17
  cli.register(BackendUnlockMethodAddCommand);
1380
18
  cli.register(BackendUnlockMethodDeleteCommand);
1381
- cli.register(UpdateReferencesCommand);
1382
- cli.register(ListCommand);
1383
- cli.register(CreateCommand);
1384
- cli.register(RemoveCommand);
19
+ cli.register(PackageUpdateReferencesCommand);
20
+ cli.register(PackageListCommand);
21
+ cli.register(PackageCreateCommand);
22
+ cli.register(PackageRemoveCommand);
1385
23
  cli.register(Builtins.HelpCommand);
1386
24
  cli.register(Builtins.VersionCommand);
1387
25
  await cli.runExit(process.argv.slice(2));