{"_id":"path-expression-matcher","_rev":"10-b15d3641436984652da6328dd048ce05","name":"path-expression-matcher","dist-tags":{"latest":"1.5.0"},"versions":{"1.0.0":{"name":"path-expression-matcher","version":"1.0.0","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.0.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"9f61d5411f33d46b063fd4ae53737121d0c0b940","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.0.0.tgz","fileCount":6,"integrity":"sha512-6SYE3bPfULK4jZ7veRtG1fmrsGV36+XcvZQxrjnp32Xv1gC1de5Jih9kvb6dZThRXEdfZgTEIXHRMQnO8E5xXg==","signatures":[{"sig":"MEUCIHAtNmoJYRIbBJVK8CfaWpMhwR0sR1rgBvoK+Lu54WBUAiEA2H0IXswTUIbbUn0FWgLpiODruXvhnI1pdLdCJN0Ida0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":30351},"main":"src/index.js","type":"module","engines":{"node":">=14.0.0"},"exports":{".":"./src/index.js","./Matcher":"./src/Matcher.js","./Expression":"./src/Expression.js"},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"9d44b9ca749a44a142aca2ed18f0b2d85e299bbf","scripts":{"test":"node test/test.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.0.0_1772803815285_0.5097220061166832","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"path-expression-matcher","version":"1.1.0","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.1.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"9a85509a5a5224bedc7792dba3e6375954105979","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.0.tgz","fileCount":6,"integrity":"sha512-dQleLfQnm6qDIV4jYwJINXlWXIA++oi+ZlXDx5UyksP2AICml71s7yKKfpJ7wk51/70spXcnM6C9S1YfoPxYPA==","signatures":[{"sig":"MEUCIEtSOlAQe7T8OGVRMLLhKXs2UnuPHBjePIni/hKOKW3iAiEAkiIxAH0tfPAsWUE2wG/wwHAIBYmgwdk9XRZ1XKWDyjc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37316},"main":"src/index.js","type":"module","engines":{"node":">=14.0.0"},"exports":{".":"./src/index.js","./Matcher":"./src/Matcher.js","./Expression":"./src/Expression.js"},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"7a69dc95231939bd677485fb9c98b85a36bb6888","scripts":{"test":"node test/namespace_test.js && node test/test.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.1.0_1773098062011_0.3291288493025948","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"path-expression-matcher","version":"1.1.1","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.1.1","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"c1fd8583ecedb74694b886c5e1715be053c05541","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.1.tgz","fileCount":6,"integrity":"sha512-NC1UVEpK8euainhgyX0UTmM0mSkWsEctNd39cjOAa8Tx424ZUpoPKcSPssPrCVDlhyjIYw7j6v36bqHxct9Xzg==","signatures":[{"sig":"MEQCIFMGq1J/rLHQayaGJMAUzv4wu3IR/S/8etA5j4pYWgj7AiBv16XNyaALz4Wxcq2D79upJVwCymb6O7rIUacLQsEI0g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37891},"main":"src/index.js","type":"module","engines":{"node":">=14.0.0"},"exports":{".":"./src/index.js","./Matcher":"./src/Matcher.js","./Expression":"./src/Expression.js"},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"2742694640f7831baa76fedac22262b480467fd8","scripts":{"test":"node test/namespace_test.js && node test/test.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.1.1_1773099565101_0.5644758911509864","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"path-expression-matcher","version":"1.1.2","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.1.2","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"0102f74415abf183857532acd78bb7ad6ff6fa00","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.2.tgz","fileCount":6,"integrity":"sha512-LXWqJmcpp2BKOEmgt4CyuESFmBfPuhJlAHKJsFzuJU6CxErWk75BrO+Ni77M9OxHN6dCYKM4vj+21Z6cOL96YQ==","signatures":[{"sig":"MEYCIQCsYYnMPUa72H60Ihdd+Am+0pFJ4eRkzgfqO7M6g1KWxgIhALk5LAd/vSK5EXjZ7lmTWwgnFVU1YWcYo5gs2LfH57VN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37747},"main":"src/index.js","type":"module","engines":{"node":">=14.0.0"},"exports":{".":"./src/index.js","./Matcher":"./src/Matcher.js","./Expression":"./src/Expression.js"},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"c2ee2c56b603f3ad7c141c0e51501002c4bdfaaa","scripts":{"test":"node test/namespace_test.js && node test/test.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.1.2_1773111051697_0.7567646605025329","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"path-expression-matcher","version":"1.1.3","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.1.3","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"8bf7c629dc1b114e42b633c071f06d14625b4e0d","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz","fileCount":11,"integrity":"sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==","signatures":[{"sig":"MEUCIBqGd3SOWaIUW+y2cP8OHccawmUyuMl+9jkYCcIV3IJQAiEA6nJkT7Kep27vpigKrf50+/RqkOJ2ErrenAZsJ2O6TG4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":94470},"main":"./lib/pem.cjs","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"bbf713e3a97d8137e1565259d5ada6dd67e6ea69","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"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^8.3.0","webpack":"^5.64.4","prettier":"^3.5.1","typescript":"5","@babel/core":"^7.13.10","@types/node":"20","webpack-cli":"^4.9.1","babel-loader":"^8.2.2","@babel/register":"^7.13.8","@babel/preset-env":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10"},"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.1.3_1773227651949_0.4204014454598046","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"path-expression-matcher","version":"1.2.0","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.2.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"9bdae3787f43b0857b0269e9caaa586c12c8abee","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz","fileCount":11,"integrity":"sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==","signatures":[{"sig":"MEUCIAxG3caw1B1u1LnKwoxNk1oovpQP3oh/d/plCmnwE6Y4AiEA695VrzJWTFM86REVyqCvOu9nrUlS+KiZjQduPqZ8l2M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":115682},"main":"./lib/pem.cjs","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"ee08b3bf81376b7fce9149ecf911b863341dfa9c","scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^8.3.0","webpack":"^5.64.4","prettier":"^3.5.1","typescript":"5","@babel/core":"^7.13.10","@types/node":"20","webpack-cli":"^4.9.1","babel-loader":"^8.2.2","@babel/register":"^7.13.8","@babel/preset-env":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10"},"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.2.0_1773973150184_0.048252377999925766","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"path-expression-matcher","version":"1.2.1","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.2.1","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"04de63e98dcef3e07af8b12a41055466f200e291","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.1.tgz","fileCount":11,"integrity":"sha512-d7gQQmLvAKXKXE2GeP9apIGbMYKz88zWdsn/BN2HRWVQsDFdUY36WSLTY0Jvd4HWi7Fb30gQ62oAOzdgJA6fZw==","signatures":[{"sig":"MEYCIQC9qjmFRJqCFKpe7LvirCikNPqdELghqmlrO/XSeqxrqQIhAJaepBMMZay+t770Nk3zDNFRo4Sec6mqwKD77Uzxe2uZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118103},"main":"./lib/pem.cjs","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"83cb5afa0c2135ca189f560e6e5ab81e67365b4e","scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^8.3.0","webpack":"^5.64.4","prettier":"^3.5.1","typescript":"5","@babel/core":"^7.13.10","@types/node":"20","webpack-cli":"^4.9.1","babel-loader":"^8.2.2","@babel/register":"^7.13.8","@babel/preset-env":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10"},"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.2.1_1775187591102_0.0762754085351025","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"path-expression-matcher","version":"1.3.0","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.3.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"dc39fd6d745df5485356ce81ff7e8efeabef4846","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.3.0.tgz","fileCount":12,"integrity":"sha512-tkolHg8cWjEFA8+TqYGk0w6aNEZVcb7pVxW8KXZpU+ebaBr3s1ogbLssjK1cL74TtEuKl/qy6cb90RnwTFt3kw==","signatures":[{"sig":"MEYCIQCha7+pXgLyJJZMcwPTQKpjMqY7FWjZFRgVzS6XccAPPwIhAKB3+3UOXCI5eb8HiqJFwcCWvPe6pRukeW8/D95m0TpT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":150470},"main":"./lib/pem.cjs","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"8b6eb81c2cc2c7d3dda87ce0218d80c947757984","scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^8.3.0","webpack":"^5.64.4","prettier":"^3.5.1","typescript":"5","@babel/core":"^7.13.10","@types/node":"20","webpack-cli":"^4.9.1","babel-loader":"^8.2.2","@babel/register":"^7.13.8","@babel/preset-env":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10"},"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.3.0_1775535679777_0.7432145293698484","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"path-expression-matcher","version":"1.4.0","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"path-expression-matcher@1.4.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"dist":{"shasum":"275730c9c21bbf2e124eba6d4c6453f02f3d331d","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.4.0.tgz","fileCount":12,"integrity":"sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==","signatures":[{"sig":"MEUCIQC/YLhYM3CXUIHlb/9zA+41whsRP72x9GvveQatzlGMQAIgGzaofWYmH2URY/Uoi7YViSv8mS0fvLMnxkwWwJNcbOM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":153837},"main":"./lib/pem.cjs","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":{"types":"./src/index.d.ts","default":"./src/index.js"},"require":{"types":"./lib/pem.d.cts","default":"./lib/pem.cjs"}}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"999843e65c04be206a4debf78e1f327c7b86ce11","scripts":{"test":"c8 --reporter=lcov --reporter=text node test/*test.js","bundle":"webpack --config webpack.cjs.config.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git","type":"git"},"_npmVersion":"11.11.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^8.3.0","webpack":"^5.64.4","prettier":"^3.5.1","typescript":"5","@babel/core":"^7.13.10","@types/node":"20","webpack-cli":"^4.9.1","babel-loader":"^8.2.2","@babel/register":"^7.13.8","@babel/preset-env":"^7.13.10","@babel/plugin-transform-runtime":"^7.13.10"},"_npmOperationalInternal":{"tmp":"tmp/path-expression-matcher_1.4.0_1775547723640_0.8318611681397561","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"path-expression-matcher","version":"1.5.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":"543e5b3718897aa7da9dfc9ea6f433d1b551720d","_id":"path-expression-matcher@1.5.0","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==","shasum":"3b98545dc88ffebb593e2d8458d0929da9275f4a","tarball":"https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz","fileCount":12,"unpackedSize":145152,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCZa1TiiSi4KawcNsQfkQhlSQYswzCAs1f9GycASoy2wwIgFcRQYnnf50Jg4Q48jsG9b5oiFoBuf0BI3BPr/8MJwqw="}]},"_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.5.0_1775791900484_0.03414041489071806"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-06T13:30:15.285Z","modified":"2026-04-10T03:31:40.772Z","1.0.0":"2026-03-06T13:30:15.454Z","1.1.0":"2026-03-09T23:14:22.159Z","1.1.1":"2026-03-09T23:39:25.280Z","1.1.2":"2026-03-10T02:50:51.859Z","1.1.3":"2026-03-11T11:14:12.123Z","1.2.0":"2026-03-20T02:19:10.325Z","1.2.1":"2026-04-03T03:39:51.251Z","1.3.0":"2026-04-07T04:21:19.931Z","1.4.0":"2026-04-07T07:42:03.782Z","1.5.0":"2026-04-10T03:31:40.667Z"},"bugs":{"url":"https://github.com/NaturalIntelligence/path-expression-matcher/issues"},"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","homepage":"https://github.com/NaturalIntelligence/path-expression-matcher#readme","keywords":["xml","json","yaml","path","matcher","pattern","xpath","selector","parser","fast-xml-parser","fast-xml-builder"],"repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/path-expression-matcher.git"},"description":"Efficient path tracking and pattern matching for XML/JSON parsers","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"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 three 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- **`MatcherView`**: A lightweight read-only view of a `Matcher`, safe to pass to callbacks\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 ``, the second `` 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\n \n \n \n\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 **`MatcherView`** — a lightweight, live read-only view of the matcher. All query and inspection methods work normally and always reflect the current state of the underlying matcher. Mutation methods (`push`, `pop`, `reset`, `updateCurrent`, `restore`) simply don't exist on `MatcherView`, so misuse is caught at **compile time** by TypeScript rather than at runtime.\n\nThe **same instance** is returned on every call — no allocation occurs per invocation. This is the recommended way to share the matcher with callbacks, plugins, or any external code that only needs to inspect the current path.\n\n```javascript\nconst view = matcher.readOnly();\n// Same reference every time — safe to cache\nview === matcher.readOnly(); // true\n```\n\n**What works on the view:**\n\n```javascript\nview.matches(expr) // ✓ pattern matching\nview.getCurrentTag() // ✓ current tag name\nview.getCurrentNamespace() // ✓ current namespace\nview.getAttrValue(\"id\") // ✓ attribute value\nview.hasAttr(\"id\") // ✓ attribute presence check\nview.getPosition() // ✓ sibling position\nview.getCounter() // ✓ occurrence counter\nview.getDepth() // ✓ path depth\nview.toString() // ✓ path as string\nview.toArray() // ✓ path as array\n```\n\n**What doesn't exist (compile-time error in TypeScript):**\n\n```javascript\nview.push(\"child\", {}) // ✗ Property 'push' does not exist on type 'MatcherView'\nview.pop() // ✗ Property 'pop' does not exist on type 'MatcherView'\nview.reset() // ✗ Property 'reset' does not exist on type 'MatcherView'\nview.updateCurrent({}) // ✗ Property 'updateCurrent' does not exist on type 'MatcherView'\nview.restore(snapshot) // ✗ Property 'restore' does not exist on type 'MatcherView'\n```\n\n**The view is live** — it always reflects the current state of the underlying matcher.\n\n```javascript\nconst matcher = new Matcher();\nconst view = matcher.readOnly();\n\nmatcher.push(\"root\");\nview.getDepth(); // 1 — immediately reflects the push\nmatcher.push(\"users\");\nview.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 View to External Consumers\n\nWhen passing the matcher into callbacks, plugins, or other code you don't control, use `readOnly()` to get a `MatcherView` — it can inspect but never mutate parser state.\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 MatcherView — consumer can inspect but not mutate\n onTag(matcher.readOnly());\n\n matcher.pop();\n}\n\n// Safe consumer — can only read\nfunction myPlugin(view) {\n if (view.matches(adminExpr)) {\n console.log(\"Admin at path:\", view.toString());\n console.log(\"Depth:\", view.getDepth());\n console.log(\"ID:\", view.getAttrValue(\"id\"));\n }\n}\n\n// view.push(...) or view.reset() don't exist on MatcherView —\n// TypeScript catches misuse at compile time.\nparseTag(\"user\", { id: \"1\", type: \"admin\" }, myPlugin);\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 `MatcherView`.\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 `MatcherView`.\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. But can be used with any formar parser.","readmeFilename":"README.md"}