You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* refactor(invitations): extract platform-invite module + eligibility hook (#3810)
Phase 2 of the invitations↔org decouple epic. Move #3715's platform-invite
code out of modules/auth into a glob-discovered optional modules/invitations,
and invert the dependency so auth no longer imports invitation code.
- New modules/invitations: controller/service/repository/model/schema/policy/
doc moved from auth (renamed auth.invitation.* → invitations.*), Mongoose
model name kept "Invitation" → collection "invitations" (live Trawl prod
docs; collection NOT renamed). Fields + indexes unchanged.
- New auth.eligibility.js: generic signup-eligibility hook registry
(registerSignupEligibility / assertSignupEligible / _reset). invitations
plugs in at boot via invitations.init.js (invitations → auth; auth never
imports invitations). init also wires the events 'error' listener
(mirrors billing.init.js crash guard).
- auth.controller signup + OAuth gates now call the generic hook; the
checker resolves the invite and stashes it (+ a bound single-use consume
closure) on the ctx carrier so the controller can pin the account email +
consume without importing invitation code. Cap logic unchanged (P3).
- Routes: canonical /api/invitations mount in modules/invitations; the legacy
/api/auth/invitations* alias stays in auth.routes.js (before the greedy
/api/auth/:strategy wildcard) pointing at the moved controller. invitationId
param registered once (app-global) by the canonical routes — no double DB hit.
- Policy registers BOTH /api/invitations and /api/auth/invitations → Invitation.
- config: inviteExpiresInDays moved to invitations config (deep-merged into
config.sign); audit.routeTypeMap.invitations → Invitation.
Behavior-preserving refactor + deprecation alias only. No P3 hardening
(two-phase claim / email index / cap unify / soft-revoke). Signup behavior
identical. Tests: moved + split into per-layer unit tests, added init/
controller/repository unit + e2e + alias/OAuth-wildcard integration tests.
Full suite green (169 suites, 2246 tests); lint clean.
* refactor(invitations): return-value eligibility seam (drop req stash)
Code-review fix-up on the Phase-2 eligibility hook:
- Replace the req._signupInvite / req._consumeSignupInvite stash + synthetic
oauthCtx={} carrier with a return-value seam. assertSignupEligible now runs
every check and returns the first non-null result (opaque to auth); the
invitations checker returns { invite, consume } instead of stashing. Deletes
both req._-prefixed props. auth.controller stays import-free of invitation
code (only comments + the generic Eligibility import).
- Fix the misleading body.inviteToken comment: HTTP signup strips unknown body
keys via the model middleware, so query is the effective source; body
fallback is for non-HTTP/direct callers. Rename the init test accordingly.
- Inline findValidByEmail into assertInvitedByEmail (drop the pure pass-through;
no other caller); update its service unit test.
- Retitle the init 'registers exactly one checker' test to what it verifies
(one init() => one checker) and document that init() is not idempotent.
Lint clean. 34 touched unit tests + 17 invitation integration/e2e tests green;
full suite 2246/2246 (one pre-existing socket-hang-up flake in auth lockout,
unrelated, passes on re-run).
* docs(auth): note throw-aborts-chain + loose-null seam semantics for P5/P8 checkers
* fix(invitations): address CodeRabbit + Copilot review findings
- auth.routes.js: add TODO(P6, #4279) with explicit rationale for
the intentional core→optional import (deprecation alias until P6)
- auth.eligibility.js: guard registerSignupEligibility against
non-function input with TypeError for earlier error surfacing
- auth.eligibility.unit.tests.js: add TypeError test for the new guard
- invitations.controller.unit.tests.js: add JSDoc to makeRes per
coding guidelines for named test helpers
- invitations.init.unit.tests.js: fix misleading comment that said
init() must not duplicate — clarify it is not idempotent by design,
boots exactly once at startup
- invitations.repository.unit.tests.js: add jest.clearAllMocks() in
beforeEach to isolate mock call state across test cases
0 commit comments