{"_id":"path-expression-matcher","_rev":"4327227","name":"path-expression-matcher","description":"Efficient path tracking and pattern matching for XML/JSON parsers","dist-tags":{"latest":"1.4.0"},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"time":{"modified":"2026-04-07T23:42:00.000Z","created":"2026-03-06T13:30:15.454Z","1.4.0":"2026-04-07T07:42:03.782Z","1.3.0":"2026-04-07T04:21:19.931Z","1.2.1":"2026-04-03T03:39:51.251Z","1.2.0":"2026-03-20T02:19:10.325Z","1.1.3":"2026-03-11T11:14:12.123Z","1.1.2":"2026-03-10T02:50:51.859Z","1.1.1":"2026-03-09T23:39:25.280Z","1.1.0":"2026-03-09T23:14:22.159Z","1.0.0":"2026-03-06T13:30:15.454Z"},"users":{},"author":{"name":"Amit Gupta","url":"https://solothought.com"},"repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"versions":{"1.4.0":{"name":"path-expression-matcher","version":"1.4.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"./lib/pem.cjs","type":"module","sideEffects":false,"module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"devDependencies":{"@babel/core":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10","@babel/preset-env":"^7.13.10","@babel/register":"^7.13.8","@types/node":"20","babel-loader":"^8.2.2","c8":"^10.1.3","eslint":"^8.3.0","prettier":"^3.5.1","typescript":"5","webpack":"^5.64.4","webpack-cli":"^4.9.1"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"999843e65c04be206a4debf78e1f327c7b86ce11","_id":"path-expression-matcher@1.4.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"275730c9c21bbf2e124eba6d4c6453f02f3d331d","size":38740,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.4.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.4.0.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.4.0_1775547723640_0.8318611681397561"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-04-07T07:42:03.782Z","publish_time":1775547723782,"_source_registry_name":"default","_cnpm_publish_time":1775547723782},"1.3.0":{"name":"path-expression-matcher","version":"1.3.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"./lib/pem.cjs","type":"module","sideEffects":false,"module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"devDependencies":{"@babel/core":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10","@babel/preset-env":"^7.13.10","@babel/register":"^7.13.8","@types/node":"20","babel-loader":"^8.2.2","c8":"^10.1.3","eslint":"^8.3.0","prettier":"^3.5.1","typescript":"5","webpack":"^5.64.4","webpack-cli":"^4.9.1"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"8b6eb81c2cc2c7d3dda87ce0218d80c947757984","_id":"path-expression-matcher@1.3.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"dc39fd6d745df5485356ce81ff7e8efeabef4846","size":38087,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.3.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.3.0.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.3.0_1775535679777_0.7432145293698484"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-04-07T04:21:19.931Z","publish_time":1775535679931,"_source_registry_name":"default","_cnpm_publish_time":1775535679931},"1.2.1":{"name":"path-expression-matcher","version":"1.2.1","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"./lib/pem.cjs","type":"module","sideEffects":false,"module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"devDependencies":{"@babel/core":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10","@babel/preset-env":"^7.13.10","@babel/register":"^7.13.8","@types/node":"20","babel-loader":"^8.2.2","c8":"^10.1.3","eslint":"^8.3.0","prettier":"^3.5.1","typescript":"5","webpack":"^5.64.4","webpack-cli":"^4.9.1"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"83cb5afa0c2135ca189f560e6e5ab81e67365b4e","_id":"path-expression-matcher@1.2.1","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"04de63e98dcef3e07af8b12a41055466f200e291","size":30559,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.2.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.2.1.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.2.1_1775187591102_0.0762754085351025"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-04-03T03:39:51.251Z","publish_time":1775187591251,"_source_registry_name":"default","_cnpm_publish_time":1775187591251},"1.2.0":{"name":"path-expression-matcher","version":"1.2.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"./lib/pem.cjs","type":"module","sideEffects":false,"module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"devDependencies":{"@babel/core":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10","@babel/preset-env":"^7.13.10","@babel/register":"^7.13.8","@types/node":"20","babel-loader":"^8.2.2","c8":"^10.1.3","eslint":"^8.3.0","prettier":"^3.5.1","typescript":"5","webpack":"^5.64.4","webpack-cli":"^4.9.1"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"ee08b3bf81376b7fce9149ecf911b863341dfa9c","_id":"path-expression-matcher@1.2.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"9bdae3787f43b0857b0269e9caaa586c12c8abee","size":30099,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.2.0.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.2.0_1773973150184_0.048252377999925766"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-20T02:19:10.325Z","publish_time":1773973150325,"_source_registry_name":"default","_cnpm_publish_time":1773973150325},"1.1.3":{"name":"path-expression-matcher","version":"1.1.3","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"./lib/pem.cjs","type":"module","sideEffects":false,"module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"scripts":{"test":"c8 --reporter=lcov --reporter=text node test/namespace_test.js && c8 --reporter=lcov --reporter=text node test/test.js","bundle":"webpack --config webpack.cjs.config.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"devDependencies":{"@babel/core":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10","@babel/preset-env":"^7.13.10","@babel/register":"^7.13.8","@types/node":"20","babel-loader":"^8.2.2","c8":"^10.1.3","eslint":"^8.3.0","prettier":"^3.5.1","typescript":"5","webpack":"^5.64.4","webpack-cli":"^4.9.1"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"bbf713e3a97d8137e1565259d5ada6dd67e6ea69","_id":"path-expression-matcher@1.1.3","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"8bf7c629dc1b114e42b633c071f06d14625b4e0d","size":25442,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.1.3.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.1.3_1773227651949_0.4204014454598046"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-11T11:14:12.123Z","publish_time":1773227652123,"_source_registry_name":"default","_cnpm_publish_time":1773227652123},"1.1.2":{"name":"path-expression-matcher","version":"1.1.2","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"src/index.js","type":"module","exports":{".":"./src/index.js","./Expression":"./src/Expression.js","./Matcher":"./src/Matcher.js"},"scripts":{"test":"node test/namespace_test.js && node test/test.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"c2ee2c56b603f3ad7c141c0e51501002c4bdfaaa","_id":"path-expression-matcher@1.1.2","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"0102f74415abf183857532acd78bb7ad6ff6fa00","size":10531,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.1.2.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.1.2.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.1.2_1773111051697_0.7567646605025329"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-10T02:50:51.859Z","publish_time":1773111051859,"_source_registry_name":"default","_cnpm_publish_time":1773111051859},"1.1.1":{"name":"path-expression-matcher","version":"1.1.1","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"src/index.js","type":"module","exports":{".":"./src/index.js","./Expression":"./src/Expression.js","./Matcher":"./src/Matcher.js"},"scripts":{"test":"node test/namespace_test.js && node test/test.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"2742694640f7831baa76fedac22262b480467fd8","_id":"path-expression-matcher@1.1.1","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"c1fd8583ecedb74694b886c5e1715be053c05541","size":10587,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.1.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.1.1.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.1.1_1773099565101_0.5644758911509864"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-09T23:39:25.280Z","publish_time":1773099565280,"_source_registry_name":"default","_cnpm_publish_time":1773099565280},"1.1.0":{"name":"path-expression-matcher","version":"1.1.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"src/index.js","type":"module","exports":{".":"./src/index.js","./Expression":"./src/Expression.js","./Matcher":"./src/Matcher.js"},"scripts":{"test":"node test/namespace_test.js && node test/test.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"7a69dc95231939bd677485fb9c98b85a36bb6888","_id":"path-expression-matcher@1.1.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"9a85509a5a5224bedc7792dba3e6375954105979","size":10358,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.1.0.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.1.0_1773098062011_0.3291288493025948"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-09T23:14:22.159Z","publish_time":1773098062159,"_source_registry_name":"default","_cnpm_publish_time":1773098062159},"1.0.0":{"name":"path-expression-matcher","version":"1.0.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","main":"src/index.js","type":"module","exports":{".":"./src/index.js","./Expression":"./src/Expression.js","./Matcher":"./src/Matcher.js"},"scripts":{"test":"node test/test.js"},"keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","engines":{"node":">=14.0.0"},"funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"gitHead":"9d44b9ca749a44a142aca2ed18f0b2d85e299bbf","_id":"path-expression-matcher@1.0.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"shasum":"9f61d5411f33d46b063fd4ae53737121d0c0b940","size":8742,"noattachment":false,"key":"/path-expression-matcher/-/path-expression-matcher-1.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/path-expression-matcher/download/path-expression-matcher-1.0.0.tgz"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/path-expression-matcher_1.0.0_1772803815285_0.5097220061166832"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-03-06T13:30:15.454Z","publish_time":1772803815454,"_source_registry_name":"default","_cnpm_publish_time":1772803815454}},"readme":"# path-expression-matcher\n\nEfficient path tracking and pattern matching for XML, JSON, YAML or any other parsers.\n\n## ???? Purpose\n\n`path-expression-matcher` provides two core classes for tracking and matching paths:\n\n- **`Expression`**: Parses and stores pattern expressions (e.g., `\"root.users.user[id]\"`)\n- **`Matcher`**: Tracks current path during parsing and matches against expressions\n\nCompatible with [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and similar tools.\n\n## ???? Installation\n\n```bash\nnpm install path-expression-matcher\n```\n\n## ???? Quick Start\n\n```javascript\nimport { Expression, Matcher } from 'path-expression-matcher';\n\n// Create expression (parse once, reuse many times)\nconst expr = new Expression(\"root.users.user\");\n\n// Create matcher (tracks current path)\nconst matcher = new Matcher();\n\nmatcher.push(\"root\");\nmatcher.push(\"users\");\nmatcher.push(\"user\", { id: \"123\" });\n\n// Match current path against expression\nif (matcher.matches(expr)) {\n  console.log(\"Match found!\");\n  console.log(\"Current path:\", matcher.toString()); // \"root.users.user\"\n}\n\n// Namespace support\nconst nsExpr = new Expression(\"soap::Envelope.soap::Body..ns::UserId\");\nmatcher.push(\"Envelope\", null, \"soap\");\nmatcher.push(\"Body\", null, \"soap\");\nmatcher.push(\"UserId\", null, \"ns\");\nconsole.log(matcher.toString()); // \"soap:Envelope.soap:Body.ns:UserId\"\n```\n\n## ???? Pattern Syntax\n\n### Basic Paths\n\n```javascript\n\"root.users.user\"           // Exact path match\n\"*.users.user\"              // Wildcard: any parent\n\"root.*.user\"               // Wildcard: any middle\n\"root.users.*\"              // Wildcard: any child\n```\n\n### Deep Wildcard\n\n```javascript\n\"..user\"                    // user anywhere in tree\n\"root..user\"                // user anywhere under root\n\"..users..user\"             // users somewhere, then user below it\n```\n\n### Attribute Matching\n\n```javascript\n\"user[id]\"                  // user with \"id\" attribute\n\"user[type=admin]\"          // user with type=\"admin\" (current node only)\n\"root[lang]..user\"          // user under root that has \"lang\" attribute\n```\n\n### Position Selectors\n\n```javascript\n\"user:first\"                // First user (counter=0)\n\"user:nth(2)\"               // Third user (counter=2, zero-based)\n\"user:odd\"                  // Odd-numbered users (counter=1,3,5...)\n\"user:even\"                 // Even-numbered users (counter=0,2,4...)\n\"root.users.user:first\"     // First user under users\n```\n\n**Note:** Position selectors use the **counter** (occurrence count of the tag name), not the position (child index). For example, in `<root><a/><b/><a/></root>`, the second `<a/>` has position=2 but counter=1.\n\n### Namespaces\n\n```javascript\n\"ns::user\"                  // user with namespace \"ns\"\n\"soap::Envelope\"            // Envelope with namespace \"soap\"\n\"ns::user[id]\"              // user with namespace \"ns\" and \"id\" attribute\n\"ns::user:first\"            // First user with namespace \"ns\"\n\"*::user\"                   // user with any namespace\n\"..ns::item\"                // item with namespace \"ns\" anywhere in tree\n\"soap::Envelope.soap::Body\" // Nested namespaced elements\n\"ns::first\"                 // Tag named \"first\" with namespace \"ns\" (NO ambiguity!)\n```\n\n**Namespace syntax:**\n- Use **double colon (::)** for namespace: `ns::tag`\n- Use **single colon (:)** for position: `tag:first`\n- Combined: `ns::tag:first` (namespace + tag + position)\n\n**Namespace matching rules:**\n- Pattern `ns::user` matches only nodes with namespace \"ns\" and tag \"user\"\n- Pattern `user` (no namespace) matches nodes with tag \"user\" regardless of namespace\n- Pattern `*::user` matches tag \"user\" with any namespace (wildcard namespace)\n- Namespaces are tracked separately for counter/position (e.g., `ns1::item` and `ns2::item` have independent counters)\n\n### Wildcard Differences\n\n**Single wildcard (`*`)** - Matches exactly ONE level:\n- `\"*.fix1\"` matches `root.fix1` (2 levels) ✅\n- `\"*.fix1\"` does NOT match `root.another.fix1` (3 levels) ❌\n- Path depth MUST equal pattern depth\n\n**Deep wildcard (`..`)** - Matches ZERO or MORE levels:\n- `\"..fix1\"` matches `root.fix1` ✅\n- `\"..fix1\"` matches `root.another.fix1` ✅\n- `\"..fix1\"` matches `a.b.c.d.fix1` ✅\n- Works at any depth\n\n### Combined Patterns\n\n```javascript\n\"..user[id]:first\"              // First user with id, anywhere\n\"root..user[type=admin]\"        // Admin user under root\n\"ns::user[id]:first\"            // First namespaced user with id\n\"soap::Envelope..ns::UserId\"    // UserId with namespace ns under SOAP envelope\n```\n\n## ???? API Reference\n\n### Expression\n\n#### Constructor\n\n```javascript\nnew Expression(pattern, options = {}, data)\n```\n\n**Parameters:**\n- `pattern` (string): Pattern to parse\n- `options.separator` (string): Path separator (default: `'.'`)\n\n**Example:**\n```javascript\nconst expr1 = new Expression(\"root.users.user\");\nconst expr2 = new Expression(\"root/users/user\", { separator: '/' });\nconst expr3 = new Expression(\"root/users/user\", { separator: '/' }, { extra: \"data\"});\nconsole.log(expr3.data) // { extra: \"data\" }\n```\n\n#### Methods\n\n- `hasDeepWildcard()` → boolean\n- `hasAttributeCondition()` → boolean\n- `hasPositionSelector()` → boolean\n- `toString()` → string\n\n### Matcher\n\n#### Constructor\n\n```javascript\nnew Matcher(options)\n```\n\n**Parameters:**\n- `options.separator` (string): Default path separator (default: `'.'`)\n\n#### Path Tracking Methods\n\n##### `push(tagName, attrValues, namespace)`\n\nAdd a tag to the current path. Position and counter are automatically calculated.\n\n**Parameters:**\n- `tagName` (string): Tag name\n- `attrValues` (object, optional): Attribute key-value pairs (current node only)\n- `namespace` (string, optional): Namespace for the tag\n\n**Example:**\n```javascript\nmatcher.push(\"user\", { id: \"123\", type: \"admin\" });\nmatcher.push(\"item\");  // No attributes\nmatcher.push(\"Envelope\", null, \"soap\");  // With namespace\nmatcher.push(\"Body\", { version: \"1.1\" }, \"soap\");  // With both\n```\n\n**Position vs Counter:**\n- **Position**: The child index in the parent (0, 1, 2, 3...)\n- **Counter**: How many times this tag name appeared at this level (0, 1, 2...)\n\nExample:\n```xml\n<root>\n  <a/>      <!-- position=0, counter=0 -->\n  <b/>      <!-- position=1, counter=0 -->\n  <a/>      <!-- position=2, counter=1 -->\n</root>\n```\n\n##### `pop()`\n\nRemove the last tag from the path.\n\n```javascript\nmatcher.pop();\n```\n\n##### `updateCurrent(attrValues)`\n\nUpdate current node's attributes (useful when attributes are parsed after push).\n\n```javascript\nmatcher.push(\"user\");  // Don't know values yet\n// ... parse attributes ...\nmatcher.updateCurrent({ id: \"123\" });\n```\n\n##### `reset()`\n\nClear the entire path.\n\n```javascript\nmatcher.reset();\n```\n\n#### Query Methods\n\n##### `matches(expression)`\n\nCheck if current path matches an Expression.\n\n```javascript\nconst expr = new Expression(\"root.users.user\");\nif (matcher.matches(expr)) {\n  // Current path matches\n}\n```\n\n#### `matchesAny(exprSet)` → `boolean`\n\nPlease check `ExpressionSet` class for more details.  \n\n```javascript\nconst matcher = new Matcher();\nconst exprSet = new ExpressionSet();\nexprSet.add(new Expression(\"root.users.user\"));\nexprSet.add(new Expression(\"root.config.*\"));\nexprSet.seal();\n\nif (matcher.matchesAny(exprSet)) {\n  // Current path matches any expression in the set\n}\n```\n\n##### `getCurrentTag()`\n\nGet current tag name.\n\n```javascript\nconst tag = matcher.getCurrentTag(); // \"user\"\n```\n\n##### `getCurrentNamespace()`\n\nGet current namespace.\n\n```javascript\nconst ns = matcher.getCurrentNamespace(); // \"soap\" or undefined\n```\n\n##### `getAttrValue(attrName)`\n\nGet attribute value of current node.\n\n```javascript\nconst id = matcher.getAttrValue(\"id\"); // \"123\"\n```\n\n##### `hasAttr(attrName)`\n\nCheck if current node has an attribute.\n\n```javascript\nif (matcher.hasAttr(\"id\")) {\n  // Current node has \"id\" attribute\n}\n```\n\n##### `getPosition()`\n\nGet sibling position of current node (child index in parent).\n\n```javascript\nconst position = matcher.getPosition(); // 0, 1, 2, ...\n```\n\n##### `getCounter()`\n\nGet repeat counter of current node (occurrence count of this tag name).\n\n```javascript\nconst counter = matcher.getCounter(); // 0, 1, 2, ...\n```\n\n##### `getIndex()` (deprecated)\n\nAlias for `getPosition()`. Use `getPosition()` or `getCounter()` instead for clarity.\n\n```javascript\nconst index = matcher.getIndex(); // Same as getPosition()\n```\n\n##### `getDepth()`\n\nGet current path depth.\n\n```javascript\nconst depth = matcher.getDepth(); // 3 for \"root.users.user\"\n```\n\n##### `toString(separator?, includeNamespace?)`\n\nGet path as string.\n\n**Parameters:**\n- `separator` (string, optional): Path separator (uses default if not provided)\n- `includeNamespace` (boolean, optional): Whether to include namespaces (default: true)\n\n```javascript\nconst path = matcher.toString();           // \"root.ns:user.item\"\nconst path2 = matcher.toString('/');       // \"root/ns:user/item\"\nconst path3 = matcher.toString('.', false); // \"root.user.item\" (no namespaces)\n```\n\n##### `toArray()`\n\nGet path as array.\n\n```javascript\nconst arr = matcher.toArray(); // [\"root\", \"users\", \"user\"]\n```\n\n#### State Management\n\n##### `snapshot()`\n\nCreate a snapshot of current state.\n\n```javascript\nconst snapshot = matcher.snapshot();\n```\n\n##### `restore(snapshot)`\n\nRestore from a snapshot.\n\n```javascript\nmatcher.restore(snapshot);\n```\n\n#### Read-Only Access\n\n##### `readOnly()`\n\nReturns a **live, read-only proxy** of the matcher. All query and inspection methods work normally, but any attempt to call a state-mutating method (`push`, `pop`, `reset`, `updateCurrent`, `restore`) or to write/delete a property throws a `TypeError`.\n\nThis is the recommended way to share the matcher with external consumers — plugins, callbacks, event handlers — that only need to inspect the current path without being able to corrupt parser state.\n\n```javascript\nconst ro = matcher.readOnly();\n```\n\n**What works on the read-only view:**\n\n```javascript\nro.matches(expr)          // ✓ pattern matching\nro.getCurrentTag()        // ✓ current tag name\nro.getCurrentNamespace()  // ✓ current namespace\nro.getAttrValue(\"id\")     // ✓ attribute value\nro.hasAttr(\"id\")          // ✓ attribute presence check\nro.getPosition()          // ✓ sibling position\nro.getCounter()           // ✓ occurrence counter\nro.getDepth()             // ✓ path depth\nro.toString()             // ✓ path as string\nro.toArray()              // ✓ path as array\nro.snapshot()             // ✓ snapshot (can be used to restore the real matcher)\n```\n\n**What throws a `TypeError`:**\n\n```javascript\nro.push(\"child\", {})      // ✗ TypeError: Cannot call 'push' on a read-only Matcher\nro.pop()                  // ✗ TypeError: Cannot call 'pop' on a read-only Matcher\nro.reset()                // ✗ TypeError: Cannot call 'reset' on a read-only Matcher\nro.updateCurrent({})      // ✗ TypeError: Cannot call 'updateCurrent' on a read-only Matcher\nro.restore(snapshot)      // ✗ TypeError: Cannot call 'restore' on a read-only Matcher\nro.separator = '/'        // ✗ TypeError: Cannot set property on a read-only Matcher\n```\n\n**Important:** The read-only view is **live** — it always reflects the current state of the underlying matcher. If you need a frozen-in-time copy instead, use `snapshot()`.\n\n```javascript\nconst matcher = new Matcher();\nconst ro = matcher.readOnly();\n\nmatcher.push(\"root\");\nro.getDepth();    // 1 — immediately reflects the push\nmatcher.push(\"users\");\nro.getDepth();    // 2 — still live\n```\n\n## ???? Usage Examples\n\n### Example 1: XML Parser with stopNodes\n\n```javascript\nimport { XMLParser } from 'fast-xml-parser';\nimport { Expression, Matcher } from 'path-expression-matcher';\n\nclass MyParser {\n  constructor() {\n    this.matcher = new Matcher();\n    \n    // Pre-compile stop node patterns\n    this.stopNodeExpressions = [\n      new Expression(\"html.body.script\"),\n      new Expression(\"html.body.style\"),\n      new Expression(\"..svg\"),\n    ];\n  }\n  \n  parseTag(tagName, attrs) {\n    this.matcher.push(tagName, attrs);\n    \n    // Check if this is a stop node\n    for (const expr of this.stopNodeExpressions) {\n      if (this.matcher.matches(expr)) {\n        // Don't parse children, read as raw text\n        return this.readRawContent();\n      }\n    }\n    \n    // Continue normal parsing\n    this.parseChildren();\n    \n    this.matcher.pop();\n  }\n}\n```\n\n### Example 2: Conditional Processing\n\n```javascript\nconst matcher = new Matcher();\nconst userExpr = new Expression(\"..user[type=admin]\");\nconst firstItemExpr = new Expression(\"..item:first\");\n\nfunction processTag(tagName, value, attrs) {\n  matcher.push(tagName, attrs);\n  \n  if (matcher.matches(userExpr)) {\n    value = enhanceAdminUser(value);\n  }\n  \n  if (matcher.matches(firstItemExpr)) {\n    value = markAsFirst(value);\n  }\n  \n  matcher.pop();\n  return value;\n}\n```\n\n### Example 3: Path-based Filtering\n\n```javascript\nconst patterns = [\n  new Expression(\"data.users.user\"),\n  new Expression(\"data.posts.post\"),\n  new Expression(\"..comment[approved=true]\"),\n];\n\nfunction shouldInclude(matcher) {\n  return patterns.some(expr => matcher.matches(expr));\n}\n```\n\n### Example 4: Custom Separator\n\n```javascript\nconst matcher = new Matcher({ separator: '/' });\nconst expr = new Expression(\"root/config/database\", { separator: '/' });\n\nmatcher.push(\"root\");\nmatcher.push(\"config\");\nmatcher.push(\"database\");\n\nconsole.log(matcher.toString()); // \"root/config/database\"\nconsole.log(matcher.matches(expr)); // true\n```\n\n### Example 5: Attribute Checking\n\n```javascript\nconst matcher = new Matcher();\nmatcher.push(\"root\");\nmatcher.push(\"user\", { id: \"123\", type: \"admin\", status: \"active\" });\n\n// Check attribute existence (current node only)\nconsole.log(matcher.hasAttr(\"id\"));        // true\nconsole.log(matcher.hasAttr(\"email\"));     // false\n\n// Get attribute value (current node only)\nconsole.log(matcher.getAttrValue(\"type\")); // \"admin\"\n\n// Match by attribute\nconst expr1 = new Expression(\"user[id]\");\nconsole.log(matcher.matches(expr1));       // true\n\nconst expr2 = new Expression(\"user[type=admin]\");\nconsole.log(matcher.matches(expr2));       // true\n```\n\n### Example 6: Position vs Counter\n\n```javascript\nconst matcher = new Matcher();\nmatcher.push(\"root\");\n\n// Mixed tags at same level\nmatcher.push(\"item\");  // position=0, counter=0 (first item)\nmatcher.pop();\n\nmatcher.push(\"div\");   // position=1, counter=0 (first div)\nmatcher.pop();\n\nmatcher.push(\"item\");  // position=2, counter=1 (second item)\n\nconsole.log(matcher.getPosition()); // 2 (third child overall)\nconsole.log(matcher.getCounter());  // 1 (second \"item\" specifically)\n\n// :first uses counter, not position\nconst expr = new Expression(\"root.item:first\");\nconsole.log(matcher.matches(expr)); // false (counter=1, not 0)\n```\n\n### Example 8: Passing a Read-Only Matcher to External Consumers\n\nWhen passing the matcher into callbacks, plugins, or other code you don't control, use `readOnly()` to prevent accidental state corruption.\n\n```javascript\nimport { Expression, Matcher } from 'path-expression-matcher';\n\nconst matcher = new Matcher();\n\nconst adminExpr = new Expression(\"..user[type=admin]\");\n\nfunction parseTag(tagName, attrs, onTag) {\n  matcher.push(tagName, attrs);\n\n  // Pass a read-only view — consumer can inspect but not mutate\n  onTag(matcher.readOnly());\n\n  matcher.pop();\n}\n\n// Safe consumer — can only read\nfunction myPlugin(ro) {\n  if (ro.matches(adminExpr)) {\n    console.log(\"Admin at path:\", ro.toString());\n    console.log(\"Depth:\", ro.getDepth());\n    console.log(\"ID:\", ro.getAttrValue(\"id\"));\n  }\n}\n\n// ro.push(...) or ro.reset() here would throw TypeError,\n// so the parser's state is always safe.\nparseTag(\"user\", { id: \"1\", type: \"admin\" }, myPlugin);\n```\n\n**Combining with `snapshot()`:** A snapshot taken via the read-only view can still be used to restore the real matcher.\n\n```javascript\nconst matcher = new Matcher();\nmatcher.push(\"root\");\nmatcher.push(\"users\");\n\nconst ro = matcher.readOnly();\nconst snap = ro.snapshot();       // ✓ snapshot works on read-only view\n\nmatcher.push(\"user\");             // continue parsing...\nmatcher.restore(snap);            // restore to \"root.users\" using the snapshot\n```\n\n```javascript\nconst matcher = new Matcher();\nconst soapExpr = new Expression(\"soap::Envelope.soap::Body..ns::UserId\");\n\n// Parse SOAP document\nmatcher.push(\"Envelope\", { xmlns: \"...\" }, \"soap\");\nmatcher.push(\"Body\", null, \"soap\");\nmatcher.push(\"GetUserRequest\", null, \"ns\");\nmatcher.push(\"UserId\", null, \"ns\");\n\n// Match namespaced pattern\nif (matcher.matches(soapExpr)) {\n  console.log(\"Found UserId in SOAP body\");\n  console.log(matcher.toString()); // \"soap:Envelope.soap:Body.ns:GetUserRequest.ns:UserId\"\n}\n\n// Namespace-specific counters\nmatcher.reset();\nmatcher.push(\"root\");\nmatcher.push(\"item\", null, \"ns1\");  // ns1::item counter=0\nmatcher.pop();\nmatcher.push(\"item\", null, \"ns2\");  // ns2::item counter=0 (different namespace)\nmatcher.pop();\nmatcher.push(\"item\", null, \"ns1\");  // ns1::item counter=1\n\nconst firstNs1Item = new Expression(\"root.ns1::item:first\");\nconsole.log(matcher.matches(firstNs1Item)); // false (counter=1)\n\nconst secondNs1Item = new Expression(\"root.ns1::item:nth(1)\");\nconsole.log(matcher.matches(secondNs1Item)); // true\n\n// NO AMBIGUITY: Tags named after position keywords\nmatcher.reset();\nmatcher.push(\"root\");\nmatcher.push(\"first\", null, \"ns\");  // Tag named \"first\" with namespace\n\nconst expr = new Expression(\"root.ns::first\");\nconsole.log(matcher.matches(expr)); // true - matches namespace \"ns\", tag \"first\"\n```\n\n## ????️ Architecture\n\n### Data Storage Strategy\n\n**Ancestor nodes:** Store only tag name, position, and counter (minimal memory)\n**Current node:** Store tag name, position, counter, and attribute values\n\nThis design minimizes memory usage:\n- No attribute names stored (derived from values object when needed)\n- Attribute values only for current node, not ancestors\n- Attribute checking for ancestors is not supported (acceptable trade-off)\n- For 1M nodes with 3 attributes each, saves ~50MB vs storing attribute names\n\n### Matching Strategy\n\nMatching is performed **bottom-to-top** (from current node toward root):\n1. Start at current node\n2. Match segments from pattern end to start\n3. Attribute checking only works for current node (ancestors have no attribute data)\n4. Position selectors use **counter** (occurrence count), not position (child index)\n\n### Performance\n\n- **Expression parsing:** One-time cost when Expression is created\n- **Expression analysis:** Cached (hasDeepWildcard, hasAttributeCondition, hasPositionSelector)\n- **Path tracking:** O(1) for push/pop operations\n- **Pattern matching:** O(n*m) where n = path depth, m = pattern segments\n- **Memory per ancestor node:** ~40-60 bytes (tag, position, counter only)\n- **Memory per current node:** ~80-120 bytes (adds attribute values)\n\n## ???? Design Patterns\n\n### Pre-compile Patterns (Recommended)\n\n```javascript\n// ✅ GOOD: Parse once, reuse many times\nconst expr = new Expression(\"..user[id]\");\n\nfor (let i = 0; i < 1000; i++) {\n  if (matcher.matches(expr)) {\n    // ...\n  }\n}\n```\n\n```javascript\n// ❌ BAD: Parse on every iteration\nfor (let i = 0; i < 1000; i++) {\n  if (matcher.matches(new Expression(\"..user[id]\"))) {\n    // ...\n  }\n}\n```\n\n### Batch Pattern Checking with ExpressionSet (Recommended)\n\nFor checking multiple patterns on every tag, use `ExpressionSet` instead of a manual loop.\nIt pre-indexes expressions at build time so each call to `matchesAny()` does an O(1) bucket\nlookup rather than a full O(N) scan:\n\n```javascript\nimport { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';\n\n// Build once at config/startup time\nconst stopNodes = new ExpressionSet();\nstopNodes\n  .add(new Expression('root.users.user'))\n  .add(new Expression('root.config.*'))\n  .add(new Expression('..script'))\n  .seal(); // prevent accidental mutation during parsing\n\n// Per-tag — hot path\nif (stopNodes.matchesAny(matcher)) {\n  // handle stop node\n}\n```\n\nThis replaces the manual loop pattern:\n\n```javascript\n// ❌ Before — O(N) per tag\nfunction isStopNode(expressions, matcher) {\n  for (let i = 0; i < expressions.length; i++) {\n    if (matcher.matches(expressions[i])) return true;\n  }\n  return false;\n}\n\n// ✅ After — O(1) lookup per tag\nconst stopNodes = new ExpressionSet();\nstopNodes.addAll(expressions);\nstopNodes.matchesAny(matcher);\n//or matcher.matchesAny(stopNodes)\n```\n\n---\n\n## ???? ExpressionSet API\n\n`ExpressionSet` is an indexed collection of `Expression` objects designed for efficient\nbulk matching. Build it once from your config, then call `matchesAny()` on every tag.\n\n### Constructor\n\n```javascript\nconst set = new ExpressionSet();\n```\n\n### `add(expression)` → `this`\n\nAdd a single `Expression`. Duplicate patterns (same pattern string) are silently ignored.\nReturns `this` for chaining. Throws `TypeError` if the set is sealed.\n\n```javascript\nset.add(new Expression('root.users.user'));\nset.add(new Expression('..script'));\n```\n\n### `addAll(expressions)` → `this`\n\nAdd an array of `Expression` objects at once. Returns `this` for chaining.\n\n```javascript\nset.addAll(config.stopNodes.map(p => new Expression(p)));\n```\n\n### `has(expression)` → `boolean`\n\nCheck whether an expression with the same pattern is already present.\n\n```javascript\nset.has(new Expression('root.users.user')); // true / false\n```\n\n### `seal()` → `this`\n\nPrevent further additions. Any subsequent call to `add()` or `addAll()` throws a `TypeError`.\nUseful to guard against accidental mutation once parsing has started.\n\n```javascript\nconst stopNodes = new ExpressionSet();\nstopNodes.addAll(patterns).seal();\n\nstopNodes.add(new Expression('root.extra')); // ❌ TypeError: ExpressionSet is sealed\n```\n\n### `size` → `number`\n\nNumber of distinct expressions in the set.\n\n```javascript\nset.size; // 3\n```\n\n### `isSealed` → `boolean`\n\nWhether `seal()` has been called.\n\n### `matchesAny(matcher)` → `boolean`\n\nReturns `true` if the matcher's current path matches **any** expression in the set.\nAccepts both a `Matcher` instance and a `ReadOnlyMatcher` view.\n\n```javascript\nif (stopNodes.matchesAny(matcher)) { /* ... */ }\nif (stopNodes.matchesAny(matcher.readOnly())) { /* ... */ } // also works\n```\n\n**How indexing works:** expressions are bucketed at `add()` time, not at match time.\n\n| Expression type | Bucket | Lookup cost |\n|---|---|---|\n| Fixed path, concrete tag (`root.users.user`) | `depth:tag` map | O(1) |\n| Fixed path, wildcard tag (`root.config.*`) | `depth` map | O(1) |\n| Deep wildcard (`..script`) | flat list | O(D) — always scanned |\n\nIn practice, deep-wildcard expressions are rare in configs, so the list stays small.\n\n### `findMatch(matcher)` → `Expression`\n\nReturns the Expression instance that matched the current path. Accepts both a `Matcher` instance and a `ReadOnlyMatcher` view.\n\n```javascript\nconst node = stopNodes.findMatch(matcher);\n```\n\n\n### Example 7: ExpressionSet in a real parser loop\n\n```javascript\nimport { XMLParser } from 'fast-xml-parser';\nimport { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';\n\n// Config-time setup\nconst stopNodes = new ExpressionSet();\nstopNodes\n  .addAll(['script', 'style'].map(t => new Expression(`..${t}`)))\n  .seal();\n\nconst matcher = new Matcher();\n\nconst parser = new XMLParser({\n  onOpenTag(tagName, attrs) {\n    matcher.push(tagName, attrs);\n    if (stopNodes.matchesAny(matcher)) {\n      // treat as stop node\n    }\n  },\n  onCloseTag() {\n    matcher.pop();\n  },\n});\n```\n\n\n\n## ???? Integration with fast-xml-parser\n\n**Basic integration:**\n\n```javascript\nimport { XMLParser } from 'fast-xml-parser';\nimport { Expression, Matcher } from 'path-expression-matcher';\n\nconst parser = new XMLParser({\n  // Custom options using path-expression-matcher\n  stopNodes: [\"script\", \"style\"].map(tag => new Expression(`..${tag}`)),\n  \n  tagValueProcessor: (tagName, value, jPath, hasAttrs, isLeaf, matcher) => {\n    // matcher is available in callbacks\n    if (matcher.matches(new Expression(\"..user[type=admin]\"))) {\n      return enhanceValue(value);\n    }\n    return value;\n  }\n});\n```\n\n## ???? License\n\nMIT\n\n## ???? Contributing\n\nIssues and PRs welcome! This package is designed to be used by XML/JSON parsers like fast-xml-parser.","_attachments":{},"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"license":"MIT"}