{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "stack": "node",
  "description": "TypeScript + JavaScript code-graph rules. Adds what the graph needs on top of test-gap-rules/node.json, which already owns sourceExtensions, excludePathGlobs and the test-path predicates. Definition patterns here are deliberately broader than the test-gap publicApiPatterns: the graph wants unexported declarations too, because Phase 1 narrows scope on the whole module, not just its public surface.",
  "comments": {
    "line": ["//"],
    "block": [["/*", "*/"]],
    "string": ["`", "\"", "'"]
  },
  "definitionPatterns": [
    {
      "id": "class",
      "kind": "class",
      "regex": "\\bclass\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
    },
    {
      "id": "interface",
      "kind": "interface",
      "regex": "\\binterface\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
    },
    {
      "id": "type",
      "kind": "type",
      "regex": "\\btype\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*(?:<[^=>]*>\\s*)?="
    },
    {
      "id": "enum",
      "kind": "enum",
      "regex": "\\benum\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
    },
    {
      "id": "const",
      "kind": "const",
      "regex": "^(?:export\\s+)?(?:declare\\s+)?const\\s+([A-Z][A-Za-z0-9_$]*)\\s*[:=]"
    },
    {
      "id": "function",
      "kind": "function",
      "regex": "\\bfunction\\s*\\*?\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
    }
  ],
  "importPatterns": [
    {
      "id": "specifier",
      "regex": "(?:\\bfrom|\\brequire\\s*\\(|\\bimport)\\s*\\(?\\s*[\"'](?:[^\"']*[\\/:])?([A-Za-z_$][A-Za-z0-9_$-]*)(?:\\.[A-Za-z0-9]+)*[\"']"
    }
  ],
  "referenceKinds": ["class", "interface", "type", "enum", "const"],
  "referenceKindsNote": "function is deliberately absent, for the reason Swift's rules record for func: a bare name matched across files is almost never a call to that exact declaration, and the generic ones (run, main, get, parse, handler) would take the top god-node slots purely because each happened to be declared once. Functions still reach the graph through their defines edge, so they stay findable by name. const is present but narrowed to an uppercase-initial declaration at column 0, which is what a component, a singleton or an exported table looks like; a lowercase const is a local and is not a declaration anyone references across files. Measured on this repo (126 sources): including function put `ok` at degree 52, `f` at 38, `tokens` at 15 and `arg` at 12 - local helper names that happen to be declared exactly once, so the ambiguity rule does not catch them either. Excluding it leaves a thin symbol layer, and that is the honest shape of this stack: a JavaScript module's exported unit is usually a function, so the useful graph here is the import graph between files. `graph-affected \"<file>.mjs\"` answers who imports a module, which is the question this stack actually gets asked.",
  "ignoredIdentifiers": [
    "this",
    "super",
    "constructor",
    "prototype",
    "arguments",
    "class",
    "interface",
    "type",
    "enum",
    "function",
    "const",
    "let",
    "var",
    "return",
    "import",
    "export",
    "default",
    "from",
    "as",
    "in",
    "of",
    "new",
    "delete",
    "typeof",
    "instanceof",
    "void",
    "await",
    "async",
    "yield",
    "if",
    "else",
    "switch",
    "case",
    "for",
    "while",
    "do",
    "break",
    "continue",
    "try",
    "catch",
    "finally",
    "throw",
    "extends",
    "implements",
    "static",
    "public",
    "private",
    "protected",
    "readonly",
    "declare",
    "namespace",
    "module",
    "require",
    "null",
    "undefined",
    "true",
    "false",
    "string",
    "number",
    "boolean",
    "object",
    "any",
    "unknown",
    "never",
    "console",
    "process",
    "window",
    "document",
    "Promise",
    "Array",
    "Object",
    "String",
    "Number",
    "Boolean",
    "Error",
    "JSON",
    "Math",
    "Date",
    "Map",
    "Set"
  ],
  "note": "A JavaScript module specifier IS a string literal, so `importSpecifiersAreStrings` tells the engine to run the import patterns over a text with comments stripped but strings intact. Without it every import edge in this stack would vanish silently, because the specifier is blanked before the pattern ever sees it. Only the import pass reads that text; definitions and the identifier set still come from the fully stripped body, so a name that appears only inside a string still produces no reference edge. Template literals are stripped whole, interpolations included: it is better to lose a real mention than to invent one from a string. The single specifier pattern captures the last path segment with any extension chain removed, which is what the engine resolves against a file basename: ./_retrieval.mjs and ../a/b/util and node:fs and @scope/pkg all reduce to one name.",
  "importSpecifiersAreStrings": true
}
