Skip to content

Typed path prep bugfixes - #64544

Open
Jake Bailey (jakebailey) wants to merge 20 commits into
microsoft:mainfrom
jakebailey:typed-paths-prep
Open

Jake Bailey (jakebailey) wants to merge 20 commits into
microsoft:mainfrom
jakebailey:typed-paths-prep

Conversation

@jakebailey

Copy link
Copy Markdown
Member

This is just the fix stack pulled out of #64159 early for separate review and merge. See that PR for more info (and commit messages).

Copilot AI balanced review requested due to automatic review settings September 29, 2026 21:43
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Sep 29, 2026

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

The auto-import fast path can corrupt sibling package paths, and the queue-close regression test is not reliably synchronized.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Extracts typed-path preparation fixes covering path identity, module resolution, source maps, API lifecycle, and filesystem casing.

Changes:

  • Preserves canonical and presentation paths across URIs, symlinks, watchers, and auto-imports.
  • Fixes API snapshot lifecycle, cancellation, and project-relative lookups.
  • Adds regression tests and refreshes source-map baselines.
File Description
tsc/​testdata/​tests/​cases/​compiler/​declarationEmitCommonJsModuleReferencedType.ts Tests nested package specifiers.
tsc/​testdata/​baselines/​reference/​tsc/​noEmitOnError/​when-declarationMap-changes.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuildWatch/​sample/​should-not-trigger-recompilation-because-of-program-emit.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuildWatch/​sample/​non-local-change-does-not-start-build-of-referencing-projects-with-circular-references.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuildWatch/​sample/​change-builds-changes-and-reports-found-errors-message-with-circular-references.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuildWatch/​sample/​builds-when-new-file-is-added,-and-its-subsequent-updates-with-circular-references.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​solution/​does-not-have-empty-files-diagnostic-when-files-is-empty-and-references-are-provided.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​sample/​reports-error-if-input-file-is-missing.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​sample/​reports-error-if-input-file-is-missing-with-force.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​sample/​builds-correctly-when-project-is-not-composite-or-doesnt-have-any-references.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​sample/​builds-correctly-when-declarationDir-is-specified.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​resolveJsonModule/​sourcemap.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​tsbuild/​resolveJsonModule/​sourcemap-non-composite.js Updates source-map baseline.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsSolutionWithDisableReferencedProjectLoadReferencingDefaultProjectDirectly.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsSolutionReferencingDefaultProjectIndirectlyThroughDisableReferencedProjectLoadInOneButWithoutItInAnother.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsSolutionReferencingDefaultProjectIndirectlyThroughDisableReferencedProjectLoad.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsSolutionReferencingDefaultProjectIndirectly.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsSolutionReferencingDefaultProjectDirectly.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsRootOfReferencedProjectDeclarationMaps.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​findAllRefsProjectWithOwnFilesReferencingFileFromReferencedProject.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMapsNotSolutionEditEnd.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMapsNotSolutionEdit.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMapsNotSolution.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMapsEditEnd.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMapsEdit.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithSourceMaps.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithDisableSourceOfProjectReferenceRedirectEditEnd.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithDisableSourceOfProjectReferenceRedirectEdit.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​fourslash/​state/​declarationMapsRenameWithDisableSourceOfProjectReferenceRedirect.baseline Refreshes declaration-map output.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of36(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of33(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of3(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of26(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of25(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of13(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​conformance/​ES5For-of1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​ternaryExpressionSourceMap.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​templateLiteralsSourceMap.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapWithCaseSensitiveFileNamesAndOutDir.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationVarInDownLevelGenerator(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationVariables.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationLambdaSpanningMultipleLines.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationLabeled.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationFunctionPropertyAssignment.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationExportAssignmentCommonjs.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDestructuringVariableStatementObjectBindingPattern3.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDestructuringVariableStatementObjectBindingPattern1.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDestructuringVariableStatementArrayBindingPattern7.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDestructuringVariableStatementArrayBindingPattern6.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDestructuringVariableStatementArrayBindingPattern4.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapValidationDebugger.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapUnclosedBlock.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMapForFunctionWithCommentPrecedingStatement01.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-SkippedNode.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-SingleSpace1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-SemiColon1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-NewLine1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-InterfacePrecedingVariableDeclaration1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-EmptyFile1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​sourceMap-Comment1(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​restParameterWithBindingPattern2.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​restParameterWithBindingPattern1.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​requireOfJsonFileWithSourceMap.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​properties(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​optionsSourcemapInlineSourcesMapRoot.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​optionsSourcemapInlineSources.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​optionsInlineSourceMapSourcemap.sourcemap.txt Updates inline-map baseline.
tsc/​testdata/​baselines/​reference/​compiler/​optionsInlineSourceMapSourcemap.js Updates inline-map output.
tsc/​testdata/​baselines/​reference/​compiler/​optionsInlineSourceMapMapRoot.sourcemap.txt Updates inline-map baseline.
tsc/​testdata/​baselines/​reference/​compiler/​optionsInlineSourceMapMapRoot.js Updates inline-map output.
tsc/​testdata/​baselines/​reference/​compiler/​noCatchBlock.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​jsFileCompilationWithMapFileAsJsWithOutDir.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​jsFileCompilationWithMapFileAsJsWithInlineSourceMap.sourcemap.txt Updates inline-map baseline.
tsc/​testdata/​baselines/​reference/​compiler/​jsFileCompilationWithMapFileAsJsWithInlineSourceMap.js Updates inline-map output.
tsc/​testdata/​baselines/​reference/​compiler/​jsFileCompilationWithMapFileAsJs.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​isolatedModulesSourceMap.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​inlineSourceMap(target=es2015).sourcemap.txt Updates inline-map baseline.
tsc/​testdata/​baselines/​reference/​compiler/​inlineSourceMap(target=es2015).js Updates inline-map output.
tsc/​testdata/​baselines/​reference/​compiler/​emptyFile-souremap(target=es2015).js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​emitBOM.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​declarationMapWithInlineSourceMap.sourcemap.txt Updates inline declaration map.
tsc/​testdata/​baselines/​reference/​compiler/​declarationMapWithInlineSourceMap.js Updates inline declaration output.
tsc/​testdata/​baselines/​reference/​compiler/​declarationMapInlineSourcesContent.js.map Omits empty sourceRoot.
tsc/​testdata/​baselines/​reference/​compiler/​declarationEmitCommonJsModuleReferencedType.types Records nested-package types.
tsc/​testdata/​baselines/​reference/​compiler/​declarationEmitCommonJsModuleReferencedType.symbols Records nested-package symbols.
tsc/​testdata/​baselines/​reference/​compiler/​declarationEmitCommonJsModuleReferencedType.js Records test inputs and emit.
tsc/​testdata/​baselines/​reference/​compiler/​declarationEmitCommonJsModuleReferencedType.errors.txt Records test diagnostics.
tsc/​testdata/​baselines/​reference/​compiler/​contentMapperDeclarationEmitFailure.js.map Omits empty sourceRoot.
tsc/​internal/​vfs/​vfsmatch/​vfsmatch.go Handles dynamic URI glob roots.
tsc/​internal/​vfs/​vfsmatch/​vfsmatch_test.go Tests dynamic-root matching.
tsc/​internal/​tspath/​path.go Fixes dynamic and file-URL roots.
tsc/​internal/​tspath/​path_test.go Tests root and canonicalization behavior.
tsc/​internal/​testutil/​projecttestutil/​projecttestutil.go Threads npm cancellation context.
tsc/​internal/​testutil/​projecttestutil/​npmexecutormock_generated.go Regenerates context-aware npm mock.
tsc/​internal/​symlinks/​knownsymlinks.go Preserves symlink presentation paths.
tsc/​internal/​symlinks/​knownsymlinks_test.go Tests child spelling preservation.
tsc/​internal/​sourcemap/​generator.go Omits unset sourceRoot.
tsc/​internal/​project/​watch_test.go Tests watcher path spelling.
tsc/​internal/​project/​snapshotfs.go Tracks canonical and presented filenames.
tsc/​internal/​project/​session_test.go Updates watcher casing expectation.
tsc/​internal/​project/​refcountcache_test.go Tests rollback and final snapshot release.
tsc/​internal/​project/​projectcollectionbuilder.go Rolls back failed API state updates.
tsc/​internal/​project/​project.go Uses presentation-aware watch inputs.
tsc/​internal/​project/​background/​queue.go Makes queue closure wait safely.
tsc/​internal/​project/​background/​queue_test.go Tests close behavior.
tsc/​internal/​modulespecifiers/​specifiers.go Fixes nested package entrypoints.
tsc/​internal/​module/​util.go Splits file/directory package-root APIs.
tsc/​internal/​lsp/​server_completion_internal_test.go Tests invalid completion filenames.
tsc/​internal/​lsp/​replay_test.go Adapts npm executor context.
tsc/​internal/​ls/​rename.go Uses file-specific package roots.
tsc/​internal/​ls/​lsconv/​converters.go Centralizes dynamic URI decoding.
tsc/​internal/​ls/​autoimport/​util.go Fixes package realpath handling.
tsc/​internal/​ls/​autoimport/​util_test.go Tests package realpath edge cases.
tsc/​internal/​execute/​watcher.go Canonicalizes mapper manifest events.
tsc/​internal/​execute/​tsctests/​tsc_test.go Tests nested triple-slash redirects.
tsc/​internal/​execute/​tsctests/​contentmapper_watch_test.go Tests case-insensitive mapper events.
tsc/​internal/​execute/​incremental/​programtosnapshot.go Roots triple-slash references correctly.
tsc/​internal/​compiler/​projectreferencedtsfakinghost.go Preserves symlink child spelling.
tsc/​internal/​api/​session.go Fixes API path resolution and validation.
tsc/​internal/​api/​session_createsourcefile_test.go Tests empty filename rejection.
tsc/​cmd/​tsc/​lsp.go Cancels npm subprocesses through context.
packages/​typescript/​test/​sync/​api.test.ts Regenerates synchronous API tests.
packages/​typescript/​test/​path.test.ts Tests VFS and URI path behavior.
packages/​typescript/​test/​async/​api.test.ts Tests asynchronous API path behavior.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread tsc/internal/ls/autoimport/util.go Outdated
Comment on lines +268 to +269
if _, ok := strings.CutPrefix(fileName, packageDir); ok {
return replacePrefix(fileName, packageDir, realPackageDir)
Comment on lines +114 to +119
go func() {
q.Close()
close(closed)
}()

assert.Check(t, !channelClosed(closed), "Close returned before the active task completed")
…A BUG)

The new createSourceFile APIs treated an empty file name as the current working directory. A directory is not a valid source file name, and accepting it bypassed the rooted-path boundary.

Reject empty names before resolving public string inputs into RootedFilePath values. Cover both in-memory and filesystem-backed source file creation.
…DA BUG)

Main and the pre-port Strada behavior use canonical path strings as
lookup keys, but some callers also reused those keys when constructing
watcher globs or substituting a symlink target. On case-insensitive
hosts that loses the spelling supplied by the filesystem or project
configuration.

Track the original filename alongside each canonical identity. Build
watcher patterns from presentation paths, and retain the symlink
spelling so child suffixes are taken from the original filename while
lookup still uses canonical keys.

This keeps identity comparisons canonical without leaking canonical
casing into user-visible paths or filesystem watch registrations.

Category: Typed-path-discovered; inherited Strada bug
…HS, STRADA BUG)

Add focused coverage for the package realpath cache used during
auto-import discovery. Exercise unscoped packages, scoped packages, and
direct files under node_modules so file paths cannot silently become
package-directory cache entries.

The pre-fix expectations record the existing missing-separator result,
which keeps this test commit independently green. The following fix
updates the expectations to the path-preserving behavior.

Category: Typed-path-discovered; inherited Strada bug
…S, STRADA BUG)

Main and the pre-port Strada implementation use the same string path
shape for both files and package directories while populating the
auto-import realpath cache. A direct file under node_modules can
therefore be interpreted as a package root, and replacing a cached
prefix with string concatenation can produce paths such as
node_modulesdep with no directory separator.

Check whether each traversal candidate is actually a directory before
caching a package realpath. Distinguish file and directory package-root
parsing, and substitute cached prefixes with path-aware joining so exact
roots and descendants preserve their separators.

Cover bare scope directories, scoped package roots, unscoped packages,
and direct files under node_modules.

Category: Typed-path-discovered; inherited Strada bug
…DA BUG)

Add coverage for deriving node_modules package roots from both file
paths and directory paths. Include unscoped packages, scoped packages,
bare scope directories, and direct files under node_modules.

The test records the old directory parser's trailing-separator result
so it remains independently green before the API is replaced.

Category: Typed-path-discovered; inherited Strada bug
…DA BUG)

Main and Strada route both files and directories through
ParseNodeModuleFromPath with a Boolean path-kind argument. That makes it
easy for callers to select the wrong boundary, especially for direct
files under node_modules and scoped package directories.

Replace the Boolean contract with explicit
NodeModulePackageRootForFile and NodeModulePackageRootForDirectory
helpers. Update module resolution, rename, auto-import realpaths, and
project-reference source mapping to call the helper matching the path
they hold.

The explicit boundary prevents files from poisoning directory caches
and keeps bare scopes distinct from scoped package roots.

Category: Typed-path-discovered; inherited Strada bug
…A BUG)

Add declaration-emit coverage for a type reached through
node_modules/foo/other/index.d.ts. Exercise both a plain child directory
and a child with its own package.json.

Without nested metadata, the emitted specifier must retain the file
path instead of treating foo/other as a package entrypoint. With nested
metadata, the child package may still define that entrypoint.

Category: Typed-path-discovered; inherited Strada bug
… BUG)

The index-based node_modules path analysis inherited by main and Strada
updates PackageRootIndex while probing nested package.json files. When a
nested package.json is absent, that loses the original package boundary
and incorrectly treats a child index.d.ts as the child directory's
package entrypoint, shortening foo/other/index.d.ts to foo/other.

Track the current candidate package root separately from the original
package base. Use the original base when deciding whether a file without
nested metadata is a package index, while still honoring actual nested
package.json entrypoints.

This keeps generated module specifiers stable for ordinary package
subdirectories without disabling valid nested packages.

Category: Typed-path-discovered; inherited Strada bug
… PARITY)

Add a tsc incremental project-reference baseline for a nested source
file with a triple-slash reference to referenced-project source.

The baseline records the incorrect source dependency retained when
redirect lookup roots the reference at the program working directory.

Category: Typed-path-discovered; restores Strada behavior
…PARITY)

Resolve triple-slash reference text from the containing source file
directory before looking up project-reference redirects.

This restores the output dependency intended by the original builder
redirect change and removes the redundant referenced-project source from
incremental build info.

Category: Typed-path-discovered; restores Strada behavior
…ADA PARITY)

The native API accepted project-relative file handles, but prepared
auto-import snapshots by converting the unresolved handle against the
session working directory. Strada and the TypeScript API instead operate
on the source file selected from the project program, so the retry could
prepare the wrong document and return no edit.

Convert the resolved source filename back to a document URI before
preparing auto imports. Apply the same rule to the completion retry
added on current main, and cover both synchronous and asynchronous
project-relative API calls.

Category: Typed-path-discovered; restores Strada behavior
…IVE API)

The JavaScript API accepts project-relative document identifiers, but
its source-file cache canonicalized those identifiers against the API
session's working directory. A project whose current directory differs
from the session therefore fetched the same server source file under a
second cache key and returned a new wrapper instead of preserving object
identity.

The native compiler and Strada resolve source files in their project
context; the bug was confined to the newer JavaScript API cache boundary
on main. Allow the API path converter to take an explicit base
directory, and use the owning project's current directory for source-
file and metadata cache keys. Keep the session directory as the default
for project and snapshot keys.

Cover both synchronous and asynchronous APIs, including metadata lookup
and cache reuse across project-relative and absolute identifiers.

Category: Typed-path-discovered; native API bug
…EW, STRADA BUG)

Treat the file scheme and localhost authority case-insensitively when
identifying local file URL volume roots. This matches URL semantics and
prevents uppercase spellings from changing path normalization behavior.

Category: Review-discovered; inherited Strada bug
Follow ECMA-426 when decoding source URLs. Preserve null source entries,
resolve empty references with the specified sourceRoot semantics, retain
duplicate source indices, and treat invalid source indices as unmapped
positions rather than indexing invalid data.

Omit sourceRoot from generated maps when it is not configured. The
standard distinguishes an absent value from an explicit empty string,
which denotes the root prefix "/"; historically TypeScript emitted an
empty string while intending the absent-value behavior.

Keep reverse mapping storage sparse-safe and reject malformed JSON
values. Retain map-relative fallback for published TypeScript maps that
used an explicit empty sourceRoot with nonempty relative sources.

Category: Review-discovered; inherited Strada bug
…DA BUG)

Strada and main flattened non-file URIs into synthetic path strings.
That representation normalized away dot segments and repeated
separators, conflated reserved-looking names with encoded names, and
could lose query, fragment, authority-only, and case-sensitive identity.
The TypeScript API mirror had the same behavior.

Introduce a reserved, reversible dynamic URI encoding in both Go and the
TypeScript API. Treat the scheme and authority as the synthetic root,
keep dynamic identities case-sensitive, and translate explicitly between
logical URI segments and physical resolver paths.

Carry that distinction through rootDirs, package.json fields, exports,
generated entrypoints, CommonJS directory lookup, and VFS glob matching.
Generic dynamic filenames outside the reserved codec root remain
literal.

Cover URI round trips, exceptional and reserved segments, dynamic
package paths, rootDirs transitions, dotted directories, and generated
module specifiers.

Category: Review-discovered; inherited Strada bug
…NLY)

API project and file opens are ref-counted in snapshot state. The
project collection builder cloned that state for each snapshot,
but then mutated the clone incrementally while processing closes and
opens. If a later project update failed, the errored snapshot retained
the earlier ref-count changes.

The API session commits its own open-resource bookkeeping only after a
successful update. Adopting the partially updated snapshot therefore
left the two layers inconsistent, allowing a later close to release
another session's reference or keep a resource loaded indefinitely.

Clone the API state again at the request boundary and restore the
pre-request state on error. This behavior is in the current native
API on main; Strada did not have this shared, ref-counted API snapshot
mechanism.

Add a regression test that forces an update failure after closing a
project and verifies that every API reference remains unchanged.

Category: Review-discovered; native-only bug
A project session owns the initial reference to its current
snapshot. Snapshot replacement releases the old session-owned reference,
but Session.Close previously closed only the SnapshotHost and left
the final current snapshot referenced.

That retained the snapshot program, parse-cache entries, checker
pools, and related project resources after the session itself had
closed. Cancel and join background work, serialize closure against
snapshot updates, then detach and dereference the current snapshot
before closing its host.

Thread the session context through automatic type acquisition and
npm execution so shutdown can terminate external installs rather than
waiting indefinitely. Make queue closure reject new work atomically
with waiting for accepted work.

LSP API sessions share the project snapshot and release their
open-resource references through another update. Track their transports
and connections so shutdown can stop and await all request handlers
before closing each child session and finally the project session.

This leak is present in the native project session on main. Strada
managed project state through its server session lifecycle and did
not have this reference-counted native snapshot ownership model.

Add regression coverage that Close waits for queued work and that
closing a project session releases both its parse-cache entry and
program reference.

Category: Review-discovered; native-only bug
…IVE ONLY)

Content mapper manifest invalidation compared watch event paths
with package manifest paths using their presentation strings. On a
case-insensitive filesystem, equivalent paths with different casing
therefore failed to trigger a mapper reload.

Canonicalize both the changed paths and each configured mapper manifest
using the watcher comparison policy before looking them up. Add a
case-insensitive watch test whose symlink target and emitted event
differ only in casing.

This bug is present on main and is independent of typed paths.

Category: Review-discovered; native-only bug
The JavaScript API virtual filesystem stored arbitrary path components
in prototype-backed objects. Names such as toString and __proto__ could
therefore collide with inherited properties instead of creating normal
files and directories.

Use null-prototype child maps and preserve entry spelling separately
from canonical lookup keys. Accept an explicit CaseSensitivity so
callers can model insensitive hosts without making sensitive hosts
alias distinct files.

Cover prototype-named components and case-insensitive lookup while
preserving directory-entry casing.

Category: Review; pre-existing native API bug

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

2 participants