@executablemd/cli 0.3.1 → 0.4.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/esm/deno.js CHANGED
@@ -1,19 +1,22 @@
1
1
  export default {
2
2
  "name": "@executablemd/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "exports": "./src/cli.ts",
5
5
  "imports": {
6
6
  "@types/node": "npm:@types/node@^24.5.2",
7
7
  "@durable-streams/client": "npm:@durable-streams/client@^0.2.2",
8
- "@effectionx/bdd/node": "file:///home/runner/work/executable.md/executable.md/test-support/bdd.ts",
9
- "@effectionx/bdd/expect": "file:///home/runner/work/executable.md/executable.md/test-support/expect.ts",
8
+ "@effectionx/bdd/node": "file:///Users/tarasmankovski/Repositories/taras/executable-markdown-agents/.claude/worktrees/fix-dnt-jsr-registry/test-support/bdd.ts",
9
+ "@effectionx/bdd/expect": "file:///Users/tarasmankovski/Repositories/taras/executable-markdown-agents/.claude/worktrees/fix-dnt-jsr-registry/test-support/expect.ts",
10
10
  "@effectionx/test-adapter": "npm:@effectionx/test-adapter@0.7.4",
11
11
  "effection": "npm:effection@4.1.0-alpha.7",
12
12
  "effection/experimental": "npm:effection@4.1.0-alpha.7/experimental",
13
- "@executablemd/durable-streams": "npm:@executablemd/durable-streams@^0.3.1",
14
- "@executablemd/runtime": "npm:@executablemd/runtime@^0.3.1",
15
- "@executablemd/runtime/test": "file:///home/runner/work/executable.md/executable.md/runtime/test/mod.ts",
16
- "@executablemd/code-review-agent": "npm:@executablemd/code-review-agent@^0.3.1",
13
+ "@executablemd/durable-streams": "npm:@executablemd/durable-streams@^0.4.0",
14
+ "@executablemd/runtime": "npm:@executablemd/runtime@^0.4.0",
15
+ "@executablemd/testing": "npm:@executablemd/testing@^0.4.0",
16
+ "@std/assert": "jsr:@std/assert@^1.0.17",
17
+ "ajv": "npm:ajv@^8.17.1",
18
+ "@executablemd/runtime/test": "file:///Users/tarasmankovski/Repositories/taras/executable-markdown-agents/.claude/worktrees/fix-dnt-jsr-registry/runtime/test/mod.ts",
19
+ "@executablemd/code-review-agent": "npm:@executablemd/code-review-agent@^0.4.0",
17
20
  "@effectionx/context-api": "npm:@effectionx/context-api@0.6.0",
18
21
  "@effectionx/converge": "npm:@effectionx/converge@0.1.4",
19
22
  "@effectionx/fetch": "npm:@effectionx/fetch@0.2.0",
@@ -32,10 +35,11 @@ export default {
32
35
  "oxlint-tsgolint": "npm:oxlint-tsgolint@^0.17",
33
36
  "unist-util-select": "npm:unist-util-select@^5",
34
37
  "mdast-util-to-string": "npm:mdast-util-to-string@^4",
35
- "@executablemd/core": "npm:@executablemd/core@^0.3.1",
36
- "@executablemd/core/": "npm:@executablemd/core@^0.3.1/",
37
- "@executablemd/durable-streams/": "npm:@executablemd/durable-streams@^0.3.1/",
38
- "@executablemd/runtime/": "npm:@executablemd/runtime@^0.3.1/",
39
- "@executablemd/code-review-agent/": "npm:@executablemd/code-review-agent@^0.3.1/"
38
+ "@executablemd/core": "npm:@executablemd/core@^0.4.0",
39
+ "@executablemd/core/": "npm:@executablemd/core@^0.4.0/",
40
+ "@executablemd/durable-streams/": "npm:@executablemd/durable-streams@^0.4.0/",
41
+ "@executablemd/runtime/": "npm:@executablemd/runtime@^0.4.0/",
42
+ "@executablemd/testing/": "npm:@executablemd/testing@^0.4.0/",
43
+ "@executablemd/code-review-agent/": "npm:@executablemd/code-review-agent@^0.4.0/"
40
44
  }
41
45
  };
package/esm/src/cli.js CHANGED
@@ -19,16 +19,11 @@ import { inspect } from "node:util";
19
19
  import process from "node:process";
20
20
  import { program, object, field, cli, commands } from "configliere";
21
21
  import { z } from "zod";
22
- import { runDocument, useNormalizedOutput, useTerminalOutput } from "@executablemd/core";
22
+ import { execute, useNormalizedOutput, useTerminalOutput } from "@executablemd/core";
23
+ import { installTestingVocabulary, TestFailureError, useTesting } from "@executablemd/testing";
23
24
  import { FileStream } from "./file-stream.js";
24
25
  import denoJson from "../deno.js";
25
- // ---------------------------------------------------------------------------
26
- // Workaround: field.default exists at runtime but is missing from the .d.ts
27
- // ---------------------------------------------------------------------------
28
26
  const defaults = (value) => (mods) => ({ ...mods, default: value });
29
- // ---------------------------------------------------------------------------
30
- // Program schema
31
- // ---------------------------------------------------------------------------
32
27
  const runConfig = object({
33
28
  docPath: {
34
29
  description: "markdown document to execute",
@@ -53,14 +48,35 @@ const runConfig = object({
53
48
  ...field(z.boolean(), defaults(false)),
54
49
  },
55
50
  });
51
+ const testConfig = object({
52
+ docPath: {
53
+ description: "markdown document to test",
54
+ ...field(z.string(), cli.argument()),
55
+ },
56
+ componentDir: {
57
+ description: "component search directory",
58
+ ...field(z.array(z.string()), defaults(["components", "."]), field.array()),
59
+ },
60
+ verbose: {
61
+ description: "log journal entries to stderr",
62
+ aliases: ["-V"],
63
+ ...field(z.boolean(), defaults(false)),
64
+ },
65
+ journal: {
66
+ description: "write a diagnostic JSONL trace (path must not exist)",
67
+ aliases: ["-j"],
68
+ ...field(z.string().optional()),
69
+ },
70
+ raw: {
71
+ description: "output raw markdown without normalization or terminal formatting",
72
+ ...field(z.boolean(), defaults(false)),
73
+ },
74
+ });
56
75
  const xmd = program({
57
76
  name: "xmd",
58
77
  version: denoJson.version,
59
- config: commands({ run: runConfig }, { default: "run" }),
78
+ config: commands({ run: runConfig, test: testConfig }, { default: "run" }),
60
79
  });
61
- // ---------------------------------------------------------------------------
62
- // Journal entry formatting
63
- // ---------------------------------------------------------------------------
64
80
  const pretty = (value) => inspect(value, {
65
81
  colors: true,
66
82
  compact: true,
@@ -97,9 +113,6 @@ function summarizeEvent(event) {
97
113
  const detail = status === "err" && "error" in event.result ? ` (${event.result.error.message})` : "";
98
114
  return `[close] ${event.coroutineId} → ${status}${detail}`;
99
115
  }
100
- // ---------------------------------------------------------------------------
101
- // Document runner
102
- // ---------------------------------------------------------------------------
103
116
  function* createJournalFile(filePath) {
104
117
  let handle;
105
118
  try {
@@ -115,7 +128,7 @@ function* createJournalFile(filePath) {
115
128
  }
116
129
  yield* until(handle.close());
117
130
  }
118
- function* run(config) {
131
+ function* run(config, mode) {
119
132
  const { docPath, componentDir, verbose, journal, raw } = config;
120
133
  // Every CLI invocation starts from an empty stream. --journal writes
121
134
  // current-run diagnostics only; existing traces are never loaded.
@@ -147,22 +160,23 @@ function* run(config) {
147
160
  }
148
161
  })
149
162
  : spawn(function* () { });
150
- // ---------------------------------------------------------------------------
151
- // Output middleware (spec §9).
152
- //
153
- // Middleware is installed on the DocumentOutput Api via Api.around() before
154
- // runDocument is called. runDocument owns the channel internally —
155
- // the CLI just installs transformations and consumes the returned stream.
156
- // ---------------------------------------------------------------------------
157
163
  if (!raw) {
158
164
  yield* useNormalizedOutput();
159
165
  }
160
166
  if (process.stdout.isTTY && !raw) {
161
167
  yield* useTerminalOutput();
162
168
  }
163
- // Run the document returns a DocumentExecution.
164
- // yield* execution waits for completion. execution.output streams chunks.
165
- const execution = yield* runDocument({
169
+ // Compose testing around the single core execution entrypoint: both
170
+ // commands register the vocabulary (assertions work in regular documents,
171
+ // explicit <Testing> boundaries affect the outcome), while `xmd test`
172
+ // additionally activates root testing through a useTesting() session.
173
+ if (mode.testing) {
174
+ yield* useTesting({ verbose });
175
+ }
176
+ else {
177
+ yield* installTestingVocabulary({ verbose });
178
+ }
179
+ const execution = yield* execute({
166
180
  docPath,
167
181
  stream,
168
182
  componentDirs: componentDir,
@@ -184,10 +198,19 @@ function* run(config) {
184
198
  signal.close();
185
199
  yield* writer;
186
200
  }
201
+ // Inspect the completion Result AFTER the report finished streaming:
202
+ // test failures, assertion aborts, and any document abort exit nonzero.
203
+ const result = yield* execution;
204
+ if (!result.ok) {
205
+ if (result.error instanceof TestFailureError) {
206
+ console.error(`\ntests failed: ${result.error.message}`);
207
+ }
208
+ else {
209
+ console.error(result.error.message);
210
+ }
211
+ yield* exit(1);
212
+ }
187
213
  }
188
- // ---------------------------------------------------------------------------
189
- // Entry point
190
- // ---------------------------------------------------------------------------
191
214
  await main(function* (args) {
192
215
  const parser = xmd.createParser({ args });
193
216
  switch (parser.type) {
@@ -208,7 +231,10 @@ await main(function* (args) {
208
231
  }
209
232
  switch (parsed.value.name) {
210
233
  case "run":
211
- yield* run(parsed.value.config);
234
+ yield* run(parsed.value.config, { testing: false });
235
+ break;
236
+ case "test":
237
+ yield* run(parsed.value.config, { testing: true });
212
238
  break;
213
239
  }
214
240
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@executablemd/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "The xmd command-line interface for executable.md.",
5
5
  "homepage": "https://executable.md",
6
6
  "repository": {
@@ -17,8 +17,9 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@effectionx/stream-helpers": "0.8.3",
20
- "@executablemd/core": "^0.3.1",
21
- "@executablemd/durable-streams": "^0.3.1",
20
+ "@executablemd/core": "^0.4.0",
21
+ "@executablemd/durable-streams": "^0.4.0",
22
+ "@executablemd/testing": "^0.4.0",
22
23
  "configliere": "^0.2.3",
23
24
  "effection": "4.1.0-alpha.7",
24
25
  "zod": "^4.3.6"
package/types/deno.d.ts CHANGED
@@ -12,6 +12,9 @@ declare namespace _default {
12
12
  "effection/experimental": string;
13
13
  "@executablemd/durable-streams": string;
14
14
  "@executablemd/runtime": string;
15
+ "@executablemd/testing": string;
16
+ "@std/assert": string;
17
+ ajv: string;
15
18
  "@executablemd/runtime/test": string;
16
19
  "@executablemd/code-review-agent": string;
17
20
  "@effectionx/context-api": string;
@@ -36,6 +39,7 @@ declare namespace _default {
36
39
  "@executablemd/core/": string;
37
40
  "@executablemd/durable-streams/": string;
38
41
  "@executablemd/runtime/": string;
42
+ "@executablemd/testing/": string;
39
43
  "@executablemd/code-review-agent/": string;
40
44
  };
41
45
  }