@supabase/server 0.1.1-rc.26 → 0.1.1-rc.28

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.
@@ -1,4 +1,4 @@
1
- import { SupabaseClient } from "@supabase/supabase-js";
1
+ import { SupabaseClient, SupabaseClientOptions } from "@supabase/supabase-js";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  /**
@@ -204,6 +204,50 @@ interface WithSupabaseConfig {
204
204
  * @defaultValue `true`
205
205
  */
206
206
  cors?: boolean | Record<string, string>;
207
+ /**
208
+ * Options forwarded to both internal `createClient()` calls.
209
+ *
210
+ * `accessToken` is stripped, and auth settings (`persistSession`, `autoRefreshToken`,
211
+ * `detectSessionInUrl`) are force-overwritten to server-safe values.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * withSupabase({
216
+ * allow: 'user',
217
+ * supabaseOptions: { db: { schema: 'api' } },
218
+ * }, handler)
219
+ * ```
220
+ */
221
+ supabaseOptions?: SupabaseClientOptions<string>;
222
+ }
223
+ /**
224
+ * Auth identity for client creation functions.
225
+ *
226
+ * @see {@link verifyAuth}, {@link verifyCredentials}
227
+ */
228
+ interface ClientAuth {
229
+ /** The caller's JWT, or `null` for anonymous access. */
230
+ token?: string | null;
231
+ /** Name of the API key to use. Falls back to `"default"`, then first available. */
232
+ keyName?: string | null;
233
+ }
234
+ /** Options for {@link createContextClient}. */
235
+ interface CreateContextClientOptions {
236
+ /** Auth identity — token and key name from the verified request. */
237
+ auth?: ClientAuth;
238
+ /** Override auto-detected environment variables. */
239
+ env?: Partial<SupabaseEnv>;
240
+ /** Options forwarded to `createClient()`. `accessToken` is stripped; auth settings are force-overwritten. */
241
+ supabaseOptions?: SupabaseClientOptions<string>;
242
+ }
243
+ /** Options for {@link createAdminClient}. */
244
+ interface CreateAdminClientOptions {
245
+ /** Auth identity — key name from the verified request. */
246
+ auth?: Pick<ClientAuth, 'keyName'>;
247
+ /** Override auto-detected environment variables. */
248
+ env?: Partial<SupabaseEnv>;
249
+ /** Options forwarded to `createClient()`. `accessToken` is stripped; auth settings are force-overwritten. */
250
+ supabaseOptions?: SupabaseClientOptions<string>;
207
251
  }
208
252
  /**
209
253
  * The Supabase context created for each authenticated request.
@@ -224,4 +268,4 @@ interface SupabaseContext<Database = unknown> {
224
268
  authType: Allow;
225
269
  }
226
270
  //#endregion
227
- export { JWTClaims as a, UserClaims as c, Credentials as i, WithSupabaseConfig as l, AllowWithKey as n, SupabaseContext as o, AuthResult as r, SupabaseEnv as s, Allow as t };
271
+ export { CreateAdminClientOptions as a, JWTClaims as c, UserClaims as d, WithSupabaseConfig as f, ClientAuth as i, SupabaseContext as l, AllowWithKey as n, CreateContextClientOptions as o, AuthResult as r, Credentials as s, Allow as t, SupabaseEnv as u };
@@ -1,4 +1,4 @@
1
- import { SupabaseClient } from "@supabase/supabase-js";
1
+ import { SupabaseClient, SupabaseClientOptions } from "@supabase/supabase-js";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  /**
@@ -204,6 +204,50 @@ interface WithSupabaseConfig {
204
204
  * @defaultValue `true`
205
205
  */
206
206
  cors?: boolean | Record<string, string>;
207
+ /**
208
+ * Options forwarded to both internal `createClient()` calls.
209
+ *
210
+ * `accessToken` is stripped, and auth settings (`persistSession`, `autoRefreshToken`,
211
+ * `detectSessionInUrl`) are force-overwritten to server-safe values.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * withSupabase({
216
+ * allow: 'user',
217
+ * supabaseOptions: { db: { schema: 'api' } },
218
+ * }, handler)
219
+ * ```
220
+ */
221
+ supabaseOptions?: SupabaseClientOptions<string>;
222
+ }
223
+ /**
224
+ * Auth identity for client creation functions.
225
+ *
226
+ * @see {@link verifyAuth}, {@link verifyCredentials}
227
+ */
228
+ interface ClientAuth {
229
+ /** The caller's JWT, or `null` for anonymous access. */
230
+ token?: string | null;
231
+ /** Name of the API key to use. Falls back to `"default"`, then first available. */
232
+ keyName?: string | null;
233
+ }
234
+ /** Options for {@link createContextClient}. */
235
+ interface CreateContextClientOptions {
236
+ /** Auth identity — token and key name from the verified request. */
237
+ auth?: ClientAuth;
238
+ /** Override auto-detected environment variables. */
239
+ env?: Partial<SupabaseEnv>;
240
+ /** Options forwarded to `createClient()`. `accessToken` is stripped; auth settings are force-overwritten. */
241
+ supabaseOptions?: SupabaseClientOptions<string>;
242
+ }
243
+ /** Options for {@link createAdminClient}. */
244
+ interface CreateAdminClientOptions {
245
+ /** Auth identity — key name from the verified request. */
246
+ auth?: Pick<ClientAuth, 'keyName'>;
247
+ /** Override auto-detected environment variables. */
248
+ env?: Partial<SupabaseEnv>;
249
+ /** Options forwarded to `createClient()`. `accessToken` is stripped; auth settings are force-overwritten. */
250
+ supabaseOptions?: SupabaseClientOptions<string>;
207
251
  }
208
252
  /**
209
253
  * The Supabase context created for each authenticated request.
@@ -224,4 +268,4 @@ interface SupabaseContext<Database = unknown> {
224
268
  authType: Allow;
225
269
  }
226
270
  //#endregion
227
- export { JWTClaims as a, UserClaims as c, Credentials as i, WithSupabaseConfig as l, AllowWithKey as n, SupabaseContext as o, AuthResult as r, SupabaseEnv as s, Allow as t };
271
+ export { CreateAdminClientOptions as a, JWTClaims as c, UserClaims as d, WithSupabaseConfig as f, ClientAuth as i, SupabaseContext as l, AllowWithKey as n, CreateContextClientOptions as o, AuthResult as r, Credentials as s, Allow as t, SupabaseEnv as u };
@@ -5,8 +5,7 @@ import { createLocalJWKSet, jwtVerify } from "jose";
5
5
  /**
6
6
  * Thrown when a required environment variable is missing or malformed.
7
7
  *
8
- * Has a fixed `status` of `500` since environment errors are server-side
9
- * configuration issues, not client errors.
8
+ * Always has `status: 500` environment errors are server-side configuration issues.
10
9
  *
11
10
  * @example
12
11
  * ```ts
@@ -23,13 +22,32 @@ import { createLocalJWKSet, jwtVerify } from "jose";
23
22
  * ```
24
23
  */
25
24
  var EnvError = class extends Error {
26
- constructor(message, code = "ENV_ERROR") {
25
+ constructor(message, code = EnvGenericError) {
27
26
  super(message);
28
27
  this.status = 500;
29
28
  this.name = "EnvError";
30
29
  this.code = code;
31
30
  }
32
31
  };
32
+ /** Generic environment error code. */
33
+ const EnvGenericError = "ENV_ERROR";
34
+ /** `SUPABASE_URL` is not set. */
35
+ const MissingSupabaseURLError = "MISSING_SUPABASE_URL";
36
+ /** Named publishable key not found in `SUPABASE_PUBLISHABLE_KEYS`. */
37
+ const MissingPublishableKeyError = "MISSING_PUBLISHABLE_KEY";
38
+ /** No default publishable key found. */
39
+ const MissingDefaultPublishableKeyError = "MISSING_DEFAULT_PUBLISHABLE_KEY";
40
+ /** Named secret key not found in `SUPABASE_SECRET_KEYS`. */
41
+ const MissingSecretKeyError = "MISSING_SECRET_KEY";
42
+ /** No default secret key found. */
43
+ const MissingDefaultSecretKeyError = "MISSING_DEFAULT_SECRET_KEY";
44
+ const EnvErrorMap = {
45
+ [MissingSupabaseURLError]: () => new EnvError("SUPABASE_URL is required but not set", MissingSupabaseURLError),
46
+ [MissingSecretKeyError]: (name) => new EnvError(`No "${name}" secret key found. Include a "${name}" entry in SUPABASE_SECRET_KEYS.`, MissingSecretKeyError),
47
+ [MissingDefaultSecretKeyError]: () => new EnvError("No default secret key found. Set SUPABASE_SECRET_KEY or include a \"default\" entry in SUPABASE_SECRET_KEYS.", MissingDefaultSecretKeyError),
48
+ [MissingPublishableKeyError]: (name) => new EnvError(`No "${name}" publishable key found. Include a "${name}" entry in SUPABASE_PUBLISHABLE_KEYS.`, MissingPublishableKeyError),
49
+ [MissingDefaultPublishableKeyError]: () => new EnvError("No default publishable key found. Set SUPABASE_PUBLISHABLE_KEY or include a \"default\" entry in SUPABASE_PUBLISHABLE_KEYS.", MissingDefaultPublishableKeyError)
50
+ };
33
51
  /**
34
52
  * Thrown when authentication or authorization fails.
35
53
  *
@@ -44,20 +62,44 @@ var EnvError = class extends Error {
44
62
  * if (error) {
45
63
  * // error is an AuthError
46
64
  * return Response.json(
47
- * { error: error.message, code: error.code },
65
+ * { message: error.message, code: error.code },
48
66
  * { status: error.status },
49
67
  * )
50
68
  * }
51
69
  * ```
52
70
  */
53
71
  var AuthError = class extends Error {
54
- constructor(message, code = "AUTH_ERROR", status = 401) {
72
+ constructor(message, code = AuthGenericError, status = 401) {
55
73
  super(message);
56
74
  this.name = "AuthError";
57
75
  this.code = code;
58
76
  this.status = status;
59
77
  }
60
78
  };
79
+ /** Generic authentication error code. */
80
+ const AuthGenericError = "AUTH_ERROR";
81
+ /** No credential matched any allowed auth mode. */
82
+ const InvalidCredentialsError = "INVALID_CREDENTIALS";
83
+ /** Failed to create a Supabase client after auth succeeded. */
84
+ const CreateSupabaseClientError = "CREATE_SUPABASE_CLIENT_ERROR";
85
+ const AuthErrorMap = {
86
+ [InvalidCredentialsError]: () => new AuthError("Invalid credentials", InvalidCredentialsError, 401),
87
+ [CreateSupabaseClientError]: () => new AuthError("Failed to create Supabase client", CreateSupabaseClientError, 500)
88
+ };
89
+ /**
90
+ * Factory map for all error types. Keyed by error code constant, each entry
91
+ * returns a pre-configured {@link EnvError} or {@link AuthError}.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * throw Errors[MissingSupabaseURLError]()
96
+ * throw Errors[MissingPublishableKeyError]('mobile')
97
+ * ```
98
+ */
99
+ const Errors = {
100
+ ...EnvErrorMap,
101
+ ...AuthErrorMap
102
+ };
61
103
 
62
104
  //#endregion
63
105
  //#region src/core/resolve-env.ts
@@ -138,7 +180,7 @@ function resolveEnv(overrides) {
138
180
  const url = overrides?.url ?? getEnvVar("SUPABASE_URL");
139
181
  if (!url) return {
140
182
  data: null,
141
- error: new EnvError("SUPABASE_URL is required but not set", "MISSING_SUPABASE_URL")
183
+ error: Errors[MissingSupabaseURLError]()
142
184
  };
143
185
  return {
144
186
  data: {
@@ -157,11 +199,8 @@ function resolveEnv(overrides) {
157
199
  * Creates an admin Supabase client that bypasses Row-Level Security.
158
200
  *
159
201
  * Uses a secret key for authentication, giving full access to all data.
160
- * Session persistence is disabled (stateless, one client per request).
202
+ * Stateless one client per request.
161
203
  *
162
- * @param env - Optional environment overrides (passed through to {@link resolveEnv}).
163
- * @param keyName - Name of the secret key to use. Falls back to `"default"`, then first available.
164
- * @returns A configured {@link SupabaseClient} with admin (service-role) privileges.
165
204
  * @throws {@link EnvError} If `SUPABASE_URL` is missing or the specified secret key is not found.
166
205
  *
167
206
  * @example
@@ -170,18 +209,32 @@ function resolveEnv(overrides) {
170
209
  * const { data } = await supabaseAdmin.from('audit_log').insert({ action: 'user_login' })
171
210
  * ```
172
211
  */
173
- function createAdminClient(env, keyName) {
174
- const { data: resolved, error } = resolveEnv(env);
212
+ function createAdminClient(options) {
213
+ const { data: resolved, error } = resolveEnv(options?.env);
175
214
  if (error) throw error;
215
+ const keyName = options?.auth?.keyName;
216
+ const supabaseOptions = options?.supabaseOptions;
176
217
  const name = keyName ?? "default";
177
218
  const keys = resolved.secretKeys;
178
219
  const secretKey = keys[name] ?? (keyName == null ? Object.values(keys)[0] : void 0);
179
- if (!secretKey) throw new EnvError(name === "default" ? "No default secret key found. Set SUPABASE_SECRET_KEY or include a \"default\" entry in SUPABASE_SECRET_KEYS." : `No "${name}" secret key found. Include a "${name}" entry in SUPABASE_SECRET_KEYS.`, "MISSING_SECRET_KEY");
180
- return createClient(resolved.url, secretKey, { auth: {
181
- persistSession: false,
182
- autoRefreshToken: false,
183
- detectSessionInUrl: false
184
- } });
220
+ if (!secretKey) throw name === "default" ? Errors[MissingDefaultSecretKeyError]() : Errors[MissingSecretKeyError](name);
221
+ const safeHeaders = { ...supabaseOptions?.global?.headers };
222
+ delete safeHeaders.Authorization;
223
+ delete safeHeaders.apikey;
224
+ return createClient(resolved.url, secretKey, {
225
+ ...supabaseOptions,
226
+ accessToken: void 0,
227
+ global: {
228
+ ...supabaseOptions?.global,
229
+ headers: safeHeaders
230
+ },
231
+ auth: {
232
+ ...supabaseOptions?.auth,
233
+ persistSession: false,
234
+ autoRefreshToken: false,
235
+ detectSessionInUrl: false
236
+ }
237
+ });
185
238
  }
186
239
 
187
240
  //#endregion
@@ -190,32 +243,44 @@ function createAdminClient(env, keyName) {
190
243
  * Creates a Supabase client scoped to the caller's context.
191
244
  *
192
245
  * Configured with a publishable key and (optionally) the caller's JWT,
193
- * so Row-Level Security policies apply. Session persistence is disabled
194
- * (stateless, one client per request).
246
+ * so Row-Level Security policies apply. Stateless one client per request.
195
247
  *
196
- * @param token - The caller's JWT, or `null` for anonymous access.
197
- * @param env - Optional environment overrides (passed through to {@link resolveEnv}).
198
- * @param keyName - Name of the publishable key to use. Falls back to `"default"`, then first available.
199
- * @returns A configured {@link SupabaseClient} with RLS enforced.
200
248
  * @throws {@link EnvError} If `SUPABASE_URL` is missing or the specified publishable key is not found.
201
249
  *
202
250
  * @example
203
251
  * ```ts
204
252
  * const { data: auth } = await verifyAuth(request, { allow: 'user' })
205
- * const supabase = createContextClient(auth.token)
253
+ * const supabase = createContextClient({
254
+ * auth: { token: auth.token, keyName: auth.keyName },
255
+ * })
206
256
  * const { data } = await supabase.rpc('get_my_items')
207
257
  * ```
208
258
  */
209
- function createContextClient(token, env, keyName) {
210
- const { data: resolved, error } = resolveEnv(env);
259
+ function createContextClient(options) {
260
+ const { data: resolved, error } = resolveEnv(options?.env);
211
261
  if (error) throw error;
262
+ const token = options?.auth?.token;
263
+ const keyName = options?.auth?.keyName;
264
+ const supabaseOptions = options?.supabaseOptions;
212
265
  const name = keyName ?? "default";
213
266
  const keys = resolved.publishableKeys;
214
267
  const anonKey = keys[name] ?? (keyName == null ? Object.values(keys)[0] : void 0);
215
- if (!anonKey) throw new EnvError(name === "default" ? "No default publishable key found. Set SUPABASE_PUBLISHABLE_KEY or include a \"default\" entry in SUPABASE_PUBLISHABLE_KEYS." : `No "${name}" publishable key found. Include a "${name}" entry in SUPABASE_PUBLISHABLE_KEYS.`, "MISSING_PUBLISHABLE_KEY");
268
+ if (!anonKey) throw name === "default" ? Errors[MissingDefaultPublishableKeyError]() : Errors[MissingPublishableKeyError](name);
269
+ const safeHeaders = { ...supabaseOptions?.global?.headers };
270
+ delete safeHeaders.Authorization;
271
+ delete safeHeaders.apikey;
216
272
  return createClient(resolved.url, anonKey, {
217
- global: { headers: token ? { Authorization: `Bearer ${token}` } : {} },
273
+ ...supabaseOptions,
274
+ accessToken: void 0,
275
+ global: {
276
+ ...supabaseOptions?.global,
277
+ headers: {
278
+ ...safeHeaders,
279
+ ...token ? { Authorization: `Bearer ${token}` } : {}
280
+ }
281
+ },
218
282
  auth: {
283
+ ...supabaseOptions?.auth,
219
284
  persistSession: false,
220
285
  autoRefreshToken: false,
221
286
  detectSessionInUrl: false
@@ -424,7 +489,7 @@ async function tryMode(mode, credentials, env) {
424
489
  * allow: ['user', 'public'],
425
490
  * })
426
491
  * if (error) {
427
- * return Response.json({ error: error.message }, { status: error.status })
492
+ * return Response.json({ message: error.message }, { status: error.status })
428
493
  * }
429
494
  * ```
430
495
  */
@@ -444,7 +509,7 @@ async function verifyCredentials(credentials, options) {
444
509
  }
445
510
  return {
446
511
  data: null,
447
- error: new AuthError("Invalid credentials", "INVALID_CREDENTIALS", 401)
512
+ error: Errors[InvalidCredentialsError]()
448
513
  };
449
514
  }
450
515
 
@@ -473,7 +538,7 @@ async function verifyCredentials(credentials, options) {
473
538
  * })
474
539
  *
475
540
  * if (error) {
476
- * return Response.json({ error: error.message }, { status: error.status })
541
+ * return Response.json({ message: error.message }, { status: error.status })
477
542
  * }
478
543
  *
479
544
  * console.log(auth.userClaims!.id) // "d0f1a2b3-..."
@@ -484,4 +549,4 @@ async function verifyAuth(request, options) {
484
549
  }
485
550
 
486
551
  //#endregion
487
- export { createAdminClient as a, EnvError as c, createContextClient as i, verifyCredentials as n, resolveEnv as o, extractCredentials as r, AuthError as s, verifyAuth as t };
552
+ export { MissingSecretKeyError as _, createAdminClient as a, AuthGenericError as c, EnvGenericError as d, Errors as f, MissingPublishableKeyError as g, MissingDefaultSecretKeyError as h, createContextClient as i, CreateSupabaseClientError as l, MissingDefaultPublishableKeyError as m, verifyCredentials as n, resolveEnv as o, InvalidCredentialsError as p, extractCredentials as r, AuthError as s, verifyAuth as t, EnvError as u, MissingSupabaseURLError as v };
@@ -5,8 +5,7 @@ let jose = require("jose");
5
5
  /**
6
6
  * Thrown when a required environment variable is missing or malformed.
7
7
  *
8
- * Has a fixed `status` of `500` since environment errors are server-side
9
- * configuration issues, not client errors.
8
+ * Always has `status: 500` environment errors are server-side configuration issues.
10
9
  *
11
10
  * @example
12
11
  * ```ts
@@ -23,13 +22,32 @@ let jose = require("jose");
23
22
  * ```
24
23
  */
25
24
  var EnvError = class extends Error {
26
- constructor(message, code = "ENV_ERROR") {
25
+ constructor(message, code = EnvGenericError) {
27
26
  super(message);
28
27
  this.status = 500;
29
28
  this.name = "EnvError";
30
29
  this.code = code;
31
30
  }
32
31
  };
32
+ /** Generic environment error code. */
33
+ const EnvGenericError = "ENV_ERROR";
34
+ /** `SUPABASE_URL` is not set. */
35
+ const MissingSupabaseURLError = "MISSING_SUPABASE_URL";
36
+ /** Named publishable key not found in `SUPABASE_PUBLISHABLE_KEYS`. */
37
+ const MissingPublishableKeyError = "MISSING_PUBLISHABLE_KEY";
38
+ /** No default publishable key found. */
39
+ const MissingDefaultPublishableKeyError = "MISSING_DEFAULT_PUBLISHABLE_KEY";
40
+ /** Named secret key not found in `SUPABASE_SECRET_KEYS`. */
41
+ const MissingSecretKeyError = "MISSING_SECRET_KEY";
42
+ /** No default secret key found. */
43
+ const MissingDefaultSecretKeyError = "MISSING_DEFAULT_SECRET_KEY";
44
+ const EnvErrorMap = {
45
+ [MissingSupabaseURLError]: () => new EnvError("SUPABASE_URL is required but not set", MissingSupabaseURLError),
46
+ [MissingSecretKeyError]: (name) => new EnvError(`No "${name}" secret key found. Include a "${name}" entry in SUPABASE_SECRET_KEYS.`, MissingSecretKeyError),
47
+ [MissingDefaultSecretKeyError]: () => new EnvError("No default secret key found. Set SUPABASE_SECRET_KEY or include a \"default\" entry in SUPABASE_SECRET_KEYS.", MissingDefaultSecretKeyError),
48
+ [MissingPublishableKeyError]: (name) => new EnvError(`No "${name}" publishable key found. Include a "${name}" entry in SUPABASE_PUBLISHABLE_KEYS.`, MissingPublishableKeyError),
49
+ [MissingDefaultPublishableKeyError]: () => new EnvError("No default publishable key found. Set SUPABASE_PUBLISHABLE_KEY or include a \"default\" entry in SUPABASE_PUBLISHABLE_KEYS.", MissingDefaultPublishableKeyError)
50
+ };
33
51
  /**
34
52
  * Thrown when authentication or authorization fails.
35
53
  *
@@ -44,20 +62,44 @@ var EnvError = class extends Error {
44
62
  * if (error) {
45
63
  * // error is an AuthError
46
64
  * return Response.json(
47
- * { error: error.message, code: error.code },
65
+ * { message: error.message, code: error.code },
48
66
  * { status: error.status },
49
67
  * )
50
68
  * }
51
69
  * ```
52
70
  */
53
71
  var AuthError = class extends Error {
54
- constructor(message, code = "AUTH_ERROR", status = 401) {
72
+ constructor(message, code = AuthGenericError, status = 401) {
55
73
  super(message);
56
74
  this.name = "AuthError";
57
75
  this.code = code;
58
76
  this.status = status;
59
77
  }
60
78
  };
79
+ /** Generic authentication error code. */
80
+ const AuthGenericError = "AUTH_ERROR";
81
+ /** No credential matched any allowed auth mode. */
82
+ const InvalidCredentialsError = "INVALID_CREDENTIALS";
83
+ /** Failed to create a Supabase client after auth succeeded. */
84
+ const CreateSupabaseClientError = "CREATE_SUPABASE_CLIENT_ERROR";
85
+ const AuthErrorMap = {
86
+ [InvalidCredentialsError]: () => new AuthError("Invalid credentials", InvalidCredentialsError, 401),
87
+ [CreateSupabaseClientError]: () => new AuthError("Failed to create Supabase client", CreateSupabaseClientError, 500)
88
+ };
89
+ /**
90
+ * Factory map for all error types. Keyed by error code constant, each entry
91
+ * returns a pre-configured {@link EnvError} or {@link AuthError}.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * throw Errors[MissingSupabaseURLError]()
96
+ * throw Errors[MissingPublishableKeyError]('mobile')
97
+ * ```
98
+ */
99
+ const Errors = {
100
+ ...EnvErrorMap,
101
+ ...AuthErrorMap
102
+ };
61
103
 
62
104
  //#endregion
63
105
  //#region src/core/resolve-env.ts
@@ -138,7 +180,7 @@ function resolveEnv(overrides) {
138
180
  const url = overrides?.url ?? getEnvVar("SUPABASE_URL");
139
181
  if (!url) return {
140
182
  data: null,
141
- error: new EnvError("SUPABASE_URL is required but not set", "MISSING_SUPABASE_URL")
183
+ error: Errors[MissingSupabaseURLError]()
142
184
  };
143
185
  return {
144
186
  data: {
@@ -157,11 +199,8 @@ function resolveEnv(overrides) {
157
199
  * Creates an admin Supabase client that bypasses Row-Level Security.
158
200
  *
159
201
  * Uses a secret key for authentication, giving full access to all data.
160
- * Session persistence is disabled (stateless, one client per request).
202
+ * Stateless one client per request.
161
203
  *
162
- * @param env - Optional environment overrides (passed through to {@link resolveEnv}).
163
- * @param keyName - Name of the secret key to use. Falls back to `"default"`, then first available.
164
- * @returns A configured {@link SupabaseClient} with admin (service-role) privileges.
165
204
  * @throws {@link EnvError} If `SUPABASE_URL` is missing or the specified secret key is not found.
166
205
  *
167
206
  * @example
@@ -170,18 +209,32 @@ function resolveEnv(overrides) {
170
209
  * const { data } = await supabaseAdmin.from('audit_log').insert({ action: 'user_login' })
171
210
  * ```
172
211
  */
173
- function createAdminClient(env, keyName) {
174
- const { data: resolved, error } = resolveEnv(env);
212
+ function createAdminClient(options) {
213
+ const { data: resolved, error } = resolveEnv(options?.env);
175
214
  if (error) throw error;
215
+ const keyName = options?.auth?.keyName;
216
+ const supabaseOptions = options?.supabaseOptions;
176
217
  const name = keyName ?? "default";
177
218
  const keys = resolved.secretKeys;
178
219
  const secretKey = keys[name] ?? (keyName == null ? Object.values(keys)[0] : void 0);
179
- if (!secretKey) throw new EnvError(name === "default" ? "No default secret key found. Set SUPABASE_SECRET_KEY or include a \"default\" entry in SUPABASE_SECRET_KEYS." : `No "${name}" secret key found. Include a "${name}" entry in SUPABASE_SECRET_KEYS.`, "MISSING_SECRET_KEY");
180
- return (0, _supabase_supabase_js.createClient)(resolved.url, secretKey, { auth: {
181
- persistSession: false,
182
- autoRefreshToken: false,
183
- detectSessionInUrl: false
184
- } });
220
+ if (!secretKey) throw name === "default" ? Errors[MissingDefaultSecretKeyError]() : Errors[MissingSecretKeyError](name);
221
+ const safeHeaders = { ...supabaseOptions?.global?.headers };
222
+ delete safeHeaders.Authorization;
223
+ delete safeHeaders.apikey;
224
+ return (0, _supabase_supabase_js.createClient)(resolved.url, secretKey, {
225
+ ...supabaseOptions,
226
+ accessToken: void 0,
227
+ global: {
228
+ ...supabaseOptions?.global,
229
+ headers: safeHeaders
230
+ },
231
+ auth: {
232
+ ...supabaseOptions?.auth,
233
+ persistSession: false,
234
+ autoRefreshToken: false,
235
+ detectSessionInUrl: false
236
+ }
237
+ });
185
238
  }
186
239
 
187
240
  //#endregion
@@ -190,32 +243,44 @@ function createAdminClient(env, keyName) {
190
243
  * Creates a Supabase client scoped to the caller's context.
191
244
  *
192
245
  * Configured with a publishable key and (optionally) the caller's JWT,
193
- * so Row-Level Security policies apply. Session persistence is disabled
194
- * (stateless, one client per request).
246
+ * so Row-Level Security policies apply. Stateless one client per request.
195
247
  *
196
- * @param token - The caller's JWT, or `null` for anonymous access.
197
- * @param env - Optional environment overrides (passed through to {@link resolveEnv}).
198
- * @param keyName - Name of the publishable key to use. Falls back to `"default"`, then first available.
199
- * @returns A configured {@link SupabaseClient} with RLS enforced.
200
248
  * @throws {@link EnvError} If `SUPABASE_URL` is missing or the specified publishable key is not found.
201
249
  *
202
250
  * @example
203
251
  * ```ts
204
252
  * const { data: auth } = await verifyAuth(request, { allow: 'user' })
205
- * const supabase = createContextClient(auth.token)
253
+ * const supabase = createContextClient({
254
+ * auth: { token: auth.token, keyName: auth.keyName },
255
+ * })
206
256
  * const { data } = await supabase.rpc('get_my_items')
207
257
  * ```
208
258
  */
209
- function createContextClient(token, env, keyName) {
210
- const { data: resolved, error } = resolveEnv(env);
259
+ function createContextClient(options) {
260
+ const { data: resolved, error } = resolveEnv(options?.env);
211
261
  if (error) throw error;
262
+ const token = options?.auth?.token;
263
+ const keyName = options?.auth?.keyName;
264
+ const supabaseOptions = options?.supabaseOptions;
212
265
  const name = keyName ?? "default";
213
266
  const keys = resolved.publishableKeys;
214
267
  const anonKey = keys[name] ?? (keyName == null ? Object.values(keys)[0] : void 0);
215
- if (!anonKey) throw new EnvError(name === "default" ? "No default publishable key found. Set SUPABASE_PUBLISHABLE_KEY or include a \"default\" entry in SUPABASE_PUBLISHABLE_KEYS." : `No "${name}" publishable key found. Include a "${name}" entry in SUPABASE_PUBLISHABLE_KEYS.`, "MISSING_PUBLISHABLE_KEY");
268
+ if (!anonKey) throw name === "default" ? Errors[MissingDefaultPublishableKeyError]() : Errors[MissingPublishableKeyError](name);
269
+ const safeHeaders = { ...supabaseOptions?.global?.headers };
270
+ delete safeHeaders.Authorization;
271
+ delete safeHeaders.apikey;
216
272
  return (0, _supabase_supabase_js.createClient)(resolved.url, anonKey, {
217
- global: { headers: token ? { Authorization: `Bearer ${token}` } : {} },
273
+ ...supabaseOptions,
274
+ accessToken: void 0,
275
+ global: {
276
+ ...supabaseOptions?.global,
277
+ headers: {
278
+ ...safeHeaders,
279
+ ...token ? { Authorization: `Bearer ${token}` } : {}
280
+ }
281
+ },
218
282
  auth: {
283
+ ...supabaseOptions?.auth,
219
284
  persistSession: false,
220
285
  autoRefreshToken: false,
221
286
  detectSessionInUrl: false
@@ -424,7 +489,7 @@ async function tryMode(mode, credentials, env) {
424
489
  * allow: ['user', 'public'],
425
490
  * })
426
491
  * if (error) {
427
- * return Response.json({ error: error.message }, { status: error.status })
492
+ * return Response.json({ message: error.message }, { status: error.status })
428
493
  * }
429
494
  * ```
430
495
  */
@@ -444,7 +509,7 @@ async function verifyCredentials(credentials, options) {
444
509
  }
445
510
  return {
446
511
  data: null,
447
- error: new AuthError("Invalid credentials", "INVALID_CREDENTIALS", 401)
512
+ error: Errors[InvalidCredentialsError]()
448
513
  };
449
514
  }
450
515
 
@@ -473,7 +538,7 @@ async function verifyCredentials(credentials, options) {
473
538
  * })
474
539
  *
475
540
  * if (error) {
476
- * return Response.json({ error: error.message }, { status: error.status })
541
+ * return Response.json({ message: error.message }, { status: error.status })
477
542
  * }
478
543
  *
479
544
  * console.log(auth.userClaims!.id) // "d0f1a2b3-..."
@@ -490,12 +555,72 @@ Object.defineProperty(exports, 'AuthError', {
490
555
  return AuthError;
491
556
  }
492
557
  });
558
+ Object.defineProperty(exports, 'AuthGenericError', {
559
+ enumerable: true,
560
+ get: function () {
561
+ return AuthGenericError;
562
+ }
563
+ });
564
+ Object.defineProperty(exports, 'CreateSupabaseClientError', {
565
+ enumerable: true,
566
+ get: function () {
567
+ return CreateSupabaseClientError;
568
+ }
569
+ });
493
570
  Object.defineProperty(exports, 'EnvError', {
494
571
  enumerable: true,
495
572
  get: function () {
496
573
  return EnvError;
497
574
  }
498
575
  });
576
+ Object.defineProperty(exports, 'EnvGenericError', {
577
+ enumerable: true,
578
+ get: function () {
579
+ return EnvGenericError;
580
+ }
581
+ });
582
+ Object.defineProperty(exports, 'Errors', {
583
+ enumerable: true,
584
+ get: function () {
585
+ return Errors;
586
+ }
587
+ });
588
+ Object.defineProperty(exports, 'InvalidCredentialsError', {
589
+ enumerable: true,
590
+ get: function () {
591
+ return InvalidCredentialsError;
592
+ }
593
+ });
594
+ Object.defineProperty(exports, 'MissingDefaultPublishableKeyError', {
595
+ enumerable: true,
596
+ get: function () {
597
+ return MissingDefaultPublishableKeyError;
598
+ }
599
+ });
600
+ Object.defineProperty(exports, 'MissingDefaultSecretKeyError', {
601
+ enumerable: true,
602
+ get: function () {
603
+ return MissingDefaultSecretKeyError;
604
+ }
605
+ });
606
+ Object.defineProperty(exports, 'MissingPublishableKeyError', {
607
+ enumerable: true,
608
+ get: function () {
609
+ return MissingPublishableKeyError;
610
+ }
611
+ });
612
+ Object.defineProperty(exports, 'MissingSecretKeyError', {
613
+ enumerable: true,
614
+ get: function () {
615
+ return MissingSecretKeyError;
616
+ }
617
+ });
618
+ Object.defineProperty(exports, 'MissingSupabaseURLError', {
619
+ enumerable: true,
620
+ get: function () {
621
+ return MissingSupabaseURLError;
622
+ }
623
+ });
499
624
  Object.defineProperty(exports, 'createAdminClient', {
500
625
  enumerable: true,
501
626
  get: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supabase/server",
3
- "version": "0.1.1-rc.26",
3
+ "version": "0.1.1-rc.28",
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",