Skip to content

Additional generator-based sync API methods - #64023

Open
Wesley Wigham (weswigham) wants to merge 6 commits into
microsoft:mainfrom
weswigham:generator-sync-api-availability
Open

Additional generator-based sync API methods#64023
Wesley Wigham (weswigham) wants to merge 6 commits into
microsoft:mainfrom
weswigham:generator-sync-api-availability

Conversation

@weswigham

@weswigham Wesley Wigham (weswigham) commented Aug 25, 2026

Copy link
Copy Markdown
Member

Basically, this adds a strongly typed

project.checker.getSymbolAtLocation.gen(node)

method for every

project.checker.getSymbolAtLocation(node)

method. This make it very easy to write a nice to use sync request batching function which runs those generators to produce batched requests, like

const [commandLine, config, symbol] = api.batch(
    api.parseCommandLine.gen(["--strict"]),
    api.readConfigFile.gen("/tsconfig.json"),
    project.checker.getSymbolAtLocation.gen(node)
);

which is also included for the sync API here, giving it an omni-request-batching API that's pretty on-par with the async API experience.

This is like the 3rd shape of this API I've tried and I like this one the most - the codegen complexity is just a bit high, so I'd like some feedback.

As an aside: Really, generators are the most flexible implementation construct here. We could just write the main API with generators and then write both the sync and async APIs as wrappers over it and do away with the async->sync codegen step entirely, but I still worry that generator object allocations are pricey for chatty non-batching API uses, which is why I still have both the straight sync and generator-based method implementations on the sync API (rather than implementing the sync API in terms of the generator one).

cc Titian Cernicova-Dragomir (@dragomirtitian) I think this probably roughly aligns with what you said you'd been building to enable batching internally.

@weswigham
Wesley Wigham (weswigham) marked this pull request as ready for review August 26, 2026 00:22
Copilot AI balanced review requested due to automatic review settings August 26, 2026 00:22
Much more exhaustive sync batcher testing
@weswigham
Wesley Wigham (weswigham) force-pushed the generator-sync-api-availability branch from e0c918c to 4f350d5 Compare August 26, 2026 00:25

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.

Pull request overview

Adds generator-backed sync API methods and batching support while preserving direct synchronous calls.

Changes:

  • Generates .gen() variants for sync API methods.
  • Adds generator request batching and error propagation.
  • Adds broad sync/generator parity tests and shared test utilities.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/typescript/scripts/generateSync.ts Generates sync and generator-backed methods.
packages/typescript/src/api/async/api.ts Refactors source API for generation.
packages/typescript/src/api/sync/api.ts Contains generated generator-enabled APIs.
packages/typescript/src/api/sync/generatorSupport.ts Executes batched request generators.
packages/typescript/src/api/sync/types.ts Exposes typed .gen() methods.
packages/typescript/test/async/api.test.ts Uses shared async test setup.
packages/typescript/test/async/api.testUtils.ts Provides async test utilities.
packages/typescript/test/sync/api.test.ts Uses shared sync test setup.
packages/typescript/test/sync/api.testUtils.ts Provides sync test utilities.
packages/typescript/test/sync/api-generators.test.ts Tests batching and API parity.

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

Comment on lines 358 to +364
if (
ts.isCallExpression(node.expression) &&
ts.isPropertyAccessExpression(node.expression.expression) &&
ts.isPropertyAccessExpression(node.expression.expression.expression) &&
node.expression.expression.expression.name.text === "client"
) {
return getTextWithOwner(node.expression);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sync binary requests can't batch anyway (rip), so this is actually fine. A feature, even. Were we to force the issue, we'd have to double decode the AST payload, which sounds terrible (once into a base64 blob in the json batch response, then again into a buffer). This is one of those cases where a bespoke getSourceFiles API with proper encoder support for the direct binary response would be good.

Comment on lines +64 to +66
const state = result.error
? requestGenerator.throw(new Error(result.error))
: requestGenerator.next(result.result);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintaining control flow just for finally cleanup blocks in the case where a subset of a batch requests has an unhandled throw is some gnarly morally reentrant state management. Like, it can be done, but I don't really like the shape it takes to do what it has to do. See here. Like - sending follow-up requests from sibling batch requests after an unhandled failure result from the server kills the whole batch? Just seems wrong.

How about we just don't, and acknowledge exceptions are exceptional? Expecting all your other requests in a batch to exit and cleanup normally when one has an unhandled exception seems rough. I'm willing to state it here: "If one request in a batch throws an unhandled (server) exception, other generators in the batch will not continue or cleanup" - There. Now it's an API consumer's problem if they use exceptions for control flow. I'm not even sure that the API client is safe to keep using if the server yields an error response for one of the methods in a batch.

Comment thread packages/typescript/src/api/sync/generatorSupport.ts
Comment thread packages/typescript/scripts/generateSync.ts Outdated
Comment thread packages/typescript/src/api/async/api.ts Outdated
@dragomirtitian

Titian Cernicova-Dragomir (dragomirtitian) commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

This is the version we have: https://gist.github.com/dragomirtitian/150926e5900e995771f1618f72051c25

The version I have here allows for composability of functions that use the batching. Is there the possibility to do that in the version in this PR? From what I see the APIRequestGenerator should allow for that, although I don't see any examples of it. Also the version we have adds things like all and spawn that allow adding tasks in the batch queue.

We were thinking of open sourcing it as a general library not tied to TSAPI, but if it's build into TS might not really be needed.

@weswigham

Copy link
Copy Markdown
Member Author

api.batch should be the equivalent of your runBatch helper and you aughta be free to compose the generator methods into your own host generator func if you'd like (the type signature is generic enough), though you're right that I don't have any tests shaped like that yet - I'll add some. I don't have a Promise.all equivalent helper exported yet, but that's not, strictly speaking, something you should need any TS internals to make. Regardless, in the same way Promise.all is useful for the async API, it's a good idea to have, and common enough to want to use, so I'll add one here.

@weswigham

Copy link
Copy Markdown
Member Author

Cool, I've extracted the all equivalent helper from the generator-running logic and now export it directly in the sync API, explicitly tested a few user-composed generator functions, added batch-flattening to the batch request builder in all, and finally removed the api-protocol-level batchRequests helper from API and pushed it down to Client (where it probably belongs), since we have nice alternatives at the API level to directly mucking about with API request objects.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants