@0disoft/laqu
Advanced tools
+43
| # Changelog | ||
| Notable user-facing changes to `@0disoft/laqu` are recorded here. The project follows | ||
| [Semantic Versioning](docs/library/semver.md). | ||
| ## [1.1.9] - 2026-08-13 | ||
| ### Added | ||
| - Added runnable examples for clean stdout, nested task trees, and versioned NDJSON events. | ||
| - Added a terminal preview and direct links to API, compatibility, migration, contribution, and | ||
| security documentation. | ||
| - Added contribution guidance, private vulnerability-reporting guidance, and structured GitHub | ||
| issue forms. | ||
| ### Changed | ||
| - Reworked the README around the core workflow: human progress on stderr, caller-owned data on | ||
| stdout, and versioned events for machine consumers. | ||
| - Expanded the packed-package consumer check to execute all new examples and require the README | ||
| preview asset in the published tarball. | ||
| - Clarified npm search metadata and removed the misleading `tui` keyword. | ||
| - Added a blocking minimum-runtime job for Node.js 22. | ||
| ### Compatibility | ||
| - No runtime API or event-schema changes. | ||
| - Lowered the minimum supported runtime from Node.js 24 to Node.js 22 after the full package and | ||
| packed-consumer checks passed on main. | ||
| ## [1.1.8] - 2026-08-10 | ||
| ### Fixed | ||
| - Preserved updates that race an active output flush. | ||
| - Stabilized cross-platform PTY resize verification. | ||
| ### Changed | ||
| - Adopted the TypeScript 7 toolchain. | ||
| [1.1.9]: https://github.com/0disoft/laqu/compare/v1.1.8...v1.1.9 | ||
| [1.1.8]: https://github.com/0disoft/laqu/releases/tag/v1.1.8 |
| import { createLaqu } from "@0disoft/laqu"; | ||
| const progress = createLaqu(); | ||
| const artifact = await progress.task("build release", { total: 3 }, async (task) => { | ||
| task.advance(1); | ||
| task.setMessage("types checked"); | ||
| task.advance(1); | ||
| task.setMessage("bundle written"); | ||
| task.advance(1); | ||
| return { artifact: "dist/laqu.js", status: "ready" }; | ||
| }); | ||
| await progress.close(); | ||
| process.stdout.write(`${JSON.stringify(artifact)}\n`); |
| import { createLaqu } from "@0disoft/laqu"; | ||
| const progress = createLaqu({ format: "ndjson" }); | ||
| await progress.task("index files", { total: 2 }, async (task) => { | ||
| task.advance(1); | ||
| task.setMessage("sources indexed"); | ||
| task.advance(1); | ||
| }); | ||
| await progress.close(); |
| import { createLaqu } from "@0disoft/laqu"; | ||
| const progress = createLaqu(); | ||
| await progress.task("release", async (release) => { | ||
| const checks = release.child("checks", { total: 2 }); | ||
| checks.advance(1); | ||
| checks.setMessage("types passed"); | ||
| checks.advance(1); | ||
| checks.succeed("all checks passed"); | ||
| const publish = release.child("publish", { total: 2 }); | ||
| publish.advance(1); | ||
| publish.setMessage("package uploaded"); | ||
| publish.advance(1); | ||
| publish.succeed("release published"); | ||
| }); | ||
| await progress.close(); |
| <svg xmlns="http://www.w3.org/2000/svg" width="960" height="420" viewBox="0 0 960 420" role="img" aria-labelledby="title description"> | ||
| <title id="title">Laqu terminal progress preview</title> | ||
| <desc id="description">Three concurrent CLI tasks render progress on stderr while a JSON result remains clean on stdout.</desc> | ||
| <rect width="960" height="420" rx="20" fill="#0b1020"/> | ||
| <rect x="1" y="1" width="958" height="418" rx="19" fill="none" stroke="#26324a" stroke-width="2"/> | ||
| <circle cx="38" cy="35" r="7" fill="#ff6b6b"/> | ||
| <circle cx="62" cy="35" r="7" fill="#ffd166"/> | ||
| <circle cx="86" cy="35" r="7" fill="#4ade80"/> | ||
| <text x="480" y="41" fill="#8b9bb4" font-family="ui-monospace, SFMono-Regular, Consolas, monospace" font-size="15" text-anchor="middle">release · laqu</text> | ||
| <line x1="0" y1="64" x2="960" y2="64" stroke="#26324a"/> | ||
| <g font-family="ui-monospace, SFMono-Regular, Consolas, monospace" font-size="21"> | ||
| <text x="36" y="112" fill="#7dd3fc">›</text> | ||
| <text x="70" y="112" fill="#e5edf8">install packages</text> | ||
| <text x="292" y="112" fill="#4ade80">[███████████████</text> | ||
| <text x="508" y="112" fill="#35425a">░░░░░]</text> | ||
| <text x="608" y="112" fill="#e5edf8">76%</text> | ||
| <text x="676" y="112" fill="#8b9bb4">downloading · 76/100</text> | ||
| <text x="36" y="158" fill="#7dd3fc">›</text> | ||
| <text x="70" y="158" fill="#e5edf8">build artifacts</text> | ||
| <text x="292" y="158" fill="#4ade80">[██████████</text> | ||
| <text x="450" y="158" fill="#35425a">░░░░░░░░░░]</text> | ||
| <text x="608" y="158" fill="#e5edf8">49%</text> | ||
| <text x="676" y="158" fill="#8b9bb4">bundling</text> | ||
| <text x="36" y="204" fill="#64748b">·</text> | ||
| <text x="70" y="204" fill="#e5edf8">publish preview</text> | ||
| <text x="292" y="204" fill="#35425a">[░░░░░░░░░░░░░░░░░░░░]</text> | ||
| <text x="608" y="204" fill="#e5edf8">0%</text> | ||
| <text x="676" y="204" fill="#8b9bb4">waiting</text> | ||
| </g> | ||
| <line x1="36" y1="242" x2="924" y2="242" stroke="#26324a"/> | ||
| <text x="36" y="280" fill="#8b9bb4" font-family="ui-monospace, SFMono-Regular, Consolas, monospace" font-size="16">stderr · live progress</text> | ||
| <text x="36" y="325" fill="#8b9bb4" font-family="ui-monospace, SFMono-Regular, Consolas, monospace" font-size="16">stdout · caller-owned result</text> | ||
| <rect x="36" y="344" width="888" height="48" rx="8" fill="#111a2d" stroke="#26324a"/> | ||
| <text x="56" y="375" fill="#f8fafc" font-family="ui-monospace, SFMono-Regular, Consolas, monospace" font-size="18">{"artifact":"dist/laqu.js","status":"ready"}</text> | ||
| </svg> |
+13
-5
| { | ||
| "name": "@0disoft/laqu", | ||
| "version": "1.1.8", | ||
| "description": "Reliable terminal progress and live CLI rendering primitives for TypeScript.", | ||
| "version": "1.1.9", | ||
| "description": "Reliable CLI progress on stderr with clean stdout and versioned events.", | ||
| "keywords": [ | ||
| "ci", | ||
| "cli", | ||
| "library", | ||
| "live-rendering", | ||
| "ndjson", | ||
| "nodejs", | ||
| "progress-bar", | ||
| "stderr", | ||
| "terminal", | ||
| "tui", | ||
| "typescript" | ||
@@ -26,2 +29,4 @@ ], | ||
| "examples", | ||
| "media", | ||
| "CHANGELOG.md", | ||
| "README.md" | ||
@@ -58,2 +63,5 @@ ], | ||
| "example:basic": "bun run build && node examples/basic.mjs", | ||
| "example:clean-stdout": "bun run build && node examples/clean-stdout.mjs", | ||
| "example:nested-tasks": "bun run build && node examples/nested-tasks.mjs", | ||
| "example:ndjson": "bun run build && node examples/ndjson-events.mjs", | ||
| "format": "oxfmt --write .", | ||
@@ -68,3 +76,3 @@ "format:check": "oxfmt --check .", | ||
| "devDependencies": { | ||
| "@types/node": "24.x", | ||
| "@types/node": "22.x", | ||
| "oxfmt": "0.x", | ||
@@ -75,5 +83,5 @@ "oxlint": "1.x", | ||
| "engines": { | ||
| "node": ">=24.0.0" | ||
| "node": ">=22.0.0" | ||
| }, | ||
| "packageManager": "bun@1.3.14" | ||
| } |
+101
-10
| # laqu | ||
| `laqu` is a strict TypeScript runtime for reliable terminal progress and live CLI rendering on Node.js 24+. | ||
| [](https://www.npmjs.com/package/@0disoft/laqu) | ||
| [](package.json) | ||
| [](LICENSE) | ||
| It treats stdout as the caller-owned data channel and sends progress, status, logs, human rendering, | ||
| and JSON/NDJSON progress events to stderr by default. The runtime keeps output format, status-stream | ||
| capability, output target, and progress policy as separate decisions instead of hiding them behind | ||
| one mode enum. | ||
| **Progress for humans. Events for machines. stdout stays clean.** | ||
| `laqu` gives Node.js and TypeScript CLIs live progress without corrupting piped JSON, CSV, file | ||
| lists, or other caller-owned output. Human status goes to stderr by default; CI-safe text and | ||
| versioned JSON/NDJSON events use the same task model when a terminal UI is not appropriate. | ||
|  | ||
| ```text | ||
| › install packages [███████████████░░░░░] 76% downloading 76/100 | ||
| › build artifacts [██████████░░░░░░░░░░] 49% bundling | ||
| · publish preview [░░░░░░░░░░░░░░░░░░░░] 0% waiting | ||
| ``` | ||
| stdout remains available for the result: | ||
| ```json | ||
| { "artifact": "dist/laqu.js", "status": "ready" } | ||
| ``` | ||
| ## Install | ||
| ```sh | ||
| npm install @0disoft/laqu | ||
| ``` | ||
| ```sh | ||
| pnpm add @0disoft/laqu | ||
| ``` | ||
| ```sh | ||
| bun add @0disoft/laqu | ||
| ``` | ||
| The published package targets Node.js 24+ and does not require Bun, Deno, Rust, native addons, WASM, or C++ bindings at runtime. | ||
| The published package targets Node.js 22+ and does not require Bun, Deno, Rust, native addons, WASM, or C++ bindings at runtime. | ||
| ## Quick Start | ||
| ```ts | ||
| import { createLaqu } from "@0disoft/laqu"; | ||
| const progress = createLaqu(); | ||
| const result = await progress.task("build", { total: 3 }, async (task) => { | ||
| task.advance(1); | ||
| task.setMessage("typecheck passed"); | ||
| task.advance(1); | ||
| task.setMessage("bundle written"); | ||
| task.advance(1); | ||
| return { artifact: "dist/laqu.js", status: "ready" }; | ||
| }); | ||
| await progress.close(); | ||
| process.stdout.write(`${JSON.stringify(result)}\n`); | ||
| ``` | ||
| When this command runs interactively, progress animates on stderr. When stdout is redirected, the | ||
| redirected file contains only the JSON result. In CI or a pipe, human progress automatically falls | ||
| back to stable append-only lines. | ||
| ## Pick The Output Your Consumer Needs | ||
| | Consumer | Configuration | Output behavior | | ||
| | ----------------------------- | --------------------------------- | ------------------------------ | | ||
| | Person in a terminal | default | live progress on stderr | | ||
| | CI log or redirected terminal | default | append-only progress on stderr | | ||
| | Event collector | `{ format: "ndjson" }` | versioned events on stderr | | ||
| | Data pipeline | default plus caller writes stdout | clean caller-owned stdout | | ||
| The output format, terminal capability, output target, and progress policy remain independent. You | ||
| can change one without pretending that terminal detection, serialization, and destination are the | ||
| same decision. | ||
| ## Runnable Examples | ||
| - [`examples/basic.mjs`](examples/basic.mjs): animated multi-task terminal demo | ||
| - [`examples/nested-tasks.mjs`](examples/nested-tasks.mjs): parent and child task hierarchy | ||
| - [`examples/clean-stdout.mjs`](examples/clean-stdout.mjs): progress on stderr with a JSON result on stdout | ||
| - [`examples/ndjson-events.mjs`](examples/ndjson-events.mjs): versioned NDJSON task and summary events | ||
| Run an example after installing dependencies: | ||
| ```sh | ||
| bun run example:clean-stdout > result.json | ||
| ``` | ||
| The progress remains visible in the terminal while `result.json` stays parseable JSON. | ||
| ## Scoped Tasks | ||
@@ -222,2 +299,14 @@ | ||
| ## Documentation And Support | ||
| - [Public API](docs/library/public-api.md) | ||
| - [Compatibility policy](docs/library/compatibility.md) | ||
| - [SemVer policy](docs/library/semver.md) | ||
| - [Migration guide](docs/library/migration-guide.md) | ||
| - [Changelog](CHANGELOG.md) | ||
| - [Architecture and operating documentation](docs/README.md) | ||
| - [Contributing](CONTRIBUTING.md) | ||
| - [Security policy](SECURITY.md) | ||
| - [Issues](https://github.com/0disoft/laqu/issues) | ||
| ## Development | ||
@@ -235,2 +324,4 @@ | ||
| CI runs both checks on Ubuntu, Windows, and macOS. The real PTY resize harness runs on Unix hosts; Windows runs the full stream, process-signal, renderer, and package suite without claiming ConPTY coverage. | ||
| An additional blocking Ubuntu job runs the same package and packed-consumer checks on the minimum | ||
| supported runtime, Node.js 22. The three-platform matrix runs on Node.js 24. | ||
| `bun run example:basic` builds the package and runs a small live progress demo. Terminal scrollback keeps the final frame; watch the command while it runs to see the bar animate in place. | ||
@@ -240,7 +331,7 @@ | ||
| GitHub Actions publishes npm releases from maintainer-created version tags. The tag must match `package.json` exactly, for example `v1.1.8` for version `1.1.8`. | ||
| GitHub Actions publishes npm releases from maintainer-created version tags. The tag must match `package.json` exactly, for example `v1.1.9` for version `1.1.9`. | ||
| ```sh | ||
| git tag -a v1.1.8 -m "v1.1.8" | ||
| git push origin main v1.1.8 | ||
| git tag -a v1.1.9 -m "v1.1.9" | ||
| git push origin main v1.1.9 | ||
| ``` | ||
@@ -250,2 +341,2 @@ | ||
| On a matching tag push, the workflow first verifies the tag, package metadata, tests, build, and installed-tarball consumer checks with read-only repository permissions. The publish job then waits on the `npm` environment gate, repeats package verification on the tagged commit, packs the release tarball, uploads that exact tarball as a retained workflow artifact, publishes the same tarball to npm with provenance through OIDC, and creates a GitHub Release with generated notes. | ||
| On a matching tag push, the workflow first verifies the tag, package metadata, tests, build, and installed-tarball consumer checks with read-only repository permissions. The publish job then waits on the `npm` environment gate, repeats package verification on the tagged commit, packs the release tarball, uploads that exact tarball as a retained workflow artifact, publishes the same tarball to npm with provenance through OIDC, and creates a GitHub Release from the matching `CHANGELOG.md` section. |
140124
7.09%48
11.63%2851
1.24%339
36.69%