Skip to content

MeshJob: Stream subscription mode — subscribeEvents async iterator (TypeScript) #1048

Description

@dhyansraj

Summary

TypeScript vertical of MeshJob Stream subscription mode (#1032 follow-up): mesh.jobs.subscribeEvents(jobId, { types, after, longPollSecs }) -> AsyncIterableIterator<JobEvent>.

Sibling to #1046/#1047 (Python). Mirrors the polyglot cadence established by #1041#1043#1045 for recv_event / send_event / postEvent.

Motivation

recvEvent (consumer side, drained once-per-message via JobController.recvEvent) and postEvent (producer/external side, fire-and-forget via mesh.jobs.postEvent) — both shipped in #1043 — cover the point-to-point event injection contract. They do not cover observer/fan-out patterns where a third party (or the commissioner itself, after the original commission/call returned) wants to watch a job's event stream without consuming from it.

Stream subscription mode adds a non-destructive read API:

  • Reads the registry's per-job event log (JobEvent ent table, (job_id, seq) indexed).
  • Resumes from a caller-supplied after cursor — late subscribers replay history; reconnecting subscribers resume from their last seq.
  • Long-polls the registry so idle subscribers don't busy-loop.
  • Server-supplied next_after watermark drives cursor advance even on empty-page poll cycles (avoids rescanning when server-side types filter excludes events).
  • Yields JobEvent objects ({ seq, type, payload, posted_by, created_at, trace_context, job_id }).

This is the symmetric counterpart to postEvent on the producer side: producers push, observers read. Multiple subscribers can observe the same job's events independently — each has its own per-call cursor.

Surface

for await (const event of mesh.jobs.subscribeEvents(jobId, { types: ["progress"], after: 0 })) {
  console.log(event.seq, event.payload);
}
  • types: optional filter list (server-side filter via existing ListJobEvents query param).
  • after: cursor; subscribers persist this to resume across restarts (default 0).
  • longPollSecs: controls per-request wait parameter. Default 30. null ≡ single immediate read (rarely needed).

Iteration runs indefinitely until the caller breaks out of the for await loop or the registry returns JobNotFoundError. There is no automatic terminal-state detection — applications signal end with a sentinel event (matches Python behavior).

Scope

  • napi-rs binding: JsJobProxy.listEvents(after, types, timeoutSecs) -> Promise<[JobEvent[], bigint]> — thin pass-through to the Rust core JobProxy::list_events (already shipped in MeshJob: Stream subscription — subscribe_events (Python) #1047). Returns a tuple [events, nextAfter]. Reuses parse_timeout_secs from ts: MeshJob event injection parity — recvEvent/sendEvent/postEvent #1043.
  • TS SDK: mesh.jobs.subscribeEvents async generator; uses the LRU JobProxy cache shared with postEvent (no new cache). Per-iteration seq type-guard rejects booleans (Python parity).
  • Integration test: tc27 in uc22_meshjob_ts — producer running run_until_done pushes 3 events; commissioner observer subscribes via subscribeEvents from after=0 and asserts each event seq/payload is observed end-to-end. Reuses existing long-task-provider-ts / long-task-consumer-ts fixtures (extends with run_until_done + commission_subscribe_observer tools).

Out of scope for this issue (separate vertical):

Acceptance

  • tsuite run --suite-path tests/integration --uc uc22_meshjob_ts --parallel 4 → baseline + 1 new tc27 pass.
  • TS unit tests cover: server filter pass-through, after cursor semantics, nextAfter watermark advance on empty pages, JobProxy cache reuse, bool-seq rejection.
  • Rust core unchanged — JobProxy::list_events ships in main as of MeshJob: Stream subscription — subscribe_events (Python) #1047.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions