Skip to content

full type coverage across user facing packages - #14319

Merged
italojs merged 158 commits into
typescript-changes-consolidatedfrom
ts/type-coverage
Aug 26, 2026
Merged

italojs merged 158 commits into
typescript-changes-consolidatedfrom
ts/type-coverage

Conversation

@italojs

@italojs italojs commented Apr 7, 2026 •

Copy link
Copy Markdown
Member

Ships a .d.ts for every user-facing Meteor package, a type test for every declaration, and CI gates so it can't regress.

User-facing = packages an app imports (meteor/mongo, meteor/accounts-base, …) or that augment a shared public surface (e.g. accounts-password → Meteor.loginWithPassword). Build/internal/deprecated packages are out of scope (waived in a manifest).

devel vs this branch

devel this branch
Packages with a .d.ts 26/74 (35%) 74/74 (100%)
type-coverage (any-free) 81% (2,043 any) 99.9% (6 any)
Declarations covered by a type test 0/377 (0%) 569/569 (100%)
.d.ts / .test-d.ts files 27 / 0 75 / 75

What changed

  • One .d.ts per package instead of the old grouped/central files — co-located with the package it types. Split-out symbols are re-exported so import paths don't change (e.g. DDPServer now lives in ddp-server, re-exported by name from meteor/ddp; each OAuth Meteor.loginWith<Service> moved into its accounts-<service> package).
  • Type tests: every declaration has a sibling *.test-d.ts using expect-type (asserts real param/return types, not just "is a function").
  • New tooling (.github/workflows/type-coverage.yml): adds check-type-test-coverage for the types:dts-test-coverage gate (--min 100), plus expect-type, type-coverage, and a breadth gate (scripts/type-coverage/) that fails if a user-facing package has no .d.ts.
  • Types are delivered to apps via zodern:types; the TS app skeletons now prefer these over @types/meteor.
  • All declarations were audited against the runtime (~130 type-vs-runtime fixes).

…ompatibility

TypeScript 6 (currently in beta) enforces TS1540: A 'namespace' declaration
should not be declared using the 'module' keyword. Please use the 'namespace'
keyword instead.

This changes the namespace-like declarations in:
- packages/mongo/mongo.d.ts (MongoInternals)
- packages/webapp/webapp.d.ts (WebApp, WebAppInternals)

Fixes #14253
@netlify

netlify Bot commented Apr 7, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for v3-migration-docs canceled.

Name Link
🔨 Latest commit b95bde3
🔍 Latest deploy log https://app.netlify.com/projects/v3-migration-docs/deploys/6a7a043ff9acbb00098afb53

@netlify

netlify Bot commented Apr 7, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for v3-meteor-api-docs ready!

Name Link
🔨 Latest commit 988288f
🔍 Latest deploy log https://app.netlify.com/projects/v3-meteor-api-docs/deploys/6a7ca040f43a0e0008b2b7c2
😎 Deploy Preview https://deploy-preview-14319.docs-online.meteor.com
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Apr 7, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request systematically tightens TypeScript type definitions across the Meteor framework packages by replacing permissive any types with specific types like unknown, Record<string, unknown>, and domain-specific types (e.g., EJSONable, FieldValue). It adds new type declaration files for previously untyped packages, introduces corresponding type test files to validate declarations, implements a new TypeScript type-coverage measurement tool, and updates configuration.

Changes

Cohort / File(s) Summary
Type System Tightening (any → unknown/specific)
packages/accounts-base/accounts-base.d.ts, packages/check/check.d.ts, packages/ddp-rate-limiter/ddp-rate-limiter.d.ts, packages/ddp-server/dummy_document_view.ts, packages/ddp-server/session_collection_view.ts, packages/ddp-server/session_document_view.ts, packages/ddp/ddp.d.ts, packages/ejson/ejson.d.ts, packages/email/email.d.ts, packages/logging/logging.d.ts, packages/meteor/meteor.d.ts, packages/mongo/cursor.ts, packages/mongo/cursor_description.ts, packages/mongo/mongo.d.ts, packages/mongo/observe_multiplex.ts, packages/mongo/oplog_tailing.ts, packages/mongo/oplog_v2_converter.ts, packages/mongo/polling_observe_driver.ts, packages/promise/promise.d.ts, packages/roles/definitions.d.ts, packages/session/session.d.ts, packages/tracker/tracker.d.ts, packages/webapp/webapp.d.ts
Replaced any with unknown, Record<string, unknown>, EJSONable, domain-specific types (e.g., FieldValue, OplogValue), and typed callbacks. Type signatures narrowed from permissive to strict (e.g., any[] → EJSONable[], Function → (computation: Computation) => void).
New Type Declaration Files
packages/base64/base64.d.ts, packages/callback-hook/callback-hook.d.ts, packages/diff-sequence/diff-sequence.d.ts, packages/id-map/id-map.d.ts, packages/mongo-id/mongo-id.d.ts, packages/ordered-dict/ordered-dict.d.ts, packages/retry/retry.d.ts, packages/routepolicy/routepolicy.d.ts, packages/server-render/server-render.d.ts, packages/sha/sha.d.ts, packages/url/url.d.ts, packages/webapp-hashing/webapp-hashing.d.ts
Added 12 new public TypeScript declaration files providing complete type definitions for previously untyped packages (interfaces, classes, function signatures with full generic constraints).
Type Validation Test Files
packages/base64/base64.test-d.ts, packages/callback-hook/callback-hook.test-d.ts, packages/ejson/ejson.test-d.ts, packages/fetch/fetch.test-d.ts, packages/id-map/id-map.test-d.ts, packages/mongo-id/mongo-id.test-d.ts, packages/mongo/mongo.test-d.ts, packages/ordered-dict/ordered-dict.test-d.ts, packages/promise/promise.test-d.ts, packages/random/random.test-d.ts, packages/reactive-dict/reactive-dict.test-d.ts, packages/reactive-var/reactive-var.test-d.ts, packages/retry/retry.test-d.ts, packages/session/session.test-d.ts, packages/sha/sha.test-d.ts, packages/url/url.test-d.ts
Added 16 compile-time type assertion test files using expectTypeOf and expectType helpers to validate exported declaration signatures and generic types.
TypeScript Coverage Tooling
scripts/check-type-test-coverage/index.js, scripts/check-type-test-coverage/src/lib.js, scripts/check-type-test-coverage/src/tsAnalyze.js, scripts/check-type-test-coverage/src/files.js, scripts/check-type-test-coverage/src/report.js, scripts/check-type-test-coverage/src/utils.js, scripts/check-type-test-coverage/test/cli.test.js, scripts/check-type-test-coverage/test/lib.test.js, scripts/check-type-test-coverage/test/files.test.js, scripts/check-type-test-coverage/test/report.test.js, scripts/check-type-test-coverage/test/utils.test.js, scripts/check-type-test-coverage/package.json, scripts/check-type-test-coverage/README.md
New CLI tool and library to measure type-test coverage by matching exported declarations against assertions in paired *.test-d.ts files using TypeScript compiler API, including symbol resolution, alias unwrapping, and coverage reporting with optional JSON output.
Coverage Tooling Test Fixtures
scripts/check-type-test-coverage/test/__fixtures__/simple/*, scripts/check-type-test-coverage/test/__fixtures__/partial/*, scripts/check-type-test-coverage/test/__fixtures__/empty-dts/*, scripts/check-type-test-coverage/test/__fixtures__/orphan/*, scripts/check-type-test-coverage/test/__fixtures__/decls/*, scripts/check-type-test-coverage/test/__fixtures__/reexport/*
Added test fixtures covering various declaration/assertion scenarios: full coverage, partial coverage, empty declarations, orphan tests, type re-exports, and diverse export kinds (interfaces, classes, functions, enums, namespaces, defaults).
Helper & Configuration
scripts/check-type-test-coverage/test/helpers/tmpProject.js, package.json, tsconfig.json, tools/unit-tests/jest.config.js, packages/mongo/connection_options.ts
Updated CI scripts (ts-coverage:ci and new ts-coverage:table), added path aliases to tsconfig.json for meteor/* imports, added Jest exclusion pattern, and added ambient TypeScript declarations to connection_options.ts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Setup ts type coverage #14320 — Related TypeScript coverage tooling and CI script enhancements (package.json ts-coverage updates and new type coverage infrastructure).

Suggested reviewers

  • nachocodoner
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: improving TypeScript type coverage across user-facing packages.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ts/type-coverage

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
packages/mongo/oplog_tailing.ts (1)

444-454: ⚠️ Potential issue | 🟠 Major

Return type string conflicts with _id being string | Record<string, unknown>.

The function signature claims to return string, but according to the updated types:

  • op.o._id is string | Record<string, unknown> | undefined
  • op.o2?._id is string | Record<string, unknown>

When _id is an ObjectId (the Record<string, unknown> case), returning it as string is incorrect. Additionally, op.o2 is optional but accessed directly at line 448.

Suggested fix
-export function idForOp(op: OplogEntry): string {
+export function idForOp(op: OplogEntry): string | Record<string, unknown> {
   if (op.op === 'd' || op.op === 'i') {
-    return op.o._id;
+    return op.o._id!;
   } else if (op.op === 'u') {
-    return op.o2._id;
+    return op.o2!._id;
   } else if (op.op === 'c') {
     throw Error("Operator 'c' doesn't supply an object with id: " + JSON.stringify(op));
   } else {
     throw Error("Unknown op: " + JSON.stringify(op));
   }
 }

Alternatively, if the callers expect string, consider adding runtime coercion or keeping the return type as-is with explicit casts and a comment explaining the assumption.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/oplog_tailing.ts` around lines 444 - 454, The idForOp
function's return type and access are incorrect: update the signature of idForOp
to return string | Record<string, unknown> (or string | Record<string,unknown> |
undefined if you want to preserve missing ids), use optional chaining when
reading op.o2 (i.e. op.o2?._id) to avoid runtime errors, and add a runtime guard
that either throws when the resolved _id is undefined or normalizes object _id
values to strings (via toString()/JSON.stringify() per caller expectations);
ensure these changes are applied in the idForOp function and any callers are
adjusted if you change the return type.
tools/fs/safe-watcher-legacy.ts (1)

351-355: ⚠️ Potential issue | 🟡 Minor

Bug: Comparing object to function instead of object identity check.

Line 352 compares statWatchers[absPath] (an object) to statWatch (the function), which will never be true. Should compare to statWatcher (the local object variable) to verify the watcher identity before cleanup.

Proposed fix
         newStat.on("stop", () => {
-            if (statWatchers[absPath] === statWatch) {
+            if (statWatchers[absPath] === statWatcher) {
                 delete statWatchers[absPath];
             }
         });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/fs/safe-watcher-legacy.ts` around lines 351 - 355, The cleanup handler
on newStat's "stop" event currently compares statWatchers[absPath] to statWatch
(the stop function) which is wrong; change the comparison to the watcher object
variable (statWatcher) so the identity check correctly verifies the registered
watcher before deleting statWatchers[absPath]; update the callback attached in
newStat.on("stop", ...) to use statWatcher in the equality test to avoid
erroneous deletion.
packages/mongo/observe_multiplex.ts (1)

195-203: ⚠️ Potential issue | 🟠 Major

Promise detection and promise chaining logic need correction.

  1. Replace the structural check 'catch' in result with Meteor._isPromise(result), which checks for the standard .then method and is consistent with usage throughout the codebase.

  2. Line 203 handle.initialAddsSent.then(result) is incorrect—.then() expects a function callback. This passes a value directly, breaking the promise chain. Remove this line or clarify the intended logic (likely should be removed if the promise error handling on line 196 is sufficient).

Suggested fix
-        if (result && typeof result === 'object' && result !== null && 'catch' in result) {
-          (result as Promise<unknown>).catch((error: unknown) => {
+        if (Meteor._isPromise(result)) {
+          (result as Promise<unknown>).catch((error: unknown) => {
             console.error(
               `Error in observeChanges callback ${callbackName}:`,
               error
             );
           });
         }
-        handle.initialAddsSent.then(result);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/observe_multiplex.ts` around lines 195 - 203, Replace the
ad-hoc promise detection and incorrect chaining: use Meteor._isPromise(result)
instead of checking for 'catch' in result when inspecting the value returned
from the observeChanges callback (refer to the variable result and
callbackName), and remove the erroneous handle.initialAddsSent.then(result) call
since .then expects a function—either delete that line or replace it with an
appropriate function if there was intended behavior; ensure any promise returned
is handled via .catch as already implemented for the promise case.
packages/meteor/meteor.d.ts (1)

165-171: ⚠️ Potential issue | 🟠 Major

Add a callback overload for Meteor.call.

The current rest-arg type excludes functions, so callback-style calls like Meteor.call("method", arg, callback) fail type-checking. This breaks backwards compatibility with established Meteor patterns. Meteor.apply supports callbacks correctly via its asyncCallback parameter; Meteor.call needs an equivalent overload to type-check valid code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/meteor/meteor.d.ts` around lines 165 - 171, The current Meteor.call
declaration disallows function callbacks because the rest-arg type excludes
functions; add an overload for Meteor.call that accepts the same name and args
but allows the final argument to be an async callback (e.g., (error: Error |
null, result?: Result) => void) similar to Meteor.apply's asyncCallback
parameter. Add a generic overload for function call signature for
Meteor.call(name: string, ...args: (EJSONable | EJSONableProperty)[], callback:
(error: Error | null, result?: Result) => void): void (or equivalent) so
callback-style usage type-checks while preserving the existing
promise-style/returning Result signature.
packages/mongo/mongo.d.ts (2)

6-16: ⚠️ Potential issue | 🔴 Critical

UnionOmit's new key constraint breaks OptionalId for schemas without _id.

OptionalId on Line 16 instantiates UnionOmit<TSchema, '_id'> for arbitrary schemas, but K extends keyof T requires _id to exist as a key in TSchema. This breaks the documented API—the insert method docstring states "May not yet have an _id attribute, in which case Meteor will generate one for you," meaning schemas without explicitly defined _id fields should be supported. Users with such schemas (a common pattern) will now get compile errors when calling insert().

Fix
-export type UnionOmit<T, K extends keyof T> = T extends T
+export type UnionOmit<T, K extends PropertyKey> = T extends T
   ? Pick<T, Exclude<keyof T, K>>
   : never;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/mongo.d.ts` around lines 6 - 16, The type parameter constraint
on UnionOmit is too strict and requires K to be a key of T, breaking OptionalId
when TSchema lacks an _id; update the UnionOmit declaration so K is any property
key (e.g., change "K extends keyof T" to "K extends PropertyKey" or similar) so
UnionOmit<T, '_id'> works even if TSchema has no _id, leaving OptionalId as
defined (OptionalId<TSchema> = UnionOmit<TSchema, '_id'> & { _id?: string |
NpmModuleMongodb.ObjectId }) and keeping Selector/Modifier untouched.

24-54: ⚠️ Potential issue | 🟠 Major

Options should support type-changing transforms via a second type parameter.

Currently, transform?: Transform<T> constrains transforms to return T, preventing find(..., { transform: doc => new ViewModel(doc) }) from type-checking correctly even though DispatchTransform is designed to infer the transformed result type.

To fix this, Options should accept both T and U type parameters: Options<T, U = T> with transform?: Transform<T, U>. This would allow the find() overload to properly infer cursor result types when transforms change the shape.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/mongo.d.ts` around lines 24 - 54, Update the Options generic
to accept a second result type parameter so transforms can change the output
type: change Options<T> to Options<T, U = T> and make the transform property use
Transform<T, U>; ensure related usages that rely on DispatchTransform still work
with the new signature (DispatchTransform, Transform, and the transform?: field
in Options should reference the two-parameter Transform<T, U> so find()/cursor
type inference can reflect transformed result types).
🧹 Nitpick comments (19)
packages/routepolicy/routepolicy.d.ts (1)

1-1: Consider exporting RoutePolicyType for downstream type reuse.

Keeping RoutePolicyType non-exported works internally, but exporting it would let callers annotate their own APIs without duplicating the union.

Suggested tweak
-type RoutePolicyType = 'network' | 'static-online';
+export type RoutePolicyType = 'network' | 'static-online';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/routepolicy/routepolicy.d.ts` at line 1, The type alias
RoutePolicyType is currently internal; export it so downstream modules can reuse
the union type. Update the declaration for RoutePolicyType (the existing "type
RoutePolicyType = 'network' | 'static-online';") to be exported (export type
RoutePolicyType = ...), ensure any references in this file (and consumers)
import the symbol rather than duplicating the union, and run TS checks to
confirm no further changes are needed.
packages/mongo/oplog_v2_converter.ts (1)

58-61: ArrayOperator interface is incomplete — missing s${number} index for nested diffs.

The code at line 171 checks if (position[0] === 's') and handles nested OplogDiff within array operators, but the interface only declares u${number} keys. The regex arrayOperatorKeyRegex also accepts s<number> patterns. This causes the cast at line 167 to include OplogDiff to compensate for the missing type.

Suggested interface update
 interface ArrayOperator {
   a: true;
   [key: `u${number}`]: OplogValue;
+  [key: `s${number}`]: OplogDiff;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/oplog_v2_converter.ts` around lines 58 - 61, The ArrayOperator
interface is missing the s${number} index for nested diffs; update the interface
named ArrayOperator to include keys of the form `s${number}` mapped to OplogDiff
(or OplogDiff | OplogValue if both appear), so it aligns with
arrayOperatorKeyRegex and the runtime check that inspects position[0] === 's'
(this will remove the need for the cast around the value at the spot where
position is parsed). Ensure the template-literal index signature for
`s${number}` is added alongside the existing `u${number}` signature in the
ArrayOperator declaration.
packages/mongo/oplog_tailing.ts (1)

326-328: Replace inline type annotation with @types/mongodb-uri.

The hardcoded inline type on line 327 doesn't catch API mismatches at compile time. The @types/mongodb-uri package exists on npm and should be installed to provide proper type checking for mongodb-uri imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/oplog_tailing.ts` around lines 326 - 328, Remove the inline
ad-hoc type on the mongodb-uri require in _startTailing and install the
`@types/mongodb-uri` dev dependency; replace the line "const mongodbUri: { parse:
(uri: string) => { database: string } } = require('mongodb-uri');" with a
properly typed import (e.g., import * as mongodbUri from 'mongodb-uri' or import
mongodbUri = require('mongodb-uri') depending on your tsconfig/moduleInterop)
and keep the existing call to mongodbUri.parse(this._oplogUrl) so the compiler
uses the official type definitions from `@types/mongodb-uri`.
packages/mongo/observe_multiplex.ts (1)

220-223: Unused variable _docId from destructuring.

The destructured _docId is never used. Consider using a bare destructuring pattern to discard it.

Proposed fix
-      const { _id: _docId, ...docFields } = clonedDoc;
+      const { _id: _, ...docFields } = clonedDoc;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/observe_multiplex.ts` around lines 220 - 223, The
destructuring currently binds an unused `_docId` from `clonedDoc`; change the
pattern to discard the id while keeping the rest as `docFields` (e.g., use a
throwaway binding) so the `_id` isn't assigned to an unused variable; update the
line that currently does `const { _id: _docId, ...docFields } = clonedDoc;` to
discard `_id` (while leaving `docFields` intact) in the same block where
`clonedDoc` is created (the code that uses `handle.nonMutatingCallbacks` and
`EJSON.clone`).
packages/retry/retry.d.ts (1)

38-38: Consider marking _timeout as private or removing from public type surface.

The underscore prefix conventionally indicates a private method. If this is intended for internal use only, consider omitting it from the public .d.ts or documenting why it's exposed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/retry/retry.d.ts` at line 38, The declaration exposes a
supposedly-private method _timeout in the public type surface; either make
_timeout truly private in the source class (so it is omitted from the generated
.d.ts), remove it from the exported type, or explicitly mark it as internal
(e.g., add a /** `@internal` */ JSDoc) so the declaration generator omits it;
alternatively, if it is intended to be public, rename/remove the underscore and
add documentation. Locate the _timeout method in the retry package (the class or
interface declaring _timeout) and apply one of these fixes, then regenerate the
type declarations.
tools/fs/watch.ts (1)

300-305: Function appears unused; remove it or add validation if intended as public API.

The reFromJSON function constructs RegExp directly from JSON without validation, creating a theoretical ReDoS vulnerability. However, WatchSet.fromJSON() is not called anywhere in the codebase, making it dead code. Either remove it or, if it's intended as a public API for future use, add validation for regex complexity to prevent catastrophic backtracking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/fs/watch.ts` around lines 300 - 305, The helper reFromJSON currently
parses unvalidated regex strings (and WatchSet.fromJSON isn't referenced),
creating dead code and a potential ReDoS risk; either delete reFromJSON (and any
WatchSet.fromJSON entrypoints) if the JSON deserialization is unused, or harden
it: make reFromJSON a defensive API that validates inputs (e.g., validate type,
max pattern length, reject known-dangerous constructs or excessive
nested/adjacent quantifiers, and wrap new RegExp in try/catch) and update
WatchSet.fromJSON to call this safe parser so any external use is protected.
packages/webapp-hashing/webapp-hashing.d.ts (1)

1-8: Export ManifestResource interface for reuse.

The ManifestResource interface is defined but not exported. Other packages like webapp could benefit from importing this type for their manifest-related declarations, improving type consistency across the codebase.

Export the interface
-interface ManifestResource {
+export interface ManifestResource {
   type: string;
   where: string;
   path: string;
   hash: string;
   url?: string;
   replaceable?: boolean;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webapp-hashing/webapp-hashing.d.ts` around lines 1 - 8, The
ManifestResource interface is currently internal; export it so other packages
can import and reuse the type. Update the declaration by converting the local
interface into an exported type (export interface ManifestResource) so consumers
(e.g., callers referencing ManifestResource) can import it for manifest-related
typings and ensure consistent cross-package declarations.
.github/workflows/type-coverage.yml (1)

7-12: Add npm cache to speed up CI.

The workflow runs npm install on every PR without caching, which can be slow. Consider adding npm caching to improve workflow performance.

Add caching
       - uses: actions/checkout@v4
       - uses: actions/setup-node@v4
         with:
           node-version: 22.x
+          cache: 'npm'
       - run: npm install
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/type-coverage.yml around lines 7 - 12, The CI currently
runs the step "- run: npm install" without caching; add an actions/cache step
before that to cache npm's cache directories (e.g., ~/.npm and .npm/_cacache)
using actions/cache@v3, with a key that includes runner.os, the node-version and
a hash of package-lock.json (and a sensible restore-keys fallback) so installs
are restored from cache when dependencies and Node version haven't changed;
place this cache step immediately before the "npm install" step in the workflow.
packages/webapp/webapp.d.ts (1)

30-30: Consider aligning manifest type with ManifestResource[] from webapp-hashing.

The manifest property is typed as Record<string, unknown>[], but packages/webapp-hashing/webapp-hashing.d.ts defines a more specific ManifestResource interface with known properties (type, where, path, hash, url, replaceable). Using ManifestResource[] here would provide better type safety and consistency across the webapp packages.

Suggested type alignment
+import { ManifestResource } from 'meteor/webapp-hashing';
+
 export declare module WebApp {
   var defaultArch: string;
   var clientPrograms: {
     [key: string]: {
       format: string;
-      manifest: Record<string, unknown>[];
+      manifest: ManifestResource[];
       version: string;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webapp/webapp.d.ts` at line 30, The manifest property is currently
typed as Record<string, unknown>[], losing the stronger shape defined by
ManifestResource in webapp-hashing; update the manifest declaration to use
ManifestResource[] instead and import that type (using an `import type {
ManifestResource } from 'webapp-hashing'` or the appropriate package export) so
the webapp.d.ts export aligns with the shared interface, and ensure any existing
references compile after replacing `manifest: Record<string, unknown>[]` with
`manifest: ManifestResource[]`.
tsconfig.json (1)

9-13: Minor: outDir is redundant when noEmit is true.

With noEmit: true, TypeScript won't emit any output files, making outDir ineffective. Consider removing it to reduce configuration noise, though it's harmless.

♻️ Optional cleanup
     "outDir": "./dist",
-    "noEmit": true,
+    "noEmit": true

Or remove outDir entirely if emit is never intended.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tsconfig.json` around lines 9 - 13, The tsconfig currently sets both "outDir"
and "noEmit"; remove the redundant "outDir" field from the config (delete the
"outDir": "./dist", entry) since "noEmit": true disables emission, leaving only
"noEmit" (and other options like "allowJs"/"checkJs"/"moduleResolution") to
avoid configuration noise; ensure no build scripts rely on outDir elsewhere
before removing.
packages/mongo-id/mongo-id.d.ts (1)

10-11: Consider documenting _str as internal.

The underscore prefix on _str suggests it's meant to be internal, but it's exposed publicly in the declaration. If external access is intended for type-safe usage, consider adding a JSDoc note clarifying this.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo-id/mongo-id.d.ts` around lines 10 - 11, The ObjectID
declaration exposes an underscored field _str but lacks documentation that it's
internal; update the declaration for class ObjectID by marking _str as internal
(either add a JSDoc comment like "@internal" or "@private" above _str to
indicate it's not part of the public API, or change the visibility to
private/protected if external access is not intended) and, if you must keep it
public for type-safe reasons, add a JSDoc note clarifying its internal usage and
that it should not be relied upon by consumers so intent is clear.
packages/ddp-server/session_document_view.ts (1)

1-2: FieldValue type is duplicated across DDP server files.

This type is identical to the one in dummy_document_view.ts. Consider extracting it to a shared module (e.g., ddp-server/types.ts) to ensure consistency and reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ddp-server/session_document_view.ts` around lines 1 - 2, The
FieldValue type is duplicated; extract it into a single shared module (create a
types.ts that exports type FieldValue), replace the inline type in
session_document_view.ts and dummy_document_view.ts by importing the exported
FieldValue, and remove the duplicate definitions so both files reference the
same exported symbol; ensure the export name is exactly FieldValue and update
the import statements in any files that previously declared the type.
packages/mongo/cursor_description.ts (1)

32-33: The @ts-ignore comment suppresses a type error that should be addressed.

Since this PR aims to improve type coverage, consider properly typing Mongo.Collection._rewriteSelector instead of suppressing the error. This would contribute to the coverage goals.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/cursor_description.ts` around lines 32 - 33, Remove the //
`@ts-ignore` and properly type Mongo.Collection._rewriteSelector instead of
suppressing the error: locate the declaration for Mongo.Collection (or add a
.d.ts in the mongo package) and add an explicit signature for _rewriteSelector
(e.g., accept the actual selector input type and return the normalized selector
type used by this.selector), or alternatively cast the result to the expected
selector type when assigning to this.selector in cursor_description.ts;
reference Mongo.Collection._rewriteSelector and this.selector when making the
change so the compiler can validate types without using `@ts-ignore`.
packages/mongo/cursor.ts (2)

102-105: Symbol index access needs careful handling.

Casting methodName as string when it could be a Symbol (e.g., Symbol.iterator) may cause issues. While this works at runtime because JavaScript handles symbol property access, the type assertion is misleading.

♻️ Suggested improvement for clarity
   (Cursor.prototype as Record<string | symbol, unknown>)[methodName] = function(this: Cursor<unknown>, ...args: unknown[]): unknown {
     const cursor = setupAsynchronousCursor(this, methodName);
-    return cursor[methodName as string](...args);
+    return cursor[methodName as keyof SynchronousCursor](...args);
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/cursor.ts` around lines 102 - 105, The code casts methodName
to string when calling the method, which is misleading for symbol keys (e.g.,
Symbol.iterator); update the call in the Cursor.prototype assignment so you
access and invoke the property using the actual methodName (no string cast) or
use Reflect.get to obtain the function and then call it with .apply, ensuring
symbol properties are supported; locate the assignment on Cursor.prototype that
uses setupAsynchronousCursor(this, methodName) and change the invocation to
retrieve and invoke the property using methodName (or Reflect.get) rather than
casting to string.

66-69: Function type should be replaced with specific callback types.

Using Function is discouraged as it's essentially any for functions. Since you're improving type coverage, consider using the actual callback signatures here to maintain consistency with other typing improvements in this PR.

♻️ Suggested improvement
-  _publishCursor(sub: { added: Function; changed: Function; removed: Function }): unknown {
+  _publishCursor(sub: { 
+    added: (collectionName: string, id: string, fields: Record<string, unknown>) => void;
+    changed: (collectionName: string, id: string, fields: Record<string, unknown>) => void;
+    removed: (collectionName: string, id: string) => void;
+  }): unknown {
     const collection = this._cursorDescription.collectionName;
     return Mongo.Collection._publishCursor(this, sub, collection);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/cursor.ts` around lines 66 - 69, The parameter type for
_publishCursor currently uses the broad Function type; replace it with precise
callback signatures to reflect Meteor subscription methods: use an object type
where added: (collectionName: string, id: string, fields: Record<string,
unknown>) => void; changed: (collectionName: string, id: string, fields:
Record<string, unknown>) => void; and removed: (collectionName: string, id:
string) => void (or define a small interface like SubscriptionCallbacks and use
it). Update the _publishCursor signature to accept that typed object
(referencing _publishCursor and the sub parameter) so callers and the call to
Mongo.Collection._publishCursor remain type-safe.
packages/mongo/polling_observe_driver.ts (2)

73-73: Fragile type assertion for internal Meteor API.

The complex cast (Meteor as unknown as { _AsynchronousQueue: new () => AsyncQueue })._AsynchronousQueue is brittle. If the internal API changes, this will fail silently at compile time but break at runtime.

Consider adding these internal types to a dedicated internal types file or augmenting the Meteor namespace properly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/polling_observe_driver.ts` at line 73, The current brittle
cast for creating this._taskQueue uses (Meteor as unknown as {
_AsynchronousQueue: new () => AsyncQueue })._AsynchronousQueue which can break
silently; replace this ad-hoc assertion by declaring a proper internal type
and/or augmenting the Meteor namespace (e.g., add an internal types file that
declares interface Meteor { _AsynchronousQueue: new () => AsyncQueue } or a
dedicated InternalMeteor type) and then use that declared type when
instantiating _AsynchronousQueue (or wrap creation in a small factory that
checks existence at runtime and throws a clear error). Target symbols:
_taskQueue, Meteor, _AsynchronousQueue, AsyncQueue, and use the new
declaration/augmentation instead of the inline cast.

113-114: Repeated inline type assertions should be extracted.

The Package['facts-base'] type assertion is duplicated. Extract to a helper or type declaration.

♻️ Extract type for reuse
// At top of file
type FactsPackage = { Facts?: { incrementServerFact: (pkg: string, fact: string, val: number) => void } } | undefined;

// Usage
(Package['facts-base'] as FactsPackage)?.Facts?.incrementServerFact(...);

Also applies to: 209-210

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mongo/polling_observe_driver.ts` around lines 113 - 114, Extract the
repeated inline type assertion for Package['facts-base'] into a reusable alias
(e.g., FactsPackage) declared near the top of polling_observe_driver.ts, then
replace the duplicated casts like (Package['facts-base'] as { Facts?: {
incrementServerFact: (...) } } | undefined) with a single cast using the alias
(Package['facts-base'] as FactsPackage) wherever used (notably in the
incrementServerFact calls at the observe-driver polling sites around the current
usages and the occurrences at lines referenced 209-210).
packages/ddp-rate-limiter/ddp-rate-limiter.d.ts (1)

10-10: Inconsistent typing for Matcher.type field.

The type field accepts string directly but the callback predicate constrains to 'method' | 'subscription'. For consistency, the direct string value should also be constrained.

♻️ Consistent type constraint
-    type?: string | ((type: 'method' | 'subscription') => boolean) | undefined;
+    type?: 'method' | 'subscription' | ((type: 'method' | 'subscription') => boolean) | undefined;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ddp-rate-limiter/ddp-rate-limiter.d.ts` at line 10, The Matcher.type
union is inconsistent: it allows any string but the predicate variant only
accepts the literal kinds 'method' or 'subscription'; update the Matcher.type
declaration so the direct value is restricted to those same literals (i.e.,
allow the literal kinds 'method' | 'subscription' or a predicate that accepts
'method' | 'subscription') so both branches use the same constrained type;
locate the type in the ddp-rate-limiter.d.ts where Matcher.type is declared and
change its union accordingly.
packages/ddp-server/session_collection_view.ts (1)

4-6: Consider using EJSONableProperty from the ejson package for type consistency.

The FieldValue type currently uses broad types (Record<string, unknown>, unknown[]) which technically cover any value at runtime, but they lack explicit representation of EJSONableCustomType. The ejson package exports EJSONableProperty which includes all EJSON-serializable types explicitly, including custom types via EJSONableCustomType. Using it would improve type precision and alignment with the ejson module's type system, though the current implementation is functionally correct since EJSON.clone() handles any EJSON type regardless of the TypeScript union.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ddp-server/session_collection_view.ts` around lines 4 - 6, Replace
the broad FieldValue type alias with the ejson package's EJSONableProperty to
align types with the EJSON type system: import EJSONableProperty from 'ejson'
and change the FieldValue declaration (or references) to use EJSONableProperty
(add "| undefined" if you need to preserve the original allowance for
undefined); this ensures EJSONableCustomType and other EJSON-specific types are
properly represented while preserving existing runtime behavior handled by
EJSON.clone().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/type-coverage.yml:
- Around line 13-14: The workflow step name "Check TypeScript type coverage
(100%)" is misleading because the npm script ts-coverage:ci uses the flag
--at-least 90; either update the workflow step name to "Check TypeScript type
coverage (90%)" or change the ts-coverage:ci script to use --at-least 100 to
match the name—locate the step in .github/workflows/type-coverage.yml and the
npm script ts-coverage:ci in package.json (look for the --at-least 90 flag) and
make the labels/thresholds consistent.

In `@package.json`:
- Around line 34-39: Remove the unnecessary root-level runtime/dev dependencies:
delete "react", "react-dom", "semantic-ui-css", and "semantic-ui-react" from
package.json and instead ensure any test apps that need them declare them in
their own package.json (since root tsconfig excludes tools/modern-tests/**);
keep "mongodb" in the root deps because packages/npm-mongo/index.d.ts relies on
its types. Ensure package.json edits only remove those four packages and do not
touch "mongodb".

In `@packages/ddp/ddp.d.ts`:
- Line 9: The apply declaration's signature is wrong: change the apply<Result =
unknown>(method: string, ...parameters: EJSONable[]): Result; to accept an args
array and optional options/callback parameters so it matches the runtime API
used by apply(name, args, options, callback); specifically update the
declaration for apply to accept method: string, args: EJSONable[], options?:
unknown, callback?: (err: Error | undefined, result?: Result) => void and return
Result so callers and the implementation (apply) align.

In `@packages/email/email.d.ts`:
- Around line 3-6: The Address interface currently requires both name and
address; make the name property optional to match nodemailer by changing the
Address interface's name member to an optional string (e.g., name?: string) so
callers can supply just an address string or an object with only address; update
any related type references or usages of Address (e.g., in send functions,
MailOptions types) to accept the adjusted interface if they perform strict
construction checks.

In `@packages/mongo/mongo.d.ts`:
- Around line 357-359: The arrayFilters type is too narrow: replace the current
definition of arrayFilters (currently typed as Record<string,
NpmModuleMongodb.Filter<T>>[] | undefined) with the upstream-compatible type
NpmModuleMongodb.Document[] | undefined so array filter objects like {
"elem.status": "A" } or { "i.score": { $gte: 10 } } are accepted; update the
declaration where arrayFilters appears in the mongo.d.ts signature (the property
named arrayFilters) to use NpmModuleMongodb.Document[] instead of Record<string,
NpmModuleMongodb.Filter<T>>[].

In `@packages/mongo/polling_observe_driver.ts`:
- Around line 164-171: The catch block in polling_observe_driver.ts assumes the
caught value `e` has a `message` property and uses it directly when calling
this._multiplexer.queryError for the first poll; instead, normalize and validate
the error before using it: in the catch around the `first` check (where `err` is
currently cast), determine a safe error message by checking `e`'s type
(instanceof Error to use e.message, string to use e, or JSON/stringify fallback)
and include a fallback like "Unknown error" so `err.message` cannot be
undefined; then pass that safe message into the Error passed to
this._multiplexer.queryError (referencing this._cursorDescription and the
`first` branch) to avoid unsafe property access.

In `@packages/roles/definitions.d.ts`:
- Around line 29-32: Remove the conflicting interface declaration "interface
RolesCollection<T> { find(...): RolesCursor<T>; findOne(...): T | undefined; }"
so it no longer collides with the top-level exported type alias RolesCollection;
delete this interface block and any duplicate references to RolesCursor<T>
within it, and if those method signatures are required ensure they're present on
the exported RolesCollection type alias (or update callers to use the existing
exported type) so no usages are left pointing to the removed interface.
- Around line 7-27: The declared local types narrow core Meteor/Mongo types and
should be replaced with or extended from the public types: update RolesCursor<T>
to use Mongo.Cursor<T> (preserving generic), replace RolesUser with Meteor.User
(or extend Meteor.User if you need extra fields), and replace RolesSortSpecifier
and RolesFieldSpecifier with the official Mongo sort/field specifier types (or
type aliases to Record<string, 1 | -1> and Mongo.FieldSpecifier if available).
Concretely, import the canonical types from the Meteor/Mongo packages and change
the declarations for RolesCursor, RolesUser, RolesSortSpecifier, and
RolesFieldSpecifier to reference those exports (or extend them) so the package
surface matches the public Meteor/Mongo APIs.

In `@packages/routepolicy/routepolicy.d.ts`:
- Around line 6-10: The declaration currently exports a class but the runtime
exports a singleton instance; update the types to export the instance shape
instead of a constructor: replace the exported class RoutePolicy with an
exported constant/namespace type representing the instantiated object (matching
the API exposed by RoutePolicy = new RoutePolicyConstructor()), keeping members
like urlPrefixTypes: Record<string, RoutePolicyType> and any instance
methods/props from RoutePolicyConstructor; remove/avoid a constructor signature
so consumers cannot call new RoutePolicy().

In `@packages/server-render/server-render.d.ts`:
- Line 59: The Callback type was narrowed to Promise<void> | void which breaks
consumers that return values; revert or soften the change by restoring the
original return type (Promise<any> | any) or make Callback generic (e.g.,
Callback<T> = (sink: Sink) => Promise<T> | T) and provide a default of
unknown/any, or add an overload/alias that preserves the old signature and mark
the void variant as new/deprecated; update the exported type named Callback
accordingly so existing TypeScript callbacks that return values continue to
compile while the runtime still ignores return values.

In `@packages/tracker/tracker.d.ts`:
- Line 22: Update the TypeScript declarations to reflect runtime behavior:
change the type of firstRunPromise from Promise<void> to Promise<unknown> (to
match the actual resolved values returned by user async autoruns) and relax
onError's parameter type from Error to unknown (e.g., onError?: ((error:
unknown) => void) | undefined) so any thrown value is accepted; adjust the
declaration for the onError property and the firstRunPromise property in
tracker.d.ts to use these types and keep JSDoc/comments in sync.

In `@tools/fs/files.ts`:
- Around line 41-43: The current sourcemap const's TypeScript type is
incomplete: extend the type annotation for the imported require('source-map') to
include both SourceNode (the class used elsewhere) and the destroy() method on
SourceMapConsumer; specifically, update the sourcemap declaration so
SourceMapConsumer's constructor signature remains, originalPositionFor exists,
and add destroy(): void, and add a SourceNode type/interface matching how
SourceNode is instantiated/used in the file so TypeScript strict mode accepts
references to SourceNode and calls to SourceMapConsumer.destroy().

In `@tools/fs/watch.ts`:
- Line 278: The fromJSON signature's directories entry types declare contents as
string | null but DirectoryEntry.contents and toJSON use string[] | null; update
the fromJSON parameter type so directories: Array<{ absPath: string; include:
Array<string | { $regex: string; $options?: string }>; exclude: Array<string | {
$regex: string; $options?: string }>; names: string[] | null; contents: string[]
| null }> to match DirectoryEntry.contents and the toJSON output, ensuring type
consistency for the fromJSON method.

In `@tools/shell-client.ts`:
- Line 59: The type for the local variable info is missing the reason property,
causing a TypeScript error when later accessing info.reason; update the
declaration of info to include reason: string (e.g., change let info: { status:
string; port: number; key: string } to include reason: string) so all accesses
like info.reason compile correctly and reflect the expected shape.

In `@tools/tool-env/profile.ts`:
- Line 450: The callback typed for session.post('Profiler.stop') is incorrect:
make the second parameter optional and require the profile property; change the
signature for the callback passed to session.post('Profiler.stop') so it is
(err: Error | null, result?: { profile: { nodes?: unknown[]; samples?:
unknown[]; timeDeltas?: unknown[]; startTime?: number; endTime?: number } }) =>
void, updating the parameter name/result typing used in that anonymous callback
to reflect result as optional and profile as required.

In `@tools/utils/eachline.ts`:
- Around line 23-26: The catch block currently uses "error: unknown" and calls
done(error) which fails TypeScript strict typing; update the error handling in
the Transform._transform callback to narrow/convert the unknown to an Error
before passing to done — e.g., check if the caught value is an instance of Error
and call done(error) in that branch, otherwise call done(new
Error(String(error))) so done always receives Error | null | undefined; locate
this change around the catch handling that references done in eachline.ts.

---

Outside diff comments:
In `@packages/meteor/meteor.d.ts`:
- Around line 165-171: The current Meteor.call declaration disallows function
callbacks because the rest-arg type excludes functions; add an overload for
Meteor.call that accepts the same name and args but allows the final argument to
be an async callback (e.g., (error: Error | null, result?: Result) => void)
similar to Meteor.apply's asyncCallback parameter. Add a generic overload for
function call signature for Meteor.call(name: string, ...args: (EJSONable |
EJSONableProperty)[], callback: (error: Error | null, result?: Result) => void):
void (or equivalent) so callback-style usage type-checks while preserving the
existing promise-style/returning Result signature.

In `@packages/mongo/mongo.d.ts`:
- Around line 6-16: The type parameter constraint on UnionOmit is too strict and
requires K to be a key of T, breaking OptionalId when TSchema lacks an _id;
update the UnionOmit declaration so K is any property key (e.g., change "K
extends keyof T" to "K extends PropertyKey" or similar) so UnionOmit<T, '_id'>
works even if TSchema has no _id, leaving OptionalId as defined
(OptionalId<TSchema> = UnionOmit<TSchema, '_id'> & { _id?: string |
NpmModuleMongodb.ObjectId }) and keeping Selector/Modifier untouched.
- Around line 24-54: Update the Options generic to accept a second result type
parameter so transforms can change the output type: change Options<T> to
Options<T, U = T> and make the transform property use Transform<T, U>; ensure
related usages that rely on DispatchTransform still work with the new signature
(DispatchTransform, Transform, and the transform?: field in Options should
reference the two-parameter Transform<T, U> so find()/cursor type inference can
reflect transformed result types).

In `@packages/mongo/observe_multiplex.ts`:
- Around line 195-203: Replace the ad-hoc promise detection and incorrect
chaining: use Meteor._isPromise(result) instead of checking for 'catch' in
result when inspecting the value returned from the observeChanges callback
(refer to the variable result and callbackName), and remove the erroneous
handle.initialAddsSent.then(result) call since .then expects a function—either
delete that line or replace it with an appropriate function if there was
intended behavior; ensure any promise returned is handled via .catch as already
implemented for the promise case.

In `@packages/mongo/oplog_tailing.ts`:
- Around line 444-454: The idForOp function's return type and access are
incorrect: update the signature of idForOp to return string | Record<string,
unknown> (or string | Record<string,unknown> | undefined if you want to preserve
missing ids), use optional chaining when reading op.o2 (i.e. op.o2?._id) to
avoid runtime errors, and add a runtime guard that either throws when the
resolved _id is undefined or normalizes object _id values to strings (via
toString()/JSON.stringify() per caller expectations); ensure these changes are
applied in the idForOp function and any callers are adjusted if you change the
return type.

In `@tools/fs/safe-watcher-legacy.ts`:
- Around line 351-355: The cleanup handler on newStat's "stop" event currently
compares statWatchers[absPath] to statWatch (the stop function) which is wrong;
change the comparison to the watcher object variable (statWatcher) so the
identity check correctly verifies the registered watcher before deleting
statWatchers[absPath]; update the callback attached in newStat.on("stop", ...)
to use statWatcher in the equality test to avoid erroneous deletion.

---

Nitpick comments:
In @.github/workflows/type-coverage.yml:
- Around line 7-12: The CI currently runs the step "- run: npm install" without
caching; add an actions/cache step before that to cache npm's cache directories
(e.g., ~/.npm and .npm/_cacache) using actions/cache@v3, with a key that
includes runner.os, the node-version and a hash of package-lock.json (and a
sensible restore-keys fallback) so installs are restored from cache when
dependencies and Node version haven't changed; place this cache step immediately
before the "npm install" step in the workflow.

In `@packages/ddp-rate-limiter/ddp-rate-limiter.d.ts`:
- Line 10: The Matcher.type union is inconsistent: it allows any string but the
predicate variant only accepts the literal kinds 'method' or 'subscription';
update the Matcher.type declaration so the direct value is restricted to those
same literals (i.e., allow the literal kinds 'method' | 'subscription' or a
predicate that accepts 'method' | 'subscription') so both branches use the same
constrained type; locate the type in the ddp-rate-limiter.d.ts where
Matcher.type is declared and change its union accordingly.

In `@packages/ddp-server/session_collection_view.ts`:
- Around line 4-6: Replace the broad FieldValue type alias with the ejson
package's EJSONableProperty to align types with the EJSON type system: import
EJSONableProperty from 'ejson' and change the FieldValue declaration (or
references) to use EJSONableProperty (add "| undefined" if you need to preserve
the original allowance for undefined); this ensures EJSONableCustomType and
other EJSON-specific types are properly represented while preserving existing
runtime behavior handled by EJSON.clone().

In `@packages/ddp-server/session_document_view.ts`:
- Around line 1-2: The FieldValue type is duplicated; extract it into a single
shared module (create a types.ts that exports type FieldValue), replace the
inline type in session_document_view.ts and dummy_document_view.ts by importing
the exported FieldValue, and remove the duplicate definitions so both files
reference the same exported symbol; ensure the export name is exactly FieldValue
and update the import statements in any files that previously declared the type.

In `@packages/mongo-id/mongo-id.d.ts`:
- Around line 10-11: The ObjectID declaration exposes an underscored field _str
but lacks documentation that it's internal; update the declaration for class
ObjectID by marking _str as internal (either add a JSDoc comment like
"@internal" or "@private" above _str to indicate it's not part of the public
API, or change the visibility to private/protected if external access is not
intended) and, if you must keep it public for type-safe reasons, add a JSDoc
note clarifying its internal usage and that it should not be relied upon by
consumers so intent is clear.

In `@packages/mongo/cursor_description.ts`:
- Around line 32-33: Remove the // `@ts-ignore` and properly type
Mongo.Collection._rewriteSelector instead of suppressing the error: locate the
declaration for Mongo.Collection (or add a .d.ts in the mongo package) and add
an explicit signature for _rewriteSelector (e.g., accept the actual selector
input type and return the normalized selector type used by this.selector), or
alternatively cast the result to the expected selector type when assigning to
this.selector in cursor_description.ts; reference
Mongo.Collection._rewriteSelector and this.selector when making the change so
the compiler can validate types without using `@ts-ignore`.

In `@packages/mongo/cursor.ts`:
- Around line 102-105: The code casts methodName to string when calling the
method, which is misleading for symbol keys (e.g., Symbol.iterator); update the
call in the Cursor.prototype assignment so you access and invoke the property
using the actual methodName (no string cast) or use Reflect.get to obtain the
function and then call it with .apply, ensuring symbol properties are supported;
locate the assignment on Cursor.prototype that uses
setupAsynchronousCursor(this, methodName) and change the invocation to retrieve
and invoke the property using methodName (or Reflect.get) rather than casting to
string.
- Around line 66-69: The parameter type for _publishCursor currently uses the
broad Function type; replace it with precise callback signatures to reflect
Meteor subscription methods: use an object type where added: (collectionName:
string, id: string, fields: Record<string, unknown>) => void; changed:
(collectionName: string, id: string, fields: Record<string, unknown>) => void;
and removed: (collectionName: string, id: string) => void (or define a small
interface like SubscriptionCallbacks and use it). Update the _publishCursor
signature to accept that typed object (referencing _publishCursor and the sub
parameter) so callers and the call to Mongo.Collection._publishCursor remain
type-safe.

In `@packages/mongo/observe_multiplex.ts`:
- Around line 220-223: The destructuring currently binds an unused `_docId` from
`clonedDoc`; change the pattern to discard the id while keeping the rest as
`docFields` (e.g., use a throwaway binding) so the `_id` isn't assigned to an
unused variable; update the line that currently does `const { _id: _docId,
...docFields } = clonedDoc;` to discard `_id` (while leaving `docFields` intact)
in the same block where `clonedDoc` is created (the code that uses
`handle.nonMutatingCallbacks` and `EJSON.clone`).

In `@packages/mongo/oplog_tailing.ts`:
- Around line 326-328: Remove the inline ad-hoc type on the mongodb-uri require
in _startTailing and install the `@types/mongodb-uri` dev dependency; replace the
line "const mongodbUri: { parse: (uri: string) => { database: string } } =
require('mongodb-uri');" with a properly typed import (e.g., import * as
mongodbUri from 'mongodb-uri' or import mongodbUri = require('mongodb-uri')
depending on your tsconfig/moduleInterop) and keep the existing call to
mongodbUri.parse(this._oplogUrl) so the compiler uses the official type
definitions from `@types/mongodb-uri`.

In `@packages/mongo/oplog_v2_converter.ts`:
- Around line 58-61: The ArrayOperator interface is missing the s${number} index
for nested diffs; update the interface named ArrayOperator to include keys of
the form `s${number}` mapped to OplogDiff (or OplogDiff | OplogValue if both
appear), so it aligns with arrayOperatorKeyRegex and the runtime check that
inspects position[0] === 's' (this will remove the need for the cast around the
value at the spot where position is parsed). Ensure the template-literal index
signature for `s${number}` is added alongside the existing `u${number}`
signature in the ArrayOperator declaration.

In `@packages/mongo/polling_observe_driver.ts`:
- Line 73: The current brittle cast for creating this._taskQueue uses (Meteor as
unknown as { _AsynchronousQueue: new () => AsyncQueue })._AsynchronousQueue
which can break silently; replace this ad-hoc assertion by declaring a proper
internal type and/or augmenting the Meteor namespace (e.g., add an internal
types file that declares interface Meteor { _AsynchronousQueue: new () =>
AsyncQueue } or a dedicated InternalMeteor type) and then use that declared type
when instantiating _AsynchronousQueue (or wrap creation in a small factory that
checks existence at runtime and throws a clear error). Target symbols:
_taskQueue, Meteor, _AsynchronousQueue, AsyncQueue, and use the new
declaration/augmentation instead of the inline cast.
- Around line 113-114: Extract the repeated inline type assertion for
Package['facts-base'] into a reusable alias (e.g., FactsPackage) declared near
the top of polling_observe_driver.ts, then replace the duplicated casts like
(Package['facts-base'] as { Facts?: { incrementServerFact: (...) } } |
undefined) with a single cast using the alias (Package['facts-base'] as
FactsPackage) wherever used (notably in the incrementServerFact calls at the
observe-driver polling sites around the current usages and the occurrences at
lines referenced 209-210).

In `@packages/retry/retry.d.ts`:
- Line 38: The declaration exposes a supposedly-private method _timeout in the
public type surface; either make _timeout truly private in the source class (so
it is omitted from the generated .d.ts), remove it from the exported type, or
explicitly mark it as internal (e.g., add a /** `@internal` */ JSDoc) so the
declaration generator omits it; alternatively, if it is intended to be public,
rename/remove the underscore and add documentation. Locate the _timeout method
in the retry package (the class or interface declaring _timeout) and apply one
of these fixes, then regenerate the type declarations.

In `@packages/routepolicy/routepolicy.d.ts`:
- Line 1: The type alias RoutePolicyType is currently internal; export it so
downstream modules can reuse the union type. Update the declaration for
RoutePolicyType (the existing "type RoutePolicyType = 'network' |
'static-online';") to be exported (export type RoutePolicyType = ...), ensure
any references in this file (and consumers) import the symbol rather than
duplicating the union, and run TS checks to confirm no further changes are
needed.

In `@packages/webapp-hashing/webapp-hashing.d.ts`:
- Around line 1-8: The ManifestResource interface is currently internal; export
it so other packages can import and reuse the type. Update the declaration by
converting the local interface into an exported type (export interface
ManifestResource) so consumers (e.g., callers referencing ManifestResource) can
import it for manifest-related typings and ensure consistent cross-package
declarations.

In `@packages/webapp/webapp.d.ts`:
- Line 30: The manifest property is currently typed as Record<string,
unknown>[], losing the stronger shape defined by ManifestResource in
webapp-hashing; update the manifest declaration to use ManifestResource[]
instead and import that type (using an `import type { ManifestResource } from
'webapp-hashing'` or the appropriate package export) so the webapp.d.ts export
aligns with the shared interface, and ensure any existing references compile
after replacing `manifest: Record<string, unknown>[]` with `manifest:
ManifestResource[]`.

In `@tools/fs/watch.ts`:
- Around line 300-305: The helper reFromJSON currently parses unvalidated regex
strings (and WatchSet.fromJSON isn't referenced), creating dead code and a
potential ReDoS risk; either delete reFromJSON (and any WatchSet.fromJSON
entrypoints) if the JSON deserialization is unused, or harden it: make
reFromJSON a defensive API that validates inputs (e.g., validate type, max
pattern length, reject known-dangerous constructs or excessive nested/adjacent
quantifiers, and wrap new RegExp in try/catch) and update WatchSet.fromJSON to
call this safe parser so any external use is protected.

In `@tsconfig.json`:
- Around line 9-13: The tsconfig currently sets both "outDir" and "noEmit";
remove the redundant "outDir" field from the config (delete the "outDir":
"./dist", entry) since "noEmit": true disables emission, leaving only "noEmit"
(and other options like "allowJs"/"checkJs"/"moduleResolution") to avoid
configuration noise; ensure no build scripts rely on outDir elsewhere before
removing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0c9dc77-c465-4fe0-8cb5-c940edccdd44

📥 Commits

Reviewing files that changed from the base of the PR and between 19420fa and a02c205.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (53)
  • .github/workflows/type-coverage.yml
  • .gitignore
  • package.json
  • packages/accounts-base/accounts-base.d.ts
  • packages/base64/base64.d.ts
  • packages/callback-hook/callback-hook.d.ts
  • packages/check/check.d.ts
  • packages/ddp-rate-limiter/ddp-rate-limiter.d.ts
  • packages/ddp-server/dummy_document_view.ts
  • packages/ddp-server/session_collection_view.ts
  • packages/ddp-server/session_document_view.ts
  • packages/ddp/ddp.d.ts
  • packages/diff-sequence/diff-sequence.d.ts
  • packages/ejson/ejson.d.ts
  • packages/email/email.d.ts
  • packages/id-map/id-map.d.ts
  • packages/logging/logging.d.ts
  • packages/meteor/meteor.d.ts
  • packages/mongo-id/mongo-id.d.ts
  • packages/mongo/connection_options.ts
  • packages/mongo/cursor.ts
  • packages/mongo/cursor_description.ts
  • packages/mongo/mongo.d.ts
  • packages/mongo/observe_multiplex.ts
  • packages/mongo/oplog_tailing.ts
  • packages/mongo/oplog_v2_converter.ts
  • packages/mongo/polling_observe_driver.ts
  • packages/non-core/blaze
  • packages/ordered-dict/ordered-dict.d.ts
  • packages/promise/promise.d.ts
  • packages/retry/retry.d.ts
  • packages/roles/definitions.d.ts
  • packages/routepolicy/routepolicy.d.ts
  • packages/server-render/server-render.d.ts
  • packages/session/session.d.ts
  • packages/sha/sha.d.ts
  • packages/tracker/tracker.d.ts
  • packages/url/url.d.ts
  • packages/webapp-hashing/webapp-hashing.d.ts
  • packages/webapp/webapp.d.ts
  • tools/fs/files.ts
  • tools/fs/optimistic.ts
  • tools/fs/safe-watcher-legacy.ts
  • tools/fs/watch.ts
  • tools/index.d.ts
  • tools/isobuild/import-scanner.ts
  • tools/isobuild/resolver.ts
  • tools/shell-client.ts
  • tools/tool-env/profile.ts
  • tools/utils/archinfo.ts
  • tools/utils/eachline.ts
  • tools/utils/parse-stack.ts
  • tsconfig.json

Comment thread .github/workflows/type-coverage.yml Outdated
Comment thread package.json Outdated
Comment thread packages/ddp/ddp.d.ts Outdated
Comment thread packages/email/email.d.ts
Comment thread packages/mongo/mongo.d.ts Outdated
Comment thread tools/fs/files.ts Outdated
Comment thread tools/fs/watch.ts Outdated
Comment thread tools/shell-client.ts Outdated
Comment thread tools/tool-env/profile.ts Outdated
Comment thread tools/utils/eachline.ts Outdated
@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown

Hello world !!!! 👋
this is pr number: #14319
testing value: VALUE

@italojs
italojs marked this pull request as draft April 7, 2026 12:20
@italojs italojs changed the title ts types coverage Add TypeScript type coverage reporting and improve type safety across packages Apr 7, 2026
@italojs
italojs changed the base branch from devel to ts/type-coverage-setup April 7, 2026 15:08
@italojs italojs mentioned this pull request Apr 7, 2026
italojs added 17 commits August 8, 2026 09:00
…h 2)

- oauth-encryption: seal returns the sealed object (SealedValue), not a string;
  open takes that object, not a string
- email: hookSend returns the unregister handle; customTransport is a settable
  optional property (not a function to call); drop phantom top-level MailComposer
  export (package only exports Email/EmailInternals)
- reload: _getData returns string|null (raw JSON); _migrate's tryReload is the
  retry callback (() => void), not a boolean
- geojson-utils: pointInBoundingBox bounds is number[][], not Geometry
- session: setDefault also accepts an object (mirrors set)
- ordered-dict: constructor overloads for (…entries) and (stringify, …entries)
- non-core/xmlbuilder: drop phantom begin() (npm module only exposes create)
- ddp-rate-limiter: addRule callback receives (result, input), not just result

Sibling .test-d.ts updated to match. tsc clean; dts-test-coverage 100%.
…h 3)

- promise: drop 5 Fiber-era phantom statics/instance methods (async, asyncApply,
  await, awaitAll, prototype.await) removed in Meteor 3; add the real `done`
- webapp: identifyBrowser returns numeric major/minor/patch (not strings)
- logging: Log.parse takes a string (runtime calls line.startsWith)
- server-render: onPageLoad carries static remove/clear/chain members
- webapp-hashing: calculateClientHash includeFilter also accepts null

Sibling .test-d.ts updated. tsc clean; dts-test-coverage 100%.
The handler map typed args as `(EJSONable | EJSONableProperty)[]`, which under
strictFunctionTypes rejected the most common pattern — a handler with a typed
parameter, e.g. `methods({ addTask(text: string) {} })`. Widen to `any[]`
(matching @types/meteor); DDP still enforces EJSON-serializable args at the call
site. Verified with the zodern:types dogfood app: a typed async method now
type-checks. tsc clean; coverage 100%.
- tracker.withComputation is a synchronous pass-through (return f()): <T>(comp, () => T) => T
- allow-deny allow()/deny() return void (bare addValidator call, no return)
- roles: GLOBAL_GROUP is null; createRole/createRoleAsync return string | null
  (null when the role already exists)
- reload._migrate returns boolean (false when a provider isn't ready)

test-d updated. tsc clean; coverage 100%.
…t, modern-browsers, binary-heap)

- meteor: drop phantom Meteor.meteorRelease (runtime is __meteor_runtime_config__
  .meteorRelease, surfaced as Meteor.release)
- accounts-google: loginWithGoogle loginUrlParameters is a free-form OIDC param
  map (include_granted_scopes optional), not a fixed required shape
- reactive-dict: equals() also accepts Date (runtime allows Date/ObjectID)
- modern-browsers: getMinimumBrowserVersions returns { version, source } entries
- binary-heap: MinHeap.maxElementId() overrides to never (always throws)

test-d updated. tsc clean; coverage 100%.
- logging.objFromText: override is optional; return also carries the spread
  override keys (& Record<string, unknown>)
- service-configuration.Configuration: model the stored document — mandatory
  `service`, optional _id, and arbitrary service-specific keys (was {appId,secret})

test-d updated. tsc clean; coverage 100%.
Replace kind-only assertions (toBeObject/toBeFunction) with exact return-type
assertions (toEqualTypeOf) for all 36 roles functions and the webapp function
surface, so a future signature change breaks the test (was vacuous before).
Locks in the audit-corrected types (createRole string|null, identifyBrowser
numeric, etc.).
…icFiles -> staticFilesByArch

From the ambiguous-findings triage (verified against runtime):
- mongo: getCollection is a member of the Mongo namespace object, not a static
  on the Collection constructor — remove the phantom static (Mongo.getCollection
  stays)
- webapp: WebAppInternals exposes staticFilesByArch ({ [arch]: StaticFiles }) at
  runtime, not staticFiles — rename the phantom var

(ddp.onReconnect and mongo.observeChanges findings were verified as FALSE
positives and intentionally left unchanged.) tsc clean; coverage 100%.
…ngo Sorter)

- meteor: drop phantom Meteor.TypedError *value* + TypedErrorStatic (never
  assigned/constructed at runtime); keep the TypedError interface (used as a
  type in error callbacks)
- hot-module-replacement: module is the Meteor Module (with .hot/onRequire),
  not NodeJS.Module
- minimongo: Sorter's 2nd constructor arg is a MongoDB collation spec, not
  { matcher }

test-d updated. tsc clean; coverage 100%.

(force-ssl isSslConnection and the email MailComposer interface shape were
judged intended/not worth changing and left as-is.)
Assert exact return types for the fetch/middleware helpers (was toBeFunction).
minimongo/facts-ui use toBeObject on interfaces/classes (appropriate) and
jquery/npm-mongo re-export external @types (left as-is).
- meteor.release is string | undefined (undefined for git-checkout builds)
- session.equals also accepts Date
- logging formatInput fields are optional (format tolerates missing ones)
- random: add insecure generator + createWithSeeds + RandomGenerator interface
- tracker: add Tracker.inFlush()
- rate-limit: add RateLimiter.checkRules / incrementRules

test-d updated. tsc clean; coverage 100%.
…secret)

- 7 accounts-<service> loginWith<Service>: add the callback-first overload
  loginWith<Service>(callback) that the runtime accepts
- 7 oauth providers: retrieveCredential credentialSecret is string | null

test-d updated (toBeCallableWith for the dual call shape). tsc clean; 100%.
… Matcher null)

- roles: createRole/createRoleAsync options.unlessExists is optional (defaulted)
- ddp-rate-limiter: Matcher fields also accept null

tsc clean; coverage 100%.
Add the Computation instance methods that exist at runtime: run(), flush(),
and the thenable then()/catch() (which delegate to firstRunPromise).

tsc clean; coverage 100%.
The TypeScript skeletons (typescript, typescript-tailwind, angular, svelte)
listed node_modules/@types/meteor/* first in tsconfig paths, so new apps always
used the community DefinitelyTyped types and never the .d.ts shipped by the
app's actual Meteor packages (generated via zodern:types on lint/run).

Put .meteor/local/types/packages.d.ts first, with @types/meteor kept as a
bootstrap fallback for before the first build generates the file.

Verified: a --typescript app typechecks cleanly against the checkout's types
(0 errors), and package-specific members (e.g. Mongo.getCollection) resolve,
confirming the app's own types are now in effect.
# Conflicts:
#	tools/static-assets/skel-typescript-tailwind/tsconfig.json
#	tools/static-assets/skel-typescript/tsconfig.json
@italojs italojs changed the title full type coverage across packages full type coverage across user facing packages Aug 10, 2026
@italojs
italojs marked this pull request as ready for review August 12, 2026 13:01
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

…eton

- Root tsconfig: replace deprecated commonjs/node10 module resolution with
  module "preserve" + moduleResolution "bundler" (TS 6 errors on node10
  with TS5107).
- Root tsconfig: TS 6 no longer auto-includes @types packages, which made
  every node builtin import (e.g. "http" in webapp.d.ts) silently resolve
  to any under skipLibCheck; declare types: ["node"] explicitly and drop
  the vestigial typeRoots entry (./types does not exist).
- skel-svelte: moduleResolution node -> bundler, and preserveValueImports
  (removed in TS 6) -> verbatimModuleSyntax, so apps created from the
  skeleton compile under the TS 6.0.3 dev bundle.
- Bump expect-type range to ^1.4.0 (already what the lockfile resolved).

All three gates pass under TypeScript 6.0.3:
tsc --noEmit clean, type-coverage 99.94%, dts-test-coverage 569/569.
… into ts/type-coverage

# Conflicts:
#	tools/static-assets/skel-angular/tsconfig.json
#	tools/static-assets/skel-svelte/tsconfig.json
#	tools/static-assets/skel-typescript-tailwind/tsconfig.json
#	tools/static-assets/skel-typescript/tsconfig.json
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.

8 participants