Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the programmatic API surface to be snapshot-based (multiple immutable snapshots with explicit lifetimes), adds snapshot diffing to support client-side caching, and updates the JS/TS client packages and tests accordingly.
Changes:
- Replace “load project” style API with
initialize+updateSnapshot, including snapshot handles and server-side ref-counted snapshot lifecycle. - Add snapshot change computation (per-project changed/deleted files) and introduce client-side
SourceFileCachekeyed by (path, parseOptionsKey, contentHash). - Rev the source-file binary encoding protocol (v2) to include a content hash and additional SourceFile extended data (path/id), and update consumers/tests.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/project/snapshot.go | Adds InvalidateAll handling and reorders API request handling during snapshot cloning. |
| internal/project/projectcollectionbuilder.go | Simplifies API request handling; updates all API-opened projects each request. |
| internal/project/filechange.go | Adds InvalidateAll and a helper to merge API-provided file changes into session changes. |
| internal/project/api.go | Introduces APIOpenProject + APIUpdateWithFileChanges to drive snapshot updates from API. |
| internal/lsp/server.go | Adds cancellable context + panic recovery for API session connections. |
| internal/compiler/program.go | Exposes FilesByPath() for snapshot diffing. |
| internal/api/session.go | Implements multi-snapshot API session with ref-counting, per-snapshot registries, updateSnapshot + resolveName, and snapshot diffing. |
| internal/api/protocol_jsonrpc.go | Ensures JSON-RPC responses encode null results explicitly. |
| internal/api/proto.go | Updates protocol methods/params (initialize/updateSnapshot/release), document identifiers, node handles, and response shapes. |
| internal/api/encoder/encoder.go | Bumps protocol to v2; adds content hash to header and extends SourceFile data (path/id). |
| internal/api/encoder/encoder_test.go | Updates encoder tests for new EncodeSourceFile signature. |
| _packages/ast/src/nodes.ts | Adds Path branded type and SourceFile.path. |
| _packages/api/src/* | Introduces sync API implementation, reworks async API around snapshots, adds caching utilities, updates FS callback wiring, and removes old root export. |
| _packages/api/test/* | Updates sync/async tests and benches for snapshot-based API + caching behavior. |
| _packages/api/package.json | Changes exports to ./sync and keeps ./async. |
Member
Author
|
My handling of SourceFileAffectingOptions needs to be updated after Jake's changes in main. I think it wasn't quite right before, anyway. |
andrewbranch
commented
Feb 18, 2026
|
|
||
| const ( | ||
| ProtocolVersion uint8 = 1 | ||
| ProtocolVersion uint8 = 3 |
Member
Author
There was a problem hiding this comment.
Where did version 2 go??
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.
This PR adds several features to the
@typescript/apiAPI client package, along with some Go support code. Please note the API is still early and empty, but this is a big step towards making it usable—hopefully the next PR can (finally) be mostly additional API surface, not support infrastructure. From big to small, the changes worth mentioning:Client-side Snapshot model, state updates, and object lifecycles
The API client's types now reflect a more accurate model of the server: all state is stored in an immutable Snapshot. Before:
After:
Previously, Types and Symbols were disposable and had to be explicitly disposed when you no longer needed them. Now, their lifecycles, along with Projects' lifecycles, are tied to the Snapshot. Types and Symbols are no longer disposable, but Snapshots are. Within a single Snapshot, Projects, Types, and Symbols maintain referential equality across multiple calls, but across Snapshots, they are always different objects, even if the server-side data is unchanged.
SourceFiles, on the other hand, are cached at a higher level than the Snapshot since they're more expensive to encode, transfer, and decode, so they maintain referential equality across Snapshots as long as the underlying file hasn't changed and at least one Snapshot that references them is still alive.
In general, we don't expect API users to be in situations where they can accidentally mix objects from different snapshots. Getting a new snapshot is analogous to
ts.createProgram()in the Strada API; for many use cases, you only ever call it once. In a real-time application like an editor plugin connected to the LSP server, you'll likely callapi.updateSnapshot()at the beginning of each handler and never hold onto more than one at a time.Snapshots have a
dispose()method and a[Symbol.dispose]()so you can automatically clean up withusingdeclarations. Disposing a Snapshot marks it as unusable, but the API always holds onto at least one Snapshot internally, which allows it to maintain the SourceFile cache across Snapshots updates that are spread out over time:Symbols have
declarationsandvalueDeclarationinformationAPI client methods that return Symbols now include information about their declarations. These objects are stubs of nodes containing enough information to resolve them to a full AST node, whether the file has already been fetched or not:
Program and Checker APIs namespaced
As seen in the earlier code examples, for organization and familiarity,
projectnow includesprogramandcheckerproperties that serve only to group related APIs.checker.resolveName()addedAlong with symbol declarations, this was a method I needed in a small proof of concept, so here it is.
Files are identified by file name or URI
To make it more convenient to work with the API in an LSP context, all API methods that took a filename now also accept a URI.
Async API supports virtual file system callbacks
Warning
This functionality is super useful for testing, but is a prime candidate for future removal. It doesn't work with the LSP-connected API and so we may replace it with a mechanism that works in both settings.
The async API client now supports the same client-side file system overrides as the sync client:
Sync API moved from
@typescript/apito@typescript/api/syncA lot of the types are duplicated between the sync and async APIs with only sync/async differences which made it confusing as a user of the async API that your types were incompatible with
import { Project } from "@typescript/api", which looks like it should be sync-agnostic. Now, only types that are identical between the APIs are exported from@typescript/api.