Skip to content

DM-55381: Add @lsst-sqre/sse-client shared SSE transport package - #528

Open
jonathansick wants to merge 4 commits into
mainfrom
tickets/DM-55381
Open

DM-55381: Add @lsst-sqre/sse-client shared SSE transport package#528
jonathansick wants to merge 4 commits into
mainfrom
tickets/DM-55381

Conversation

@jonathansick

@jonathansick jonathansick commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

  • Add @lsst-sqre/sse-client, a new minimal workspace package wrapping eventsource-client as the monorepo's shared SSE transport, replacing the unmaintained @microsoft/fetch-event-source. It exports subscribeToEventSource(url, options) — raw message/connect/disconnect/reconnect callbacks, custom headers, credentials defaulting to 'include' (Gafaelfawr same-origin cookie auth), external AbortSignal, fetch override, with a cleanup function that closes the client — and a thin useEventSource() React hook. No payload parsing/validation in this layer; consumers own their schemas.
  • Rewrite @lsst-sqre/times-square-client's SSE layer (subscribeToHtmlEvents()) on subscribeToEventSource(), preserving the public API exactly. Zod validation of HtmlEvent, auto-abort on execution completion, onComplete, and the optional structured Logger stay in this layer; reconnect policy follows the transport default (automatic backoff honoring server retry:, including after 4xx), with connection loss surfaced via onError and scheduled reconnects logged through the Logger.
  • Migrate the app's TimesSquareHtmlEventsProviderClient to the packaged subscribeToHtmlEvents(), deleting its duplicated inline fetchEventSource logic and local HtmlEvent type; the app's SSE path gains Zod validation, credentials: 'include', and automatic reconnection. @microsoft/fetch-event-source is now removed from every package.json in the repo. Also conforms the dev mock github/[...tsSlug] route to the client's PageSchema so the dev-mode Times Square flow works end-to-end.
  • Follow the no-build-step pattern (main/types point at src/index.ts; consumers transpile) and register the package in transpilePackages in apps/squareone/next.config.js. Includes changesets for @lsst-sqre/sse-client (new), @lsst-sqre/times-square-client, and squareone.

Validation steps

  • Run pnpm install and confirm it succeeds with eventsource-client present only in packages/sse-client, and that grep -r "fetch-event-source" across the repo's package.json files and source returns no matches.
  • Run pnpm test --filter @lsst-sqre/sse-client --filter @lsst-sqre/times-square-client from the repo root and confirm the SSE tests pass (transport: message delivery, connect/disconnect callbacks, cleanup, external abort; Times Square layer: valid event → onEvent, malformed JSON ignored, Zod-invalid payload rejected with logger warning, auto-abort + onComplete on completion, cleanup aborts).
  • Confirm @lsst-sqre/sse-client is listed in transpilePackages in apps/squareone/next.config.js.
  • Confirm subscribeToHtmlEvents() call sites in apps/squareone compile unchanged, and that TimesSquareHtmlEventsProviderClient.tsx contains no inline SSE parsing and no local HtmlEvent type.
  • With pnpm dev --filter squareone, open /times-square/github/lsst-sqre/times-square-demo/matplotlib/gaussian2d?a=1 and confirm the page receives the mock SSE event (execution stats render, e.g. "in 14.2 seconds"), context values update, and the htmlevents connection closes after the complete event (a single request in the Network tab, no reconnect loop).

References

Create packages/sse-client wrapping eventsource-client as the
monorepo's shared SSE transport, exporting subscribeToEventSource()
(raw message/connect/disconnect/reconnect callbacks, headers,
credentials defaulting to 'include', external AbortSignal, fetch
override, cleanup closes the client) and a thin useEventSource() React
hook, so Times Square today and Semaphore later share one SSE
primitive.

Key decisions:
- No payload parsing/validation in this layer; messages are delivered
  raw (EventSourceMessage) and consumers own their schemas.
- External AbortSignal is wired to client.close(); an already-aborted
  signal skips connecting entirely and returns a no-op cleanup.
- useEventSource reads options through a ref: only url changes
  reconnect; callback identity changes never do. url may be null to
  disable the subscription.
- No-build-step package (main/types point at src/index.ts), react
  ^18||^19 peer dep, eventsource-client sole runtime dep; added to
  transpilePackages in apps/squareone/next.config.js.

Next-iteration notes:
- Tests use a mocked FetchLike returning a ReadableStream SSE body
  (honoring init.signal with an AbortError); the helper is duplicated
  in both test files and could be shared if task #526 needs it.
- EventSourceMessage and FetchLike are re-exported from src/index.ts
  for consumers (times-square-client in #526).

Closes #525
@changeset-bot

changeset-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 12f3adb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
squareone Patch
@lsst-sqre/times-square-client Minor
@lsst-sqre/sse-client Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Rebuild subscribeToHtmlEvents() on the shared subscribeToEventSource()
transport, dropping the unmaintained @microsoft/fetch-event-source
dependency while preserving the public SSE API exactly.

Key decisions:
- Zod validation, auto-abort on complete+html_hash, onComplete, and the
  structured Logger stay in this layer; the transport stays raw.
- Reconnect policy is the library default (automatic backoff honoring
  server retry:, including after 4xx); connection loss is surfaced via
  onError from onDisconnect, and scheduled reconnects are logged at
  debug level through the Logger.
- sse.test.ts mocks @lsst-sqre/sse-client at the module boundary and
  drives the captured callbacks directly, keeping the tests focused on
  this layer's semantics rather than transport behavior.
- Changeset is minor: no API change, but the reconnect-after-4xx
  behavior and transpile graph (sse-client source) change for consumers.

Next-iteration notes:
- Task #527 (app provider refactor) can rely on subscribeToHtmlEvents
  signatures being unchanged; @lsst-sqre/sse-client is already in
  apps/squareone transpilePackages from task #525.
- @microsoft/fetch-event-source still remains in apps/squareone
  package.json; #527 removes the last repo reference.

Closes #526
Rewrite TimesSquareHtmlEventsProviderClient on the packaged
subscribeToHtmlEvents() from @lsst-sqre/times-square-client, deleting the
duplicated inline fetchEventSource logic and the local HtmlEvent type. The
app's SSE path now gains Zod validation, credentials: 'include', and
automatic reconnection from the shared transport, and
@microsoft/fetch-event-source is removed from the entire repo.

Key decisions:
- The provider's isClient gate was dropped: the component is loaded via
  next/dynamic with ssr: false and useEffect never runs on the server, so
  the extra state only forced a redundant effect cycle.
- The URL query string from TimesSquareUrlParametersContext is converted to
  a params record with URLSearchParams so subscribeToHtmlEvents owns URL
  construction (no more manual `?${qs}` concatenation).
- The dev mock github/[...tsSlug] route drifted from the client's PageSchema
  (string description, missing date_added/uploader_username), which Zod-failed
  useTimesSquarePage and blocked the end-to-end SSE check; it was minimally
  updated to conform.

Next-iteration notes:
- The dev mock /times-square/api/v1/github contents route still fails
  GitHubContentsRootSchema validation (console ZodError on Times Square pages
  in dev; sidebar nav 404s). Pre-existing drift, out of scope here.

Closes #527
Update the vendored Times Square OpenAPI spec to v0.24.0 and fix the dev
mock API routes that had drifted from it:

- The GitHub contents-tree mocks (github and github-pr) omitted the
  required contents array on leaf page nodes, which failed
  GitHubContentsRootSchema/GitHubPrContentsSchema validation and broke
  the sidebar nav in dev.
- The pages/[page] and github-pr page-metadata mocks still returned a
  plain-string description and lacked date_added, uploader_username,
  html_events_url, and github required by PageSchema.

Mock payloads are now typed with the Page/GitHubContentsRoot/
GitHubPrContents types from @lsst-sqre/times-square-client for
compile-time conformance, and a new schema-conformance test parses
every JSON mock route with the package's Zod schemas so future drift
fails in CI instead of as a runtime ZodError in dev.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant