@executablemd/testing 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.
Files changed (94) hide show
  1. package/esm/{testing/npm/src/src → src}/assertions.js +15 -24
  2. package/esm/{testing/npm/src/src → src}/handlers.js +140 -10
  3. package/esm/{testing/npm/src/src → src}/vocabulary.js +3 -0
  4. package/package.json +3 -3
  5. package/types/src/assertions.d.ts +11 -1
  6. package/types/src/handlers.d.ts +1 -0
  7. package/esm/core/mod.js +0 -55
  8. package/esm/core/src/api.js +0 -31
  9. package/esm/core/src/collect.js +0 -24
  10. package/esm/core/src/component-api.js +0 -47
  11. package/esm/core/src/content-context.js +0 -23
  12. package/esm/core/src/deno-compiler.js +0 -51
  13. package/esm/core/src/errors.js +0 -13
  14. package/esm/core/src/eval-context.js +0 -13
  15. package/esm/core/src/eval-handler.js +0 -79
  16. package/esm/core/src/eval-interpolate.js +0 -63
  17. package/esm/core/src/eval-transform.js +0 -378
  18. package/esm/core/src/execute.js +0 -378
  19. package/esm/core/src/expand.js +0 -988
  20. package/esm/core/src/frontmatter.js +0 -98
  21. package/esm/core/src/heal.js +0 -29
  22. package/esm/core/src/interpolate.js +0 -39
  23. package/esm/core/src/modifiers/daemon.js +0 -60
  24. package/esm/core/src/modifiers/persist.js +0 -35
  25. package/esm/core/src/modifiers/timeout.js +0 -55
  26. package/esm/core/src/modifiers.js +0 -113
  27. package/esm/core/src/output/normalize.js +0 -33
  28. package/esm/core/src/output/terminal.js +0 -20
  29. package/esm/core/src/render.js +0 -30
  30. package/esm/core/src/replay-stream.js +0 -58
  31. package/esm/core/src/sample-api.js +0 -45
  32. package/esm/core/src/scanner.js +0 -632
  33. package/esm/core/src/temp-file-compiler.js +0 -68
  34. package/esm/core/src/types.js +0 -7
  35. package/esm/core/src/validate.js +0 -82
  36. package/esm/durable-streams/combinators.js +0 -227
  37. package/esm/durable-streams/context.js +0 -13
  38. package/esm/durable-streams/divergence.js +0 -56
  39. package/esm/durable-streams/each.js +0 -193
  40. package/esm/durable-streams/effect.js +0 -273
  41. package/esm/durable-streams/ephemeral.js +0 -80
  42. package/esm/durable-streams/errors.js +0 -92
  43. package/esm/durable-streams/http-stream.js +0 -207
  44. package/esm/durable-streams/mod.js +0 -34
  45. package/esm/durable-streams/operations.js +0 -88
  46. package/esm/durable-streams/replay-guard.js +0 -87
  47. package/esm/durable-streams/replay-index.js +0 -132
  48. package/esm/durable-streams/run.js +0 -137
  49. package/esm/durable-streams/serialize.js +0 -65
  50. package/esm/durable-streams/stream.js +0 -56
  51. package/esm/durable-streams/types.js +0 -10
  52. package/esm/runtime/apis.js +0 -237
  53. package/esm/runtime/find-free-port.js +0 -33
  54. package/esm/runtime/mod.js +0 -20
  55. /package/esm/{testing/npm/src/_dnt.polyfills.js → _dnt.polyfills.js} +0 -0
  56. /package/esm/{testing/npm/src/_dnt.shims.js → _dnt.shims.js} +0 -0
  57. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/almost_equals.js +0 -0
  58. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/array_includes.js +0 -0
  59. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/assert.js +0 -0
  60. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/assertion_error.js +0 -0
  61. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/equal.js +0 -0
  62. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/equals.js +0 -0
  63. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/exists.js +0 -0
  64. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/fail.js +0 -0
  65. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/false.js +0 -0
  66. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/greater.js +0 -0
  67. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/greater_or_equal.js +0 -0
  68. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/instance_of.js +0 -0
  69. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/is_error.js +0 -0
  70. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/less.js +0 -0
  71. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/less_or_equal.js +0 -0
  72. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/match.js +0 -0
  73. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/mod.js +0 -0
  74. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/not_equals.js +0 -0
  75. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/not_instance_of.js +0 -0
  76. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/not_match.js +0 -0
  77. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/not_strict_equals.js +0 -0
  78. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/object_match.js +0 -0
  79. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/rejects.js +0 -0
  80. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/strict_equals.js +0 -0
  81. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/string_includes.js +0 -0
  82. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/throws.js +0 -0
  83. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/unimplemented.js +0 -0
  84. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/assert/1.0.19/unreachable.js +0 -0
  85. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/build_message.js +0 -0
  86. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/diff.js +0 -0
  87. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/diff_str.js +0 -0
  88. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/format.js +0 -0
  89. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/styles.js +0 -0
  90. /package/esm/{testing/npm/src/deps → deps}/jsr.io/@std/internal/1.0.14/types.js +0 -0
  91. /package/esm/{testing/npm/src/mod.js → mod.js} +0 -0
  92. /package/esm/{testing/npm/src/src → src}/journal.js +0 -0
  93. /package/esm/{testing/npm/src/src → src}/test-api.js +0 -0
  94. /package/esm/{testing/npm/src/src → src}/use-testing.js +0 -0
@@ -1,378 +0,0 @@
1
- /**
2
- * Entry point — execute (spec §7).
3
- *
4
- * Wires together the boundary scanner, component import, expansion engine,
5
- * modifier system, and journal infrastructure. `execute` is delivered
6
- * through the Execution Api so extensions can decorate the execution
7
- * lifecycle as middleware.
8
- *
9
- * Journal runtime integration is concentrated at execution boundaries.
10
- * See DEC-005 in specs/decisions.md.
11
- */
12
- import { Err, Ok, scoped, spawn, withResolvers, until } from "effection";
13
- import { createApi } from "@effectionx/context-api";
14
- import { durableRun, createDurableOperation, ephemeral, } from "@executablemd/durable-streams";
15
- import { exec, readTextFile, stat, cwd } from "@executablemd/runtime";
16
- import { createReplayStream } from "./replay-stream.ts";
17
- import { useDenoCompiler } from "./deno-compiler.ts";
18
- import { useTempFileCompiler } from "./temp-file-compiler.ts";
19
- import { scanSegments } from "./scanner.ts";
20
- import { parseFrontmatter } from "./frontmatter.ts";
21
- import { expandSegments, expandBody, bodyHasOutput, validateOutputPlacement, createBlockCounter, } from "./expand.ts";
22
- import { Component, importComponent } from "./component-api.ts";
23
- import { renderSegment } from "./render.ts";
24
- import { DocumentOutput } from "./api.ts";
25
- import { composeModifierChain, buildCommand, createModifierRegistry, useCodeBlock, } from "./modifiers.ts";
26
- import { evalFactory } from "./eval-handler.ts";
27
- import { persistFactory } from "./modifiers/persist.ts";
28
- import { timeoutFactory } from "./modifiers/timeout.ts";
29
- import { daemonFactory } from "./modifiers/daemon.ts";
30
- import { useEvalScope } from "@effectionx/scope-eval";
31
- import { Stdio } from "@effectionx/process";
32
- // Re-export gray-matter — we use it for YAML frontmatter extraction
33
- import matter from "gray-matter";
34
- // ---------------------------------------------------------------------------
35
- // durableImportComponent (spec §4.3)
36
- //
37
- // This is a Workflow<ComponentDefinition | FunctionComponentDefinition> —
38
- // it yields durable import effects and returns either markdown or function
39
- // component definitions.
40
- // See DEC-001, DEC-004 in specs/decisions.md.
41
- // ---------------------------------------------------------------------------
42
- function* durableImportComponent(name, rootDocPath, searchPaths) {
43
- const result = (yield createDurableOperation({ type: "import_component", name }, function* () {
44
- // Resolve the path — runs inside Operation context
45
- let path;
46
- if (name === "__root__" && rootDocPath) {
47
- path = rootDocPath;
48
- }
49
- else {
50
- path = yield* resolveComponentPath(name, searchPaths);
51
- }
52
- const content = yield* readTextFile(path);
53
- return { path, content };
54
- }));
55
- // Function component: .ts file — import() the module
56
- if (result.path.endsWith(".ts")) {
57
- // Resolve to absolute path for dynamic import
58
- const currentDir = yield* ephemeral(cwd());
59
- const absolutePath = result.path.startsWith("/") ? result.path : `${currentDir}/${result.path}`;
60
- const mod = (yield* ephemeral(until(import(`file://${absolutePath}`))));
61
- const fn = mod.default;
62
- if (typeof fn !== "function") {
63
- throw new Error(`Function component "${name}" at ${result.path} must have a default export that is a generator function`);
64
- }
65
- const typedFn = fn;
66
- const inputs = (mod.inputs ?? {});
67
- return {
68
- kind: "function",
69
- name,
70
- path: result.path,
71
- inputs,
72
- fn: typedFn,
73
- };
74
- }
75
- // Markdown component: parse at runtime — deterministic from content
76
- const parsed = matter(result.content);
77
- const { meta, inputs } = parseFrontmatter(parsed.data);
78
- // The markdown body is a verbatim suffix of the raw file, so the body start
79
- // is computed by length — never by content search, which could false-match
80
- // body text repeated inside frontmatter. The invariant check turns any
81
- // gray-matter normalization surprise into a loud error instead of silently
82
- // wrong source positions.
83
- const bodyStart = result.content.length - parsed.content.length;
84
- if (result.content.slice(bodyStart) !== parsed.content) {
85
- throw new Error(`frontmatter parse did not preserve the markdown body verbatim: ${result.path}`);
86
- }
87
- let baseLine = 1;
88
- for (let i = 0; i < bodyStart; i++) {
89
- if (result.content[i] === "\n") {
90
- baseLine++;
91
- }
92
- }
93
- const bodySegments = scanSegments(parsed.content, {
94
- path: result.path,
95
- baseOffset: bodyStart,
96
- baseLine,
97
- });
98
- return {
99
- kind: "markdown",
100
- name,
101
- path: result.path,
102
- meta,
103
- inputs,
104
- bodySegments,
105
- };
106
- }
107
- // ---------------------------------------------------------------------------
108
- // Component path resolution — runs inside Operation context (not Workflow)
109
- // ---------------------------------------------------------------------------
110
- function* resolveComponentPath(name, searchPaths) {
111
- const baseName = name.replace(/\./g, "/");
112
- for (const dir of searchPaths) {
113
- // Try {dir}/{Name}.md (backward compat — .md wins over .ts)
114
- const mdCandidate = normalizePath(dir === "." ? `${baseName}.md` : `${dir}/${baseName}.md`);
115
- const mdStat = yield* stat(mdCandidate);
116
- if (mdStat.exists && mdStat.isFile) {
117
- return mdCandidate;
118
- }
119
- // Try {dir}/{Name}.ts (function component)
120
- const tsCandidate = normalizePath(dir === "." ? `${baseName}.ts` : `${dir}/${baseName}.ts`);
121
- const tsStat = yield* stat(tsCandidate);
122
- if (tsStat.exists && tsStat.isFile) {
123
- return tsCandidate;
124
- }
125
- // Try {dir}/{Name}/index.md
126
- const indexMdCandidate = normalizePath(dir === "." ? `${baseName}/index.md` : `${dir}/${baseName}/index.md`);
127
- const indexMdStat = yield* stat(indexMdCandidate);
128
- if (indexMdStat.exists && indexMdStat.isFile) {
129
- return indexMdCandidate;
130
- }
131
- // Try {dir}/{Name}/index.ts
132
- const indexTsCandidate = normalizePath(dir === "." ? `${baseName}/index.ts` : `${dir}/${baseName}/index.ts`);
133
- const indexTsStat = yield* stat(indexTsCandidate);
134
- if (indexTsStat.exists && indexTsStat.isFile) {
135
- return indexTsCandidate;
136
- }
137
- }
138
- throw new Error(`Cannot resolve component: ${name} (searched: ${searchPaths.join(", ")})`);
139
- }
140
- /** Strip leading ./ from paths for workspace-relative normalization. */
141
- function normalizePath(path) {
142
- return path.replace(/^\.\//, "");
143
- }
144
- // ---------------------------------------------------------------------------
145
- // Built-in modifier handlers (spec §3.3)
146
- //
147
- // Modifier handlers return Workflow<CodeBlockResult> since terminal
148
- // handlers (exec) yield DurableEffects. See DEC-003 in specs/decisions.md.
149
- // ---------------------------------------------------------------------------
150
- const execFactory = (_params) => (_args, _next) => (function* () {
151
- const context = yield* useCodeBlock();
152
- const command = buildCommand(context.language, context.content);
153
- const result = (yield createDurableOperation({
154
- type: "exec",
155
- name: `exec:${context.content.slice(0, 40).replace(/\n/g, " ")}`,
156
- command: command,
157
- }, function* () {
158
- const execResult = yield* exec({
159
- command,
160
- timeout: 30_000,
161
- });
162
- return execResult;
163
- }));
164
- return {
165
- output: result.stdout,
166
- exitCode: result.exitCode,
167
- stderr: result.stderr,
168
- };
169
- })();
170
- const silentFactory = (_params) => (_args, next) => (function* () {
171
- yield* next(); // inner chain runs — exec journals its result
172
- return { output: "", exitCode: 0, stderr: "" };
173
- })();
174
- // ---------------------------------------------------------------------------
175
- // Document workflow (spec §7.1)
176
- //
177
- // This is a Workflow<string> — it yields DurableEffects (via
178
- // durableImportComponent, exec handler). Non-journaled work
179
- // (interpolation, validation, parsing) runs as synchronous function
180
- // calls inside the expansion engine. See DEC-002.
181
- // ---------------------------------------------------------------------------
182
- function* documentWorkflow() {
183
- // Import root — same pipeline as any component. The provider middleware
184
- // installed by execute maps "__root__" to the document path. The
185
- // ephemeral() wrapper bridges typing only — the import inside remains a
186
- // durable, journaled operation.
187
- const root = yield* ephemeral(importComponent("__root__"));
188
- if (root.kind === "function") {
189
- throw new Error("Root document must be a markdown file, not a function component");
190
- }
191
- const rootEnv = { values: {} };
192
- // Per-root-segment emission loop (spec §9).
193
- // Mutable counter preserves deterministic blockIds across
194
- // per-segment expansion calls (see spec §6.1).
195
- const counter = createBlockCounter();
196
- // The root binding environment is installed as scope-local middleware
197
- // around the entire loop so all segments share it. Resources spawned by
198
- // `persist` blocks are retained in the eval scope until expansion
199
- // completes, then torn down.
200
- const scopedExpansion = scoped(function* () {
201
- yield* Component.around({ env: () => rootEnv }, { at: "min" });
202
- // Structural preflight (spec §6.9): a root with misplaced <Output>
203
- // executes no body side effects; the aggregate diagnostic renders as a
204
- // comment (root policy is "collect").
205
- const placementError = validateOutputPlacement(root.bodySegments);
206
- if (placementError) {
207
- const text = renderSegment(placementError);
208
- yield* ephemeral(DocumentOutput.operations.output(text));
209
- return text;
210
- }
211
- // A root declaring top-level <Output> buffers completely (spec §5.4):
212
- // execute the whole body, then emit the selected regions only after
213
- // successful completion. A documentation failure throws before any emit,
214
- // so no partial output is produced.
215
- if (bodyHasOutput(root.bodySegments)) {
216
- const expanded = yield* expandBody(root.bodySegments, [], root.meta, {}, new Set(), counter, undefined);
217
- const text = expanded.map(renderSegment).join("");
218
- // An empty buffered root emits no output event.
219
- if (text) {
220
- yield* ephemeral(DocumentOutput.operations.output(text));
221
- }
222
- return text;
223
- }
224
- // Per-root-segment emission loop for roots without <Output> (spec §5.4).
225
- const chunks = [];
226
- for (const segment of root.bodySegments) {
227
- const expanded = yield* expandSegments([segment], root.meta, {}, new Set(), counter);
228
- for (const resolved of expanded) {
229
- const text = renderSegment(resolved);
230
- if (text) {
231
- // Emit through the Document Output Api (spec §9).
232
- // ephemeral() bridges from Workflow (durable) to Operation
233
- // (non-durable) — output emission is a derived side effect,
234
- // not journaled.
235
- yield* ephemeral(DocumentOutput.operations.output(text));
236
- chunks.push(text);
237
- }
238
- }
239
- }
240
- return chunks.join("");
241
- });
242
- return yield* ephemeral(scopedExpansion);
243
- }
244
- /**
245
- * Execute a markdown document as a durable workflow.
246
- *
247
- * Returns a `DocumentExecution` — an operation you can `yield*` for the
248
- * completion `Result<string>`, with a `.output` stream for chunk-by-chunk
249
- * consumption. Once the handle exists, completion never throws: every
250
- * later failure — document, infrastructure, or policy middleware — closes
251
- * `output` (with the complete or partial rendered text) and resolves
252
- * `Err(error)`.
253
- *
254
- * Simple — get the outcome:
255
- *
256
- * ```ts
257
- * const execution = yield* execute(options);
258
- * const result = yield* execution;
259
- * if (result.ok) {
260
- * console.log(result.value);
261
- * }
262
- * ```
263
- *
264
- * Streaming — consume chunks as they arrive:
265
- *
266
- * ```ts
267
- * const execution = yield* execute(options);
268
- * const output = yield* forEach(function* (chunk) {
269
- * process.stdout.write(chunk);
270
- * }, execution.output);
271
- * ```
272
- */
273
- function* executeDocument(options) {
274
- const { docPath, stream, componentDirs = ["components", "."], modifiers: customModifiers = {}, } = options;
275
- // Build modifier registry — pure data, no scope side effects.
276
- const registry = createModifierRegistry();
277
- registry.set("exec", execFactory);
278
- registry.set("silent", silentFactory);
279
- registry.set("eval", evalFactory);
280
- registry.set("persist", persistFactory);
281
- registry.set("timeout", timeoutFactory);
282
- registry.set("daemon", daemonFactory);
283
- for (const [name, handler] of Object.entries(customModifiers)) {
284
- registry.set(name, handler);
285
- }
286
- // ---------------------------------------------------------------------------
287
- // Document execution.
288
- //
289
- // The workflow runs in a spawned child scope that contains all
290
- // execution state: eval context/scope and the
291
- // DocumentOutput→channel bridge. Nothing leaks onto the caller's scope.
292
- //
293
- // withResolvers captures the completion result so `yield* execution`
294
- // can wait for it without needing to await the spawned Task directly
295
- // (which would propagate errors through scope teardown).
296
- // ---------------------------------------------------------------------------
297
- // Replay-safe transport: late subscribers receive every chunk and the
298
- // close value, so subscription readiness before first emission is never
299
- // required (spec §9; see replay-stream.ts).
300
- const channel = createReplayStream();
301
- const { operation, resolve } = withResolvers();
302
- yield* spawn(function* () {
303
- let emitted = false;
304
- let emittedText = "";
305
- // The ENTIRE setup and workflow sit inside one error boundary: once the
306
- // handle exists, any failure closes output (with whatever rendered so
307
- // far) and resolves Err — completion never throws.
308
- try {
309
- // Install platform-appropriate compiler middleware
310
- // deno-lint-ignore no-explicit-any
311
- if (typeof globalThis.Deno !== "undefined") {
312
- yield* useDenoCompiler();
313
- }
314
- else {
315
- yield* useTempFileCompiler();
316
- }
317
- // DocumentOutput → channel bridge (innermost middleware — output flows
318
- // through caller-installed normalize/terminal middleware first, then here).
319
- yield* DocumentOutput.around({
320
- *output([text]) {
321
- emitted = true;
322
- emittedText += text;
323
- yield* channel.send(text);
324
- },
325
- });
326
- yield* Stdio.around({
327
- *stdout() { },
328
- *stderr() { },
329
- });
330
- // Create per-document eval scope (spec §3.1).
331
- // Created in the same scope as durableRun so that DurableCtx
332
- // (set by durableRun) is visible to eval code that calls
333
- // renderChildren → importComponent → createDurableOperation.
334
- const rootEvalScope = yield* useEvalScope();
335
- // Install the document's runtime Component providers before durableRun
336
- // so the workflow inherits them: component import, modifier execution,
337
- // and the root eval scope.
338
- yield* Component.around({
339
- *importComponent([name], _next) {
340
- return yield* durableImportComponent(name, name === "__root__" ? docPath : undefined, componentDirs);
341
- },
342
- *applyModifiers([modifiers, context], _next) {
343
- const chain = composeModifierChain(modifiers, context, registry);
344
- return yield* chain();
345
- },
346
- evalScope: () => rootEvalScope,
347
- }, { at: "min" });
348
- const output = yield* durableRun(() => documentWorkflow(), {
349
- stream,
350
- });
351
- // Preserve output for any synchronous completion path that did not emit
352
- // through the streaming API.
353
- if (!emitted && output) {
354
- yield* DocumentOutput.operations.output(output);
355
- }
356
- yield* channel.close(output);
357
- resolve(Ok(output));
358
- }
359
- catch (error) {
360
- // Close with everything already emitted — diagnostics produced before
361
- // an abort stay visible to consumers of the close value.
362
- yield* channel.close(emittedText);
363
- resolve(Err(error instanceof Error ? error : new Error(String(error))));
364
- }
365
- });
366
- return {
367
- *[Symbol.iterator]() {
368
- return yield* operation;
369
- },
370
- output: channel,
371
- };
372
- }
373
- export const Execution = createApi("Execution", {
374
- *execute(options) {
375
- return yield* executeDocument(options);
376
- },
377
- });
378
- export const { execute } = Execution.operations;