/**
 * ASTronomical - AST query language for JavaScript
 * @license Apache-2.0
 * Copyright (c) Erlend Oftedal
 */

// src/nodeutils.ts
var isNode = (candidate) => {
  return typeof candidate === "object" && candidate != null && "type" in candidate;
};
var isNodePath = (candidate) => {
  return typeof candidate === "object" && candidate != null && "node" in candidate;
};
var isPrimitive = (value) => {
  return typeof value == "string" || typeof value == "number" || typeof value == "boolean";
};
var isUpdateExpression = (value) => {
  return isNode(value) && value.type === "UpdateExpression";
};
var isAssignmentExpression = (node) => {
  return node.type === "AssignmentExpression";
};
var isMemberExpression = (node) => {
  return node.type === "MemberExpression";
};
var isIdentifier = (node) => {
  return node.type === "Identifier";
};
var isFunctionDeclaration = (node) => {
  return node.type === "FunctionDeclaration";
};
var isFunctionExpression = (node) => {
  return node.type === "FunctionExpression";
};
var isVariableDeclarator = (node) => {
  return node.type === "VariableDeclarator";
};
var isVariableDeclaration = (node) => {
  return node.type === "VariableDeclaration";
};
var isBinding = (node, parentNode, grandParentNode) => {
  if (grandParentNode && node.type === "Identifier" && parentNode.type === "Property" && grandParentNode.type === "ObjectExpression") {
    return false;
  }
  const keys = bindingIdentifiersKeys[parentNode.type] ?? [];
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const val = (
      // @ts-expect-error key must present in parent
      parentNode[key]
    );
    if (Array.isArray(val)) {
      if (val.indexOf(node) >= 0) return true;
    } else {
      if (val === node) return true;
    }
  }
  return false;
};
var bindingIdentifiersKeys = {
  DeclareClass: ["id"],
  DeclareFunction: ["id"],
  DeclareModule: ["id"],
  DeclareVariable: ["id"],
  DeclareInterface: ["id"],
  DeclareTypeAlias: ["id"],
  DeclareOpaqueType: ["id"],
  InterfaceDeclaration: ["id"],
  TypeAlias: ["id"],
  OpaqueType: ["id"],
  CatchClause: ["param"],
  LabeledStatement: ["label"],
  UnaryExpression: ["argument"],
  AssignmentExpression: ["left"],
  ImportSpecifier: ["local"],
  ImportNamespaceSpecifier: ["local"],
  ImportDefaultSpecifier: ["local"],
  ImportDeclaration: ["specifiers"],
  ExportSpecifier: ["exported"],
  ExportNamespaceSpecifier: ["exported"],
  ExportDefaultSpecifier: ["exported"],
  FunctionDeclaration: ["id", "params"],
  FunctionExpression: ["id", "params"],
  ArrowFunctionExpression: ["params"],
  ObjectMethod: ["params"],
  ClassMethod: ["params"],
  ClassPrivateMethod: ["params"],
  ForInStatement: ["left"],
  ForOfStatement: ["left"],
  ClassDeclaration: ["id"],
  ClassExpression: ["id"],
  RestElement: ["argument"],
  UpdateExpression: ["argument"],
  ObjectProperty: ["value"],
  AssignmentPattern: ["left"],
  ArrayPattern: ["elements"],
  ObjectPattern: ["properties"],
  VariableDeclaration: ["declarations"],
  VariableDeclarator: ["id"]
};
var VISITOR_KEYS = {
  ArrayExpression: ["elements"],
  ArrayPattern: ["elements"],
  ArrowFunctionExpression: ["params", "body"],
  AssignmentExpression: ["left", "right"],
  AssignmentPattern: ["left", "right"],
  AwaitExpression: ["argument"],
  BinaryExpression: ["left", "right"],
  BlockStatement: ["body"],
  BreakStatement: [],
  CallExpression: ["callee", "arguments"],
  CatchClause: ["param", "body"],
  ChainExpression: ["expression"],
  ClassBody: ["body"],
  ClassDeclaration: ["id", "superClass", "body"],
  ClassExpression: ["id", "superClass", "body"],
  ConditionalExpression: ["test", "consequent", "alternate"],
  ContinueStatement: [],
  DebuggerStatement: [],
  DoWhileStatement: ["body", "test"],
  EmptyStatement: [],
  ExportAllDeclaration: ["source"],
  ExportDefaultDeclaration: ["declaration"],
  ExportNamedDeclaration: ["declaration", "specifiers", "source"],
  ExportSpecifier: ["local", "exported"],
  ExpressionStatement: ["expression"],
  ForInStatement: ["left", "right", "body"],
  ForOfStatement: ["left", "right", "body"],
  ForStatement: ["init", "test", "update", "body"],
  FunctionDeclaration: ["id", "params", "body"],
  FunctionExpression: ["id", "params", "body"],
  Identifier: [],
  IfStatement: ["test", "consequent", "alternate"],
  ImportAttribute: ["key", "value"],
  ImportDeclaration: ["specifiers", "source"],
  ImportDefaultSpecifier: ["local"],
  ImportNamespaceSpecifier: ["local"],
  ImportSpecifier: ["local", "imported"],
  LabeledStatement: ["label", "body"],
  Literal: [],
  LogicalExpression: ["left", "right"],
  MemberExpression: ["object", "property"],
  MetaProperty: ["meta", "property"],
  MethodDefinition: ["key", "value"],
  NewExpression: ["callee", "arguments"],
  ObjectExpression: ["properties"],
  ObjectPattern: ["properties"],
  Program: ["body"],
  Property: ["key", "value"],
  RestElement: ["argument"],
  ReturnStatement: ["argument"],
  SequenceExpression: ["expressions"],
  SpreadElement: ["argument"],
  Super: [],
  SwitchCase: ["test", "consequent"],
  SwitchStatement: ["discriminant", "cases"],
  TaggedTemplateExpression: ["tag", "quasi"],
  TemplateElement: [],
  TemplateLiteral: ["quasis", "expressions"],
  ThisExpression: [],
  ThrowStatement: ["argument"],
  TryStatement: ["block", "handler", "finalizer"],
  UnaryExpression: ["argument"],
  UpdateExpression: ["argument"],
  VariableDeclaration: ["declarations"],
  VariableDeclarator: ["id", "init"],
  WhileStatement: ["test", "body"],
  WithStatement: ["object", "body"],
  YieldExpression: ["argument"],
  ImportExpression: ["source"],
  Decorator: ["expression"],
  PropertyDefinition: ["key", "value"],
  AccessorProperty: ["key", "value"],
  JSXAttribute: ["name", "value"],
  JSXNamespacedName: ["namespace", "name"],
  JSXElement: ["openingElement", "closingElement", "children"],
  JSXClosingElement: ["name"],
  JSXOpeningElement: ["name", "attributes"],
  JSXFragment: ["openingFragment", "closingFragment", "children"],
  JSXOpeningFragment: [],
  JSXClosingFragment: [],
  JSXText: [],
  JSXExpressionContainer: ["expression"],
  JSXSpreadChild: ["expression"],
  JSXEmptyExpression: [],
  JSXSpreadAttribute: ["argument"],
  JSXIdentifier: [],
  PrivateIdentifier: [],
  JSXMemberExpression: ["object", "property"],
  ParenthesizedExpression: ["expression"],
  StaticBlock: ["body"]
};
function isBlockStatement(node) {
  return node.type === "BlockStatement";
}
function isFunction(node) {
  return node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
}
function isCatchClause(node) {
  return node.type === "CatchClause";
}
function isPattern(node) {
  switch (node.type) {
    case "AssignmentPattern":
    case "ArrayPattern":
    case "ObjectPattern":
      return true;
  }
  return false;
}
function isScope(node, parentNode) {
  if (isBlockStatement(node) && (isFunction(parentNode) || isCatchClause(parentNode))) {
    return false;
  }
  if (isPattern(node) && (isFunction(parentNode) || isCatchClause(parentNode))) {
    return true;
  }
  return isFunctionDeclaration(parentNode) || isFunctionExpression(parentNode) || isScopable(node);
}
function isScopable(node) {
  switch (node.type) {
    case "BlockStatement":
    case "CatchClause":
    case "DoWhileStatement":
    case "ForInStatement":
    case "ForStatement":
    case "FunctionDeclaration":
    case "FunctionExpression":
    case "Program":
    case "MethodDefinition":
    case "SwitchStatement":
    case "WhileStatement":
    case "ArrowFunctionExpression":
    case "ClassExpression":
    case "ClassDeclaration":
    case "ForOfStatement":
    case "StaticBlock":
      return true;
  }
  return false;
}
function isExportSpecifier(node) {
  return node.type === "ExportSpecifier";
}

// src/parseQuery.ts
var debugLogEnabled = false;
var log = debugLogEnabled ? {
  debug: (...args) => {
    console.debug(...args);
  }
} : void 0;
var visitorKeys = Object.keys(VISITOR_KEYS);
var supportedIdentifiers = {};
for (let i = 0; i < visitorKeys.length; i++) {
  const k = visitorKeys[i];
  supportedIdentifiers[k] = k;
}
var NodeType = {
  PARENT: 241,
  CHILD: 242,
  DESCENDANT: 243,
  AND: 244,
  OR: 245,
  EQUALS: 246,
  LITERAL: 247,
  FUNCTION: 248
};
function isIdentifierToken(token) {
  if (token == void 0) return false;
  if (token.tokenType != 0 /* IDENTIFIER */ && token.tokenType != 1 /* WILDCARD */) return false;
  if (!token.value) return false;
  if (!(token.value in supportedIdentifiers) && token.value != "*") {
    throw new Error("Unsupported identifier: " + token.value);
  }
  ;
  return true;
}
var whitespace = " \n\r	";
function isCharacter(charcode) {
  return charcode >= 65 && charcode <= 90 || charcode >= 97 && charcode <= 122;
}
function isInteger(charcode) {
  return charcode >= 48 && charcode <= 57;
}
function tokenize(input) {
  let s = 0;
  const result = [];
  while (s < input.length) {
    while (whitespace.includes(input[s])) s++;
    if (s >= input.length) break;
    if (input[s] == "/") {
      if (input[s + 1] == "/") {
        result.push({ tokenType: 2 /* DESCENDANT */ });
        s += 2;
        continue;
      }
      result.push({ tokenType: 3 /* CHILD */ });
      s++;
      continue;
    }
    if (input[s] == ":") {
      result.push({ tokenType: 9 /* ATTRIBUTESELECTOR */ });
      s++;
      continue;
    }
    if (input[s] == "$" && input[s + 1] == "$") {
      result.push({ tokenType: 10 /* RESOLVESELECTOR */ });
      s += 2;
      continue;
    }
    if (input[s] == "$") {
      result.push({ tokenType: 11 /* BINDINGSELECTOR */ });
      s++;
      continue;
    }
    if (input[s] == "[") {
      result.push({ tokenType: 12 /* FILTERBEGIN */ });
      s++;
      continue;
    }
    if (input[s] == "]") {
      result.push({ tokenType: 13 /* FILTEREND */ });
      s++;
      continue;
    }
    if (input[s] == ",") {
      result.push({ tokenType: 14 /* SEPARATOR */ });
      s++;
      continue;
    }
    if (input[s] == "(") {
      result.push({ tokenType: 15 /* PARAMETERSBEGIN */ });
      s++;
      continue;
    }
    if (input[s] == "f" && input[s + 1] == "n" && input[s + 2] == ":") {
      result.push({ tokenType: 17 /* FUNCTION */ });
      s += 3;
      continue;
    }
    if (input[s] == ")") {
      result.push({ tokenType: 16 /* PARAMETERSEND */ });
      s++;
      continue;
    }
    if (input[s] == "&" && input[s + 1] == "&") {
      result.push({ tokenType: 5 /* AND */ });
      s += 2;
      continue;
    }
    if (input[s] == "|" && input[s + 1] == "|") {
      result.push({ tokenType: 6 /* OR */ });
      s += 2;
      continue;
    }
    if (input[s] == "=" && input[s + 1] == "=") {
      result.push({ tokenType: 7 /* EQUALS */ });
      s += 2;
      continue;
    }
    if (input[s] == "'" || input[s] == '"') {
      const begin = input[s];
      const start = s;
      s++;
      while (s < input.length && input[s] != begin) s++;
      result.push({ tokenType: 8 /* LITERAL */, value: input.slice(start + 1, s) });
      s++;
      continue;
    }
    if (input[s] == "." && input[s + 1] == ".") {
      result.push({ tokenType: 4 /* PARENT */ });
      s += 2;
      continue;
    }
    if (input[s] == "*") {
      result.push({ tokenType: 1 /* WILDCARD */, value: "*" });
      s++;
      continue;
    }
    const charCode = input.charCodeAt(s);
    if (isCharacter(charCode)) {
      const start = s;
      while (s < input.length && isCharacter(input.charCodeAt(s))) s++;
      result.push({ tokenType: 0 /* IDENTIFIER */, value: input.slice(start, s) });
      continue;
    }
    if (isInteger(charCode)) {
      const start = s;
      while (s < input.length && isInteger(input.charCodeAt(s))) s++;
      result.push({ tokenType: 8 /* LITERAL */, value: input.slice(start, s) });
      continue;
    }
    throw new Error("Unexpected token: " + input[s]);
  }
  return result;
}
function buildFilter(tokens) {
  log?.debug("BUILD FILTER", tokens);
  tokens.shift();
  const p = buildTree(tokens);
  const next = tokens[0];
  if (next.tokenType == 5 /* AND */) {
    return {
      type: NodeType.AND,
      left: p,
      right: buildFilter(tokens)
    };
  }
  if (next.tokenType == 6 /* OR */) {
    return {
      type: NodeType.OR,
      left: p,
      right: buildFilter(tokens)
    };
  }
  if (next.tokenType == 7 /* EQUALS */) {
    const right = buildFilter(tokens);
    if (right.type == NodeType.OR || right.type == NodeType.AND) {
      return {
        type: right.type,
        left: {
          type: NodeType.EQUALS,
          left: p,
          right: right.left
        },
        right: right.right
      };
    }
    if (right.type == NodeType.EQUALS) throw new Error("Unexpected equals in equals");
    return {
      type: NodeType.EQUALS,
      left: p,
      right
    };
  }
  if (next.tokenType == 13 /* FILTEREND */) {
    tokens.shift();
    return p;
  }
  throw new Error("Unexpected token in filter: " + next?.tokenType);
}
var subNodes = [3 /* CHILD */, 2 /* DESCENDANT */];
function buildTree(tokens) {
  log?.debug("BUILD TREE", tokens);
  if (tokens.length == 0) throw new Error("Unexpected end of input");
  const token = tokens.shift();
  if (token == void 0) throw new Error("Unexpected end of input");
  if (token.tokenType == 4 /* PARENT */) {
    return {
      type: NodeType.PARENT,
      child: buildTree(tokens)
    };
  }
  if (subNodes.includes(token.tokenType)) {
    let next = tokens.shift();
    if (next?.tokenType == 17 /* FUNCTION */) {
      const name = tokens.shift();
      if (name == void 0 || name.tokenType != 0 /* IDENTIFIER */ || name.value == void 0 || typeof name.value != "string") throw new Error("Unexpected token: " + name?.tokenType + ". Expecting function name");
      const value = name.value;
      if (!isAvailableFunction(value)) {
        throw new Error("Unsupported function: " + name.value);
      }
      return buildFunctionCall(value, tokens);
    }
    if (next?.tokenType == 4 /* PARENT */) {
      return { type: NodeType.PARENT, child: buildTree(tokens) };
    }
    const modifiers = [];
    while (next && (next?.tokenType == 9 /* ATTRIBUTESELECTOR */ || next?.tokenType == 11 /* BINDINGSELECTOR */ || next?.tokenType == 10 /* RESOLVESELECTOR */)) {
      modifiers.push(next);
      next = tokens.shift();
    }
    const isAttribute = modifiers.some((m) => m.tokenType == 9 /* ATTRIBUTESELECTOR */);
    const isBinding2 = modifiers.some((m) => m.tokenType == 11 /* BINDINGSELECTOR */);
    const isResolve = modifiers.some((m) => m.tokenType == 10 /* RESOLVESELECTOR */);
    if (isResolve && isBinding2) throw new Error("Cannot have both resolve and binding");
    if (!next || !next.value || !isAttribute && !isIdentifierToken(next)) throw new Error("Unexpected or missing token: " + next?.tokenType);
    const identifer = next.value;
    let filter = void 0;
    if (tokens.length > 0 && tokens[0].tokenType == 12 /* FILTERBEGIN */) {
      filter = buildFilter(tokens);
      log?.debug("FILTER", filter, tokens);
    }
    let child = void 0;
    if (tokens.length > 0 && subNodes.includes(tokens[0].tokenType)) {
      child = buildTree(tokens);
    }
    if (typeof identifer != "string") throw new Error("Identifier must be a string");
    let nodeType = NodeType.CHILD;
    if (token.tokenType == 2 /* DESCENDANT */) {
      nodeType = NodeType.DESCENDANT;
    } else if (token.tokenType != 3 /* CHILD */) {
      throw new Error("Unexpected token:" + token.tokenType);
    }
    return {
      type: nodeType,
      value: identifer,
      attribute: isAttribute,
      binding: isBinding2,
      resolve: isResolve,
      filter,
      child
    };
  }
  if (token.tokenType == 8 /* LITERAL */) {
    return {
      type: NodeType.LITERAL,
      value: token.value
    };
  }
  throw new Error("Unexpected token: " + token.tokenType);
}
function buildFunctionCall(name, tokens) {
  log?.debug("BUILD FUNCTION", name, tokens);
  const parameters = [];
  const next = tokens.shift();
  if (next?.tokenType != 15 /* PARAMETERSBEGIN */) throw new Error("Unexpected token: " + next?.tokenType);
  while (tokens.length > 0 && tokens[0].tokenType != 16 /* PARAMETERSEND */) {
    parameters.push(buildTree(tokens));
    if (tokens[0].tokenType == 14 /* SEPARATOR */) tokens.shift();
  }
  if (tokens.length == 0) throw new Error("Unexpected end of input");
  tokens.shift();
  return {
    type: NodeType.FUNCTION,
    function: name,
    parameters
  };
}
function parse(input) {
  const tokens = tokenize(input);
  const result = buildTree(tokens);
  log?.debug("RESULT", result);
  if (!result) throw new Error("No root element found");
  return result;
}

// src/index.ts
import { parseScript } from "meriyah";

// src/utils.ts
function toArray(value) {
  return Array.isArray(value) ? value : [value];
}
function isDefined(value) {
  return value != void 0 && value != null;
}

// src/index.ts
var debugLogEnabled2 = false;
var log2 = debugLogEnabled2 ? {
  debug: (...args) => {
    console.debug(...args);
  }
} : void 0;
var functions = {
  "join": {
    fn: (result) => {
      if (result.length != 2) throw new Error("Invalid number of arugments for join");
      const [values, separators] = result;
      if (separators.length != 1) throw new Error("Invalid number of separators for join");
      const separator = separators[0];
      if (typeof separator != "string") throw new Error("Separator must be a string");
      if (values.length == 0) return [];
      return [values.join(separator)];
    }
  },
  "concat": {
    fn: (result) => {
      const flattened = [];
      for (let i = 0; i < result.length; i++) {
        if (result[i].length === 0) return [];
        for (let j = 0; j < result[i].length; j++) {
          flattened.push(result[i][j]);
        }
      }
      return [flattened.join("")];
    }
  },
  "first": {
    fn: (result) => {
      if (result.length != 1) throw new Error("Invalid number of arugments for first");
      if (result[0].length == 0) return [];
      return [result[0][0]];
    }
  },
  "nthchild": {
    fn: (result) => {
      if (result.length != 2) throw new Error("Invalid number of arguments for nthchild");
      if (result[1].length != 1) throw new Error("Invalid number of arguments for nthchild");
      const x = result[1][0];
      const number = typeof x == "number" ? x : parseInt(x);
      return [result[0][number]];
    }
  }
};
var functionNames = new Set(Object.keys(functions));
function isAvailableFunction(name) {
  return functionNames.has(name);
}
function breadCrumb(path) {
  if (!debugLogEnabled2) return "";
  return {
    //Using the toString trick here to avoid calculating the breadcrumb if debug logging is off
    valueOf() {
      if (path.parentPath == void 0) return "@" + path.node.type;
      return breadCrumb(path.parentPath) + "." + (path.parentKey == path.key ? path.key : path.parentKey + "[" + path.key + "]") + "@" + path.node.type;
    }
  };
}
function createQuerier() {
  const traverser = createTraverser();
  const { getChildren, getPrimitiveChildren, getPrimitiveChildrenOrNodePaths, getBinding, createNodePath, traverse } = traverser;
  function createFilter(filter, filterResult) {
    if (filter.type == NodeType.AND || filter.type == NodeType.OR || filter.type == NodeType.EQUALS) {
      return {
        type: filter.type,
        left: createFilter(filter.left, []),
        right: createFilter(filter.right, [])
      };
    } else if (filter.type == NodeType.LITERAL) {
      const r = [filter.value];
      return {
        node: filter,
        result: r
      };
    }
    return createFNode(filter, filterResult);
  }
  function createFNode(token, result) {
    return {
      node: token,
      result
    };
  }
  function addFilterChildrenToState(filter, state) {
    if ("type" in filter && (filter.type == NodeType.AND || filter.type == NodeType.OR || filter.type == NodeType.EQUALS)) {
      addFilterChildrenToState(filter.left, state);
      addFilterChildrenToState(filter.right, state);
    } else if ("node" in filter) {
      if (filter.node.type == NodeType.CHILD) {
        log2?.debug("ADDING FILTER CHILD", filter.node);
        state.child[state.depth + 1].push(filter);
      }
      if (filter.node.type == NodeType.DESCENDANT) {
        log2?.debug("ADDING FILTER DESCENDANT", filter.node);
        state.descendant[state.depth + 1].push(filter);
      }
    }
  }
  function createFNodeAndAddToState(token, result, state) {
    log2?.debug("ADDING FNODE", token);
    const fnode = createFNode(token, result);
    if (token.type == NodeType.CHILD) {
      state.child[state.depth + 1].push(fnode);
    } else if (token.type == NodeType.DESCENDANT) {
      state.descendant[state.depth + 1].push(fnode);
    }
    return fnode;
  }
  function isMatch(fnode, path) {
    if (fnode.node.attribute) {
      const m2 = fnode.node.value == path.parentKey || fnode.node.value == path.key;
      if (m2) log2?.debug("ATTR MATCH", fnode.node.value, breadCrumb(path));
      return m2;
    }
    if (fnode.node.value == "*") {
      return true;
    }
    const m = fnode.node.value == path.node.type;
    if (m) log2?.debug("NODE MATCH", fnode.node.value, breadCrumb(path));
    return m;
  }
  function addIfTokenMatch(fnode, path, state) {
    if (!isMatch(fnode, path)) return;
    state.matches[state.depth].push([fnode, path]);
    if (fnode.node.filter) {
      const filter = createFilter(fnode.node.filter, []);
      const filteredResult = [];
      const f = { filter, qNode: fnode.node, node: path.node, result: filteredResult };
      state.filters[state.depth].push(f);
      let fmap = state.filtersMap[state.depth].get(fnode.node);
      if (!fmap) {
        fmap = [];
        state.filtersMap[state.depth].set(fnode.node, fmap);
      }
      fmap.push(f);
      addFilterChildrenToState(filter, state);
      const child = fnode.node.child;
      if (child) {
        if (child.type == NodeType.FUNCTION) {
          const fr = addFunction(fnode, child, path, state);
          state.functionCalls[state.depth].push(fr);
        } else {
          createFNodeAndAddToState(child, filteredResult, state);
        }
      }
    } else {
      const child = fnode.node.child;
      if (child?.type == NodeType.FUNCTION) {
        const fr = addFunction(fnode, child, path, state);
        state.functionCalls[state.depth].push(fr);
      } else if (child && !fnode.node.binding && !fnode.node.resolve) {
        createFNodeAndAddToState(child, fnode.result, state);
      }
    }
  }
  function addFunction(rootNode, functionCall, path, state) {
    const functionNode = { node: rootNode.node, functionCall, parameters: [], result: [] };
    for (const param of functionCall.parameters) {
      if (param.type == NodeType.LITERAL) {
        functionNode.parameters.push({ node: param, result: [param.value] });
      } else {
        if (param.type == NodeType.FUNCTION) {
          functionNode.parameters.push(addFunction(functionNode, param, path, state));
        } else {
          functionNode.parameters.push(createFNodeAndAddToState(param, [], state));
        }
      }
    }
    return functionNode;
  }
  function addPrimitiveAttributeIfMatch(fnode, path) {
    if (!fnode.node.attribute || fnode.node.value == void 0) return;
    if (fnode.node.child || fnode.node.filter) return;
    if (!Object.hasOwn(path.node, fnode.node.value)) return;
    const nodes = getPrimitiveChildren(fnode.node.value, path);
    if (nodes.length == 0) return;
    log2?.debug("PRIMITIVE", fnode.node.value, nodes);
    fnode.result.push(...nodes);
  }
  function evaluateFilter(filter, path) {
    log2?.debug("EVALUATING FILTER", filter, breadCrumb(path));
    if ("type" in filter) {
      if (filter.type == NodeType.AND) {
        const left = evaluateFilter(filter.left, path);
        if (left.length == 0) {
          return [];
        }
        const r = evaluateFilter(filter.right, path);
        return r;
      }
      if (filter.type == NodeType.OR) {
        const left = evaluateFilter(filter.left, path);
        if (left.length > 0) {
          return left;
        }
        const r = evaluateFilter(filter.right, path);
        return r;
      }
      if (filter.type == NodeType.EQUALS) {
        const left = evaluateFilter(filter.left, path);
        const right = evaluateFilter(filter.right, path);
        if (right.length > 3) {
          const rightSet = new Set(right);
          const r2 = [];
          for (let i = 0; i < left.length; i++) {
            if (rightSet.has(left[i])) r2.push(left[i]);
          }
          return r2;
        }
        const r = [];
        for (let i = 0; i < left.length; i++) {
          if (right.includes(left[i])) r.push(left[i]);
        }
        return r;
      }
      throw new Error("Unknown filter type: " + filter.type);
    }
    if (filter.node.type == NodeType.PARENT) {
      const r = resolveFilterWithParent(filter.node, path);
      return r;
    }
    if (filter.result.length === 0 && filter.node.attribute) {
      return resolveDirectly(filter.node, path);
    }
    return filter.result;
  }
  function resolveBinding(path) {
    if (!isIdentifier(path.node)) return void 0;
    log2?.debug("RESOLVING BINDING FOR ", path.node);
    const name = path.node.name;
    if (name == void 0 || typeof name != "string") return void 0;
    const binding = getBinding(path.scopeId, name);
    if (!binding) return void 0;
    log2?.debug("THIS IS THE BINDING", binding);
    return binding.path;
  }
  function resolveFilterWithParent(node, path) {
    let startNode = node;
    let startPath = path;
    while (startNode.type == NodeType.PARENT) {
      if (!startNode.child) throw new Error("Parent filter must have child");
      if (!startPath.parentPath) return [];
      log2?.debug("STEP OUT", startNode, breadCrumb(startPath));
      startNode = startNode.child;
      startPath = startPath.parentPath;
    }
    return resolveDirectly(startNode, startPath);
  }
  let subQueryCounter = 0;
  const memo = /* @__PURE__ */ new Map();
  function resolveDirectly(node, path) {
    let startNode = node;
    const startPath = path;
    let paths = [startPath];
    while (startNode.attribute && startNode.type == NodeType.CHILD) {
      const lookup = startNode.value;
      if (!lookup) throw new Error("Selector must have a value");
      const nodes = [];
      for (let i = 0; i < paths.length; i++) {
        const p = paths[i];
        if (!isNodePath(p)) continue;
        const arr = getPrimitiveChildrenOrNodePaths(lookup, p);
        for (let j = 0; j < arr.length; j++) {
          nodes.push(arr[j]);
        }
      }
      if (nodes.length == 0) return [];
      paths = nodes;
      if (startNode.resolve) {
        const resolved = [];
        for (let i = 0; i < paths.length; i++) {
          const p = paths[i];
          if (!isNodePath(p)) continue;
          const binding = resolveBinding(p);
          if (!binding) continue;
          const children = getChildren("init", binding);
          for (let j = 0; j < children.length; j++) {
            resolved.push(children[j]);
          }
        }
        if (resolved.length > 0) paths = resolved;
      } else if (startNode.binding) {
        const bindings = [];
        for (let i = 0; i < paths.length; i++) {
          const p = paths[i];
          if (!isNodePath(p)) continue;
          const binding = resolveBinding(p);
          if (binding) bindings.push(binding);
        }
        paths = bindings;
      }
      const filter = startNode.filter;
      if (filter) {
        const filtered = [];
        for (let i = 0; i < paths.length; i++) {
          const p = paths[i];
          if (!isNodePath(p)) continue;
          if (travHandle({ subquery: filter }, p).subquery.length > 0) {
            filtered.push(p);
          }
        }
        paths = filtered;
      }
      if (!startNode.child) {
        const results = new Array(paths.length);
        for (let i = 0; i < paths.length; i++) {
          const p = paths[i];
          results[i] = isPrimitive(p) ? p : p.node;
        }
        return results;
      }
      startNode = startNode.child;
    }
    const result = [];
    for (const path2 of paths) {
      if (isNodePath(path2)) {
        if (memo.has(startNode) && memo.get(startNode).has(path2)) {
          const cached = memo.get(startNode).get(path2);
          for (let i = 0; i < cached.length; i++) {
            result.push(cached[i]);
          }
        } else {
          const subQueryKey = "subquery-" + subQueryCounter++;
          const subQueryResult = travHandle({ [subQueryKey]: startNode }, path2)[subQueryKey];
          if (!memo.has(startNode)) memo.set(startNode, /* @__PURE__ */ new Map());
          memo.get(startNode)?.set(path2, subQueryResult);
          for (let i = 0; i < subQueryResult.length; i++) {
            result.push(subQueryResult[i]);
          }
        }
      }
    }
    log2?.debug("DIRECT TRAV RESOLVE RESULT", result);
    return result;
  }
  function addResultIfTokenMatch(fnode, path, state) {
    const matchingFilters = [];
    const filters = [];
    const nodeFilters = state.filtersMap[state.depth].get(fnode.node);
    if (nodeFilters) {
      for (let i = 0; i < nodeFilters.length; i++) {
        const f = nodeFilters[i];
        if (f.qNode !== fnode.node) continue;
        if (f.node !== path.node) continue;
        filters.push(f);
      }
      for (let i = 0; i < filters.length; i++) {
        const f = filters[i];
        if (evaluateFilter(f.filter, path).length > 0) {
          matchingFilters.push(f);
        }
      }
      if (filters.length > 0 && matchingFilters.length == 0) return;
    }
    if (fnode.node.resolve) {
      const binding = resolveBinding(path);
      const resolved = binding ? getChildren("init", binding)[0] : void 0;
      if (fnode.node.child) {
        const result = resolveDirectly(fnode.node.child, resolved ?? path);
        for (let i = 0; i < result.length; i++) {
          fnode.result.push(result[i]);
        }
      } else {
        fnode.result.push(path.node);
      }
    } else if (fnode.node.binding) {
      const binding = resolveBinding(path);
      if (binding) {
        if (fnode.node.child) {
          const result = resolveDirectly(fnode.node.child, binding);
          for (let i = 0; i < result.length; i++) {
            fnode.result.push(result[i]);
          }
        } else {
          fnode.result.push(binding.node);
        }
      }
    } else if (!fnode.node.child) {
      fnode.result.push(path.node);
    } else if (fnode.node.child.type == NodeType.FUNCTION) {
      const functionCallResult = state.functionCalls[state.depth].find((f) => f.node == fnode.node);
      if (!functionCallResult) throw new Error("Did not find expected function call for " + fnode.node.child.function);
      resolveFunctionCalls(fnode, functionCallResult, path, state);
    } else if (matchingFilters.length > 0) {
      log2?.debug("HAS MATCHING FILTER", fnode.result.length, matchingFilters.length, breadCrumb(path));
      for (let i = 0; i < matchingFilters.length; i++) {
        const filterResult = matchingFilters[i].result;
        for (let j = 0; j < filterResult.length; j++) {
          fnode.result.push(filterResult[j]);
        }
      }
    } else if (fnode.node.child.attribute) {
      if (fnode.node.child.child || fnode.node.child.filter) {
        const attrName = fnode.node.child.value;
        if (attrName) {
          const attrValue = path.node[attrName];
          const isASTNode = (v) => typeof v === "object" && v !== null && "type" in v;
          const wouldBeTraversed = isASTNode(attrValue) || Array.isArray(attrValue) && attrValue.length > 0 && isASTNode(attrValue[0]);
          if (!wouldBeTraversed) {
            const result = resolveDirectly(fnode.node.child, path);
            for (let i = 0; i < result.length; i++) {
              fnode.result.push(result[i]);
            }
          }
        }
      }
    }
  }
  function resolveFunctionCalls(fnode, functionCallResult, path, state) {
    const parameterResults = [];
    for (let i = 0; i < functionCallResult.parameters.length; i++) {
      const p = functionCallResult.parameters[i];
      if ("parameters" in p) {
        resolveFunctionCalls(p, p, path, state);
        parameterResults.push(p.result);
      } else {
        parameterResults.push(p.result);
      }
    }
    const functionResult = functions[functionCallResult.functionCall.function].fn(parameterResults);
    log2?.debug("PARAMETER RESULTS", functionCallResult.functionCall.function, parameterResults, functionResult);
    for (let i = 0; i < functionResult.length; i++) {
      fnode.result.push(functionResult[i]);
    }
  }
  function travHandle(queries, root) {
    const results = {};
    const queryKeys = Object.keys(queries);
    for (let i = 0; i < queryKeys.length; i++) {
      results[queryKeys[i]] = [];
    }
    const state = {
      depth: 0,
      child: [[], []],
      descendant: [[], []],
      filters: [[], []],
      filtersMap: [/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map()],
      matches: [[]],
      functionCalls: [[]]
    };
    for (const [name, node] of Object.entries(queries)) {
      createFNodeAndAddToState(node, results[name], state);
    }
    const childAtDepth = state.child[state.depth + 1];
    for (let i = 0; i < childAtDepth.length; i++) {
      addPrimitiveAttributeIfMatch(childAtDepth[i], root);
    }
    const descendantSlice = state.descendant.slice(0, state.depth + 1);
    for (let i = 0; i < descendantSlice.length; i++) {
      const fnodes = descendantSlice[i];
      for (let j = 0; j < fnodes.length; j++) {
        addPrimitiveAttributeIfMatch(fnodes[j], root);
      }
    }
    traverse(root.node, {
      enter(path, state2) {
        state2.depth++;
        state2.child.push([]);
        state2.descendant.push([]);
        state2.filters.push([]);
        state2.filtersMap.push(/* @__PURE__ */ new Map());
        state2.matches.push([]);
        state2.functionCalls.push([]);
        for (const fnode of state2.child[state2.depth]) {
          addIfTokenMatch(fnode, path, state2);
        }
        for (const fnodes of state2.descendant.slice(0, state2.depth + 1)) {
          for (const fnode of fnodes) {
            addIfTokenMatch(fnode, path, state2);
          }
        }
      },
      exit(path, state2) {
        log2?.debug("EXIT", breadCrumb(path));
        const childAtDepthPlusOne = state2.child[state2.depth + 1];
        for (let i = 0; i < childAtDepthPlusOne.length; i++) {
          addPrimitiveAttributeIfMatch(childAtDepthPlusOne[i], path);
        }
        for (let i = 0; i < state2.descendant.length; i++) {
          const fnodes = state2.descendant[i];
          for (let j = 0; j < fnodes.length; j++) {
            addPrimitiveAttributeIfMatch(fnodes[j], path);
          }
        }
        const matchesAtDepth = state2.matches[state2.depth];
        for (let i = 0; i < matchesAtDepth.length; i++) {
          addResultIfTokenMatch(matchesAtDepth[i][0], matchesAtDepth[i][1], state2);
        }
        state2.depth--;
        state2.child.pop();
        state2.descendant.pop();
        state2.filters.pop();
        state2.filtersMap.pop();
        state2.matches.pop();
        state2.functionCalls.pop();
      }
    }, root.scopeId, state, root);
    return results;
  }
  function beginHandle(queries, path) {
    const rootPath = createNodePath(path, void 0, void 0, void 0, void 0);
    const r = travHandle(queries, rootPath);
    memo.clear();
    return r;
  }
  return {
    beginHandle
  };
}
var defaultKey = "__default__";
function query(code, query2, returnAST) {
  const result = multiQuery(code, { [defaultKey]: query2 }, returnAST);
  if (returnAST) {
    const r = result[defaultKey];
    r.__AST = result.__AST;
    return r;
  }
  return result[defaultKey];
}
function multiQuery(code, namedQueries, returnAST) {
  const start = Date.now();
  const ast = typeof code == "string" ? parseSource(code) : code;
  if (ast == null) throw new Error("Could not pase code");
  const queries = {};
  const entries = Object.entries(namedQueries);
  for (let i = 0; i < entries.length; i++) {
    const [name, queryStr] = entries[i];
    queries[name] = parse(queryStr);
  }
  const querier = createQuerier();
  const result = querier.beginHandle(queries, ast);
  log2?.debug("Query time: ", Date.now() - start);
  if (returnAST) {
    return { ...result, __AST: ast };
  }
  return result;
}
function parseSource(source, optimize = true) {
  const parsingOptions = optimize ? { loc: false, ranges: false } : { loc: true, ranges: true };
  try {
    return parseScript(source, { module: true, next: true, ...parsingOptions });
  } catch {
    return parseScript(source, { module: false, next: true, ...parsingOptions, webcompat: true });
  }
}
function createTraverser() {
  let scopeIdCounter = 0;
  const scopes = /* @__PURE__ */ new Map();
  let removedScopes = 0;
  const nodePathsCreated = {};
  function createScope(parentScopeId) {
    const id = scopeIdCounter++;
    if (parentScopeId != void 0) {
      scopes.set(id, parentScopeId ?? -1);
    }
    return id;
  }
  function getBinding(scopeId, name) {
    let currentScope = scopes.get(scopeId);
    while (currentScope !== void 0) {
      if (typeof currentScope !== "number") {
        if (currentScope.bindings[name]) {
          return currentScope.bindings[name];
        }
        if (currentScope.parentScopeId === -1) break;
        currentScope = scopes.get(currentScope.parentScopeId);
      } else {
        if (currentScope === -1 || currentScope == void 0) break;
        currentScope = scopes.get(currentScope);
      }
    }
    return void 0;
  }
  function setBinding(scopeId, name, binding) {
    let scope = scopes.get(scopeId);
    if (typeof scope === "number" || scope === void 0) {
      scope = { bindings: {}, id: scopeId, parentScopeId: scope };
      scopes.set(scopeId, scope);
    }
    if (scope && typeof scope !== "number") {
      scope.bindings[name] = binding;
    }
  }
  let pathsCreated = 0;
  function getChildren(key, path) {
    if (key in path.node) {
      const r = path.node[key];
      if (Array.isArray(r)) {
        const len = r.length;
        const result = new Array(len);
        for (let i = 0; i < len; i++) {
          result[i] = createNodePath(r[i], i, key, path.scopeId, path.functionScopeId, path);
        }
        return result;
      } else if (r != void 0) {
        return [createNodePath(r, key, key, path.scopeId, path.functionScopeId, path)];
      }
    }
    return [];
  }
  function getPrimitiveChildren(key, path) {
    if (key in path.node) {
      const r = path.node[key];
      const arr = toArray(r);
      const result = [];
      for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        if (isDefined(item) && isPrimitive(item)) {
          result.push(item);
        }
      }
      return result;
    }
    return [];
  }
  function getPrimitiveChildrenOrNodePaths(key, path) {
    if (key in path.node) {
      const r = path.node[key];
      if (Array.isArray(r)) {
        const len = r.length;
        const result = new Array(len);
        for (let i = 0; i < len; i++) {
          const n = r[i];
          result[i] = isPrimitive(n) ? n : createNodePath(n, i, key, path.scopeId, path.functionScopeId, path);
        }
        return result;
      } else if (r != void 0) {
        return [
          isPrimitive(r) ? r : createNodePath(r, key, key, path.scopeId, path.functionScopeId, path)
        ];
      }
    }
    return [];
  }
  const nodePathMap = /* @__PURE__ */ new WeakMap();
  function createNodePath(node, key, parentKey, scopeId, functionScopeId, nodePath) {
    if (nodePathMap.has(node)) {
      const path2 = nodePathMap.get(node);
      if (nodePath && isExportSpecifier(nodePath.node) && key == "exported" && path2.key == "local") {
        path2.key = "exported";
        path2.parentPath = nodePath;
        return path2;
      }
      if (key != void 0) path2.key = typeof key == "number" ? key.toString() : key;
      if (parentKey != void 0) path2.parentKey = parentKey;
      if (nodePath != void 0) path2.parentPath = nodePath;
      return path2;
    }
    const finalScope = (node.extra && node.extra.scopeId != void 0 ? node.extra.scopeId : scopeId) ?? createScope();
    const finalFScope = (node.extra && node.extra.functionScopeId != void 0 ? node.extra.functionScopeId : functionScopeId) ?? finalScope;
    const path = {
      node,
      scopeId: finalScope,
      functionScopeId: finalFScope,
      parentPath: nodePath,
      key: typeof key == "number" ? key.toString() : key,
      parentKey
    };
    if (isNode(node)) {
      nodePathMap.set(node, path);
    }
    nodePathsCreated[node.type] = (nodePathsCreated[node.type] ?? 0) + 1;
    pathsCreated++;
    return path;
  }
  function registerBinding(stack, scopeId, functionScopeId, key, parentKey) {
    const node = stack[stack.length - 1];
    if (!isIdentifier(node)) return;
    const parentNode = stack[stack.length - 2];
    if (isAssignmentExpression(parentNode) || isMemberExpression(parentNode) || isUpdateExpression(parentNode) || isExportSpecifier(parentNode)) return;
    const grandParentNode = stack[stack.length - 3];
    if (!isBinding(node, parentNode, grandParentNode)) return;
    if (key == "id" && !isVariableDeclarator(parentNode)) {
      setBinding(functionScopeId, node.name, { path: createNodePath(node, void 0, void 0, scopeId, functionScopeId) });
      return;
    }
    if (isVariableDeclarator(parentNode) && isVariableDeclaration(grandParentNode)) {
      if (grandParentNode.kind == "var") {
        setBinding(functionScopeId, node.name, { path: createNodePath(parentNode, void 0, void 0, scopeId, functionScopeId) });
        return;
      } else {
        setBinding(scopeId, node.name, { path: createNodePath(parentNode, void 0, void 0, scopeId, functionScopeId) });
        return;
      }
    }
    if (isScope(node, parentNode)) {
      setBinding(scopeId, node.name, { path: createNodePath(node, key, parentKey, scopeId, functionScopeId) });
    }
  }
  let bindingNodesVisited = 0;
  function registerBindings(stack, scopeId, functionScopeId) {
    const node = stack[stack.length - 1];
    if (!isNode(node)) return;
    if (node.extra?.scopeId != void 0) return;
    node.extra = node.extra ?? {};
    node.extra.scopeId = scopeId;
    bindingNodesVisited++;
    const keys = VISITOR_KEYS[node.type];
    if (keys.length == 0) return;
    let childScopeId = scopeId;
    if (isScopable(node)) {
      childScopeId = createScope(scopeId);
    }
    for (let keyIdx = 0; keyIdx < keys.length; keyIdx++) {
      const key = keys[keyIdx];
      const childNodes = node[key];
      const children = toArray(childNodes);
      for (let i = 0; i < children.length; i++) {
        const child = children[i];
        if (!isDefined(child) || !isNode(child)) continue;
        const f = key === "body" && (isFunctionDeclaration(node) || isFunctionExpression(node)) ? childScopeId : functionScopeId;
        stack.push(child);
        if (isIdentifier(child)) {
          const k = Array.isArray(childNodes) ? i : key;
          registerBinding(stack, childScopeId, f, k, key);
        } else {
          registerBindings(stack, childScopeId, f);
        }
        stack.pop();
      }
    }
    if (childScopeId != scopeId && typeof scopes.get(childScopeId) == "number") {
      scopes.set(childScopeId, scopes.get(scopeId));
      removedScopes++;
    }
  }
  function traverseInner(node, visitor, scopeId, functionScopeId, state, path) {
    const nodePath = path ?? createNodePath(node, void 0, void 0, scopeId, functionScopeId);
    const keys = VISITOR_KEYS[node.type];
    if (nodePath.parentPath) {
      const stack = [];
      if (nodePath.parentPath.parentPath?.node) stack.push(nodePath.parentPath.parentPath.node);
      stack.push(nodePath.parentPath.node, nodePath.node);
      registerBindings(stack, nodePath.scopeId, nodePath.functionScopeId);
    }
    const stateTyped = state;
    const hasDescendantQueries = stateTyped.descendant && stateTyped.descendant.some((arr) => arr.length > 0);
    const hasChildQueriesAtNextDepth = stateTyped.child && stateTyped.child[stateTyped.depth + 1] && stateTyped.child[stateTyped.depth + 1].length > 0;
    if (!hasDescendantQueries && !hasChildQueriesAtNextDepth) {
      return;
    }
    for (let keyIdx = 0; keyIdx < keys.length; keyIdx++) {
      const key = keys[keyIdx];
      const childNodes = node[key];
      const children = Array.isArray(childNodes) ? childNodes : childNodes ? [childNodes] : [];
      const nodePaths = [];
      for (let i = 0; i < children.length; i++) {
        const child = children[i];
        if (isNode(child)) {
          const childPath = createNodePath(child, Array.isArray(childNodes) ? i : key, key, nodePath.scopeId, nodePath.functionScopeId, nodePath);
          nodePaths.push(childPath);
        }
      }
      for (let i = 0; i < nodePaths.length; i++) {
        const childPath = nodePaths[i];
        visitor.enter(childPath, state);
        traverseInner(childPath.node, visitor, nodePath.scopeId, nodePath.functionScopeId, state, childPath);
        visitor.exit(childPath, state);
      }
    }
  }
  const sOut = [];
  function traverse(node, visitor, scopeId, state, path) {
    const fscope = path?.functionScopeId ?? node.extra?.functionScopeId ?? scopeId;
    traverseInner(node, visitor, scopeId, fscope, state, path);
    if (!sOut.includes(scopeIdCounter)) {
      log2?.debug("Scopes created", scopeIdCounter, " Scopes removed", removedScopes, "Paths created", pathsCreated, bindingNodesVisited);
      sOut.push(scopeIdCounter);
      const k = Object.fromEntries(Object.entries(nodePathsCreated).sort((a, b) => a[1] - b[1]));
      log2?.debug("Node paths created", k);
    }
  }
  return {
    traverse,
    createNodePath,
    getChildren,
    getPrimitiveChildren,
    getPrimitiveChildrenOrNodePaths,
    getBinding
  };
}
export {
  createTraverser as default,
  functions,
  isAvailableFunction,
  multiQuery,
  parseSource,
  query
};
//# sourceMappingURL=index.mjs.map