diff --git a/README.md b/README.md index 5856547..6ae17da 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ container run -p 3000:3000 jmespath-playground 1. **Enter a JMESPath expression** in the top input field (e.g., `people[*].name`) 2. **Add JSON data** using one of these methods: - - **Load an Object**: Click "๐Ÿ“„ Load an Object" to upload standard JSON files (.json) - - **Load a Log File**: Click "๐Ÿ“‹ Load a Log File" to upload JSON Lines files (.log) - each line converted to array + - **Load an Object**: Click "Load an Object" to upload standard JSON files (.json) + - **Load a Log File**: Click "Load a Log File" to upload JSON Lines files (.log) - each line converted to array - **Paste or type**: Enter JSON data directly in the bottom-left textarea - **Load sample**: Use the "Load Sample" button for quick testing 3. **View the results** in the bottom-right output area @@ -87,7 +87,7 @@ container run -p 3000:3000 jmespath-playground The application includes a REST API for uploading sample data remotely: -1. **Access API Key**: Click the key-lock button (๐Ÿ”’) to view your unique API key +1. **Access API Key**: Click the key-lock button to view your unique API key 2. **Upload Data**: Use curl or any HTTP client to upload JSON data: ```bash curl -X POST \ diff --git a/package.json b/package.json index c7fe3f5..b61268b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jmespath-playground", - "version": "1.4.0", + "version": "1.4.1", "description": "A React-based web application for testing JMESPath expressions against JSON data", "main": "index.js", "scripts": { diff --git a/scripts/new-version.mjs b/scripts/new-version.mjs index bdd1b30..bef2686 100755 --- a/scripts/new-version.mjs +++ b/scripts/new-version.mjs @@ -4,12 +4,43 @@ import { execSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import semver from 'semver'; + +function tagMatchesVersion(tag, version) { + if (!tag) { + return false; + } + if (tag === version) { + return true; + } + if (tag.startsWith('v')) { + return tag.slice(1) === version; + } + return false; +} + +function hasMatchingTag(tagsOutput, version) { + return tagsOutput + .split('\n') + .map(tag => tag.trim()) + .filter(Boolean) + .some(tag => tagMatchesVersion(tag, version)); +} + +function findMatchingTag(tagsOutput, version) { + return tagsOutput + .split('\n') + .map(tag => tag.trim()) + .filter(Boolean) + .find(tag => tagMatchesVersion(tag, version)) || null; +} function showUsage() { console.log('Usage: node scripts/new-version.mjs [--force] [-m|--message "commit message"]'); console.log(' node scripts/new-version.mjs --check '); console.log(''); console.log('Creates a new version by tagging the current commit.'); + console.log('Version must be valid semver (e.g., 1.2.3).'); console.log(''); console.log('Options:'); console.log(' --force Force version creation even with dirty repo or package.json mismatch'); @@ -24,7 +55,7 @@ function showUsage() { } function performCheck(targetVersion) { - console.log('๐Ÿ” Repository Analysis Report'); + console.log('Repository Analysis Report'); console.log('============================'); try { @@ -33,7 +64,7 @@ function performCheck(targetVersion) { const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8')); const currentVersion = pkg.version; - console.log(`๐Ÿ“ฆ Package.json version: ${currentVersion}`); + console.log(`Package.json version: ${currentVersion}`); // Check repository status let isRepoDirty = false; @@ -43,71 +74,71 @@ function performCheck(targetVersion) { isRepoDirty = status.trim() !== ''; dirtyFiles = status.trim(); } catch (error) { - console.log('โš ๏ธ Cannot determine git status'); + console.log('Warning: Cannot determine git status'); } if (isRepoDirty) { - console.log('๐Ÿ”„ Repository status: DIRTY'); + console.log('Repository status: DIRTY'); console.log(' Uncommitted changes:'); dirtyFiles.split('\n').forEach(line => { if (line.trim()) console.log(` ${line}`); }); } else { - console.log('โœ… Repository status: CLEAN'); + console.log('Repository status: CLEAN'); } // Check current commit info try { const currentCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim(); - console.log(`๐ŸŒŸ Current commit: ${currentCommit.substring(0, 7)} (${currentBranch})`); + console.log(`Current commit: ${currentCommit.substring(0, 7)} (${currentBranch})`); // Check if current commit is tagged const tagsOnHead = execSync('git tag --points-at HEAD', { encoding: 'utf8' }).trim(); if (tagsOnHead) { - console.log(`๐Ÿท๏ธ Current commit tags: ${tagsOnHead.split('\n').join(', ')}`); + console.log(`Current commit tags: ${tagsOnHead.split('\n').join(', ')}`); } else { - console.log('๐Ÿท๏ธ Current commit: No tags'); + console.log('Current commit: No tags'); } } catch (error) { - console.log('โš ๏ธ Cannot determine commit info'); + console.log('Warning: Cannot determine commit info'); } // List recent tags try { const recentTags = execSync('git tag --sort=-version:refname | head -5', { encoding: 'utf8' }).trim(); if (recentTags) { - console.log('๐Ÿ“‹ Recent tags:'); + console.log('Recent tags:'); recentTags.split('\n').forEach(tag => { if (tag.trim()) console.log(` ${tag}`); }); } else { - console.log('๐Ÿ“‹ No tags found in repository'); + console.log('No tags found in repository'); } } catch (error) { - console.log('โš ๏ธ Cannot list tags'); + console.log('Warning: Cannot list tags'); } console.log(''); // Analysis for target version (if provided) if (targetVersion) { - const tagName = `v${targetVersion}`; - console.log(`๐ŸŽฏ Analysis for version ${targetVersion}:`); + const tagName = targetVersion; + console.log(`Analysis for version ${targetVersion}:`); console.log('====================================='); // Check if target tag exists try { const existingTags = execSync('git tag -l', { encoding: 'utf8' }); - const tagExists = existingTags.split('\n').includes(tagName); + const matchingTag = findMatchingTag(existingTags, targetVersion); - if (tagExists) { - console.log(`โŒ Tag '${tagName}' already exists - CANNOT CREATE`); + if (matchingTag) { + console.log(`Error: Tag '${matchingTag}' already exists - CANNOT CREATE`); return; } - console.log(`โœ… Tag '${tagName}' available`); + console.log(`Tag '${tagName}' available`); } catch (error) { - console.log('โš ๏ธ Cannot check tag availability'); + console.log('Warning: Cannot check tag availability'); return; } @@ -116,20 +147,20 @@ function performCheck(targetVersion) { const needsPackageUpdate = !packageJsonMatches; const needsCommit = isRepoDirty || needsPackageUpdate; - console.log(`๐Ÿ“ Package.json: ${packageJsonMatches ? 'MATCHES' : `NEEDS UPDATE (${currentVersion} โ†’ ${targetVersion})`}`); + console.log(`Package.json: ${packageJsonMatches ? 'MATCHES' : `NEEDS UPDATE (${currentVersion} -> ${targetVersion})`}`); if (needsCommit) { - console.log('โšก Actions needed:'); + console.log('Actions needed:'); if (needsPackageUpdate) { - console.log(' โ€ข Update package.json'); + console.log(' - Update package.json'); } if (isRepoDirty) { - console.log(' โ€ข Stage uncommitted changes'); + console.log(' - Stage uncommitted changes'); } - console.log(' โ€ข Create commit'); - console.log(` โ€ข Create tag ${tagName}`); + console.log(' - Create commit'); + console.log(` - Create tag ${tagName}`); console.log(''); - console.log('๐Ÿ“‹ Commands that would work:'); + console.log('Commands that would work:'); if (isRepoDirty || needsPackageUpdate) { console.log(` node scripts/new-version.mjs ${targetVersion} --force`); } else { @@ -137,25 +168,25 @@ function performCheck(targetVersion) { console.log(` node scripts/new-version.mjs ${targetVersion} --force`); } } else { - console.log('โšก Actions needed:'); - console.log(` โ€ข Create tag ${tagName} (no commit needed)`); + console.log('Actions needed:'); + console.log(` - Create tag ${tagName} (no commit needed)`); console.log(''); - console.log('๐Ÿ“‹ Commands that would work:'); + console.log('Commands that would work:'); console.log(` node scripts/new-version.mjs ${targetVersion}`); console.log(` node scripts/new-version.mjs ${targetVersion} --force`); } console.log(''); - console.log('๐Ÿšฆ Default mode requirements:'); + console.log('Default mode requirements:'); if (isRepoDirty) { - console.log(' โŒ Repository must be clean (currently dirty)'); + console.log(' Repository must be clean (currently dirty)'); } else { - console.log(' โœ… Repository is clean'); + console.log(' Repository is clean'); } if (!packageJsonMatches) { - console.log(` โŒ Package.json must match version (currently ${currentVersion})`); + console.log(` Package.json must match version (currently ${currentVersion})`); } else { - console.log(' โœ… Package.json version matches'); + console.log(' Package.json version matches'); } } else { @@ -165,7 +196,7 @@ function performCheck(targetVersion) { } } catch (error) { - console.error('โŒ Error during analysis:', error.message); + console.error('Error during analysis:', error.message); process.exit(1); } } @@ -202,28 +233,43 @@ function main() { // For normal operation, version is required newVersion = args.find(arg => !arg.startsWith('--') && arg !== '-m' && arg !== customMessage); if (!newVersion) { + console.error('Error: Version argument required'); + showUsage(); + process.exit(1); } } + if (newVersion && newVersion.startsWith('v')) { + console.error('Error: Version must not start with "v". Use plain semver like 1.2.3.'); + process.exit(1); + } + + const normalizedVersion = newVersion; + if (!semver.valid(normalizedVersion)) { + console.error('Error: Version must be valid semver (e.g., 1.2.3)'); + process.exit(1); + } + if (isCheck) { - performCheck(newVersion); + performCheck(normalizedVersion); return; } - const tagName = `v${newVersion}`; + const tagName = normalizedVersion; - console.log(`๐Ÿท๏ธ Creating new version: ${newVersion}${isForce ? ' (forced)' : ''}`); + console.log(`Creating new version: ${normalizedVersion}${isForce ? ' (forced)' : ''}`); try { // 1. Check if tag already exists - Always ERROR try { const existingTags = execSync('git tag -l', { encoding: 'utf8' }); - if (existingTags.split('\n').includes(tagName)) { - console.error(`โŒ Error: Tag '${tagName}' already exists`); + const matchingTag = findMatchingTag(existingTags, normalizedVersion); + if (matchingTag) { + console.error(`Error: Tag '${matchingTag}' already exists`); process.exit(1); } } catch (error) { - console.error('โŒ Error: Failed to check existing tags'); + console.error('Error: Failed to check existing tags'); process.exit(1); } @@ -233,7 +279,7 @@ function main() { const status = execSync('git status --porcelain', { encoding: 'utf8' }); isRepoDirty = status.trim() !== ''; } catch (error) { - console.error('โŒ Error: Failed to check git status'); + console.error('Error: Failed to check git status'); process.exit(1); } @@ -241,7 +287,7 @@ function main() { const packagePath = './package.json'; const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8')); const currentVersion = pkg.version; - const packageJsonMatches = currentVersion === newVersion; + const packageJsonMatches = currentVersion === normalizedVersion; // 4. Determine what action is needed const needsPackageUpdate = !packageJsonMatches; @@ -250,12 +296,12 @@ function main() { // 5. Check if force is required if (!isForce) { if (isRepoDirty) { - console.error('โŒ Error: Working directory has uncommitted changes'); + console.error('Error: Working directory has uncommitted changes'); console.error('Please commit your changes first or use --force'); process.exit(1); } if (needsPackageUpdate) { - console.error(`โŒ Error: Package.json version is ${currentVersion}, requested ${newVersion}`); + console.error(`Error: Package.json version is ${currentVersion}, requested ${normalizedVersion}`); console.error('Use --force to update package.json'); process.exit(1); } @@ -263,38 +309,38 @@ function main() { // 6. Execute the versioning if (needsCommit) { - console.log(`๐Ÿ“ฆ Needs commit: ${needsPackageUpdate ? 'package.json update' : ''}${needsPackageUpdate && isRepoDirty ? ' + ' : ''}${isRepoDirty ? 'uncommitted changes' : ''}`); + console.log(`Needs commit: ${needsPackageUpdate ? 'package.json update' : ''}${needsPackageUpdate && isRepoDirty ? ' + ' : ''}${isRepoDirty ? 'uncommitted changes' : ''}`); // Update package.json if needed if (needsPackageUpdate) { - pkg.version = newVersion; + pkg.version = normalizedVersion; fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + '\n'); - console.log(`๐Ÿ“ Updated package.json: ${currentVersion} โ†’ ${newVersion}`); + console.log(`Updated package.json: ${currentVersion} -> ${normalizedVersion}`); } // Stage all changes execSync('git add .', { stdio: 'inherit' }); // Commit - const commitMessage = customMessage || (needsPackageUpdate ? `Version ${newVersion}` : `Prepare for version ${newVersion}`); + const commitMessage = customMessage || (needsPackageUpdate ? `Version ${normalizedVersion}` : `Prepare for version ${normalizedVersion}`); execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' }); - console.log(`โœ… Committed changes`); + console.log('Committed changes'); } else { - console.log(`โœ… Repository clean, package.json matches - tagging current commit`); + console.log('Repository clean, package.json matches - tagging current commit'); } // 7. Tag the commit execSync(`git tag ${tagName}`, { stdio: 'inherit' }); - console.log(`๐Ÿท๏ธ Created tag: ${tagName}`); + console.log(`Created tag: ${tagName}`); console.log(''); - console.log('๐ŸŽ‰ Version created successfully!'); + console.log('Version created successfully!'); console.log(''); console.log('Next steps:'); console.log(` git push origin main --tags # Push the commit and tag`); } catch (error) { - console.error('โŒ Error during version creation:', error.message); + console.error('Error during version creation:', error.message); process.exit(1); } } diff --git a/server.js b/server.js index 7af76a1..df55370 100644 --- a/server.js +++ b/server.js @@ -29,7 +29,7 @@ function encrypt(data, key) { tag: authTag.toString("hex"), }; } catch (error) { - console.error("โš ๏ธ Encryption exception:", { + console.error("Encryption exception:", { message: error.message, algorithm: "aes-256-gcm", keyLength: key ? key.length : "undefined", @@ -56,7 +56,7 @@ function decrypt(encryptedObj, key) { return JSON.parse(decrypted); } catch (error) { - console.error("โš ๏ธ Decryption exception:", { + console.error("Decryption exception:", { message: error.message, algorithm: "aes-256-gcm", keyLength: key ? key.length : "undefined", @@ -100,7 +100,7 @@ function createApp(devMode = false) { if (devMode) { app.use((req, res, next) => { const timestamp = new Date().toISOString(); - console.log(`๐Ÿ“จ [${timestamp}] ${req.method} ${req.path}`); + console.log(`[${timestamp}] ${req.method} ${req.path}`); if (req.method !== "GET" && Object.keys(req.body).length > 0) { const bodySize = Buffer.byteLength(JSON.stringify(req.body), "utf8"); console.log(` Request body size: ${(bodySize / 1024).toFixed(2)}KB`); @@ -108,7 +108,7 @@ function createApp(devMode = false) { const originalJson = res.json; res.json = function (data) { - console.log(` โœ“ Response: ${res.statusCode}`); + console.log(` Response: ${res.statusCode}`); return originalJson.call(this, data); }; next(); @@ -125,7 +125,7 @@ function createApp(devMode = false) { if (now - session.createdAt > MAX_SESSION_TTL) { sessions.delete(sessionId); console.log( - `๐Ÿงน Cleaned up expired session: ${sessionId.substring(0, 8)}...`, + `Cleaned up expired session: ${sessionId.substring(0, 8)}...`, ); } } @@ -192,12 +192,12 @@ function createApp(devMode = false) { }); console.log( - `๐Ÿ“ Session created: ${sessionId.substring(0, 8)}... (${sessions.size}/${MAX_SESSIONS})`, + `Session created: ${sessionId.substring(0, 8)}... (${sessions.size}/${MAX_SESSIONS})`, ); res.json({ message: "OK" }); } catch (error) { - console.error("โš ๏ธ Upload endpoint exception occurred:", { + console.error("Upload endpoint exception occurred:", { message: error.message, stack: error.stack, sessionCount: sessions.size, @@ -257,12 +257,12 @@ function createApp(devMode = false) { // Remove session after first access (one-time use) sessions.delete(sessionId); console.log( - `๐Ÿ“ค Sample data retrieved and session cleared: ${sessionId.substring(0, 8)}...`, + `Sample data retrieved and session cleared: ${sessionId.substring(0, 8)}...`, ); res.json(decryptedData); } catch (error) { - console.error("โš ๏ธ Sample retrieval exception occurred:", { + console.error("Sample retrieval exception occurred:", { message: error.message, stack: error.stack, sessionCount: sessions.size, @@ -318,7 +318,7 @@ function createApp(devMode = false) { res.json({ state: session.state }); } catch (error) { - console.error("โš ๏ธ State retrieval exception occurred:", { + console.error("State retrieval exception occurred:", { message: error.message, stack: error.stack, sessionCount: sessions.size, @@ -402,7 +402,7 @@ if (require.main === module) { app.listen(PORT, HOST, () => { console.log(`JMESPath Playground Server running`); if (DEV_MODE) { - console.log(` ๐Ÿ”ง Development Mode Enabled`); + console.log(" Development Mode Enabled"); } // Show actual accessible URLs