Repository navigation
TypeScript SDK: add the Join helper for parallel branches - #1552
Merged
Merged
Conversation
Join synchronizes parallel branches through a ChannelMap of branch
results. fork keys a round by the split Step execution ID and counts the
branch builders; forkWithRoundKey takes an explicit key for RPCs.
Branches report with endBranch or reportBranchResult; the join Step
waits with forAllBranches, forFirstBranch, or forBranches; the next
round resets the previous one with forRemainingBranches.
JoinRound is the only new persistent shape: {roundKey, branchCount}.
PersistenceSchema.channels accepts a Join, which registers as its
ChannelMap. Adds the JoinRound contract test and integration tests.
The TypeScript analyzer now treats new Join(...) as a ChannelMap resource, reads the branch and join movements of Join.fork and Join.forkWithRoundKey, including through a local variable, and treats Join.endBranch as a dead-end decision. Adds a join.ts fixture golden.
Make the Join example complete and compilable, document that a cancelling join's round resets only after substitute results, recommend explicit cancellation for RPC-forked rounds, and state that requeueing consumed results on cancellation depends on the server version. The all-branches test now starts the join Step only after both results commit, and the first-branch test reads the pending results through an RPC while the run waits.
…tBranch on empty rounds The forkWithRoundKey example is now a complete, compilable Flow. forFirstBranch on a round without branches fails waitFor, matching the cross-SDK rule; the invalid-count integration scenario covers it.
This was referenced Nov 11, 2026
Join.fork and forkWithRoundKey builders passed through a local variable initialized with an array literal or a map call no longer report a dynamic target. Builders pushed onto the local list count as repeated branches. The join.ts fixture covers both forms.
Join.channelMap is the registered ChannelMap that carries the branch results, so applications can load and read a round's pending results with the ChannelMap API, for example in an RPC. Integration tests read pending results through it; a contract test pins that it is the registered definition.
A push adds Join branches only when it targets the same binding as the fork's list and runs before the fork on its path. Pushes after the fork, in the other branch of an if, or on a shadowing variable are ignored. A conditional push or another use of the list reports a dynamic target. Only pushes in loops or iteration callbacks are marked as repeated.
Only Array iteration callbacks count as loops; a push in any other callback is undetermined and reports a dynamic target. Non-push uses of a builder list follow the same position and path rule as pushes, so uses after the fork are ignored. A movement list spread into goToMany uses the same classification, so a single push is no longer repeated.
The next shared SDK release is 1.43.0, so the Join version markers move from 1.41.0 to 1.43.0.
A movement list spread into goToMany again yields every pushed target, including conditional pushes; only a push in a loop or a callback that may repeat is marked repeated. Array literal elements count once. The strict undetermined rule stays for Join branch builder lists. Iteration callbacks count as loops only when their receiver is provably an array.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced Nov 11, 2026
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.
Summary
Adds the Join helper to the TypeScript SDK: a join construct for parallel branches, built only on the existing ChannelMap. No server, IDL, or persistent-format change besides the
JoinRoundJSON value.Join<T>(listed inPersistenceSchema.channels, registered as its inner ChannelMap):fork/forkWithRoundKeystart the branches and the join Step with oneJoinRound; branches report withendBranchorreportBranchResult; the join waits withforAllBranches,forFirstBranch, orforBranches; the next round resets withforRemainingBranches; results are read withbranchResults/remainingBranchResults.JoinRound { roundKey, branchCount }andjoinRoundCodec, whose JSON{"roundKey","branchCount"}is pinned by a contract test; decoding validates both fields and ignores unknown ones.PersistenceSchema.channelswidens to(Channel | ChannelMap | Join)[]; code that builds schemas is source compatible.forkWithRoundKeytakes an explicit key (for RPCs). Forking the same Join twice in one attempt fails. Invalid counts throw when the condition is built (the SDK's existing style), failing the WaitFor;forFirstBranchon a zero-branch round fails (shared rule for all five SDKs).forkWithRoundKeyexample) and the rules: each branch reports exactly once;receivedCountcounts only what the join consumed; a cancelling join's round is fully reset only if an RPC reports the cancelled branches; RPC forks use explicit cancellation.@since 1.41.0. Channel and ChannelMap docs point to Join.new Join(...), the movements built byfork/forkWithRoundKey(array literals,.map(...)as ×N), andendBranchas a branch end, with a golden fixture.docs/reference/typescript/changelog.mdx(1.41) andsdk-typescript/CHANGELOG.md.Why
The owner approved the Join helper design (decisions D1-D6, 2026-11-10). Without it, an application implementing a join counts branches and round keys itself, so 14 Workflow Patterns are rated
+/-; with a documented construct in all five SDKs they become+. This is the TypeScript part; Java is #1549, Rust #1550, Go and Python follow. Examples migrate after the SDK release becauseexamples/typescriptpins the released SDK.doc-map: no change needed: SDK API, analyzer, and reference changelog only; docs, skills, and examples change after all five SDKs ship
Testing
npm run docs:check,npm run typecheck(Node 22),npm test(221).test/integ/join.integration.test.ts(12, every scenario in the design's test table, including a gate Step proving results were committed before the join waited and an RPC read of the pending late result after sibling cancellation); fullrun-integration-tests.sh(142).go test -tags=integration ./internal/flowviz/and./internal/command/ -run Visualize;make copyright-check;cd docs && npm run check.