hazo_files 1.4.7 → 1.5.1

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/CHANGE_LOG.md ADDED
@@ -0,0 +1,481 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 1.5.0
9
+
10
+ - **NEW**: `background_upload` module — UploadManager, Job, PipelineExecutor for pipelines that survive React component unmount.
11
+ - Subpath: `hazo_files/background-upload` (core, framework-agnostic)
12
+ - Subpath: `hazo_files/background-upload/react` (Provider, useFileUpload, useJobStatus, sonner toast bridge)
13
+
14
+ ## [1.4.7] - 2026-05-10
15
+
16
+ ### Changed
17
+ - Aligned dev dependencies to workspace canonical versions: `@types/node` (`^20.14.10`), `@types/react` (`^18.3.3`), `@types/react-dom` (`^18.3.0`), `typescript` (`^5.7.2`)
18
+ - Tightened peer dependency ranges from loose `>=` to pinned major.minor: `hazo_connect` (`^2.4.0`), `hazo_debug` (`^2.0.0`), `hazo_llm_api` (`^1.2.0`), `hazo_logs` (`^1.0.13`)
19
+ - test-app migrated to Tailwind CSS v4: replaced `tailwindcss-animate` with `tw-animate-css`, removed `autoprefixer` (Lightning CSS in `@tailwindcss/postcss` handles prefixing), updated `globals.css` to use `@import "tailwindcss"` syntax with `@config` directive
20
+ - test-app `clsx` (`^2.1.1`) and `tailwind-merge` (`^3.5.0`) bumped to canonical versions
21
+
22
+ ### Notes
23
+ - Consumers using the optional `hazo_debug` integration must be on `hazo_debug ^2.0.0` (previously `>=1.0.0`)
24
+ - No public API changes; consumer setup remains unchanged
25
+
26
+ ## [1.4.6] - 2026-03-30
27
+
28
+ ### Added
29
+ - Structured `file_operation` log entries with timing (`duration_ms`), storage provider, and operation type emitted on every file operation via the `logger` option
30
+ - `logger` option on `FileManagerOptions` — enables structured logging at the storage level even without database tracking
31
+ - Logger passthrough from `createHazoFilesServer` → `TrackedFileManager` → `FileMetadataService` for end-to-end operation logging
32
+ - `hazo_debug` as optional peer dependency for visual file operation debugging
33
+ - Debug integration section in README documenting how to bridge hazo_files logs to hazo_debug
34
+
35
+ ## [1.4.5] - 2026-03-27
36
+
37
+ ### Security
38
+ - Fix path traversal vulnerability in `renameFile` and `renameFolder` — user-supplied `newName` is now validated to reject path separators and `..`
39
+ - Fix Google Drive API query injection — single quotes in file/folder names are now properly escaped in Drive API queries
40
+ - Fix SQL injection in schema helper functions (`getSchemaForTable`, `getMigrationForTable`, `getNamingSchemaForTable`, `getMigrationV3ForTable`) — custom table names are now validated against `^[a-z_][a-z0-9_]*$`
41
+
42
+ ### Fixed
43
+ - `initializeSync` now throws a clear error instead of silently creating a broken state (storage modules require async initialization)
44
+ - `hasFileChanged` no longer hashes file path strings — returns `null` (unknown) when download returns a path instead of content
45
+ - `exchangeCodeForTokens` handles nullable `refresh_token` with fallback instead of non-null assertion
46
+ - `server-only` guard in `hazo_files/server` now logs a warning instead of silently swallowing the check
47
+
48
+ ### Changed
49
+ - **Breaking (peer deps)**: `googleapis`, `dropbox`, and `xxhash-wasm` moved from `dependencies` to optional `peerDependencies` — consumers must now install the SDKs for the storage providers they use
50
+ - Live `hazo_files_config.ini` no longer published to npm (prevents accidental credential exposure)
51
+
52
+ ### Added
53
+ - `tsconfig.build.json` — separate build config per workspace standard
54
+ - `config/hazo_files_config.ini.sample` — configuration template with all options documented including Dropbox
55
+ - `design/architecture.md` — architecture overview and design decisions
56
+ - `migrations/` directory with SQL files for all schema versions (001, 002, 003)
57
+
58
+ ## [1.4.1] - 2026-02-09
59
+
60
+ ### Fixed
61
+ - Extension validation mismatch in LocalStorageModule: config extensions with leading dots (e.g., `.pdf`) were not matching extracted extensions without dots (e.g., `pdf`), causing all uploads to fail when `allowed_extensions` was configured with dotted format
62
+ - Extensions are now normalized at initialization (trimmed, dot-stripped, lowercased), making validation dot-agnostic and case-insensitive
63
+
64
+ ## [1.0.0] - 2025-12-09
65
+
66
+ ### Added
67
+
68
+ #### Core Features
69
+ - Initial release of hazo_files package
70
+ - Unified FileManager service providing consistent API across storage providers
71
+ - Modular architecture for easy extension with custom storage providers
72
+ - Full TypeScript support with comprehensive type definitions
73
+
74
+ #### Storage Providers
75
+ - **LocalStorageModule**: Direct filesystem operations using Node.js `fs` module
76
+ - Extension filtering via `allowedExtensions` configuration
77
+ - File size limits via `maxFileSize` configuration
78
+ - Progress tracking for upload/download operations
79
+ - Recursive directory operations
80
+ - **GoogleDriveModule**: Google Drive integration with OAuth 2.0
81
+ - Full Google Drive API v3 implementation
82
+ - OAuth authentication with automatic token refresh
83
+ - Token persistence via callback system
84
+ - Configurable root folder support
85
+
86
+ #### File Operations
87
+ - Create and remove directories (with recursive option)
88
+ - Upload files from local path, Buffer, or ReadableStream
89
+ - Download files to local path or return as Buffer
90
+ - Move and rename files and folders
91
+ - Delete files
92
+ - List directory contents with filtering options
93
+ - Get file/folder metadata
94
+ - Check file/folder existence
95
+ - Generate folder tree structure
96
+
97
+ #### UI Components
98
+ - **FileBrowser**: Complete drop-in file browser component
99
+ - Folder tree navigation with lazy loading
100
+ - File list with grid and list view modes
101
+ - Breadcrumb navigation
102
+ - File preview for images, text, and PDFs
103
+ - Action toolbar with upload, download, create, rename, delete
104
+ - Drag-and-drop support (via API adapter)
105
+ - **Individual Components**: PathBreadcrumb, FolderTree, FileList, FilePreview, FileActions
106
+ - **Dialogs**: CreateFolderDialog, RenameDialog, DeleteConfirmDialog, UploadDialog
107
+ - **Hooks**: useFileBrowser, useFileOperations, useMultiFileOperations
108
+
109
+ #### Configuration System
110
+ - INI file configuration (`hazo_files_config.ini`)
111
+ - Environment variable support with automatic override
112
+ - Programmatic configuration via code
113
+ - Configuration validation and defaults
114
+ - Sample configuration generator
115
+
116
+ #### Common Utilities
117
+ - **Error Types**: 12 specific error classes for different failure scenarios
118
+ - FileNotFoundError, DirectoryNotFoundError
119
+ - FileExistsError, DirectoryExistsError
120
+ - DirectoryNotEmptyError, PermissionDeniedError
121
+ - InvalidPathError, FileTooLargeError
122
+ - InvalidExtensionError, AuthenticationError
123
+ - ConfigurationError, OperationError
124
+ - **Path Utilities**: Normalization, joining, validation, sanitization
125
+ - **MIME Type Detection**: 50+ file types with category classification
126
+ - **Helper Functions**: ID generation, byte formatting, item sorting/filtering
127
+
128
+ #### Developer Experience
129
+ - Comprehensive documentation
130
+ - README with quick start and usage examples
131
+ - Technical documentation (TECHDOC.md)
132
+ - AI-optimized reference guide (CLAUDE.md)
133
+ - Setup checklist (SETUP_CHECKLIST.md)
134
+ - Module creation guide (docs/ADDING_MODULES.md)
135
+ - Complete TypeScript definitions
136
+ - Example Next.js test application
137
+ - Progress callbacks for upload/download operations
138
+ - OperationResult pattern for consistent error handling
139
+
140
+ ### Design Decisions
141
+
142
+ #### Virtual Path System
143
+ **Decision**: Use Unix-style virtual paths (`/folder/file.pdf`) across all storage providers.
144
+
145
+ **Rationale**:
146
+ - Provides consistent API regardless of storage backend
147
+ - Prevents Windows/Unix path separator conflicts
148
+ - Simplifies path manipulation and validation
149
+ - Easier to implement access control and path restrictions
150
+
151
+ #### OperationResult Pattern
152
+ **Decision**: Return `{ success: boolean, data?: T, error?: string }` instead of throwing exceptions.
153
+
154
+ **Rationale**:
155
+ - Expected failures (file not found, permission denied) shouldn't be exceptions
156
+ - Easier error handling in async code
157
+ - Consistent pattern across all operations
158
+ - Better for API responses (JSON-serializable)
159
+ - Allows partial success in batch operations
160
+
161
+ #### Module System
162
+ **Decision**: BaseStorageModule abstract class with StorageModule interface.
163
+
164
+ **Rationale**:
165
+ - Enforces consistent interface across providers
166
+ - Provides common utilities (path normalization, result helpers)
167
+ - Reduces code duplication
168
+ - Makes adding new providers straightforward
169
+ - Separates provider-specific logic from common functionality
170
+
171
+ #### Separate UI Package Export
172
+ **Decision**: Export UI components separately (`hazo_files/ui`)
173
+
174
+ **Rationale**:
175
+ - Core package has no React dependency
176
+ - Allows using file management without UI
177
+ - Reduces bundle size for server-only usage
178
+ - Clear separation of concerns
179
+ - Different peer dependencies (React only for UI)
180
+
181
+ #### Configuration Priority
182
+ **Decision**: Code > Environment Variables > INI File > Defaults
183
+
184
+ **Rationale**:
185
+ - Code config for programmatic control (tests, dynamic config)
186
+ - Environment variables for deployment (Docker, cloud platforms)
187
+ - INI file for local development and simple deployments
188
+ - Defaults prevent configuration errors from breaking initialization
189
+
190
+ ### Security
191
+
192
+ - Path validation prevents directory traversal attacks
193
+ - Filename sanitization removes dangerous characters
194
+ - Extension whitelisting prevents unwanted file types
195
+ - File size limits prevent resource exhaustion
196
+ - OAuth 2.0 for Google Drive with automatic token refresh
197
+ - No client-side credential exposure
198
+
199
+ ### Performance
200
+
201
+ - Local storage: Sub-10ms operations for most actions
202
+ - Google Drive: 200-1000ms operations (network-dependent)
203
+ - Stream-based transfers for memory efficiency
204
+ - Lazy loading for folder tree
205
+ - Progress tracking without blocking
206
+
207
+ ### Dependencies
208
+
209
+ #### Runtime
210
+ - `googleapis` (^140.0.1): Google Drive API client
211
+ - `ini` (^4.1.3): INI file parsing
212
+
213
+ #### Development
214
+ - `typescript` (^5.3.3): TypeScript compiler
215
+ - `tsup` (^8.0.1): Build tool
216
+ - `vitest` (^1.1.0): Testing framework
217
+
218
+ #### Peer Dependencies
219
+ - `react` (^18.0.0): For UI components
220
+ - `react-dom` (^18.0.0): For UI components
221
+
222
+ ### Known Limitations
223
+
224
+ - Google Drive path resolution requires multiple API calls (O(n) where n = path depth)
225
+ - No built-in caching for Google Drive path-to-ID mappings
226
+ - UI components do not implement virtual scrolling for large directories
227
+ - File preview loads entire file into memory
228
+ - No batch operation support for Google Drive
229
+ - Local storage requires shared filesystem for horizontal scaling
230
+
231
+ ### Migration Notes
232
+
233
+ This is the initial release, no migration required.
234
+
235
+ ### Contributors
236
+
237
+ - Pubs Abayasiri (Creator and maintainer)
238
+
239
+ ---
240
+
241
+ ## [Unreleased]
242
+
243
+ ### Added
244
+
245
+ #### FileBrowser Drag-and-Drop File Moving (2026-01-19)
246
+
247
+ **Feature**: Native drag-and-drop functionality for moving files and folders within the FileBrowser component.
248
+
249
+ **User-Facing Features**:
250
+ - Drag files and folders from the file list (grid or list view)
251
+ - Drop onto folders in either the sidebar tree or main file list
252
+ - Visual feedback with opacity and colored borders during drag
253
+ - Real-time validation prevents invalid operations
254
+ - Shows dragged item preview (icon + name) following cursor
255
+
256
+ **Visual Feedback**:
257
+ - **Dragging**: Dragged item becomes semi-transparent (opacity-50)
258
+ - **Valid drop target**: Green ring border (`ring-2 ring-green-500`) and light green background (`bg-green-50`)
259
+ - **Invalid drop target**: No highlighting (drop is ignored)
260
+ - **Drag preview**: White card with shadow showing file/folder icon and name (max width 200px)
261
+
262
+ **Drop Validation** (automatic, no user action needed):
263
+ - Prevents dropping item onto itself
264
+ - Prevents dropping into current parent directory (no-op)
265
+ - Prevents dropping folder into its own descendant (circular reference prevention)
266
+ - All other moves are allowed
267
+
268
+ **Technical Implementation**:
269
+ - Uses `@dnd-kit/core` library (already a peer dependency for NamingRuleConfigurator)
270
+ - Single top-level DndContext in FileBrowser component
271
+ - PointerSensor with 8px activation distance prevents accidental drags
272
+ - `closestCenter` collision detection algorithm
273
+
274
+ **New Component** (`src/ui/components/DragPreview.tsx`):
275
+ - `DragPreview` - Visual preview component shown during drag
276
+ - Props: `item: FileSystemItem`, `className?: string`
277
+ - Displays file/folder icon and name with styling
278
+
279
+ **API Integration**:
280
+ - Requires `FileBrowserAPI.moveItem(sourcePath, destinationPath)` method
281
+ - Automatically refreshes directory list and folder tree after successful move
282
+ - Error handling via `onError` callback prop
283
+
284
+ **ID Patterns Used**:
285
+ - Draggable items: `file-item-{path}` (all files and folders in FileList)
286
+ - Drop targets in tree: `folder-drop-tree-{path}` (folders in FolderTree sidebar)
287
+ - Drop targets in list: `folder-drop-list-{path}` (folders in FileList)
288
+
289
+ **State Management**:
290
+ - `draggedItem`: Currently dragged FileSystemItem
291
+ - `dropTargetPath`: Path of valid drop target being hovered
292
+ - `isDragging`: Boolean flag for drag in progress
293
+
294
+ **Files Changed**:
295
+ - `src/ui/components/FileBrowser.tsx` - Added DndContext, drag handlers, state management
296
+ - `src/ui/components/DragPreview.tsx` - New component for drag preview
297
+ - `src/ui/components/FileList.tsx` - Added useDraggable and useDroppable hooks
298
+ - `src/ui/components/FolderTree.tsx` - Added useDroppable hook
299
+ - `src/ui/index.ts` - Export DragPreview component and DragPreviewProps type
300
+
301
+ **Dependencies**:
302
+ - No new dependencies (uses existing `@dnd-kit/core` peer dependency)
303
+
304
+ **Design Decisions**:
305
+
306
+ **Single DndContext Pattern**:
307
+ - **Decision**: Use single top-level DndContext in FileBrowser, not in child components
308
+ - **Rationale**: Nested DndContext blocks drag events from parent handlers (same pattern as NamingRuleConfigurator). Child components use only useDroppable/useDraggable hooks.
309
+
310
+ **ID Prefix Convention**:
311
+ - **Decision**: Use prefixed IDs (`file-item-`, `folder-drop-tree-`, `folder-drop-list-`)
312
+ - **Rationale**: Allows distinguishing between draggable items and drop targets. Necessary because folders are both draggable and droppable.
313
+
314
+ **Validation Before API Call**:
315
+ - **Decision**: Validate drop targets twice: during drag (for visual feedback) and before API call
316
+ - **Rationale**: Prevents unnecessary API calls for invalid drops, provides immediate visual feedback
317
+
318
+ **Performance Optimization**:
319
+ - 8px activation distance prevents accidental drags during normal clicking
320
+ - CSS-based visual feedback (no re-renders)
321
+ - DragOverlay renders outside main component tree (isolated updates)
322
+
323
+ **Use Cases**:
324
+ - Reorganizing files into folders
325
+ - Moving files between project folders
326
+ - Quick file organization without copy/paste
327
+ - Dragging from file list to sidebar tree for easy navigation
328
+
329
+ #### Naming Rules Configuration System (2025-12-09)
330
+
331
+ **Feature**: Comprehensive naming rules system for generating consistent file and folder names.
332
+
333
+ **Core Types** (`src/types/naming.ts`):
334
+ - `NamingVariable` - Define custom and system variables with descriptions and examples
335
+ - `PatternSegment` - Building blocks for naming patterns (variable or literal)
336
+ - `NamingRuleSchema` - Complete schema with file and folder patterns, metadata
337
+ - `GeneratedNameResult` - Result object from name generation
338
+ - `NameGenerationOptions` - Options for controlling generation behavior
339
+
340
+ **Utility Functions** (`src/common/naming-utils.ts`):
341
+ - `hazo_files_generate_file_name()` - Generate file names from schemas
342
+ - `hazo_files_generate_folder_name()` - Generate folder paths from schemas
343
+ - `validateNamingRuleSchema()` - Validate schema structure
344
+ - `parsePatternString()` - Parse "{var}text" strings to segments
345
+ - `patternToString()` - Convert segments back to strings
346
+ - `formatDateToken()` - Format dates to various tokens (YYYY, MM, DD, etc.)
347
+ - `formatCounter()` - Format counter with zero padding
348
+ - `createVariableSegment()`, `createLiteralSegment()` - Segment builders
349
+
350
+ **System Variables**:
351
+ - **Date variables**: YYYY, YY, MM, M, DD, D, MMM, MMMM, YYYY-MM-DD, YYYY-MMM-DD, DD-MM-YYYY, MM-DD-YYYY
352
+ - **File metadata**: original_name, extension, ext
353
+ - **Counter**: counter (auto-incrementing with padding)
354
+ - Exported as constants: `SYSTEM_DATE_VARIABLES`, `SYSTEM_FILE_VARIABLES`, `SYSTEM_COUNTER_VARIABLES`, `ALL_SYSTEM_VARIABLES`
355
+
356
+ **UI Components** (`src/ui/components/naming/`):
357
+ - `NamingRuleConfigurator` - Main drop-in component for visual pattern building
358
+ - `VariableList` - Category tabs (User/Date/File/Counter) with draggable variables
359
+ - `PatternBuilder` - Drag-and-drop zones for file and folder patterns
360
+ - `PatternPreview` - Live preview of generated names with example values
361
+ - `PatternSegmentItem` - Individual segments in patterns (editable/removable)
362
+ - `DraggableVariable` - Draggable variable chips
363
+ - `SeparatorPicker` - Quick-add common separators (-, _, space, /)
364
+
365
+ **Hooks** (`src/ui/hooks/useNamingRule.ts`):
366
+ - `useNamingRule` - State management for naming patterns with:
367
+ - Undo/redo support (max 50 history entries)
368
+ - Keyboard shortcuts (Ctrl+Z, Ctrl+Y, Ctrl+Shift+Z)
369
+ - Pattern manipulation functions (add, remove, update, reorder, clear)
370
+ - Schema import/export
371
+ - isDirty tracking
372
+
373
+ **Configuration** (`hazo_files_config.ini`):
374
+ - New `[naming]` section
375
+ - `date_formats` setting for customizing available date format tokens
376
+
377
+ **Dependencies**:
378
+ - Added peer dependencies: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` (for drag-and-drop UI)
379
+
380
+ **Design Decisions**:
381
+
382
+ **Variable-based Pattern System**:
383
+ - **Decision**: Use segment-based patterns instead of regex or string templates
384
+ - **Rationale**:
385
+ - Type-safe with full IDE support
386
+ - Easier to manipulate programmatically
387
+ - Works well with React drag-and-drop
388
+ - Clear separation between variables and literals
389
+ - Enables visual editing without string parsing complexity
390
+
391
+ **System vs User Variables**:
392
+ - **Decision**: Separate system variables (dates, file metadata) from user variables
393
+ - **Rationale**:
394
+ - System variables work automatically without configuration
395
+ - User variables are application-specific
396
+ - Category-based UI organization improves UX
397
+ - Clear distinction prevents naming conflicts
398
+
399
+ **Undo/Redo System**:
400
+ - **Decision**: Implement history with keyboard shortcuts in the hook
401
+ - **Rationale**:
402
+ - Essential for pattern building workflow
403
+ - Users expect standard shortcuts (Ctrl+Z)
404
+ - Separates history logic from UI components
405
+ - 50-entry limit prevents memory issues
406
+
407
+ **Prefix Convention**:
408
+ - **Decision**: Use `hazo_files_` prefix for main generation functions
409
+ - **Rationale**:
410
+ - Prevents naming conflicts with user code
411
+ - Makes functions easily discoverable
412
+ - Consistent with package naming
413
+ - Clear these are primary API functions
414
+
415
+ **Use Cases**:
416
+ - Document management systems requiring consistent naming
417
+ - Automated file organization workflows
418
+ - Multi-tenant applications with client-specific naming
419
+ - Date-based archival systems
420
+ - Sequential numbering for versioned documents
421
+
422
+ ### Fixed
423
+
424
+ #### NamingRuleConfigurator Drag-and-Drop Issues (2025-12-10)
425
+
426
+ **Fixed drag-and-drop for variables into patterns**:
427
+ - **Problem**: Variables from VariableList could not be dragged into PatternBuilder drop zones
428
+ - **Root Cause**: PatternBuilder had a nested DndContext that blocked drag events from the parent NamingRuleConfigurator
429
+ - **Solution**: Removed nested DndContext from PatternBuilder. Now uses single parent DndContext in NamingRuleConfigurator with SortableContext and useDroppable only in PatternBuilder
430
+ - **Impact**: Variables can now be dragged from the variable list into file and folder pattern areas
431
+ - **Files Changed**: `src/ui/components/naming/PatternBuilder.tsx`, `src/ui/components/naming/NamingRuleConfigurator.tsx`
432
+
433
+ **Why Nested DndContext Fails**: @dnd-kit prevents nested DndContext to avoid event bubbling conflicts. Child contexts block drag events from reaching parent handlers. Always use a single top-level DndContext with droppable/sortable children.
434
+
435
+ **Fixed segment reordering within patterns**:
436
+ - **Problem**: Segments could not be reordered by dragging within the same pattern
437
+ - **Root Cause**: handleDragEnd in NamingRuleConfigurator didn't handle the reordering case, only handled new variable drops
438
+ - **Solution**: Added Case 2 logic to handleDragEnd that detects when dragging between segments in the same pattern and calls reorderFilePattern or reorderFolderPattern with the correct indices
439
+ - **Impact**: Users can now reorder segments by dragging them to new positions within file or folder patterns
440
+ - **Files Changed**: `src/ui/components/naming/NamingRuleConfigurator.tsx`
441
+
442
+ **Improved drag activation sensitivity**:
443
+ - **Change**: Added PointerSensor with 8px activation distance
444
+ - **Rationale**: Prevents accidental drags when clicking on variables or segments
445
+ - **Impact**: More intentional drag interactions, better UX
446
+ - **Files Changed**: `src/ui/components/naming/NamingRuleConfigurator.tsx`
447
+
448
+ **Added visual drag feedback**:
449
+ - **Change**: Implemented DragOverlay showing the dragged variable during drag operation
450
+ - **Rationale**: Provides clear visual feedback of what is being dragged
451
+ - **Impact**: Users can see the variable chip following their cursor during drag
452
+ - **Files Changed**: `src/ui/components/naming/NamingRuleConfigurator.tsx`
453
+
454
+ **Made NamingRuleConfigurator content scrollable**:
455
+ - **Problem**: With many variables or long patterns, content overflowed the container with no way to scroll
456
+ - **Solution**: Wrapped Available Variables, Pattern Builders, and Preview sections in a scrollable container with `flex-1 overflow-y-auto min-h-0`, keeping Action Bar fixed at bottom
457
+ - **Impact**: Component now works well in fixed-height containers, all content is accessible via scroll
458
+ - **Files Changed**: `src/ui/components/naming/NamingRuleConfigurator.tsx`
459
+
460
+ ### Planned Features
461
+
462
+ - Amazon S3 storage module
463
+ - Dropbox storage module
464
+ - OneDrive storage module
465
+ - WebDAV support
466
+ - Path caching for Google Drive (performance optimization)
467
+ - Batch operations for Google Drive
468
+ - Virtual scrolling for large file lists
469
+ - File versioning support
470
+ - Sharing and permissions system
471
+ - Advanced search and filtering
472
+ - Thumbnail generation
473
+ - File compression/decompression
474
+ - Trash/recycle bin functionality
475
+
476
+ ---
477
+
478
+ **Note**: This changelog follows the principles of [Keep a Changelog](https://keepachangelog.com). Each version should be documented with Added, Changed, Deprecated, Removed, Fixed, and Security sections as applicable.
479
+
480
+ [1.0.0]: https://github.com/pub12/hazo_files/releases/tag/v1.0.0
481
+ [Unreleased]: https://github.com/pub12/hazo_files/compare/v1.0.0...HEAD
package/README.md CHANGED
@@ -20,6 +20,7 @@ A powerful, modular file management package for Node.js and React applications w
20
20
  - **File Change Detection**: xxHash-based content hashing for efficient change detection
21
21
  - **Content Tagging**: Optional LLM-based content classification at upload time or on-demand via `content_tag` field
22
22
  - **Schema Migrations**: Built-in V2/V3 migration utilities for adding reference tracking and content tagging to existing databases
23
+ - **Background Upload Pipelines**: Framework-agnostic `UploadManager` + React `HazoFileUploadProvider` for multi-step upload pipelines that survive component unmount, with optional sonner toast bridge
23
24
  - **TypeScript**: Full type safety and IntelliSense support
24
25
  - **OAuth Integration**: Built-in Google Drive and Dropbox OAuth authentication
25
26
  - **Prompt Cache Invalidation**: Passthrough for hazo_llm_api prompt cache management via server instance
@@ -61,6 +62,12 @@ npm install server-only # Server-side safety (recommended)
61
62
  npm install xxhash-wasm # File change detection (optional)
62
63
  ```
63
64
 
65
+ For the background-upload sonner toast bridge (optional):
66
+
67
+ ```bash
68
+ npm install sonner # Toast notifications for background upload pipelines
69
+ ```
70
+
64
71
  ### Tailwind CSS v4 Setup (Required for UI Components)
65
72
 
66
73
  If you're using Tailwind CSS v4 with the UI components, you must add a `@source` directive to your CSS file to ensure Tailwind scans the package's files for utility classes.
@@ -1601,6 +1608,157 @@ const { fileManager, metadataService, namingService, extractionService, uploadEx
1601
1608
  invalidatePromptCache?.('classification', 'classify_document');
1602
1609
  ```
1603
1610
 
1611
+ ## Background Upload Pipelines
1612
+
1613
+ A framework-agnostic upload pipeline engine that survives React component unmount. Useful when uploads include multi-step server work (upload → LLM extract → user confirmation → DB commit) and the user may navigate away mid-flight.
1614
+
1615
+ Two subpath exports:
1616
+
1617
+ - `hazo_files/background-upload` — core, no React dependency (`UploadManager`, `Job`, `PipelineExecutor`, `TypedEventEmitter`, all types)
1618
+ - `hazo_files/background-upload/react` — React bindings (`HazoFileUploadProvider`, `useFileUpload`, `useJobStatus`, `useFileUploadToasts`)
1619
+
1620
+ ### Core API (framework-agnostic)
1621
+
1622
+ ```typescript
1623
+ import { UploadManager } from 'hazo_files/background-upload';
1624
+ import type { PipelineStep, PipelineContext, JobHandle } from 'hazo_files/background-upload';
1625
+
1626
+ const uploadStep: PipelineStep = {
1627
+ name: 'upload',
1628
+ async execute(ctx: PipelineContext, handle: JobHandle) {
1629
+ handle.set_status('uploading');
1630
+ for (let i = 0; i < ctx.files.length; i++) {
1631
+ // ... POST file to your /api/files/upload route
1632
+ handle.set_progress(i + 1, ctx.files.length);
1633
+ }
1634
+ },
1635
+ };
1636
+
1637
+ const extractStep: PipelineStep = {
1638
+ name: 'extract',
1639
+ async execute(ctx, handle) {
1640
+ handle.set_status('processing');
1641
+ const extracted = await fetch('/api/extract', { /* ... */ }).then(r => r.json());
1642
+ ctx.extracted_data = extracted;
1643
+ },
1644
+ };
1645
+
1646
+ const manager = new UploadManager({
1647
+ max_concurrent: 2,
1648
+ default_pipeline_steps: [uploadStep, extractStep],
1649
+ });
1650
+
1651
+ manager.on('job:completed', ({ job }) => console.log('done', job.job_id));
1652
+
1653
+ const batch_id = manager.submit_batch({
1654
+ files: [file1, file2],
1655
+ group_id: 'project-123',
1656
+ group_label: 'Q4 Tax Documents',
1657
+ });
1658
+ ```
1659
+
1660
+ ### React Provider + Hooks
1661
+
1662
+ ```tsx
1663
+ // app/layout.tsx (Next.js) or your app root
1664
+ 'use client';
1665
+ import { HazoFileUploadProvider } from 'hazo_files/background-upload/react';
1666
+ import { Toaster } from 'sonner';
1667
+
1668
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
1669
+ return (
1670
+ <HazoFileUploadProvider config={{ max_concurrent: 2 }}>
1671
+ <Toaster richColors />
1672
+ {children}
1673
+ </HazoFileUploadProvider>
1674
+ );
1675
+ }
1676
+ ```
1677
+
1678
+ ```tsx
1679
+ 'use client';
1680
+ import { useFileUpload, useJobStatus } from 'hazo_files/background-upload/react';
1681
+
1682
+ export function UploadButton() {
1683
+ const { submit_batch, active_jobs } = useFileUpload();
1684
+
1685
+ function onPick(files: FileList) {
1686
+ submit_batch({
1687
+ files: Array.from(files),
1688
+ group_id: 'project-123',
1689
+ group_label: 'Project 123',
1690
+ pipeline_steps: [/* your PipelineStep[] */],
1691
+ });
1692
+ }
1693
+
1694
+ return (
1695
+ <>
1696
+ <input type="file" multiple onChange={(e) => onPick(e.target.files!)} />
1697
+ <ul>
1698
+ {active_jobs.map((j) => (
1699
+ <li key={j.job_id}>{j.group_label}: {j.status}</li>
1700
+ ))}
1701
+ </ul>
1702
+ </>
1703
+ );
1704
+ }
1705
+
1706
+ // Track a single job
1707
+ export function JobBadge({ job_id }: { job_id: string }) {
1708
+ const job = useJobStatus(job_id);
1709
+ if (!job) return null;
1710
+ return <span>{job.status} {job.progress && `${job.progress.current}/${job.progress.total}`}</span>;
1711
+ }
1712
+ ```
1713
+
1714
+ ### Confirmation Steps (user-in-the-loop)
1715
+
1716
+ ```typescript
1717
+ const confirmStep: PipelineStep = {
1718
+ name: 'confirm',
1719
+ async execute(ctx, handle) {
1720
+ handle.set_status('awaiting_confirmation');
1721
+ const result = await handle.request_confirmation({
1722
+ conflicts: ctx.extracted_data.conflicts,
1723
+ });
1724
+ if (!result.confirmed) throw new Error('User cancelled');
1725
+ // ctx.extracted_data now reflects user choices via result.data
1726
+ },
1727
+ };
1728
+ ```
1729
+
1730
+ In the UI, subscribe to `job:confirmation_needed` (or read jobs in `awaiting_confirmation` status), render a dialog, then:
1731
+
1732
+ ```typescript
1733
+ const { resolve_confirmation } = useFileUpload();
1734
+ resolve_confirmation(job_id, { confirmed: true, data: userChoices });
1735
+ ```
1736
+
1737
+ ### Sonner Toast Bridge
1738
+
1739
+ The provider mounts a `ToastBridge` by default (`enable_toasts={true}`) that uses sonner to notify on `job:completed`, `job:error`, `job:confirmation_needed`, and `batch:completed`. Sonner is a soft optional peer dependency — if it isn't installed, the bridge is a no-op. Set `enable_toasts={false}` on the provider to opt out, or wire `useFileUploadToasts(manager)` yourself for custom toast behavior.
1740
+
1741
+ ### Events
1742
+
1743
+ | Event | Payload | Fired when |
1744
+ |-------|---------|------------|
1745
+ | `job:created` | `{ job }` | Job enters the queue |
1746
+ | `job:status_changed` | `{ job, previous_status }` | Status transitions (queued → uploading → processing → ...) |
1747
+ | `job:progress` | `{ job }` | `handle.set_progress` called inside a pipeline step |
1748
+ | `job:completed` | `{ job }` | All pipeline steps finished successfully |
1749
+ | `job:error` | `{ job, error }` | A pipeline step threw |
1750
+ | `job:confirmation_needed` | `{ job, payload }` | `handle.request_confirmation` called |
1751
+ | `job:confirmation_resolved` | `{ job, result }` | `resolve_confirmation` called |
1752
+ | `batch:progress` | `{ batch }` | Any job in the batch settles |
1753
+ | `batch:completed` | `{ batch }` | All jobs in the batch are `done` or `error` |
1754
+
1755
+ ### Design Notes
1756
+
1757
+ - **Survives unmount**: `UploadManager` lives on a `useRef`; pipelines run on the manager, not on React state. Navigating away does not abort uploads.
1758
+ - **Single source of truth**: `useFileUpload` / `useJobStatus` subscribe via `useSyncExternalStore` against the manager's event emitter, so multiple components stay consistent.
1759
+ - **Concurrency**: `max_concurrent` controls how many jobs the executor runs in parallel; the rest wait in the FIFO queue.
1760
+ - **No DB writes**: This module is purely an in-memory pipeline runner — your pipeline steps own all server I/O.
1761
+
1604
1762
  ## API Reference
1605
1763
 
1606
1764
  ### FileManager