feat(query)!: better joins #1

Merged
dan merged 6 commits from better-joins into main 2026-09-13 21:33:24 +00:00
Member

Summary

Completes the better-joins OpenSpec change: 29/29 tasks verified.

  • Replace join-after-select with table-first source composition.
  • Support join, left/right/full/cross joins, optional ON/USING, typed strings/refs, aliases, and qualified wildcards.
  • Infer only projected outputs, including required nullable outer-join fields and conservative duplicate-name types.
  • Preserve source-aware mappings, subquery snapshots, and projected view metadata.
  • Use explicit domain names QuerySource and QuerySourceBuilder, with matching filenames and no old-name aliases.
  • Match existing construction style: source refs remain plain objects; schema/table/view entry points create separate builders. Builder internals use TypeScript private, with no ES-private fields or bound-method forwarding.
  • Update README with all forms and breaking-change migration guidance.

Breaking: replace select(...).join(...) with join(...).select(...).

Approved SQLite exception

For explicitly projected unqualified common columns coalesced by FULL JOIN USING, emit a same-name AS so SQLite does not retain identifier quotes in the output key. Caller aliases remain optional. No source aliases, distinct output names, row rewriting, or capability probes are invented.

Verification

  • tests/joins.test.ts: 61 permanent Bun tests, 119 runtime assertions, passing on Bun 1.4.2 / SQLite 3.53.2.
  • tests/joinTypes.ts: 29 exact type assertions and 22 negative compile-time checks, verified by bun run check.
  • Every new README join example compiles and executes.
  • bun run check: pass.
  • bun run lint: pass, 14 existing warnings and no errors (baseline: 17 warnings).
  • bun run test: 61 pass, 0 fail. Bun still emits its existing directory-mismatch diagnostic.
  • Configured formatting check on all 23 changed TypeScript files, diff whitespace checks, and strict OpenSpec validation pass.
  • Permanent regressions are retained in tests/, using the existing Bun runner and TypeScript test project. No dependencies or configuration changes.

Existing limitations outside scope

  • Inserting NULL through the mapped-date INSERT conversion throws; nullable storage fixtures use raw SQL setup.
  • Existing schema SQL generation splits dotted names; SELECT literal-component escaping is fixed without changing schema generation.
  • Bun's cached Database.query() can retain old row keys after a view's output columns are renamed. Reproduced without Outlaw; fresh prepared statements return correct names. View recreation/convergence was verified with fresh statement shapes. No adapter cache-policy changes were made.
  • No live D1 verification.

Commits

  • a30bdff: source-first typed join pipeline.
  • 9d6afa1: approved naming exception and literal identifiers.
  • 7d9fece: isolate source internals, final documentation, and formatting.
  • b987652: retain permanent runtime and compile-time regression tests.
  • a4b81fc: separate source objects from builders and restore existing private-member conventions.
  • e955507: rename query source types, files, imports, and exports.

All commits are pushed. Progress and verification are mirrored in this PR. Ready for review; the change has not been archived.

## Summary Completes the `better-joins` OpenSpec change: **29/29 tasks verified**. - Replace join-after-select with table-first source composition. - Support join, left/right/full/cross joins, optional ON/USING, typed strings/refs, aliases, and qualified wildcards. - Infer only projected outputs, including required nullable outer-join fields and conservative duplicate-name types. - Preserve source-aware mappings, subquery snapshots, and projected view metadata. - Use explicit domain names `QuerySource` and `QuerySourceBuilder`, with matching filenames and no old-name aliases. - Match existing construction style: source refs remain plain objects; schema/table/view entry points create separate builders. Builder internals use TypeScript `private`, with no ES-private fields or bound-method forwarding. - Update README with all forms and breaking-change migration guidance. **Breaking:** replace `select(...).join(...)` with `join(...).select(...)`. ## Approved SQLite exception For explicitly projected unqualified common columns coalesced by FULL JOIN USING, emit a same-name `AS` so SQLite does not retain identifier quotes in the output key. Caller aliases remain optional. No source aliases, distinct output names, row rewriting, or capability probes are invented. ## Verification - `tests/joins.test.ts`: **61 permanent Bun tests**, **119 runtime assertions**, passing on Bun 1.4.2 / SQLite 3.53.2. - `tests/joinTypes.ts`: **29 exact type assertions** and **22 negative compile-time checks**, verified by `bun run check`. - Every new README join example compiles and executes. - `bun run check`: pass. - `bun run lint`: pass, 14 existing warnings and no errors (baseline: 17 warnings). - `bun run test`: **61 pass, 0 fail**. Bun still emits its existing directory-mismatch diagnostic. - Configured formatting check on all 23 changed TypeScript files, diff whitespace checks, and strict OpenSpec validation pass. - Permanent regressions are retained in `tests/`, using the existing Bun runner and TypeScript test project. No dependencies or configuration changes. ## Existing limitations outside scope - Inserting NULL through the mapped-date INSERT conversion throws; nullable storage fixtures use raw SQL setup. - Existing schema SQL generation splits dotted names; SELECT literal-component escaping is fixed without changing schema generation. - Bun's cached `Database.query()` can retain old row keys after a view's output columns are renamed. Reproduced without Outlaw; fresh prepared statements return correct names. View recreation/convergence was verified with fresh statement shapes. No adapter cache-policy changes were made. - No live D1 verification. ## Commits - `a30bdff`: source-first typed join pipeline. - `9d6afa1`: approved naming exception and literal identifiers. - `7d9fece`: isolate source internals, final documentation, and formatting. - `b987652`: retain permanent runtime and compile-time regression tests. - `a4b81fc`: separate source objects from builders and restore existing private-member conventions. - `e955507`: rename query source types, files, imports, and exports. All commits are pushed. Progress and verification are mirrored in this PR. Ready for review; the change has not been archived.
feat(query)!: compose typed join sources before selection
All checks were successful
Checks / checks (push) Successful in 5s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 5s
Checks / release (pull_request) Has been skipped
a30bdffb0a
Checkpoint the source and projection pipeline with 11 of 29 tasks verified.
Strict type fixtures and the build pass. Runtime verification continues
with the approved FULL JOIN USING output-name exception.

BREAKING CHANGE: compose joins before select; join-after-select is removed.
fix(query): preserve full-join output names and literal identifiers
All checks were successful
Checks / checks (push) Successful in 5s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 5s
Checks / release (pull_request) Has been skipped
9d6afa1935
Apply the approved same-name alias exception for coalesced USING outputs.
Escape literal identifier components without splitting schema punctuation.
Verify 48 SQLite scenarios and strict positive and negative type fixtures.
fix(query): isolate source internals and finish join guidance
All checks were successful
Checks / checks (push) Successful in 5s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 5s
Checks / release (pull_request) Has been skipped
7d9fece3d1
Protect column refs named query, connection, and addJoin from builder state.
Document all join forms, migration guidance, mappings, and SQLite limits.
Apply configured formatting to the changed source modules.

Verify 57 SQLite scenarios, 29 exact type assertions, 22 negative checks,
and every new README join example. Build and lint pass; the existing test
script reports no tests. All 29 better-joins tasks are complete.
test(query): retain join runtime and type regressions
All checks were successful
Checks / checks (push) Successful in 6s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 5s
Checks / release (pull_request) Has been skipped
b987652853
Add 59 Bun tests with 100 runtime assertions under tests/.
Retain 29 exact type assertions and 22 negative compile-time checks.
Use the existing test runner and TypeScript project without config changes.
refactor(query): separate sources from query builders
All checks were successful
Checks / checks (push) Successful in 6s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 5s
Checks / release (pull_request) Has been skipped
a4b81fc5c7
Keep source refs on plain objects and construct builders at entry points.
Restore TypeScript private members without bound-method forwarding.
Verify 61 runtime tests and existing compile-time regression checks.
refactor(query): name query source types explicitly
All checks were successful
Checks / checks (push) Successful in 6s
Checks / release (push) Has been skipped
Checks / checks (pull_request) Successful in 6s
Checks / release (pull_request) Has been skipped
e955507313
Rename Source and SourceBuilder to QuerySource and QuerySourceBuilder.
Update filenames, imports, namespaces, and exports without aliases.
Verify 61 runtime tests and the existing compile-time regression checks.
dan changed title from feat(query)!: replace join-after-select with typed source composition to feat(query)!: better joins 2026-09-13 21:31:22 +00:00
dan merged commit 51a984b173 into main 2026-09-13 21:33:24 +00:00
dan deleted branch better-joins 2026-09-13 21:33:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sigitex/outlaw!1
No description provided.