{"version":3,"file":"index.mjs","names":[],"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["/* Shared core: types, helpers, and classes that\n * use a late-bound native backend (NAPI-RS or WASM).\n * Call initBinding() before constructing classes. */\n\n// ── Native binding types ────────────────────────\n\nexport type NativeBinding = {\n  AhoCorasick: new (\n    patterns: string[],\n    options?: Record<string, unknown>,\n  ) => NativeAhoCorasickInstance;\n  StreamMatcher: new (\n    patterns: string[],\n    options?: Record<string, unknown>,\n  ) => NativeStreamMatcherInstance;\n};\n\ntype NativeAhoCorasickInstance = {\n  patternCount: number;\n  isMatch(haystack: string): boolean;\n  _findIterPacked(haystack: string): Uint32Array;\n  _findOverlappingIterPacked(haystack: string): Uint32Array;\n  replaceAll(\n    haystack: string,\n    replacements: string[],\n  ): string;\n  _findIterPackedBuf(\n    haystack: Buffer | Uint8Array,\n  ): Uint32Array;\n  findIterBuf(haystack: Buffer | Uint8Array): ByteMatch[];\n  isMatchBuf(haystack: Buffer | Uint8Array): boolean;\n};\n\ntype NativeStreamMatcherInstance = {\n  write(chunk: Buffer | Uint8Array): ByteMatch[];\n  flush(): ByteMatch[];\n  reset(): void;\n};\n\n// ── Late-bound native binding ───────────────────\n\nlet binding: NativeBinding;\n\n/** Set the native backend. Must be called once\n *  before any class constructor. */\nexport const initBinding = (b: NativeBinding) => {\n  binding = b;\n};\n\n// ── Public types ────────────────────────────────\n\n/**\n * Which match semantics to use.\n *\n * - `\"leftmost-first\"`: report the first pattern\n *   that matches (insertion order) at each position.\n * - `\"leftmost-longest\"`: report the longest match\n *   at each position.\n */\nexport type MatchKind =\n  | \"leftmost-first\"\n  | \"leftmost-longest\";\n\n/** Options for constructing an automaton. */\nexport type Options = {\n  /**\n   * Match semantics.\n   * @default \"leftmost-first\"\n   */\n  matchKind?: MatchKind;\n  /**\n   * Case-insensitive matching (ASCII only).\n   * @default false\n   */\n  caseInsensitive?: boolean;\n  /**\n   * Force DFA mode. Uses more memory but can be\n   * faster for large pattern sets.\n   * @default false\n   */\n  dfa?: boolean;\n  /**\n   * Only match whole words. Uses Unicode\n   * `is_alphanumeric()` for boundary detection\n   * by default (covers all scripts). Set\n   * `unicodeBoundaries: false` for ASCII-only.\n   * @default false\n   */\n  wholeWords?: boolean;\n  /**\n   * Use Unicode word boundaries for `wholeWords`.\n   * When `true` (default), `is_alphanumeric()` is\n   * used (covers all scripts). When `false`, only\n   * `[a-zA-Z0-9_]` are word characters.\n   * @default true\n   */\n  unicodeBoundaries?: boolean;\n};\n\n/** A named pattern entry. */\nexport type PatternEntry =\n  | string\n  | { pattern: string; name?: string };\n\n/** A single match result (string methods). */\nexport type Match = {\n  /** Index into the patterns array. */\n  pattern: number;\n  /** Start UTF-16 code unit offset (compatible\n   *  with `String.prototype.slice()`). */\n  start: number;\n  /** End offset (exclusive). */\n  end: number;\n  /** The matched text\n   *  (`haystack.slice(start, end)`). */\n  text: string;\n  /** Pattern name (if provided). */\n  name?: string;\n};\n\n/**\n * A single match result (Buffer / streaming\n * methods). Offsets are **byte** positions, not\n * UTF-16 code units.\n */\nexport type ByteMatch = {\n  /** Index into the patterns array. */\n  pattern: number;\n  /** Start byte offset. */\n  start: number;\n  /** End byte offset (exclusive). */\n  end: number;\n};\n\n// ── Unpack helper ───────────────────────────────\n\nfunction unpack(\n  packed: Uint32Array,\n  haystack: string,\n  names: (string | undefined)[] | null,\n): Match[] {\n  const len = packed.length;\n  // `Math.floor` is defensive: if the native side\n  // ever returned a length that is not a multiple of\n  // 3, `new Array(non-integer)` would throw a cryptic\n  // `RangeError` before the per-triple guard could\n  // surface a descriptive error.\n  // eslint-disable-next-line unicorn/no-new-array\n  const matches = new Array<Match>(Math.floor(len / 3));\n  for (let i = 0, j = 0; i < len; i += 3, j++) {\n    const idx = packed[i];\n    const start = packed[i + 1];\n    const end = packed[i + 2];\n    if (\n      idx === undefined ||\n      start === undefined ||\n      end === undefined\n    ) {\n      throw new Error(\n        `Malformed packed matches at offset ${String(i)}`,\n      );\n    }\n    const m: Match = {\n      pattern: idx,\n      start,\n      end,\n      text: haystack.slice(start, end),\n    };\n    if (names && names[idx] !== undefined)\n      m.name = names[idx];\n    matches[j] = m;\n  }\n  return matches;\n}\n\n/** Unpack a buffer-mode packed result. Offsets are\n *  bytes (the buffer path does not translate to\n *  UTF-16 code units), and `ByteMatch` has no\n *  `text` field. */\nfunction unpackBuf(packed: Uint32Array): ByteMatch[] {\n  const len = packed.length;\n  // `Math.floor` is defensive: if the native side\n  // ever returned a length that is not a multiple of\n  // 3, `new Array(non-integer)` would throw a cryptic\n  // `RangeError` before the per-triple guard could\n  // surface a descriptive error.\n  // eslint-disable-next-line unicorn/no-new-array\n  const matches = new Array<ByteMatch>(Math.floor(len / 3));\n  for (let i = 0, j = 0; i < len; i += 3, j++) {\n    const idx = packed[i];\n    const start = packed[i + 1];\n    const end = packed[i + 2];\n    if (\n      idx === undefined ||\n      start === undefined ||\n      end === undefined\n    ) {\n      throw new Error(\n        `Malformed packed matches at offset ${String(i)}`,\n      );\n    }\n    matches[j] = { pattern: idx, start, end };\n  }\n  return matches;\n}\n\n// ── Word boundary helpers ───────────────────────\n\nfunction isWordCharUnicode(ch: string): boolean {\n  return /[\\p{L}\\p{N}_]/u.test(ch);\n}\n\nfunction isWordCharAscii(ch: string): boolean {\n  return /[a-zA-Z0-9_]/.test(ch);\n}\n\nfunction checkBoundary(\n  haystack: string,\n  pos: number,\n  ascii: boolean,\n): boolean {\n  const isWc = ascii ? isWordCharAscii : isWordCharUnicode;\n  const before = pos > 0 && isWc(haystack.charAt(pos - 1));\n  const after =\n    pos < haystack.length && isWc(haystack.charAt(pos));\n  return before !== after;\n}\n\nfunction filterWholeWords(\n  matches: Match[],\n  haystack: string,\n  ascii: boolean,\n): Match[] {\n  return matches.filter(\n    (m) =>\n      checkBoundary(haystack, m.start, ascii) &&\n      checkBoundary(haystack, m.end, ascii),\n  );\n}\n\n// ── Pattern normalization ───────────────────────\n\nfunction normalizePatterns(patterns: readonly unknown[]): {\n  strings: string[];\n  names: (string | undefined)[] | null;\n} {\n  if (!Array.isArray(patterns)) {\n    throw new TypeError(\"Patterns must be an array\");\n  }\n\n  const strings: string[] = [];\n  const names: (string | undefined)[] = [];\n  let hasNames = false;\n\n  for (const p of patterns) {\n    if (typeof p === \"string\") {\n      strings.push(p);\n      names.push(undefined);\n      continue;\n    }\n\n    if (\n      typeof p !== \"object\" ||\n      p === null ||\n      !(\"pattern\" in p) ||\n      typeof p.pattern !== \"string\"\n    ) {\n      throw new TypeError(\n        \"Pattern must be a string or \" +\n          \"{ pattern: string; name?: string }\",\n      );\n    }\n\n    strings.push(p.pattern);\n    if (!(\"name\" in p) || p.name === undefined) {\n      names.push(undefined);\n      continue;\n    }\n\n    if (typeof p.name !== \"string\") {\n      throw new TypeError(\"Pattern name must be a string\");\n    }\n\n    hasNames = true;\n    names.push(p.name);\n  }\n\n  return { strings, names: hasNames ? names : null };\n}\n\n// ── Classes ─────────────────────────────────────\n\n/**\n * Aho-Corasick automaton for multi-pattern string\n * searching.\n *\n * @throws {Error} If the automaton cannot be built\n *   (e.g. patterns exceed internal size limits).\n *\n * @example\n * ```ts\n * const ac = new AhoCorasick([\"foo\", \"bar\"]);\n * ac.findIter(\"foo bar\");\n * // [\n * //   { pattern: 0, start: 0, end: 3,\n * //     text: \"foo\" },\n * //   { pattern: 1, start: 4, end: 7,\n * //     text: \"bar\" },\n * // ]\n * ```\n */\nexport class AhoCorasick {\n  private _inner: NativeAhoCorasickInstance;\n  private _names: (string | undefined)[] | null;\n  private _jsWholeWords: boolean;\n\n  constructor(patterns: PatternEntry[], options?: Options) {\n    const { strings, names } = normalizePatterns(patterns);\n    this._names = names;\n\n    const unicodeWb = options?.unicodeBoundaries ?? true;\n    this._jsWholeWords =\n      !unicodeWb && (options?.wholeWords ?? false);\n\n    const nativeOpts: Record<string, unknown> | undefined =\n      options ? { ...options } : undefined;\n    if (nativeOpts) {\n      delete nativeOpts[\"unicodeBoundaries\"];\n      if (this._jsWholeWords) {\n        nativeOpts[\"wholeWords\"] = false;\n      }\n    }\n\n    this._inner = new binding.AhoCorasick(\n      strings,\n      nativeOpts,\n    );\n  }\n\n  /** Number of patterns in the automaton. */\n  get patternCount(): number {\n    return this._inner.patternCount;\n  }\n\n  /** Returns `true` if any pattern matches. */\n  isMatch(haystack: string): boolean {\n    if (!this._jsWholeWords) {\n      return this._inner.isMatch(haystack);\n    }\n    return this.findIter(haystack).length > 0;\n  }\n\n  /** Find all non-overlapping matches. */\n  findIter(haystack: string): Match[] {\n    let matches = unpack(\n      this._inner._findIterPacked(haystack),\n      haystack,\n      this._names,\n    );\n    if (this._jsWholeWords) {\n      matches = filterWholeWords(matches, haystack, true);\n    }\n    return matches;\n  }\n\n  /** Find all overlapping matches. */\n  findOverlappingIter(haystack: string): Match[] {\n    let matches = unpack(\n      this._inner._findOverlappingIterPacked(haystack),\n      haystack,\n      this._names,\n    );\n    if (this._jsWholeWords) {\n      matches = filterWholeWords(matches, haystack, true);\n    }\n    return matches;\n  }\n\n  /**\n   * Replace all non-overlapping matches.\n   * `replacements[i]` replaces pattern `i`.\n   *\n   * @throws {Error} If `replacements.length` does\n   *   not equal `patternCount`.\n   */\n  replaceAll(\n    haystack: string,\n    replacements: string[],\n  ): string {\n    if (replacements.length !== this.patternCount) {\n      throw new Error(\n        `Expected ${this.patternCount} ` +\n          `replacements, got ${replacements.length}`,\n      );\n    }\n    if (!this._jsWholeWords) {\n      return this._inner.replaceAll(haystack, replacements);\n    }\n    const matches = this.findIter(haystack);\n    let result = \"\";\n    let last = 0;\n    for (const m of matches) {\n      result += haystack.slice(last, m.start);\n      result += replacements[m.pattern];\n      last = m.end;\n    }\n    result += haystack.slice(last);\n    return result;\n  }\n\n  /**\n   * Find matches in a `Buffer` / `Uint8Array`.\n   * Returns **byte offsets** (not UTF-16).\n   */\n  findIterBuf(haystack: Buffer | Uint8Array): ByteMatch[] {\n    return unpackBuf(\n      this._inner._findIterPackedBuf(haystack),\n    );\n  }\n\n  /**\n   * Check whether any pattern matches in a\n   * `Buffer` / `Uint8Array`.\n   */\n  isMatchBuf(haystack: Buffer | Uint8Array): boolean {\n    return this._inner.isMatchBuf(haystack);\n  }\n}\n\n/**\n * Streaming matcher that handles chunk boundaries.\n *\n * @example\n * ```ts\n * const sm = new StreamMatcher([\"needle\"]);\n * for await (const chunk of stream) {\n *   for (const m of sm.write(chunk)) {\n *     console.log(`Pattern ${m.pattern} at`\n *       + ` byte ${m.start}..${m.end}`);\n *   }\n * }\n * sm.flush();\n * ```\n */\nexport class StreamMatcher {\n  private _inner: NativeStreamMatcherInstance;\n\n  constructor(patterns: string[], options?: Options) {\n    const nativeOpts: Record<string, unknown> | undefined =\n      options ? { ...options } : undefined;\n    if (nativeOpts) {\n      delete nativeOpts[\"unicodeBoundaries\"];\n    }\n    this._inner = new binding.StreamMatcher(\n      patterns,\n      nativeOpts,\n    );\n  }\n\n  /** Feed a chunk, get matches with global byte\n   *  offsets. */\n  write(chunk: Buffer | Uint8Array): ByteMatch[] {\n    return this._inner.write(chunk);\n  }\n\n  /** Flush remaining state. */\n  flush(): ByteMatch[] {\n    return this._inner.flush();\n  }\n\n  /** Reset for reuse. */\n  reset(): void {\n    return this._inner.reset();\n  }\n}\n","/* Main entry point — loads the native NAPI-RS\n * binding and re-exports the public API. */\n\nimport { createRequire } from \"node:module\";\n\nimport { initBinding, type NativeBinding } from \"./core\";\n\nconst require = createRequire(import.meta.url);\n// SAFETY: NAPI-RS auto-generated loader returns the\n// native binding object; its shape is validated by\n// usage in the core classes.\nconst native = require(\"../index.cjs\") as NativeBinding;\n\ninitBinding(native);\n\nexport { AhoCorasick, StreamMatcher } from \"./core\";\n\nexport type {\n  ByteMatch,\n  Match,\n  MatchKind,\n  NativeBinding,\n  Options,\n  PatternEntry,\n} from \"./core\";\n"],"mappings":"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"}