hazo_files 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.
package/README.md ADDED
@@ -0,0 +1,929 @@
1
+ # hazo_files
2
+
3
+ [![npm version](https://img.shields.io/npm/v/hazo_files.svg)](https://www.npmjs.com/package/hazo_files)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A powerful, modular file management package for Node.js and React applications with support for local filesystem and Google Drive storage. Built with TypeScript for type safety and developer experience.
7
+
8
+ ## Features
9
+
10
+ - **Multiple Storage Providers**: Local filesystem and Google Drive support out of the box
11
+ - **Modular Architecture**: Easily add custom storage providers
12
+ - **Unified API**: Single consistent interface across all storage providers
13
+ - **React UI Components**: Drop-in FileBrowser component with folder tree, file list, and preview
14
+ - **Naming Rules System**: Visual configurator and utilities for generating consistent file/folder names
15
+ - **TypeScript**: Full type safety and IntelliSense support
16
+ - **OAuth Integration**: Built-in Google Drive OAuth authentication
17
+ - **Progress Tracking**: Upload/download progress callbacks
18
+ - **File Validation**: Extension filtering and file size limits
19
+ - **Error Handling**: Comprehensive error types and handling
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install hazo_files
25
+ ```
26
+
27
+ For React UI components, ensure you have React 18+ installed:
28
+
29
+ ```bash
30
+ npm install react react-dom
31
+ ```
32
+
33
+ For the NamingRuleConfigurator component (drag-and-drop interface), also install:
34
+
35
+ ```bash
36
+ npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Basic Usage (Server-side)
42
+
43
+ ```typescript
44
+ import { createInitializedFileManager } from 'hazo_files';
45
+
46
+ // Create and initialize file manager
47
+ const fileManager = await createInitializedFileManager({
48
+ config: {
49
+ provider: 'local',
50
+ local: {
51
+ basePath: './files',
52
+ maxFileSize: 10 * 1024 * 1024, // 10MB
53
+ allowedExtensions: ['jpg', 'png', 'pdf', 'txt']
54
+ }
55
+ }
56
+ });
57
+
58
+ // Create a directory
59
+ await fileManager.createDirectory('/documents');
60
+
61
+ // Upload a file
62
+ await fileManager.uploadFile(
63
+ './local-file.pdf',
64
+ '/documents/file.pdf',
65
+ {
66
+ onProgress: (progress, bytes, total) => {
67
+ console.log(`Upload progress: ${progress}%`);
68
+ }
69
+ }
70
+ );
71
+
72
+ // List directory contents
73
+ const result = await fileManager.listDirectory('/documents');
74
+ if (result.success) {
75
+ console.log(result.data);
76
+ }
77
+
78
+ // Download a file
79
+ await fileManager.downloadFile('/documents/file.pdf', './downloaded.pdf');
80
+ ```
81
+
82
+ ### Using Configuration File
83
+
84
+ Create `hazo_files_config.ini` in your project root:
85
+
86
+ ```ini
87
+ [general]
88
+ provider = local
89
+
90
+ [local]
91
+ base_path = ./files
92
+ max_file_size = 10485760
93
+ allowed_extensions = jpg,png,pdf,txt
94
+ ```
95
+
96
+ Then initialize without config object:
97
+
98
+ ```typescript
99
+ import { createInitializedFileManager } from 'hazo_files';
100
+
101
+ const fileManager = await createInitializedFileManager();
102
+ ```
103
+
104
+ ### React UI Component
105
+
106
+ ```typescript
107
+ import { FileBrowser } from 'hazo_files/ui';
108
+ import type { FileBrowserAPI } from 'hazo_files/ui';
109
+
110
+ // Create an API adapter that calls your server endpoints
111
+ const api: FileBrowserAPI = {
112
+ async listDirectory(path: string) {
113
+ const res = await fetch(`/api/files?action=list&path=${path}`);
114
+ return res.json();
115
+ },
116
+ async getFolderTree(path = '/', depth = 3) {
117
+ const res = await fetch(`/api/files?action=tree&path=${path}&depth=${depth}`);
118
+ return res.json();
119
+ },
120
+ async uploadFile(file: File, remotePath: string) {
121
+ const formData = new FormData();
122
+ formData.append('file', file);
123
+ formData.append('path', remotePath);
124
+ const res = await fetch('/api/files/upload', { method: 'POST', body: formData });
125
+ return res.json();
126
+ },
127
+ // ... implement other methods
128
+ };
129
+
130
+ function MyFileBrowser() {
131
+ return (
132
+ <FileBrowser
133
+ api={api}
134
+ initialPath="/"
135
+ showPreview={true}
136
+ showTree={true}
137
+ viewMode="grid"
138
+ />
139
+ );
140
+ }
141
+ ```
142
+
143
+ ## Advanced Usage
144
+
145
+ ### Google Drive Integration
146
+
147
+ #### 1. Set up Google Cloud Console
148
+
149
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com)
150
+ 2. Create a new project or select an existing one
151
+ 3. Enable the Google Drive API
152
+ 4. Create OAuth 2.0 credentials
153
+ 5. Add authorized redirect URIs (e.g., `http://localhost:3000/api/auth/callback/google`)
154
+
155
+ #### 2. Configure Environment Variables
156
+
157
+ Create `.env.local`:
158
+
159
+ ```env
160
+ GOOGLE_DRIVE_CLIENT_ID=your-client-id.apps.googleusercontent.com
161
+ GOOGLE_DRIVE_CLIENT_SECRET=your-client-secret
162
+ GOOGLE_DRIVE_REDIRECT_URI=http://localhost:3000/api/auth/callback/google
163
+ ```
164
+
165
+ #### 3. Configure hazo_files
166
+
167
+ ```ini
168
+ [general]
169
+ provider = google_drive
170
+
171
+ [google_drive]
172
+ client_id =
173
+ client_secret =
174
+ redirect_uri = http://localhost:3000/api/auth/callback/google
175
+ refresh_token =
176
+ ```
177
+
178
+ Environment variables will automatically override empty values.
179
+
180
+ #### 4. Implement OAuth Flow
181
+
182
+ ```typescript
183
+ import { createFileManager, GoogleDriveModule } from 'hazo_files';
184
+
185
+ // Initialize with Google Drive
186
+ const fileManager = createFileManager({
187
+ config: {
188
+ provider: 'google_drive',
189
+ google_drive: {
190
+ clientId: process.env.GOOGLE_DRIVE_CLIENT_ID!,
191
+ clientSecret: process.env.GOOGLE_DRIVE_CLIENT_SECRET!,
192
+ redirectUri: process.env.GOOGLE_DRIVE_REDIRECT_URI!,
193
+ }
194
+ }
195
+ });
196
+
197
+ await fileManager.initialize();
198
+
199
+ // Get the Google Drive module to access auth methods
200
+ const module = fileManager.getModule() as GoogleDriveModule;
201
+ const auth = module.getAuth();
202
+
203
+ // Generate auth URL
204
+ const authUrl = auth.getAuthUrl();
205
+ console.log('Visit:', authUrl);
206
+
207
+ // After user authorizes, exchange code for tokens
208
+ const tokens = await auth.exchangeCodeForTokens(authCode);
209
+
210
+ // Authenticate the module
211
+ await module.authenticate(tokens);
212
+
213
+ // Now you can use the file manager
214
+ await fileManager.createDirectory('/MyFolder');
215
+ ```
216
+
217
+ ### Next.js API Route Example
218
+
219
+ ```typescript
220
+ // app/api/files/route.ts
221
+ import { NextRequest, NextResponse } from 'next/server';
222
+ import { createInitializedFileManager } from 'hazo_files';
223
+
224
+ async function getFileManager() {
225
+ return createInitializedFileManager({
226
+ config: {
227
+ provider: 'local',
228
+ local: {
229
+ basePath: process.env.LOCAL_STORAGE_BASE_PATH || './files',
230
+ }
231
+ }
232
+ });
233
+ }
234
+
235
+ export async function GET(request: NextRequest) {
236
+ const { searchParams } = new URL(request.url);
237
+ const action = searchParams.get('action');
238
+ const path = searchParams.get('path') || '/';
239
+
240
+ const fm = await getFileManager();
241
+
242
+ switch (action) {
243
+ case 'list':
244
+ return NextResponse.json(await fm.listDirectory(path));
245
+ case 'tree':
246
+ const depth = parseInt(searchParams.get('depth') || '3', 10);
247
+ return NextResponse.json(await fm.getFolderTree(path, depth));
248
+ default:
249
+ return NextResponse.json({ success: false, error: 'Invalid action' });
250
+ }
251
+ }
252
+
253
+ export async function POST(request: NextRequest) {
254
+ const body = await request.json();
255
+ const { action, ...params } = body;
256
+
257
+ const fm = await getFileManager();
258
+
259
+ switch (action) {
260
+ case 'createDirectory':
261
+ return NextResponse.json(await fm.createDirectory(params.path));
262
+ case 'deleteFile':
263
+ return NextResponse.json(await fm.deleteFile(params.path));
264
+ case 'renameFile':
265
+ return NextResponse.json(await fm.renameFile(params.path, params.newName));
266
+ default:
267
+ return NextResponse.json({ success: false, error: 'Invalid action' });
268
+ }
269
+ }
270
+ ```
271
+
272
+ ### File Upload API Route
273
+
274
+ ```typescript
275
+ // app/api/files/upload/route.ts
276
+ import { NextRequest, NextResponse } from 'next/server';
277
+ import { createInitializedFileManager } from 'hazo_files';
278
+
279
+ export async function POST(request: NextRequest) {
280
+ const formData = await request.formData();
281
+ const file = formData.get('file') as File;
282
+ const path = formData.get('path') as string;
283
+
284
+ const fm = await getFileManager();
285
+
286
+ // Convert File to Buffer
287
+ const arrayBuffer = await file.arrayBuffer();
288
+ const buffer = Buffer.from(arrayBuffer);
289
+
290
+ const result = await fm.uploadFile(buffer, path);
291
+ return NextResponse.json(result);
292
+ }
293
+ ```
294
+
295
+ ### Progress Tracking
296
+
297
+ ```typescript
298
+ // Upload with progress tracking
299
+ await fileManager.uploadFile(
300
+ './large-file.zip',
301
+ '/uploads/large-file.zip',
302
+ {
303
+ onProgress: (progress, bytesTransferred, totalBytes) => {
304
+ console.log(`Progress: ${progress.toFixed(2)}%`);
305
+ console.log(`${bytesTransferred} / ${totalBytes} bytes`);
306
+ }
307
+ }
308
+ );
309
+
310
+ // Download with progress tracking
311
+ await fileManager.downloadFile(
312
+ '/uploads/large-file.zip',
313
+ './downloaded-file.zip',
314
+ {
315
+ onProgress: (progress, bytesTransferred, totalBytes) => {
316
+ console.log(`Download: ${progress.toFixed(2)}%`);
317
+ }
318
+ }
319
+ );
320
+ ```
321
+
322
+ ### File Operations
323
+
324
+ ```typescript
325
+ // Create directory structure
326
+ await fileManager.createDirectory('/projects/2024/docs');
327
+
328
+ // Upload file
329
+ const uploadResult = await fileManager.uploadFile(
330
+ buffer,
331
+ '/projects/2024/docs/report.pdf'
332
+ );
333
+
334
+ // Move file
335
+ await fileManager.moveItem(
336
+ '/projects/2024/docs/report.pdf',
337
+ '/archive/2024/report.pdf'
338
+ );
339
+
340
+ // Rename file
341
+ await fileManager.renameFile(
342
+ '/archive/2024/report.pdf',
343
+ 'annual-report.pdf'
344
+ );
345
+
346
+ // Copy file (convenience method)
347
+ await fileManager.copyFile(
348
+ '/archive/2024/annual-report.pdf',
349
+ '/backup/annual-report.pdf'
350
+ );
351
+
352
+ // Delete file
353
+ await fileManager.deleteFile('/backup/annual-report.pdf');
354
+
355
+ // Remove directory (recursive)
356
+ await fileManager.removeDirectory('/archive/2024', true);
357
+
358
+ // Check if file exists
359
+ const exists = await fileManager.exists('/projects/2024/docs');
360
+
361
+ // Get file/folder information
362
+ const itemResult = await fileManager.getItem('/projects/2024/docs/report.pdf');
363
+ if (itemResult.success && itemResult.data) {
364
+ console.log('File:', itemResult.data.name);
365
+ console.log('Size:', itemResult.data.size);
366
+ console.log('Modified:', itemResult.data.modifiedAt);
367
+ }
368
+
369
+ // List directory with options
370
+ const listResult = await fileManager.listDirectory('/projects', {
371
+ recursive: true,
372
+ includeHidden: false,
373
+ filter: (item) => !item.isDirectory && item.name.endsWith('.pdf')
374
+ });
375
+ ```
376
+
377
+ ### Working with Text Files
378
+
379
+ ```typescript
380
+ // Write text file
381
+ await fileManager.writeFile('/notes/readme.txt', 'Hello, World!');
382
+
383
+ // Read text file
384
+ const readResult = await fileManager.readFile('/notes/readme.txt');
385
+ if (readResult.success) {
386
+ console.log(readResult.data); // "Hello, World!"
387
+ }
388
+ ```
389
+
390
+ ### Folder Tree
391
+
392
+ ```typescript
393
+ // Get folder tree (3 levels deep by default)
394
+ const treeResult = await fileManager.getFolderTree('/projects', 3);
395
+ if (treeResult.success && treeResult.data) {
396
+ console.log(JSON.stringify(treeResult.data, null, 2));
397
+ }
398
+
399
+ // Output:
400
+ // [
401
+ // {
402
+ // "id": "abc123",
403
+ // "name": "2024",
404
+ // "path": "/projects/2024",
405
+ // "children": [
406
+ // {
407
+ // "id": "def456",
408
+ // "name": "docs",
409
+ // "path": "/projects/2024/docs",
410
+ // "children": []
411
+ // }
412
+ // ]
413
+ // }
414
+ // ]
415
+ ```
416
+
417
+ ## Configuration
418
+
419
+ ### Configuration File (`hazo_files_config.ini`)
420
+
421
+ ```ini
422
+ [general]
423
+ provider = local
424
+
425
+ [local]
426
+ base_path = ./files
427
+ allowed_extensions = jpg,png,pdf,txt,doc,docx
428
+ max_file_size = 10485760
429
+
430
+ [google_drive]
431
+ client_id = your-client-id.apps.googleusercontent.com
432
+ client_secret = your-client-secret
433
+ redirect_uri = http://localhost:3000/api/auth/callback/google
434
+ refresh_token =
435
+ access_token =
436
+ root_folder_id =
437
+
438
+ [naming]
439
+ ; Supported date format tokens for naming rules
440
+ date_formats = YYYY,YY,MM,M,DD,D,MMM,MMMM,YYYY-MM-DD,YYYY-MMM-DD,DD-MM-YYYY,MM-DD-YYYY
441
+ ```
442
+
443
+ ### Environment Variables
444
+
445
+ The following environment variables can override configuration file values:
446
+
447
+ - `GOOGLE_DRIVE_CLIENT_ID`
448
+ - `GOOGLE_DRIVE_CLIENT_SECRET`
449
+ - `GOOGLE_DRIVE_REDIRECT_URI`
450
+ - `GOOGLE_DRIVE_REFRESH_TOKEN`
451
+ - `GOOGLE_DRIVE_ACCESS_TOKEN`
452
+ - `GOOGLE_DRIVE_ROOT_FOLDER_ID`
453
+
454
+ ### Configuration via Code
455
+
456
+ ```typescript
457
+ import { createInitializedFileManager } from 'hazo_files';
458
+
459
+ const fileManager = await createInitializedFileManager({
460
+ config: {
461
+ provider: 'local',
462
+ local: {
463
+ basePath: './storage',
464
+ allowedExtensions: ['jpg', 'png', 'gif', 'pdf'],
465
+ maxFileSize: 5 * 1024 * 1024 // 5MB
466
+ }
467
+ }
468
+ });
469
+ ```
470
+
471
+ ## UI Components
472
+
473
+ ### FileBrowser Component
474
+
475
+ The `FileBrowser` is a complete, drop-in file management UI with:
476
+
477
+ - Folder tree navigation
478
+ - File list (grid or list view)
479
+ - Breadcrumb navigation
480
+ - File preview (images, text, PDFs)
481
+ - Context menus and actions
482
+ - Upload, download, rename, delete operations
483
+ - Drag-and-drop support (if implemented in API)
484
+
485
+ ```typescript
486
+ import { FileBrowser } from 'hazo_files/ui';
487
+
488
+ <FileBrowser
489
+ api={api}
490
+ initialPath="/"
491
+ showPreview={true}
492
+ showTree={true}
493
+ viewMode="grid"
494
+ treeWidth={250}
495
+ previewHeight={300}
496
+ onError={(error) => console.error(error)}
497
+ onNavigate={(path) => console.log('Navigated to:', path)}
498
+ onSelect={(item) => console.log('Selected:', item)}
499
+ />
500
+ ```
501
+
502
+ ### Individual Components
503
+
504
+ You can also use individual components:
505
+
506
+ ```typescript
507
+ import {
508
+ PathBreadcrumb,
509
+ FolderTree,
510
+ FileList,
511
+ FilePreview,
512
+ FileActions
513
+ } from 'hazo_files/ui';
514
+
515
+ // Use individually with your own layout
516
+ ```
517
+
518
+ ### Hooks
519
+
520
+ ```typescript
521
+ import { useFileBrowser, useFileOperations } from 'hazo_files/ui';
522
+
523
+ function MyCustomFileBrowser() {
524
+ const {
525
+ currentPath,
526
+ files,
527
+ tree,
528
+ selectedItem,
529
+ isLoading,
530
+ navigate,
531
+ refresh,
532
+ selectItem
533
+ } = useFileBrowser(api, '/');
534
+
535
+ const {
536
+ createFolder,
537
+ uploadFiles,
538
+ deleteItem,
539
+ renameItem
540
+ } = useFileOperations(api, currentPath);
541
+
542
+ // Build your custom UI
543
+ }
544
+ ```
545
+
546
+ ### Naming Rule Configurator
547
+
548
+ Build consistent file/folder naming patterns with a visual drag-and-drop interface:
549
+
550
+ ```typescript
551
+ import { NamingRuleConfigurator } from 'hazo_files/ui';
552
+ import type { NamingVariable } from 'hazo_files/ui';
553
+
554
+ function NamingConfig() {
555
+ // Define user-specific variables
556
+ const userVariables: NamingVariable[] = [
557
+ {
558
+ variable_name: 'project_name',
559
+ description: 'Name of the project',
560
+ example_value: 'WebApp',
561
+ category: 'user'
562
+ },
563
+ {
564
+ variable_name: 'client_id',
565
+ description: 'Client identifier',
566
+ example_value: 'ACME',
567
+ category: 'user'
568
+ },
569
+ ];
570
+
571
+ const handleSchemaChange = (schema) => {
572
+ console.log('New schema:', schema);
573
+ // Save to database or state
574
+ };
575
+
576
+ const handleExport = (schema) => {
577
+ // Export as JSON file
578
+ const blob = new Blob([JSON.stringify(schema, null, 2)], { type: 'application/json' });
579
+ const url = URL.createObjectURL(blob);
580
+ const a = document.createElement('a');
581
+ a.href = url;
582
+ a.download = 'naming-rule.json';
583
+ a.click();
584
+ };
585
+
586
+ return (
587
+ <NamingRuleConfigurator
588
+ variables={userVariables}
589
+ onChange={handleSchemaChange}
590
+ onExport={handleExport}
591
+ sampleFileName="proposal.pdf"
592
+ />
593
+ );
594
+ }
595
+ ```
596
+
597
+ The configurator provides:
598
+ - **Category Tabs**: User, Date, File, Counter variables
599
+ - **Drag & Drop**: Build patterns by dragging variables into file/folder patterns
600
+ - **Segment Reordering**: Drag segments within patterns to reorder them
601
+ - **Live Preview**: See generated names in real-time with example values
602
+ - **Undo/Redo**: Full history with keyboard shortcuts (Ctrl+Z, Ctrl+Y)
603
+ - **Import/Export**: Save and load naming rules as JSON
604
+ - **Scrollable Layout**: Works in fixed-height containers with scrollable content area
605
+
606
+ System variables included:
607
+ - **Date**: YYYY, YY, MM, DD, YYYY-MM-DD, MMM, MMMM, etc.
608
+ - **File**: original_name, extension, ext
609
+ - **Counter**: counter (auto-incrementing with padding)
610
+
611
+ ## Naming Rules API
612
+
613
+ Generate file and folder names programmatically from naming schemas:
614
+
615
+ ```typescript
616
+ import {
617
+ hazo_files_generate_file_name,
618
+ hazo_files_generate_folder_name,
619
+ createVariableSegment,
620
+ createLiteralSegment,
621
+ type NamingRuleSchema
622
+ } from 'hazo_files';
623
+
624
+ // Create a naming schema
625
+ const schema: NamingRuleSchema = {
626
+ version: 1,
627
+ filePattern: [
628
+ createVariableSegment('client_id'),
629
+ createLiteralSegment('_'),
630
+ createVariableSegment('project_name'),
631
+ createLiteralSegment('_'),
632
+ createVariableSegment('YYYY-MM-DD'),
633
+ createLiteralSegment('_'),
634
+ createVariableSegment('counter'),
635
+ ],
636
+ folderPattern: [
637
+ createVariableSegment('YYYY'),
638
+ createLiteralSegment('/'),
639
+ createVariableSegment('client_id'),
640
+ createLiteralSegment('/'),
641
+ createVariableSegment('project_name'),
642
+ ],
643
+ };
644
+
645
+ // Define variable values
646
+ const variables = {
647
+ client_id: 'ACME',
648
+ project_name: 'Website',
649
+ };
650
+
651
+ // Generate file name
652
+ const fileResult = hazo_files_generate_file_name(
653
+ schema,
654
+ variables,
655
+ 'original-document.pdf',
656
+ {
657
+ counterValue: 42,
658
+ preserveExtension: true, // Keep original .pdf extension
659
+ date: new Date('2024-12-09'),
660
+ }
661
+ );
662
+
663
+ if (fileResult.success) {
664
+ console.log(fileResult.name);
665
+ // Output: "ACME_Website_2024-12-09_042.pdf"
666
+ }
667
+
668
+ // Generate folder path
669
+ const folderResult = hazo_files_generate_folder_name(schema, variables);
670
+
671
+ if (folderResult.success) {
672
+ console.log(folderResult.name);
673
+ // Output: "2024/ACME/Website"
674
+ }
675
+
676
+ // Use with FileManager
677
+ const uploadPath = `/${folderResult.name}/${fileResult.name}`;
678
+ await fileManager.uploadFile(buffer, uploadPath);
679
+ ```
680
+
681
+ ### Available System Variables
682
+
683
+ **Date Variables** (use current date unless overridden):
684
+ - `YYYY` - Full year (2024)
685
+ - `YY` - Two-digit year (24)
686
+ - `MM` - Month with zero padding (01-12)
687
+ - `M` - Month without padding (1-12)
688
+ - `DD` - Day with zero padding (01-31)
689
+ - `D` - Day without padding (1-31)
690
+ - `MMM` - Short month name (Jan, Feb, etc.)
691
+ - `MMMM` - Full month name (January, February, etc.)
692
+ - `YYYY-MM-DD` - ISO date format (2024-01-15)
693
+ - `YYYY-MMM-DD` - Date with month name (2024-Jan-15)
694
+ - `DD-MM-YYYY` - European format (15-01-2024)
695
+ - `MM-DD-YYYY` - US format (01-15-2024)
696
+
697
+ **File Metadata Variables** (from original filename):
698
+ - `original_name` - Filename without extension
699
+ - `extension` - File extension with dot (.pdf)
700
+ - `ext` - Extension without dot (pdf)
701
+
702
+ **Counter Variable**:
703
+ - `counter` - Auto-incrementing number with zero padding (001, 042, 123)
704
+
705
+ ### Parsing Pattern Strings
706
+
707
+ You can also parse pattern strings directly:
708
+
709
+ ```typescript
710
+ import { parsePatternString, patternToString } from 'hazo_files';
711
+
712
+ // Parse string to segments
713
+ const segments = parsePatternString('{client_id}_{YYYY-MM-DD}_{counter}');
714
+ console.log(segments);
715
+ // [
716
+ // { id: '...', type: 'variable', value: 'client_id' },
717
+ // { id: '...', type: 'literal', value: '_' },
718
+ // { id: '...', type: 'variable', value: 'YYYY-MM-DD' },
719
+ // { id: '...', type: 'literal', value: '_' },
720
+ // { id: '...', type: 'variable', value: 'counter' },
721
+ // ]
722
+
723
+ // Convert back to string
724
+ const patternStr = patternToString(segments);
725
+ // "{client_id}_{YYYY-MM-DD}_{counter}"
726
+ ```
727
+
728
+ ## API Reference
729
+
730
+ ### FileManager
731
+
732
+ Main service class providing unified file operations.
733
+
734
+ #### Methods
735
+
736
+ - `initialize(config?: HazoFilesConfig): Promise<void>` - Initialize the file manager
737
+ - `createDirectory(path: string): Promise<OperationResult<FolderItem>>` - Create directory
738
+ - `removeDirectory(path: string, recursive?: boolean): Promise<OperationResult>` - Remove directory
739
+ - `uploadFile(source, remotePath, options?): Promise<OperationResult<FileItem>>` - Upload file
740
+ - `downloadFile(remotePath, localPath?, options?): Promise<OperationResult<Buffer | string>>` - Download file
741
+ - `moveItem(sourcePath, destinationPath, options?): Promise<OperationResult<FileSystemItem>>` - Move file/folder
742
+ - `deleteFile(path: string): Promise<OperationResult>` - Delete file
743
+ - `renameFile(path, newName, options?): Promise<OperationResult<FileItem>>` - Rename file
744
+ - `renameFolder(path, newName, options?): Promise<OperationResult<FolderItem>>` - Rename folder
745
+ - `listDirectory(path, options?): Promise<OperationResult<FileSystemItem[]>>` - List directory contents
746
+ - `getItem(path: string): Promise<OperationResult<FileSystemItem>>` - Get file/folder info
747
+ - `exists(path: string): Promise<boolean>` - Check if file/folder exists
748
+ - `getFolderTree(path?, depth?): Promise<OperationResult<TreeNode[]>>` - Get folder tree
749
+ - `writeFile(path, content, options?): Promise<OperationResult<FileItem>>` - Write text file
750
+ - `readFile(path: string): Promise<OperationResult<string>>` - Read text file
751
+ - `copyFile(sourcePath, destinationPath, options?): Promise<OperationResult<FileItem>>` - Copy file
752
+ - `ensureDirectory(path: string): Promise<OperationResult<FolderItem>>` - Ensure directory exists
753
+
754
+ ### Types
755
+
756
+ ```typescript
757
+ type StorageProvider = 'local' | 'google_drive';
758
+
759
+ interface FileItem {
760
+ id: string;
761
+ name: string;
762
+ path: string;
763
+ size: number;
764
+ mimeType: string;
765
+ createdAt: Date;
766
+ modifiedAt: Date;
767
+ isDirectory: false;
768
+ parentId?: string;
769
+ metadata?: Record<string, unknown>;
770
+ }
771
+
772
+ interface FolderItem {
773
+ id: string;
774
+ name: string;
775
+ path: string;
776
+ createdAt: Date;
777
+ modifiedAt: Date;
778
+ isDirectory: true;
779
+ parentId?: string;
780
+ children?: (FileItem | FolderItem)[];
781
+ metadata?: Record<string, unknown>;
782
+ }
783
+
784
+ interface OperationResult<T = void> {
785
+ success: boolean;
786
+ data?: T;
787
+ error?: string;
788
+ }
789
+
790
+ interface UploadOptions {
791
+ overwrite?: boolean;
792
+ onProgress?: (progress: number, bytesTransferred: number, totalBytes: number) => void;
793
+ metadata?: Record<string, unknown>;
794
+ }
795
+ ```
796
+
797
+ See `src/types/index.ts` for complete type definitions.
798
+
799
+ ## Error Handling
800
+
801
+ hazo_files provides comprehensive error types:
802
+
803
+ ```typescript
804
+ import {
805
+ FileNotFoundError,
806
+ DirectoryNotFoundError,
807
+ FileExistsError,
808
+ DirectoryExistsError,
809
+ DirectoryNotEmptyError,
810
+ PermissionDeniedError,
811
+ InvalidPathError,
812
+ FileTooLargeError,
813
+ InvalidExtensionError,
814
+ AuthenticationError,
815
+ ConfigurationError,
816
+ OperationError
817
+ } from 'hazo_files';
818
+
819
+ // Use in try-catch
820
+ try {
821
+ await fileManager.uploadFile(buffer, '/files/test.exe');
822
+ } catch (error) {
823
+ if (error instanceof InvalidExtensionError) {
824
+ console.error('File type not allowed');
825
+ } else if (error instanceof FileTooLargeError) {
826
+ console.error('File is too large');
827
+ }
828
+ }
829
+ ```
830
+
831
+ ## Extending with Custom Storage Providers
832
+
833
+ See [docs/ADDING_MODULES.md](docs/ADDING_MODULES.md) for a complete guide on creating custom storage modules.
834
+
835
+ Quick example:
836
+
837
+ ```typescript
838
+ import { BaseStorageModule } from 'hazo_files';
839
+ import type { StorageProvider, OperationResult, FileItem } from 'hazo_files';
840
+
841
+ class S3StorageModule extends BaseStorageModule {
842
+ readonly provider: StorageProvider = 's3' as StorageProvider;
843
+
844
+ async initialize(config: HazoFilesConfig): Promise<void> {
845
+ await super.initialize(config);
846
+ // Initialize S3 client
847
+ }
848
+
849
+ async uploadFile(source, remotePath, options?): Promise<OperationResult<FileItem>> {
850
+ // Implement S3 upload
851
+ }
852
+
853
+ // Implement other required methods...
854
+ }
855
+
856
+ // Register the module
857
+ import { registerModule } from 'hazo_files';
858
+ registerModule('s3', () => new S3StorageModule());
859
+ ```
860
+
861
+ ## Testing
862
+
863
+ The package includes a test application in `test-app/` demonstrating:
864
+
865
+ - Next.js 14+ integration
866
+ - API routes for file operations
867
+ - FileBrowser UI component usage
868
+ - Local storage and Google Drive switching
869
+ - OAuth flow implementation
870
+
871
+ To run the test app:
872
+
873
+ ```bash
874
+ cd test-app
875
+ npm install
876
+ npm run dev
877
+ ```
878
+
879
+ Visit `http://localhost:3000`
880
+
881
+ ## Browser Compatibility
882
+
883
+ The UI components require:
884
+
885
+ - Modern browsers with ES2020+ support
886
+ - React 18+
887
+ - CSS Grid and Flexbox support
888
+
889
+ Server-side code requires Node.js 16+
890
+
891
+ ## License
892
+
893
+ MIT License - see LICENSE file for details
894
+
895
+ ## Contributing
896
+
897
+ Contributions are welcome! Please:
898
+
899
+ 1. Fork the repository
900
+ 2. Create a feature branch
901
+ 3. Commit your changes with clear messages
902
+ 4. Add tests for new functionality
903
+ 5. Submit a pull request
904
+
905
+ ## Support
906
+
907
+ - GitHub Issues: [https://github.com/pub12/hazo_files/issues](https://github.com/pub12/hazo_files/issues)
908
+ - Documentation: [https://github.com/pub12/hazo_files](https://github.com/pub12/hazo_files)
909
+
910
+ ## Roadmap
911
+
912
+ - Amazon S3 storage module
913
+ - Dropbox storage module
914
+ - OneDrive storage module
915
+ - WebDAV support
916
+ - Advanced search and filtering
917
+ - Batch operations
918
+ - File versioning
919
+ - Sharing and permissions
920
+
921
+ ## Credits
922
+
923
+ Created by Pubs Abayasiri
924
+
925
+ Built with:
926
+ - TypeScript
927
+ - React
928
+ - Google APIs (googleapis)
929
+ - tsup for building