Version 1.4.1

This commit is contained in:
2026-02-02 07:14:25 +01:00
parent 25d4668661
commit 85a67867c9
4 changed files with 115 additions and 69 deletions

View File

@@ -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`) 1. **Enter a JMESPath expression** in the top input field (e.g., `people[*].name`)
2. **Add JSON data** using one of these methods: 2. **Add JSON data** using one of these methods:
- **Load an Object**: Click "📄 Load an Object" to upload standard JSON files (.json) - **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 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 - **Paste or type**: Enter JSON data directly in the bottom-left textarea
- **Load sample**: Use the "Load Sample" button for quick testing - **Load sample**: Use the "Load Sample" button for quick testing
3. **View the results** in the bottom-right output area 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: 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: 2. **Upload Data**: Use curl or any HTTP client to upload JSON data:
```bash ```bash
curl -X POST \ curl -X POST \

View File

@@ -1,6 +1,6 @@
{ {
"name": "jmespath-playground", "name": "jmespath-playground",
"version": "1.4.0", "version": "1.4.1",
"description": "A React-based web application for testing JMESPath expressions against JSON data", "description": "A React-based web application for testing JMESPath expressions against JSON data",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@@ -4,12 +4,43 @@ import { execSync } from 'node:child_process';
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; 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() { function showUsage() {
console.log('Usage: node scripts/new-version.mjs <version> [--force] [-m|--message "commit message"]'); console.log('Usage: node scripts/new-version.mjs <version> [--force] [-m|--message "commit message"]');
console.log(' node scripts/new-version.mjs --check <version>'); console.log(' node scripts/new-version.mjs --check <version>');
console.log(''); console.log('');
console.log('Creates a new version by tagging the current commit.'); 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('');
console.log('Options:'); console.log('Options:');
console.log(' --force Force version creation even with dirty repo or package.json mismatch'); console.log(' --force Force version creation even with dirty repo or package.json mismatch');
@@ -24,7 +55,7 @@ function showUsage() {
} }
function performCheck(targetVersion) { function performCheck(targetVersion) {
console.log('🔍 Repository Analysis Report'); console.log('Repository Analysis Report');
console.log('============================'); console.log('============================');
try { try {
@@ -33,7 +64,7 @@ function performCheck(targetVersion) {
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8')); const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
const currentVersion = pkg.version; const currentVersion = pkg.version;
console.log(`📦 Package.json version: ${currentVersion}`); console.log(`Package.json version: ${currentVersion}`);
// Check repository status // Check repository status
let isRepoDirty = false; let isRepoDirty = false;
@@ -43,71 +74,71 @@ function performCheck(targetVersion) {
isRepoDirty = status.trim() !== ''; isRepoDirty = status.trim() !== '';
dirtyFiles = status.trim(); dirtyFiles = status.trim();
} catch (error) { } catch (error) {
console.log('⚠️ Cannot determine git status'); console.log('Warning: Cannot determine git status');
} }
if (isRepoDirty) { if (isRepoDirty) {
console.log('🔄 Repository status: DIRTY'); console.log('Repository status: DIRTY');
console.log(' Uncommitted changes:'); console.log(' Uncommitted changes:');
dirtyFiles.split('\n').forEach(line => { dirtyFiles.split('\n').forEach(line => {
if (line.trim()) console.log(` ${line}`); if (line.trim()) console.log(` ${line}`);
}); });
} else { } else {
console.log('Repository status: CLEAN'); console.log('Repository status: CLEAN');
} }
// Check current commit info // Check current commit info
try { try {
const currentCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); const currentCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
const currentBranch = execSync('git rev-parse --abbrev-ref 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 // Check if current commit is tagged
const tagsOnHead = execSync('git tag --points-at HEAD', { encoding: 'utf8' }).trim(); const tagsOnHead = execSync('git tag --points-at HEAD', { encoding: 'utf8' }).trim();
if (tagsOnHead) { if (tagsOnHead) {
console.log(`🏷️ Current commit tags: ${tagsOnHead.split('\n').join(', ')}`); console.log(`Current commit tags: ${tagsOnHead.split('\n').join(', ')}`);
} else { } else {
console.log('🏷️ Current commit: No tags'); console.log('Current commit: No tags');
} }
} catch (error) { } catch (error) {
console.log('⚠️ Cannot determine commit info'); console.log('Warning: Cannot determine commit info');
} }
// List recent tags // List recent tags
try { try {
const recentTags = execSync('git tag --sort=-version:refname | head -5', { encoding: 'utf8' }).trim(); const recentTags = execSync('git tag --sort=-version:refname | head -5', { encoding: 'utf8' }).trim();
if (recentTags) { if (recentTags) {
console.log('📋 Recent tags:'); console.log('Recent tags:');
recentTags.split('\n').forEach(tag => { recentTags.split('\n').forEach(tag => {
if (tag.trim()) console.log(` ${tag}`); if (tag.trim()) console.log(` ${tag}`);
}); });
} else { } else {
console.log('📋 No tags found in repository'); console.log('No tags found in repository');
} }
} catch (error) { } catch (error) {
console.log('⚠️ Cannot list tags'); console.log('Warning: Cannot list tags');
} }
console.log(''); console.log('');
// Analysis for target version (if provided) // Analysis for target version (if provided)
if (targetVersion) { if (targetVersion) {
const tagName = `v${targetVersion}`; const tagName = targetVersion;
console.log(`🎯 Analysis for version ${targetVersion}:`); console.log(`Analysis for version ${targetVersion}:`);
console.log('====================================='); console.log('=====================================');
// Check if target tag exists // Check if target tag exists
try { try {
const existingTags = execSync('git tag -l', { encoding: 'utf8' }); const existingTags = execSync('git tag -l', { encoding: 'utf8' });
const tagExists = existingTags.split('\n').includes(tagName); const matchingTag = findMatchingTag(existingTags, targetVersion);
if (tagExists) { if (matchingTag) {
console.log(` Tag '${tagName}' already exists - CANNOT CREATE`); console.log(`Error: Tag '${matchingTag}' already exists - CANNOT CREATE`);
return; return;
} }
console.log(`Tag '${tagName}' available`); console.log(`Tag '${tagName}' available`);
} catch (error) { } catch (error) {
console.log('⚠️ Cannot check tag availability'); console.log('Warning: Cannot check tag availability');
return; return;
} }
@@ -116,20 +147,20 @@ function performCheck(targetVersion) {
const needsPackageUpdate = !packageJsonMatches; const needsPackageUpdate = !packageJsonMatches;
const needsCommit = isRepoDirty || needsPackageUpdate; 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) { if (needsCommit) {
console.log('Actions needed:'); console.log('Actions needed:');
if (needsPackageUpdate) { if (needsPackageUpdate) {
console.log(' Update package.json'); console.log(' - Update package.json');
} }
if (isRepoDirty) { if (isRepoDirty) {
console.log(' Stage uncommitted changes'); console.log(' - Stage uncommitted changes');
} }
console.log(' Create commit'); console.log(' - Create commit');
console.log(` Create tag ${tagName}`); console.log(` - Create tag ${tagName}`);
console.log(''); console.log('');
console.log('📋 Commands that would work:'); console.log('Commands that would work:');
if (isRepoDirty || needsPackageUpdate) { if (isRepoDirty || needsPackageUpdate) {
console.log(` node scripts/new-version.mjs ${targetVersion} --force`); console.log(` node scripts/new-version.mjs ${targetVersion} --force`);
} else { } else {
@@ -137,25 +168,25 @@ function performCheck(targetVersion) {
console.log(` node scripts/new-version.mjs ${targetVersion} --force`); console.log(` node scripts/new-version.mjs ${targetVersion} --force`);
} }
} else { } else {
console.log('Actions needed:'); console.log('Actions needed:');
console.log(` Create tag ${tagName} (no commit needed)`); console.log(` - Create tag ${tagName} (no commit needed)`);
console.log(''); 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}`);
console.log(` node scripts/new-version.mjs ${targetVersion} --force`); console.log(` node scripts/new-version.mjs ${targetVersion} --force`);
} }
console.log(''); console.log('');
console.log('🚦 Default mode requirements:'); console.log('Default mode requirements:');
if (isRepoDirty) { if (isRepoDirty) {
console.log(' Repository must be clean (currently dirty)'); console.log(' Repository must be clean (currently dirty)');
} else { } else {
console.log(' Repository is clean'); console.log(' Repository is clean');
} }
if (!packageJsonMatches) { if (!packageJsonMatches) {
console.log(` Package.json must match version (currently ${currentVersion})`); console.log(` Package.json must match version (currently ${currentVersion})`);
} else { } else {
console.log(' Package.json version matches'); console.log(' Package.json version matches');
} }
} else { } else {
@@ -165,7 +196,7 @@ function performCheck(targetVersion) {
} }
} catch (error) { } catch (error) {
console.error('Error during analysis:', error.message); console.error('Error during analysis:', error.message);
process.exit(1); process.exit(1);
} }
} }
@@ -202,28 +233,43 @@ function main() {
// For normal operation, version is required // For normal operation, version is required
newVersion = args.find(arg => !arg.startsWith('--') && arg !== '-m' && arg !== customMessage); newVersion = args.find(arg => !arg.startsWith('--') && arg !== '-m' && arg !== customMessage);
if (!newVersion) { 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) { if (isCheck) {
performCheck(newVersion); performCheck(normalizedVersion);
return; 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 { try {
// 1. Check if tag already exists - Always ERROR // 1. Check if tag already exists - Always ERROR
try { try {
const existingTags = execSync('git tag -l', { encoding: 'utf8' }); const existingTags = execSync('git tag -l', { encoding: 'utf8' });
if (existingTags.split('\n').includes(tagName)) { const matchingTag = findMatchingTag(existingTags, normalizedVersion);
console.error(`❌ Error: Tag '${tagName}' already exists`); if (matchingTag) {
console.error(`Error: Tag '${matchingTag}' already exists`);
process.exit(1); process.exit(1);
} }
} catch (error) { } catch (error) {
console.error('Error: Failed to check existing tags'); console.error('Error: Failed to check existing tags');
process.exit(1); process.exit(1);
} }
@@ -233,7 +279,7 @@ function main() {
const status = execSync('git status --porcelain', { encoding: 'utf8' }); const status = execSync('git status --porcelain', { encoding: 'utf8' });
isRepoDirty = status.trim() !== ''; isRepoDirty = status.trim() !== '';
} catch (error) { } catch (error) {
console.error('Error: Failed to check git status'); console.error('Error: Failed to check git status');
process.exit(1); process.exit(1);
} }
@@ -241,7 +287,7 @@ function main() {
const packagePath = './package.json'; const packagePath = './package.json';
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8')); const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
const currentVersion = pkg.version; const currentVersion = pkg.version;
const packageJsonMatches = currentVersion === newVersion; const packageJsonMatches = currentVersion === normalizedVersion;
// 4. Determine what action is needed // 4. Determine what action is needed
const needsPackageUpdate = !packageJsonMatches; const needsPackageUpdate = !packageJsonMatches;
@@ -250,12 +296,12 @@ function main() {
// 5. Check if force is required // 5. Check if force is required
if (!isForce) { if (!isForce) {
if (isRepoDirty) { 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'); console.error('Please commit your changes first or use --force');
process.exit(1); process.exit(1);
} }
if (needsPackageUpdate) { 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'); console.error('Use --force to update package.json');
process.exit(1); process.exit(1);
} }
@@ -263,38 +309,38 @@ function main() {
// 6. Execute the versioning // 6. Execute the versioning
if (needsCommit) { 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 // Update package.json if needed
if (needsPackageUpdate) { if (needsPackageUpdate) {
pkg.version = newVersion; pkg.version = normalizedVersion;
fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + '\n'); 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 // Stage all changes
execSync('git add .', { stdio: 'inherit' }); execSync('git add .', { stdio: 'inherit' });
// Commit // 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' }); execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
console.log(`Committed changes`); console.log('Committed changes');
} else { } 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 // 7. Tag the commit
execSync(`git tag ${tagName}`, { stdio: 'inherit' }); execSync(`git tag ${tagName}`, { stdio: 'inherit' });
console.log(`🏷️ Created tag: ${tagName}`); console.log(`Created tag: ${tagName}`);
console.log(''); console.log('');
console.log('🎉 Version created successfully!'); console.log('Version created successfully!');
console.log(''); console.log('');
console.log('Next steps:'); console.log('Next steps:');
console.log(` git push origin main --tags # Push the commit and tag`); console.log(` git push origin main --tags # Push the commit and tag`);
} catch (error) { } catch (error) {
console.error('Error during version creation:', error.message); console.error('Error during version creation:', error.message);
process.exit(1); process.exit(1);
} }
} }

View File

@@ -29,7 +29,7 @@ function encrypt(data, key) {
tag: authTag.toString("hex"), tag: authTag.toString("hex"),
}; };
} catch (error) { } catch (error) {
console.error("⚠️ Encryption exception:", { console.error("Encryption exception:", {
message: error.message, message: error.message,
algorithm: "aes-256-gcm", algorithm: "aes-256-gcm",
keyLength: key ? key.length : "undefined", keyLength: key ? key.length : "undefined",
@@ -56,7 +56,7 @@ function decrypt(encryptedObj, key) {
return JSON.parse(decrypted); return JSON.parse(decrypted);
} catch (error) { } catch (error) {
console.error("⚠️ Decryption exception:", { console.error("Decryption exception:", {
message: error.message, message: error.message,
algorithm: "aes-256-gcm", algorithm: "aes-256-gcm",
keyLength: key ? key.length : "undefined", keyLength: key ? key.length : "undefined",
@@ -100,7 +100,7 @@ function createApp(devMode = false) {
if (devMode) { if (devMode) {
app.use((req, res, next) => { app.use((req, res, next) => {
const timestamp = new Date().toISOString(); 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) { if (req.method !== "GET" && Object.keys(req.body).length > 0) {
const bodySize = Buffer.byteLength(JSON.stringify(req.body), "utf8"); const bodySize = Buffer.byteLength(JSON.stringify(req.body), "utf8");
console.log(` Request body size: ${(bodySize / 1024).toFixed(2)}KB`); console.log(` Request body size: ${(bodySize / 1024).toFixed(2)}KB`);
@@ -108,7 +108,7 @@ function createApp(devMode = false) {
const originalJson = res.json; const originalJson = res.json;
res.json = function (data) { res.json = function (data) {
console.log(` Response: ${res.statusCode}`); console.log(` Response: ${res.statusCode}`);
return originalJson.call(this, data); return originalJson.call(this, data);
}; };
next(); next();
@@ -125,7 +125,7 @@ function createApp(devMode = false) {
if (now - session.createdAt > MAX_SESSION_TTL) { if (now - session.createdAt > MAX_SESSION_TTL) {
sessions.delete(sessionId); sessions.delete(sessionId);
console.log( 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( 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" }); res.json({ message: "OK" });
} catch (error) { } catch (error) {
console.error("⚠️ Upload endpoint exception occurred:", { console.error("Upload endpoint exception occurred:", {
message: error.message, message: error.message,
stack: error.stack, stack: error.stack,
sessionCount: sessions.size, sessionCount: sessions.size,
@@ -257,12 +257,12 @@ function createApp(devMode = false) {
// Remove session after first access (one-time use) // Remove session after first access (one-time use)
sessions.delete(sessionId); sessions.delete(sessionId);
console.log( 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); res.json(decryptedData);
} catch (error) { } catch (error) {
console.error("⚠️ Sample retrieval exception occurred:", { console.error("Sample retrieval exception occurred:", {
message: error.message, message: error.message,
stack: error.stack, stack: error.stack,
sessionCount: sessions.size, sessionCount: sessions.size,
@@ -318,7 +318,7 @@ function createApp(devMode = false) {
res.json({ state: session.state }); res.json({ state: session.state });
} catch (error) { } catch (error) {
console.error("⚠️ State retrieval exception occurred:", { console.error("State retrieval exception occurred:", {
message: error.message, message: error.message,
stack: error.stack, stack: error.stack,
sessionCount: sessions.size, sessionCount: sessions.size,
@@ -402,7 +402,7 @@ if (require.main === module) {
app.listen(PORT, HOST, () => { app.listen(PORT, HOST, () => {
console.log(`JMESPath Playground Server running`); console.log(`JMESPath Playground Server running`);
if (DEV_MODE) { if (DEV_MODE) {
console.log(` 🔧 Development Mode Enabled`); console.log(" Development Mode Enabled");
} }
// Show actual accessible URLs // Show actual accessible URLs