( invariants: readonly Invariant[], ctx: InvariantContext, )
| 13 | } from './types.js'; |
| 14 | |
| 15 | export async function runAllInvariants( |
| 16 | invariants: readonly Invariant[], |
| 17 | ctx: InvariantContext, |
| 18 | ): Promise<InvariantRunReport> { |
| 19 | const startedAt = new Date(); |
| 20 | const violations: Violation[] = []; |
| 21 | const stats: Record<string, InvariantRunStats> = {}; |
| 22 | |
| 23 | // Allocate a fresh per-run Stripe customer cache so invariants that hit |
| 24 | // `customers.retrieve` for the same id only pay the API call once per run. |
| 25 | // (#1 and #3 in Phase 1 both walk the same set.) Caller can override by |
| 26 | // pre-populating ctx.stripeCustomerCache; we leave that alone. |
| 27 | const ctxWithCache: InvariantContext = ctx.stripeCustomerCache |
| 28 | ? ctx |
| 29 | : { ...ctx, stripeCustomerCache: new Map() }; |
| 30 | |
| 31 | // Sequential by design (Phase 1): predictable Stripe/WorkOS API budget, |
| 32 | // simpler reasoning, no race against per-invariant rate limits. Phase 2 |
| 33 | // can introduce bounded parallelism once we've measured real costs. |
| 34 | for (const inv of invariants) { |
| 35 | stats[inv.name] = await runOneInvariantInto(inv, ctxWithCache, violations); |
| 36 | } |
| 37 | |
| 38 | const completedAt = new Date(); |
| 39 | const violationsBySeverity: Record<Severity, number> = { |
| 40 | critical: 0, |
| 41 | warning: 0, |
| 42 | info: 0, |
| 43 | }; |
| 44 | for (const v of violations) { |
| 45 | violationsBySeverity[v.severity]++; |
| 46 | } |
| 47 | |
| 48 | return { |
| 49 | started_at: startedAt, |
| 50 | completed_at: completedAt, |
| 51 | total_violations: violations.length, |
| 52 | violations_by_severity: violationsBySeverity, |
| 53 | violations, |
| 54 | stats, |
| 55 | }; |
| 56 | } |
| 57 | |
| 58 | export async function runOneInvariant( |
| 59 | invariant: Invariant, |
no test coverage detected