feat(lib/progress): streaming and headless nodes #56

Merged
clo merged 8 commits from progress into master 2026-01-12 04:11:19 -08:00
Owner

resolves #33.
resolves #47.
resolves #40.
should try #34

this PR reworks log and progress, and adds many more treats to make that a reality.

for a general intro on what progress is, check out it's documentation comment
https://git.paperclover.net/clo/sitegen/src/commit/progress/lib/progress.ts#L1

description contained within commit messages

resolves #33. resolves #47. resolves #40. should try #34 this PR reworks log and progress, and adds many more treats to make that a reality. for a general intro on what progress is, check out it's documentation comment https://git.paperclover.net/clo/sitegen/src/commit/progress/lib/progress.ts#L1 description contained within commit messages
WIP: pls extract the other stuff into own commits

node signaling is done by providing a `progress.Root` to every node,
dispatching events to it when the node changes. the root is connected to
an observer to construct a UI out of it. this commit adds two built in
observers:

`attachToScreen` binds a root to a TTY screen (via the log.Widget API).
the primary use of this is to implement the top level `progress.start`.

`encodeStream` converts these events into a `ReadableStream`. by
batching events together, the stream contents remain small, that way the
code that constructs progress nodes do not have to worry about calling
many setters at once, it gets debounced be the serializer. a server may:

    const root = new progress.Root();
    doActionWithProgress(root satisfies progress.Ref)
      .then(
        (result) => root.end({ success: true, result }),
        (error) => root.end({ success: false, error })
      );
    if (req.headers.get("Accept").includes(progress.contentType))
      return new Response(progress.encodeStream(root), {
        headers: { 'Content-Type': progress.contentType },
      });
    // to support non-streaming clients
    const result = await root.once("end");
    return result.success ? ... : ...;

and `decodeStream` on the client:

    const output = document.getElementById("output");
    const res = await fetch(...);
    if (!res.ok) throw ...;
    const root = progress.decodeStream(res.body);
    output.innerHTML = progress.formatHtml(root.active);
    root.on("change", () => {
      output.innerHTML = progress.formatHtml(root.active);
    });
    const result = await root.once("end");
    output.innerText = JSON.stringify(result);

this feature is something that my work is interested in using. while
`progress.ts` will not be the home of a React hook -- it's trivial to
implement

the library overall gains extra features:

- a typed event emitter (`Events.ts`) compatible with the browser
- cancelable promises in `async.ts`
- more type helpers in `ts.ts`
clo changed title from WIP: feat(lib/progress): implement streaming and headless nodes to WIP: feat(lib/progress): streaming and headless nodes 2026-01-02 20:35:57 -08:00
clo changed title from WIP: feat(lib/progress): streaming and headless nodes to feat(lib/progress): streaming and headless nodes 2026-01-12 03:15:51 -08:00
clo left a comment
Author
Owner

blockers

blockers
lib/Lru.ts Outdated
@ -6,3 +5,1 @@
* // Extra types are in a `declare namespace`
* const opts: Lru.Options = { capacity: 2 };
* const lru = new Lru(opts);
* ```
Author
Owner

```ts

\```ts
clo marked this conversation as resolved
lib/progress.ts Outdated
@ -405,0 +1328,4 @@
estimatedTimeNonNull: boolean;
futureProofingPacket: boolean;
Author
Owner

unhandled

unhandled
clo marked this conversation as resolved
@ -4,20 +4,47 @@
### breaking
Author
Owner

ensure up to date

ensure up to date
clo marked this conversation as resolved
@ -201,13 +201,16 @@ export function mergeStyles(code: string): string {
* turned into their original names.
*/
export function debugAnsi(text: string): string {
// return JSON.stringify(text);
Author
Owner

delete this

delete this
clo marked this conversation as resolved
clo merged commit 9625a96900 into master 2026-01-12 04:11:19 -08:00
Sign in to join this conversation.
No reviewers
No labels
bug
chore
feat
lib
site
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
clo/sitegen!56
No description provided.