| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- /**
- * TypeScript definitions for path-expression-matcher (CommonJS)
- */
- /**
- * Options for creating an Expression
- */
- declare interface ExpressionOptions {
- /**
- * Path separator character
- * @default '.'
- */
- separator?: string;
- }
- /**
- * Parsed segment from an expression pattern
- */
- declare interface Segment {
- /**
- * Type of segment
- */
- type: 'tag' | 'deep-wildcard';
- /**
- * Tag name (e.g., "user", "*" for wildcard)
- * Only present when type is 'tag'
- */
- tag?: string;
- /**
- * Namespace prefix (e.g., "ns" in "ns::user")
- * Only present when namespace is specified
- */
- namespace?: string;
- /**
- * Attribute name to match (e.g., "id" in "user[id]")
- * Only present when attribute condition exists
- */
- attrName?: string;
- /**
- * Attribute value to match (e.g., "123" in "user[id=123]")
- * Only present when attribute value is specified
- */
- attrValue?: string;
- /**
- * Position selector type
- * Only present when position selector exists
- */
- position?: 'first' | 'last' | 'odd' | 'even' | 'nth';
- /**
- * Numeric value for nth() selector
- * Only present when position is 'nth'
- */
- positionValue?: number;
- }
- /**
- * Expression - Parses and stores a tag pattern expression
- *
- * Patterns are parsed once and stored in an optimized structure for fast matching.
- *
- * @example
- * ```javascript
- * const { Expression } = require('path-expression-matcher');
- * const expr = new Expression("root.users.user");
- * const expr2 = new Expression("..user[id]:first");
- * const expr3 = new Expression("root/users/user", { separator: '/' });
- * ```
- *
- * Pattern Syntax:
- * - `root.users.user` - Match exact path
- * - `..user` - Match "user" at any depth (deep wildcard)
- * - `user[id]` - Match user tag with "id" attribute
- * - `user[id=123]` - Match user tag where id="123"
- * - `user:first` - Match first occurrence of user tag
- * - `ns::user` - Match user tag with namespace "ns"
- * - `ns::user[id]:first` - Combine namespace, attribute, and position
- * ```
- */
- declare class Expression {
- /**
- * Original pattern string
- */
- readonly pattern: string;
- /**
- * Path separator character
- */
- readonly separator: string;
- /**
- * Parsed segments
- */
- readonly segments: Segment[];
- /**
- * Create a new Expression
- * @param pattern - Pattern string (e.g., "root.users.user", "..user[id]")
- * @param options - Configuration options
- */
- constructor(pattern: string, options?: ExpressionOptions);
- /**
- * Get the number of segments
- */
- get length(): number;
- /**
- * Check if expression contains deep wildcard (..)
- */
- hasDeepWildcard(): boolean;
- /**
- * Check if expression has attribute conditions
- */
- hasAttributeCondition(): boolean;
- /**
- * Check if expression has position selectors
- */
- hasPositionSelector(): boolean;
- /**
- * Get string representation
- */
- toString(): string;
- }
- /**
- * Options for creating a Matcher
- */
- declare interface MatcherOptions {
- /**
- * Default path separator
- * @default '.'
- */
- separator?: string;
- }
- /**
- * Internal node structure in the path stack
- */
- declare interface PathNode {
- /**
- * Tag name
- */
- tag: string;
- /**
- * Namespace (if present)
- */
- namespace?: string;
- /**
- * Position in sibling list (child index in parent)
- */
- position: number;
- /**
- * Counter (occurrence count of this tag name)
- */
- counter: number;
- /**
- * Attribute key-value pairs
- * Only present for the current (last) node in path
- */
- values?: Record<string, any>;
- }
- /**
- * Snapshot of matcher state
- */
- declare interface MatcherSnapshot {
- /**
- * Copy of the path stack
- */
- path: PathNode[];
- /**
- * Copy of sibling tracking maps
- */
- siblingStacks: Map<string, number>[];
- }
- /**
- * ReadOnlyMatcher - A safe, read-only view over a {@link Matcher} instance.
- *
- * Returned by {@link Matcher.readOnly}. Exposes all query and inspection
- * methods but **throws a `TypeError`** if any state-mutating method is called
- * (`push`, `pop`, `reset`, `updateCurrent`, `restore`). Direct property
- * writes are also blocked.
- *
- * Pass this to consumers that only need to inspect or match the current path
- * so they cannot accidentally corrupt the parser state.
- *
- * @example
- * ```javascript
- * const matcher = new Matcher();
- * matcher.push("root", {});
- * matcher.push("users", {});
- * matcher.push("user", { id: "123" });
- *
- * const ro: ReadOnlyMatcher = matcher.readOnly();
- *
- * ro.matches(expr); // ✓ works
- * ro.getCurrentTag(); // ✓ "user"
- * ro.getDepth(); // ✓ 3
- * ro.push("child", {}); // ✗ TypeError: Cannot call 'push' on a read-only Matcher
- * ro.reset(); // ✗ TypeError: Cannot call 'reset' on a read-only Matcher
- * ```
- */
- declare interface ReadOnlyMatcher {
- /**
- * Default path separator (read-only)
- */
- readonly separator: string;
- /**
- * Current path stack (each node is a frozen copy)
- */
- readonly path: ReadonlyArray<Readonly<PathNode>>;
- // ── Query methods ───────────────────────────────────────────────────────────
- /**
- * Get current tag name
- * @returns Current tag name or undefined if path is empty
- */
- getCurrentTag(): string | undefined;
- /**
- * Get current namespace
- * @returns Current namespace or undefined if not present or path is empty
- */
- getCurrentNamespace(): string | undefined;
- /**
- * Get current node's attribute value
- * @param attrName - Attribute name
- * @returns Attribute value or undefined
- */
- getAttrValue(attrName: string): any;
- /**
- * Check if current node has an attribute
- * @param attrName - Attribute name
- */
- hasAttr(attrName: string): boolean;
- /**
- * Get current node's sibling position (child index in parent)
- * @returns Position index or -1 if path is empty
- */
- getPosition(): number;
- /**
- * Get current node's repeat counter (occurrence count of this tag name)
- * @returns Counter value or -1 if path is empty
- */
- getCounter(): number;
- /**
- * Get current node's sibling index (alias for getPosition for backward compatibility)
- * @returns Index or -1 if path is empty
- * @deprecated Use getPosition() or getCounter() instead
- */
- getIndex(): number;
- /**
- * Get current path depth
- * @returns Number of nodes in the path
- */
- getDepth(): number;
- /**
- * Get path as string
- * @param separator - Optional separator (uses default if not provided)
- * @param includeNamespace - Whether to include namespace in output
- * @returns Path string (e.g., "root.users.user" or "ns:root.ns:users.user")
- */
- toString(separator?: string, includeNamespace?: boolean): string;
- /**
- * Get path as array of tag names
- * @returns Array of tag names
- */
- toArray(): string[];
- /**
- * Match current path against an Expression
- * @param expression - The expression to match against
- * @returns True if current path matches the expression
- */
- matches(expression: Expression): boolean;
- /**
- * Test whether the matcher's current path matches **any** expression in the set.
- *
- * @param exprSet - A `ExpressionSet` instance
- * @returns `true` if at least one expression matches the current path
- */
- matchesAny(exprSet: ExpressionSet): boolean;
- /**
- * Create a snapshot of current state
- * @returns State snapshot that can be restored later
- */
- snapshot(): MatcherSnapshot;
- // ── Blocked mutating methods ────────────────────────────────────────────────
- // These are present in the type so callers get a compile-time error with a
- // helpful message instead of a silent "property does not exist" error.
- /**
- * @throws {TypeError} Always – mutation is not allowed on a read-only view.
- */
- push(tagName: string, attrValues?: Record<string, any> | null, namespace?: string | null): never;
- /**
- * @throws {TypeError} Always – mutation is not allowed on a read-only view.
- */
- pop(): never;
- /**
- * @throws {TypeError} Always – mutation is not allowed on a read-only view.
- */
- updateCurrent(attrValues: Record<string, any>): never;
- /**
- * @throws {TypeError} Always – mutation is not allowed on a read-only view.
- */
- reset(): never;
- /**
- * @throws {TypeError} Always – mutation is not allowed on a read-only view.
- */
- restore(snapshot: MatcherSnapshot): never;
- }
- /**
- * Matcher - Tracks current path in XML/JSON tree and matches against Expressions
- *
- * The matcher maintains a stack of nodes representing the current path from root to
- * current tag. It only stores attribute values for the current (top) node to minimize
- * memory usage.
- *
- * @example
- * ```javascript
- * const { Matcher } = require('path-expression-matcher');
- * const matcher = new Matcher();
- * matcher.push("root", {});
- * matcher.push("users", {});
- * matcher.push("user", { id: "123", type: "admin" });
- *
- * const expr = new Expression("root.users.user");
- * matcher.matches(expr); // true
- *
- * matcher.pop();
- * matcher.matches(expr); // false
- * ```
- */
- declare class Matcher {
- /**
- * Default path separator
- */
- readonly separator: string;
- /**
- * Current path stack
- */
- readonly path: PathNode[];
- /**
- * Create a new Matcher
- * @param options - Configuration options
- */
- constructor(options?: MatcherOptions);
- /**
- * Push a new tag onto the path
- * @param tagName - Name of the tag
- * @param attrValues - Attribute key-value pairs for current node (optional)
- * @param namespace - Namespace for the tag (optional)
- *
- * @example
- * ```javascript
- * matcher.push("user", { id: "123", type: "admin" });
- * matcher.push("user", { id: "456" }, "ns");
- * matcher.push("container", null);
- * ```
- */
- push(tagName: string, attrValues?: Record<string, any> | null, namespace?: string | null): void;
- /**
- * Pop the last tag from the path
- * @returns The popped node or undefined if path is empty
- */
- pop(): PathNode | undefined;
- /**
- * Update current node's attribute values
- * Useful when attributes are parsed after push
- * @param attrValues - Attribute values
- */
- updateCurrent(attrValues: Record<string, any>): void;
- /**
- * Get current tag name
- * @returns Current tag name or undefined if path is empty
- */
- getCurrentTag(): string | undefined;
- /**
- * Get current namespace
- * @returns Current namespace or undefined if not present or path is empty
- */
- getCurrentNamespace(): string | undefined;
- /**
- * Get current node's attribute value
- * @param attrName - Attribute name
- * @returns Attribute value or undefined
- */
- getAttrValue(attrName: string): any;
- /**
- * Check if current node has an attribute
- * @param attrName - Attribute name
- */
- hasAttr(attrName: string): boolean;
- /**
- * Get current node's sibling position (child index in parent)
- * @returns Position index or -1 if path is empty
- */
- getPosition(): number;
- /**
- * Get current node's repeat counter (occurrence count of this tag name)
- * @returns Counter value or -1 if path is empty
- */
- getCounter(): number;
- /**
- * Get current node's sibling index (alias for getPosition for backward compatibility)
- * @returns Index or -1 if path is empty
- * @deprecated Use getPosition() or getCounter() instead
- */
- getIndex(): number;
- /**
- * Get current path depth
- * @returns Number of nodes in the path
- */
- getDepth(): number;
- /**
- * Get path as string
- * @param separator - Optional separator (uses default if not provided)
- * @param includeNamespace - Whether to include namespace in output
- * @returns Path string (e.g., "root.users.user" or "ns:root.ns:users.user")
- */
- toString(separator?: string, includeNamespace?: boolean): string;
- /**
- * Get path as array of tag names
- * @returns Array of tag names
- */
- toArray(): string[];
- /**
- * Reset the path to empty
- */
- reset(): void;
- /**
- * Match current path against an Expression
- * @param expression - The expression to match against
- * @returns True if current path matches the expression
- *
- * @example
- * ```javascript
- * const expr = new Expression("root.users.user[id]");
- * const matcher = new Matcher();
- *
- * matcher.push("root");
- * matcher.push("users");
- * matcher.push("user", { id: "123" });
- *
- * matcher.matches(expr); // true
- * ```
- */
- matches(expression: Expression): boolean;
- /**
- * Test whether the matcher's current path matches **any** expression in the set.
- *
- * Uses the pre-built index to evaluate only the relevant bucket(s):
- * 1. Exact depth + tag — O(1) lookup
- * 2. Depth-matched wildcard tag — O(1) lookup
- * 3. Deep-wildcard expressions — always scanned (typically a small list)
- *
- * @param exprSet - A `ExpressionSet` instance
- * @returns `true` if at least one expression matches the current path
- *
- * @example
- * ```typescript
- * // Replaces:
- * // for (const expr of stopNodeExpressions) {
- * // if (matcher.matches(expr)) return true;
- * // }
- *
- * if (matcher.matchesAny(stopNodes)) {
- * // current tag is a stop node
- * }
- * ```
- */
- matchesAny(exprSet: ExpressionSet): boolean;
- /**
- * Create a snapshot of current state
- * @returns State snapshot that can be restored later
- */
- snapshot(): MatcherSnapshot;
- /**
- * Restore state from snapshot
- * @param snapshot - State snapshot from previous snapshot() call
- */
- restore(snapshot: MatcherSnapshot): void;
- /**
- * Return a read-only view of this matcher.
- */
- readOnly(): ReadOnlyMatcher;
- }
- /**
- * ExpressionSet - An indexed collection of Expressions for efficient bulk matching
- *
- * Pre-indexes expressions at insertion time by depth and terminal tag name so
- * that `matchesAny()` performs an O(1) bucket lookup rather than a full O(E)
- * linear scan on every tag.
- *
- * @example
- * ```javascript
- * const { Expression, ExpressionSet, Matcher } = require('path-expression-matcher');
- *
- * // Build once at config time
- * const stopNodes = new ExpressionSet();
- * stopNodes
- * .add(new Expression('root.users.user'))
- * .add(new Expression('root.config.*'))
- * .add(new Expression('..script'))
- * .seal();
- *
- * // Per-tag — hot path
- * if (stopNodes.matchesAny(matcher)) { ... }
- * ```
- */
- declare class ExpressionSet {
- constructor();
- /** Number of expressions currently in the set. */
- readonly size: number;
- /** Whether the set has been sealed against further modifications. */
- readonly isSealed: boolean;
- /**
- * Add a single Expression. Duplicate patterns are silently ignored.
- * @throws {TypeError} if the set has been sealed
- */
- add(expression: Expression): this;
- /**
- * Add multiple expressions at once.
- * @throws {TypeError} if the set has been sealed
- */
- addAll(expressions: Expression[]): this;
- /** Check whether an expression with the same pattern is already present. */
- has(expression: Expression): boolean;
- /**
- * Seal the set against further modifications.
- * Any subsequent call to add() or addAll() will throw a TypeError.
- */
- seal(): this;
- /**
- * Test whether the matcher's current path matches any expression in the set.
- * Accepts both a Matcher instance and a ReadOnlyMatcher view.
- *
- *
- * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
- * @returns Expression if at least one expression matches the current path
- */
- matchesAny(matcher: Matcher | ReadOnlyMatcher): boolean;
- /**
- * Find the first expression in the set that matches the matcher's current path.
- *
- * @param matcher - A `Matcher` instance or a `ReadOnlyMatcher` view
- * @returns Expression if at least one expression matches the current path
- *
- * @example
- * ```typescript
- * const node = stopNodes.findMatch(matcher);
- * ```
- */
- findMatch(matcher: Matcher | ReadOnlyMatcher): Expression;
- }
- declare namespace pathExpressionMatcher {
- export {
- Expression,
- Matcher,
- ExpressionSet,
- ExpressionOptions,
- MatcherOptions,
- Segment,
- PathNode,
- MatcherSnapshot,
- };
- }
- export = pathExpressionMatcher;
|