Skip to content

fix(types): drain Tier 4 any from public contract (SD-2834)#3258

Merged
caio-pizzol merged 6 commits into
mainfrom
caio-pizzol/SD-2834-tier4-public-contract
May 14, 2026
Merged

fix(types): drain Tier 4 any from public contract (SD-2834)#3258
caio-pizzol merged 6 commits into
mainfrom
caio-pizzol/SD-2834-tier4-public-contract

Conversation

@caio-pizzol
Copy link
Copy Markdown
Contributor

Three surgical fixes to the curated public-contract file packages/superdoc/src/core/types/index.ts. Each one was visible in the deep audit's tier-4-public-contract bucket and represents direct IntelliSense pain on a documented public API.

  • EditorTransactionEvent.transaction: anyTransaction from prosemirror-state. Hovering over the onTransaction callback's transaction param now shows the real ProseMirror type (.docChanged, .steps, .selection, etc.). prosemirror-state is already an optional peer, matching the existing pattern for @hocuspocus/provider.
  • Config.onAwarenessUpdate.states: any[]AwarenessState[]. New small public type covering the documented fields SuperDoc populates per remote client (user, clientId, color). y-protocols/awareness itself types states as Map<number, { [x: string]: any }> so a precise imported type wasn't available; defined locally rather than collapsing to unknown[].
  • ProofingError.cause?: anyunknown. Removes the eslint-disable @typescript-eslint/no-explicit-any that was sitting on the awareness line.

AwarenessState is intentionally permissive: the [key: string]: unknown index signature is there so consumers using a custom provider can still attach application-specific fields. The documented fields give IDE help; the index signature keeps the pass-through contract.

cause?: unknown is intentional, not a lazy any replacement. Error causes are genuinely opaque (whatever the proofing provider threw) and consumers should narrow with instanceof or shape checks before reading. This is the Error-cause convention TC39 adopted.

Modules.config-passthrough fixture updated to consume the new type through documented field access (state.user?.name, state.clientId, state.color) plus an index-signature read (state['customField']) to assert the pass-through still works.

Compatibility note. Consumers writing custom callback signatures with any[] will see a strict-mode error after this lands. The index signature keeps the surface permissive for application-specific fields; updates should be 1-2 lines per callsite.

Verified:

  • node tests/consumer-typecheck/typecheck-matrix.mjs: 53 passed, 0 failed
  • node tests/consumer-typecheck/deep-type-audit.mjs: 1799 → 1791 findings (tier-4-public-contract: 26 → 18)
  • AssertNotAny<AwarenessState> passes (check-public-types gate)

Linear: SD-2834

…ProofingError (SD-2834)

Three surgical fixes to the curated public-contract file
`packages/superdoc/src/core/types/index.ts`. Each one was visible in the
deep audit's tier-4-public-contract bucket and represents direct
customer IntelliSense pain on a documented public API.

1. `EditorTransactionEvent.transaction: any` -> `Transaction` from
   `prosemirror-state`. Anyone hovering over an `onTransaction`
   callback's transaction param now sees the real ProseMirror type with
   `.docChanged`, `.steps`, `.selection`, etc., instead of `any`.
   `prosemirror-state` is already an optional peer dep, consistent with
   the existing pattern for `@hocuspocus/provider`.

2. `Config.onAwarenessUpdate.states: any[]` -> `AwarenessState[]`. New
   small public type defines the documented fields SuperDoc populates
   per remote client (`user`, `clientId`, `color`) plus an index
   signature for application-specific keys. y-protocols' Awareness type
   itself uses `Record<string, any>` for the value side, so a precise
   imported type was not available; defined locally rather than
   collapsing to `unknown[]` per the audit README's guidance against
   draining with `unknown` when a real shape is documentable. Removes
   the existing `eslint-disable @typescript-eslint/no-explicit-any` on
   the same line.

3. `ProofingError.cause?: any` -> `unknown`. Error causes are genuinely
   opaque (whatever the proofing provider threw), so `unknown` is the
   correct level of precision per the Error-cause convention.
   Consumers narrow with `instanceof` or shape checks before reading.

Also re-exports `AwarenessState` via the JSDoc typedef in
`packages/superdoc/src/index.js`, adds the AssertNotAny assertion to
`tests/consumer-typecheck/src/all-public-types.ts`, and updates the
existing modules-config-passthrough fixture to consume the new type
through documented field access.

Verified:
- node tests/consumer-typecheck/typecheck-matrix.mjs: 53/0
- node tests/consumer-typecheck/deep-type-audit.mjs: 1799 -> 1791
  (tier-4-public-contract: 26 -> 18)
- AssertNotAny<AwarenessState> passes (check-public-types gate)
@caio-pizzol caio-pizzol requested a review from a team as a code owner May 12, 2026 19:49
@linear
Copy link
Copy Markdown

linear Bot commented May 12, 2026

SD-2834

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 784db7fddd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/superdoc/src/core/types/index.ts Outdated
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Code review caught that `packages/react/src/types.ts:71` defines a
hand-mirrored `SuperDocTransactionEvent` interface that still had
`transaction: any` after the canonical `EditorTransactionEvent` was
narrowed to `Transaction` in the previous commit. The mirror's JSDoc
explicitly says "Mirrors superdoc's EditorTransactionEvent", so React
consumers using `@superdoc-dev/react` would have kept seeing
`transaction: any` and lost IntelliSense - the exact symptom this PR is
meant to fix.

Sync the React mirror:
- Add `Transaction` to the `import type { ... } from 'superdoc'`
  (avoids adding `prosemirror-state` as a new dep; superdoc already
  re-exports the type via JSDoc typedef)
- Change `transaction: any` -> `transaction: Transaction`
- Drop "or transaction-like payload" from the JSDoc to match the
  canonical wording

Verified the awareness and proofing fixes don't need React mirrors -
those flow through `Config` directly (which React extracts from
SuperDoc's constructor parameters) so consumers already see them.

Verified:
- pnpm --filter @superdoc-dev/react run type-check: passes
Code review caught that the previous AwarenessState shape pointed
consumers at fields that don't exist at runtime. The
`awarenessStatesToArray` helper at `shared/common/collaboration/awareness.ts`
returns `{ clientId, ...value.user, color }` per entry - i.e., the
remote user's `name`, `email`, `image` are SPREAD onto the top of the
state, not nested under `state.user`.

The first cut typed `user?: User`, which made `state.user?.name`
typecheck (always undefined at runtime) while real fields like
`state.name` and `state.email` only resolved through the
`unknown` index signature. That's worse than `any` - it documents
the wrong shape.

Flip AwarenessState to `extends User`, matching the runtime spread.
Update the doc comment to spell out the flatten and point consumers at
`state.name` / `state.email` directly. Update the consumer fixture to
read the flattened fields (`state.name`, `state.email`, `state.clientId`,
`state.color`, plus the index-signature `state['customField']`).

Verified:
- pnpm --filter superdoc run pack:es: ok
- node tests/consumer-typecheck/typecheck-matrix.mjs: 53/0
- node tests/consumer-typecheck/deep-type-audit.mjs: 1791 (unchanged)
@caio-pizzol caio-pizzol self-assigned this May 13, 2026
@caio-pizzol caio-pizzol merged commit f390043 into main May 14, 2026
71 checks passed
@caio-pizzol caio-pizzol deleted the caio-pizzol/SD-2834-tier4-public-contract branch May 14, 2026 12:09
@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 14, 2026

🎉 This PR is included in @superdoc-dev/mcp v0.3.0-next.92

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 14, 2026

🎉 This PR is included in vscode-ext v2.3.0-next.138

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 14, 2026

🎉 This PR is included in @superdoc-dev/react v1.2.0-next.136

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 14, 2026

🎉 This PR is included in superdoc-cli v0.8.0-next.107

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 14, 2026

🎉 This PR is included in superdoc v1.30.0-next.88

The release is available on GitHub release

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.

2 participants