omkit 0.8.22 → 0.8.23

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.
@@ -1,6 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import path from "node:path";
3
- import { action } from "../core/action.js";
3
+ import { action, launchedFrom } from "../core/action.js";
4
+ import { callerSite } from "../foundation/callsite.js";
4
5
  import { debuggerFreeEnv, killTree, streamLines } from "../system/proc.js";
5
6
  /**
6
7
  * Build a zx tagged template that runs `cmd` verbatim.
@@ -24,19 +25,31 @@ export function commandName(cmdOrFile, options = {}) {
24
25
  export function command(cmdOrFile, options = {}) {
25
26
  const cwd = path.resolve(options.cwd ?? ".");
26
27
  const name = commandName(cmdOrFile, options);
28
+ // Built per call, so each launch below is in this file; record the user's line instead.
29
+ const site = callerSite();
30
+ // What the run record shows as this call's args. `env` is left out on purpose: it is a whole
31
+ // environment, secrets included, and the record is written to disk and into the report.
32
+ const shown = [
33
+ cmdOrFile,
34
+ {
35
+ ...(options.args && { args: options.args }),
36
+ ...(options.cwd && { cwd: options.cwd }),
37
+ ...(options.inheritDebugger && { inheritDebugger: true }),
38
+ },
39
+ ];
27
40
  // Shell command — the command string is the action (and proc) name.
28
41
  if (options.args === undefined) {
29
- return action(name).run((ctx) => ctx.proc(cmdOrFile, {
42
+ return launchedFrom(site, () => action(name).run((ctx, ..._shown) => ctx.proc(cmdOrFile, {
30
43
  cwd,
31
44
  ...(options.env && { env: options.env }),
32
45
  ...(options.inheritDebugger && {
33
46
  inheritDebugger: true,
34
47
  }),
35
- })(verbatim(cmdOrFile)))();
48
+ })(verbatim(cmdOrFile)))(...shown));
36
49
  }
37
50
  // Direct executable — the file plus its args form the action name.
38
51
  const args = options.args;
39
- return action(name).run(async (ctx) => {
52
+ return launchedFrom(site, () => action(name).run(async (ctx, ..._shown) => {
40
53
  const inherit = options.inheritDebugger;
41
54
  const env = inherit
42
55
  ? (options.env ?? process.env)
@@ -83,5 +96,5 @@ export function command(cmdOrFile, options = {}) {
83
96
  finished = true;
84
97
  ctx.signal.removeEventListener("abort", onAbort);
85
98
  }
86
- })();
99
+ })(...shown));
87
100
  }
@@ -1,5 +1,6 @@
1
1
  import { setTimeout as sleep } from "node:timers/promises";
2
- import { action } from "../core/action.js";
2
+ import { action, launchedFrom } from "../core/action.js";
3
+ import { callerSite } from "../foundation/callsite.js";
3
4
  /**
4
5
  * Gate: calls `predicate` every `intervalMs` until it returns something other than `false`,
5
6
  * `null` or `undefined`, and resolves with that value — so it can find a thing as well as wait
@@ -9,7 +10,11 @@ import { action } from "../core/action.js";
9
10
  */
10
11
  export function pollUntil(description, predicate, options = {}) {
11
12
  const { intervalMs = 200, timeoutMs = 30_000 } = options;
12
- return action(description).run(async ({ signal }) => {
13
+ // Built per call, so the launch below is in this file; record the user's line instead.
14
+ const site = callerSite();
15
+ return launchedFrom(site, () =>
16
+ // The call's own arguments go through the launch, so the run record shows them as args.
17
+ action(description).run(async ({ signal }, ..._call) => {
13
18
  const deadline = Date.now() + timeoutMs;
14
19
  let attempts = 0;
15
20
  for (;;) {
@@ -26,5 +31,5 @@ export function pollUntil(description, predicate, options = {}) {
26
31
  }
27
32
  await sleep(Math.min(intervalMs, remaining), undefined, { signal });
28
33
  }
29
- })();
34
+ })(description, predicate, options));
30
35
  }
@@ -1,4 +1,12 @@
1
1
  import type { ActionBuilderEvents, NoEvents } from "./types.ts";
2
+ /**
3
+ * Run `launch` (which must launch exactly one action, synchronously) with `site` recorded as
4
+ * that launch's `calledAt`. For batteries that build their action per call inside a factory
5
+ * (`command`, `pollUntil`): the launch then happens in omkit's own file, so the frame the
6
+ * launch would record is the factory, not the user's line. The factory captures its caller
7
+ * with `callerSite()` and hands it over here.
8
+ */
9
+ export declare function launchedFrom<T>(site: string | undefined, launch: () => T): T;
2
10
  /**
3
11
  * Define an action. Provide the impl via `.run(…)`, optionally after declaring
4
12
  * metadata (`.describe(…)`), events (`.emits<…>()`), an imperative handle
@@ -2,6 +2,29 @@ import { ExecutionTree } from "./ExecutionTree.js";
2
2
  import { callerSite } from "../foundation/callsite.js";
3
3
  import { describeSchema } from "./discovery-mode.js";
4
4
  import { formatIssues } from "./args.js";
5
+ /** A call site handed over by {@link launchedFrom}, taken by the very next launch. */
6
+ let handedSite;
7
+ const takeHandedSite = () => {
8
+ const site = handedSite;
9
+ handedSite = undefined;
10
+ return site;
11
+ };
12
+ /**
13
+ * Run `launch` (which must launch exactly one action, synchronously) with `site` recorded as
14
+ * that launch's `calledAt`. For batteries that build their action per call inside a factory
15
+ * (`command`, `pollUntil`): the launch then happens in omkit's own file, so the frame the
16
+ * launch would record is the factory, not the user's line. The factory captures its caller
17
+ * with `callerSite()` and hands it over here.
18
+ */
19
+ export function launchedFrom(site, launch) {
20
+ handedSite = site;
21
+ try {
22
+ return launch();
23
+ }
24
+ finally {
25
+ handedSite = undefined;
26
+ }
27
+ }
5
28
  /**
6
29
  * The builder returned by `action(name)`: declare metadata (`.describe(…)`), events
7
30
  * (`.emits<…>()`), an imperative handle (`.ref<…>()`), and/or pin the args type
@@ -56,7 +79,7 @@ class Builder {
56
79
  const exec = body;
57
80
  const create = (...args) => {
58
81
  // where this launch was written — for a battery, the user's call rather than its definition
59
- const calledAt = callerSite();
82
+ const calledAt = takeHandedSite() ?? callerSite();
60
83
  const spec = { name, args, tags: [], body: exec, definedAt, calledAt, meta };
61
84
  return ExecutionTree.require().launch(spec);
62
85
  };
@@ -124,7 +147,7 @@ class ArgsBuilder {
124
147
  const exec = body;
125
148
  const create = (...args) => {
126
149
  // where this launch was written — for a battery, the user's call rather than its definition
127
- const calledAt = callerSite();
150
+ const calledAt = takeHandedSite() ?? callerSite();
128
151
  const spec = { name, args, tags: [], body: exec, definedAt, calledAt, meta };
129
152
  return ExecutionTree.require().launch(spec);
130
153
  };
@@ -752,7 +752,8 @@
752
752
  const need = text.length * 6.6 + 14;
753
753
  const inside = w >= need;
754
754
  const clipX = Math.max(x0, 0);
755
- const outside = !inside && w <= 60;
755
+ // outside, too, when inside would cut it to a stub (`port =…`): a call label needs room
756
+ const outside = !inside && w < 16 * 6.6 + 14;
756
757
  const shown = inside || outside ? text : truncate(text, (w - 14) / 6.6);
757
758
  const tw = shown.length * 6.6;
758
759
  // an outside label that would run off the chart hangs left of its bar instead
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omkit",
3
- "version": "0.8.22",
3
+ "version": "0.8.23",
4
4
  "description": "A tiny runtime for developer workflows: typed actions that pass runtime capabilities and emit typed events, under one run and one shared log. The operational/runtime layer to tskb's knowledge layer.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",