managarten/packages/spiral-db/demo.ts
Till-JS f1518e8c39 feat(spiral-db): add pixel-based spiral database package
Implement SpiralDB - a novel data storage format that encodes structured
data into PNG images using an 8-color palette. Data grows in a spiral
pattern from the center outward, enabling infinite expansion.

Features:
- 8-color palette (3-bit per pixel) for compression robustness
- Spiral coordinate system with ring-based organization
- Schema-based serialization (int, bool, string, timestamp, arrays)
- Record management with CRUD operations and status tracking
- PNG export/import with pako compression
- Browser support (Canvas, Blob, DataURL)
- Todo schema as reference implementation

Storage structure:
- Ring 0: Magic byte (validation)
- Ring 1: Database header (version, flags, counts)
- Ring 2: Schema definition
- Ring 3+: Master index (spans multiple rings if needed)
- Ring 4+: Record data
2026-02-17 10:42:09 +01:00

152 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* SpiralDB Demo
* Run with: npx tsx demo.ts
*/
import {
SpiralDB,
createTodoSchema,
visualizeImageEmoji,
visualizeSpiralOrder,
saveToPngFile,
loadFromPngFile,
exportToPngBytes,
} from './src/index.js';
import { existsSync } from 'fs';
console.log('='.repeat(60));
console.log('🌀 SpiralDB Demo - Pixel-Based Spiral Database');
console.log('='.repeat(60));
// Show spiral order for a 7x7 image
console.log('\n📐 Spiral Index Order (7x7):');
console.log(visualizeSpiralOrder(7));
// Create database
console.log('\n📦 Creating SpiralDB with Todo schema...');
const db = new SpiralDB({
schema: createTodoSchema(),
compression: true,
});
console.log('\n📊 Initial Stats:');
console.log(db.getStats());
// Insert some todos
const todos = [
{
id: 0,
status: 0,
priority: 2, // high
createdAt: new Date('2025-01-01'),
dueDate: new Date('2025-01-15'),
completedAt: null,
title: 'Build SpiralDB',
description: 'Create a pixel-based database',
tags: [1, 2],
},
{
id: 0,
status: 0,
priority: 1, // medium
createdAt: new Date('2025-01-02'),
dueDate: new Date('2025-01-20'),
completedAt: null,
title: 'Write tests',
description: 'Add unit tests',
tags: [1],
},
{
id: 0,
status: 0,
priority: 0, // low
createdAt: new Date('2025-01-03'),
dueDate: null,
completedAt: null,
title: 'Documentation',
description: null,
tags: [],
},
];
console.log('\n✏ Inserting todos...');
for (const todo of todos) {
const result = db.insert(todo);
console.log(` → ID ${result.recordId}: ${todo.title}`);
}
console.log('\n📊 Stats after inserts:');
const stats = db.getStats();
console.log(` Image size: ${stats.imageSize}×${stats.imageSize}`);
console.log(` Total pixels: ${stats.totalPixels}`);
console.log(` Used pixels: ${stats.usedPixels}`);
console.log(` Active records: ${stats.activeRecords}`);
console.log(` Current ring: ${stats.currentRing}`);
// Complete one todo
console.log('\n✅ Completing todo #1...');
db.complete(1);
// Read back
console.log('\n📖 Reading all todos:');
const allTodos = db.getAll();
for (const record of allTodos) {
const statusIcon = record.meta.status === 'completed' ? '✅' : '⬜';
console.log(` ${statusIcon} [${record.meta.id}] ${record.data.title}`);
}
// Visualize the image
console.log('\n🎨 Database Image (emoji visualization):');
const image = db.getImage();
console.log(visualizeImageEmoji(image));
// Legend
console.log('\n📚 Color Legend:');
console.log(' ⬛ Black (000) - Null/Empty/Active');
console.log(' 🟦 Blue (001) - Data Type 1');
console.log(' 🟩 Green (010) - Completed/True');
console.log(' 🔷 Cyan (011) - Data Type 3');
console.log(' 🟥 Red (100) - Deleted/Important');
console.log(' 🟪 Magenta (101) - Data Type 5');
console.log(' 🟨 Yellow (110) - Warning/Archived');
console.log(' ⬜ White (111) - Magic/Separator/End');
// Calculate storage efficiency
const jsonSize = JSON.stringify(todos).length;
const pixelBits = stats.usedPixels * 3;
const pixelBytes = Math.ceil(pixelBits / 8);
console.log('\n📈 Storage Comparison:');
console.log(` JSON size: ${jsonSize} bytes`);
console.log(` Pixel size: ${pixelBytes} bytes (${stats.usedPixels} pixels × 3 bits)`);
console.log(` Compression: ${((1 - pixelBytes / jsonSize) * 100).toFixed(1)}%`);
// PNG Export Demo
console.log('\n📸 PNG Export Demo:');
const pngPath = './demo-output.png';
const pngBytes = exportToPngBytes(image);
console.log(` Raw PNG size: ${pngBytes.length} bytes`);
// Save to file
await saveToPngFile(image, pngPath);
console.log(` Saved to: ${pngPath}`);
// Verify by loading back
if (existsSync(pngPath)) {
const loadedImage = await loadFromPngFile(pngPath);
console.log(` Loaded back: ${loadedImage.width}×${loadedImage.height} pixels`);
// Verify data integrity
const loadedDb = SpiralDB.fromImage(loadedImage, createTodoSchema());
const loadedTodos = loadedDb.getAll();
console.log(` Verified: ${loadedTodos.length} todos recovered`);
for (const record of loadedTodos) {
const statusIcon = record.meta.status === 'completed' ? '✅' : '⬜';
console.log(` ${statusIcon} [${record.meta.id}] ${record.data.title}`);
}
}
console.log('\n' + '='.repeat(60));
console.log('Demo complete!');