Skip to content

Fix: declare @types/js-yaml in dev/package.json - #567

Open
AmaadMartin wants to merge 2 commits into
mainfrom
fix/dev-types-js-yaml-adm-zip-devdeps
Open

Fix: declare @types/js-yaml in dev/package.json#567
AmaadMartin wants to merge 2 commits into
mainfrom
fix/dev-types-js-yaml-adm-zip-devdeps

Conversation

@AmaadMartin

@AmaadMartin AmaadMartin commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

  1. Link to an existing issue (if applicable):
    N/A — no existing issue.
  2. Or, if no issue exists, describe the change:

Problem: @google/adk-devtools (dev/) consumes a type-only package that its manifest does not declare. It resolves today only because npm workspace hoisting puts it in the repository-root node_modules, so the defect is invisible from inside the monorepo and only appears when the workspace is installed on its own.

Import sites:

  • dev/src/conformance/yaml_agent_loader.ts:9import yaml from 'js-yaml';
  • dev/src/conformance/yaml_test_loader.ts:10import yaml from 'js-yaml';

The value package is declared (dev/package.json dependencies, "js-yaml": "^4.1.1"), but js-yaml ships no bundled declarations — verified against the version the lockfile actually resolves:

$ node -p "const p=require('js-yaml/package.json'); [p.version, p.types, p.typings]"
[ '4.2.0', undefined, undefined ]

The declarations come from @types/js-yaml, which is declared only in the repository-root package.json:45. The repo's tsconfig.json extends gts/tsconfig-google.json, which sets "strict": true (hence noImplicitAny), so a missing declaration is a hard TS7016, not a silent any. dev's own build script is "build": "tsc --emitDeclarationOnly && node ./build.js", so a standalone npm install + npm run build inside dev/ is exactly the path that breaks.

That the types come from the hoisted root rather than from dev is directly observable:

$ npm ls @types/js-yaml
adk@1.5.0 /…/adk-js
└── @types/js-yaml@4.0.9          # attached to the ROOT project, not to dev

$ npx tsc --noEmit --traceResolution -p dev/tsconfig.json 2>&1 | grep "Module name 'js-yaml'"
======== Module name 'js-yaml' was successfully resolved to
         '<repo-root>/node_modules/@types/js-yaml/index.d.mts'
         with Package ID '@types/js-yaml/index.d.mts@4.0.9'. ========

Solution: declare @types/js-yaml in dev/package.json devDependencies, keeping the block alphabetically sorted. package-lock.json is regenerated by npm install and mirrors the same range under packages["dev"].devDependencies. The whole PR is 2 added lines across 2 files, 0 deletions.

Deliberate details:

  • The range is copied verbatim^4.0.9 from root package.json:45. Not bumped, not pinned, not widened; divergent ranges across workspaces are how a second copy of a type package ends up in the tree. @types/js-yaml@4.0.9 was already resolved (root-hoisted) in the lockfile, so no new package enters the dependency tree — only the range declaration.
  • Why devDependencies and not dependencies. A sibling change moves @types/express in the opposite direction (out of dev's devDependencies and into dependencies), so the apparent inconsistency is worth pre-answering. The test is whether the type leaks into the published dist/types declarations, which a consumer must resolve at install time:
    • @types/express does leak — dev's emitted .d.ts files reference express types. That makes it a runtime dependency.
    • js-yaml does not leak. yaml.load() returns unknown in @types/js-yaml v4 and both call sites narrow immediately (yaml_agent_loader.ts:31, yaml_test_loader.ts:47,58,72). The only exported symbols from those modules are batchLoadYamlAgentConfig and batchLoadYamlTestDefs, typed with local domain types (YamlAgentConfig, TestSpec), and neither module is re-exported from dev/src/index.ts. No js-yaml type reaches the public surface.
  • @types/adm-zip was considered and deliberately excluded. dev/build.js:14 imports adm-zip and dev does not declare its types (only core does), which looks like the same defect. It is not, because that declaration would have no reader: dev/tsconfig.json has "include": ["src/**/*"] with no allowJs/checkJs, so tsc never reads dev/build.js. Confirmed directly — npx tsc -p dev/tsconfig.json --listFilesOnly | grep -c dev/build.js returns 0. Declaring @types/adm-zip in dev would therefore assert a typecheck relationship that does not exist, and would land as an unused devDependency that depcheck/knip flag and that churns on version bumps with no signal behind it. It belongs with a change that actually makes build.js typechecked, not here. dev/build.js is untouched by this PR, including its pre-existing @ts-ignore.
  • No dependency-analysis tooling (knip, depcheck, import/no-extraneous-dependencies, a new CI step) is introduced. Out of scope here.

Collision check (open PRs on this fork). Enumerated all 465 open PRs and resolved the changed-file list for every one; 21 touch dev/package.json (#133, #148, #158, #169, #228, #244, #249, #250, #277, #290, #323, #382, #387, #444, #445, #484, #485, #508, #541, #544, #562). Diffed every one of them plus the adjacent lockfile/build.js PRs (#183, #345, #482, #507) for ^\+.*"@types/(js-yaml|adm-zip)": zero hits. No open PR lands this change, so this is not a duplicate. Textual overlap only: #562 deletes @types/express from this exact block (two lines above the insertion point) and #541/#323 add entries to dev's dependencies block. Neither is a functional prerequisite and this change stands alone, so it is branched from main rather than stacked; if #562 lands first, the resolution is to keep its deletion and place @types/js-yaml between @types/cors and @types/node.

Testing Plan

Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.

Unit Tests:
[ ] I have added or updated unit tests for my change. — intentionally none; reasoning immediately below.
[x] All existing unit tests for the touched workspace pass locally (one pre-existing, environment-dependent failure documented below).

No test file is added, deliberately. This change adds zero executable lines — one JSON key naming a .d.ts-only package that is erased at compile time. vitest.config.ts scopes coverage to core/src/**/*.ts, dev/src/**/*.ts and integrations/src/**/*.ts, so both the numerator and the denominator are unchanged and the configured thresholds are unaffected. The only test one could write is an assertion about the contents of dev/package.json from a _test.ts file — that is the dependency-checking harness this change explicitly excludes, it would be a tautology against the file the same PR edits, and no such manifest test exists on main to extend. The regression signal for this class of bug belongs in a repo-wide manifest check, which is separately scoped.

The "prove it can fail" obligation is therefore discharged at the manifest level, with a real isolated install rather than a percentage.

Before/after repro (the primary evidence). The defect is invisible in the monorepo, so the repro isolates the dev workspace and installs only what dev/package.json declares:

cp -r dev /tmp/dev-repro && cd /tmp/dev-repro
rm -rf node_modules dist .cache
npm install
# dev/tsconfig.json extends ../tsconfig.json, which does not exist outside the
# monorepo, so the effective options are passed explicitly. They mirror the root
# tsconfig.json exactly: target ES2020, lib ES2022+DOM, nodenext, esModuleInterop,
# skipLibCheck, plus strict from gts/tsconfig-google.json.
npx tsc --noEmit --strict --esModuleInterop --skipLibCheck \
  --module nodenext --moduleResolution nodenext --target ES2020 --lib ES2022,DOM \
  src/conformance/yaml_agent_loader.ts src/conformance/yaml_test_loader.ts

BEFORE (main) — 734 packages installed, node_modules/@types/ contains no js-yaml:

src/conformance/yaml_agent_loader.ts(9,18): error TS7016: Could not find a declaration file for module 'js-yaml'. '/tmp/dev-repro/node_modules/js-yaml/dist/js-yaml.mjs' implicitly has an 'any' type.
  Try `npm i --save-dev @types/js-yaml` if it exists or add a new declaration (.d.ts) file containing `declare module 'js-yaml';`
src/conformance/yaml_test_loader.ts(10,18): error TS7016: Could not find a declaration file for module 'js-yaml'. '/tmp/dev-repro/node_modules/js-yaml/dist/js-yaml.mjs' implicitly has an 'any' type.
  Try `npm i --save-dev @types/js-yaml` if it exists or add a new declaration (.d.ts) file containing `declare module 'js-yaml';`
exit=2

AFTER (this branch) — 735 packages, exactly one added, @types/js-yaml now present:

exit=0

Targeted mutation — is the added line load-bearing? Coverage-style "it ran" is not proof, so the line was removed from the fixed tree and the check re-run: reinstalling without "@types/js-yaml": "^4.0.9" and re-running the same tsc invocation restores both TS7016 errors verbatim and exit=2. The single added line is the whole fix, and nothing else in the diff is doing work.

Repo-wide checks (run on the pushed commit, in the monorepo):

command result
npm install clean; lockfile diff is the one range under packages["dev"].devDependencies and nothing else
npm run build pass (all workspaces, including dev's tsc --emitDeclarationOnly && node ./build.js)
npm run lint pass
npm run format:check pass — "All matched files use Prettier code style!"
npx secretlint on both changed files pass
npx vitest run --project unit:dev dev/test/conformance/yaml_agent_loader_test.ts dev/test/conformance/yaml_test_loader_test.ts 10 passed — the tests for the two files whose types this fixes
npx vitest run --project unit:dev (whole touched workspace) 222 passed, 1 failed — pre-existing, see below

Two honest caveats rather than a clean-sweep claim:

  • dev/test/cli/cli_create_test.ts > "should handle Vertex AI selection with gcloud defaults" fails identically with this change stashed, i.e. on the unmodified base. It is environment-dependent: the test expects a mocked project id but the workstation's real gcloud default project leaks through. Unrelated to this PR and not something it should paper over.
  • npm run ts:check reports 281 pre-existing error TS diagnostics (278 in core/test, 3 in tests/integration). Captured with and without this change: the two outputs are byte-identical, which is the expected result — in the monorepo @types/js-yaml is already root-hoisted, so declaring it in dev cannot change root type resolution. Note ts:check is not one of the steps .github/workflows/validation.yaml runs.

Manual End-to-End (E2E) Tests:

  1. From a clean checkout of this branch: npm install && npm run build — the dev workspace builds.
  2. Isolate the workspace and confirm it is now self-sufficient:
    cp -r dev /tmp/dev-check && cd /tmp/dev-check
    rm -rf node_modules dist .cache && npm install
    ls node_modules/@types/js-yaml                              # present
    npx tsc --noEmit --strict --esModuleInterop --skipLibCheck \
      --module nodenext --moduleResolution nodenext --target ES2020 --lib ES2022,DOM \
      src/conformance/yaml_agent_loader.ts src/conformance/yaml_test_loader.ts
    # exits 0; on main this exits 2 with TS7016
  3. git diff main --stat — exactly dev/package.json and package-lock.json, 2 insertions, 0 deletions.
  4. git diff main -- package-lock.json — the change is confined to packages["dev"].devDependencies; no resolved URLs, integrity hashes or unrelated regions move.
  5. Eyeball the ordering — @types/js-yaml sits between @types/express and @types/node.

Checklist

[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[ ] I have added tests that prove my fix is effective or that my feature works. — no test file; effectiveness is proven by the isolated before/after repro and the mutation above.
[x] New and existing unit tests pass locally with my changes.

Amaad Martin added 2 commits August 2, 2026 23:06
@google/adk-devtools imports js-yaml (dev/src/conformance/yaml_agent_loader.ts,
dev/src/conformance/yaml_test_loader.ts) and adm-zip (dev/build.js), but neither
package ships bundled type declarations and dev/package.json declared neither
@types/* package. They resolved only because npm workspace hoisting places them
in the repository-root node_modules -- @types/js-yaml from the root manifest and
@types/adm-zip from core's.

The manifest was therefore untruthful about the workspace's build requirements:
a standalone `npm install` + `tsc` inside dev/ failed with TS7016 for 'js-yaml'.

Ranges are copied verbatim from the existing declarations (^4.0.9 from the root
manifest, ^0.5.8 from core) so no second copy of either type package enters the
tree. Both are devDependencies because neither type leaks into the published
dist/types declarations.
Review feedback: @types/adm-zip has no reader in the dev workspace, so
declaring it asserts a typecheck relationship that does not exist.

dev/tsconfig.json has "include": ["src/**/*"] with no allowJs/checkJs, so
tsc never reads dev/build.js -- the only adm-zip importer in the workspace.
Verified with `tsc -p dev/tsconfig.json --listFilesOnly`, which does not
list dev/build.js at all. The declaration would land as an unused
devDependency that pins a version with no signal behind it; it belongs with
a change that actually makes build.js typechecked.

@types/js-yaml is unaffected and remains the whole fix: dev/src/conformance
imports js-yaml from inside src/**/*, so that one is load-bearing.
@AmaadMartin AmaadMartin changed the title Fix: declare @types/js-yaml and @types/adm-zip in dev/package.json Fix: declare @types/js-yaml in dev/package.json Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant