@supabase/server 1.1.0-rc.63 → 1.1.0-rc.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -36
- package/dist/adapters/elysia/index.cjs +66 -0
- package/dist/adapters/elysia/index.d.cts +90 -0
- package/dist/adapters/elysia/index.d.mts +90 -0
- package/dist/adapters/elysia/index.mjs +65 -0
- package/dist/adapters/h3/index.d.cts +1 -1
- package/dist/adapters/h3/index.d.mts +1 -1
- package/dist/adapters/hono/index.d.cts +1 -1
- package/dist/adapters/hono/index.d.mts +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.mts +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/docs/adapters/elysia.md +140 -0
- package/docs/api-reference.md +32 -0
- package/package.json +12 -2
- /package/dist/{errors-0dbzn5gA.d.mts → errors-C43um2Gg.d.mts} +0 -0
- /package/dist/{errors-CZFEYnV_.d.cts → errors-fa-CyugW.d.cts} +0 -0
- /package/dist/{types-Bjy1j07i.d.cts → types-CwKZOVIv.d.cts} +0 -0
- /package/dist/{types-DP9l5Cvf.d.mts → types-DbvLfq25.d.mts} +0 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://pkg.pr.new/~/supabase/server)
|
|
6
6
|
[](https://supabase.github.io/server/)
|
|
7
7
|
|
|
8
|
-
> **v1.
|
|
8
|
+
> **v1.X — Public Beta.** First stable release under SemVer: breaking changes only ship as a major bump. The package is still early — expect new adapters, ergonomic improvements, and features to land frequently in minor releases. Found a rough edge? [Open an issue](https://github.com/supabase/server/issues) or [submit a PR](https://github.com/supabase/server/blob/main/CONTRIBUTING.md).
|
|
9
9
|
|
|
10
10
|
> **Coming from a `0.x` release?** See [MIGRATION.md](MIGRATION.md) for the v0 → v1 rename map (`allow` → `auth`, `'public'` → `'publishable'`, `authType` → `authMode`, `claims` → `jwtClaims`, …).
|
|
11
11
|
|
|
@@ -262,40 +262,59 @@ withSupabase(
|
|
|
262
262
|
|
|
263
263
|
## Framework Adapters
|
|
264
264
|
|
|
265
|
-
Adapters wrap `withSupabase` for a specific framework's middleware contract.
|
|
265
|
+
Adapters wrap `withSupabase` for a specific framework's middleware contract. They ship inside `@supabase/server`, so a single `npm install @supabase/server` covers the framework you're using — no separate package per adapter.
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
| --------- | -------------------------------- | ----------------- | ---------------------------------------------- |
|
|
269
|
-
| Hono | `@supabase/server/adapters/hono` | `^4.0.0` | [docs/adapters/hono.md](docs/adapters/hono.md) |
|
|
270
|
-
| H3 / Nuxt | `@supabase/server/adapters/h3` | `^2.0.0` | [docs/adapters/h3.md](docs/adapters/h3.md) |
|
|
267
|
+
> **Adapters are a community-driven initiative.** They're developed, maintained, and evolved by contributors — including responding to upstream framework changes. See [`src/adapters/README.md`](src/adapters/README.md) for the contribution requirements (tests, types, docs, build wiring) if you'd like to add or help maintain one.
|
|
271
268
|
|
|
272
|
-
|
|
269
|
+
| Framework | Import | Framework version | Docs |
|
|
270
|
+
| --------- | ---------------------------------- | ----------------- | -------------------------------------------------- |
|
|
271
|
+
| Hono | `@supabase/server/adapters/hono` | `^4.0.0` | [docs/adapters/hono.md](docs/adapters/hono.md) |
|
|
272
|
+
| H3 / Nuxt | `@supabase/server/adapters/h3` | `^2.0.0` | [docs/adapters/h3.md](docs/adapters/h3.md) |
|
|
273
|
+
| Elysia | `@supabase/server/adapters/elysia` | `^1.4.0` | [docs/adapters/elysia.md](docs/adapters/elysia.md) |
|
|
273
274
|
|
|
274
|
-
|
|
275
|
-
import { Hono } from 'hono'
|
|
276
|
-
import { withSupabase } from '@supabase/server/adapters/hono'
|
|
275
|
+
See the per-adapter docs above for setup, per-route auth, CORS, error handling, and other patterns.
|
|
277
276
|
|
|
278
|
-
|
|
279
|
-
app.use('*', withSupabase({ auth: 'user' }))
|
|
277
|
+
### Elysia
|
|
280
278
|
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
```ts
|
|
280
|
+
import { Elysia } from 'elysia'
|
|
281
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
282
|
+
|
|
283
|
+
const app = new Elysia()
|
|
284
|
+
// Protected — plugin resolves supabaseContext before handlers run
|
|
285
|
+
.use(withSupabase({ auth: 'user' }))
|
|
286
|
+
.get('/games', async ({ supabaseContext }) => {
|
|
287
|
+
const { data: myGames } = await supabaseContext.supabase
|
|
288
|
+
.from('favorite_games')
|
|
289
|
+
.select()
|
|
290
|
+
return myGames
|
|
291
|
+
})
|
|
292
|
+
// Public — no plugin means no auth
|
|
293
|
+
.get('/health', () => ({ status: 'ok' }))
|
|
283
294
|
|
|
284
|
-
|
|
295
|
+
app.listen(3000)
|
|
296
|
+
```
|
|
285
297
|
|
|
286
|
-
|
|
298
|
+
For per-route auth, use scoped groups:
|
|
287
299
|
|
|
288
300
|
```ts
|
|
289
|
-
import {
|
|
290
|
-
import { withSupabase } from '@supabase/server/adapters/
|
|
291
|
-
|
|
292
|
-
const app = new
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
import { Elysia } from 'elysia'
|
|
302
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
303
|
+
|
|
304
|
+
const app = new Elysia()
|
|
305
|
+
.get('/health', () => ({ status: 'ok' }))
|
|
306
|
+
.group('/api', (app) =>
|
|
307
|
+
app
|
|
308
|
+
.use(withSupabase({ auth: 'user' }))
|
|
309
|
+
.get('/profile', async ({ supabaseContext }) => {
|
|
310
|
+
return supabaseContext.userClaims
|
|
311
|
+
}),
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
app.listen(3000)
|
|
296
315
|
```
|
|
297
316
|
|
|
298
|
-
|
|
317
|
+
The adapter does not handle CORS — use `@elysiajs/cors` for that.
|
|
299
318
|
|
|
300
319
|
## Primitives
|
|
301
320
|
|
|
@@ -421,12 +440,17 @@ For other environments, pass overrides via the `env` config option or `resolveEn
|
|
|
421
440
|
|
|
422
441
|
## Runtimes
|
|
423
442
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
443
|
+
`@supabase/server` runs anywhere standard Web `fetch` does — pick the row that matches your deployment target.
|
|
444
|
+
|
|
445
|
+
| Target | Notes |
|
|
446
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
447
|
+
| **Supabase Edge Functions** | Zero config — environment variables are auto-injected. |
|
|
448
|
+
| **Vercel Functions** | Edge runtime: `export default { fetch }`. Node runtime: use a [framework adapter](#framework-adapters) or [core primitives](#primitives). |
|
|
449
|
+
| **Cloudflare Workers** | Enable `nodejs_compat` in `wrangler.toml`, or pass overrides via the `env` config option. |
|
|
450
|
+
| **Deno / Bun** | Works out of the box via `export default { fetch }`. |
|
|
451
|
+
| **Node.js** | Use a [framework adapter](#framework-adapters) or [core primitives](#primitives) with your framework of choice. |
|
|
452
|
+
|
|
453
|
+
Using a framework? See [Framework Adapters](#framework-adapters) for Hono, H3 / Nuxt, and Elysia, or [`docs/ssr-frameworks.md`](docs/ssr-frameworks.md) for Next.js / SvelteKit / Remix (compose with [`@supabase/ssr`](https://github.com/supabase/ssr)).
|
|
430
454
|
|
|
431
455
|
### Does this replace `@supabase/ssr`?
|
|
432
456
|
|
|
@@ -434,12 +458,13 @@ No. `@supabase/ssr` handles cookie-based session management for frameworks like
|
|
|
434
458
|
|
|
435
459
|
## Exports
|
|
436
460
|
|
|
437
|
-
| Export
|
|
438
|
-
|
|
|
439
|
-
| `@supabase/server`
|
|
440
|
-
| `@supabase/server/core`
|
|
441
|
-
| `@supabase/server/adapters/hono`
|
|
442
|
-
| `@supabase/server/adapters/h3`
|
|
461
|
+
| Export | What's in it |
|
|
462
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
463
|
+
| `@supabase/server` | `withSupabase`, `createSupabaseContext` |
|
|
464
|
+
| `@supabase/server/core` | `verifyAuth`, `verifyCredentials`, `extractCredentials`, `createContextClient`, `createAdminClient`, `resolveEnv` |
|
|
465
|
+
| `@supabase/server/adapters/hono` | `withSupabase` (Hono middleware) |
|
|
466
|
+
| `@supabase/server/adapters/h3` | `withSupabase` (H3 / Nuxt middleware) |
|
|
467
|
+
| `@supabase/server/adapters/elysia` | `withSupabase` (Elysia plugin) |
|
|
443
468
|
|
|
444
469
|
## Documentation
|
|
445
470
|
|
|
@@ -450,6 +475,7 @@ No. `@supabase/ssr` handles cookie-based session management for frameworks like
|
|
|
450
475
|
| Which framework adapters exist? How do I contribute one? | [`src/adapters/README.md`](src/adapters/README.md) |
|
|
451
476
|
| How do I use this with Hono? | [`docs/adapters/hono.md`](docs/adapters/hono.md) |
|
|
452
477
|
| How do I use this with H3 / Nuxt? | [`docs/adapters/h3.md`](docs/adapters/h3.md) |
|
|
478
|
+
| How do I use this with Elysia? | [`docs/adapters/elysia.md`](docs/adapters/elysia.md) |
|
|
453
479
|
| How do I use low-level primitives for custom flows? | [`docs/core-primitives.md`](docs/core-primitives.md) |
|
|
454
480
|
| How do environment variables work across runtimes? | [`docs/environment-variables.md`](docs/environment-variables.md) |
|
|
455
481
|
| How do I handle errors? What codes exist? | [`docs/error-handling.md`](docs/error-handling.md) |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_create_supabase_context = require('../../create-supabase-context-CKv-4AIg.cjs');
|
|
3
|
+
let elysia = require("elysia");
|
|
4
|
+
|
|
5
|
+
//#region src/adapters/elysia/plugin.ts
|
|
6
|
+
var SupabaseError = class extends Error {
|
|
7
|
+
constructor(inner) {
|
|
8
|
+
super(inner.message, { cause: inner });
|
|
9
|
+
this.status = inner.status;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Elysia plugin that creates a {@link SupabaseContext} and makes it available in route handlers.
|
|
14
|
+
*
|
|
15
|
+
* Skips if a previous plugin already set the context, enabling route-level overrides.
|
|
16
|
+
* Throws a `SupabaseError` on auth failure. `.status` is on the error directly; the original
|
|
17
|
+
* `AuthError` is available as the typed `.cause`. Discriminate in `onError` via `code === 'SupabaseError'`.
|
|
18
|
+
*
|
|
19
|
+
* @param config - Auth modes and optional environment overrides. CORS is excluded — use Elysia's CORS utilities.
|
|
20
|
+
* @returns An Elysia plugin that exposes `supabaseContext`.
|
|
21
|
+
*
|
|
22
|
+
* @example App-wide auth via `.use()`
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Elysia } from 'elysia'
|
|
25
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
26
|
+
*
|
|
27
|
+
* const app = new Elysia()
|
|
28
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
29
|
+
* .get('/games', async ({ supabaseContext }) => {
|
|
30
|
+
* const { data } = await supabaseContext.supabase.from('favorite_games').select()
|
|
31
|
+
* return data
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Per-route auth via scoped `.use()`
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { Elysia } from 'elysia'
|
|
40
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
41
|
+
*
|
|
42
|
+
* const app = new Elysia()
|
|
43
|
+
* .get('/health', () => ({ status: 'ok' }))
|
|
44
|
+
* .group('/api', (app) =>
|
|
45
|
+
* app
|
|
46
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
47
|
+
* .get('/profile', async ({ supabaseContext }) => {
|
|
48
|
+
* return supabaseContext.userClaims
|
|
49
|
+
* })
|
|
50
|
+
* )
|
|
51
|
+
*
|
|
52
|
+
* app.listen(3000)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function withSupabase(config) {
|
|
56
|
+
return new elysia.Elysia().error({ SupabaseError }).resolve(async (ctx) => {
|
|
57
|
+
const existing = ctx.supabaseContext;
|
|
58
|
+
if (existing) return { supabaseContext: existing };
|
|
59
|
+
const { data, error } = await require_create_supabase_context.createSupabaseContext(ctx.request, config);
|
|
60
|
+
if (error) throw new SupabaseError(error);
|
|
61
|
+
return { supabaseContext: data };
|
|
62
|
+
}).as("scoped");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
exports.withSupabase = withSupabase;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { t as AuthError } from "../../errors-fa-CyugW.cjs";
|
|
2
|
+
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-CwKZOVIv.cjs";
|
|
3
|
+
import * as elysia from "elysia";
|
|
4
|
+
import { Elysia } from "elysia";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/elysia/plugin.d.ts
|
|
7
|
+
declare class SupabaseError extends Error {
|
|
8
|
+
status: number;
|
|
9
|
+
cause: AuthError;
|
|
10
|
+
constructor(inner: AuthError);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Elysia plugin that creates a {@link SupabaseContext} and makes it available in route handlers.
|
|
14
|
+
*
|
|
15
|
+
* Skips if a previous plugin already set the context, enabling route-level overrides.
|
|
16
|
+
* Throws a `SupabaseError` on auth failure. `.status` is on the error directly; the original
|
|
17
|
+
* `AuthError` is available as the typed `.cause`. Discriminate in `onError` via `code === 'SupabaseError'`.
|
|
18
|
+
*
|
|
19
|
+
* @param config - Auth modes and optional environment overrides. CORS is excluded — use Elysia's CORS utilities.
|
|
20
|
+
* @returns An Elysia plugin that exposes `supabaseContext`.
|
|
21
|
+
*
|
|
22
|
+
* @example App-wide auth via `.use()`
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Elysia } from 'elysia'
|
|
25
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
26
|
+
*
|
|
27
|
+
* const app = new Elysia()
|
|
28
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
29
|
+
* .get('/games', async ({ supabaseContext }) => {
|
|
30
|
+
* const { data } = await supabaseContext.supabase.from('favorite_games').select()
|
|
31
|
+
* return data
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Per-route auth via scoped `.use()`
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { Elysia } from 'elysia'
|
|
40
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
41
|
+
*
|
|
42
|
+
* const app = new Elysia()
|
|
43
|
+
* .get('/health', () => ({ status: 'ok' }))
|
|
44
|
+
* .group('/api', (app) =>
|
|
45
|
+
* app
|
|
46
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
47
|
+
* .get('/profile', async ({ supabaseContext }) => {
|
|
48
|
+
* return supabaseContext.userClaims
|
|
49
|
+
* })
|
|
50
|
+
* )
|
|
51
|
+
*
|
|
52
|
+
* app.listen(3000)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>): Elysia<"", {
|
|
56
|
+
decorator: {};
|
|
57
|
+
store: {};
|
|
58
|
+
derive: {};
|
|
59
|
+
resolve: {};
|
|
60
|
+
}, {
|
|
61
|
+
typebox: {};
|
|
62
|
+
error: {
|
|
63
|
+
readonly SupabaseError: SupabaseError;
|
|
64
|
+
};
|
|
65
|
+
}, {
|
|
66
|
+
schema: {};
|
|
67
|
+
standaloneSchema: {};
|
|
68
|
+
macro: {};
|
|
69
|
+
macroFn: {};
|
|
70
|
+
parser: {};
|
|
71
|
+
response: {};
|
|
72
|
+
}, {}, {
|
|
73
|
+
derive: {};
|
|
74
|
+
resolve: {
|
|
75
|
+
supabaseContext: SupabaseContext;
|
|
76
|
+
};
|
|
77
|
+
schema: {};
|
|
78
|
+
standaloneSchema: {};
|
|
79
|
+
response: elysia.ExtractErrorFromHandle<{
|
|
80
|
+
supabaseContext: SupabaseContext;
|
|
81
|
+
}>;
|
|
82
|
+
}, {
|
|
83
|
+
derive: {};
|
|
84
|
+
resolve: {};
|
|
85
|
+
schema: {};
|
|
86
|
+
standaloneSchema: {};
|
|
87
|
+
response: {};
|
|
88
|
+
}>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { withSupabase };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { t as AuthError } from "../../errors-C43um2Gg.mjs";
|
|
2
|
+
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-DbvLfq25.mjs";
|
|
3
|
+
import * as elysia from "elysia";
|
|
4
|
+
import { Elysia } from "elysia";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/elysia/plugin.d.ts
|
|
7
|
+
declare class SupabaseError extends Error {
|
|
8
|
+
status: number;
|
|
9
|
+
cause: AuthError;
|
|
10
|
+
constructor(inner: AuthError);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Elysia plugin that creates a {@link SupabaseContext} and makes it available in route handlers.
|
|
14
|
+
*
|
|
15
|
+
* Skips if a previous plugin already set the context, enabling route-level overrides.
|
|
16
|
+
* Throws a `SupabaseError` on auth failure. `.status` is on the error directly; the original
|
|
17
|
+
* `AuthError` is available as the typed `.cause`. Discriminate in `onError` via `code === 'SupabaseError'`.
|
|
18
|
+
*
|
|
19
|
+
* @param config - Auth modes and optional environment overrides. CORS is excluded — use Elysia's CORS utilities.
|
|
20
|
+
* @returns An Elysia plugin that exposes `supabaseContext`.
|
|
21
|
+
*
|
|
22
|
+
* @example App-wide auth via `.use()`
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Elysia } from 'elysia'
|
|
25
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
26
|
+
*
|
|
27
|
+
* const app = new Elysia()
|
|
28
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
29
|
+
* .get('/games', async ({ supabaseContext }) => {
|
|
30
|
+
* const { data } = await supabaseContext.supabase.from('favorite_games').select()
|
|
31
|
+
* return data
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Per-route auth via scoped `.use()`
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { Elysia } from 'elysia'
|
|
40
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
41
|
+
*
|
|
42
|
+
* const app = new Elysia()
|
|
43
|
+
* .get('/health', () => ({ status: 'ok' }))
|
|
44
|
+
* .group('/api', (app) =>
|
|
45
|
+
* app
|
|
46
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
47
|
+
* .get('/profile', async ({ supabaseContext }) => {
|
|
48
|
+
* return supabaseContext.userClaims
|
|
49
|
+
* })
|
|
50
|
+
* )
|
|
51
|
+
*
|
|
52
|
+
* app.listen(3000)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>): Elysia<"", {
|
|
56
|
+
decorator: {};
|
|
57
|
+
store: {};
|
|
58
|
+
derive: {};
|
|
59
|
+
resolve: {};
|
|
60
|
+
}, {
|
|
61
|
+
typebox: {};
|
|
62
|
+
error: {
|
|
63
|
+
readonly SupabaseError: SupabaseError;
|
|
64
|
+
};
|
|
65
|
+
}, {
|
|
66
|
+
schema: {};
|
|
67
|
+
standaloneSchema: {};
|
|
68
|
+
macro: {};
|
|
69
|
+
macroFn: {};
|
|
70
|
+
parser: {};
|
|
71
|
+
response: {};
|
|
72
|
+
}, {}, {
|
|
73
|
+
derive: {};
|
|
74
|
+
resolve: {
|
|
75
|
+
supabaseContext: SupabaseContext;
|
|
76
|
+
};
|
|
77
|
+
schema: {};
|
|
78
|
+
standaloneSchema: {};
|
|
79
|
+
response: elysia.ExtractErrorFromHandle<{
|
|
80
|
+
supabaseContext: SupabaseContext;
|
|
81
|
+
}>;
|
|
82
|
+
}, {
|
|
83
|
+
derive: {};
|
|
84
|
+
resolve: {};
|
|
85
|
+
schema: {};
|
|
86
|
+
standaloneSchema: {};
|
|
87
|
+
response: {};
|
|
88
|
+
}>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { withSupabase };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { t as createSupabaseContext } from "../../create-supabase-context-BxSEJN8a.mjs";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/elysia/plugin.ts
|
|
5
|
+
var SupabaseError = class extends Error {
|
|
6
|
+
constructor(inner) {
|
|
7
|
+
super(inner.message, { cause: inner });
|
|
8
|
+
this.status = inner.status;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Elysia plugin that creates a {@link SupabaseContext} and makes it available in route handlers.
|
|
13
|
+
*
|
|
14
|
+
* Skips if a previous plugin already set the context, enabling route-level overrides.
|
|
15
|
+
* Throws a `SupabaseError` on auth failure. `.status` is on the error directly; the original
|
|
16
|
+
* `AuthError` is available as the typed `.cause`. Discriminate in `onError` via `code === 'SupabaseError'`.
|
|
17
|
+
*
|
|
18
|
+
* @param config - Auth modes and optional environment overrides. CORS is excluded — use Elysia's CORS utilities.
|
|
19
|
+
* @returns An Elysia plugin that exposes `supabaseContext`.
|
|
20
|
+
*
|
|
21
|
+
* @example App-wide auth via `.use()`
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Elysia } from 'elysia'
|
|
24
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
25
|
+
*
|
|
26
|
+
* const app = new Elysia()
|
|
27
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
28
|
+
* .get('/games', async ({ supabaseContext }) => {
|
|
29
|
+
* const { data } = await supabaseContext.supabase.from('favorite_games').select()
|
|
30
|
+
* return data
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* app.listen(3000)
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example Per-route auth via scoped `.use()`
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { Elysia } from 'elysia'
|
|
39
|
+
* import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
40
|
+
*
|
|
41
|
+
* const app = new Elysia()
|
|
42
|
+
* .get('/health', () => ({ status: 'ok' }))
|
|
43
|
+
* .group('/api', (app) =>
|
|
44
|
+
* app
|
|
45
|
+
* .use(withSupabase({ allow: 'user' }))
|
|
46
|
+
* .get('/profile', async ({ supabaseContext }) => {
|
|
47
|
+
* return supabaseContext.userClaims
|
|
48
|
+
* })
|
|
49
|
+
* )
|
|
50
|
+
*
|
|
51
|
+
* app.listen(3000)
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
function withSupabase(config) {
|
|
55
|
+
return new Elysia().error({ SupabaseError }).resolve(async (ctx) => {
|
|
56
|
+
const existing = ctx.supabaseContext;
|
|
57
|
+
if (existing) return { supabaseContext: existing };
|
|
58
|
+
const { data, error } = await createSupabaseContext(ctx.request, config);
|
|
59
|
+
if (error) throw new SupabaseError(error);
|
|
60
|
+
return { supabaseContext: data };
|
|
61
|
+
}).as("scoped");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { withSupabase };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-
|
|
1
|
+
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-CwKZOVIv.cjs";
|
|
2
2
|
import { MiddlewareHandler } from "hono";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/hono/middleware.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-
|
|
1
|
+
import { d as SupabaseContext, m as WithSupabaseConfig } from "../../types-DbvLfq25.mjs";
|
|
2
2
|
import { MiddlewareHandler } from "hono";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/hono/middleware.d.ts
|
package/dist/core/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as
|
|
1
|
+
import { i as EnvError, t as AuthError } from "../errors-fa-CyugW.cjs";
|
|
2
|
+
import { a as AuthResult, c as CreateContextClientOptions, f as SupabaseEnv, i as AuthModeWithKey, l as Credentials, o as ClientAuth, s as CreateAdminClientOptions } from "../types-CwKZOVIv.cjs";
|
|
3
3
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
4
4
|
|
|
5
5
|
//#region src/core/resolve-env.d.ts
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as
|
|
1
|
+
import { i as EnvError, t as AuthError } from "../errors-C43um2Gg.mjs";
|
|
2
|
+
import { a as AuthResult, c as CreateContextClientOptions, f as SupabaseEnv, i as AuthModeWithKey, l as Credentials, o as ClientAuth, s as CreateAdminClientOptions } from "../types-DbvLfq25.mjs";
|
|
3
3
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
4
4
|
|
|
5
5
|
//#region src/core/resolve-env.d.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as
|
|
1
|
+
import { a as EnvGenericError, c as MissingDefaultPublishableKeyError, d as MissingSecretKeyError, f as MissingSupabaseURLError, i as EnvError, l as MissingDefaultSecretKeyError, n as AuthGenericError, o as Errors, r as CreateSupabaseClientError, s as InvalidCredentialsError, t as AuthError, u as MissingPublishableKeyError } from "./errors-fa-CyugW.cjs";
|
|
2
|
+
import { a as AuthResult, c as CreateContextClientOptions, d as SupabaseContext, f as SupabaseEnv, i as AuthModeWithKey, l as Credentials, m as WithSupabaseConfig, n as AllowWithKey, o as ClientAuth, p as UserClaims, r as AuthMode, s as CreateAdminClientOptions, t as Allow, u as JWTClaims } from "./types-CwKZOVIv.cjs";
|
|
3
3
|
|
|
4
4
|
//#region src/with-supabase.d.ts
|
|
5
5
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as
|
|
1
|
+
import { a as EnvGenericError, c as MissingDefaultPublishableKeyError, d as MissingSecretKeyError, f as MissingSupabaseURLError, i as EnvError, l as MissingDefaultSecretKeyError, n as AuthGenericError, o as Errors, r as CreateSupabaseClientError, s as InvalidCredentialsError, t as AuthError, u as MissingPublishableKeyError } from "./errors-C43um2Gg.mjs";
|
|
2
|
+
import { a as AuthResult, c as CreateContextClientOptions, d as SupabaseContext, f as SupabaseEnv, i as AuthModeWithKey, l as Credentials, m as WithSupabaseConfig, n as AllowWithKey, o as ClientAuth, p as UserClaims, r as AuthMode, s as CreateAdminClientOptions, t as Allow, u as JWTClaims } from "./types-DbvLfq25.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/with-supabase.d.ts
|
|
5
5
|
/**
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Elysia Adapter
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Install Elysia as a peer dependency:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add elysia
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The adapter exports its own `withSupabase` that returns an Elysia plugin instead of a fetch handler.
|
|
12
|
+
|
|
13
|
+
## Basic app with auth
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Elysia } from 'elysia'
|
|
17
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
18
|
+
|
|
19
|
+
const app = new Elysia()
|
|
20
|
+
.use(withSupabase({ auth: 'user' }))
|
|
21
|
+
.get('/todos', async ({ supabaseContext }) => {
|
|
22
|
+
const { data } = await supabaseContext.supabase.from('todos').select()
|
|
23
|
+
return data
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
app.listen(3000)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The context is available as `supabaseContext` in your route handlers and contains the same `SupabaseContext` fields as the main `withSupabase` wrapper: `supabase`, `supabaseAdmin`, `userClaims`, `jwtClaims`, and `authMode`.
|
|
30
|
+
|
|
31
|
+
## Per-route auth
|
|
32
|
+
|
|
33
|
+
Apply different auth modes to different routes by using the plugin on scoped route groups:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { Elysia } from 'elysia'
|
|
37
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
38
|
+
|
|
39
|
+
const app = new Elysia()
|
|
40
|
+
// Public route — no auth
|
|
41
|
+
.get('/health', () => ({ status: 'ok' }))
|
|
42
|
+
// User-authenticated routes
|
|
43
|
+
.group('/api', (app) =>
|
|
44
|
+
app
|
|
45
|
+
.use(withSupabase({ auth: 'user' }))
|
|
46
|
+
.get('/todos', async ({ supabaseContext }) => {
|
|
47
|
+
const { data } = await supabaseContext.supabase.from('todos').select()
|
|
48
|
+
return data
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
// Secret-key-protected admin routes
|
|
52
|
+
.group('/admin', (app) =>
|
|
53
|
+
app
|
|
54
|
+
.use(withSupabase({ auth: 'secret' }))
|
|
55
|
+
.post('/sync', async ({ supabaseContext }) => {
|
|
56
|
+
const { data } = await supabaseContext.supabaseAdmin
|
|
57
|
+
.from('audit_log')
|
|
58
|
+
.insert({ action: 'sync' })
|
|
59
|
+
return data
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
app.listen(3000)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Skip behavior
|
|
67
|
+
|
|
68
|
+
If a previous plugin already resolved `supabaseContext`, subsequent `withSupabase` calls skip auth. This allows chaining plugins without redundant work.
|
|
69
|
+
|
|
70
|
+
**Important:** The plugin calls `.as('scoped')` so its `resolve` hook propagates one level up to the parent app — routes registered after `.use(withSupabase(...))` will see `supabaseContext`. The skip-if-set pattern cannot make a route stricter than an already-resolved context.
|
|
71
|
+
|
|
72
|
+
For routes that need different auth than the rest of the app, use scoped `.group()` with `.use(withSupabase(...))` without an app-wide plugin (see the "Per-route auth" section above).
|
|
73
|
+
|
|
74
|
+
## CORS
|
|
75
|
+
|
|
76
|
+
The Elysia adapter does not handle CORS — the `cors` option is excluded from its config type. Use Elysia's CORS plugin:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { Elysia } from 'elysia'
|
|
80
|
+
import { cors } from '@elysiajs/cors'
|
|
81
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
82
|
+
|
|
83
|
+
const app = new Elysia()
|
|
84
|
+
.use(cors())
|
|
85
|
+
.use(withSupabase({ auth: 'user' }))
|
|
86
|
+
.get('/todos', async ({ supabaseContext }) => {
|
|
87
|
+
const { data } = await supabaseContext.supabase.from('todos').select()
|
|
88
|
+
return data
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
app.listen(3000)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Error handling
|
|
95
|
+
|
|
96
|
+
When auth fails, the plugin throws a `SupabaseError`. The HTTP status is on `.status` directly, and the original `AuthError` is available as the typed `.cause`. Discriminate in `onError` via `code === 'SupabaseError'`:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { Elysia } from 'elysia'
|
|
100
|
+
import { withSupabase } from '@supabase/server/adapters/elysia'
|
|
101
|
+
|
|
102
|
+
const app = new Elysia()
|
|
103
|
+
.use(withSupabase({ auth: 'user' }))
|
|
104
|
+
.onError(({ code, error, status }) => {
|
|
105
|
+
if (code !== 'SupabaseError') return
|
|
106
|
+
return status(error.status as 401, {
|
|
107
|
+
error: error.message,
|
|
108
|
+
code: error.cause.code,
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
.get('/todos', async ({ supabaseContext }) => {
|
|
112
|
+
const { data } = await supabaseContext.supabase.from('todos').select()
|
|
113
|
+
return data
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
app.listen(3000)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Without a custom `onError`, Elysia uses the `status` property on the thrown `SupabaseError` to set the response status automatically (401 for auth failures, 500 for internal errors).
|
|
120
|
+
|
|
121
|
+
## Environment overrides
|
|
122
|
+
|
|
123
|
+
Pass `env` to override auto-detected environment variables, same as the main wrapper:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
app.use(withSupabase({ auth: 'user', env: { url: 'http://localhost:54321' } }))
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Supabase client options
|
|
130
|
+
|
|
131
|
+
Forward options to the underlying `createClient()` calls:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
app.use(
|
|
135
|
+
withSupabase({
|
|
136
|
+
auth: 'user',
|
|
137
|
+
supabaseOptions: { db: { schema: 'api' } },
|
|
138
|
+
}),
|
|
139
|
+
)
|
|
140
|
+
```
|
package/docs/api-reference.md
CHANGED
|
@@ -134,6 +134,38 @@ Defaults to `auth: 'user'` when config is omitted.
|
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
|
137
|
+
## @supabase/server/adapters/h3
|
|
138
|
+
|
|
139
|
+
### withSupabase (H3)
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>): Middleware
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
H3 middleware. Sets `event.context.supabaseContext` on the H3 event. Throws `HTTPError` on auth failure with `cause: AuthError`.
|
|
146
|
+
|
|
147
|
+
Skips if `event.context.supabaseContext` is already set (enables chained middleware).
|
|
148
|
+
|
|
149
|
+
Defaults to `auth: 'user'` when config is omitted.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## @supabase/server/adapters/elysia
|
|
154
|
+
|
|
155
|
+
### withSupabase (Elysia)
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
function withSupabase(config?: Omit<WithSupabaseConfig, 'cors'>): Elysia
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Elysia plugin that resolves `supabaseContext` into the request context. Throws an error on auth failure with `cause: AuthError`.
|
|
162
|
+
|
|
163
|
+
Skips if `supabaseContext` is already resolved by a prior plugin.
|
|
164
|
+
|
|
165
|
+
Defaults to `auth: 'user'` when config is omitted.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
137
169
|
## Types
|
|
138
170
|
|
|
139
171
|
### AuthMode
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supabase/server",
|
|
3
|
-
"version": "1.1.0-rc.
|
|
3
|
+
"version": "1.1.0-rc.65",
|
|
4
4
|
"description": "Server-side utilities for Supabase. Handles auth, client creation, and context injection so you write business logic, not boilerplate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"edge",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"import": "./dist/adapters/h3/index.mjs",
|
|
40
40
|
"require": "./dist/adapters/h3/index.cjs"
|
|
41
41
|
},
|
|
42
|
+
"./adapters/elysia": {
|
|
43
|
+
"types": "./dist/adapters/elysia/index.d.mts",
|
|
44
|
+
"import": "./dist/adapters/elysia/index.mjs",
|
|
45
|
+
"require": "./dist/adapters/elysia/index.cjs"
|
|
46
|
+
},
|
|
42
47
|
"./package.json": "./package.json"
|
|
43
48
|
},
|
|
44
49
|
"main": "./dist/index.cjs",
|
|
@@ -72,7 +77,8 @@
|
|
|
72
77
|
"peerDependencies": {
|
|
73
78
|
"@supabase/supabase-js": "^2.0.0",
|
|
74
79
|
"h3": "^2.0.0",
|
|
75
|
-
"hono": "^4.0.0"
|
|
80
|
+
"hono": "^4.0.0",
|
|
81
|
+
"elysia": "^1.4.0"
|
|
76
82
|
},
|
|
77
83
|
"peerDependenciesMeta": {
|
|
78
84
|
"h3": {
|
|
@@ -80,6 +86,9 @@
|
|
|
80
86
|
},
|
|
81
87
|
"hono": {
|
|
82
88
|
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"elysia": {
|
|
91
|
+
"optional": true
|
|
83
92
|
}
|
|
84
93
|
},
|
|
85
94
|
"devDependencies": {
|
|
@@ -87,6 +96,7 @@
|
|
|
87
96
|
"@commitlint/config-conventional": "^20.4.2",
|
|
88
97
|
"@supabase/supabase-js": "^2.105.4",
|
|
89
98
|
"eslint": "^10.0.2",
|
|
99
|
+
"elysia": "^1.4.0",
|
|
90
100
|
"h3": "2.0.1-rc.20",
|
|
91
101
|
"hono": "^4.12.5",
|
|
92
102
|
"prettier": "3.8.1",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|