nextjs-hasura-auth 0.1.1 → 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.
@@ -0,0 +1,183 @@
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.hashPassword = hashPassword;
16
+ exports.comparePassword = comparePassword;
17
+ exports.getOrCreateUserAndAccount = getOrCreateUserAndAccount;
18
+ const bcrypt_1 = __importDefault(require("bcrypt"));
19
+ const client_1 = require("@apollo/client"); // Import ApolloError
20
+ const debug_1 = __importDefault(require("./debug"));
21
+ const debug = (0, debug_1.default)('auth:db-utils');
22
+ const SALT_ROUNDS = 10;
23
+ /**
24
+ * Hashes a password using bcrypt.
25
+ * @param password The plain text password.
26
+ * @returns The hashed password.
27
+ */
28
+ function hashPassword(password) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ const hashedPassword = yield bcrypt_1.default.hash(password, SALT_ROUNDS);
31
+ debug('Password hashed successfully.');
32
+ return hashedPassword;
33
+ });
34
+ }
35
+ /**
36
+ * Compares a plain text password with a hash.
37
+ * @param password The plain text password.
38
+ * @param hash The hash to compare against.
39
+ * @returns True if the password matches the hash, false otherwise.
40
+ */
41
+ function comparePassword(password, hash) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ const isMatch = yield bcrypt_1.default.compare(password, hash);
44
+ debug(`Password comparison result: ${isMatch}`);
45
+ return isMatch;
46
+ });
47
+ }
48
+ /**
49
+ * Finds or creates a user and their associated account based on provider information.
50
+ * This function handles the core logic of linking OAuth/Credentials logins to Hasura users.
51
+ *
52
+ * @param client The initialized NHA Client instance.
53
+ * @param provider The OAuth provider name (e.g., 'google', 'credentials').
54
+ * @param providerAccountId The user's unique ID from the provider.
55
+ * @param profile Optional profile information from the provider (name, email, image).
56
+ * @returns The Hasura user object associated with the account.
57
+ * @throws Error if user/account processing fails.
58
+ */
59
+ function getOrCreateUserAndAccount(client, provider, providerAccountId, profile) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ var _a, _b, _c;
62
+ debug(`getOrCreateUserAndAccount called for provider: ${provider}, providerAccountId: ${providerAccountId}`);
63
+ // --- 1. Try to find the account ---
64
+ let existingUser = null;
65
+ try {
66
+ const accountResult = yield client.select({
67
+ table: 'accounts',
68
+ where: {
69
+ provider: { _eq: provider },
70
+ provider_account_id: { _eq: providerAccountId },
71
+ },
72
+ returning: [
73
+ { user: ['id', 'name', 'email', 'email_verified', 'image', 'password', 'created_at', 'updated_at', 'is_admin', 'hasura_role'] } // Removed extra backslashes
74
+ ],
75
+ limit: 1, // Optimization: we only need one
76
+ });
77
+ if (((_a = accountResult === null || accountResult === void 0 ? void 0 : accountResult.accounts) === null || _a === void 0 ? void 0 : _a.length) > 0 && accountResult.accounts[0].user) {
78
+ existingUser = accountResult.accounts[0].user;
79
+ debug(`Found existing account for ${provider}:${providerAccountId}. User ID: ${existingUser.id}`);
80
+ // Optionally update user profile info (name, image) from provider here if desired
81
+ // await client.update({ table: 'users', pk_columns: { id: existingUser.id }, _set: { name: profile.name, image: profile.image } });
82
+ return existingUser;
83
+ }
84
+ }
85
+ catch (error) {
86
+ debug(`Error searching for account ${provider}:${providerAccountId}:`, error);
87
+ throw new Error(`Failed to search for existing account: ${error.message}`);
88
+ }
89
+ debug(`No existing account found for ${provider}:${providerAccountId}. Proceeding to find/create user.`);
90
+ // --- 2. Try to find the user by email (if provided) ---
91
+ // Important: Only link if email is provided and preferably verified by the OAuth provider.
92
+ // For credentials, we find the user first in the `authorize` function.
93
+ if ((profile === null || profile === void 0 ? void 0 : profile.email) && provider !== 'credentials') { // Avoid linking for credentials here
94
+ try {
95
+ const userByEmailResult = yield client.select({
96
+ table: 'users',
97
+ where: { email: { _eq: profile.email } },
98
+ returning: ['id', 'name', 'email', 'email_verified', 'image', 'password', 'created_at', 'updated_at', 'is_admin', 'hasura_role'], // Removed extra backslashes
99
+ limit: 1,
100
+ });
101
+ if (((_b = userByEmailResult === null || userByEmailResult === void 0 ? void 0 : userByEmailResult.users) === null || _b === void 0 ? void 0 : _b.length) > 0) {
102
+ existingUser = userByEmailResult.users[0];
103
+ debug(`Found existing user by email ${profile.email}. User ID: ${existingUser.id}. Linking account.`);
104
+ // Link account to this existing user
105
+ yield client.insert({
106
+ table: 'accounts',
107
+ object: {
108
+ user_id: existingUser.id,
109
+ provider: provider,
110
+ provider_account_id: providerAccountId,
111
+ type: provider === 'credentials' ? 'credentials' : 'oauth', // Set type based on provider
112
+ },
113
+ returning: ['id'], // Removed extra backslashes
114
+ });
115
+ debug(`Account ${provider}:${providerAccountId} linked to user ${existingUser.id}.`);
116
+ return existingUser;
117
+ }
118
+ }
119
+ catch (error) {
120
+ // Handle potential duplicate account insertion error gracefully if needed
121
+ if (error instanceof client_1.ApolloError && error.message.includes('Uniqueness violation')) {
122
+ debug(`Account ${provider}:${providerAccountId} likely already linked during concurrent request. Attempting to refetch.`);
123
+ // Retry finding the account, as it might have been created concurrently
124
+ return getOrCreateUserAndAccount(client, provider, providerAccountId, profile);
125
+ }
126
+ else {
127
+ debug(`Error searching for user by email ${profile.email} or linking account:`, error);
128
+ throw new Error(`Failed to process user by email or link account: ${error.message}`);
129
+ }
130
+ }
131
+ }
132
+ // --- 3. Create new user and account ---
133
+ debug(`No existing user found by email or account link. Creating new user and account for ${provider}:${providerAccountId}.`);
134
+ try {
135
+ // We need to insert the user first, then the account linking to it.
136
+ // Hasura doesn't directly support nested inserts with linking back in the same mutation easily via the generator.
137
+ const newUserInput = {
138
+ name: profile === null || profile === void 0 ? void 0 : profile.name,
139
+ email: profile === null || profile === void 0 ? void 0 : profile.email, // Will be null for credentials initially, set later?
140
+ image: profile === null || profile === void 0 ? void 0 : profile.image,
141
+ hasura_role: 'user', // Default role
142
+ // email_verified: profile?.email_verified ? new Date().toISOString() : null, // Need verification logic for OAuth
143
+ };
144
+ // For credentials, the user is created *after* email verification, but we handle it here for OAuth
145
+ const newUserResult = yield client.insert({
146
+ table: 'users',
147
+ object: newUserInput,
148
+ // Return all fields needed for the session/JWT
149
+ returning: ['id', 'name', 'email', 'email_verified', 'image', 'password', 'created_at', 'updated_at', 'is_admin', 'hasura_role'], // Removed extra backslashes
150
+ });
151
+ if (!((_c = newUserResult === null || newUserResult === void 0 ? void 0 : newUserResult.insert_users_one) === null || _c === void 0 ? void 0 : _c.id)) {
152
+ throw new Error('Failed to create new user or retrieve its ID.');
153
+ }
154
+ const newUser = newUserResult.insert_users_one;
155
+ debug(`New user created with ID: ${newUser.id}`);
156
+ // Now create the account linked to the new user
157
+ yield client.insert({
158
+ table: 'accounts',
159
+ object: {
160
+ user_id: newUser.id,
161
+ provider: provider,
162
+ provider_account_id: providerAccountId,
163
+ type: provider === 'credentials' ? 'credentials' : 'oauth', // Set type based on provider
164
+ },
165
+ returning: ['id'], // Removed extra backslashes
166
+ });
167
+ debug(`Account ${provider}:${providerAccountId} created and linked to new user ${newUser.id}.`);
168
+ return newUser;
169
+ }
170
+ catch (error) {
171
+ debug('Error creating new user or account:', error);
172
+ // Handle potential duplicate user email error more gracefully if necessary
173
+ if (error instanceof client_1.ApolloError && error.message.includes('Uniqueness violation') && error.message.includes('users_email_key')) {
174
+ debug(`User with email ${profile === null || profile === void 0 ? void 0 : profile.email} likely already exists. Attempting to find and link.`);
175
+ // If user creation failed due to duplicate email, retry finding by email and linking account
176
+ if ((profile === null || profile === void 0 ? void 0 : profile.email) && provider !== 'credentials') {
177
+ return getOrCreateUserAndAccount(client, provider, providerAccountId, profile);
178
+ }
179
+ }
180
+ throw new Error(`Failed to create new user/account: ${error.message}`);
181
+ }
182
+ });
183
+ }
@@ -0,0 +1,81 @@
1
+ import { QueryHookOptions as ApolloQueryHookOptions, SubscriptionHookOptions as ApolloSubscriptionHookOptions, MutationHookOptions as ApolloMutationHookOptions, MutationTuple, QueryResult, SubscriptionResult, OperationVariables, ApolloClient, NormalizedCacheObject, FetchResult, Observable } from '@apollo/client';
2
+ import { GenerateOptions } from './generator';
3
+ interface ClientMethodOptions extends Omit<GenerateOptions, 'operation'> {
4
+ role?: string;
5
+ }
6
+ export declare class Client {
7
+ private apolloClient;
8
+ private generate;
9
+ constructor(apolloClient: ApolloClient<NormalizedCacheObject>);
10
+ /**
11
+ * Executes a GraphQL query (select operation).
12
+ * @param options - Options for generating the query, including an optional `role`.
13
+ * @returns Promise resolving with the query result data.
14
+ * @throws ApolloError if the query fails or returns GraphQL errors.
15
+ */
16
+ select<TData = any>(options: ClientMethodOptions): Promise<TData>;
17
+ /**
18
+ * Executes a GraphQL insert mutation.
19
+ * @param options - Options for generating the mutation, including an optional `role`.
20
+ * @returns Promise resolving with the mutation result data.
21
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
22
+ */
23
+ insert<TData = any>(options: ClientMethodOptions): Promise<TData>;
24
+ /**
25
+ * Executes a GraphQL update mutation.
26
+ * @param options - Options for generating the mutation, including an optional `role`.
27
+ * @returns Promise resolving with the mutation result data.
28
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
29
+ */
30
+ update<TData = any>(options: ClientMethodOptions): Promise<TData>;
31
+ /**
32
+ * Executes a GraphQL delete mutation.
33
+ * @param options - Options for generating the mutation, including an optional `role`.
34
+ * @returns Promise resolving with the mutation result data.
35
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
36
+ */
37
+ delete<TData = any>(options: ClientMethodOptions): Promise<TData>;
38
+ /**
39
+ * Initiates a GraphQL subscription.
40
+ * Note: Role support via context might not work for WebSockets depending on Apollo Link setup.
41
+ * Role is typically set during WebSocket connection establishment.
42
+ * @param options - Options for generating the subscription, including an optional `role`.
43
+ * @returns An Observable for the subscription results.
44
+ */
45
+ subscribe<TData = any, TVariables extends OperationVariables = OperationVariables>(options: ClientMethodOptions): Observable<FetchResult<TData>>;
46
+ useSubscription<TData = any, TVariables extends OperationVariables = OperationVariables>(generateOptions: ClientGeneratorOptions, hookOptions?: SubscriptionHookOptions<TData, TVariables> & {
47
+ variables?: TVariables;
48
+ }): SubscriptionResult<TData, TVariables>;
49
+ useQuery<TData = any, TVariables extends OperationVariables = OperationVariables>(generateOptions: ClientGeneratorOptions, hookOptions?: QueryHookOptions<TData, TVariables> & {
50
+ variables?: TVariables;
51
+ }): QueryResult<TData, TVariables>;
52
+ }
53
+ /**
54
+ * Hook to get the Apollo Client instance.
55
+ * Prefers the explicitly provided client, falls back to context, throws if none found.
56
+ * @param providedClient - An optional ApolloClient instance.
57
+ * @returns The ApolloClient instance.
58
+ * @throws Error if no client is found.
59
+ */
60
+ export declare function useClient(providedClient?: ApolloClient<any> | null): Client;
61
+ type BaseHookOptions = {
62
+ role?: string;
63
+ };
64
+ type QueryHookOptions<TData, TVariables extends OperationVariables> = BaseHookOptions & Omit<ApolloQueryHookOptions<TData, TVariables>, 'query' | 'variables' | 'context'>;
65
+ type SubscriptionHookOptions<TData, TVariables extends OperationVariables> = BaseHookOptions & Omit<ApolloSubscriptionHookOptions<TData, TVariables>, 'query' | 'variables' | 'context'>;
66
+ type MutationHookOptions<TData, TVariables extends OperationVariables> = BaseHookOptions & Omit<ApolloMutationHookOptions<TData, TVariables>, 'mutation' | 'variables' | 'context'>;
67
+ type ClientGeneratorOptions = Omit<GenerateOptions, 'operation'>;
68
+ export declare function useQuery<TData = any, TVariables extends OperationVariables = OperationVariables>(generateOptions: ClientGeneratorOptions, hookOptions?: QueryHookOptions<TData, TVariables> & {
69
+ variables?: TVariables;
70
+ }): QueryResult<TData, TVariables>;
71
+ export declare function useSubscription<TData = any, TVariables extends OperationVariables = OperationVariables>(generateOptions: ClientGeneratorOptions, hookOptions?: SubscriptionHookOptions<TData, TVariables> & {
72
+ variables?: TVariables;
73
+ }): SubscriptionResult<TData, TVariables>;
74
+ export declare function useMutation<TData = any, TVariables extends OperationVariables = OperationVariables>(generateOptions: GenerateOptions, // Use full GenerateOptions for mutations
75
+ hookOptions?: MutationHookOptions<TData, TVariables>): MutationTuple<TData, TVariables>;
76
+ export declare const useSelect: typeof useQuery;
77
+ export declare const useInsert: (genOpts: Omit<GenerateOptions, "operation">, hookOpts?: MutationHookOptions<any, any>) => MutationTuple<any, any>;
78
+ export declare const useUpdate: (genOpts: Omit<GenerateOptions, "operation">, hookOpts?: MutationHookOptions<any, any>) => MutationTuple<any, any>;
79
+ export declare const useDelete: (genOpts: Omit<GenerateOptions, "operation">, hookOpts?: MutationHookOptions<any, any>) => MutationTuple<any, any>;
80
+ export declare const useSubscribe: typeof useSubscription;
81
+ export {};
@@ -0,0 +1,353 @@
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 __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.useSubscribe = exports.useDelete = exports.useUpdate = exports.useInsert = exports.useSelect = exports.Client = void 0;
27
+ exports.useClient = useClient;
28
+ exports.useQuery = useQuery;
29
+ exports.useSubscription = useSubscription;
30
+ exports.useMutation = useMutation;
31
+ const client_1 = require("@apollo/client");
32
+ const react_1 = require("react");
33
+ const client_2 = require("@apollo/client");
34
+ const debug_1 = __importDefault(require("./debug"));
35
+ const generator_1 = __importDefault(require("./generator"));
36
+ const debug = (0, debug_1.default)('nha:client');
37
+ class Client {
38
+ constructor(apolloClient) {
39
+ if (!apolloClient) {
40
+ throw new Error('❌ ApolloClient instance must be provided to the Client constructor.');
41
+ }
42
+ this.apolloClient = apolloClient;
43
+ this.generate = generator_1.default; // Use the imported generator function
44
+ debug('Client class initialized with ApolloClient instance.');
45
+ }
46
+ /**
47
+ * Executes a GraphQL query (select operation).
48
+ * @param options - Options for generating the query, including an optional `role`.
49
+ * @returns Promise resolving with the query result data.
50
+ * @throws ApolloError if the query fails or returns GraphQL errors.
51
+ */
52
+ select(options) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ const { role } = options, genOptions = __rest(options, ["role"]); // Extract role
55
+ debug('Executing select with options:', genOptions, 'Role:', role);
56
+ const generated = this.generate(Object.assign(Object.assign({}, genOptions), { operation: 'query' }));
57
+ try {
58
+ const result = yield this.apolloClient.query({
59
+ query: generated.query,
60
+ variables: generated.variables,
61
+ fetchPolicy: 'network-only', // Ensure fresh data for direct calls
62
+ context: role ? { role } : undefined, // Pass role in context
63
+ });
64
+ if (result.errors) {
65
+ debug('GraphQL errors during select:', result.errors);
66
+ throw new client_1.ApolloError({ graphQLErrors: result.errors });
67
+ }
68
+ debug('Select successful, returning data.');
69
+ return result.data;
70
+ }
71
+ catch (error) {
72
+ debug('Error during select:', error);
73
+ throw error; // Re-throw original error (could be ApolloError or network error)
74
+ }
75
+ });
76
+ }
77
+ /**
78
+ * Executes a GraphQL insert mutation.
79
+ * @param options - Options for generating the mutation, including an optional `role`.
80
+ * @returns Promise resolving with the mutation result data.
81
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
82
+ */
83
+ insert(options) {
84
+ return __awaiter(this, void 0, void 0, function* () {
85
+ var _a;
86
+ const { role } = options, genOptions = __rest(options, ["role"]); // Extract role
87
+ debug('Executing insert with options:', genOptions, 'Role:', role);
88
+ const generated = this.generate(Object.assign(Object.assign({}, genOptions), { operation: 'insert' }));
89
+ try {
90
+ const result = yield this.apolloClient.mutate({
91
+ mutation: generated.query,
92
+ variables: generated.variables,
93
+ context: role ? { role } : undefined, // Pass role in context
94
+ });
95
+ if (result.errors) {
96
+ debug('GraphQL errors during insert:', result.errors);
97
+ throw new client_1.ApolloError({ graphQLErrors: result.errors });
98
+ }
99
+ // Check if data exists, otherwise return an empty object or handle as needed
100
+ const returnData = (_a = result.data) !== null && _a !== void 0 ? _a : {};
101
+ debug('Insert successful, returning data.');
102
+ return returnData;
103
+ }
104
+ catch (error) {
105
+ debug('Error during insert:', error);
106
+ throw error;
107
+ }
108
+ });
109
+ }
110
+ /**
111
+ * Executes a GraphQL update mutation.
112
+ * @param options - Options for generating the mutation, including an optional `role`.
113
+ * @returns Promise resolving with the mutation result data.
114
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
115
+ */
116
+ update(options) {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ var _a;
119
+ const { role } = options, genOptions = __rest(options, ["role"]); // Extract role
120
+ debug('Executing update with options:', genOptions, 'Role:', role);
121
+ const generated = this.generate(Object.assign(Object.assign({}, genOptions), { operation: 'update' }));
122
+ try {
123
+ const result = yield this.apolloClient.mutate({
124
+ mutation: generated.query,
125
+ variables: generated.variables,
126
+ context: role ? { role } : undefined, // Pass role in context
127
+ });
128
+ if (result.errors) {
129
+ debug('GraphQL errors during update:', result.errors);
130
+ throw new client_1.ApolloError({ graphQLErrors: result.errors });
131
+ }
132
+ const returnData = (_a = result.data) !== null && _a !== void 0 ? _a : {};
133
+ debug('Update successful, returning data.');
134
+ return returnData;
135
+ }
136
+ catch (error) {
137
+ debug('Error during update:', error);
138
+ throw error;
139
+ }
140
+ });
141
+ }
142
+ /**
143
+ * Executes a GraphQL delete mutation.
144
+ * @param options - Options for generating the mutation, including an optional `role`.
145
+ * @returns Promise resolving with the mutation result data.
146
+ * @throws ApolloError if the mutation fails or returns GraphQL errors.
147
+ */
148
+ delete(options) {
149
+ return __awaiter(this, void 0, void 0, function* () {
150
+ var _a;
151
+ const { role } = options, genOptions = __rest(options, ["role"]); // Extract role
152
+ debug('Executing delete with options:', genOptions, 'Role:', role);
153
+ const generated = this.generate(Object.assign(Object.assign({}, genOptions), { operation: 'delete' }));
154
+ try {
155
+ const result = yield this.apolloClient.mutate({
156
+ mutation: generated.query,
157
+ variables: generated.variables,
158
+ context: role ? { role } : undefined, // Pass role in context
159
+ });
160
+ if (result.errors) {
161
+ debug('GraphQL errors during delete:', result.errors);
162
+ throw new client_1.ApolloError({ graphQLErrors: result.errors });
163
+ }
164
+ const returnData = (_a = result.data) !== null && _a !== void 0 ? _a : {};
165
+ debug('Delete successful, returning data.');
166
+ return returnData;
167
+ }
168
+ catch (error) {
169
+ debug('Error during delete:', error);
170
+ throw error;
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Initiates a GraphQL subscription.
176
+ * Note: Role support via context might not work for WebSockets depending on Apollo Link setup.
177
+ * Role is typically set during WebSocket connection establishment.
178
+ * @param options - Options for generating the subscription, including an optional `role`.
179
+ * @returns An Observable for the subscription results.
180
+ */
181
+ subscribe(options) {
182
+ const { role } = options, genOptions = __rest(options, ["role"]); // Extract role
183
+ debug('Initiating subscribe with options:', genOptions, 'Role:', role);
184
+ const generated = this.generate(Object.assign(Object.assign({}, genOptions), { operation: 'subscription' }));
185
+ return this.apolloClient.subscribe({
186
+ query: generated.query,
187
+ variables: generated.variables,
188
+ context: role ? { role } : undefined, // Pass role, effectiveness depends on link chain
189
+ });
190
+ }
191
+ useSubscription(
192
+ // First arg: Generator options (table, returning, where, etc.)
193
+ generateOptions,
194
+ // Second arg: Hook options (Apollo options + role)
195
+ hookOptions // Allow variables here for subscription
196
+ ) {
197
+ return useSubscription(generateOptions, (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, hookOptions), { client: this.apolloClient })), [hookOptions]));
198
+ }
199
+ ;
200
+ useQuery(
201
+ // First arg: Generator options (table, returning, where, etc.)
202
+ generateOptions,
203
+ // Second arg: Hook options (Apollo options + role)
204
+ hookOptions // Allow variables here for query
205
+ ) {
206
+ return useQuery(generateOptions, (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, hookOptions), { client: this.apolloClient })), [hookOptions]));
207
+ }
208
+ }
209
+ exports.Client = Client;
210
+ // --- React Hooks ---
211
+ /**
212
+ * Hook to get the Apollo Client instance.
213
+ * Prefers the explicitly provided client, falls back to context, throws if none found.
214
+ * @param providedClient - An optional ApolloClient instance.
215
+ * @returns The ApolloClient instance.
216
+ * @throws Error if no client is found.
217
+ */
218
+ function useClient(providedClient) {
219
+ const ApolloContext = (0, client_1.getApolloContext)();
220
+ const contextValue = (0, react_1.useContext)(ApolloContext);
221
+ const contextClient = contextValue === null || contextValue === void 0 ? void 0 : contextValue.client;
222
+ const client = providedClient !== null && providedClient !== void 0 ? providedClient : contextClient;
223
+ if (!client) {
224
+ throw new Error('❌ useClient: No ApolloClient instance found. Provide one directly or ensure the component is wrapped in an ApolloProvider.');
225
+ }
226
+ return (0, react_1.useMemo)(() => new Client(client), [client]);
227
+ }
228
+ // Helper to extract Apollo options and context for HOOKS
229
+ // Adjusted to not expect variables in mutation options directly
230
+ function prepareHookArgs(options, isMutation = false) {
231
+ const _a = options || {}, { role } = _a, apolloOptions = __rest(_a, ["role"]);
232
+ const context = role ? { role } : undefined;
233
+ // Extract variables only if not a mutation (variables are passed to mutate fn)
234
+ // And if options actually contain variables (e.g., for query/sub)
235
+ const variables = !isMutation && options && 'variables' in options ? options.variables : undefined;
236
+ debug('Preparing hook args', { role, context, variables: isMutation ? '(mutation - in mutate fn)' : variables, apolloOptions });
237
+ return { context, variables, apolloOptions };
238
+ }
239
+ // Standalone Hooks
240
+ function useQuery(
241
+ // First arg: Generator options (table, returning, where, etc.)
242
+ generateOptions,
243
+ // Second arg: Hook options (Apollo options + role)
244
+ hookOptions // Allow variables here for query
245
+ ) {
246
+ var _a, _b;
247
+ const client = (0, client_2.useApolloClient)(); // Use client from context
248
+ // Generate query using the imported 'generate' function
249
+ const { query: queryString, variables: generatedVariables, varCounter } = (0, react_1.useMemo)(() => (0, generator_1.default)(Object.assign({ operation: 'query' }, generateOptions)), [generateOptions]);
250
+ // Ensure gql receives a string
251
+ const query = (0, react_1.useMemo)(() => (0, client_1.gql) `${queryString}`, [queryString]);
252
+ // Combine variables from generator and hook options (hook options override)
253
+ // Cast the combined object to TVariables
254
+ const combinedVariables = Object.assign(Object.assign({}, generatedVariables), hookOptions === null || hookOptions === void 0 ? void 0 : hookOptions.variables);
255
+ // Prepare arguments for the actual Apollo hook
256
+ const { context, apolloOptions } = prepareHookArgs(hookOptions, false);
257
+ const operationName = ((_a = query.definitions[0]) === null || _a === void 0 ? void 0 : _a.kind) === 'OperationDefinition' ? ((_b = query.definitions[0].name) === null || _b === void 0 ? void 0 : _b.value) || 'UnnamedQuery' : 'UnnamedQuery';
258
+ debug(`Executing query ${operationName}`, { query: queryString, variables: combinedVariables, context, options: apolloOptions });
259
+ const result = (0, client_2.useQuery)(query, Object.assign({ client, variables: combinedVariables, context }, apolloOptions));
260
+ debug(`Query ${operationName} result`, { loading: result.loading, error: result.error, data: !!result.data });
261
+ return result;
262
+ }
263
+ function useSubscription(
264
+ // First arg: Generator options (table, returning, where, etc.)
265
+ generateOptions,
266
+ // Second arg: Hook options (Apollo options + role)
267
+ hookOptions // Allow variables here for subscription
268
+ ) {
269
+ var _a, _b, _c;
270
+ const apolloClient = (0, client_2.useApolloClient)(); // Use client from context
271
+ const client = (_a = hookOptions === null || hookOptions === void 0 ? void 0 : hookOptions.client) !== null && _a !== void 0 ? _a : apolloClient;
272
+ // Generate subscription using the imported 'generate' function
273
+ const { query: subscriptionString, variables: generatedVariables, varCounter } = (0, react_1.useMemo)(() => (0, generator_1.default)(Object.assign({ operation: 'subscription' }, generateOptions)), [generateOptions]);
274
+ // Ensure gql receives a string
275
+ const subscription = (0, react_1.useMemo)(() => (0, client_1.gql) `${subscriptionString}`, [subscriptionString]);
276
+ // Combine variables from generator and hook options (hook options override)
277
+ // Cast the combined object to TVariables
278
+ const combinedVariables = Object.assign(Object.assign({}, generatedVariables), hookOptions === null || hookOptions === void 0 ? void 0 : hookOptions.variables);
279
+ // Prepare arguments for the actual Apollo hook
280
+ const { context, apolloOptions } = prepareHookArgs(hookOptions, false);
281
+ const operationName = ((_b = subscription.definitions[0]) === null || _b === void 0 ? void 0 : _b.kind) === 'OperationDefinition' ? ((_c = subscription.definitions[0].name) === null || _c === void 0 ? void 0 : _c.value) || 'UnnamedSub' : 'UnnamedSub';
282
+ // Note: WebSocketLink in apollo.tsx might not support dynamic context/role per subscription easily.
283
+ // The role set via context here primarily affects the initial HTTP request if applicable,
284
+ // or needs custom WebSocket handling if role must change mid-subscription.
285
+ debug(`Executing subscription ${operationName}`, { query: subscriptionString, variables: combinedVariables, context, options: apolloOptions });
286
+ // Explicitly cast apolloOptions to the correct type expected by useApolloSubscription
287
+ const typedApolloOptions = apolloOptions;
288
+ const result = (0, client_2.useSubscription)(subscription, Object.assign({ client, variables: combinedVariables, context }, (typedApolloOptions)));
289
+ debug(`Subscription ${operationName} result`, { loading: result.loading, error: result.error, data: !!result.data });
290
+ return result;
291
+ }
292
+ function useMutation(
293
+ // First arg: Generator options (MUST include operation: 'insert' | 'update' | 'delete')
294
+ generateOptions, // Use full GenerateOptions for mutations
295
+ // Second arg: Hook options (Apollo options + role)
296
+ hookOptions) {
297
+ var _a, _b, _c;
298
+ const apolloClient = (0, client_2.useApolloClient)(); // Use client from context
299
+ const client = (_a = hookOptions === null || hookOptions === void 0 ? void 0 : hookOptions.client) !== null && _a !== void 0 ? _a : apolloClient;
300
+ // Validate that operation is insert/update/delete
301
+ if (!['insert', 'update', 'delete'].includes(generateOptions.operation)) {
302
+ throw new Error(`useMutation hook requires operation to be 'insert', 'update', or 'delete' in generateOptions.`);
303
+ }
304
+ // Generate mutation using the imported 'generate' function
305
+ // Variables from generateOptions are the *base* for the mutation
306
+ const { query: mutationString, variables: baseVariables, varCounter } = (0, react_1.useMemo)(() => (0, generator_1.default)(generateOptions), [generateOptions]);
307
+ // Ensure gql receives a string
308
+ const mutation = (0, react_1.useMemo)(() => (0, client_1.gql) `${mutationString}`, [mutationString]);
309
+ // Prepare arguments for the actual Apollo hook (context, apolloOptions)
310
+ // Variables are NOT prepared here, they are passed to the mutate function
311
+ const { context, apolloOptions } = prepareHookArgs(hookOptions, true); // Pass true for isMutation
312
+ const operationName = ((_b = mutation.definitions[0]) === null || _b === void 0 ? void 0 : _b.kind) === 'OperationDefinition' ? ((_c = mutation.definitions[0].name) === null || _c === void 0 ? void 0 : _c.value) || 'UnnamedMutation' : 'UnnamedMutation';
313
+ debug(`Preparing mutation ${operationName}`, { query: mutationString, context, options: apolloOptions, baseVariables });
314
+ // Explicitly cast apolloOptions to the correct type expected by useApolloMutation
315
+ const typedApolloOptions = apolloOptions;
316
+ // Variables are passed to the mutate function, not the hook itself usually
317
+ const [mutate, result] = (0, client_2.useMutation)(mutation, Object.assign({ client, // Pass the client explicitly if needed
318
+ context }, (typedApolloOptions)));
319
+ // Wrap the mutate function to potentially add logging or other logic
320
+ const wrappedMutate = (0, react_1.useCallback)((mutateOptions) => __awaiter(this, void 0, void 0, function* () {
321
+ // Combine base variables from generator with variables passed to this specific mutate call
322
+ // Cast the combined object to TVariables
323
+ const finalVariables = Object.assign(Object.assign({}, baseVariables), mutateOptions === null || mutateOptions === void 0 ? void 0 : mutateOptions.variables);
324
+ debug(`Executing mutation ${operationName}`, {
325
+ variables: finalVariables,
326
+ context: (mutateOptions === null || mutateOptions === void 0 ? void 0 : mutateOptions.context) || context, // Use context from mutate call or default
327
+ optimisticResponse: mutateOptions === null || mutateOptions === void 0 ? void 0 : mutateOptions.optimisticResponse,
328
+ update: !!(mutateOptions === null || mutateOptions === void 0 ? void 0 : mutateOptions.update),
329
+ });
330
+ try {
331
+ // Pass final combined variables to the actual mutate function
332
+ const mutationResult = yield mutate(Object.assign(Object.assign({}, mutateOptions), { variables: finalVariables }));
333
+ debug(`Mutation ${operationName} success`, { data: mutationResult.data });
334
+ return mutationResult;
335
+ }
336
+ catch (error) {
337
+ debug(`Mutation ${operationName} error`, { error });
338
+ // Let the error propagate to be handled by Apollo Client / component
339
+ throw error;
340
+ }
341
+ }), [mutate, operationName, context, baseVariables]); // Include baseVariables in dependency array
342
+ debug(`Mutation ${operationName} hook state`, { loading: result.loading, error: result.error, data: result.data });
343
+ return [wrappedMutate, result];
344
+ }
345
+ // Aliases for convenience (Hooks)
346
+ exports.useSelect = useQuery;
347
+ const useInsert = (genOpts, hookOpts) => useMutation(Object.assign({ operation: 'insert' }, genOpts), hookOpts);
348
+ exports.useInsert = useInsert;
349
+ const useUpdate = (genOpts, hookOpts) => useMutation(Object.assign({ operation: 'update' }, genOpts), hookOpts);
350
+ exports.useUpdate = useUpdate;
351
+ const useDelete = (genOpts, hookOpts) => useMutation(Object.assign({ operation: 'delete' }, genOpts), hookOpts);
352
+ exports.useDelete = useDelete;
353
+ exports.useSubscribe = useSubscription;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Sends the verification email to a newly registered user.
3
+ * @param email - The recipient's email address.
4
+ * @param token - The verification token.
5
+ * @returns Promise<boolean> - True if email was sent successfully.
6
+ */
7
+ export declare function sendVerificationEmail(email: string, token: string): Promise<boolean>;