next-supa-utils 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/middleware/withSupaAuth.ts","../../src/server/actions/actionWrapper.ts","../../src/shared/utils/error-handler.ts"],"sourcesContent":["import { createServerClient, type CookieOptions } from \"@supabase/ssr\";\nimport { NextResponse, type NextRequest } from \"next/server\";\n\nimport type { SupaAuthConfig } from \"../../types\";\n\n/**\n * Create a Next.js middleware handler that protects routes based on\n * Supabase authentication state.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { withSupaAuth } from \"next-supa-utils/server\";\n *\n * export default withSupaAuth({\n * protectedRoutes: [\"/dashboard\", \"/admin\"],\n * redirectTo: \"/login\",\n * publicRoutes: [\"/admin/login\"],\n * });\n *\n * export const config = {\n * matcher: [\"/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)\"],\n * };\n * ```\n */\nexport function withSupaAuth(config: SupaAuthConfig) {\n const {\n protectedRoutes,\n redirectTo = \"/login\",\n publicRoutes = [],\n onAuthSuccess,\n } = config;\n\n return async function middleware(request: NextRequest): Promise<NextResponse> {\n // ── 1. Create a mutable response so Supabase can set cookies ──\n let response = NextResponse.next({\n request: { headers: request.headers },\n });\n\n const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;\n const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;\n\n if (!supabaseUrl || !supabaseAnonKey) {\n console.error(\n \"[next-supa-utils] Missing NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY environment variables.\",\n );\n return response;\n }\n\n // ── 2. Initialize server client with middleware cookie helpers ─\n const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {\n cookies: {\n getAll() {\n return request.cookies.getAll();\n },\n setAll(cookiesToSet: { name: string; value: string; options?: CookieOptions }[]) {\n // Forward cookies to the request so downstream server\n // components can read the updated session.\n cookiesToSet.forEach(({ name, value }) => {\n request.cookies.set(name, value);\n });\n\n // Re-create the response so it carries the updated request.\n response = NextResponse.next({ request });\n\n // Set cookies on the outgoing response so the browser\n // stores the refreshed tokens.\n cookiesToSet.forEach(({ name, value, options }) => {\n response.cookies.set(name, value, options);\n });\n },\n },\n });\n\n // ── 3. Refresh session (required to keep tokens alive) ────────\n const {\n data: { user },\n } = await supabase.auth.getUser();\n\n const { pathname } = request.nextUrl;\n\n // ── 4. Check if current path matches a public override ────────\n const isPublicRoute = publicRoutes.some((route) =>\n pathname.startsWith(route),\n );\n\n if (isPublicRoute) {\n return response;\n }\n\n // ── 5. Check if current path requires authentication ──────────\n const isProtectedRoute = protectedRoutes.some((route) =>\n pathname.startsWith(route),\n );\n\n if (isProtectedRoute && !user) {\n const loginUrl = new URL(redirectTo, request.url);\n // Preserve the originally-requested URL so the app can redirect\n // back after login.\n loginUrl.searchParams.set(\"next\", pathname);\n return NextResponse.redirect(loginUrl);\n }\n\n // ── 6. Optional success callback ──────────────────────────────\n if (user && onAuthSuccess) {\n await onAuthSuccess({ id: user.id, email: user.email ?? undefined });\n }\n\n return response;\n };\n}\n","import { createServerClient, type CookieOptions } from \"@supabase/ssr\";\nimport { cookies } from \"next/headers\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\nimport type { ActionResponse, SupaError } from \"../../types\";\nimport { handleSupaError } from \"../../shared/utils/error-handler\";\n\n/**\n * Create a type-safe Server Action that automatically:\n * 1. Initialises a Supabase server client (with cookies)\n * 2. Wraps execution in try/catch\n * 3. Returns a standardised `{ data, error }` response\n *\n * @example\n * ```ts\n * // app/actions/profile.ts\n * \"use server\";\n * import { createAction } from \"next-supa-utils/server\";\n *\n * export const getProfile = createAction(async (supabase, userId: string) => {\n * const { data, error } = await supabase\n * .from(\"profiles\")\n * .select(\"*\")\n * .eq(\"id\", userId)\n * .single();\n *\n * if (error) throw error;\n * return data;\n * });\n *\n * // Usage in a Server Component or Client Component:\n * const result = await getProfile(\"user-uuid\");\n * if (result.error) { ... }\n * ```\n */\nexport function createAction<TArgs extends unknown[], TResult>(\n fn: (supabase: SupabaseClient, ...args: TArgs) => Promise<TResult>,\n): (...args: TArgs) => Promise<ActionResponse<TResult>> {\n return async (...args: TArgs): Promise<ActionResponse<TResult>> => {\n try {\n const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;\n const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;\n\n if (!supabaseUrl || !supabaseAnonKey) {\n return {\n data: null,\n error: {\n message:\n \"Missing NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY environment variables.\",\n code: \"CONFIG_ERROR\",\n },\n };\n }\n\n const cookieStore = await cookies();\n\n const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {\n cookies: {\n getAll() {\n return cookieStore.getAll();\n },\n setAll(cookiesToSet: { name: string; value: string; options?: CookieOptions }[]) {\n try {\n cookiesToSet.forEach(({ name, value, options }) => {\n cookieStore.set(name, value, options);\n });\n } catch {\n // `cookies().set()` throws when called from a Server Component.\n // In that context we only need read access — the middleware\n // handles token refresh.\n }\n },\n },\n });\n\n const data = await fn(supabase, ...args);\n\n return { data, error: null };\n } catch (caught: unknown) {\n const error: SupaError = handleSupaError(caught);\n return { data: null, error };\n }\n };\n}\n","import type { SupaError } from \"../../types\";\n\n/**\n * Normalize any thrown value into a consistent `SupaError` shape.\n *\n * Handles:\n * - Supabase `AuthError` / `PostgrestError` (has `.message` and optional `.code` / `.status`)\n * - Standard `Error` instances\n * - Plain strings\n * - Unknown values (fallback)\n */\nexport function handleSupaError(error: unknown): SupaError {\n // ── Supabase errors & standard Error instances ──────────────────\n if (error instanceof Error) {\n const record = error as unknown as Record<string, unknown>;\n return {\n message: error.message,\n code: typeof record.code === \"string\" ? record.code : undefined,\n status: typeof record.status === \"number\" ? record.status : undefined,\n };\n }\n\n // ── Plain object with a message property ────────────────────────\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n ) {\n const err = error as Record<string, unknown>;\n return {\n message: err.message as string,\n code: typeof err.code === \"string\" ? err.code : undefined,\n status: typeof err.status === \"number\" ? err.status : undefined,\n };\n }\n\n // ── String ──────────────────────────────────────────────────────\n if (typeof error === \"string\") {\n return { message: error };\n }\n\n // ── Fallback ────────────────────────────────────────────────────\n return { message: \"An unknown error occurred\" };\n}\n"],"mappings":"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","names":["createServerClient","createServerClient"]}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/shared/index.ts
21
+ var shared_exports = {};
22
+ __export(shared_exports, {
23
+ handleSupaError: () => handleSupaError
24
+ });
25
+ module.exports = __toCommonJS(shared_exports);
26
+
27
+ // src/shared/utils/error-handler.ts
28
+ function handleSupaError(error) {
29
+ if (error instanceof Error) {
30
+ const record = error;
31
+ return {
32
+ message: error.message,
33
+ code: typeof record.code === "string" ? record.code : void 0,
34
+ status: typeof record.status === "number" ? record.status : void 0
35
+ };
36
+ }
37
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
38
+ const err = error;
39
+ return {
40
+ message: err.message,
41
+ code: typeof err.code === "string" ? err.code : void 0,
42
+ status: typeof err.status === "number" ? err.status : void 0
43
+ };
44
+ }
45
+ if (typeof error === "string") {
46
+ return { message: error };
47
+ }
48
+ return { message: "An unknown error occurred" };
49
+ }
50
+ // Annotate the CommonJS export names for ESM import in node:
51
+ 0 && (module.exports = {
52
+ handleSupaError
53
+ });
54
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shared/index.ts","../../src/shared/utils/error-handler.ts"],"sourcesContent":["// ── Shared entry point ──────────────────────────────────────────────\n// Re-exports types and utilities used across client & server modules.\n\nexport { handleSupaError } from \"./utils/error-handler\";\n\nexport type {\n SupaError,\n ActionResponse,\n SupaAuthConfig,\n UseSupaUserReturn,\n UseSupaSessionReturn,\n} from \"../types\";\n","import type { SupaError } from \"../../types\";\n\n/**\n * Normalize any thrown value into a consistent `SupaError` shape.\n *\n * Handles:\n * - Supabase `AuthError` / `PostgrestError` (has `.message` and optional `.code` / `.status`)\n * - Standard `Error` instances\n * - Plain strings\n * - Unknown values (fallback)\n */\nexport function handleSupaError(error: unknown): SupaError {\n // ── Supabase errors & standard Error instances ──────────────────\n if (error instanceof Error) {\n const record = error as unknown as Record<string, unknown>;\n return {\n message: error.message,\n code: typeof record.code === \"string\" ? record.code : undefined,\n status: typeof record.status === \"number\" ? record.status : undefined,\n };\n }\n\n // ── Plain object with a message property ────────────────────────\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n ) {\n const err = error as Record<string, unknown>;\n return {\n message: err.message as string,\n code: typeof err.code === \"string\" ? err.code : undefined,\n status: typeof err.status === \"number\" ? err.status : undefined,\n };\n }\n\n // ── String ──────────────────────────────────────────────────────\n if (typeof error === \"string\") {\n return { message: error };\n }\n\n // ── Fallback ────────────────────────────────────────────────────\n return { message: \"An unknown error occurred\" };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWO,SAAS,gBAAgB,OAA2B;AAEzD,MAAI,iBAAiB,OAAO;AAC1B,UAAM,SAAS;AACf,WAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,MACtD,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAAA,IAC9D;AAAA,EACF;AAGA,MACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,OAAQ,MAAkC,YAAY,UACtD;AACA,UAAM,MAAM;AACZ,WAAO;AAAA,MACL,SAAS,IAAI;AAAA,MACb,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAAA,MAChD,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAAA,IACxD;AAAA,EACF;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAGA,SAAO,EAAE,SAAS,4BAA4B;AAChD;","names":[]}
@@ -0,0 +1,67 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+
3
+ /** Standardized error shape returned by all next-supa-utils helpers. */
4
+ interface SupaError {
5
+ message: string;
6
+ code?: string;
7
+ status?: number;
8
+ }
9
+ /** Discriminated success/failure response returned by `createAction`. */
10
+ type ActionResponse<T> = {
11
+ data: T;
12
+ error: null;
13
+ } | {
14
+ data: null;
15
+ error: SupaError;
16
+ };
17
+ interface SupaAuthConfig {
18
+ /**
19
+ * Route prefixes that require an authenticated user.
20
+ * Supports simple prefix matching.
21
+ *
22
+ * @example ["/dashboard", "/admin", "/settings"]
23
+ */
24
+ protectedRoutes: string[];
25
+ /**
26
+ * Where to redirect unauthenticated users.
27
+ * @default "/login"
28
+ */
29
+ redirectTo?: string;
30
+ /**
31
+ * Routes that are always public, even if they match a protected prefix.
32
+ *
33
+ * @example ["/admin/login"]
34
+ */
35
+ publicRoutes?: string[];
36
+ /**
37
+ * Optional callback invoked after session refresh,
38
+ * before the redirect decision. Useful for custom logging or headers.
39
+ */
40
+ onAuthSuccess?: (user: {
41
+ id: string;
42
+ email?: string;
43
+ }) => void | Promise<void>;
44
+ }
45
+ interface UseSupaUserReturn {
46
+ user: Awaited<ReturnType<SupabaseClient["auth"]["getUser"]>>["data"]["user"];
47
+ loading: boolean;
48
+ error: SupaError | null;
49
+ }
50
+ interface UseSupaSessionReturn {
51
+ session: Awaited<ReturnType<SupabaseClient["auth"]["getSession"]>>["data"]["session"];
52
+ loading: boolean;
53
+ error: SupaError | null;
54
+ }
55
+
56
+ /**
57
+ * Normalize any thrown value into a consistent `SupaError` shape.
58
+ *
59
+ * Handles:
60
+ * - Supabase `AuthError` / `PostgrestError` (has `.message` and optional `.code` / `.status`)
61
+ * - Standard `Error` instances
62
+ * - Plain strings
63
+ * - Unknown values (fallback)
64
+ */
65
+ declare function handleSupaError(error: unknown): SupaError;
66
+
67
+ export { type ActionResponse, type SupaAuthConfig, type SupaError, type UseSupaSessionReturn, type UseSupaUserReturn, handleSupaError };
@@ -0,0 +1,67 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+
3
+ /** Standardized error shape returned by all next-supa-utils helpers. */
4
+ interface SupaError {
5
+ message: string;
6
+ code?: string;
7
+ status?: number;
8
+ }
9
+ /** Discriminated success/failure response returned by `createAction`. */
10
+ type ActionResponse<T> = {
11
+ data: T;
12
+ error: null;
13
+ } | {
14
+ data: null;
15
+ error: SupaError;
16
+ };
17
+ interface SupaAuthConfig {
18
+ /**
19
+ * Route prefixes that require an authenticated user.
20
+ * Supports simple prefix matching.
21
+ *
22
+ * @example ["/dashboard", "/admin", "/settings"]
23
+ */
24
+ protectedRoutes: string[];
25
+ /**
26
+ * Where to redirect unauthenticated users.
27
+ * @default "/login"
28
+ */
29
+ redirectTo?: string;
30
+ /**
31
+ * Routes that are always public, even if they match a protected prefix.
32
+ *
33
+ * @example ["/admin/login"]
34
+ */
35
+ publicRoutes?: string[];
36
+ /**
37
+ * Optional callback invoked after session refresh,
38
+ * before the redirect decision. Useful for custom logging or headers.
39
+ */
40
+ onAuthSuccess?: (user: {
41
+ id: string;
42
+ email?: string;
43
+ }) => void | Promise<void>;
44
+ }
45
+ interface UseSupaUserReturn {
46
+ user: Awaited<ReturnType<SupabaseClient["auth"]["getUser"]>>["data"]["user"];
47
+ loading: boolean;
48
+ error: SupaError | null;
49
+ }
50
+ interface UseSupaSessionReturn {
51
+ session: Awaited<ReturnType<SupabaseClient["auth"]["getSession"]>>["data"]["session"];
52
+ loading: boolean;
53
+ error: SupaError | null;
54
+ }
55
+
56
+ /**
57
+ * Normalize any thrown value into a consistent `SupaError` shape.
58
+ *
59
+ * Handles:
60
+ * - Supabase `AuthError` / `PostgrestError` (has `.message` and optional `.code` / `.status`)
61
+ * - Standard `Error` instances
62
+ * - Plain strings
63
+ * - Unknown values (fallback)
64
+ */
65
+ declare function handleSupaError(error: unknown): SupaError;
66
+
67
+ export { type ActionResponse, type SupaAuthConfig, type SupaError, type UseSupaSessionReturn, type UseSupaUserReturn, handleSupaError };
@@ -0,0 +1,27 @@
1
+ // src/shared/utils/error-handler.ts
2
+ function handleSupaError(error) {
3
+ if (error instanceof Error) {
4
+ const record = error;
5
+ return {
6
+ message: error.message,
7
+ code: typeof record.code === "string" ? record.code : void 0,
8
+ status: typeof record.status === "number" ? record.status : void 0
9
+ };
10
+ }
11
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
12
+ const err = error;
13
+ return {
14
+ message: err.message,
15
+ code: typeof err.code === "string" ? err.code : void 0,
16
+ status: typeof err.status === "number" ? err.status : void 0
17
+ };
18
+ }
19
+ if (typeof error === "string") {
20
+ return { message: error };
21
+ }
22
+ return { message: "An unknown error occurred" };
23
+ }
24
+ export {
25
+ handleSupaError
26
+ };
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shared/utils/error-handler.ts"],"sourcesContent":["import type { SupaError } from \"../../types\";\n\n/**\n * Normalize any thrown value into a consistent `SupaError` shape.\n *\n * Handles:\n * - Supabase `AuthError` / `PostgrestError` (has `.message` and optional `.code` / `.status`)\n * - Standard `Error` instances\n * - Plain strings\n * - Unknown values (fallback)\n */\nexport function handleSupaError(error: unknown): SupaError {\n // ── Supabase errors & standard Error instances ──────────────────\n if (error instanceof Error) {\n const record = error as unknown as Record<string, unknown>;\n return {\n message: error.message,\n code: typeof record.code === \"string\" ? record.code : undefined,\n status: typeof record.status === \"number\" ? record.status : undefined,\n };\n }\n\n // ── Plain object with a message property ────────────────────────\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n ) {\n const err = error as Record<string, unknown>;\n return {\n message: err.message as string,\n code: typeof err.code === \"string\" ? err.code : undefined,\n status: typeof err.status === \"number\" ? err.status : undefined,\n };\n }\n\n // ── String ──────────────────────────────────────────────────────\n if (typeof error === \"string\") {\n return { message: error };\n }\n\n // ── Fallback ────────────────────────────────────────────────────\n return { message: \"An unknown error occurred\" };\n}\n"],"mappings":";AAWO,SAAS,gBAAgB,OAA2B;AAEzD,MAAI,iBAAiB,OAAO;AAC1B,UAAM,SAAS;AACf,WAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,MACtD,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAAA,IAC9D;AAAA,EACF;AAGA,MACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,OAAQ,MAAkC,YAAY,UACtD;AACA,UAAM,MAAM;AACZ,WAAO;AAAA,MACL,SAAS,IAAI;AAAA,MACb,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAAA,MAChD,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAAA,IACxD;AAAA,EACF;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAGA,SAAO,EAAE,SAAS,4BAA4B;AAChD;","names":[]}
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "next-supa-utils",
3
+ "version": "0.1.0",
4
+ "description": "Eliminate Supabase boilerplate in Next.js App Router — hooks, middleware helpers, and server action wrappers.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/shared/index.js",
8
+ "module": "./dist/shared/index.mjs",
9
+ "types": "./dist/shared/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/shared/index.d.mts",
14
+ "default": "./dist/shared/index.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/shared/index.d.ts",
18
+ "default": "./dist/shared/index.js"
19
+ }
20
+ },
21
+ "./client": {
22
+ "import": {
23
+ "types": "./dist/client/index.d.mts",
24
+ "default": "./dist/client/index.mjs"
25
+ },
26
+ "require": {
27
+ "types": "./dist/client/index.d.ts",
28
+ "default": "./dist/client/index.js"
29
+ }
30
+ },
31
+ "./server": {
32
+ "import": {
33
+ "types": "./dist/server/index.d.mts",
34
+ "default": "./dist/server/index.mjs"
35
+ },
36
+ "require": {
37
+ "types": "./dist/server/index.d.ts",
38
+ "default": "./dist/server/index.js"
39
+ }
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit",
50
+ "clean": "rimraf dist",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "peerDependencies": {
54
+ "react": ">=18",
55
+ "next": ">=14",
56
+ "@supabase/supabase-js": "^2.0.0",
57
+ "@supabase/ssr": ">=0.5.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "react": {
61
+ "optional": false
62
+ },
63
+ "next": {
64
+ "optional": false
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@supabase/ssr": "^0.5.2",
69
+ "@supabase/supabase-js": "^2.49.1",
70
+ "@types/node": "^22.13.10",
71
+ "@types/react": "^19.0.10",
72
+ "next": "^15.2.3",
73
+ "react": "^19.0.0",
74
+ "react-dom": "^19.0.0",
75
+ "tsup": "^8.4.0",
76
+ "typescript": "^5.8.2"
77
+ },
78
+ "keywords": [
79
+ "next.js",
80
+ "supabase",
81
+ "react",
82
+ "hooks",
83
+ "middleware",
84
+ "server-actions",
85
+ "app-router",
86
+ "ssr",
87
+ "typescript"
88
+ ],
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "https://github.com/aryaintarann/next-supa-utils.git"
92
+ },
93
+ "bugs": {
94
+ "url": "https://github.com/aryaintarann/next-supa-utils/issues"
95
+ },
96
+ "engines": {
97
+ "node": ">=18"
98
+ }
99
+ }