3a-ecommerce-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +163 -0
  2. package/dist/chunk-PEAZVBSD.mjs +597 -0
  3. package/dist/client-DYGi_pyp.d.mts +87 -0
  4. package/dist/client-DYGi_pyp.d.ts +87 -0
  5. package/dist/index.d.mts +496 -0
  6. package/dist/index.d.ts +496 -0
  7. package/dist/index.js +17707 -0
  8. package/dist/index.mjs +17043 -0
  9. package/dist/validation/server.d.mts +50 -0
  10. package/dist/validation/server.d.ts +50 -0
  11. package/dist/validation/server.js +518 -0
  12. package/dist/validation/server.mjs +168 -0
  13. package/package.json +69 -0
  14. package/src/api/address.queries.ts +96 -0
  15. package/src/api/category.queries.ts +85 -0
  16. package/src/api/coupon.queries.ts +120 -0
  17. package/src/api/dashboard.queries.ts +35 -0
  18. package/src/api/errorHandler.ts +164 -0
  19. package/src/api/graphqlClient.ts +113 -0
  20. package/src/api/index.ts +10 -0
  21. package/src/api/logger.client.ts +89 -0
  22. package/src/api/logger.ts +135 -0
  23. package/src/api/order.queries.ts +211 -0
  24. package/src/api/product.queries.ts +144 -0
  25. package/src/api/review.queries.ts +56 -0
  26. package/src/api/user.queries.ts +232 -0
  27. package/src/assets/3A.png +0 -0
  28. package/src/assets/index.ts +1 -0
  29. package/src/assets.d.ts +29 -0
  30. package/src/auth.ts +176 -0
  31. package/src/config/jest.backend.config.js +42 -0
  32. package/src/config/jest.frontend.config.js +50 -0
  33. package/src/config/postcss.config.js +6 -0
  34. package/src/config/tailwind.config.ts +70 -0
  35. package/src/config/tsconfig.base.json +36 -0
  36. package/src/config/vite.config.ts +86 -0
  37. package/src/config/vitest.base.config.ts +74 -0
  38. package/src/config/webpack.base.config.ts +126 -0
  39. package/src/constants/index.ts +312 -0
  40. package/src/cookies.ts +104 -0
  41. package/src/helpers.ts +400 -0
  42. package/src/index.ts +32 -0
  43. package/src/validation/client.ts +287 -0
  44. package/src/validation/index.ts +3 -0
  45. package/src/validation/server.ts +32 -0
@@ -0,0 +1,144 @@
1
+ export const GET_PRODUCTS_QUERY = `
2
+ query GetProducts($page: Int, $limit: Int, $search: String, $category: String, $minPrice: Float, $maxPrice: Float, $sortBy: String, $sortOrder: String, $featured: Boolean, $includeInactive: Boolean) {
3
+ products(page: $page, limit: $limit, search: $search, category: $category, minPrice: $minPrice, maxPrice: $maxPrice, sortBy: $sortBy, sortOrder: $sortOrder, featured: $featured, includeInactive: $includeInactive) {
4
+ products {
5
+ id
6
+ name
7
+ description
8
+ price
9
+ stock
10
+ category
11
+ sellerId
12
+ isActive
13
+ imageUrl
14
+ tags
15
+ rating
16
+ reviewCount
17
+ createdAt
18
+ updatedAt
19
+ }
20
+ pagination {
21
+ page
22
+ limit
23
+ total
24
+ pages
25
+ }
26
+ }
27
+ }
28
+ `;
29
+
30
+ export const GET_PRODUCT_QUERY = `
31
+ query GetProduct($id: ID!) {
32
+ product(id: $id) {
33
+ id
34
+ name
35
+ description
36
+ price
37
+ stock
38
+ category
39
+ sellerId
40
+ seller {
41
+ id
42
+ name
43
+ email
44
+ }
45
+ isActive
46
+ imageUrl
47
+ tags
48
+ rating
49
+ reviewCount
50
+ createdAt
51
+ updatedAt
52
+ }
53
+ }
54
+ `;
55
+
56
+ export const GET_PRODUCTS_BY_SELLER_QUERY = `
57
+ query GetProductsBySeller($sellerId: String!) {
58
+ productsBySeller(sellerId: $sellerId) {
59
+ id
60
+ name
61
+ description
62
+ price
63
+ stock
64
+ category
65
+ sellerId
66
+ isActive
67
+ imageUrl
68
+ tags
69
+ rating
70
+ reviewCount
71
+ createdAt
72
+ updatedAt
73
+ }
74
+ }
75
+ `;
76
+
77
+ export const CREATE_PRODUCT_MUTATION = `
78
+ mutation CreateProduct($input: CreateProductInput!) {
79
+ createProduct(input: $input) {
80
+ id
81
+ name
82
+ description
83
+ price
84
+ stock
85
+ category
86
+ sellerId
87
+ isActive
88
+ imageUrl
89
+ tags
90
+ rating
91
+ reviewCount
92
+ createdAt
93
+ updatedAt
94
+ }
95
+ }
96
+ `;
97
+
98
+ export const UPDATE_PRODUCT_MUTATION = `
99
+ mutation UpdateProduct($id: ID!, $input: UpdateProductInput!) {
100
+ updateProduct(id: $id, input: $input) {
101
+ id
102
+ name
103
+ description
104
+ price
105
+ stock
106
+ category
107
+ sellerId
108
+ isActive
109
+ imageUrl
110
+ tags
111
+ rating
112
+ reviewCount
113
+ createdAt
114
+ updatedAt
115
+ }
116
+ }
117
+ `;
118
+
119
+ export const DELETE_PRODUCT_MUTATION = `
120
+ mutation DeleteProduct($id: ID!) {
121
+ deleteProduct(id: $id)
122
+ }
123
+ `;
124
+
125
+ export const SEARCH_PRODUCTS_QUERY = `
126
+ query SearchProducts($search: String!, $limit: Int) {
127
+ searchProducts(search: $search, limit: $limit) {
128
+ id
129
+ name
130
+ description
131
+ price
132
+ stock
133
+ category
134
+ sellerId
135
+ isActive
136
+ imageUrl
137
+ tags
138
+ rating
139
+ reviewCount
140
+ createdAt
141
+ updatedAt
142
+ }
143
+ }
144
+ `;
@@ -0,0 +1,56 @@
1
+ export const GET_PRODUCT_REVIEWS_QUERY = `
2
+ query GetProductReviews($productId: ID!, $page: Int, $limit: Int) {
3
+ productReviews(productId: $productId, page: $page, limit: $limit) {
4
+ reviews {
5
+ id
6
+ productId
7
+ userId
8
+ userName
9
+ rating
10
+ comment
11
+ helpful
12
+ createdAt
13
+ }
14
+ pagination {
15
+ page
16
+ limit
17
+ total
18
+ pages
19
+ }
20
+ }
21
+ }
22
+ `;
23
+
24
+ export const CREATE_REVIEW_MUTATION = `
25
+ mutation CreateReview($productId: ID!, $input: CreateReviewInput!) {
26
+ createReview(productId: $productId, input: $input) {
27
+ success
28
+ message
29
+ review {
30
+ id
31
+ productId
32
+ userId
33
+ userName
34
+ rating
35
+ comment
36
+ helpful
37
+ createdAt
38
+ }
39
+ }
40
+ }
41
+ `;
42
+
43
+ export const MARK_REVIEW_HELPFUL_MUTATION = `
44
+ mutation MarkReviewHelpful($reviewId: ID!) {
45
+ markReviewHelpful(reviewId: $reviewId) {
46
+ id
47
+ helpful
48
+ }
49
+ }
50
+ `;
51
+
52
+ export const DELETE_REVIEW_MUTATION = `
53
+ mutation DeleteReview($reviewId: ID!) {
54
+ deleteReview(reviewId: $reviewId)
55
+ }
56
+ `;
@@ -0,0 +1,232 @@
1
+ export const GET_USERS_QUERY = `
2
+ query GetUsers($page: Int, $limit: Int, $search: String, $role: String) {
3
+ users(page: $page, limit: $limit, search: $search, role: $role) {
4
+ users {
5
+ id
6
+ name
7
+ email
8
+ role
9
+ isActive
10
+ emailVerified
11
+ createdAt
12
+ lastLogin
13
+ }
14
+ pagination {
15
+ page
16
+ limit
17
+ total
18
+ pages
19
+ sellerCount
20
+ adminCount
21
+ customerCount
22
+ }
23
+ }
24
+ }
25
+ `;
26
+
27
+ export const GET_USER_QUERY = `
28
+ query GetUser($id: ID!) {
29
+ user(id: $id) {
30
+ id
31
+ name
32
+ email
33
+ role
34
+ isActive
35
+ emailVerified
36
+ createdAt
37
+ lastLogin
38
+ }
39
+ }
40
+ `;
41
+
42
+ export const GET_USER_BY_ID_QUERY = `
43
+ query GetUserById($id: ID!) {
44
+ getUserById(id: $id) {
45
+ user {
46
+ id
47
+ name
48
+ email
49
+ role
50
+ isActive
51
+ emailVerified
52
+ createdAt
53
+ lastLogin
54
+ }
55
+ accessToken
56
+ refreshToken
57
+ tokenExpiry
58
+ }
59
+ }
60
+ `;
61
+
62
+ export const GET_ME_QUERY = `
63
+ query GetMe {
64
+ me {
65
+ id
66
+ name
67
+ email
68
+ role
69
+ isActive
70
+ emailVerified
71
+ createdAt
72
+ lastLogin
73
+ }
74
+ }
75
+ `;
76
+
77
+ export const LOGIN_MUTATION = `
78
+ mutation Login($input: LoginInput!) {
79
+ login(input: $input) {
80
+ user {
81
+ id
82
+ name
83
+ email
84
+ role
85
+ isActive
86
+ emailVerified
87
+ createdAt
88
+ }
89
+ accessToken
90
+ refreshToken
91
+ }
92
+ }
93
+ `;
94
+
95
+ export const REGISTER_MUTATION = `
96
+ mutation Register($input: RegisterInput!) {
97
+ register(input: $input) {
98
+ user {
99
+ id
100
+ name
101
+ email
102
+ role
103
+ isActive
104
+ emailVerified
105
+ createdAt
106
+ }
107
+ accessToken
108
+ refreshToken
109
+ }
110
+ }
111
+ `;
112
+
113
+ export const GOOGLE_AUTH_MUTATION = `
114
+ mutation GoogleAuth($input: GoogleAuthInput!) {
115
+ googleAuth(input: $input) {
116
+ user {
117
+ id
118
+ name
119
+ email
120
+ role
121
+ isActive
122
+ emailVerified
123
+ createdAt
124
+ profilePicture
125
+ }
126
+ accessToken
127
+ refreshToken
128
+ }
129
+ }
130
+ `;
131
+
132
+ export const LOGOUT_MUTATION = `
133
+ mutation Logout {
134
+ logout
135
+ }
136
+ `;
137
+
138
+ export const UPDATE_USER_ROLE_MUTATION = `
139
+ mutation UpdateUserRole($id: ID!, $role: String!) {
140
+ updateUserRole(id: $id, role: $role) {
141
+ id
142
+ name
143
+ email
144
+ role
145
+ isActive
146
+ emailVerified
147
+ createdAt
148
+ lastLogin
149
+ }
150
+ }
151
+ `;
152
+
153
+ export const DELETE_USER_MUTATION = `
154
+ mutation DeleteUser($id: ID!) {
155
+ deleteUser(id: $id)
156
+ }
157
+ `;
158
+
159
+ export const SEND_VERIFICATION_EMAIL_MUTATION = `
160
+ mutation SendVerificationEmail($source: String) {
161
+ sendVerificationEmail(source: $source) {
162
+ success
163
+ message
164
+ }
165
+ }
166
+ `;
167
+
168
+ export const VERIFY_EMAIL_MUTATION = `
169
+ mutation VerifyEmail {
170
+ verifyEmail {
171
+ success
172
+ message
173
+ user {
174
+ id
175
+ name
176
+ email
177
+ role
178
+ isActive
179
+ emailVerified
180
+ createdAt
181
+ }
182
+ }
183
+ }
184
+ `;
185
+
186
+ export const FORGOT_PASSWORD_MUTATION = `
187
+ mutation ForgotPassword($email: String!, $role: String!) {
188
+ forgotPassword(email: $email, role: $role) {
189
+ success
190
+ message
191
+ resetToken
192
+ resetUrl
193
+ }
194
+ }
195
+ `;
196
+
197
+ export const RESET_PASSWORD_MUTATION = `
198
+ mutation ResetPassword($token: String!, $password: String!, $confirmPassword: String!) {
199
+ resetPassword(token: $token, password: $password, confirmPassword: $confirmPassword) {
200
+ success
201
+ message
202
+ }
203
+ }
204
+ `;
205
+
206
+ export const VALIDATE_RESET_TOKEN_QUERY = `
207
+ query ValidateResetToken($token: String!) {
208
+ validateResetToken(token: $token) {
209
+ success
210
+ message
211
+ email
212
+ }
213
+ }
214
+ `;
215
+
216
+ export const UPDATE_PROFILE_MUTATION = `
217
+ mutation UpdateProfile($input: UpdateProfileInput!) {
218
+ updateProfile(input: $input) {
219
+ success
220
+ message
221
+ user {
222
+ id
223
+ name
224
+ email
225
+ role
226
+ isActive
227
+ emailVerified
228
+ createdAt
229
+ }
230
+ }
231
+ }
232
+ `;
Binary file
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ declare module '*.png' {
2
+ const content: string;
3
+ export default content;
4
+ }
5
+
6
+ declare module '*.jpg' {
7
+ const content: string;
8
+ export default content;
9
+ }
10
+
11
+ declare module '*.jpeg' {
12
+ const content: string;
13
+ export default content;
14
+ }
15
+
16
+ declare module '*.svg' {
17
+ const content: string;
18
+ export default content;
19
+ }
20
+
21
+ declare module '*.gif' {
22
+ const content: string;
23
+ export default content;
24
+ }
25
+
26
+ declare module '*.webp' {
27
+ const content: string;
28
+ export default content;
29
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,176 @@
1
+ import { SHELL_APP_URL } from './constants';
2
+ import {
3
+ setCookie,
4
+ getCookie,
5
+ removeCookie,
6
+ AUTH_COOKIE_NAMES,
7
+ } from './cookies';
8
+
9
+ export interface AuthTokens {
10
+ accessToken: string;
11
+ refreshToken?: string;
12
+ expiresIn?: number;
13
+ }
14
+
15
+ export interface StoredAuth {
16
+ user: any;
17
+ token: string;
18
+ expiresIn: number;
19
+ }
20
+
21
+ // Cookie expiry in days
22
+ const ACCESS_TOKEN_EXPIRY = 1; // 1 day
23
+ const REFRESH_TOKEN_EXPIRY = 7; // 7 days
24
+
25
+ export const storeAuth = (data: {
26
+ user: any;
27
+ accessToken: string;
28
+ refreshToken?: string;
29
+ expiresIn?: number;
30
+ }) => {
31
+ // Store user data
32
+ setCookie(AUTH_COOKIE_NAMES.USER, JSON.stringify(data.user), {
33
+ expires: REFRESH_TOKEN_EXPIRY,
34
+ });
35
+
36
+ // Store access token
37
+ setCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN, data.accessToken, {
38
+ expires: ACCESS_TOKEN_EXPIRY,
39
+ });
40
+
41
+ // Store refresh token if provided
42
+ if (data.refreshToken) {
43
+ setCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN, data.refreshToken, {
44
+ expires: REFRESH_TOKEN_EXPIRY,
45
+ });
46
+ }
47
+
48
+ // Calculate and store token expiry time
49
+ const expiresIn = data.expiresIn || 3600;
50
+ const expiryTime = new Date().getTime() + expiresIn * 1000;
51
+ setCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY, expiryTime.toString(), {
52
+ expires: ACCESS_TOKEN_EXPIRY,
53
+ });
54
+ };
55
+
56
+ export const getStoredAuth = (): StoredAuth | null => {
57
+ const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
58
+ const token = getCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
59
+ const expiryStr = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
60
+
61
+ if (!userStr || !token) {
62
+ return null;
63
+ }
64
+
65
+ try {
66
+ const user = JSON.parse(userStr);
67
+ const expiresIn = expiryStr ? parseInt(expiryStr) - new Date().getTime() : 0;
68
+
69
+ return {
70
+ user,
71
+ token,
72
+ expiresIn: Math.max(0, expiresIn),
73
+ };
74
+ } catch {
75
+ return null;
76
+ }
77
+ };
78
+
79
+ export const isTokenExpired = (): boolean => {
80
+ const tokenExpiry = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
81
+
82
+ if (!tokenExpiry) {
83
+ return true;
84
+ }
85
+
86
+ const now = new Date().getTime();
87
+ return now > parseInt(tokenExpiry);
88
+ };
89
+
90
+ export const willTokenExpireSoon = (): boolean => {
91
+ const tokenExpiry = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
92
+
93
+ if (!tokenExpiry) {
94
+ return true;
95
+ }
96
+
97
+ const now = new Date().getTime();
98
+ const expiryTime = parseInt(tokenExpiry);
99
+ const timeUntilExpiry = expiryTime - now;
100
+
101
+ return timeUntilExpiry < 300000;
102
+ };
103
+
104
+ export const clearAuth = () => {
105
+ removeCookie(AUTH_COOKIE_NAMES.USER);
106
+ removeCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
107
+ removeCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN);
108
+ removeCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
109
+ };
110
+
111
+ export const validateUserRole = (requiredRole: string): boolean => {
112
+ const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
113
+
114
+ if (!userStr) {
115
+ return false;
116
+ }
117
+
118
+ try {
119
+ const user = JSON.parse(userStr);
120
+ return user.role === requiredRole;
121
+ } catch {
122
+ return false;
123
+ }
124
+ };
125
+
126
+ export const getCurrentUser = () => {
127
+ const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
128
+
129
+ if (!userStr) {
130
+ return null;
131
+ }
132
+
133
+ try {
134
+ return JSON.parse(userStr);
135
+ } catch {
136
+ return null;
137
+ }
138
+ };
139
+
140
+ export const getAccessToken = (): string | null => {
141
+ return getCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
142
+ };
143
+
144
+ export const getRefreshToken = (): string | null => {
145
+ return getCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN);
146
+ };
147
+
148
+ export const updateAccessToken = (newToken: string, expiresIn?: number) => {
149
+ setCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN, newToken, {
150
+ expires: ACCESS_TOKEN_EXPIRY,
151
+ });
152
+
153
+ if (expiresIn) {
154
+ const expiryTime = new Date().getTime() + expiresIn * 1000;
155
+ setCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY, expiryTime.toString(), {
156
+ expires: ACCESS_TOKEN_EXPIRY,
157
+ });
158
+ }
159
+ };
160
+
161
+ export const setupAutoRefresh = (refreshFn: () => Promise<void>) => {
162
+ const checkAndRefresh = async () => {
163
+ if (willTokenExpireSoon() && !isTokenExpired()) {
164
+ try {
165
+ await refreshFn();
166
+ } catch (error) {
167
+ clearAuth();
168
+ window.location.href = SHELL_APP_URL;
169
+ }
170
+ }
171
+ };
172
+
173
+ const interval = setInterval(checkAndRefresh, 5 * 60 * 1000);
174
+
175
+ return () => clearInterval(interval);
176
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Base Jest configuration for backend Node.js services
3
+ * @type {import('jest').Config}
4
+ */
5
+ module.exports = {
6
+ preset: 'ts-jest',
7
+ testEnvironment: 'node',
8
+ roots: ['<rootDir>/src', '<rootDir>/tests'],
9
+ testMatch: ['**/*.test.ts', '**/*.spec.ts'],
10
+ transform: {
11
+ '^.+\\.tsx?$': [
12
+ 'ts-jest',
13
+ {
14
+ tsconfig: 'tsconfig.json',
15
+ },
16
+ ],
17
+ },
18
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
19
+ collectCoverageFrom: [
20
+ 'src/**/*.ts',
21
+ '!src/**/*.d.ts',
22
+ '!src/swagger/**',
23
+ '!src/index.ts',
24
+ ],
25
+ coverageDirectory: 'coverage',
26
+ coverageReporters: ['text', 'lcov', 'html'],
27
+ coverageThreshold: {
28
+ global: {
29
+ branches: 60,
30
+ functions: 70,
31
+ lines: 70,
32
+ statements: 70,
33
+ },
34
+ },
35
+ setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
36
+ testTimeout: 10000,
37
+ verbose: true,
38
+ moduleNameMapper: {
39
+ '^@e-commerce/utils/server$': '<rootDir>/tests/__mocks__/utils.ts',
40
+ '^@e-commerce/types$': '<rootDir>/../../packages/types/src/index.ts',
41
+ },
42
+ };