fix(highlight.run): make published TypeScript declarations resolvable#629
Open
Vadman97 wants to merge 2 commits into
Open
fix(highlight.run): make published TypeScript declarations resolvable#629Vadman97 wants to merge 2 commits into
Vadman97 wants to merge 2 commits into
Conversation
The rolled-up dist/index.d.ts referenced types from packages that are not runtime dependencies and from an internal path alias, so importing @launchdarkly/observability / @launchdarkly/session-replay (or highlight.run directly) failed type-checking whenever skipLibCheck was not enabled — which is TypeScript's default. Unresolvable specifiers in the published declarations were: @opentelemetry/api, @highlight-run/rrweb-types, graphql-request (+ its deep graphql-request/build/cjs/types), stacktrace-js, and client/types/observe. Fixes: - Inline @opentelemetry/api and @highlight-run/rrweb-types into the declaration bundle via vite-plugin-dts `bundledPackages`. - Declare stacktrace-js as a runtime dependency (api-extractor cannot follow its `export =` namespace `StackFrame` symbol, so it cannot be bundled). - Make the three `client/types/observe` imports relative so the baseUrl alias no longer leaks into the emitted declarations. - Keep the internal GraphQL client off the public surface: the `graphqlSDK` fields on the SDK classes are now ECMAScript-private (`#`). They are the only thing that dragged graphql-request / graphql / cross-fetch into the public types, and bundling graphql-request just relocated the leak (its own declarations are not self-contained). The sdk unit test now stubs the generated `getSdk` factory instead of assigning the (now private) field. Adds a CI guard (scripts/check-published-types.mjs, wired into turbo.yml) that packs the built package, installs it into a clean consumer outside the monorepo, and type-checks an import with skipLibCheck:false under both bundler and classic node resolution — failing before publish if any declaration import is unresolvable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6b92bc5 to
e6bf45f
Compare
abelonogov-ld
approved these changes
Jun 16, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Importing
@launchdarkly/observability(or@launchdarkly/session-replay, orhighlight.rundirectly) breaks consumer type-checking wheneverskipLibCheckis not set totrue— andfalseis TypeScript's default. A customer hit this.Root cause is in
highlight.run's publisheddist/index.d.ts(the@launchdarkly/*packages are thin re-export wrappers over it). The rolled-up declaration fileimports types from specifiers that don't exist in a consumer'snode_modules:.d.ts@opentelemetry/api@highlight-run/rrweb-typesgraphql-request+graphql-request/build/cjs/typesexportsmap eitherstacktrace-jsclient/types/observebaseUrlpath alias that leaked into the published types — not a package at allIn-repo
tscnever catches this because inside the monorepo every devDependency and thebaseUrlalias resolve. The only faithful test is a clean consumer install (see the new CI guard).Fix
@opentelemetry/apiand@highlight-run/rrweb-typesinto the declaration viavite-plugin-dtsbundledPackages.stacktrace-jsa runtime dependency. api-extractor can't follow itsexport =namespaceStackFramesymbol, so it can't be bundled; making it a real dep means the existingimport { StackFrame } from 'stacktrace-js'resolves (same mechanism that already makes@launchdarkly/js-client-sdkwork).client/types/observeimports relative, so thebaseUrlalias stops leaking.graphqlSDKfields on the SDK classes are now ECMAScript-private (#graphqlSDK). They were the only thing dragginggraphql-request(and transitivelygraphql,cross-fetch,@graphql-typed-document-node/core) into the public types. Bundlinggraphql-requestinstead just relocates the leak — its own declarations aren't self-contained, and the deepbuild/cjs/typesimport isn't resolvable underbundler/node16regardless. Making the field#-private removes the whole chain. The sdk unit test now stubs the generatedgetSdkfactory instead of assigning the (now private) field.CI guard
scripts/check-published-types.mjs(new step inturbo.yml, before publish) packs the freshly built package, installs it into a clean consumer outside the monorepo (so only declared runtime deps are present), and type-checks an import withskipLibCheck: falseunder bothbundlerand classicnoderesolution. It fails the build if any declaration import is unresolvable. This is what caught a first iteration of this PR where bundlinggraphql-requesthad relocated the leak — the guard is doing its job. Run locally withyarn check:published-typesafter a build.(
node16/nodenextare intentionally not exercised:@launchdarkly/js-client-sdkcurrently ships ESM declarations with extensionless relative imports that those modes reject (TS2834) — unrelated noise from a dep we don't control here.)Notes
observability-node, but that package is independent (it does not depend onhighlight.run) and is unaffected. The broken package is the browser SDK@launchdarkly/observability.highlight.run(patch); theworkspace:*wrappers@launchdarkly/observabilityand@launchdarkly/session-replayneed to republish against the fixedhighlight.runfor consumers to pick up the corrected types — please confirm the release pipeline cascades those.🤖 Generated with Claude Code
Note
Medium Risk
Touches published SDK types and adds a runtime dependency (
stacktrace-js); runtime behavior is largely unchanged but a bad release would break downstream TypeScript until republished.Overview
Fixes consumer TypeScript failures when importing
highlight.run/@launchdarkly/*browser SDKs with defaultskipLibCheck: false. The rolled-updist/index.d.tswas importing types from dev-only deps, workspace packages, internal path aliases, and publicgraphqlSDKfields that dragged ingraphql-request.Declaration surface:
vite-plugin-dtsnow bundles@opentelemetry/apiand@highlight-run/rrweb-typesinto the published types.stacktrace-jsmoves from devDependency to runtime dependency so its types resolve in clean installs.client/types/observeimports are switched to relative paths sobaseUrlaliases do not leak. Internal GraphQL clients onHighlight,ObserveSDK, andRecordSDKuse#graphqlSDKso GraphQL types stay off the public API.CI: New
scripts/check-published-types.mjspacks the built package, installs it in a temp consumer outside the monorepo, and runstscwithskipLibCheck: falseunderbundlerandnoderesolution—wired intoturbo.ymlbefore publish and asyarn check:published-types.Tests: SDK unit tests mock
getSdkinstead of assigning the now-privategraphqlSDKfield.Reviewed by Cursor Bugbot for commit d355581. Bugbot is set up for automated code reviews on this repo. Configure here.