From f020958e0042c2d474603fa99d477d07c2da4c56 Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Thu, 15 May 2025 19:14:58 -0300 Subject: [PATCH 1/6] Add no 'entry key' emoji to avoid expression evaluation --- src/MetaInfoProducer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MetaInfoProducer.ts b/src/MetaInfoProducer.ts index 9cf9db1e..c68aeb69 100644 --- a/src/MetaInfoProducer.ts +++ b/src/MetaInfoProducer.ts @@ -57,6 +57,8 @@ export default class MetaInfoProducer { const emit: MetaInfo[] = []; async function getPaths(o:any, path: JsonPointerStructureArray = [], isTemp=false) { + if (String(path.at(-1))[0] === '⛔') return; + const type = typeof o; const metaInfo: MetaInfo = { "materialized__": true, From c9e71e95d18dca2b9348764b313189def7fd73d9 Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Thu, 15 May 2025 22:35:02 -0300 Subject: [PATCH 2/6] Updating tests and README.md --- README.md | 70 ++++++++ example/ex25.json | 5 + example/ex26.json | 20 +++ src/test/MetaInfoProducer.test.js | 256 ++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 example/ex25.json create mode 100644 example/ex26.json diff --git a/README.md b/README.md index 31068c2b..5ab4713e 100644 --- a/README.md +++ b/README.md @@ -872,6 +872,76 @@ expression are also tagged. } ``` +## Blocked Properties with ⛔ +Properties whose names start with "⛔" are completely ignored during template processing. This is useful for: + +1. Adding notes or comments that should not be evaluated +2. Temporarily disabling parts of a template +3. Keeping sensitive or debug information in the template but preventing its evaluation + +When a property name starts with "⛔", the MetaInfoProducer will skip that property and all of its children, effectively making them invisible to the template processor. + +```json +> .init -f "example/ex25.json" +{ + "a": 42, + "⛔b": "${$string('should not be evaluated: ') & a}", + "c": "${a}" +} +> .out +{ + "a": 42, + "⛔b": "${$string('should not be evaluated: ') & a}", + "c": 42 +} +> .init -f "example/ex26.json" +{ + "a": 42, + "⛔note": "${$string('Will NOT be evaluated: ') & a}", + "note": "${$string('Will be evaluated: ') & a}", + "config": { + "⛔productionDb": "${$env('DB_PROD_URL', 'mysql://prod.example.com:3306')}", + "productionDb": "${$env('DB_PROD_URL', 'mysql://prod.example.com:3306')}", + "localDb": "mysql://localhost:3306" + }, + "features": { + "⛔experimental": { + "enabled": true, + "endpoint": "${$string('https://api.example.com/config/') & 'experimental'}" + }, + "experimental": { + "enabled": true, + "endpoint": "${$string('https://api.example.com/config/') & 'experimental'}" + } + } +} +> .out +{ + "a": 42, + "⛔note": "${$string('Will NOT be evaluated: ') & a}", + "note": "Will be evaluated: 42", + "config": { + "⛔productionDb": "${$env('DB_PROD_URL', 'mysql://prod.example.com:3306')}", + "productionDb": "mysql://prod.example.com:3306", + "localDb": "mysql://localhost:3306" + }, + "features": { + "⛔experimental": { + "enabled": true, + "endpoint": "${$string('https://api.example.com/config/') & 'experimental'}" + }, + "experimental": { + "enabled": true, + "endpoint": "https://api.example.com/config/experimental" + } + } +} +``` + +In these examples, `⛔...` will be completely ignored during template processing - the expression will not be evaluated. + +Note that array elements with "⛔" in their values will still be processed, as array indices are numbers, not property names. Only object properties that start with "⛔" are blocked. + # Generative Templates Templates can contain generative expressions that cause their content to change over time. For instance the `$setInterval` function behaves exactly as it does in Javascript. Below, diff --git a/example/ex25.json b/example/ex25.json new file mode 100644 index 00000000..9b022603 --- /dev/null +++ b/example/ex25.json @@ -0,0 +1,5 @@ +{ + "a": 42, + "⛔note": "${$string('should not be evaluated: ') & a}", + "c": "${a}" +} \ No newline at end of file diff --git a/example/ex26.json b/example/ex26.json new file mode 100644 index 00000000..e189abd8 --- /dev/null +++ b/example/ex26.json @@ -0,0 +1,20 @@ +{ + "a": 42, + "⛔note": "${$string('Will NOT be evaluated: ') & a}", + "note": "${$string('Will be evaluated: ') & a}", + "config": { + "⛔productionDb": "${$env('DB_PROD_URL', 'mysql://prod.example.com:3306')}", + "productionDb": "${$env('DB_PROD_URL', 'mysql://prod.example.com:3306')}", + "localDb": "mysql://localhost:3306" + }, + "features": { + "⛔experimental": { + "enabled": true, + "endpoint": "${$string('https://api.example.com/config/') & 'experimental'}" + }, + "experimental": { + "enabled": true, + "endpoint": "${$string('https://api.example.com/config/') & 'experimental'}" + } + } +} \ No newline at end of file diff --git a/src/test/MetaInfoProducer.test.js b/src/test/MetaInfoProducer.test.js index 19b3a672..e6b2c8d3 100644 --- a/src/test/MetaInfoProducer.test.js +++ b/src/test/MetaInfoProducer.test.js @@ -1117,5 +1117,261 @@ test("temp vars 3", async () => { ]); }); +test("blocked paths with ⛔ character 1", async () => { + const template = { + "a": 42, + "⛔b": "Should be ignored", + "c": "${a}" + }; + const metaInfos = await MetaInfoProducer.getMetaInfos(template); + expect(JSON.parse(stringify(metaInfos))).toEqual([ + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "a" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "exprRootPath__": null, + "exprTargetJsonPointer__": [], + "expr__": "a", + "jsonPointer__": [ + "c" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": true + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": true + } + ]); +}); + +test("blocked paths with ⛔ character 2", async () => { + const template = { + "a": 42, + "b": { + "normal": "value", + "⛔secret": { + "nested": "This should be ignored" + } + }, + "c": "${a}" + }; + const metaInfos = await MetaInfoProducer.getMetaInfos(template); + expect(JSON.parse(stringify(metaInfos))).toEqual([ + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "a" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "b", + "normal" + ], + "materialized__": true, + "parent__": [ + "b" + ], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "b" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "exprRootPath__": null, + "exprTargetJsonPointer__": [], + "expr__": "a", + "jsonPointer__": [ + "c" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": true + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": true + } + ]); +}); + +test("blocked paths with ⛔ character in array", async () => { + const template = { + "a": 42, + "arr": [ + "normal", + "⛔blocked", // This should be included since array indices are numbers, not strings with ⛔ + { + "⛔key": "blocked object key", + "normal": "normal object key" + } + ] + }; + const metaInfos = await MetaInfoProducer.getMetaInfos(template); + expect(JSON.parse(stringify(metaInfos))).toEqual([ + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "a" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "arr", + 0 + ], + "materialized__": true, + "parent__": [ + "arr" + ], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "arr", + 1 + ], + "materialized__": true, + "parent__": [ + "arr" + ], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "arr", + 2, + "normal" + ], + "materialized__": true, + "parent__": [ + "arr", + 2 + ], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "arr", + 2 + ], + "materialized__": true, + "parent__": [ + "arr" + ], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [ + "arr" + ], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + }, + { + "absoluteDependencies__": [], + "dependees__": [], + "dependencies__": [], + "jsonPointer__": [], + "materialized__": true, + "parent__": [], + "tags__": [], + "temp__": false, + "treeHasExpressions__": false + } + ]); +}); + From 5e9d24579bd369af69dde6ab937feff29a7437a7 Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Fri, 16 May 2025 10:39:24 -0300 Subject: [PATCH 3/6] Updating test property --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ab4713e..0467d75f 100644 --- a/README.md +++ b/README.md @@ -885,13 +885,13 @@ When a property name starts with "⛔", the MetaInfoProducer will skip that prop > .init -f "example/ex25.json" { "a": 42, - "⛔b": "${$string('should not be evaluated: ') & a}", + "⛔note": "${$string('should not be evaluated: ') & a}", "c": "${a}" } > .out { "a": 42, - "⛔b": "${$string('should not be evaluated: ') & a}", + "⛔note": "${$string('should not be evaluated: ') & a}", "c": 42 } > .init -f "example/ex26.json" From c2a1b3ffd03fa30e3937a464e121811a2b8c7982 Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Fri, 16 May 2025 19:46:44 -0300 Subject: [PATCH 4/6] Merging with main --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8997e610..4a7cfa81 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,5 +43,5 @@ jobs: - name: Deploy TypeDocs uses: peaceiris/actions-gh-pages@v3.9.3 with: - personal_token: ${{ secrets.GH_PAT }} + github_token: ${{ secrets.GH_PAT }} publish_dir: ./docs From 31aae5502f9b49c99eaad022bec78cf7a307ed1e Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Fri, 16 May 2025 19:48:12 -0300 Subject: [PATCH 5/6] Merging with main --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a7cfa81..8997e610 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,5 +43,5 @@ jobs: - name: Deploy TypeDocs uses: peaceiris/actions-gh-pages@v3.9.3 with: - github_token: ${{ secrets.GH_PAT }} + personal_token: ${{ secrets.GH_PAT }} publish_dir: ./docs From 3bb3ca4eb70d8e85be722ad6c250ef25eb26635d Mon Sep 17 00:00:00 2001 From: Marcelo Rodrigues Date: Sat, 17 May 2025 09:58:56 -0300 Subject: [PATCH 6/6] Forcing CI