Summary
The JOIN methods in packages/sqlite-wrapper/src/query-builder/index.ts currently mutate the existing QueryBuilder instance and then cast this to a new merged result type (for example QueryBuilder<T, ResultType & JT>). This can make older references to the same builder appear type-safe at compile time while executing a different joined query at runtime.
Why this matters
The current API contract suggests that methods like join, innerJoin, leftJoin, rightJoin, fullJoin, and crossJoin return a freshly typed builder. In practice they mutate shared state on the same instance. That mismatch can lead to unsound typing and surprising behavior when the same builder reference is reused.
Required changes
Choose one consistent approach for all JOIN methods:
- Preferred: return a fresh cloned builder instance before applying the JOIN, preserving correct type evolution, or
- keep the API mutable and do not widen the generic result type when returning
this.
If the cloning approach is chosen, make sure the new builder preserves the current query state needed for chaining (for example WHERE clauses, params, ordering, limits, offsets, selected columns, and JOIN state as applicable).
Affected areas
packages/sqlite-wrapper/src/query-builder/index.ts
- Potentially related state-sharing / cloning logic in:
packages/sqlite-wrapper/src/query-builder/select.ts
packages/sqlite-wrapper/src/query-builder/join.ts
packages/sqlite-wrapper/src/query-builder/base.ts
packages/sqlite-wrapper/src/types.ts
Acceptance criteria
- All JOIN methods use one consistent correctness-preserving strategy.
- The runtime behavior matches the public TypeScript return types.
- Reusing an older builder reference cannot silently execute a differently typed joined query.
- Tests cover the chosen behavior, especially around builder reuse after a JOIN chain.
Backlinks
Requested by @Its4Nik.
Summary
The JOIN methods in
packages/sqlite-wrapper/src/query-builder/index.tscurrently mutate the existingQueryBuilderinstance and then castthisto a new merged result type (for exampleQueryBuilder<T, ResultType & JT>). This can make older references to the same builder appear type-safe at compile time while executing a different joined query at runtime.Why this matters
The current API contract suggests that methods like
join,innerJoin,leftJoin,rightJoin,fullJoin, andcrossJoinreturn a freshly typed builder. In practice they mutate shared state on the same instance. That mismatch can lead to unsound typing and surprising behavior when the same builder reference is reused.Required changes
Choose one consistent approach for all JOIN methods:
this.If the cloning approach is chosen, make sure the new builder preserves the current query state needed for chaining (for example WHERE clauses, params, ordering, limits, offsets, selected columns, and JOIN state as applicable).
Affected areas
packages/sqlite-wrapper/src/query-builder/index.tspackages/sqlite-wrapper/src/query-builder/select.tspackages/sqlite-wrapper/src/query-builder/join.tspackages/sqlite-wrapper/src/query-builder/base.tspackages/sqlite-wrapper/src/types.tsAcceptance criteria
Backlinks
Requested by @Its4Nik.