nextjs-hasura-auth 0.1.2 → 0.1.3

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/APOLLO.md CHANGED
@@ -1,174 +1,101 @@
1
- # Apollo Client Setup (`APOLLO.md`)
2
-
3
- This document describes the configured Apollo Client instance provided by this package (`lib/apollo.tsx` or similar) for interacting with your Hasura GraphQL API.
4
-
5
- ## Purpose
6
-
7
- The Apollo Client setup provides a unified interface for sending GraphQL queries, mutations, and subscriptions to Hasura, automatically handling authentication based on the provided JWT token or Hasura Admin Secret.
8
-
9
- ## Key Features
10
-
11
- * **Direct Connection:** Connects directly to your Hasura GraphQL endpoint (`NEXT_PUBLIC_HASURA_GRAPHQL_URL`) for HTTP requests and WebSocket endpoint (`NEXT_PUBLIC_HASURA_WS_ENDPOINT` derived from the HTTP URL) for subscriptions.
12
- * **Split Link:** Intelligently routes GraphQL operations:
13
- * **HTTP Requests (Queries/Mutations):** Sent via standard HTTP.
14
- * **WebSocket Connections (Subscriptions):** Uses a WebSocket connection (`graphql-ws`). This requires `ws: true` during client creation and only works client-side.
15
- * **Flexible Authentication:**
16
- * **JWT Token Priority:** If a `token` is provided during client creation, it's sent as a `Bearer` token in the `Authorization` header (for both HTTP and WS).
17
- * **Admin Secret Fallback:** If no `token` is provided, but the `HASURA_ADMIN_SECRET` environment variable is set (or a `secret` is passed explicitly), it's sent in the `x-hasura-admin-secret` header (for both HTTP and WS).
18
- * **Public Access:** If neither a token nor a secret is available, requests are sent without authentication headers.
19
- * **Server-Side Rendering (SSR) / Client-Side Rendering (CSR) Compatibility:** The client can be created and used both on the server and the client.
20
- * **Convenient Provider:** The `createApolloClient` function returns an enhanced client instance that includes a `.Provider` component (`ApolloClientWithProvider`) for easily wrapping parts of your React application.
21
- * **Helper Hook:** Provides `useCreateApolloClient` hook for easily creating memoized client instances in React components, especially useful for integrating with authentication state.
22
-
23
- <details>
24
- <summary>Core Exports & Options (`lib/apollo.tsx`)</summary>
25
-
26
- * `createApolloClient(options: ApolloOptions): ApolloClientWithProvider`: Creates a new Apollo Client instance.
27
- * `options.url`: Hasura GraphQL endpoint URL (defaults to `process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL`).
28
- * `options.ws`: Boolean, enable WebSocket link for subscriptions (defaults to `false`, only works client-side).
29
- * `options.token`: String, JWT token for Bearer authentication.
30
- * `options.secret`: String, Hasura Admin Secret (defaults to `process.env.HASURA_ADMIN_SECRET`).
31
- * `useCreateApolloClient(options: ApolloOptions): ApolloClientWithProvider`: React hook to create a memoized Apollo Client instance. Recreates the client if `options` change.
32
- * `getClient(options?: ApolloOptions): ApolloClient<any>`: Gets or creates a singleton Apollo Client instance. *Note: Using the singleton pattern might be tricky if the authentication token needs to change dynamically. `useCreateApolloClient` is often preferred in React components.*
33
- * `checkConnection(client?): Promise<boolean>`: Sends a simple introspection query to check connectivity to the Hasura endpoint.
34
- * `getJwtSecret(): Uint8Array`: Utility to get the Hasura JWT secret key from `process.env.HASURA_JWT_SECRET` (used internally by other parts of the auth system, not typically needed directly for Apollo client setup).
35
-
36
- </details>
37
-
38
- ## Usage
39
-
40
- ### Client-Side (React Components - Recommended)
41
-
42
- 1. **Ensure `SessionProvider` is set up:** The Apollo client often relies on session data (like a JWT) for authentication. Make sure `next-auth/react`'s `SessionProvider` wraps your application, usually in the root layout.
43
- 2. **Wrap your application/layout with the client's Provider:** Use the `useCreateApolloClient` hook to create a client instance based on the session state and wrap your components using `client.Provider`.
44
-
45
- ```typescript
46
- // Example in app/layout.tsx
47
- 'use client'; // Layout needs to be client-side for providers and hooks
48
-
49
- import { SessionProvider, useSession } from "next-auth/react";
50
- import { useCreateApolloClient } from '@/lib/apollo'; // Adjust path
51
- import { useMemo } from "react";
52
- // Other imports (ThemeProvider, etc.)
53
-
54
- // Wrapper component to manage Apollo client based on session
55
- function ApolloWrapper({ children }: { children: React.ReactNode }) {
56
- const { data: session } = useSession(); // Get session from next-auth
57
-
58
- // Create Apollo Client, passing the Hasura JWT from the session
59
- // The client will be recreated if the session changes
60
- const client = useCreateApolloClient(useMemo(() => ({
61
- // Make sure your next-auth session callback adds the correct
62
- // Hasura-compatible JWT to the session object (e.g., session.accessToken)
63
- token: session?.accessToken, // Pass the Hasura JWT
64
- ws: true // Enable WebSocket support for subscriptions
65
- }), [session]));
66
-
67
- // Use the convenient Provider attached to the client instance
68
- return (
69
- <client.Provider>
70
- {children}
71
- </client.Provider>
72
- );
73
- }
74
-
75
- export default function RootLayout({ children }: { children: React.ReactNode }) {
76
- return (
77
- <html lang="en" suppressHydrationWarning>
78
- <head />
79
- <body>
80
- {/* SessionProvider is required for useSession hook */}
81
- <SessionProvider>
82
- {/* ApolloWrapper provides the Apollo client based on session */}
83
- <ApolloWrapper>
84
- {/* Other providers like ThemeProvider */}
85
- {/* <ThemeProvider ...> */}
86
- {children}
87
- {/* </ThemeProvider> */}
88
- </ApolloWrapper>
89
- </SessionProvider>
90
- </body>
91
- </html>
92
- );
93
- }
94
- ```
95
-
96
- 3. **Use Apollo Hooks in Components:** Import and use hooks like `useQuery`, `useMutation`, or `useSubscription` directly. The client provided by the context will handle authentication automatically based on the token passed during its creation.
97
-
98
- ```typescript
99
- import { useQuery, gql } from '@apollo/client';
100
- // import { Generator } from 'nextjs-hasura-auth'; // Optional: Use with Generator
101
-
102
- function UserProfile() {
103
- // Assume user ID is available, e.g., from session or props
104
- const userId = '...';
105
-
106
- const GET_USER = gql`
107
- query GetUser($userId: uuid!) {
108
- users_by_pk(id: $userId) {
109
- id
110
- name
111
- email
112
- }
113
- }
114
- `;
115
-
116
- // The client from ApolloWrapper's context is used automatically
117
- const { loading, error, data } = useQuery(GET_USER, {
118
- variables: { userId }
119
- });
120
-
121
- // ... render logic ...
122
- }
123
- ```
124
-
125
- ### Server-Side (SSR/SSG/Server Components/API Routes)
126
-
127
- 1. **Create Client Instance:** Use `createApolloClient` directly. You need to decide how to authenticate:
128
- * **Admin Access:** Pass the admin secret (e.g., from environment variables) using the `secret` option if you need privileged access.
129
- * **User Access:** If running in the context of a specific user request, obtain their Hasura JWT (e.g., from session data, request headers) and pass it using the `token` option.
130
-
131
- ```typescript
132
- // Example in an API Route or Server Component
133
- import { createApolloClient } from '@/lib/apollo'; // Adjust path
134
- import { gql } from '@apollo/client';
135
- // import { getToken } from "next-auth/jwt"
136
-
137
- async function getUserDataOnServer(userId: string, request?: Request) {
138
- // Option 1: Use Admin Secret for privileged access
139
- // const client = createApolloClient({
140
- // secret: process.env.HASURA_ADMIN_SECRET // Ensure secret is available server-side
141
- // });
142
-
143
- // Option 2: Use User Token (if available from request/session)
144
- // Example: const sessionToken = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET });
145
- // const hasuraToken = sessionToken?.accessToken;
146
- const client = createApolloClient({
147
- token: process.env.SOME_SERVICE_ACCOUNT_TOKEN_IF_NEEDED // Or fetch user token if applicable
148
- // url: process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL // URL can also be passed explicitly
149
- });
150
-
151
- const GET_USER = gql`...`; // Your query
152
-
153
- try {
154
- const { data } = await client.query({
155
- query: GET_USER,
156
- variables: { userId },
157
- // Important for server-side: Avoid using client-side cache
158
- fetchPolicy: 'network-only',
159
- });
160
- return data.users_by_pk;
161
- } catch (error) {
162
- console.error('Error fetching user data:', error);
163
- return null;
164
- }
165
- }
166
- ```
167
-
168
- 2. **Fetch Data:** Use `client.query(...)` or `client.mutate(...)`.
169
-
170
- ## Important Considerations
171
-
172
- * **JWT Token:** Ensure your authentication system (like `next-auth`) generates a Hasura-compatible JWT containing the necessary `https://hasura.io/jwt/claims` and includes it in the session data (e.g., as `session.accessToken`) for the client-side integration to work.
173
- * **Environment Variables:** Securely manage `HASURA_ADMIN_SECRET`, `NEXTAUTH_SECRET`, and potentially `HASURA_JWT_SECRET` using environment variable configuration in your hosting provider (e.g., Vercel). `NEXT_PUBLIC_HASURA_GRAPHQL_URL` needs to be accessible client-side.
174
- * **WebSocket Security:** WebSocket connections initiated from the client include the token/secret directly in the `connectionParams`. Ensure your Hasura endpoint is properly secured (e.g., using HTTPS/WSS).
1
+ # Apollo Client Setup (`lib/apollo.tsx`)
2
+
3
+ This file provides the `useCreateApolloClient` hook, a crucial part of the library for interacting with your Hasura GraphQL backend.
4
+
5
+ ## `useCreateApolloClient` Hook
6
+
7
+ A React hook to create and memoize an Apollo Client instance configured for the Hasura backend.
8
+
9
+ ```tsx
10
+ import { useCreateApolloClient } from 'nextjs-hasura-auth';
11
+
12
+ function MyComponent() {
13
+ // Create client for HTTP only
14
+ const apolloClientHttp = useCreateApolloClient({ token: 'your-auth-token' });
15
+
16
+ // Create client for HTTP and WebSocket (subscriptions)
17
+ const apolloClientWs = useCreateApolloClient({ token: 'your-auth-token', ws: true });
18
+
19
+ // ... use apolloClient
20
+ }
21
+ ```
22
+
23
+ ### Purpose
24
+
25
+ This hook simplifies the setup of an Apollo Client tailored for Hasura environments, handling both HTTP and WebSocket connections, authentication headers, and role management.
26
+
27
+ ### Arguments
28
+
29
+ - `options` (object):
30
+ - `token` (string | undefined): Optional authentication token (JWT) to be included in headers.
31
+ - `ws` (boolean): Optional flag (default: `false`) to enable WebSocket connection for subscriptions.
32
+
33
+ ### Return Value
34
+
35
+ - `ApolloClient<NormalizedCacheObject>`: An initialized and memoized Apollo Client instance.
36
+
37
+ ### Key Features
38
+
39
+ 1. **HTTP Link (`httpLink`)**: Configured for standard GraphQL operations (queries, mutations) using `createHttpLink`.
40
+ 2. **WebSocket Link (`wsLink`)**: Configured for GraphQL subscriptions using `graphql-ws`. Enabled only if `ws: true` is passed in the options. It uses `createClient` from `graphql-ws`.
41
+ 3. **Role Link (`roleLink`)**: **New!** This is an `ApolloLink` middleware using `setContext` that intercepts outgoing **HTTP requests**. It checks the `context` object passed to the Apollo operation (e.g., in `useQuery`, `useMutation`). If a `role` property is found in the operation's context, it adds the `X-Hasura-Role` header to the HTTP request with the specified role.
42
+ * **Important Note:** This dynamic role setting via context currently **only works for HTTP requests** (queries and mutations). It **does not** dynamically set the role for WebSocket connections (subscriptions). For subscriptions, the role is determined by the `connectionParams` when the WebSocket connection is established, which typically uses the `token` provided to `useCreateApolloClient` to infer the role on the backend.
43
+ 4. **Authentication Headers**:
44
+ * For HTTP requests, it automatically includes `Authorization: Bearer <token>` (if provided) and `X-Hasura-Admin-Secret` (if `process.env.HASURA_ADMIN_SECRET` is set). The `roleLink` adds the `X-Hasura-Role` header if a `role` is provided in the operation's context.
45
+ * For WebSocket connections, `connectionParams` are set with `headers` containing `Authorization` and `X-Hasura-Admin-Secret`.
46
+ 5. **Request Splitting**: Uses `split` to direct operations to `httpLink` (via `roleLink` and `authLink`) or `wsLink` based on the operation type. Subscriptions go to `wsLink` (if `ws: true`), others go to `httpLink`.
47
+ 6. **Error Handling (`errorLink`)**: Includes basic error logging for GraphQL and network errors using `onError`.
48
+ 7. **Caching**: Uses `InMemoryCache` for client-side caching.
49
+ 8. **Memoization**: The `useMemo` hook ensures the client instance is created only once per component lifecycle or when the `token` or `ws` options change, preventing unnecessary client recreations.
50
+
51
+ ### How `roleLink` Works
52
+
53
+ The `roleLink` leverages Apollo Link's `setContext` middleware.
54
+
55
+ ```typescript
56
+ // Simplified logic within useCreateApolloClient
57
+ import { setContext } from '@apollo/client/link/context';
58
+
59
+ const roleLink = setContext((_operation, context) => {
60
+ // context here is the *link* context, which includes headers etc.
61
+ // and importantly, the context passed from the hook/operation call.
62
+ const operationContext = context?.graphqlContext || {}; // Access context passed from useQuery/useMutation etc.
63
+ const role = operationContext.role; // Get the role from the operation's context
64
+
65
+ // If a role is provided in the operation's context, add it to the headers
66
+ if (role) {
67
+ return {
68
+ headers: {
69
+ ...context.headers, // Preserve existing headers (like Authorization)
70
+ 'X-Hasura-Role': role,
71
+ },
72
+ };
73
+ }
74
+
75
+ // Otherwise, return the existing headers unchanged
76
+ return {
77
+ headers: context.headers,
78
+ };
79
+ });
80
+
81
+ // This roleLink is then chained before the httpLink:
82
+ // link: from([errorLink, roleLink, authLink, httpLink]) // for HTTP
83
+ ```
84
+
85
+ When you use the data fetching hooks from `lib/client.tsx` (like `useQuery`, `useMutation`) and pass a `role` in the `hookOptions`, that `role` becomes part of the operation's context (`graphqlContext`), which `roleLink` then reads to set the appropriate `X-Hasura-Role` header for that specific HTTP request.
86
+
87
+ ```tsx
88
+ // Example using useQuery with a role
89
+ const { data } = useQuery(GET_POSTS, {
90
+ hookOptions: {
91
+ // These options are passed to Apollo's useQuery
92
+ variables: { limit: 10 },
93
+ // This context object is passed along the link chain
94
+ context: {
95
+ role: 'editor', // roleLink will pick this up
96
+ },
97
+ },
98
+ });
99
+ ```
100
+
101
+ This allows for fine-grained role control on a per-operation basis for queries and mutations. Remember, WebSocket subscriptions rely on the role established during the initial connection based on the token.
@@ -31,6 +31,8 @@ export declare function getClient(options?: {}): ApolloClient<any>;
31
31
  * @returns Apollo client instance
32
32
  */
33
33
  export declare function useCreateApolloClient(options: ApolloOptions): ApolloClientWithProvider;
34
+ export declare const CHECK_CONNECTION_SUBSCRIPTION: import("@apollo/client").DocumentNode;
35
+ export declare const CHECK_CONNECTION_QUERY: import("@apollo/client").DocumentNode;
34
36
  /**
35
37
  * Check connection to Hasura GraphQL endpoint
36
38
  * @returns {Promise<boolean>} True if connection is successful
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CHECK_CONNECTION_QUERY = exports.CHECK_CONNECTION_SUBSCRIPTION = void 0;
16
+ exports.createApolloClient = createApolloClient;
17
+ exports.getClient = getClient;
18
+ exports.useCreateApolloClient = useCreateApolloClient;
19
+ exports.checkConnection = checkConnection;
20
+ const jsx_runtime_1 = require("react/jsx-runtime");
21
+ const client_1 = require("@apollo/client");
22
+ const context_1 = require("@apollo/client/link/context");
23
+ const utilities_1 = require("@apollo/client/utilities");
24
+ // Restore GraphQLWsLink imports
25
+ const subscriptions_1 = require("@apollo/client/link/subscriptions");
26
+ const graphql_ws_1 = require("graphql-ws");
27
+ const cross_fetch_1 = __importDefault(require("cross-fetch"));
28
+ const debug_1 = __importDefault(require("./debug"));
29
+ const react_1 = require("react");
30
+ const jwt_1 = require("./jwt");
31
+ // Remove deprecated WebSocketLink import
32
+ // import { WebSocketLink } from '@apollo/client/link/ws';
33
+ const error_1 = require("@apollo/client/link/error");
34
+ // Create a debug logger for this module
35
+ const debug = (0, debug_1.default)('apollo');
36
+ // Determine if running on client
37
+ const isClient = typeof window !== 'undefined';
38
+ const createRoleLink = () => (0, context_1.setContext)((request, previousContext) => {
39
+ // Get the role from the operation's context passed in the hook options
40
+ const role = previousContext === null || previousContext === void 0 ? void 0 : previousContext.role; // Correctly access context via the second argument
41
+ debug(`roleLink: Role from context: ${role}`);
42
+ if (role) {
43
+ return {
44
+ headers: Object.assign(Object.assign({}, previousContext.headers), { 'X-Hasura-Role': role }),
45
+ };
46
+ }
47
+ // If no role is provided in the context, don't add the header
48
+ return {};
49
+ });
50
+ /**
51
+ * Create Apollo Client
52
+ *
53
+ * @param {Object} options - Options for creating the client
54
+ * @param {boolean} options.ws - Use WebSocket connection
55
+ * @param {string} options.token - JWT token for authorization
56
+ * @param {string} options.secret - Admin secret for Hasura
57
+ * @returns {ApolloClient} Apollo Client
58
+ */
59
+ function createApolloClient(options = {}) {
60
+ // Default values
61
+ const { url = process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL, ws = false, token = undefined, secret = process.env.HASURA_ADMIN_SECRET } = options;
62
+ if (!url) {
63
+ throw new Error('❌ options.url or NEXT_PUBLIC_HASURA_GRAPHQL_URL not defined');
64
+ }
65
+ debug('apollo', '🔌 Creating Apollo client with endpoint:', url);
66
+ // --- NEW: Link to set Hasura Role Header ---
67
+ const roleLink = createRoleLink();
68
+ // --- END NEW ---
69
+ // --- Existing Links ---
70
+ // Create base HttpLink (no auth here initially)
71
+ const baseHttpLink = new client_1.HttpLink({
72
+ uri: url,
73
+ fetch: cross_fetch_1.default,
74
+ });
75
+ // Create link for adding auth (JWT or Admin Secret)
76
+ const authHeaderLink = (0, context_1.setContext)((_, { headers }) => {
77
+ // Return the headers to the context so httpLink can read them
78
+ if (token) {
79
+ debug('apollo', '🔒 Using JWT token for Authorization header');
80
+ return {
81
+ headers: Object.assign(Object.assign({}, headers), { Authorization: `Bearer ${token}` })
82
+ };
83
+ }
84
+ else if (secret) {
85
+ debug('apollo', '🔑 Using Admin Secret for x-hasura-admin-secret header');
86
+ return {
87
+ headers: Object.assign(Object.assign({}, headers), { 'x-hasura-admin-secret': secret })
88
+ };
89
+ }
90
+ debug('apollo', '🔓 Sending request without authentication headers');
91
+ return { headers };
92
+ });
93
+ // Chain the links: roleLink -> authHeaderLink -> baseHttpLink
94
+ const httpLink = client_1.ApolloLink.from([roleLink, authHeaderLink, baseHttpLink]);
95
+ // --- End Existing Links Modification ---
96
+ // --- WebSocket Link Setup (Remains largely the same) ---
97
+ let link = httpLink; // Start with the combined HTTP link
98
+ // --- Debugging WS Link Creation ---
99
+ debug('apollo', `🚀 Checking WS setup: ws=${ws}, isClient=${isClient}`);
100
+ if (ws && isClient) {
101
+ debug('apollo', '✅ Entering WS Link creation block.'); // Log entry
102
+ const wsEndpoint = url.replace('http', 'ws').replace('https://', 'wss');
103
+ debug('apollo', '🔌 Setting up GraphQLWsLink for:', wsEndpoint);
104
+ // --- Restore GraphQLWsLink ---
105
+ const wsLink = new subscriptions_1.GraphQLWsLink((0, graphql_ws_1.createClient)({
106
+ url: wsEndpoint,
107
+ connectionParams: () => __awaiter(this, void 0, void 0, function* () {
108
+ debug('apollo', '⚙️ Evaluating connectionParams function...');
109
+ const params = {};
110
+ if (token) {
111
+ debug('apollo', '🔒 Using JWT token for WS connectionParams (from function)');
112
+ params.headers = { Authorization: `Bearer ${token}` };
113
+ }
114
+ else if (secret) {
115
+ debug('apollo', '🔑 Using Admin Secret for WS connectionParams (from function)');
116
+ params.headers = { 'x-hasura-admin-secret': secret };
117
+ }
118
+ else {
119
+ debug('apollo', '🔓 WebSocket connection without specific auth params (from function)');
120
+ }
121
+ debug('apollo', '⚙️ connectionParams function returning:', params);
122
+ return params;
123
+ }),
124
+ // Note: Dynamically changing headers per subscription via context
125
+ // is not standard in GraphQLWsLink. The roleLink above won't affect this.
126
+ // --- Add event handlers for diagnostics ---
127
+ on: {
128
+ connected: (socket) => debug('apollo', '🔗 [graphql-ws] WebSocket connected:', socket),
129
+ ping: (received) => debug('apollo', `➡️ [graphql-ws] Ping ${received ? 'received' : 'sent'}`),
130
+ pong: (received) => debug('apollo', `⬅️ [graphql-ws] Pong ${received ? 'received' : 'sent'}`),
131
+ error: (err) => debug('apollo', '❌ [graphql-ws] WebSocket error:', err),
132
+ closed: (event) => debug('apollo', '🚪 [graphql-ws] WebSocket closed:', event),
133
+ }
134
+ // --- End event handlers ---
135
+ }));
136
+ /* --- Remove Deprecated WebSocketLink ---
137
+ const wsLink = new WebSocketLink({
138
+ uri: wsEndpoint,
139
+ options: {
140
+ reconnect: true,
141
+ connectionParams: connectionParams, // Pass prepared headers here
142
+ // Note: Timeout options might be needed depending on env
143
+ }
144
+ });
145
+ debug('apollo', '🔗 DEPRECATED WebSocketLink created.');
146
+ */
147
+ // Split based on operation type
148
+ link = (0, client_1.split)(({ query }) => {
149
+ const definition = (0, utilities_1.getMainDefinition)(query);
150
+ const isSubscription = definition.kind === 'OperationDefinition' &&
151
+ definition.operation === 'subscription';
152
+ debug('apollo', `🔗 Split link decision: isSubscription=${isSubscription}`);
153
+ return (definition.kind === 'OperationDefinition' &&
154
+ definition.operation === 'subscription');
155
+ }, wsLink, // Use wsLink for subscriptions
156
+ httpLink // Use httpLink (with roleLink and authHeaderLink) for query/mutation
157
+ );
158
+ }
159
+ else {
160
+ debug('apollo', '❌ Skipping WS Link creation.', { ws, isClient });
161
+ }
162
+ // --- End WebSocket Setup ---
163
+ // Create Apollo Client with the final composed link
164
+ const apolloClient = new client_1.ApolloClient({
165
+ link: link, // Use the potentially split link
166
+ cache: new client_1.InMemoryCache(),
167
+ defaultOptions: {
168
+ watchQuery: {
169
+ fetchPolicy: 'network-only',
170
+ errorPolicy: 'all',
171
+ },
172
+ query: {
173
+ fetchPolicy: 'network-only',
174
+ errorPolicy: 'all',
175
+ },
176
+ mutate: {
177
+ errorPolicy: 'all',
178
+ },
179
+ }
180
+ });
181
+ apolloClient.Provider = function Provider({ children }) {
182
+ return (0, jsx_runtime_1.jsx)(client_1.ApolloProvider, { client: apolloClient, children: children });
183
+ };
184
+ return apolloClient;
185
+ }
186
+ // Default client instance
187
+ let clientInstance = null;
188
+ /**
189
+ * Get or create Apollo client instance
190
+ * @param options Client options
191
+ * @returns Apollo client instance
192
+ */
193
+ function getClient(options = {}) {
194
+ if (!clientInstance) {
195
+ clientInstance = createApolloClient(options);
196
+ }
197
+ return clientInstance;
198
+ }
199
+ /**
200
+ * React hook to get Apollo client instance
201
+ * @returns Apollo client instance
202
+ */
203
+ function useCreateApolloClient(options) {
204
+ return (0, react_1.useMemo)(() => createApolloClient(options), [options]);
205
+ }
206
+ exports.CHECK_CONNECTION_SUBSCRIPTION = (0, client_1.gql) `
207
+ subscription CheckConnection {
208
+ __schema {
209
+ queryType {
210
+ name
211
+ }
212
+ }
213
+ }
214
+ `;
215
+ exports.CHECK_CONNECTION_QUERY = (0, client_1.gql) `
216
+ query CheckConnection {
217
+ __schema {
218
+ queryType {
219
+ name
220
+ }
221
+ }
222
+ }
223
+ `;
224
+ /**
225
+ * Check connection to Hasura GraphQL endpoint
226
+ * @returns {Promise<boolean>} True if connection is successful
227
+ */
228
+ function checkConnection() {
229
+ return __awaiter(this, arguments, void 0, function* (client = getClient()) {
230
+ var _a, _b, _c;
231
+ const result = yield client.query({ query: exports.CHECK_CONNECTION_QUERY });
232
+ return !!((_c = (_b = (_a = result.data) === null || _a === void 0 ? void 0 : _a.__schema) === null || _b === void 0 ? void 0 : _b.queryType) === null || _c === void 0 ? void 0 : _c.name);
233
+ });
234
+ }
235
+ const errorLink = (0, error_1.onError)(({ graphQLErrors, networkError, operation, forward }) => {
236
+ // Handle errors
237
+ });
238
+ exports.default = {
239
+ createApolloClient,
240
+ getClient,
241
+ getJwtSecret: jwt_1.getJwtSecret,
242
+ checkConnection
243
+ };
@@ -0,0 +1,48 @@
1
+ import { Client } from 'nextjs-hasura-auth';
2
+ /**
3
+ * Hashes a password using bcrypt.
4
+ * @param password The plain text password.
5
+ * @returns The hashed password.
6
+ */
7
+ export declare function hashPassword(password: string): Promise<string>;
8
+ /**
9
+ * Compares a plain text password with a hash.
10
+ * @param password The plain text password.
11
+ * @param hash The hash to compare against.
12
+ * @returns True if the password matches the hash, false otherwise.
13
+ */
14
+ export declare function comparePassword(password: string, hash: string): Promise<boolean>;
15
+ interface UserProfileFromProvider {
16
+ name?: string | null;
17
+ email?: string | null;
18
+ image?: string | null;
19
+ }
20
+ export interface HasuraUser {
21
+ id: string;
22
+ name?: string | null;
23
+ email?: string | null;
24
+ email_verified?: string | null;
25
+ image?: string | null;
26
+ password?: string | null;
27
+ created_at: string;
28
+ updated_at: string;
29
+ is_admin?: boolean | null;
30
+ hasura_role?: string | null;
31
+ accounts?: {
32
+ provider: string;
33
+ provider_account_id: string;
34
+ }[];
35
+ }
36
+ /**
37
+ * Finds or creates a user and their associated account based on provider information.
38
+ * This function handles the core logic of linking OAuth/Credentials logins to Hasura users.
39
+ *
40
+ * @param client The initialized NHA Client instance.
41
+ * @param provider The OAuth provider name (e.g., 'google', 'credentials').
42
+ * @param providerAccountId The user's unique ID from the provider.
43
+ * @param profile Optional profile information from the provider (name, email, image).
44
+ * @returns The Hasura user object associated with the account.
45
+ * @throws Error if user/account processing fails.
46
+ */
47
+ export declare function getOrCreateUserAndAccount(client: Client, provider: string, providerAccountId: string, profile?: UserProfileFromProvider | null): Promise<HasuraUser>;
48
+ export {};