Skip to content

Commit bfe8cd2

Browse files
mandariniclaude
andcommitted
refactor: rename plugins option to middleware on withSupabase
The array holds middleware entries (per-request behavior from defineMiddleware) — the word 'plugins' is reserved for the package-level concept whose client namespace goes in createClient({ plugins }). One word per concept: server-side composition is 'middleware', client-side namespaces are 'plugins', a Plugin is the package that ships both. PluginsCtx -> MiddlewareCtx; overload trick unchanged (middleware?: never on overload 1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a576cda commit bfe8cd2

2 files changed

Lines changed: 40 additions & 37 deletions

File tree

src/with-supabase.test.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ describe('withSupabase', () => {
159159
})
160160
})
161161

162-
describe('plugins', () => {
163-
it('composes plugins after the Supabase context is established', async () => {
162+
describe('middleware', () => {
163+
it('composes middleware after the Supabase context is established', async () => {
164164
const withFlag = defineMiddleware<
165165
'flag',
166166
void,
@@ -172,7 +172,7 @@ describe('withSupabase', () => {
172172
})
173173

174174
const handler = withSupabase(
175-
{ auth: 'none', env: baseEnv, plugins: [withFlag()] },
175+
{ auth: 'none', env: baseEnv, middleware: [withFlag()] },
176176
async (_req, ctx) =>
177177
Response.json({ authMode: ctx.authMode, flag: ctx.flag }),
178178
)
@@ -183,7 +183,7 @@ describe('withSupabase', () => {
183183
expect(body.flag).toBe(true)
184184
})
185185

186-
it('plugin receives the Supabase context at runtime', async () => {
186+
it('middleware receives the Supabase context at runtime', async () => {
187187
let capturedHasSupabase = false
188188

189189
const withCapture = defineMiddleware<
@@ -200,15 +200,15 @@ describe('withSupabase', () => {
200200
})
201201

202202
const handler = withSupabase(
203-
{ auth: 'none', env: baseEnv, plugins: [withCapture()] },
203+
{ auth: 'none', env: baseEnv, middleware: [withCapture()] },
204204
async () => Response.json({ ok: true }),
205205
)
206206

207207
await handler(new Request('http://localhost'))
208208
expect(capturedHasSupabase).toBe(true)
209209
})
210210

211-
it('plugin can short-circuit before the handler', async () => {
211+
it('middleware can short-circuit before the handler', async () => {
212212
const withBlock = defineMiddleware<
213213
'blocked',
214214
void,
@@ -222,7 +222,7 @@ describe('withSupabase', () => {
222222
const innerHandler = vi.fn(async () => Response.json({ ok: true }))
223223

224224
const handler = withSupabase(
225-
{ auth: 'none', env: baseEnv, plugins: [withBlock()] },
225+
{ auth: 'none', env: baseEnv, middleware: [withBlock()] },
226226
innerHandler,
227227
)
228228

@@ -231,7 +231,7 @@ describe('withSupabase', () => {
231231
expect(innerHandler).not.toHaveBeenCalled()
232232
})
233233

234-
it('plugins run in array order (first = outermost, runs first on request)', async () => {
234+
it('middleware run in array order (first = outermost, runs first on request)', async () => {
235235
const order: string[] = []
236236

237237
const withA = defineMiddleware<'a', void, Record<never, never>, true>({
@@ -250,7 +250,7 @@ describe('withSupabase', () => {
250250
})
251251

252252
const handler = withSupabase(
253-
{ auth: 'none', env: baseEnv, plugins: [withA(), withB()] },
253+
{ auth: 'none', env: baseEnv, middleware: [withA(), withB()] },
254254
async (_req, ctx) => Response.json({ a: ctx.a, b: ctx.b }),
255255
)
256256

@@ -259,7 +259,7 @@ describe('withSupabase', () => {
259259
expect(await res.json()).toEqual({ a: true, b: true })
260260
})
261261

262-
it('CORS headers still apply when plugins are present', async () => {
262+
it('CORS headers still apply when middleware are present', async () => {
263263
const withNoop = defineMiddleware<
264264
'noop',
265265
void,
@@ -271,7 +271,7 @@ describe('withSupabase', () => {
271271
})
272272

273273
const handler = withSupabase(
274-
{ auth: 'none', env: baseEnv, plugins: [withNoop()] },
274+
{ auth: 'none', env: baseEnv, middleware: [withNoop()] },
275275
async () => Response.json({ ok: true }),
276276
)
277277

src/with-supabase.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ type AnyEntry = Entry<string, object, unknown>
88
type AnyHandler = (req: Request, ctx: any) => Promise<Response>
99

1010
/**
11-
* Accumulate the ctx contributions of a plugin tuple — same logic as
11+
* Accumulate the ctx contributions of a middleware tuple — same logic as
1212
* `pipeline`'s internal `Accumulate`, seeded from `object` (no `BaseContext`
1313
* or `_runtime` in the visible ctx type; see implementation note below).
1414
*/
15-
type PluginsCtx<Plugins extends readonly AnyEntry[]> =
16-
Plugins extends readonly [
15+
type MiddlewareCtx<Entries extends readonly AnyEntry[]> =
16+
Entries extends readonly [
1717
Entry<infer Key extends string, object, infer Contribution>,
1818
...infer Rest,
1919
]
2020
? Rest extends readonly AnyEntry[]
21-
? { [P in Key]: Contribution } & PluginsCtx<Rest>
21+
? { [P in Key]: Contribution } & MiddlewareCtx<Rest>
2222
: { [P in Key]: Contribution }
2323
: object
2424

@@ -39,7 +39,7 @@ type PluginsCtx<Plugins extends readonly AnyEntry[]> =
3939
* ```ts
4040
* import { withSupabase } from '@supabase/server'
4141
*
42-
* // Without plugins — existing API, unchanged.
42+
* // Without middleware — existing API, unchanged.
4343
* export default {
4444
* fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
4545
* const { data } = await ctx.supabase.rpc('get_my_profile')
@@ -49,15 +49,17 @@ type PluginsCtx<Plugins extends readonly AnyEntry[]> =
4949
* ```
5050
*/
5151
export function withSupabase<Database = unknown>(
52-
config: WithSupabaseConfig & { plugins?: never },
52+
config: WithSupabaseConfig & { middleware?: never },
5353
handler: (req: Request, ctx: SupabaseContext<Database>) => Promise<Response>,
5454
): (req: Request) => Promise<Response>
5555

5656
/**
57-
* Variant that accepts a `plugins` array — each `withFoo(config)` call returns
58-
* an `Entry` from `@supabase/web-middleware`. Plugins run **after** the Supabase
59-
* context is established; they receive `ctx.supabase`, `ctx.userClaims`, etc.
60-
* already present and contribute their own typed keys on top.
57+
* Variant that accepts a `middleware` array — each `withFoo(config)` call
58+
* returns an `Entry` from `@supabase/web-middleware`. Middleware run **after**
59+
* the Supabase context is established; they receive `ctx.supabase`,
60+
* `ctx.userClaims`, etc. already present and contribute their own typed keys
61+
* on top. (This is the server leg of a Plugin: the package's middleware goes
62+
* here; its client namespace goes in `createClient`'s `plugins` array.)
6163
*
6264
* @example
6365
* ```ts
@@ -67,7 +69,7 @@ export function withSupabase<Database = unknown>(
6769
*
6870
* export default {
6971
* fetch: withSupabase(
70-
* { auth: 'user', plugins: [withRateLimit({ rpm: 100 }), withGuestbook()] },
72+
* { auth: 'user', middleware: [withRateLimit({ rpm: 100 }), withGuestbook()] },
7173
* async (req, ctx) => {
7274
* ctx.supabase // from @supabase/server
7375
* ctx.rateLimit // from withRateLimit
@@ -78,27 +80,27 @@ export function withSupabase<Database = unknown>(
7880
* }
7981
* ```
8082
*
81-
* **Type note.** `PluginsCtx<Plugins>` accumulates the key contributions of the
82-
* plugins array. Plugins that declare `In` prerequisites on Supabase-provided
83-
* keys (`supabase`, `userClaims`, …) satisfy those at runtime (the Supabase
84-
* context is merged before plugins run) but not at the type level — a full
85-
* implementation would widen the prerequisite-validation seed to include
86-
* `SupabaseContext`. Ordering and collision checks within the plugins array work
87-
* normally via `web-middleware`'s runtime chain.
83+
* **Type note.** `MiddlewareCtx<Entries>` accumulates the key contributions of
84+
* the middleware array. Middleware that declare `In` prerequisites on
85+
* Supabase-provided keys (`supabase`, `userClaims`, …) satisfy those at runtime
86+
* (the Supabase context is merged before the middleware run) but not at the
87+
* type level — a full implementation would widen the prerequisite-validation
88+
* seed to include `SupabaseContext`. Ordering and collision checks within the
89+
* middleware array work normally via `web-middleware`'s runtime chain.
8890
*/
8991
export function withSupabase<
9092
Database = unknown,
91-
const Plugins extends readonly AnyEntry[] = readonly AnyEntry[],
93+
const Entries extends readonly AnyEntry[] = readonly AnyEntry[],
9294
>(
93-
config: WithSupabaseConfig & { plugins: Plugins },
95+
config: WithSupabaseConfig & { middleware: Entries },
9496
handler: (
9597
req: Request,
96-
ctx: SupabaseContext<Database> & PluginsCtx<Plugins>,
98+
ctx: SupabaseContext<Database> & MiddlewareCtx<Entries>,
9799
) => Promise<Response>,
98100
): (req: Request) => Promise<Response>
99101

100102
export function withSupabase<Database = unknown>(
101-
config: WithSupabaseConfig & { plugins?: readonly AnyEntry[] },
103+
config: WithSupabaseConfig & { middleware?: readonly AnyEntry[] },
102104
handler: AnyHandler,
103105
): (req: Request) => Promise<Response> {
104106
return async (req: Request) => {
@@ -126,11 +128,12 @@ export function withSupabase<Database = unknown>(
126128
}
127129

128130
let response: Response
129-
if (config.plugins?.length) {
130-
// Compose plugins around the handler — same fold as pipeline's reduceRight,
131-
// but without calling pipeline() so we supply the seeded ctx ourselves.
131+
if (config.middleware?.length) {
132+
// Compose the middleware around the handler — same fold as pipeline's
133+
// reduceRight, but without calling pipeline() so we supply the seeded
134+
// ctx ourselves.
132135
const composed = (
133-
config.plugins as readonly AnyEntry[]
136+
config.middleware as readonly AnyEntry[]
134137
).reduceRight<AnyHandler>((h, entry) => entry(h), handler)
135138
// Seed _runtime so web-middleware entries recognise this as an upstream
136139
// context (isContext() checks for _runtime.getEnv). Falls through to

0 commit comments

Comments
 (0)