Skip to content

feat(entrypoints): base-class matcher over resolved heritage, with dispatch and via - #160

Merged
rahlk merged 1 commit into
mainfrom
feat/issue-159-entrypoint-bases
Sep 6, 2026
Merged

feat(entrypoints): base-class matcher over resolved heritage, with dispatch and via#160
rahlk merged 1 commit into
mainfrom
feat/issue-159-entrypoint-bases

Conversation

@rahlk

@rahlk rahlk commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Closes #159. Unit 4 of the entrypoint spec (#150); plan docs/design/plans/2026-09-03-entrypoints-units-2-5.md, Task 6. Stacked on #158 (unit 3) — this PR targets that branch, not main.

Class-declared entrypoints: a framework whose contract is "subclass this and define these methods". Python's entrypoints_from_bases, ported.

What this adds

entrypointsFromBases: a bases: rule matches when ANY base of the class — resolved through the import table, else as written — matches rule.match. With transitive: true, the bases of every in-project ancestor reachable through extends_ids are checked too (external ancestors have no node, so the walk stops at the project boundary; a seen set guards cycles). The class gets one record per matching rule; each dispatch: name the class actually DEFINES gets a record with rule: "<id>.dispatch", via: <class id>, and http_methods: [VERB] only when the name is one of the seven HTTP verbs — so a ListView with only get gains no phantom post, and list yields [].

Runs under the stage-0 gate, inside the per-framework loop, before the heuristic tiers — so a base-rule claim counts as framework-claimed for never-doubles.

No new shipped rule. NestJS and Angular are decorator-only, so the matcher is exercised through a user rules file in the test. A shipped bases: rule arrives when a real TypeScript framework needs one.

Verification

  • bun test — 278 pass, 0 fail (3 new): direct base via the import table with dispatch only for defined methods; transitive through an in-project ancestor; the gate (no import, no dependency → nothing)
  • Task-scoped review, which stands as this single-task unit's whole-branch review: approved, no Important findings. Two minors deferred to the final unit-5 review: no test for transitive: false or an empty dispatch; the per-type resolver map could be per-module

One plan defect, fixed in the open

The plan's resolve: (written) => string — one closure built from the leaf class's module — cannot resolve an ancestor's written base spelling, because that ancestor's import { View } from "viewfw" lives in a different module. The transitive test is only satisfiable if resolve takes the owning type and dispatches to that type's own module's import table. That is what shipped: resolve(written, owner), with one import table per module built up front. Internal signature only; no wire effect.

@rahlk
rahlk force-pushed the feat/issue-159-entrypoint-bases branch from 5a58e0e to 9f6932f Compare September 6, 2026 12:56
@rahlk
rahlk merged commit 0683bfc into main Sep 6, 2026
1 check passed
@rahlk
rahlk deleted the feat/issue-159-entrypoint-bases branch September 6, 2026 12:57
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.

feat(entrypoints): base-class matcher with transitive heritage, dispatch and via (unit 4)

1 participant