UNPKG

zon-format

Version:

ZON: The most token-efficient serialization format for LLMs - beats CSV, TOON, JSON, and all competitors

249 lines (247 loc) 8.08 kB
"use strict"; /** * ZON Data Migration Manager * * Manages schema migrations for evolving ZON data structures. * Supports versioned migration functions with automatic path finding. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.globalMigrationManager = exports.ZonMigrationManager = void 0; exports.createAddFieldMigration = createAddFieldMigration; exports.createRemoveFieldMigration = createRemoveFieldMigration; exports.createRenameFieldMigration = createRenameFieldMigration; /** * Manager for ZON schema migrations. * Allows registering migration functions and automatically finding migration paths. */ class ZonMigrationManager { constructor() { this.migrations = new Map(); } /** * Registers a migration from one version to another. * * @param from - Source version * @param to - Target version * @param migrate - Migration function * @param description - Optional description of the migration * * @example * ```typescript * manager.registerMigration("1.0.0", "2.0.0", (data) => { * if (data.users) { * data.users = data.users.map(u => ({ ...u, email: `${u.name}@example.com` })); * } * return data; * }, "Added email field to users"); * ``` */ registerMigration(from, to, migrate, description) { const key = `${from}->${to}`; this.migrations.set(key, { from, to, migrate, description }); } /** * Migrates data from one version to another. * Automatically finds the migration path if direct migration not available. * * @param data - Data to migrate * @param fromVersion - Current version * @param toVersion - Target version * @returns Migrated data * * @throws Error if no migration path exists */ migrate(data, fromVersion, toVersion) { if (fromVersion === toVersion) { return data; } const directKey = `${fromVersion}->${toVersion}`; if (this.migrations.has(directKey)) { const migration = this.migrations.get(directKey); console.log(`Migrating ${fromVersion}${toVersion}: ${migration.description || 'no description'}`); return migration.migrate(data, fromVersion, toVersion); } const path = this.findMigrationPath(fromVersion, toVersion); if (!path || path.length === 0) { throw new Error(`No migration path found from ${fromVersion} to ${toVersion}`); } let current = data; for (const migration of path) { console.log(`Migrating ${migration.from}${migration.to}: ${migration.description || 'no description'}`); current = migration.migrate(current, migration.from, migration.to); } return current; } /** * Finds a migration path between two versions using BFS. * * @param from - Source version * @param to - Target version * @returns Array of migrations to apply, or null if no path exists */ findMigrationPath(from, to) { const visited = new Set(); const queue = [{ version: from, path: [] }]; while (queue.length > 0) { const { version, path } = queue.shift(); if (version === to) { return path; } if (visited.has(version)) { continue; } visited.add(version); for (const [key, migration] of this.migrations) { if (migration.from === version) { queue.push({ version: migration.to, path: [...path, migration] }); } } } return null; } /** * Checks if migration is possible between two versions. * * @param from - Source version * @param to - Target version * @returns True if migration path exists */ canMigrate(from, to) { if (from === to) return true; const directKey = `${from}->${to}`; if (this.migrations.has(directKey)) return true; return this.findMigrationPath(from, to) !== null; } /** * Gets all registered migrations. * * @returns Array of all migrations */ getMigrations() { return Array.from(this.migrations.values()); } /** * Gets migrations available from a specific version. * * @param version - Source version * @returns Array of migrations starting from this version */ getMigrationsFrom(version) { return Array.from(this.migrations.values()).filter(m => m.from === version); } /** * Clears all registered migrations. */ clear() { this.migrations.clear(); } } exports.ZonMigrationManager = ZonMigrationManager; /** * Global migration manager instance. * Can be used to register and manage migrations across the application. */ exports.globalMigrationManager = new ZonMigrationManager(); /** * Helper function to create a migration that adds a default value for a new field. * * @param path - Path to the field (e.g., "users.email") * @param defaultValue - Default value for the field * @returns Migration function */ function createAddFieldMigration(path, defaultValue) { return (data) => { const keys = path.split('.'); const lastKey = keys.pop(); let current = data; for (const key of keys) { if (!(key in current)) { return data; } current = current[key]; } if (Array.isArray(current)) { return { ...data, [keys.join('.')]: current.map(item => ({ ...item, [lastKey]: defaultValue })) }; } if (typeof current === 'object' && current !== null) { current[lastKey] = defaultValue; } return data; }; } /** * Helper function to create a migration that removes a field. * * @param path - Path to the field to remove * @returns Migration function */ function createRemoveFieldMigration(path) { return (data) => { const keys = path.split('.'); const lastKey = keys.pop(); let current = data; for (const key of keys) { if (!(key in current)) { return data; } current = current[key]; } if (Array.isArray(current)) { current.forEach(item => { if (typeof item === 'object' && item !== null) { delete item[lastKey]; } }); } else if (typeof current === 'object' && current !== null) { delete current[lastKey]; } return data; }; } /** * Helper function to create a migration that renames a field. * * @param oldPath - Current field path * @param newPath - New field path * @returns Migration function */ function createRenameFieldMigration(oldPath, newPath) { return (data) => { const oldKeys = oldPath.split('.'); const newKeys = newPath.split('.'); const oldLastKey = oldKeys.pop(); const newLastKey = newKeys.pop(); let current = data; for (const key of oldKeys) { if (!(key in current)) { return data; } current = current[key]; } if (Array.isArray(current)) { current.forEach(item => { if (typeof item === 'object' && item !== null && oldLastKey in item) { item[newLastKey] = item[oldLastKey]; delete item[oldLastKey]; } }); } else if (typeof current === 'object' && current !== null && oldLastKey in current) { current[newLastKey] = current[oldLastKey]; delete current[oldLastKey]; } return data; }; }