Skip to content

chore(js): validate API declarations with API Extractor - #6794

Merged
kixelated merged 10 commits into
mainfrom
quest/m1/api-extractor-checks
Oct 27, 2026
Merged

kixelated merged 10 commits into
mainfrom
quest/m1/api-extractor-checks

Conversation

@kixelated

@kixelated kixelated commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

js/common/declarations.ts and js/common/docs.ts hand-roll checks that API Extractor already does, and the shared API report configuration silenced every API Extractor message. So a public signature could use a type no entrypoint exports, such as the old @moq/auth AccessInput, or an export could ship without docs, and the build would still pass.

Approach

Every published @moq/* entrypoint now goes through API Extractor at build time, with compiler, extractor and TSDoc messages as errors. The two hand-rolled checkers and their tests are deleted.

The maintainer picked the broader design: resolve declared dependency types without needing sibling packages to be built.

  • Declared dependencies only. The isolated dist/ copy gets a node_modules that links only the declared dependencies, peer dependencies and optional dependencies, each to its workspace install. A workspace sibling resolves to its source through its exports, so nothing has to be built first. An undeclared bare import fails with TS2307.
  • Dependency messages ignored by location. Messages in a dependency's own declaration files are ignored because that dependency's build checks them. This goes by file location, not message code, so there is no blanket suppression of external-module diagnostics.
  • Narrow ae-forgotten-export acceptance. API Extractor analyzes one entrypoint at a time. One compiler state now covers all of a package's entrypoints, and a forgotten export is accepted only when every same-named declaration in this package that the signature references is nameable from some entrypoint. Nameable means one of:
    • an export,
    • a namespace member,
    • a rename of a nameable declaration (type Props = ConnectionProps, static parse: typeof parse).
  • Namespace re-exports. export * as X is rewritten to import-then-export in the copy, because API Extractor treats each namespace re-export as a new namespace.
  • JSR summaries. @module stays JSR's summary tag; the copy adds @packageDocumentation beside it.

Existing findings were migrated. Where a type was only an implementation detail it was inlined; where it was part of a public signature it was exported and documented. Doc comments were added across the packages. The API reports are regenerated, and several shrank because namespaces are no longer duplicated.

Impact

  • Public API: additions only, no breaks.
    • @moq/signals: Subscriber, SetterType; ArrayElement from dom.
    • @moq/watch: the *Output types of the audio and video decoders, the emitter, the sources, sync and broadcast.
    • @moq/publish:
      • the *Output types of the sources, the capture and encoder modules, and transcode;
      • Fanout and FanoutProps;
      • EncoderOutput from the audio module.
    • @moq/net: Announce.Options, HopSchema.
    • @moq/room: TrackRef, UserInput, Consumed.
    • @moq/qmux: Datagrams, SendStreamOptions, and the WebSocketStreamData, WebSocketStreamOpenEvent, WebSocketStreamCloseEvent and WebSocketStreamCloseInfo types.
    • @moq/json: Window.DecoderConfig (renamed from an unexported ConsumerConfig that collided with Window.ConsumerConfig).
  • Wire: none.
  • Unpublished packages: @moq/p2p and @moq/e2ee lose the old declarations check, because only published entrypoints are analyzed.

Choices made without the maintainer

  • ae-missing-release-tag is off: @internal with stripInternal is the only visibility tier.
  • ae-unresolved-link is off: JSR resolves {@link} in the comment's lexical scope, while API Extractor resolves links only from entrypoint exports.
  • The cross-entrypoint and rename acceptance rules for ae-forgotten-export, described above.
  • The new exports listed under Impact, and the Window.DecoderConfig name.
  • Implementation-only aliases were inlined rather than exported: watch Kind and Status, signals Truthy and Falsy.
  • Custom elements type observedAttributes as typeof OBSERVED and the attributeChangedCallback name as (typeof OBSERVED)[number], so the documented constant is the only name.

Validation

  • js/common/api.test.ts has 5 fixtures covering:
    • a documented report;
    • a missing summary;
    • a forgotten export;
    • a type from another entrypoint or a rename being accepted;
    • a declared dependency resolving while an undeclared one fails.
  • Proof: removing the Installed export and the wall() doc comment from @moq/clock fails its build with ae-forgotten-export and ae-undocumented. Reverted afterwards.
  • Strict builds of all 18 published JS packages pass.
  • just check and quest check pass.

Follow-ups

  • The generated JS API reference (quest/m1/js-api-reference.md) renders the doc model with ae-unresolved-link off, so it may need to resolve or rewrite lexical-scope links.
  • Possibly bring declaration checks back for the unpublished @moq/p2p and @moq/e2ee.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 2 commits October 25, 2026 09:05
Enable strict extractor diagnostics, define the shared module tag, and exercise declaration failures. External dependency resolution remains a maintainer decision; workspace migration and checker removal are incomplete.

Co-Authored-By: GPT-6 <noreply@openai.com>
@kixelated kixelated changed the title API Extractor checks for @moq/* declarations chore(js): validate API declarations with API Extractor Oct 25, 2026
kixelated and others added 5 commits October 25, 2026 15:36
…Extractor

Link each package's declared dependencies into the isolated dist copy so
API Extractor resolves their types, with workspace siblings resolving to
source. Accept a forgotten export only when another entrypoint or a rename
makes it nameable, migrate the findings, and delete the hand-rolled
declarations and docs checkers. Record the maintainer's decision in the
quest Plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	js/net/api/index.api.md
#	js/net/src/connection/accept.ts
#	js/net/src/connection/connect.ts
API Extractor types its program `unknown`, so the checks libav's build
script runs failed to typecheck. Declare the parts of the TypeScript
compiler API the analysis uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review October 25, 2026 23:48
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 78781718-1c59-48bc-84d9-43b0b2fca62e
Commits

Reviewing files that changed from the base of the PR and between 97a6693 and b5f7f5a.

Files selected for processing: 109
  • js/auth/api/index.api.md
  • js/auth/src/claims.ts
  • js/auth/src/set.ts
  • js/clock/api/index.api.md
  • js/common/api.test.ts
  • js/common/api.ts
  • js/common/declarations.test.ts
  • js/common/declarations.ts
  • js/common/docs.test.ts
  • js/common/docs.ts
  • js/common/package.ts
  • js/common/tsdoc.json
  • js/flate/api/index.api.md
  • js/flate/src/snapshot/consumer.ts
  • js/flate/src/snapshot/producer.ts
  • js/flate/src/stream/consumer.ts
  • js/flate/src/stream/producer.ts
  • js/hang/api/catalog.api.md
  • js/hang/api/cmaf.api.md
  • js/hang/api/container.api.md
  • js/hang/api/index.api.md
  • js/hang/api/util.api.md
  • js/hang/src/container/types.ts
  • js/hang/src/timeline.ts
  • js/json/api/index.api.md
  • js/json/src/snapshot/consumer.ts
  • js/json/src/snapshot/encoder.ts
  • js/json/src/snapshot/producer.ts
  • js/json/src/stream/consumer.ts
  • js/json/src/stream/producer.ts
  • js/json/src/window/consumer.ts
  • js/json/src/window/decoder.ts
  • js/json/src/window/index.ts
  • js/libav/api/index.api.md
  • js/loc/api/index.api.md
  • js/moq-boy/api/element.api.md
  • js/moq-boy/api/index.api.md
  • js/moq-boy/src/element.tsx
  • js/msf/api/index.api.md
  • js/net/api/index.api.md
  • js/net/api/zod.api.md
  • js/net/src/announce.ts
  • js/net/src/broadcast.ts
  • js/net/src/connection/established.ts
  • js/net/src/connection/pool.ts
  • js/net/src/hop.ts
  • js/net/src/origin.ts
  • js/pattern/api/index.api.md
  • js/pattern/src/index.ts
  • js/publish/api/element.api.md
  • js/publish/api/index.api.md
  • js/publish/api/support-element.api.md
  • js/publish/api/support.api.md
  • js/publish/api/ui.api.md
  • js/publish/src/audio/capture.ts
  • js/publish/src/audio/encoder.ts
  • js/publish/src/audio/index.ts
  • js/publish/src/audio/types.ts
  • js/publish/src/element.ts
  • js/publish/src/index.ts
  • js/publish/src/preview.ts
  • js/publish/src/source/camera.ts
  • js/publish/src/source/device.ts
  • js/publish/src/source/file.ts
  • js/publish/src/source/microphone.ts
  • js/publish/src/source/screen.ts
  • js/publish/src/support/element.ts
  • js/publish/src/ui/element.ts
  • js/publish/src/video/capture.ts
  • js/publish/src/video/encoder.ts
  • js/publish/src/video/types.ts
  • js/qmux/api/index.api.md
  • js/qmux/src/index.ts
  • js/qmux/src/session.ts
  • js/qmux/src/web-socket-stream.ts
  • js/room/api/index.api.md
  • js/room/src/index.ts
  • js/room/src/metadata.ts
  • js/signals/api/dom.api.md
  • js/signals/api/index.api.md
  • js/signals/api/react.api.md
  • js/signals/api/solid.api.md
  • js/signals/src/dom.ts
  • js/signals/src/index.ts
  • js/ui/api/index.api.md
  • js/ui/src/support.ts
  • js/video/api/index.api.md
  • js/watch/api/element.api.md
  • js/watch/api/index.api.md
  • js/watch/api/support-element.api.md
  • js/watch/api/support.api.md
  • js/watch/api/ui.api.md
  • js/watch/src/audio/decoder.ts
  • js/watch/src/audio/emitter.ts
  • js/watch/src/audio/source.ts
  • js/watch/src/broadcast.ts
  • js/watch/src/element.ts
  • js/watch/src/player.ts
  • js/watch/src/support/element.ts
  • js/watch/src/sync.ts
  • js/watch/src/text/source.ts
  • js/watch/src/ui/element.ts
  • js/watch/src/video/decoder.ts
  • js/watch/src/video/source.ts
  • quest/m1/README.md
  • quest/m1/api-extractor-checks.md
  • quest/m1/js-api-reference.md
  • quest/m2/audit-docs.md
  • sh/js/workspace.py
Files with no reviewable changes: 23
  • js/common/declarations.test.ts
  • js/common/docs.test.ts
  • js/common/docs.ts
  • quest/m1/README.md
  • js/auth/api/index.api.md
  • js/clock/api/index.api.md
  • js/loc/api/index.api.md
  • js/hang/api/catalog.api.md
  • js/signals/api/solid.api.md
  • js/video/api/index.api.md
  • js/watch/api/ui.api.md
  • js/signals/api/react.api.md
  • quest/m1/api-extractor-checks.md
  • js/common/declarations.ts
  • js/pattern/api/index.api.md
  • js/msf/api/index.api.md
  • js/hang/api/cmaf.api.md
  • js/publish/api/ui.api.md
  • js/publish/api/support.api.md
  • js/watch/api/support.api.md
  • js/publish/api/support-element.api.md
  • js/watch/api/support-element.api.md
  • js/libav/api/index.api.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

Walkthrough

The change replaces JavaScript declaration and documentation checks with API Extractor reporting and adds tests for report generation, exports, and dependency resolution. It also updates public exports and type declarations across multiple packages, including publishing, playback, networking, signals, and room APIs. Documentation comments and generated API reports were updated. The summaries show no runtime behavior changes.

Priority: Normal

Merge Risk: Minimal · up to b5f7f

The API reporting and public type updates appear ready to merge after normal checks; no actionable merge-blocking issue remains.

Pre-merge checks: Passed 4 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Docstring Coverage Inconclusive Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 50 files. (36 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title "chore(js): validate API declarations with API Extractor" matches the main change. The PR replaces the hand-rolled declaration and docs checkers in js/common with API Extractor checks run on…
Description check Passed The description is clearly related to the changeset. It explains the problem with the hand-rolled checkers in js/common/declarations.ts and js/common/docs.ts, the API Extractor approach, the isolated …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "chore(js): validate API declarations with API Extractor" matches the main change. The PR replaces the hand-rolled declaration and docs checkers in js/common with API Extractor checks run on published @moq/* entrypoints. The title is short, specific, and follows conventional-commit style.

Full details: Description check

Explanation

The description is clearly related to the changeset. It explains the problem with the hand-rolled checkers in js/common/declarations.ts and js/common/docs.ts, the API Extractor approach, the isolated dist copy with declared dependencies, the migrated findings, public API additions, and validation steps. It matches the summarized file changes.

Full details: Docstring Coverage

Explanation

Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 50 files. (36 skipped: 21 unsupported, 15 over the file limit.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T21:28:34.792566Z b5f7f5a New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: 851378c

No actionable bugs found in the reviewed changes. Consolidating declaration/docs validation into the existing API Extractor build gate is a sound direction; the shared compiler state, declared-dependency isolation and symbol-based cross-entrypoint exceptions justify the remaining custom logic. The public type additions and alias inlining preserve the inspected runtime behavior.

Useful follow-up coverage: add adversarial fixtures for same-named hidden/exported declarations and chained aliases around nameableReference, since this exception is now part of the release gate. The five fixtures cover the principal positive/negative cases but not those boundaries.

Verification: static inspection of the checker, packaging/workspace integration, fixtures and API/source changes only. I did not run builds/tests or independently reproduce the reported planted-failure checks. Dependency-source diagnostics and unresolved TSDoc links remain intentionally outside this gate.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing summary: the OpenAI review of 851378ca84 found no actionable bugs and Codex gave a thumbs-up, so no changes were needed after it. Main has moved since, but only in files this PR doesn't touch, so I'm landing the reviewed head without merging main again. quest check against origin/main passes. The choices made without the maintainer are listed under "Choices made without the maintainer" in the description.

The review suggested more fixtures for nameableReference, covering declarations that share a name where one is hidden and one exported, and chains of aliases. I'm treating that as a follow-up rather than blocking on it.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
# Conflicts:
#	js/net/api/index.api.md
#	js/net/api/zod.api.md
#	quest/m1/README.md
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Oct 26, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Requeuing after the merge queue dropped this on checks_timed_out (queue load, not a failure of this PR).

  • No changes pushed: 53c0186f4e5e9f32f000cf62304505489a08318c merges cleanly with current main.
  • Locally, that head merged with main (8810d73) passes just check, including the strict API Extractor build of @moq/net with the newly landed track.ts changes from fix(net): a widened max age re-offers skipped cached groups #6717, and quest check.
  • Review: Codex had no findings on this head. The earlier review's suggestion of extra adversarial fixtures for nameableReference (same-named hidden/exported declarations, chained aliases) is left as a follow-up.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
…r-checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged origin/main (after moq-dev/moq#6859) to clear the merge-queue conflict:

  • js/hang/api/index.api.md: kept this branch's single Catalog namespace (main still had the duplicated Catalog_2 that this PR removes) and main's removal of TooManyRenditions. Rebuilding @moq/hang, @moq/net, @moq/json and @moq/watch regenerated identical reports.
  • quest/m1/js-api-reference.md: kept this branch's wording, which no longer links the deleted api-extractor-checks quest, and main's new bullet about nameableReference fixtures. The Required link is dropped because this PR finishes that quest.

just check and quest check pass on b5f7f5a.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit b8561a3 Oct 27, 2026
10 checks passed
@kixelated
kixelated deleted the quest/m1/api-extractor-checks branch October 27, 2026 01:01
kixelated added a commit that referenced this pull request Oct 27, 2026
js/hang/src/timeline.ts stays deleted: main only documented its fields
(#6794), and this branch replaces the module with Hls.Consumer. The hang
API reports are regenerated with main's API Extractor setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant