heximon 0.7.0 → 0.8.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/README.md +1 -1
- package/dist/bin.mjs +84 -26
- package/package.json +17 -15
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ const main = defineCommand({
|
|
|
78
78
|
| `heximon provision` | Creates a resource your build needs an identifier for, before the build runs. |
|
|
79
79
|
| `heximon migrations` | Runs `generate`, `migrate`, `status`, or `push` against your database, with no hand-written `drizzle-kit` config. |
|
|
80
80
|
| `heximon workflows` | Compares a durable workflow's current shape against its committed upgrade baseline. |
|
|
81
|
-
| `heximon task run <Module> <Task>` | Runs one task against the production graph of the app, prints its result as JSON, and exits. It starts no HTTP listener, no schedule timer, no polling loop, and no outbox relay (`--with-outbox-relay` starts it). It refuses a graph that needs platform bindings. `--payload` sets the event payload, and `--env-file` loads variables. |
|
|
81
|
+
| `heximon task run <Module> <Task>` | Runs one task against the production graph of the app, prints its result as JSON, and exits. It starts no HTTP listener, no schedule timer, no polling loop, and no outbox relay (`--with-outbox-relay` starts it). It refuses a graph that needs platform bindings. The module can be a packed library module: the command runs the tasks that the module exports. `--payload` sets the event payload, and `--env-file` loads variables. Exit status: `0` when the task returned, `2` when the task itself threw (stdout then holds one `{"error":{"name":…,"message":…}}` line), `1` for every other failure. |
|
|
82
82
|
| `heximon skills` | `add <url>` records an agent-skill source in `package.json` (`heximon.skills`) and installs it. `sync` re-installs every recorded source. |
|
|
83
83
|
| `heximon complete <shell>` | Installs shell completion for `zsh`, `bash`, `fish`, `powershell`, or `fig`. |
|
|
84
84
|
|
package/dist/bin.mjs
CHANGED
|
@@ -3467,8 +3467,9 @@ var TaskTargetError = class extends Error {
|
|
|
3467
3467
|
};
|
|
3468
3468
|
/**
|
|
3469
3469
|
* Picks the module class and the task class that `heximon task run <ModuleClass> <TaskClass>` names, by class
|
|
3470
|
-
* name, in the compiled graph. A
|
|
3471
|
-
*
|
|
3470
|
+
* name, in the compiled graph. A packed library module is a boundary of the graph: the command reaches only the
|
|
3471
|
+
* tasks that it exports. A name that matches nothing or more than one class fails with the names that come
|
|
3472
|
+
* close.
|
|
3472
3473
|
*/
|
|
3473
3474
|
var TaskTargetResolver = class TaskTargetResolver {
|
|
3474
3475
|
/** The most edits between a typed name and a suggestion. */
|
|
@@ -3478,20 +3479,24 @@ var TaskTargetResolver = class TaskTargetResolver {
|
|
|
3478
3479
|
/**
|
|
3479
3480
|
* @param graph - The module graph of the production compile.
|
|
3480
3481
|
* @param moduleName - The module class name.
|
|
3481
|
-
* @param taskName - The task class name; the module must provide it.
|
|
3482
|
+
* @param taskName - The task class name; the module must provide it (a library module: export it).
|
|
3483
|
+
* @param boundaries - The packed library modules of the graph.
|
|
3482
3484
|
* @returns The module and the task.
|
|
3483
3485
|
* @throws {TaskTargetError} When a name matches no class, or more than one.
|
|
3484
3486
|
*/
|
|
3485
|
-
static resolve(graph, moduleName, taskName) {
|
|
3486
|
-
const
|
|
3487
|
-
|
|
3487
|
+
static resolve(graph, moduleName, taskName, boundaries = []) {
|
|
3488
|
+
const moduleReferences = [...graph.modules.map((node) => node.reference), ...boundaries.map((boundary) => boundary.reference)];
|
|
3489
|
+
const modules = TaskTargetResolver.distinct(moduleReferences, moduleName);
|
|
3490
|
+
if (modules.length === 0) throw new TaskTargetError(`No module class named "${moduleName}" is in the compiled graph.` + TaskTargetResolver.suggestions(moduleName, moduleReferences.map((reference) => TaskTargetResolver.nameOf(reference)), "Modules in the graph"));
|
|
3488
3491
|
if (modules.length > 1) throw new TaskTargetError(`More than one module class is named "${moduleName}":\n` + modules.map((reference) => ` - ${reference.sourceFile}`).join("\n"));
|
|
3489
3492
|
const module = modules[0];
|
|
3490
3493
|
const nodes = graph.modules.filter((node) => TaskTargetResolver.isSame(node.reference, module));
|
|
3491
|
-
const
|
|
3492
|
-
const
|
|
3493
|
-
|
|
3494
|
-
|
|
3494
|
+
const libraries = boundaries.filter((boundary) => TaskTargetResolver.isSame(boundary.reference, module));
|
|
3495
|
+
const isLibrary = nodes.length === 0 && libraries.length > 0;
|
|
3496
|
+
const candidates = isLibrary ? libraries.flatMap((boundary) => boundary.exports.map((exported) => exported.token)) : nodes.flatMap((node) => node.providers.map((provider) => provider.token));
|
|
3497
|
+
const tasks = TaskTargetResolver.distinct(candidates, taskName);
|
|
3498
|
+
if (tasks.length === 0) throw new TaskTargetError((isLibrary ? `The library module ${moduleName} does not export a class named "${taskName}". \`heximon task run\` reaches the tasks that a library module exports: export the task from the module and pack the library again.` : `The module ${moduleName} does not provide a class named "${taskName}".`) + TaskTargetResolver.otherOwner(graph, boundaries, module, taskName) + TaskTargetResolver.suggestions(taskName, candidates.map((token) => TaskTargetResolver.nameOf(token)), isLibrary ? `Exports of ${moduleName}` : `Providers of ${moduleName}`));
|
|
3499
|
+
if (tasks.length > 1) throw new TaskTargetError(`The module ${moduleName} ${isLibrary ? "exports" : "provides"} more than one class named "${taskName}":\n` + tasks.map((reference) => ` - ${reference.sourceFile}`).join("\n"));
|
|
3495
3500
|
return {
|
|
3496
3501
|
module,
|
|
3497
3502
|
task: tasks[0]
|
|
@@ -3511,11 +3516,11 @@ var TaskTargetResolver = class TaskTargetResolver {
|
|
|
3511
3516
|
for (const reference of references) if (TaskTargetResolver.nameOf(reference) === name && !found.some((known) => TaskTargetResolver.isSame(known, reference))) found.push(reference);
|
|
3512
3517
|
return found;
|
|
3513
3518
|
}
|
|
3514
|
-
/** A hint when another module of the graph provides the class. */
|
|
3515
|
-
static otherOwner(graph, own, taskName) {
|
|
3516
|
-
const owners = graph.modules.filter((node) => !
|
|
3519
|
+
/** A hint when another module of the graph provides (or a library module exports) the class. */
|
|
3520
|
+
static otherOwner(graph, boundaries, own, taskName) {
|
|
3521
|
+
const owners = [...graph.modules.filter((node) => !TaskTargetResolver.isSame(node.reference, own) && node.providers.some((provider) => TaskTargetResolver.nameOf(provider.token) === taskName)).map((node) => node.reference), ...boundaries.filter((boundary) => !TaskTargetResolver.isSame(boundary.reference, own) && boundary.exports.some((exported) => TaskTargetResolver.nameOf(exported.token) === taskName)).map((boundary) => boundary.reference)];
|
|
3517
3522
|
if (owners.length === 0) return "";
|
|
3518
|
-
return ` The class is provided by ${[...new Set(owners.map((
|
|
3523
|
+
return ` The class is provided by ${[...new Set(owners.map((reference) => TaskTargetResolver.nameOf(reference)))].join(", ")}.`;
|
|
3519
3524
|
}
|
|
3520
3525
|
/** The "did you mean" line, or the start of the list when nothing is close. */
|
|
3521
3526
|
static suggestions(typed, candidates, listTitle) {
|
|
@@ -3654,6 +3659,8 @@ var RefusingInternalClient = class {
|
|
|
3654
3659
|
};
|
|
3655
3660
|
/** The failure a run reports to the caller; the message is ready to print. */
|
|
3656
3661
|
var TaskRunFailure = class extends Error {};
|
|
3662
|
+
/** The task itself threw. The message is ready to print, and the thrown value is the cause. */
|
|
3663
|
+
var TaskRefusal = class extends TaskRunFailure {};
|
|
3657
3664
|
/**
|
|
3658
3665
|
* `heximon task run`: compiles the production graph into a temporary directory, boots it in this process,
|
|
3659
3666
|
* runs one {@link Task}, prints its result as JSON, and removes the compile output.
|
|
@@ -3671,6 +3678,8 @@ var TaskRunner = class TaskRunner {
|
|
|
3671
3678
|
static drainTimeoutMilliseconds = 1e4;
|
|
3672
3679
|
/** The most the interrupt handler waits for the app to shut down. */
|
|
3673
3680
|
static interruptShutdownMilliseconds = 5e3;
|
|
3681
|
+
/** The exit code of a run whose task itself threw. Every other failure exits with `1`. */
|
|
3682
|
+
static refusalExitCode = 2;
|
|
3674
3683
|
static wiringFilePattern = /^apps\/.+\.wiring\.js$/;
|
|
3675
3684
|
outputDirectory;
|
|
3676
3685
|
app;
|
|
@@ -3688,14 +3697,17 @@ var TaskRunner = class TaskRunner {
|
|
|
3688
3697
|
/** The result of the task as one line of JSON, printed after the shutdown. */
|
|
3689
3698
|
serialized;
|
|
3690
3699
|
disposing;
|
|
3700
|
+
/** How many package specifiers the run imported, for the file names of {@link ProjectModules.import}. */
|
|
3701
|
+
packageImports = 0;
|
|
3691
3702
|
constructor(options) {
|
|
3692
3703
|
this.options = options;
|
|
3693
3704
|
}
|
|
3694
3705
|
/**
|
|
3695
3706
|
* Run the task.
|
|
3696
3707
|
*
|
|
3697
|
-
* @returns The process exit code: `0` when the task returned, `
|
|
3698
|
-
*
|
|
3708
|
+
* @returns The process exit code: `0` when the task returned, `2` when the task itself threw (stdout then
|
|
3709
|
+
* holds one `{"error":{"name":…,"message":…}}` line), `1` for every other outcome. The reason is already
|
|
3710
|
+
* on stderr.
|
|
3699
3711
|
*/
|
|
3700
3712
|
async run() {
|
|
3701
3713
|
let interrupts = 0;
|
|
@@ -3718,9 +3730,12 @@ var TaskRunner = class TaskRunner {
|
|
|
3718
3730
|
await this.execute(target);
|
|
3719
3731
|
} catch (error) {
|
|
3720
3732
|
TaskRunner.report(error);
|
|
3721
|
-
|
|
3733
|
+
if (error instanceof TaskRefusal) {
|
|
3734
|
+
exitCode = TaskRunner.refusalExitCode;
|
|
3735
|
+
this.serialized = TaskRunner.errorLine(error.cause);
|
|
3736
|
+
} else exitCode = 1;
|
|
3722
3737
|
} finally {
|
|
3723
|
-
if (!await this.dispose()) exitCode = 1;
|
|
3738
|
+
if (!await this.dispose() && exitCode === 0) exitCode = 1;
|
|
3724
3739
|
this.stdout.stop();
|
|
3725
3740
|
if (this.serialized !== void 0) this.stdout.write(`${this.serialized}\n`);
|
|
3726
3741
|
process.off("SIGINT", interrupt);
|
|
@@ -3793,7 +3808,7 @@ var TaskRunner = class TaskRunner {
|
|
|
3793
3808
|
/** Pick the module and the task from the compiled graph. */
|
|
3794
3809
|
pickTarget(output) {
|
|
3795
3810
|
if (output.moduleGraph === void 0) throw new TaskRunFailure("The compile produced no module graph, so the task cannot be found.");
|
|
3796
|
-
return TaskTargetResolver.resolve(output.moduleGraph, this.options.moduleName, this.options.taskName);
|
|
3811
|
+
return TaskTargetResolver.resolve(output.moduleGraph, this.options.moduleName, this.options.taskName, output.boundaryNodes);
|
|
3797
3812
|
}
|
|
3798
3813
|
/** Refuse a graph that needs bindings only its platform has, before anything boots. */
|
|
3799
3814
|
static refuseBindings(output, platformName, outputDirectory) {
|
|
@@ -3837,15 +3852,34 @@ var TaskRunner = class TaskRunner {
|
|
|
3837
3852
|
const moduleClass = await this.importClass(target.module.sourceFile, target.module.className);
|
|
3838
3853
|
const taskClass = await this.importClass(target.task.sourceFile, target.task.className);
|
|
3839
3854
|
if (typeof taskClass.prototype.run !== "function") throw new TaskRunFailure(`${this.options.taskName} is not a task: the class has no run() method. A task extends Task.`);
|
|
3840
|
-
|
|
3855
|
+
let task;
|
|
3856
|
+
try {
|
|
3857
|
+
task = await this.app.from(moduleClass).get(taskClass);
|
|
3858
|
+
} catch (error) {
|
|
3859
|
+
throw new TaskRunFailure(`The task ${this.options.taskName} could not be created:\n${TaskRunner.describe(error)}`, { cause: error });
|
|
3860
|
+
}
|
|
3861
|
+
const event = {
|
|
3862
|
+
name: this.options.taskName,
|
|
3863
|
+
...this.options.payload === void 0 ? {} : { payload: this.options.payload }
|
|
3864
|
+
};
|
|
3865
|
+
const seeds = await this.seedsOf(runtime);
|
|
3841
3866
|
let outcome;
|
|
3842
3867
|
try {
|
|
3843
|
-
outcome = await
|
|
3844
|
-
|
|
3845
|
-
|
|
3868
|
+
outcome = await this.context.run(async () => {
|
|
3869
|
+
try {
|
|
3870
|
+
await seeds?.apply(event, this.context);
|
|
3871
|
+
} catch (error) {
|
|
3872
|
+
throw new TaskRunFailure(`The task ${this.options.taskName} did not start:\n${TaskRunner.describe(error)}`, { cause: error });
|
|
3873
|
+
}
|
|
3874
|
+
try {
|
|
3875
|
+
return await task.run(event);
|
|
3876
|
+
} catch (error) {
|
|
3877
|
+
throw new TaskRefusal(`The task ${this.options.taskName} failed:\n${TaskRunner.describe(error)}`, { cause: error });
|
|
3878
|
+
}
|
|
3846
3879
|
});
|
|
3847
3880
|
} catch (error) {
|
|
3848
|
-
|
|
3881
|
+
if (error instanceof TaskRunFailure) throw error;
|
|
3882
|
+
throw new TaskRunFailure(`The task ${this.options.taskName} did not start:\n${TaskRunner.describe(error)}`, { cause: error });
|
|
3849
3883
|
}
|
|
3850
3884
|
try {
|
|
3851
3885
|
this.serialized = JSON.stringify(outcome);
|
|
@@ -3863,9 +3897,13 @@ var TaskRunner = class TaskRunner {
|
|
|
3863
3897
|
throw error;
|
|
3864
3898
|
}
|
|
3865
3899
|
}
|
|
3866
|
-
/**
|
|
3900
|
+
/**
|
|
3901
|
+
* Import a class by file (or package specifier) and export name. A package specifier resolves from the
|
|
3902
|
+
* project, like the generated wiring, so the class is the one the app holds.
|
|
3903
|
+
*/
|
|
3867
3904
|
async importClass(sourceFile, className) {
|
|
3868
|
-
|
|
3905
|
+
this.packageImports += 1;
|
|
3906
|
+
const found = (isAbsolute(sourceFile) ? await import(pathToFileURL(sourceFile).href) : await ProjectModules.import(this.outputDirectory, `project-package-${this.packageImports}.js`, `export * from ${JSON.stringify(sourceFile)};\n`))[className];
|
|
3869
3907
|
if (found === void 0) throw new TaskRunFailure(`${sourceFile} does not export a class named ${className}.`);
|
|
3870
3908
|
return found;
|
|
3871
3909
|
}
|
|
@@ -3916,6 +3954,26 @@ var TaskRunner = class TaskRunner {
|
|
|
3916
3954
|
});
|
|
3917
3955
|
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
3918
3956
|
}
|
|
3957
|
+
/** The text of a thrown value: the stack of an error, else its string form. */
|
|
3958
|
+
static describe(error) {
|
|
3959
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
3960
|
+
}
|
|
3961
|
+
/**
|
|
3962
|
+
* The one stdout line of a refused run: `{"error":{"name":…,"message":…}}`. The name is the error name, or the
|
|
3963
|
+
* class name when the class leaves the generic `Error` name.
|
|
3964
|
+
*/
|
|
3965
|
+
static errorLine(thrown) {
|
|
3966
|
+
if (!(thrown instanceof Error)) return JSON.stringify({ error: {
|
|
3967
|
+
name: "Error",
|
|
3968
|
+
message: String(thrown)
|
|
3969
|
+
} });
|
|
3970
|
+
const className = thrown.constructor?.name;
|
|
3971
|
+
const name = thrown.name === "Error" && className !== void 0 && className !== "" ? className : thrown.name;
|
|
3972
|
+
return JSON.stringify({ error: {
|
|
3973
|
+
name,
|
|
3974
|
+
message: thrown.message
|
|
3975
|
+
} });
|
|
3976
|
+
}
|
|
3919
3977
|
/** Print a failure on stderr. */
|
|
3920
3978
|
static report(error) {
|
|
3921
3979
|
if (error instanceof TaskRunFailure || error instanceof TaskTargetError) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "heximon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The Heximon CLI: create, develop, build, migrate, and deploy Heximon apps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -38,25 +38,27 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@bomb.sh/tab": "^0.0.22",
|
|
40
40
|
"@clack/prompts": "^1.8.1",
|
|
41
|
-
"@heximon/compiler": "0.
|
|
42
|
-
"@heximon/cqrs": "0.
|
|
43
|
-
"@heximon/deploy": "0.
|
|
44
|
-
"@heximon/http": "0.
|
|
45
|
-
"@heximon/node": "0.
|
|
46
|
-
"@heximon/runtime": "0.
|
|
47
|
-
"@heximon/testing": "0.
|
|
41
|
+
"@heximon/compiler": "0.8.0",
|
|
42
|
+
"@heximon/cqrs": "0.8.0",
|
|
43
|
+
"@heximon/deploy": "0.8.0",
|
|
44
|
+
"@heximon/http": "0.8.0",
|
|
45
|
+
"@heximon/node": "0.8.0",
|
|
46
|
+
"@heximon/runtime": "0.8.0",
|
|
47
|
+
"@heximon/testing": "0.8.0",
|
|
48
48
|
"citty": "^0.2.2",
|
|
49
49
|
"giget": "^3.3.1",
|
|
50
50
|
"pathe": "^2.0.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@heximon/cloudflare": "0.
|
|
54
|
-
"@heximon/durable": "0.
|
|
55
|
-
"@heximon/integration": "0.
|
|
56
|
-
"@heximon/
|
|
57
|
-
"@heximon/
|
|
58
|
-
"@heximon/
|
|
59
|
-
"@heximon/
|
|
53
|
+
"@heximon/cloudflare": "0.8.0",
|
|
54
|
+
"@heximon/durable": "0.8.0",
|
|
55
|
+
"@heximon/integration": "0.8.0",
|
|
56
|
+
"@heximon/library": "0.8.0",
|
|
57
|
+
"@heximon/nitro": "0.8.0",
|
|
58
|
+
"@heximon/queue": "0.8.0",
|
|
59
|
+
"@heximon/schedule": "0.8.0",
|
|
60
|
+
"@heximon/schema": "0.8.0",
|
|
61
|
+
"@heximon/test-support": "0.0.1",
|
|
60
62
|
"@types/node": "^24.19.0",
|
|
61
63
|
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|
|
62
64
|
"@vitest/coverage-v8": "5.0.1",
|