fix(jsr): resolve slow-type errors in elysia and h3 adapters (#69)

* fix(jsr): resolve slow-type errors in elysia and h3 adapters

* chore(lint): disable no-empty-object-type for elysia plugin return type
This commit is contained in:
Tomás Pozo
2026-05-19 11:36:03 -05:00
committed by GitHub
parent 148169e5f7
commit 7c56b13298
5 changed files with 61 additions and 12 deletions
+14
View File
@@ -10,6 +10,20 @@ pnpm add h3
The adapter exports its own `withSupabase` that returns H3 middleware instead of a fetch handler. Works with standalone H3 servers and Nuxt server routes (which run on H3 under the hood).
## Typing `event.context.supabaseContext`
The middleware stores the context on `event.context.supabaseContext`. Add this declaration once in your project (e.g. in `types/h3.d.ts`) for typed access:
```ts
import type { SupabaseContext } from '@supabase/server'
declare module 'h3' {
interface H3EventContext {
supabaseContext: SupabaseContext
}
}
```
## Basic app with auth
```ts
+1 -1
View File
@@ -4,4 +4,4 @@
* @packageDocumentation
*/
export { withSupabase } from './plugin.js'
export { SupabaseError, withSupabase } from './plugin.js'
+35 -3
View File
@@ -1,10 +1,10 @@
import { Elysia } from 'elysia'
import { Elysia, type ExtractErrorFromHandle } from 'elysia'
import { createSupabaseContext } from '../../create-supabase-context.js'
import type { AuthError } from '../../errors.js'
import type { SupabaseContext, WithSupabaseConfig } from '../../types.js'
class SupabaseError extends Error {
export class SupabaseError extends Error {
status: number
declare cause: AuthError
constructor(inner: AuthError) {
@@ -56,7 +56,39 @@ class SupabaseError extends Error {
* app.listen(3000)
* ```
*/
export function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>) {
// The explicit return type below mirrors Elysia's own generic defaults, which use
// `{}` literals — switching to `object` or `Record<string, never>` would not satisfy
// the corresponding generic constraints.
/* eslint-disable @typescript-eslint/no-empty-object-type */
export function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>): Elysia<
'',
{ decorator: {}; store: {}; derive: {}; resolve: {} },
{ typebox: {}; error: { readonly SupabaseError: SupabaseError } },
{
schema: {}
standaloneSchema: {}
macro: {}
macroFn: {}
parser: {}
response: {}
},
{},
{
derive: {}
resolve: { supabaseContext: SupabaseContext }
schema: {}
standaloneSchema: {}
response: ExtractErrorFromHandle<{ supabaseContext: SupabaseContext }>
},
{
derive: {}
resolve: {}
schema: {}
standaloneSchema: {}
response: {}
}
> {
/* eslint-enable @typescript-eslint/no-empty-object-type */
return new Elysia()
.error({ SupabaseError })
.resolve(async (ctx): Promise<{ supabaseContext: SupabaseContext }> => {
+8
View File
@@ -1,8 +1,16 @@
import { H3, HTTPError, onError } from 'h3'
import { describe, expect, it } from 'vitest'
import type { SupabaseContext } from '../../types.js'
import { withSupabase } from './middleware.js'
declare module 'h3' {
interface H3EventContext {
supabaseContext: SupabaseContext
}
}
describe('h3 supabase middleware', () => {
const env = {
url: 'https://test.supabase.co',
+3 -8
View File
@@ -47,18 +47,13 @@ export function withSupabase(
config?: Omit<WithSupabaseConfig, 'cors'>,
): Middleware {
return defineMiddleware(async (event, next) => {
if (event.context.supabaseContext) return next()
const context = event.context as { supabaseContext?: SupabaseContext }
if (context.supabaseContext) return next()
const { data: ctx, error } = await createSupabaseContext(event.req, config)
if (error) {
throw new HTTPError(error.message, { status: error.status, cause: error })
}
event.context.supabaseContext = ctx
context.supabaseContext = ctx
return next()
})
}
declare module 'h3' {
interface H3EventContext {
supabaseContext: SupabaseContext
}
}