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.
- package/APOLLO.md +101 -174
- package/GENERATOR.md +4 -5
- package/README.md +9 -5
- package/dist/lib/apollo.d.ts +11 -9
- package/dist/lib/apollo.js +127 -90
- package/dist/lib/auth.d.ts +16 -0
- package/dist/lib/auth.js +159 -0
- package/dist/lib/authDbUtils.d.ts +48 -0
- package/dist/lib/authDbUtils.js +183 -0
- package/dist/lib/client.d.ts +81 -0
- package/dist/lib/client.js +353 -0
- package/dist/lib/email.d.ts +7 -0
- package/dist/lib/email.js +90 -0
- package/dist/lib/generator.d.ts +2 -7
- package/dist/lib/generator.js +394 -392
- package/dist/lib/graphql-proxy.d.ts +6 -0
- package/dist/lib/graphql-proxy.js +385 -0
- package/dist/lib/hasura-schema.d.ts +1 -0
- package/dist/lib/hasura-schema.js +65 -0
- package/dist/lib/hasura.d.ts +16 -0
- package/dist/lib/hasura.js +102 -0
- package/dist/lib/hooks/useCheckConnection.d.ts +7 -0
- package/dist/lib/hooks/useCheckConnection.js +62 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/test-utils.d.ts +11 -0
- package/dist/lib/test-utils.js +59 -0
- package/dist/lib/tokenUtils.d.ts +14 -0
- package/dist/lib/tokenUtils.js +94 -0
- package/dist/package.json +22 -4
- package/dist/public/hasura-schema.json +7995 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +17 -6
@@ -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>;
|