@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 CHANGED
@@ -1,55 +1,50 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/).
7
-
8
- ## [0.4.0] - 2026-10-16
9
-
10
- The package is renamed, and the repository moved from `sksizer/sdlc` to
11
- [osdlc/osdlc](https://github.com/osdlc/osdlc).
12
-
13
- ### Breaking
14
-
15
- - The package is now `@osdlc/command-seam`, renamed from
16
- `@sksizer/command-seam`. To move, replace `@sksizer/command-seam` with
17
- `@osdlc/command-seam` in `package.json` and in every import, including
18
- `@osdlc/command-seam/testing`. `@sksizer/command-seam` gets no further
19
- releases.
20
-
21
- ### Added
22
-
23
- These exports are experimental (see the README's Stability section).
24
-
25
- - `installGroupSignalForwarding(opts?)`, with options type
26
- `GroupSignalForwardingOptions`: opt-in. While a group started with
27
- `processGroup: true` (async and managed runners) is live, `SIGINT`,
28
- `SIGTERM` and `SIGHUP` send `SIGTERM` to every group, `SIGKILL` after
29
- `graceMs` (default 1000), then exit with `128 + signal number`. A repeat
30
- signal escalates to `SIGKILL` at once; an `exit` handler `SIGKILL`s groups
31
- whose run is still in flight. Listeners are attached only while a group is
32
- starting or live, ahead of the host's own. A host with its own listener, `process.once`
33
- included, keeps the exit; its groups still get `SIGKILL` after `graceMs`, or
34
- on exit if sooner and the run is in flight. Returns an uninstall function.
35
- POSIX only; a no-op on Windows. `SIGKILL` of the host still orphans its groups. When
36
- forwarding owns the exit, a new `processGroup: true` run after the first
37
- forwarded signal is refused: it starts nothing and settles with
38
- `reason: 'abort'`. The uninstall function resets this.
39
- - `forwardedSignal()`: the signal forwarded when forwarding owns the exit, or
40
- `undefined`. A host that runs one command after another checks it to stop.
41
- - `forwardedExitCode()`: `128 + signal number` for `forwardedSignal()`, or
42
- `undefined`. A host that sets its own exit code keeps this one over it.
43
- - `liveProcessGroups()`: the pids of the groups currently registered. A group
44
- stays registered until it is empty, also after its run settles or its
45
- leader exits, so a forwarded signal still kills a background member or one
46
- waiting for the `SIGKILL` follow-up. The host's normal exit kills only the
47
- groups whose run is still in flight, so a daemon a settled run left behind
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 a short
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
- - Update `CHANGELOG.md` for changes users can see.
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 listed under
649
- "Breaking" in the CHANGELOG.
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.0.1-stage",
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')