@gaurav_bhandari/common-frontend-services 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 (56) hide show
  1. package/README.md +40 -0
  2. package/dist/bin/cli.js +22 -0
  3. package/dist/src/cli/commands/add.js +104 -0
  4. package/dist/src/cli/commands/list.js +19 -0
  5. package/dist/src/cli/registry.js +125 -0
  6. package/package.json +39 -0
  7. package/src/services/API-Service/baseApiService.ts +296 -0
  8. package/src/services/API-Service/csrfAPIService.ts +93 -0
  9. package/src/services/API-Service/docs/WhatIsBaseApiService.md +121 -0
  10. package/src/services/API-Service/docs/WhatIsCsrfApiService.md +67 -0
  11. package/src/services/Architecture-Reports-Service/architecture-checklist.md +54 -0
  12. package/src/services/Architecture-Reports-Service/report-generation-prompt.md +60 -0
  13. package/src/services/Authorization-Service/docs/WhatIsRBACService.md +112 -0
  14. package/src/services/Authorization-Service/permissions.ts +16 -0
  15. package/src/services/Authorization-Service/rbacService.ts +63 -0
  16. package/src/services/Authorization-Service/rolePermissions.ts +10 -0
  17. package/src/services/Authorization-Service/userPermissions/adminPermissions.ts +13 -0
  18. package/src/services/Cache-Service/README.md +50 -0
  19. package/src/services/Cache-Service/cacheService.ts +142 -0
  20. package/src/services/Cache-Service/simple-explanation.md +55 -0
  21. package/src/services/Cache-Service/sw-strategies.ts +51 -0
  22. package/src/services/Date-Service/dateService.ts +93 -0
  23. package/src/services/Date-Service/docs/WhatIsDateService.md +63 -0
  24. package/src/services/Environment-Config-Service/docs/WhatIsEnvironmentConfigService.md +68 -0
  25. package/src/services/Environment-Config-Service/environmentConfigService.ts +112 -0
  26. package/src/services/Environment-Config-Service/simple-explanation.md +49 -0
  27. package/src/services/Feature-Flag-Service/docs/WhatIsFeatureFlagService.md +98 -0
  28. package/src/services/Feature-Flag-Service/featureFlagService.ts +91 -0
  29. package/src/services/File-Service/docs/WhatIsFileService.md +78 -0
  30. package/src/services/File-Service/fileService.ts +224 -0
  31. package/src/services/File-Service/simple-explanation.md +49 -0
  32. package/src/services/Google-Login-Service/README.md +48 -0
  33. package/src/services/Google-Login-Service/googleLoginService.ts +209 -0
  34. package/src/services/GraphQl-Service/docs/WhatIsGraphQLService.md +101 -0
  35. package/src/services/GraphQl-Service/graphqlService.ts +81 -0
  36. package/src/services/Logger-Service/README.md +44 -0
  37. package/src/services/Logger-Service/loggerService.ts +125 -0
  38. package/src/services/Logger-Service/simple-explanation.md +43 -0
  39. package/src/services/Logger-Service/verify-logger.ts +62 -0
  40. package/src/services/Monitoring-Service/README.md +48 -0
  41. package/src/services/Monitoring-Service/monitoringService.ts +229 -0
  42. package/src/services/Monitoring-Service/simple-explanation.md +48 -0
  43. package/src/services/Monitoring-Service/verify-monitoring.ts +74 -0
  44. package/src/services/Security-Reports-Service/report-generation-prompt.md +52 -0
  45. package/src/services/Security-Reports-Service/security-checklist.md +66 -0
  46. package/src/services/Storage-Service/docs/WhatIsStorageService.md +70 -0
  47. package/src/services/Storage-Service/indexedDBService.ts +103 -0
  48. package/src/services/Storage-Service/localStorageService.ts +42 -0
  49. package/src/services/Storage-Service/sessionStorageService.ts +42 -0
  50. package/src/services/Validation-Service/form.ts +66 -0
  51. package/src/services/Validation-Service/rules.ts +338 -0
  52. package/src/services/Worker-Service/docs/WhatIsWorkerService.md +57 -0
  53. package/src/services/Worker-Service/workerService.ts +122 -0
  54. package/src/services/i18n-Service/docs/WhatIsI18nService.md +108 -0
  55. package/src/services/i18n-Service/i18nService.ts +127 -0
  56. package/src/services/i18n-Service/locales/en.json +17 -0
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Frontend Services Collection
2
+
3
+ A comprehensive collection of framework-agnostic frontend services designed for scalability, maintainability, and performance.
4
+
5
+ ## 📦 Services Overview
6
+
7
+ This repository contains the following services. Each service directory includes its own `docs` folder with detailed usage instructions and examples.
8
+
9
+ ### Core Foundation
10
+
11
+ - **[API Service](./API-Service)**: Base API handling wrapper around Axios with interceptors, error handling, and CSRF protection.
12
+ - **[Environment Config Service](./Environment-Config-Service)**: Centralized configuration management for environment variables and runtime settings.
13
+ - **[Logger Service](./Logger-Service)**: structured logging service with support for multiple transports (Console, External).
14
+ - **[Monitoring Service](./Monitoring-Service)**: Error tracking and performance monitoring (Web Vitals) with backend reporting capabilities.
15
+
16
+ ### Application Logic
17
+
18
+ - **[Authorization Service](./Authorization-Service)**: Role-Based Access Control (RBAC) service for managing user permissions and route guards.
19
+ - **[Cache Service](./Cache-Service)**: Two-layer caching mechanism (Memory + IndexedDB) with LRU eviction policies for performance optimization.
20
+ - **[Feature Flag Service](./Feature-Flag-Service)**: Remote feature toggling service with retry logic and caching.
21
+ - **[Google Login Service](./Google-Login-Service)**: Integration wrapper for Google Identity Services SDK for easy authentication.
22
+ - **[GraphQL Service](./GraphQl-Service)**: Client for handling GraphQL queries and mutations.
23
+ - **[i18n Service](./i18n-Service)**: Internationalization service for managing translations and locale settings.
24
+ - **[Validation Service](./Validation-Service)**: Pure TypeScript, framework-agnostic form validation logic.
25
+ - **[Worker Service](./Worker-Service)**: Utility for safely executing heavy computations in Web Workers with timeout support.
26
+
27
+ ### Data & Storage
28
+
29
+ - **[Date Service](./Date-Service)**: Date manipulation and formatting utilities (wrapper around `date-fns`) with time zone support.
30
+ - **[File Service](./File-Service)**: Robust file handling service including support for chunked uploads and progress tracking.
31
+ - **[Storage Service](./Storage-Service)**: Type-safe wrappers for LocalStorage, SessionStorage, and IndexedDB.
32
+
33
+ ### Documentation & Standards
34
+
35
+ - **[Architecture Reports](./Architecture-Reports-Service)**: Checklists and guidelines for maintaining architectural health and standards.
36
+ - **[Security Reports](./Security-Reports-Service)**: Security best practices, checklists (including OWASP Top 10), and audit guides.
37
+
38
+ ## 📚 Documentation
39
+
40
+ For detailed implementation details, API references, and usage examples, please refer to the `docs` folder located within each service's directory.
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const list_1 = require("../src/cli/commands/list");
6
+ const add_1 = require("../src/cli/commands/add");
7
+ const program = new commander_1.Command();
8
+ program
9
+ .name("frontend-services")
10
+ .description("CLI to add frontend services to your project")
11
+ .version("1.0.0");
12
+ program
13
+ .command("list")
14
+ .description("List all available services")
15
+ .action(list_1.listCommand);
16
+ program
17
+ .command("add")
18
+ .description("Add a service to your project")
19
+ .argument("<service>", "Name of the service to add")
20
+ .option("-p, --path <path>", "Path to install services", "src/services")
21
+ .action(add_1.addCommand);
22
+ program.parse();
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.addCommand = addCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const prompts_1 = __importDefault(require("prompts"));
11
+ const registry_1 = require("../registry");
12
+ async function addCommand(serviceName, options) {
13
+ const service = registry_1.REGISTRY[serviceName];
14
+ if (!service) {
15
+ console.error(chalk_1.default.red(`Service '${serviceName}' not found.`));
16
+ console.log(chalk_1.default.yellow("Run 'npx frontend-services list' to see available services."));
17
+ process.exit(1);
18
+ }
19
+ console.log(chalk_1.default.bold.blue(`\nPreparing to add ${service.name}...`));
20
+ // Confirm installation path
21
+ const response = await (0, prompts_1.default)({
22
+ type: "text",
23
+ name: "path",
24
+ message: "Where should we install this service?",
25
+ initial: options.path,
26
+ });
27
+ const installPath = path_1.default.resolve(process.cwd(), response.path || options.path);
28
+ // Check dependencies
29
+ if (service.dependencies.length > 0) {
30
+ console.log(chalk_1.default.yellow(`\nThis service requires the following dependencies:`));
31
+ console.log(chalk_1.default.cyan(service.dependencies.join(", ")));
32
+ const { installDeps } = await (0, prompts_1.default)({
33
+ type: "confirm",
34
+ name: "installDeps",
35
+ message: "Do you want to see the install command?",
36
+ initial: true,
37
+ });
38
+ if (installDeps) {
39
+ console.log(chalk_1.default.green(`\nRun: npm install ${service.dependencies.join(" ")}`));
40
+ }
41
+ }
42
+ console.log(`\nCopying files to ${chalk_1.default.gray(installPath)}...`);
43
+ // Simple resolution strategy:
44
+ // We assume the CLI is running from within the package
45
+ // The source files are in "../../services" relative to this file
46
+ // But when installed as a package, it might be different.
47
+ // Ideally, we publish the `services` folder.
48
+ // For local dev/monorepo usage:
49
+ const sourceRoot = path_1.default.resolve(__dirname, "../../");
50
+ // NOTE: In a real npm package, we might need to adjust `sourceRoot`
51
+ // depending on how tsc builds the structure (e.g. dist/cli/commands -> dist/services)
52
+ // Wait, I moved services to src/services.
53
+ // If I compile with tsc, they will be in dist/services.
54
+ // sourceRoot should be calculating path relative to this file.
55
+ // Let's assume we run ts-node or compiled JS.
56
+ // ../../services should work if stucture is preserved.
57
+ try {
58
+ // Ensure target dir exists
59
+ await fs_extra_1.default.ensureDir(installPath);
60
+ for (const fileRelativePath of service.files) {
61
+ // fileRelativePath in registry is like "services/API-Service/baseApiService.ts"
62
+ // We need to resolve the source path correctly.
63
+ // The registry paths include "services/" prefix which I added earlier?
64
+ // Let's check registry. "services/API-Service/..."
65
+ // I define sourceRoot as "../../services".
66
+ // So I should strip "services/" from registry path or adjust sourceRoot.
67
+ // Let's adjust algorithm:
68
+ // Source is: <package-root>/src/services/<Service-Folder>/<file>
69
+ // Registry says: "services/<Service-Folder>/<file>"
70
+ // We need to find where the package root is.
71
+ // __dirname is .../src/cli/commands
72
+ // package root is .../
73
+ const packageRoot = path_1.default.resolve(__dirname, "../../..");
74
+ const sourceFile = path_1.default.resolve(packageRoot, "src", fileRelativePath.replace("services/", "services/"));
75
+ // The registry entries start with "services/".
76
+ // let's just resolve from package root for now.
77
+ // Wait, if users install this package, they won't have the source TS files in `src`.
78
+ // They will likely have compiled JS in `dist` OR we publish the `src` folder.
79
+ // For a library like this meant to copy code, we usually publish the raw TS files
80
+ // alongside the CLI.
81
+ // CHECK: Does "src" exist in the published package?
82
+ // We should ensure "files" in package.json includes "src".
83
+ // Let's assume for this task we are running locally or from a properly built package.
84
+ const fileName = path_1.default.basename(fileRelativePath);
85
+ const targetFile = path_1.default.join(installPath, fileName);
86
+ // We need to verify source existence
87
+ if (!fs_extra_1.default.existsSync(sourceFile)) {
88
+ // Try a fallback for local dev vs production build
89
+ // If we are in dist/, we might need to look elsewhere?
90
+ // Actually, simplest is to look at adjacent "services" folder if we bundle them.
91
+ // Let's try to resolve relative to __dirname first if we are in strict mode.
92
+ // But for now, let's trust the relative path from package root.
93
+ console.warn(chalk_1.default.yellow(`Warning: Source file not found at ${sourceFile}`));
94
+ continue;
95
+ }
96
+ await fs_extra_1.default.copy(sourceFile, targetFile);
97
+ console.log(` Created ${chalk_1.default.green(fileName)}`);
98
+ }
99
+ console.log(chalk_1.default.bold.green("\nService added successfully! 🚀"));
100
+ }
101
+ catch (error) {
102
+ console.error(chalk_1.default.red("Error copying files:"), error);
103
+ }
104
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listCommand = listCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const registry_1 = require("../registry");
9
+ async function listCommand() {
10
+ console.log(chalk_1.default.bold.blue("\nAvailable Services:\n"));
11
+ for (const [key, service] of Object.entries(registry_1.REGISTRY)) {
12
+ console.log(`${chalk_1.default.green(key)}: ${service.name}`);
13
+ console.log(` ${chalk_1.default.gray(service.description)}`);
14
+ if (service.dependencies.length > 0) {
15
+ console.log(` Dependencies: ${chalk_1.default.yellow(service.dependencies.join(", "))}`);
16
+ }
17
+ console.log("");
18
+ }
19
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REGISTRY = void 0;
4
+ exports.REGISTRY = {
5
+ api: {
6
+ name: "API Service",
7
+ description: "Axios wrapper with interceptors and error handling",
8
+ files: [
9
+ "services/API-Service/baseApiService.ts",
10
+ "services/API-Service/csrfAPIService.ts",
11
+ ],
12
+ dependencies: ["axios"],
13
+ devDependencies: [],
14
+ },
15
+ authorization: {
16
+ name: "Authorization Service",
17
+ description: "RBAC with dynamic roles and route guards",
18
+ files: [
19
+ "services/Authorization-Service/permissions.ts",
20
+ "services/Authorization-Service/rbacService.ts",
21
+ "services/Authorization-Service/rolePermissions.ts",
22
+ ],
23
+ dependencies: [],
24
+ devDependencies: [],
25
+ },
26
+ cache: {
27
+ name: "Cache Service",
28
+ description: "Two-layer cache (Memory + IndexedDB) with LRU",
29
+ files: [
30
+ "services/Cache-Service/cacheService.ts",
31
+ "services/Cache-Service/simple-explanation.md",
32
+ ],
33
+ dependencies: ["idb"],
34
+ devDependencies: [],
35
+ },
36
+ date: {
37
+ name: "Date Service",
38
+ description: "Date manipulation wrapper around date-fns",
39
+ files: ["services/Date-Service/dateService.ts"],
40
+ dependencies: ["date-fns", "date-fns-tz"],
41
+ devDependencies: [],
42
+ },
43
+ env: {
44
+ name: "Environment Config",
45
+ description: "Centralized configuration management",
46
+ files: [
47
+ "services/Environment-Config-Service/environmentConfigService.ts",
48
+ "services/Environment-Config-Service/simple-explanation.md",
49
+ ],
50
+ dependencies: [],
51
+ devDependencies: [],
52
+ },
53
+ "feature-flag": {
54
+ name: "Feature Flag Service",
55
+ description: "Remote feature toggles with retry logic",
56
+ files: ["services/Feature-Flag-Service/featureFlagService.ts"],
57
+ dependencies: [],
58
+ devDependencies: [],
59
+ },
60
+ file: {
61
+ name: "File Service",
62
+ description: "File upload (standard/chunked), preview, compression",
63
+ files: [
64
+ "services/File-Service/fileService.ts",
65
+ "services/File-Service/simple-explanation.md",
66
+ ],
67
+ dependencies: [],
68
+ devDependencies: [],
69
+ },
70
+ "google-login": {
71
+ name: "Google Login Service",
72
+ description: "Google Identity Services SDK wrapper",
73
+ files: ["services/Google-Login-Service/googleLoginService.ts"],
74
+ dependencies: [],
75
+ devDependencies: ["@types/google.accounts"],
76
+ },
77
+ logger: {
78
+ name: "Logger Service",
79
+ description: "Structured logging with multiple transports",
80
+ files: [
81
+ "services/Logger-Service/loggerService.ts",
82
+ "services/Logger-Service/simple-explanation.md",
83
+ ],
84
+ dependencies: [],
85
+ devDependencies: [],
86
+ },
87
+ monitoring: {
88
+ name: "Monitoring Service",
89
+ description: "Error tracking and Web Vitals monitoring",
90
+ files: [
91
+ "services/Monitoring-Service/monitoringService.ts",
92
+ "services/Monitoring-Service/simple-explanation.md",
93
+ ],
94
+ dependencies: [],
95
+ devDependencies: [],
96
+ },
97
+ validation: {
98
+ name: "Validation Service",
99
+ description: "Pure TypeScript form validation",
100
+ files: [
101
+ "services/Validation-Service/form.ts",
102
+ "services/Validation-Service/rules.ts",
103
+ ],
104
+ dependencies: [],
105
+ devDependencies: [],
106
+ },
107
+ worker: {
108
+ name: "Worker Service",
109
+ description: "Safe Web Worker execution wrapper",
110
+ files: ["services/Worker-Service/workerService.ts"],
111
+ dependencies: [],
112
+ devDependencies: [],
113
+ },
114
+ storage: {
115
+ name: "Storage Service",
116
+ description: "Wrappers for LocalStorage, SessionStorage, IndexedDB",
117
+ files: [
118
+ "services/Storage-Service/indexedDBService.ts",
119
+ "services/Storage-Service/localStorageService.ts",
120
+ "services/Storage-Service/sessionStorageService.ts",
121
+ ],
122
+ dependencies: ["idb"],
123
+ devDependencies: [],
124
+ },
125
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@gaurav_bhandari/common-frontend-services",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "bin": {
6
+ "frontend-services": "./dist/bin/cli.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "src/services"
11
+ ],
12
+ "scripts": {
13
+ "test": "echo \"Error: no test specified\" && exit 1",
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build",
16
+ "cli": "npx tsx bin/cli.ts"
17
+ },
18
+ "keywords": [],
19
+ "author": "",
20
+ "license": "ISC",
21
+ "description": "",
22
+ "dependencies": {
23
+ "axios": "^1.13.5",
24
+ "chalk": "^5.4.1",
25
+ "commander": "^13.1.0",
26
+ "date-fns": "^4.1.0",
27
+ "date-fns-tz": "^3.2.0",
28
+ "fs-extra": "^11.3.0",
29
+ "idb": "^8.0.3",
30
+ "prompts": "^2.4.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/fs-extra": "^11.0.4",
34
+ "@types/node": "^25.2.3",
35
+ "@types/prompts": "^2.4.9",
36
+ "tsx": "^4.21.0",
37
+ "typescript": "^5.9.3"
38
+ }
39
+ }
@@ -0,0 +1,296 @@
1
+ import axios from "axios";
2
+ import type {
3
+ AxiosInstance,
4
+ AxiosResponse,
5
+ AxiosError,
6
+ InternalAxiosRequestConfig,
7
+ } from "axios";
8
+ import csrfService from "./csrfAPIService";
9
+
10
+ interface EnvironmentConfig {
11
+ isDevelopment: boolean;
12
+ isProduction: boolean;
13
+ isStaging: boolean;
14
+ environment: string;
15
+ enableLogging: boolean;
16
+ enableDetailedErrors: boolean;
17
+ apiTimeout: number;
18
+ }
19
+
20
+ class BaseService {
21
+ private static instance: BaseService;
22
+ private readonly api: AxiosInstance;
23
+ private readonly config: EnvironmentConfig;
24
+
25
+ private constructor() {
26
+ const env = (import.meta as any).env || {};
27
+
28
+ // Environment detection and configuration
29
+ this.config = this.initializeEnvironmentConfig(env);
30
+
31
+ this.api = axios.create({
32
+ baseURL: env.VITE_API_BASE_URL,
33
+ timeout: this.config.apiTimeout,
34
+ withCredentials: true, // Always true for cookie-based auth
35
+ xsrfCookieName: "XSRF-TOKEN",
36
+ xsrfHeaderName: "X-XSRF-TOKEN",
37
+ headers: {
38
+ // Don't set Content-Type here - let axios auto-detect based on data type
39
+ // For JSON: axios will set 'application/json'
40
+ // For FormData: axios will set 'multipart/form-data' with boundary
41
+ Accept: "application/json",
42
+ ...(this.config.isDevelopment && { "X-Debug-Mode": "true" }),
43
+ },
44
+ });
45
+
46
+ this.setupInterceptors();
47
+ }
48
+
49
+ private initializeEnvironmentConfig(env: any): EnvironmentConfig {
50
+ // Prefer explicit VITE_ENVIRONMENT; fallback to Vite MODE; then legacy vars
51
+ const detected = String(
52
+ env.VITE_APP_ENV || env.NODE_ENV || "development",
53
+ ).toLowerCase();
54
+
55
+ const isLocal = detected === "local";
56
+ const isDevelopment = detected === "development" || isLocal;
57
+
58
+ return {
59
+ environment: detected,
60
+ isDevelopment,
61
+ isProduction: detected === "production",
62
+ isStaging: detected === "staging",
63
+ enableLogging:
64
+ env.VITE_ENABLE_API_LOGGING !== "false" && detected !== "production",
65
+ enableDetailedErrors: isDevelopment,
66
+ apiTimeout: parseInt(env.VITE_API_TIMEOUT || "30000", 10),
67
+ };
68
+ }
69
+
70
+ public static getInstance(): BaseService {
71
+ if (!BaseService.instance) {
72
+ BaseService.instance = new BaseService();
73
+ }
74
+ return BaseService.instance;
75
+ }
76
+
77
+ private setupInterceptors(): void {
78
+ // Request interceptor
79
+ this.api.interceptors.request.use(
80
+ async (config: InternalAxiosRequestConfig) => {
81
+ if (!config.headers["X-XSRF-TOKEN"]) {
82
+ if (
83
+ ["post", "put", "patch", "delete"].includes(
84
+ config.method?.toLowerCase() || "",
85
+ )
86
+ ) {
87
+ try {
88
+ await csrfService.ensureToken();
89
+ } catch (error) {
90
+ this.logError("CSRF token ensure failed:", error);
91
+ }
92
+ }
93
+
94
+ //fallback
95
+ const cookieStr =
96
+ typeof document !== "undefined" ? document.cookie : "";
97
+ const match = cookieStr
98
+ ?.split("; ")
99
+ .find((row) => row.startsWith("XSRF-TOKEN="))
100
+ ?.split("=")[1];
101
+
102
+ if (match) {
103
+ const decoded = decodeURIComponent(match);
104
+ // Axios v1 headers can be AxiosHeaders; support both APIs
105
+ // Type guard: config.headers is AxiosHeaders
106
+ if (typeof config.headers.set === "function") {
107
+ config.headers.set("X-XSRF-TOKEN", decoded);
108
+ } else {
109
+ (config.headers as any)["X-XSRF-TOKEN"] = decoded;
110
+ }
111
+ }
112
+
113
+ return config;
114
+ }
115
+
116
+ return config;
117
+ },
118
+ (error: AxiosError) => {
119
+ this.logError("Request Error:", error);
120
+ return Promise.reject(error);
121
+ },
122
+ );
123
+
124
+ // Response interceptor
125
+ this.api.interceptors.response.use(
126
+ (response: AxiosResponse) => {
127
+ return response;
128
+ },
129
+ async (error: AxiosError) => {
130
+ if (error.response) {
131
+ await this.handleResponseError(error);
132
+ } else if (error.request) {
133
+ this.logError(
134
+ "Network Error: No response received",
135
+ this.config.enableDetailedErrors
136
+ ? error.request
137
+ : "Network issue occurred",
138
+ );
139
+ } else {
140
+ this.logError("Request Setup Error:", error.message);
141
+ }
142
+ return Promise.reject(error);
143
+ },
144
+ );
145
+ }
146
+
147
+ private async handleResponseError(error: AxiosError): Promise<void> {
148
+ const errorDetails = this.config.enableDetailedErrors
149
+ ? { data: error.response?.data, headers: error.response?.headers }
150
+ : "Enable detailed errors in development environment";
151
+
152
+ switch (error.response?.status) {
153
+ case 400:
154
+ this.logError("Bad Request:", errorDetails);
155
+ break;
156
+ case 401: {
157
+ this.logWarning("Unauthorized: Token might be invalid or expired");
158
+ // Only call handleUnauthorized for requests that are NOT excluded endpoints
159
+ const excludedEndpoints = ["/login"];
160
+ if (
161
+ error.config?.url &&
162
+ !excludedEndpoints.some((endpoint) =>
163
+ error.config?.url?.includes(endpoint),
164
+ )
165
+ ) {
166
+ await this.handleUnauthorized();
167
+ }
168
+ break;
169
+ }
170
+ case 402: {
171
+ this.logError("Payment Required:", errorDetails);
172
+ // Optionally, redirect to a payment or subscription page
173
+ this.handleUnSubscribed();
174
+ break;
175
+ }
176
+ case 403:
177
+ this.logWarning("Forbidden: No permission to access this resource");
178
+ break;
179
+ case 404:
180
+ this.logWarning("Not Found: Resource does not exist");
181
+ break;
182
+ case 428:
183
+ this.logWarning(
184
+ "Account Not Verified: Please verify your account to proceed.",
185
+ );
186
+ await this.handleUnVerified();
187
+ break;
188
+ case 500:
189
+ this.logError(
190
+ "Internal Server Error:",
191
+ this.config.enableDetailedErrors
192
+ ? errorDetails
193
+ : "Server error occurred",
194
+ );
195
+ break;
196
+ default:
197
+ this.logError(`Error ${error.response?.status}:`, error.message);
198
+ }
199
+ }
200
+
201
+ // Environment-aware logging methods
202
+ private logError(message: string, data?: any): void {
203
+ if (this.config.enableLogging) {
204
+ console.error(`❌ ${message}`, data);
205
+ }
206
+ }
207
+
208
+ private logWarning(message: string, data?: any): void {
209
+ if (this.config.enableLogging) {
210
+ console.warn(`⚠️ ${message}`, data);
211
+ }
212
+ }
213
+
214
+ private async handleUnauthorized(): Promise<void> {
215
+ console.log(
216
+ "Authentication Error",
217
+ "Your session has expired. Please log in again.",
218
+ );
219
+ }
220
+
221
+ private async handleUnSubscribed(): Promise<void> {
222
+ console.log(
223
+ "Subscription Error",
224
+ "Your subscription has expired. Please renew your subscription to continue using our services.",
225
+ );
226
+ }
227
+
228
+ private async handleUnVerified(): Promise<void> {
229
+ console.log(
230
+ "Verification Error",
231
+ "Your account is not verified. Please verify your account to continue using our services.",
232
+ );
233
+ }
234
+
235
+ // Public getter for environment info
236
+ public getEnvironmentInfo(): EnvironmentConfig {
237
+ return { ...this.config };
238
+ }
239
+
240
+ // HTTP methods remain the same
241
+ public async get<T>(url: string, params?: any): Promise<T> {
242
+ const response = await this.api.get<T>(url, { params });
243
+ return response.data;
244
+ }
245
+
246
+ public async post<T>(url: string, data?: any): Promise<T> {
247
+ const response = await this.api.post<T>(url, data);
248
+ return response.data;
249
+ }
250
+
251
+ public async put<T>(url: string, data?: any): Promise<T> {
252
+ const response = await this.api.put<T>(url, data);
253
+ return response.data;
254
+ }
255
+
256
+ public async delete<T>(url: string): Promise<T> {
257
+ const response = await this.api.delete<T>(url);
258
+ return response.data;
259
+ }
260
+
261
+ public async patch<T>(url: string, data?: any): Promise<T> {
262
+ const response = await this.api.patch<T>(url, data);
263
+ return response.data;
264
+ }
265
+
266
+ /**
267
+ * POST request that returns a Blob response (for binary data like images)
268
+ * Useful for fetching images, PDFs, or other binary files
269
+ */
270
+ public async postBlob(url: string, data?: any): Promise<Blob> {
271
+ const response = await this.api.post(url, data, {
272
+ responseType: "blob",
273
+ });
274
+ return response.data;
275
+ }
276
+
277
+ /**
278
+ * POST request for binary data (e.g., PDF files)
279
+ * @param endpoint - API endpoint
280
+ * @param data - Request payload
281
+ * @returns Promise<ArrayBuffer> - Binary response data
282
+ */
283
+ public async postBinary(endpoint: string, data: any): Promise<ArrayBuffer> {
284
+ try {
285
+ const response = await this.api.post(endpoint, data, {
286
+ responseType: "arraybuffer",
287
+ });
288
+ return response.data;
289
+ } catch (error) {
290
+ this.logError(`Binary request failed [${endpoint}]:`, error);
291
+ throw error;
292
+ }
293
+ }
294
+ }
295
+
296
+ export default BaseService.getInstance();