From d0961c68fa5ede4281191b34c6de4f4d4fa7e90a Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Mon, 26 Jan 2026 18:38:47 +0100 Subject: [PATCH] Version 1.2.5 --- README.md | 10 ++ package-lock.json | 260 +++++++++++++++++++++++++---------- package.json | 5 +- scripts/build-image.js | 4 +- server.js | 30 +++- src/components/ApiKeyPage.js | 30 ++-- 6 files changed, 241 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index ae92d79..9b21d3b 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/package-lock.json b/package-lock.json index 0d14366..b4c3a2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 06a55be..b40faf4 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/scripts/build-image.js b/scripts/build-image.js index 48d8b89..c76876e 100644 --- a/scripts/build-image.js +++ b/scripts/build-image.js @@ -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`); } } diff --git a/server.js b/server.js index b79be5f..1a3beb3 100644 --- a/server.js +++ b/server.js @@ -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') { diff --git a/src/components/ApiKeyPage.js b/src/components/ApiKeyPage.js index bd3d885..751ba36 100644 --- a/src/components/ApiKeyPage.js +++ b/src/components/ApiKeyPage.js @@ -64,34 +64,28 @@ function ApiKeyPage({ apiKey, onRegenerateApiKey }) {
📡 Remote Data Upload API

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 .bashrc.

+              export JMESPATH_PLAYGROUND_API_URL={window.location.origin}
export JMESPATH_PLAYGROUND_API_KEY={apiKey}
+
+

Then, use the following curl command to upload your data:

+
 {`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"`}
+    -H "X-API-Key: $JMESPATH_PLAYGROUND_API_KEY" \\
+    --data @__JSON_FILE_NAME__ \\
+    "$\{JMESPATH_PLAYGROUND_API_URL}/api/v1/upload"`}
               
- Replace {'{{JSON_FILE_NAME}}'} with the path to your JSON file containing the sample data. + Replace {'__JSON_FILE_NAME__'} with the path to your JSON file containing the sample data. + or use - to read from standard input.
- For localhost clients: The X-API-Key header is optional and can be omitted. + For localhost clients: The X-API-Key should be omitted.
- -
-
â„šī¸ How it works:
- -