@osdlc/command-seam 0.0.1-stage → 0.4.1
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/CHANGELOG.md +45 -51
- package/CONTRIBUTING.md +7 -3
- package/README.md +2 -2
- package/package.json +2 -3
- package/smoke.mjs +0 -61
package/CHANGELOG.md
CHANGED
|
@@ -1,55 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
`@sksizer/command-seam
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
keeps running.
|
|
49
|
-
- `unref` on the managed-process runner's options: the parent can exit while
|
|
50
|
-
the (usually `processGroup`) child keeps running. Such a group is not tracked
|
|
51
|
-
by `installGroupSignalForwarding`: a signal to the parent does not reach it
|
|
52
|
-
and the parent's exit does not `SIGKILL` it.
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4fa373f: The npm package no longer contains `smoke.mjs`. It was a check for this
|
|
8
|
+
repository's CI, not part of the API.
|
|
9
|
+
|
|
10
|
+
## 0.4.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- e30f155: **Breaking:** the package is now `@osdlc/command-seam`, renamed from `@sksizer/command-seam`. The repository moved from `sksizer/sdlc` to [osdlc/osdlc](https://github.com/osdlc/osdlc).
|
|
15
|
+
|
|
16
|
+
To move, replace `@sksizer/command-seam` with `@osdlc/command-seam` in `package.json` and in every import, including `@osdlc/command-seam/testing`. `@sksizer/command-seam` gets no further releases.
|
|
17
|
+
- e30f155: Add process-group signal forwarding and `unref` on the managed-process runner. These exports are experimental (see the README's Stability section).
|
|
18
|
+
|
|
19
|
+
- `installGroupSignalForwarding(opts?)`, with options type
|
|
20
|
+
`GroupSignalForwardingOptions`: opt-in. While a group started with
|
|
21
|
+
`processGroup: true` (async and managed runners) is live, `SIGINT`,
|
|
22
|
+
`SIGTERM` and `SIGHUP` send `SIGTERM` to every group, `SIGKILL` after
|
|
23
|
+
`graceMs` (default 1000), then exit with `128 + signal number`. A repeat
|
|
24
|
+
signal escalates to `SIGKILL` at once; an `exit` handler `SIGKILL`s groups
|
|
25
|
+
whose run is still in flight. Listeners are attached only while a group is
|
|
26
|
+
starting or live, ahead of the host's own. A host with its own listener,
|
|
27
|
+
`process.once` included, keeps the exit; its groups still get `SIGKILL`
|
|
28
|
+
after `graceMs`, or on exit if sooner and the run is in flight. Returns an
|
|
29
|
+
uninstall function. POSIX only; a no-op on Windows. `SIGKILL` of the host
|
|
30
|
+
still orphans its groups. When forwarding owns the exit, a new
|
|
31
|
+
`processGroup: true` run after the first forwarded signal is refused: it
|
|
32
|
+
starts nothing and settles with `reason: 'abort'`. The uninstall function
|
|
33
|
+
resets this.
|
|
34
|
+
- `forwardedSignal()`: the signal forwarded when forwarding owns the exit, or
|
|
35
|
+
`undefined`. A host that runs one command after another checks it to stop.
|
|
36
|
+
- `forwardedExitCode()`: `128 + signal number` for `forwardedSignal()`, or
|
|
37
|
+
`undefined`. A host that sets its own exit code keeps this one over it.
|
|
38
|
+
- `liveProcessGroups()`: the pids of the groups currently registered. A group
|
|
39
|
+
stays registered until it is empty, also after its run settles or its
|
|
40
|
+
leader exits, so a forwarded signal still kills a background member or one
|
|
41
|
+
waiting for the `SIGKILL` follow-up. The host's normal exit kills only the
|
|
42
|
+
groups whose run is still in flight, so a daemon a settled run left behind
|
|
43
|
+
keeps running.
|
|
44
|
+
- `unref` on the managed-process runner's options: the parent can exit while
|
|
45
|
+
the (usually `processGroup`) child keeps running. Such a group is not
|
|
46
|
+
tracked by `installGroupSignalForwarding`: a signal to the parent does not
|
|
47
|
+
reach it and the parent's exit does not `SIGKILL` it.
|
|
53
48
|
|
|
54
49
|
## [0.3.0] - 2026-10-15
|
|
55
50
|
|
|
@@ -324,5 +319,4 @@ entry.
|
|
|
324
319
|
exports. A breaking change to a stable export will be listed under
|
|
325
320
|
"Breaking".
|
|
326
321
|
|
|
327
|
-
[0.4.0]: https://github.com/osdlc/osdlc/tree/command-seam/v0.4.0/packages/ts/command-seam
|
|
328
322
|
[0.3.0]: https://github.com/sksizer/sdlc/tree/command-seam/v0.3.0/packages/command-seam
|
package/CONTRIBUTING.md
CHANGED
|
@@ -30,8 +30,9 @@ pnpm test
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Build before testing: the examples import the package by name, and under Node
|
|
33
|
-
that name resolves to `dist/`. After a build, `pnpm run smoke` runs
|
|
34
|
-
check of the built package under Node.
|
|
33
|
+
that name resolves to `dist/`. After a build, `pnpm run smoke` runs
|
|
34
|
+
`tests/smoke.mjs`, a short check of the built package under Node. It is not
|
|
35
|
+
published.
|
|
35
36
|
|
|
36
37
|
Other checks:
|
|
37
38
|
|
|
@@ -55,4 +56,7 @@ is installed.
|
|
|
55
56
|
code a user would write. The change is judged by how that code reads. See
|
|
56
57
|
[D-15](https://github.com/osdlc/osdlc/blob/main/packages/ts/command-seam/docs/planning/decisions/D-15-api-changes-start-from-consumer-code.md).
|
|
57
58
|
- Keep comments short. Say why, not what.
|
|
58
|
-
-
|
|
59
|
+
- For a change users can see, add a changeset: run `pnpm changeset` at the
|
|
60
|
+
repository root. Start a breaking change's summary with `**Breaking:**`.
|
|
61
|
+
Do not edit `CHANGELOG.md` or `version` by hand; the release writes them.
|
|
62
|
+
See [packages/README.md](https://github.com/osdlc/osdlc/blob/main/packages/README.md).
|
package/README.md
CHANGED
|
@@ -645,8 +645,8 @@ The package is at 0.x. Each export is stable or experimental.
|
|
|
645
645
|
- **Stable** exports are the runner contract: the runners, their options and
|
|
646
646
|
results, `CommandFailed`, and the helpers around them. The runners and
|
|
647
647
|
helpers are in use by other packages; a type that names their options or
|
|
648
|
-
results is stable with them. A breaking change to one is
|
|
649
|
-
|
|
648
|
+
results is stable with them. A breaking change to one is marked
|
|
649
|
+
**Breaking** in the CHANGELOG.
|
|
650
650
|
- **Experimental** exports are newer or have no user yet. Any release may
|
|
651
651
|
change or remove one. Each has an `@experimental` tag in its TSDoc.
|
|
652
652
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osdlc/command-seam",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Injectable subprocess runner for Node: capture, stream and managed-process runners, hard timeouts, failures as results.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SK Sizer",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"dist/",
|
|
52
|
-
"smoke.mjs",
|
|
53
52
|
"index.ts",
|
|
54
53
|
"sync.ts",
|
|
55
54
|
"async.ts",
|
|
@@ -88,7 +87,7 @@
|
|
|
88
87
|
"build": "tsc -p tsconfig.build.json",
|
|
89
88
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
90
89
|
"test": "bun test",
|
|
91
|
-
"smoke": "node smoke.mjs",
|
|
90
|
+
"smoke": "node tests/smoke.mjs",
|
|
92
91
|
"format": "prettier --write .",
|
|
93
92
|
"format:check": "prettier --check .",
|
|
94
93
|
"lint": "oxlint .",
|
package/smoke.mjs
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Runs the BUILT package under Node, which no other check does: `bun test`
|
|
2
|
-
// resolves the `bun` export condition to the TypeScript sources, so dist/ is
|
|
3
|
-
// compiled and never executed. A broken emit — an unrewritten `.ts` import, a
|
|
4
|
-
// dependency with no dist/ of its own — passes every other gate and fails on
|
|
5
|
-
// the consumer's machine.
|
|
6
|
-
//
|
|
7
|
-
// Deliberately not a test-runner suite: it must run from the extracted
|
|
8
|
-
// snapshot with only `dist/` and production dependencies present.
|
|
9
|
-
|
|
10
|
-
import { strict as assert } from 'node:assert'
|
|
11
|
-
import { spawnRunner, runChecked, CommandFailed, asAsync, withDefaults } from './dist/index.js'
|
|
12
|
-
import { fakeProcess, fakeRunner } from './dist/testing.js'
|
|
13
|
-
|
|
14
|
-
const runner = spawnRunner('node')
|
|
15
|
-
const ok = runner.run(['-e', "process.stdout.write('hi')"], { cwd: process.cwd() })
|
|
16
|
-
assert.equal(ok.exitCode, 0)
|
|
17
|
-
assert.equal(ok.stdout, 'hi')
|
|
18
|
-
|
|
19
|
-
const bad = runner.run(['-e', "process.stderr.write('boom'); process.exit(3)"], {
|
|
20
|
-
cwd: process.cwd(),
|
|
21
|
-
})
|
|
22
|
-
assert.equal(bad.exitCode, 3)
|
|
23
|
-
assert.match(bad.stderr, /boom/)
|
|
24
|
-
|
|
25
|
-
// `run` never throws; `runChecked` does.
|
|
26
|
-
assert.throws(
|
|
27
|
-
() => runChecked(runner, ['-e', 'process.exit(1)'], { cwd: process.cwd() }),
|
|
28
|
-
(e) => e instanceof CommandFailed && e.argv.length === 2,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
// detail joins both streams, empty ones dropped — the reason it exists is a
|
|
32
|
-
// command that explains itself on stdout while git reports failure on stderr.
|
|
33
|
-
const both = new CommandFailed(['x'], { exitCode: 1, stdout: 'why\n', stderr: 'what\n' })
|
|
34
|
-
assert.equal(both.detail, 'what\nwhy')
|
|
35
|
-
assert.equal(new CommandFailed(['x'], { exitCode: 1, stdout: 'only\n', stderr: '' }).detail, 'only')
|
|
36
|
-
|
|
37
|
-
// env merges over the parent environment.
|
|
38
|
-
const withEnv = runner.run(['-e', "process.stdout.write(process.env.SMOKE_VAR ?? '')"], {
|
|
39
|
-
cwd: process.cwd(),
|
|
40
|
-
env: { SMOKE_VAR: 'set' },
|
|
41
|
-
})
|
|
42
|
-
assert.equal(withEnv.stdout, 'set')
|
|
43
|
-
|
|
44
|
-
const async = asAsync(runner)
|
|
45
|
-
const viaAsync = await async.run(['-e', "process.stdout.write('async')"], { cwd: process.cwd() })
|
|
46
|
-
assert.equal(viaAsync.stdout, 'async')
|
|
47
|
-
|
|
48
|
-
// An invalid timeoutMs is a caller error.
|
|
49
|
-
assert.throws(() => runner.run(['-e', ''], { cwd: process.cwd(), timeoutMs: 0 }), RangeError)
|
|
50
|
-
|
|
51
|
-
const bound = withDefaults(runner, { cwd: process.cwd() })
|
|
52
|
-
assert.equal(bound.run(['-e', "process.stdout.write('bound')"]).stdout, 'bound')
|
|
53
|
-
|
|
54
|
-
// The /testing entry point, built.
|
|
55
|
-
const fake = fakeRunner({ 'rev-parse HEAD': { stdout: 'abc\n' } })
|
|
56
|
-
assert.equal(runChecked(fake.runner, ['rev-parse', 'HEAD'], { cwd: '/' }).stdout, 'abc\n')
|
|
57
|
-
const proc = fakeProcess()
|
|
58
|
-
proc.kill()
|
|
59
|
-
assert.equal((await proc.exited).reason, 'abort')
|
|
60
|
-
|
|
61
|
-
console.log('command-seam smoke: OK')
|