@floatingpixels/supabase-nuxt 0.5.12 → 0.6.0
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 +4 -0
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/runtime/composables/useSupabaseUser.d.ts +13 -2
- package/dist/runtime/composables/useSupabaseUser.js +44 -2
- package/dist/runtime/plugins/middleware-auth-redirect.js +1 -1
- package/dist/runtime/plugins/supabase.client.js +6 -2
- package/dist/runtime/plugins/supabase.server.js +9 -3
- package/dist/runtime/server/services/supabaseServerClient.js +10 -3
- package/dist/runtime/server/services/supabaseServiceRole.js +10 -3
- package/package.json +19 -19
package/README.md
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
Checkout the [Nuxt 3](https://v3.nuxtjs.org) documentation and [Supabase](https://supabase.com) to learn more.
|
|
13
13
|
|
|
14
|
+
## Testing
|
|
15
|
+
|
|
16
|
+
Test setup and command behavior are documented in [docs/testing.md](./docs/testing.md).
|
|
17
|
+
|
|
14
18
|
## Installation
|
|
15
19
|
|
|
16
20
|
Add `@floatingpixels/supabase-nuxt` dev dependency to your project:
|
package/dist/module.d.mts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { NitroConfig, HookResult } from '@nuxt/schema';
|
|
2
3
|
import { SupabaseClientOptions } from '@supabase/supabase-js';
|
|
3
4
|
|
|
4
5
|
declare module '@nuxt/schema' {
|
|
6
|
+
interface NuxtHooks {
|
|
7
|
+
'nitro:config': (nitroConfig: NitroConfig) => HookResult
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
interface RuntimeConfig {
|
|
6
11
|
supabase: {
|
|
7
12
|
serviceRoleKey?: string
|
package/dist/module.json
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
import type { User, AuthError } from '@supabase/supabase-js';
|
|
2
|
+
export interface SupabaseAuthUser {
|
|
3
|
+
id: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
aud?: string;
|
|
8
|
+
app_metadata: User['app_metadata'];
|
|
9
|
+
user_metadata: User['user_metadata'];
|
|
10
|
+
claims: Record<string, unknown>;
|
|
11
|
+
}
|
|
1
12
|
export declare const useSupabaseUser: () => Promise<{
|
|
2
|
-
data:
|
|
3
|
-
error:
|
|
13
|
+
data: SupabaseAuthUser | null;
|
|
14
|
+
error: AuthError | null;
|
|
4
15
|
}>;
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
import { useSupabaseClient } from "./useSupabaseClient.js";
|
|
2
|
+
const isRecord = (value) => {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
};
|
|
5
|
+
const normalizeUser = ({
|
|
6
|
+
claims,
|
|
7
|
+
user
|
|
8
|
+
}) => {
|
|
9
|
+
const claimUserId = typeof claims.sub === "string" ? claims.sub : void 0;
|
|
10
|
+
const userId = user?.id ?? claimUserId;
|
|
11
|
+
if (!userId) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const claimAppMetadata = isRecord(claims.app_metadata) ? claims.app_metadata : {};
|
|
15
|
+
const claimUserMetadata = isRecord(claims.user_metadata) ? claims.user_metadata : {};
|
|
16
|
+
return {
|
|
17
|
+
id: userId,
|
|
18
|
+
email: user?.email ?? (typeof claims.email === "string" ? claims.email : void 0),
|
|
19
|
+
phone: user?.phone ?? (typeof claims.phone === "string" ? claims.phone : void 0),
|
|
20
|
+
role: user?.role ?? (typeof claims.role === "string" ? claims.role : void 0),
|
|
21
|
+
aud: user?.aud ?? (typeof claims.aud === "string" ? claims.aud : void 0),
|
|
22
|
+
app_metadata: user?.app_metadata ?? claimAppMetadata,
|
|
23
|
+
user_metadata: user?.user_metadata ?? claimUserMetadata,
|
|
24
|
+
claims
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const needsUserFallback = (claims) => {
|
|
28
|
+
return !isRecord(claims.app_metadata) || !isRecord(claims.user_metadata) || typeof claims.email !== "string";
|
|
29
|
+
};
|
|
2
30
|
export const useSupabaseUser = async () => {
|
|
3
31
|
const supabase = useSupabaseClient();
|
|
4
|
-
const { data, error } = await supabase.auth.getClaims();
|
|
5
|
-
|
|
32
|
+
const { data: claimsData, error: claimsError } = await supabase.auth.getClaims();
|
|
33
|
+
if (claimsError) {
|
|
34
|
+
return { data: null, error: claimsError };
|
|
35
|
+
}
|
|
36
|
+
const claims = claimsData?.claims;
|
|
37
|
+
if (!claims || !isRecord(claims)) {
|
|
38
|
+
return { data: null, error: null };
|
|
39
|
+
}
|
|
40
|
+
if (!needsUserFallback(claims)) {
|
|
41
|
+
return { data: normalizeUser({ claims }), error: null };
|
|
42
|
+
}
|
|
43
|
+
const { data: userData, error: userError } = await supabase.auth.getUser();
|
|
44
|
+
if (userError) {
|
|
45
|
+
return { data: null, error: userError };
|
|
46
|
+
}
|
|
47
|
+
return { data: normalizeUser({ claims, user: userData.user }), error: null };
|
|
6
48
|
};
|
|
@@ -14,7 +14,7 @@ export default defineNuxtPlugin({
|
|
|
14
14
|
if (isExcluded) return;
|
|
15
15
|
const { data: user, error } = await useSupabaseUser();
|
|
16
16
|
if (error || !user) {
|
|
17
|
-
return navigateTo("/login", { redirectCode: 302 });
|
|
17
|
+
return navigateTo(login || "/login", { redirectCode: 302 });
|
|
18
18
|
}
|
|
19
19
|
}),
|
|
20
20
|
{ global: true }
|
|
@@ -4,8 +4,12 @@ export default defineNuxtPlugin({
|
|
|
4
4
|
name: "supabase",
|
|
5
5
|
enforce: "pre",
|
|
6
6
|
async setup() {
|
|
7
|
-
const { url, publishableKey } = useRuntimeConfig().public.supabase;
|
|
8
|
-
const supabaseBrowserClient = createBrowserClient(
|
|
7
|
+
const { url, publishableKey, clientOptions } = useRuntimeConfig().public.supabase;
|
|
8
|
+
const supabaseBrowserClient = createBrowserClient(
|
|
9
|
+
url,
|
|
10
|
+
publishableKey,
|
|
11
|
+
clientOptions
|
|
12
|
+
);
|
|
9
13
|
return {
|
|
10
14
|
provide: {
|
|
11
15
|
supabase: {
|
|
@@ -5,12 +5,13 @@ export default defineNuxtPlugin({
|
|
|
5
5
|
name: "supabase",
|
|
6
6
|
enforce: "pre",
|
|
7
7
|
async setup() {
|
|
8
|
-
const { url, publishableKey } = useRuntimeConfig().public.supabase;
|
|
8
|
+
const { url, publishableKey, clientOptions } = useRuntimeConfig().public.supabase;
|
|
9
9
|
const event = useRequestEvent();
|
|
10
10
|
if (!event) {
|
|
11
11
|
throw new Error("No request event found");
|
|
12
12
|
}
|
|
13
|
-
const
|
|
13
|
+
const serverClientOptions = {
|
|
14
|
+
...clientOptions,
|
|
14
15
|
cookies: {
|
|
15
16
|
getAll: () => {
|
|
16
17
|
const cookie_records = parseCookies(event);
|
|
@@ -29,7 +30,12 @@ export default defineNuxtPlugin({
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
}
|
|
33
|
+
};
|
|
34
|
+
const supabaseServerClient = createServerClient(
|
|
35
|
+
url,
|
|
36
|
+
publishableKey,
|
|
37
|
+
serverClientOptions
|
|
38
|
+
);
|
|
33
39
|
return {
|
|
34
40
|
provide: {
|
|
35
41
|
supabase: {
|
|
@@ -3,11 +3,13 @@ import { setCookie, parseCookies } from "h3";
|
|
|
3
3
|
import { useRuntimeConfig } from "#imports";
|
|
4
4
|
export const supabaseServerClient = async (event) => {
|
|
5
5
|
const {
|
|
6
|
-
supabase: { url, publishableKey }
|
|
6
|
+
supabase: { url, publishableKey, clientOptions }
|
|
7
7
|
} = useRuntimeConfig().public;
|
|
8
8
|
let supabaseClient = event.context._supabaseClient;
|
|
9
|
+
const createTypedServerClient = createServerClient;
|
|
9
10
|
if (!supabaseClient) {
|
|
10
|
-
|
|
11
|
+
const serverClientOptions = {
|
|
12
|
+
...clientOptions,
|
|
11
13
|
cookies: {
|
|
12
14
|
getAll: () => {
|
|
13
15
|
const cookie_records = parseCookies(event);
|
|
@@ -26,7 +28,12 @@ export const supabaseServerClient = async (event) => {
|
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
}
|
|
31
|
+
};
|
|
32
|
+
supabaseClient = createTypedServerClient(
|
|
33
|
+
url,
|
|
34
|
+
publishableKey,
|
|
35
|
+
serverClientOptions
|
|
36
|
+
);
|
|
30
37
|
event.context._supabaseClient = supabaseClient;
|
|
31
38
|
}
|
|
32
39
|
return supabaseClient;
|
|
@@ -5,15 +5,17 @@ export const supabaseServiceRole = async (event) => {
|
|
|
5
5
|
const {
|
|
6
6
|
supabase: { serviceRoleKey },
|
|
7
7
|
public: {
|
|
8
|
-
supabase: { url }
|
|
8
|
+
supabase: { url, clientOptions }
|
|
9
9
|
}
|
|
10
10
|
} = useRuntimeConfig();
|
|
11
11
|
if (!serviceRoleKey) {
|
|
12
12
|
throw new Error("Missing `SUPABASE_SERVICE_ROLE_KEY` in `.env`");
|
|
13
13
|
}
|
|
14
14
|
let supabaseClient = event.context._supabaseServiceRole;
|
|
15
|
+
const createTypedServerClient = createServerClient;
|
|
15
16
|
if (!supabaseClient) {
|
|
16
|
-
|
|
17
|
+
const serverClientOptions = {
|
|
18
|
+
...clientOptions,
|
|
17
19
|
cookies: {
|
|
18
20
|
getAll: () => {
|
|
19
21
|
const cookie_records = parseCookies(event);
|
|
@@ -32,7 +34,12 @@ export const supabaseServiceRole = async (event) => {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
}
|
|
37
|
+
};
|
|
38
|
+
supabaseClient = createTypedServerClient(
|
|
39
|
+
url,
|
|
40
|
+
serviceRoleKey,
|
|
41
|
+
serverClientOptions
|
|
42
|
+
);
|
|
36
43
|
event.context._supabaseServiceRole = supabaseClient;
|
|
37
44
|
}
|
|
38
45
|
return supabaseClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floatingpixels/supabase-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Supabase module for Nuxt",
|
|
5
5
|
"repository": "floatingpixels/supabase-nuxt",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
39
39
|
"release": "pnpm run lint && pnpm run prepack && release-it",
|
|
40
40
|
"lint": "eslint .",
|
|
41
|
-
"test": "nuxi prepare playground && pnpm vitest",
|
|
41
|
+
"test": "nuxi prepare playground && pnpm exec tsc --noEmit --pretty false && pnpm vitest",
|
|
42
42
|
"test:watch": "vitest watch",
|
|
43
43
|
"test:pw": "playwright test test/playwright/",
|
|
44
44
|
"test:e2e": "pnpm run --silent db:start && pnpm run --silent db:reset && pnpm run --silent test:pw && pnpm run --silent db:stop",
|
|
@@ -58,37 +58,37 @@
|
|
|
58
58
|
"cleanup": "pnpm nuxi cleanup && rm -rf node_modules pnpm-lock.yaml && pnpm i"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@supabase/ssr": "^0.
|
|
62
|
-
"@supabase/supabase-js": "^2.
|
|
61
|
+
"@supabase/ssr": "^0.9.0",
|
|
62
|
+
"@supabase/supabase-js": "^2.99.1",
|
|
63
63
|
"cookie": "^1.1.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@nuxt/devtools": "3.
|
|
67
|
-
"@nuxt/eslint-config": "^1.
|
|
68
|
-
"@nuxt/kit": "^4.
|
|
66
|
+
"@nuxt/devtools": "3.2.3",
|
|
67
|
+
"@nuxt/eslint-config": "^1.15.2",
|
|
68
|
+
"@nuxt/kit": "^4.4.2",
|
|
69
69
|
"@nuxt/module-builder": "^1.0.2",
|
|
70
|
-
"@nuxt/schema": "^4.
|
|
71
|
-
"@nuxt/test-utils": "
|
|
72
|
-
"@playwright/test": "^1.58.
|
|
70
|
+
"@nuxt/schema": "^4.4.2",
|
|
71
|
+
"@nuxt/test-utils": "4.0.0",
|
|
72
|
+
"@playwright/test": "^1.58.2",
|
|
73
73
|
"@snaplet/copycat": "^6.0.0",
|
|
74
74
|
"@snaplet/seed": "^0.98.0",
|
|
75
75
|
"@types/eslint-config-prettier": "^6.11.3",
|
|
76
|
-
"@types/node": "^25.
|
|
77
|
-
"@vitest/coverage-v8": "4.0
|
|
76
|
+
"@types/node": "^25.5.0",
|
|
77
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
78
78
|
"@vue/test-utils": "^2.4.6",
|
|
79
79
|
"changelogen": "^0.6.2",
|
|
80
|
-
"eslint": "^
|
|
80
|
+
"eslint": "^10.0.3",
|
|
81
81
|
"eslint-config-prettier": "^10.1.8",
|
|
82
|
-
"happy-dom": "^20.4
|
|
83
|
-
"nuxt": "^4.
|
|
84
|
-
"playwright-core": "^1.58.
|
|
82
|
+
"happy-dom": "^20.8.4",
|
|
83
|
+
"nuxt": "^4.4.2",
|
|
84
|
+
"playwright-core": "^1.58.2",
|
|
85
85
|
"postgres": "^3.4.8",
|
|
86
86
|
"prettier": "^3.8.1",
|
|
87
87
|
"release-it": "^19.2.4",
|
|
88
|
-
"supabase": "^2.
|
|
88
|
+
"supabase": "^2.78.1",
|
|
89
89
|
"typescript": "5.9.3",
|
|
90
|
-
"vitest": "^4.0
|
|
91
|
-
"vue-tsc": "^3.2.
|
|
90
|
+
"vitest": "^4.1.0",
|
|
91
|
+
"vue-tsc": "^3.2.5"
|
|
92
92
|
},
|
|
93
93
|
"pnpm": {
|
|
94
94
|
"onlyBuiltDependencies": [
|