Skip to content

Commit a7c6ddc

Browse files
author
Developer
committed
feat: implement Phase 6.3 - FS5 Media Extensions with comprehensive test suite
Core Implementation: - Add media-extensions.ts with putImage, getThumbnail, getImageMetadata, createImageGallery - Add media-types.ts with TypeScript interfaces for media operations - Fix TypeScript 5.8 Blob constructor compatibility in progressive/loader.ts Testing: - Add unit tests (test/fs/media-extensions.test.ts) with mocked FS5 operations - Add integration tests (test/fs/media-extensions.integration.test.ts) marked as skip for CI - Add standalone real S5 portal test (test/integration/test-media-real.js) • 14 tests organized into 4 logical groups • 100% pass rate with real S5 portal • Sequential execution with registry propagation delays • Portal registration and identity management Improvements: - Resolve registry conflicts with concurrency: 1 for reliable S5 operations - Add 5+ second delays for registry propagation - Support thumbnail generation, metadata extraction, and gallery creation - Integrate with existing FS5 path-based API All tests pass.
1 parent d3760c2 commit a7c6ddc

File tree

14 files changed

+2149
-24
lines changed

14 files changed

+2149
-24
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,24 @@ This test validates:
183183
- Error handling modes
184184
- Metadata preservation
185185

186+
### 5. Media Extensions Test (Phase 6.3)
187+
188+
Tests FS5 media integration (putImage, getThumbnail, getImageMetadata, createImageGallery) with real S5 instance:
189+
190+
```bash
191+
node test/integration/test-media-real.js
192+
```
193+
194+
This test validates:
195+
- Image upload with automatic thumbnail generation
196+
- Metadata extraction (format, dimensions)
197+
- Thumbnail retrieval (pre-generated and on-demand)
198+
- Gallery creation with manifest.json
199+
- Directory integration with media operations
200+
- Path-based API (no CID exposure)
201+
202+
Expected output: 10/10 tests passing
203+
186204
### Important Notes
187205

188206
- **Use Fresh Identities**: The new deterministic key derivation system requires fresh identities. Old accounts created with the previous system won't work.

docs/API.md

Lines changed: 236 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,9 @@ async getMetadata(path: string): Promise<Record<string, any> | undefined>
264264
name: "example.txt",
265265
size: 1234, // Size in bytes
266266
mediaType: "text/plain",
267-
timestamp: 1705432100000, // Milliseconds since epoch
268-
hash: "..." // File hash
267+
timestamp: 1705432100000 // Milliseconds since epoch
268+
// Note: Content hashes (CIDs) are not exposed in the path-based API
269+
// Files are identified by their paths, abstracting away content addressing
269270
}
270271
```
271272

@@ -1594,6 +1595,239 @@ async function extractColorPalette(imagePath: string) {
15941595
}
15951596
```
15961597

1598+
1599+
## FS5 Media Extensions (Phase 6.3)
1600+
1601+
The FS5 class provides integrated media operations that combine file system functionality with image processing capabilities. These methods use path-based identifiers consistent with FS5's design philosophy.
1602+
1603+
### putImage()
1604+
1605+
Upload an image with automatic metadata extraction and thumbnail generation.
1606+
1607+
```typescript
1608+
async putImage(
1609+
path: string,
1610+
blob: Blob,
1611+
options?: PutImageOptions
1612+
): Promise<ImageReference>
1613+
```
1614+
1615+
#### Parameters
1616+
1617+
- **path** (string): File system path where the image will be stored
1618+
- **blob** (Blob): Image data to upload
1619+
- **options** (PutImageOptions): Optional configuration
1620+
1621+
#### PutImageOptions
1622+
1623+
```typescript
1624+
interface PutImageOptions {
1625+
generateThumbnail?: boolean; // Default: true
1626+
thumbnailOptions?: ThumbnailOptions;
1627+
extractMetadata?: boolean; // Default: true
1628+
progressive?: boolean; // Default: false
1629+
progressiveOptions?: ProgressiveLoadingOptions;
1630+
// Plus all standard PutOptions (encryption, etc.)
1631+
}
1632+
```
1633+
1634+
#### Returns
1635+
1636+
```typescript
1637+
interface ImageReference {
1638+
path: string; // Path to uploaded image
1639+
thumbnailPath?: string; // Path to generated thumbnail
1640+
metadata?: ImageMetadata; // Extracted image metadata
1641+
}
1642+
```
1643+
1644+
**Note**: Content identifiers (CIDs) are not exposed. The path-based API abstracts away content addressing - files are identified by paths.
1645+
1646+
#### Example
1647+
1648+
```typescript
1649+
// Basic usage
1650+
const imageFile = await fetch('/photo.jpg').then(r => r.blob());
1651+
const result = await s5.fs.putImage('home/photos/vacation.jpg', imageFile);
1652+
1653+
console.log(`Uploaded to: ${result.path}`);
1654+
console.log(`Thumbnail at: ${result.thumbnailPath}`);
1655+
console.log(`Dimensions: ${result.metadata.width}x${result.metadata.height}`);
1656+
1657+
// With custom options
1658+
const result = await s5.fs.putImage('home/photos/portrait.jpg', imageFile, {
1659+
generateThumbnail: true,
1660+
thumbnailOptions: {
1661+
maxWidth: 256,
1662+
maxHeight: 256,
1663+
quality: 85,
1664+
format: 'webp'
1665+
},
1666+
extractMetadata: true
1667+
});
1668+
1669+
// Skip thumbnail generation
1670+
const result = await s5.fs.putImage('home/photos/raw.jpg', imageFile, {
1671+
generateThumbnail: false
1672+
});
1673+
```
1674+
1675+
### getThumbnail()
1676+
1677+
Retrieve or generate a thumbnail for an image.
1678+
1679+
```typescript
1680+
async getThumbnail(
1681+
path: string,
1682+
options?: GetThumbnailOptions
1683+
): Promise<Blob>
1684+
```
1685+
1686+
#### Parameters
1687+
1688+
- **path** (string): Path to the image file
1689+
- **options** (GetThumbnailOptions): Optional configuration
1690+
1691+
#### GetThumbnailOptions
1692+
1693+
```typescript
1694+
interface GetThumbnailOptions {
1695+
thumbnailOptions?: ThumbnailOptions; // Used if generating on-demand
1696+
cache?: boolean; // Cache generated thumbnail (default: true)
1697+
}
1698+
```
1699+
1700+
#### Example
1701+
1702+
```typescript
1703+
// Get pre-generated thumbnail
1704+
const thumbnail = await s5.fs.getThumbnail('home/photos/vacation.jpg');
1705+
const url = URL.createObjectURL(thumbnail);
1706+
document.getElementById('img').src = url;
1707+
1708+
// Generate on-demand with custom size
1709+
const thumbnail = await s5.fs.getThumbnail('home/photos/large.jpg', {
1710+
thumbnailOptions: {
1711+
maxWidth: 128,
1712+
maxHeight: 128
1713+
},
1714+
cache: true // Save generated thumbnail for future use
1715+
});
1716+
```
1717+
1718+
### getImageMetadata()
1719+
1720+
Extract metadata from a stored image.
1721+
1722+
```typescript
1723+
async getImageMetadata(path: string): Promise<ImageMetadata>
1724+
```
1725+
1726+
#### Example
1727+
1728+
```typescript
1729+
const metadata = await s5.fs.getImageMetadata('home/photos/vacation.jpg');
1730+
1731+
console.log(`Format: ${metadata.format}`);
1732+
console.log(`Size: ${metadata.width}x${metadata.height}`);
1733+
console.log(`Aspect: ${metadata.aspectRatio}`);
1734+
if (metadata.exif) {
1735+
console.log(`Camera: ${metadata.exif.make} ${metadata.exif.model}`);
1736+
}
1737+
```
1738+
1739+
### createImageGallery()
1740+
1741+
Batch upload multiple images with thumbnails and manifest generation.
1742+
1743+
```typescript
1744+
async createImageGallery(
1745+
galleryPath: string,
1746+
images: ImageUpload[],
1747+
options?: CreateImageGalleryOptions
1748+
): Promise<ImageReference[]>
1749+
```
1750+
1751+
#### Parameters
1752+
1753+
- **galleryPath** (string): Directory path for the gallery
1754+
- **images** (ImageUpload[]): Array of images to upload
1755+
- **options** (CreateImageGalleryOptions): Optional configuration
1756+
1757+
#### CreateImageGalleryOptions
1758+
1759+
```typescript
1760+
interface CreateImageGalleryOptions {
1761+
concurrency?: number; // Parallel uploads (default: 4)
1762+
generateThumbnails?: boolean; // Generate thumbnails (default: true)
1763+
thumbnailOptions?: ThumbnailOptions;
1764+
onProgress?: (completed: number, total: number) => void;
1765+
createManifest?: boolean; // Create manifest.json (default: true)
1766+
}
1767+
```
1768+
1769+
#### Example
1770+
1771+
```typescript
1772+
// Prepare images
1773+
const images = [
1774+
{ name: 'photo1.jpg', blob: await fetch('/img1.jpg').then(r => r.blob()) },
1775+
{ name: 'photo2.jpg', blob: await fetch('/img2.jpg').then(r => r.blob()) },
1776+
{ name: 'photo3.jpg', blob: await fetch('/img3.jpg').then(r => r.blob()) }
1777+
];
1778+
1779+
// Upload gallery with progress tracking
1780+
const results = await s5.fs.createImageGallery('home/galleries/vacation', images, {
1781+
concurrency: 2,
1782+
generateThumbnails: true,
1783+
thumbnailOptions: {
1784+
maxWidth: 256,
1785+
maxHeight: 256,
1786+
quality: 85
1787+
},
1788+
onProgress: (completed, total) => {
1789+
console.log(`Uploaded ${completed}/${total} images`);
1790+
},
1791+
createManifest: true
1792+
});
1793+
1794+
// Access the manifest
1795+
const manifestData = await s5.fs.get('home/galleries/vacation/manifest.json');
1796+
const manifest = JSON.parse(manifestData);
1797+
console.log(`Gallery contains ${manifest.count} images`);
1798+
```
1799+
1800+
#### Gallery Manifest Structure
1801+
1802+
```typescript
1803+
interface GalleryManifest {
1804+
created: string; // ISO 8601 timestamp
1805+
count: number; // Number of images
1806+
images: Array<{
1807+
name: string; // Image filename
1808+
path: string; // Full path to image
1809+
thumbnailPath?: string; // Path to thumbnail
1810+
metadata?: ImageMetadata; // Image metadata
1811+
}>;
1812+
}
1813+
```
1814+
1815+
### Path-Based Design Philosophy
1816+
1817+
FS5 media extensions follow the path-based API design:
1818+
1819+
- **Paths are identifiers**: Files are accessed by filesystem paths, not content hashes
1820+
- **Content addressing abstracted**: The underlying S5 content-addressed storage is an implementation detail
1821+
- **Simple, familiar interface**: Works like traditional file systems
1822+
- **No CID exposure**: Content identifiers (CIDs) are not exposed in the public API
1823+
1824+
This design makes the API:
1825+
- Easier to use for web developers
1826+
- Consistent with file system semantics
1827+
- Independent of underlying storage implementation
1828+
1829+
For advanced use cases requiring content addressing, access the internal `FileRef` structures through the S5Node API.
1830+
15971831
## Performance Considerations
15981832

15991833
- **Directory Caching**: Directory metadata is cached during path traversal

docs/IMPLEMENTATION.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,16 @@
316316
- [x] Add JPEG progressive support (multiple quality scans)
317317
- [x] Add PNG interlacing support (Adam7)
318318
- [x] Add WebP quality levels (configurable quality progression)
319-
- [ ] **6.3 FS5 Integration**
320-
- [ ] Create src/fs/media-extensions.ts
321-
- [ ] Extend FS5 with putImage method
322-
- [ ] Add getThumbnail method
323-
- [ ] Add getImageMetadata method
324-
- [ ] Add createImageGallery method
319+
- [x] **6.3 FS5 Integration** ✅ COMPLETE
320+
- [x] Create src/fs/media-extensions.ts
321+
- [x] Extend FS5 with putImage method
322+
- [x] Add getThumbnail method
323+
- [x] Add getImageMetadata method
324+
- [x] Add createImageGallery method
325+
- [x] Align with path-based API design (CIDs abstracted away)
326+
- [x] Create comprehensive unit test suite (29 tests passing)
327+
- [x] Create integration test suite (skipped pending IndexedDB)
328+
- [x] Update API documentation with media extensions
325329
- [ ] **6.4 Bundle Optimisation**
326330
- [ ] Configure webpack for code splitting
327331
- [ ] Implement WASM lazy loading
@@ -389,6 +393,7 @@
389393
7. **Phase 5**: Media Processing Foundation (Complete) ✅
390394
8. **Phase 6.1**: Thumbnail Generation ✅
391395
9. **Phase 6.2**: Progressive Loading ✅
396+
10. **Phase 6.3**: FS5 Integration ✅
392397

393398
### Phase 5 Status (Media Processing)
394399

@@ -404,9 +409,7 @@
404409
**Completed Sub-phases:**
405410
-**6.1**: Thumbnail Generation (Canvas-based with smart cropping & size optimization)
406411
-**6.2**: Progressive Loading (JPEG/PNG/WebP multi-layer support)
407-
408-
**In Progress:**
409-
- 🚧 **6.3**: FS5 Integration (putImage, getThumbnail, getImageMetadata, createImageGallery)
412+
-**6.3**: FS5 Integration (putImage, getThumbnail, getImageMetadata, createImageGallery with path-based design)
410413

411414
### Key Achievements
412415

@@ -418,13 +421,14 @@
418421
- Browser capability detection and smart strategy selection
419422
- Thumbnail generation with smart cropping and size optimization
420423
- Progressive image loading (JPEG/PNG/WebP)
421-
- Comprehensive test suite (204 tests passing across 12 test files)
424+
- FS5 media integration with path-based API (no CID exposure)
425+
- Comprehensive test suite (233 tests passing across 14 test files)
422426
- Full API documentation
423427
- Performance benchmarks documented
424428

425429
### Current Work
426430

427-
**Phase 6.3**: FS5 Integration - Integrating media features with file system operations (putImage, getThumbnail, getImageMetadata, createImageGallery)
431+
**Phase 6.4**: Bundle Optimisation - Next phase focuses on webpack configuration, code splitting, and bundle size verification
428432

429433
## Notes
430434

0 commit comments

Comments
 (0)