[
  {
    "path": ".clinerules",
    "content": "<!-- Canonical source: AGENTS.md — keep this file in sync -->\n# node-re2 — AI Agent Rules\n\n## Project identity\n\nnode-re2 provides Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`.\n\n## Critical rules\n\n- **CommonJS.** The project is `\"type\": \"commonjs\"`. Use `require()` in source, `import` in tests (`.mjs`).\n- **No transpilation.** JavaScript code runs directly.\n- **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules.\n- **Do not modify or delete test expectations** without understanding why they changed.\n- **Do not add comments or remove comments** unless explicitly asked.\n- **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`.\n- **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64).\n- **RE2 is always Unicode-mode.** The `u` flag is always added implicitly.\n- **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input.\n\n## Code style\n\n- C++ code: tabs, 4-wide indentation. JavaScript: 2-space indentation.\n- Prettier: 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens \"avoid\" (see `.prettierrc`).\n- nan (Native Abstractions for Node.js) for the C++ addon API.\n- Semicolons are enforced by Prettier (default `semi: true`).\n\n## Architecture quick reference\n\n- `re2.js` is the main entry point. Loads `build/Release/re2.node`, sets up Symbol aliases (`Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, `Symbol.matchAll`).\n- C++ addon (`lib/*.cc`) wraps Google's RE2 via nan. Each RegExp method has its own `.cc` file.\n- `lib/new.cc` handles construction: parse pattern/flags, translate RegExp → RE2 syntax (via `lib/pattern.cc`).\n- `lib/pattern.cc` translates Unicode class names (`\\p{Letter}` → `\\p{L}`, `\\p{Script=Latin}` → `\\p{Latin}`).\n- `lib/set.cc` implements `RE2.Set` for multi-pattern matching.\n- `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion and buffer helpers.\n- Prebuilt artifacts downloaded at install time via `install-artifact-from-github`.\n\n## Verification commands\n\n- `npm test` — run the full test suite (worker threads)\n- `node tests/test-<name>.mjs` — run a single test file directly\n- `npm run test:seq` — run sequentially\n- `npm run test:proc` — run multi-process\n- `npm run ts-check` — TypeScript type checking\n- `npm run lint` — Prettier check\n- `npm run lint:fix` — Prettier write\n- `npm run verify-build` — quick smoke test\n- `npm run rebuild` — rebuild the native addon (release)\n- `npm run rebuild:dev` — rebuild the native addon (debug)\n\n## File layout\n\n- Entry point: `re2.js` + `re2.d.ts`\n- C++ addon: `lib/*.cc`, `lib/*.h`\n- Build config: `binding.gyp`\n- Tests: `tests/test-*.mjs`\n- TypeScript tests: `ts-tests/test-*.ts`\n- Benchmarks: `bench/`\n- Vendored deps: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules)\n- CI: `.github/workflows/`, `.github/actions/`\n\n## When reading the codebase\n\n- Start with `ARCHITECTURE.md` for the module map and dependency graph.\n- `re2.d.ts` is the best API reference for the public API. It includes `internalSource` and Buffer overloads.\n- `re2.js` is tiny — read it first for the JS-side setup.\n- `lib/addon.cc` shows how all C++ methods are registered.\n- `lib/wrapped_re2.h` defines the core C++ class.\n"
  },
  {
    "path": ".cursorrules",
    "content": "<!-- Canonical source: AGENTS.md — keep this file in sync -->\n# node-re2 — AI Agent Rules\n\n## Project identity\n\nnode-re2 provides Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`.\n\n## Critical rules\n\n- **CommonJS.** The project is `\"type\": \"commonjs\"`. Use `require()` in source, `import` in tests (`.mjs`).\n- **No transpilation.** JavaScript code runs directly.\n- **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules.\n- **Do not modify or delete test expectations** without understanding why they changed.\n- **Do not add comments or remove comments** unless explicitly asked.\n- **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`.\n- **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64).\n- **RE2 is always Unicode-mode.** The `u` flag is always added implicitly.\n- **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input.\n\n## Code style\n\n- C++ code: tabs, 4-wide indentation. JavaScript: 2-space indentation.\n- Prettier: 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens \"avoid\" (see `.prettierrc`).\n- nan (Native Abstractions for Node.js) for the C++ addon API.\n- Semicolons are enforced by Prettier (default `semi: true`).\n\n## Architecture quick reference\n\n- `re2.js` is the main entry point. Loads `build/Release/re2.node`, sets up Symbol aliases (`Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, `Symbol.matchAll`).\n- C++ addon (`lib/*.cc`) wraps Google's RE2 via nan. Each RegExp method has its own `.cc` file.\n- `lib/new.cc` handles construction: parse pattern/flags, translate RegExp → RE2 syntax (via `lib/pattern.cc`).\n- `lib/pattern.cc` translates Unicode class names (`\\p{Letter}` → `\\p{L}`, `\\p{Script=Latin}` → `\\p{Latin}`).\n- `lib/set.cc` implements `RE2.Set` for multi-pattern matching.\n- `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion and buffer helpers.\n- Prebuilt artifacts downloaded at install time via `install-artifact-from-github`.\n\n## Verification commands\n\n- `npm test` — run the full test suite (worker threads)\n- `node tests/test-<name>.mjs` — run a single test file directly\n- `npm run test:seq` — run sequentially\n- `npm run test:proc` — run multi-process\n- `npm run ts-check` — TypeScript type checking\n- `npm run lint` — Prettier check\n- `npm run lint:fix` — Prettier write\n- `npm run verify-build` — quick smoke test\n- `npm run rebuild` — rebuild the native addon (release)\n- `npm run rebuild:dev` — rebuild the native addon (debug)\n\n## File layout\n\n- Entry point: `re2.js` + `re2.d.ts`\n- C++ addon: `lib/*.cc`, `lib/*.h`\n- Build config: `binding.gyp`\n- Tests: `tests/test-*.mjs`\n- TypeScript tests: `ts-tests/test-*.ts`\n- Benchmarks: `bench/`\n- Vendored deps: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules)\n- CI: `.github/workflows/`, `.github/actions/`\n\n## When reading the codebase\n\n- Start with `ARCHITECTURE.md` for the module map and dependency graph.\n- `re2.d.ts` is the best API reference for the public API. It includes `internalSource` and Buffer overloads.\n- `re2.js` is tiny — read it first for the JS-side setup.\n- `lib/addon.cc` shows how all C++ methods are registered.\n- `lib/wrapped_re2.h` defines the core C++ class.\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\n\n[*.{h,cc,cpp}]\nindent_style = tab\nindent_size = 4\n"
  },
  {
    "path": ".github/COPILOT-INSTRUCTIONS.md",
    "content": "<!-- GitHub Copilot project instructions — canonical source is AGENTS.md -->\n\nSee [AGENTS.md](../AGENTS.md) for all AI agent rules and project conventions.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: uhop\nbuy_me_a_coffee: uhop\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-20/Dockerfile",
    "content": "FROM node:20-alpine\n\nRUN apk add --no-cache python3 make gcc g++ linux-headers\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-20/action.yml",
    "content": "name: 'Create a binary artifact for Node 20 on Alpine Linux'\ndescription: 'Create a binary artifact for Node 20 on Alpine Linux using musl'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-20/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-22/Dockerfile",
    "content": "FROM node:22-alpine\n\nRUN apk add --no-cache python3 make gcc g++ linux-headers\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-22/action.yml",
    "content": "name: 'Create a binary artifact for Node 22 on Alpine Linux'\ndescription: 'Create a binary artifact for Node 22 on Alpine Linux using musl'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-22/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-24/Dockerfile",
    "content": "FROM node:24-alpine\n\nRUN apk add --no-cache python3 make gcc g++ linux-headers\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-24/action.yml",
    "content": "name: 'Create a binary artifact for Node 24 on Alpine Linux'\ndescription: 'Create a binary artifact for Node 24 on Alpine Linux using musl'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-24/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-25/Dockerfile",
    "content": "FROM node:25-alpine\n\nRUN apk add --no-cache python3 make gcc g++ linux-headers\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-25/action.yml",
    "content": "name: 'Create a binary artifact for Node 25 on Alpine Linux'\ndescription: 'Create a binary artifact for Node 25 on Alpine Linux using musl'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-alpine-node-25/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-node-20/Dockerfile",
    "content": "FROM node:20-bullseye\n\nRUN apt install python3 make gcc g++\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-node-20/action.yml",
    "content": "name: 'Create a binary artifact for Node 20 on Debian Bullseye Linux'\ndescription: 'Create a binary artifact for Node 20 on Debian Bullseye Linux'\ninputs:\n  node-version:\n    description: 'Node.js version'\n    required: false\n    default: '20'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-node-20/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-node-22/Dockerfile",
    "content": "FROM node:22-bullseye\n\nRUN apt install python3 make gcc g++\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-node-22/action.yml",
    "content": "name: 'Create a binary artifact for Node 22 on Debian Bullseye Linux'\ndescription: 'Create a binary artifact for Node 22 on Debian Bullseye Linux'\ninputs:\n  node-version:\n    description: 'Node.js version'\n    required: false\n    default: '22'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-node-22/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-node-24/Dockerfile",
    "content": "FROM node:24-bullseye\n\nRUN apt install python3 make gcc g++\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-node-24/action.yml",
    "content": "name: 'Create a binary artifact for Node 24 on Debian Bullseye Linux'\ndescription: 'Create a binary artifact for Node 24 on Debian Bullseye Linux'\ninputs:\n  node-version:\n    description: 'Node.js version'\n    required: false\n    default: '24'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-node-24/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/actions/linux-node-25/Dockerfile",
    "content": "FROM node:25-trixie\n\nRUN apt install python3 make gcc g++\n\nCOPY entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".github/actions/linux-node-25/action.yml",
    "content": "name: 'Create a binary artifact for Node 25 on Debian Trixie Linux'\ndescription: 'Create a binary artifact for Node 25 on Debian Trixie Linux'\ninputs:\n  node-version:\n    description: 'Node.js version'\n    required: false\n    default: '25'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{inputs.node-version}}\n"
  },
  {
    "path": ".github/actions/linux-node-25/entrypoint.sh",
    "content": "#!/bin/sh\n\nset -e\n\nexport USERNAME=`whoami`\nexport DEVELOPMENT_SKIP_GETTING_ASSET=true\nnpm i\nnpm run build --if-present\nnpm test\nnpm run save-to-github\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"npm\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Node.js builds\n\non:\n  push:\n    tags:\n      - v?[0-9]+.[0-9]+.[0-9]+.[0-9]+\n      - v?[0-9]+.[0-9]+.[0-9]+\n      - v?[0-9]+.[0-9]+\n\npermissions:\n  id-token: write\n  contents: write\n  attestations: write\n\njobs:\n  create-release:\n    name: Create release\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v6\n      - env:\n          GH_TOKEN: ${{github.token}}\n        run: |\n          REF=${{github.ref}}\n          TAG=${REF#\"refs/tags/\"}\n          gh release create -t \"Release ${TAG}\" -n \"\" \"${{github.ref}}\"\n\n  build:\n    name: Node.js ${{matrix.node-version}} on ${{matrix.os}}\n    needs: create-release\n    runs-on: ${{matrix.os}}\n    strategy:\n      matrix:\n        os: [macos-latest, windows-latest, macos-15-intel, windows-11-arm]\n        node-version: [20, 22, 24, 25]\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Setup Node.js ${{matrix.node-version}}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{matrix.node-version}}\n      - name: Install the package and run tests\n        env:\n          DEVELOPMENT_SKIP_GETTING_ASSET: true\n        run: |\n          npm i\n          npm run build --if-present\n          npm test\n      - name: Save to GitHub\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n        run: npm run save-to-github\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-node-20:\n    name: Node.js 20 on Bullseye\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-20/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-node-22:\n    name: Node.js 22 on Bullseye\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-22/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-alpine-node-20:\n    name: Node.js 20 on Alpine\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-20/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-alpine-node-22:\n    name: Node.js 22 on Alpine\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-22/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-node-20:\n    name: Node.js 20 on Bullseye ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-20/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-node-22:\n    name: Node.js 22 on Bullseye ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-22/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-alpine-node-20:\n    name: Node.js 20 on Alpine ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-20/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-alpine-node-22:\n    name: Node.js 22 on Alpine ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-22/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-node-24:\n    name: Node.js 24 on Bullseye\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-24/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-alpine-node-24:\n    name: Node.js 24 on Alpine\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-24/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-node-24:\n    name: Node.js 24 on Bullseye ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-24/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-alpine-node-24:\n    name: Node.js 24 on Alpine ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-24/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-node-25:\n    name: Node.js 25 on Trixie\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-25/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-alpine-node-25:\n    name: Node.js 25 on Alpine\n    needs: create-release\n    runs-on: ubuntu-latest\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-25/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-node-25:\n    name: Node.js 25 on Trixie ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-node-25/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n\n  build-linux-arm64-alpine-node-25:\n    name: Node.js 25 on Alpine ARM64\n    needs: create-release\n    runs-on: ubuntu-24.04-arm\n    continue-on-error: true\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Install, test, and create artifact\n        uses: ./.github/actions/linux-alpine-node-25/\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Attest\n        if: env.CREATED_ASSET_NAME != ''\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: '${{ env.CREATED_ASSET_NAME }}'\n          subject-path: '${{ github.workspace }}/build/Release/re2.node'\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Node.js CI\n\non:\n  push:\n    branches: ['*']\n  pull_request:\n    branches: [master]\n\njobs:\n  tests:\n    name: Node.js ${{matrix.node-version}} on ${{matrix.os}}\n    permissions:\n      contents: read\n    runs-on: ${{matrix.os}}\n\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macOS-latest, windows-latest]\n        node-version: [20, 22, 24, 25]\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          submodules: true\n      - name: Setup Node.js ${{matrix.node-version}}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{matrix.node-version}}\n      - name: Install the package and run tests\n        env:\n          DEVELOPMENT_SKIP_GETTING_ASSET: true\n        run: |\n          npm i\n          npm run build --if-present\n          npm test\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nbuild/\nreport/\ncoverage/\n.AppleDouble\n/.development\n/.developmentx\n/.xdevelopment\n\n/scripts/save-local.sh\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"vendor/re2\"]\n\tpath = vendor/re2\n\turl = https://github.com/google/re2\n[submodule \"vendor/abseil-cpp\"]\n\tpath = vendor/abseil-cpp\n\turl = https://github.com/abseil/abseil-cpp\n[submodule \"wiki\"]\n\tpath = wiki\n\turl = git@github.com:uhop/node-re2.wiki.git\n"
  },
  {
    "path": ".prettierignore",
    "content": "/.windsurf/workflows\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"bracketSpacing\": false,\n  \"arrowParens\": \"avoid\",\n  \"trailingComma\": \"none\"\n}\n"
  },
  {
    "path": ".vscode/c_cpp_properties.json",
    "content": "{\n    \"configurations\": [\n        {\n            \"name\": \"Mac\",\n            \"includePath\": [\n                \"${workspaceFolder}/**\",\n                \"/${env.NVM_INC}/**\"\n            ],\n            \"defines\": [],\n            \"macFrameworkPath\": [\n                \"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks\"\n            ],\n            \"compilerPath\": \"/usr/bin/clang\",\n            \"cStandard\": \"c17\",\n            \"cppStandard\": \"c++17\",\n            \"intelliSenseMode\": \"macos-clang-arm64\"\n        }\n    ],\n    \"version\": 4\n}"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug tests\",\n      \"preLaunchTask\": \"npm: build:dev\",\n      \"program\": \"${env:NVM_BIN}/node\",\n      \"args\": [\"${workspaceFolder}/tests/tests.js\"],\n      \"cwd\": \"${workspaceFolder}\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\n    \"heya\",\n    \"PCRE\",\n    \"replacee\",\n    \"Submatch\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": [\n\t\t{\n\t\t\t\"type\": \"npm\",\n\t\t\t\"script\": \"build:dev\",\n\t\t\t\"group\": \"build\",\n\t\t\t\"problemMatcher\": [],\n\t\t\t\"label\": \"npm: build:dev\",\n\t\t\t\"detail\": \"node-gyp -j max build --debug\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": ".windsurf/skills/docs-review/SKILL.md",
    "content": "---\nname: docs-review\ndescription: Review and improve English in documentation files for brevity and clarity. Use when asked to review docs, improve documentation writing, or edit prose for clarity.\n---\n\n# Review Documentation for node-re2\n\nReview and improve English in documentation files for brevity, clarity, and correctness.\n\n## Steps\n\n1. Read the target documentation file(s).\n2. Check for:\n   - Grammatical errors and awkward phrasing.\n   - Verbose or redundant sentences — prefer concise, direct language.\n   - Consistency with existing project terminology (RE2, RegExp, Buffer, nan, node-gyp, etc.).\n   - Correct code examples that match the current API.\n   - Accurate links (wiki, npm, GitHub).\n3. Make edits directly in the file:\n   - Preserve the existing structure and headings.\n   - Do not add or remove comments in code examples unless explicitly asked.\n   - Keep technical accuracy — do not change meaning.\n4. If reviewing `README.md`, cross-check API descriptions against `re2.d.ts`.\n5. If reviewing `llms.txt` or `llms-full.txt`, ensure examples are runnable and API signatures match `re2.d.ts`.\n6. Report a summary of changes made.\n\n## Style guidelines\n\n- Use active voice.\n- Prefer short sentences.\n- Use \"RE2\" (not \"re2\" or \"Re2\") when referring to the engine or the JS object.\n- Use backticks for code references: `RE2`, `Buffer`, `exec()`, etc.\n- Use \"e.g.\" and \"i.e.\" sparingly — prefer \"for example\" and \"that is\" in longer prose.\n- American English spelling.\n"
  },
  {
    "path": ".windsurf/skills/write-tests/SKILL.md",
    "content": "---\nname: write-tests\ndescription: Write or update tape-six tests for a module or feature. Use when asked to write tests, add test coverage, or create typing tests for node-re2.\n---\n\n# Write Tests for node-re2\n\nWrite or update tests using the tape-six testing library.\n\n## Steps\n\n1. Read `node_modules/tape-six/TESTING.md` for the full tape-six API reference (assertions, hooks, patterns, configuration).\n2. Identify the module or feature to test. Read its source code to understand the public API.\n3. Check existing tests in `tests/` for node-re2 conventions and patterns.\n4. Create or update the test file in `tests/`:\n   - For runtime tests use `.mjs`.\n   - Import RE2 with: `import {RE2} from '../re2.js';`\n   - Import tape-six with: `import test from 'tape-six';`\n   - Test with both **string** and **Buffer** inputs — Buffer support is a first-class feature.\n   - Test edge cases: empty strings, no match, global flag behavior, lastIndex, Unicode input.\n5. For TypeScript typing tests, update `ts-tests/test-types.ts`:\n   - Verify typed usage patterns compile correctly.\n   // turbo\n6. Run the new test file directly to verify: `node tests/test-<name>.mjs`\n   // turbo\n7. Run the full test suite to check for regressions: `npm test`\n   - If debugging, use `npm run test:seq` (runs sequentially, easier to trace issues).\n8. Report results and any failures.\n\n## node-re2 test conventions\n\n- Test file naming: `test-*.mjs` in `tests/`.\n- TypeScript typing tests: `test-*.ts` in `ts-tests/`.\n- Runtime tests (`.mjs`): ESM imports, `import test from 'tape-six'`.\n- Tests are configured in `package.json` under the `\"tape6\"` section.\n- Test files should be directly executable: `node tests/test-foo.mjs`.\n- Existing tests use synchronous `t => { ... }` style (not async/promise-based).\n- Always test both string and Buffer variants of methods.\n- Use `t.ok()`, `t.equal()`, `t.deepEqual()`, `t.fail()` for assertions.\n- Use try/catch blocks to test error conditions (e.g., invalid patterns throwing `SyntaxError`).\n"
  },
  {
    "path": ".windsurf/workflows/add-module.md",
    "content": "---\ndescription: Checklist for adding a new C++ method or JS feature to node-re2\n---\n\n# Add a New Module\n\nFollow these steps when adding a new method, feature, or C++ implementation.\n\n## New C++ method (e.g., `lib/foo.cc`)\n\n1. Create `lib/foo.cc` with the implementation.\n   - Use nan for the Node.js addon API.\n   - Follow existing patterns in `lib/exec.cc` or `lib/test.cc`.\n   - Tabs for indentation, 4-wide.\n   - Include `lib/wrapped_re2.h` and `lib/util.h` as needed.\n2. Register the method in `lib/addon.cc`:\n   - Add `Nan::SetPrototypeMethod(tpl, \"foo\", Foo);` or equivalent.\n3. Add the method to `lib/wrapped_re2.h` if it needs a static declaration.\n4. Add the source file to `binding.gyp` in the `\"sources\"` array.\n   // turbo\n5. Rebuild the addon: `npm run rebuild`\n6. Update `re2.js` if JS-side setup is needed (e.g., Symbol aliases).\n7. Update `re2.d.ts` with TypeScript declarations for the new method.\n   - Keep `re2.js` and `re2.d.ts` in sync.\n8. Create `tests/test-foo.mjs` with automated tests (tape-six, ESM):\n   - `import {RE2} from '../re2.js';`\n   - Test with strings and Buffers.\n   - Test edge cases (empty input, no match, global flag, etc.).\n   // turbo\n9. Run the new test: `node tests/test-foo.mjs`\n10. Update TypeScript tests in `ts-tests/test-types.ts` if the public API changed.\n11. Update `README.md` with documentation for the new feature.\n12. Update `ARCHITECTURE.md` — add to project layout and C++ addon table.\n13. Update `llms.txt` and `llms-full.txt` with a description and examples.\n14. Update `AGENTS.md` if the architecture quick reference needs updating.\n    // turbo\n15. Verify: `npm test`\n    // turbo\n16. Verify: `npm run ts-check`\n    // turbo\n17. Verify: `npm run lint`\n\n## JS-only feature (e.g., new Symbol alias, helper)\n\n1. Add the implementation to `re2.js`.\n2. Update `re2.d.ts` with TypeScript declarations.\n3. Create or update tests in `tests/`.\n   // turbo\n4. Run the new test: `node tests/test-<name>.mjs`\n5. Update `README.md`, `llms.txt`, `llms-full.txt`.\n6. Update `AGENTS.md` and `ARCHITECTURE.md` if needed.\n   // turbo\n7. Verify: `npm test`\n   // turbo\n8. Verify: `npm run ts-check`\n   // turbo\n9. Verify: `npm run lint`\n"
  },
  {
    "path": ".windsurf/workflows/ai-docs-update.md",
    "content": "---\ndescription: Update AI-facing documentation files after API or architecture changes\n---\n\n# AI Documentation Update\n\nUpdate all AI-facing files after changes to the public API, modules, or project structure.\n\n## Steps\n\n1. Read `re2.js` and `re2.d.ts` to identify the current public API.\n2. Read `AGENTS.md` and `ARCHITECTURE.md` for current state.\n3. Identify what changed (new methods, new flags, new C++ files, renamed exports, removed features, etc.).\n4. Update `llms.txt`:\n   - Ensure the API section matches `re2.d.ts`.\n   - Update common patterns if new features were added.\n   - Keep it concise — this is for quick LLM consumption.\n5. Update `llms-full.txt`:\n   - Full API reference with all methods, options, and examples.\n   - Include any new features, RE2.Set changes, or Buffer behavior.\n6. Update `ARCHITECTURE.md` if project structure or module dependencies changed.\n7. Update `AGENTS.md` if critical rules, commands, or architecture quick reference changed.\n8. Sync `.windsurfrules`, `.cursorrules`, `.clinerules` if `AGENTS.md` changed:\n   - These three files should be identical copies of the condensed rules.\n9. Update `README.md` if the public-facing docs need to reflect new features.\n10. Track progress with the todo list and provide a summary when done.\n"
  },
  {
    "path": ".windsurf/workflows/release-check.md",
    "content": "---\ndescription: Pre-release verification checklist for node-re2\n---\n\n# Release Check\n\nRun through this checklist before publishing a new version.\n\n## Steps\n\n1. Check that `re2.js` and `re2.d.ts` are in sync (all exports, all types).\n2. Check that `ARCHITECTURE.md` reflects any structural changes.\n3. Check that `AGENTS.md` is up to date with any rule or workflow changes.\n4. Check that `.windsurfrules`, `.clinerules`, `.cursorrules` are in sync with `AGENTS.md`.\n5. Check that `llms.txt` and `llms-full.txt` are up to date with any API changes.\n6. Verify `package.json`:\n   - `files` array includes all necessary entries (`binding.gyp`, `lib`, `re2.d.ts`, `scripts/*.js`, `vendor`).\n   - `main` points to `re2.js`.\n   - `types` points to `re2.d.ts`.\n7. Check that the copyright year in `LICENSE` includes the current year.\n8. Bump `version` in `package.json`.\n9. Update release history in `README.md`.\n10. Run `npm install` to regenerate `package-lock.json`.\n    // turbo\n11. Rebuild the native addon: `npm run rebuild`\n    // turbo\n12. Run the quick smoke test: `npm run verify-build`\n    // turbo\n13. Run the full test suite: `npm test`\n    // turbo\n14. Run TypeScript check: `npm run ts-check`\n    // turbo\n15. Run lint: `npm run lint`\n    // turbo\n16. Dry-run publish to verify package contents: `npm pack --dry-run`\n"
  },
  {
    "path": ".windsurfrules",
    "content": "<!-- Canonical source: AGENTS.md — keep this file in sync -->\n# node-re2 — AI Agent Rules\n\n## Project identity\n\nnode-re2 provides Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`.\n\n## Critical rules\n\n- **CommonJS.** The project is `\"type\": \"commonjs\"`. Use `require()` in source, `import` in tests (`.mjs`).\n- **No transpilation.** JavaScript code runs directly.\n- **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules.\n- **Do not modify or delete test expectations** without understanding why they changed.\n- **Do not add comments or remove comments** unless explicitly asked.\n- **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`.\n- **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64).\n- **RE2 is always Unicode-mode.** The `u` flag is always added implicitly.\n- **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input.\n\n## Code style\n\n- C++ code: tabs, 4-wide indentation. JavaScript: 2-space indentation.\n- Prettier: 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens \"avoid\" (see `.prettierrc`).\n- nan (Native Abstractions for Node.js) for the C++ addon API.\n- Semicolons are enforced by Prettier (default `semi: true`).\n\n## Architecture quick reference\n\n- `re2.js` is the main entry point. Loads `build/Release/re2.node`, sets up Symbol aliases (`Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, `Symbol.matchAll`).\n- C++ addon (`lib/*.cc`) wraps Google's RE2 via nan. Each RegExp method has its own `.cc` file.\n- `lib/new.cc` handles construction: parse pattern/flags, translate RegExp → RE2 syntax (via `lib/pattern.cc`).\n- `lib/pattern.cc` translates Unicode class names (`\\p{Letter}` → `\\p{L}`, `\\p{Script=Latin}` → `\\p{Latin}`).\n- `lib/set.cc` implements `RE2.Set` for multi-pattern matching.\n- `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion and buffer helpers.\n- Prebuilt artifacts downloaded at install time via `install-artifact-from-github`.\n\n## Verification commands\n\n- `npm test` — run the full test suite (worker threads)\n- `node tests/test-<name>.mjs` — run a single test file directly\n- `npm run test:seq` — run sequentially\n- `npm run test:proc` — run multi-process\n- `npm run ts-check` — TypeScript type checking\n- `npm run lint` — Prettier check\n- `npm run lint:fix` — Prettier write\n- `npm run verify-build` — quick smoke test\n- `npm run rebuild` — rebuild the native addon (release)\n- `npm run rebuild:dev` — rebuild the native addon (debug)\n\n## File layout\n\n- Entry point: `re2.js` + `re2.d.ts`\n- C++ addon: `lib/*.cc`, `lib/*.h`\n- Build config: `binding.gyp`\n- Tests: `tests/test-*.mjs`\n- TypeScript tests: `ts-tests/test-*.ts`\n- Benchmarks: `bench/`\n- Vendored deps: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules)\n- CI: `.github/workflows/`, `.github/actions/`\n\n## When reading the codebase\n\n- Start with `ARCHITECTURE.md` for the module map and dependency graph.\n- `re2.d.ts` is the best API reference for the public API. It includes `internalSource` and Buffer overloads.\n- `re2.js` is tiny — read it first for the JS-side setup.\n- `lib/addon.cc` shows how all C++ methods are registered.\n- `lib/wrapped_re2.h` defines the core C++ class.\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md — node-re2\n\n> `node-re2` provides Node.js bindings for [RE2](https://github.com/google/re2): a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`.\n\nFor project structure, module dependencies, and the architecture overview see [ARCHITECTURE.md](./ARCHITECTURE.md).\nFor detailed usage docs see the [README](./README.md) and the [wiki](https://github.com/uhop/node-re2/wiki).\n\n## Setup\n\nThis project uses git submodules for vendored dependencies (RE2 and Abseil):\n\n```bash\ngit clone --recursive git@github.com:uhop/node-re2.git\ncd node-re2\nnpm install\n```\n\nIf the native addon fails to download a prebuilt artifact, it builds locally via `node-gyp`.\n\n## Commands\n\n- **Install:** `npm install` (downloads prebuilt artifact or builds from source)\n- **Build (release):** `npm run rebuild` (or `node-gyp -j max rebuild`)\n- **Build (debug):** `npm run rebuild:dev` (or `node-gyp -j max rebuild --debug`)\n- **Test:** `npm test` (runs `tape6 --flags FO`, worker threads)\n- **Test (sequential):** `npm run test:seq`\n- **Test (multi-process):** `npm run test:proc`\n- **Test (single file):** `node tests/test-<name>.mjs`\n- **TypeScript check:** `npm run ts-check`\n- **Lint:** `npm run lint` (Prettier check)\n- **Lint fix:** `npm run lint:fix` (Prettier write)\n- **Verify build:** `npm run verify-build`\n\n## Project structure\n\n```\nnode-re2/\n├── package.json          # Package config; \"tape6\" section configures test discovery\n├── binding.gyp           # node-gyp build configuration for the C++ addon\n├── re2.js                # Main entry point: loads native addon, sets up Symbol aliases\n├── re2.d.ts              # TypeScript declarations for the public API\n├── tsconfig.json         # TypeScript config (noEmit, strict, types: [\"node\"])\n├── lib/                  # C++ source code (native addon)\n│   ├── addon.cc          # Node.js addon initialization, method registration\n│   ├── wrapped_re2.h     # WrappedRE2 class definition (core C++ wrapper)\n│   ├── wrapped_re2_set.h # WrappedRE2Set class definition (RE2.Set wrapper)\n│   ├── isolate_data.h    # Per-isolate data struct for thread-safe addon state\n│   ├── new.cc            # Constructor: parse pattern/flags, create RE2 instance\n│   ├── exec.cc           # RE2.prototype.exec() implementation\n│   ├── test.cc           # RE2.prototype.test() implementation\n│   ├── match.cc          # RE2.prototype.match() implementation\n│   ├── replace.cc        # RE2.prototype.replace() implementation\n│   ├── search.cc         # RE2.prototype.search() implementation\n│   ├── split.cc          # RE2.prototype.split() implementation\n│   ├── to_string.cc      # RE2.prototype.toString() implementation\n│   ├── accessors.cc      # Property accessors (source, flags, lastIndex, etc.)\n│   ├── pattern.cc        # Pattern translation (RegExp → RE2 syntax, Unicode classes)\n│   ├── set.cc            # RE2.Set implementation (multi-pattern matching)\n│   ├── util.cc           # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers)\n│   ├── util.h            # Utility declarations\n│   └── pattern.h         # Pattern translation declarations\n├── scripts/\n│   └── verify-build.js   # Quick smoke test for the built addon\n├── tests/                # Test files (test-*.mjs using tape-six)\n├── ts-tests/             # TypeScript type-checking tests\n│   └── test-types.ts     # Verifies type declarations compile correctly\n├── bench/                # Benchmarks\n├── vendor/               # Vendored C++ dependencies (git submodules)\n│   ├── re2/              # Google RE2 library source\n│   └── abseil-cpp/       # Abseil C++ library (RE2 dependency)\n└── .github/              # CI workflows, Dependabot config, actions\n```\n\n## Code style\n\n- **CommonJS** throughout (`\"type\": \"commonjs\"` in package.json).\n- **No transpilation** — JavaScript code runs directly.\n- **C++ code** uses tabs for indentation, 4-wide. JavaScript uses 2-space indentation.\n- **Prettier** for JS/TS formatting (see `.prettierrc`): 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens \"avoid\".\n- **nan** (Native Abstractions for Node.js) for the C++ addon API.\n- Semicolons are enforced by Prettier (default `semi: true`).\n- Imports use `require()` syntax in source, `import` in tests (`.mjs`).\n\n## Critical rules\n\n- **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules.\n- **Do not modify or delete test expectations** without understanding why they changed.\n- **Do not add comments or remove comments** unless explicitly asked.\n- **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`.\n- **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64).\n- **RE2 is always Unicode-mode.** The `u` flag is always added implicitly.\n- **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input.\n\n## Architecture\n\n- `re2.js` is the main entry point. It loads the native C++ addon from `build/Release/re2.node` and sets up `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` on the prototype.\n- The C++ addon (`lib/*.cc`) wraps Google's RE2 library via nan. Each RegExp method has its own `.cc` file.\n- `lib/new.cc` handles construction: parsing patterns, translating RegExp syntax to RE2 syntax (via `lib/pattern.cc`), and creating the underlying `re2::RE2` instance.\n- `lib/pattern.cc` translates JavaScript RegExp features to RE2 equivalents, including Unicode class names (`\\p{Letter}` → `\\p{L}`, `\\p{Script=Latin}` → `\\p{Latin}`).\n- `lib/set.cc` implements `RE2.Set` for multi-pattern matching using `re2::RE2::Set`.\n- `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion helpers and buffer utilities.\n- Prebuilt native artifacts are hosted on GitHub Releases and downloaded at install time via `install-artifact-from-github`.\n\n## Writing tests\n\n```js\nimport test from 'tape-six';\nimport {RE2} from '../re2.js';\n\ntest('example', t => {\n  const re = new RE2('a(b*)', 'i');\n  const result = re.exec('aBbC');\n  t.ok(result);\n  t.equal(result[0], 'aBb');\n  t.equal(result[1], 'Bb');\n});\n```\n\n- Test files use `tape-six`: `.mjs` for runtime tests, `.ts` for TypeScript typing tests.\n- Test file naming convention: `test-*.mjs` in `tests/`, `test-*.ts` in `ts-tests/`.\n- Tests are configured in `package.json` under the `\"tape6\"` section.\n- Test files should be directly executable: `node tests/test-foo.mjs`.\n\n## Key conventions\n\n- The library is a drop-in replacement for `RegExp` — the `RE2` object emulates the standard `RegExp` API.\n- `RE2.Set` provides multi-pattern matching: `new RE2.Set(patterns, flags, options)`.\n- Static helpers: `RE2.getUtf8Length(str)`, `RE2.getUtf16Length(buf)`.\n- `RE2.unicodeWarningLevel` controls behavior when non-Unicode regexps are created.\n- The `install` script tries to download a prebuilt `.node` artifact before falling back to `node-gyp rebuild`.\n- All C++ source is in `lib/`, all vendored third-party C++ is in `vendor/`.\n"
  },
  {
    "path": "ARCHITECTURE.md",
    "content": "# Architecture\n\n`node-re2` provides Node.js bindings for Google's [RE2](https://github.com/google/re2) regular expression engine. It is a C++ native addon built with `node-gyp` and `nan`. The `RE2` object is a drop-in replacement for `RegExp` with guaranteed linear-time matching (no ReDoS).\n\n## Project layout\n\n```\npackage.json              # Package config; \"tape6\" section configures test discovery\nbinding.gyp               # node-gyp build configuration for the C++ addon\nre2.js                    # Main entry point: loads native addon, sets up Symbol aliases\nre2.d.ts                  # TypeScript declarations for the public API\ntsconfig.json             # TypeScript config (noEmit, strict, types: [\"node\"])\nlib/                      # C++ source code (native addon)\n├── addon.cc              # Node.js addon initialization, method registration\n├── wrapped_re2.h         # WrappedRE2 class definition (core C++ wrapper)\n├── wrapped_re2_set.h     # WrappedRE2Set class definition (RE2.Set wrapper)\n├── isolate_data.h        # Per-isolate data struct for thread-safe addon state\n├── new.cc                # Constructor: parse pattern/flags, create RE2 instance\n├── exec.cc               # RE2.prototype.exec() implementation\n├── test.cc               # RE2.prototype.test() implementation\n├── match.cc              # RE2.prototype.match() implementation\n├── replace.cc            # RE2.prototype.replace() implementation\n├── search.cc             # RE2.prototype.search() implementation\n├── split.cc              # RE2.prototype.split() implementation\n├── to_string.cc          # RE2.prototype.toString() implementation\n├── accessors.cc          # Property accessors (source, flags, lastIndex, etc.)\n├── pattern.cc            # Pattern translation (RegExp → RE2 syntax, Unicode classes)\n├── pattern.h             # Pattern translation declarations\n├── set.cc                # RE2.Set implementation (multi-pattern matching)\n├── util.cc               # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers)\n└── util.h                # Utility declarations\nscripts/\n└── verify-build.js       # Quick smoke test for the built addon\ntests/                    # Test files (test-*.mjs using tape-six)\nts-tests/                 # TypeScript type-checking tests\n└── test-types.ts         # Verifies type declarations compile correctly\nbench/                    # Benchmarks\nvendor/                   # Vendored C++ dependencies (git submodules) — DO NOT MODIFY\n├── re2/                  # Google RE2 library source\n└── abseil-cpp/           # Abseil C++ library (RE2 dependency)\n.github/                  # CI workflows, Dependabot config, actions\n```\n\n## Core concepts\n\n### How the addon works\n\n1. `re2.js` is the entry point. It loads the compiled C++ addon from `build/Release/re2.node`.\n2. The addon exposes an `RE2` constructor that wraps `re2::RE2` from Google's RE2 library.\n3. `re2.js` adds `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` to the prototype so `RE2` instances work with ES6 string methods.\n4. The `RE2` constructor can be called with or without `new` (factory mode).\n\n### C++ addon structure\n\nEach RegExp method has its own `.cc` file for maintainability:\n\n| File            | Purpose                                                          |\n| --------------- | ---------------------------------------------------------------- |\n| `addon.cc`      | Node.js module initialization, registers all methods/accessors   |\n| `isolate_data.h` | Per-isolate data struct (`AddonData`) for thread-safe addon state |\n| `wrapped_re2.h` | `WrappedRE2` class: holds `re2::RE2*`, flags, lastIndex, source |\n| `new.cc`        | Constructor: parses pattern + flags, translates syntax, creates RE2 instance |\n| `exec.cc`       | `exec()` — find match with capture groups                       |\n| `test.cc`       | `test()` — boolean match check                                  |\n| `match.cc`      | `match()` — String.prototype.match equivalent                   |\n| `replace.cc`    | `replace()` — substitution with string or function replacer     |\n| `search.cc`     | `search()` — find index of first match                          |\n| `split.cc`      | `split()` — split string by pattern                             |\n| `to_string.cc`  | `toString()` — `/pattern/flags` representation                  |\n| `accessors.cc`  | Property getters: `source`, `flags`, `lastIndex`, `global`, `ignoreCase`, `multiline`, `dotAll`, `unicode`, `sticky`, `hasIndices`, `internalSource` |\n| `pattern.cc`    | Translates JS RegExp syntax to RE2 syntax, maps Unicode property names |\n| `set.cc`        | `RE2.Set` — multi-pattern matching via `re2::RE2::Set`          |\n| `util.cc`       | UTF-8 ↔ UTF-16 conversion, buffer/string helpers                |\n\n### Pattern translation (pattern.cc)\n\nJavaScript RegExp features are translated to RE2 equivalents:\n\n- Named groups: `(?<name>...)` syntax is preserved (RE2 supports it natively).\n- Unicode classes: long names like `\\p{Letter}` are mapped to short names `\\p{L}`. Script names like `\\p{Script=Latin}` are mapped to `\\p{Latin}`.\n- Backreferences and lookahead assertions are **not supported** — RE2 throws `SyntaxError`.\n\n### Buffer support\n\nAll methods accept both strings and Node.js Buffers:\n\n- Buffer inputs are assumed UTF-8 encoded.\n- Buffer inputs produce Buffer outputs (in composite result objects too).\n- Offsets and lengths are in bytes (not characters) when using Buffers.\n- The `useBuffers` property on replacer functions controls offset reporting in `replace()`.\n\n### RE2.Set (set.cc)\n\nMulti-pattern matching using `re2::RE2::Set`:\n\n- `new RE2.Set(patterns, flags?, options?)` — compile multiple patterns into a single automaton.\n- `set.test(str)` — returns `true` if any pattern matches.\n- `set.match(str)` — returns array of indices of matching patterns.\n- Properties: `size`, `source`, `sources`, `flags`, `anchor`.\n\n### Build system\n\n- `binding.gyp` defines the node-gyp build: compiles all `.cc` files in `lib/` plus vendored RE2 and Abseil sources.\n- Platform-specific compiler flags are set for GCC, Clang, and MSVC.\n- The `install` npm script first tries to download a prebuilt `re2.node` from GitHub Releases via `install-artifact-from-github`, falling back to a local `node-gyp rebuild`.\n- Prebuilt artifacts cover: Linux (x64, arm64, Alpine/musl), macOS (x64, arm64), Windows (x64, arm64).\n\n## Module dependency graph\n\n```\nre2.js ──→ build/Release/re2.node (compiled C++ addon)\n                │\n                ├── lib/addon.cc (init)\n                │     ├── lib/new.cc ──→ lib/pattern.cc\n                │     ├── lib/exec.cc\n                │     ├── lib/test.cc\n                │     ├── lib/match.cc\n                │     ├── lib/replace.cc\n                │     ├── lib/search.cc\n                │     ├── lib/split.cc\n                │     ├── lib/to_string.cc\n                │     ├── lib/accessors.cc\n                │     └── lib/set.cc\n                │\n                ├── lib/wrapped_re2.h (shared class definition)\n                ├── lib/wrapped_re2_set.h (RE2.Set class)\n                ├── lib/util.cc / lib/util.h (shared utilities)\n                │\n                └── vendor/ (re2 + abseil-cpp)\n```\n\n## Testing\n\n- **Framework**: tape-six (`tape6`)\n- **Run all**: `npm test` (worker threads via `tape6 --flags FO`)\n- **Run sequential**: `npm run test:seq`\n- **Run multi-process**: `npm run test:proc`\n- **Run single file**: `node tests/test-<name>.mjs`\n- **TypeScript check**: `npm run ts-check`\n- **Lint**: `npm run lint` (Prettier check)\n- **Lint fix**: `npm run lint:fix` (Prettier write)\n- **Verify build**: `npm run verify-build` (quick smoke test)\n\n## Import paths\n\n```js\n// CommonJS (source, scripts)\nconst RE2 = require('re2');\n\n// ESM (tests)\nimport {RE2} from '../re2.js';\n```\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "<!-- Claude Code project instructions — canonical source is AGENTS.md -->\n\nSee [AGENTS.md](./AGENTS.md) for all AI agent rules and project conventions.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to node-re2\n\nThank you for your interest in contributing!\n\n## Getting started\n\nThis project uses git submodules for vendored dependencies (RE2 and Abseil). Clone recursively:\n\n```bash\ngit clone --recursive git@github.com:uhop/node-re2.git\ncd node-re2\nnpm install\n```\n\nSee [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and dependency graph.\n\n## Development workflow\n\n1. Make your changes.\n2. Rebuild the addon: `npm run rebuild`\n3. Lint: `npm run lint:fix`\n4. Test: `npm test`\n5. Type-check: `npm run ts-check`\n\n## Code style\n\n- CommonJS (`require()`/`module.exports`) in JavaScript source, ESM (`import`) in tests (`.mjs`).\n- C++ code uses tabs (4-wide indentation). JavaScript uses 2-space indentation.\n- Formatted with Prettier — see `.prettierrc` for settings.\n- C++ addon API uses nan (Native Abstractions for Node.js).\n- Keep `re2.js` and `re2.d.ts` in sync.\n\n## Important notes\n\n- Never edit files under `vendor/` — they are git submodules.\n- RE2 always operates in Unicode mode — the `u` flag is added implicitly.\n- Buffer support is a first-class feature — all methods must handle both strings and Buffers.\n\n## AI agents\n\nIf you are an AI coding agent, see [AGENTS.md](./AGENTS.md) for detailed project conventions, commands, and architecture.\n"
  },
  {
    "path": "LICENSE",
    "content": "This library is available under the terms of the modified BSD license. No external contributions\nare allowed under licenses which are fundamentally incompatible with the BSD license that this library is distributed under.\n\nThe text of the BSD license is reproduced below.\n\n-------------------------------------------------------------------------------\nThe \"New\" BSD License:\n**********************\n\nCopyright (c) 2005-2026, Eugene Lazutkin\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this\n    list of conditions and the following disclaimer.\n  * Redistributions in binary form must reproduce the above copyright notice,\n    this list of conditions and the following disclaimer in the documentation\n    and/or other materials provided with the distribution.\n  * Neither the name of Eugene Lazutkin nor the names of other contributors\n    may be used to endorse or promote products derived from this software\n    without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# node-re2 [![NPM version][npm-img]][npm-url]\n\n[npm-img]: https://img.shields.io/npm/v/re2.svg\n[npm-url]: https://npmjs.org/package/re2\n\nThis project provides Node.js bindings for [RE2](https://github.com/google/re2):\na fast, safe alternative to backtracking regular expression engines written by [Russ Cox](http://swtch.com/~rsc/) in C++.\nTo learn more about RE2, start with [Regular Expression Matching in the Wild](http://swtch.com/~rsc/regexp/regexp3.html). More resources are on his [Implementing Regular Expressions](http://swtch.com/~rsc/regexp/) page.\n\n`RE2`'s regular expression language is almost a superset of what `RegExp` provides\n(see [Syntax](https://github.com/google/re2/wiki/Syntax)),\nbut it lacks backreferences and lookahead assertions. See below for details.\n\n`RE2` always works in [Unicode mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode) &mdash; character codes are interpreted as Unicode code points, not as binary values of UTF-16.\nSee `RE2.unicodeWarningLevel` below for details.\n\n`RE2` emulates standard `RegExp`, making it a practical drop-in replacement in most cases.\nIt also provides `String`-based regular expression methods. The constructor accepts `RegExp` directly, honoring all properties.\n\nIt can work with [Node.js Buffers](https://nodejs.org/api/buffer.html) directly, reducing overhead and making processing of long files fast.\n\nThe project is a C++ addon built with [nan](https://github.com/nodejs/nan). It cannot be used in web browsers.\nAll documentation is in this README and in the [wiki](https://github.com/uhop/node-re2/wiki).\n\n## Why use node-re2?\n\nThe built-in Node.js regular expression engine can run in exponential time with a special combination:\n - A vulnerable regular expression\n - \"Evil input\"\n\nThis can lead to what is known as a [Regular Expression Denial of Service (ReDoS)](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\nTo check if your regular expressions are vulnerable, try one of these projects:\n - [rxxr2](http://www.cs.bham.ac.uk/~hxt/research/rxxr2/)\n - [safe-regex](https://github.com/substack/safe-regex)\n\nNeither project is perfect.\n\nnode-re2 protects against ReDoS by evaluating patterns in `RE2` instead of the built-in regex engine.\n\nTo run the bundled benchmark (make sure node-re2 is built first):\n\n```bash\nnpx nano-bench bench/bad-pattern.mjs\n```\n\n## Standard features\n\n`RE2` objects are created just like `RegExp`:\n\n* [`new RE2(pattern[, flags])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)\n\nSupported flags: `g` (global), `i` (ignoreCase), `m` (multiline), `s` (dotAll), `u` (unicode, always on), `y` (sticky), `d` (hasIndices).\n\nSupported properties:\n\n* [`re2.lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)\n* [`re2.global`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global)\n* [`re2.ignoreCase`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase)\n* [`re2.multiline`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline)\n* [`re2.dotAll`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll)\n* [`re2.unicode`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode) &mdash; always `true`; see details below.\n* [`re2.sticky`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky)\n* [`re2.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)\n* [`re2.source`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source)\n* [`re2.flags`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags)\n\nSupported methods:\n\n* [`re2.exec(str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec)\n* [`re2.test(str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test)\n* [`re2.toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString)\n\nWell-known symbol-based methods are supported (see [Symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)):\n\n* [`re2[Symbol.match](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match)\n* [`re2[Symbol.matchAll](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/matchAll)\n* [`re2[Symbol.search](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/search)\n* [`re2[Symbol.replace](str, newSubStr|function)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace)\n* [`re2[Symbol.split](str[, limit])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/split)\n\nThis lets you use `RE2` instances on strings directly, just like `RegExp`:\n\n```js\nconst re = new RE2('1');\n'213'.match(re);        // [ '1', index: 1, input: '213' ]\n'213'.search(re);       // 1\n'213'.replace(re, '+'); // 2+3\n'213'.split(re);        // [ '2', '3' ]\n\nArray.from('2131'.matchAll(new RE2('1', 'g'))); // matchAll requires the g flag\n// [['1', index: 1, input: '2131'], ['1', index: 3, input: '2131']]\n```\n\n[Named groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) are supported.\n\n## Extensions\n\n### Shortcut construction\n\n`RE2` can be created from a regular expression:\n\n```js\nconst re1 = new RE2(/ab*/ig); // from a RegExp object\nconst re2 = new RE2(re1);     // from another RE2 object\n```\n\n### `String` methods\n\n`RE2` provides the standard `String` regex methods with swapped receiver and argument:\n\n* `re2.match(str)`\n  * See [`str.match(regexp)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match)\n* `re2.replace(str, newSubStr|function)`\n  * See [`str.replace(regexp, newSubStr|function)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)\n* `re2.search(str)`\n  * See [`str.search(regexp)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search)\n* `re2.split(str[, limit])`\n  * See [`str.split(regexp[, limit])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split)\n\nThese methods are also available as well-known symbol-based methods for transparent use with ES6 string/regex machinery.\n\n### `Buffer` support\n\nMost methods accept Buffers instead of strings for direct UTF-8 processing:\n\n* `re2.exec(buf)`\n* `re2.test(buf)`\n* `re2.match(buf)`\n* `re2.search(buf)`\n* `re2.split(buf[, limit])`\n* `re2.replace(buf, replacer)`\n\nDifferences from string-based versions:\n\n* All buffers are assumed to be encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8)\n  (ASCII is a proper subset of UTF-8).\n* Results are `Buffer` objects, even in composite objects. Convert with\n  [`buf.toString()`](https://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end).\n* All offsets and lengths are in bytes, not characters (each UTF-8 character occupies 1–4 bytes).\n  This lets you slice buffers directly without costly character-to-byte recalculations.\n\nWhen `re2.replace()` is used with a replacer function, the replacer receives string arguments and character offsets by default. Set `useBuffers` to `true` on the function to receive byte offsets instead:\n\n```js\nfunction strReplacer(match, offset, input) {\n\t// typeof match == \"string\"\n\treturn \"<= \" + offset + \" characters|\";\n}\n\nRE2(\"б\").replace(\"абв\", strReplacer);\n// \"а<= 1 characters|в\"\n\nfunction bufReplacer(match, offset, input) {\n\t// typeof match == \"string\"\n\treturn \"<= \" + offset + \" bytes|\";\n}\nbufReplacer.useBuffers = true;\n\nRE2(\"б\").replace(\"абв\", bufReplacer);\n// \"а<= 2 bytes|в\"\n```\n\nThis works for both string and buffer inputs. Buffer input produces buffer output; string input produces string output.\n\n### `RE2.Set`\n\nUse `RE2.Set` when the same string must be tested against many patterns. It builds a single automaton and frequently beats running individual regular expressions one by one.\n\nWhile `test()` can be simulated by combining patterns with `|`, `match()` returns which patterns matched &mdash; something a single regular expression cannot do.\n\n* `new RE2.Set(patterns[, flagsOrOptions][, options])`\n  * `patterns` is any iterable of strings, `Buffer`s, `RegExp`, or `RE2` instances; flags (if provided) apply to the whole set.\n  * `flagsOrOptions` can be a string/`Buffer` with standard flags (`i`, `m`, `s`, `u`, `g`, `y`, `d`).\n  * `options.anchor` can be `'unanchored'` (default), `'start'`, or `'both'`.\n* `set.test(str)` returns `true` if any pattern matches and `false` otherwise.\n* `set.match(str)` returns an array of indexes of matching patterns.\n  * This is an array of integer indices of patterns that matched sorted in ascending order.\n  * If no patterns matched, an empty array is returned.\n* Read-only properties:\n  * `set.size` (number of patterns), `set.flags` (`RegExp` flags as a string), `set.anchor` (anchor mode as a string)\n  * `set.source` (all patterns joined with `|` as a string), `set.sources` (individual pattern sources as an array of strings)\n\nIt is based on [RE2::Set](https://github.com/google/re2/blob/main/re2/set.h).\n\nExample:\n\n```js\nconst routes = new RE2.Set([\n  '^/users/\\\\d+$',\n  '^/posts/\\\\d+$'\n], 'i', {anchor: 'start'});\n\nroutes.test('/users/7');     // true\nroutes.match('/posts/42');   // [1]\nroutes.sources;              // ['^/users/\\\\d+$', '^/posts/\\\\d+$']\nroutes.toString();           // '/^/users/\\\\d+$|^/posts/\\\\d+$/iu'\n```\n\nTo run the bundled benchmark (make sure node-re2 is built first):\n\n```bash\nnpx nano-bench bench/set-match.mjs\n```\n\n### Calculate length\n\nTwo helpers convert between UTF-8 and UTF-16 sizes:\n\n* `RE2.getUtf8Length(str)` &mdash; byte size needed to encode a string as a UTF-8 buffer.\n* `RE2.getUtf16Length(buf)` &mdash; character count needed to decode a UTF-8 buffer as a string.\n\n### Property: `internalSource`\n\n`source` emulates the standard `RegExp` property and can recreate an identical `RE2` or `RegExp` instance. To inspect the RE2-translated pattern (useful for debugging), use the read-only `internalSource` property.\n\n### Unicode warning level\n\n`RE2` always works in Unicode mode. In most cases this is either invisible or preferred. For applications that need tight control, the static property `RE2.unicodeWarningLevel` governs what happens when a non-Unicode regular expression is created.\n\nIf a regular expression lacks the `u` flag, it is added silently by default:\n\n```js\nconst x = /./;\nx.flags; // ''\nconst y = new RE2(x);\ny.flags; // 'u'\n```\n\nValues of `RE2.unicodeWarningLevel`:\n\n* `'nothing'` (default) &mdash; silently add `u`.\n* `'warnOnce'` &mdash; warn once, then silently add `u`. Assigning this value resets the one-time flag.\n* `'warn'` &mdash; warn every time, still add `u`.\n* `'throw'` &mdash; throw `SyntaxError`.\n* Any other value is silently ignored, leaving the previous value unchanged.\n\nWarnings and exceptions help audit an application for stray non-Unicode regular expressions.\n\n`RE2.unicodeWarningLevel` is global. Be careful in multi-threaded environments &mdash; it is shared across threads.\n\n## How to install\n\n```bash\nnpm install re2\n```\n\nThe project works with other package managers but is not tested with them.\nSee the wiki for notes on [yarn](https://github.com/uhop/node-re2/wiki/Using-with-yarn) and [pnpm](https://github.com/uhop/node-re2/wiki/Using-with-pnpm).\n\n### Precompiled artifacts\n\nThe [install script](https://github.com/uhop/install-artifact-from-github/blob/master/bin/install-from-cache.js) attempts to download a prebuilt artifact from GitHub Releases. Override the download location with the `RE2_DOWNLOAD_MIRROR` environment variable.\n\nIf the download fails, the script builds RE2 locally using [node-gyp](https://github.com/nodejs/node-gyp).\n\n## How to use\n\nIt is used just like `RegExp`.\n\n```js\nconst RE2 = require('re2');\n\n// with default flags\nlet re = new RE2('a(b*)');\nlet result = re.exec('abbc');\nconsole.log(result[0]); // 'abb'\nconsole.log(result[1]); // 'bb'\n\nresult = re.exec('aBbC');\nconsole.log(result[0]); // 'a'\nconsole.log(result[1]); // ''\n\n// with explicit flags\nre = new RE2('a(b*)', 'i');\nresult = re.exec('aBbC');\nconsole.log(result[0]); // 'aBb'\nconsole.log(result[1]); // 'Bb'\n\n// from regular expression object\nconst regexp = new RegExp('a(b*)', 'i');\nre = new RE2(regexp);\nresult = re.exec('aBbC');\nconsole.log(result[0]); // 'aBb'\nconsole.log(result[1]); // 'Bb'\n\n// from regular expression literal\nre = new RE2(/a(b*)/i);\nresult = re.exec('aBbC');\nconsole.log(result[0]); // 'aBb'\nconsole.log(result[1]); // 'Bb'\n\n// from another RE2 object\nconst rex = new RE2(re);\nresult = rex.exec('aBbC');\nconsole.log(result[0]); // 'aBb'\nconsole.log(result[1]); // 'Bb'\n\n// shortcut\nresult = new RE2('ab*').exec('abba');\n\n// factory\nresult = RE2('ab*').exec('abba');\n```\n\n## Limitations (things RE2 does not support)\n\n`RE2` avoids any regular expression features that require worst-case exponential time to evaluate.\nThe most notable missing features are backreferences and lookahead assertions.\nIf your application uses them, you should continue to use `RegExp` &mdash;\nbut since they are fundamentally vulnerable to\n[ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS),\nconsider replacing them.\n\n`RE2` throws `SyntaxError` for unsupported features.\nWrap `RE2` declarations in a try-catch to fall back to `RegExp`:\n\n```js\nlet re = /(a)+(b)*/;\ntry {\n  re = new RE2(re);\n  // use RE2 as a drop-in replacement\n} catch (e) {\n  // use the original RegExp\n}\nconst result = re.exec(sample);\n```\n\n`RE2` may also behave differently from the built-in engine in corner cases.\n\n### Backreferences\n\n`RE2` does not support backreferences &mdash; numbered references to previously\nmatched groups (`\\1`, `\\2`, etc.). Example:\n\n```js\n/(cat|dog)\\1/.test(\"catcat\"); // true\n/(cat|dog)\\1/.test(\"dogdog\"); // true\n/(cat|dog)\\1/.test(\"catdog\"); // false\n/(cat|dog)\\1/.test(\"dogcat\"); // false\n```\n\n### Lookahead assertions\n\n`RE2` does not support lookahead assertions, which make a match depend on subsequent contents.\n\n```js\n/abc(?=def)/; // match abc only if it is followed by def\n/abc(?!def)/; // match abc only if it is not followed by def\n```\n\n### Mismatched behavior\n\n`RE2` and the built-in engine may disagree in edge cases. Verify your regular expressions before switching. They should work in the vast majority of cases.\n\nExample:\n\n```js\nconst RE2 = require('re2');\n\nconst pattern = '(?:(a)|(b)|(c))+';\n\nconst built_in = new RegExp(pattern);\nconst re2 = new RE2(pattern);\n\nconst input = 'abc';\n\nconst bi_res = built_in.exec(input);\nconst re2_res = re2.exec(input);\n\nconsole.log('bi_res: ' + bi_res);    // prints: bi_res: abc,,,c\nconsole.log('re2_res : ' + re2_res); // prints: re2_res : abc,a,b,c\n```\n\n### Unicode\n\n`RE2` always works in Unicode mode. See `RE2.unicodeWarningLevel` above for details.\n\n#### Unicode classes `\\p{...}` and `\\P{...}`\n\n`RE2` supports a subset of Unicode classes as defined in [RE2 Syntax](https://github.com/google/re2/wiki/Syntax). Google RE2 natively supports only short names (e.g., `L` for `Letter`). Like `RegExp`, node-re2 also accepts long names by translating them to short names.\n\nOnly the `\\p{name}` form is supported, not `\\p{name=value}` in general.\nThe exception is `Script` and `sc`, e.g., `\\p{Script=Latin}` and `\\p{sc=Cyrillic}`.\nThe same applies to `\\P{...}`.\n\n## Release history\n\n- 1.24.0 *Fixed multi-threaded crash in worker threads (#235). Added named import: `import {RE2} from 're2'`. Added CJS test. Updated docs and dependencies.*\n- 1.23.3 *Updated Abseil and dev dependencies.*\n- 1.23.2 *Updated dev dependencies.*\n- 1.23.1 *Updated Abseil and dev dependencies.*\n- 1.23.0 *Updated all dependencies, upgraded tooling. New feature: `RE2.Set` (thx, [Wes](https://github.com/wrmedford)).*\n- 1.22.3 *Technical release: upgraded QEMU emulations to native ARM runners to speed up the build process.*\n- 1.22.2 *Updated all dependencies and the list of pre-compiled targets: Node 20, 22, 24, 25 (thx, [Jiayu Liu](https://github.com/jimexist)).*\n- 1.22.1 *Added support for translation of scripts as Unicode classes.*\n- 1.22.0 *Added support for translation of Unicode classes (thx, [John Livingston](https://github.com/JohnXLivingston)). Added [attestations](https://github.com/uhop/node-re2/attestations).*\n- 1.21.5 *Updated all dependencies and the list of pre-compiled targets. Fixed minor bugs. C++ style fix (thx, [Benjamin Brienen](https://github.com/BenjaminBrienen)). Added Windows 11 ARM build runner (thx, [Kagami Sascha Rosylight](https://github.com/saschanaz)).*\n- 1.21.4 *Fixed a regression reported by [caroline-matsec](https://github.com/caroline-matsec), thx! Added pre-compilation targets for Alpine Linux on ARM. Updated deps.*\n- 1.21.3 *Fixed an empty string regression reported by [Rhys Arkins](https://github.com/rarkins), thx! Updated deps.*\n- 1.21.2 *Fixed another memory regression reported by [matthewvalentine](https://github.com/matthewvalentine), thx! Updated deps. Added more tests and benchmarks.*\n- 1.21.1 *Fixed a memory regression reported by [matthewvalentine](https://github.com/matthewvalentine), thx! Updated deps.*\n- 1.21.0 *Fixed the performance problem reported by [matthewvalentine](https://github.com/matthewvalentine) (thx!). The change improves performance for multiple use cases.*\n- 1.20.12 *Updated deps. Maintenance chores. Fixes for buffer-related bugs: `exec()` index (reported by [matthewvalentine](https://github.com/matthewvalentine), thx) and `match()` index.*\n- 1.20.11 *Updated deps. Added support for Node 22 (thx, [Elton Leong](https://github.com/eltonkl)).*\n- 1.20.10 *Updated deps. Removed files the pack used for development (thx, [Haruaki OTAKE](https://github.com/aaharu)). Added arm64 Linux prebilds (thx, [Christopher M](https://github.com/cmanou)). Fixed non-`npm` `corepack` problem (thx, [Steven](https://github.com/styfle)).*\n- 1.20.9 *Updated deps. Added more `absail-cpp` files that manifested itself on NixOS. Thx, [Laura Hausmann](https://github.com/zotanmew).*\n- 1.20.8 *Updated deps: `install-artifact-from-github`. A default HTTPS agent is used for fetching precompiled artifacts avoiding unnecessary long wait times.*\n- 1.20.7 *Added more `absail-cpp` files that manifested itself on ARM Alpine. Thx, [Laura Hausmann](https://github.com/zotanmew).*\n- 1.20.6 *Updated deps, notably `node-gyp`.*\n- 1.20.5 *Updated deps, added Node 21 and retired Node 16 as pre-compilation targets.*\n- 1.20.4 *Updated deps. Fix: the 2nd argument of the constructor overrides flags. Thx, [gost-serb](https://github.com/gost-serb).*\n- 1.20.3 *Fix: subsequent numbers are incorporated into group if they would form a legal group reference. Thx, [Oleksii Vasyliev](https://github.com/le0pard).*\n- 1.20.2 *Fix: added a missing C++ file, which caused a bug on Alpine Linux. Thx, [rbitanga-manticore](https://github.com/rbitanga-manticore).*\n- 1.20.1 *Fix: files included in the npm package to build the C++ code.*\n- 1.20.0 *Updated RE2. New version uses `abseil-cpp` and required the adaptation work. Thx, [Stefano Rivera](https://github.com/stefanor).*\n\nThe rest can be consulted in the project's wiki [Release history](https://github.com/uhop/node-re2/wiki/Release-history).\n\n## License\n\nBSD-3-Clause\n"
  },
  {
    "path": "bench/bad-pattern.mjs",
    "content": "import {RE2} from '../re2.js';\n\nconst BAD_PATTERN = '([a-z]+)+$';\nconst BAD_INPUT = 'a'.repeat(10) + '!';\n\nconst regExp = new RegExp(BAD_PATTERN);\nconst re2 = new RE2(BAD_PATTERN);\n\nexport default {\n  RegExp: n => {\n    let count = 0;\n    for (let i = 0; i < n; ++i) {\n      if (regExp.test(BAD_INPUT)) ++count;\n    }\n    return count;\n  },\n  RE2: n => {\n    let count = 0;\n    for (let i = 0; i < n; ++i) {\n      if (re2.test(BAD_INPUT)) ++count;\n    }\n    return count;\n  }\n};\n"
  },
  {
    "path": "bench/set-match.mjs",
    "content": "import {RE2} from '../re2.js';\n\nconst PATTERN_COUNT = 200;\n\nconst patterns = [];\nfor (let i = 0; i < PATTERN_COUNT; ++i) {\n  patterns.push('token' + i + '(?:[a-z]+)?');\n}\n\nconst INPUT_COUNT = 500;\n\nconst inputs = [];\nfor (let j = 0; j < INPUT_COUNT; ++j) {\n  inputs.push(\n    'xx' +\n      (j % PATTERN_COUNT) +\n      ' ' +\n      (j & 7) +\n      ' token' +\n      (j % PATTERN_COUNT) +\n      ' tail'\n  );\n}\n\nconst re2Set = new RE2.Set(patterns);\nconst re2List = patterns.map(p => new RE2(p));\nconst jsList = patterns.map(p => new RegExp(p));\n\nexport default {\n  RegExp: n => {\n    let count = 0;\n    for (let i = 0; i < n; ++i) {\n      for (const input of inputs) {\n        const matches = [];\n        for (const pattern of jsList) {\n          if (pattern.test(input)) matches.push(pattern);\n        }\n        count += matches.length;\n      }\n    }\n    return count;\n  },\n  RE2: n => {\n    let count = 0;\n    for (let i = 0; i < n; ++i) {\n      for (const input of inputs) {\n        const matches = [];\n        for (const pattern of re2List) {\n          if (pattern.test(input)) matches.push(pattern);\n        }\n        count += matches.length;\n      }\n    }\n    return count;\n  },\n  'RE2.Set': n => {\n    let count = 0;\n    for (let i = 0; i < n; ++i) {\n      for (const input of inputs) {\n        const matches = re2Set.match(input);\n        count += matches.length;\n      }\n    }\n    return count;\n  }\n};\n"
  },
  {
    "path": "binding.gyp",
    "content": "{\n  \"targets\": [\n    {\n      \"target_name\": \"re2\",\n      \"sources\": [\n        \"lib/addon.cc\",\n        \"lib/accessors.cc\",\n        \"lib/pattern.cc\",\n        \"lib/util.cc\",\n        \"lib/new.cc\",\n        \"lib/exec.cc\",\n        \"lib/test.cc\",\n        \"lib/match.cc\",\n        \"lib/replace.cc\",\n        \"lib/search.cc\",\n        \"lib/split.cc\",\n        \"lib/to_string.cc\",\n        \"lib/set.cc\",\n        \"vendor/re2/re2/bitmap256.cc\",\n        \"vendor/re2/re2/bitstate.cc\",\n        \"vendor/re2/re2/compile.cc\",\n        \"vendor/re2/re2/dfa.cc\",\n        \"vendor/re2/re2/filtered_re2.cc\",\n        \"vendor/re2/re2/mimics_pcre.cc\",\n        \"vendor/re2/re2/nfa.cc\",\n        \"vendor/re2/re2/onepass.cc\",\n        \"vendor/re2/re2/parse.cc\",\n        \"vendor/re2/re2/perl_groups.cc\",\n        \"vendor/re2/re2/prefilter.cc\",\n        \"vendor/re2/re2/prefilter_tree.cc\",\n        \"vendor/re2/re2/prog.cc\",\n        \"vendor/re2/re2/re2.cc\",\n        \"vendor/re2/re2/regexp.cc\",\n        \"vendor/re2/re2/set.cc\",\n        \"vendor/re2/re2/simplify.cc\",\n        \"vendor/re2/re2/tostring.cc\",\n        \"vendor/re2/re2/unicode_casefold.cc\",\n        \"vendor/re2/re2/unicode_groups.cc\",\n        \"vendor/re2/util/pcre.cc\",\n        \"vendor/re2/util/rune.cc\",\n        \"vendor/re2/util/strutil.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/cycleclock.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/low_level_alloc.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/raw_logging.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/spinlock.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/spinlock_wait.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/strerror.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/sysinfo.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/thread_identity.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/throw_delegate.cc\",\n        \"vendor/abseil-cpp/absl/base/internal/unscaledcycleclock.cc\",\n        \"vendor/abseil-cpp/absl/container/internal/hashtablez_sampler.cc\",\n        \"vendor/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc\",\n        \"vendor/abseil-cpp/absl/container/internal/raw_hash_set.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/borrowed_fixup_buffer.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/decode_rust_punycode.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/demangle.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/demangle_rust.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/address_is_readable.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/elf_mem_image.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/examine_stack.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/utf8_for_code_point.cc\",\n        \"vendor/abseil-cpp/absl/debugging/internal/vdso_support.cc\",\n        \"vendor/abseil-cpp/absl/debugging/stacktrace.cc\",\n        \"vendor/abseil-cpp/absl/debugging/symbolize.cc\",\n        \"vendor/abseil-cpp/absl/flags/commandlineflag.cc\",\n        \"vendor/abseil-cpp/absl/flags/internal/commandlineflag.cc\",\n        \"vendor/abseil-cpp/absl/flags/internal/flag.cc\",\n        \"vendor/abseil-cpp/absl/flags/internal/private_handle_accessor.cc\",\n        \"vendor/abseil-cpp/absl/flags/internal/program_name.cc\",\n        \"vendor/abseil-cpp/absl/flags/marshalling.cc\",\n        \"vendor/abseil-cpp/absl/flags/reflection.cc\",\n        \"vendor/abseil-cpp/absl/flags/usage_config.cc\",\n        \"vendor/abseil-cpp/absl/hash/internal/city.cc\",\n        \"vendor/abseil-cpp/absl/hash/internal/hash.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/globals.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/log_format.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/log_message.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/log_sink_set.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/nullguard.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/proto.cc\",\n        \"vendor/abseil-cpp/absl/log/internal/structured_proto.cc\",\n        \"vendor/abseil-cpp/absl/log/globals.cc\",\n        \"vendor/abseil-cpp/absl/log/log_sink.cc\",\n        \"vendor/abseil-cpp/absl/numeric/int128.cc\",\n        \"vendor/abseil-cpp/absl/strings/ascii.cc\",\n        \"vendor/abseil-cpp/absl/strings/charconv.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/charconv_bigint.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/charconv_parse.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/memutil.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/arg.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/bind.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/extension.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/output.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/str_format/parser.cc\",\n        \"vendor/abseil-cpp/absl/strings/internal/utf8.cc\",\n        \"vendor/abseil-cpp/absl/strings/match.cc\",\n        \"vendor/abseil-cpp/absl/strings/numbers.cc\",\n        \"vendor/abseil-cpp/absl/strings/str_cat.cc\",\n        \"vendor/abseil-cpp/absl/strings/str_split.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/graphcycles.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/futex_waiter.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/kernel_timeout.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/internal/waiter_base.cc\",\n        \"vendor/abseil-cpp/absl/synchronization/mutex.cc\",\n        \"vendor/abseil-cpp/absl/time/clock.cc\",\n        \"vendor/abseil-cpp/absl/time/duration.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc\",\n        \"vendor/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc\",\n        \"vendor/abseil-cpp/absl/time/time.cc\",\n      ],\n      \"cflags\": [\n        \"-std=c++2a\",\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wno-sign-compare\",\n        \"-Wno-unused-parameter\",\n        \"-Wno-missing-field-initializers\",\n        \"-Wno-cast-function-type\",\n        \"-O3\",\n        \"-g\"\n      ],\n      \"defines\": [\n        \"NDEBUG\",\n        \"NOMINMAX\"\n      ],\n      \"include_dirs\": [\n        \"<!(node -e \\\"require('nan')\\\")\",\n        \"vendor/re2\",\n        \"vendor/abseil-cpp\",\n      ],\n      \"xcode_settings\": {\n        \"MACOSX_DEPLOYMENT_TARGET\": \"10.15\",\n        \"CLANG_CXX_LANGUAGE_STANDARD\": \"c++2a\",\n        \"CLANG_CXX_LIBRARY\": \"libc++\",\n        \"OTHER_CFLAGS\": [\n          \"-std=c++2a\",\n          \"-Wall\",\n          \"-Wextra\",\n          \"-Wno-sign-compare\",\n          \"-Wno-unused-parameter\",\n          \"-Wno-missing-field-initializers\",\n          \"-O3\",\n          \"-g\"\n        ]\n      },\n      \"conditions\": [\n        [\"OS==\\\"linux\\\"\", {\n          \"cflags\": [\n            \"-pthread\"\n          ],\n          \"ldflags\": [\n            \"-pthread\"\n          ]\n        }],\n        [\"OS==\\\"win\\\"\", {\n          \"sources\": [\n            \"vendor/abseil-cpp/absl/synchronization/internal/win32_waiter.cc\",\n            \"vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_name_win.cc\"\n          ]\n        }]\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "lib/accessors.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <cstring>\n#include <string>\n#include <vector>\n\nNAN_GETTER(WrappedRE2::GetSource)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"(?:)\").ToLocalChecked());\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(Nan::New(re2->source).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2::GetInternalSource)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"(?:)\").ToLocalChecked());\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(Nan::New(re2->regexp.pattern()).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2::GetFlags)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"\").ToLocalChecked());\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\n\tstd::string flags;\n\tif (re2->hasIndices)\n\t{\n\t\tflags += \"d\";\n\t}\n\tif (re2->global)\n\t{\n\t\tflags += \"g\";\n\t}\n\tif (re2->ignoreCase)\n\t{\n\t\tflags += \"i\";\n\t}\n\tif (re2->multiline)\n\t{\n\t\tflags += \"m\";\n\t}\n\tif (re2->dotAll)\n\t{\n\t\tflags += \"s\";\n\t}\n\tflags += \"u\";\n\tif (re2->sticky)\n\t{\n\t\tflags += \"y\";\n\t}\n\n\tinfo.GetReturnValue().Set(Nan::New(flags).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2::GetGlobal)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->global);\n}\n\nNAN_GETTER(WrappedRE2::GetIgnoreCase)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->ignoreCase);\n}\n\nNAN_GETTER(WrappedRE2::GetMultiline)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->multiline);\n}\n\nNAN_GETTER(WrappedRE2::GetDotAll)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->dotAll);\n}\n\nNAN_GETTER(WrappedRE2::GetUnicode)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tinfo.GetReturnValue().Set(true);\n}\n\nNAN_GETTER(WrappedRE2::GetSticky)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->sticky);\n}\n\nNAN_GETTER(WrappedRE2::GetHasIndices)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(re2->hasIndices);\n}\n\nNAN_GETTER(WrappedRE2::GetLastIndex)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\tinfo.GetReturnValue().SetUndefined();\n\t\treturn;\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tinfo.GetReturnValue().Set(static_cast<int>(re2->lastIndex));\n}\n\nNAN_SETTER(WrappedRE2::SetLastIndex)\n{\n\tif (!WrappedRE2::HasInstance(info.This()))\n\t{\n\t\treturn Nan::ThrowTypeError(\"Cannot set lastIndex of an invalid RE2 object.\");\n\t}\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (value->IsNumber())\n\t{\n\t\tint n = value->NumberValue(Nan::GetCurrentContext()).FromMaybe(0);\n\t\tre2->lastIndex = n <= 0 ? 0 : n;\n\t}\n}\n\nstd::atomic<WrappedRE2::UnicodeWarningLevels> WrappedRE2::unicodeWarningLevel{WrappedRE2::NOTHING};\n\nNAN_GETTER(WrappedRE2::GetUnicodeWarningLevel)\n{\n\tstd::string level;\n\tswitch (unicodeWarningLevel)\n\t{\n\tcase THROW:\n\t\tlevel = \"throw\";\n\t\tbreak;\n\tcase WARN:\n\t\tlevel = \"warn\";\n\t\tbreak;\n\tcase WARN_ONCE:\n\t\tlevel = \"warnOnce\";\n\t\tbreak;\n\tdefault:\n\t\tlevel = \"nothing\";\n\t\tbreak;\n\t}\n\tinfo.GetReturnValue().Set(Nan::New(level).ToLocalChecked());\n}\n\nNAN_SETTER(WrappedRE2::SetUnicodeWarningLevel)\n{\n\tif (value->IsString())\n\t{\n\t\tNan::Utf8String s(value);\n\t\tif (!strcmp(*s, \"throw\"))\n\t\t{\n\t\t\tunicodeWarningLevel = THROW;\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp(*s, \"warn\"))\n\t\t{\n\t\t\tunicodeWarningLevel = WARN;\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp(*s, \"warnOnce\"))\n\t\t{\n\t\t\tunicodeWarningLevel = WARN_ONCE;\n\t\t\talreadyWarnedAboutUnicode = false;\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp(*s, \"nothing\"))\n\t\t{\n\t\t\tunicodeWarningLevel = NOTHING;\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/addon.cc",
    "content": "#include \"./wrapped_re2.h\"\n#include \"./wrapped_re2_set.h\"\n#include \"./isolate_data.h\"\n\n#include <mutex>\n#include <unordered_map>\n\nstatic std::mutex addonDataMutex;\nstatic std::unordered_map<v8::Isolate *, AddonData *> addonDataMap;\n\nAddonData *getAddonData(v8::Isolate *isolate)\n{\n\tstd::lock_guard<std::mutex> lock(addonDataMutex);\n\tauto it = addonDataMap.find(isolate);\n\treturn it != addonDataMap.end() ? it->second : nullptr;\n}\n\nvoid setAddonData(v8::Isolate *isolate, AddonData *data)\n{\n\tstd::lock_guard<std::mutex> lock(addonDataMutex);\n\taddonDataMap[isolate] = data;\n}\n\nvoid deleteAddonData(v8::Isolate *isolate)\n{\n\tstd::lock_guard<std::mutex> lock(addonDataMutex);\n\tauto it = addonDataMap.find(isolate);\n\tif (it != addonDataMap.end())\n\t{\n\t\tdelete it->second;\n\t\taddonDataMap.erase(it);\n\t}\n}\n\nstatic NAN_METHOD(GetUtf8Length)\n{\n\tauto t = info[0]->ToString(Nan::GetCurrentContext());\n\tif (t.IsEmpty())\n\t{\n\t\treturn;\n\t}\n\tauto s = t.ToLocalChecked();\n\tinfo.GetReturnValue().Set(static_cast<int>(s->Utf8Length(v8::Isolate::GetCurrent())));\n}\n\nstatic NAN_METHOD(GetUtf16Length)\n{\n\tif (node::Buffer::HasInstance(info[0]))\n\t{\n\t\tconst auto *s = node::Buffer::Data(info[0]);\n\t\tinfo.GetReturnValue().Set(static_cast<int>(getUtf16Length(s, s + node::Buffer::Length(info[0]))));\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(-1);\n}\n\nstatic void cleanup(void *p)\n{\n\tv8::Isolate *isolate = static_cast<v8::Isolate *>(p);\n\tdeleteAddonData(isolate);\n}\n\n// NAN_MODULE_INIT(WrappedRE2::Init)\nv8::Local<v8::Function> WrappedRE2::Init()\n{\n\tNan::EscapableHandleScope scope;\n\n\t// prepare constructor template\n\n\tauto tpl = Nan::New<v8::FunctionTemplate>(New);\n\ttpl->SetClassName(Nan::New(\"RE2\").ToLocalChecked());\n\tauto instanceTemplate = tpl->InstanceTemplate();\n\tinstanceTemplate->SetInternalFieldCount(1);\n\n\t// save the template in per-isolate storage\n\tauto isolate = v8::Isolate::GetCurrent();\n\tauto data = new AddonData();\n\tdata->re2Tpl.Reset(tpl);\n\tsetAddonData(isolate, data);\n\tnode::AddEnvironmentCleanupHook(isolate, cleanup, isolate);\n\n\t// prototype\n\n\tNan::SetPrototypeMethod(tpl, \"toString\", ToString);\n\n\tNan::SetPrototypeMethod(tpl, \"exec\", Exec);\n\tNan::SetPrototypeMethod(tpl, \"test\", Test);\n\n\tNan::SetPrototypeMethod(tpl, \"match\", Match);\n\tNan::SetPrototypeMethod(tpl, \"replace\", Replace);\n\tNan::SetPrototypeMethod(tpl, \"search\", Search);\n\tNan::SetPrototypeMethod(tpl, \"split\", Split);\n\n\tNan::SetPrototypeTemplate(tpl, \"source\", Nan::New(\"(?:)\").ToLocalChecked());\n\tNan::SetPrototypeTemplate(tpl, \"flags\", Nan::New(\"\").ToLocalChecked());\n\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"source\").ToLocalChecked(), GetSource);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"flags\").ToLocalChecked(), GetFlags);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"global\").ToLocalChecked(), GetGlobal);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"ignoreCase\").ToLocalChecked(), GetIgnoreCase);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"multiline\").ToLocalChecked(), GetMultiline);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"dotAll\").ToLocalChecked(), GetDotAll);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"unicode\").ToLocalChecked(), GetUnicode);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"sticky\").ToLocalChecked(), GetSticky);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"hasIndices\").ToLocalChecked(), GetHasIndices);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"lastIndex\").ToLocalChecked(), GetLastIndex, SetLastIndex);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"internalSource\").ToLocalChecked(), GetInternalSource);\n\n\tauto ctr = Nan::GetFunction(tpl).ToLocalChecked();\n\tauto setCtr = WrappedRE2Set::Init();\n\n\tNan::Set(ctr, Nan::New(\"Set\").ToLocalChecked(), setCtr);\n\n\t// properties\n\n\tNan::Export(ctr, \"getUtf8Length\", GetUtf8Length);\n\tNan::Export(ctr, \"getUtf16Length\", GetUtf16Length);\n\tNan::SetAccessor(v8::Local<v8::Object>(ctr), Nan::New(\"unicodeWarningLevel\").ToLocalChecked(), GetUnicodeWarningLevel, SetUnicodeWarningLevel);\n\n\treturn scope.Escape(ctr);\n}\n\nNODE_MODULE_INIT()\n{\n\tNan::HandleScope scope;\n\tNan::Set(module->ToObject(context).ToLocalChecked(), Nan::New(\"exports\").ToLocalChecked(), WrappedRE2::Init());\n}\n\nWrappedRE2::~WrappedRE2()\n{\n\tdropCache();\n}\n\n// private methods\n\nvoid WrappedRE2::dropCache()\n{\n\tif (!lastString.IsEmpty())\n\t{\n\t\t// lastString.ClearWeak();\n\t\tlastString.Reset();\n\t}\n\tif (!lastCache.IsEmpty())\n\t{\n\t\t// lastCache.ClearWeak();\n\t\tlastCache.Reset();\n\t}\n\tlastStringValue.clear();\n}\n\nconst StrVal &WrappedRE2::prepareArgument(const v8::Local<v8::Value> &arg, bool ignoreLastIndex)\n{\n\tsize_t startFrom = ignoreLastIndex ? 0 : lastIndex;\n\n\tif (!lastString.IsEmpty())\n\t{\n\t\tlastString.ClearWeak();\n\t}\n\tif (!lastCache.IsEmpty())\n\t{\n\t\tlastCache.ClearWeak();\n\t}\n\n\tif (lastString == arg && !node::Buffer::HasInstance(arg) && !lastCache.IsEmpty())\n\t{\n\t\t// we have a properly cached string\n\t\tlastStringValue.setIndex(startFrom);\n\t\treturn lastStringValue;\n\t}\n\n\tdropCache();\n\n\tif (node::Buffer::HasInstance(arg))\n\t{\n\t\t// no need to cache buffers\n\n\t\tlastString.Reset(arg);\n\n\t\tauto argSize = node::Buffer::Length(arg);\n\t\tlastStringValue.reset(arg, argSize, argSize, startFrom, true);\n\n\t\treturn lastStringValue;\n\t}\n\n\t// caching the string\n\n\tauto t = arg->ToString(Nan::GetCurrentContext());\n\tif (t.IsEmpty())\n\t{\n\t\t// do not process bad strings\n\t\tlastStringValue.isBad = true;\n\t\treturn lastStringValue;\n\t}\n\n\tlastString.Reset(arg);\n\n\tauto isolate = v8::Isolate::GetCurrent();\n\n\tauto s = t.ToLocalChecked();\n\tauto argLength = s->Utf8Length(isolate);\n\n\tauto buffer = node::Buffer::New(isolate, s).ToLocalChecked();\n\tlastCache.Reset(buffer);\n\n\tauto argSize = node::Buffer::Length(buffer);\n\tlastStringValue.reset(buffer, argSize, argLength, startFrom);\n\n\treturn lastStringValue;\n};\n\nvoid WrappedRE2::doneWithLastString()\n{\n\tif (!lastString.IsEmpty())\n\t{\n\t\tstatic_cast<v8::PersistentBase<v8::Value> &>(lastString).SetWeak();\n\t}\n\n\tif (!lastCache.IsEmpty())\n\t{\n\t\tstatic_cast<v8::PersistentBase<v8::Object> &>(lastCache).SetWeak();\n\t}\n}\n\n// StrVal\n\nvoid StrVal::setIndex(size_t newIndex)\n{\n\tisValidIndex = newIndex <= length;\n\tif (!isValidIndex)\n\t{\n\t\tindex = newIndex;\n\t\tbyteIndex = 0;\n\t\treturn;\n\t}\n\n\tif (newIndex == index)\n\t\treturn;\n\n\tif (isBuffer)\n\t{\n\t\tbyteIndex = index = newIndex;\n\t\treturn;\n\t}\n\n\t// String\n\n\tif (!newIndex)\n\t{\n\t\tbyteIndex = index = 0;\n\t\treturn;\n\t}\n\n\tif (newIndex == length)\n\t{\n\t\tbyteIndex = size;\n\t\tindex = length;\n\t\treturn;\n\t}\n\n\tbyteIndex = index < newIndex ? getUtf16PositionByCounter(data, byteIndex, newIndex - index) : getUtf16PositionByCounter(data, 0, newIndex);\n\tindex = newIndex;\n}\n\nstatic char null_buffer[] = {'\\0'};\n\nvoid StrVal::reset(const v8::Local<v8::Value> &arg, size_t argSize, size_t argLength, size_t newIndex, bool buffer)\n{\n\tclear();\n\tisBuffer = buffer;\n\tsize = argSize;\n\tlength = argLength;\n\tdata = size ? node::Buffer::Data(arg) : null_buffer;\n\tsetIndex(newIndex);\n}\n"
  },
  {
    "path": "lib/exec.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <vector>\n\nNAN_METHOD(WrappedRE2::Exec)\n{\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().SetNull();\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& str = prep;\n\tif (str.isBad) return; // throws an exception\n\n\tif (re2->global || re2->sticky)\n\t{\n\t\tif (!str.isValidIndex)\n\t\t{\n\t\t\tre2->lastIndex = 0;\n\t\t\tinfo.GetReturnValue().SetNull();\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// actual work\n\n\tstd::vector<re2::StringPiece> groups(re2->regexp.NumberOfCapturingGroups() + 1);\n\n\tif (!re2->regexp.Match(str, str.byteIndex, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &groups[0], groups.size()))\n\t{\n\t\tif (re2->global || re2->sticky)\n\t\t{\n\t\t\tre2->lastIndex = 0;\n\t\t}\n\t\tinfo.GetReturnValue().SetNull();\n\t\treturn;\n\t}\n\n\t// form a result\n\n\tauto result = Nan::New<v8::Array>(), indices = Nan::New<v8::Array>();\n\tint indexOffset = re2->global || re2->sticky ? re2->lastIndex : 0;\n\n\tif (str.isBuffer)\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked());\n\t\t\t\tif (re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tauto pair = Nan::New<v8::Array>();\n\t\t\t\t\tauto offset = data - str.data - str.byteIndex;\n\t\t\t\t\tauto length = item.size();\n\t\t\t\t\tNan::Set(pair, 0, Nan::New<v8::Integer>(indexOffset + static_cast<int>(offset)));\n\t\t\t\t\tNan::Set(pair, 1, Nan::New<v8::Integer>(indexOffset + static_cast<int>(offset + length)));\n\t\t\t\t\tNan::Set(indices, i, pair);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t\tif (re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tNan::Set(indices, i, Nan::Undefined());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tNan::Set(result, Nan::New(\"index\").ToLocalChecked(), Nan::New<v8::Integer>(indexOffset + static_cast<int>(groups[0].data() - str.data - str.byteIndex)));\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked());\n\t\t\t\tif (re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tauto pair = Nan::New<v8::Array>();\n\t\t\t\t\tauto offset = getUtf16Length(str.data + str.byteIndex, data);\n\t\t\t\t\tauto length = getUtf16Length(data, data + item.size());\n\t\t\t\t\tNan::Set(pair, 0, Nan::New<v8::Integer>(indexOffset + static_cast<int>(offset)));\n\t\t\t\t\tNan::Set(pair, 1, Nan::New<v8::Integer>(indexOffset + static_cast<int>(offset + length)));\n\t\t\t\t\tNan::Set(indices, i, pair);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t\tif (re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tNan::Set(indices, i, Nan::Undefined());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tNan::Set(\n\t\t\tresult,\n\t\t\tNan::New(\"index\").ToLocalChecked(),\n\t\t\tNan::New<v8::Integer>(indexOffset +\n\t\t\t\t\t\t\t\t  static_cast<int>(getUtf16Length(str.data + str.byteIndex, groups[0].data()))));\n\t}\n\n\tif (re2->global || re2->sticky)\n\t{\n\t\tre2->lastIndex +=\n\t\t\tstr.isBuffer ? groups[0].data() - str.data + groups[0].size() - str.byteIndex : getUtf16Length(str.data + str.byteIndex, groups[0].data() + groups[0].size());\n\t}\n\n\tNan::Set(result, Nan::New(\"input\").ToLocalChecked(), info[0]);\n\n\tconst auto &groupNames = re2->regexp.CapturingGroupNames();\n\tif (!groupNames.empty())\n\t{\n\t\tauto groups = Nan::New<v8::Object>();\n\t\tNan::SetPrototype(groups, Nan::Null());\n\n\t\tfor (auto group : groupNames)\n\t\t{\n\t\t\tauto value = Nan::Get(result, group.first);\n\t\t\tif (!value.IsEmpty())\n\t\t\t{\n\t\t\t\tNan::Set(groups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());\n\t\t\t}\n\t\t}\n\n\t\tNan::Set(result, Nan::New(\"groups\").ToLocalChecked(), groups);\n\n\t\tif (re2->hasIndices)\n\t\t{\n\t\t\tauto indexGroups = Nan::New<v8::Object>();\n\t\t\tNan::SetPrototype(indexGroups, Nan::Null());\n\n\t\t\tfor (auto group : groupNames)\n\t\t\t{\n\t\t\t\tauto value = Nan::Get(indices, group.first);\n\t\t\t\tif (!value.IsEmpty())\n\t\t\t\t{\n\t\t\t\t\tNan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNan::Set(indices, Nan::New(\"groups\").ToLocalChecked(), indexGroups);\n\t\t}\n\t}\n\telse\n\t{\n\t\tNan::Set(result, Nan::New(\"groups\").ToLocalChecked(), Nan::Undefined());\n\t\tif (re2->hasIndices)\n\t\t{\n\t\t\tNan::Set(indices, Nan::New(\"groups\").ToLocalChecked(), Nan::Undefined());\n\t\t}\n\t}\n\n\tif (re2->hasIndices)\n\t{\n\t\tNan::Set(result, Nan::New(\"indices\").ToLocalChecked(), indices);\n\t}\n\n\tinfo.GetReturnValue().Set(result);\n}\n"
  },
  {
    "path": "lib/isolate_data.h",
    "content": "#pragma once\n\n#include <nan.h>\n\nstruct AddonData {\n\tNan::Persistent<v8::FunctionTemplate> re2Tpl;\n\tNan::Persistent<v8::FunctionTemplate> re2SetTpl;\n};\n\nAddonData *getAddonData(v8::Isolate *isolate);\nvoid setAddonData(v8::Isolate *isolate, AddonData *data);\nvoid deleteAddonData(v8::Isolate *isolate);\n"
  },
  {
    "path": "lib/match.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <vector>\n\nNAN_METHOD(WrappedRE2::Match)\n{\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().SetNull();\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& str = prep;\n\tif (str.isBad) return; // throws an exception\n\n\tif (!str.isValidIndex)\n\t{\n\t\tre2->lastIndex = 0;\n\t\tinfo.GetReturnValue().SetNull();\n\t\treturn;\n\t}\n\n\tstd::vector<re2::StringPiece> groups;\n\tsize_t byteIndex = 0;\n\tauto anchor = re2::RE2::UNANCHORED;\n\n\t// actual work\n\n\tif (re2->global)\n\t{\n\t\t// global: collect all matches\n\n\t\tre2::StringPiece match;\n\n\t\tif (re2->sticky)\n\t\t{\n\t\t\tanchor = re2::RE2::ANCHOR_START;\n\t\t}\n\n\t\twhile (re2->regexp.Match(str, byteIndex, str.size, anchor, &match, 1))\n\t\t{\n\t\t\tgroups.push_back(match);\n\t\t\tbyteIndex = match.data() - str.data + match.size();\n\t\t}\n\n\t\tif (groups.empty())\n\t\t{\n\t\t\tinfo.GetReturnValue().SetNull();\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// non-global: just like exec()\n\n\t\tif (re2->sticky)\n\t\t{\n\t\t\tbyteIndex = str.byteIndex;\n\t\t\tanchor = RE2::ANCHOR_START;\n\t\t}\n\n\t\tgroups.resize(re2->regexp.NumberOfCapturingGroups() + 1);\n\t\tif (!re2->regexp.Match(str, byteIndex, str.size, anchor, &groups[0], groups.size()))\n\t\t{\n\t\t\tif (re2->sticky)\n\t\t\t\tre2->lastIndex = 0;\n\t\t\tinfo.GetReturnValue().SetNull();\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// form a result\n\n\tauto result = Nan::New<v8::Array>(), indices = Nan::New<v8::Array>();\n\n\tif (str.isBuffer)\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked());\n\t\t\t\tif (!re2->global && re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tauto pair = Nan::New<v8::Array>();\n\t\t\t\t\tauto offset = data - str.data - byteIndex;\n\t\t\t\t\tauto length = item.size();\n\t\t\t\t\tNan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));\n\t\t\t\t\tNan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + length)));\n\t\t\t\t\tNan::Set(indices, i, pair);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t\tif (!re2->global && re2->hasIndices)\n\t\t\t\t\tNan::Set(indices, i, Nan::Undefined());\n\t\t\t}\n\t\t}\n\t\tif (!re2->global)\n\t\t{\n\t\t\tNan::Set(result, Nan::New(\"index\").ToLocalChecked(), Nan::New<v8::Integer>(static_cast<int>(groups[0].data() - str.data)));\n\t\t\tNan::Set(result, Nan::New(\"input\").ToLocalChecked(), info[0]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked());\n\t\t\t\tif (!re2->global && re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tauto pair = Nan::New<v8::Array>();\n\t\t\t\t\tauto offset = getUtf16Length(str.data + byteIndex, data);\n\t\t\t\t\tauto length = getUtf16Length(data, data + item.size());\n\t\t\t\t\tNan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));\n\t\t\t\t\tNan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + length)));\n\t\t\t\t\tNan::Set(indices, i, pair);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t\tif (!re2->global && re2->hasIndices)\n\t\t\t\t{\n\t\t\t\t\tNan::Set(indices, i, Nan::Undefined());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!re2->global)\n\t\t{\n\t\t\tNan::Set(result, Nan::New(\"index\").ToLocalChecked(), Nan::New<v8::Integer>(static_cast<int>(getUtf16Length(str.data, groups[0].data()))));\n\t\t\tNan::Set(result, Nan::New(\"input\").ToLocalChecked(), info[0]);\n\t\t}\n\t}\n\n\tif (re2->global)\n\t{\n\t\tre2->lastIndex = 0;\n\t}\n\telse if (re2->sticky)\n\t{\n\t\tre2->lastIndex +=\n\t\t\tstr.isBuffer ? groups[0].data() - str.data + groups[0].size() - byteIndex : getUtf16Length(str.data + byteIndex, groups[0].data() + groups[0].size());\n\t}\n\n\tif (!re2->global)\n\t{\n\t\tconst auto &groupNames = re2->regexp.CapturingGroupNames();\n\t\tif (!groupNames.empty())\n\t\t{\n\t\t\tauto groups = Nan::New<v8::Object>();\n\t\t\tNan::SetPrototype(groups, Nan::Null());\n\n\t\t\tfor (auto group : groupNames)\n\t\t\t{\n\t\t\t\tauto value = Nan::Get(result, group.first);\n\t\t\t\tif (!value.IsEmpty())\n\t\t\t\t{\n\t\t\t\t\tNan::Set(groups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNan::Set(result, Nan::New(\"groups\").ToLocalChecked(), groups);\n\n\t\t\tif (re2->hasIndices)\n\t\t\t{\n\t\t\t\tauto indexGroups = Nan::New<v8::Object>();\n\t\t\t\tNan::SetPrototype(indexGroups, Nan::Null());\n\n\t\t\t\tfor (auto group : groupNames)\n\t\t\t\t{\n\t\t\t\t\tauto value = Nan::Get(indices, group.first);\n\t\t\t\t\tif (!value.IsEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tNan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tNan::Set(indices, Nan::New(\"groups\").ToLocalChecked(), indexGroups);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNan::Set(result, Nan::New(\"groups\").ToLocalChecked(), Nan::Undefined());\n\t\t\tif (re2->hasIndices)\n\t\t\t{\n\t\t\t\tNan::Set(indices, Nan::New(\"groups\").ToLocalChecked(), Nan::Undefined());\n\t\t\t}\n\t\t}\n\t\tif (re2->hasIndices)\n\t\t{\n\t\t\tNan::Set(result, Nan::New(\"indices\").ToLocalChecked(), indices);\n\t\t}\n\t}\n\n\tinfo.GetReturnValue().Set(result);\n}\n"
  },
  {
    "path": "lib/new.cc",
    "content": "#include \"./wrapped_re2.h\"\n#include \"./util.h\"\n#include \"./pattern.h\"\n\n#include <map>\n#include <memory>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\nstd::atomic<bool> WrappedRE2::alreadyWarnedAboutUnicode{false};\n\nstatic const char *deprecationMessage = \"BMP patterns aren't supported by node-re2. An implicit \\\"u\\\" flag is assumed by the RE2 constructor. In a future major version, calling the RE2 constructor without the \\\"u\\\" flag may become forbidden, or cause a different behavior. Please see https://github.com/uhop/node-re2/issues/21 for more information.\";\n\ninline bool ensureUniqueNamedGroups(const std::map<int, std::string> &groups)\n{\n\tstd::unordered_set<std::string> names;\n\n\tfor (auto group : groups)\n\t{\n\t\tif (!names.insert(group.second).second)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nNAN_METHOD(WrappedRE2::New)\n{\n\n\tif (!info.IsConstructCall())\n\t{\n\t\t// call a constructor and return the result\n\n\t\tstd::vector<v8::Local<v8::Value>> parameters(info.Length());\n\t\tfor (size_t i = 0, n = info.Length(); i < n; ++i)\n\t\t{\n\t\t\tparameters[i] = info[i];\n\t\t}\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto data = getAddonData(isolate);\n\t\tif (!data) return;\n\t\tauto newObject = Nan::NewInstance(Nan::GetFunction(data->re2Tpl.Get(isolate)).ToLocalChecked(), parameters.size(), &parameters[0]);\n\t\tif (!newObject.IsEmpty())\n\t\t{\n\t\t\tinfo.GetReturnValue().Set(newObject.ToLocalChecked());\n\t\t}\n\t\treturn;\n\t}\n\n\t// process arguments\n\n\tstd::vector<char> buffer;\n\n\tchar *data = NULL;\n\tsize_t size = 0;\n\n\tstd::string source;\n\tbool global = false;\n\tbool ignoreCase = false;\n\tbool multiline = false;\n\tbool dotAll = false;\n\tbool unicode = false;\n\tbool sticky = false;\n\tbool hasIndices = false;\n\n\tauto context = Nan::GetCurrentContext();\n\tbool needFlags = true;\n\n\tif (info.Length() > 1)\n\t{\n\t\tif (info[1]->IsString())\n\t\t{\n\t\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\t\tauto t = info[1]->ToString(Nan::GetCurrentContext());\n\t\t\tauto s = t.ToLocalChecked();\n\t\t\tsize = s->Utf8Length(isolate);\n\t\t\tbuffer.resize(size + 1);\n\t\t\tdata = &buffer[0];\n\t\t\ts->WriteUtf8(isolate, data, buffer.size());\n\t\t\tbuffer[size] = '\\0';\n\t\t}\n\t\telse if (node::Buffer::HasInstance(info[1]))\n\t\t{\n\t\t\tsize = node::Buffer::Length(info[1]);\n\t\t\tdata = node::Buffer::Data(info[1]);\n\t\t}\n\t\tfor (size_t i = 0; i < size; ++i)\n\t\t{\n\t\t\tswitch (data[i])\n\t\t\t{\n\t\t\tcase 'g':\n\t\t\t\tglobal = true;\n\t\t\t\tbreak;\n\t\t\tcase 'i':\n\t\t\t\tignoreCase = true;\n\t\t\t\tbreak;\n\t\t\tcase 'm':\n\t\t\t\tmultiline = true;\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tdotAll = true;\n\t\t\t\tbreak;\n\t\t\tcase 'u':\n\t\t\t\tunicode = true;\n\t\t\t\tbreak;\n\t\t\tcase 'y':\n\t\t\t\tsticky = true;\n\t\t\t\tbreak;\n\t\t\tcase 'd':\n\t\t\t\thasIndices = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tsize = 0;\n\t\tneedFlags = false;\n\t}\n\n\tbool needConversion = true;\n\n\tif (node::Buffer::HasInstance(info[0]))\n\t{\n\t\tsize = node::Buffer::Length(info[0]);\n\t\tdata = node::Buffer::Data(info[0]);\n\n\t\tsource = escapeRegExp(data, size);\n\t}\n\telse if (info[0]->IsRegExp())\n\t{\n\t\tconst auto *re = v8::RegExp::Cast(*info[0]);\n\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto t = re->GetSource()->ToString(Nan::GetCurrentContext());\n\t\tauto s = t.ToLocalChecked();\n\t\tsize = s->Utf8Length(isolate);\n\t\tbuffer.resize(size + 1);\n\t\tdata = &buffer[0];\n\t\ts->WriteUtf8(isolate, data, buffer.size());\n\t\tbuffer[size] = '\\0';\n\n\t\tsource = escapeRegExp(data, size);\n\n\t\tif (needFlags)\n\t\t{\n\t\t\tv8::RegExp::Flags flags = re->GetFlags();\n\t\t\tglobal = bool(flags & v8::RegExp::kGlobal);\n\t\t\tignoreCase = bool(flags & v8::RegExp::kIgnoreCase);\n\t\t\tmultiline = bool(flags & v8::RegExp::kMultiline);\n\t\t\tdotAll = bool(flags & v8::RegExp::kDotAll);\n\t\t\tunicode = bool(flags & v8::RegExp::kUnicode);\n\t\t\tsticky = bool(flags & v8::RegExp::kSticky);\n\t\t\thasIndices = bool(flags & v8::RegExp::kHasIndices);\n\t\t\tneedFlags = false;\n\t\t}\n\t}\n\telse if (info[0]->IsObject() && !info[0]->IsString())\n\t{\n\t\tWrappedRE2 *re2 = nullptr;\n\t\tauto object = info[0]->ToObject(context).ToLocalChecked();\n\t\tif (!object.IsEmpty() && object->InternalFieldCount() > 0)\n\t\t{\n\t\t\tre2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(object);\n\t\t}\n\t\tif (re2)\n\t\t{\n\t\t\tconst auto &pattern = re2->regexp.pattern();\n\t\t\tsize = pattern.size();\n\t\t\tbuffer.resize(size);\n\t\t\tdata = &buffer[0];\n\t\t\tmemcpy(data, pattern.data(), size);\n\t\t\tneedConversion = false;\n\n\t\t\tsource = re2->source;\n\n\t\t\tif (needFlags)\n\t\t\t{\n\t\t\t\tglobal = re2->global;\n\t\t\t\tignoreCase = re2->ignoreCase;\n\t\t\t\tmultiline = re2->multiline;\n\t\t\t\tdotAll = re2->dotAll;\n\t\t\t\tunicode = true;\n\t\t\t\tsticky = re2->sticky;\n\t\t\t\thasIndices = re2->hasIndices;\n\t\t\t\tneedFlags = false;\n\t\t\t}\n\t\t}\n\t}\n\telse if (info[0]->IsString())\n\t{\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto t = info[0]->ToString(Nan::GetCurrentContext());\n\t\tauto s = t.ToLocalChecked();\n\t\tsize = s->Utf8Length(isolate);\n\t\tbuffer.resize(size + 1);\n\t\tdata = &buffer[0];\n\t\ts->WriteUtf8(isolate, data, buffer.size());\n\t\tbuffer[size] = '\\0';\n\n\t\tsource = escapeRegExp(data, size);\n\t}\n\n\tif (!data)\n\t{\n\t\treturn Nan::ThrowTypeError(\"Expected string, Buffer, RegExp, or RE2 as the 1st argument.\");\n\t}\n\n\tif (!unicode)\n\t{\n\t\tswitch (unicodeWarningLevel)\n\t\t{\n\t\tcase THROW:\n\t\t\treturn Nan::ThrowSyntaxError(deprecationMessage);\n\t\tcase WARN:\n\t\t\tprintDeprecationWarning(deprecationMessage);\n\t\t\tbreak;\n\t\tcase WARN_ONCE:\n\t\t\tif (!alreadyWarnedAboutUnicode)\n\t\t\t{\n\t\t\t\tprintDeprecationWarning(deprecationMessage);\n\t\t\t\talreadyWarnedAboutUnicode = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (needConversion && translateRegExp(data, size, multiline, buffer))\n\t{\n\t\tsize = buffer.size() - 1;\n\t\tdata = &buffer[0];\n\t}\n\n\t// create and return an object\n\n\tre2::RE2::Options options;\n\toptions.set_case_sensitive(!ignoreCase);\n\toptions.set_one_line(!multiline); // to track this state, otherwise it is ignored\n\toptions.set_dot_nl(dotAll);\n\toptions.set_log_errors(false); // inappropriate when embedding\n\n\tstd::unique_ptr<WrappedRE2> re2(new WrappedRE2(re2::StringPiece(data, size), options, source, global, ignoreCase, multiline, dotAll, sticky, hasIndices));\n\tif (!re2->regexp.ok())\n\t{\n\t\treturn Nan::ThrowSyntaxError(re2->regexp.error().c_str());\n\t}\n\tif (!ensureUniqueNamedGroups(re2->regexp.CapturingGroupNames()))\n\t{\n\t\treturn Nan::ThrowSyntaxError(\"duplicate capture group name\");\n\t}\n\tre2->Wrap(info.This());\n\tre2.release();\n\n\tinfo.GetReturnValue().Set(info.This());\n}\n"
  },
  {
    "path": "lib/pattern.cc",
    "content": "#include \"./pattern.h\"\n#include \"./wrapped_re2.h\"\n\n#include <cstring>\n#include <map>\n#include <string>\n\nstatic char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\n\ninline bool isUpperCaseAlpha(char ch)\n{\n\treturn 'A' <= ch && ch <= 'Z';\n}\n\ninline bool isHexadecimal(char ch)\n{\n\treturn ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');\n}\n\nstatic std::map<std::string, std::string> unicodeClasses = {\n\t{\"Uppercase_Letter\", \"Lu\"},\n\t{\"Lowercase_Letter\", \"Ll\"},\n\t{\"Titlecase_Letter\", \"Lt\"},\n\t{\"Cased_Letter\", \"LC\"},\n\t{\"Modifier_Letter\", \"Lm\"},\n\t{\"Other_Letter\", \"Lo\"},\n\t{\"Letter\", \"L\"},\n\t{\"Nonspacing_Mark\", \"Mn\"},\n\t{\"Spacing_Mark\", \"Mc\"},\n\t{\"Enclosing_Mark\", \"Me\"},\n\t{\"Mark\", \"M\"},\n\t{\"Decimal_Number\", \"Nd\"},\n\t{\"Letter_Number\", \"Nl\"},\n\t{\"Other_Number\", \"No\"},\n\t{\"Number\", \"N\"},\n\t{\"Connector_Punctuation\", \"Pc\"},\n\t{\"Dash_Punctuation\", \"Pd\"},\n\t{\"Open_Punctuation\", \"Ps\"},\n\t{\"Close_Punctuation\", \"Pe\"},\n\t{\"Initial_Punctuation\", \"Pi\"},\n\t{\"Final_Punctuation\", \"Pf\"},\n\t{\"Other_Punctuation\", \"Po\"},\n\t{\"Punctuation\", \"P\"},\n\t{\"Math_Symbol\", \"Sm\"},\n\t{\"Currency_Symbol\", \"Sc\"},\n\t{\"Modifier_Symbol\", \"Sk\"},\n\t{\"Other_Symbol\", \"So\"},\n\t{\"Symbol\", \"S\"},\n\t{\"Space_Separator\", \"Zs\"},\n\t{\"Line_Separator\", \"Zl\"},\n\t{\"Paragraph_Separator\", \"Zp\"},\n\t{\"Separator\", \"Z\"},\n\t{\"Control\", \"Cc\"},\n\t{\"Format\", \"Cf\"},\n\t{\"Surrogate\", \"Cs\"},\n\t{\"Private_Use\", \"Co\"},\n\t{\"Unassigned\", \"Cn\"},\n\t{\"Other\", \"C\"},\n};\n\nbool translateRegExp(const char *data, size_t size, bool multiline, std::vector<char> &buffer)\n{\n\tstd::string result;\n\tbool changed = false;\n\n\tif (!size)\n\t{\n\t\tresult = \"(?:)\";\n\t\tchanged = true;\n\t}\n\telse if (multiline)\n\t{\n\t\tresult = \"(?m)\";\n\t\tchanged = true;\n\t}\n\n\tfor (size_t i = 0; i < size;)\n\t{\n\t\tchar ch = data[i];\n\t\tif (ch == '\\\\')\n\t\t{\n\t\t\tif (i + 1 < size)\n\t\t\t{\n\t\t\t\tch = data[i + 1];\n\t\t\t\tswitch (ch)\n\t\t\t\t{\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tresult += \"\\\\\\\\\";\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'c':\n\t\t\t\t\tif (i + 2 < size)\n\t\t\t\t\t{\n\t\t\t\t\t\tch = data[i + 2];\n\t\t\t\t\t\tif (isUpperCaseAlpha(ch))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tresult += \"\\\\x\";\n\t\t\t\t\t\t\tresult += hex[((ch - '@') / 16) & 15];\n\t\t\t\t\t\t\tresult += hex[(ch - '@') & 15];\n\t\t\t\t\t\t\ti += 3;\n\t\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult += \"\\\\c\";\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'u':\n\t\t\t\t\tif (i + 2 < size)\n\t\t\t\t\t{\n\t\t\t\t\t\tch = data[i + 2];\n\t\t\t\t\t\tif (isHexadecimal(ch))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tresult += \"\\\\x{\";\n\t\t\t\t\t\t\tresult += ch;\n\t\t\t\t\t\t\ti += 3;\n\t\t\t\t\t\t\tfor (size_t j = 0; j < 3 && i < size; ++i, ++j)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tch = data[i];\n\t\t\t\t\t\t\t\tif (!isHexadecimal(ch))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tresult += ch;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult += '}';\n\t\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (ch == '{')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tresult += \"\\\\x\";\n\t\t\t\t\t\t\ti += 2;\n\t\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult += \"\\\\u\";\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'p':\n\t\t\t\tcase 'P':\n\t\t\t\t\tif (i + 2 < size) {\n\t\t\t\t\t\tif (data[i + 2] == '{') {\n\t\t\t\t\t\t\tsize_t j = i + 3;\n\t\t\t\t\t\t\twhile (j < size && data[j] != '}') ++j;\n\t\t\t\t\t\t\tif (j < size) {\n\t\t\t\t\t\t\t\tresult += \"\\\\\";\n\t\t\t\t\t\t\t\tresult += data[i + 1];\n\t\t\t\t\t\t\t\tstd::string name(data + i + 3, j - i - 3);\n\t\t\t\t\t\t\t\tif (unicodeClasses.find(name) != unicodeClasses.end()) {\n\t\t\t\t\t\t\t\t\tname = unicodeClasses[name];\n\t\t\t\t\t\t\t\t} else if (name.size() > 7 && !strncmp(name.c_str(), \"Script=\", 7)) {\n\t\t\t\t\t\t\t\t\tname = name.substr(7);\n\t\t\t\t\t\t\t\t} else if (name.size() > 3 && !strncmp(name.c_str(), \"sc=\", 3)) {\n\t\t\t\t\t\t\t\t\tname = name.substr(3);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (name.size() == 1) {\n\t\t\t\t\t\t\t\t\tresult += name;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tresult += \"{\";\n\t\t\t\t\t\t\t\t\tresult += name;\n\t\t\t\t\t\t\t\t\tresult += \"}\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ti = j + 1;\n\t\t\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult += \"\\\\\";\n\t\t\t\t\tresult += data[i + 1];\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tdefault:\n\t\t\t\t\tresult += \"\\\\\";\n\t\t\t\t\tsize_t sym_size = getUtf8CharSize(ch);\n\t\t\t\t\tresult.append(data + i + 1, sym_size);\n\t\t\t\t\ti += sym_size + 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (ch == '/')\n\t\t{\n\t\t\tresult += \"\\\\/\";\n\t\t\ti += 1;\n\t\t\tchanged = true;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (ch == '(' && i + 2 < size && data[i + 1] == '?' && data[i + 2] == '<')\n\t\t{\n\t\t\tif (i + 3 >= size || (data[i + 3] != '=' && data[i + 3] != '!'))\n\t\t\t{\n\t\t\t\tresult += \"(?P<\";\n\t\t\t\ti += 3;\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tsize_t sym_size = getUtf8CharSize(ch);\n\t\tresult.append(data + i, sym_size);\n\t\ti += sym_size;\n\t}\n\n\tif (!changed)\n\t{\n\t\treturn false;\n\t}\n\n\tbuffer.resize(0);\n\tbuffer.insert(buffer.end(), result.data(), result.data() + result.size());\n\tbuffer.push_back('\\0');\n\n\treturn true;\n}\n\nstd::string escapeRegExp(const char *data, size_t size)\n{\n\tstd::string result;\n\n\tif (!size)\n\t{\n\t\tresult = \"(?:)\";\n\t}\n\n\tsize_t prevBackSlashes = 0;\n\tfor (size_t i = 0; i < size;)\n\t{\n\t\tchar ch = data[i];\n\t\tif (ch == '\\\\')\n\t\t{\n\t\t\t++prevBackSlashes;\n\t\t}\n\t\telse if (ch == '/' && !(prevBackSlashes & 1))\n\t\t{\n\t\t\tresult += \"\\\\/\";\n\t\t\ti += 1;\n\t\t\tprevBackSlashes = 0;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprevBackSlashes = 0;\n\t\t}\n\t\tsize_t sym_size = getUtf8CharSize(ch);\n\t\tresult.append(data + i, sym_size);\n\t\ti += sym_size;\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "lib/pattern.h",
    "content": "#pragma once\n\n#include <string>\n#include <vector>\n\n// Shared helpers for translating JavaScript-style regular expressions\n// into RE2-compatible patterns.\nbool translateRegExp(const char *data, size_t size, bool multiline, std::vector<char> &buffer);\nstd::string escapeRegExp(const char *data, size_t size);\n\n"
  },
  {
    "path": "lib/replace.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <vector>\n\ninline int getMaxSubmatch(\n\tconst char *data,\n\tsize_t size,\n\tconst std::map<std::string, int> &namedGroups)\n{\n\tint maxSubmatch = 0, index, index2;\n\tconst char *nameBegin;\n\tconst char *nameEnd;\n\tfor (size_t i = 0; i < size;)\n\t{\n\t\tchar ch = data[i];\n\t\tif (ch == '$')\n\t\t{\n\t\t\tif (i + 1 < size)\n\t\t\t{\n\t\t\t\tch = data[i + 1];\n\t\t\t\tswitch (ch)\n\t\t\t\t{\n\t\t\t\tcase '$':\n\t\t\t\tcase '&':\n\t\t\t\tcase '`':\n\t\t\t\tcase '\\'':\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '0':\n\t\t\t\tcase '1':\n\t\t\t\tcase '2':\n\t\t\t\tcase '3':\n\t\t\t\tcase '4':\n\t\t\t\tcase '5':\n\t\t\t\tcase '6':\n\t\t\t\tcase '7':\n\t\t\t\tcase '8':\n\t\t\t\tcase '9':\n\t\t\t\t\tindex = ch - '0';\n\t\t\t\t\tif (i + 2 < size)\n\t\t\t\t\t{\n\t\t\t\t\t\tch = data[i + 2];\n\t\t\t\t\t\tif ('0' <= ch && ch <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tindex2 = index * 10 + (ch - '0');\n\t\t\t\t\t\t\tif (maxSubmatch < index2)\n\t\t\t\t\t\t\t\tmaxSubmatch = index2;\n\t\t\t\t\t\t\ti += 3;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (maxSubmatch < index)\n\t\t\t\t\t\tmaxSubmatch = index;\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '<':\n\t\t\t\t\tnameBegin = data + i + 2;\n\t\t\t\t\tnameEnd = (const char *)memchr(nameBegin, '>', size - i - 2);\n\t\t\t\t\tif (nameEnd)\n\t\t\t\t\t{\n\t\t\t\t\t\tstd::string name(nameBegin, nameEnd - nameBegin);\n\t\t\t\t\t\tauto group = namedGroups.find(name);\n\t\t\t\t\t\tif (group != namedGroups.end())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tindex = group->second;\n\t\t\t\t\t\t\tif (maxSubmatch < index)\n\t\t\t\t\t\t\t\tmaxSubmatch = index;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti = nameEnd + 1 - data;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ti += 2;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\t++i;\n\t\t\tcontinue;\n\t\t}\n\t\ti += getUtf8CharSize(ch);\n\t}\n\treturn maxSubmatch;\n}\n\ninline std::string replace(\n\tconst char *data,\n\tsize_t size,\n\tconst std::vector<re2::StringPiece> &groups,\n\tconst re2::StringPiece &str,\n\tconst std::map<std::string, int> &namedGroups)\n{\n\tstd::string result;\n\tsize_t index, index2;\n\tconst char *nameBegin;\n\tconst char *nameEnd;\n\tfor (size_t i = 0; i < size;)\n\t{\n\t\tchar ch = data[i];\n\t\tif (ch == '$')\n\t\t{\n\t\t\tif (i + 1 < size)\n\t\t\t{\n\t\t\t\tch = data[i + 1];\n\t\t\t\tswitch (ch)\n\t\t\t\t{\n\t\t\t\tcase '$':\n\t\t\t\t\tresult += ch;\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '&':\n\t\t\t\t\tresult += (std::string)groups[0];\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '`':\n\t\t\t\t\tresult += std::string(str.data(), groups[0].data() - str.data());\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '\\'':\n\t\t\t\t\tresult += std::string(groups[0].data() + groups[0].size(),\n\t\t\t\t\t\t\t\t\t\t  str.data() + str.size() - groups[0].data() - groups[0].size());\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '0':\n\t\t\t\tcase '1':\n\t\t\t\tcase '2':\n\t\t\t\tcase '3':\n\t\t\t\tcase '4':\n\t\t\t\tcase '5':\n\t\t\t\tcase '6':\n\t\t\t\tcase '7':\n\t\t\t\tcase '8':\n\t\t\t\tcase '9':\n\t\t\t\t\tindex = ch - '0';\n\t\t\t\t\tif (i + 2 < size)\n\t\t\t\t\t{\n\t\t\t\t\t\tch = data[i + 2];\n\t\t\t\t\t\tif ('0' <= ch && ch <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ti += 3;\n\t\t\t\t\t\t\tindex2 = index * 10 + (ch - '0');\n\t\t\t\t\t\t\tif (index2 && index2 < groups.size())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tresult += (std::string)groups[index2];\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (index && index < groups.size())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tresult += (std::string)groups[index];\n\t\t\t\t\t\t\t\tresult += ch;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult += '$';\n\t\t\t\t\t\t\tresult += '0' + index;\n\t\t\t\t\t\t\tresult += ch;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tch = '0' + index;\n\t\t\t\t\t}\n\t\t\t\t\ti += 2;\n\t\t\t\t\tif (index && index < groups.size())\n\t\t\t\t\t{\n\t\t\t\t\t\tresult += (std::string)groups[index];\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tresult += '$';\n\t\t\t\t\tresult += ch;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '<':\n\t\t\t\t\tif (!namedGroups.empty())\n\t\t\t\t\t{\n\t\t\t\t\t\tnameBegin = data + i + 2;\n\t\t\t\t\t\tnameEnd = (const char *)memchr(nameBegin, '>', size - i - 2);\n\t\t\t\t\t\tif (nameEnd)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstd::string name(nameBegin, nameEnd - nameBegin);\n\t\t\t\t\t\t\tauto group = namedGroups.find(name);\n\t\t\t\t\t\t\tif (group != namedGroups.end())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex = group->second;\n\t\t\t\t\t\t\t\tresult += (std::string)groups[index];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ti = nameEnd + 1 - data;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tresult += \"$<\";\n\t\t\t\t\t\t\ti += 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tresult += \"$<\";\n\t\t\t\t\t\ti += 2;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult += '$';\n\t\t\t++i;\n\t\t\tcontinue;\n\t\t}\n\t\tsize_t sym_size = getUtf8CharSize(ch);\n\t\tresult.append(data + i, sym_size);\n\t\ti += sym_size;\n\t}\n\treturn result;\n}\n\nstatic Nan::Maybe<std::string> replace(\n\tWrappedRE2 *re2,\n\tconst StrVal &replacee,\n\tconst char *replacer,\n\tsize_t replacer_size)\n{\n\tconst re2::StringPiece str = replacee;\n\tconst char *data = str.data();\n\tsize_t size = str.size();\n\n\tconst auto &namedGroups = re2->regexp.NamedCapturingGroups();\n\n\tstd::vector<re2::StringPiece> groups(std::min(re2->regexp.NumberOfCapturingGroups(), getMaxSubmatch(replacer, replacer_size, namedGroups)) + 1);\n\tconst auto &match = groups[0];\n\n\tsize_t byteIndex = 0;\n\tstd::string result;\n\tauto anchor = re2::RE2::UNANCHORED;\n\n\tif (re2->sticky)\n\t{\n\t\tif (!re2->global)\n\t\t\tbyteIndex = replacee.byteIndex;\n\t\tanchor = re2::RE2::ANCHOR_START;\n\t}\n\n\tif (byteIndex)\n\t{\n\t\tresult = std::string(data, byteIndex);\n\t}\n\n\tbool noMatch = true;\n\twhile (byteIndex <= size && re2->regexp.Match(str, byteIndex, size, anchor, &groups[0], groups.size()))\n\t{\n\t\tnoMatch = false;\n\t\tauto offset = match.data() - data;\n\t\tif (!re2->global && re2->sticky)\n\t\t{\n\t\t\tre2->lastIndex +=\n\t\t\t\treplacee.isBuffer ? offset + match.size() - byteIndex : getUtf16Length(data + byteIndex, match.data() + match.size());\n\t\t}\n\t\tif (match.data() == data || offset > static_cast<long>(byteIndex))\n\t\t{\n\t\t\tresult += std::string(data + byteIndex, offset - byteIndex);\n\t\t}\n\t\tresult += replace(replacer, replacer_size, groups, str, namedGroups);\n\t\tif (match.size())\n\t\t{\n\t\t\tbyteIndex = offset + match.size();\n\t\t}\n\t\telse if ((size_t)offset < size)\n\t\t{\n\t\t\tauto sym_size = getUtf8CharSize(data[offset]);\n\t\t\tresult.append(data + offset, sym_size);\n\t\t\tbyteIndex = offset + sym_size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbyteIndex = size;\n\t\t\tbreak;\n\t\t}\n\t\tif (!re2->global)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (byteIndex < size)\n\t{\n\t\tresult += std::string(data + byteIndex, size - byteIndex);\n\t}\n\n\tif (re2->global)\n\t{\n\t\tre2->lastIndex = 0;\n\t}\n\telse if (re2->sticky)\n\t{\n\t\tif (noMatch)\n\t\t\tre2->lastIndex = 0;\n\t}\n\n\treturn Nan::Just(result);\n}\n\ninline Nan::Maybe<std::string> replace(\n\tconst Nan::Callback *replacer,\n\tconst std::vector<re2::StringPiece> &groups,\n\tconst re2::StringPiece &str,\n\tconst v8::Local<v8::Value> &input,\n\tbool useBuffers,\n\tconst std::map<std::string, int> &namedGroups)\n{\n\tstd::vector<v8::Local<v8::Value>> argv;\n\n\tauto context = Nan::GetCurrentContext();\n\n\tif (useBuffers)\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\targv.push_back(Nan::CopyBuffer(data, item.size()).ToLocalChecked());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\targv.push_back(Nan::Undefined());\n\t\t\t}\n\t\t}\n\t\targv.push_back(Nan::New(static_cast<int>(groups[0].data() - str.data())));\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0, n = groups.size(); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = groups[i];\n\t\t\tconst auto data = item.data();\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\targv.push_back(Nan::New(data, item.size()).ToLocalChecked());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\targv.push_back(Nan::Undefined());\n\t\t\t}\n\t\t}\n\t\targv.push_back(Nan::New(static_cast<int>(getUtf16Length(str.data(), groups[0].data()))));\n\t}\n\targv.push_back(input);\n\n\tif (!namedGroups.empty())\n\t{\n\t\tauto groups = Nan::New<v8::Object>();\n\t\tNan::SetPrototype(groups, Nan::Null());\n\n\t\tfor (std::pair<std::string, int> group : namedGroups)\n\t\t{\n\t\t\tNan::Set(groups, Nan::New(group.first).ToLocalChecked(), argv[group.second]);\n\t\t}\n\n\t\targv.push_back(groups);\n\t}\n\n\tauto maybeResult = Nan::CallAsFunction(replacer->GetFunction(), context->Global(), static_cast<int>(argv.size()), &argv[0]);\n\n\tif (maybeResult.IsEmpty())\n\t{\n\t\treturn Nan::Nothing<std::string>();\n\t}\n\n\tauto result = maybeResult.ToLocalChecked();\n\n\tif (node::Buffer::HasInstance(result))\n\t{\n\t\treturn Nan::Just(std::string(node::Buffer::Data(result), node::Buffer::Length(result)));\n\t}\n\n\tauto t = result->ToString(Nan::GetCurrentContext());\n\tif (t.IsEmpty())\n\t{\n\t\treturn Nan::Nothing<std::string>();\n\t}\n\n\tv8::String::Utf8Value s(v8::Isolate::GetCurrent(), t.ToLocalChecked());\n\treturn Nan::Just(std::string(*s));\n}\n\nstatic Nan::Maybe<std::string> replace(\n\tWrappedRE2 *re2,\n\tconst StrVal &replacee,\n\tconst Nan::Callback *replacer,\n\tconst v8::Local<v8::Value> &input,\n\tbool useBuffers)\n{\n\tconst re2::StringPiece str = replacee;\n\tconst char *data = str.data();\n\tsize_t size = str.size();\n\n\tstd::vector<re2::StringPiece> groups(re2->regexp.NumberOfCapturingGroups() + 1);\n\tconst auto &match = groups[0];\n\n\tsize_t byteIndex = 0;\n\tstd::string result;\n\tauto anchor = re2::RE2::UNANCHORED;\n\n\tif (re2->sticky)\n\t{\n\t\tif (!re2->global)\n\t\t\tbyteIndex = replacee.byteIndex;\n\t\tanchor = RE2::ANCHOR_START;\n\t}\n\n\tif (byteIndex)\n\t{\n\t\tresult = std::string(data, byteIndex);\n\t}\n\n\tconst auto &namedGroups = re2->regexp.NamedCapturingGroups();\n\n\tbool noMatch = true;\n\twhile (byteIndex <= size && re2->regexp.Match(str, byteIndex, size, anchor, &groups[0], groups.size()))\n\t{\n\t\tnoMatch = false;\n\t\tauto offset = match.data() - data;\n\t\tif (!re2->global && re2->sticky)\n\t\t{\n\t\t\tre2->lastIndex += replacee.isBuffer ? offset + match.size() - byteIndex : getUtf16Length(data + byteIndex, match.data() + match.size());\n\t\t}\n\t\tif (match.data() == data || offset > static_cast<long>(byteIndex))\n\t\t{\n\t\t\tresult += std::string(data + byteIndex, offset - byteIndex);\n\t\t}\n\t\tconst auto part = replace(replacer, groups, str, input, useBuffers, namedGroups);\n\t\tif (part.IsNothing())\n\t\t{\n\t\t\treturn part;\n\t\t}\n\t\tresult += part.FromJust();\n\t\tif (match.size())\n\t\t{\n\t\t\tbyteIndex = offset + match.size();\n\t\t}\n\t\telse if ((size_t)offset < size)\n\t\t{\n\t\t\tauto sym_size = getUtf8CharSize(data[offset]);\n\t\t\tresult.append(data + offset, sym_size);\n\t\t\tbyteIndex = offset + sym_size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbyteIndex = size;\n\t\t\tbreak;\n\t\t}\n\t\tif (!re2->global)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (byteIndex < size)\n\t{\n\t\tresult += std::string(data + byteIndex, size - byteIndex);\n\t}\n\n\tif (re2->global)\n\t{\n\t\tre2->lastIndex = 0;\n\t}\n\telse if (re2->sticky)\n\t{\n\t\tif (noMatch)\n\t\t{\n\t\t\tre2->lastIndex = 0;\n\t\t}\n\t}\n\n\treturn Nan::Just(result);\n}\n\nstatic bool requiresBuffers(const v8::Local<v8::Function> &f)\n{\n\tauto flag(Nan::Get(f, Nan::New(\"useBuffers\").ToLocalChecked()).ToLocalChecked());\n\tif (flag->IsUndefined() || flag->IsNull() || flag->IsFalse())\n\t{\n\t\treturn false;\n\t}\n\tif (flag->IsNumber())\n\t{\n\t\treturn flag->NumberValue(Nan::GetCurrentContext()).FromMaybe(0) != 0;\n\t}\n\tif (flag->IsString())\n\t{\n\t\treturn flag->ToString(Nan::GetCurrentContext()).ToLocalChecked()->Length() > 0;\n\t}\n\treturn true;\n}\n\nNAN_METHOD(WrappedRE2::Replace)\n{\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().Set(info[0]);\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& replacee = prep;\n\tif (replacee.isBad) return; // throws an exception\n\n\tif (!replacee.isValidIndex)\n\t{\n\t\tinfo.GetReturnValue().Set(info[0]);\n\t\treturn;\n\t}\n\n\tstd::string result;\n\n\tif (info[1]->IsFunction())\n\t{\n\t\tauto fun = info[1].As<v8::Function>();\n\t\tconst std::unique_ptr<const Nan::Callback> cb(new Nan::Callback(fun));\n\t\tconst auto replaced = replace(re2, replacee, cb.get(), info[0], requiresBuffers(fun));\n\t\tif (replaced.IsNothing())\n\t\t{\n\t\t\tinfo.GetReturnValue().Set(info[0]);\n\t\t\treturn;\n\t\t}\n\t\tresult = replaced.FromJust();\n\t}\n\telse\n\t{\n\t\tv8::Local<v8::Object> replacer;\n\t\tif (node::Buffer::HasInstance(info[1]))\n\t\t{\n\t\t\treplacer = info[1].As<v8::Object>();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tauto t = info[1]->ToString(Nan::GetCurrentContext());\n\t\t\tif (t.IsEmpty())\n\t\t\t\treturn; // throws an exception\n\t\t\treplacer = node::Buffer::New(v8::Isolate::GetCurrent(), t.ToLocalChecked()).ToLocalChecked();\n\t\t}\n\n\t\tauto data = node::Buffer::Data(replacer);\n\t\tauto size = node::Buffer::Length(replacer);\n\n\t\tconst auto replaced = replace(re2, replacee, data, size);\n\t\tif (replaced.IsNothing())\n\t\t{\n\t\t\tinfo.GetReturnValue().Set(info[0]);\n\t\t\treturn;\n\t\t}\n\t\tresult = replaced.FromJust();\n\t}\n\n\tif (replacee.isBuffer)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked());\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(Nan::New(result).ToLocalChecked());\n}\n"
  },
  {
    "path": "lib/search.cc",
    "content": "#include \"./wrapped_re2.h\"\n\nNAN_METHOD(WrappedRE2::Search)\n{\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().Set(-1);\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& str = prep;\n\tif (str.isBad) return; // throws an exception\n\n\tif (!str.data)\n\t\treturn;\n\n\t// actual work\n\n\tre2::StringPiece match;\n\n\tif (re2->regexp.Match(str, 0, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &match, 1))\n\t{\n\t\tinfo.GetReturnValue().Set(static_cast<int>(str.isBuffer ? match.data() - str.data : getUtf16Length(str.data, match.data())));\n\t\treturn;\n\t}\n\n\tinfo.GetReturnValue().Set(-1);\n}\n"
  },
  {
    "path": "lib/set.cc",
    "content": "#include \"./wrapped_re2_set.h\"\n#include \"./pattern.h\"\n#include \"./util.h\"\n#include \"./wrapped_re2.h\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <vector>\n\nstruct SetFlags\n{\n\tbool global = false;\n\tbool ignoreCase = false;\n\tbool multiline = false;\n\tbool dotAll = false;\n\tbool unicode = false;\n\tbool sticky = false;\n\tbool hasIndices = false;\n};\n\nstatic bool parseFlags(const v8::Local<v8::Value> &arg, SetFlags &flags)\n{\n\tconst char *data = nullptr;\n\tsize_t size = 0;\n\tstd::vector<char> buffer;\n\n\tif (arg->IsString())\n\t{\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto t = arg->ToString(Nan::GetCurrentContext());\n\t\tif (t.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tauto s = t.ToLocalChecked();\n\t\tsize = s->Utf8Length(isolate);\n\t\tbuffer.resize(size + 1);\n\t\ts->WriteUtf8(isolate, &buffer[0], buffer.size());\n\t\tbuffer[buffer.size() - 1] = '\\0';\n\t\tdata = &buffer[0];\n\t}\n\telse if (node::Buffer::HasInstance(arg))\n\t{\n\t\tsize = node::Buffer::Length(arg);\n\t\tdata = node::Buffer::Data(arg);\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n\n\tfor (size_t i = 0; i < size; ++i)\n\t{\n\t\tswitch (data[i])\n\t\t{\n\t\tcase 'd':\n\t\t\tflags.hasIndices = true;\n\t\t\tbreak;\n\t\tcase 'g':\n\t\t\tflags.global = true;\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tflags.ignoreCase = true;\n\t\t\tbreak;\n\t\tcase 'm':\n\t\t\tflags.multiline = true;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tflags.dotAll = true;\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tflags.unicode = true;\n\t\t\tbreak;\n\t\tcase 'y':\n\t\t\tflags.sticky = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic bool sameEffectiveOptions(const SetFlags &a, const SetFlags &b)\n{\n\treturn a.ignoreCase == b.ignoreCase && a.multiline == b.multiline && a.dotAll == b.dotAll && a.unicode == b.unicode;\n}\n\nstatic std::string flagsToString(const SetFlags &flags)\n{\n\tstd::string result;\n\tif (flags.hasIndices)\n\t{\n\t\tresult += 'd';\n\t}\n\tif (flags.global)\n\t{\n\t\tresult += 'g';\n\t}\n\tif (flags.ignoreCase)\n\t{\n\t\tresult += 'i';\n\t}\n\tif (flags.multiline)\n\t{\n\t\tresult += 'm';\n\t}\n\tif (flags.dotAll)\n\t{\n\t\tresult += 's';\n\t}\n\tresult += 'u';\n\tif (flags.sticky)\n\t{\n\t\tresult += 'y';\n\t}\n\treturn result;\n}\n\nstatic bool collectIterable(const v8::Local<v8::Value> &input, std::vector<v8::Local<v8::Value>> &items)\n{\n\tauto context = Nan::GetCurrentContext();\n\tauto isolate = v8::Isolate::GetCurrent();\n\n\tif (input->IsArray())\n\t{\n\t\tauto array = v8::Local<v8::Array>::Cast(input);\n\t\tauto length = array->Length();\n\t\titems.reserve(length);\n\t\tfor (uint32_t i = 0; i < length; ++i)\n\t\t{\n\t\t\tauto maybe = Nan::Get(array, i);\n\t\t\tif (maybe.IsEmpty())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\titems.push_back(maybe.ToLocalChecked());\n\t\t}\n\t\treturn true;\n\t}\n\n\tauto maybeObject = input->ToObject(context);\n\tif (maybeObject.IsEmpty())\n\t{\n\t\treturn false;\n\t}\n\tauto object = maybeObject.ToLocalChecked();\n\n\tauto maybeIteratorFn = object->Get(context, v8::Symbol::GetIterator(isolate));\n\tif (maybeIteratorFn.IsEmpty())\n\t{\n\t\treturn false;\n\t}\n\tauto iteratorFn = maybeIteratorFn.ToLocalChecked();\n\tif (!iteratorFn->IsFunction())\n\t{\n\t\treturn false;\n\t}\n\n\tauto maybeIterator = iteratorFn.As<v8::Function>()->Call(context, object, 0, nullptr);\n\tif (maybeIterator.IsEmpty())\n\t{\n\t\treturn false;\n\t}\n\tauto iterator = maybeIterator.ToLocalChecked();\n\tif (!iterator->IsObject())\n\t{\n\t\treturn false;\n\t}\n\n\tauto nextKey = Nan::New(\"next\").ToLocalChecked();\n\tauto valueKey = Nan::New(\"value\").ToLocalChecked();\n\tauto doneKey = Nan::New(\"done\").ToLocalChecked();\n\n\tfor (;;)\n\t{\n\t\tauto maybeNext = Nan::Get(iterator.As<v8::Object>(), nextKey);\n\t\tif (maybeNext.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tauto next = maybeNext.ToLocalChecked();\n\t\tif (!next->IsFunction())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tauto maybeResult = next.As<v8::Function>()->Call(context, iterator, 0, nullptr);\n\t\tif (maybeResult.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tauto result = maybeResult.ToLocalChecked();\n\t\tif (!result->IsObject())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tauto resultObj = result->ToObject(context).ToLocalChecked();\n\t\tauto maybeDone = Nan::Get(resultObj, doneKey);\n\t\tif (maybeDone.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (maybeDone.ToLocalChecked()->BooleanValue(isolate))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tauto maybeValue = Nan::Get(resultObj, valueKey);\n\t\tif (maybeValue.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\titems.push_back(maybeValue.ToLocalChecked());\n\t}\n\n\treturn true;\n}\n\nstatic bool parseAnchor(const v8::Local<v8::Value> &arg, re2::RE2::Anchor &anchor)\n{\n\tif (arg.IsEmpty() || arg->IsUndefined() || arg->IsNull())\n\t{\n\t\tanchor = re2::RE2::UNANCHORED;\n\t\treturn true;\n\t}\n\n\tv8::Local<v8::Value> value = arg;\n\tif (arg->IsObject() && !arg->IsString())\n\t{\n\t\tauto context = Nan::GetCurrentContext();\n\t\tauto object = arg->ToObject(context).ToLocalChecked();\n\t\tauto maybeAnchor = Nan::Get(object, Nan::New(\"anchor\").ToLocalChecked());\n\t\tif (maybeAnchor.IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tvalue = maybeAnchor.ToLocalChecked();\n\t\tif (value->IsUndefined() || value->IsNull())\n\t\t{\n\t\t\tanchor = re2::RE2::UNANCHORED;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (!value->IsString())\n\t{\n\t\treturn false;\n\t}\n\n\tNan::Utf8String val(value);\n\tstd::string text(*val, val.length());\n\n\tif (text == \"unanchored\")\n\t{\n\t\tanchor = re2::RE2::UNANCHORED;\n\t\treturn true;\n\t}\n\tif (text == \"start\")\n\t{\n\t\tanchor = re2::RE2::ANCHOR_START;\n\t\treturn true;\n\t}\n\tif (text == \"both\")\n\t{\n\t\tanchor = re2::RE2::ANCHOR_BOTH;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool fillInput(const v8::Local<v8::Value> &arg, StrVal &str, v8::Local<v8::Object> &keepAlive)\n{\n\tif (node::Buffer::HasInstance(arg))\n\t{\n\t\tauto size = node::Buffer::Length(arg);\n\t\tstr.reset(arg, size, size, 0, true);\n\t\treturn true;\n\t}\n\n\tauto context = Nan::GetCurrentContext();\n\tauto isolate = v8::Isolate::GetCurrent();\n\tauto t = arg->ToString(context);\n\tif (t.IsEmpty())\n\t{\n\t\treturn false;\n\t}\n\tauto s = t.ToLocalChecked();\n\tauto utf8Length = s->Utf8Length(isolate);\n\tauto buffer = node::Buffer::New(isolate, s).ToLocalChecked();\n\tkeepAlive = buffer;\n\tstr.reset(buffer, node::Buffer::Length(buffer), utf8Length, 0);\n\treturn true;\n}\n\nstatic std::string anchorToString(re2::RE2::Anchor anchor)\n{\n\tswitch (anchor)\n\t{\n\tcase re2::RE2::ANCHOR_BOTH:\n\t\treturn \"both\";\n\tcase re2::RE2::ANCHOR_START:\n\t\treturn \"start\";\n\tdefault:\n\t\treturn \"unanchored\";\n\t}\n}\n\nstatic std::string makeCombinedSource(const std::vector<std::string> &sources)\n{\n\tif (sources.empty())\n\t{\n\t\treturn \"(?:)\";\n\t}\n\n\tstd::string combined;\n\tfor (size_t i = 0, n = sources.size(); i < n; ++i)\n\t{\n\t\tif (i)\n\t\t{\n\t\t\tcombined += '|';\n\t\t}\n\t\tcombined += sources[i];\n\t}\n\treturn combined;\n}\n\nstatic const char setDeprecationMessage[] = \"BMP patterns aren't supported by node-re2. An implicit \\\"u\\\" flag is assumed by RE2.Set. In a future major version, calling RE2.Set without the \\\"u\\\" flag may become forbidden, or cause a different behavior. Please see https://github.com/uhop/node-re2/issues/21 for more information.\";\n\nNAN_METHOD(WrappedRE2Set::New)\n{\n\tauto context = Nan::GetCurrentContext();\n\tauto isolate = context->GetIsolate();\n\n\tif (!info.IsConstructCall())\n\t{\n\t\tstd::vector<v8::Local<v8::Value>> parameters(info.Length());\n\t\tfor (size_t i = 0, n = info.Length(); i < n; ++i)\n\t\t{\n\t\t\tparameters[i] = info[i];\n\t\t}\n\t\tauto isolate = context->GetIsolate();\n\t\tauto addonData = getAddonData(isolate);\n\t\tif (!addonData) return;\n\t\tauto maybeNew = Nan::NewInstance(Nan::GetFunction(addonData->re2SetTpl.Get(isolate)).ToLocalChecked(), parameters.size(), &parameters[0]);\n\t\tif (!maybeNew.IsEmpty())\n\t\t{\n\t\t\tinfo.GetReturnValue().Set(maybeNew.ToLocalChecked());\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!info.Length())\n\t{\n\t\treturn Nan::ThrowTypeError(\"Expected an iterable of patterns as the 1st argument.\");\n\t}\n\n\tSetFlags flags;\n\tbool haveFlags = false;\n\tbool flagsFromArg = false;\n\n\tv8::Local<v8::Value> flagsArg;\n\tv8::Local<v8::Value> optionsArg;\n\tif (info.Length() > 1)\n\t{\n\t\tif (info[1]->IsObject() && !info[1]->IsString() && !node::Buffer::HasInstance(info[1]))\n\t\t{\n\t\t\toptionsArg = info[1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tflagsArg = info[1];\n\t\t\tif (info.Length() > 2)\n\t\t\t{\n\t\t\t\toptionsArg = info[2];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!flagsArg.IsEmpty())\n\t{\n\t\tif (!parseFlags(flagsArg, flags))\n\t\t{\n\t\t\treturn Nan::ThrowTypeError(\"Invalid flags for RE2.Set.\");\n\t\t}\n\t\thaveFlags = true;\n\t\tflagsFromArg = true;\n\t}\n\n\tre2::RE2::Anchor anchor = re2::RE2::UNANCHORED;\n\tif (!optionsArg.IsEmpty())\n\t{\n\t\tif (!parseAnchor(optionsArg, anchor))\n\t\t{\n\t\t\treturn Nan::ThrowTypeError(\"Invalid anchor option for RE2.Set.\");\n\t\t}\n\t}\n\n\tstd::vector<v8::Local<v8::Value>> patterns;\n\tif (!collectIterable(info[0], patterns))\n\t{\n\t\treturn Nan::ThrowTypeError(\"Expected an iterable of patterns as the 1st argument.\");\n\t}\n\n\tauto mergeFlags = [&](const SetFlags &candidate) {\n\t\tif (flagsFromArg)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tif (!haveFlags)\n\t\t{\n\t\t\tflags = candidate;\n\t\t\thaveFlags = true;\n\t\t\treturn true;\n\t\t}\n\t\treturn sameEffectiveOptions(flags, candidate);\n\t};\n\n\tfor (auto &value : patterns)\n\t{\n\t\tSetFlags patternFlags;\n\t\tbool hasFlagsForPattern = false;\n\n\t\tif (value->IsRegExp())\n\t\t{\n\t\t\tconst auto *re = v8::RegExp::Cast(*value);\n\t\t\tv8::RegExp::Flags reFlags = re->GetFlags();\n\t\t\tpatternFlags.global = bool(reFlags & v8::RegExp::kGlobal);\n\t\t\tpatternFlags.ignoreCase = bool(reFlags & v8::RegExp::kIgnoreCase);\n\t\t\tpatternFlags.multiline = bool(reFlags & v8::RegExp::kMultiline);\n\t\t\tpatternFlags.dotAll = bool(reFlags & v8::RegExp::kDotAll);\n\t\t\tpatternFlags.unicode = bool(reFlags & v8::RegExp::kUnicode);\n\t\t\tpatternFlags.sticky = bool(reFlags & v8::RegExp::kSticky);\n\t\t\tpatternFlags.hasIndices = bool(reFlags & v8::RegExp::kHasIndices);\n\t\t\thasFlagsForPattern = true;\n\t\t}\n\t\telse if (value->IsObject())\n\t\t{\n\t\t\tauto maybeObj = value->ToObject(context);\n\t\t\tif (!maybeObj.IsEmpty())\n\t\t\t{\n\t\t\t\tauto obj = maybeObj.ToLocalChecked();\n\t\t\t\tif (WrappedRE2::HasInstance(obj))\n\t\t\t\t{\n\t\t\t\t\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(obj);\n\t\t\t\t\tpatternFlags.global = re2->global;\n\t\t\t\t\tpatternFlags.ignoreCase = re2->ignoreCase;\n\t\t\t\t\tpatternFlags.multiline = re2->multiline;\n\t\t\t\t\tpatternFlags.dotAll = re2->dotAll;\n\t\t\t\t\tpatternFlags.unicode = true;\n\t\t\t\t\tpatternFlags.sticky = re2->sticky;\n\t\t\t\t\tpatternFlags.hasIndices = re2->hasIndices;\n\t\t\t\t\thasFlagsForPattern = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasFlagsForPattern && !mergeFlags(patternFlags))\n\t\t{\n\t\t\treturn Nan::ThrowTypeError(\"All patterns in RE2.Set must use the same flags.\");\n\t\t}\n\t}\n\n\tif (!flags.unicode)\n\t{\n\t\tswitch (WrappedRE2::unicodeWarningLevel)\n\t\t{\n\tcase WrappedRE2::THROW:\n\t\t\treturn Nan::ThrowSyntaxError(setDeprecationMessage);\n\t\tcase WrappedRE2::WARN:\n\t\t\tprintDeprecationWarning(setDeprecationMessage);\n\t\t\tbreak;\n\t\tcase WrappedRE2::WARN_ONCE:\n\t\t\tif (!WrappedRE2::alreadyWarnedAboutUnicode)\n\t\t\t{\n\t\t\t\tprintDeprecationWarning(setDeprecationMessage);\n\t\t\t\tWrappedRE2::alreadyWarnedAboutUnicode = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tre2::RE2::Options options;\n\toptions.set_case_sensitive(!flags.ignoreCase);\n\toptions.set_one_line(!flags.multiline);\n\toptions.set_dot_nl(flags.dotAll);\n\toptions.set_log_errors(false);\n\n\tstd::unique_ptr<WrappedRE2Set> set(new WrappedRE2Set(options, anchor, flagsToString(flags)));\n\tstd::vector<char> buffer;\n\n\tfor (auto &value : patterns)\n\t{\n\t\tconst char *data = nullptr;\n\t\tsize_t size = 0;\n\t\tstd::string source;\n\n\t\tif (node::Buffer::HasInstance(value))\n\t\t{\n\t\t\tsize = node::Buffer::Length(value);\n\t\t\tdata = node::Buffer::Data(value);\n\t\t\tsource = escapeRegExp(data, size);\n\t\t}\n\t\telse if (value->IsRegExp())\n\t\t{\n\t\t\tconst auto *re = v8::RegExp::Cast(*value);\n\t\t\tauto t = re->GetSource()->ToString(context);\n\t\t\tif (t.IsEmpty())\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tauto s = t.ToLocalChecked();\n\t\t\tsize = s->Utf8Length(isolate);\n\t\t\tbuffer.resize(size + 1);\n\t\t\ts->WriteUtf8(isolate, &buffer[0], buffer.size());\n\t\t\tbuffer[size] = '\\0';\n\t\t\tdata = &buffer[0];\n\t\t\tsource = escapeRegExp(data, size);\n\t\t}\n\t\telse if (value->IsString())\n\t\t{\n\t\t\tauto t = value->ToString(context);\n\t\t\tif (t.IsEmpty())\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tauto s = t.ToLocalChecked();\n\t\t\tsize = s->Utf8Length(isolate);\n\t\t\tbuffer.resize(size + 1);\n\t\t\ts->WriteUtf8(isolate, &buffer[0], buffer.size());\n\t\t\tbuffer[size] = '\\0';\n\t\t\tdata = &buffer[0];\n\t\t\tsource = escapeRegExp(data, size);\n\t\t}\n\t\telse if (value->IsObject())\n\t\t{\n\t\t\tauto maybeObj = value->ToObject(context);\n\t\t\tif (maybeObj.IsEmpty())\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tauto obj = maybeObj.ToLocalChecked();\n\t\t\tif (!WrappedRE2::HasInstance(obj))\n\t\t\t{\n\t\t\t\treturn Nan::ThrowTypeError(\"Expected a string, Buffer, RegExp, or RE2 instance in the pattern list.\");\n\t\t\t}\n\n\t\t\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(obj);\n\t\t\tsource = re2->source;\n\t\t\tdata = source.data();\n\t\t\tsize = source.size();\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn Nan::ThrowTypeError(\"Expected a string, Buffer, RegExp, or RE2 instance in the pattern list.\");\n\t\t}\n\n\t\tif (translateRegExp(data, size, flags.multiline, buffer))\n\t\t{\n\t\t\tdata = &buffer[0];\n\t\t\tsize = buffer.size() - 1;\n\t\t}\n\n\t\tstd::string error;\n\t\tif (set->set.Add(re2::StringPiece(data, size), &error) < 0)\n\t\t{\n\t\t\tif (error.empty())\n\t\t\t{\n\t\t\t\terror = \"Invalid pattern in RE2.Set.\";\n\t\t\t}\n\t\t\treturn Nan::ThrowSyntaxError(error.c_str());\n\t\t}\n\t\tset->sources.push_back(source);\n\t}\n\n\tif (!set->set.Compile())\n\t{\n\t\treturn Nan::ThrowError(\"RE2.Set could not be compiled.\");\n\t}\n\n\tset->combinedSource = makeCombinedSource(set->sources);\n\tset->Wrap(info.This());\n\tset.release();\n\n\tinfo.GetReturnValue().Set(info.This());\n}\n\nNAN_METHOD(WrappedRE2Set::Test)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(false);\n\t\treturn;\n\t}\n\n\tStrVal str;\n\tv8::Local<v8::Object> keepAlive;\n\tif (!fillInput(info[0], str, keepAlive))\n\t{\n\t\treturn;\n\t}\n\n\tre2::RE2::Set::ErrorInfo errorInfo{re2::RE2::Set::kNoError};\n\tbool matched = re2set->set.Match(str, nullptr, &errorInfo);\n\tif (!matched && errorInfo.kind != re2::RE2::Set::kNoError)\n\t{\n\t\tconst char *message = \"RE2.Set matching failed.\";\n\t\tswitch (errorInfo.kind)\n\t\t{\n\t\tcase re2::RE2::Set::kOutOfMemory:\n\t\t\tmessage = \"RE2.Set matching failed: out of memory.\";\n\t\t\tbreak;\n\t\tcase re2::RE2::Set::kInconsistent:\n\t\t\tmessage = \"RE2.Set matching failed: inconsistent result.\";\n\t\t\tbreak;\n\t\tcase re2::RE2::Set::kNotCompiled:\n\t\t\tmessage = \"RE2.Set matching failed: set is not compiled.\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\treturn Nan::ThrowError(message);\n\t}\n\n\tinfo.GetReturnValue().Set(matched);\n}\n\nNAN_METHOD(WrappedRE2Set::Match)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New<v8::Array>(0));\n\t\treturn;\n\t}\n\n\tStrVal str;\n\tv8::Local<v8::Object> keepAlive;\n\tif (!fillInput(info[0], str, keepAlive))\n\t{\n\t\treturn;\n\t}\n\n\tstd::vector<int> matches;\n\tre2::RE2::Set::ErrorInfo errorInfo{re2::RE2::Set::kNoError};\n\tbool matched = re2set->set.Match(str, &matches, &errorInfo);\n\tif (!matched && errorInfo.kind != re2::RE2::Set::kNoError)\n\t{\n\t\tconst char *message = \"RE2.Set matching failed.\";\n\t\tswitch (errorInfo.kind)\n\t\t{\n\t\tcase re2::RE2::Set::kOutOfMemory:\n\t\t\tmessage = \"RE2.Set matching failed: out of memory.\";\n\t\t\tbreak;\n\t\tcase re2::RE2::Set::kInconsistent:\n\t\t\tmessage = \"RE2.Set matching failed: inconsistent result.\";\n\t\t\tbreak;\n\t\tcase re2::RE2::Set::kNotCompiled:\n\t\t\tmessage = \"RE2.Set matching failed: set is not compiled.\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\treturn Nan::ThrowError(message);\n\t}\n\n\tstd::sort(matches.begin(), matches.end());\n\tauto result = Nan::New<v8::Array>(matches.size());\n\tfor (size_t i = 0, n = matches.size(); i < n; ++i)\n\t{\n\t\tNan::Set(result, i, Nan::New(matches[i]));\n\t}\n\n\tinfo.GetReturnValue().Set(result);\n}\n\nNAN_METHOD(WrappedRE2Set::ToString)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().SetEmptyString();\n\t\treturn;\n\t}\n\n\tstd::string result = \"/\";\n\tresult += re2set->combinedSource;\n\tresult += \"/\";\n\tresult += re2set->flags;\n\tinfo.GetReturnValue().Set(Nan::New(result).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2Set::GetFlags)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"u\").ToLocalChecked());\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(Nan::New(re2set->flags).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2Set::GetSources)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New<v8::Array>(0));\n\t\treturn;\n\t}\n\tauto result = Nan::New<v8::Array>(re2set->sources.size());\n\tfor (size_t i = 0, n = re2set->sources.size(); i < n; ++i)\n\t{\n\t\tNan::Set(result, i, Nan::New(re2set->sources[i]).ToLocalChecked());\n\t}\n\tinfo.GetReturnValue().Set(result);\n}\n\nNAN_GETTER(WrappedRE2Set::GetSource)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"(?:)\").ToLocalChecked());\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(Nan::New(re2set->combinedSource).ToLocalChecked());\n}\n\nNAN_GETTER(WrappedRE2Set::GetSize)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(0);\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(static_cast<uint32_t>(re2set->sources.size()));\n}\n\nNAN_GETTER(WrappedRE2Set::GetAnchor)\n{\n\tauto re2set = Nan::ObjectWrap::Unwrap<WrappedRE2Set>(info.This());\n\tif (!re2set)\n\t{\n\t\tinfo.GetReturnValue().Set(Nan::New(\"unanchored\").ToLocalChecked());\n\t\treturn;\n\t}\n\tinfo.GetReturnValue().Set(Nan::New(anchorToString(re2set->anchor)).ToLocalChecked());\n}\n\nv8::Local<v8::Function> WrappedRE2Set::Init()\n{\n\tNan::EscapableHandleScope scope;\n\n\tauto tpl = Nan::New<v8::FunctionTemplate>(New);\n\ttpl->SetClassName(Nan::New(\"RE2Set\").ToLocalChecked());\n\tauto instanceTemplate = tpl->InstanceTemplate();\n\tinstanceTemplate->SetInternalFieldCount(1);\n\n\tNan::SetPrototypeMethod(tpl, \"test\", Test);\n\tNan::SetPrototypeMethod(tpl, \"match\", Match);\n\tNan::SetPrototypeMethod(tpl, \"toString\", ToString);\n\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"flags\").ToLocalChecked(), GetFlags);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"sources\").ToLocalChecked(), GetSources);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"source\").ToLocalChecked(), GetSource);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"size\").ToLocalChecked(), GetSize);\n\tNan::SetAccessor(instanceTemplate, Nan::New(\"anchor\").ToLocalChecked(), GetAnchor);\n\n\tauto isolate = v8::Isolate::GetCurrent();\n\tauto data = getAddonData(isolate);\n\tif (data)\n\t{\n\t\tdata->re2SetTpl.Reset(tpl);\n\t}\n\treturn scope.Escape(Nan::GetFunction(tpl).ToLocalChecked());\n}\n"
  },
  {
    "path": "lib/split.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <algorithm>\n#include <limits>\n#include <vector>\n\nNAN_METHOD(WrappedRE2::Split)\n{\n\n\tauto result = Nan::New<v8::Array>();\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tNan::Set(result, 0, info[0]);\n\t\tinfo.GetReturnValue().Set(result);\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& str = prep;\n\tif (str.isBad) return; // throws an exception\n\n\tsize_t limit = std::numeric_limits<size_t>::max();\n\tif (info.Length() > 1 && info[1]->IsNumber())\n\t{\n\t\tsize_t lim = info[1]->NumberValue(Nan::GetCurrentContext()).FromMaybe(0);\n\t\tif (lim > 0)\n\t\t{\n\t\t\tlimit = lim;\n\t\t}\n\t}\n\n\t// actual work\n\n\tstd::vector<re2::StringPiece> groups(re2->regexp.NumberOfCapturingGroups() + 1), pieces;\n\tconst auto &match = groups[0];\n\tsize_t byteIndex = 0;\n\n\twhile (byteIndex < str.size && re2->regexp.Match(str, byteIndex, str.size, RE2::UNANCHORED, &groups[0], groups.size()))\n\t{\n\t\tif (match.size())\n\t\t{\n\t\t\tpieces.push_back(re2::StringPiece(str.data + byteIndex, match.data() - str.data - byteIndex));\n\t\t\tbyteIndex = match.data() - str.data + match.size();\n\t\t\tpieces.insert(pieces.end(), groups.begin() + 1, groups.end());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsize_t sym_size = getUtf8CharSize(str.data[byteIndex]);\n\t\t\tpieces.push_back(re2::StringPiece(str.data + byteIndex, sym_size));\n\t\t\tbyteIndex += sym_size;\n\t\t}\n\t\tif (pieces.size() >= limit)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (pieces.size() < limit && (byteIndex < str.size || (byteIndex == str.size && match.size())))\n\t{\n\t\tpieces.push_back(re2::StringPiece(str.data + byteIndex, str.size - byteIndex));\n\t}\n\n\tif (pieces.empty())\n\t{\n\t\tNan::Set(result, 0, info[0]);\n\t\tinfo.GetReturnValue().Set(result);\n\t\treturn;\n\t}\n\n\t// form a result\n\n\tif (str.isBuffer)\n\t{\n\t\tfor (size_t i = 0, n = std::min(pieces.size(), limit); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = pieces[i];\n\t\t\tif (item.data())\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::CopyBuffer(item.data(), item.size()).ToLocalChecked());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0, n = std::min(pieces.size(), limit); i < n; ++i)\n\t\t{\n\t\t\tconst auto &item = pieces[i];\n\t\t\tif (item.data())\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::New(item.data(), item.size()).ToLocalChecked());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNan::Set(result, i, Nan::Undefined());\n\t\t\t}\n\t\t}\n\t}\n\n\tinfo.GetReturnValue().Set(result);\n}\n"
  },
  {
    "path": "lib/test.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <vector>\n\nNAN_METHOD(WrappedRE2::Test)\n{\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().Set(false);\n\t\treturn;\n\t}\n\n\tPrepareLastString prep(re2, info[0]);\n\tStrVal& str = prep;\n\tif (str.isBad) return; // throws an exception\n\n\tif (!re2->global && !re2->sticky)\n\t{\n\t\tinfo.GetReturnValue().Set(re2->regexp.Match(str, 0, str.size, re2::RE2::UNANCHORED, NULL, 0));\n\t\treturn;\n\t}\n\n\tif (!str.isValidIndex)\n\t{\n\t\tre2->lastIndex = 0;\n\t\tinfo.GetReturnValue().Set(false);\n\t\treturn;\n\t}\n\n\t// actual work\n\n\tre2::StringPiece match;\n\tif (re2->regexp.Match(str, str.byteIndex, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &match, 1))\n\t{\n\t\tre2->lastIndex +=\n\t\t\tstr.isBuffer ? match.data() - str.data + match.size() - str.byteIndex : getUtf16Length(str.data + str.byteIndex, match.data() + match.size());\n\t\tinfo.GetReturnValue().Set(true);\n\t\treturn;\n\t}\n\tre2->lastIndex = 0;\n\tinfo.GetReturnValue().Set(false);\n}\n"
  },
  {
    "path": "lib/to_string.cc",
    "content": "#include \"./wrapped_re2.h\"\n\n#include <string>\n\nNAN_METHOD(WrappedRE2::ToString)\n{\n\n\t// unpack arguments\n\n\tauto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());\n\tif (!re2)\n\t{\n\t\tinfo.GetReturnValue().SetEmptyString();\n\t\treturn;\n\t}\n\n\t// actual work\n\n\tstd::string buffer(\"/\");\n\tbuffer += re2->source;\n\tbuffer += \"/\";\n\n\tif (re2->hasIndices)\n\t{\n\t\tbuffer += \"d\";\n\t}\n\tif (re2->global)\n\t{\n\t\tbuffer += \"g\";\n\t}\n\tif (re2->ignoreCase)\n\t{\n\t\tbuffer += \"i\";\n\t}\n\tif (re2->multiline)\n\t{\n\t\tbuffer += \"m\";\n\t}\n\tif (re2->dotAll)\n\t{\n\t\tbuffer += \"s\";\n\t}\n\tbuffer += \"u\";\n\tif (re2->sticky)\n\t{\n\t\tbuffer += \"y\";\n\t}\n\n\tinfo.GetReturnValue().Set(Nan::New(buffer).ToLocalChecked());\n}\n"
  },
  {
    "path": "lib/util.cc",
    "content": "#include \"./util.h\"\n\nvoid consoleCall(const v8::Local<v8::String> &methodName, v8::Local<v8::Value> text)\n{\n\tauto context = Nan::GetCurrentContext();\n\n\tauto maybeConsole = bind<v8::Object>(\n\t\tNan::Get(context->Global(), Nan::New(\"console\").ToLocalChecked()),\n\t\t[context](v8::Local<v8::Value> console) { return console->ToObject(context); });\n\tif (maybeConsole.IsEmpty())\n\t\treturn;\n\n\tauto console = maybeConsole.ToLocalChecked();\n\n\tauto maybeMethod = bind<v8::Object>(\n\t\tNan::Get(console, methodName),\n\t\t[context](v8::Local<v8::Value> method) { return method->ToObject(context); });\n\tif (maybeMethod.IsEmpty())\n\t\treturn;\n\n\tauto method = maybeMethod.ToLocalChecked();\n\tif (!method->IsFunction())\n\t\treturn;\n\n\tNan::CallAsFunction(method, console, 1, &text);\n}\n\nvoid printDeprecationWarning(const char *warning)\n{\n\tstd::string prefixedWarning = \"DeprecationWarning: \";\n\tprefixedWarning += warning;\n\tconsoleCall(Nan::New(\"error\").ToLocalChecked(), Nan::New(prefixedWarning).ToLocalChecked());\n}\n\nv8::Local<v8::String> callToString(const v8::Local<v8::Object> &object)\n{\n\tauto context = Nan::GetCurrentContext();\n\n\tauto maybeMethod = bind<v8::Object>(\n\t\tNan::Get(object, Nan::New(\"toString\").ToLocalChecked()),\n\t\t[context](v8::Local<v8::Value> method) { return method->ToObject(context); });\n\tif (maybeMethod.IsEmpty())\n\t\treturn Nan::New(\"No toString() is found\").ToLocalChecked();\n\n\tauto method = maybeMethod.ToLocalChecked();\n\tif (!method->IsFunction())\n\t\treturn Nan::New(\"No toString() is found\").ToLocalChecked();\n\n\tauto maybeResult = Nan::CallAsFunction(method, object, 0, nullptr);\n\tif (maybeResult.IsEmpty())\n\t{\n\t\treturn Nan::New(\"nothing was returned\").ToLocalChecked();\n\t}\n\n\tauto result = maybeResult.ToLocalChecked();\n\n\tif (result->IsObject())\n\t{\n\t\treturn callToString(result->ToObject(context).ToLocalChecked());\n\t}\n\n\tNan::Utf8String val(result->ToString(context).ToLocalChecked());\n\treturn Nan::New(std::string(*val, val.length())).ToLocalChecked();\n}\n"
  },
  {
    "path": "lib/util.h",
    "content": "#pragma once\n\n#include \"./wrapped_re2.h\"\n\ntemplate <typename R, typename P, typename L>\ninline v8::MaybeLocal<R> bind(v8::MaybeLocal<P> param, L lambda)\n{\n\treturn param.IsEmpty() ? v8::MaybeLocal<R>() : lambda(param.ToLocalChecked());\n}\n\nvoid consoleCall(const v8::Local<v8::String> &methodName, v8::Local<v8::Value> text);\nvoid printDeprecationWarning(const char *warning);\n\nv8::Local<v8::String> callToString(const v8::Local<v8::Object> &object);\n"
  },
  {
    "path": "lib/wrapped_re2.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <string>\n#include <nan.h>\n#include <re2/re2.h>\n\n#include \"./isolate_data.h\"\n\nstruct StrVal\n{\n\tchar *data;\n\tsize_t size, length;\n\tsize_t index, byteIndex;\n\tbool isBuffer, isValidIndex, isBad;\n\n\tStrVal() : data(NULL), size(0), length(0), index(0), byteIndex(0), isBuffer(false), isValidIndex(false), isBad(false) {}\n\n\toperator re2::StringPiece() const { return re2::StringPiece(data, size); }\n\n\tvoid setIndex(size_t newIndex = 0);\n\tvoid reset(const v8::Local<v8::Value> &arg, size_t size, size_t length, size_t newIndex = 0, bool buffer = false);\n\n\tvoid clear()\n\t{\n\t\tisBad = isBuffer = isValidIndex = false;\n\t\tsize = length = index = byteIndex = 0;\n\t\tdata = nullptr;\n\t}\n};\n\nclass WrappedRE2 : public Nan::ObjectWrap\n{\nprivate:\n\tWrappedRE2(\n\t\tconst re2::StringPiece &pattern,\n\t\tconst re2::RE2::Options &options,\n\t\tconst std::string &src,\n\t\tconst bool &g,\n\t\tconst bool &i,\n\t\tconst bool &m,\n\t\tconst bool &s,\n\t\tconst bool &y,\n\t\tconst bool &d) : regexp(pattern, options),\n\t\t\t\t\t\t source(src),\n\t\t\t\t\t\t global(g),\n\t\t\t\t\t\t ignoreCase(i),\n\t\t\t\t\t\t multiline(m),\n\t\t\t\t\t\t dotAll(s),\n\t\t\t\t\t\t sticky(y),\n\t\t\t\t\t\t hasIndices(d),\n\t\t\t\t\t\t lastIndex(0) {}\n\n\tstatic NAN_METHOD(New);\n\tstatic NAN_METHOD(ToString);\n\n\tstatic NAN_GETTER(GetSource);\n\tstatic NAN_GETTER(GetFlags);\n\tstatic NAN_GETTER(GetGlobal);\n\tstatic NAN_GETTER(GetIgnoreCase);\n\tstatic NAN_GETTER(GetMultiline);\n\tstatic NAN_GETTER(GetDotAll);\n\tstatic NAN_GETTER(GetUnicode);\n\tstatic NAN_GETTER(GetSticky);\n\tstatic NAN_GETTER(GetHasIndices);\n\tstatic NAN_GETTER(GetLastIndex);\n\tstatic NAN_SETTER(SetLastIndex);\n\tstatic NAN_GETTER(GetInternalSource);\n\n\t// RegExp methods\n\tstatic NAN_METHOD(Exec);\n\tstatic NAN_METHOD(Test);\n\n\t// String methods\n\tstatic NAN_METHOD(Match);\n\tstatic NAN_METHOD(Replace);\n\tstatic NAN_METHOD(Search);\n\tstatic NAN_METHOD(Split);\n\n\t// strict Unicode warning support\n\tstatic NAN_GETTER(GetUnicodeWarningLevel);\n\tstatic NAN_SETTER(SetUnicodeWarningLevel);\n\npublic:\n\t~WrappedRE2();\n\n\tstatic v8::Local<v8::Function> Init();\n\n\tstatic inline bool HasInstance(v8::Local<v8::Object> object)\n\t{\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto data = getAddonData(isolate);\n\t\tif (!data || data->re2Tpl.IsEmpty()) return false;\n\t\treturn data->re2Tpl.Get(isolate)->HasInstance(object);\n\t}\n\n\tenum UnicodeWarningLevels\n\t{\n\t\tNOTHING,\n\t\tWARN_ONCE,\n\t\tWARN,\n\t\tTHROW\n\t};\n\tstatic std::atomic<UnicodeWarningLevels> unicodeWarningLevel;\n\tstatic std::atomic<bool> alreadyWarnedAboutUnicode;\n\n\tre2::RE2 regexp;\n\tstd::string source;\n\tbool global;\n\tbool ignoreCase;\n\tbool multiline;\n\tbool dotAll;\n\tbool sticky;\n\tbool hasIndices;\n\tsize_t lastIndex;\n\n\tfriend struct PrepareLastString;\n\nprivate:\n\tNan::Persistent<v8::Value> lastString; // weak pointer\n\tNan::Persistent<v8::Object> lastCache; // weak pointer\n\tStrVal lastStringValue;\n\n\tvoid dropCache();\n\tconst StrVal &prepareArgument(const v8::Local<v8::Value> &arg, bool ignoreLastIndex = false);\n\tvoid doneWithLastString();\n};\n\nstruct PrepareLastString\n{\n\tPrepareLastString(WrappedRE2 *re2, const v8::Local<v8::Value> &arg, bool ignoreLastIndex = false) : re2(re2) {\n\t\tre2->prepareArgument(arg, ignoreLastIndex);\n\t}\n\n\t~PrepareLastString() {\n\t\tre2->doneWithLastString();\n\t}\n\n\toperator const StrVal&() const {\n\t\treturn re2->lastStringValue;\n\t}\n\n\toperator StrVal&() {\n\t\treturn re2->lastStringValue;\n\t}\n\n\tWrappedRE2 *re2;\n};\n\n// utilities\n\ninline size_t getUtf8Length(const uint16_t *from, const uint16_t *to)\n{\n\tsize_t n = 0;\n\twhile (from != to)\n\t{\n\t\tuint16_t ch = *from++;\n\t\tif (ch <= 0x7F)\n\t\t\t++n;\n\t\telse if (ch <= 0x7FF)\n\t\t\tn += 2;\n\t\telse if (0xD800 <= ch && ch <= 0xDFFF)\n\t\t{\n\t\t\tn += 4;\n\t\t\tif (from == to)\n\t\t\t\tbreak;\n\t\t\t++from;\n\t\t}\n\t\telse if (ch < 0xFFFF)\n\t\t\tn += 3;\n\t\telse\n\t\t\tn += 4;\n\t}\n\treturn n;\n}\n\ninline size_t getUtf16Length(const char *from, const char *to)\n{\n\tsize_t n = 0;\n\twhile (from != to)\n\t{\n\t\tunsigned ch = *from & 0xFF;\n\t\tif (ch < 0xF0)\n\t\t{\n\t\t\tif (ch < 0x80)\n\t\t\t{\n\t\t\t\t++from;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (ch < 0xE0)\n\t\t\t\t{\n\t\t\t\t\tfrom += 2;\n\t\t\t\t\tif (from == to + 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t++n;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfrom += 3;\n\t\t\t\t\tif (from > to && from < to + 3)\n\t\t\t\t\t{\n\t\t\t\t\t\t++n;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t++n;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfrom += 4;\n\t\t\tn += 2;\n\t\t\tif (from > to && from < to + 4)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn n;\n}\n\ninline size_t getUtf8CharSize(char ch)\n{\n\treturn ((0xE5000000 >> ((ch >> 3) & 0x1E)) & 3) + 1;\n}\n\ninline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n)\n{\n\tfor (; n > 0; --n)\n\t{\n\t\tsize_t s = getUtf8CharSize(data[from]);\n\t\tfrom += s;\n\t\tif (s == 4 && n >= 2)\n\t\t\t--n; // this utf8 character will take two utf16 characters\n\t\t\t\t // the decrement above is protected to avoid an overflow of an unsigned integer\n\t}\n\treturn from;\n}\n"
  },
  {
    "path": "lib/wrapped_re2_set.h",
    "content": "#pragma once\n\n#include <nan.h>\n#include <re2/re2.h>\n#include <re2/set.h>\n\n#include \"./isolate_data.h\"\n\n#include <string>\n#include <vector>\n\nclass WrappedRE2Set : public Nan::ObjectWrap\n{\npublic:\n\tstatic v8::Local<v8::Function> Init();\n\tstatic inline bool HasInstance(v8::Local<v8::Object> object)\n\t{\n\t\tauto isolate = v8::Isolate::GetCurrent();\n\t\tauto data = getAddonData(isolate);\n\t\tif (!data || data->re2SetTpl.IsEmpty()) return false;\n\t\treturn data->re2SetTpl.Get(isolate)->HasInstance(object);\n\t}\n\nprivate:\n\tWrappedRE2Set(const re2::RE2::Options &options, re2::RE2::Anchor anchor, const std::string &flags) : set(options, anchor), flags(flags), anchor(anchor) {}\n\n\tstatic NAN_METHOD(New);\n\tstatic NAN_METHOD(Test);\n\tstatic NAN_METHOD(Match);\n\tstatic NAN_METHOD(ToString);\n\n\tstatic NAN_GETTER(GetFlags);\n\tstatic NAN_GETTER(GetSources);\n\tstatic NAN_GETTER(GetSource);\n\tstatic NAN_GETTER(GetSize);\n\tstatic NAN_GETTER(GetAnchor);\n\n\tre2::RE2::Set set;\n\tstd::vector<std::string> sources;\n\tstd::string combinedSource;\n\tstd::string flags;\n\tre2::RE2::Anchor anchor;\n};\n\n"
  },
  {
    "path": "llms-full.txt",
    "content": "# node-re2\n\n> Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS (Regular Expression Denial of Service). Works with strings and Buffers. C++ native addon built with node-gyp and nan.\n\n- Drop-in replacement for RegExp with linear-time matching guarantee\n- Prevents ReDoS by disallowing backreferences and lookahead assertions\n- Full Unicode mode (always on)\n- Buffer support for high-performance binary/UTF-8 processing\n- Named capture groups\n- Symbol-based methods (Symbol.match, Symbol.search, Symbol.replace, Symbol.split, Symbol.matchAll)\n- RE2.Set for multi-pattern matching\n- Prebuilt binaries for Linux, macOS, Windows (x64 + arm64)\n- TypeScript declarations included\n\n## Install\n\n```bash\nnpm install re2\n```\n\nPrebuilt native binaries are downloaded automatically. Falls back to building from source via node-gyp if no prebuilt is available.\n\n## Quick start\n\n```js\nconst RE2 = require('re2');\n\n// Create and use like RegExp\nconst re = new RE2('a(b*)', 'i');\nconst result = re.exec('aBbC');\nconsole.log(result[0]); // \"aBb\"\nconsole.log(result[1]); // \"Bb\"\n\n// Works with ES6 string methods\n'hello world'.match(new RE2('\\\\w+', 'g')); // ['hello', 'world']\n'hello world'.replace(new RE2('world'), 'RE2'); // 'hello RE2'\n```\n\n## Importing\n\n```js\n// CommonJS\nconst RE2 = require('re2');\n\n// ESM\nimport { RE2 } from 're2';\n```\n\n## Construction\n\n`new RE2(pattern[, flags])` or `RE2(pattern[, flags])` (factory mode).\n\nPattern can be:\n- **String**: `new RE2('\\\\d+')`\n- **String with flags**: `new RE2('\\\\d+', 'gi')`\n- **RegExp**: `new RE2(/ab*/ig)` — copies pattern and flags.\n- **RE2**: `new RE2(existingRE2)` — copies pattern and flags.\n- **Buffer**: `new RE2(Buffer.from('pattern'))` — pattern from UTF-8 buffer.\n\nSupported flags:\n- `g` — global (find all matches)\n- `i` — ignoreCase\n- `m` — multiline (`^`/`$` match line boundaries)\n- `s` — dotAll (`.` matches `\\n`)\n- `u` — unicode (always on, added implicitly)\n- `y` — sticky (match at lastIndex only)\n- `d` — hasIndices (include index info for capture groups)\n\nInvalid patterns throw `SyntaxError`. Patterns with backreferences or lookahead throw `SyntaxError`.\n\n## Properties\n\n### Instance properties\n\n- `re.source` (string) — the pattern string, escaped for use in `new RE2(re.source)` or `new RegExp(re.source)`.\n- `re.flags` (string) — the flags string (e.g., `'giu'`).\n- `re.lastIndex` (number) — the index at which to start the next match (used with `g` or `y` flags).\n- `re.global` (boolean) — whether the `g` flag is set.\n- `re.ignoreCase` (boolean) — whether the `i` flag is set.\n- `re.multiline` (boolean) — whether the `m` flag is set.\n- `re.dotAll` (boolean) — whether the `s` flag is set.\n- `re.unicode` (boolean) — always `true` (RE2 always operates in Unicode mode).\n- `re.sticky` (boolean) — whether the `y` flag is set.\n- `re.hasIndices` (boolean) — whether the `d` flag is set.\n- `re.internalSource` (string) — the RE2-translated pattern (for debugging; may differ from `source`).\n\n### Static properties\n\n- `RE2.unicodeWarningLevel` (string) — controls behavior when a non-Unicode regexp is created:\n  - `'nothing'` (default) — silently add `u` flag.\n  - `'warnOnce'` — warn once, then silently add `u`. Assigning resets the one-time flag.\n  - `'warn'` — warn every time.\n  - `'throw'` — throw `SyntaxError` every time.\n\n## RegExp methods\n\n### re.exec(str)\n\nExecutes a search for a match. Returns a result array or `null`.\n\n```js\nconst re = new RE2('a(b+)', 'g');\nconst result = re.exec('abbc abbc');\n// result[0] === 'abb'\n// result[1] === 'bb'\n// result.index === 0\n// result.input === 'abbc abbc'\n// re.lastIndex === 3\n```\n\nWith `d` flag (hasIndices), result has `.indices` property with `[start, end]` pairs for each group.\n\nWith `g` or `y` flag, advances `lastIndex`. Call repeatedly to iterate matches.\n\n### re.test(str)\n\nReturns `true` if the pattern matches, `false` otherwise.\n\n```js\nnew RE2('\\\\d+').test('abc123'); // true\nnew RE2('\\\\d+').test('abcdef'); // false\n```\n\nWith `g` or `y` flag, advances `lastIndex`.\n\n### re.toString()\n\nReturns `'/pattern/flags'` string representation.\n\n```js\nnew RE2('abc', 'gi').toString(); // '/abc/giu'\n```\n\n## String methods (via Symbol)\n\nRE2 instances implement well-known symbols, so they work directly with ES6 string methods:\n\n### str.match(re) / re[Symbol.match](str)\n\n```js\n'test 123 test 456'.match(new RE2('\\\\d+', 'g')); // ['123', '456']\n'test 123'.match(new RE2('(\\\\d+)')); // ['123', '123', index: 5, input: 'test 123']\n```\n\n### str.matchAll(re) / re[Symbol.matchAll](str)\n\nReturns an iterator of all matches (requires `g` flag).\n\n```js\nconst re = new RE2('\\\\d+', 'g');\nfor (const m of '1a2b3c'.matchAll(re)) {\n  console.log(m[0]); // '1', '2', '3'\n}\n```\n\n### str.search(re) / re[Symbol.search](str)\n\nReturns the index of the first match, or `-1`.\n\n```js\n'hello world'.search(new RE2('world')); // 6\n```\n\n### str.replace(re, replacement) / re[Symbol.replace](str, replacement)\n\nReturns a new string with matches replaced.\n\n```js\n'aabba'.replace(new RE2('b', 'g'), 'c'); // 'aacca'\n```\n\nReplacement string supports:\n- `$1`, `$2`, ... — numbered capture groups.\n- `$<name>` — named capture groups.\n- `$&` — the matched substring.\n- `` $` `` — portion before the match.\n- `$'` — portion after the match.\n- `$$` — literal `$`.\n\nReplacement function receives `(match, ...groups, offset, input)`:\n\n```js\n'abc'.replace(new RE2('(b)'), (match, g1, offset) => `[${g1}@${offset}]`);\n// 'a[b@1]c'\n```\n\n### str.split(re[, limit]) / re[Symbol.split](str[, limit])\n\nSplits string by pattern.\n\n```js\n'a1b2c3'.split(new RE2('\\\\d')); // ['a', 'b', 'c', '']\n'a1b2c3'.split(new RE2('\\\\d'), 2); // ['a', 'b']\n```\n\n## String methods (direct)\n\nThese are convenience methods on the RE2 instance with swapped argument order:\n\n- `re.match(str)` — equivalent to `str.match(re)`.\n- `re.search(str)` — equivalent to `str.search(re)`.\n- `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`.\n- `re.split(str[, limit])` — equivalent to `str.split(re, limit)`.\n\n```js\nconst re = new RE2('\\\\d+', 'g');\nre.match('test 123 test 456'); // ['123', '456']\nre.search('test 123');          // 5\nre.replace('test 1 and 2', 'N');  // 'test N and N' (global replaces all)\nre.split('a1b2c');              // ['a', 'b', 'c']\n```\n\n## Buffer support\n\nAll methods accept Node.js Buffers (UTF-8) instead of strings. When given Buffer input, they return Buffer output.\n\n```js\nconst re = new RE2('матч', 'g');\nconst buf = Buffer.from('тест матч тест');\nconst result = re.exec(buf);\n// result[0] is a Buffer containing 'матч' in UTF-8\n// result.index is in bytes (not characters)\n```\n\nDifferences from string mode:\n- All offsets and lengths are in **bytes**, not characters.\n- Results contain Buffers instead of strings.\n- Use `buf.toString()` to convert results back to strings.\n\n### useBuffers on replacer functions\n\nWhen using `re.replace(buf, replacerFn)`, the replacer receives string arguments and character offsets by default. Set `replacerFn.useBuffers = true` to receive byte offsets instead:\n\n```js\nfunction replacer(match, offset, input) {\n  return '<' + offset + ' bytes>';\n}\nreplacer.useBuffers = true;\nnew RE2('б').replace(Buffer.from('абв'), replacer);\n```\n\n## RE2.Set\n\nMulti-pattern matching — compile many patterns into a single automaton and test/match against all of them at once. Faster than testing individual patterns when the number of patterns is large.\n\n### Constructor\n\n```js\nnew RE2.Set(patterns[, flagsOrOptions][, options])\n```\n\n- `patterns` — any iterable of strings, Buffers, RegExp, or RE2 instances.\n- `flagsOrOptions` — optional string/Buffer with flags (apply to all patterns), or options object.\n- `options.anchor` — `'unanchored'` (default), `'start'`, or `'both'`.\n\n```js\nconst set = new RE2.Set([\n  '^/users/\\\\d+$',\n  '^/posts/\\\\d+$',\n  '^/api/.*$'\n], 'i', {anchor: 'start'});\n```\n\n### set.test(str)\n\nReturns `true` if any pattern matches, `false` otherwise.\n\n```js\nset.test('/users/42');  // true\nset.test('/unknown');   // false\n```\n\n### set.match(str)\n\nReturns an array of indices of matching patterns, sorted ascending. Empty array if none match.\n\n```js\nset.match('/users/42');  // [0]\nset.match('/api/users'); // [2]\nset.match('/unknown');   // []\n```\n\n### Properties\n\n- `set.size` (number) — number of patterns.\n- `set.source` (string) — all patterns joined with `|`.\n- `set.sources` (string[]) — individual pattern sources.\n- `set.flags` (string) — flags string.\n- `set.anchor` (string) — anchor mode.\n\n### set.toString()\n\nReturns `'/pattern1|pattern2|.../flags'`.\n\n```js\nset.toString(); // '/^/users/\\\\d+$|^/posts/\\\\d+$|^/api/.*$/iu'\n```\n\n## Static helpers\n\n### RE2.getUtf8Length(str)\n\nCalculate the byte size needed to encode a UTF-16 string as UTF-8.\n\n```js\nRE2.getUtf8Length('hello'); // 5\nRE2.getUtf8Length('привет'); // 12\n```\n\n### RE2.getUtf16Length(buf)\n\nCalculate the character count needed to encode a UTF-8 buffer as a UTF-16 string.\n\n```js\nRE2.getUtf16Length(Buffer.from('hello')); // 5\nRE2.getUtf16Length(Buffer.from('привет')); // 6\n```\n\n## Named groups\n\nNamed capture groups are supported:\n\n```js\nconst re = new RE2('(?<year>\\\\d{4})-(?<month>\\\\d{2})-(?<day>\\\\d{2})');\nconst result = re.exec('2024-01-15');\nresult.groups.year;  // '2024'\nresult.groups.month; // '01'\nresult.groups.day;   // '15'\n```\n\nNamed backreferences in replacement strings:\n\n```js\n'2024-01-15'.replace(\n  new RE2('(?<y>\\\\d{4})-(?<m>\\\\d{2})-(?<d>\\\\d{2})'),\n  '$<d>/$<m>/$<y>'\n); // '15/01/2024'\n```\n\n## Unicode classes\n\nRE2 supports Unicode property escapes. Long names are translated to RE2 short names:\n\n```js\nnew RE2('\\\\p{Letter}+');        // same as \\p{L}+\nnew RE2('\\\\p{Number}+');        // same as \\p{N}+\nnew RE2('\\\\p{Script=Latin}+');  // same as \\p{Latin}+\nnew RE2('\\\\p{sc=Cyrillic}+');   // same as \\p{Cyrillic}+\nnew RE2('\\\\P{Letter}+');        // negated: non-letters\n```\n\nOnly `\\p{name}` form is supported (not `\\p{name=value}` in general). Exception: `Script` and `sc` names.\n\n## Limitations\n\nRE2 does **not** support:\n\n- **Backreferences** (`\\1`, `\\2`, etc.) — throw `SyntaxError`.\n- **Lookahead assertions** (`(?=...)`, `(?!...)`) — throw `SyntaxError`.\n- **Lookbehind assertions** (`(?<=...)`, `(?<!...)`) — throw `SyntaxError`.\n\nFallback pattern:\n\n```js\nlet re = /pattern-with-lookahead(?=foo)/;\ntry {\n  re = new RE2(re);\n} catch (e) {\n  // use original RegExp as fallback\n}\nconst result = re.exec(input);\n```\n\n## Common patterns\n\n### Drop-in RegExp replacement\n\n```js\nconst RE2 = require('re2');\n\n// Before (vulnerable to ReDoS):\nconst re = new RegExp(userInput);\n\n// After (safe):\nconst re = new RE2(userInput);\n```\n\n### Process Buffer data efficiently\n\n```js\nconst RE2 = require('re2');\nconst fs = require('fs');\n\nconst data = fs.readFileSync('large-file.txt');\nconst re = new RE2('pattern', 'g');\nlet match;\nwhile ((match = re.exec(data)) !== null) {\n  console.log('Found at byte offset:', match.index);\n}\n```\n\n### Route matching with RE2.Set\n\n```js\nconst RE2 = require('re2');\n\nconst routes = new RE2.Set([\n  '^/users/\\\\d+$',\n  '^/posts/\\\\d+$',\n  '^/api/v\\\\d+/.*$'\n], 'i');\n\nfunction findRoute(path) {\n  const matches = routes.match(path);\n  return matches.length > 0 ? matches[0] : -1;\n}\n\nfindRoute('/users/42');   // 0\nfindRoute('/posts/7');    // 1\nfindRoute('/api/v2/foo'); // 2\nfindRoute('/unknown');    // -1\n```\n\n### Validate user-supplied patterns safely\n\n```js\nconst RE2 = require('re2');\n\nfunction safeMatch(input, pattern, flags) {\n  try {\n    const re = new RE2(pattern, flags);\n    return re.test(input);\n  } catch (e) {\n    return false; // invalid pattern\n  }\n}\n```\n\n## TypeScript\n\n```ts\nimport RE2 from 're2';\n\nconst re: RE2 = new RE2('\\\\d+', 'g');\nconst result: RegExpExecArray | null = re.exec('test 123');\n\n// Buffer overloads\nconst bufResult: RE2BufferExecArray | null = re.exec(Buffer.from('test 123'));\n\n// RE2.Set\nconst set: RE2Set = new RE2.Set(['a', 'b'], 'i');\nconst matches: number[] = set.match('abc');\n```\n\n## Project structure notes\n\n- Entry point: `re2.js` (loads native addon), types: `re2.d.ts`.\n- C++ addon source: `lib/*.cc`, `lib/*.h`.\n- Tests: `tests/test-*.mjs` (runtime), `ts-tests/test-*.ts` (type-checking).\n- Vendored dependencies: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules) — **never modify files under `vendor/`**.\n\n## Links\n\n- Docs: https://github.com/uhop/node-re2/wiki\n- npm: https://www.npmjs.com/package/re2\n- Repository: https://github.com/uhop/node-re2\n- RE2 syntax: https://github.com/google/re2/wiki/Syntax\n"
  },
  {
    "path": "llms.txt",
    "content": "# node-re2\n\n> Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS. Works with strings and Buffers.\n\n## Install\n\nnpm install re2\n\n## Quick start\n\n```js\n// CommonJS\nconst RE2 = require('re2');\n\n// ESM\nimport {RE2} from 're2';\n\nconst re = new RE2('a(b*)', 'i');\nconst result = re.exec('aBbC');\nconsole.log(result[0]); // \"aBb\"\nconsole.log(result[1]); // \"Bb\"\n```\n\n## Why use node-re2?\n\nThe built-in Node.js RegExp engine can run in exponential time with vulnerable patterns (ReDoS). RE2 guarantees linear-time matching by disallowing backreferences and lookahead assertions.\n\n## API\n\n### Construction\n\n```js\nconst RE2 = require('re2');\n\nconst re1 = new RE2('\\\\d+');           // from string\nconst re2 = new RE2('\\\\d+', 'gi');     // with flags\nconst re3 = new RE2(/ab*/ig);          // from RegExp\nconst re4 = new RE2(re3);              // from another RE2\nconst re5 = RE2('\\\\d+');               // factory (no new)\n```\n\nSupported flags: `g` (global), `i` (ignoreCase), `m` (multiline), `s` (dotAll), `u` (unicode, always on), `y` (sticky), `d` (hasIndices).\n\n### RegExp methods\n\n- `re.exec(str)` — find match with capture groups.\n- `re.test(str)` — boolean match check.\n- `re.toString()` — `/pattern/flags` representation.\n\n### String methods (via Symbol)\n\nRE2 instances work with ES6 string methods:\n\n```js\n'abc'.match(re);\n'abc'.search(re);\n'abc'.replace(re, 'x');\n'abc'.split(re);\nArray.from('abc'.matchAll(re));\n```\n\n### String methods (direct)\n\n- `re.match(str)` — equivalent to `str.match(re)`.\n- `re.search(str)` — equivalent to `str.search(re)`.\n- `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`.\n- `re.split(str[, limit])` — equivalent to `str.split(re, limit)`.\n\n### Properties\n\n- `re.source` — pattern string.\n- `re.flags` — flags string.\n- `re.lastIndex` — index for next match (with `g` or `y` flag).\n- `re.global`, `re.ignoreCase`, `re.multiline`, `re.dotAll`, `re.unicode`, `re.sticky`, `re.hasIndices` — boolean flag accessors.\n- `re.internalSource` — RE2-translated pattern (for debugging).\n\n### Buffer support\n\nAll methods accept Buffers (UTF-8) instead of strings. Buffer input produces Buffer output. Offsets are in bytes.\n\n```js\nconst re = new RE2('матч', 'g');\nconst buf = Buffer.from('тест матч тест');\nconst result = re.exec(buf);\n// result[0] is a Buffer\n```\n\n### RE2.Set\n\nMulti-pattern matching — test a string against many patterns at once.\n\n```js\nconst set = new RE2.Set(['^/users/\\\\d+$', '^/posts/\\\\d+$'], 'i');\nset.test('/users/7');     // true\nset.match('/posts/42');   // [1]\nset.sources;              // ['^/users/\\\\d+$', '^/posts/\\\\d+$']\n```\n\n- `new RE2.Set(patterns[, flags][, options])` — compile patterns.\n  - `options.anchor`: `'unanchored'` (default), `'start'`, or `'both'`.\n- `set.test(str)` — returns `true` if any pattern matches.\n- `set.match(str)` — returns array of matching pattern indices.\n- Properties: `size`, `source`, `sources`, `flags`, `anchor`.\n\n### Static helpers\n\n- `RE2.getUtf8Length(str)` — byte size of string as UTF-8.\n- `RE2.getUtf16Length(buf)` — character count of UTF-8 buffer as UTF-16 string.\n- `RE2.unicodeWarningLevel` — `'nothing'` (default), `'warnOnce'`, `'warn'`, or `'throw'`.\n\n## Limitations\n\nRE2 does not support:\n- **Backreferences** (`\\1`, `\\2`, etc.)\n- **Lookahead assertions** (`(?=...)`, `(?!...)`)\n\nThese throw `SyntaxError`. Use try-catch to fall back to RegExp when needed:\n\n```js\nlet re = /pattern-with-lookahead/;\ntry { re = new RE2(re); } catch (e) { /* use original RegExp */ }\n```\n\n## Project notes\n\n- C++ addon source is in `lib/`. Vendored deps (`vendor/re2/`, `vendor/abseil-cpp/`) are git submodules — **never modify files under `vendor/`**.\n\n## Links\n\n- Docs: https://github.com/uhop/node-re2/wiki\n- npm: https://www.npmjs.com/package/re2\n- Full LLM reference: https://github.com/uhop/node-re2/blob/master/llms-full.txt\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"re2\",\n  \"version\": \"1.24.0\",\n  \"description\": \"Bindings for RE2: fast, safe alternative to backtracking regular expression engines.\",\n  \"homepage\": \"https://github.com/uhop/node-re2\",\n  \"bugs\": \"https://github.com/uhop/node-re2/issues\",\n  \"type\": \"commonjs\",\n  \"main\": \"re2.js\",\n  \"types\": \"re2.d.ts\",\n  \"files\": [\n    \"binding.gyp\",\n    \"lib\",\n    \"re2.d.ts\",\n    \"scripts/*.js\",\n    \"vendor\"\n  ],\n  \"dependencies\": {\n    \"install-artifact-from-github\": \"^1.4.0\",\n    \"nan\": \"^2.26.2\",\n    \"node-gyp\": \"^12.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^25.5.0\",\n    \"nano-benchmark\": \"^1.0.15\",\n    \"prettier\": \"^3.8.1\",\n    \"tape-six\": \"^1.7.13\",\n    \"tape-six-proc\": \"^1.2.8\",\n    \"typescript\": \"^6.0.2\"\n  },\n  \"scripts\": {\n    \"test\": \"tape6 --flags FO\",\n    \"test:seq\": \"tape6-seq --flags FO\",\n    \"test:proc\": \"tape6-proc --flags FO\",\n    \"save-to-github\": \"save-to-github-cache --artifact build/Release/re2.node\",\n    \"install\": \"install-from-cache --artifact build/Release/re2.node --host-var RE2_DOWNLOAD_MIRROR --skip-path-var RE2_DOWNLOAD_SKIP_PATH --skip-ver-var RE2_DOWNLOAD_SKIP_VER || node-gyp -j max rebuild\",\n    \"verify-build\": \"node scripts/verify-build.js\",\n    \"build:dev\": \"node-gyp -j max build --debug\",\n    \"build\": \"node-gyp -j max build\",\n    \"build1\": \"node-gyp build\",\n    \"rebuild:dev\": \"node-gyp -j max rebuild --debug\",\n    \"rebuild\": \"node-gyp -j max rebuild\",\n    \"rebuild1\": \"node-gyp rebuild\",\n    \"clean\": \"node-gyp clean && node-gyp configure\",\n    \"clean-build\": \"node-gyp clean\",\n    \"ts-check\": \"tsc --noEmit\",\n    \"lint\": \"prettier --check *.js *.ts tests/ bench/\",\n    \"lint:fix\": \"prettier --write *.js *.ts tests/ bench/\"\n  },\n  \"github\": \"https://github.com/uhop/node-re2\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/uhop/node-re2.git\"\n  },\n  \"keywords\": [\n    \"RegExp\",\n    \"RegEx\",\n    \"text processing\",\n    \"PCRE alternative\"\n  ],\n  \"author\": \"Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://lazutkin.com/)\",\n  \"funding\": \"https://github.com/sponsors/uhop\",\n  \"license\": \"BSD-3-Clause\",\n  \"tape6\": {\n    \"tests\": [\n      \"/tests/test-*.*js\",\n      \"/tests/test-*.*ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "re2.d.ts",
    "content": "/// <reference types=\"node\" />\n\ndeclare module 're2' {\n  interface RE2BufferExecArray {\n    index: number;\n    input: Buffer;\n    0: Buffer;\n    groups?: {\n      [key: string]: Buffer;\n    };\n    indices?: RegExpIndicesArray;\n  }\n\n  interface RE2BufferMatchArray {\n    index?: number;\n    input?: Buffer;\n    0: Buffer;\n    groups?: {\n      [key: string]: Buffer;\n    };\n  }\n\n  interface RE2 extends RegExp {\n    readonly internalSource: string;\n    exec(str: string): RegExpExecArray | null;\n    exec(str: Buffer): RE2BufferExecArray | null;\n\n    match(str: string): RegExpMatchArray | null;\n    match(str: Buffer): RE2BufferMatchArray | null;\n\n    test(str: string | Buffer): boolean;\n\n    replace<K extends String | Buffer>(\n      str: K,\n      replaceValue: string | Buffer\n    ): K;\n    replace<K extends String | Buffer>(\n      str: K,\n      replacer: (substring: string, ...args: any[]) => string | Buffer\n    ): K;\n\n    search(str: string | Buffer): number;\n\n    split<K extends String | Buffer>(str: K, limit?: number): K[];\n  }\n\n  interface RE2SetOptions {\n    anchor?: 'unanchored' | 'start' | 'both';\n  }\n\n  interface RE2Set {\n    readonly size: number;\n    readonly source: string;\n    readonly sources: string[];\n    readonly flags: string;\n    readonly anchor: 'unanchored' | 'start' | 'both';\n\n    match(str: string | Buffer): number[];\n    test(str: string | Buffer): boolean;\n    toString(): string;\n  }\n\n  interface RE2SetConstructor {\n    new (\n      patterns: Iterable<Buffer | RegExp | RE2 | string>,\n      flagsOrOptions?: string | Buffer | RE2SetOptions,\n      options?: RE2SetOptions\n    ): RE2Set;\n    (\n      patterns: Iterable<Buffer | RegExp | RE2 | string>,\n      flagsOrOptions?: string | Buffer | RE2SetOptions,\n      options?: RE2SetOptions\n    ): RE2Set;\n    readonly prototype: RE2Set;\n  }\n\n  interface RE2Constructor extends RegExpConstructor {\n    new (pattern: Buffer | RegExp | RE2 | string): RE2;\n    new (pattern: Buffer | string, flags?: string | Buffer): RE2;\n    (pattern: Buffer | RegExp | RE2 | string): RE2;\n    (pattern: Buffer | string, flags?: string | Buffer): RE2;\n    readonly prototype: RE2;\n\n    unicodeWarningLevel: 'nothing' | 'warnOnce' | 'warn' | 'throw';\n    getUtf8Length(value: string): number;\n    getUtf16Length(value: Buffer): number;\n\n    Set: RE2SetConstructor;\n    RE2: RE2Constructor;\n  }\n\n  var RE2: RE2Constructor;\n  export = RE2;\n}\n"
  },
  {
    "path": "re2.js",
    "content": "'use strict';\n\nconst RE2 = require('./build/Release/re2.node');\n// const RE2 = require('./build/Debug/re2.node');\n\nconst setAliases = (object, dict) => {\n  for (let [name, alias] of Object.entries(dict)) {\n    Object.defineProperty(\n      object,\n      alias,\n      Object.getOwnPropertyDescriptor(object, name)\n    );\n  }\n};\n\nsetAliases(RE2.prototype, {\n  match: Symbol.match,\n  search: Symbol.search,\n  replace: Symbol.replace,\n  split: Symbol.split\n});\n\nRE2.prototype[Symbol.matchAll] = function* (str) {\n  if (!this.global)\n    throw TypeError(\n      'String.prototype.matchAll() is called with a non-global RE2 argument'\n    );\n\n  const re = new RE2(this);\n  re.lastIndex = this.lastIndex;\n  for (;;) {\n    const result = re.exec(str);\n    if (!result) break;\n    if (result[0] === '') ++re.lastIndex;\n    yield result;\n  }\n};\n\nmodule.exports = RE2;\nmodule.exports.RE2 = RE2;\n"
  },
  {
    "path": "scripts/verify-build.js",
    "content": "'use strict';\n\n// This is a light-weight script to make sure that the package works.\n\nconst assert = require('assert').strict;\n\nconst RE2  = require(\"../re2\");\n\nconst sample = \"abbcdefabh\";\n\nconst re1 = new RE2(\"ab*\", \"g\");\nassert(re1.test(sample));\n\nconst re2 = RE2(\"ab*\");\nassert(re2.test(sample));\n\nconst re3 = new RE2(\"abc\");\nassert(!re3.test(sample));\n"
  },
  {
    "path": "tests/manual/matchall-bench.js",
    "content": "'use strict';\n\nconst RE2 = require('../../re2');\n\nconst N = 1_000_000;\n\nconst s = 'a'.repeat(N),\n  re = new RE2('a', 'g'),\n  matches = s.matchAll(re);\n\nlet n = 0;\nfor (const _ of matches) ++n;\n\nif (n !== s.length) console.log('Wrong result.');\n\nconsole.log('Done.');\n"
  },
  {
    "path": "tests/manual/memory-check.js",
    "content": "'use strict';\n\nconst RE2 = require('../../re2.js');\n\nconst L = 20 * 1024 * 1024,\n  N = 100;\n\nif (typeof globalThis.gc != 'function')\n  console.log(\n    \"Warning: to run it with explicit gc() calls, you should use --expose-gc as a node's argument.\"\n  );\n\nconst gc = typeof globalThis.gc == 'function' ? globalThis.gc : () => {};\n\nconst s = 'a'.repeat(L),\n  objects = [];\n\nfor (let i = 0; i < N; ++i) {\n  const re2 = new RE2('x', 'g');\n  objects.push(re2);\n  const result = s.replace(re2, '');\n  if (result.length !== s.length) console.log('Wrong result.');\n  gc();\n}\n\nconsole.log(\n  'Done. Now it is spinning: check the memory consumption! To stop it, press Ctrl+C.'\n);\n\nfor (;;);\n"
  },
  {
    "path": "tests/manual/memory-monitor.js",
    "content": "'use strict';\n\nconst RE2 = require('../../re2');\n\nconst N = 5_000_000;\n\nconsole.log('Never-ending loop: exit with Ctrl+C.');\n\nconst aCharCode = 'a'.charCodeAt(0);\nconst randomAlpha = () =>\n  String.fromCharCode(aCharCode + Math.floor(Math.random() * 26));\n\nconst humanizeNumber = n => {\n  const negative = n < 0;\n  if (negative) n = -n;\n\n  const s = n.toFixed();\n  let group1 = s.length % 3;\n  if (!group1) group1 = 3;\n\n  let result = s.substring(0, group1);\n  for (let i = group1; i < s.length; i += 3) {\n    result += ',' + s.substring(i, i + 3);\n  }\n\n  return (negative ? '-' : '') + result;\n};\n\nconst CSI = '\\x1B[';\nconst cursorUp = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'A';\nconst sgr = (cmd = '') =>\n  CSI + (Array.isArray(cmd) ? cmd.join(';') : cmd) + 'm';\nconst RESET = sgr();\nconst NOTE = sgr(91);\n\nlet first = true;\nconst maxMemory = {\n    heapTotal: 0,\n    heapUsed: 0,\n    external: 0,\n    arrayBuffers: 0,\n    rss: 0\n  },\n  labels = {\n    heapTotal: 'heap total',\n    heapUsed: 'heap used',\n    external: 'external',\n    arrayBuffers: 'array buffers',\n    rss: 'resident set size'\n  },\n  maxLabelSize = Math.max(\n    ...Array.from(Object.values(labels)).map(label => label.length)\n  );\n\nconst report = () => {\n  const memoryUsage = process.memoryUsage(),\n    previousMax = {...maxMemory};\n\n  console.log(\n    (first ? '' : '\\r' + cursorUp(6)) + ''.padStart(maxLabelSize + 1),\n    'Current'.padStart(15),\n    'Max'.padStart(15)\n  );\n  for (const name in maxMemory) {\n    const prefix =\n      previousMax[name] && previousMax[name] < memoryUsage[name] ? NOTE : RESET;\n    console.log(\n      (labels[name] + ':').padStart(maxLabelSize + 1),\n      prefix + humanizeNumber(memoryUsage[name]).padStart(15) + RESET,\n      humanizeNumber(maxMemory[name]).padStart(15)\n    );\n  }\n\n  for (const [name, value] of Object.entries(maxMemory)) {\n    maxMemory[name] = Math.max(value, memoryUsage[name]);\n  }\n\n  first = false;\n};\n\nfor (;;) {\n  const re2 = new RE2(randomAlpha(), 'g');\n\n  let s = '';\n  for (let i = 0; i < N; ++i) s += randomAlpha();\n\n  let n = 0;\n  for (const _ of s.matchAll(re2)) ++n;\n\n  re2.lastIndex = 0;\n  const r = s.replace(re2, '');\n  if (r.length + n != s.length) {\n    console.log(\n      'ERROR!',\n      's:',\n      s.length,\n      'r:',\n      r.length,\n      'n:',\n      n,\n      're2:',\n      re2.toString()\n    );\n    break;\n  }\n\n  report();\n}\n"
  },
  {
    "path": "tests/manual/test-unicode-warning.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../../re2.js';\n\n// tests\n// these tests modify the global state of RE2 and cannot be run in parallel with other tests in the same process\n\ntest('test new unicode warnOnce', t => {\n  let errorMessage = '';\n\n  const oldConsole = console;\n  console = {error: msg => (errorMessage = msg)};\n  RE2.unicodeWarningLevel = 'warnOnce';\n\n  let a = new RE2('.*');\n  t.ok(errorMessage);\n  errorMessage = '';\n\n  a = new RE2('.?');\n  t.notOk(errorMessage);\n\n  RE2.unicodeWarningLevel = 'warnOnce';\n  a = new RE2('.+');\n  t.ok(errorMessage);\n\n  RE2.unicodeWarningLevel = 'nothing';\n  console = oldConsole;\n});\n\ntest('test new unicode warn', t => {\n  let errorMessage = '';\n\n  const oldConsole = console;\n  console = {error: msg => (errorMessage = msg)};\n  RE2.unicodeWarningLevel = 'warn';\n\n  let a = new RE2('.*');\n  t.ok(errorMessage);\n  errorMessage = '';\n\n  a = new RE2('.?');\n  t.ok(errorMessage);\n\n  RE2.unicodeWarningLevel = 'nothing';\n  console = oldConsole;\n});\n\ntest('test new unicode throw', t => {\n  RE2.unicodeWarningLevel = 'throw';\n\n  try {\n    let a = new RE2('.');\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof SyntaxError);\n  }\n\n  RE2.unicodeWarningLevel = 'nothing';\n});\n"
  },
  {
    "path": "tests/manual/worker.js",
    "content": "'use strict';\n\nconst {Worker, isMainThread} = require('worker_threads');\n\nconst RE2 = require('../../re2');\n\nif (isMainThread) {\n  // This re-loads the current file inside a Worker instance.\n  console.log('Inside Master!');\n  const worker = new Worker(__filename);\n  worker.on('exit', code => {\n    console.log('Exit code:', code);\n    test('#2');\n  });\n  test('#1');\n} else {\n  console.log('Inside Worker!');\n  test();\n}\n\nfunction test(msg) {\n  msg && console.log(isMainThread ? 'Main' : 'Worker', msg);\n\n  const a = new RE2('^\\\\d+$');\n  console.log(\n    isMainThread,\n    a.test('123'),\n    a.test('abc'),\n    a.test('123abc'),\n    a instanceof RE2\n  );\n\n  const b = RE2('^\\\\d+$');\n  console.log(\n    isMainThread,\n    b.test('123'),\n    b.test('abc'),\n    b.test('123abc'),\n    b instanceof RE2\n  );\n}\n"
  },
  {
    "path": "tests/test-cjs.cjs",
    "content": "const {test} = require('tape-six');\nconst RE2 = require('../re2.js');\n\ntest('CJS require', t => {\n  t.ok(RE2, 'RE2 is loaded');\n  t.equal(typeof RE2, 'function', 'RE2 is a constructor');\n});\n\ntest('CJS construct and test', t => {\n  const re = new RE2('a(b*)', 'u');\n  t.ok(re instanceof RE2, 'instanceof RE2');\n  t.ok(re.test('aBb'), 'test matches');\n  t.notOk(re.test('xyz'), 'test rejects non-match');\n});\n\ntest('CJS exec', t => {\n  const re = new RE2('(\\\\d+)', 'u');\n  const result = re.exec('abc 123 def');\n  t.ok(result, 'exec returns a result');\n  t.equal(result[0], '123');\n  t.equal(result[1], '123');\n  t.equal(result.index, 4);\n});\n\ntest('CJS exec with Buffer', t => {\n  const re = new RE2('(\\\\d+)', 'u');\n  const result = re.exec(Buffer.from('abc 123 def'));\n  t.ok(result, 'exec returns a result');\n  t.ok(Buffer.isBuffer(result[0]), 'result is a Buffer');\n  t.equal(result[0].toString(), '123');\n});\n\ntest('CJS match', t => {\n  const re = new RE2('\\\\w+', 'gu');\n  const result = 'hello world'.match(re);\n  t.ok(result, 'match returns a result');\n  t.deepEqual(result, ['hello', 'world']);\n});\n\ntest('CJS search', t => {\n  const re = new RE2('world', 'u');\n  const idx = 'hello world'.search(re);\n  t.equal(idx, 6);\n});\n\ntest('CJS replace', t => {\n  const re = new RE2('world', 'u');\n  const result = 'hello world'.replace(re, 'RE2');\n  t.equal(result, 'hello RE2');\n});\n\ntest('CJS split', t => {\n  const re = new RE2('\\\\s+', 'u');\n  const result = 'a b  c'.split(re);\n  t.deepEqual(result, ['a', 'b', 'c']);\n});\n\ntest('CJS named groups', t => {\n  const re = new RE2('(?P<year>\\\\d{4})-(?P<month>\\\\d{2})', 'u');\n  const result = re.exec('2025-03');\n  t.ok(result, 'exec returns a result');\n  t.equal(result.groups.year, '2025');\n  t.equal(result.groups.month, '03');\n});\n\ntest('CJS RE2.Set', t => {\n  const set = new RE2.Set(['abc', 'def', 'ghi'], 'u');\n  t.ok(set, 'set is created');\n  t.ok(set.test('abc'), 'test matches first pattern');\n  t.deepEqual(set.match('abcghi'), [0, 2]);\n});\n\ntest('CJS named import pattern', t => {\n  const {RE2: NamedRE2} = require('../re2.js');\n  t.equal(NamedRE2, RE2, 'RE2.RE2 === RE2');\n  const re = new NamedRE2('abc', 'u');\n  t.ok(re instanceof RE2, 'instance created via named import');\n  t.ok(re.test('abc'), 'works correctly');\n});\n\ntest('CJS static helpers', t => {\n  t.equal(RE2.getUtf8Length('hello'), 5);\n  t.equal(RE2.getUtf16Length(Buffer.from('hello')), 5);\n});\n"
  },
  {
    "path": "tests/test-exec.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec\n\ntest('exec basic', t => {\n  const re = new RE2('quick\\\\s(brown).+?(jumps)', 'ig');\n\n  t.equal(re.source, 'quick\\\\s(brown).+?(jumps)');\n  t.ok(re.ignoreCase);\n  t.ok(re.global);\n  t.ok(!re.multiline);\n\n  const result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');\n\n  t.deepEqual(Array.from(result), ['Quick Brown Fox Jumps', 'Brown', 'Jumps']);\n  t.equal(result.index, 4);\n  t.equal(result.input, 'The Quick Brown Fox Jumps Over The Lazy Dog');\n  t.equal(re.lastIndex, 25);\n});\n\ntest('exec succ', t => {\n  const str = 'abbcdefabh';\n\n  const re = new RE2('ab*', 'g');\n  let result = re.exec(str);\n\n  t.ok(result);\n  t.equal(result[0], 'abb');\n  t.equal(result.index, 0);\n  t.equal(re.lastIndex, 3);\n\n  result = re.exec(str);\n\n  t.ok(result);\n  t.equal(result[0], 'ab');\n  t.equal(result.index, 7);\n  t.equal(re.lastIndex, 9);\n\n  result = re.exec(str);\n\n  t.notOk(result);\n});\n\ntest('exec simple', t => {\n  const re = new RE2('(hello \\\\S+)');\n  const result = re.exec('This is a hello world!');\n\n  t.equal(result[1], 'hello world!');\n});\n\ntest('exec fail', t => {\n  const re = new RE2('(a+)?(b+)?');\n  let result = re.exec('aaabb');\n\n  t.equal(result[1], 'aaa');\n  t.equal(result[2], 'bb');\n\n  result = re.exec('aaacbb');\n\n  t.equal(result[1], 'aaa');\n  t.equal(result[2], undefined);\n  t.equal(result.length, 3);\n});\n\ntest('exec anchored to beginning', t => {\n  const re = RE2('^hello', 'g');\n\n  const result = re.exec('hellohello');\n\n  t.deepEqual(Array.from(result), ['hello']);\n  t.equal(result.index, 0);\n  t.equal(re.lastIndex, 5);\n\n  t.equal(re.exec('hellohello'), null);\n});\n\ntest('exec invalid', t => {\n  const re = RE2('');\n\n  try {\n    re.exec({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n});\n\ntest('exec anchor 1', t => {\n  const re = new RE2('b|^a', 'g');\n\n  let result = re.exec('aabc');\n  t.ok(result);\n  t.equal(result.index, 0);\n  t.equal(re.lastIndex, 1);\n\n  result = re.exec('aabc');\n  t.ok(result);\n  t.equal(result.index, 2);\n  t.equal(re.lastIndex, 3);\n\n  result = re.exec('aabc');\n  t.notOk(result);\n});\n\ntest('exec anchor 2', t => {\n  const re = new RE2('(?:^a)', 'g');\n\n  let result = re.exec('aabc');\n  t.ok(result);\n  t.equal(result.index, 0);\n  t.equal(re.lastIndex, 1);\n\n  result = re.exec('aabc');\n  t.notOk(result);\n});\n\n// Unicode tests\n\ntest('exec unicode', t => {\n  const re = new RE2('охотник\\\\s(желает).+?(где)', 'ig');\n\n  t.equal(re.source, 'охотник\\\\s(желает).+?(где)');\n  t.ok(re.ignoreCase);\n  t.ok(re.global);\n  t.ok(!re.multiline);\n\n  const result = re.exec('Каждый Охотник Желает Знать Где Сидит Фазан');\n\n  t.deepEqual(Array.from(result), [\n    'Охотник Желает Знать Где',\n    'Желает',\n    'Где'\n  ]);\n  t.equal(result.index, 7);\n  t.equal(result.input, 'Каждый Охотник Желает Знать Где Сидит Фазан');\n  t.equal(re.lastIndex, 31);\n\n  t.equal(\n    result.input.substr(result.index),\n    'Охотник Желает Знать Где Сидит Фазан'\n  );\n  t.equal(result.input.substr(re.lastIndex), ' Сидит Фазан');\n});\n\ntest('exec unicode subsequent', t => {\n  const str = 'аббвгдеабё';\n\n  const re = new RE2('аб*', 'g');\n  let result = re.exec(str);\n\n  t.ok(result);\n  t.equal(result[0], 'абб');\n  t.equal(result.index, 0);\n  t.equal(re.lastIndex, 3);\n\n  result = re.exec(str);\n\n  t.ok(result);\n  t.equal(result[0], 'аб');\n  t.equal(result.index, 7);\n  t.equal(re.lastIndex, 9);\n\n  result = re.exec(str);\n\n  t.notOk(result);\n});\n\ntest('exec unicode supplementary', t => {\n  const re = new RE2('\\\\u{1F603}', 'g');\n\n  t.equal(re.source, '\\\\u{1F603}');\n  t.notOk(re.ignoreCase);\n  t.ok(re.global);\n  t.notOk(re.multiline);\n\n  const result = re.exec('\\u{1F603}'); // 1F603 is the SMILING FACE WITH OPEN MOUTH emoji\n\n  t.deepEqual(Array.from(result), ['\\u{1F603}']);\n  t.equal(result.index, 0);\n  t.equal(result.input, '\\u{1F603}');\n  t.equal(re.lastIndex, 2);\n\n  const re2 = new RE2('.', 'g');\n\n  t.equal(re2.source, '.');\n  t.notOk(re2.ignoreCase);\n  t.ok(re2.global);\n  t.notOk(re2.multiline);\n\n  const result2 = re2.exec('\\u{1F603}');\n\n  t.deepEqual(Array.from(result2), ['\\u{1F603}']);\n  t.equal(result2.index, 0);\n  t.equal(result2.input, '\\u{1F603}');\n  t.equal(re2.lastIndex, 2);\n\n  const re3 = new RE2('[\\u{1F603}-\\u{1F605}]', 'g');\n\n  t.equal(re3.source, '[\\u{1F603}-\\u{1F605}]');\n  t.notOk(re3.ignoreCase);\n  t.ok(re3.global);\n  t.notOk(re3.multiline);\n\n  const result3 = re3.exec('\\u{1F604}');\n\n  t.deepEqual(Array.from(result3), ['\\u{1F604}']);\n  t.equal(result3.index, 0);\n  t.equal(result3.input, '\\u{1F604}');\n  t.equal(re3.lastIndex, 2);\n});\n\n// Buffer tests\n\ntest('exec buffer', t => {\n  const re = new RE2('охотник\\\\s(желает).+?(где)', 'ig');\n  const buf = Buffer.from('Каждый Охотник Желает Знать Где Сидит Фазан');\n\n  const result = re.exec(buf);\n\n  t.equal(result.length, 3);\n  t.ok(result[0] instanceof Buffer);\n  t.ok(result[1] instanceof Buffer);\n  t.ok(result[2] instanceof Buffer);\n\n  t.equal(result[0].toString(), 'Охотник Желает Знать Где');\n  t.equal(result[1].toString(), 'Желает');\n  t.equal(result[2].toString(), 'Где');\n\n  t.equal(result.index, 13);\n  t.ok(result.input instanceof Buffer);\n  t.equal(\n    result.input.toString(),\n    'Каждый Охотник Желает Знать Где Сидит Фазан'\n  );\n\n  t.equal(re.lastIndex, 58);\n\n  t.equal(\n    result.input.toString('utf8', result.index),\n    'Охотник Желает Знать Где Сидит Фазан'\n  );\n  t.equal(result.input.toString('utf8', re.lastIndex), ' Сидит Фазан');\n});\n\n// Sticky tests\n\ntest('exec sticky', t => {\n  const re = new RE2('\\\\s+', 'y');\n\n  t.equal(re.exec('Hello world, how are you?'), null);\n\n  re.lastIndex = 5;\n\n  const result = re.exec('Hello world, how are you?');\n\n  t.deepEqual(Array.from(result), [' ']);\n  t.equal(result.index, 5);\n  t.equal(re.lastIndex, 6);\n\n  const re2 = new RE2('\\\\s+', 'gy');\n\n  t.equal(re2.exec('Hello world, how are you?'), null);\n\n  re2.lastIndex = 5;\n\n  const result2 = re2.exec('Hello world, how are you?');\n\n  t.deepEqual(Array.from(result2), [' ']);\n  t.equal(result2.index, 5);\n  t.equal(re2.lastIndex, 6);\n});\n\ntest('exec supplemental', t => {\n  const re = new RE2('\\\\w+', 'g');\n  const testString = '🤡🤡🤡 Hello clown world!';\n\n  let result = re.exec(testString);\n  t.deepEqual(Array.from(result), ['Hello']);\n\n  result = re.exec(testString);\n  t.deepEqual(Array.from(result), ['clown']);\n\n  result = re.exec(testString);\n  t.deepEqual(Array.from(result), ['world']);\n});\n\n// Multiline test\n\ntest('exec multiline', t => {\n  const re = new RE2('^xy', 'm'),\n    pattern = ` xy1\nxy2 (at start of line)\nxy3`;\n\n  const result = re.exec(pattern);\n\n  t.ok(result);\n  t.equal(result[0], 'xy');\n  t.ok(result.index > 3);\n  t.ok(result.index < pattern.length - 4);\n  t.equal(\n    result[0],\n    pattern.substring(result.index, result.index + result[0].length)\n  );\n});\n\n// dotAll tests\n\ntest('exec dotAll', t => {\n  t.ok(new RE2('a.c').test('abc'));\n  t.ok(new RE2(/a.c/).test('a c'));\n  t.notOk(new RE2(/a.c/).test('a\\nc'));\n\n  t.ok(new RE2('a.c', 's').test('abc'));\n  t.ok(new RE2(/a.c/s).test('a c'));\n  t.ok(new RE2(/a.c/s).test('a\\nc'));\n});\n\n// hasIndices tests\n\ntest('exec hasIndices', t => {\n  t.notOk(new RE2('1').hasIndices);\n  t.notOk(new RE2(/1/).hasIndices);\n\n  const re = new RE2('(aa)(?<b>b)?(?<c>ccc)', 'd');\n\n  t.ok(re.hasIndices);\n\n  let result = re.exec('1aabccc2');\n\n  t.equal(result.length, 4);\n  t.equal(result.input, '1aabccc2');\n  t.equal(result.index, 1);\n  t.equal(Object.keys(result.groups).length, 2);\n  t.equal(result.groups.b, 'b');\n  t.equal(result.groups.c, 'ccc');\n  t.equal(result[0], 'aabccc');\n  t.equal(result[1], 'aa');\n  t.equal(result[2], 'b');\n  t.equal(result[3], 'ccc');\n  t.equal(result.indices.length, 4);\n  t.deepEqual(Array.from(result.indices), [\n    [1, 7],\n    [1, 3],\n    [3, 4],\n    [4, 7]\n  ]);\n  t.equal(Object.keys(result.indices.groups).length, 2);\n  t.deepEqual(result.indices.groups.b, [3, 4]);\n  t.deepEqual(result.indices.groups.c, [4, 7]);\n\n  result = re.exec('1aaccc2');\n\n  t.equal(result.length, 4);\n  t.equal(result.input, '1aaccc2');\n  t.equal(result.index, 1);\n  t.equal(Object.keys(result.groups).length, 2);\n  t.equal(result.groups.b, undefined);\n  t.equal(result.groups.c, 'ccc');\n  t.equal(result[0], 'aaccc');\n  t.equal(result[1], 'aa');\n  t.equal(result[2], undefined);\n  t.equal(result[3], 'ccc');\n  t.equal(result.indices.length, 4);\n  t.deepEqual(Array.from(result.indices), [[1, 6], [1, 3], undefined, [3, 6]]);\n  t.equal(Object.keys(result.indices.groups).length, 2);\n  t.deepEqual(result.indices.groups.b, undefined);\n  t.deepEqual(result.indices.groups.c, [3, 6]);\n\n  try {\n    const re = new RE2(new RegExp('1', 'd'));\n    t.ok(re.hasIndices);\n  } catch (e) {\n    // squelch\n  }\n});\n\ntest('exec hasIndices lastIndex', t => {\n  const re2 = new RE2('a', 'dg');\n\n  t.equal(re2.lastIndex, 0);\n\n  let result = re2.exec('abca');\n  t.equal(re2.lastIndex, 1);\n  t.equal(result.index, 0);\n  t.deepEqual(Array.from(result.indices), [[0, 1]]);\n\n  result = re2.exec('abca');\n  t.equal(re2.lastIndex, 4);\n  t.equal(result.index, 3);\n  t.deepEqual(Array.from(result.indices), [[3, 4]]);\n\n  result = re2.exec('abca');\n  t.equal(re2.lastIndex, 0);\n  t.equal(result, null);\n});\n\ntest('exec buffer vs string', t => {\n  const re2 = new RE2('.', 'g'),\n    pattern = 'abcdefg';\n\n  re2.lastIndex = 2;\n  const result1 = re2.exec(pattern);\n\n  re2.lastIndex = 2;\n  const result2 = re2.exec(Buffer.from(pattern));\n\n  t.equal(result1[0], 'c');\n  t.deepEqual(result2[0], Buffer.from('c'));\n\n  t.equal(result1.index, 2);\n  t.equal(result2.index, 2);\n});\n\ntest('exec found empty string', t => {\n  const re2 = new RE2('^.*?'),\n    match = re2.exec('');\n\n  t.equal(match[0], '');\n  t.equal(match.index, 0);\n  t.equal(match.input, '');\n  t.equal(match.groups, undefined);\n});\n"
  },
  {
    "path": "tests/test-general.mjs",
    "content": "import test from 'tape-six';\nimport {default as RE2} from '../re2.js';\n\n// utilities\n\nconst compare = (re1, re2, t) => {\n  // compares regular expression objects\n  t.equal(re1.source, re2.source);\n  t.equal(re1.global, re2.global);\n  t.equal(re1.ignoreCase, re2.ignoreCase);\n  t.equal(re1.multiline, re2.multiline);\n  // (t.equal(re1.unicode, re2.unicode));\n  t.equal(re1.sticky, re2.sticky);\n};\n\n// tests\n\ntest('general ctr', t => {\n  t.ok(!!RE2);\n  t.ok(!!RE2.prototype);\n  t.equal(RE2.toString(), 'function RE2() { [native code] }');\n});\n\ntest('general inst', t => {\n  let re1 = new RE2('\\\\d+');\n\n  t.ok(!!re1);\n  t.ok(re1 instanceof RE2);\n\n  let re2 = RE2('\\\\d+');\n\n  t.ok(!!re2);\n  t.ok(re2 instanceof RE2);\n  compare(re1, re2, t);\n\n  re1 = new RE2('\\\\d+', 'm');\n\n  t.ok(!!re1);\n  t.ok(re1 instanceof RE2);\n\n  re2 = RE2('\\\\d+', 'm');\n\n  t.ok(!!re2);\n  t.ok(re2 instanceof RE2);\n  compare(re1, re2, t);\n});\n\ntest('general inst errors', t => {\n  try {\n    const re = new RE2([]);\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = new RE2({});\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = new RE2(new Date());\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = new RE2(null);\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = new RE2();\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = RE2();\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n\n  try {\n    const re = RE2({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n});\n\ntest('general in', t => {\n  const re = new RE2('\\\\d+');\n\n  t.ok('exec' in re);\n  t.ok('test' in re);\n  t.ok('match' in re);\n  t.ok('replace' in re);\n  t.ok('search' in re);\n  t.ok('split' in re);\n  t.ok('source' in re);\n  t.ok('flags' in re);\n  t.ok('global' in re);\n  t.ok('ignoreCase' in re);\n  t.ok('multiline' in re);\n  t.ok('dotAll' in re);\n  t.ok('sticky' in re);\n  t.ok('lastIndex' in re);\n});\n\ntest('general present', t => {\n  const re = new RE2('\\\\d+');\n\n  t.equal(typeof re.exec, 'function');\n  t.equal(typeof re.test, 'function');\n  t.equal(typeof re.match, 'function');\n  t.equal(typeof re.replace, 'function');\n  t.equal(typeof re.search, 'function');\n  t.equal(typeof re.split, 'function');\n  t.equal(typeof re.source, 'string');\n  t.equal(typeof re.flags, 'string');\n  t.equal(typeof re.global, 'boolean');\n  t.equal(typeof re.ignoreCase, 'boolean');\n  t.equal(typeof re.multiline, 'boolean');\n  t.equal(typeof re.dotAll, 'boolean');\n  t.equal(typeof re.sticky, 'boolean');\n  t.equal(typeof re.lastIndex, 'number');\n});\n\ntest('general lastIndex', t => {\n  const re = new RE2('\\\\d+');\n\n  t.equal(re.lastIndex, 0);\n\n  re.lastIndex = 5;\n  t.equal(re.lastIndex, 5);\n\n  re.lastIndex = 0;\n  t.equal(re.lastIndex, 0);\n});\n\ntest('general RegExp', t => {\n  let re1 = new RegExp('\\\\d+');\n  let re2 = new RE2('\\\\d+');\n\n  compare(re1, re2, t);\n\n  re2 = new RE2(re1);\n\n  compare(re1, re2, t);\n\n  re1 = new RegExp('a', 'ig');\n  re2 = new RE2('a', 'ig');\n\n  compare(re1, re2, t);\n\n  re2 = new RE2(re1);\n\n  compare(re1, re2, t);\n\n  re1 = /\\s/gm;\n  re2 = new RE2('\\\\s', 'mg');\n\n  compare(re1, re2, t);\n\n  re2 = new RE2(re1);\n\n  compare(re1, re2, t);\n\n  re2 = new RE2(/\\s/gm);\n\n  compare(/\\s/gm, re2, t);\n\n  re1 = new RE2('b', 'gm');\n  re2 = new RE2(re1);\n\n  compare(re1, re2, t);\n\n  re1 = new RE2('b', 'sgm');\n  re2 = new RE2(re1);\n\n  compare(re1, re2, t);\n\n  re2 = new RE2(/\\s/gms);\n\n  compare(/\\s/gms, re2, t);\n});\n\ntest('general utf8', t => {\n  const s = 'Привет!';\n\n  t.equal(s.length, 7);\n  t.equal(RE2.getUtf8Length(s), 13);\n\n  const b = Buffer.from(s);\n  t.equal(b.length, 13);\n  t.equal(RE2.getUtf16Length(b), 7);\n\n  const s2 = '\\u{1F603}';\n\n  t.equal(s2.length, 2);\n  t.equal(RE2.getUtf8Length(s2), 4);\n\n  const b2 = Buffer.from(s2);\n\n  t.equal(b2.length, 4);\n  t.equal(RE2.getUtf16Length(b2), 2);\n\n  const s3 = '\\uD83D';\n\n  t.equal(s3.length, 1);\n  t.equal(RE2.getUtf8Length(s3), 3);\n\n  const s4 = '🤡';\n\n  t.equal(s4.length, 2);\n  t.equal(RE2.getUtf8Length(s4), 4);\n  t.equal(RE2.getUtf16Length(Buffer.from(s4, 'utf8')), s4.length);\n\n  const b3 = Buffer.from([0xf0]);\n\n  t.equal(b3.length, 1);\n  t.equal(RE2.getUtf16Length(b3), 2);\n\n  try {\n    RE2.getUtf8Length({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n\n  t.equal(\n    RE2.getUtf16Length({\n      toString() {\n        throw 'corner';\n      }\n    }),\n    -1\n  );\n});\n\ntest('general flags', t => {\n  let re = new RE2('a', 'u');\n  t.equal(re.flags, 'u');\n\n  re = new RE2('a', 'iu');\n  t.equal(re.flags, 'iu');\n\n  re = new RE2('a', 'mu');\n  t.equal(re.flags, 'mu');\n\n  re = new RE2('a', 'gu');\n  t.equal(re.flags, 'gu');\n\n  re = new RE2('a', 'yu');\n  t.equal(re.flags, 'uy');\n\n  re = new RE2('a', 'yiu');\n  t.equal(re.flags, 'iuy');\n\n  re = new RE2('a', 'yigu');\n  t.equal(re.flags, 'giuy');\n\n  re = new RE2('a', 'miu');\n  t.equal(re.flags, 'imu');\n\n  re = new RE2('a', 'ygu');\n  t.equal(re.flags, 'guy');\n\n  re = new RE2('a', 'myu');\n  t.equal(re.flags, 'muy');\n\n  re = new RE2('a', 'migyu');\n  t.equal(re.flags, 'gimuy');\n\n  re = new RE2('a', 'smigyu');\n  t.equal(re.flags, 'gimsuy');\n});\n\ntest('general flags 2nd', t => {\n  let re = new RE2(/a/, 'u');\n  t.equal(re.flags, 'u');\n\n  re = new RE2(/a/gm, 'iu');\n  t.equal(re.flags, 'iu');\n\n  re = new RE2(/a/gi, 'mu');\n  t.equal(re.flags, 'mu');\n\n  re = new RE2(/a/g, 'gu');\n  t.equal(re.flags, 'gu');\n\n  re = new RE2(/a/m, 'yu');\n  t.equal(re.flags, 'uy');\n\n  re = new RE2(/a/, 'yiu');\n  t.equal(re.flags, 'iuy');\n\n  re = new RE2(/a/gim, 'yigu');\n  t.equal(re.flags, 'giuy');\n\n  re = new RE2(/a/gm, 'miu');\n  t.equal(re.flags, 'imu');\n\n  re = new RE2(/a/i, 'ygu');\n  t.equal(re.flags, 'guy');\n\n  re = new RE2(/a/g, 'myu');\n  t.equal(re.flags, 'muy');\n\n  re = new RE2(/a/, 'migyu');\n  t.equal(re.flags, 'gimuy');\n\n  re = new RE2(/a/s, 'smigyu');\n  t.equal(re.flags, 'gimsuy');\n});\n"
  },
  {
    "path": "tests/test-groups.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('groups normal', t => {\n  t.equal(RE2('(?<a>\\\\d)').test('9'), true);\n  t.deepEqual(RE2('(?<a>-)', 'g').match('a-b-c'), ['-', '-']);\n  t.deepEqual(RE2('(?<a>-)').split('a-b-c'), ['a', '-', 'b', '-', 'c']);\n  t.equal(RE2('(?<a>-)', 'g').search('a-b-c'), 1);\n});\n\ntest('groups exec', t => {\n  let result = new RE2('(\\\\d)').exec('k9');\n  t.ok(result);\n  t.equal(result[0], '9');\n  t.equal(result[1], '9');\n  t.equal(result.index, 1);\n  t.equal(result.input, 'k9');\n  t.equal(typeof result.groups, 'undefined');\n\n  result = new RE2('(?<a>\\\\d)').exec('k9');\n  t.ok(result);\n  t.equal(result[0], '9');\n  t.equal(result[1], '9');\n  t.equal(result.index, 1);\n  t.equal(result.input, 'k9');\n  t.deepEqual(result.groups, {a: '9'});\n});\n\ntest('groups match', t => {\n  let result = new RE2('(\\\\d)').match('k9');\n  t.ok(result);\n  t.equal(result[0], '9');\n  t.equal(result[1], '9');\n  t.equal(result.index, 1);\n  t.equal(result.input, 'k9');\n  t.equal(typeof result.groups, 'undefined');\n\n  result = new RE2('(?<a>\\\\d)').match('k9');\n  t.ok(result);\n  t.equal(result[0], '9');\n  t.equal(result[1], '9');\n  t.equal(result.index, 1);\n  t.equal(result.input, 'k9');\n  t.deepEqual(result.groups, {a: '9'});\n});\n\ntest('groups replace', t => {\n  t.equal(RE2('(?<w>\\\\w)(?<d>\\\\d)', 'g').replace('a1b2c', '$2$1'), '1a2bc');\n  t.equal(RE2('(?<w>\\\\w)(?<d>\\\\d)', 'g').replace('a1b2c', '$<d>$<w>'), '1a2bc');\n\n  t.equal(\n    RE2('(?<w>\\\\w)(?<d>\\\\d)', 'g').replace('a1b2c', replacerByNumbers),\n    '1a2bc'\n  );\n  t.equal(\n    RE2('(?<w>\\\\w)(?<d>\\\\d)', 'g').replace('a1b2c', replacerByNames),\n    '1a2bc'\n  );\n\n  function replacerByNumbers(match, group1, group2, index, source, groups) {\n    return group2 + group1;\n  }\n  function replacerByNames(match, group1, group2, index, source, groups) {\n    return groups.d + groups.w;\n  }\n});\n\ntest('groups invalid', t => {\n  try {\n    RE2('(?<>.)');\n    t.fail(); // shouldn'be here\n  } catch (e) {\n    t.ok(e instanceof SyntaxError);\n  }\n\n  // TODO: do we need to enforce the correct id?\n  // try {\n  // \tRE2('(?<1>.)');\n  // \tt.fail(); // shouldn'be here\n  // } catch(e) {\n  // \teval(t.TEST(\"e instanceof SyntaxError\"));\n  // }\n\n  try {\n    RE2('(?<a>.)(?<a>.)');\n    t.fail(); // shouldn'be here\n  } catch (e) {\n    t.ok(e instanceof SyntaxError);\n  }\n});\n"
  },
  {
    "path": "tests/test-invalid.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('invalid', t => {\n  let threw;\n\n  // Backreferences\n  threw = false;\n  try {\n    new RE2(/(a)\\1/);\n  } catch (e) {\n    threw = true;\n    t.ok(e instanceof SyntaxError);\n    t.equal(e.message, 'invalid escape sequence: \\\\1');\n  }\n  t.ok(threw);\n\n  // Lookahead assertions\n\n  // Positive\n  threw = false;\n  try {\n    new RE2(/a(?=b)/);\n  } catch (e) {\n    threw = true;\n    t.ok(e instanceof SyntaxError);\n    t.equal(e.message, 'invalid perl operator: (?=');\n  }\n  t.ok(threw);\n\n  // Negative\n  threw = false;\n  try {\n    new RE2(/a(?!b)/);\n  } catch (e) {\n    threw = true;\n    t.ok(e instanceof SyntaxError);\n    t.equal(e.message, 'invalid perl operator: (?!');\n  }\n  t.ok(threw);\n});\n"
  },
  {
    "path": "tests/test-match.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match\n\ntest('test match', t => {\n  const str = 'For more information, see Chapter 3.4.5.1';\n\n  const re = new RE2(/(chapter \\d+(\\.\\d)*)/i);\n  const result = re.match(str);\n\n  t.equal(result.input, str);\n  t.equal(result.index, 26);\n  t.equal(result.length, 3);\n  t.equal(result[0], 'Chapter 3.4.5.1');\n  t.equal(result[1], 'Chapter 3.4.5.1');\n  t.equal(result[2], '.1');\n});\n\ntest('test_matchGlobal', t => {\n  const re = new RE2(/[A-E]/gi);\n  const result = re.match(\n    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\n  );\n\n  t.deepEqual(result, ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']);\n});\n\ntest('test match fail', t => {\n  const re = new RE2('(a+)?(b+)?');\n  let result = re.match('aaabb');\n\n  t.equal(result[1], 'aaa');\n  t.equal(result[2], 'bb');\n\n  result = re.match('aaacbb');\n\n  t.equal(result[1], 'aaa');\n  t.equal(result[2], undefined);\n});\n\ntest('test match invalid', t => {\n  const re = RE2('');\n\n  try {\n    re.match({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n});\n\n// Unicode tests\n\ntest('test match unicode', t => {\n  const str = 'Это ГЛАВА 3.4.5.1';\n\n  const re = new RE2(/(глава \\d+(\\.\\d)*)/i);\n  const result = re.match(str);\n\n  t.equal(result.input, str);\n  t.equal(result.index, 4);\n  t.equal(result.length, 3);\n  t.equal(result[0], 'ГЛАВА 3.4.5.1');\n  t.equal(result[1], 'ГЛАВА 3.4.5.1');\n  t.equal(result[2], '.1');\n});\n\n// Buffer tests\n\ntest('test match buffer', t => {\n  const buf = Buffer.from('Это ГЛАВА 3.4.5.1');\n\n  const re = new RE2(/(глава \\d+(\\.\\d)*)/i);\n  const result = re.match(buf);\n\n  t.ok(result.input instanceof Buffer);\n  t.equal(result.length, 3);\n  t.ok(result[0] instanceof Buffer);\n  t.ok(result[1] instanceof Buffer);\n  t.ok(result[2] instanceof Buffer);\n\n  t.equal(result.input, buf);\n  t.equal(result.index, 7);\n  t.equal(result.input.toString('utf8', result.index), 'ГЛАВА 3.4.5.1');\n  t.equal(result[0].toString(), 'ГЛАВА 3.4.5.1');\n  t.equal(result[1].toString(), 'ГЛАВА 3.4.5.1');\n  t.equal(result[2].toString(), '.1');\n});\n\n// Sticky tests\n\ntest('test match sticky', t => {\n  const re = new RE2('\\\\s+', 'y');\n\n  t.equal(re.match('Hello world, how are you?'), null);\n\n  re.lastIndex = 5;\n\n  const result = re.match('Hello world, how are you?');\n\n  t.deepEqual(Array.from(result), [' ']);\n  t.equal(result.index, 5);\n  t.equal(re.lastIndex, 6);\n\n  const re2 = new RE2('\\\\s+', 'gy');\n\n  t.equal(re2.match('Hello world, how are you?'), null);\n\n  re2.lastIndex = 5;\n\n  t.equal(re2.match('Hello world, how are you?'), null);\n\n  const re3 = new RE2(/[A-E]/giy);\n  const result3 = re3.match(\n    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\n  );\n\n  t.deepEqual(result3, ['A', 'B', 'C', 'D', 'E']);\n});\n\n// hasIndices tests\n\ntest('test match has indices', t => {\n  const re = new RE2('(aa)(?<b>b)?(?<c>ccc)', 'd'),\n    str1 = '1aabccc2',\n    str2 = '1aaccc2';\n\n  t.deepEqual(str1.match(re), re.exec(str1));\n  t.deepEqual(str2.match(re), re.exec(str2));\n});\n\ntest('test match has indices global', t => {\n  const re = new RE2('(?<zzz>a)', 'dg'),\n    result = 'abca'.match(re);\n\n  t.deepEqual(result, ['a', 'a']);\n  t.notOk('indices' in result);\n  t.notOk('groups' in result);\n});\n\ntest('test match lastIndex', t => {\n  const re = new RE2(/./g),\n    pattern = 'Я123';\n\n  re.lastIndex = 2;\n  const result1 = pattern.match(re);\n  t.deepEqual(result1, ['Я', '1', '2', '3']);\n  t.equal(re.lastIndex, 0);\n\n  const re2 = RE2(re);\n  re2.lastIndex = 2;\n  const result2 = re2.match(Buffer.from(pattern));\n  t.deepEqual(\n    result2.map(b => b.toString()),\n    ['Я', '1', '2', '3']\n  );\n  t.equal(re2.lastIndex, 0);\n});\n"
  },
  {
    "path": "tests/test-matchAll.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll\n\ntest('test matchAll', t => {\n  const str = 'test1test2';\n  const re = new RE2(/t(e)(st(\\d?))/g);\n  const result = Array.from(str.matchAll(re));\n\n  t.equal(result.length, 2);\n  t.equal(result[0].input, str);\n  t.equal(result[0].index, 0);\n  t.equal(result[0].length, 4);\n  t.equal(result[0][0], 'test1');\n  t.equal(result[0][1], 'e');\n  t.equal(result[0][2], 'st1');\n  t.equal(result[0][3], '1');\n  t.equal(result[1].input, str);\n  t.equal(result[1].index, 5);\n  t.equal(result[1].length, 4);\n  t.equal(result[1][0], 'test2');\n  t.equal(result[1][1], 'e');\n  t.equal(result[1][2], 'st2');\n  t.equal(result[1][3], '2');\n});\n\ntest('test matchAll iterator', t => {\n  const str = 'table football, foosball';\n  const re = new RE2('foo[a-z]*', 'g');\n\n  const expected = [\n    {start: 6, finish: 14},\n    {start: 16, finish: 24}\n  ];\n  let i = 0;\n  for (const match of str.matchAll(re)) {\n    t.equal(match.index, expected[i].start);\n    t.equal(match.index + match[0].length, expected[i].finish);\n    ++i;\n  }\n});\n\ntest('test matchAll non global', t => {\n  const re = RE2('b');\n\n  try {\n    'abc'.matchAll(re);\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n});\n\ntest('test matchAll lastIndex', t => {\n  const re = RE2('[a-c]', 'g');\n  re.lastIndex = 1;\n\n  const expected = ['b', 'c'];\n  let i = 0;\n  for (const match of 'abc'.matchAll(re)) {\n    t.equal(re.lastIndex, 1);\n    t.equal(match[0], expected[i]);\n    ++i;\n  }\n});\n\ntest('test matchAll empty match', t => {\n  const str = 'foo';\n  // Matches empty strings, but should not cause an infinite loop\n  const re = new RE2('(?:)', 'g');\n  const result = Array.from(str.matchAll(re));\n\n  t.equal(result.length, str.length + 1);\n  for (let i = 0; i < result.length; ++i) {\n    t.equal(result[i][0], '');\n  }\n});\n"
  },
  {
    "path": "tests/test-prototype.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test prototype', t => {\n  t.equal(RE2.prototype.source, '(?:)');\n  t.equal(RE2.prototype.flags, '');\n  t.equal(RE2.prototype.global, undefined);\n  t.equal(RE2.prototype.ignoreCase, undefined);\n  t.equal(RE2.prototype.multiline, undefined);\n  t.equal(RE2.prototype.dotAll, undefined);\n  t.equal(RE2.prototype.sticky, undefined);\n  t.equal(RE2.prototype.lastIndex, undefined);\n});\n"
  },
  {
    "path": "tests/test-replace.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace\n\ntest('test replace string', t => {\n  let re = new RE2(/apples/gi);\n  let result = re.replace('Apples are round, and apples are juicy.', 'oranges');\n  t.equal(result, 'oranges are round, and oranges are juicy.');\n\n  re = new RE2(/xmas/i);\n  result = re.replace('Twas the night before Xmas...', 'Christmas');\n  t.equal(result, 'Twas the night before Christmas...');\n\n  re = new RE2(/(\\w+)\\s(\\w+)/);\n  result = re.replace('John Smith', '$2, $1');\n  t.equal(result, 'Smith, John');\n});\n\ntest('test replace functional replacer', t => {\n  function replacer(match, p1, p2, p3, offset, string) {\n    // p1 is nondigits, p2 digits, and p3 non-alphanumerics\n    return [p1, p2, p3].join(' - ');\n  }\n\n  const re = new RE2(/([^\\d]*)(\\d*)([^\\w]*)/);\n  const result = re.replace('abc12345#$*%', replacer);\n  t.equal(result, 'abc - 12345 - #$*%');\n});\n\ntest('test replace functional upper to hyphen lower', t => {\n  function upperToHyphenLower(match) {\n    return '-' + match.toLowerCase();\n  }\n\n  const re = new RE2(/[A-Z]/g);\n  const result = re.replace('borderTop', upperToHyphenLower);\n  t.equal(result, 'border-top');\n});\n\ntest('test replace functional convert', t => {\n  function convert(str, p1, offset, s) {\n    return ((p1 - 32) * 5) / 9 + 'C';\n  }\n\n  const re = new RE2(/(\\d+(?:\\.\\d*)?)F\\b/g);\n\n  t.equal(re.replace('32F', convert), '0C');\n  t.equal(re.replace('41F', convert), '5C');\n  t.equal(re.replace('50F', convert), '10C');\n  t.equal(re.replace('59F', convert), '15C');\n  t.equal(re.replace('68F', convert), '20C');\n  t.equal(re.replace('77F', convert), '25C');\n  t.equal(re.replace('86F', convert), '30C');\n  t.equal(re.replace('95F', convert), '35C');\n  t.equal(re.replace('104F', convert), '40C');\n  t.equal(re.replace('113F', convert), '45C');\n  t.equal(re.replace('212F', convert), '100C');\n});\n\ntest('test replace functional loop', t => {\n  const logs = [];\n  RE2(/(x_*)|(-)/g).replace('x-x_', function (match, p1, p2) {\n    if (p1) {\n      logs.push('on:  ' + p1.length);\n    }\n    if (p2) {\n      logs.push('off: 1');\n    }\n  });\n  t.deepEqual(logs, ['on:  1', 'off: 1', 'on:  2']);\n});\n\ntest('test replace invalid', t => {\n  const re = RE2('');\n\n  try {\n    re.replace(\n      {\n        toString() {\n          throw 'corner1';\n        }\n      },\n      ''\n    );\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner1');\n  }\n\n  try {\n    re.replace('', {\n      toString() {\n        throw 'corner2';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner2');\n  }\n\n  let arg2Stringified = false;\n\n  try {\n    re.replace(\n      {\n        toString() {\n          throw 'corner1';\n        }\n      },\n      {\n        toString() {\n          arg2Stringified = true;\n          throw 'corner2';\n        }\n      }\n    );\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner1');\n    t.notOk(arg2Stringified);\n  }\n\n  try {\n    re.replace('', () => {\n      throw 'corner2';\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner2');\n  }\n\n  try {\n    re.replace('', () => ({\n      toString() {\n        throw 'corner2';\n      }\n    }));\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner2');\n  }\n});\n\n// Unicode tests\n\ntest('test replace string unicode', t => {\n  let re = new RE2(/яблоки/gi);\n  let result = re.replace('Яблоки красны, яблоки сочны.', 'апельсины');\n  t.equal(result, 'апельсины красны, апельсины сочны.');\n\n  re = new RE2(/иван/i);\n  result = re.replace('Могуч Иван Иванов...', 'Сидор');\n  t.equal(result, 'Могуч Сидор Иванов...');\n\n  re = new RE2(/иван/gi);\n  result = re.replace('Могуч Иван Иванов...', 'Сидор');\n  t.equal(result, 'Могуч Сидор Сидоров...');\n\n  re = new RE2(/([а-яё]+)\\s+([а-яё]+)/i);\n  result = re.replace('Пётр Петров', '$2, $1');\n  t.equal(result, 'Петров, Пётр');\n});\n\ntest('test replace functional unicode', t => {\n  function replacer(match, offset, string) {\n    t.equal(typeof offset, 'number');\n    t.equal(typeof string, 'string');\n    t.ok(offset === 0 || offset === 7);\n    t.equal(string, 'ИВАН и пЁтр');\n    return match.charAt(0).toUpperCase() + match.substr(1).toLowerCase();\n  }\n\n  const re = new RE2(/(?:иван|пётр|сидор)/gi);\n  const result = re.replace('ИВАН и пЁтр', replacer);\n  t.equal(result, 'Иван и Пётр');\n});\n\n// Buffer tests\n\ntest('test replace string buffer', t => {\n  const re = new RE2(/яблоки/gi);\n  let result = re.replace(\n    Buffer.from('Яблоки красны, яблоки сочны.'),\n    'апельсины'\n  );\n  t.ok(result instanceof Buffer);\n  t.equal(result.toString(), 'апельсины красны, апельсины сочны.');\n\n  result = re.replace(\n    Buffer.from('Яблоки красны, яблоки сочны.'),\n    Buffer.from('апельсины')\n  );\n  t.ok(result instanceof Buffer);\n  t.equal(result.toString(), 'апельсины красны, апельсины сочны.');\n\n  result = re.replace('Яблоки красны, яблоки сочны.', Buffer.from('апельсины'));\n  t.equal(typeof result, 'string');\n  t.equal(result, 'апельсины красны, апельсины сочны.');\n});\n\ntest('test replace functional buffer', t => {\n  function replacer(match, offset, string) {\n    t.ok(match instanceof Buffer);\n    t.equal(typeof offset, 'number');\n    t.equal(typeof string, 'string');\n    t.ok(offset === 0 || offset === 12);\n    t.equal(string, 'ИВАН и пЁтр');\n    const s = match.toString();\n    return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();\n  }\n  replacer.useBuffers = true;\n\n  const re = new RE2(/(?:иван|пётр|сидор)/gi);\n  const result = re.replace('ИВАН и пЁтр', replacer);\n  t.equal(typeof result, 'string');\n  t.equal(result, 'Иван и Пётр');\n});\n\ntest('test replace0', t => {\n  const replacer = match => 'MARKER' + match;\n\n  let re = new RE2(/^/g);\n  let result = re.replace('foo bar', 'MARKER');\n  t.equal(result, 'MARKERfoo bar');\n  result = re.replace('foo bar', replacer);\n  t.equal(result, 'MARKERfoo bar');\n\n  re = new RE2(/$/g);\n  result = re.replace('foo bar', 'MARKER');\n  t.equal(result, 'foo barMARKER');\n  result = re.replace('foo bar', replacer);\n  t.equal(result, 'foo barMARKER');\n\n  re = new RE2(/\\b/g);\n  result = re.replace('foo bar', 'MARKER');\n  t.equal(result, 'MARKERfooMARKER MARKERbarMARKER');\n  result = re.replace('foo bar', replacer);\n  t.equal(result, 'MARKERfooMARKER MARKERbarMARKER');\n});\n\n// Sticky tests\n\ntest('test replace sticky', t => {\n  const re = new RE2(/[A-E]/y);\n\n  t.equal(re.replace('ABCDEFABCDEF', '!'), '!BCDEFABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), 'A!CDEFABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), 'AB!DEFABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABC!EFABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABCD!FABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABCDEFABCDEF');\n  t.equal(re.replace('ABCDEFABCDEF', '!'), '!BCDEFABCDEF');\n\n  const re2 = new RE2(/[A-E]/gy);\n\n  t.equal(re2.replace('ABCDEFABCDEF', '!'), '!!!!!FABCDEF');\n  t.equal(re2.replace('FABCDEFABCDE', '!'), 'FABCDEFABCDE');\n\n  re2.lastIndex = 3;\n\n  t.equal(re2.replace('ABCDEFABCDEF', '!'), '!!!!!FABCDEF');\n  t.equal(re2.lastIndex, 0);\n});\n\n// Non-matches\n\ntest('test replace one non-match', t => {\n  const replacer = (match, capture, offset, string) => {\n    t.equal(typeof offset, 'number');\n    t.equal(typeof match, 'string');\n    t.equal(typeof string, 'string');\n    t.equal(typeof capture, 'undefined');\n    t.equal(offset, 0);\n    t.equal(string, 'hello ');\n    return '';\n  };\n\n  const re = new RE2(/hello (world)?/);\n  re.replace('hello ', replacer);\n});\n\ntest('test replace two non-matches', t => {\n  const replacer = (match, capture1, capture2, offset, string, groups) => {\n    t.equal(typeof offset, 'number');\n    t.equal(typeof match, 'string');\n    t.equal(typeof string, 'string');\n    t.equal(typeof capture1, 'undefined');\n    t.equal(typeof capture2, 'undefined');\n    t.equal(offset, 1);\n    t.equal(match, 'b & y');\n    t.equal(string, 'ab & yz');\n    t.equal(typeof groups, 'object');\n    t.equal(Object.keys(groups).length, 2);\n    t.equal(groups.a, undefined);\n    t.equal(groups.b, undefined);\n    return '';\n  };\n\n  const re = new RE2(/b(?<a>1)? & (?<b>2)?y/);\n  const result = re.replace('ab & yz', replacer);\n  t.equal(result, 'az');\n});\n\ntest('test replace group simple', t => {\n  const re = new RE2(/(2)/);\n\n  let result = re.replace('123', '$0');\n  t.equal(result, '1$03');\n  result = re.replace('123', '$1');\n  t.equal(result, '123');\n  result = re.replace('123', '$2');\n  t.equal(result, '1$23');\n\n  result = re.replace('123', '$00');\n  t.equal(result, '1$003');\n  result = re.replace('123', '$01');\n  t.equal(result, '123');\n  result = re.replace('123', '$02');\n  t.equal(result, '1$023');\n});\n\ntest('test replace group cases', t => {\n  let re = new RE2(/(test)/g);\n  let result = re.replace('123', '$1$20');\n  t.equal(result, '123');\n\n  re = new RE2(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/g);\n  result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20');\n  t.equal(result, 'jb0wo0123');\n\n  re = new RE2(/(.)(.)(.)(.)(.)/g);\n  result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20');\n  t.equal(result, 'a0b0f0g0k0l0p0q0u0v0z123');\n\n  re = new RE2(\n    /(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/g\n  );\n  result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20');\n  t.equal(result, 'jtvwxyz123');\n\n  re = new RE2(/abcd/g);\n  result = re.replace('abcd123', '$1$2');\n  t.equal(result, '$1$2123');\n});\n\ntest('test replace empty replacement', t => {\n  t.equal('ac', 'abc'.replace(RE2('b'), ''));\n});\n"
  },
  {
    "path": "tests/test-search.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test search', t => {\n  const str = 'Total is 42 units.';\n\n  let re = new RE2(/\\d+/i);\n  let result = re.search(str);\n  t.equal(result, 9);\n\n  re = new RE2('\\\\b[a-z]+\\\\b');\n  result = re.search(str);\n  t.equal(result, 6);\n\n  re = new RE2('\\\\b\\\\w+\\\\b');\n  result = re.search(str);\n  t.equal(result, 0);\n\n  re = new RE2('z', 'gm');\n  result = re.search(str);\n  t.equal(result, -1);\n});\n\ntest('test search invalid', t => {\n  const re = RE2('');\n\n  try {\n    re.search({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n});\n\ntest('test search unicode', t => {\n  const str = 'Всего 42 штуки.';\n\n  let re = new RE2(/\\d+/i);\n  let result = re.search(str);\n  t.equal(result, 6);\n\n  re = new RE2('\\\\s[а-я]+');\n  result = re.search(str);\n  t.equal(result, 8);\n\n  re = new RE2('[а-яА-Я]+');\n  result = re.search(str);\n  t.equal(result, 0);\n\n  re = new RE2('z', 'gm');\n  result = re.search(str);\n  t.equal(result, -1);\n});\n\ntest('test search buffer', t => {\n  const buf = Buffer.from('Всего 42 штуки.');\n\n  let re = new RE2(/\\d+/i);\n  let result = re.search(buf);\n  t.equal(result, 11);\n\n  re = new RE2('\\\\s[а-я]+');\n  result = re.search(buf);\n  t.equal(result, 13);\n\n  re = new RE2('[а-яА-Я]+');\n  result = re.search(buf);\n  t.equal(result, 0);\n\n  re = new RE2('z', 'gm');\n  result = re.search(buf);\n  t.equal(result, -1);\n});\n\ntest('test search sticky', t => {\n  const str = 'Total is 42 units.';\n\n  let re = new RE2(/\\d+/y);\n  let result = re.search(str);\n  t.equal(result, -1);\n\n  re = new RE2('\\\\b[a-z]+\\\\b', 'y');\n  result = re.search(str);\n  t.equal(result, -1);\n\n  re = new RE2('\\\\b\\\\w+\\\\b', 'y');\n  result = re.search(str);\n  t.equal(result, 0);\n\n  re = new RE2('z', 'gmy');\n  result = re.search(str);\n  t.equal(result, -1);\n});\n"
  },
  {
    "path": "tests/test-set.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\ntest('test set basics', t => {\n  const set = new RE2.Set(['foo', 'bar'], 'im');\n\n  t.ok(set instanceof Object);\n  t.equal(typeof set.match, 'function');\n  t.equal(set.size, 2);\n  t.equal(set.flags, 'imu');\n  t.equal(set.anchor, 'unanchored');\n  t.ok(Array.isArray(set.sources));\n  t.equal(set.sources[0], 'foo');\n  t.equal(set.source, 'foo|bar');\n  t.equal(set.toString(), '/foo|bar/imu');\n});\n\ntest('test set matching', t => {\n  const set = new RE2.Set(['foo', 'bar'], 'i');\n\n  const result = set.match('xxFOOxxbar');\n  t.equal(result.length, 2);\n  result.sort((a, b) => a - b);\n  t.deepEqual(result, [0, 1]);\n  t.equal(set.test('nothing here'), false);\n  t.equal(set.match('nothing here').length, 0);\n});\n\ntest('test set anchors', t => {\n  const start = new RE2.Set(['abc'], {anchor: 'start'});\n  const both = new RE2.Set(['abc'], {anchor: 'both'});\n\n  t.equal(start.test('zabc'), false);\n  t.equal(start.test('abc'), true);\n  t.ok(both.test('abc'));\n  t.notOk(both.test('abc1'));\n});\n\ntest('test set iterable', t => {\n  function* gen() {\n    yield 'cat';\n    yield 'dog';\n  }\n\n  const set = new RE2.Set(gen());\n  t.equal(set.size, 2);\n  const result = set.match('hotdog');\n  t.equal(result.length, 1);\n  t.equal(result[0], 1);\n});\n\ntest('test set flags override', t => {\n  const set = new RE2.Set([/abc/], 'i');\n  t.ok(set.test('ABC'));\n  t.equal(set.flags, 'iu');\n});\n\ntest('test set unicode inputs', t => {\n  const patterns = ['🙂', '猫', '🍣+', '東京', '\\\\p{Hiragana}+'];\n  const set = new RE2.Set(patterns, 'u');\n  const input = 'prefix🙂と猫と🍣🍣を食べる東京ひらがな';\n\n  const result = set.match(input);\n  t.equal(result.length, 5);\n  t.notEqual(result.indexOf(0), -1);\n  t.notEqual(result.indexOf(1), -1);\n  t.notEqual(result.indexOf(2), -1);\n  t.notEqual(result.indexOf(3), -1);\n  t.notEqual(result.indexOf(4), -1);\n\n  const buf = Buffer.from(input);\n  const bufResult = set.match(buf);\n  t.equal(bufResult.length, 5);\n  t.ok(set.test(buf));\n\n  const miss = new RE2.Set(['🚀', '漢字'], 'u');\n  t.notOk(miss.test(input));\n  t.equal(miss.match(input).length, 0);\n});\n\ntest('test set empty and duplicates', t => {\n  const emptySet = new RE2.Set([]);\n  t.equal(emptySet.size, 0);\n  t.equal(emptySet.test('anything'), false);\n\n  const dup = new RE2.Set(['foo', 'foo', 'bar']);\n  const r = dup.match('foo bar');\n  // two foo entries plus bar\n  t.equal(r.length, 3);\n  r.sort((a, b) => a - b);\n  t.deepEqual(r, [0, 1, 2]);\n});\n\ntest('test set inconsistent flags', t => {\n  try {\n    const set = new RE2.Set([/abc/i, /abc/m]);\n    t.fail();\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n});\n\ntest('test set invalid flags char', t => {\n  try {\n    const set = new RE2.Set(['foo'], 'q');\n    t.fail();\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n});\n\ntest('test set anchor option with flags', t => {\n  const set = new RE2.Set(['^foo', '^bar'], 'i', {anchor: 'both'});\n  t.equal(set.anchor, 'both');\n  t.equal(set.match('foo').length, 1);\n  t.equal(set.match('xfoo').length, 0);\n});\n\ntest('test set invalid', t => {\n  try {\n    const set = new RE2.Set([null]);\n    t.fail();\n  } catch (e) {\n    t.ok(e instanceof TypeError);\n  }\n});\n"
  },
  {
    "path": "tests/test-source.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test source identity', t => {\n  let re = new RE2('a\\\\cM\\\\u34\\\\u1234\\\\u10abcdz');\n  t.equal(re.source, 'a\\\\cM\\\\u34\\\\u1234\\\\u10abcdz');\n\n  re = new RE2('a\\\\cM\\\\u34\\\\u1234\\\\u{10abcd}z');\n  t.equal(re.source, 'a\\\\cM\\\\u34\\\\u1234\\\\u{10abcd}z');\n\n  re = new RE2('');\n  t.equal(re.source, '(?:)');\n\n  re = new RE2('foo/bar');\n  t.equal(re.source, 'foo\\\\/bar');\n\n  re = new RE2('foo\\\\/bar');\n  t.equal(re.source, 'foo\\\\/bar');\n\n  re = new RE2('(?<foo>bar)', 'u');\n  t.equal(re.source, '(?<foo>bar)');\n});\n\ntest('test source translation', t => {\n  let re = new RE2('a\\\\cM\\\\u34\\\\u1234\\\\u10abcdz');\n  t.equal(re.internalSource, 'a\\\\x0D\\\\x{34}\\\\x{1234}\\\\x{10ab}cdz');\n\n  re = new RE2('a\\\\cM\\\\u34\\\\u1234\\\\u{10abcd}z');\n  t.equal(re.internalSource, 'a\\\\x0D\\\\x{34}\\\\x{1234}\\\\x{10abcd}z');\n\n  re = new RE2('');\n  t.equal(re.internalSource, '(?:)');\n\n  re = new RE2('foo/bar');\n  t.equal(re.internalSource, 'foo\\\\/bar');\n\n  re = new RE2('foo\\\\/bar');\n  t.equal(re.internalSource, 'foo\\\\/bar');\n\n  re = new RE2('(?<foo>bar)', 'u');\n  t.equal(re.internalSource, '(?P<foo>bar)');\n\n  re = new RE2('foo\\\\/bar', 'm');\n  t.equal(re.internalSource, '(?m)foo\\\\/bar');\n});\n\ntest('test source backslashes', t => {\n  const compare = (source, expected) => {\n    const s = new RE2(source).source;\n    t.equal(s, expected);\n  };\n\n  compare('a/b', 'a\\\\/b');\n  compare('a\\/b', 'a\\\\/b');\n  compare('a\\\\/b', 'a\\\\/b');\n  compare('a\\\\\\/b', 'a\\\\/b');\n  compare('a\\\\\\\\/b', 'a\\\\\\\\\\\\/b');\n  compare('a\\\\\\\\\\/b', 'a\\\\\\\\\\\\/b');\n\n  compare('/a/b', '\\\\/a\\\\/b');\n  compare('\\\\/a/b', '\\\\/a\\\\/b');\n  compare('\\\\/a\\\\/b', '\\\\/a\\\\/b');\n  compare('\\\\/a\\\\\\\\/b', '\\\\/a\\\\\\\\\\\\/b');\n});\n"
  },
  {
    "path": "tests/test-split.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// utilities\n\nconst verifyBuffer = (bufArray, t) =>\n  bufArray.map(x => {\n    t.ok(x instanceof Buffer);\n    return x.toString();\n  });\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split\n\ntest('test split', t => {\n  let re = new RE2(/\\s+/);\n  let result = re.split('Oh brave new world that has such people in it.');\n  t.deepEqual(result, [\n    'Oh',\n    'brave',\n    'new',\n    'world',\n    'that',\n    'has',\n    'such',\n    'people',\n    'in',\n    'it.'\n  ]);\n\n  re = new RE2(',');\n  result = re.split('Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec');\n  t.deepEqual(result, [\n    'Jan',\n    'Feb',\n    'Mar',\n    'Apr',\n    'May',\n    'Jun',\n    'Jul',\n    'Aug',\n    'Sep',\n    'Oct',\n    'Nov',\n    'Dec'\n  ]);\n\n  re = new RE2(',');\n  result = re.split(',Jan,Feb,Mar,Apr,May,Jun,,Jul,Aug,Sep,Oct,Nov,Dec,');\n  t.deepEqual(result, [\n    '',\n    'Jan',\n    'Feb',\n    'Mar',\n    'Apr',\n    'May',\n    'Jun',\n    '',\n    'Jul',\n    'Aug',\n    'Sep',\n    'Oct',\n    'Nov',\n    'Dec',\n    ''\n  ]);\n\n  re = new RE2(/\\s*;\\s*/);\n  result = re.split(\n    'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand '\n  );\n  t.deepEqual(result, [\n    'Harry Trump',\n    'Fred Barney',\n    'Helen Rigby',\n    'Bill Abel',\n    'Chris Hand '\n  ]);\n\n  re = new RE2(/\\s+/);\n  result = re.split('Hello World. How are you doing?', 3);\n  t.deepEqual(result, ['Hello', 'World.', 'How']);\n\n  re = new RE2(/(\\d)/);\n  result = re.split('Hello 1 word. Sentence number 2.');\n  t.deepEqual(result, ['Hello ', '1', ' word. Sentence number ', '2', '.']);\n\n  t.deepEqual(\n    RE2(/[x-z]*/)\n      .split('asdfghjkl')\n      .reverse()\n      .join(''),\n    'lkjhgfdsa'\n  );\n});\n\ntest('test_splitInvalid', t => {\n  const re = RE2('');\n\n  try {\n    re.split({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n});\n\ntest('test_cornerCases', t => {\n  const re = new RE2(/1/);\n  const result = re.split('23456');\n  t.deepEqual(result, ['23456']);\n});\n\n// Unicode tests\n\ntest('test split unicode', t => {\n  let re = new RE2(/\\s+/);\n  let result = re.split('Она не понимает, что этим убивает меня.');\n  t.deepEqual(result, [\n    'Она',\n    'не',\n    'понимает,',\n    'что',\n    'этим',\n    'убивает',\n    'меня.'\n  ]);\n\n  re = new RE2(',');\n  result = re.split('Пн,Вт,Ср,Чт,Пт,Сб,Вс');\n  t.deepEqual(result, ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']);\n\n  re = new RE2(/\\s*;\\s*/);\n  result = re.split('Ваня Иванов ;Петро Петренко; Саша Машин ; Маша Сашина');\n  t.deepEqual(result, [\n    'Ваня Иванов',\n    'Петро Петренко',\n    'Саша Машин',\n    'Маша Сашина'\n  ]);\n\n  re = new RE2(/\\s+/);\n  result = re.split('Привет мир. Как дела?', 3);\n  t.deepEqual(result, ['Привет', 'мир.', 'Как']);\n\n  re = new RE2(/(\\d)/);\n  result = re.split('Привет 1 слово. Предложение номер 2.');\n  t.deepEqual(result, ['Привет ', '1', ' слово. Предложение номер ', '2', '.']);\n\n  t.deepEqual(\n    RE2(/[э-я]*/)\n      .split('фывапролд')\n      .reverse()\n      .join(''),\n    'длорпавыф'\n  );\n});\n\n// Buffer tests\n\ntest('test split buffer', t => {\n  let re = new RE2(/\\s+/);\n  let result = re.split(Buffer.from('Она не понимает, что этим убивает меня.'));\n  t.deepEqual(verifyBuffer(result, t), [\n    'Она',\n    'не',\n    'понимает,',\n    'что',\n    'этим',\n    'убивает',\n    'меня.'\n  ]);\n\n  re = new RE2(',');\n  result = re.split(Buffer.from('Пн,Вт,Ср,Чт,Пт,Сб,Вс'));\n  t.deepEqual(verifyBuffer(result, t), [\n    'Пн',\n    'Вт',\n    'Ср',\n    'Чт',\n    'Пт',\n    'Сб',\n    'Вс'\n  ]);\n\n  re = new RE2(/\\s*;\\s*/);\n  result = re.split(\n    Buffer.from('Ваня Иванов ;Петро Петренко; Саша Машин ; Маша Сашина')\n  );\n  t.deepEqual(verifyBuffer(result, t), [\n    'Ваня Иванов',\n    'Петро Петренко',\n    'Саша Машин',\n    'Маша Сашина'\n  ]);\n\n  re = new RE2(/\\s+/);\n  result = re.split(Buffer.from('Привет мир. Как дела?'), 3);\n  t.deepEqual(verifyBuffer(result, t), ['Привет', 'мир.', 'Как']);\n\n  re = new RE2(/(\\d)/);\n  result = re.split(Buffer.from('Привет 1 слово. Предложение номер 2.'));\n  t.deepEqual(verifyBuffer(result, t), [\n    'Привет ',\n    '1',\n    ' слово. Предложение номер ',\n    '2',\n    '.'\n  ]);\n\n  t.deepEqual(\n    RE2(/[э-я]*/)\n      .split(Buffer.from('фывапролд'))\n      .reverse()\n      .join(''),\n    'длорпавыф'\n  );\n});\n\ntest('test split alternation groups', t => {\n  const re = new RE2(/(a)|(b)/);\n  const result = re.split('xaxbx');\n  t.deepEqual(result, ['x', 'a', undefined, 'x', undefined, 'b', 'x']);\n\n  const re2 = new RE2(/(a)|(b)/);\n  const bufResult = re2.split(Buffer.from('xaxbx'));\n  t.equal(bufResult.length, 7);\n  t.ok(bufResult[0] instanceof Buffer);\n  t.equal(bufResult[0].toString(), 'x');\n  t.ok(bufResult[1] instanceof Buffer);\n  t.equal(bufResult[1].toString(), 'a');\n  t.equal(bufResult[2], undefined);\n  t.equal(bufResult[4], undefined);\n  t.ok(bufResult[5] instanceof Buffer);\n  t.equal(bufResult[5].toString(), 'b');\n});\n\n// Sticky tests\n\ntest('test split sticky', t => {\n  const re = new RE2(/\\s+/y); // sticky is ignored\n\n  const result = re.split('Oh brave new world that has such people in it.');\n  t.deepEqual(result, [\n    'Oh',\n    'brave',\n    'new',\n    'world',\n    'that',\n    'has',\n    'such',\n    'people',\n    'in',\n    'it.'\n  ]);\n\n  const result2 = re.split(' Oh brave new world that has such people in it.');\n  t.deepEqual(result2, [\n    '',\n    'Oh',\n    'brave',\n    'new',\n    'world',\n    'that',\n    'has',\n    'such',\n    'people',\n    'in',\n    'it.'\n  ]);\n});\n"
  },
  {
    "path": "tests/test-symbols.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test match symbol', t => {\n  if (typeof Symbol == 'undefined' || !Symbol.match) return;\n\n  const str = 'For more information, see Chapter 3.4.5.1';\n\n  const re = new RE2(/(chapter \\d+(\\.\\d)*)/i);\n  const result = str.match(re);\n\n  t.equal(result.input, str);\n  t.equal(result.index, 26);\n  t.equal(result.length, 3);\n  t.equal(result[0], 'Chapter 3.4.5.1');\n  t.equal(result[1], 'Chapter 3.4.5.1');\n  t.equal(result[2], '.1');\n});\n\ntest('test search symbol', t => {\n  if (typeof Symbol == 'undefined' || !Symbol.search) return;\n\n  const str = 'Total is 42 units.';\n\n  let re = new RE2(/\\d+/i);\n  let result = str.search(re);\n  t.equal(result, 9);\n\n  re = new RE2('\\\\b[a-z]+\\\\b');\n  result = str.search(re);\n  t.equal(result, 6);\n\n  re = new RE2('\\\\b\\\\w+\\\\b');\n  result = str.search(re);\n  t.equal(result, 0);\n\n  re = new RE2('z', 'gm');\n  result = str.search(re);\n  t.equal(result, -1);\n});\n\ntest('test replace symbol', t => {\n  if (typeof Symbol == 'undefined' || !Symbol.replace) return;\n\n  let re = new RE2(/apples/gi);\n  let result = 'Apples are round, and apples are juicy.'.replace(re, 'oranges');\n  t.equal(result, 'oranges are round, and oranges are juicy.');\n\n  re = new RE2(/xmas/i);\n  result = 'Twas the night before Xmas...'.replace(re, 'Christmas');\n  t.equal(result, 'Twas the night before Christmas...');\n\n  re = new RE2(/(\\w+)\\s(\\w+)/);\n  result = 'John Smith'.replace(re, '$2, $1');\n  t.equal(result, 'Smith, John');\n});\n\ntest('test split symbol', t => {\n  if (typeof Symbol == 'undefined' || !Symbol.split) return;\n\n  let re = new RE2(/\\s+/);\n  let result = 'Oh brave new world that has such people in it.'.split(re);\n  t.deepEqual(result, [\n    'Oh',\n    'brave',\n    'new',\n    'world',\n    'that',\n    'has',\n    'such',\n    'people',\n    'in',\n    'it.'\n  ]);\n\n  re = new RE2(',');\n  result = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(re);\n  t.deepEqual(result, [\n    'Jan',\n    'Feb',\n    'Mar',\n    'Apr',\n    'May',\n    'Jun',\n    'Jul',\n    'Aug',\n    'Sep',\n    'Oct',\n    'Nov',\n    'Dec'\n  ]);\n\n  re = new RE2(/\\s*;\\s*/);\n  result =\n    'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand '.split(re);\n  t.deepEqual(result, [\n    'Harry Trump',\n    'Fred Barney',\n    'Helen Rigby',\n    'Bill Abel',\n    'Chris Hand '\n  ]);\n\n  re = new RE2(/\\s+/);\n  result = 'Hello World. How are you doing?'.split(re, 3);\n  t.deepEqual(result, ['Hello', 'World.', 'How']);\n\n  re = new RE2(/(\\d)/);\n  result = 'Hello 1 word. Sentence number 2.'.split(re);\n  t.deepEqual(result, ['Hello ', '1', ' word. Sentence number ', '2', '.']);\n\n  t.equal(\n    'asdfghjkl'\n      .split(RE2(/[x-z]*/))\n      .reverse()\n      .join(''),\n    'lkjhgfdsa'\n  );\n});\n"
  },
  {
    "path": "tests/test-test.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\n// These tests are copied from MDN:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test\n\ntest('test test from exec', t => {\n  let re = new RE2('quick\\\\s(brown).+?(jumps)', 'i');\n\n  t.equal(re.test('The Quick Brown Fox Jumps Over The Lazy Dog'), true);\n  t.equal(re.test('tHE qUICK bROWN fOX jUMPS oVER tHE lAZY dOG'), true);\n  t.equal(re.test('the quick brown fox jumps over the lazy dog'), true);\n  t.equal(re.test('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG'), true);\n  t.equal(re.test('THE KWIK BROWN FOX JUMPS OVER THE LAZY DOG'), false);\n\n  re = new RE2('ab*', 'g');\n\n  t.ok(re.test('abbcdefabh'));\n  t.notOk(re.test('qwerty'));\n\n  re = new RE2('(hello \\\\S+)');\n\n  t.ok(re.test('This is a hello world!'));\n  t.notOk(re.test('This is a Hello world!'));\n});\n\ntest('test test successive', t => {\n  const str = 'abbcdefabh';\n\n  const re = new RE2('ab*', 'g');\n  let result = re.test(str);\n\n  t.ok(result);\n  t.equal(re.lastIndex, 3);\n\n  result = re.test(str);\n\n  t.ok(result);\n  t.equal(re.lastIndex, 9);\n\n  result = re.test(str);\n\n  t.notOk(result);\n});\n\ntest('test test simple', t => {\n  const str = 'abbcdefabh';\n\n  const re1 = new RE2('ab*', 'g');\n  t.ok(re1.test(str));\n\n  const re2 = new RE2('ab*');\n  t.ok(re2.test(str));\n\n  const re3 = new RE2('abc');\n  t.notOk(re3.test(str));\n});\n\ntest('test test anchored to beginning', t => {\n  const re = RE2('^hello', 'g');\n\n  t.ok(re.test('hellohello'));\n  t.notOk(re.test('hellohello'));\n});\n\ntest('test test invalid', t => {\n  const re = RE2('');\n\n  try {\n    re.test({\n      toString() {\n        throw 'corner';\n      }\n    });\n    t.fail(); // shouldn't be here\n  } catch (e) {\n    t.equal(e, 'corner');\n  }\n});\n\ntest('test test anchor 1', t => {\n  const re = new RE2('b|^a', 'g');\n\n  let result = re.test('aabc');\n  t.ok(result);\n  t.equal(re.lastIndex, 1);\n\n  result = re.test('aabc');\n  t.ok(result);\n  t.equal(re.lastIndex, 3);\n\n  result = re.test('aabc');\n  t.notOk(result);\n});\n\ntest('test test anchor 2', t => {\n  const re = new RE2('(?:^a)', 'g');\n\n  let result = re.test('aabc');\n  t.ok(result);\n  t.equal(re.lastIndex, 1);\n\n  result = re.test('aabc');\n  t.notOk(result);\n});\n\n// Unicode tests\n\ntest('test test unicode', t => {\n  let re = new RE2('охотник\\\\s(желает).+?(где)', 'i');\n\n  t.ok(re.test('Каждый Охотник Желает Знать Где Сидит Фазан'));\n  t.ok(re.test('кАЖДЫЙ оХОТНИК жЕЛАЕТ зНАТЬ гДЕ сИДИТ фАЗАН'));\n  t.ok(re.test('каждый охотник желает знать где сидит фазан'));\n  t.ok(re.test('КАЖДЫЙ ОХОТНИК ЖЕЛАЕТ ЗНАТЬ ГДЕ СИДИТ ФАЗАН'));\n  t.notOk(re.test('Кажный Стрелок Хочет Найти Иде Прячется Птица'));\n\n  re = new RE2('аб*', 'g');\n\n  t.ok(re.test('аббвгдеабё'));\n  t.notOk(re.test('йцукен'));\n\n  re = new RE2('(привет \\\\S+)');\n\n  t.ok(re.test('Это просто привет всем.'));\n  t.notOk(re.test('Это просто Привет всем.'));\n});\n\ntest('test test unicode subsequent', t => {\n  const str = 'аббвгдеабё';\n\n  const re = new RE2('аб*', 'g');\n  let result = re.test(str);\n\n  t.ok(result);\n  t.equal(re.lastIndex, 3);\n\n  result = re.test(str);\n\n  t.ok(result);\n  t.equal(re.lastIndex, 9);\n\n  result = re.test(str);\n\n  t.notOk(result);\n});\n\n// Buffer tests\n\ntest('test test buffer', t => {\n  let re = new RE2('охотник\\\\s(желает).+?(где)', 'i');\n\n  t.ok(re.test(Buffer.from('Каждый Охотник Желает Знать Где Сидит Фазан')));\n  t.ok(re.test(Buffer.from('кАЖДЫЙ оХОТНИК жЕЛАЕТ зНАТЬ гДЕ сИДИТ фАЗАН')));\n  t.ok(re.test(Buffer.from('каждый охотник желает знать где сидит фазан')));\n  t.ok(re.test(Buffer.from('КАЖДЫЙ ОХОТНИК ЖЕЛАЕТ ЗНАТЬ ГДЕ СИДИТ ФАЗАН')));\n  t.notOk(\n    re.test(Buffer.from('Кажный Стрелок Хочет Найти Иде Прячется Птица'))\n  );\n\n  re = new RE2('аб*', 'g');\n\n  t.ok(re.test(Buffer.from('аббвгдеабё')));\n  t.notOk(re.test(Buffer.from('йцукен')));\n\n  re = new RE2('(привет \\\\S+)');\n\n  t.ok(re.test(Buffer.from('Это просто привет всем.')));\n  t.notOk(re.test(Buffer.from('Это просто Привет всем.')));\n});\n\n// Sticky tests\n\ntest('test test sticky', t => {\n  const re = new RE2('\\\\s+', 'y');\n\n  t.notOk(re.test('Hello world, how are you?'));\n\n  re.lastIndex = 5;\n\n  t.ok(re.test('Hello world, how are you?'));\n  t.equal(re.lastIndex, 6);\n\n  const re2 = new RE2('\\\\s+', 'gy');\n\n  t.notOk(re2.test('Hello world, how are you?'));\n\n  re2.lastIndex = 5;\n\n  t.ok(re2.test('Hello world, how are you?'));\n  t.equal(re2.lastIndex, 6);\n});\n"
  },
  {
    "path": "tests/test-toString.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test toString', t => {\n  t.equal(RE2('').toString(), '/(?:)/u');\n  t.equal(RE2('a').toString(), '/a/u');\n  t.equal(RE2('b', 'i').toString(), '/b/iu');\n  t.equal(RE2('c', 'g').toString(), '/c/gu');\n  t.equal(RE2('d', 'm').toString(), '/d/mu');\n  t.equal(RE2('\\\\d+', 'gi') + '', '/\\\\d+/giu');\n  t.equal(RE2('\\\\s*', 'gm') + '', '/\\\\s*/gmu');\n  t.equal(RE2('\\\\S{1,3}', 'ig') + '', '/\\\\S{1,3}/giu');\n  t.equal(RE2('\\\\D{,2}', 'mig') + '', '/\\\\D{,2}/gimu');\n  t.equal(RE2('^a{2,}', 'mi') + '', '/^a{2,}/imu');\n  t.equal(RE2('^a{5}$', 'gim') + '', '/^a{5}$/gimu');\n  t.equal(RE2('\\\\u{1F603}/', 'iy') + '', '/\\\\u{1F603}\\\\//iuy');\n  t.equal(RE2('^a{2,}', 'smi') + '', '/^a{2,}/imsu');\n\n  t.equal(RE2('c', 'ug').toString(), '/c/gu');\n  t.equal(RE2('d', 'um').toString(), '/d/mu');\n\n  t.equal(RE2('a', 'd').toString(), '/a/du');\n  t.equal(RE2('a', 'dg').toString(), '/a/dgu');\n  t.equal(RE2('a', 'dgi').toString(), '/a/dgiu');\n  t.equal(RE2('a', 'dgimsy').toString(), '/a/dgimsuy');\n});\n"
  },
  {
    "path": "tests/test-unicode-classes.mjs",
    "content": "import test from 'tape-six';\nimport {RE2} from '../re2.js';\n\n// tests\n\ntest('test_unicodeClasses', t => {\n  'use strict';\n\n  let re2 = new RE2(/\\p{L}/u);\n  t.ok(re2.test('a'));\n  t.notOk(re2.test('1'));\n\n  re2 = new RE2(/\\p{Letter}/u);\n  t.ok(re2.test('a'));\n  t.notOk(re2.test('1'));\n\n  re2 = new RE2(/\\p{Lu}/u);\n  t.ok(re2.test('A'));\n  t.notOk(re2.test('a'));\n\n  re2 = new RE2(/\\p{Uppercase_Letter}/u);\n  t.ok(re2.test('A'));\n  t.notOk(re2.test('a'));\n\n  re2 = new RE2(/\\p{Script=Latin}/u);\n  t.ok(re2.test('a'));\n  t.notOk(re2.test('ф'));\n\n  re2 = new RE2(/\\p{sc=Cyrillic}/u);\n  t.notOk(re2.test('a'));\n  t.ok(re2.test('ф'));\n});\n"
  },
  {
    "path": "ts-tests/test-types.ts",
    "content": "import RE2 from 're2';\n\nfunction assertType<T>(_val: T) {}\n\nfunction test_constructors() {\n  const re1 = new RE2('abc');\n  const re2 = new RE2('abc', 'gi');\n  const re3 = new RE2(Buffer.from('abc'));\n  const re4 = new RE2(Buffer.from('abc'), 'i');\n  const re5 = new RE2(/abc/i);\n  const re6 = new RE2(re1);\n  const re7 = RE2('abc');\n  const re8 = RE2('abc', 'gi');\n  const re9 = RE2(Buffer.from('abc'));\n  const re10 = RE2(/abc/i);\n  assertType<RE2>(re1);\n  assertType<RE2>(re2);\n  assertType<RE2>(re3);\n  assertType<RE2>(re4);\n  assertType<RE2>(re5);\n  assertType<RE2>(re6);\n  assertType<RE2>(re7);\n  assertType<RE2>(re8);\n  assertType<RE2>(re9);\n  assertType<RE2>(re10);\n}\n\nfunction test_properties() {\n  const re = new RE2('abc', 'dgimsuy');\n  assertType<string>(re.source);\n  assertType<string>(re.flags);\n  assertType<boolean>(re.global);\n  assertType<boolean>(re.ignoreCase);\n  assertType<boolean>(re.multiline);\n  assertType<boolean>(re.dotAll);\n  assertType<boolean>(re.unicode);\n  assertType<boolean>(re.sticky);\n  assertType<boolean>(re.hasIndices);\n  assertType<number>(re.lastIndex);\n  assertType<string>(re.internalSource);\n  re.lastIndex = 5;\n}\n\nfunction test_execTypes() {\n  const re = new RE2('quick\\\\s(brown).+?(?<verb>jumps)', 'ig');\n  const result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');\n  if (!(result && result.groups)) {\n    throw 'Unexpected Result';\n  }\n  assertType<number>(result.index);\n  assertType<string>(result.input);\n  assertType<string | undefined>(result.groups['verb']);\n}\n\nfunction test_execBufferTypes() {\n  const re = new RE2('abc', 'ig');\n  const result = re.exec(Buffer.from('xabcx'));\n  if (!result) {\n    throw 'Unexpected Result';\n  }\n  assertType<number>(result.index);\n  assertType<Buffer>(result.input);\n  assertType<Buffer>(result[0]);\n}\n\nfunction test_matchTypes() {\n  const re = new RE2('quick\\\\s(brown).+?(?<verb>jumps)', 'ig');\n  const result = re.match('The Quick Brown Fox Jumps Over The Lazy Dog');\n  if (!(result && result.index && result.input && result.groups)) {\n    throw 'Unexpected Result';\n  }\n  assertType<number>(result.index);\n  assertType<string>(result.input);\n  assertType<string | undefined>(result.groups['verb']);\n}\n\nfunction test_matchBufferTypes() {\n  const re = new RE2('abc', 'i');\n  const result = re.match(Buffer.from('xabcx'));\n  if (!result) {\n    throw 'Unexpected Result';\n  }\n  assertType<Buffer>(result[0]);\n}\n\nfunction test_testTypes() {\n  const re = new RE2('abc');\n  assertType<boolean>(re.test('xabcx'));\n  assertType<boolean>(re.test(Buffer.from('xabcx')));\n}\n\nfunction test_searchTypes() {\n  const re = new RE2('abc');\n  assertType<number>(re.search('xabcx'));\n  assertType<number>(re.search(Buffer.from('xabcx')));\n}\n\nfunction test_replaceTypes() {\n  const re = new RE2('abc', 'g');\n  assertType<string>(re.replace('xabcx', 'def'));\n  assertType<string>(re.replace('xabcx', (match: string) => match.toUpperCase()));\n  assertType<Buffer>(re.replace(Buffer.from('xabcx'), Buffer.from('def')));\n}\n\nfunction test_splitTypes() {\n  const re = new RE2(',');\n  assertType<string[]>(re.split('a,b,c'));\n  assertType<string[]>(re.split('a,b,c', 2));\n  assertType<Buffer[]>(re.split(Buffer.from('a,b,c')));\n  assertType<Buffer[]>(re.split(Buffer.from('a,b,c'), 2));\n}\n\nfunction test_toStringType() {\n  const re = new RE2('abc', 'gi');\n  assertType<string>(re.toString());\n}\n\nfunction test_staticMembers() {\n  assertType<number>(RE2.getUtf8Length('hello'));\n  assertType<number>(RE2.getUtf16Length(Buffer.from('hello')));\n  assertType<'nothing' | 'warnOnce' | 'warn' | 'throw'>(\n    RE2.unicodeWarningLevel\n  );\n  RE2.unicodeWarningLevel = 'nothing';\n\n  const {RE2: NamedRE2} = RE2;\n  assertType<typeof RE2>(NamedRE2);\n  const re = new NamedRE2('abc');\n  assertType<RE2>(re);\n}\n\nfunction test_setTypes() {\n  const set = new RE2.Set(['alpha', Buffer.from('beta')], 'i', {\n    anchor: 'start'\n  });\n  assertType<number[]>(set.match('alphabet'));\n  assertType<boolean>(set.test(Buffer.from('alphabet')));\n  assertType<'unanchored' | 'start' | 'both'>(set.anchor);\n  assertType<string[]>(set.sources);\n  assertType<string>(set.flags);\n  assertType<number>(set.size);\n  assertType<string>(set.source);\n  assertType<string>(set.toString());\n\n  const set2 = RE2.Set(['a', 'b']);\n  assertType<number[]>(set2.match('a'));\n\n  const set3 = new RE2.Set([new RE2('a'), /b/]);\n  assertType<boolean>(set3.test('a'));\n\n  const set4 = new RE2.Set(['a'], {anchor: 'both'});\n  assertType<'unanchored' | 'start' | 'both'>(set4.anchor);\n}\n\ntest_constructors();\ntest_properties();\ntest_execTypes();\ntest_execBufferTypes();\ntest_matchTypes();\ntest_matchBufferTypes();\ntest_testTypes();\ntest_searchTypes();\ntest_replaceTypes();\ntest_splitTypes();\ntest_toStringType();\ntest_staticMembers();\ntest_setTypes();\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"noEmit\": true,\n    \"lib\": [\"ES2022\"],\n    \"types\": [\"node\"],\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"strict\": true,\n    \"allowUnusedLabels\": false,\n    \"allowUnreachableCode\": false,\n    \"exactOptionalPropertyTypes\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitOverride\": true,\n    \"noImplicitReturns\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"skipDefaultLibCheck\": false\n  },\n  \"include\": [\"**/*.ts\"],\n  \"exclude\": [\"vendor/re2/app/**\"]\n}\n"
  }
]