@eldrin-project/eldrin-app-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/dist/cli/release.d.ts +35 -0
- package/dist/cli/release.js +592 -0
- package/dist/cli/release.js.map +1 -0
- package/dist/cli/submit.d.ts +37 -0
- package/dist/cli/submit.js +261 -0
- package/dist/cli/submit.js.map +1 -0
- package/dist/index.cjs +582 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +485 -0
- package/dist/index.d.ts +485 -0
- package/dist/index.js +562 -0
- package/dist/index.js.map +1 -0
- package/dist/vite.cjs +91 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +41 -0
- package/dist/vite.d.ts +41 -0
- package/dist/vite.js +89 -0
- package/dist/vite.js.map +1 -0
- package/package.json +102 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/migrations/checksum.ts","../../src/migrations/sql-parser.ts","../../src/migrations/marketplace.ts","../../src/cli/release.ts"],"names":["resolve","existsSync","readdir","readFile"],"mappings":"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","file":"release.js","sourcesContent":["/**\n * Checksum utilities for migration integrity verification\n *\n * Uses SHA-256 for content hashing via Web Crypto API\n * (compatible with Cloudflare Workers runtime)\n */\n\n/** Prefix for SHA-256 checksums in marketplace format */\nexport const CHECKSUM_PREFIX = 'sha256:';\n\n/**\n * Calculate SHA-256 checksum of content\n *\n * @param content - String content to hash\n * @param options - Options for checksum calculation\n * @param options.prefixed - If true, returns \"sha256:...\" format (default: false for backwards compatibility)\n * @returns Hex-encoded SHA-256 hash, optionally with prefix\n */\nexport async function calculateChecksum(\n content: string,\n options: { prefixed?: boolean } = {}\n): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(content);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n\n return options.prefixed ? `${CHECKSUM_PREFIX}${hex}` : hex;\n}\n\n/**\n * Calculate SHA-256 checksum with sha256: prefix\n *\n * Convenience function for marketplace format\n *\n * @param content - String content to hash\n * @returns Prefixed checksum (e.g., \"sha256:abc123...\")\n */\nexport async function calculatePrefixedChecksum(content: string): Promise<string> {\n return calculateChecksum(content, { prefixed: true });\n}\n\n/**\n * Verify that content matches an expected checksum\n *\n * Handles both prefixed (\"sha256:...\") and unprefixed checksums\n *\n * @param content - Content to verify\n * @param expectedChecksum - Expected SHA-256 hex string (with or without prefix)\n * @returns true if checksums match\n */\nexport async function verifyChecksum(\n content: string,\n expectedChecksum: string\n): Promise<boolean> {\n // Strip prefix if present for comparison\n const normalizedExpected = expectedChecksum.startsWith(CHECKSUM_PREFIX)\n ? expectedChecksum.slice(CHECKSUM_PREFIX.length)\n : expectedChecksum;\n\n const actualChecksum = await calculateChecksum(content);\n return actualChecksum === normalizedExpected;\n}\n","/**\n * SQL statement parser for migration files\n *\n * Parses SQL content into individual statements for execution via db.batch()\n */\n\n/**\n * Parse SQL content into individual statements\n *\n * - Removes SQL comments (-- style)\n * - Splits by semicolon\n * - Handles multi-line statements\n * - Preserves string literals containing semicolons\n *\n * @param sql - Raw SQL content\n * @returns Array of individual SQL statements\n */\nexport function parseSQLStatements(sql: string): string[] {\n // First, handle the content preserving strings\n const statements: string[] = [];\n let current = '';\n let inString = false;\n let stringChar = '';\n let i = 0;\n\n // Pre-process: remove single-line comments\n const lines = sql.split('\\n');\n const cleanedLines: string[] = [];\n\n for (const line of lines) {\n let cleanLine = '';\n let lineInString = false;\n let lineStringChar = '';\n\n for (let j = 0; j < line.length; j++) {\n const char = line[j];\n const nextChar = line[j + 1];\n\n if (!lineInString) {\n // Check for comment start\n if (char === '-' && nextChar === '-') {\n // Rest of line is a comment, skip it\n break;\n }\n // Check for string start\n if (char === \"'\" || char === '\"') {\n lineInString = true;\n lineStringChar = char;\n }\n } else {\n // Check for string end (handle escaped quotes)\n if (char === lineStringChar) {\n // Check if it's an escaped quote\n if (nextChar === lineStringChar) {\n cleanLine += char;\n j++; // Skip the next char\n cleanLine += line[j];\n continue;\n }\n lineInString = false;\n }\n }\n\n cleanLine += char;\n }\n\n cleanedLines.push(cleanLine);\n }\n\n const cleanedSql = cleanedLines.join('\\n');\n\n // Now split by semicolons, respecting string literals\n for (i = 0; i < cleanedSql.length; i++) {\n const char = cleanedSql[i];\n\n if (!inString) {\n if (char === \"'\" || char === '\"') {\n inString = true;\n stringChar = char;\n current += char;\n } else if (char === ';') {\n // End of statement\n const trimmed = current.trim();\n if (trimmed.length > 0) {\n statements.push(trimmed);\n }\n current = '';\n } else {\n current += char;\n }\n } else {\n current += char;\n\n // Check for end of string\n if (char === stringChar) {\n // Check for escaped quote\n const nextChar = cleanedSql[i + 1];\n if (nextChar === stringChar) {\n // Escaped quote, skip next char\n i++;\n current += nextChar;\n } else {\n inString = false;\n }\n }\n }\n }\n\n // Don't forget any remaining content\n const trimmed = current.trim();\n if (trimmed.length > 0) {\n statements.push(trimmed);\n }\n\n return statements;\n}\n\n/**\n * Validate that a filename follows the migration naming convention\n *\n * Expected format: TIMESTAMP-description.sql\n * - TIMESTAMP: 14 digits (YYYYMMDDHHmmss)\n * - description: kebab-case\n *\n * @param filename - Filename to validate\n * @returns true if valid, false otherwise\n */\nexport function isValidMigrationFilename(filename: string): boolean {\n // Must end with .sql (but not .rollback.sql)\n if (!filename.endsWith('.sql') || filename.endsWith('.rollback.sql')) {\n return false;\n }\n\n // Pattern: 14-digit timestamp, hyphen, description, .sql\n const pattern = /^\\d{14}-[a-z0-9-]+\\.sql$/;\n return pattern.test(filename);\n}\n\n/**\n * Validate that a filename follows the rollback naming convention\n *\n * Expected format: TIMESTAMP-description.rollback.sql\n *\n * @param filename - Filename to validate\n * @returns true if valid rollback file, false otherwise\n */\nexport function isValidRollbackFilename(filename: string): boolean {\n // Must end with .rollback.sql\n if (!filename.endsWith('.rollback.sql')) {\n return false;\n }\n\n // Pattern: 14-digit timestamp, hyphen, description, .rollback.sql\n const pattern = /^\\d{14}-[a-z0-9-]+\\.rollback\\.sql$/;\n return pattern.test(filename);\n}\n\n/**\n * Extract timestamp from migration filename\n *\n * @param filename - Migration filename\n * @returns Timestamp string or null if invalid\n */\nexport function extractTimestamp(filename: string): string | null {\n const match = filename.match(/^(\\d{14})-/);\n return match ? match[1] : null;\n}\n\n/**\n * Get the rollback filename for a migration\n *\n * @param migrationFilename - Migration filename (e.g., \"20250115120000-create-table.sql\")\n * @returns Rollback filename (e.g., \"20250115120000-create-table.rollback.sql\")\n */\nexport function getRollbackFilename(migrationFilename: string): string {\n return migrationFilename.replace(/\\.sql$/, '.rollback.sql');\n}\n","/**\n * Marketplace utilities for migration packaging\n *\n * Provides tools to generate migration manifests for marketplace distribution.\n * This is used at build time to prepare apps for submission.\n */\n\nimport { readdir, readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { resolve, basename } from 'path';\nimport { calculatePrefixedChecksum } from './checksum';\nimport { isValidMigrationFilename, extractTimestamp } from './sql-parser';\nimport type { MigrationFile } from '../types';\n\n/**\n * Migration entry in the marketplace manifest\n */\nexport interface MigrationManifestEntry {\n /** Migration ID (timestamp) */\n id: string;\n /** Full filename */\n file: string;\n /** SHA-256 checksum with prefix */\n checksum: string;\n}\n\n/**\n * Marketplace migration manifest format\n */\nexport interface MigrationManifest {\n /** Logical database name (used for table prefixing) */\n database: string;\n /** List of migrations in order */\n migrations: MigrationManifestEntry[];\n}\n\n/**\n * Options for generating migration manifest\n */\nexport interface GenerateMigrationManifestOptions {\n /** Directory containing migration SQL files */\n migrationsDir: string;\n /** Logical database name for the app */\n database: string;\n}\n\n/**\n * Result of migration manifest generation\n */\nexport interface GenerateMigrationManifestResult {\n /** The generated manifest */\n manifest: MigrationManifest;\n /** Migration files with content (for copying to output) */\n files: MigrationFile[];\n}\n\n/**\n * Read migration files from a directory\n *\n * @param dir - Directory containing migration files\n * @returns Array of migration files sorted by name\n */\nasync function readMigrationFiles(dir: string): Promise<MigrationFile[]> {\n if (!existsSync(dir)) {\n return [];\n }\n\n const files = await readdir(dir);\n const sqlFiles = files\n .filter((f) => isValidMigrationFilename(f))\n .sort();\n\n const migrations: MigrationFile[] = [];\n\n for (const file of sqlFiles) {\n const content = await readFile(resolve(dir, file), 'utf-8');\n migrations.push({\n name: basename(file),\n content,\n });\n }\n\n return migrations;\n}\n\n/**\n * Generate a marketplace migration manifest from migration files\n *\n * Reads migration files from the specified directory and generates\n * a manifest with checksums suitable for marketplace distribution.\n *\n * @param options - Generation options\n * @returns Manifest and file contents\n *\n * @example\n * ```typescript\n * import { generateMigrationManifest } from '@eldrin-project/eldrin-app-core';\n *\n * const result = await generateMigrationManifest({\n * migrationsDir: './migrations',\n * database: 'invoicing',\n * });\n *\n * // Write manifest to output directory\n * await writeFile(\n * 'dist/migrations/index.json',\n * JSON.stringify(result.manifest, null, 2)\n * );\n *\n * // Copy migration files\n * for (const file of result.files) {\n * await writeFile(`dist/migrations/${file.name}`, file.content);\n * }\n * ```\n */\nexport async function generateMigrationManifest(\n options: GenerateMigrationManifestOptions\n): Promise<GenerateMigrationManifestResult> {\n const { migrationsDir, database } = options;\n\n const files = await readMigrationFiles(migrationsDir);\n\n const migrations: MigrationManifestEntry[] = await Promise.all(\n files.map(async (file) => {\n const timestamp = extractTimestamp(file.name);\n const checksum = await calculatePrefixedChecksum(file.content);\n\n return {\n id: timestamp || file.name.replace('.sql', ''),\n file: file.name,\n checksum,\n };\n })\n );\n\n return {\n manifest: {\n database,\n migrations,\n },\n files,\n };\n}\n\n/**\n * Validate a migration manifest against actual files\n *\n * Useful for CI/CD validation in the marketplace-dist repository.\n *\n * @param manifest - The manifest to validate\n * @param files - The actual migration files\n * @returns Validation result with any errors\n */\nexport async function validateMigrationManifest(\n manifest: MigrationManifest,\n files: MigrationFile[]\n): Promise<{\n valid: boolean;\n errors: string[];\n}> {\n const errors: string[] = [];\n\n // Check all manifest entries have corresponding files\n for (const entry of manifest.migrations) {\n const file = files.find((f) => f.name === entry.file);\n\n if (!file) {\n errors.push(`Missing file: ${entry.file}`);\n continue;\n }\n\n // Verify checksum\n const actualChecksum = await calculatePrefixedChecksum(file.content);\n if (actualChecksum !== entry.checksum) {\n errors.push(\n `Checksum mismatch for ${entry.file}: ` +\n `expected ${entry.checksum}, got ${actualChecksum}`\n );\n }\n\n // Verify ID matches timestamp\n const timestamp = extractTimestamp(file.name);\n if (timestamp !== entry.id) {\n errors.push(\n `ID mismatch for ${entry.file}: ` +\n `expected ${timestamp}, got ${entry.id}`\n );\n }\n }\n\n // Check all files are in manifest\n for (const file of files) {\n if (!manifest.migrations.some((m) => m.file === file.name)) {\n errors.push(`File not in manifest: ${file.name}`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","/**\n * Release Script for Eldrin Apps\n *\n * Prepares app for marketplace submission by:\n * 1. Building the library bundle\n * 2. Generating migration manifest with checksums\n * 3. Creating deployment bundle (worker + client assets)\n * 4. Copying files to release directory\n *\n * Usage: npx eldrin-release [options]\n *\n * Output structure:\n * dist/release/{developerId}/{appId}/\n * ├── migrations/ # All migrations at app level (shared across versions)\n * │ ├── index.json\n * │ └── *.sql\n * └── v{version}/\n * ├── eldrin-app.manifest.json\n * ├── bundle.js\n * └── deploy-bundle.json (if client assets exist)\n */\n\nimport { mkdir, copyFile, writeFile, readFile, readdir, stat } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { resolve, relative } from 'path';\nimport { execSync } from 'child_process';\nimport { createInterface } from 'readline';\nimport { generateMigrationManifest } from '../migrations/marketplace.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface AppManifest {\n id: string;\n name: string;\n version: string;\n description?: string;\n developer: {\n id: string;\n name: string;\n };\n entry?: string;\n [key: string]: unknown;\n}\n\ntype BumpType = 'patch' | 'minor' | 'major';\n\ninterface ReleaseOptions {\n version?: string;\n bumpType?: BumpType;\n skipBuild?: boolean;\n manifestPath?: string;\n bundlePath?: string;\n migrationsDir?: string;\n outputDir?: string;\n marketplaceUrl?: string;\n}\n\ninterface ReleaseConfig {\n manifestPath: string;\n bundlePath: string;\n migrationsDir: string;\n buildCommand: string;\n clientDir?: string; // Path to client build output (e.g., dist/client)\n workerEntry?: string; // Worker entry file (e.g., dist/worker/index.js)\n}\n\n// Deployment bundle types\ninterface DeploymentAsset {\n path: string;\n contentType: string;\n size: number;\n base64: string;\n}\n\ninterface DeploymentBundle {\n version: string;\n worker: {\n script: string;\n main_module: string;\n };\n assets: DeploymentAsset[];\n}\n\n// Version manifest types\ninterface VersionChanges {\n features: string[];\n fixes: string[];\n breaking: string[];\n}\n\ninterface VersionEntry {\n version: string;\n releaseDate: string;\n changes: VersionChanges;\n}\n\ninterface VersionsManifest {\n latest: string;\n versions: VersionEntry[];\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MARKETPLACE_URL = 'https://eldrin.io';\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction parseArgs(): ReleaseOptions {\n const args = process.argv.slice(2);\n const options: ReleaseOptions = {};\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n const nextArg = args[i + 1];\n\n switch (arg) {\n case '--type':\n case '-t':\n if (nextArg === 'patch' || nextArg === 'minor' || nextArg === 'major') {\n options.bumpType = nextArg;\n } else {\n console.error(`Invalid bump type: ${nextArg}. Must be patch, minor, or major.`);\n process.exit(1);\n }\n i++;\n break;\n case '--version':\n case '-v':\n options.version = nextArg;\n i++;\n break;\n case '--skip-build':\n options.skipBuild = true;\n break;\n case '--manifest':\n case '-m':\n options.manifestPath = nextArg;\n i++;\n break;\n case '--bundle':\n case '-b':\n options.bundlePath = nextArg;\n i++;\n break;\n case '--migrations':\n options.migrationsDir = nextArg;\n i++;\n break;\n case '--output':\n case '-o':\n options.outputDir = nextArg;\n i++;\n break;\n case '--marketplace-url':\n case '-u':\n options.marketplaceUrl = nextArg;\n i++;\n break;\n case '--help':\n case '-h':\n printHelp();\n process.exit(0);\n }\n }\n\n return options;\n}\n\n/**\n * Bump a semver version string\n */\nfunction bumpVersion(version: string, type: BumpType): string {\n const parts = version.split('.').map(Number);\n if (parts.length !== 3 || parts.some(isNaN)) {\n throw new Error(`Invalid version format: ${version}. Expected semver (e.g., 1.0.0)`);\n }\n\n const [major, minor, patch] = parts;\n\n switch (type) {\n case 'major':\n return `${major + 1}.0.0`;\n case 'minor':\n return `${major}.${minor + 1}.0`;\n case 'patch':\n return `${major}.${minor}.${patch + 1}`;\n }\n}\n\n/**\n * Compare two semver versions\n * Returns: -1 if a < b, 0 if a == b, 1 if a > b\n */\nfunction compareVersions(a: string, b: string): number {\n const partsA = a.split('.').map(Number);\n const partsB = b.split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n if (partsA[i] < partsB[i]) return -1;\n if (partsA[i] > partsB[i]) return 1;\n }\n return 0;\n}\n\n/**\n * Prompt user for input\n */\nasync function prompt(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt user to choose from options\n */\nasync function promptChoice(question: string, options: string[]): Promise<number> {\n console.log(question);\n options.forEach((opt, i) => console.log(` ${i + 1}. ${opt}`));\n\n while (true) {\n const answer = await prompt(`Choose (1-${options.length}): `);\n const choice = parseInt(answer, 10);\n if (choice >= 1 && choice <= options.length) {\n return choice - 1;\n }\n console.log(`Invalid choice. Please enter a number between 1 and ${options.length}.`);\n }\n}\n\n/**\n * Fetch the latest version from the marketplace\n */\nasync function fetchLatestVersion(\n marketplaceUrl: string,\n developerId: string,\n appId: string\n): Promise<string | null> {\n const url = `${marketplaceUrl}/api/marketplace/versions?developerId=${encodeURIComponent(developerId)}&appId=${encodeURIComponent(appId)}`;\n\n try {\n const response = await fetch(url);\n const contentType = response.headers.get('content-type') || '';\n\n if (!contentType.includes('application/json')) {\n throw new Error('Marketplace error, please try again later.');\n }\n\n const data = await response.json() as { success: boolean; latest?: string | null; error?: string };\n\n if (!data.success) {\n throw new Error(data.error || 'Marketplace error, please try again later.');\n }\n\n return data.latest || null;\n } catch (error) {\n if (error instanceof Error && error.message.startsWith('Marketplace')) {\n throw error;\n }\n throw new Error('Marketplace error, please try again later.');\n }\n}\n\nfunction printHelp(): void {\n console.log(`\nEldrin Release Tool\n\nPrepares an Eldrin app for marketplace submission.\n\nUsage:\n npx eldrin-release [options]\n\nOptions:\n -t, --type <type> Bump version: patch, minor, or major\n Fetches latest from marketplace and bumps accordingly\n -v, --version <version> Override version explicitly (ignores --type)\n --skip-build Skip running the build command\n -m, --manifest <path> Path to manifest (default: ./eldrin-app.manifest.json)\n -b, --bundle <path> Path to bundle (default: auto-detected from manifest)\n --migrations <path> Path to migrations directory (default: ./migrations)\n -o, --output <path> Output directory (default: ./dist/release/...)\n -u, --marketplace-url Marketplace URL (default: https://eldrin.io)\n -h, --help Show this help message\n\nExamples:\n npx eldrin-release # Re-release current version\n npx eldrin-release --type patch # Bump patch (0.0.1 -> 0.0.2)\n npx eldrin-release --type minor # Bump minor (0.0.1 -> 0.1.0)\n npx eldrin-release --type major # Bump major (0.0.1 -> 1.0.0)\n npx eldrin-release --version 2.0.0 # Set explicit version\n npx eldrin-release --skip-build # Skip build step\n`);\n}\n\nasync function findManifest(projectRoot: string): Promise<string | null> {\n const candidates = [\n 'eldrin-app.manifest.json',\n 'manifest.json',\n ];\n\n for (const candidate of candidates) {\n const path = resolve(projectRoot, candidate);\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nasync function findBundle(projectRoot: string, manifest: AppManifest): Promise<string | null> {\n // Check manifest entry field\n if (manifest.entry) {\n const fromDist = resolve(projectRoot, 'dist', manifest.entry);\n if (existsSync(fromDist)) return fromDist;\n }\n\n // Try common patterns\n const patterns = [\n `dist/eldrin-${manifest.id}.js`,\n `dist/${manifest.id}.js`,\n 'dist/bundle.js',\n 'dist/index.js',\n ];\n\n for (const pattern of patterns) {\n const path = resolve(projectRoot, pattern);\n if (existsSync(path)) {\n return path;\n }\n }\n\n // Search dist directory\n const distDir = resolve(projectRoot, 'dist');\n if (existsSync(distDir)) {\n const files = await readdir(distDir);\n const jsFile = files.find(f => f.endsWith('.js') && !f.endsWith('.d.js'));\n if (jsFile) {\n return resolve(distDir, jsFile);\n }\n }\n\n return null;\n}\n\nasync function ensureDir(dir: string): Promise<void> {\n if (!existsSync(dir)) {\n await mkdir(dir, { recursive: true });\n }\n}\n\nasync function loadConfig(projectRoot: string, options: ReleaseOptions): Promise<ReleaseConfig> {\n // Try to load from .eldrinrc.json\n const rcPath = resolve(projectRoot, '.eldrinrc.json');\n let rcConfig: Partial<ReleaseConfig> = {};\n\n if (existsSync(rcPath)) {\n try {\n rcConfig = JSON.parse(await readFile(rcPath, 'utf-8'));\n } catch {\n // Ignore parse errors\n }\n }\n\n return {\n manifestPath: options.manifestPath || rcConfig.manifestPath || './eldrin-app.manifest.json',\n bundlePath: options.bundlePath || rcConfig.bundlePath || '',\n migrationsDir: options.migrationsDir || rcConfig.migrationsDir || './migrations',\n buildCommand: rcConfig.buildCommand || 'npm run build:lib',\n clientDir: rcConfig.clientDir || 'dist/client',\n workerEntry: rcConfig.workerEntry || 'dist/worker/index.js',\n };\n}\n\n// ============================================================================\n// Deployment Bundle Functions\n// ============================================================================\n\nfunction getContentType(filename: string): string {\n const ext = filename.toLowerCase().split('.').pop();\n const contentTypes: Record<string, string> = {\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'application/javascript',\n 'json': 'application/json',\n 'svg': 'image/svg+xml',\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'ico': 'image/x-icon',\n 'woff': 'font/woff',\n 'woff2': 'font/woff2',\n 'ttf': 'font/ttf',\n 'eot': 'application/vnd.ms-fontobject',\n 'webp': 'image/webp',\n 'gif': 'image/gif',\n 'map': 'application/json',\n };\n return contentTypes[ext || ''] || 'application/octet-stream';\n}\n\nasync function bundleClientAssets(clientDir: string): Promise<DeploymentAsset[]> {\n const assets: DeploymentAsset[] = [];\n\n async function processDir(dir: string, baseDir: string) {\n const items = await readdir(dir);\n\n for (const item of items) {\n const fullPath = resolve(dir, item);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n // Skip hidden directories like .vite\n if (!item.startsWith('.')) {\n await processDir(fullPath, baseDir);\n }\n } else {\n // Skip dev files\n if (item === '.dev.vars' || item === 'wrangler.json') continue;\n\n const relativePath = '/' + relative(baseDir, fullPath);\n const content = await readFile(fullPath);\n const base64 = content.toString('base64');\n\n assets.push({\n path: relativePath,\n contentType: getContentType(item),\n size: stats.size,\n base64,\n });\n }\n }\n }\n\n await processDir(clientDir, clientDir);\n return assets;\n}\n\nasync function createDeploymentBundle(\n workerPath: string,\n clientDir: string,\n version: string\n): Promise<DeploymentBundle> {\n // Read worker script\n const workerScript = await readFile(workerPath, 'utf-8');\n\n // Bundle client assets\n const assets = await bundleClientAssets(clientDir);\n\n return {\n version,\n worker: {\n script: workerScript,\n main_module: 'index.js',\n },\n assets,\n };\n}\n\n// ============================================================================\n// Versions Manifest Functions\n// ============================================================================\n\nasync function loadVersionsManifest(appDir: string): Promise<VersionsManifest> {\n const versionsPath = resolve(appDir, 'versions.json');\n\n if (existsSync(versionsPath)) {\n try {\n const content = await readFile(versionsPath, 'utf-8');\n return JSON.parse(content);\n } catch {\n // If corrupted, start fresh\n }\n }\n\n return { latest: '', versions: [] };\n}\n\nasync function updateVersionsManifest(\n appDir: string,\n version: string,\n changelog?: string\n): Promise<void> {\n const manifest = await loadVersionsManifest(appDir);\n\n // Check if version already exists\n const existingIndex = manifest.versions.findIndex(v => v.version === version);\n\n const today = new Date().toISOString().split('T')[0];\n\n // Parse changelog or use default\n const changes: VersionChanges = {\n features: [],\n fixes: [],\n breaking: [],\n };\n\n if (changelog) {\n // Simple parsing: assume changelog is a feature description\n changes.features.push(changelog);\n } else if (existingIndex === -1) {\n // Default message for new versions\n changes.features.push(`Release ${version}`);\n }\n\n const entry: VersionEntry = {\n version,\n releaseDate: today,\n changes,\n };\n\n if (existingIndex >= 0) {\n // Update existing entry\n manifest.versions[existingIndex] = entry;\n } else {\n // Add new entry at the beginning\n manifest.versions.unshift(entry);\n }\n\n // Update latest pointer\n manifest.latest = version;\n\n // Write updated manifest\n await writeFile(\n resolve(appDir, 'versions.json'),\n JSON.stringify(manifest, null, 2)\n );\n}\n\n// ============================================================================\n// Main Release Function\n// ============================================================================\n\nexport async function release(options: ReleaseOptions = {}): Promise<void> {\n const projectRoot = process.cwd();\n const config = await loadConfig(projectRoot, options);\n\n // Step 1: Find and load manifest\n console.log('\\n📦 Eldrin Release Tool\\n');\n\n const manifestPath = resolve(projectRoot, config.manifestPath);\n if (!existsSync(manifestPath)) {\n const found = await findManifest(projectRoot);\n if (!found) {\n throw new Error(\n 'Could not find eldrin-app.manifest.json. Create one or specify with --manifest'\n );\n }\n config.manifestPath = found;\n }\n\n console.log(`📄 Loading manifest from ${config.manifestPath}`);\n const manifest: AppManifest = JSON.parse(\n await readFile(resolve(projectRoot, config.manifestPath), 'utf-8')\n );\n\n const developerId = manifest.developer.id;\n const appId = manifest.id;\n const localVersion = manifest.version || '0.0.0';\n\n // Fetch remote version from marketplace\n const marketplaceUrl = options.marketplaceUrl || process.env.ELDRIN_MARKETPLACE_URL || DEFAULT_MARKETPLACE_URL;\n console.log(` App: ${appId} by ${developerId}`);\n console.log(` Local version: ${localVersion}`);\n console.log(` Fetching latest version from marketplace...`);\n\n let remoteVersion: string | null = null;\n try {\n remoteVersion = await fetchLatestVersion(marketplaceUrl, developerId, appId);\n } catch (error) {\n console.log(` ⚠️ Could not fetch remote version: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n\n if (remoteVersion) {\n console.log(` Remote version: ${remoteVersion}`);\n } else {\n console.log(` Remote version: (none - new app)`);\n }\n\n // Determine version: explicit > bump > interactive > manifest\n let version: string;\n\n if (options.version) {\n // Explicit version provided\n version = options.version;\n console.log(` Using explicit version: ${version}\\n`);\n } else if (options.bumpType) {\n // Bump type provided via CLI\n const baseVersion = remoteVersion || localVersion;\n version = bumpVersion(baseVersion, options.bumpType);\n console.log(` Bumping ${options.bumpType}: ${baseVersion} → ${version}\\n`);\n } else if (remoteVersion && compareVersions(remoteVersion, localVersion) > 0) {\n // Remote version is higher than local - prompt user\n console.log(`\\n⚠️ Remote version (${remoteVersion}) is higher than local (${localVersion})\\n`);\n\n const choice = await promptChoice('What would you like to do?', [\n `Use remote version (${remoteVersion})`,\n 'Bump version',\n ]);\n\n if (choice === 0) {\n // Use remote version\n version = remoteVersion;\n console.log(`\\n Using remote version: ${version}\\n`);\n } else {\n // Bump version - ask for bump type\n const bumpChoice = await promptChoice('\\nSelect bump type:', [\n `Patch (${bumpVersion(remoteVersion, 'patch')})`,\n `Minor (${bumpVersion(remoteVersion, 'minor')})`,\n `Major (${bumpVersion(remoteVersion, 'major')})`,\n ]);\n\n const bumpTypes: BumpType[] = ['patch', 'minor', 'major'];\n version = bumpVersion(remoteVersion, bumpTypes[bumpChoice]);\n console.log(`\\n Bumping ${bumpTypes[bumpChoice]}: ${remoteVersion} → ${version}\\n`);\n }\n } else if (remoteVersion && compareVersions(remoteVersion, localVersion) === 0) {\n // Versions are equal - re-release or bump\n console.log(`\\n Local and remote versions match (${localVersion})`);\n console.log(` Re-releasing current version\\n`);\n version = localVersion;\n } else {\n // Local is higher or no remote - use local version\n version = localVersion;\n console.log(` Using local version: ${version}\\n`);\n }\n\n // Update manifest and package.json if version changed\n if (version !== manifest.version) {\n console.log(`📝 Updating manifest version: ${manifest.version} → ${version}`);\n const updatedManifest = { ...manifest, version };\n await writeFile(\n resolve(projectRoot, config.manifestPath),\n JSON.stringify(updatedManifest, null, 2) + '\\n'\n );\n\n // Also update package.json if it exists\n const packageJsonPath = resolve(projectRoot, 'package.json');\n if (existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\n if (packageJson.version !== version) {\n console.log(`📝 Updating package.json version: ${packageJson.version} → ${version}`);\n packageJson.version = version;\n await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\\n');\n }\n }\n }\n\n // Step 2: Build the library bundle\n if (!options.skipBuild) {\n console.log('🔨 Building library bundle...');\n try {\n execSync(config.buildCommand, { stdio: 'inherit', cwd: projectRoot });\n } catch {\n throw new Error('Build failed. Fix errors and try again, or use --skip-build');\n }\n } else {\n console.log('⏭️ Skipping build (--skip-build)');\n }\n\n // Step 3: Find bundle\n const bundlePath = config.bundlePath\n ? resolve(projectRoot, config.bundlePath)\n : await findBundle(projectRoot, manifest);\n\n if (!bundlePath || !existsSync(bundlePath)) {\n throw new Error(\n `Bundle not found. Build the app first or specify with --bundle`\n );\n }\n\n console.log(`📦 Found bundle: ${bundlePath}`);\n\n // Step 4: Prepare output directory\n // Version-specific files go in version folder, migrations at app level\n const appDir = resolve(projectRoot, `dist/release/${developerId}/${appId}`);\n const outputDir =\n options.outputDir ||\n resolve(appDir, `v${version}`);\n const migrationsOutputDir = resolve(appDir, 'migrations');\n\n console.log('📁 Creating output directories...');\n await ensureDir(outputDir);\n await ensureDir(migrationsOutputDir);\n\n // Step 5: Copy manifest (with updated version)\n console.log('📄 Writing manifest...');\n const outputManifest = { ...manifest, version };\n await writeFile(\n resolve(outputDir, 'eldrin-app.manifest.json'),\n JSON.stringify(outputManifest, null, 2)\n );\n\n // Step 6: Copy bundle\n console.log('📦 Copying bundle...');\n await copyFile(bundlePath, resolve(outputDir, 'bundle.js'));\n\n // Step 6b: Create deployment bundle (for apps with client assets)\n const clientDir = resolve(projectRoot, config.clientDir || 'dist/client');\n const workerPath = resolve(projectRoot, config.workerEntry || 'dist/worker/index.js');\n\n if (existsSync(clientDir) && existsSync(workerPath)) {\n console.log('📦 Creating deployment bundle...');\n\n const deployBundle = await createDeploymentBundle(workerPath, clientDir, version);\n\n await writeFile(\n resolve(outputDir, 'deploy-bundle.json'),\n JSON.stringify(deployBundle) // No pretty print to save space\n );\n\n console.log(` ✓ ${deployBundle.assets.length} client asset(s) bundled`);\n console.log(` ✓ Worker script: ${deployBundle.worker.script.length} bytes`);\n }\n\n // Step 7: Generate migration manifest\n const migrationsDir = resolve(projectRoot, config.migrationsDir);\n if (existsSync(migrationsDir)) {\n console.log('🗃️ Generating migration manifest...');\n\n const result = await generateMigrationManifest({\n migrationsDir,\n database: appId,\n });\n\n if (result.files.length > 0) {\n // Write migration index\n await writeFile(\n resolve(migrationsOutputDir, 'index.json'),\n JSON.stringify(result.manifest, null, 2)\n );\n\n // Copy migration files\n for (const file of result.files) {\n await writeFile(resolve(migrationsOutputDir, file.name), file.content);\n }\n\n console.log(` ✓ ${result.files.length} migration(s) processed`);\n } else {\n console.log(' ℹ️ No migrations found');\n }\n } else {\n console.log('ℹ️ No migrations directory found');\n }\n\n // Step 8: Update versions manifest\n console.log('📋 Updating versions manifest...');\n await updateVersionsManifest(appDir, version);\n console.log(` ✓ versions.json updated (latest: ${version})`);\n\n // Step 9: Summary\n console.log('\\n✅ Release package created successfully!\\n');\n console.log(`📍 Output: ${outputDir}`);\n console.log('\\nFiles:');\n console.log(' • eldrin-app.manifest.json');\n console.log(' • bundle.js');\n if (existsSync(resolve(outputDir, 'deploy-bundle.json'))) {\n console.log(' • deploy-bundle.json (deployment bundle with client assets)');\n }\n if (existsSync(migrationsDir)) {\n console.log(' • migrations/index.json');\n console.log(' • migrations/*.sql');\n }\n console.log(' • versions.json (at app level)');\n\n console.log('\\n📋 Next steps:');\n console.log(` npx eldrin-submit -d ${outputDir}`);\n console.log('');\n}\n\n// ============================================================================\n// CLI Entry Point\n// ============================================================================\n\nasync function main(): Promise<void> {\n const options = parseArgs();\n await release(options);\n}\n\nmain().catch((error) => {\n console.error('\\n❌ Release failed:', error.message);\n process.exit(1);\n});\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App Submission Script for Eldrin Marketplace
|
|
3
|
+
*
|
|
4
|
+
* Submits a release package to the Eldrin Marketplace for review.
|
|
5
|
+
* Creates a PR in the eldrin-marketplace-dist repository.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx eldrin-submit --release-dir ./dist/release/eldrin.io/invoicing/v0.0.1
|
|
9
|
+
*
|
|
10
|
+
* Environment variables:
|
|
11
|
+
* ELDRIN_API_KEY - Your developer API key (required)
|
|
12
|
+
* ELDRIN_DEVELOPER_ID - Your developer ID (required)
|
|
13
|
+
* ELDRIN_MARKETPLACE_URL - Marketplace API URL (default: https://eldrin.io)
|
|
14
|
+
*
|
|
15
|
+
* Or use a config file (.eldrinrc.json):
|
|
16
|
+
* {
|
|
17
|
+
* "developerId": "your-developer-id",
|
|
18
|
+
* "marketplaceUrl": "https://eldrin.io"
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
interface SubmitAppResponse {
|
|
22
|
+
success: boolean;
|
|
23
|
+
prUrl?: string;
|
|
24
|
+
branch?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
interface SubmitOptions {
|
|
28
|
+
releaseDir: string;
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
developerId?: string;
|
|
31
|
+
marketplaceUrl?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
dryRun?: boolean;
|
|
34
|
+
}
|
|
35
|
+
declare function submitApp(options: SubmitOptions): Promise<SubmitAppResponse>;
|
|
36
|
+
|
|
37
|
+
export { submitApp };
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { resolve, dirname, relative } from 'path';
|
|
5
|
+
import { config } from 'dotenv';
|
|
6
|
+
|
|
7
|
+
config();
|
|
8
|
+
var DEFAULT_MARKETPLACE_URL = "https://eldrin.io";
|
|
9
|
+
var BINARY_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot"];
|
|
10
|
+
function isBinaryFile(filename) {
|
|
11
|
+
const ext = filename.toLowerCase().slice(filename.lastIndexOf("."));
|
|
12
|
+
return BINARY_EXTENSIONS.includes(ext);
|
|
13
|
+
}
|
|
14
|
+
async function readFilesRecursively(dir, baseDir = dir) {
|
|
15
|
+
const files = [];
|
|
16
|
+
const entries = await readdir(dir);
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const fullPath = resolve(dir, entry);
|
|
19
|
+
const stats = await stat(fullPath);
|
|
20
|
+
if (stats.isDirectory()) {
|
|
21
|
+
const subFiles = await readFilesRecursively(fullPath, baseDir);
|
|
22
|
+
files.push(...subFiles);
|
|
23
|
+
} else {
|
|
24
|
+
const relativePath = relative(baseDir, fullPath);
|
|
25
|
+
const isBinary = isBinaryFile(entry);
|
|
26
|
+
const content = await readFile(fullPath, isBinary ? "base64" : "utf-8");
|
|
27
|
+
files.push({
|
|
28
|
+
path: relativePath,
|
|
29
|
+
content,
|
|
30
|
+
encoding: isBinary ? "base64" : "utf-8"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
async function loadConfig(projectDir) {
|
|
37
|
+
const configPath = resolve(projectDir, ".eldrinrc.json");
|
|
38
|
+
if (!existsSync(configPath)) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const content = await readFile(configPath, "utf-8");
|
|
43
|
+
return JSON.parse(content);
|
|
44
|
+
} catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function extractAppInfo(releaseDir) {
|
|
49
|
+
const manifestPath = resolve(releaseDir, "eldrin-app.manifest.json");
|
|
50
|
+
if (!existsSync(manifestPath)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const content = await readFile(manifestPath, "utf-8");
|
|
54
|
+
const manifest = JSON.parse(content);
|
|
55
|
+
return {
|
|
56
|
+
appId: manifest.id,
|
|
57
|
+
version: manifest.version
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function parseArgs() {
|
|
61
|
+
const args = process.argv.slice(2);
|
|
62
|
+
const options = {
|
|
63
|
+
releaseDir: ""
|
|
64
|
+
};
|
|
65
|
+
for (let i = 0; i < args.length; i++) {
|
|
66
|
+
const arg = args[i];
|
|
67
|
+
const nextArg = args[i + 1];
|
|
68
|
+
switch (arg) {
|
|
69
|
+
case "--release-dir":
|
|
70
|
+
case "-d":
|
|
71
|
+
options.releaseDir = nextArg || "";
|
|
72
|
+
i++;
|
|
73
|
+
break;
|
|
74
|
+
case "--api-key":
|
|
75
|
+
case "-k":
|
|
76
|
+
options.apiKey = nextArg;
|
|
77
|
+
i++;
|
|
78
|
+
break;
|
|
79
|
+
case "--developer-id":
|
|
80
|
+
options.developerId = nextArg;
|
|
81
|
+
i++;
|
|
82
|
+
break;
|
|
83
|
+
case "--marketplace-url":
|
|
84
|
+
case "-u":
|
|
85
|
+
options.marketplaceUrl = nextArg;
|
|
86
|
+
i++;
|
|
87
|
+
break;
|
|
88
|
+
case "--description":
|
|
89
|
+
case "-m":
|
|
90
|
+
options.description = nextArg;
|
|
91
|
+
i++;
|
|
92
|
+
break;
|
|
93
|
+
case "--dry-run":
|
|
94
|
+
options.dryRun = true;
|
|
95
|
+
break;
|
|
96
|
+
case "--help":
|
|
97
|
+
case "-h":
|
|
98
|
+
printHelp();
|
|
99
|
+
process.exit(0);
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
if (!arg.startsWith("-") && !options.releaseDir) {
|
|
103
|
+
options.releaseDir = arg;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return options;
|
|
108
|
+
}
|
|
109
|
+
function printHelp() {
|
|
110
|
+
console.log(`
|
|
111
|
+
Eldrin App Submission Tool
|
|
112
|
+
|
|
113
|
+
Usage:
|
|
114
|
+
npx eldrin-submit [options] <release-dir>
|
|
115
|
+
|
|
116
|
+
Options:
|
|
117
|
+
-d, --release-dir <dir> Release directory to submit
|
|
118
|
+
-k, --api-key <key> Developer API key (or set ELDRIN_API_KEY env var)
|
|
119
|
+
--developer-id <id> Developer ID (or set ELDRIN_DEVELOPER_ID env var)
|
|
120
|
+
-u, --marketplace-url <url> Marketplace URL (default: https://eldrin.io)
|
|
121
|
+
-m, --description <text> PR description
|
|
122
|
+
--dry-run Show what would be submitted without sending
|
|
123
|
+
-h, --help Show this help message
|
|
124
|
+
|
|
125
|
+
Environment Variables:
|
|
126
|
+
ELDRIN_API_KEY Developer API key
|
|
127
|
+
ELDRIN_DEVELOPER_ID Developer ID
|
|
128
|
+
ELDRIN_MARKETPLACE_URL Marketplace API URL
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
npx eldrin-submit -d ./dist/release/eldrin.io/invoicing/v0.0.1
|
|
132
|
+
|
|
133
|
+
Config File (.eldrinrc.json):
|
|
134
|
+
{
|
|
135
|
+
"developerId": "your-developer-id",
|
|
136
|
+
"marketplaceUrl": "https://eldrin.io"
|
|
137
|
+
}
|
|
138
|
+
`);
|
|
139
|
+
}
|
|
140
|
+
async function submitApp(options) {
|
|
141
|
+
const { releaseDir, dryRun } = options;
|
|
142
|
+
if (!releaseDir) {
|
|
143
|
+
return { success: false, error: "Release directory is required" };
|
|
144
|
+
}
|
|
145
|
+
const absoluteReleaseDir = resolve(process.cwd(), releaseDir);
|
|
146
|
+
if (!existsSync(absoluteReleaseDir)) {
|
|
147
|
+
return { success: false, error: `Release directory not found: ${absoluteReleaseDir}` };
|
|
148
|
+
}
|
|
149
|
+
const projectDir = dirname(dirname(absoluteReleaseDir));
|
|
150
|
+
const config = await loadConfig(projectDir);
|
|
151
|
+
const apiKey = options.apiKey || process.env.ELDRIN_API_KEY;
|
|
152
|
+
const developerId = options.developerId || process.env.ELDRIN_DEVELOPER_ID || config.developerId;
|
|
153
|
+
const marketplaceUrl = options.marketplaceUrl || process.env.ELDRIN_MARKETPLACE_URL || config.marketplaceUrl || DEFAULT_MARKETPLACE_URL;
|
|
154
|
+
if (!apiKey) {
|
|
155
|
+
return { success: false, error: "API key is required. Set ELDRIN_API_KEY or use --api-key" };
|
|
156
|
+
}
|
|
157
|
+
if (!developerId) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: "Developer ID is required. Set ELDRIN_DEVELOPER_ID or use --developer-id"
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const appInfo = await extractAppInfo(absoluteReleaseDir);
|
|
164
|
+
if (!appInfo) {
|
|
165
|
+
return { success: false, error: "Could not find eldrin-app.manifest.json in release directory" };
|
|
166
|
+
}
|
|
167
|
+
console.log(`
|
|
168
|
+
\u{1F4E6} Submitting ${appInfo.appId} v${appInfo.version}
|
|
169
|
+
`);
|
|
170
|
+
console.log("\u{1F4C2} Reading release files...");
|
|
171
|
+
const versionFiles = await readFilesRecursively(absoluteReleaseDir);
|
|
172
|
+
const appDir = dirname(absoluteReleaseDir);
|
|
173
|
+
const migrationsDir = resolve(appDir, "migrations");
|
|
174
|
+
let migrationFiles = [];
|
|
175
|
+
if (existsSync(migrationsDir)) {
|
|
176
|
+
const rawMigrationFiles = await readFilesRecursively(migrationsDir);
|
|
177
|
+
migrationFiles = rawMigrationFiles.map((f) => ({
|
|
178
|
+
...f,
|
|
179
|
+
path: `migrations/${f.path}`
|
|
180
|
+
}));
|
|
181
|
+
console.log(` Found ${migrationFiles.length} migration file(s)`);
|
|
182
|
+
}
|
|
183
|
+
const versionsPath = resolve(appDir, "versions.json");
|
|
184
|
+
let versionsFile = [];
|
|
185
|
+
if (existsSync(versionsPath)) {
|
|
186
|
+
const content = await readFile(versionsPath, "utf-8");
|
|
187
|
+
versionsFile = [{
|
|
188
|
+
path: "versions.json",
|
|
189
|
+
content,
|
|
190
|
+
encoding: "utf-8"
|
|
191
|
+
}];
|
|
192
|
+
console.log(" Found versions.json");
|
|
193
|
+
}
|
|
194
|
+
const files = [...versionFiles, ...migrationFiles, ...versionsFile];
|
|
195
|
+
console.log(` Total: ${files.length} file(s)`);
|
|
196
|
+
if (dryRun) {
|
|
197
|
+
console.log("\n\u{1F50D} Dry run - files that would be submitted:");
|
|
198
|
+
for (const file of files) {
|
|
199
|
+
const size = Buffer.byteLength(file.content, file.encoding === "base64" ? "base64" : "utf-8");
|
|
200
|
+
console.log(` \u2022 ${file.path} (${(size / 1024).toFixed(1)} KB)`);
|
|
201
|
+
}
|
|
202
|
+
console.log("\n\u2705 Dry run complete. No files were submitted.");
|
|
203
|
+
return { success: true };
|
|
204
|
+
}
|
|
205
|
+
const request = {
|
|
206
|
+
apiKey,
|
|
207
|
+
developerId,
|
|
208
|
+
appId: appInfo.appId,
|
|
209
|
+
version: appInfo.version,
|
|
210
|
+
files,
|
|
211
|
+
description: options.description
|
|
212
|
+
};
|
|
213
|
+
console.log(`\u{1F4E4} Submitting to ${marketplaceUrl}...`);
|
|
214
|
+
try {
|
|
215
|
+
const response = await fetch(`${marketplaceUrl}/api/apps/submit`, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: {
|
|
218
|
+
"Content-Type": "application/json"
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify(request)
|
|
221
|
+
});
|
|
222
|
+
const result = await response.json();
|
|
223
|
+
if (!result.success) {
|
|
224
|
+
console.error(`
|
|
225
|
+
\u274C Submission failed: ${result.error}`);
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
console.log("\n\u2705 Submission successful!\n");
|
|
229
|
+
console.log(`\u{1F4CB} Pull Request: ${result.prUrl}`);
|
|
230
|
+
console.log(`\u{1F33F} Branch: ${result.branch}`);
|
|
231
|
+
console.log("\n\u{1F4CB} Next steps:");
|
|
232
|
+
console.log(" 1. Review the PR in GitHub");
|
|
233
|
+
console.log(" 2. Wait for automated checks to pass");
|
|
234
|
+
console.log(" 3. Eldrin team will review and merge");
|
|
235
|
+
console.log("");
|
|
236
|
+
return result;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const message = error instanceof Error ? error.message : "Network error";
|
|
239
|
+
console.error(`
|
|
240
|
+
\u274C Submission failed: ${message}`);
|
|
241
|
+
return { success: false, error: message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async function main() {
|
|
245
|
+
const options = parseArgs();
|
|
246
|
+
if (!options.releaseDir) {
|
|
247
|
+
console.error("Error: Release directory is required");
|
|
248
|
+
console.log("Run with --help for usage information");
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
const result = await submitApp(options);
|
|
252
|
+
process.exit(result.success ? 0 : 1);
|
|
253
|
+
}
|
|
254
|
+
main().catch((error) => {
|
|
255
|
+
console.error("Error:", error);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export { submitApp };
|
|
260
|
+
//# sourceMappingURL=submit.js.map
|
|
261
|
+
//# sourceMappingURL=submit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/submit.ts"],"names":["loadEnv"],"mappings":"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","file":"submit.js","sourcesContent":["/**\n * App Submission Script for Eldrin Marketplace\n *\n * Submits a release package to the Eldrin Marketplace for review.\n * Creates a PR in the eldrin-marketplace-dist repository.\n *\n * Usage:\n * npx eldrin-submit --release-dir ./dist/release/eldrin.io/invoicing/v0.0.1\n *\n * Environment variables:\n * ELDRIN_API_KEY - Your developer API key (required)\n * ELDRIN_DEVELOPER_ID - Your developer ID (required)\n * ELDRIN_MARKETPLACE_URL - Marketplace API URL (default: https://eldrin.io)\n *\n * Or use a config file (.eldrinrc.json):\n * {\n * \"developerId\": \"your-developer-id\",\n * \"marketplaceUrl\": \"https://eldrin.io\"\n * }\n */\n\nimport { readdir, readFile, stat } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { resolve, relative, dirname } from 'path';\nimport { config as loadEnv } from 'dotenv';\n\n// Load .env file from current working directory\nloadEnv();\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SubmissionFile {\n path: string;\n content: string;\n encoding?: 'utf-8' | 'base64';\n}\n\ninterface SubmitAppRequest {\n apiKey: string;\n developerId: string;\n appId: string;\n version: string;\n files: SubmissionFile[];\n description?: string;\n}\n\ninterface SubmitAppResponse {\n success: boolean;\n prUrl?: string;\n branch?: string;\n error?: string;\n}\n\ninterface SubmitOptions {\n releaseDir: string;\n apiKey?: string;\n developerId?: string;\n marketplaceUrl?: string;\n description?: string;\n dryRun?: boolean;\n}\n\ninterface EldrinConfig {\n developerId?: string;\n marketplaceUrl?: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MARKETPLACE_URL = 'https://eldrin.io';\nconst BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.eot'];\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a file should be encoded as base64\n */\nfunction isBinaryFile(filename: string): boolean {\n const ext = filename.toLowerCase().slice(filename.lastIndexOf('.'));\n return BINARY_EXTENSIONS.includes(ext);\n}\n\n/**\n * Recursively read all files in a directory\n */\nasync function readFilesRecursively(\n dir: string,\n baseDir: string = dir\n): Promise<SubmissionFile[]> {\n const files: SubmissionFile[] = [];\n const entries = await readdir(dir);\n\n for (const entry of entries) {\n const fullPath = resolve(dir, entry);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n const subFiles = await readFilesRecursively(fullPath, baseDir);\n files.push(...subFiles);\n } else {\n const relativePath = relative(baseDir, fullPath);\n const isBinary = isBinaryFile(entry);\n\n const content = await readFile(fullPath, isBinary ? 'base64' : 'utf-8');\n\n files.push({\n path: relativePath,\n content: content as string,\n encoding: isBinary ? 'base64' : 'utf-8',\n });\n }\n }\n\n return files;\n}\n\n/**\n * Load config from .eldrinrc.json\n */\nasync function loadConfig(projectDir: string): Promise<EldrinConfig> {\n const configPath = resolve(projectDir, '.eldrinrc.json');\n\n if (!existsSync(configPath)) {\n return {};\n }\n\n try {\n const content = await readFile(configPath, 'utf-8');\n return JSON.parse(content);\n } catch {\n return {};\n }\n}\n\n/**\n * Extract app info from manifest\n */\nasync function extractAppInfo(\n releaseDir: string\n): Promise<{ appId: string; version: string } | null> {\n const manifestPath = resolve(releaseDir, 'eldrin-app.manifest.json');\n\n if (!existsSync(manifestPath)) {\n return null;\n }\n\n const content = await readFile(manifestPath, 'utf-8');\n const manifest = JSON.parse(content);\n\n return {\n appId: manifest.id,\n version: manifest.version,\n };\n}\n\n/**\n * Parse command line arguments\n */\nfunction parseArgs(): SubmitOptions {\n const args = process.argv.slice(2);\n const options: SubmitOptions = {\n releaseDir: '',\n };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n const nextArg = args[i + 1];\n\n switch (arg) {\n case '--release-dir':\n case '-d':\n options.releaseDir = nextArg || '';\n i++;\n break;\n case '--api-key':\n case '-k':\n options.apiKey = nextArg;\n i++;\n break;\n case '--developer-id':\n options.developerId = nextArg;\n i++;\n break;\n case '--marketplace-url':\n case '-u':\n options.marketplaceUrl = nextArg;\n i++;\n break;\n case '--description':\n case '-m':\n options.description = nextArg;\n i++;\n break;\n case '--dry-run':\n options.dryRun = true;\n break;\n case '--help':\n case '-h':\n printHelp();\n process.exit(0);\n break;\n default:\n if (!arg.startsWith('-') && !options.releaseDir) {\n options.releaseDir = arg;\n }\n }\n }\n\n return options;\n}\n\n/**\n * Print help message\n */\nfunction printHelp(): void {\n console.log(`\nEldrin App Submission Tool\n\nUsage:\n npx eldrin-submit [options] <release-dir>\n\nOptions:\n -d, --release-dir <dir> Release directory to submit\n -k, --api-key <key> Developer API key (or set ELDRIN_API_KEY env var)\n --developer-id <id> Developer ID (or set ELDRIN_DEVELOPER_ID env var)\n -u, --marketplace-url <url> Marketplace URL (default: https://eldrin.io)\n -m, --description <text> PR description\n --dry-run Show what would be submitted without sending\n -h, --help Show this help message\n\nEnvironment Variables:\n ELDRIN_API_KEY Developer API key\n ELDRIN_DEVELOPER_ID Developer ID\n ELDRIN_MARKETPLACE_URL Marketplace API URL\n\nExample:\n npx eldrin-submit -d ./dist/release/eldrin.io/invoicing/v0.0.1\n\nConfig File (.eldrinrc.json):\n {\n \"developerId\": \"your-developer-id\",\n \"marketplaceUrl\": \"https://eldrin.io\"\n }\n`);\n}\n\n// ============================================================================\n// Main\n// ============================================================================\n\nexport async function submitApp(options: SubmitOptions): Promise<SubmitAppResponse> {\n const { releaseDir, dryRun } = options;\n\n // 1. Validate release directory\n if (!releaseDir) {\n return { success: false, error: 'Release directory is required' };\n }\n\n const absoluteReleaseDir = resolve(process.cwd(), releaseDir);\n\n if (!existsSync(absoluteReleaseDir)) {\n return { success: false, error: `Release directory not found: ${absoluteReleaseDir}` };\n }\n\n // 2. Load config\n const projectDir = dirname(dirname(absoluteReleaseDir)); // Go up from release dir\n const config = await loadConfig(projectDir);\n\n // 3. Resolve options\n const apiKey = options.apiKey || process.env.ELDRIN_API_KEY;\n const developerId = options.developerId || process.env.ELDRIN_DEVELOPER_ID || config.developerId;\n const marketplaceUrl =\n options.marketplaceUrl ||\n process.env.ELDRIN_MARKETPLACE_URL ||\n config.marketplaceUrl ||\n DEFAULT_MARKETPLACE_URL;\n\n if (!apiKey) {\n return { success: false, error: 'API key is required. Set ELDRIN_API_KEY or use --api-key' };\n }\n\n if (!developerId) {\n return {\n success: false,\n error: 'Developer ID is required. Set ELDRIN_DEVELOPER_ID or use --developer-id',\n };\n }\n\n // 4. Extract app info from manifest\n const appInfo = await extractAppInfo(absoluteReleaseDir);\n if (!appInfo) {\n return { success: false, error: 'Could not find eldrin-app.manifest.json in release directory' };\n }\n\n console.log(`\\n📦 Submitting ${appInfo.appId} v${appInfo.version}\\n`);\n\n // 5. Read all files (version folder + migrations at app level)\n console.log('📂 Reading release files...');\n\n // Read version folder files\n const versionFiles = await readFilesRecursively(absoluteReleaseDir);\n\n // Check for migrations at app level (parent of version folder)\n const appDir = dirname(absoluteReleaseDir);\n const migrationsDir = resolve(appDir, 'migrations');\n let migrationFiles: SubmissionFile[] = [];\n\n if (existsSync(migrationsDir)) {\n const rawMigrationFiles = await readFilesRecursively(migrationsDir);\n // Prefix paths with 'migrations/'\n migrationFiles = rawMigrationFiles.map(f => ({\n ...f,\n path: `migrations/${f.path}`,\n }));\n console.log(` Found ${migrationFiles.length} migration file(s)`);\n }\n\n // Check for versions.json at app level\n const versionsPath = resolve(appDir, 'versions.json');\n let versionsFile: SubmissionFile[] = [];\n\n if (existsSync(versionsPath)) {\n const content = await readFile(versionsPath, 'utf-8');\n versionsFile = [{\n path: 'versions.json',\n content,\n encoding: 'utf-8',\n }];\n console.log(' Found versions.json');\n }\n\n const files = [...versionFiles, ...migrationFiles, ...versionsFile];\n console.log(` Total: ${files.length} file(s)`);\n\n // 6. Dry run - just show what would be submitted\n if (dryRun) {\n console.log('\\n🔍 Dry run - files that would be submitted:');\n for (const file of files) {\n const size = Buffer.byteLength(file.content, file.encoding === 'base64' ? 'base64' : 'utf-8');\n console.log(` • ${file.path} (${(size / 1024).toFixed(1)} KB)`);\n }\n console.log('\\n✅ Dry run complete. No files were submitted.');\n return { success: true };\n }\n\n // 7. Prepare request\n const request: SubmitAppRequest = {\n apiKey,\n developerId,\n appId: appInfo.appId,\n version: appInfo.version,\n files,\n description: options.description,\n };\n\n // 8. Submit to marketplace\n console.log(`📤 Submitting to ${marketplaceUrl}...`);\n\n try {\n const response = await fetch(`${marketplaceUrl}/api/apps/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(request),\n });\n\n const result: SubmitAppResponse = await response.json();\n\n if (!result.success) {\n console.error(`\\n❌ Submission failed: ${result.error}`);\n return result;\n }\n\n console.log('\\n✅ Submission successful!\\n');\n console.log(`📋 Pull Request: ${result.prUrl}`);\n console.log(`🌿 Branch: ${result.branch}`);\n console.log('\\n📋 Next steps:');\n console.log(' 1. Review the PR in GitHub');\n console.log(' 2. Wait for automated checks to pass');\n console.log(' 3. Eldrin team will review and merge');\n console.log('');\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error';\n console.error(`\\n❌ Submission failed: ${message}`);\n return { success: false, error: message };\n }\n}\n\n// CLI entry point\nasync function main(): Promise<void> {\n const options = parseArgs();\n\n if (!options.releaseDir) {\n console.error('Error: Release directory is required');\n console.log('Run with --help for usage information');\n process.exit(1);\n }\n\n const result = await submitApp(options);\n process.exit(result.success ? 0 : 1);\n}\n\n// Run if called directly\nmain().catch((error) => {\n console.error('Error:', error);\n process.exit(1);\n});\n"]}
|