mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-17 16:34:25 +00:00
staticweb package for easier static web server hosting
This commit is contained in:
592
pkg/server/staticweb/plan.md
Normal file
592
pkg/server/staticweb/plan.md
Normal file
@@ -0,0 +1,592 @@
|
||||
# StaticWeb Package Interface-Driven Refactoring Plan
|
||||
|
||||
## Overview
|
||||
Refactor `pkg/server/staticweb` to be interface-driven, router-agnostic, and maintainable. This is a breaking change that replaces the existing API with a cleaner design.
|
||||
|
||||
## User Requirements
|
||||
- ✅ Work with any server (not just Gorilla mux)
|
||||
- ✅ Serve static files from zip files or directories
|
||||
- ✅ Support embedded, local filesystems
|
||||
- ✅ Interface-driven and maintainable architecture
|
||||
- ✅ Struct-based configuration
|
||||
- ✅ Breaking changes acceptable
|
||||
- 🔮 Remote HTTP/HTTPS and S3 support (future feature)
|
||||
|
||||
## Design Principles
|
||||
1. **Interface-first**: Define clear interfaces for all abstractions
|
||||
2. **Composition over inheritance**: Combine small, focused components
|
||||
3. **Router-agnostic**: Return standard `http.Handler` for universal compatibility
|
||||
4. **Configurable policies**: Extract hardcoded behavior into pluggable strategies
|
||||
5. **Resource safety**: Proper lifecycle management with `Close()` methods
|
||||
6. **Testability**: Mock-friendly interfaces with clear boundaries
|
||||
7. **Extensibility**: Easy to add new providers (HTTP, S3, etc.) in future
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core Interfaces (pkg/server/staticweb/interfaces.go)
|
||||
|
||||
```go
|
||||
// FileSystemProvider abstracts file sources (local, zip, embedded, future: http, s3)
|
||||
type FileSystemProvider interface {
|
||||
Open(name string) (fs.File, error)
|
||||
Close() error
|
||||
Type() string
|
||||
}
|
||||
|
||||
// CachePolicy defines caching behavior
|
||||
type CachePolicy interface {
|
||||
GetCacheTime(path string) int
|
||||
GetCacheHeaders(path string) map[string]string
|
||||
}
|
||||
|
||||
// MIMETypeResolver determines content types
|
||||
type MIMETypeResolver interface {
|
||||
GetMIMEType(path string) string
|
||||
RegisterMIMEType(extension, mimeType string)
|
||||
}
|
||||
|
||||
// FallbackStrategy handles missing files
|
||||
type FallbackStrategy interface {
|
||||
ShouldFallback(path string) bool
|
||||
GetFallbackPath(path string) string
|
||||
}
|
||||
|
||||
// StaticFileService manages mount points
|
||||
type StaticFileService interface {
|
||||
Mount(config MountConfig) error
|
||||
Unmount(urlPrefix string) error
|
||||
ListMounts() []string
|
||||
Reload() error
|
||||
Close() error
|
||||
Handler() http.Handler // Router-agnostic integration
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration (pkg/server/staticweb/config.go)
|
||||
|
||||
```go
|
||||
// MountConfig configures a single mount point
|
||||
type MountConfig struct {
|
||||
URLPrefix string
|
||||
Provider FileSystemProvider
|
||||
CachePolicy CachePolicy // Optional, uses default if nil
|
||||
MIMEResolver MIMETypeResolver // Optional, uses default if nil
|
||||
FallbackStrategy FallbackStrategy // Optional, no fallback if nil
|
||||
}
|
||||
|
||||
// ServiceConfig configures the service
|
||||
type ServiceConfig struct {
|
||||
DefaultCacheTime int // Default: 48 hours
|
||||
DefaultMIMETypes map[string]string // Additional MIME types
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Step 1: Create Core Interfaces
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/interfaces.go` (NEW)
|
||||
|
||||
Define all interfaces listed above. This establishes the contract for all components.
|
||||
|
||||
### Step 2: Implement Default Policies
|
||||
**Directory**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/policies/` (NEW)
|
||||
|
||||
**File**: `policies/cache.go`
|
||||
- `SimpleCachePolicy` - Single TTL for all files
|
||||
- `ExtensionBasedCachePolicy` - Different TTL per file extension
|
||||
- `NoCachePolicy` - Disables caching
|
||||
|
||||
**File**: `policies/mime.go`
|
||||
- `DefaultMIMEResolver` - Standard web MIME types + stdlib
|
||||
- `ConfigurableMIMEResolver` - User-defined mappings
|
||||
- Migrate hardcoded MIME types from `InitMimeTypes()` (lines 60-76)
|
||||
|
||||
**File**: `policies/fallback.go`
|
||||
- `NoFallback` - Returns 404 for missing files
|
||||
- `HTMLFallbackStrategy` - SPA routing (serves index.html)
|
||||
- `ExtensionBasedFallback` - Current behavior (checks extensions)
|
||||
- Migrate logic from `StaticHTMLFallbackHandler()` (lines 241-285)
|
||||
|
||||
### Step 3: Implement FileSystem Providers
|
||||
**Directory**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/providers/` (NEW)
|
||||
|
||||
**File**: `providers/local.go`
|
||||
```go
|
||||
type LocalFSProvider struct {
|
||||
path string
|
||||
fs fs.FS
|
||||
}
|
||||
|
||||
func NewLocalFSProvider(path string) (*LocalFSProvider, error)
|
||||
func (l *LocalFSProvider) Open(name string) (fs.File, error)
|
||||
func (l *LocalFSProvider) Close() error
|
||||
func (l *LocalFSProvider) Type() string
|
||||
```
|
||||
|
||||
**File**: `providers/zip.go`
|
||||
```go
|
||||
type ZipFSProvider struct {
|
||||
zipPath string
|
||||
zipReader *zip.ReadCloser
|
||||
zipFS *zipfs.ZipFS
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewZipFSProvider(zipPath string) (*ZipFSProvider, error)
|
||||
func (z *ZipFSProvider) Open(name string) (fs.File, error)
|
||||
func (z *ZipFSProvider) Close() error
|
||||
func (z *ZipFSProvider) Type() string
|
||||
```
|
||||
- Integrates with existing `pkg/server/zipfs/zipfs.go`
|
||||
- Manages zip file lifecycle properly
|
||||
|
||||
**File**: `providers/embed.go`
|
||||
```go
|
||||
type EmbedFSProvider struct {
|
||||
embedFS *embed.FS
|
||||
zipFile string // Optional: path within embedded FS to zip file
|
||||
zipReader *zip.ReadCloser
|
||||
fs fs.FS
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewEmbedFSProvider(embedFS *embed.FS, zipFile string) (*EmbedFSProvider, error)
|
||||
func (e *EmbedFSProvider) Open(name string) (fs.File, error)
|
||||
func (e *EmbedFSProvider) Close() error
|
||||
func (e *EmbedFSProvider) Type() string
|
||||
```
|
||||
|
||||
### Step 4: Implement Mount Point
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/mount.go` (NEW)
|
||||
|
||||
```go
|
||||
type mountPoint struct {
|
||||
urlPrefix string
|
||||
provider FileSystemProvider
|
||||
cachePolicy CachePolicy
|
||||
mimeResolver MIMETypeResolver
|
||||
fallbackStrategy FallbackStrategy
|
||||
}
|
||||
|
||||
func newMountPoint(config MountConfig, defaults *ServiceConfig) (*mountPoint, error)
|
||||
func (m *mountPoint) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
func (m *mountPoint) Close() error
|
||||
```
|
||||
|
||||
**Key behaviors**:
|
||||
- Strips URL prefix before passing to provider
|
||||
- Applies cache headers via `CachePolicy`
|
||||
- Sets Content-Type via `MIMETypeResolver`
|
||||
- Falls back via `FallbackStrategy` if file not found
|
||||
- Integrates with `http.FileServer()` for actual serving
|
||||
|
||||
### Step 5: Implement Service
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/service.go` (NEW)
|
||||
|
||||
```go
|
||||
type service struct {
|
||||
mounts map[string]*mountPoint // urlPrefix -> mount
|
||||
config *ServiceConfig
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewService(config *ServiceConfig) StaticFileService
|
||||
func (s *service) Mount(config MountConfig) error
|
||||
func (s *service) Unmount(urlPrefix string) error
|
||||
func (s *service) ListMounts() []string
|
||||
func (s *service) Reload() error
|
||||
func (s *service) Close() error
|
||||
func (s *service) Handler() http.Handler
|
||||
```
|
||||
|
||||
**Handler Implementation**:
|
||||
- Performs longest-prefix matching to find mount point
|
||||
- Delegates to mount point's `ServeHTTP()`
|
||||
- Returns silently if no match (allows API routes to handle)
|
||||
- Thread-safe with `sync.RWMutex`
|
||||
|
||||
### Step 6: Create Configuration Helpers
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/config.go` (NEW)
|
||||
|
||||
```go
|
||||
// Default configurations
|
||||
func DefaultServiceConfig() *ServiceConfig
|
||||
func DefaultCachePolicy() CachePolicy
|
||||
func DefaultMIMEResolver() MIMETypeResolver
|
||||
|
||||
// Helper constructors
|
||||
func LocalProvider(path string) FileSystemProvider
|
||||
func ZipProvider(zipPath string) FileSystemProvider
|
||||
func EmbedProvider(embedFS *embed.FS, zipFile string) FileSystemProvider
|
||||
|
||||
// Policy constructors
|
||||
func SimpleCache(seconds int) CachePolicy
|
||||
func ExtensionCache(rules map[string]int) CachePolicy
|
||||
func HTMLFallback(indexFile string) FallbackStrategy
|
||||
func ExtensionFallback(staticExtensions []string) FallbackStrategy
|
||||
```
|
||||
|
||||
### Step 7: Update/Remove Existing Files
|
||||
|
||||
**REMOVE**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/staticweb.go`
|
||||
- All functionality migrated to new interface-based design
|
||||
- No backward compatibility needed per user request
|
||||
|
||||
**KEEP**: `/home/hein/hein/dev/ResolveSpec/pkg/server/zipfs/zipfs.go`
|
||||
- Still used by `ZipFSProvider`
|
||||
- Already implements `fs.FS` interface correctly
|
||||
|
||||
### Step 8: Create Examples and Tests
|
||||
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/example_test.go` (NEW)
|
||||
|
||||
```go
|
||||
func ExampleService_basic() { /* Serve local directory */ }
|
||||
func ExampleService_spa() { /* SPA with fallback */ }
|
||||
func ExampleService_multiple() { /* Multiple mount points */ }
|
||||
func ExampleService_zip() { /* Serve from zip file */ }
|
||||
func ExampleService_embedded() { /* Serve from embedded zip */ }
|
||||
```
|
||||
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/service_test.go` (NEW)
|
||||
- Test mount/unmount operations
|
||||
- Test longest-prefix matching
|
||||
- Test concurrent access
|
||||
- Test resource cleanup
|
||||
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/providers/providers_test.go` (NEW)
|
||||
- Test each provider implementation
|
||||
- Test resource cleanup
|
||||
- Test error handling
|
||||
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/testing/mocks.go` (NEW)
|
||||
- `MockFileSystemProvider` - In-memory file storage
|
||||
- `MockCachePolicy` - Configurable cache behavior
|
||||
- `MockMIMEResolver` - Custom MIME mappings
|
||||
- Test helpers for common scenarios
|
||||
|
||||
### Step 9: Create Documentation
|
||||
|
||||
**File**: `/home/hein/hein/dev/ResolveSpec/pkg/server/staticweb/README.md` (NEW)
|
||||
|
||||
Document:
|
||||
- Quick start examples
|
||||
- Interface overview
|
||||
- Provider implementations
|
||||
- Policy customization
|
||||
- Router integration patterns
|
||||
- Migration guide from old API
|
||||
- Future features roadmap
|
||||
|
||||
## Key Improvements Over Current Implementation
|
||||
|
||||
### 1. Router-Agnostic Design
|
||||
**Before**: Coupled to Gorilla mux via `RegisterRoutes(*mux.Router)`
|
||||
**After**: Returns `http.Handler`, works with any router
|
||||
|
||||
```go
|
||||
// Works with Gorilla Mux
|
||||
muxRouter.PathPrefix("/").Handler(service.Handler())
|
||||
|
||||
// Works with standard http.ServeMux
|
||||
http.Handle("/", service.Handler())
|
||||
|
||||
// Works with any http.Handler-compatible router
|
||||
```
|
||||
|
||||
### 2. Configurable Behaviors
|
||||
**Before**: Hardcoded MIME types, cache times, file extensions
|
||||
**After**: Pluggable policies
|
||||
|
||||
```go
|
||||
// Custom cache per file type
|
||||
cachePolicy := ExtensionCache(map[string]int{
|
||||
".html": 3600, // 1 hour
|
||||
".js": 86400, // 1 day
|
||||
".css": 86400, // 1 day
|
||||
".png": 604800, // 1 week
|
||||
})
|
||||
|
||||
// Custom fallback logic
|
||||
fallback := HTMLFallback("index.html")
|
||||
|
||||
service.Mount(MountConfig{
|
||||
URLPrefix: "/",
|
||||
Provider: LocalProvider("./dist"),
|
||||
CachePolicy: cachePolicy,
|
||||
FallbackStrategy: fallback,
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Better Resource Management
|
||||
**Before**: Manual zip file cleanup, easy to leak resources
|
||||
**After**: Proper lifecycle with `Close()` on all components
|
||||
|
||||
```go
|
||||
defer service.Close() // Cleans up all providers
|
||||
```
|
||||
|
||||
### 4. Testability
|
||||
**Before**: Hard to test, coupled to filesystem
|
||||
**After**: Mock providers for testing
|
||||
|
||||
```go
|
||||
mockProvider := testing.NewInMemoryProvider(map[string][]byte{
|
||||
"index.html": []byte("<html>test</html>"),
|
||||
})
|
||||
|
||||
service.Mount(MountConfig{
|
||||
URLPrefix: "/",
|
||||
Provider: mockProvider,
|
||||
})
|
||||
```
|
||||
|
||||
### 5. Extensibility
|
||||
**Before**: Need to modify code to support new file sources
|
||||
**After**: Implement `FileSystemProvider` interface
|
||||
|
||||
```go
|
||||
// Future: Add HTTP provider without changing core code
|
||||
type HTTPFSProvider struct { /* ... */ }
|
||||
func (h *HTTPFSProvider) Open(name string) (fs.File, error) { /* ... */ }
|
||||
func (h *HTTPFSProvider) Close() error { /* ... */ }
|
||||
func (h *HTTPFSProvider) Type() string { return "http" }
|
||||
```
|
||||
|
||||
## Future Features (To Implement Later)
|
||||
|
||||
### Remote HTTP/HTTPS Provider
|
||||
**File**: `providers/http.go` (FUTURE)
|
||||
|
||||
Serve static files from remote HTTP servers with local caching:
|
||||
|
||||
```go
|
||||
type HTTPFSProvider struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
cache LocalCache // Optional disk/memory cache
|
||||
cacheTTL time.Duration
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Example usage
|
||||
service.Mount(MountConfig{
|
||||
URLPrefix: "/cdn",
|
||||
Provider: HTTPProvider("https://cdn.example.com/assets"),
|
||||
})
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Fetch files from remote URLs on-demand
|
||||
- Local cache to reduce remote requests
|
||||
- Configurable TTL and cache eviction
|
||||
- HEAD request support for metadata
|
||||
- Retry logic and timeout handling
|
||||
- Support for authentication headers
|
||||
|
||||
### S3-Compatible Provider
|
||||
**File**: `providers/s3.go` (FUTURE)
|
||||
|
||||
Serve static files from S3, MinIO, or S3-compatible storage:
|
||||
|
||||
```go
|
||||
type S3FSProvider struct {
|
||||
bucket string
|
||||
prefix string
|
||||
region string
|
||||
client *s3.Client
|
||||
cache LocalCache
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Example usage
|
||||
service.Mount(MountConfig{
|
||||
URLPrefix: "/media",
|
||||
Provider: S3Provider("my-bucket", "static/", "us-east-1"),
|
||||
})
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- List and fetch objects from S3 buckets
|
||||
- Support for AWS S3, MinIO, DigitalOcean Spaces, etc.
|
||||
- IAM role or credential-based authentication
|
||||
- Optional local caching layer
|
||||
- Efficient metadata retrieval
|
||||
- Support for presigned URLs
|
||||
|
||||
### Other Future Providers
|
||||
- **GitProvider**: Serve files from Git repositories
|
||||
- **MemoryProvider**: In-memory filesystem for testing/temporary files
|
||||
- **ProxyProvider**: Proxy to another static file server
|
||||
- **CompositeProvider**: Fallback chain across multiple providers
|
||||
|
||||
### Advanced Cache Policies (FUTURE)
|
||||
- **TimedCachePolicy**: Different cache times by time of day
|
||||
- **UserAgentCachePolicy**: Cache based on client type
|
||||
- **ConditionalCachePolicy**: Smart cache based on file size/type
|
||||
- **DistributedCachePolicy**: Shared cache across service instances
|
||||
|
||||
### Advanced Fallback Strategies (FUTURE)
|
||||
- **RegexFallbackStrategy**: Pattern-based routing
|
||||
- **I18nFallbackStrategy**: Language-based file resolution
|
||||
- **VersionedFallbackStrategy**: A/B testing support
|
||||
|
||||
## Critical Files Summary
|
||||
|
||||
### Files to CREATE (in order):
|
||||
1. `pkg/server/staticweb/interfaces.go` - Core contracts
|
||||
2. `pkg/server/staticweb/config.go` - Configuration structs and helpers
|
||||
3. `pkg/server/staticweb/policies/cache.go` - Cache policy implementations
|
||||
4. `pkg/server/staticweb/policies/mime.go` - MIME resolver implementations
|
||||
5. `pkg/server/staticweb/policies/fallback.go` - Fallback strategy implementations
|
||||
6. `pkg/server/staticweb/providers/local.go` - Local directory provider
|
||||
7. `pkg/server/staticweb/providers/zip.go` - Zip file provider
|
||||
8. `pkg/server/staticweb/providers/embed.go` - Embedded filesystem provider
|
||||
9. `pkg/server/staticweb/mount.go` - Mount point implementation
|
||||
10. `pkg/server/staticweb/service.go` - Main service implementation
|
||||
11. `pkg/server/staticweb/testing/mocks.go` - Test helpers
|
||||
12. `pkg/server/staticweb/service_test.go` - Service tests
|
||||
13. `pkg/server/staticweb/providers/providers_test.go` - Provider tests
|
||||
14. `pkg/server/staticweb/example_test.go` - Example code
|
||||
15. `pkg/server/staticweb/README.md` - Documentation
|
||||
|
||||
### Files to REMOVE:
|
||||
1. `pkg/server/staticweb/staticweb.go` - Replaced by new design
|
||||
|
||||
### Files to KEEP:
|
||||
1. `pkg/server/zipfs/zipfs.go` - Used by ZipFSProvider
|
||||
|
||||
### Files for FUTURE (not in this refactoring):
|
||||
1. `pkg/server/staticweb/providers/http.go` - HTTP/HTTPS remote provider
|
||||
2. `pkg/server/staticweb/providers/s3.go` - S3-compatible storage provider
|
||||
3. `pkg/server/staticweb/providers/composite.go` - Fallback chain provider
|
||||
|
||||
## Example Usage After Refactoring
|
||||
|
||||
### Basic Static Site
|
||||
```go
|
||||
service := staticweb.NewService(nil) // Use defaults
|
||||
|
||||
err := service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/static",
|
||||
Provider: staticweb.LocalProvider("./public"),
|
||||
})
|
||||
|
||||
muxRouter.PathPrefix("/").Handler(service.Handler())
|
||||
```
|
||||
|
||||
### SPA with API Routes
|
||||
```go
|
||||
service := staticweb.NewService(nil)
|
||||
|
||||
service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/",
|
||||
Provider: staticweb.LocalProvider("./dist"),
|
||||
FallbackStrategy: staticweb.HTMLFallback("index.html"),
|
||||
})
|
||||
|
||||
// API routes take precedence (registered first)
|
||||
muxRouter.HandleFunc("/api/users", usersHandler)
|
||||
muxRouter.HandleFunc("/api/posts", postsHandler)
|
||||
|
||||
// Static files handle all other routes
|
||||
muxRouter.PathPrefix("/").Handler(service.Handler())
|
||||
```
|
||||
|
||||
### Multiple Mount Points with Different Policies
|
||||
```go
|
||||
service := staticweb.NewService(&staticweb.ServiceConfig{
|
||||
DefaultCacheTime: 3600,
|
||||
})
|
||||
|
||||
// Assets with long cache
|
||||
service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/assets",
|
||||
Provider: staticweb.LocalProvider("./assets"),
|
||||
CachePolicy: staticweb.SimpleCache(604800), // 1 week
|
||||
})
|
||||
|
||||
// HTML with short cache
|
||||
service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/",
|
||||
Provider: staticweb.LocalProvider("./public"),
|
||||
CachePolicy: staticweb.SimpleCache(300), // 5 minutes
|
||||
})
|
||||
|
||||
router.PathPrefix("/").Handler(service.Handler())
|
||||
```
|
||||
|
||||
### Embedded Files from Zip
|
||||
```go
|
||||
//go:embed assets.zip
|
||||
var assetsZip embed.FS
|
||||
|
||||
service := staticweb.NewService(nil)
|
||||
|
||||
service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/static",
|
||||
Provider: staticweb.EmbedProvider(&assetsZip, "assets.zip"),
|
||||
})
|
||||
|
||||
router.PathPrefix("/").Handler(service.Handler())
|
||||
```
|
||||
|
||||
### Future: CDN Fallback (when HTTP provider is implemented)
|
||||
```go
|
||||
// Primary CDN with local fallback
|
||||
service.Mount(staticweb.MountConfig{
|
||||
URLPrefix: "/static",
|
||||
Provider: staticweb.CompositeProvider(
|
||||
staticweb.HTTPProvider("https://cdn.example.com/assets"),
|
||||
staticweb.LocalProvider("./public/assets"),
|
||||
),
|
||||
})
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Each provider implementation independently
|
||||
- Each policy implementation independently
|
||||
- Mount point request handling
|
||||
- Service mount/unmount operations
|
||||
|
||||
### Integration Tests
|
||||
- Full request flow through service
|
||||
- Multiple mount points
|
||||
- Longest-prefix matching
|
||||
- Resource cleanup
|
||||
|
||||
### Example Tests
|
||||
- Executable examples in `example_test.go`
|
||||
- Demonstrate common usage patterns
|
||||
|
||||
## Migration Impact
|
||||
|
||||
### Breaking Changes
|
||||
- Complete API redesign (acceptable per user)
|
||||
- Package not currently used in codebase (no migration needed)
|
||||
- New consumers will use new API from start
|
||||
|
||||
### Future Extensibility
|
||||
The interface-driven design allows future additions without breaking changes:
|
||||
- Add HTTPFSProvider by implementing `FileSystemProvider`
|
||||
- Add S3FSProvider by implementing `FileSystemProvider`
|
||||
- Add custom cache policies by implementing `CachePolicy`
|
||||
- Add custom fallback strategies by implementing `FallbackStrategy`
|
||||
|
||||
## Implementation Order
|
||||
1. Interfaces (foundation)
|
||||
2. Configuration (API surface)
|
||||
3. Policies (pluggable behavior)
|
||||
4. Providers (filesystem abstraction)
|
||||
5. Mount Point (request handling)
|
||||
6. Service (orchestration)
|
||||
7. Tests (validation)
|
||||
8. Documentation (usage)
|
||||
9. Remove old code (cleanup)
|
||||
|
||||
This order ensures each layer builds on tested, working components.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user