Skip to content

✨ feat(context-api): user-defined middleware groups#215

Open
taras wants to merge 3 commits intomainfrom
feat/context-api-custom-groups
Open

✨ feat(context-api): user-defined middleware groups#215
taras wants to merge 3 commits intomainfrom
feat/context-api-custom-groups

Conversation

@taras
Copy link
Copy Markdown
Member

@taras taras commented Apr 23, 2026

Motivation

Consumers of @effectionx/context-api (notably Tisyn) need a third structural middleware lane that sits between general wrappers and core-providing middleware — specifically, a replay lane positioned as max wrappers → replay restore → dispatch/default min → core.

Today context-api hard-codes exactly two lanes (max and min), so replay restore has to compete with other min middleware by install order. That encodes a fragile invariant in call ordering instead of in the API surface. The fix is to let each createApi() declare the lanes it needs, keeping max/min as the backward-compatible default.

Approach

  • New optional options.groups on createApi(): an ordered list of { name, mode } where mode is "append" (earlier registrations outer; parent-outer/child-inner across scopes) or "prepend" (later registrations outer; child-outer/parent-inner).
  • around({ at }) now targets any declared group name; at defaults to the first declared group.
  • Default groups remain [{ name: "max", mode: "append" }, { name: "min", mode: "prepend" }], so existing call sites — including the "defaults to max" semantics — are unchanged.
  • Validation at createApi() call time: throws on empty groups and on duplicate names; around() throws at runtime on an unknown at, with the known group names in the message.
  • New public types: GroupMode, MiddlewareGroup<Name>, CreateApiOptions<Group>. Api<A, Group> gains a second generic with a default of "max" | "min" so existing Api<A> usages keep compiling. Callers pass groups: [...] as const to infer the literal union.
  • Tests: added a describe("custom groups", ...) block in scope-middleware.test.ts covering declared order, install-order semantics per mode, middle-lane stability, parent/child composition across all lanes, isolation after scope exit, live updates for spawned tasks on the middle lane, mixed later-parent updates, and all three validation paths. Existing tests unchanged.
  • README: updated the "Min/Max Priority" section to frame it as the default configuration and added a "Custom groups" subsection with the three-lane example and execution-order diagram. Updated the createApi and around API reference entries.

Verification (all green): pnpm check, pnpm lint, pnpm fmt:check, and pnpm test (389 passing, 6 skipped — 11 new tests). No runtime behavior change for existing max/min callers.

createApi now accepts an ordered list of middleware groups via
options.groups. Each group declares a name and a mode of "append"
(earlier registrations outer, parent-outer/child-inner across scopes)
or "prepend" (later registrations outer, child-outer/parent-inner).
around({ at }) targets a declared group and defaults to the first one.

The default configuration remains
[{ name: "max", mode: "append" }, { name: "min", mode: "prepend" }],
so existing callers keep their current behavior, including the
default-to-max semantics.

This lets consumers like Tisyn express structural lanes (e.g. a
"replay" lane between wrappers and defaults) as an invariant of the
API rather than by coordinating install order.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

@taras has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 2 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 39 minutes and 2 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 187222c3-508b-48a6-b537-de7a90543b71

📥 Commits

Reviewing files that changed from the base of the PR and between cc31dd4 and 7b570a9.

📒 Files selected for processing (4)
  • context-api/README.md
  • context-api/mod.ts
  • context-api/package.json
  • context-api/scope-middleware.test.ts
📝 Walkthrough

Walkthrough

The changes generalize the middleware architecture from fixed "max"/"min" lanes to a configurable system of named middleware groups. The createApi signature now accepts optional configuration, the Api type becomes generic over group names, and around() accepts a group selection option. Middleware collection logic is refactored to handle arbitrary groups while maintaining deterministic ordering.

Changes

Cohort / File(s) Summary
Documentation
context-api/README.md
Updated to document middleware groups as configurable via createApi(name, handler, options?) with runtime validation, default two-group configuration, and custom group examples.
Core implementation
context-api/mod.ts
Generalized middleware system from fixed "max"/"min" lanes to configurable named groups via new types (GroupMode, MiddlewareGroup, CreateApiOptions), generic Api<A, Group>, and updated createApi with group configuration and validation. Refactored middleware collection/ordering logic to process all declared groups deterministically.
Test coverage
context-api/scope-middleware.test.ts
Added comprehensive test suite covering three-lane configurations, cross-scope behavior, isolation guarantees, live task updates, and validation of invalid group configurations and unknown group references.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • cowboyd
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature: user-defined middleware groups. It accurately reflects the primary change enabling customizable middleware configuration instead of hard-coded max/min lanes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Policy Compliance ✅ Passed The pull request passes all policy compliance checks including no agent marketing, appropriate version bump, and valid package.json metadata.
Description check ✅ Passed The PR description thoroughly addresses both motivation and approach sections, detailing the new features, backward compatibility, validation logic, and verification results.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/context-api-custom-groups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 23, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@effectionx/context-api@215

commit: 7b570a9

@taras taras requested a review from cowboyd April 23, 2026 23:49
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
context-api/README.md (1)

7-11: 🧹 Nitpick | 🔵 Trivial

Minor: update intro to reflect arbitrary groups.

The opening still says "supports min/max priority ordering". With this PR, users can declare arbitrary ordered groups; consider rephrasing so the entry-point description advertises the full capability (while still mentioning min/max as the default).

✏️ Proposed tweak
-Often called "Algebraic Effects" or "Contextual Effects", Context APIs let you
-access an operation via the context in a way that it can be easily (and
-contextually) wrapped with middleware. Middleware is powered by
-[`@effectionx/middleware`](../middleware/README.md) and supports min/max priority
-ordering.
+Often called "Algebraic Effects" or "Contextual Effects", Context APIs let you
+access an operation via the context in a way that it can be easily (and
+contextually) wrapped with middleware. Middleware is powered by
+[`@effectionx/middleware`](../middleware/README.md) and supports ordered
+middleware groups — with `min`/`max` as the default two-lane configuration.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@context-api/README.md` around lines 7 - 11, Update the README intro sentence
that currently says middleware "supports min/max priority ordering" to instead
advertise that middleware supports arbitrary ordered groups with min/max as the
default; specifically edit the sentence referencing `@effectionx/middleware` to
mention "supports arbitrary ordered middleware groups (min/max priority ordering
is the default)" so the entry-point description reflects the new capability
while retaining the existing min/max wording.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@context-api/mod.ts`:
- Around line 29-57: The CreateApiOptions.groups JSDoc should mention callers
can pass a readonly tuple (e.g. groups: [...] as const) to preserve
literal-union inference for Group; update the comment above the CreateApiOptions
type to add a short sentence advising to use the "as const" pattern when passing
groups so the MiddlewareGroup<Group>[] (and MiddlewareGroup) retain literal
types for Group during inference.

In `@context-api/scope-middleware.test.ts`:
- Around line 1545-1613: The test's duplicate-group assertion is too loose
because the top-level API name "groups.dup" can satisfy /max/; update the
assertion in the "createApi throws on duplicate group names" test so it verifies
that the error message contains the duplicate-group phrase adjacent to the
duplicated name (e.g. change the second expect on the caught message to match
/duplicate group name.*\bmax\b/ or otherwise assert that the "duplicate group
name" substring and the symbol "max" both appear in the same message context),
locating the change around the test that calls createApi(...) and inspects
message from the thrown Error captured in that spec.

---

Outside diff comments:
In `@context-api/README.md`:
- Around line 7-11: Update the README intro sentence that currently says
middleware "supports min/max priority ordering" to instead advertise that
middleware supports arbitrary ordered groups with min/max as the default;
specifically edit the sentence referencing `@effectionx/middleware` to mention
"supports arbitrary ordered middleware groups (min/max priority ordering is the
default)" so the entry-point description reflects the new capability while
retaining the existing min/max wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bd0d8330-aabc-44e8-896a-92a405bcd4f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6982b1c and cc31dd4.

📒 Files selected for processing (3)
  • context-api/README.md
  • context-api/mod.ts
  • context-api/scope-middleware.test.ts

Comment thread context-api/mod.ts
Comment thread context-api/scope-middleware.test.ts
taras added 2 commits April 23, 2026 19:51
Tighten around() so options?: { at: Group } — options is still optional,
but if you pass it, at is required. Prevents custom-group callers from
silently landing in the first lane by omission.
- bump context-api to 0.7.0 for the new published API
- reframe README intro around ordered middleware groups
- document the `as const` idiom on CreateApiOptions JSDoc
- tighten duplicate-group-name test to anchor on the duplicate list
Copy link
Copy Markdown
Member Author

taras commented Apr 24, 2026

Addressed the remaining CodeRabbit feedback in 7b570a9: the README intro now leads with ordered middleware groups and describes max/min as the default configuration, the CreateApiOptions JSDoc now documents the as const inference pattern, the duplicate-name test regex is tightened, and @effectionx/context-api is bumped to 0.7.0.

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.

1 participant