@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
@@ -0,0 +1,98 @@
1
+ # FeatureFlagService
2
+
3
+ The `FeatureFlagService` provides a simple, framework-agnostic way to manage feature toggles in your application. It allows you to enable or disable features remotely without redeploying code.
4
+
5
+ ## Overview
6
+
7
+ Feature flags are boolean values (true/false) associated with a key (e.g., `new-dashboard`). The service fetches these flags from the backend at startup and makes them available throughout the app.
8
+
9
+ ### Key Features
10
+
11
+ - **Backend Driven**: Flags are fetched from an API endpoint (`GET /feature-flags`).
12
+ - **Framework Agnostic**: Can be used in Vue, React, or plain TypeScript files.
13
+ - **Singleton**: Ensures flags are fetched only once per session.
14
+
15
+ ## How to Use
16
+
17
+ ### 1. Initialization
18
+
19
+ You must initialize the service before using it, typically in your application's entry point.
20
+
21
+ **Vue (`main.ts`):**
22
+
23
+ ```typescript
24
+ import { createApp } from "vue";
25
+ import App from "./App.vue";
26
+ import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
27
+
28
+ async function init() {
29
+ await featureFlagService.init(); // Fetch flags
30
+ createApp(App).mount("#app");
31
+ }
32
+
33
+ init();
34
+ ```
35
+
36
+ **React (`main.tsx`):**
37
+
38
+ ```typescript
39
+ import React from "react";
40
+ import ReactDOM from "react-dom/client";
41
+ import App from "./App";
42
+ import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
43
+
44
+ featureFlagService.init().then(() => {
45
+ ReactDOM.createRoot(document.getElementById("root")!).render(
46
+ <React.StrictMode>
47
+ <App />
48
+ </React.StrictMode>
49
+ );
50
+ });
51
+ ```
52
+
53
+ ### 2. Checking Flags
54
+
55
+ Use `isEnabled(key)` to check if a feature is active.
56
+
57
+ ```typescript
58
+ import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
59
+
60
+ if (featureFlagService.isEnabled("new-dashboard")) {
61
+ // Show new dashboard
62
+ } else {
63
+ // Show legacy dashboard
64
+ }
65
+ ```
66
+
67
+ ### 3. Framework specific integration
68
+
69
+ You can easily create wrapper components or directives.
70
+
71
+ **Vue Directive (`v-feature`)**:
72
+
73
+ ```typescript
74
+ // directives/feature.ts
75
+ import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
76
+
77
+ export const vFeature = {
78
+ mounted(el, binding) {
79
+ if (!featureFlagService.isEnabled(binding.value)) {
80
+ el.parentNode?.removeChild(el);
81
+ }
82
+ },
83
+ };
84
+ ```
85
+
86
+ **React Component (`<Feature>`)**:
87
+
88
+ ```tsx
89
+ import { featureFlagService } from "@/Feature-Flag-Service/featureFlagService";
90
+
91
+ const Feature = ({ name, children }) => {
92
+ return featureFlagService.isEnabled(name) ? children : null;
93
+ };
94
+ ```
95
+
96
+ ## Configuration
97
+
98
+ The service attempts to fetch flags from `GET /feature-flags`. Ensure your `BaseApiService` is correctly configured and that your backend provides this endpoint.
@@ -0,0 +1,91 @@
1
+ import BaseService from "../API-Service/baseApiService";
2
+
3
+ export class FeatureFlagService {
4
+ private static instance: FeatureFlagService;
5
+ private flags: Record<string, boolean> = {};
6
+ private isInitialized = false;
7
+ private fetchPromise: Promise<void> | null = null;
8
+
9
+ private constructor() {
10
+ // Optionally load default flags from local storage or env
11
+ }
12
+
13
+ public static getInstance(): FeatureFlagService {
14
+ if (!FeatureFlagService.instance) {
15
+ FeatureFlagService.instance = new FeatureFlagService();
16
+ }
17
+ return FeatureFlagService.instance;
18
+ }
19
+
20
+ /**
21
+ * Initialize the service by fetching flags from the backend.
22
+ * Call this in your main entry point (main.ts/App.tsx).
23
+ */
24
+ public async init(): Promise<void> {
25
+ if (this.isInitialized) return;
26
+
27
+ // Prevent multiple simultaneous fetches
28
+ if (this.fetchPromise) {
29
+ return this.fetchPromise;
30
+ }
31
+
32
+ this.fetchPromise = this.fetchFlags();
33
+ await this.fetchPromise;
34
+ this.isInitialized = true;
35
+ this.fetchPromise = null;
36
+ }
37
+
38
+ /**
39
+ * Force refresh flags from the server.
40
+ * Useful if the user changes settings or switches accounts.
41
+ */
42
+ public async refresh(): Promise<void> {
43
+ this.isInitialized = false;
44
+ await this.init();
45
+ }
46
+
47
+ /**
48
+ * Check if a feature is enabled.
49
+ * @param key The feature flag key (e.g., 'new-dashboard')
50
+ * @param defaultValue Default value if flag is missing (default: false)
51
+ */
52
+ public isEnabled(key: string, defaultValue = false): boolean {
53
+ if (!this.isInitialized) {
54
+ console.warn(
55
+ `FeatureFlagService: Checking flag '${key}' before initialization.`,
56
+ );
57
+ }
58
+ return this.flags[key] ?? defaultValue;
59
+ }
60
+
61
+ /**
62
+ * Get all currently loaded flags.
63
+ */
64
+ public getFlags(): Record<string, boolean> {
65
+ return { ...this.flags };
66
+ }
67
+
68
+ private async fetchFlags(retries = 3, delay = 1000): Promise<void> {
69
+ try {
70
+ // Assuming GET /feature-flags returns { "flag-name": true, ... }
71
+ this.flags =
72
+ await BaseService.get<Record<string, boolean>>("feature-flags");
73
+ } catch (error) {
74
+ if (retries > 0) {
75
+ console.warn(
76
+ `Failed to fetch feature flags. Retrying in ${delay}ms... (${retries} attempts left)`,
77
+ );
78
+ await new Promise((resolve) => setTimeout(resolve, delay));
79
+ return this.fetchFlags(retries - 1, delay * 2);
80
+ } else {
81
+ console.error(
82
+ "Failed to fetch feature flags after multiple attempts:",
83
+ error,
84
+ );
85
+ // Fallback strategies could go here (e.g. use cached values or defaults)
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ export const featureFlagService = FeatureFlagService.getInstance();
@@ -0,0 +1,78 @@
1
+ # FileService
2
+
3
+ The `FileService` provides utilities for handling file uploads, client-side processing, and previews.
4
+
5
+ ## Overview
6
+
7
+ Handling files on the frontend involves more than just sending them to an API. Key challenges include:
8
+
9
+ 1. **Large Files**: Uploading 1GB+ files in one go often fails. We need chunking.
10
+ 2. **Performance**: Compressing images _before_ upload saves bandwidth.
11
+ 3. **UX**: Showing instant previews without waiting for a server round-trip.
12
+
13
+ ### Key Features
14
+
15
+ - **Chunked Uploads**: Splits large files into 5MB chunks and uploads them sequentially.
16
+ - **Client-Side Compression**: Resizes and compresses images using the browser's Canvas API.
17
+ - **Instant Previews**: Generates `blob:` or `data:` URLs for immediate display.
18
+
19
+ ## How to Use
20
+
21
+ ### 1. Simple Upload
22
+
23
+ For small files (e.g., avatars, documents < 10MB).
24
+
25
+ ```typescript
26
+ import { fileService } from "@/File-Service/fileService";
27
+
28
+ async function handleUpload(file: File) {
29
+ try {
30
+ const response = await fileService.upload(file, "users/avatar");
31
+ console.log("Uploaded:", response);
32
+ } catch (error) {
33
+ console.error("Upload failed", error);
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### 2. Chunked Upload (Recommended for Video/Large Files)
39
+
40
+ Automatically handles the `init -> chunk -> complete` flow.
41
+
42
+ ```typescript
43
+ const onProgress = (progress) => {
44
+ console.log(`Upload is ${progress.percentage}% complete`);
45
+ };
46
+
47
+ await fileService.uploadChunked(largeVideoFile, "videos/upload", onProgress);
48
+ ```
49
+
50
+ **Backend Requirements**:
51
+ Your backend must support the following endpoints for chunked uploads:
52
+
53
+ - `POST /init`: Returns `{ uploadId: string }`
54
+ - `POST /chunk`: Accepts `formData` with `chunk`, `uploadId`, `chunkIndex`
55
+ - `POST /complete`: Accepts `{ uploadId }` and reassembles the file.
56
+
57
+ ### 3. Image Compression
58
+
59
+ Compress an image before uploading to save bandwidth.
60
+
61
+ ```typescript
62
+ const compressedFile = await fileService.compressImage(originalFile, {
63
+ maxWidth: 1024,
64
+ quality: 0.7, // 70% quality
65
+ type: "image/webp",
66
+ });
67
+
68
+ // Now upload the smaller file
69
+ await fileService.upload(compressedFile, "posts/image");
70
+ ```
71
+
72
+ ### 4. Generatng Previews
73
+
74
+ ```typescript
75
+ // specific method
76
+ const previewUrl = await fileService.generatePreview(file);
77
+ // <img src={previewUrl} />
78
+ ```
@@ -0,0 +1,224 @@
1
+ import BaseService from "@/API-Service/baseApiService";
2
+
3
+ export interface UploadProgress {
4
+ loaded: number;
5
+ total: number;
6
+ percentage: number;
7
+ }
8
+
9
+ export interface CompressionOptions {
10
+ maxWidth?: number;
11
+ maxHeight?: number;
12
+ quality?: number; // 0 to 1
13
+ type?: "image/jpeg" | "image/png" | "image/webp";
14
+ }
15
+
16
+ /**
17
+ * Interface for custom chunked upload strategies (e.g., S3, specific backend protocols).
18
+ */
19
+ export interface ChunkedUploadAdapter {
20
+ /**
21
+ * Initialize the upload.
22
+ * @returns Promise resolving to an upload ID or context object.
23
+ */
24
+ init(file: File): Promise<string>;
25
+
26
+ /**
27
+ * Upload a single chunk.
28
+ * @param uploadId The ID returned from init
29
+ * @param chunk The blob of data
30
+ * @param index Chunk index (0-based)
31
+ */
32
+ uploadChunk(uploadId: string, chunk: Blob, index: number): Promise<void>;
33
+
34
+ /**
35
+ * Complete the upload.
36
+ */
37
+ complete<T>(uploadId: string): Promise<T>;
38
+ }
39
+
40
+ export class FileService {
41
+ private static instance: FileService;
42
+
43
+ private constructor() {}
44
+
45
+ public static getInstance(): FileService {
46
+ if (!FileService.instance) {
47
+ FileService.instance = new FileService();
48
+ }
49
+ return FileService.instance;
50
+ }
51
+
52
+ /**
53
+ * Standard single-request upload
54
+ */
55
+ public async upload<T>(
56
+ file: File,
57
+ url: string,
58
+ onProgress?: (progress: UploadProgress) => void,
59
+ ): Promise<T> {
60
+ const formData = new FormData();
61
+ formData.append("file", file);
62
+
63
+ return BaseService.post<T>(url, formData);
64
+ }
65
+
66
+ /**
67
+ * Upload a file in chunks.
68
+ * Supports standard API endpoint (string) or custom adapter.
69
+ */
70
+ public async uploadChunked<T>(
71
+ file: File,
72
+ destination: string | ChunkedUploadAdapter,
73
+ onProgress?: (progress: UploadProgress) => void,
74
+ chunkSize: number = 5 * 1024 * 1024, // 5MB default
75
+ ): Promise<T> {
76
+ const totalChunks = Math.ceil(file.size / chunkSize);
77
+ let uploadId: string;
78
+
79
+ // 1. Initialize
80
+ if (typeof destination === "string") {
81
+ const initResponse = await BaseService.post<{ uploadId: string }>(
82
+ `${destination}/init`,
83
+ {
84
+ filename: file.name,
85
+ totalChunks,
86
+ totalSize: file.size,
87
+ contentType: file.type,
88
+ },
89
+ );
90
+ uploadId = initResponse.uploadId;
91
+ } else {
92
+ uploadId = await destination.init(file);
93
+ }
94
+
95
+ let loaded = 0;
96
+
97
+ // 2. Upload chunks sequentially
98
+ for (let i = 0; i < totalChunks; i++) {
99
+ const start = i * chunkSize;
100
+ const end = Math.min(start + chunkSize, file.size);
101
+ const chunk = file.slice(start, end);
102
+
103
+ if (typeof destination === "string") {
104
+ const formData = new FormData();
105
+ formData.append("chunk", chunk);
106
+ formData.append("uploadId", uploadId);
107
+ formData.append("chunkIndex", i.toString());
108
+ await BaseService.post(`${destination}/chunk`, formData);
109
+ } else {
110
+ await destination.uploadChunk(uploadId, chunk, i);
111
+ }
112
+
113
+ loaded += chunk.size;
114
+ if (onProgress) {
115
+ onProgress({
116
+ loaded,
117
+ total: file.size,
118
+ percentage: Math.round((loaded / file.size) * 100),
119
+ });
120
+ }
121
+ }
122
+
123
+ // 3. Complete
124
+ if (typeof destination === "string") {
125
+ return BaseService.post<T>(`${destination}/complete`, { uploadId });
126
+ } else {
127
+ return destination.complete<T>(uploadId);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Generate a preview URL for a given file.
133
+ * Supports Images (via FileReader) and returns a blob URL for others.
134
+ */
135
+ public async generatePreview(file: File): Promise<string> {
136
+ if (file.type.startsWith("image/")) {
137
+ return new Promise<string>((resolve, reject) => {
138
+ const reader = new FileReader();
139
+ reader.onload = () => resolve(reader.result as string);
140
+ reader.onerror = reject;
141
+ reader.readAsDataURL(file);
142
+ });
143
+ }
144
+ return URL.createObjectURL(file);
145
+ }
146
+
147
+ /**
148
+ * Compress an image file using HTML Canvas.
149
+ */
150
+ public async compressImage(
151
+ file: File,
152
+ options: CompressionOptions = {},
153
+ ): Promise<File> {
154
+ if (!file.type.startsWith("image/")) {
155
+ console.warn(
156
+ "FileService: compressImage only supports image files. Returning original file.",
157
+ );
158
+ return file;
159
+ }
160
+
161
+ const {
162
+ maxWidth = 1920,
163
+ maxHeight = 1080,
164
+ quality = 0.8,
165
+ type = "image/jpeg",
166
+ } = options;
167
+
168
+ return new Promise((resolve, reject) => {
169
+ const img = new Image();
170
+ img.src = URL.createObjectURL(file);
171
+
172
+ img.onload = () => {
173
+ let width = img.width;
174
+ let height = img.height;
175
+
176
+ // Maintain aspect ratio
177
+ if (width > height) {
178
+ if (width > maxWidth) {
179
+ height = Math.round((height * maxWidth) / width);
180
+ width = maxWidth;
181
+ }
182
+ } else {
183
+ if (height > maxHeight) {
184
+ width = Math.round((width * maxHeight) / height);
185
+ height = maxHeight;
186
+ }
187
+ }
188
+
189
+ const canvas = document.createElement("canvas");
190
+ canvas.width = width;
191
+ canvas.height = height;
192
+ const ctx = canvas.getContext("2d");
193
+
194
+ if (!ctx) {
195
+ reject(new Error("Could not get canvas context"));
196
+ return;
197
+ }
198
+
199
+ ctx.drawImage(img, 0, 0, width, height);
200
+
201
+ canvas.toBlob(
202
+ (blob) => {
203
+ if (!blob) {
204
+ reject(new Error("Canvas toBlob failed"));
205
+ return;
206
+ }
207
+ // Create new File object
208
+ const newFile = new File([blob], file.name, {
209
+ type: type,
210
+ lastModified: Date.now(),
211
+ });
212
+ resolve(newFile);
213
+ },
214
+ type,
215
+ quality,
216
+ );
217
+ };
218
+
219
+ img.onerror = (err) => reject(err);
220
+ });
221
+ }
222
+ }
223
+
224
+ export const fileService = FileService.getInstance();
@@ -0,0 +1,49 @@
1
+ # Simple Explanation of File Service
2
+
3
+ The File Service is like your **Professional Shipping Company**. It handles everything from sending small letters to moving an entire house, ensuring your items (files) arrive safely and efficiently.
4
+
5
+ ## 🧠 Think of it like this:
6
+
7
+ - **Standard Upload**: This is like **Mailing a Letter**. You put it in an envelope and send it in one go. Quick and easy for small things.
8
+ - **Chunked Upload**: This is like **Moving a Piano**. You can't fit it through the door, so you take it apart (chunks), carry each piece separately, and reassemble it at the new house. If you trip while carrying one leg, you only pick up that leg, not the whole piano.
9
+ - **Compression**: This is like **Vacuum Sealing** your clothes. You squeeze out the air (unnecessary bytes) so they take up less space in the truck, but they're still clothes when you unpack them.
10
+
11
+ ## 🔑 Key Features
12
+
13
+ - **Reliability**: If a big file fails halfway, you don't have to start over from 0%.
14
+ - **Versatility**: It can talk to your own backend server or specialized storage systems (like AWS S3).
15
+ - **User Experience**: It creates previews (thumbnails) so users can see what they are uploading instantly.
16
+
17
+ ## 🛠️ Functions Explained
18
+
19
+ ### 1. `upload(file, url)`
20
+
21
+ **What it does:** Sends a file in one piece.
22
+
23
+ **Usage:** Best for profile pictures or small documents.
24
+
25
+ ### 2. `uploadChunked(file, destination)`
26
+
27
+ **What it does:** Breaks a huge file into small pieces and sends them one by one.
28
+
29
+ **How it works:**
30
+
31
+ 1. **Init**: Tells the server "Hey, I'm sending a big file in 10 parts."
32
+ 2. **Upload Loop**: Sends Part 1, then Part 2, etc. Tracks progress (e.g., "40% done").
33
+ 3. **Complete**: Tells the server "That was the last piece, put it all together now."
34
+
35
+ ### 3. `generatePreview(file)`
36
+
37
+ **What it does:** Lets you peek inside the box.
38
+
39
+ **How it works:**
40
+
41
+ - If it's an image, it creates a visual thumbnail.
42
+ - If it's a PDF or other file, it creates a link to open it.
43
+
44
+ ### 4. `compressImage(file, options)`
45
+
46
+ **What it does:** Shrinks images to save space and data.
47
+
48
+ **How it works:**
49
+ It takes a huge 4K photo, resizes it (e.g., to 1080p), and slightly lowers the quality (like saving as a JPEG) so it uploads much faster without looking noticeably different.
@@ -0,0 +1,48 @@
1
+ # Google Login Service
2
+
3
+ This service provides a wrapper around Google Identity Services (GIS) for implementing "Sign In With Google".
4
+
5
+ ## Features
6
+
7
+ - **Automatic Script Loading**: Handles lazy loading of the Google JS SDK.
8
+ - **Environment Configuration**: API keys managed via `EnvironmentConfigService`.
9
+ - **Easy Button Rendering**: Simple method to render the standard Google button into any container.
10
+ - **Type Safety**: TypeScript interfaces for configuration and button options.
11
+
12
+ ## Configuration
13
+
14
+ Ensure `VITE_GOOGLE_CLIENT_ID` is set in your `.env` file or via `window.__CONFIG__`.
15
+
16
+ ```env
17
+ VITE_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```typescript
23
+ import { googleLoginService } from "./Google-Login-Service/googleLoginService";
24
+
25
+ // 1. Initialize logic
26
+ const handleCredentialResponse = (response: any) => {
27
+ console.log("Encoded JWT ID token: " + response.credential);
28
+ // Send this token to your backend for verification
29
+ };
30
+
31
+ await googleLoginService.init({
32
+ callback: handleCredentialResponse,
33
+ autoSelect: false,
34
+ });
35
+
36
+ // 2. Render Button
37
+ googleLoginService.renderButton("google-btn-container", {
38
+ theme: "outline",
39
+ size: "large",
40
+ width: "100%",
41
+ });
42
+ ```
43
+
44
+ ## HTML
45
+
46
+ ```html
47
+ <div id="google-btn-container"></div>
48
+ ```