Install the beta with pnpm add -D [email protected]. require("tsrs") exports version, typescriptVersion and
typescriptCommit.
The package also ships TypeScript 7's programmatic API, copied from microsoft/TypeScript
packages/typescript under the same subpaths as the typescript 7 package. Both the compiler and SDK follow
3e9236361fa9 (TypeScript 7.1 beta); UPSTREAM.json records the copied SDK revision
and adaptations.
| subpath | contents |
|---|---|
tsrs/sync |
API with synchronous calls (MessagePack over the server's stdio) |
tsrs/async |
API with promise-returning calls (JSON-RPC) |
tsrs/ast (/is, /factory, /utils, /scanner, /visitor, /clone) |
AST types, guards, factory and traversal |
tsrs/fs, /proto |
filesystem callbacks/request filesystems, protocol types |
tsrs/path |
strongly typed path constructors and helpers |
tsrs/vscode |
createTypeScriptModuleLoader and upstream editor SDK types; tsrs does not bundle the VS Code extension |
import { API } from "tsrs/sync";
const api = new API({ cwd: process.cwd() }); // spawns `tsrs --api`; tsserverPath overrides the binary
const snapshot = api.createSnapshot({ openProjects: ["/abs/path/tsconfig.json"] });
const project = snapshot.getConfiguredProject("/abs/path/tsconfig.json");
console.log(project.program.getSemanticDiagnostics("/abs/path/src/index.ts"));
project.program.emit(); // follows the project's compiler options
api.close();createSnapshot and updateSnapshot accept userPreferences for language-service behavior and
prepareAutoImports with the file to index in the new snapshot. Request that preparation before
getCompletionsAtPosition with includeSymbol: true when auto-imports may be needed: the API then returns
symbols owned by the requested snapshot. An unprepared snapshot reports a client error for that request.
The sync and async APIs expose getSymbol(declaration), and each package exports a standalone getSymbol
that uses the remote declaration's owning API. It returns the binder symbol from the exact cached source file;
synthetic and configuration declarations have no cached file owner, and a released or replaced file cannot be
looked up after its cached result is gone.
The API reports caseSensitivity as TypeScript's enum in its initialize response.
Inferred projects omit configFileName; snapshot program and transpile requests accept raw compiler
options, with relative option paths resolved from the API server's current directory. Document
identifiers must contain a non-empty file name or URI.
The code is TypeScript's, with one intentional difference: if the server process dies, the async API rejects
every pending and later request with an error starting API server connection lost: instead of leaving them
pending forever (see npm packaging).
The module loader accepts full typescript/* specifiers and resolves them through the
selected package manifest. Passing a tsrs installation's package.json loads tsrs/* from that
installation. This is a package-side helper; tsrs does not supply the upstream VS Code extension's SDK event,
API-pipe lifecycle, or in-place-upgrade restart handling.
The 0.20.0 beta uses the upstream beta entry-point names. It removes deprecated openProject,
Project.rootFiles / .compilerOptions, and the Snapshot.internal formatting alias; use openProjects,
project.parsedCommandLine, and project.languageService.formatNodeForInsertion. The server's project response
no longer sends the removed duplicate fields. The API is served by the tsrs binary (tsrs --api), whose
coverage of the protocol is still incomplete. The method inventory records requests from
the pinned SDK suite, and the coverage matrix records implementation status and gaps.
Sync API generators are single-use across API.batch calls. Reusing an instance, even after its first
batch completes, throws Cannot execute the same generator instance more than once, as in upstream TypeScript.
TS_API_TIMING_DIR set to a nonempty directory enables timing collection even with collectTiming: false.
Each sync or async API client writes one uniquely named JSON snapshot when closed, with client timing and either
server timing or a server timing error. Repeated close() calls do not write another snapshot. An external socket
server with timing disabled still yields the client's measured snapshot. After a synchronous filesystem callback
throws, later channel requests rethrow that error rather than reading an out-of-sequence response; closing still
writes the client snapshot.
TypeScript consumers need esnext.disposable (or a newer lib) for the declarations' Symbol.dispose and
Symbol.asyncDispose members. The async Snapshot, Program, and BuildOrchestrator use
Symbol.asyncDispose, so await using waits for the returned cleanup Promise. The JS API runs on the package's
minimum Node (16.20; checked on 16.20, 18, 20, 22 and 24). Where Node lacks the disposal symbols, call
close() / dispose() explicitly.
Port of the TypeScript 7 native API through the workspace Cargo.toml pin
3e9236361fa97a9e6c685f4833ac457ae2d8c50a: server side tsc/internal/api + tsc/internal/ipc, client
side packages/typescript/src/{api,ast} exported as sync and async. The wire
format, method names, params and response schemas follow the ported Go revision; tsrs does not define its own
protocol or a one-shot CLI wrapper, and it is not compatible with TS5 createProgram objects.
Status: shipped (in the npm package since 0.3.0, docs/STATUS.md), with the gaps below. The matrix at the end is
the source of truth: supported
means a core Rust test against a real compiler session exercises it; partial means it is implemented
and dispatched but only covered by the upstream Node suites, component-level checks, or has a documented
divergence. Unimplemented methods return an explicit api: unsupported: ... error, never a fake success.
The recorded beta SDK inventory has 968/968 passing tests on both tsgo and tsrs, with successful responses for 170 of 177 methods. Methods absent from that suite need separate evidence; passing client assertions alone does not establish byte-level response parity. See API validation for the oracle comparison and historical runs.
Historical evidence from the initial API merge (pinned upstream client, node tools/node-api/run-upstream.mjs --binary target/debug/tsrs --label tsrs --suite upstream --filter <file>): test/sync/api.test.ts 339/339 (three
consecutive runs; one earlier run had a single unreproduced failure), test/async/api.test.ts 348/348,
sync/ast 111/111, sync/astnav + async/astnav 4/4 each, sync/api-generators 43/43, parity rss 2/2.
These are suite results, not byte-level response parity; the parity lane's Go-oracle comparison is the
authority for that.
Allocator-perturbation run (MALLOC_PERTURB_=165 TSRS_ARENA_POISON=1 cargo test --no-fail-fast -p <pkg> -- --skip released_snapshots --skip create_and_release --skip repeated_transpile --skip repeated_config; the four
skipped tests measure RSS, which poison mode makes grow by design), counted per test binary at c2abd68:
tsrs_api lib unit tests 27 (mostly the checker lane's), batch_test 1, callbackfs_test 2, config_test 7,
module_resolution_test 2, program_test 9, requestfs_test 6, sourcefile_test 3, transpile_test 2
(tsrs_api total 59); tsrs_project 102 (2 ignored); tsrs_lsp 41. "197"/"202" in earlier reports are these
three packages summed at different heads; a count restricted to core-owned tsrs_api integration binaries is
32. All pass, 0 fail.
The API was developed in parallel lanes; the names stay in use below and in the matrix.
| area | code |
|---|---|
| core | crates/tsrs_api (except src/checker.rs, src/checker/**), crates/tsrs_cli/src/api.rs, --api dispatch in main.rs |
| runtime | crates/tsrs_api_transport (MessagePack tuple protocol, JSON-RPC framing, sync/async conns, stdio/pipe transports, timing) |
| codec | crates/tsrs_api_codec (AST binary encoder/decoder, string table, node index tables, source-file id) |
| checker | crates/tsrs_api/src/checker.rs, crates/tsrs_api/src/checker/**, necessary checker exports |
| sdk | npm/** (ported sync / async client, AST package, Node tests) |
| parity | tools/node-api/**, the node-api-clients job in .github/workflows/ci.yml; the older standalone node-api.yml.bak workflow is disabled |
pub enum Response { Json(String), Binary(Vec<u8>) } // Go `any` result / `RawBinary`
pub struct ApiError { pub kind: ErrorKind, pub message: String } // Display == Go err.Error()
pub enum ErrorKind { InvalidRequest, ClientError, Unsupported, Internal }
pub trait Handler: Send + Sync { // Go ipc.Handler
fn handle_request(&self, method: &str, params: &[u8]) -> ApiResult<Response>; // params = raw JSON bytes ("" = absent)
fn handle_notification(&self, method: &str, params: &[u8]) -> ApiResult<()>;
}
pub trait ClientConn: Send + Sync { // Go ipc.Conn.Call (server -> client)
fn call(&self, method: &str, params: &str) -> ApiResult<String>; // JSON text in/out
}
tsrs_api::Session::new(SessionOptions { cwd, default_library_path, fs, binary_responses, run_external_code }) -> Arc<Session>
session.set_connection(Arc<dyn ClientConn>); session.close(); // Session: Handlerbinary_responsesis true for the sync MessagePack protocol (GoUseBinaryResponses); source-file responses are thenResponse::Binary, otherwise{"data": base64}JSON.echoandpingare handled before method lookup exactly like Go. Unknown methods areInvalidRequest(unknown API method "x").- Both protocols put errors on the wire as
jsonrpc.CodeInternalErrorwitherr.to_string(). - Handler panics are caught and returned as
panic: ...errors (Go conn recovers too).
The transport does not depend on tsrs_api. It defines its own handler/conn traits with the same shape;
crates/tsrs_cli/src/api.rs (core) adapts them. Entry points:
- a sync conn running the msgpack
[type, method, payload]tuple protocol over stdin/stdout or a pipe, supporting re-entrantcallfrom inside a request (filesystem callbacks), with concurrent callbacks answering nested requests in stack order, and an async JSON-RPC conn with concurrent request dispatch and outstanding server->client calls; RawBinary-equivalent results written as msgpackbin;getServerTiming/resetServerTiminghandled in the conn as in Goipc/timing.go;- deterministic shutdown: EOF on input ends
runwithOk, malformed frames end it with an error without writing non-protocol bytes to stdout.
--api --pipe <path> creates a new private directory on Unix, with a socket at
<path>/socket (0700 directory, 0600 socket); an existing path is an error. On Windows,
<path> is a named pipe restricted to the current user. The spawned sync client gives its
Windows pipe a random 128-bit suffix. LSP custom/initializeAPISession returns the same private pipe transport for an API session over the language
server's project session; an API-client disconnect releases its resources, and an explicit LSP shutdown stops pending
and connected sessions. Like pinned Go (3e9236361fa9), LSP EOF or context cancellation alone does not stop a
pending API accept. Its listener remains until explicitly stopped or the process exits, and the private Unix socket
directory survives process exit. See the LSP known gaps.
getCurrentLanguageServerSnapshot flushes pending editor changes and applies the API client's opens, closes and
program changes. Each client owns its open references and synthetic programs; closing it releases those resources.
Standalone API sessions reject this method with Go's client error.
Build orchestration is also available through this connection. As in Go, builds use the session host's filesystem
and current directory, reading saved files rather than editor overlays.
encode_source_file(&'static SourceFile) -> Result<(Vec<u8>, NodeIndexTable), EncodeError>(Goencoder.EncodeSourceFile) andset_source_file_id(&mut [u8], u64);- a per-file node index table (Go
encoder.GetNodeIndexTable) mapping index <-> node so core can produce/resolve node handles"<index>.<kind>.<path>"; decode_nodes(&[u8])(Goencoder.DecodeNodes) forprintNode/formatNodeForInsertionandcreateSourceFileround trips.- Positions: the encoder itself writes UTF-16 positions (as the pinned Go encoder does); request positions from the client are UTF-16 and are converted with the file's position map.
#[derive(Default)] pub struct CheckerSnapshotState { .. } // per-snapshot registries, dropped with the snapshot
pub(crate) fn handle(session: &Session, method: &str, params: &json::Value) -> Option<ApiResult<Response>>;Core provides (stable API for the checker lane):
Session::snapshot_data(SnapshotID) -> ApiResult<Arc<SnapshotData>>(pins the snapshot);Session::setup_checker(SnapshotID, &ProjectID) -> ApiResult<CheckerSetup>with fieldssd,snapshot,project,program: &'static Program,checker: CheckerHandle; the setup keeps the snapshot alive and holds the API-lifetime checker until dropped (not reentrant);SnapshotData::get_program,SnapshotData::checker_state;session::parse_params,session::json_response; node handles throughCheckerHost::node_handle/resolve_node_handle(core wraps the codec'snode_handle/resolve_node_index).
handle receives every method whose methods::METHODS owner is Checker (119 methods). None means
"not recognized" and becomes ApiError::unsupported.
- Snapshot handles map to
Arc<SnapshotData>;releasedrops the registry reference and the project snapshot is dereferenced when the last in-flight request finishes. - Region pointers (
P<T>,&'static Program) never cross the wire and are only reached through a liveSnapshotData. Handles from another session or from a released snapshot fail withapi: client error.
The pinned protocol has 177 Method constants (core 58, checker 119). Kept in sync with
crates/tsrs_api/src/methods.rs.
Checker-scoped methods include getMergedSymbol, getSymbolOfNode,
getSymbolOfDeclarationForChecker, and getParentOfSymbolForChecker. All resolve handles in the requested
snapshot and project. The node/declaration and parent methods return null when no symbol exists; the
existing core getSymbolOfDeclaration instead retrieves a binder symbol from a cached source file.
| # | method | owner | status | evidence / gap |
|---|---|---|---|---|
| 1 | release |
core | supported | program_test (refcounted; stale/cross-session handles rejected) |
| 2 | releaseSourceFile |
core | supported | sourcefile_test |
| 3 | retainSourceFile |
core | partial | descriptor validation tested; positive path via upstream suites |
| 4 | getCachedSourceFile |
core | partial | via upstream suites only |
| 5 | getSymbolOfDeclaration |
core | supported | sourcefile_test and upstream sync/async declaration-symbol, cache, and stale-descriptor cases |
| 6 | batchRequests |
core | supported | batch_test (nesting error, pagination with continuation tokens); binary-in-batch base64 path untested |
| 7 | initialize |
core | supported | config_test |
| 8 | createSnapshot |
core | supported | program_test, module_resolution_test, requestfs_test (full/layer request filesystems, removedPaths) |
| 9 | updateSnapshot |
core | supported | program_test, requestfs_test (layers compacted over full, retained base, release of base, fileNotifications expanded to request-symlink aliases) |
| 10 | getCurrentLanguageServerSnapshot |
core | supported | session_apistate_test (port of Go's session_apistate_test.go: pending LSP changes, per-session open/close ownership, created programs released on close, standalone sessions rejected); tsrs_lsp server_apisession_test (custom/initializeAPISession, open-file overlays over the socket, shutdown) |
| 11 | createBuildOrchestrator |
core | supported | tsrs_cli api_test and tsrs_lsp server_apisession_test (shared in-process build backend; fresh orchestrator per call instead of Go's recheckAllProjects reuse) |
| 12 | disposeBuildOrchestrator |
core | supported | tsrs_cli api_test |
| 13 | build |
core | supported | tsrs_cli api_test (references, up-to-date rebuild) |
| 14 | buildReferences |
core | supported | tsrs_cli api_test |
| 15 | cleanBuild |
core | supported | tsrs_cli api_test |
| 16 | cleanReferences |
core | partial | ported Go clean(onlyReferences); no dedicated test |
| 17 | createModuleResolver |
core | supported | module_resolution_test (default, static entries, callback; custom resolutions skip disk-layout import diagnostics under rewriteRelativeImportExtensions) |
| 18 | releaseModuleResolver |
core | supported | module_resolution_test |
| 19 | resolveModuleName |
core | supported | module_resolution_test (standalone, snapshot-scoped callback); inProgressSnapshot path exercised only via callbacks during program build |
| 20 | parseCommandLine |
core | supported | config_test |
| 21 | readConfigFile |
core | supported | config_test |
| 22 | parseJsonConfigFileContent |
core | supported | config_test |
| 23 | parseConfigFile |
core | supported | config_test |
| 24 | createSourceFile |
core | supported | sourcefile_test, upstream sync suite |
| 25 | createSourceFileFromFile |
core | partial | dispatches; exercised by upstream suites |
| 26 | transpileModule |
core | supported | transpile_test |
| 27 | transpileModuleFromFile |
core | supported | transpile_test |
| 28 | transpileDeclaration |
core | supported | transpile_test (upstream api.test.ts expectations); observed: no TS9007 for export function g() { return 1; } under isolatedDeclarations, unverified against tsgo |
| 29 | transpileDeclarationFromFile |
core | supported | transpile_test |
| 30 | getDefaultProjectForFile |
core | supported | program_test |
| 31 | getSymbolAtPosition |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 32 | getSymbolsAtPositions |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 33 | getSymbolAtLocation |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 34 | getSymbolsAtLocations |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 35 | getSymbolOfSourceFile |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 36 | getSymbolsOfSourceFiles |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 37 | getTypeOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 38 | getTypesOfSymbols |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 39 | getDeclaredTypeOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 40 | getNonMissingTypeOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 41 | getSourceFile |
core | supported | sourcefile_test (binary + base64, decoded by tsrs_api_codec), upstream sync/async suites |
| 42 | getSourceFileNames |
core | supported | program_test |
| 43 | getSourceFileMetadata |
core | supported | program_test |
| 44 | getModeForUsageLocation |
core | partial | via upstream sync suite (Program resolved modules) |
| 45 | getModeForResolutionAtIndex |
core | partial | via upstream sync suite |
| 46 | getResolvedModule |
core | partial | via upstream sync suite |
| 47 | getResolvedModuleFromModuleSpecifier |
core | partial | via upstream sync suite |
| 48 | getResolvedTypeReferenceDirective |
core | partial | via upstream sync suite |
| 49 | getResolvedTypeReferenceDirectiveFromTypeReferenceDirective |
core | partial | via upstream sync suite |
| 50 | getConfigFileNames |
core | supported | program_test (extends chain, synthetic null) |
| 51 | getConfigSourceFile |
core | partial | dispatches (root + extended configs); exercised only by upstream suites |
| 52 | resolveName |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 53 | getSymbolsInScope |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 54 | getSignaturesOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 55 | getResolvedSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 56 | getTypeAtLocation |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 57 | getTypeAtLocations |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 58 | getTypeAtPosition |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 59 | getTypesAtPositions |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 60 | getParentOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 61 | getMembersOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 62 | getExportsOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 63 | getExportSymbolOfSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 64 | getSymbolOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 65 | getTargetOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 66 | getFreshTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 67 | getRegularTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 68 | getTypesOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 69 | getTypeParametersOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 70 | getOuterTypeParametersOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 71 | getLocalTypeParametersOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 72 | getThisTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 73 | getAliasTypeArgumentsOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 74 | getAliasSymbolOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 75 | getObjectTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 76 | getIndexTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 77 | getCheckTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 78 | getExtendsTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 79 | getBaseTypeOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 80 | getConstraintOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 81 | getTypeParameterOfMappedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 82 | getConstraintTypeOfMappedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 83 | getNameTypeOfMappedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 84 | getTemplateTypeOfMappedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 85 | getTypeParametersOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 86 | getParametersOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 87 | getThisParameterOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 88 | getTargetOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 89 | getContextualType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 90 | getContextualTypeForArgument |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 91 | getAwaitedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 92 | getBaseTypeOfLiteralType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 93 | getNonNullableType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 94 | getTypeFromTypeNode |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 95 | getWidenedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 96 | getParameterType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 97 | getTypeParameterAtPosition |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 98 | isArrayLikeType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 99 | isTypeAssignableTo |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 100 | getShorthandAssignmentValueSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 101 | getTypeOfSymbolAtLocation |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 102 | typeToTypeNode |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 103 | signatureToSignatureDeclaration |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 104 | typeToString |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 105 | isContextSensitive |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 106 | getReturnTypeOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 107 | getRestTypeOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 108 | getTypePredicateOfSignature |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 109 | getBaseTypes |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 110 | getPropertiesOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 111 | getApparentPropertiesOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 112 | getApparentType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 113 | getReducedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 114 | getPropertyOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 115 | getTypeOfPropertyOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 116 | getIndexInfoOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 117 | getIndexInfosOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 118 | getConstraintOfTypeParameter |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 119 | getDefaultFromTypeParameter |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 120 | getBaseConstraintOfType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 121 | getTypeArguments |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 122 | getImportAdderEdits |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 123 | getTrueTypeOfConditionalType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 124 | getFalseTypeOfConditionalType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 125 | getConstantValue |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 126 | getSignatureFromDeclaration |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 127 | getExportSpecifierLocalTargetSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 128 | getAliasedSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 129 | getImmediateAliasedSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 130 | getTargetSymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 131 | getMergedSymbol |
checker | partial | checker lane: source 29 real-program Rust test and upstream sync/async API cases; no pinned-Go golden or byte-level response comparison |
| 132 | getSymbolOfNode |
checker | partial | checker lane: source 29 real-program Rust test and upstream sync/async API cases; no pinned-Go golden or byte-level response comparison |
| 133 | getSymbolOfDeclarationForChecker |
checker | partial | checker lane: source 29 real-program Rust test and upstream sync/async API cases; no pinned-Go golden or byte-level response comparison |
| 134 | getParentOfSymbolForChecker |
checker | partial | checker lane: source 29 real-program Rust test and upstream sync/async API cases; no pinned-Go golden or byte-level response comparison |
| 135 | getExportSymbolOfSymbolForChecker |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 136 | getFullyQualifiedName |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 137 | getExportsOfModule |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 138 | getMemberInModuleExports |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 139 | getJsDocTags |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 140 | getDocumentationComment |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 141 | isArrayType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 142 | isReadonlySymbol |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 143 | getReferencesToSymbolInFile |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 144 | getReferencedSymbolsForNode |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 145 | getSignatureUsages |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 146 | getCompletionsAtPosition |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 147 | getSyntacticDiagnostics |
core | supported | program_test |
| 148 | getBindDiagnostics |
core | supported | program_test |
| 149 | getSemanticDiagnostics |
core | supported | program_test |
| 150 | getSuggestionDiagnostics |
core | partial | dispatches; no fixture asserting a specific suggestion yet |
| 151 | getDeclarationDiagnostics |
core | supported | program_test |
| 152 | getProgramDiagnostics |
core | supported | program_test (empty case) |
| 153 | getGlobalDiagnostics |
core | supported | program_test (empty case) |
| 154 | getConfigFileParsingDiagnostics |
core | supported | program_test (client-supplied diagnostics round trip) |
| 155 | printNode |
core | partial | upstream sync printNode/printFile tests pass; recovered printer panics use Go's Kind… names (printing.rs unit test) |
| 156 | formatNodeForInsertion |
core | partial | upstream sync formatNodeForInsertion tests pass |
| 157 | emit |
core | supported | program_test (write-through, no TSRS_EMIT), requestfs_test (full filesystem returns emittedFilesContents, no disk write) |
| 158 | emitToString |
core | supported | program_test |
| 159 | getJavaScriptEmit |
core | supported | program_test |
| 160 | getDeclarationEmit |
core | supported | program_test |
| 161 | getAnyType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 162 | getStringType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 163 | getNumberType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 164 | getBooleanType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 165 | getVoidType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 166 | getUndefinedType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 167 | getNullType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 168 | getNeverType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 169 | getUnknownType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 170 | getBigIntType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 171 | getESSymbolType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 172 | getNonPrimitiveType |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 173 | getWellKnownSymbols |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 174 | getWellKnownSignatures |
checker | partial | checker lane: real-snapshot unit tests + pinned-Go golden subset; dispatched through core Session; not byte-compared end-to-end |
| 175 | startCPUProfile |
core | not implemented | |
| 176 | stopCPUProfile |
core | not implemented | |
| 177 | saveHeapProfile |
core | not implemented |
cargo test -p tsrs_api --test integration requestfs_differential runs the production request filesystem against 11
pinned-Go scenarios (20 updates, 142 filesystem queries and every update's change summary), plus layering over
real files and symlinks. No Cargo feature is needed. Fixtures and their generator are in
crates/tsrs_api/tests/fixtures/: place requestfs_gen.go.txt in the pinned
tsc/internal/api/requestfilesystem package and run
GOWORK=off SCENARIOS=<requestfs_scenarios.json> SCENARIOS_OUT=<out> go test -run ZZDump ./internal/api/requestfilesystem/.
- Request filesystems are ported (
crates/tsrs_api/src/requestfs.rs) and enter the project snapshot as Go's layered, rebasable file system: overlays are rebased under them and clientfileNotificationsare expanded to request-symlink aliases (GoExpandFileChanges; requestfs_test). Callback filesystems (--callbacks, Gocallbackfs.go) are ported incrates/tsrs_api_transport/src/callbackfs.rs; like Go, invalid callback responses panic and become request errors (a panic on a worker thread can poison shared caches; not yet hardened). - API builds differ from
tsrs -bin how they run, not in what they write: eachbuildcall uses a fresh CLI orchestrator (the state Go'srecheckAllProjectsleaves; reuse across builds comes from the.tsbuildinfofiles on disk, as for any rebuild), runs one project at a time on the request thread, and builds programs single-threaded, so--buildersand checker parallelism do not speed up API builds andsingleThreadedis forced on (it does not appear in outputs or build info). The output files,.tsbuildinfoincluded, and the exit status are byte-identical totsrs -bon the same graph (api_build_outputs_match_cli_build), and up-to-date rebuilds build nothing (api_test). This is what makes per-build memory freeable; it trades build throughput for bounded memory. - Build orchestration runs the
tsc -borchestrator in-process throughtsrs_api::build::BuildBackend. Every API session installs this backend by default, including library and LSP-attached sessions; embedding hosts can replace it. Clean uses the last build's graph like Go; clean existence checks use the uncached filesystem. - Profiling (
startCPUProfile,stopCPUProfile,saveHeapProfile) is not implemented (no pprof equivalent). - Node index tables are cached per live source file keyed by the file's address (lookups never assign the file's lazy node id) and evicted when the file's region is freed; Go stores them on the file itself.
- Re-entrancy attribution: snapshot builds read files through a
ScopedFsthat enters the creating request's transportRequestScope, so client callbacks from compiler worker threads are attributed to that request; API builds run their tasks on the request thread. Remaining divergence: a request whose own slow client callback holds a contended resource cannot be told apart from callback re-entry by attribution alone, so a waiter can still get the re-entrancy error after the transport's configurable grace period where Go would wait (or hang). Reads made later by a retained snapshot count against its finished request. - Re-entrancy: nested requests from client callbacks are served; the build orchestrator lock uses the
transport's
lock_for_request(bounded error instead of deadlock); the checker lane gates its API checker lease the same way. Request params are validated with Go's strict JSON (unpaired surrogates, duplicate members) before decoding;nullparams decode to the zero-value struct. Field-type mismatches have Go's invalid-request class andfailed to unmarshal *api.<Type>prefix, but not Go's exact jsontext wording. Every method's top-level params fields (generated from pinnedproto.gointoparamfields.rs) are checked in Go's field order before any lookup, for core and checker methods alike (predecode.rs); unknown keys are ignored. Integer fields are checked against the raw number lexeme and the Go type's range (1e3/1.0are invalid syntax; int32/uint32/int/uint64 bounds), array element kinds are checked, and null elements of[]string-like arrays are zero values, as in Go. Remaining wording differences (runtime matrix on f70371e): Go includes the number literal and: invalid syntax, reports the first invalid field in document order (this reports struct order), qualifies element types ([]api.BatchRequest) and prints alias names (core.ModuleKind). Nested api-package structs (program options, symbol references, import adder actions, module-resolution specs, diagnostics, file notifications) are checked the same way before any lookup, andproject.SyntheticProjectIDvalues go through Go's decoder rules (null and non-synthetic text are decode errors). Integer IDs are read from their own literal (by position in the parsed params tree), so values above 2^53 are exact at any depth; batch items keep their raw params bytes (Gojson.Value). Structs from other Go packages are decoded from the parsed value with Go's rules, checked against runtime's c246 session review: integer fields ofcore.CompilerOptions(int32 enums,*int) andcore.BuildOptions.buildersneed plain integer syntax (1e1is invalid).target,moduleandjsxare int32 newtypes (as in Go), so any number Go accepts is kept, echoed and compiled with Go's semantics: switch defaults and comparisons see the raw value (e.g.target:12345transforms maximally,module:-1emits CommonJS,jsx:12345keeps JSX without the "--jsx not set" error); checked against runtime-f2-review's cases.moduleResolutionis an int32 newtype too: a number with no named kind is kept (createPrograms, createBuildOrchestrator, builds of import-free projects and their outputs/cleans work as in Go), and resolving a module with it reaches the ported resolver'sUnexpected moduleResolutionpanic at the same point as pinned Go (resolver.goResolveModuleName); Go's server crashes there, tsrs converts that panic to the stable client errorunsupported moduleResolution value N (not a ModuleResolutionKind)and stays usable (orchestrator map lock poisoning is tolerated). UnknownmoduleDetection/newLinenumbers are echoed and compile like Go's fallback (runtime f552 review). Go*intoptions (maxNodeModuleJsDepth,builders,checkers) take any int64, exact from the request literal and echoed exactly (json::Value::Integer); above int64 is out of range.checkersis clamped like Go (max(min(n, files, 256), 1); tsrs used to turn a negative count into the file count). Prior limitation, not API-specific: a tsconfig float far outside int64 (9.3e18,1e19) for these options saturates in Rust; Go's float-to-int conversion is architecture dependent there (amd64 gives MinInt64, arm64 saturates). Malformed tristates and unknown options are ignored, as in Go. Request filesystems are type-checked before any lookup (kind,files,directories,symlinks.target/host,removedPaths), withnullvalues and elements as zero values. Pinned Go crashes on a relative, empty or null project reference path and on a missing referenced project; tsrs instead returns a client error for non-absolute paths and reports a missing project as aFile '…' not foundprogram diagnostic. Error wording is encoding/json v1 style. - Response shapes from paired runs (parity f703):
updateSnapshotomits an emptychanges(json/v2omitempty);cleanBuildkeeps Go's per-orchestrator existence answers until the next build, so a clean after a clean lists the project's outputs again. - Unwinding builds (a panic while building a program that the API answers as an error, today the module
resolver's
Unexpected moduleResolution): a snapshot clone rolls back the parse-cache references its programs took (journal inProjectCollectionBuilder), the file loader frees its project reference mapper, createSnapshot releases its root reference and an API build frees its fresh orchestrator, so repeated failures stay flat (codec d9be067 probe;failed_program_builds_release_their_files,failed_builds_free_their_orchestrator). Other state an arbitrary panic could leave is not audited. - Memory: snapshots free their programs, checkers, emit allocations and a full build's shared data
(processed files, project-reference mapper, loader and dts-faking resolution hosts) with the build's base
region (createSnapshot+release cycles stay flat;
tests/memory_test.rs). The first attempt at freeing the mapper exposed a pre-existing use-after-free intsrs_project(update_programread the replaced program after dropping its owner); fixed, with a perturbation/ASAN-validated regression test (inferred_project_rebuild_frees_safely). Transpile frees its one-file program including the compiler checker pool's checkers (previously leaked; ~5 KiB/call of allocator retention remains), and config requests run in scratch regions. API builds allocate in per-task regions (one builder, tasks run on the request thread, programs single-threaded and freed after each project) and each build handle keeps only its last build (for cleans); 120 rebuilds retain no more than one (memory_testsincrates/tsrs_cli/src/api.rs; ~19 MiB per rebuild before). File texts parsed inside a freeable region are copied into it and unregistered with it instead of leaked. Still retained: the request thread's arena use outside these paths. Unregistered source texts free their memory but not their registry slot; slots are never reused, so a very long-lived session that parses more than 2^20 files in freeable regions falls back to the slower text lookup path.