mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-02-02 02:13:48 -05:00
463 lines
14 KiB
JavaScript
463 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Generates VaultVersions and VaultSql for Swift and Kotlin from TypeScript source.
|
|
* This allows native iOS and Android code to create vaults with the correct SQL schema.
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Paths
|
|
const REPO_ROOT = path.join(__dirname, '../../..');
|
|
const TS_VERSIONS_SOURCE = path.join(REPO_ROOT, 'core/vault/src/sql/VaultVersions.ts');
|
|
const TS_SQL_SOURCE = path.join(REPO_ROOT, 'core/vault/src/sql/SqlConstants.ts');
|
|
|
|
// Output paths
|
|
// Note: VaultModels is a framework that can be imported by other targets (including AliasVaultUITests)
|
|
const SWIFT_OUTPUT_DIR = path.join(REPO_ROOT, 'apps/mobile-app/ios/VaultModels');
|
|
const KOTLIN_OUTPUT_DIR = path.join(REPO_ROOT, 'apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/models');
|
|
|
|
/**
|
|
* Parse VaultVersions from TypeScript source
|
|
*/
|
|
function parseVaultVersions(tsContent) {
|
|
const versions = [];
|
|
|
|
// Find the VAULT_VERSIONS array
|
|
const match = tsContent.match(/export const VAULT_VERSIONS[^=]*=\s*\[([\s\S]*?)\];/);
|
|
if (!match) {
|
|
throw new Error('Could not find VAULT_VERSIONS in source');
|
|
}
|
|
|
|
const body = match[1];
|
|
|
|
// Match each version object
|
|
const versionRegex = /\{\s*revision:\s*(\d+),\s*version:\s*'([^']+)',\s*description:\s*'([^']+)',\s*releaseVersion:\s*'([^']+)',\s*compatibleUpToVersion:\s*'([^']+)',?\s*\}/g;
|
|
let versionMatch;
|
|
|
|
while ((versionMatch = versionRegex.exec(body)) !== null) {
|
|
versions.push({
|
|
revision: parseInt(versionMatch[1], 10),
|
|
version: versionMatch[2],
|
|
description: versionMatch[3],
|
|
releaseVersion: versionMatch[4],
|
|
compatibleUpToVersion: versionMatch[5]
|
|
});
|
|
}
|
|
|
|
return versions;
|
|
}
|
|
|
|
/**
|
|
* Parse COMPLETE_SCHEMA_SQL from TypeScript source
|
|
*/
|
|
function parseCompleteSchemaSql(tsContent) {
|
|
// Find the COMPLETE_SCHEMA_SQL constant
|
|
const match = tsContent.match(/export const COMPLETE_SCHEMA_SQL\s*=\s*`([\s\S]*?)`;/);
|
|
if (!match) {
|
|
throw new Error('Could not find COMPLETE_SCHEMA_SQL in source');
|
|
}
|
|
|
|
return match[1].trim();
|
|
}
|
|
|
|
/**
|
|
* Parse MIGRATION_SCRIPTS from TypeScript source
|
|
*/
|
|
function parseMigrationScripts(tsContent) {
|
|
const migrations = {};
|
|
|
|
// Find the MIGRATION_SCRIPTS object
|
|
const match = tsContent.match(/export const MIGRATION_SCRIPTS:\s*Record<number,\s*string>\s*=\s*\{([\s\S]*?)\n\};/);
|
|
if (!match) {
|
|
// MIGRATION_SCRIPTS might be optional or have different format
|
|
console.log('Note: MIGRATION_SCRIPTS not found or has different format');
|
|
return migrations;
|
|
}
|
|
|
|
const body = match[1];
|
|
|
|
// Match each migration: number: `sql`
|
|
const migrationRegex = /(\d+):\s*`([\s\S]*?)`/g;
|
|
let migrationMatch;
|
|
|
|
while ((migrationMatch = migrationRegex.exec(body)) !== null) {
|
|
migrations[parseInt(migrationMatch[1], 10)] = migrationMatch[2].trim();
|
|
}
|
|
|
|
return migrations;
|
|
}
|
|
|
|
/**
|
|
* Escape string for Swift multiline string literal
|
|
*/
|
|
function escapeSwiftString(str) {
|
|
// Swift multiline strings handle most escaping, but we need to escape backslashes and quotes
|
|
return str
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/"/g, '\\"');
|
|
}
|
|
|
|
/**
|
|
* Escape string for Kotlin multiline string literal (trimIndent)
|
|
*/
|
|
function escapeKotlinString(str) {
|
|
// Kotlin raw strings don't need much escaping, but $ needs escaping
|
|
return str.replace(/\$/g, '\\$');
|
|
}
|
|
|
|
/**
|
|
* Generate Swift VaultVersions struct
|
|
*/
|
|
function generateSwiftVaultVersions(versions) {
|
|
const latestVersion = versions[versions.length - 1];
|
|
|
|
const versionsArray = versions.map(v => {
|
|
return ` VaultVersion(
|
|
revision: ${v.revision},
|
|
version: "${v.version}",
|
|
description: "${v.description}",
|
|
releaseVersion: "${v.releaseVersion}",
|
|
compatibleUpToVersion: "${v.compatibleUpToVersion}"
|
|
)`;
|
|
}).join(',\n');
|
|
|
|
return `// <auto-generated />
|
|
// This file is auto-generated from core/vault/src/sql/VaultVersions.ts
|
|
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
|
|
// swiftlint:disable all
|
|
|
|
import Foundation
|
|
|
|
/// Vault database version information.
|
|
public struct VaultVersion {
|
|
/// The migration revision number.
|
|
public let revision: Int
|
|
|
|
/// The internal migration version number that equals the AliasClientDb database version (e.g., "1.5.0").
|
|
public let version: String
|
|
|
|
/// Description of changes in this version.
|
|
public let description: String
|
|
|
|
/// The AliasVault release that this vault version was introduced in (e.g., "0.14.0").
|
|
public let releaseVersion: String
|
|
|
|
/// The last AliasVault release version that the vault was compatible with before requiring this migration.
|
|
public let compatibleUpToVersion: String
|
|
}
|
|
|
|
/// Registry of all vault versions.
|
|
public struct VaultVersions {
|
|
/// All vault migrations/versions in chronological order.
|
|
public static let all: [VaultVersion] = [
|
|
${versionsArray}
|
|
]
|
|
|
|
/// Get the latest vault version.
|
|
public static var latest: VaultVersion {
|
|
return all.last!
|
|
}
|
|
|
|
/// Get the latest vault version string (e.g., "1.7.0").
|
|
public static var latestVersion: String {
|
|
return latest.version
|
|
}
|
|
|
|
/// Get the latest vault revision number.
|
|
public static var latestRevision: Int {
|
|
return latest.revision
|
|
}
|
|
|
|
/// Find a vault version by revision number.
|
|
public static func findByRevision(_ revision: Int) -> VaultVersion? {
|
|
return all.first { $0.revision == revision }
|
|
}
|
|
|
|
/// Find a vault version by version string.
|
|
public static func findByVersion(_ version: String) -> VaultVersion? {
|
|
return all.first { $0.version == version }
|
|
}
|
|
}
|
|
|
|
// swiftlint:enable all
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Generate Kotlin VaultVersions object
|
|
*/
|
|
function generateKotlinVaultVersions(versions) {
|
|
const versionsArray = versions.map(v => {
|
|
return ` VaultVersion(
|
|
revision = ${v.revision},
|
|
version = "${v.version}",
|
|
description = "${v.description}",
|
|
releaseVersion = "${v.releaseVersion}",
|
|
compatibleUpToVersion = "${v.compatibleUpToVersion}",
|
|
)`;
|
|
}).join(',\n');
|
|
|
|
return `// <auto-generated />
|
|
// This file is auto-generated from core/vault/src/sql/VaultVersions.ts
|
|
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
|
|
@file:Suppress("all")
|
|
|
|
package net.aliasvault.app.vaultstore.models
|
|
|
|
/**
|
|
* Vault database version information.
|
|
*/
|
|
data class VaultVersion(
|
|
/** The migration revision number. */
|
|
val revision: Int,
|
|
/** The internal migration version number that equals the AliasClientDb database version (e.g., "1.5.0"). */
|
|
val version: String,
|
|
/** Description of changes in this version. */
|
|
val description: String,
|
|
/** The AliasVault release that this vault version was introduced in (e.g., "0.14.0"). */
|
|
val releaseVersion: String,
|
|
/** The last AliasVault release version that the vault was compatible with before requiring this migration. */
|
|
val compatibleUpToVersion: String,
|
|
)
|
|
|
|
/**
|
|
* Registry of all vault versions.
|
|
*/
|
|
object VaultVersions {
|
|
/**
|
|
* All vault migrations/versions in chronological order.
|
|
*/
|
|
val all: List<VaultVersion> = listOf(
|
|
${versionsArray},
|
|
)
|
|
|
|
/**
|
|
* Get the latest vault version.
|
|
*/
|
|
val latest: VaultVersion
|
|
get() = all.last()
|
|
|
|
/**
|
|
* Get the latest vault version string (e.g., "1.7.0").
|
|
*/
|
|
val latestVersion: String
|
|
get() = latest.version
|
|
|
|
/**
|
|
* Get the latest vault revision number.
|
|
*/
|
|
val latestRevision: Int
|
|
get() = latest.revision
|
|
|
|
/**
|
|
* Find a vault version by revision number.
|
|
*/
|
|
fun findByRevision(revision: Int): VaultVersion? {
|
|
return all.find { it.revision == revision }
|
|
}
|
|
|
|
/**
|
|
* Find a vault version by version string.
|
|
*/
|
|
fun findByVersion(version: String): VaultVersion? {
|
|
return all.find { it.version == version }
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Indent each line of a multi-line string for Swift multi-line string literals
|
|
*/
|
|
function indentSwiftMultilineString(str, spaces = 8) {
|
|
const indent = ' '.repeat(spaces);
|
|
return str.split('\n').map(line => indent + line).join('\n');
|
|
}
|
|
|
|
/**
|
|
* Generate Swift VaultSql struct
|
|
*/
|
|
function generateSwiftVaultSql(completeSchemaSql, migrations) {
|
|
const escapedSql = escapeSwiftString(completeSchemaSql);
|
|
const indentedSql = indentSwiftMultilineString(escapedSql);
|
|
|
|
// Generate migration entries
|
|
const migrationEntries = Object.entries(migrations).map(([key, sql]) => {
|
|
const escapedMigrationSql = escapeSwiftString(sql);
|
|
const indentedMigrationSql = indentSwiftMultilineString(escapedMigrationSql, 12);
|
|
return ` ${key}: """
|
|
${indentedMigrationSql}
|
|
"""`;
|
|
}).join(',\n');
|
|
|
|
const migrationsDict = Object.keys(migrations).length > 0
|
|
? `
|
|
/// Migration SQL scripts indexed by migration number.
|
|
/// Key is the source migration number (migration FROM this version).
|
|
public static let migrations: [Int: String] = [
|
|
${migrationEntries}
|
|
]`
|
|
: `
|
|
/// Migration SQL scripts indexed by migration number.
|
|
public static let migrations: [Int: String] = [:]`;
|
|
|
|
return `// <auto-generated />
|
|
// This file is auto-generated from core/vault/src/sql/SqlConstants.ts
|
|
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
|
|
// swiftlint:disable all
|
|
|
|
import Foundation
|
|
|
|
/// SQL statements for vault creation and migration.
|
|
public struct VaultSql {
|
|
/// Complete database schema SQL for creating a new vault with the latest schema.
|
|
public static let completeSchema = """
|
|
${indentedSql}
|
|
"""
|
|
${migrationsDict}
|
|
|
|
/// Get migration SQL for upgrading from a specific revision.
|
|
public static func getMigrationSql(fromRevision: Int) -> String? {
|
|
return migrations[fromRevision]
|
|
}
|
|
}
|
|
|
|
// swiftlint:enable all
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Generate Kotlin VaultSql object
|
|
*/
|
|
function generateKotlinVaultSql(completeSchemaSql, migrations) {
|
|
const escapedSql = escapeKotlinString(completeSchemaSql);
|
|
|
|
// Generate migration entries
|
|
const migrationEntries = Object.entries(migrations).map(([key, sql]) => {
|
|
const escapedMigrationSql = escapeKotlinString(sql);
|
|
return ` ${key} to """
|
|
${escapedMigrationSql}
|
|
""".trimIndent()`;
|
|
}).join(',\n');
|
|
|
|
const migrationsMap = Object.keys(migrations).length > 0
|
|
? `
|
|
/**
|
|
* Migration SQL scripts indexed by migration number.
|
|
* Key is the source migration number (migration FROM this version).
|
|
*/
|
|
val migrations: Map<Int, String> = mapOf(
|
|
${migrationEntries},
|
|
)`
|
|
: `
|
|
/**
|
|
* Migration SQL scripts indexed by migration number.
|
|
*/
|
|
val migrations: Map<Int, String> = emptyMap()`;
|
|
|
|
return `// <auto-generated />
|
|
// This file is auto-generated from core/vault/src/sql/SqlConstants.ts
|
|
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
|
|
@file:Suppress("all")
|
|
|
|
package net.aliasvault.app.vaultstore.models
|
|
|
|
/**
|
|
* SQL statements for vault creation and migration.
|
|
*/
|
|
object VaultSql {
|
|
/**
|
|
* Complete database schema SQL for creating a new vault with the latest schema.
|
|
*/
|
|
val completeSchema = """
|
|
${escapedSql}
|
|
""".trimIndent()
|
|
${migrationsMap}
|
|
|
|
/**
|
|
* Get migration SQL for upgrading from a specific revision.
|
|
*/
|
|
fun getMigrationSql(fromRevision: Int): String? {
|
|
return migrations[fromRevision]
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Ensure directory exists
|
|
*/
|
|
function ensureDir(filePath) {
|
|
const dir = path.dirname(filePath);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main execution
|
|
*/
|
|
function main() {
|
|
console.log('Generating vault SQL for Swift and Kotlin...\n');
|
|
|
|
// Read TypeScript VaultVersions source
|
|
if (!fs.existsSync(TS_VERSIONS_SOURCE)) {
|
|
throw new Error(`Source file not found: ${TS_VERSIONS_SOURCE}`);
|
|
}
|
|
|
|
const tsVersionsContent = fs.readFileSync(TS_VERSIONS_SOURCE, 'utf8');
|
|
const versions = parseVaultVersions(tsVersionsContent);
|
|
|
|
if (versions.length === 0) {
|
|
throw new Error('No vault versions found in source file');
|
|
}
|
|
|
|
console.log(`Parsed ${versions.length} vault versions`);
|
|
console.log(`Latest version: ${versions[versions.length - 1].version} (revision ${versions[versions.length - 1].revision})`);
|
|
|
|
// Read TypeScript SqlConstants source
|
|
if (!fs.existsSync(TS_SQL_SOURCE)) {
|
|
throw new Error(`Source file not found: ${TS_SQL_SOURCE}`);
|
|
}
|
|
|
|
const tsSqlContent = fs.readFileSync(TS_SQL_SOURCE, 'utf8');
|
|
const completeSchemaSql = parseCompleteSchemaSql(tsSqlContent);
|
|
const migrations = parseMigrationScripts(tsSqlContent);
|
|
|
|
console.log(`Parsed complete schema SQL (${completeSchemaSql.length} chars)`);
|
|
console.log(`Parsed ${Object.keys(migrations).length} migration scripts`);
|
|
|
|
// Generate Swift VaultVersions
|
|
const swiftVersionsPath = path.join(SWIFT_OUTPUT_DIR, 'VaultVersions.swift');
|
|
ensureDir(swiftVersionsPath);
|
|
const swiftVersionsContent = generateSwiftVaultVersions(versions);
|
|
fs.writeFileSync(swiftVersionsPath, swiftVersionsContent, 'utf8');
|
|
console.log(`Generated: ${swiftVersionsPath}`);
|
|
|
|
// Generate Swift VaultSql
|
|
const swiftSqlPath = path.join(SWIFT_OUTPUT_DIR, 'VaultSql.swift');
|
|
ensureDir(swiftSqlPath);
|
|
const swiftSqlContent = generateSwiftVaultSql(completeSchemaSql, migrations);
|
|
fs.writeFileSync(swiftSqlPath, swiftSqlContent, 'utf8');
|
|
console.log(`Generated: ${swiftSqlPath}`);
|
|
|
|
// Generate Kotlin VaultVersions
|
|
const kotlinVersionsPath = path.join(KOTLIN_OUTPUT_DIR, 'VaultVersions.kt');
|
|
ensureDir(kotlinVersionsPath);
|
|
const kotlinVersionsContent = generateKotlinVaultVersions(versions);
|
|
fs.writeFileSync(kotlinVersionsPath, kotlinVersionsContent, 'utf8');
|
|
console.log(`Generated: ${kotlinVersionsPath}`);
|
|
|
|
// Generate Kotlin VaultSql
|
|
const kotlinSqlPath = path.join(KOTLIN_OUTPUT_DIR, 'VaultSql.kt');
|
|
ensureDir(kotlinSqlPath);
|
|
const kotlinSqlContent = generateKotlinVaultSql(completeSchemaSql, migrations);
|
|
fs.writeFileSync(kotlinSqlPath, kotlinSqlContent, 'utf8');
|
|
console.log(`Generated: ${kotlinSqlPath}`);
|
|
|
|
console.log('\nVault SQL generation complete!');
|
|
}
|
|
|
|
main();
|