Version 1.2.5

This commit is contained in:
2026-01-26 18:38:47 +01:00
parent b1fd6da218
commit d0961c68fa
6 changed files with 241 additions and 98 deletions

View File

@@ -43,6 +43,16 @@ A React-based web application for testing and validating JMESPath expressions ag
4. **Open your browser** and navigate to `http://localhost:3000`
### Development
For development with hot reload on component changes:
```bash
npm run dev
```
This runs both the React dev server (with hot reload) and the API server concurrently. The React app will proxy API requests to the backend server.
### Container Deployment
You can optionally run the application in a container:

260
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "jmespath-playground",
"version": "1.0.4",
"version": "1.2.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jmespath-playground",
"version": "1.0.4",
"version": "1.2.4",
"license": "MIT",
"dependencies": {
"@testing-library/jest-dom": "^6.1.4",
@@ -21,6 +21,7 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"concurrently": "^8.2.2",
"supertest": "^7.2.2"
},
"engines": {
@@ -5656,58 +5657,6 @@
"wrap-ansi": "^7.0.0"
}
},
"node_modules/cliui/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/cliui/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/cliui/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -5936,6 +5885,94 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
"node_modules/concurrently": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
"integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^2.30.0",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"spawn-command": "0.0.2",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": "^14.13.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/concurrently/node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/concurrently/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/concurrently/node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/concurrently/node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/confusing-browser-globals": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
@@ -6521,6 +6558,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.21.0"
},
"engines": {
"node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -14513,6 +14567,16 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@@ -15165,6 +15229,12 @@
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
"license": "MIT"
},
"node_modules/spawn-command": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
"dev": true
},
"node_modules/spdy": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
@@ -15385,6 +15455,26 @@
"integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==",
"license": "MIT"
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -16293,6 +16383,16 @@
"node": ">=8"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"license": "MIT",
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
@@ -17546,6 +17646,38 @@
"workbox-core": "6.6.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -17648,26 +17780,6 @@
"node": ">=10"
}
},
"node_modules/yargs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/yargs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "jmespath-playground",
"version": "1.2.4",
"version": "1.2.5",
"description": "A React-based web application for testing JMESPath expressions against JSON data",
"main": "index.js",
"scripts": {
@@ -10,8 +10,10 @@
"test": "react-scripts test --watchAll=false",
"test:watch": "react-scripts test",
"server": "node server.js",
"dev": "concurrently \"npm start\" \"npm run server\"",
"build-image": "node scripts/build-image.js"
},
"proxy": "http://localhost:3000",
"engines": {
"node": ">=24.0.0"
},
@@ -61,6 +63,7 @@
"author": "",
"license": "MIT",
"devDependencies": {
"concurrently": "^8.2.2",
"supertest": "^7.2.2"
}
}

View File

@@ -72,7 +72,7 @@ function main() {
// Show usage instructions
if (isRelease) {
console.log(`\nTo run the container:`);
console.log(` ${containerTool} run -p 3000:3000 skoszewski/jmespath-playground:${version}`);
console.log(` ${containerTool} run --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:${version}`);
if (containerTool === 'docker') {
console.log(`\nTo push to Docker Hub:`);
console.log(` docker push skoszewski/jmespath-playground:${version}`);
@@ -80,7 +80,7 @@ function main() {
}
} else {
console.log(`\nTo run the container:`);
console.log(` ${containerTool} run -p 3000:3000 skoszewski/jmespath-playground:dev`);
console.log(` ${containerTool} run --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:dev`);
}
}

View File

@@ -110,7 +110,7 @@ function deriveKey(apiKey, salt) {
}
// Create Express app
function createApp() {
function createApp(devMode = false) {
const app = express();
// Trust proxy to get real client IP (needed for localhost detection)
@@ -120,6 +120,25 @@ function createApp() {
app.use(express.json({ limit: MAX_SAMPLE_SIZE }));
app.use(express.static(path.join(__dirname, 'build')));
// Dev mode request logging middleware
if (devMode) {
app.use((req, res, next) => {
const timestamp = new Date().toISOString();
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`);
}
const originalJson = res.json;
res.json = function(data) {
console.log(` ✓ Response: ${res.statusCode}`);
return originalJson.call(this, data);
};
next();
});
}
// Session storage
const sessions = new Map();
@@ -396,16 +415,21 @@ if (require.main === module) {
const { values } = parseArgs({
options: {
'listen-addr': { type: 'string', short: 'h', default: process.env.LISTEN_ADDR || '127.0.0.1' },
'port': { type: 'string', short: 'p', default: process.env.LISTEN_PORT || '3000' }
'port': { type: 'string', short: 'p', default: process.env.LISTEN_PORT || '3000' },
'dev': { type: 'boolean', default: process.env.DEV_MODE === 'true' || false }
}
});
const app = createApp();
const DEV_MODE = values.dev;
const app = createApp(DEV_MODE);
const PORT = parseInt(values.port);
const HOST = values['listen-addr'];
app.listen(PORT, HOST, () => {
console.log(`JMESPath Playground Server running`);
if (DEV_MODE) {
console.log(` 🔧 Development Mode Enabled`);
}
// Show actual accessible URLs
if (HOST === '0.0.0.0') {

View File

@@ -64,34 +64,28 @@ function ApiKeyPage({ apiKey, onRegenerateApiKey }) {
<h6>📡 Remote Data Upload API</h6>
<p className="text-muted">
External tools can upload sample data remotely using the REST API.
For remote clients, the API key is required for authentication:
For remote clients, the API key is required for authentication. Define two
environment variables in your <code>.bashrc</code>.
</p>
<pre className="bg-light p-3 rounded border">
<code>export JMESPATH_PLAYGROUND_API_URL={window.location.origin}<br/>export JMESPATH_PLAYGROUND_API_KEY={apiKey}</code>
</pre>
<p className="text-muted">Then, use the following <code>curl</code> command to upload your data:</p>
<pre className="bg-light p-3 rounded border">
<code>{`curl -s -X POST \\
-H "Content-Type: application/json" \\
-H "Accept: application/json" \\
-H "X-API-Key: ${apiKey}" \\
--data @{{JSON_FILE_NAME}} \\
"${window.location.origin}/api/v1/upload"`}</code>
-H "X-API-Key: $JMESPATH_PLAYGROUND_API_KEY" \\
--data @__JSON_FILE_NAME__ \\
"$\{JMESPATH_PLAYGROUND_API_URL}/api/v1/upload"`}</code>
</pre>
<div className="form-text">
Replace <code>{'{{JSON_FILE_NAME}}'}</code> with the path to your JSON file containing the sample data.
Replace <code>{'__JSON_FILE_NAME__'}</code> with the path to your JSON file containing the sample data.
or use <code>-</code> to read from standard input.
<br />
<strong>For localhost clients:</strong> The X-API-Key header is optional and can be omitted.
<strong>For localhost clients:</strong> The X-API-Key should be omitted.
</div>
</div>
<div className="alert alert-info">
<h6 className="alert-heading"> How it works:</h6>
<ul className="mb-0">
<li>Remote clients require API key authentication for security</li>
<li>Localhost clients (127.0.0.1) can access the API without authentication</li>
<li>Your data is encrypted using AES-256-GCM with PBKDF2 key derivation</li>
<li>Data is automatically cleared after first retrieval (one-time use)</li>
<li>Sessions expire after 1 hour for security</li>
<li>Maximum 100 concurrent sessions supported</li>
</ul>
</div>
</div>
</div>
</div>