path-expression-matcher 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -138,7 +138,7 @@ console.log(matcher.toString()); // "soap:Envelope.soap:Body.ns:UserId"
138
138
  #### Constructor
139
139
 
140
140
  ```javascript
141
- new Expression(pattern, options)
141
+ new Expression(pattern, options = {}, data)
142
142
  ```
143
143
 
144
144
  **Parameters:**
@@ -149,6 +149,8 @@ new Expression(pattern, options)
149
149
  ```javascript
150
150
  const expr1 = new Expression("root.users.user");
151
151
  const expr2 = new Expression("root/users/user", { separator: '/' });
152
+ const expr3 = new Expression("root/users/user", { separator: '/' }, { extra: "data"});
153
+ console.log(expr3.data) // { extra: "data" }
152
154
  ```
153
155
 
154
156
  #### Methods
@@ -240,6 +242,22 @@ if (matcher.matches(expr)) {
240
242
  }
241
243
  ```
242
244
 
245
+ #### `matchesAny(exprSet)` → `boolean`
246
+
247
+ Please check `ExpressionSet` class for more details.
248
+
249
+ ```javascript
250
+ const matcher = new Matcher();
251
+ const exprSet = new ExpressionSet();
252
+ exprSet.add(new Expression("root.users.user"));
253
+ exprSet.add(new Expression("root.config.*"));
254
+ exprSet.seal();
255
+
256
+ if (matcher.matchesAny(exprSet)) {
257
+ // Current path matches any expression in the set
258
+ }
259
+ ```
260
+
243
261
  ##### `getCurrentTag()`
244
262
 
245
263
  Get current tag name.
@@ -674,21 +692,168 @@ for (let i = 0; i < 1000; i++) {
674
692
  }
675
693
  ```
676
694
 
677
- ### Batch Pattern Checking
695
+ ### Batch Pattern Checking with ExpressionSet (Recommended)
696
+
697
+ For checking multiple patterns on every tag, use `ExpressionSet` instead of a manual loop.
698
+ It pre-indexes expressions at build time so each call to `matchesAny()` does an O(1) bucket
699
+ lookup rather than a full O(N) scan:
678
700
 
679
701
  ```javascript
680
- // For multiple patterns, check all at once
681
- const patterns = [
682
- new Expression("..user"),
683
- new Expression("..post"),
684
- new Expression("..comment"),
685
- ];
702
+ import { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';
686
703
 
687
- function matchesAny(matcher, patterns) {
688
- return patterns.some(expr => matcher.matches(expr));
704
+ // Build once at config/startup time
705
+ const stopNodes = new ExpressionSet();
706
+ stopNodes
707
+ .add(new Expression('root.users.user'))
708
+ .add(new Expression('root.config.*'))
709
+ .add(new Expression('..script'))
710
+ .seal(); // prevent accidental mutation during parsing
711
+
712
+ // Per-tag — hot path
713
+ if (stopNodes.matchesAny(matcher)) {
714
+ // handle stop node
715
+ }
716
+ ```
717
+
718
+ This replaces the manual loop pattern:
719
+
720
+ ```javascript
721
+ // ❌ Before — O(N) per tag
722
+ function isStopNode(expressions, matcher) {
723
+ for (let i = 0; i < expressions.length; i++) {
724
+ if (matcher.matches(expressions[i])) return true;
725
+ }
726
+ return false;
689
727
  }
728
+
729
+ // ✅ After — O(1) lookup per tag
730
+ const stopNodes = new ExpressionSet();
731
+ stopNodes.addAll(expressions);
732
+ stopNodes.matchesAny(matcher);
733
+ //or matcher.matchesAny(stopNodes)
734
+ ```
735
+
736
+ ---
737
+
738
+ ## 📦 ExpressionSet API
739
+
740
+ `ExpressionSet` is an indexed collection of `Expression` objects designed for efficient
741
+ bulk matching. Build it once from your config, then call `matchesAny()` on every tag.
742
+
743
+ ### Constructor
744
+
745
+ ```javascript
746
+ const set = new ExpressionSet();
747
+ ```
748
+
749
+ ### `add(expression)` → `this`
750
+
751
+ Add a single `Expression`. Duplicate patterns (same pattern string) are silently ignored.
752
+ Returns `this` for chaining. Throws `TypeError` if the set is sealed.
753
+
754
+ ```javascript
755
+ set.add(new Expression('root.users.user'));
756
+ set.add(new Expression('..script'));
757
+ ```
758
+
759
+ ### `addAll(expressions)` → `this`
760
+
761
+ Add an array of `Expression` objects at once. Returns `this` for chaining.
762
+
763
+ ```javascript
764
+ set.addAll(config.stopNodes.map(p => new Expression(p)));
765
+ ```
766
+
767
+ ### `has(expression)` → `boolean`
768
+
769
+ Check whether an expression with the same pattern is already present.
770
+
771
+ ```javascript
772
+ set.has(new Expression('root.users.user')); // true / false
690
773
  ```
691
774
 
775
+ ### `seal()` → `this`
776
+
777
+ Prevent further additions. Any subsequent call to `add()` or `addAll()` throws a `TypeError`.
778
+ Useful to guard against accidental mutation once parsing has started.
779
+
780
+ ```javascript
781
+ const stopNodes = new ExpressionSet();
782
+ stopNodes.addAll(patterns).seal();
783
+
784
+ stopNodes.add(new Expression('root.extra')); // ❌ TypeError: ExpressionSet is sealed
785
+ ```
786
+
787
+ ### `size` → `number`
788
+
789
+ Number of distinct expressions in the set.
790
+
791
+ ```javascript
792
+ set.size; // 3
793
+ ```
794
+
795
+ ### `isSealed` → `boolean`
796
+
797
+ Whether `seal()` has been called.
798
+
799
+ ### `matchesAny(matcher)` → `boolean`
800
+
801
+ Returns `true` if the matcher's current path matches **any** expression in the set.
802
+ Accepts both a `Matcher` instance and a `ReadOnlyMatcher` view.
803
+
804
+ ```javascript
805
+ if (stopNodes.matchesAny(matcher)) { /* ... */ }
806
+ if (stopNodes.matchesAny(matcher.readOnly())) { /* ... */ } // also works
807
+ ```
808
+
809
+ **How indexing works:** expressions are bucketed at `add()` time, not at match time.
810
+
811
+ | Expression type | Bucket | Lookup cost |
812
+ |---|---|---|
813
+ | Fixed path, concrete tag (`root.users.user`) | `depth:tag` map | O(1) |
814
+ | Fixed path, wildcard tag (`root.config.*`) | `depth` map | O(1) |
815
+ | Deep wildcard (`..script`) | flat list | O(D) — always scanned |
816
+
817
+ In practice, deep-wildcard expressions are rare in configs, so the list stays small.
818
+
819
+ ### `findMatch(matcher)` → `Expression`
820
+
821
+ Returns the Expression instance that matched the current path. Accepts both a `Matcher` instance and a `ReadOnlyMatcher` view.
822
+
823
+ ```javascript
824
+ const node = stopNodes.findMatch(matcher);
825
+ ```
826
+
827
+
828
+ ### Example 7: ExpressionSet in a real parser loop
829
+
830
+ ```javascript
831
+ import { XMLParser } from 'fast-xml-parser';
832
+ import { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';
833
+
834
+ // Config-time setup
835
+ const stopNodes = new ExpressionSet();
836
+ stopNodes
837
+ .addAll(['script', 'style'].map(t => new Expression(`..${t}`)))
838
+ .seal();
839
+
840
+ const matcher = new Matcher();
841
+
842
+ const parser = new XMLParser({
843
+ onOpenTag(tagName, attrs) {
844
+ matcher.push(tagName, attrs);
845
+ if (stopNodes.matchesAny(matcher)) {
846
+ // treat as stop node
847
+ }
848
+ },
849
+ onCloseTag() {
850
+ matcher.pop();
851
+ },
852
+ });
853
+ ```
854
+
855
+
856
+
692
857
  ## 🔗 Integration with fast-xml-parser
693
858
 
694
859
  **Basic integration:**
package/lib/pem.cjs CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";var t={d:(e,s)=>{for(var i in s)t.o(s,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:s[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{Expression:()=>s,Matcher:()=>n,default:()=>r});class s{constructor(t,e={}){this.pattern=t,this.separator=e.separator||".",this.segments=this._parse(t),this._hasDeepWildcard=this.segments.some(t=>"deep-wildcard"===t.type),this._hasAttributeCondition=this.segments.some(t=>void 0!==t.attrName),this._hasPositionSelector=this.segments.some(t=>void 0!==t.position)}_parse(t){const e=[];let s=0,i="";for(;s<t.length;)t[s]===this.separator?s+1<t.length&&t[s+1]===this.separator?(i.trim()&&(e.push(this._parseSegment(i.trim())),i=""),e.push({type:"deep-wildcard"}),s+=2):(i.trim()&&e.push(this._parseSegment(i.trim())),i="",s++):(i+=t[s],s++);return i.trim()&&e.push(this._parseSegment(i.trim())),e}_parseSegment(t){const e={type:"tag"};let s=null,i=t;const n=t.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);if(n&&(i=n[1]+n[3],n[2])){const t=n[2].slice(1,-1);t&&(s=t)}let r,a,h=i;if(i.includes("::")){const e=i.indexOf("::");if(r=i.substring(0,e).trim(),h=i.substring(e+2).trim(),!r)throw new Error(`Invalid namespace in pattern: ${t}`)}let o=null;if(h.includes(":")){const t=h.lastIndexOf(":"),e=h.substring(0,t).trim(),s=h.substring(t+1).trim();["first","last","odd","even"].includes(s)||/^nth\(\d+\)$/.test(s)?(a=e,o=s):a=h}else a=h;if(!a)throw new Error(`Invalid segment pattern: ${t}`);if(e.tag=a,r&&(e.namespace=r),s)if(s.includes("=")){const t=s.indexOf("=");e.attrName=s.substring(0,t).trim(),e.attrValue=s.substring(t+1).trim()}else e.attrName=s.trim();if(o){const t=o.match(/^nth\((\d+)\)$/);t?(e.position="nth",e.positionValue=parseInt(t[1],10)):e.position=o}return e}get length(){return this.segments.length}hasDeepWildcard(){return this._hasDeepWildcard}hasAttributeCondition(){return this._hasAttributeCondition}hasPositionSelector(){return this._hasPositionSelector}toString(){return this.pattern}}const i=new Set(["push","pop","reset","updateCurrent","restore"]);class n{constructor(t={}){this.separator=t.separator||".",this.path=[],this.siblingStacks=[]}push(t,e=null,s=null){this._pathStringCache=null,this.path.length>0&&(this.path[this.path.length-1].values=void 0);const i=this.path.length;this.siblingStacks[i]||(this.siblingStacks[i]=new Map);const n=this.siblingStacks[i],r=s?`${s}:${t}`:t,a=n.get(r)||0;let h=0;for(const t of n.values())h+=t;n.set(r,a+1);const o={tag:t,position:h,counter:a};null!=s&&(o.namespace=s),null!=e&&(o.values=e),this.path.push(o)}pop(){if(0===this.path.length)return;this._pathStringCache=null;const t=this.path.pop();return this.siblingStacks.length>this.path.length+1&&(this.siblingStacks.length=this.path.length+1),t}updateCurrent(t){if(this.path.length>0){const e=this.path[this.path.length-1];null!=t&&(e.values=t)}}getCurrentTag(){return this.path.length>0?this.path[this.path.length-1].tag:void 0}getCurrentNamespace(){return this.path.length>0?this.path[this.path.length-1].namespace:void 0}getAttrValue(t){if(0===this.path.length)return;const e=this.path[this.path.length-1];return e.values?.[t]}hasAttr(t){if(0===this.path.length)return!1;const e=this.path[this.path.length-1];return void 0!==e.values&&t in e.values}getPosition(){return 0===this.path.length?-1:this.path[this.path.length-1].position??0}getCounter(){return 0===this.path.length?-1:this.path[this.path.length-1].counter??0}getIndex(){return this.getPosition()}getDepth(){return this.path.length}toString(t,e=!0){const s=t||this.separator;if(s===this.separator&&!0===e){if(null!==this._pathStringCache&&void 0!==this._pathStringCache)return this._pathStringCache;const t=this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s);return this._pathStringCache=t,t}return this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s)}toArray(){return this.path.map(t=>t.tag)}reset(){this._pathStringCache=null,this.path=[],this.siblingStacks=[]}matches(t){const e=t.segments;return 0!==e.length&&(t.hasDeepWildcard()?this._matchWithDeepWildcard(e):this._matchSimple(e))}_matchSimple(t){if(this.path.length!==t.length)return!1;for(let e=0;e<t.length;e++){const s=t[e],i=this.path[e],n=e===this.path.length-1;if(!this._matchSegment(s,i,n))return!1}return!0}_matchWithDeepWildcard(t){let e=this.path.length-1,s=t.length-1;for(;s>=0&&e>=0;){const i=t[s];if("deep-wildcard"===i.type){if(s--,s<0)return!0;const i=t[s];let n=!1;for(let t=e;t>=0;t--){const r=t===this.path.length-1;if(this._matchSegment(i,this.path[t],r)){e=t-1,s--,n=!0;break}}if(!n)return!1}else{const t=e===this.path.length-1;if(!this._matchSegment(i,this.path[e],t))return!1;e--,s--}}return s<0}_matchSegment(t,e,s){if("*"!==t.tag&&t.tag!==e.tag)return!1;if(void 0!==t.namespace&&"*"!==t.namespace&&t.namespace!==e.namespace)return!1;if(void 0!==t.attrName){if(!s)return!1;if(!e.values||!(t.attrName in e.values))return!1;if(void 0!==t.attrValue){const s=e.values[t.attrName];if(String(s)!==String(t.attrValue))return!1}}if(void 0!==t.position){if(!s)return!1;const i=e.counter??0;if("first"===t.position&&0!==i)return!1;if("odd"===t.position&&i%2!=1)return!1;if("even"===t.position&&i%2!=0)return!1;if("nth"===t.position&&i!==t.positionValue)return!1}return!0}snapshot(){return{path:this.path.map(t=>({...t})),siblingStacks:this.siblingStacks.map(t=>new Map(t))}}restore(t){this._pathStringCache=null,this.path=t.path.map(t=>({...t})),this.siblingStacks=t.siblingStacks.map(t=>new Map(t))}readOnly(){return new Proxy(this,{get(t,e,s){if(i.has(e))return()=>{throw new TypeError(`Cannot call '${e}' on a read-only Matcher. Obtain a writable instance to mutate state.`)};const n=Reflect.get(t,e,s);return"path"===e||"siblingStacks"===e?Object.freeze(Array.isArray(n)?n.map(t=>t instanceof Map?Object.freeze(new Map(t)):Object.freeze({...t})):n):"function"==typeof n?n.bind(t):n},set(t,e){throw new TypeError(`Cannot set property '${String(e)}' on a read-only Matcher.`)},deleteProperty(t,e){throw new TypeError(`Cannot delete property '${String(e)}' from a read-only Matcher.`)}})}}const r={Expression:s,Matcher:n};module.exports=e})();
1
+ (()=>{"use strict";var t={d:(e,s)=>{for(var n in s)t.o(s,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:s[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{Expression:()=>s,ExpressionSet:()=>r,Matcher:()=>i,default:()=>a});class s{constructor(t,e={},s){this.pattern=t,this.separator=e.separator||".",this.segments=this._parse(t),this.data=s,this._hasDeepWildcard=this.segments.some(t=>"deep-wildcard"===t.type),this._hasAttributeCondition=this.segments.some(t=>void 0!==t.attrName),this._hasPositionSelector=this.segments.some(t=>void 0!==t.position)}_parse(t){const e=[];let s=0,n="";for(;s<t.length;)t[s]===this.separator?s+1<t.length&&t[s+1]===this.separator?(n.trim()&&(e.push(this._parseSegment(n.trim())),n=""),e.push({type:"deep-wildcard"}),s+=2):(n.trim()&&e.push(this._parseSegment(n.trim())),n="",s++):(n+=t[s],s++);return n.trim()&&e.push(this._parseSegment(n.trim())),e}_parseSegment(t){const e={type:"tag"};let s=null,n=t;const i=t.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);if(i&&(n=i[1]+i[3],i[2])){const t=i[2].slice(1,-1);t&&(s=t)}let r,a,h=n;if(n.includes("::")){const e=n.indexOf("::");if(r=n.substring(0,e).trim(),h=n.substring(e+2).trim(),!r)throw new Error(`Invalid namespace in pattern: ${t}`)}let l=null;if(h.includes(":")){const t=h.lastIndexOf(":"),e=h.substring(0,t).trim(),s=h.substring(t+1).trim();["first","last","odd","even"].includes(s)||/^nth\(\d+\)$/.test(s)?(a=e,l=s):a=h}else a=h;if(!a)throw new Error(`Invalid segment pattern: ${t}`);if(e.tag=a,r&&(e.namespace=r),s)if(s.includes("=")){const t=s.indexOf("=");e.attrName=s.substring(0,t).trim(),e.attrValue=s.substring(t+1).trim()}else e.attrName=s.trim();if(l){const t=l.match(/^nth\((\d+)\)$/);t?(e.position="nth",e.positionValue=parseInt(t[1],10)):e.position=l}return e}get length(){return this.segments.length}hasDeepWildcard(){return this._hasDeepWildcard}hasAttributeCondition(){return this._hasAttributeCondition}hasPositionSelector(){return this._hasPositionSelector}toString(){return this.pattern}}const n=new Set(["push","pop","reset","updateCurrent","restore"]);class i{constructor(t={}){this.separator=t.separator||".",this.path=[],this.siblingStacks=[],this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null}push(t,e=null,s=null){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path.length>0&&(this.path[this.path.length-1].values=void 0);const n=this.path.length;this.siblingStacks[n]||(this.siblingStacks[n]=new Map);const i=this.siblingStacks[n],r=s?`${s}:${t}`:t,a=i.get(r)||0;let h=0;for(const t of i.values())h+=t;i.set(r,a+1);const l={tag:t,position:h,counter:a};null!=s&&(l.namespace=s),null!=e&&(l.values=e),this.path.push(l)}pop(){if(0===this.path.length)return;this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null;const t=this.path.pop();return this.siblingStacks.length>this.path.length+1&&(this.siblingStacks.length=this.path.length+1),t}updateCurrent(t){if(this.path.length>0){const e=this.path[this.path.length-1];null!=t&&(e.values=t,this._frozenPathCache=null)}}getCurrentTag(){return this.path.length>0?this.path[this.path.length-1].tag:void 0}getCurrentNamespace(){return this.path.length>0?this.path[this.path.length-1].namespace:void 0}getAttrValue(t){if(0===this.path.length)return;const e=this.path[this.path.length-1];return e.values?.[t]}hasAttr(t){if(0===this.path.length)return!1;const e=this.path[this.path.length-1];return void 0!==e.values&&t in e.values}getPosition(){return 0===this.path.length?-1:this.path[this.path.length-1].position??0}getCounter(){return 0===this.path.length?-1:this.path[this.path.length-1].counter??0}getIndex(){return this.getPosition()}getDepth(){return this.path.length}toString(t,e=!0){const s=t||this.separator;if(s===this.separator&&!0===e){if(null!==this._pathStringCache&&void 0!==this._pathStringCache)return this._pathStringCache;const t=this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s);return this._pathStringCache=t,t}return this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s)}toArray(){return this.path.map(t=>t.tag)}reset(){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path=[],this.siblingStacks=[]}matches(t){const e=t.segments;return 0!==e.length&&(t.hasDeepWildcard()?this._matchWithDeepWildcard(e):this._matchSimple(e))}_matchSimple(t){if(this.path.length!==t.length)return!1;for(let e=0;e<t.length;e++){const s=t[e],n=this.path[e],i=e===this.path.length-1;if(!this._matchSegment(s,n,i))return!1}return!0}_matchWithDeepWildcard(t){let e=this.path.length-1,s=t.length-1;for(;s>=0&&e>=0;){const n=t[s];if("deep-wildcard"===n.type){if(s--,s<0)return!0;const n=t[s];let i=!1;for(let t=e;t>=0;t--){const r=t===this.path.length-1;if(this._matchSegment(n,this.path[t],r)){e=t-1,s--,i=!0;break}}if(!i)return!1}else{const t=e===this.path.length-1;if(!this._matchSegment(n,this.path[e],t))return!1;e--,s--}}return s<0}_matchSegment(t,e,s){if("*"!==t.tag&&t.tag!==e.tag)return!1;if(void 0!==t.namespace&&"*"!==t.namespace&&t.namespace!==e.namespace)return!1;if(void 0!==t.attrName){if(!s)return!1;if(!e.values||!(t.attrName in e.values))return!1;if(void 0!==t.attrValue){const s=e.values[t.attrName];if(String(s)!==String(t.attrValue))return!1}}if(void 0!==t.position){if(!s)return!1;const n=e.counter??0;if("first"===t.position&&0!==n)return!1;if("odd"===t.position&&n%2!=1)return!1;if("even"===t.position&&n%2!=0)return!1;if("nth"===t.position&&n!==t.positionValue)return!1}return!0}matchesAny(t){return t.matchesAny(this)}snapshot(){return{path:this.path.map(t=>({...t})),siblingStacks:this.siblingStacks.map(t=>new Map(t))}}restore(t){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path=t.path.map(t=>({...t})),this.siblingStacks=t.siblingStacks.map(t=>new Map(t))}readOnly(){return new Proxy(this,{get(t,e,s){if(n.has(e))return()=>{throw new TypeError(`Cannot call '${e}' on a read-only Matcher. Obtain a writable instance to mutate state.`)};if("path"===e)return null===t._frozenPathCache&&(t._frozenPathCache=Object.freeze(t.path.map(t=>Object.freeze({...t})))),t._frozenPathCache;if("siblingStacks"===e)return null===t._frozenSiblingsCache&&(t._frozenSiblingsCache=Object.freeze(t.siblingStacks.map(t=>Object.freeze(new Map(t))))),t._frozenSiblingsCache;const i=Reflect.get(t,e,s);return"function"==typeof i?i.bind(t):i},set(t,e){throw new TypeError(`Cannot set property '${String(e)}' on a read-only Matcher.`)},deleteProperty(t,e){throw new TypeError(`Cannot delete property '${String(e)}' from a read-only Matcher.`)}})}}class r{constructor(){this._byDepthAndTag=new Map,this._wildcardByDepth=new Map,this._deepWildcards=[],this._patterns=new Set,this._sealed=!1}add(t){if(this._sealed)throw new TypeError("ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.");if(this._patterns.has(t.pattern))return this;if(this._patterns.add(t.pattern),t.hasDeepWildcard())return this._deepWildcards.push(t),this;const e=t.length,s=t.segments[t.segments.length-1],n=s?.tag;if(n&&"*"!==n){const s=`${e}:${n}`;this._byDepthAndTag.has(s)||this._byDepthAndTag.set(s,[]),this._byDepthAndTag.get(s).push(t)}else this._wildcardByDepth.has(e)||this._wildcardByDepth.set(e,[]),this._wildcardByDepth.get(e).push(t);return this}addAll(t){for(const e of t)this.add(e);return this}has(t){return this._patterns.has(t.pattern)}get size(){return this._patterns.size}seal(){return this._sealed=!0,this}get isSealed(){return this._sealed}matchesAny(t){return null!==this.findMatch(t)}findMatch(t){const e=t.getDepth(),s=`${e}:${t.getCurrentTag()}`,n=this._byDepthAndTag.get(s);if(n)for(let e=0;e<n.length;e++)if(t.matches(n[e]))return n[e];const i=this._wildcardByDepth.get(e);if(i)for(let e=0;e<i.length;e++)if(t.matches(i[e]))return i[e];for(let e=0;e<this._deepWildcards.length;e++)if(t.matches(this._deepWildcards[e]))return this._deepWildcards[e];return null}}const a={Expression:s,Matcher:i,ExpressionSet:r};module.exports=e})();
package/lib/pem.d.cts CHANGED
@@ -299,6 +299,13 @@ declare interface ReadOnlyMatcher {
299
299
  */
300
300
  matches(expression: Expression): boolean;
301
301
 
302
+ /**
303
+ * Test whether the matcher's current path matches **any** expression in the set.
304
+ *
305
+ * @param exprSet - A `ExpressionSet` instance
306
+ * @returns `true` if at least one expression matches the current path
307
+ */
308
+ matchesAny(exprSet: ExpressionSet): boolean;
302
309
  /**
303
310
  * Create a snapshot of current state
304
311
  * @returns State snapshot that can be restored later
@@ -490,6 +497,31 @@ declare class Matcher {
490
497
  */
491
498
  matches(expression: Expression): boolean;
492
499
 
500
+
501
+ /**
502
+ * Test whether the matcher's current path matches **any** expression in the set.
503
+ *
504
+ * Uses the pre-built index to evaluate only the relevant bucket(s):
505
+ * 1. Exact depth + tag — O(1) lookup
506
+ * 2. Depth-matched wildcard tag — O(1) lookup
507
+ * 3. Deep-wildcard expressions — always scanned (typically a small list)
508
+ *
509
+ * @param exprSet - A `ExpressionSet` instance
510
+ * @returns `true` if at least one expression matches the current path
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * // Replaces:
515
+ * // for (const expr of stopNodeExpressions) {
516
+ * // if (matcher.matches(expr)) return true;
517
+ * // }
518
+ *
519
+ * if (matcher.matchesAny(stopNodes)) {
520
+ * // current tag is a stop node
521
+ * }
522
+ * ```
523
+ */
524
+ matchesAny(exprSet: ExpressionSet): boolean;
493
525
  /**
494
526
  * Create a snapshot of current state
495
527
  * @returns State snapshot that can be restored later
@@ -508,10 +540,89 @@ declare class Matcher {
508
540
  readOnly(): ReadOnlyMatcher;
509
541
  }
510
542
 
543
+ /**
544
+ * ExpressionSet - An indexed collection of Expressions for efficient bulk matching
545
+ *
546
+ * Pre-indexes expressions at insertion time by depth and terminal tag name so
547
+ * that `matchesAny()` performs an O(1) bucket lookup rather than a full O(E)
548
+ * linear scan on every tag.
549
+ *
550
+ * @example
551
+ * ```javascript
552
+ * const { Expression, ExpressionSet, Matcher } = require('path-expression-matcher');
553
+ *
554
+ * // Build once at config time
555
+ * const stopNodes = new ExpressionSet();
556
+ * stopNodes
557
+ * .add(new Expression('root.users.user'))
558
+ * .add(new Expression('root.config.*'))
559
+ * .add(new Expression('..script'))
560
+ * .seal();
561
+ *
562
+ * // Per-tag — hot path
563
+ * if (stopNodes.matchesAny(matcher)) { ... }
564
+ * ```
565
+ */
566
+ declare class ExpressionSet {
567
+ constructor();
568
+
569
+ /** Number of expressions currently in the set. */
570
+ readonly size: number;
571
+
572
+ /** Whether the set has been sealed against further modifications. */
573
+ readonly isSealed: boolean;
574
+
575
+ /**
576
+ * Add a single Expression. Duplicate patterns are silently ignored.
577
+ * @throws {TypeError} if the set has been sealed
578
+ */
579
+ add(expression: Expression): this;
580
+
581
+ /**
582
+ * Add multiple expressions at once.
583
+ * @throws {TypeError} if the set has been sealed
584
+ */
585
+ addAll(expressions: Expression[]): this;
586
+
587
+ /** Check whether an expression with the same pattern is already present. */
588
+ has(expression: Expression): boolean;
589
+
590
+ /**
591
+ * Seal the set against further modifications.
592
+ * Any subsequent call to add() or addAll() will throw a TypeError.
593
+ */
594
+ seal(): this;
595
+
596
+ /**
597
+ * Test whether the matcher's current path matches any expression in the set.
598
+ * Accepts both a Matcher instance and a ReadOnlyMatcher view.
599
+ *
600
+ *
601
+ * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
602
+ * @returns Expression if at least one expression matches the current path
603
+ */
604
+ matchesAny(matcher: Matcher | ReadOnlyMatcher): boolean;
605
+
606
+ /**
607
+ * Find the first expression in the set that matches the matcher's current path.
608
+
609
+ *
610
+ * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
611
+ * @returns Expression if at least one expression matches the current path
612
+ *
613
+ * @example
614
+ * ```typescript
615
+ * const node = stopNodes.findMatch(matcher);
616
+ * ```
617
+ */
618
+ findMatch(matcher: Matcher | ReadOnlyMatcher): Expression;
619
+ }
620
+
511
621
  declare namespace pathExpressionMatcher {
512
622
  export {
513
623
  Expression,
514
624
  Matcher,
625
+ ExpressionSet,
515
626
  ExpressionOptions,
516
627
  MatcherOptions,
517
628
  Segment,
package/lib/pem.min.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.pem=e():t.pem=e()}(this,()=>(()=>{"use strict";var t={d:(e,s)=>{for(var n in s)t.o(s,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:s[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{Expression:()=>s,Matcher:()=>i,default:()=>r});class s{constructor(t,e={}){this.pattern=t,this.separator=e.separator||".",this.segments=this._parse(t),this._hasDeepWildcard=this.segments.some(t=>"deep-wildcard"===t.type),this._hasAttributeCondition=this.segments.some(t=>void 0!==t.attrName),this._hasPositionSelector=this.segments.some(t=>void 0!==t.position)}_parse(t){const e=[];let s=0,n="";for(;s<t.length;)t[s]===this.separator?s+1<t.length&&t[s+1]===this.separator?(n.trim()&&(e.push(this._parseSegment(n.trim())),n=""),e.push({type:"deep-wildcard"}),s+=2):(n.trim()&&e.push(this._parseSegment(n.trim())),n="",s++):(n+=t[s],s++);return n.trim()&&e.push(this._parseSegment(n.trim())),e}_parseSegment(t){const e={type:"tag"};let s=null,n=t;const i=t.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);if(i&&(n=i[1]+i[3],i[2])){const t=i[2].slice(1,-1);t&&(s=t)}let r,a,h=n;if(n.includes("::")){const e=n.indexOf("::");if(r=n.substring(0,e).trim(),h=n.substring(e+2).trim(),!r)throw new Error(`Invalid namespace in pattern: ${t}`)}let o=null;if(h.includes(":")){const t=h.lastIndexOf(":"),e=h.substring(0,t).trim(),s=h.substring(t+1).trim();["first","last","odd","even"].includes(s)||/^nth\(\d+\)$/.test(s)?(a=e,o=s):a=h}else a=h;if(!a)throw new Error(`Invalid segment pattern: ${t}`);if(e.tag=a,r&&(e.namespace=r),s)if(s.includes("=")){const t=s.indexOf("=");e.attrName=s.substring(0,t).trim(),e.attrValue=s.substring(t+1).trim()}else e.attrName=s.trim();if(o){const t=o.match(/^nth\((\d+)\)$/);t?(e.position="nth",e.positionValue=parseInt(t[1],10)):e.position=o}return e}get length(){return this.segments.length}hasDeepWildcard(){return this._hasDeepWildcard}hasAttributeCondition(){return this._hasAttributeCondition}hasPositionSelector(){return this._hasPositionSelector}toString(){return this.pattern}}const n=new Set(["push","pop","reset","updateCurrent","restore"]);class i{constructor(t={}){this.separator=t.separator||".",this.path=[],this.siblingStacks=[]}push(t,e=null,s=null){this._pathStringCache=null,this.path.length>0&&(this.path[this.path.length-1].values=void 0);const n=this.path.length;this.siblingStacks[n]||(this.siblingStacks[n]=new Map);const i=this.siblingStacks[n],r=s?`${s}:${t}`:t,a=i.get(r)||0;let h=0;for(const t of i.values())h+=t;i.set(r,a+1);const o={tag:t,position:h,counter:a};null!=s&&(o.namespace=s),null!=e&&(o.values=e),this.path.push(o)}pop(){if(0===this.path.length)return;this._pathStringCache=null;const t=this.path.pop();return this.siblingStacks.length>this.path.length+1&&(this.siblingStacks.length=this.path.length+1),t}updateCurrent(t){if(this.path.length>0){const e=this.path[this.path.length-1];null!=t&&(e.values=t)}}getCurrentTag(){return this.path.length>0?this.path[this.path.length-1].tag:void 0}getCurrentNamespace(){return this.path.length>0?this.path[this.path.length-1].namespace:void 0}getAttrValue(t){if(0===this.path.length)return;const e=this.path[this.path.length-1];return e.values?.[t]}hasAttr(t){if(0===this.path.length)return!1;const e=this.path[this.path.length-1];return void 0!==e.values&&t in e.values}getPosition(){return 0===this.path.length?-1:this.path[this.path.length-1].position??0}getCounter(){return 0===this.path.length?-1:this.path[this.path.length-1].counter??0}getIndex(){return this.getPosition()}getDepth(){return this.path.length}toString(t,e=!0){const s=t||this.separator;if(s===this.separator&&!0===e){if(null!==this._pathStringCache&&void 0!==this._pathStringCache)return this._pathStringCache;const t=this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s);return this._pathStringCache=t,t}return this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s)}toArray(){return this.path.map(t=>t.tag)}reset(){this._pathStringCache=null,this.path=[],this.siblingStacks=[]}matches(t){const e=t.segments;return 0!==e.length&&(t.hasDeepWildcard()?this._matchWithDeepWildcard(e):this._matchSimple(e))}_matchSimple(t){if(this.path.length!==t.length)return!1;for(let e=0;e<t.length;e++){const s=t[e],n=this.path[e],i=e===this.path.length-1;if(!this._matchSegment(s,n,i))return!1}return!0}_matchWithDeepWildcard(t){let e=this.path.length-1,s=t.length-1;for(;s>=0&&e>=0;){const n=t[s];if("deep-wildcard"===n.type){if(s--,s<0)return!0;const n=t[s];let i=!1;for(let t=e;t>=0;t--){const r=t===this.path.length-1;if(this._matchSegment(n,this.path[t],r)){e=t-1,s--,i=!0;break}}if(!i)return!1}else{const t=e===this.path.length-1;if(!this._matchSegment(n,this.path[e],t))return!1;e--,s--}}return s<0}_matchSegment(t,e,s){if("*"!==t.tag&&t.tag!==e.tag)return!1;if(void 0!==t.namespace&&"*"!==t.namespace&&t.namespace!==e.namespace)return!1;if(void 0!==t.attrName){if(!s)return!1;if(!e.values||!(t.attrName in e.values))return!1;if(void 0!==t.attrValue){const s=e.values[t.attrName];if(String(s)!==String(t.attrValue))return!1}}if(void 0!==t.position){if(!s)return!1;const n=e.counter??0;if("first"===t.position&&0!==n)return!1;if("odd"===t.position&&n%2!=1)return!1;if("even"===t.position&&n%2!=0)return!1;if("nth"===t.position&&n!==t.positionValue)return!1}return!0}snapshot(){return{path:this.path.map(t=>({...t})),siblingStacks:this.siblingStacks.map(t=>new Map(t))}}restore(t){this._pathStringCache=null,this.path=t.path.map(t=>({...t})),this.siblingStacks=t.siblingStacks.map(t=>new Map(t))}readOnly(){return new Proxy(this,{get(t,e,s){if(n.has(e))return()=>{throw new TypeError(`Cannot call '${e}' on a read-only Matcher. Obtain a writable instance to mutate state.`)};const i=Reflect.get(t,e,s);return"path"===e||"siblingStacks"===e?Object.freeze(Array.isArray(i)?i.map(t=>t instanceof Map?Object.freeze(new Map(t)):Object.freeze({...t})):i):"function"==typeof i?i.bind(t):i},set(t,e){throw new TypeError(`Cannot set property '${String(e)}' on a read-only Matcher.`)},deleteProperty(t,e){throw new TypeError(`Cannot delete property '${String(e)}' from a read-only Matcher.`)}})}}const r={Expression:s,Matcher:i};return e})());
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.pem=e():t.pem=e()}(this,()=>(()=>{"use strict";var t={d:(e,s)=>{for(var n in s)t.o(s,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:s[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{Expression:()=>s,ExpressionSet:()=>r,Matcher:()=>i,default:()=>a});class s{constructor(t,e={},s){this.pattern=t,this.separator=e.separator||".",this.segments=this._parse(t),this.data=s,this._hasDeepWildcard=this.segments.some(t=>"deep-wildcard"===t.type),this._hasAttributeCondition=this.segments.some(t=>void 0!==t.attrName),this._hasPositionSelector=this.segments.some(t=>void 0!==t.position)}_parse(t){const e=[];let s=0,n="";for(;s<t.length;)t[s]===this.separator?s+1<t.length&&t[s+1]===this.separator?(n.trim()&&(e.push(this._parseSegment(n.trim())),n=""),e.push({type:"deep-wildcard"}),s+=2):(n.trim()&&e.push(this._parseSegment(n.trim())),n="",s++):(n+=t[s],s++);return n.trim()&&e.push(this._parseSegment(n.trim())),e}_parseSegment(t){const e={type:"tag"};let s=null,n=t;const i=t.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);if(i&&(n=i[1]+i[3],i[2])){const t=i[2].slice(1,-1);t&&(s=t)}let r,a,h=n;if(n.includes("::")){const e=n.indexOf("::");if(r=n.substring(0,e).trim(),h=n.substring(e+2).trim(),!r)throw new Error(`Invalid namespace in pattern: ${t}`)}let o=null;if(h.includes(":")){const t=h.lastIndexOf(":"),e=h.substring(0,t).trim(),s=h.substring(t+1).trim();["first","last","odd","even"].includes(s)||/^nth\(\d+\)$/.test(s)?(a=e,o=s):a=h}else a=h;if(!a)throw new Error(`Invalid segment pattern: ${t}`);if(e.tag=a,r&&(e.namespace=r),s)if(s.includes("=")){const t=s.indexOf("=");e.attrName=s.substring(0,t).trim(),e.attrValue=s.substring(t+1).trim()}else e.attrName=s.trim();if(o){const t=o.match(/^nth\((\d+)\)$/);t?(e.position="nth",e.positionValue=parseInt(t[1],10)):e.position=o}return e}get length(){return this.segments.length}hasDeepWildcard(){return this._hasDeepWildcard}hasAttributeCondition(){return this._hasAttributeCondition}hasPositionSelector(){return this._hasPositionSelector}toString(){return this.pattern}}const n=new Set(["push","pop","reset","updateCurrent","restore"]);class i{constructor(t={}){this.separator=t.separator||".",this.path=[],this.siblingStacks=[],this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null}push(t,e=null,s=null){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path.length>0&&(this.path[this.path.length-1].values=void 0);const n=this.path.length;this.siblingStacks[n]||(this.siblingStacks[n]=new Map);const i=this.siblingStacks[n],r=s?`${s}:${t}`:t,a=i.get(r)||0;let h=0;for(const t of i.values())h+=t;i.set(r,a+1);const o={tag:t,position:h,counter:a};null!=s&&(o.namespace=s),null!=e&&(o.values=e),this.path.push(o)}pop(){if(0===this.path.length)return;this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null;const t=this.path.pop();return this.siblingStacks.length>this.path.length+1&&(this.siblingStacks.length=this.path.length+1),t}updateCurrent(t){if(this.path.length>0){const e=this.path[this.path.length-1];null!=t&&(e.values=t,this._frozenPathCache=null)}}getCurrentTag(){return this.path.length>0?this.path[this.path.length-1].tag:void 0}getCurrentNamespace(){return this.path.length>0?this.path[this.path.length-1].namespace:void 0}getAttrValue(t){if(0===this.path.length)return;const e=this.path[this.path.length-1];return e.values?.[t]}hasAttr(t){if(0===this.path.length)return!1;const e=this.path[this.path.length-1];return void 0!==e.values&&t in e.values}getPosition(){return 0===this.path.length?-1:this.path[this.path.length-1].position??0}getCounter(){return 0===this.path.length?-1:this.path[this.path.length-1].counter??0}getIndex(){return this.getPosition()}getDepth(){return this.path.length}toString(t,e=!0){const s=t||this.separator;if(s===this.separator&&!0===e){if(null!==this._pathStringCache&&void 0!==this._pathStringCache)return this._pathStringCache;const t=this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s);return this._pathStringCache=t,t}return this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s)}toArray(){return this.path.map(t=>t.tag)}reset(){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path=[],this.siblingStacks=[]}matches(t){const e=t.segments;return 0!==e.length&&(t.hasDeepWildcard()?this._matchWithDeepWildcard(e):this._matchSimple(e))}_matchSimple(t){if(this.path.length!==t.length)return!1;for(let e=0;e<t.length;e++){const s=t[e],n=this.path[e],i=e===this.path.length-1;if(!this._matchSegment(s,n,i))return!1}return!0}_matchWithDeepWildcard(t){let e=this.path.length-1,s=t.length-1;for(;s>=0&&e>=0;){const n=t[s];if("deep-wildcard"===n.type){if(s--,s<0)return!0;const n=t[s];let i=!1;for(let t=e;t>=0;t--){const r=t===this.path.length-1;if(this._matchSegment(n,this.path[t],r)){e=t-1,s--,i=!0;break}}if(!i)return!1}else{const t=e===this.path.length-1;if(!this._matchSegment(n,this.path[e],t))return!1;e--,s--}}return s<0}_matchSegment(t,e,s){if("*"!==t.tag&&t.tag!==e.tag)return!1;if(void 0!==t.namespace&&"*"!==t.namespace&&t.namespace!==e.namespace)return!1;if(void 0!==t.attrName){if(!s)return!1;if(!e.values||!(t.attrName in e.values))return!1;if(void 0!==t.attrValue){const s=e.values[t.attrName];if(String(s)!==String(t.attrValue))return!1}}if(void 0!==t.position){if(!s)return!1;const n=e.counter??0;if("first"===t.position&&0!==n)return!1;if("odd"===t.position&&n%2!=1)return!1;if("even"===t.position&&n%2!=0)return!1;if("nth"===t.position&&n!==t.positionValue)return!1}return!0}matchesAny(t){return t.matchesAny(this)}snapshot(){return{path:this.path.map(t=>({...t})),siblingStacks:this.siblingStacks.map(t=>new Map(t))}}restore(t){this._pathStringCache=null,this._frozenPathCache=null,this._frozenSiblingsCache=null,this.path=t.path.map(t=>({...t})),this.siblingStacks=t.siblingStacks.map(t=>new Map(t))}readOnly(){return new Proxy(this,{get(t,e,s){if(n.has(e))return()=>{throw new TypeError(`Cannot call '${e}' on a read-only Matcher. Obtain a writable instance to mutate state.`)};if("path"===e)return null===t._frozenPathCache&&(t._frozenPathCache=Object.freeze(t.path.map(t=>Object.freeze({...t})))),t._frozenPathCache;if("siblingStacks"===e)return null===t._frozenSiblingsCache&&(t._frozenSiblingsCache=Object.freeze(t.siblingStacks.map(t=>Object.freeze(new Map(t))))),t._frozenSiblingsCache;const i=Reflect.get(t,e,s);return"function"==typeof i?i.bind(t):i},set(t,e){throw new TypeError(`Cannot set property '${String(e)}' on a read-only Matcher.`)},deleteProperty(t,e){throw new TypeError(`Cannot delete property '${String(e)}' from a read-only Matcher.`)}})}}class r{constructor(){this._byDepthAndTag=new Map,this._wildcardByDepth=new Map,this._deepWildcards=[],this._patterns=new Set,this._sealed=!1}add(t){if(this._sealed)throw new TypeError("ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.");if(this._patterns.has(t.pattern))return this;if(this._patterns.add(t.pattern),t.hasDeepWildcard())return this._deepWildcards.push(t),this;const e=t.length,s=t.segments[t.segments.length-1],n=s?.tag;if(n&&"*"!==n){const s=`${e}:${n}`;this._byDepthAndTag.has(s)||this._byDepthAndTag.set(s,[]),this._byDepthAndTag.get(s).push(t)}else this._wildcardByDepth.has(e)||this._wildcardByDepth.set(e,[]),this._wildcardByDepth.get(e).push(t);return this}addAll(t){for(const e of t)this.add(e);return this}has(t){return this._patterns.has(t.pattern)}get size(){return this._patterns.size}seal(){return this._sealed=!0,this}get isSealed(){return this._sealed}matchesAny(t){return null!==this.findMatch(t)}findMatch(t){const e=t.getDepth(),s=`${e}:${t.getCurrentTag()}`,n=this._byDepthAndTag.get(s);if(n)for(let e=0;e<n.length;e++)if(t.matches(n[e]))return n[e];const i=this._wildcardByDepth.get(e);if(i)for(let e=0;e<i.length;e++)if(t.matches(i[e]))return i[e];for(let e=0;e<this._deepWildcards.length;e++)if(t.matches(this._deepWildcards[e]))return this._deepWildcards[e];return null}}const a={Expression:s,Matcher:i,ExpressionSet:r};return e})());
2
2
  //# sourceMappingURL=pem.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"./lib/pem.min.js","mappings":"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","sources":["webpack://pem/webpack/universalModuleDefinition","webpack://pem/webpack/bootstrap","webpack://pem/webpack/runtime/define property getters","webpack://pem/webpack/runtime/hasOwnProperty shorthand","webpack://pem/webpack/runtime/make namespace object","webpack://pem/./src/Expression.js","webpack://pem/./src/Matcher.js","webpack://pem/./src/index.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"pem\"] = factory();\n\telse\n\t\troot[\"pem\"] = factory();\n})(this, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions\n * \n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n * \n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n * \n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\n\n/**\n * Names of methods that mutate Matcher state.\n * Any attempt to call these on a read-only view throws a TypeError.\n * @type {Set<string>}\n */\nconst MUTATING_METHODS = new Set(['push', 'pop', 'reset', 'updateCurrent', 'restore']);\n\nexport default class Matcher {\n /**\n * Create a new Matcher\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Default path separator (default: '.')\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag: string, values: object, position: number, counter: number }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n }\n\n /**\n * Push a new tag onto the path\n * @param {string} tagName - Name of the tag\n * @param {Object} attrValues - Attribute key-value pairs for current node (optional)\n * @param {string} namespace - Namespace for the tag (optional)\n */\n push(tagName, attrValues = null, namespace = null) {\n this._pathStringCache = null; // invalidate\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n const prev = this.path[this.path.length - 1];\n prev.values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n if (!this.siblingStacks[currentLevel]) {\n this.siblingStacks[currentLevel] = new Map();\n }\n\n const siblings = this.siblingStacks[currentLevel];\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = siblings.get(siblingKey) || 0;\n\n // Calculate position (total children at this level so far)\n let position = 0;\n for (const count of siblings.values()) {\n position += count;\n }\n\n // Update sibling count for this tag\n siblings.set(siblingKey, counter + 1);\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n // Store namespace if provided\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n // Store values only for current node\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n }\n\n /**\n * Pop the last tag from the path\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) {\n return undefined;\n }\n this._pathStringCache = null; // invalidate\n const node = this.path.pop();\n\n // Clean up sibling tracking for levels deeper than current\n // After pop, path.length is the new depth\n // We need to clean up siblingStacks[path.length + 1] and beyond\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values\n * Useful when attributes are parsed after push\n * @param {Object} attrValues - Attribute values\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n }\n }\n }\n\n /**\n * Get current tag name\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value\n * @param {string} attrName - Attribute name\n * @returns {*} Attribute value or undefined\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n const current = this.path[this.path.length - 1];\n return current.values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute\n * @param {string} attrName - Attribute name\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get current node's sibling position (child index in parent)\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name)\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition for backward compatibility)\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string\n * @param {string} separator - Optional separator (uses default if not provided)\n * @param {boolean} includeNamespace - Whether to include namespace in output (default: true)\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null && this._pathStringCache !== undefined) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n // Non-default separator or includeNamespace=false: don't cache (rare case)\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty\n */\n reset() {\n this._pathStringCache = null; // invalidate\n this.path = [];\n this.siblingStacks = [];\n }\n\n /**\n * Match current path against an Expression\n * @param {Expression} expression - The expression to match against\n * @returns {boolean} True if current path matches the expression\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n // Handle deep wildcard patterns\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n // Simple path matching (no deep wildcards)\n return this._matchSimple(segments);\n }\n\n /**\n * Match simple path (no deep wildcards)\n * @private\n */\n _matchSimple(segments) {\n // Path must be same length as segments\n if (this.path.length !== segments.length) {\n return false;\n }\n\n // Match each segment bottom-to-top\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n const node = this.path[i];\n const isCurrentNode = (i === this.path.length - 1);\n\n if (!this._matchSegment(segment, node, isCurrentNode)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match path with deep wildcards\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1; // Start from current node (bottom)\n let segIdx = segments.length - 1; // Start from last segment\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n // \"..\" matches zero or more levels\n segIdx--;\n\n if (segIdx < 0) {\n // Pattern ends with \"..\", always matches\n return true;\n }\n\n // Find where next segment matches in the path\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n const isCurrentNode = (i === this.path.length - 1);\n if (this._matchSegment(nextSeg, this.path[i], isCurrentNode)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n // Regular segment\n const isCurrentNode = (pathIdx === this.path.length - 1);\n if (!this._matchSegment(segment, this.path[pathIdx], isCurrentNode)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n // All segments must be consumed\n return segIdx < 0;\n }\n\n /**\n * Match a single segment against a node\n * @private\n * @param {Object} segment - Segment from Expression\n * @param {Object} node - Node from path\n * @param {boolean} isCurrentNode - Whether this is the current (last) node\n * @returns {boolean}\n */\n _matchSegment(segment, node, isCurrentNode) {\n // Match tag name (* is wildcard)\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n // Match namespace if specified in segment\n if (segment.namespace !== undefined) {\n // Segment has namespace - node must match it\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n // If segment has no namespace, it matches nodes with or without namespace\n\n // Match attribute name (check if node has this attribute)\n // Can only check for current node since ancestors don't have values\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n // Can't check attributes for ancestor nodes (values not stored)\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n // Match attribute value (only possible for current node)\n if (segment.attrValue !== undefined) {\n const actualValue = node.values[segment.attrName];\n // Both should be strings\n if (String(actualValue) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n // Match position (only for current node)\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n // Can't check position for ancestor nodes\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth') {\n if (counter !== segment.positionValue) {\n return false;\n }\n }\n }\n\n return true;\n }\n\n /**\n * Create a snapshot of current state\n * @returns {Object} State snapshot\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(map => new Map(map))\n };\n }\n\n /**\n * Restore state from snapshot\n * @param {Object} snapshot - State snapshot\n */\n restore(snapshot) {\n this._pathStringCache = null; // invalidate\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(map => new Map(map));\n }\n\n /**\n * Return a read-only view of this matcher.\n *\n * The returned object exposes all query/inspection methods but throws a\n * TypeError if any state-mutating method is called (`push`, `pop`, `reset`,\n * `updateCurrent`, `restore`). Property reads (e.g. `.path`, `.separator`)\n * are allowed but the returned arrays/objects are frozen so callers cannot\n * mutate internal state through them either.\n *\n * @returns {ReadOnlyMatcher} A proxy that forwards read operations and blocks writes.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n *\n * const ro = matcher.readOnly();\n * ro.matches(expr); // ✓ works\n * ro.getCurrentTag(); // ✓ works\n * ro.push(\"child\", {}); // ✗ throws TypeError\n * ro.reset(); // ✗ throws TypeError\n */\n readOnly() {\n const self = this;\n\n return new Proxy(self, {\n get(target, prop, receiver) {\n // Block mutating methods\n if (MUTATING_METHODS.has(prop)) {\n return () => {\n throw new TypeError(\n `Cannot call '${prop}' on a read-only Matcher. ` +\n `Obtain a writable instance to mutate state.`\n );\n };\n }\n\n const value = Reflect.get(target, prop, receiver);\n\n // Freeze array/object properties so callers can't mutate internal\n // state through direct property access (e.g. matcher.path.push(...))\n if (prop === 'path' || prop === 'siblingStacks') {\n return Object.freeze(\n Array.isArray(value)\n ? value.map(item =>\n item instanceof Map\n ? Object.freeze(new Map(item)) // freeze a copy of each Map\n : Object.freeze({ ...item }) // freeze a copy of each node\n )\n : value\n );\n }\n\n // Bind methods so `this` inside them still refers to the real Matcher\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n\n // Prevent any property assignment on the read-only view\n set(_target, prop) {\n throw new TypeError(\n `Cannot set property '${String(prop)}' on a read-only Matcher.`\n );\n },\n\n // Prevent property deletion\n deleteProperty(_target, prop) {\n throw new TypeError(\n `Cannot delete property '${String(prop)}' from a read-only Matcher.`\n );\n }\n });\n }\n}","/**\n * fast-xml-tagger - XML/JSON path matching library\n * \n * Provides efficient path tracking and pattern matching for XML/JSON parsers.\n * \n * @example\n * import { Expression, Matcher } from 'fast-xml-tagger';\n * \n * // Create expression (parse once)\n * const expr = new Expression(\"root.users.user[id]\");\n * \n * // Create matcher (track path)\n * const matcher = new Matcher();\n * matcher.push(\"root\", [], {}, 0);\n * matcher.push(\"users\", [], {}, 0);\n * matcher.push(\"user\", [\"id\", \"type\"], { id: \"123\", type: \"admin\" }, 0);\n * \n * // Match\n * if (matcher.matches(expr)) {\n * console.log(\"Match found!\");\n * }\n */\n\nimport Expression from './Expression.js';\nimport Matcher from './Matcher.js';\n\nexport { Expression, Matcher };\nexport default { Expression, Matcher };\n"],"names":["root","factory","exports","module","define","amd","this","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","Expression","constructor","pattern","options","separator","segments","_parse","_hasDeepWildcard","some","seg","type","_hasAttributeCondition","undefined","attrName","_hasPositionSelector","position","i","currentPart","length","trim","push","_parseSegment","part","segment","bracketContent","withoutBrackets","bracketMatch","match","content","slice","namespace","tag","tagAndPosition","includes","nsIndex","indexOf","substring","Error","positionMatch","colonIndex","lastIndexOf","tagPart","posPart","test","eqIndex","attrValue","nthMatch","positionValue","parseInt","hasDeepWildcard","hasAttributeCondition","hasPositionSelector","toString","MUTATING_METHODS","Set","Matcher","path","siblingStacks","tagName","attrValues","_pathStringCache","values","currentLevel","Map","siblings","siblingKey","counter","count","set","node","pop","updateCurrent","current","getCurrentTag","getCurrentNamespace","getAttrValue","hasAttr","getPosition","getCounter","getIndex","getDepth","includeNamespace","sep","result","map","n","join","toArray","reset","matches","expression","_matchWithDeepWildcard","_matchSimple","isCurrentNode","_matchSegment","pathIdx","segIdx","nextSeg","found","actualValue","String","snapshot","restore","readOnly","Proxy","target","receiver","has","TypeError","Reflect","freeze","Array","isArray","item","bind","_target","deleteProperty"],"sourceRoot":""}
1
+ {"version":3,"file":"./lib/pem.min.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAa,IAAID,IAEjBD,EAAU,IAAIC,GACf,CATD,CASGK,KAAM,I,mBCRT,IAAIC,EAAsB,CCA1BA,EAAwB,CAACL,EAASM,KACjC,IAAI,IAAIC,KAAOD,EACXD,EAAoBG,EAAEF,EAAYC,KAASF,EAAoBG,EAAER,EAASO,IAC5EE,OAAOC,eAAeV,EAASO,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3EF,EAAwB,CAACQ,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFT,EAAyBL,IACH,oBAAXkB,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeV,EAASkB,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeV,EAAS,aAAc,CAAEoB,OAAO,M,sFCKxC,MAAMC,EAOnBC,WAAAA,CAAYC,EAASC,EAAU,CAAC,EAAGC,GACjCrB,KAAKmB,QAAUA,EACfnB,KAAKsB,UAAYF,EAAQE,WAAa,IACtCtB,KAAKuB,SAAWvB,KAAKwB,OAAOL,GAC5BnB,KAAKqB,KAAOA,EAEZrB,KAAKyB,iBAAmBzB,KAAKuB,SAASG,KAAKC,GAAoB,kBAAbA,EAAIC,MACtD5B,KAAK6B,uBAAyB7B,KAAKuB,SAASG,KAAKC,QAAwBG,IAAjBH,EAAII,UAC5D/B,KAAKgC,qBAAuBhC,KAAKuB,SAASG,KAAKC,QAAwBG,IAAjBH,EAAIM,SAC5D,CAQAT,MAAAA,CAAOL,GACL,MAAMI,EAAW,GAGjB,IAAIW,EAAI,EACJC,EAAc,GAElB,KAAOD,EAAIf,EAAQiB,QACbjB,EAAQe,KAAOlC,KAAKsB,UAElBY,EAAI,EAAIf,EAAQiB,QAAUjB,EAAQe,EAAI,KAAOlC,KAAKsB,WAEhDa,EAAYE,SACdd,EAASe,KAAKtC,KAAKuC,cAAcJ,EAAYE,SAC7CF,EAAc,IAGhBZ,EAASe,KAAK,CAAEV,KAAM,kBACtBM,GAAK,IAGDC,EAAYE,QACdd,EAASe,KAAKtC,KAAKuC,cAAcJ,EAAYE,SAE/CF,EAAc,GACdD,MAGFC,GAAehB,EAAQe,GACvBA,KASJ,OAJIC,EAAYE,QACdd,EAASe,KAAKtC,KAAKuC,cAAcJ,EAAYE,SAGxCd,CACT,CAQAgB,aAAAA,CAAcC,GACZ,MAAMC,EAAU,CAAEb,KAAM,OAwBxB,IAAIc,EAAiB,KACjBC,EAAkBH,EAEtB,MAAMI,EAAeJ,EAAKK,MAAM,8BAChC,GAAID,IACFD,EAAkBC,EAAa,GAAKA,EAAa,GAC7CA,EAAa,IAAI,CACnB,MAAME,EAAUF,EAAa,GAAGG,MAAM,GAAI,GACtCD,IACFJ,EAAiBI,EAErB,CAIF,IAAIE,EAcAC,EAbAC,EAAiBP,EAErB,GAAIA,EAAgBQ,SAAS,MAAO,CAClC,MAAMC,EAAUT,EAAgBU,QAAQ,MAIxC,GAHAL,EAAYL,EAAgBW,UAAU,EAAGF,GAASf,OAClDa,EAAiBP,EAAgBW,UAAUF,EAAU,GAAGf,QAEnDW,EACH,MAAM,IAAIO,MAAM,iCAAiCf,IAErD,CAIA,IAAIgB,EAAgB,KAEpB,GAAIN,EAAeC,SAAS,KAAM,CAChC,MAAMM,EAAaP,EAAeQ,YAAY,KACxCC,EAAUT,EAAeI,UAAU,EAAGG,GAAYpB,OAClDuB,EAAUV,EAAeI,UAAUG,EAAa,GAAGpB,OAG/B,CAAC,QAAS,OAAQ,MAAO,QAAQc,SAASS,IAClE,eAAeC,KAAKD,IAGpBX,EAAMU,EACNH,EAAgBI,GAGhBX,EAAMC,CAEV,MACED,EAAMC,EAGR,IAAKD,EACH,MAAM,IAAIM,MAAM,4BAA4Bf,KAS9C,GANAC,EAAQQ,IAAMA,EACVD,IACFP,EAAQO,UAAYA,GAIlBN,EACF,GAAIA,EAAeS,SAAS,KAAM,CAChC,MAAMW,EAAUpB,EAAeW,QAAQ,KACvCZ,EAAQV,SAAWW,EAAeY,UAAU,EAAGQ,GAASzB,OACxDI,EAAQsB,UAAYrB,EAAeY,UAAUQ,EAAU,GAAGzB,MAC5D,MACEI,EAAQV,SAAWW,EAAeL,OAKtC,GAAImB,EAAe,CACjB,MAAMQ,EAAWR,EAAcX,MAAM,kBACjCmB,GACFvB,EAAQR,SAAW,MACnBQ,EAAQwB,cAAgBC,SAASF,EAAS,GAAI,KAE9CvB,EAAQR,SAAWuB,CAEvB,CAEA,OAAOf,CACT,CAMA,UAAIL,GACF,OAAOpC,KAAKuB,SAASa,MACvB,CAMA+B,eAAAA,GACE,OAAOnE,KAAKyB,gBACd,CAMA2C,qBAAAA,GACE,OAAOpE,KAAK6B,sBACd,CAMAwC,mBAAAA,GACE,OAAOrE,KAAKgC,oBACd,CAMAsC,QAAAA,GACE,OAAOtE,KAAKmB,OACd,EC/MF,MAAMoD,EAAmB,IAAIC,IAAI,CAAC,OAAQ,MAAO,QAAS,gBAAiB,YAE5D,MAAMC,EAMnBvD,WAAAA,CAAYE,EAAU,CAAC,GACrBpB,KAAKsB,UAAYF,EAAQE,WAAa,IACtCtB,KAAK0E,KAAO,GACZ1E,KAAK2E,cAAgB,GAIrB3E,KAAK4E,iBAAmB,KACxB5E,KAAK6E,iBAAmB,KACxB7E,KAAK8E,qBAAuB,IAC9B,CAQAxC,IAAAA,CAAKyC,EAASC,EAAa,KAAMhC,EAAY,MAE3ChD,KAAK4E,iBAAmB,KACxB5E,KAAK6E,iBAAmB,KACxB7E,KAAK8E,qBAAuB,KAExB9E,KAAK0E,KAAKtC,OAAS,IACRpC,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GACrC6C,YAASnD,GAIhB,MAAMoD,EAAelF,KAAK0E,KAAKtC,OAC1BpC,KAAK2E,cAAcO,KACtBlF,KAAK2E,cAAcO,GAAgB,IAAIC,KAGzC,MAAMC,EAAWpF,KAAK2E,cAAcO,GAG9BG,EAAarC,EAAY,GAAGA,KAAa+B,IAAYA,EAGrDO,EAAUF,EAAS5E,IAAI6E,IAAe,EAG5C,IAAIpD,EAAW,EACf,IAAK,MAAMsD,KAASH,EAASH,SAC3BhD,GAAYsD,EAIdH,EAASI,IAAIH,EAAYC,EAAU,GAGnC,MAAMG,EAAO,CACXxC,IAAK8B,EACL9C,SAAUA,EACVqD,QAASA,GAIPtC,UACFyC,EAAKzC,UAAYA,GAIfgC,UACFS,EAAKR,OAASD,GAGhBhF,KAAK0E,KAAKpC,KAAKmD,EACjB,CAMAC,GAAAA,GACE,GAAyB,IAArB1F,KAAK0E,KAAKtC,OAAc,OAE5BpC,KAAK4E,iBAAmB,KACxB5E,KAAK6E,iBAAmB,KACxB7E,KAAK8E,qBAAuB,KAC5B,MAAMW,EAAOzF,KAAK0E,KAAKgB,MASvB,OAJI1F,KAAK2E,cAAcvC,OAASpC,KAAK0E,KAAKtC,OAAS,IACjDpC,KAAK2E,cAAcvC,OAASpC,KAAK0E,KAAKtC,OAAS,GAG1CqD,CACT,CAOAE,aAAAA,CAAcX,GACZ,GAAIhF,KAAK0E,KAAKtC,OAAS,EAAG,CACxB,MAAMwD,EAAU5F,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GACzC4C,UACFY,EAAQX,OAASD,EACjBhF,KAAK6E,iBAAmB,KAE5B,CACF,CAMAgB,aAAAA,GACE,OAAO7F,KAAK0E,KAAKtC,OAAS,EAAIpC,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAAGa,SAAMnB,CACtE,CAMAgE,mBAAAA,GACE,OAAO9F,KAAK0E,KAAKtC,OAAS,EAAIpC,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAAGY,eAAYlB,CAC5E,CAOAiE,YAAAA,CAAahE,GACX,GAAyB,IAArB/B,KAAK0E,KAAKtC,OAAc,OAC5B,MAAMwD,EAAU5F,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAC7C,OAAOwD,EAAQX,SAASlD,EAC1B,CAOAiE,OAAAA,CAAQjE,GACN,GAAyB,IAArB/B,KAAK0E,KAAKtC,OAAc,OAAO,EACnC,MAAMwD,EAAU5F,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAC7C,YAA0BN,IAAnB8D,EAAQX,QAAwBlD,KAAY6D,EAAQX,MAC7D,CAMAgB,WAAAA,GACE,OAAyB,IAArBjG,KAAK0E,KAAKtC,QAAsB,EAC7BpC,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAAGH,UAAY,CACrD,CAMAiE,UAAAA,GACE,OAAyB,IAArBlG,KAAK0E,KAAKtC,QAAsB,EAC7BpC,KAAK0E,KAAK1E,KAAK0E,KAAKtC,OAAS,GAAGkD,SAAW,CACpD,CAOAa,QAAAA,GACE,OAAOnG,KAAKiG,aACd,CAMAG,QAAAA,GACE,OAAOpG,KAAK0E,KAAKtC,MACnB,CAQAkC,QAAAA,CAAShD,EAAW+E,GAAmB,GACrC,MAAMC,EAAMhF,GAAatB,KAAKsB,UAG9B,GAFmBgF,IAAQtG,KAAKsB,YAAkC,IAArB+E,EAE9B,CACb,GAA8B,OAA1BrG,KAAK4E,uBAAuD9C,IAA1B9B,KAAK4E,iBACzC,OAAO5E,KAAK4E,iBAEd,MAAM2B,EAASvG,KAAK0E,KAAK8B,IAAIC,GAC1BJ,GAAoBI,EAAEzD,UAAa,GAAGyD,EAAEzD,aAAayD,EAAExD,MAAQwD,EAAExD,KAClEyD,KAAKJ,GAEP,OADAtG,KAAK4E,iBAAmB2B,EACjBA,CACT,CAGA,OAAOvG,KAAK0E,KAAK8B,IAAIC,GAClBJ,GAAoBI,EAAEzD,UAAa,GAAGyD,EAAEzD,aAAayD,EAAExD,MAAQwD,EAAExD,KAClEyD,KAAKJ,EACT,CAMAK,OAAAA,GACE,OAAO3G,KAAK0E,KAAK8B,IAAIC,GAAKA,EAAExD,IAC9B,CAKA2D,KAAAA,GAEE5G,KAAK4E,iBAAmB,KACxB5E,KAAK6E,iBAAmB,KACxB7E,KAAK8E,qBAAuB,KAC5B9E,KAAK0E,KAAO,GACZ1E,KAAK2E,cAAgB,EACvB,CAOAkC,OAAAA,CAAQC,GACN,MAAMvF,EAAWuF,EAAWvF,SAE5B,OAAwB,IAApBA,EAASa,SAKT0E,EAAW3C,kBACNnE,KAAK+G,uBAAuBxF,GAI9BvB,KAAKgH,aAAazF,GAC3B,CAMAyF,YAAAA,CAAazF,GAEX,GAAIvB,KAAK0E,KAAKtC,SAAWb,EAASa,OAChC,OAAO,EAIT,IAAK,IAAIF,EAAI,EAAGA,EAAIX,EAASa,OAAQF,IAAK,CACxC,MAAMO,EAAUlB,EAASW,GACnBuD,EAAOzF,KAAK0E,KAAKxC,GACjB+E,EAAiB/E,IAAMlC,KAAK0E,KAAKtC,OAAS,EAEhD,IAAKpC,KAAKkH,cAAczE,EAASgD,EAAMwB,GACrC,OAAO,CAEX,CAEA,OAAO,CACT,CAMAF,sBAAAA,CAAuBxF,GACrB,IAAI4F,EAAUnH,KAAK0E,KAAKtC,OAAS,EAC7BgF,EAAS7F,EAASa,OAAS,EAE/B,KAAOgF,GAAU,GAAKD,GAAW,GAAG,CAClC,MAAM1E,EAAUlB,EAAS6F,GAEzB,GAAqB,kBAAjB3E,EAAQb,KAA0B,CAIpC,GAFAwF,IAEIA,EAAS,EAEX,OAAO,EAIT,MAAMC,EAAU9F,EAAS6F,GACzB,IAAIE,GAAQ,EAEZ,IAAK,IAAIpF,EAAIiF,EAASjF,GAAK,EAAGA,IAAK,CACjC,MAAM+E,EAAiB/E,IAAMlC,KAAK0E,KAAKtC,OAAS,EAChD,GAAIpC,KAAKkH,cAAcG,EAASrH,KAAK0E,KAAKxC,GAAI+E,GAAgB,CAC5DE,EAAUjF,EAAI,EACdkF,IACAE,GAAQ,EACR,KACF,CACF,CAEA,IAAKA,EACH,OAAO,CAEX,KAAO,CAEL,MAAML,EAAiBE,IAAYnH,KAAK0E,KAAKtC,OAAS,EACtD,IAAKpC,KAAKkH,cAAczE,EAASzC,KAAK0E,KAAKyC,GAAUF,GACnD,OAAO,EAETE,IACAC,GACF,CACF,CAGA,OAAOA,EAAS,CAClB,CAUAF,aAAAA,CAAczE,EAASgD,EAAMwB,GAE3B,GAAoB,MAAhBxE,EAAQQ,KAAeR,EAAQQ,MAAQwC,EAAKxC,IAC9C,OAAO,EAIT,QAA0BnB,IAAtBW,EAAQO,WAEgB,MAAtBP,EAAQO,WAAqBP,EAAQO,YAAcyC,EAAKzC,UAC1D,OAAO,EAOX,QAAyBlB,IAArBW,EAAQV,SAAwB,CAClC,IAAKkF,EAEH,OAAO,EAGT,IAAKxB,EAAKR,UAAYxC,EAAQV,YAAY0D,EAAKR,QAC7C,OAAO,EAIT,QAA0BnD,IAAtBW,EAAQsB,UAAyB,CACnC,MAAMwD,EAAc9B,EAAKR,OAAOxC,EAAQV,UAExC,GAAIyF,OAAOD,KAAiBC,OAAO/E,EAAQsB,WACzC,OAAO,CAEX,CACF,CAGA,QAAyBjC,IAArBW,EAAQR,SAAwB,CAClC,IAAKgF,EAEH,OAAO,EAGT,MAAM3B,EAAUG,EAAKH,SAAW,EAEhC,GAAyB,UAArB7C,EAAQR,UAAoC,IAAZqD,EAClC,OAAO,EACF,GAAyB,QAArB7C,EAAQR,UAAsBqD,EAAU,GAAM,EACvD,OAAO,EACF,GAAyB,SAArB7C,EAAQR,UAAuBqD,EAAU,GAAM,EACxD,OAAO,EACF,GAAyB,QAArB7C,EAAQR,UACbqD,IAAY7C,EAAQwB,cACtB,OAAO,CAGb,CAEA,OAAO,CACT,CAOAwD,UAAAA,CAAWC,GACT,OAAOA,EAAQD,WAAWzH,KAC5B,CAMA2H,QAAAA,GACE,MAAO,CACLjD,KAAM1E,KAAK0E,KAAK8B,IAAIf,IAAQ,IAAMA,KAClCd,cAAe3E,KAAK2E,cAAc6B,IAAIA,GAAO,IAAIrB,IAAIqB,IAEzD,CAMAoB,OAAAA,CAAQD,GAEN3H,KAAK4E,iBAAmB,KACxB5E,KAAK6E,iBAAmB,KACxB7E,KAAK8E,qBAAuB,KAC5B9E,KAAK0E,KAAOiD,EAASjD,KAAK8B,IAAIf,IAAQ,IAAMA,KAC5CzF,KAAK2E,cAAgBgD,EAAShD,cAAc6B,IAAIA,GAAO,IAAIrB,IAAIqB,GACjE,CAuBAqB,QAAAA,GAGE,OAAO,IAAIC,MAFE9H,KAEU,CACrBQ,GAAAA,CAAIuH,EAAQrH,EAAMsH,GAEhB,GAAIzD,EAAiB0D,IAAIvH,GACvB,MAAO,KACL,MAAM,IAAIwH,UACR,gBAAgBxH,2EAOtB,GAAa,SAATA,EAMF,OALgC,OAA5BqH,EAAOlD,mBACTkD,EAAOlD,iBAAmBxE,OAAO8H,OAC/BJ,EAAOrD,KAAK8B,IAAIf,GAAQpF,OAAO8H,OAAO,IAAK1C,OAGxCsC,EAAOlD,iBAIhB,GAAa,kBAATnE,EAMF,OALoC,OAAhCqH,EAAOjD,uBACTiD,EAAOjD,qBAAuBzE,OAAO8H,OACnCJ,EAAOpD,cAAc6B,IAAIA,GAAOnG,OAAO8H,OAAO,IAAIhD,IAAIqB,OAGnDuB,EAAOjD,qBAGhB,MAAM9D,EAAQoH,QAAQ5H,IAAIuH,EAAQrH,EAAMsH,GAGxC,MAAqB,mBAAVhH,EACFA,EAAMqH,KAAKN,GAGb/G,CACT,EAGAwE,GAAAA,CAAI8C,EAAS5H,GACX,MAAM,IAAIwH,UACR,wBAAwBV,OAAO9G,8BAEnC,EAGA6H,cAAAA,CAAeD,EAAS5H,GACtB,MAAM,IAAIwH,UACR,2BAA2BV,OAAO9G,gCAEtC,GAEJ,ECngBa,MAAM8H,EACnBtH,WAAAA,GAEElB,KAAKyI,eAAiB,IAAItD,IAG1BnF,KAAK0I,iBAAmB,IAAIvD,IAG5BnF,KAAK2I,eAAiB,GAGtB3I,KAAK4I,UAAY,IAAIpE,IAGrBxE,KAAK6I,SAAU,CACjB,CAcAC,GAAAA,CAAIhC,GACF,GAAI9G,KAAK6I,QACP,MAAM,IAAIX,UACR,gFAKJ,GAAIlI,KAAK4I,UAAUX,IAAInB,EAAW3F,SAAU,OAAOnB,KAGnD,GAFAA,KAAK4I,UAAUE,IAAIhC,EAAW3F,SAE1B2F,EAAW3C,kBAEb,OADAnE,KAAK2I,eAAerG,KAAKwE,GAClB9G,KAGT,MAAM+I,EAAQjC,EAAW1E,OACnB4G,EAAUlC,EAAWvF,SAASuF,EAAWvF,SAASa,OAAS,GAC3Da,EAAM+F,GAAS/F,IAErB,GAAKA,GAAe,MAARA,EAIL,CAEL,MAAM9C,EAAM,GAAG4I,KAAS9F,IACnBjD,KAAKyI,eAAeR,IAAI9H,IAAMH,KAAKyI,eAAejD,IAAIrF,EAAK,IAChEH,KAAKyI,eAAejI,IAAIL,GAAKmC,KAAKwE,EACpC,MAPO9G,KAAK0I,iBAAiBT,IAAIc,IAAQ/I,KAAK0I,iBAAiBlD,IAAIuD,EAAO,IACxE/I,KAAK0I,iBAAiBlI,IAAIuI,GAAOzG,KAAKwE,GAQxC,OAAO9G,IACT,CAcAiJ,MAAAA,CAAOC,GACL,IAAK,MAAMC,KAAQD,EAAalJ,KAAK8I,IAAIK,GACzC,OAAOnJ,IACT,CAQAiI,GAAAA,CAAInB,GACF,OAAO9G,KAAK4I,UAAUX,IAAInB,EAAW3F,QACvC,CAMA,QAAIiI,GACF,OAAOpJ,KAAK4I,UAAUQ,IACxB,CASAC,IAAAA,GAEE,OADArJ,KAAK6I,SAAU,EACR7I,IACT,CAMA,YAAIsJ,GACF,OAAOtJ,KAAK6I,OACd,CAkBApB,UAAAA,CAAW8B,GACT,OAAmC,OAA5BvJ,KAAKwJ,UAAUD,EACxB,CAkBAC,SAAAA,CAAUD,GACR,MAAMR,EAAQQ,EAAQnD,WAIhBqD,EAAW,GAAGV,KAHRQ,EAAQ1D,kBAId6D,EAAc1J,KAAKyI,eAAejI,IAAIiJ,GAC5C,GAAIC,EACF,IAAK,IAAIxH,EAAI,EAAGA,EAAIwH,EAAYtH,OAAQF,IACtC,GAAIqH,EAAQ1C,QAAQ6C,EAAYxH,IAAK,OAAOwH,EAAYxH,GAK5D,MAAMyH,EAAiB3J,KAAK0I,iBAAiBlI,IAAIuI,GACjD,GAAIY,EACF,IAAK,IAAIzH,EAAI,EAAGA,EAAIyH,EAAevH,OAAQF,IACzC,GAAIqH,EAAQ1C,QAAQ8C,EAAezH,IAAK,OAAOyH,EAAezH,GAKlE,IAAK,IAAIA,EAAI,EAAGA,EAAIlC,KAAK2I,eAAevG,OAAQF,IAC9C,GAAIqH,EAAQ1C,QAAQ7G,KAAK2I,eAAezG,IAAK,OAAOlC,KAAK2I,eAAezG,GAG1E,OAAO,IACT,ECnLF,SAAiBjB,WAAU,EAAEwD,QAAO,EAAE+D,cAAaA,G","sources":["webpack://pem/webpack/universalModuleDefinition","webpack://pem/webpack/bootstrap","webpack://pem/webpack/runtime/define property getters","webpack://pem/webpack/runtime/hasOwnProperty shorthand","webpack://pem/webpack/runtime/make namespace object","webpack://pem/./src/Expression.js","webpack://pem/./src/Matcher.js","webpack://pem/./src/ExpressionSet.js","webpack://pem/./src/index.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"pem\"] = factory();\n\telse\n\t\troot[\"pem\"] = factory();\n})(this, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}, data) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n this.data = data;\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","import ExpressionSet from \"./ExpressionSet.js\";\n/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions\n * \n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n * \n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n * \n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\n\n/**\n * Names of methods that mutate Matcher state.\n * Any attempt to call these on a read-only view throws a TypeError.\n * @type {Set<string>}\n */\nconst MUTATING_METHODS = new Set(['push', 'pop', 'reset', 'updateCurrent', 'restore']);\n\nexport default class Matcher {\n /**\n * Create a new Matcher\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Default path separator (default: '.')\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag: string, values: object, position: number, counter: number }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n this._pathStringCache = null;\n this._frozenPathCache = null; // cache for readOnly().path\n this._frozenSiblingsCache = null; // cache for readOnly().siblingStacks\n }\n\n /**\n * Push a new tag onto the path\n * @param {string} tagName - Name of the tag\n * @param {Object} attrValues - Attribute key-value pairs for current node (optional)\n * @param {string} namespace - Namespace for the tag (optional)\n */\n push(tagName, attrValues = null, namespace = null) {\n //invalidate cache\n this._pathStringCache = null;\n this._frozenPathCache = null;\n this._frozenSiblingsCache = null;\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n const prev = this.path[this.path.length - 1];\n prev.values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n if (!this.siblingStacks[currentLevel]) {\n this.siblingStacks[currentLevel] = new Map();\n }\n\n const siblings = this.siblingStacks[currentLevel];\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = siblings.get(siblingKey) || 0;\n\n // Calculate position (total children at this level so far)\n let position = 0;\n for (const count of siblings.values()) {\n position += count;\n }\n\n // Update sibling count for this tag\n siblings.set(siblingKey, counter + 1);\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n // Store namespace if provided\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n // Store values only for current node\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n }\n\n /**\n * Pop the last tag from the path\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) return undefined;\n //invalidate cache\n this._pathStringCache = null;\n this._frozenPathCache = null;\n this._frozenSiblingsCache = null;\n const node = this.path.pop();\n\n // Clean up sibling tracking for levels deeper than current\n // After pop, path.length is the new depth\n // We need to clean up siblingStacks[path.length + 1] and beyond\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values\n * Useful when attributes are parsed after push\n * @param {Object} attrValues - Attribute values\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n this._frozenPathCache = null;\n }\n }\n }\n\n /**\n * Get current tag name\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value\n * @param {string} attrName - Attribute name\n * @returns {*} Attribute value or undefined\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n const current = this.path[this.path.length - 1];\n return current.values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute\n * @param {string} attrName - Attribute name\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get current node's sibling position (child index in parent)\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name)\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition for backward compatibility)\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string\n * @param {string} separator - Optional separator (uses default if not provided)\n * @param {boolean} includeNamespace - Whether to include namespace in output (default: true)\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null && this._pathStringCache !== undefined) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n // Non-default separator or includeNamespace=false: don't cache (rare case)\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty\n */\n reset() {\n //invalidate cache\n this._pathStringCache = null;\n this._frozenPathCache = null;\n this._frozenSiblingsCache = null;\n this.path = [];\n this.siblingStacks = [];\n }\n\n /**\n * Match current path against an Expression\n * @param {Expression} expression - The expression to match against\n * @returns {boolean} True if current path matches the expression\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n // Handle deep wildcard patterns\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n // Simple path matching (no deep wildcards)\n return this._matchSimple(segments);\n }\n\n /**\n * Match simple path (no deep wildcards)\n * @private\n */\n _matchSimple(segments) {\n // Path must be same length as segments\n if (this.path.length !== segments.length) {\n return false;\n }\n\n // Match each segment bottom-to-top\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n const node = this.path[i];\n const isCurrentNode = (i === this.path.length - 1);\n\n if (!this._matchSegment(segment, node, isCurrentNode)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match path with deep wildcards\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1; // Start from current node (bottom)\n let segIdx = segments.length - 1; // Start from last segment\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n // \"..\" matches zero or more levels\n segIdx--;\n\n if (segIdx < 0) {\n // Pattern ends with \"..\", always matches\n return true;\n }\n\n // Find where next segment matches in the path\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n const isCurrentNode = (i === this.path.length - 1);\n if (this._matchSegment(nextSeg, this.path[i], isCurrentNode)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n // Regular segment\n const isCurrentNode = (pathIdx === this.path.length - 1);\n if (!this._matchSegment(segment, this.path[pathIdx], isCurrentNode)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n // All segments must be consumed\n return segIdx < 0;\n }\n\n /**\n * Match a single segment against a node\n * @private\n * @param {Object} segment - Segment from Expression\n * @param {Object} node - Node from path\n * @param {boolean} isCurrentNode - Whether this is the current (last) node\n * @returns {boolean}\n */\n _matchSegment(segment, node, isCurrentNode) {\n // Match tag name (* is wildcard)\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n // Match namespace if specified in segment\n if (segment.namespace !== undefined) {\n // Segment has namespace - node must match it\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n // If segment has no namespace, it matches nodes with or without namespace\n\n // Match attribute name (check if node has this attribute)\n // Can only check for current node since ancestors don't have values\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n // Can't check attributes for ancestor nodes (values not stored)\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n // Match attribute value (only possible for current node)\n if (segment.attrValue !== undefined) {\n const actualValue = node.values[segment.attrName];\n // Both should be strings\n if (String(actualValue) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n // Match position (only for current node)\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n // Can't check position for ancestor nodes\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth') {\n if (counter !== segment.positionValue) {\n return false;\n }\n }\n }\n\n return true;\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet - The set of expressions to match against.\n * @returns {boolean} - True if any expression in the set matches the current path, false otherwise.\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this);\n }\n\n /**\n * Create a snapshot of current state\n * @returns {Object} State snapshot\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(map => new Map(map))\n };\n }\n\n /**\n * Restore state from snapshot\n * @param {Object} snapshot - State snapshot\n */\n restore(snapshot) {\n //invalidate cache\n this._pathStringCache = null;\n this._frozenPathCache = null;\n this._frozenSiblingsCache = null;\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(map => new Map(map));\n }\n\n /**\n * Return a read-only view of this matcher.\n *\n * The returned object exposes all query/inspection methods but throws a\n * TypeError if any state-mutating method is called (`push`, `pop`, `reset`,\n * `updateCurrent`, `restore`). Property reads (e.g. `.path`, `.separator`)\n * are allowed but the returned arrays/objects are frozen so callers cannot\n * mutate internal state through them either.\n *\n * @returns {ReadOnlyMatcher} A proxy that forwards read operations and blocks writes.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n *\n * const ro = matcher.readOnly();\n * ro.matches(expr); // ✓ works\n * ro.getCurrentTag(); // ✓ works\n * ro.push(\"child\", {}); // ✗ throws TypeError\n * ro.reset(); // ✗ throws TypeError\n */\n readOnly() {\n const self = this;\n\n return new Proxy(self, {\n get(target, prop, receiver) {\n // Block mutating methods\n if (MUTATING_METHODS.has(prop)) {\n return () => {\n throw new TypeError(\n `Cannot call '${prop}' on a read-only Matcher. ` +\n `Obtain a writable instance to mutate state.`\n );\n };\n }\n\n // Return cached frozen copy of path — rebuilt only after push/pop/updateCurrent/reset/restore\n if (prop === 'path') {\n if (target._frozenPathCache === null) {\n target._frozenPathCache = Object.freeze(\n target.path.map(node => Object.freeze({ ...node }))\n );\n }\n return target._frozenPathCache;\n }\n\n // Return cached frozen copy of siblingStacks — rebuilt only after push/pop/reset/restore\n if (prop === 'siblingStacks') {\n if (target._frozenSiblingsCache === null) {\n target._frozenSiblingsCache = Object.freeze(\n target.siblingStacks.map(map => Object.freeze(new Map(map)))\n );\n }\n return target._frozenSiblingsCache;\n }\n\n const value = Reflect.get(target, prop, receiver);\n\n // Bind methods so `this` inside them still refers to the real Matcher\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n\n // Prevent any property assignment on the read-only view\n set(_target, prop) {\n throw new TypeError(\n `Cannot set property '${String(prop)}' on a read-only Matcher.`\n );\n },\n\n // Prevent property deletion\n deleteProperty(_target, prop) {\n throw new TypeError(\n `Cannot delete property '${String(prop)}' from a read-only Matcher.`\n );\n }\n });\n }\n}","/**\n * ExpressionSet - An indexed collection of Expressions for efficient bulk matching\n *\n * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes\n * them at insertion time by depth and terminal tag name. At match time, only\n * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)\n * lookup plus O(small bucket) matches.\n *\n * Three buckets are maintained:\n * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)\n * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)\n * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)\n *\n * @example\n * import { Expression, ExpressionSet } from 'fast-xml-tagger';\n *\n * // Build once at config time\n * const stopNodes = new ExpressionSet();\n * stopNodes.add(new Expression('root.users.user'));\n * stopNodes.add(new Expression('root.config.setting'));\n * stopNodes.add(new Expression('..script'));\n *\n * // Query on every tag — hot path\n * if (stopNodes.matchesAny(matcher)) { ... }\n */\nexport default class ExpressionSet {\n constructor() {\n /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */\n this._byDepthAndTag = new Map();\n\n /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */\n this._wildcardByDepth = new Map();\n\n /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */\n this._deepWildcards = [];\n\n /** @type {Set<string>} pattern strings already added — used for deduplication */\n this._patterns = new Set();\n\n /** @type {boolean} whether the set is sealed against further additions */\n this._sealed = false;\n }\n\n /**\n * Add an Expression to the set.\n * Duplicate patterns (same pattern string) are silently ignored.\n *\n * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance\n * @returns {this} for chaining\n * @throws {TypeError} if called after seal()\n *\n * @example\n * set.add(new Expression('root.users.user'));\n * set.add(new Expression('..script'));\n */\n add(expression) {\n if (this._sealed) {\n throw new TypeError(\n 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'\n );\n }\n\n // Deduplicate by pattern string\n if (this._patterns.has(expression.pattern)) return this;\n this._patterns.add(expression.pattern);\n\n if (expression.hasDeepWildcard()) {\n this._deepWildcards.push(expression);\n return this;\n }\n\n const depth = expression.length;\n const lastSeg = expression.segments[expression.segments.length - 1];\n const tag = lastSeg?.tag;\n\n if (!tag || tag === '*') {\n // Can index by depth but not by tag\n if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);\n this._wildcardByDepth.get(depth).push(expression);\n } else {\n // Tightest bucket: depth + tag\n const key = `${depth}:${tag}`;\n if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);\n this._byDepthAndTag.get(key).push(expression);\n }\n\n return this;\n }\n\n /**\n * Add multiple expressions at once.\n *\n * @param {import('./Expression.js').default[]} expressions - Array of Expression instances\n * @returns {this} for chaining\n *\n * @example\n * set.addAll([\n * new Expression('root.users.user'),\n * new Expression('root.config.setting'),\n * ]);\n */\n addAll(expressions) {\n for (const expr of expressions) this.add(expr);\n return this;\n }\n\n /**\n * Check whether a pattern string is already present in the set.\n *\n * @param {import('./Expression.js').default} expression\n * @returns {boolean}\n */\n has(expression) {\n return this._patterns.has(expression.pattern);\n }\n\n /**\n * Number of expressions in the set.\n * @type {number}\n */\n get size() {\n return this._patterns.size;\n }\n\n /**\n * Seal the set against further modifications.\n * Useful to prevent accidental mutations after config is built.\n * Calling add() or addAll() on a sealed set throws a TypeError.\n *\n * @returns {this}\n */\n seal() {\n this._sealed = true;\n return this;\n }\n\n /**\n * Whether the set has been sealed.\n * @type {boolean}\n */\n get isSealed() {\n return this._sealed;\n }\n\n /**\n * Test whether the matcher's current path matches any expression in the set.\n *\n * Evaluation order (cheapest → most expensive):\n * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions\n * 2. Depth-only wildcard bucket — O(1) lookup, rare\n * 3. Deep-wildcard list — always checked, but usually small\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {boolean} true if any expression matches the current path\n *\n * @example\n * if (stopNodes.matchesAny(matcher)) {\n * // handle stop node\n * }\n */\n matchesAny(matcher) {\n return this.findMatch(matcher) !== null;\n }\n /**\n * Find and return the first Expression that matches the matcher's current path.\n *\n * Uses the same evaluation order as matchesAny (cheapest → most expensive):\n * 1. Exact depth + tag bucket\n * 2. Depth-only wildcard bucket\n * 3. Deep-wildcard list\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {import('./Expression.js').default | null} the first matching Expression, or null\n *\n * @example\n * const expr = stopNodes.findMatch(matcher);\n * if (expr) {\n * // access expr.config, expr.pattern, etc.\n * }\n */\n findMatch(matcher) {\n const depth = matcher.getDepth();\n const tag = matcher.getCurrentTag();\n\n // 1. Tightest bucket — most expressions live here\n const exactKey = `${depth}:${tag}`;\n const exactBucket = this._byDepthAndTag.get(exactKey);\n if (exactBucket) {\n for (let i = 0; i < exactBucket.length; i++) {\n if (matcher.matches(exactBucket[i])) return exactBucket[i];\n }\n }\n\n // 2. Depth-matched wildcard-tag expressions\n const wildcardBucket = this._wildcardByDepth.get(depth);\n if (wildcardBucket) {\n for (let i = 0; i < wildcardBucket.length; i++) {\n if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];\n }\n }\n\n // 3. Deep wildcards — cannot be pre-filtered by depth or tag\n for (let i = 0; i < this._deepWildcards.length; i++) {\n if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];\n }\n\n return null;\n }\n}\n","/**\n * fast-xml-tagger - XML/JSON path matching library\n * \n * Provides efficient path tracking and pattern matching for XML/JSON parsers.\n * \n * @example\n * import { Expression, Matcher } from 'fast-xml-tagger';\n * \n * // Create expression (parse once)\n * const expr = new Expression(\"root.users.user[id]\");\n * \n * // Create matcher (track path)\n * const matcher = new Matcher();\n * matcher.push(\"root\", [], {}, 0);\n * matcher.push(\"users\", [], {}, 0);\n * matcher.push(\"user\", [\"id\", \"type\"], { id: \"123\", type: \"admin\" }, 0);\n * \n * // Match\n * if (matcher.matches(expr)) {\n * console.log(\"Match found!\");\n * }\n */\n\nimport Expression from './Expression.js';\nimport Matcher from './Matcher.js';\nimport ExpressionSet from './ExpressionSet.js';\n\nexport { Expression, Matcher, ExpressionSet };\nexport default { Expression, Matcher, ExpressionSet };\n"],"names":["root","factory","exports","module","define","amd","this","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","Expression","constructor","pattern","options","data","separator","segments","_parse","_hasDeepWildcard","some","seg","type","_hasAttributeCondition","undefined","attrName","_hasPositionSelector","position","i","currentPart","length","trim","push","_parseSegment","part","segment","bracketContent","withoutBrackets","bracketMatch","match","content","slice","namespace","tag","tagAndPosition","includes","nsIndex","indexOf","substring","Error","positionMatch","colonIndex","lastIndexOf","tagPart","posPart","test","eqIndex","attrValue","nthMatch","positionValue","parseInt","hasDeepWildcard","hasAttributeCondition","hasPositionSelector","toString","MUTATING_METHODS","Set","Matcher","path","siblingStacks","_pathStringCache","_frozenPathCache","_frozenSiblingsCache","tagName","attrValues","values","currentLevel","Map","siblings","siblingKey","counter","count","set","node","pop","updateCurrent","current","getCurrentTag","getCurrentNamespace","getAttrValue","hasAttr","getPosition","getCounter","getIndex","getDepth","includeNamespace","sep","result","map","n","join","toArray","reset","matches","expression","_matchWithDeepWildcard","_matchSimple","isCurrentNode","_matchSegment","pathIdx","segIdx","nextSeg","found","actualValue","String","matchesAny","exprSet","snapshot","restore","readOnly","Proxy","target","receiver","has","TypeError","freeze","Reflect","bind","_target","deleteProperty","ExpressionSet","_byDepthAndTag","_wildcardByDepth","_deepWildcards","_patterns","_sealed","add","depth","lastSeg","addAll","expressions","expr","size","seal","isSealed","matcher","findMatch","exactKey","exactBucket","wildcardBucket"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "path-expression-matcher",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Efficient path tracking and pattern matching for XML/JSON parsers",
5
5
  "main": "./lib/pem.cjs",
6
6
  "type": "module",
package/src/Expression.js CHANGED
@@ -15,11 +15,11 @@ export default class Expression {
15
15
  * @param {Object} options - Configuration options
16
16
  * @param {string} options.separator - Path separator (default: '.')
17
17
  */
18
- constructor(pattern, options = {}) {
18
+ constructor(pattern, options = {}, data) {
19
19
  this.pattern = pattern;
20
20
  this.separator = options.separator || '.';
21
21
  this.segments = this._parse(pattern);
22
-
22
+ this.data = data;
23
23
  // Cache expensive checks for performance (O(1) instead of O(n))
24
24
  this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');
25
25
  this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);
@@ -0,0 +1,209 @@
1
+ /**
2
+ * ExpressionSet - An indexed collection of Expressions for efficient bulk matching
3
+ *
4
+ * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes
5
+ * them at insertion time by depth and terminal tag name. At match time, only
6
+ * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)
7
+ * lookup plus O(small bucket) matches.
8
+ *
9
+ * Three buckets are maintained:
10
+ * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)
11
+ * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)
12
+ * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)
13
+ *
14
+ * @example
15
+ * import { Expression, ExpressionSet } from 'fast-xml-tagger';
16
+ *
17
+ * // Build once at config time
18
+ * const stopNodes = new ExpressionSet();
19
+ * stopNodes.add(new Expression('root.users.user'));
20
+ * stopNodes.add(new Expression('root.config.setting'));
21
+ * stopNodes.add(new Expression('..script'));
22
+ *
23
+ * // Query on every tag — hot path
24
+ * if (stopNodes.matchesAny(matcher)) { ... }
25
+ */
26
+ export default class ExpressionSet {
27
+ constructor() {
28
+ /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */
29
+ this._byDepthAndTag = new Map();
30
+
31
+ /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */
32
+ this._wildcardByDepth = new Map();
33
+
34
+ /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */
35
+ this._deepWildcards = [];
36
+
37
+ /** @type {Set<string>} pattern strings already added — used for deduplication */
38
+ this._patterns = new Set();
39
+
40
+ /** @type {boolean} whether the set is sealed against further additions */
41
+ this._sealed = false;
42
+ }
43
+
44
+ /**
45
+ * Add an Expression to the set.
46
+ * Duplicate patterns (same pattern string) are silently ignored.
47
+ *
48
+ * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance
49
+ * @returns {this} for chaining
50
+ * @throws {TypeError} if called after seal()
51
+ *
52
+ * @example
53
+ * set.add(new Expression('root.users.user'));
54
+ * set.add(new Expression('..script'));
55
+ */
56
+ add(expression) {
57
+ if (this._sealed) {
58
+ throw new TypeError(
59
+ 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'
60
+ );
61
+ }
62
+
63
+ // Deduplicate by pattern string
64
+ if (this._patterns.has(expression.pattern)) return this;
65
+ this._patterns.add(expression.pattern);
66
+
67
+ if (expression.hasDeepWildcard()) {
68
+ this._deepWildcards.push(expression);
69
+ return this;
70
+ }
71
+
72
+ const depth = expression.length;
73
+ const lastSeg = expression.segments[expression.segments.length - 1];
74
+ const tag = lastSeg?.tag;
75
+
76
+ if (!tag || tag === '*') {
77
+ // Can index by depth but not by tag
78
+ if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);
79
+ this._wildcardByDepth.get(depth).push(expression);
80
+ } else {
81
+ // Tightest bucket: depth + tag
82
+ const key = `${depth}:${tag}`;
83
+ if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);
84
+ this._byDepthAndTag.get(key).push(expression);
85
+ }
86
+
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * Add multiple expressions at once.
92
+ *
93
+ * @param {import('./Expression.js').default[]} expressions - Array of Expression instances
94
+ * @returns {this} for chaining
95
+ *
96
+ * @example
97
+ * set.addAll([
98
+ * new Expression('root.users.user'),
99
+ * new Expression('root.config.setting'),
100
+ * ]);
101
+ */
102
+ addAll(expressions) {
103
+ for (const expr of expressions) this.add(expr);
104
+ return this;
105
+ }
106
+
107
+ /**
108
+ * Check whether a pattern string is already present in the set.
109
+ *
110
+ * @param {import('./Expression.js').default} expression
111
+ * @returns {boolean}
112
+ */
113
+ has(expression) {
114
+ return this._patterns.has(expression.pattern);
115
+ }
116
+
117
+ /**
118
+ * Number of expressions in the set.
119
+ * @type {number}
120
+ */
121
+ get size() {
122
+ return this._patterns.size;
123
+ }
124
+
125
+ /**
126
+ * Seal the set against further modifications.
127
+ * Useful to prevent accidental mutations after config is built.
128
+ * Calling add() or addAll() on a sealed set throws a TypeError.
129
+ *
130
+ * @returns {this}
131
+ */
132
+ seal() {
133
+ this._sealed = true;
134
+ return this;
135
+ }
136
+
137
+ /**
138
+ * Whether the set has been sealed.
139
+ * @type {boolean}
140
+ */
141
+ get isSealed() {
142
+ return this._sealed;
143
+ }
144
+
145
+ /**
146
+ * Test whether the matcher's current path matches any expression in the set.
147
+ *
148
+ * Evaluation order (cheapest → most expensive):
149
+ * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions
150
+ * 2. Depth-only wildcard bucket — O(1) lookup, rare
151
+ * 3. Deep-wildcard list — always checked, but usually small
152
+ *
153
+ * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)
154
+ * @returns {boolean} true if any expression matches the current path
155
+ *
156
+ * @example
157
+ * if (stopNodes.matchesAny(matcher)) {
158
+ * // handle stop node
159
+ * }
160
+ */
161
+ matchesAny(matcher) {
162
+ return this.findMatch(matcher) !== null;
163
+ }
164
+ /**
165
+ * Find and return the first Expression that matches the matcher's current path.
166
+ *
167
+ * Uses the same evaluation order as matchesAny (cheapest → most expensive):
168
+ * 1. Exact depth + tag bucket
169
+ * 2. Depth-only wildcard bucket
170
+ * 3. Deep-wildcard list
171
+ *
172
+ * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)
173
+ * @returns {import('./Expression.js').default | null} the first matching Expression, or null
174
+ *
175
+ * @example
176
+ * const expr = stopNodes.findMatch(matcher);
177
+ * if (expr) {
178
+ * // access expr.config, expr.pattern, etc.
179
+ * }
180
+ */
181
+ findMatch(matcher) {
182
+ const depth = matcher.getDepth();
183
+ const tag = matcher.getCurrentTag();
184
+
185
+ // 1. Tightest bucket — most expressions live here
186
+ const exactKey = `${depth}:${tag}`;
187
+ const exactBucket = this._byDepthAndTag.get(exactKey);
188
+ if (exactBucket) {
189
+ for (let i = 0; i < exactBucket.length; i++) {
190
+ if (matcher.matches(exactBucket[i])) return exactBucket[i];
191
+ }
192
+ }
193
+
194
+ // 2. Depth-matched wildcard-tag expressions
195
+ const wildcardBucket = this._wildcardByDepth.get(depth);
196
+ if (wildcardBucket) {
197
+ for (let i = 0; i < wildcardBucket.length; i++) {
198
+ if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];
199
+ }
200
+ }
201
+
202
+ // 3. Deep wildcards — cannot be pre-filtered by depth or tag
203
+ for (let i = 0; i < this._deepWildcards.length; i++) {
204
+ if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];
205
+ }
206
+
207
+ return null;
208
+ }
209
+ }
package/src/Matcher.js CHANGED
@@ -1,3 +1,4 @@
1
+ import ExpressionSet from "./ExpressionSet.js";
1
2
  /**
2
3
  * Matcher - Tracks current path in XML/JSON tree and matches against Expressions
3
4
  *
@@ -35,6 +36,9 @@ export default class Matcher {
35
36
  // Each path node: { tag: string, values: object, position: number, counter: number }
36
37
  // values only present for current (last) node
37
38
  // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level
39
+ this._pathStringCache = null;
40
+ this._frozenPathCache = null; // cache for readOnly().path
41
+ this._frozenSiblingsCache = null; // cache for readOnly().siblingStacks
38
42
  }
39
43
 
40
44
  /**
@@ -44,7 +48,10 @@ export default class Matcher {
44
48
  * @param {string} namespace - Namespace for the tag (optional)
45
49
  */
46
50
  push(tagName, attrValues = null, namespace = null) {
47
- this._pathStringCache = null; // invalidate
51
+ //invalidate cache
52
+ this._pathStringCache = null;
53
+ this._frozenPathCache = null;
54
+ this._frozenSiblingsCache = null;
48
55
  // Remove values from previous current node (now becoming ancestor)
49
56
  if (this.path.length > 0) {
50
57
  const prev = this.path[this.path.length - 1];
@@ -99,10 +106,11 @@ export default class Matcher {
99
106
  * @returns {Object|undefined} The popped node
100
107
  */
101
108
  pop() {
102
- if (this.path.length === 0) {
103
- return undefined;
104
- }
105
- this._pathStringCache = null; // invalidate
109
+ if (this.path.length === 0) return undefined;
110
+ //invalidate cache
111
+ this._pathStringCache = null;
112
+ this._frozenPathCache = null;
113
+ this._frozenSiblingsCache = null;
106
114
  const node = this.path.pop();
107
115
 
108
116
  // Clean up sibling tracking for levels deeper than current
@@ -125,6 +133,7 @@ export default class Matcher {
125
133
  const current = this.path[this.path.length - 1];
126
134
  if (attrValues !== null && attrValues !== undefined) {
127
135
  current.values = attrValues;
136
+ this._frozenPathCache = null;
128
137
  }
129
138
  }
130
139
  }
@@ -241,7 +250,10 @@ export default class Matcher {
241
250
  * Reset the path to empty
242
251
  */
243
252
  reset() {
244
- this._pathStringCache = null; // invalidate
253
+ //invalidate cache
254
+ this._pathStringCache = null;
255
+ this._frozenPathCache = null;
256
+ this._frozenSiblingsCache = null;
245
257
  this.path = [];
246
258
  this.siblingStacks = [];
247
259
  }
@@ -413,6 +425,15 @@ export default class Matcher {
413
425
  return true;
414
426
  }
415
427
 
428
+ /**
429
+ * Match any expression in the given set against the current path.
430
+ * @param {ExpressionSet} exprSet - The set of expressions to match against.
431
+ * @returns {boolean} - True if any expression in the set matches the current path, false otherwise.
432
+ */
433
+ matchesAny(exprSet) {
434
+ return exprSet.matchesAny(this);
435
+ }
436
+
416
437
  /**
417
438
  * Create a snapshot of current state
418
439
  * @returns {Object} State snapshot
@@ -429,7 +450,10 @@ export default class Matcher {
429
450
  * @param {Object} snapshot - State snapshot
430
451
  */
431
452
  restore(snapshot) {
432
- this._pathStringCache = null; // invalidate
453
+ //invalidate cache
454
+ this._pathStringCache = null;
455
+ this._frozenPathCache = null;
456
+ this._frozenSiblingsCache = null;
433
457
  this.path = snapshot.path.map(node => ({ ...node }));
434
458
  this.siblingStacks = snapshot.siblingStacks.map(map => new Map(map));
435
459
  }
@@ -470,22 +494,28 @@ export default class Matcher {
470
494
  };
471
495
  }
472
496
 
473
- const value = Reflect.get(target, prop, receiver);
497
+ // Return cached frozen copy of path — rebuilt only after push/pop/updateCurrent/reset/restore
498
+ if (prop === 'path') {
499
+ if (target._frozenPathCache === null) {
500
+ target._frozenPathCache = Object.freeze(
501
+ target.path.map(node => Object.freeze({ ...node }))
502
+ );
503
+ }
504
+ return target._frozenPathCache;
505
+ }
474
506
 
475
- // Freeze array/object properties so callers can't mutate internal
476
- // state through direct property access (e.g. matcher.path.push(...))
477
- if (prop === 'path' || prop === 'siblingStacks') {
478
- return Object.freeze(
479
- Array.isArray(value)
480
- ? value.map(item =>
481
- item instanceof Map
482
- ? Object.freeze(new Map(item)) // freeze a copy of each Map
483
- : Object.freeze({ ...item }) // freeze a copy of each node
484
- )
485
- : value
486
- );
507
+ // Return cached frozen copy of siblingStacks rebuilt only after push/pop/reset/restore
508
+ if (prop === 'siblingStacks') {
509
+ if (target._frozenSiblingsCache === null) {
510
+ target._frozenSiblingsCache = Object.freeze(
511
+ target.siblingStacks.map(map => Object.freeze(new Map(map)))
512
+ );
513
+ }
514
+ return target._frozenSiblingsCache;
487
515
  }
488
516
 
517
+ const value = Reflect.get(target, prop, receiver);
518
+
489
519
  // Bind methods so `this` inside them still refers to the real Matcher
490
520
  if (typeof value === 'function') {
491
521
  return value.bind(target);
package/src/index.d.ts CHANGED
@@ -299,6 +299,14 @@ export interface ReadOnlyMatcher {
299
299
  */
300
300
  matches(expression: Expression): boolean;
301
301
 
302
+ /**
303
+ * Test whether the matcher's current path matches **any** expression in the set.
304
+ *
305
+ * @param exprSet - A `ExpressionSet` instance
306
+ * @returns `true` if at least one expression matches the current path
307
+ */
308
+ matchesAny(exprSet: ExpressionSet): boolean;
309
+
302
310
  /**
303
311
  * Create a snapshot of current state
304
312
  * @returns State snapshot that can be restored later
@@ -489,6 +497,30 @@ export class Matcher {
489
497
  */
490
498
  matches(expression: Expression): boolean;
491
499
 
500
+ /**
501
+ * Test whether the matcher's current path matches **any** expression in the set.
502
+ *
503
+ * Uses the pre-built index to evaluate only the relevant bucket(s):
504
+ * 1. Exact depth + tag — O(1) lookup
505
+ * 2. Depth-matched wildcard tag — O(1) lookup
506
+ * 3. Deep-wildcard expressions — always scanned (typically a small list)
507
+ *
508
+ * @param exprSet - A `ExpressionSet` instance
509
+ * @returns `true` if at least one expression matches the current path
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * // Replaces:
514
+ * // for (const expr of stopNodeExpressions) {
515
+ * // if (matcher.matches(expr)) return true;
516
+ * // }
517
+ *
518
+ * if (matcher.matchesAny(stopNodes)) {
519
+ * // current tag is a stop node
520
+ * }
521
+ * ```
522
+ */
523
+ matchesAny(exprSet: ExpressionSet): boolean;
492
524
  /**
493
525
  * Create a snapshot of current state
494
526
  * @returns State snapshot that can be restored later
@@ -508,11 +540,167 @@ export class Matcher {
508
540
  }
509
541
 
510
542
  /**
511
- * Default export containing both Expression and Matcher
543
+ * ExpressionSet - An indexed collection of Expressions for efficient bulk matching
544
+ *
545
+ * Pre-indexes expressions at insertion time by depth and terminal tag name so
546
+ * that `matchesAny()` performs an O(1) bucket lookup rather than a full O(E)
547
+ * linear scan on every tag.
548
+ *
549
+ * Three internal buckets are maintained automatically:
550
+ * - **exact** — expressions with a fixed depth and a concrete terminal tag
551
+ * - **depth-wildcard** — fixed depth but terminal tag is `*`
552
+ * - **deep-wildcard** — expressions containing `..` (cannot be depth-indexed)
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * import { Expression, ExpressionSet, Matcher } from 'fast-xml-tagger';
557
+ *
558
+ * // Build once at config time
559
+ * const stopNodes = new ExpressionSet();
560
+ * stopNodes
561
+ * .add(new Expression('root.users.user'))
562
+ * .add(new Expression('root.config.*'))
563
+ * .add(new Expression('..script'))
564
+ * .seal(); // prevent accidental mutation during parsing
565
+ *
566
+ * // Query on every tag — hot path
567
+ * if (stopNodes.matchesAny(matcher)) {
568
+ * // handle stop node
569
+ * }
570
+ * ```
571
+ */
572
+ export class ExpressionSet {
573
+ /**
574
+ * Create an empty ExpressionSet.
575
+ */
576
+ constructor();
577
+
578
+ /**
579
+ * Number of expressions currently in the set.
580
+ */
581
+ readonly size: number;
582
+
583
+ /**
584
+ * Whether the set has been sealed against further modifications.
585
+ */
586
+ readonly isSealed: boolean;
587
+
588
+ /**
589
+ * Add a single Expression to the set.
590
+ *
591
+ * Duplicate patterns (same `expression.pattern` string) are silently ignored.
592
+ *
593
+ * @param expression - A pre-constructed Expression instance
594
+ * @returns `this` — for chaining
595
+ * @throws {TypeError} if the set has been sealed
596
+ *
597
+ * @example
598
+ * ```typescript
599
+ * set.add(new Expression('root.users.user'));
600
+ * set.add(new Expression('..script'));
601
+ * ```
602
+ */
603
+ add(expression: Expression): this;
604
+
605
+ /**
606
+ * Add multiple expressions at once.
607
+ *
608
+ * @param expressions - Array of Expression instances
609
+ * @returns `this` — for chaining
610
+ * @throws {TypeError} if the set has been sealed
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * set.addAll([
615
+ * new Expression('root.users.user'),
616
+ * new Expression('root.config.setting'),
617
+ * ]);
618
+ * ```
619
+ */
620
+ addAll(expressions: Expression[]): this;
621
+
622
+ /**
623
+ * Check whether an Expression with the same pattern is already present.
624
+ *
625
+ * @param expression - Expression to look up
626
+ * @returns `true` if the pattern was already added
627
+ */
628
+ has(expression: Expression): boolean;
629
+
630
+ /**
631
+ * Seal the set against further modifications.
632
+ *
633
+ * After calling `seal()`, any call to `add()` or `addAll()` will throw a
634
+ * `TypeError`. This is useful to prevent accidental mutation once the config
635
+ * has been fully built and parsing has started.
636
+ *
637
+ * @returns `this` — for chaining
638
+ *
639
+ * @example
640
+ * ```typescript
641
+ * const stopNodes = new ExpressionSet();
642
+ * stopNodes.addAll(patterns.map(p => new Expression(p))).seal();
643
+ *
644
+ * // Later — safe: reads are still allowed
645
+ * stopNodes.matchesAny(matcher);
646
+ *
647
+ * // Later — throws TypeError: ExpressionSet is sealed
648
+ * stopNodes.add(new Expression('root.extra'));
649
+ * ```
650
+ */
651
+ seal(): this;
652
+
653
+ /**
654
+ * Test whether the matcher's current path matches **any** expression in the set.
655
+ *
656
+ * Uses the pre-built index to evaluate only the relevant bucket(s):
657
+ * 1. Exact depth + tag — O(1) lookup
658
+ * 2. Depth-matched wildcard tag — O(1) lookup
659
+ * 3. Deep-wildcard expressions — always scanned (typically a small list)
660
+ *
661
+ * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
662
+ * @returns `true` if at least one expression matches the current path
663
+ *
664
+ * @example
665
+ * ```typescript
666
+ * // Replaces:
667
+ * // for (const expr of stopNodeExpressions) {
668
+ * // if (matcher.matches(expr)) return true;
669
+ * // }
670
+ *
671
+ * if (stopNodes.matchesAny(matcher)) {
672
+ * // current tag is a stop node
673
+ * }
674
+ * ```
675
+ */
676
+ matchesAny(matcher: Matcher | ReadOnlyMatcher): boolean;
677
+
678
+ /**
679
+ * Find the first expression in the set that matches the matcher's current path.
680
+ *
681
+ * Uses the pre-built index to evaluate only the relevant bucket(s):
682
+ * 1. Exact depth + tag — O(1) lookup
683
+ * 2. Depth-matched wildcard tag — O(1) lookup
684
+ * 3. Deep-wildcard expressions — always scanned (typically a small list)
685
+ *
686
+ * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
687
+ * @returns Expression if at least one expression matches the current path
688
+ *
689
+ * @example
690
+ * ```typescript
691
+ * const node = stopNodes.findMatch(matcher);
692
+ * ```
693
+ */
694
+ findMatch(matcher: Matcher | ReadOnlyMatcher): Expression;
695
+ }
696
+
697
+ /**
698
+ * Default export containing Expression, Matcher, and ExpressionSet
512
699
  */
513
700
  declare const _default: {
514
701
  Expression: typeof Expression;
515
702
  Matcher: typeof Matcher;
703
+ ExpressionSet: typeof ExpressionSet;
516
704
  };
517
705
 
518
706
  export default _default;
package/src/index.js CHANGED
@@ -23,6 +23,7 @@
23
23
 
24
24
  import Expression from './Expression.js';
25
25
  import Matcher from './Matcher.js';
26
+ import ExpressionSet from './ExpressionSet.js';
26
27
 
27
- export { Expression, Matcher };
28
- export default { Expression, Matcher };
28
+ export { Expression, Matcher, ExpressionSet };
29
+ export default { Expression, Matcher, ExpressionSet };