bodevops-features 1.0.0 → 1.0.8

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/README.md CHANGED
@@ -1,6 +1,17 @@
1
- # bodevops-features
1
+ # BoDevOps Features
2
2
 
3
- BoDevOps features library - a collection of utilities for Google Drive, Google Sheets, and iDrive e2 integrations.
3
+ A collection of framework-agnostic TypeScript utilities for Google Drive, Google Sheets, and iDrive e2 operations.
4
+
5
+ [![NPM Version](https://img.shields.io/npm/v/bodevops-features.svg)](https://www.npmjs.com/package/bodevops-features)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - 🚀 **Framework-agnostic** - Works with any TypeScript/JavaScript project
11
+ - 📦 **Tree-shakeable** - Import only what you need
12
+ - 🔒 **Type-safe** - Full TypeScript support with no `any` types
13
+ - 📚 **Well-documented** - Comprehensive JSDoc comments in English
14
+ - ✨ **Easy to use** - Clean, intuitive API design
4
15
 
5
16
  ## Installation
6
17
 
@@ -8,21 +19,699 @@ BoDevOps features library - a collection of utilities for Google Drive, Google S
8
19
  npm install bodevops-features
9
20
  ```
10
21
 
11
- ## Usage
22
+ ## Modules
23
+
24
+ ### 🗂️ Google Drive (`GGDrive`)
25
+
26
+ Manage files and folders in Google Drive with ease.
27
+
28
+ **Features:**
29
+
30
+ - Upload files to Drive
31
+ - Create folder hierarchies
32
+ - Share files and folders
33
+ - Transfer ownership
34
+ - Get storage quota information
35
+ - List files in folders
36
+ - Delete files
37
+
38
+ ### 📊 Google Sheets (`GGSheet`)
39
+
40
+ Read, write, and manage Google Spreadsheet data.
41
+
42
+ **Features:**
43
+
44
+ - Read sheet data
45
+ - Export data (Append/Overwrite modes)
46
+ - Update cells, rows, and columns
47
+ - Find row by value
48
+ - Delete rows
49
+ - Convert sheet data to typed objects
50
+ - Column name/index conversion utilities
51
+
52
+ ---
53
+
54
+ ## Quick Start
55
+
56
+ ### Google Drive
12
57
 
13
58
  ```typescript
14
59
  import { GGDrive } from 'bodevops-features';
15
60
 
16
- // Use GGDrive utilities
17
- GGDrive.test();
61
+ // Initialize client
62
+ const driveClient = new GGDrive.GoogleDriveClient({
63
+ keyFilePath: './service-account.json',
64
+ });
65
+
66
+ // Get storage information
67
+ const storage = await driveClient.getStorageInfo();
68
+ console.log(`Used: ${storage.formattedUsed} / ${storage.formattedTotal}`);
69
+
70
+ // Upload a file
71
+ const result = await driveClient.uploadFile({
72
+ localFilePath: './document.pdf',
73
+ driveFolder: 'MyFolder/SubFolder',
74
+ fileName: 'uploaded-document.pdf',
75
+ });
76
+ console.log(`View at: ${result.webViewLink}`);
77
+
78
+ // Upload and share with someone
79
+ const sharedResult = await driveClient.uploadFileAndShare({
80
+ localFilePath: './report.pdf',
81
+ driveFolder: 'SharedReports',
82
+ shareWithEmail: 'colleague@example.com',
83
+ role: 'writer',
84
+ });
85
+
86
+ // List files in a folder
87
+ const files = await driveClient.listFilesInFolder({ folderId: 'root' });
88
+ for (const file of files) {
89
+ console.log(`${file.name} (${file.mimeType})`);
90
+ }
18
91
  ```
19
92
 
20
- ## Features
93
+ ### Google Sheets
94
+
95
+ ```typescript
96
+ import { GGSheet } from 'bodevops-features';
97
+
98
+ // Initialize client
99
+ const sheetClient = new GGSheet.GoogleSheetClient({
100
+ keyFilePath: './service-account.json',
101
+ });
102
+
103
+ // Read data from a sheet
104
+ const data = await sheetClient.getValues({
105
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
106
+ sheetName: 'Sheet1',
107
+ });
108
+
109
+ // Convert to typed objects
110
+ interface Person {
111
+ name: string;
112
+ email: string;
113
+ age: string;
114
+ }
115
+
116
+ const people = GGSheet.convertValueSheet<Person>({
117
+ values: data,
118
+ rowOffset: 0, // First row is header
119
+ });
120
+
121
+ // Export data (Overwrite mode)
122
+ await sheetClient.export({
123
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
124
+ sheetName: 'Sheet1',
125
+ listCols: ['Name', 'Email', 'Age'],
126
+ valsExport: [
127
+ ['John Doe', 'john@example.com', '30'],
128
+ ['Jane Smith', 'jane@example.com', '25'],
129
+ ],
130
+ typeExport: GGSheet.ETypeExport.Overwrite,
131
+ });
132
+
133
+ // Update specific cells
134
+ await sheetClient.updateValuesMultiCells({
135
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
136
+ sheetName: 'Sheet1',
137
+ cells: [
138
+ { row: 0, col: 0, content: 'Updated Value' },
139
+ { row: 1, col: 1, content: 'Another Update' },
140
+ ],
141
+ rowOffset: 0,
142
+ });
143
+
144
+ // Find a row by value
145
+ const rowIndex = await sheetClient.getIdxRow({
146
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
147
+ sheetName: 'Sheet1',
148
+ colName: 'A',
149
+ value: 'John Doe',
150
+ });
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Authentication
156
+
157
+ Both Google Drive and Google Sheets require a Google Service Account for authentication.
158
+
159
+ ### Creating a Service Account
160
+
161
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
162
+ 2. Create a new project or select an existing one
163
+ 3. Enable the Google Drive API and Google Sheets API
164
+ 4. Create a Service Account:
165
+ - Go to "IAM & Admin" → "Service Accounts"
166
+ - Click "Create Service Account"
167
+ - Fill in the details and create
168
+ 5. Create a JSON key:
169
+ - Click on the created service account
170
+ - Go to "Keys" tab
171
+ - Click "Add Key" → "Create new key"
172
+ - Select "JSON" and download
173
+
174
+ ### Using Credentials
175
+
176
+ ```typescript
177
+ // Option 1: Using key file path
178
+ const client = new GGDrive.GoogleDriveClient({
179
+ keyFilePath: './service-account.json',
180
+ });
181
+
182
+ // Option 2: Using credentials object
183
+ const client = new GGDrive.GoogleDriveClient({
184
+ credentials: {
185
+ type: 'service_account',
186
+ project_id: 'your-project-id',
187
+ private_key: '-----BEGIN PRIVATE KEY-----\n...',
188
+ client_email: 'service-account@project.iam.gserviceaccount.com',
189
+ // ... other fields
190
+ },
191
+ });
192
+ ```
193
+
194
+ ---
195
+
196
+ ## API Reference
197
+
198
+ ### Google Drive
199
+
200
+ #### `GoogleDriveClient`
201
+
202
+ **Constructor:**
203
+
204
+ ```typescript
205
+ new GoogleDriveClient(config: IGoogleDriveConfig)
206
+ ```
207
+
208
+ **Methods:**
209
+
210
+ | Method | Description |
211
+ | ------------------------------- | ----------------------------------- |
212
+ | `getStorageInfo()` | Get Drive storage quota information |
213
+ | `uploadFile(params)` | Upload a file to Drive |
214
+ | `uploadFileAndShare(params)` | Upload and share with email |
215
+ | `deleteFile(params)` | Delete a file from Drive |
216
+ | `listFilesInFolder(params)` | List files in a folder |
217
+ | `makeFilePublic(params)` | Make file accessible via link |
218
+ | `transferFileOwnership(params)` | Transfer file to another user |
219
+ | `shareFolderWithEmail(params)` | Share folder with permissions |
220
+ | `getFolderIdByPath(params)` | Get folder ID from path string |
221
+ | `fileExistsInFolder(params)` | Check if file exists |
222
+
223
+ **Utility Functions:**
224
+
225
+ - `formatBytes({ bytes })` - Convert bytes to human-readable format
226
+ - `normalizeFilePath({ filePath })` - Normalize file path
227
+ - `validateFileExists({ filePath })` - Check if file exists
228
+ - `getFileInfo({ filePath })` - Get file metadata
229
+
230
+ ---
231
+
232
+ ### Google Sheets
233
+
234
+ #### `GoogleSheetClient`
235
+
236
+ **Constructor:**
237
+
238
+ ```typescript
239
+ new GoogleSheetClient(config: IGoogleSheetConfig)
240
+ ```
241
+
242
+ **Methods:**
243
+
244
+ | Method | Description |
245
+ | ---------------------------------------- | ------------------------------ |
246
+ | `getSheetInfo(params)` | Get spreadsheet metadata |
247
+ | `getValues(params)` | Read all values from sheet |
248
+ | `getIdxRow(params)` | Find row index by value |
249
+ | `export(params)` | Export data (Append/Overwrite) |
250
+ | `updateValuesMultiCells(params)` | Update specific cells |
251
+ | `updateValuesMultiColsByRow(params)` | Update columns in a row |
252
+ | `updateValuesMultiRowsByCol(params)` | Update rows in a column |
253
+ | `updateValuesMultiRowsMultiCols(params)` | Batch update range |
254
+ | `deleteRowSheet(params)` | Delete a row |
255
+
256
+ **Utility Functions:**
257
+
258
+ - `convertIndexToColumnName({ columnIndex })` - Convert 0 → "A", 26 → "AA"
259
+ - `convertColumnNameToIndex({ columnName })` - Convert "A" → 0, "AA" → 26
260
+ - `convertValueSheet({ values, rowOffset })` - Parse sheet to typed objects
261
+ - `getSheetIdFromUrl({ sheetUrl })` - Extract spreadsheet ID
262
+ - `calculateActualRow({ dataRowIndex, rowOffset })` - Calculate sheet row number
263
+
264
+ **Enums:**
265
+
266
+ - `ETypeExport.Append` - Append data to existing
267
+ - `ETypeExport.Overwrite` - Replace all data
268
+
269
+ ---
270
+
271
+ ## Advanced Examples
272
+
273
+ ### Google Drive: Batch Upload with Progress
274
+
275
+ ```typescript
276
+ const files = ['file1.pdf', 'file2.pdf', 'file3.pdf'];
277
+
278
+ for (const file of files) {
279
+ const result = await driveClient.uploadFile({
280
+ localFilePath: `./documents/${file}`,
281
+ driveFolder: 'Uploads/2024',
282
+ fileName: file,
283
+ });
284
+ console.log(`✓ Uploaded: ${result.name}`);
285
+ }
286
+ ```
287
+
288
+ ### Google Sheets: Export Database Query Results
289
+
290
+ ```typescript
291
+ import { GGSheet } from 'bodevops-features';
292
+
293
+ // Assume you have query results
294
+ const users = await database.query('SELECT name, email, created_at FROM users');
295
+
296
+ // Map to column definitions
297
+ const colsMapping = {
298
+ name: 'Full Name',
299
+ email: 'Email Address',
300
+ created_at: 'Registration Date',
301
+ };
302
+
303
+ // Extract columns and values
304
+ const { listCols, valsExport } = GGSheet.getListColsAndValsExport({
305
+ colsForSheet: colsMapping,
306
+ resultItems: users,
307
+ });
308
+
309
+ // Export to sheet
310
+ await sheetClient.export({
311
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
312
+ sheetName: 'Users',
313
+ listCols,
314
+ valsExport,
315
+ typeExport: GGSheet.ETypeExport.Overwrite,
316
+ });
317
+ ```
318
+
319
+ ### Google Sheets: Update Multiple Rows
320
+
321
+ ```typescript
322
+ // Update column C for rows 0-4
323
+ await sheetClient.updateValuesMultiRowsByCol({
324
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
325
+ sheetName: 'Sheet1',
326
+ col: 2, // Column C (0-based)
327
+ values: [
328
+ { row: 0, content: 'Status: Complete' },
329
+ { row: 1, content: 'Status: Pending' },
330
+ { row: 2, content: 'Status: Complete' },
331
+ { row: 3, content: 'Status: Failed' },
332
+ { row: 4, content: 'Status: Complete' },
333
+ ],
334
+ });
335
+ ```
336
+
337
+ ---
338
+
339
+ ## Google Sheets Update & Delete Methods - Detailed Guide
340
+
341
+ ### 📝 Understanding `rowOffset`
342
+
343
+ All update and delete methods support a `rowOffset` parameter to handle different sheet structures:
344
+
345
+ - **`rowOffset: 0`** (default): Header at row 1, data starts at row 2
346
+ - **`rowOffset: 1`**: Header at row 1, skip row 2, data starts at row 3
347
+ - **`rowOffset: 2`**: Header at row 1, skip rows 2-3, data starts at row 4
348
+
349
+ **Example Sheet Structure:**
350
+
351
+ ```
352
+ Row 1: [Name, Email, Status] ← Header
353
+ Row 2: [John, john@example.com, Active] ← Data row 0 (with rowOffset=0)
354
+ Row 3: [Jane, jane@example.com, Pending] ← Data row 1 (with rowOffset=0)
355
+ ```
356
+
357
+ ---
358
+
359
+ ### 1️⃣ `updateValuesMultiCells()` - Update Specific Cells
360
+
361
+ Update multiple cells at specific row and column positions.
362
+
363
+ **Use Case:** Update scattered cells across the sheet
364
+
365
+ ```typescript
366
+ await sheetClient.updateValuesMultiCells({
367
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
368
+ sheetName: 'Sheet1',
369
+ cells: [
370
+ { row: 0, col: 0, content: 'John Doe' }, // A2
371
+ { row: 0, col: 2, content: 'Active' }, // C2
372
+ { row: 1, col: 1, content: 'jane@new.com' }, // B3
373
+ { row: 5, col: 3, content: 'Updated' }, // D7
374
+ ],
375
+ rowOffset: 0, // Optional, default is 0
376
+ });
377
+ ```
21
378
 
22
- - **GGDrive**: Google Drive utilities
23
- - **GGSheet**: Google Sheets utilities
24
- - **iDrive E2**: iDrive E2 storage utilities
379
+ **Parameters:**
380
+
381
+ - `cells`: Array of `{ row, col, content }` objects
382
+ - `row`: 0-based data row index
383
+ - `col`: 0-based column index (0=A, 1=B, 2=C, ...)
384
+ - `content`: String value to write
385
+
386
+ ---
387
+
388
+ ### 2️⃣ `updateValuesMultiColsByRow()` - Update Multiple Columns in One Row
389
+
390
+ Update several columns in a single row.
391
+
392
+ **Use Case:** Update a complete user record
393
+
394
+ ```typescript
395
+ // Update row 3 (data row index 2) - columns A, B, C
396
+ await sheetClient.updateValuesMultiColsByRow({
397
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
398
+ sheetName: 'Users',
399
+ row: 2, // Data row index (actual sheet row 4 with rowOffset=0)
400
+ values: [
401
+ { col: 0, content: 'Updated Name' }, // Column A
402
+ { col: 1, content: 'new@email.com' }, // Column B
403
+ { col: 2, content: 'Active' }, // Column C
404
+ { col: 4, content: '2026-01-07' }, // Column E
405
+ ],
406
+ rowOffset: 0,
407
+ });
408
+ ```
409
+
410
+ **Real-world Example:**
411
+
412
+ ```typescript
413
+ // Update user status and last login
414
+ await sheetClient.updateValuesMultiColsByRow({
415
+ sheetUrl: SHEET_URL,
416
+ sheetName: 'Users',
417
+ row: userId,
418
+ values: [
419
+ { col: 5, content: 'Online' }, // Status column
420
+ { col: 6, content: new Date().toISOString() }, // Last login column
421
+ ],
422
+ });
423
+ ```
424
+
425
+ ---
426
+
427
+ ### 3️⃣ `updateValuesMultiRowsByCol()` - Update Multiple Rows in One Column
428
+
429
+ Update several rows in a single column.
430
+
431
+ **Use Case:** Batch update status for multiple items
432
+
433
+ ```typescript
434
+ // Update status column (C) for multiple rows
435
+ await sheetClient.updateValuesMultiRowsByCol({
436
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
437
+ sheetName: 'Tasks',
438
+ col: 2, // Column C (0-based)
439
+ values: [
440
+ { row: 0, content: 'Completed' },
441
+ { row: 1, content: 'In Progress' },
442
+ { row: 2, content: 'Completed' },
443
+ { row: 5, content: 'Pending' },
444
+ { row: 8, content: 'Completed' },
445
+ ],
446
+ rowOffset: 0,
447
+ });
448
+ ```
449
+
450
+ **Real-world Example:**
451
+
452
+ ```typescript
453
+ // Mark all selected tasks as completed
454
+ const completedTaskIds = [0, 3, 5, 7];
455
+
456
+ await sheetClient.updateValuesMultiRowsByCol({
457
+ sheetUrl: SHEET_URL,
458
+ sheetName: 'Tasks',
459
+ col: 3, // Status column
460
+ values: completedTaskIds.map((taskId) => ({
461
+ row: taskId,
462
+ content: 'Completed ✓',
463
+ })),
464
+ });
465
+ ```
466
+
467
+ ---
468
+
469
+ ### 4️⃣ `updateValuesMultiRowsMultiCols()` - Batch Update a Range
470
+
471
+ Update a rectangular range of cells (multiple rows and columns).
472
+
473
+ **Use Case:** Update a table section or paste data block
474
+
475
+ ```typescript
476
+ // Update a 3x3 range starting at row 0, column 0
477
+ await sheetClient.updateValuesMultiRowsMultiCols({
478
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
479
+ sheetName: 'Data',
480
+ values: [
481
+ ['A1', 'B1', 'C1'],
482
+ ['A2', 'B2', 'C2'],
483
+ ['A3', 'B3', 'C3'],
484
+ ],
485
+ startRow: 0, // Start at data row 0 (sheet row 2)
486
+ startCol: 0, // Start at column A
487
+ rowOffset: 0,
488
+ });
489
+ ```
490
+
491
+ **Advanced Example with Custom Range:**
492
+
493
+ ```typescript
494
+ // Update columns D-F (indices 3-5) for rows 10-15
495
+ await sheetClient.updateValuesMultiRowsMultiCols({
496
+ sheetUrl: SHEET_URL,
497
+ sheetName: 'Report',
498
+ values: [
499
+ ['Q1', '1000', '95%'],
500
+ ['Q2', '1200', '98%'],
501
+ ['Q3', '1100', '96%'],
502
+ ['Q4', '1300', '99%'],
503
+ ],
504
+ startRow: 10, // Data row 10
505
+ endRow: 13, // Data row 13 (4 rows total)
506
+ startCol: 3, // Column D
507
+ rowOffset: 0,
508
+ });
509
+ ```
510
+
511
+ **Paste Clipboard Data:**
512
+
513
+ ```typescript
514
+ // Paste a copied table from Excel/Sheets
515
+ const clipboardData = [
516
+ ['Product', 'Price', 'Stock'],
517
+ ['Item A', '100', '50'],
518
+ ['Item B', '200', '30'],
519
+ ['Item C', '150', '40'],
520
+ ];
521
+
522
+ await sheetClient.updateValuesMultiRowsMultiCols({
523
+ sheetUrl: SHEET_URL,
524
+ sheetName: 'Inventory',
525
+ values: clipboardData,
526
+ startRow: 0,
527
+ startCol: 0,
528
+ });
529
+ ```
530
+
531
+ ---
532
+
533
+ ### 5️⃣ `deleteRowSheet()` - Delete a Row
534
+
535
+ Delete a specific row from the sheet.
536
+
537
+ **Use Case:** Remove a record from the sheet
538
+
539
+ ```typescript
540
+ // Delete data row 5 (actual sheet row 7 with rowOffset=0)
541
+ await sheetClient.deleteRowSheet({
542
+ sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
543
+ sheetName: 'Users',
544
+ row: 5, // Data row index
545
+ rowOffset: 0,
546
+ });
547
+ ```
548
+
549
+ **Real-world Example:**
550
+
551
+ ```typescript
552
+ // Delete user by finding their row first
553
+ const userEmail = 'user@example.com';
554
+
555
+ // Find the row
556
+ const rowIndex = await sheetClient.getIdxRow({
557
+ sheetUrl: SHEET_URL,
558
+ sheetName: 'Users',
559
+ colName: 'B', // Email column
560
+ value: userEmail,
561
+ });
562
+
563
+ if (rowIndex >= 0) {
564
+ // Delete the row
565
+ await sheetClient.deleteRowSheet({
566
+ sheetUrl: SHEET_URL,
567
+ sheetName: 'Users',
568
+ row: rowIndex,
569
+ });
570
+ console.log(`Deleted user: ${userEmail}`);
571
+ }
572
+ ```
573
+
574
+ **⚠️ Important Notes:**
575
+
576
+ - Deleting a row shifts all rows below it up by one
577
+ - The row index is 0-based for data rows (excluding header)
578
+ - Cannot be undone - use with caution!
579
+
580
+ ---
581
+
582
+ ### 🎯 Method Selection Guide
583
+
584
+ | Scenario | Recommended Method |
585
+ | ----------------------------------- | ---------------------------------- |
586
+ | Update 1-2 specific cells | `updateValuesMultiCells()` |
587
+ | Update entire user record (one row) | `updateValuesMultiColsByRow()` |
588
+ | Batch update status column | `updateValuesMultiRowsByCol()` |
589
+ | Update a table section/range | `updateValuesMultiRowsMultiCols()` |
590
+ | Remove a record | `deleteRowSheet()` |
591
+
592
+ ---
593
+
594
+ ### 💡 Pro Tips
595
+
596
+ **1. Batch Operations for Performance:**
597
+
598
+ ```typescript
599
+ // ❌ Bad: Multiple individual updates
600
+ for (const item of items) {
601
+ await sheetClient.updateValuesMultiCells({
602
+ sheetUrl: SHEET_URL,
603
+ sheetName: 'Data',
604
+ cells: [{ row: item.id, col: 2, content: item.status }],
605
+ });
606
+ }
607
+
608
+ // ✅ Good: Single batch update
609
+ await sheetClient.updateValuesMultiRowsByCol({
610
+ sheetUrl: SHEET_URL,
611
+ sheetName: 'Data',
612
+ col: 2,
613
+ values: items.map((item) => ({ row: item.id, content: item.status })),
614
+ });
615
+ ```
616
+
617
+ **2. Handle rowOffset Correctly:**
618
+
619
+ ```typescript
620
+ // If your sheet has a description row after header:
621
+ // Row 1: [Name, Email, Status] ← Header
622
+ // Row 2: [Enter name, Enter email, ...] ← Description
623
+ // Row 3: [John, john@example.com, Active] ← First data row
624
+
625
+ await sheetClient.updateValuesMultiCells({
626
+ sheetUrl: SHEET_URL,
627
+ sheetName: 'Sheet1',
628
+ cells: [{ row: 0, col: 0, content: 'Updated' }],
629
+ rowOffset: 1, // Skip the description row
630
+ });
631
+ // This updates row 3 (first data row)
632
+ ```
633
+
634
+ **3. Validate Before Delete:**
635
+
636
+ ```typescript
637
+ // Always confirm before deleting
638
+ const confirmDelete = await getUserConfirmation();
639
+ if (confirmDelete) {
640
+ await sheetClient.deleteRowSheet({
641
+ sheetUrl: SHEET_URL,
642
+ sheetName: 'Users',
643
+ row: rowIndex,
644
+ });
645
+ }
646
+ ```
647
+
648
+ ---
649
+
650
+ ## TypeScript Support
651
+
652
+ This library is written in TypeScript and provides full type definitions.
653
+
654
+ ```typescript
655
+ import { GGDrive, GGSheet } from 'bodevops-features';
656
+
657
+ // All types are exported
658
+ type UploadResult = GGDrive.IUploadFileResult;
659
+ type SheetInfo = GGSheet.ISpreadsheetInfo;
660
+ type ExportMode = GGSheet.ETypeExport;
661
+ ```
662
+
663
+ ---
664
+
665
+ ## Error Handling
666
+
667
+ All methods throw descriptive errors. Wrap calls in try-catch:
668
+
669
+ ```typescript
670
+ try {
671
+ const result = await driveClient.uploadFile({
672
+ localFilePath: './document.pdf',
673
+ driveFolder: 'MyFolder',
674
+ });
675
+ } catch (error) {
676
+ console.error('Upload failed:', error.message);
677
+ // Handle error appropriately
678
+ }
679
+ ```
680
+
681
+ ---
682
+
683
+ ## Contributing
684
+
685
+ Contributions are welcome! Please follow these guidelines:
686
+
687
+ 1. Fork the repository
688
+ 2. Create a feature branch
689
+ 3. Write clean, documented code
690
+ 4. Add tests if applicable
691
+ 5. Submit a pull request
692
+
693
+ ---
25
694
 
26
695
  ## License
27
696
 
28
- MIT
697
+ MIT © [BoDevOps](https://github.com/HaiSonMin)
698
+
699
+ ---
700
+
701
+ ## Support
702
+
703
+ - 📧 Email: support@bodevops.com
704
+ - 🐛 Issues: [GitHub Issues](https://github.com/HaiSonMin/BoDevOpsFeature/issues)
705
+ - 📖 Documentation: [Full API Docs](https://github.com/HaiSonMin/BoDevOpsFeature#readme)
706
+
707
+ ---
708
+
709
+ ## Changelog
710
+
711
+ ### v1.0.0 (2026-01-07)
712
+
713
+ - ✨ Initial release
714
+ - 🗂️ Google Drive module
715
+ - 📊 Google Sheets module
716
+ - 📚 Full TypeScript support
717
+ - 📝 Comprehensive documentation