Package Exports
- @silexlabs/grapesjs-version-flow
- @silexlabs/grapesjs-version-flow/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@silexlabs/grapesjs-version-flow) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@silexlabs/grapesjs-version-flow
A GrapesJS plugin for managing version upgrades and migrations with sequential upgrade flow and modal UI
Detects when a project was saved with an older version of your app, upgrades it step-by-step, and shows progress and changelogs in a modal
This code is part of a bigger project: Silex v3 This has been vibe coded entirely by @lexoyo, check the
prompts/folder
π Features
- Automatic Version Detection: Detects when a project was saved with an older application version
- Sequential Upgrades: Guides users through upgrades step-by-step in the correct order
- Modal UI: Clean, accessible modal interface showing progress, logs, and "What's new"
- Error Handling: Comprehensive error handling with retry functionality
- Internationalization: Built-in support for multiple languages (EN/FR included)
- Flexible Configuration: Customizable styling, version comparison, and error handling
- Event System: Rich event system for monitoring upgrade lifecycle
- Persistence: Automatic version persistence in project data
π¦ Installation
NPM
npm install @silexlabs/grapesjs-version-flowCDN
<script src="https://unpkg.com/@silexlabs/grapesjs-version-flow"></script>π― Quick Start
import grapesjs from 'grapesjs';
import versionFlowPlugin from '@silexlabs/grapesjs-version-flow';
const editor = grapesjs.init({
container: '#gjs',
plugins: [versionFlowPlugin],
pluginsOpts: {
[versionFlowPlugin]: {
builderVersion: '2.1.0',
versions: [
{
builderVersion: '2.0.0',
upgrade: (ctx) => {
// Your upgrade logic here
ctx.addLog('info', 'Upgrading to v2.0.0');
// Return logs or nothing
return [
{ level: 'info', message: 'Migration completed' }
];
},
whatsNew: (ctx) => {
// Optional: show what's new
alert('New features in v2.0.0!');
}
}
]
}
}
});βοΈ Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
builderVersion |
string |
Required | Current application version |
versions |
VersionStep[] |
Required | Array of version upgrade steps |
compareFn |
function |
null |
Custom version comparison function |
continueOnError |
boolean |
false |
Whether to continue upgrades after errors |
styles.classPrefix |
string |
'gjs-version-flow' |
CSS class prefix for styling |
styles.injectCSS |
string |
null |
Custom CSS to inject |
i18n |
object |
{} |
Custom translations |
VersionStep Contract
interface VersionStep {
builderVersion: string; // Target version
upgrade: (ctx: UpgradeContext) => Promise<Log[]> | Log[] | void; // Upgrade function
whatsNew?: (ctx: UpgradeContext) => Promise<void> | void; // Optional what's new
}
interface Log {
level: 'info' | 'warn' | 'error';
message: string;
}
interface UpgradeContext {
editor: Editor;
getComponents: () => Component[];
getStyles: () => CSSRule[];
getPages: () => Page[];
getProjectData: () => any;
setProjectData: (data: any) => void;
addLog: (level: string, message: string) => Log;
}π¨ Styling
The plugin uses scoped CSS classes with the configurable prefix. Default classes:
.gjs-version-flow-modal { /* Modal overlay */ }
.gjs-version-flow-modal-content { /* Modal content */ }
.gjs-version-flow-btn { /* Buttons */ }
.gjs-version-flow-btn-primary { /* Primary buttons */ }
.gjs-version-flow-logs { /* Log container */ }
.gjs-version-flow-log-entry { /* Individual log entries */ }
/* ... and more */Custom Styling
{
styles: {
classPrefix: 'my-custom-prefix',
injectCSS: `
.custom-modal {
background: linear-gradient(45deg, #007cba, #005a87);
}
`
}
}π Internationalization
Built-in Languages
- English (
en) - Default - French (
fr)
Custom Translations
{
i18n: {
'es': {
'grapesjs-version-flow': {
'modal.title': 'ActualizaciΓ³n de VersiΓ³n Detectada',
'modal.outdated.action': 'Actualizar Ahora',
// ... more translations
}
}
}
}Available Translation Keys
modal.title
modal.outdated.title
modal.outdated.message
modal.outdated.action
modal.upgrading.title
modal.upgrading.current
modal.upgrading.logs
modal.completed.title
modal.completed.message
modal.completed.logs
modal.completed.whatsNew
modal.completed.saveNow
modal.error.title
modal.error.message
modal.error.retry
modal.error.viewLogs
modal.firstRun.title
modal.firstRun.message
modal.firstRun.whatsNew
modal.close
modal.skip
log.level.info
log.level.warn
log.level.errorπ‘ Events
The plugin emits events throughout the upgrade lifecycle:
// Version outdated detected
editor.on('version:outdated', ({ savedVersion, currentVersion }) => {
console.log(`Upgrade needed from ${savedVersion} to ${currentVersion}`);
});
// Upgrade process started
editor.on('version:upgrade:start', ({ pending }) => {
console.log('Starting upgrades for versions:', pending);
});
// Individual version upgrade started
editor.on('version:versionUpgrade:start', ({ toVersion }) => {
console.log('Upgrading to:', toVersion);
});
// Individual version upgrade completed
editor.on('version:versionUpgrade:end', ({ toVersion, log }) => {
console.log('Completed upgrade to:', toVersion, 'with logs:', log);
});
// All upgrades completed successfully
editor.on('version:upgrade:end', ({ upgradedTo }) => {
console.log('All upgrades completed. Final version:', upgradedTo);
});
// Upgrade error occurred
editor.on('version:upgrade:error', ({ toVersion, error }) => {
console.error('Upgrade failed for version:', toVersion, error);
});π§ Advanced Usage
Custom Version Comparison
{
compareFn: (versionA, versionB) => {
// Custom comparison logic
// Return -1 if A < B, 0 if A === B, 1 if A > B
const parseVersion = v => v.split('-')[0].split('.').map(Number);
const a = parseVersion(versionA);
const b = parseVersion(versionB);
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const diff = (a[i] || 0) - (b[i] || 0);
if (diff !== 0) return diff < 0 ? -1 : 1;
}
return 0;
}
}Complex Upgrade Example
{
builderVersion: '3.2.0',
versions: [
{
builderVersion: '3.0.0',
upgrade: async (ctx) => {
const logs = [];
try {
// Step 1: Migrate components
const components = ctx.getComponents();
const migratedCount = components.length;
components.forEach(comp => {
const oldClass = comp.getAttributes().class;
if (oldClass?.includes('legacy-')) {
comp.addAttributes({
class: oldClass.replace('legacy-', 'modern-')
});
}
});
logs.push({
level: 'info',
message: `Migrated ${migratedCount} components to new class structure`
});
// Step 2: Update project metadata
const projectData = ctx.getProjectData();
projectData.metadata = {
...projectData.metadata,
migrationDate: new Date().toISOString(),
previousVersion: '2.x.x'
};
ctx.setProjectData(projectData);
logs.push({
level: 'info',
message: 'Updated project metadata'
});
// Step 3: Validate migration
const hasLegacyClasses = components.some(comp =>
comp.getAttributes().class?.includes('legacy-')
);
if (hasLegacyClasses) {
logs.push({
level: 'warn',
message: 'Some legacy classes may still exist'
});
}
return logs;
} catch (error) {
logs.push({
level: 'error',
message: `Migration failed: ${error.message}`
});
throw error;
}
},
whatsNew: async (ctx) => {
// Show rich what's new content
const modal = ctx.editor.Modal.open({
title: 'Welcome to Version 3.0!',
content: `
<div style="padding: 20px;">
<h3>π Major Updates</h3>
<ul>
<li>New modern component architecture</li>
<li>Improved performance and stability</li>
<li>Enhanced accessibility features</li>
</ul>
<h3>π§ Breaking Changes</h3>
<ul>
<li>Legacy class names have been updated</li>
<li>Old API methods are deprecated</li>
</ul>
</div>
`
});
}
}
]
}Error Handling & Recovery
{
continueOnError: true, // Continue with next versions even if one fails
versions: [
{
builderVersion: '2.1.0',
upgrade: (ctx) => {
try {
// Risky operation
performComplexMigration();
return [{ level: 'info', message: 'Migration successful' }];
} catch (error) {
// Log error but don't throw to allow continuation
ctx.addLog('error', `Non-critical migration failed: ${error.message}`);
return [{ level: 'warn', message: 'Partial migration completed' }];
}
}
}
]
}π§ͺ Example Project
See the _index.html file for a complete working example with:
- Multiple version steps
- Simulated failures and recovery
- Demo controls for testing different scenarios
- Event logging
π οΈ Development
# Clone the repository
git clone https://github.com/silexlabs/grapesjs-version-flow.git
cd grapesjs-version-flow
# Install dependencies
npm install
# Start development server
npm start
# Build for production
npm run build
# Run tests
npm testποΈ Architecture
The plugin consists of several key components:
- VersionManager: Handles version persistence, comparison, and upgrade detection
- UpgradeEngine: Executes upgrade steps sequentially with error handling
- ModalUI: Manages the user interface and different modal states
- EventSystem: Provides lifecycle event emission and handling
- StyleManager: Handles CSS injection and scoping
π Security Considerations
- Upgrade functions run in the same context as your application
- Always validate and sanitize data in upgrade functions
- Be cautious with
eval()or similar dynamic code execution - Test upgrade paths thoroughly before deployment
π€ Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature - Commit your changes:
git commit -am 'Add new feature' - Push to the branch:
git push origin feature/new-feature - Submit a pull request
π License
AGPL-v3 - see LICENSE file for details.
π Acknowledgments
- Built with GrapesJS CLI
- Inspired by database migration patterns
- Thanks to the GrapesJS community for feedback and suggestions